<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fais Azis Wibowo</title>
    <description>The latest articles on DEV Community by Fais Azis Wibowo (@faith_b6e08f3b8f05a77bb5f).</description>
    <link>https://dev.to/faith_b6e08f3b8f05a77bb5f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3838124%2F70f71d92-4c28-4811-ae93-6a8a54cb928d.jpg</url>
      <title>DEV Community: Fais Azis Wibowo</title>
      <link>https://dev.to/faith_b6e08f3b8f05a77bb5f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faith_b6e08f3b8f05a77bb5f"/>
    <language>en</language>
    <item>
      <title>Deep Learning for Image Classification: Ensemble CNN Architectures with Test Time Augmentation</title>
      <dc:creator>Fais Azis Wibowo</dc:creator>
      <pubDate>Sun, 29 Mar 2026 08:46:33 +0000</pubDate>
      <link>https://dev.to/faith_b6e08f3b8f05a77bb5f/ensemble-cnn-with-test-time-augmentation-for-mnist-digit-recognition-a-top-6-kaggle-solution-2a1d</link>
      <guid>https://dev.to/faith_b6e08f3b8f05a77bb5f/ensemble-cnn-with-test-time-augmentation-for-mnist-digit-recognition-a-top-6-kaggle-solution-2a1d</guid>
      <description>&lt;p&gt;&lt;em&gt;Accuracy: 0.99628 · Rank: 72 / 1,181 · Kaggle Digit Recognizer Competition&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiuy1mj9xxrhilm18ua3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiuy1mj9xxrhilm18ua3s.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Framing
&lt;/h2&gt;

&lt;p&gt;MNIST is a solved problem in the academic sense — state-of-the-art models have exceeded human-level performance on it for years. The challenge in a competitive context is not whether a CNN can classify handwritten digits, but how much variance you can squeeze out of an already high-performing system when the ceiling is 1.0, and the marginal gains are measured in the fourth decimal place.&lt;/p&gt;

&lt;p&gt;At 99.6%+ accuracy, a single misclassified digit per 200 samples is the difference between medal territory and the middle of the leaderboard. The solution presented here addresses this precision problem through two compounding mechanisms: ensemble diversity to reduce variance across model architectures, and Test Time Augmentation (TTA) to reduce variance across the inference distribution. The combination pushed a single-model baseline of 0.995035 to a final leaderboard score of 0.99628.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Preprocessing Pipeline
&lt;/h2&gt;

&lt;p&gt;The preprocessing pipeline is intentionally minimal — MNIST's controlled acquisition conditions mean aggressive preprocessing adds noise rather than signal.&lt;/p&gt;

&lt;p&gt;Normalisation scales pixel intensities from the raw [0, 255] integer range to [0.0, 1.0] float32. This is not optional — unnormalised inputs cause gradient instability during early training epochs, particularly with BatchNormalisation layers in the network.&lt;/p&gt;

&lt;p&gt;Reshaping transforms the flat 784-dimensional pixel vectors into (28, 28, 1) tensors. The channel dimension is explicit even though MNIST is grayscale — omitting it causes shape mismatches in Conv2D layers expecting a channel axis.&lt;/p&gt;

&lt;p&gt;Label encoding uses one-hot encoding across 10 classes, compatible with categorical cross-entropy loss.&lt;/p&gt;

&lt;p&gt;No augmentation is applied at the preprocessing stage — augmentation is handled dynamically during training via Keras &lt;em&gt;ImageDataGenerator&lt;/em&gt;, keeping the validation set clean and unperturbed for honest accuracy measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ensemble Architecture Design
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fla54tnbo59j3h76keysh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fla54tnbo59j3h76keysh.png" alt=" " width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core hypothesis driving the ensemble design is that architecturally diverse models make uncorrelated errors. When models fail on different samples, their averaged softmax outputs smooth over individual weaknesses. Five CNN architectures were selected to span a range of representational depths and widths:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CNN-A and CNN-C (2-Block Shallow)&lt;/strong&gt;&lt;br&gt;
Conv2D(32) × 2 → MaxPool → Conv2D(64) × 2 → MaxPool → Dense(256)&lt;br&gt;
Shallow networks generalise quickly and act as low-variance baselines. Their representational capacity is sufficient for MNIST's relatively simple feature hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CNN-B and CNN-E (3-Block Deep)&lt;/strong&gt;&lt;br&gt;
Conv2D(32) × 2 → MaxPool → Conv2D(64) × 2 → MaxPool → Conv2D(128) × 2 → MaxPool → Dense(512)&lt;br&gt;
The additional convolutional block captures higher-order spatial relationships — stroke intersections, curve terminations — that shallower networks approximate less precisely.&lt;br&gt;
CNN-D (Wide 2-Block)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conv2D(64) × 2 → MaxPool → Conv2D(128) × 2 → MaxPool → Dense(512)&lt;/strong&gt;&lt;br&gt;
Wider early filters increase the number of low-level feature detectors without adding depth. This architecture is architecturally distinct from both A/C and B/E, contributing a different error profile to the ensemble.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regularisation applied uniformly across all five architectures:&lt;/strong&gt;&lt;br&gt;
BatchNormalisation after each convolutional block — stabilises activations and reduces sensitivity to weight initialisation&lt;/p&gt;

&lt;p&gt;Dropout(0.25) after convolutional blocks, Dropout(0.5) before the final dense layer — independently drops units during training to prevent co-adaptation, &lt;em&gt;MaxPooling2D&lt;/em&gt; for spatial downsampling and translation invariance&lt;/p&gt;

&lt;p&gt;The choice to duplicate architectures (A/C, B/E) rather than use five entirely distinct designs is intentional — identical architectures trained from different random initialisations with different augmentation sequences produce meaningfully different weight configurations and therefore different error patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training Strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Optimiser:&lt;/strong&gt; Adam with default parameters. Adaptive learning rate methods consistently outperform SGD with momentum on vision tasks at this scale without requiring manual schedule tuning.&lt;br&gt;
*&lt;em&gt;2. Real-time Data Augmentation via &lt;em&gt;ImageDataGenerator&lt;/em&gt;: *&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rotation range (15°): Simulates natural variations in handwritten digits&lt;/li&gt;
&lt;li&gt;Zoom range (15%): Accounts for differences in digit size/scale&lt;/li&gt;
&lt;li&gt;Width shift range (15%): Handles horizontal misalignment or centering variation&lt;/li&gt;
&lt;li&gt;Height shift range (15%): Handles vertical misalignment or centering variation&lt;/li&gt;
&lt;li&gt;Shear range (0.1): Simulates slight perspective distortions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Augmentation is applied on-the-fly during training — each epoch sees a stochastically transformed version of the training set, effectively expanding the training distribution without increasing dataset size. This is the primary mechanism preventing overfitting across 50 training epochs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Callbacks:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;ReduceLROnPlateau&lt;/em&gt; monitors validation accuracy and halves the learning rate when no improvement is observed over a patience window. This allows the optimiser to take larger steps during early training and finer steps as it approaches the loss minimum — recovering accuracy that flat learning rate schedules leave on the table.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;EarlyStopping&lt;/em&gt; with &lt;em&gt;restore_best_weights=True&lt;/em&gt; terminates training when validation accuracy plateaus and restores the checkpoint from the optimal epoch. This is critical in an ensemble context — each of the five models must contribute its best possible weights, not its final weights.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Inference: Test Time Augmentation&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fou3mt7na3bwemn1tklml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fou3mt7na3bwemn1tklml.png" alt=" " width="800" height="397"&gt;&lt;/a&gt;&lt;br&gt;
A trained model's prediction on a single test image is a point estimate — one forward pass through a stochastic function approximator. TTA converts this point estimate into a distributional estimate by averaging predictions across multiple augmented versions of the same image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. TTA protocol:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each test image, generate 15 augmented variants using the same ImageDataGenerator configuration used during training&lt;/li&gt;
&lt;li&gt;Run each variant through each of the 5 trained models&lt;/li&gt;
&lt;li&gt;Collect softmax probability vectors from all 80 forward passes (5 models × 16 passes: 1 original + 15 augmented)&lt;/li&gt;
&lt;li&gt;Average the 80 probability vectors element-wise&lt;/li&gt;
&lt;li&gt;Select the class index with the highest averaged probability as the final prediction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mathematical intuition: if a model misclassifies an augmented variant of a digit, the correct class still accumulates probability mass across the other 79 passes. The averaging operation suppresses low-confidence noise and amplifies the consistent signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Quantified impact of each component:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfp3vobgai0rg1sn6lx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfp3vobgai0rg1sn6lx8.png" alt=" " width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single CNN baseline: ~99.54% validation accuracy, &lt;strong&gt;0.99503&lt;/strong&gt; leaderboard score&lt;/li&gt;
&lt;li&gt;5-model ensemble + TTA: ~99.57% average validation accuracy, &lt;strong&gt;0.99628&lt;/strong&gt; leaderboard score&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The leaderboard gain of &lt;strong&gt;+0.00125&lt;/strong&gt; over the single-model baseline — while appearing marginal — represents a reduction of roughly 1 misclassification per 800 test samples. At this accuracy regime, that is the practical limit of what ensemble diversity and distributional inference averaging can recover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash# 
Download competition data
kaggle competitions download &lt;span class="nt"&gt;-c&lt;/span&gt; digit-recognizer

&lt;span class="c"&gt;# Train all five CNN architectures&lt;/span&gt;
python train_ensemble.py

&lt;span class="c"&gt;# Run TTA inference and generate submission&lt;/span&gt;
python predict_ensemble.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Analysis: Where the Remaining Errors Live&lt;/strong&gt;&lt;br&gt;
At 0.99628, the residual errors are concentrated in a small set of structurally ambiguous digit pairs — primarily (4, 9), (3, 5), and (7, 1) — where stroke topology is genuinely similar and the distinguishing feature is a single curve or termination point. These are the cases where even human annotators disagree at non-trivial rates.&lt;/p&gt;

&lt;p&gt;Pushing beyond this threshold would require either capsule networks (which preserve spatial hierarchies that MaxPooling discards), or a larger training set sourced from distributions beyond MNIST's controlled acquisition environment. Within the constraints of this competition, 80-pass TTA across a 5-model ensemble represents a practical ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Findings
&lt;/h2&gt;

&lt;p&gt;Three methodological conclusions generalise beyond MNIST:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectural diversity outperforms depth uniformity in ensembles. Five architecturally varied models with moderate depth outperform five deep identical models — the variance reduction mechanism requires uncorrelated errors, which requires architectural differences.&lt;/li&gt;
&lt;li&gt;TTA is a zero-cost accuracy gain at inference time. Once models are trained, TTA costs only additional forward passes. On MNIST-scale images this is computationally trivial. On larger datasets the compute cost scales proportionally with image size and TTA count — budget accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;EarlyStopping&lt;/em&gt; with weight restoration is non-negotiable in multi-model ensembles. Final-epoch weights frequently underperform best-epoch weights by 0.1–0.3% validation accuracy. Across five models this compounds — the ensemble ceiling is only as high as the best checkpoint of each contributor.&lt;/p&gt;

&lt;p&gt;Full implementation available at: &lt;a href="https://github.com/faissssss/kaggle-digit-recognizer" rel="noopener noreferrer"&gt;github.com/faissssss/kaggle-digit-recognizer&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://www.kaggle.com/c/digit-recognizer" rel="noopener noreferrer"&gt;kaggle.com/c/digit-recognizer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>deeplearning</category>
      <category>cnn</category>
    </item>
    <item>
      <title>Tabular Machine Learning for Predictive Modeling: A Ridge-XGBoost N-gram Pipeline for Customer Churn</title>
      <dc:creator>Fais Azis Wibowo</dc:creator>
      <pubDate>Sun, 29 Mar 2026 07:37:33 +0000</pubDate>
      <link>https://dev.to/faith_b6e08f3b8f05a77bb5f/how-i-reached-top-8-on-kaggle-with-a-ridge-xgboost-n-gram-pipeline-32pa</link>
      <guid>https://dev.to/faith_b6e08f3b8f05a77bb5f/how-i-reached-top-8-on-kaggle-with-a-ridge-xgboost-n-gram-pipeline-32pa</guid>
      <description>&lt;p&gt;&lt;em&gt;Kaggle Playground Series S6E3 — Predict Customer Churn | ROC-AUC 0.91685 | Rank 286 / 3,718&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cnyo230xu9k0ohbiijm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cnyo230xu9k0ohbiijm.png" alt=" " width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Customer churn prediction sounds straightforward — given a telecom customer's usage history and contract details, predict whether they'll leave. But the Kaggle Playground S6E3 dataset had &lt;strong&gt;594,000 rows&lt;/strong&gt; of heavily categorical data where the signal was buried inside combinations of features, not individual columns. Standard approaches plateau quickly here.&lt;br&gt;
My starting point was a LightGBM single model. It was decent — but decent doesn't crack the top 10%. Getting there required rethinking how the model saw the categorical features entirely.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Core Insight: Treat Categories Like Text
&lt;/h2&gt;

&lt;p&gt;The breakthrough came from an unconventional direction — NLP. In text classification, n-grams capture phrase-level patterns that individual words miss. The same logic applies to categorical feature combinations.&lt;/p&gt;

&lt;p&gt;A customer with Contract: Month-to-month is one signal. A customer with Contract: Month-to-month + InternetService: Fiber optic + PaymentMethod: Electronic check is a completely different risk profile — and that combination is what predicts churn.&lt;/p&gt;

&lt;p&gt;So I treated categorical columns like tokens and generated bigrams and trigrams across high-impact features: Contract, InternetService, and PaymentMethod. Each unique combination became a new feature, capturing interaction patterns that a standard feature matrix would miss entirely.&lt;/p&gt;
&lt;h2&gt;
  
  
  Feature Engineering Pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fig1c5wlsxcbzhs3pq451.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fig1c5wlsxcbzhs3pq451.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — N-gram Categorical Interactions&lt;/strong&gt;&lt;br&gt;
For each high-signal categorical column, I generated pairwise (bigram) and three-way (trigram) combinations across the feature space. This produced a set of composite interaction features that encoded relationship patterns directly into the model input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Nested Target Encoding&lt;/strong&gt;&lt;br&gt;
Raw target encoding leaks — if you encode a categorical feature using the target mean, the model sees information from the row it's predicting. The fix is nested k-fold encoding: encode each fold using only the other folds' target statistics. I used a nested 5-fold stratified scheme applied to both the original categorical features and the new n-gram features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Service Stack Analysis&lt;/strong&gt;&lt;br&gt;
Beyond the n-grams, I engineered explicit service combination counts — how many internet services, how many phone add-ons — and their intersections. Customers with more bundled services behave differently at churn time, and these counts captured that pattern numerically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Digit Features&lt;/strong&gt;&lt;br&gt;
For continuous columns like tenure and MonthlyCharges, I extracted distributional digit features — essentially encoding the numerical range and pattern of each value. This gave the model a richer representation of where each customer sat within the tenure and charge distributions.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Two-Stage Ensemble
&lt;/h2&gt;

&lt;p&gt;With the feature matrix built, I used a two-stage stacking approach rather than a single model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjec38twuilgih89es2cx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjec38twuilgih89es2cx.png" alt=" " width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 1 — Ridge Regression&lt;/strong&gt;&lt;br&gt;
A heavily regularised Ridge classifier served as the first-stage learner. Ridge is simple and interpretable — it captures broad linear trends across the feature space and generalises cleanly. Critically, I ran this with 10-fold stratified cross-validation and collected the out-of-fold (OOF) predictions. These OOF predictions became meta-features for Stage 2.&lt;br&gt;
The reason for Ridge first: it acts as a stabilising baseline. Its predictions encode a smooth, low-variance signal that helps XGBoost in Stage 2 avoid overfitting to noisy feature interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 2 — XGBoost on Original + OOF Features&lt;/strong&gt;&lt;br&gt;
The second-stage XGBoost classifier was trained on the full engineered feature matrix plus the Ridge OOF predictions as an additional input. This gave XGBoost a pre-computed linear summary of the data to work with alongside the raw features — effectively letting it model residuals and non-linear interactions on top of the Ridge baseline.&lt;br&gt;
Cross-validation remained 10-fold stratified with a fixed seed throughout, ensuring consistent and reproducible evaluation across both stages.&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;MetricValuePublic&lt;/em&gt; Leaderboard &lt;strong&gt;AUC 0.91685&lt;/strong&gt; Global Rank286 / 3,718 Percentile Top 8%&lt;br&gt;
The top 20 features by importance were dominated by the n-gram interaction features and nested-encoded categorical combinations — validating the core hypothesis that combination patterns outperform individual categorical signals on this dataset.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;The LightGBM baseline I started with was actually competitive on its own — the jump came almost entirely from the n-gram feature engineering, not from model complexity. In hindsight, I would have invested more time earlier in feature interaction design and less time tuning hyperparameters on simpler models.&lt;/p&gt;

&lt;p&gt;A second improvement would be experimenting with higher-order n-grams (four-way combinations) on the service stack features — the signal was clearly present in three-way combinations, and there may have been further lift available.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code &amp;amp; Reproducibility
&lt;/h2&gt;

&lt;p&gt;The full pipeline is open source. The main winning script is src/train_ridge_xgb_ngram.py — run with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;br&gt;
 &lt;code&gt;bashpython src/train_ridge_xgb_ngram.py --folds 10 --inner-folds 5 --seed 42 --output-prefix ridge_xgb_ngram10&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;Full repository: &lt;a href="//github.com/faissssss/predict-customer-churn"&gt;github.com/faissssss/predict-customer-churn&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Three things that actually moved the needle on this competition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;N-gram thinking for categorical data. If your features are categorical and interactions matter, treat them like text tokens. The combination is the signal.&lt;/li&gt;
&lt;li&gt;Nested target encoding, not naive encoding. Leaky encoding hurts generalisation silently — you won't see it in training metrics until the leaderboard disagrees with your CV score.&lt;/li&gt;
&lt;li&gt;Stack for stability, not complexity. Ridge + XGBoost worked not because XGBoost needed help, but because Ridge's OOF predictions gave it a cleaner starting point. Stacking should reduce variance, not add layers for its own sake.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sources:&lt;br&gt;
&lt;a href="//github.com/faissssss/predict-customer-churn"&gt;github.com/faissssss/predict-customer-churn&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.kaggle.com/competitions/playground-series-s6e3" rel="noopener noreferrer"&gt;kaggle.com/competitions/playground-series-s6e3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>machinelearning</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Hello, this is my first time joining this platform. I wanna share my experience building my first SaaS called Skoowl AI. I hope u find it helpful. Cheers!</title>
      <dc:creator>Fais Azis Wibowo</dc:creator>
      <pubDate>Mon, 23 Mar 2026 17:00:42 +0000</pubDate>
      <link>https://dev.to/faith_b6e08f3b8f05a77bb5f/hello-this-is-my-first-time-joining-this-platform-i-wanna-share-my-experience-building-my-first-3ao8</link>
      <guid>https://dev.to/faith_b6e08f3b8f05a77bb5f/hello-this-is-my-first-time-joining-this-platform-i-wanna-share-my-experience-building-my-first-3ao8</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/faith_b6e08f3b8f05a77bb5f/heres-how-i-built-my-first-saas-and-the-stack-behind-it-49h5" class="crayons-story__hidden-navigation-link"&gt;Here's How I Built My First SaaS and the Stack Behind It&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/faith_b6e08f3b8f05a77bb5f" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3838124%2F70f71d92-4c28-4811-ae93-6a8a54cb928d.jpg" alt="faith_b6e08f3b8f05a77bb5f profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/faith_b6e08f3b8f05a77bb5f" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Fais Azis Wibowo
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Fais Azis Wibowo
                
              
              &lt;div id="story-author-preview-content-3390375" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/faith_b6e08f3b8f05a77bb5f" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3838124%2F70f71d92-4c28-4811-ae93-6a8a54cb928d.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Fais Azis Wibowo&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/faith_b6e08f3b8f05a77bb5f/heres-how-i-built-my-first-saas-and-the-stack-behind-it-49h5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 23&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/faith_b6e08f3b8f05a77bb5f/heres-how-i-built-my-first-saas-and-the-stack-behind-it-49h5" id="article-link-3390375"&gt;
          Here's How I Built My First SaaS and the Stack Behind It
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nextjs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nextjs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/faith_b6e08f3b8f05a77bb5f/heres-how-i-built-my-first-saas-and-the-stack-behind-it-49h5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Designing and Deploying an AI-Powered EdTech SaaS with LLM Integration</title>
      <dc:creator>Fais Azis Wibowo</dc:creator>
      <pubDate>Mon, 23 Mar 2026 16:58:02 +0000</pubDate>
      <link>https://dev.to/faith_b6e08f3b8f05a77bb5f/heres-how-i-built-my-first-saas-and-the-stack-behind-it-49h5</link>
      <guid>https://dev.to/faith_b6e08f3b8f05a77bb5f/heres-how-i-built-my-first-saas-and-the-stack-behind-it-49h5</guid>
      <description>&lt;h2&gt;
  
  
  It Started With My Brother
&lt;/h2&gt;

&lt;p&gt;My brother has a habit. Whenever he's studying from a YouTube video, he doesn't just watch it — he wants to be tested on it. Every time, he'd come to me: &lt;em&gt;"Can you make me a quiz from this video?"&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Every. Single. Time.&lt;/p&gt;

&lt;p&gt;The first few times, I did it manually. Watched the video, wrote some questions, and formatted them. It took longer to make the quiz than it took him to watch the video. There had to be a better way.&lt;/p&gt;

&lt;p&gt;That frustration became Skoowl AI.&lt;/p&gt;

&lt;p&gt;I'm Fais — a 20-year-old CS enthusiast from Indonesia. I had never shipped a full SaaS product before. I had the technical background (Next.js, TypeScript, some AI/ML work), but building something real that is deployed and actually used was new territory.&lt;/p&gt;

&lt;p&gt;This is the full story of how I built &lt;a href="https://www.skoowlai.com/" rel="noopener noreferrer"&gt;Skoowl AI&lt;/a&gt;: what it does, every major tech decision, the AI pipeline that powers it, and what's coming next.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Skoowl AI?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gnoyjsd67lpa8l849n4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gnoyjsd67lpa8l849n4.png" alt="Turn Your Materials into&amp;lt;br&amp;gt;
Study Decks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.skoowlai.com/" rel="noopener noreferrer"&gt;Skoowl AI&lt;/a&gt; is a study platform that takes raw educational content — PDFs, audio recordings, YouTube videos — and transforms it into structured, study-ready materials using large language models.&lt;/p&gt;

&lt;p&gt;Here's what it can do:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;📝 Smart Notes&lt;/td&gt;
&lt;td&gt;Auto-generate formatted study notes from any uploaded file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡ Flashcards&lt;/td&gt;
&lt;td&gt;Create spaced-repetition flashcards for key terms and concepts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❓ Adaptive Quizzes&lt;/td&gt;
&lt;td&gt;Generate MCQ, true/false, or fill-in-the-blank quizzes with AI hints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧠 Mind Maps&lt;/td&gt;
&lt;td&gt;Visualize topics with interactive Radial, Tree, Fishbone, and much more layouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎙️ Live Transcription&lt;/td&gt;
&lt;td&gt;Record lectures in real-time or upload audio for instant transcription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📺 YouTube Learning&lt;/td&gt;
&lt;td&gt;Paste a video URL, extract and process the knowledge directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗣️ Chat Assistant&lt;/td&gt;
&lt;td&gt;Ask questions and get answers scoped to your own study notes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The idea is simple: you bring the content, Skoowl handles the processing. You spend your time learning, not formatting.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Stack — And Why I Chose It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkiyjp64sv67v6vmovj0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkiyjp64sv67v6vmovj0l.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every decision in this stack was deliberate. Here's the breakdown.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚡ Frontend
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Next.js 15 (App Router) + React 19 + TypeScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next.js with the App Router gives me server components, streaming, and a clean file-based routing model in one package. React 19's concurrency improvements matter specifically for this app — the UI needs to stay responsive while AI is generating content in the background. TypeScript across the entire codebase keeps everything honest; when you're wiring together AI responses, database models, and API handlers, loose types cause real bugs.&lt;/p&gt;

&lt;p&gt;For styling, Tailwind CSS v4 handles the utility-first layout and design system. Framer Motion covers complex, fluid UI animations — page transitions, entrance/exit effects, and loading states. Radix UI provides the headless, accessible primitives (dialogs, dropdowns, tabs, accordions) so I didn't have to reinvent accessibility from scratch, and Lucide React keeps the icon system consistent throughout.&lt;/p&gt;
&lt;h2&gt;
  
  
  🎨 Styling &amp;amp; UI Enhancements
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tiptap + React Markdown + KaTeX + React Flow + Three.js + GSAP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI-generated notes need to be editable, so I built a full rich text editing experience using Tiptap — a headless editor that supports highlights, text alignment, color, and more. Users can edit, format, and reorganize their generated notes directly in the app.&lt;/p&gt;

&lt;p&gt;For rendering AI output that includes markdown, tables, and mathematical formulas, react-markdown with remark/rehype plugins and KaTeX handles LaTeX math rendering cleanly. This matters for STEM content — physics equations and calculus notation render correctly, not as broken text.&lt;/p&gt;

&lt;p&gt;React Flow powers the interactive mind maps — a node-based graph UI where users can drag, expand, and explore topic hierarchies.&lt;/p&gt;

&lt;p&gt;For the heavier visual layer: Three.js and React Three Fiber handle interactive 3D elements in the UI, ShaderGradient creates the smooth WebGL-powered gradient backgrounds, and GSAP handles complex timeline-based animation sequences that go beyond what Framer Motion covers.&lt;/p&gt;
&lt;h2&gt;
  
  
  🗄️ Backend &amp;amp; Database
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL (Neon) + Prisma + Upstash Redis + Clerk + Dodo Payments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL via Neon Serverless is the production database — relational data fits this product well since users, documents, and generated content all have clear structure. Locally, I use SQLite for fast, zero-config development. Prisma v5 sits on top as the ORM, giving type-safe queries and clean migrations that talk directly to TypeScript.&lt;/p&gt;

&lt;p&gt;Upstash Redis handles two things: caching expensive AI results so the same document doesn't get reprocessed unnecessarily, and rate limiting API routes to protect endpoints from abuse. Both are the kind of infrastructure you don't think about until you need them — I added Upstash early and it paid off.&lt;/p&gt;

&lt;p&gt;Clerk handles user authentication — sessions, OAuth, user management. It took roughly a day to integrate and has needed zero maintenance since. Rolling your own auth is a classic beginner mistake; I skipped it.&lt;/p&gt;

&lt;p&gt;Dodo Payments handles the billing and subscription infrastructure. Svix sits alongside it for webhook signature verification — ensuring incoming webhooks from Clerk and Dodo are legitimate before acting on them.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤖 AI Layer
&lt;/h2&gt;

&lt;p&gt;Full pipeline breakdown in the next section, but the core libraries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vercel AI SDK — the backbone of all AI integration, handles streaming responses seamlessly to the React frontend&lt;/li&gt;
&lt;li&gt;Google Gemini + OpenAI (via Vercel AI SDK) — fast, efficient reasoning for notes, quizzes, and all text generation&lt;/li&gt;
&lt;li&gt;Deepgram — high-quality real-time audio and speech transcription, covering both uploaded files and live recordings&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  📄 File Processing
&lt;/h2&gt;

&lt;p&gt;The content ingestion layer handles more formats than most people expect:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Documents: pdf-parse for PDFs, mammoth for Word files, officeparser for PowerPoint and other Office formats&lt;/li&gt;
&lt;li&gt;YouTube: youtube-transcript for caption extraction, @distube/ytdl-core and yt-dlp-exec for videos requiring deeper audio/video processing&lt;/li&gt;
&lt;li&gt;Audio: Deepgram SDK for both batch and real-time speech-to-text&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.skoowlai.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fskoowlai.com%2Fopengraph-image.png%3F597a1dcee2783d84" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.skoowlai.com/" rel="noopener noreferrer" class="c-link"&gt;
            Skoowl AI - AI-Powered Study Assistant
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Turn your lectures into structured notes, flashcards, quizzes, and mind maps instantly.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.skoowlai.com%2Fskoowl-logo.png%3Fv%3D2"&gt;
          skoowlai.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  The AI Pipeline — How It Actually Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Document Pipeline (PDF / Word / Text)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The file is parsed server-side to extract raw text, then sent to Gemini via the Vercel AI SDK with a structured prompt. The SDK handles streaming, so users see notes generating word-by-word rather than waiting for a full response.&lt;/p&gt;

&lt;p&gt;For structured outputs like flashcard arrays or quiz sets, the model returns JSON validated by Zod — if the response doesn't match the expected schema, it retries before surfacing an error to the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Audio Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deepgram handles both modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uploaded audio: Batch transcription — the file is sent and a full transcript comes back, then feeds into the same Gemini pipeline.&lt;/li&gt;
&lt;li&gt;Live recording: Deepgram's streaming API returns incremental transcripts as the user speaks, with low enough latency that the text appears almost in real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;3. YouTube Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one is the reason Skoowl exists — my brother's quiz requests, remember?&lt;/p&gt;

&lt;p&gt;The pipeline uses a YouTube transcript to extract auto-generated or creator-provided captions from a YouTube URL, cleans them up, and sends them through Gemini like any other text input. For videos without captions, yt-dlp-exec pulls the audio, which then goes through Deepgram for transcription first.&lt;/p&gt;

&lt;p&gt;Paste a link, get a quiz in seconds. My brother now generates his own quizzes. Problem solved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Mind Maps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model is prompted to return a structured JSON graph representing the topic hierarchy. React Flow renders it as an interactive visual — users can drag, expand, and explore nodes. Zod validates the JSON schema before it ever reaches the renderer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment &amp;amp; Getting to 100+ Users
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zcohdidxyzeio31zdhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zcohdidxyzeio31zdhk.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Skoowl AI is deployed on &lt;strong&gt;Vercel&lt;/strong&gt;, which was the obvious choice given the Next.js stack. Edge functions, automatic preview deployments per branch, and zero-config CI/CD — it removed an entire category of DevOps problems.&lt;/p&gt;

&lt;p&gt;Getting the first users was uncomfortable in a productive way. I posted in a few Reddit communities, framed it as "I built this, honest feedback welcome." The early adopters came from that. Real usage exposed things that no amount of local testing would have found — different file encodings, unexpected internet speeds affecting streaming, and mobile layouts that needed work.&lt;/p&gt;

&lt;p&gt;The 100+ international users milestone came within the first few weeks, which validated the core idea more than any internal testing could.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next for Skoowl AI
&lt;/h2&gt;

&lt;p&gt;The current feature set covers the core study workflow, but there's a lot more planned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Discover&lt;/strong&gt; — An AI-powered research assistant that automatically finds relevant materials for your topic — web searches, books, papers — so you don't have to start from scratch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📊 Slides&lt;/strong&gt; — Auto-generate presentation slides directly from your uploaded content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🖼️ Infographics&lt;/strong&gt; — Turn dense information into shareable visual summaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🎙️ AI Podcast&lt;/strong&gt; — Convert your study materials into a conversational audio format you can listen to on the go&lt;/li&gt;
&lt;li&gt;And many more new features in the future...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Discover feature is the one I'm most excited about. Right now, users bring their own content to Skoowl AI. Discover flips that — Skoowl AI helps you find the content in the first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Building Skoowl AI taught me more in a few months than I could have learned any other way. Shipping something real, getting it in front of real users, and watching it actually solve a problem — even a small one like my brother's quiz requests — is a different kind of education.&lt;/p&gt;

&lt;p&gt;If you've built something similar, have thoughts on the stack, or want to explore a collaboration, drop a comment. I read all of them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://skoowlai.com" rel="noopener noreferrer"&gt;skoowlai.com&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/faissssss/skoowlai" rel="noopener noreferrer"&gt;github.com/faissssss/skoowlai&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Instagram:&lt;/strong&gt; &lt;a href="https://www.instagram.com/skoowlai/" rel="noopener noreferrer"&gt;instagram.com/skoowlai/&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;X:&lt;/strong&gt; &lt;a href="https://x.com/skoowlai" rel="noopener noreferrer"&gt;x.com/skoowlai&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/company/skoowl-ai/" rel="noopener noreferrer"&gt;linkedin.com/company/skoowl-ai/&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Tiktok:&lt;/strong&gt; &lt;a href="https://www.tiktok.com/@skoowlai" rel="noopener noreferrer"&gt;tiktok.com/@skoowlai&lt;/a&gt;&lt;br&gt;
🔗 &lt;strong&gt;Discord:&lt;/strong&gt; &lt;a href="https://discord.com/invite/QWJXV9k8" rel="noopener noreferrer"&gt;discord/skoowlai&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;This was my first SaaS. It won't be my last.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
