<?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: Jatin Sisodia</title>
    <description>The latest articles on DEV Community by Jatin Sisodia (@sisodiajatin).</description>
    <link>https://dev.to/sisodiajatin</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%2F1067249%2F9059bc33-0e11-483c-9b16-aed0918b2fce.jpg</url>
      <title>DEV Community: Jatin Sisodia</title>
      <link>https://dev.to/sisodiajatin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sisodiajatin"/>
    <language>en</language>
    <item>
      <title>Battle of the CNNs: ResNet vs. MobileNet vs. EfficientNet for Fruit Disease Detection</title>
      <dc:creator>Jatin Sisodia</dc:creator>
      <pubDate>Wed, 14 Jan 2026 07:48:39 +0000</pubDate>
      <link>https://dev.to/sisodiajatin/battle-of-the-cnns-resnet-vs-mobilenet-vs-efficientnet-for-fruit-disease-detection-3ffi</link>
      <guid>https://dev.to/sisodiajatin/battle-of-the-cnns-resnet-vs-mobilenet-vs-efficientnet-for-fruit-disease-detection-3ffi</guid>
      <description>&lt;p&gt;So here's the thing: I've always been fascinated by how deep learning can solve real-world problems, and fruit disease detection seemed like the perfect challenge. Not too simple, not impossibly complex, and actually useful for farmers dealing with crop losses.&lt;/p&gt;

&lt;p&gt;I ended up building FruitScan-AI and testing three different neural network architectures to see which one actually works best. Spoiler: they each have their strengths, and the "best" one totally depends on what you're building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Even Bother With Fruit Disease Detection?&lt;/strong&gt;&lt;br&gt;
Look, I know what you're thinking: "why fruits?" But hear me out. Farmers lose something like 20 to 40% of their crops every year to diseases and pests. That's HUGE. And the traditional way of checking? Walking through fields, manually inspecting every plant, hoping you catch problems early. It's slow, inconsistent, and requires expertise that not everyone has access to.&lt;/p&gt;

&lt;p&gt;So I thought: what if we could just snap a photo and get an instant diagnosis? That's where FruitScan-AI comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Built&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/sisodiajatin/FruitScan-AI" rel="noopener noreferrer"&gt;FruitScan-AI&lt;/a&gt; is basically a deep learning system that looks at fruit images and tells you two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What kind of fruit it is&lt;/li&gt;
&lt;li&gt;Whether it's healthy or diseased&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here's where it gets interesting... I didn't just build one model. I built three different versions using EfficientNet, MobileNetV2, and ResNet50 to compare them side by side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Dataset&lt;/strong&gt;&lt;br&gt;
I worked with images of over 15 different fruits: apples, bananas, grapes, mangoes, tomatoes, peppers... you name it. Each category has both healthy specimens and diseased ones (bacterial spots, fungal infections, rot, all the nasty stuff).&lt;/p&gt;

&lt;p&gt;The images are high resolution enough to catch the subtle details that matter for accurate classification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Three Architectures (And Why I Picked Them)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;EfficientNet: The Balanced One&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
EfficientNet is like that friend who's good at everything without trying too hard. It scales network depth, width, and resolution together using this "compound scaling" approach. Translation? You get great accuracy without your model becoming a computational monster.&lt;/p&gt;

&lt;p&gt;I went with EfficientNet because it's efficient and actually performs really well on image classification tasks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;MobileNetV2: The Lightweight Champion&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
This one's designed for mobile devices. It uses these clever "depthwise separable convolutions" that basically do more with less. Perfect if you want to deploy your model on a phone or a Raspberry Pi in the middle of a farm.&lt;/p&gt;

&lt;p&gt;If I were building an app for farmers in the field, MobileNetV2 would be my go to.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;ResNet50: The Heavyweight&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
ResNet50 is the veteran here. It introduced "skip connections" that let you train really deep networks without everything exploding (vanishing gradients are fun like that). It's deeper, it's powerful, and it can learn some seriously complex patterns.&lt;/p&gt;

&lt;p&gt;I included ResNet50 because it's battle tested and gives you a solid baseline for comparison.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Actually Works&lt;/strong&gt;&lt;br&gt;
All three models use transfer learning... basically, I'm not starting from scratch. These models were pretrained on ImageNet (millions of images), so they already know what edges, textures, and shapes look like.&lt;/p&gt;

&lt;p&gt;Here's the general approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# I freeze the pretrained layers at first
base_model = EfficientNetB0(weights='imagenet', include_top=False)
base_model.trainable = False

# Then add my own classification layers on top
model = Sequential([
    base_model,
    GlobalAveragePooling2D(),
    Dense(256, activation='relu'),
    Dropout(0.5),
    Dense(num_classes, activation='softmax')
])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why transfer learning? Three reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Training is way faster &lt;/li&gt;
&lt;li&gt;You need less data &lt;/li&gt;
&lt;li&gt;Better results &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Training Pipeline&lt;br&gt;
Nothing fancy here, just good practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resize images to what each model expects (224x224 for most)&lt;/li&gt;
&lt;li&gt;Normalize using ImageNet statistics&lt;/li&gt;
&lt;li&gt;Add data augmentation (flips, rotations, brightness tweaks) so the model doesn't just memorize training data&lt;/li&gt;
&lt;li&gt;Train in batches to keep my GPU from crying&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tracked the usual suspects: accuracy, precision, recall, F1 score, and confusion matrices to see where each model struggled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Started (If You Want to Try This)&lt;br&gt;
What You Need&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install tensorflow keras numpy pandas matplotlib scikit-learn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Grab the code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/sisodiajatin/FruitScan-AI.git
cd FruitScan-AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repo's organized by architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FruitScan-AI/
├── EfficiencyNet/    # EfficientNet notebooks
├── MobileNetV2/      # MobileNetV2 experiments  
├── ResNet50/         # ResNet50 implementation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Running it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd EfficiencyNet  # or whichever model you want
jupyter notebook
# Open the training notebook and go through it cell by cell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Making Predictions&lt;/strong&gt;&lt;br&gt;
Once you've got a trained model, using it is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Load your model
model = load_model('fruit_disease_model.h5')

# Prep your image
img = load_img('suspicious_apple.jpg', target_size=(224, 224))
img_array = img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
img_array = preprocess_input(img_array)

# Get prediction
predictions = model.predict(img_array)
class_idx = np.argmax(predictions[0])

print(f"This looks like: {class_labels[class_idx]}")
print(f"Confidence: {predictions[0][class_idx]*100:.2f}%")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a quick Flask wrapper if you want to turn this into an API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask, request, jsonify

app = Flask(__name__)
model = tf.keras.models.load_model('best_model.h5')

@app.route('/predict', methods=['POST'])
def predict():
    file = request.files['image']
    img = preprocess_image(file)
    prediction = model.predict(img)

    return jsonify({
        'fruit': get_fruit_name(prediction),
        'status': 'healthy' if is_healthy(prediction) else 'diseased',
        'confidence': float(np.max(prediction))
    })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;So... Which Model Won?&lt;/strong&gt;&lt;br&gt;
Honestly? It depends what you're optimizing for.&lt;br&gt;
From what I've seen:&lt;/p&gt;

&lt;p&gt;EfficientNet hits around 92 to 95% accuracy with decent speed (about 30 to 50ms per image). Good all rounder.&lt;br&gt;
MobileNetV2 gets 88 to 91% accuracy but is FAST (10 to 20ms) and tiny. Perfect for mobile apps.&lt;br&gt;
ResNet50 lands at 90 to 93% accuracy but is slower (50 to 80ms) and bigger. Great for research or when accuracy is everything.&lt;/p&gt;

&lt;p&gt;Try It Out!&lt;br&gt;
If this sounds interesting:&lt;br&gt;
⭐ Star the repo - &lt;a href="https://github.com/sisodiajatin/FruitScan-AI" rel="noopener noreferrer"&gt;https://github.com/sisodiajatin/FruitScan-AI&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>😲 Your Face is the Playlist: Building an Emotion-Aware Android App</title>
      <dc:creator>Jatin Sisodia</dc:creator>
      <pubDate>Sun, 11 Jan 2026 08:53:10 +0000</pubDate>
      <link>https://dev.to/sisodiajatin/your-face-is-the-playlist-building-an-emotion-aware-android-app-4e6o</link>
      <guid>https://dev.to/sisodiajatin/your-face-is-the-playlist-building-an-emotion-aware-android-app-4e6o</guid>
      <description>&lt;p&gt;&lt;strong&gt;The Problem: Static Playlists in a Dynamic World&lt;/strong&gt;&lt;br&gt;
Music is deeply personal, but our music players are surprisingly impersonal. We’ve all been there: You’re having a rough day, but your "Daily Mix" decides it’s the perfect time for high-energy dance pop. Or you're in the zone working, and a jarring ballad breaks your flow.&lt;/p&gt;

&lt;p&gt;We curate playlists for specific moods, but scrolling through them takes effort. What if your phone could just look at you, understand how you’re feeling, and play the perfect track automatically?&lt;/p&gt;

&lt;p&gt;In this post, we’re diving into the &lt;strong&gt;EmotionToMusic-App&lt;/strong&gt;, an open-source Android project that bridges the gap between computer vision and music recommendation. We’ll explore how to build a pipeline that goes from &lt;strong&gt;Face&lt;/strong&gt; &lt;strong&gt;-&amp;gt;&lt;/strong&gt; &lt;strong&gt;Emotion&lt;/strong&gt; &lt;strong&gt;-&amp;gt;&lt;/strong&gt; &lt;strong&gt;Music&lt;/strong&gt; in real-time, all on-device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Overview&lt;/strong&gt;&lt;br&gt;
This application is built natively in Kotlin and follows modern Android development practices. The core philosophy is on-device inference. By running the Machine Learning (ML) models locally, we ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; No images of the user are ever sent to a cloud server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency:&lt;/strong&gt; Music reaction is near-instantaneous.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline Capability:&lt;/strong&gt; It works without an internet connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Tech Stack&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Kotlin&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Camera:&lt;/strong&gt; CameraX (Jetpack library for easy camera lifecycle management)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ML Engine:&lt;/strong&gt; TensorFlow Lite (for emotion classification)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Face Detection:&lt;/strong&gt; Google ML Kit (to locate the face before analysis)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
The application functions as a continuous loop. It doesn't just take a single photo; it analyzes the video stream frame-by-frame. Here is the high-level flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capture:&lt;/strong&gt; CameraX intercepts a frame from the live preview.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detection:&lt;/strong&gt; ML Kit scans the full frame to find a face.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preprocessing:&lt;/strong&gt; The face is cropped, converted to grayscale, and resized (usually to 48x48 pixels) to match the model's input requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; The processed image is fed into a TFLite model (often a CNN trained on the FER2013 dataset).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; The model returns a probability array (e.g., [Happy: 0.8, Sad: 0.1, Neutral: 0.1]).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; The app maps the highest probability emotion to a specific genre and triggers the Media Player.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Implementation&lt;/strong&gt;&lt;br&gt;
Let's look at the code that powers this "AI DJ."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;The Analyzer ( The "Eye")&lt;/strong&gt;&lt;br&gt;
The heart of the app is the &lt;strong&gt;ImageAnalysis.Analyzer&lt;/strong&gt;. This runs on a background thread and processes frames. Note how we use ML Kit first to find the face, ensuring we don't feed background noise to our emotion model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class EmotionAnalyzer(private val listener: EmotionListener) : ImageAnalysis.Analyzer {

    @androidx.annotation.OptIn(androidx.camera.core.ExperimentalGetImage::class)
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

            // Step 1: Detect Face
            FaceDetection.getClient().process(inputImage)
                .addOnSuccessListener { faces -&amp;gt;
                    if (faces.isNotEmpty()) {
                        // Step 2: Crop Face &amp;amp; Run Inference
                        val emotion = recognizeEmotion(faces[0], mediaImage)
                        listener.onEmotionDetected(emotion)
                    }
                }
                .addOnCompleteListener {
                    imageProxy.close() // Important: Release frame!
                }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; &lt;strong&gt;The Model (The "Brain")&lt;/strong&gt;&lt;br&gt;
Once we have the face, we interpret it. The &lt;strong&gt;TensorFlowLite&lt;/strong&gt; interpreter takes the raw byte buffer of the image and outputs the probabilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun recognizeEmotion(face: Face, image: Image): String {
    // 1. Convert YUV image to Bitmap and crop to face bounding box
    val faceBitmap = BitmapUtils.cropToFace(image, face.boundingBox)

    // 2. Resize to model input size (e.g., 48x48) &amp;amp; Grayscale
    val processedBitmap = BitmapUtils.preprocess(faceBitmap)

    // 3. Run Inference
    val output = Array(1) { FloatArray(7) } // 7 emotions (Happy, Sad, Angry, etc.)
    tfliteInterpreter.run(processedBitmap, output)

    // 4. Get the index of the highest confidence
    val maxIndex = output[0].indices.maxByOrNull { output[0][it] } ?: 0

    return emotionLabels[maxIndex] // e.g., "Happy"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; &lt;strong&gt;The Mapper (The "DJ")&lt;/strong&gt;&lt;br&gt;
Finally, a simple controller maps the result to a playlist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun playMusicForEmotion(emotion: String) {
    val playlist = when(emotion) {
        "Happy" -&amp;gt; R.raw.upbeat_pop
        "Sad"   -&amp;gt; R.raw.melancholy_piano
        "Angry" -&amp;gt; R.raw.heavy_rock
        else    -&amp;gt; R.raw.chill_lofi
    }
    mediaPlayer.play(playlist)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Design Decisions &amp;amp; Challenges&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Lighting is the Enemy&lt;/strong&gt;&lt;br&gt;
Computer vision models struggle in low light. During testing, shadows across the face were often misclassified as "Angry" or "Sad."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: We implemented a check for average luminosity. If the frame is too dark, the app pauses detection and prompts the user to move to the light.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Jitter Problem&lt;/strong&gt;&lt;br&gt;
Real-time inference is fast. Your face might register as "Happy" for 10 frames, "Neutral" for 1 frame, and "Happy" again. If we switched the song every time the emotion flickered, the experience would be terrible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Smoothing. We use a buffer that stores the last 10 detected emotions and only changes the music if the dominant emotion changes for a sustained period (e.g., 2 seconds).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The EmotionToMusic-App is a great example of how accessible AI has become. You don't need to be a data scientist to build "smart" apps; you just need to know how to wire the components together.&lt;/p&gt;

&lt;p&gt;GitHub Repository—&lt;a href="https://github.com/sisodiajatin/EmotionToMusic-App" rel="noopener noreferrer"&gt;https://github.com/sisodiajatin/EmotionToMusic-App&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>android</category>
      <category>machinelearning</category>
      <category>coding</category>
    </item>
    <item>
      <title>Inside a Scholarly Search Engine: Indexing, Ranking, and Retrieval</title>
      <dc:creator>Jatin Sisodia</dc:creator>
      <pubDate>Sun, 11 Jan 2026 07:35:54 +0000</pubDate>
      <link>https://dev.to/sisodiajatin/inside-a-scholarly-search-engine-indexing-ranking-and-retrieval-pea</link>
      <guid>https://dev.to/sisodiajatin/inside-a-scholarly-search-engine-indexing-ranking-and-retrieval-pea</guid>
      <description>&lt;p&gt;Repository: &lt;a href="https://github.com/sisodiajatin/CS547-IR-Scholarly-Search" rel="noopener noreferrer"&gt;https://github.com/sisodiajatin/CS547-IR-Scholarly-Search&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s be real for a second: academic search is broken.&lt;/p&gt;

&lt;p&gt;If you have ever tried to find a specific paper on a generic search engine, you know the pain. You type "neural networks," and you get a mix of Medium articles, YouTube tutorials, and maybe, if you are lucky, the actual PDF you were looking for on page 3.&lt;/p&gt;

&lt;p&gt;I ran into this exact wall recently. I realized that building a search engine is not just about matching strings; it is about understanding intent. So, instead of complaining about it, I decided to build one.&lt;/p&gt;

&lt;p&gt;This is the story of Scholarly Search, a project where I stopped relying on external search services and built a custom Information Retrieval (IR) system from the ground up using Python and Flask.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Are We Actually Building?&lt;/strong&gt;&lt;br&gt;
At its core, this project is a specialized search engine for academic papers. The goal was not just to "find text" but to rank it intelligently. If a user searches for "machine learning," a paper with that phrase in the title should rank higher than one that mentions it once in the footnotes.&lt;/p&gt;

&lt;p&gt;To make this happen, I had to move away from simple database queries and embrace the Inverted Index, the data structure that powers basically every search engine on the planet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core:&lt;/strong&gt; Python (handling all logic and data structures).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web Framework:&lt;/strong&gt; Flask (serving both the API and the UI).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; HTML,CSS &amp;amp; Vanilla JavaScript (keeping it lightweight and monolithic).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Secret Sauce:&lt;/strong&gt; A custom-built Inverted Index and BM25 Ranking algorithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Aha!" Moment: Why Simple Counts Do Not Work&lt;/strong&gt;&lt;br&gt;
When I first started, I thought, "Easy. I will just count how many times the word appears."&lt;/p&gt;

&lt;p&gt;I was wrong.&lt;/p&gt;

&lt;p&gt;If you search for "the analysis," the word "the" appears in almost every document. If you rank by pure frequency, your results will be dominated by papers that just happen to be wordy, not relevant.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;BM25&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;BM25 is the industry standard for a reason. It does two smart things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It penalizes common words&lt;/strong&gt;. (Inverse Document Frequency)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It penalizes long documents&lt;/strong&gt;. (Length Normalization)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the actual Python code used to calculate the score. It looks a bit math heavy, but it is really just balancing term frequency against document length:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def score_bm25(n, f, qf, r, N, dl, avdl):
    # K is a scaling factor based on doc length (dl) vs average (avdl)
    K = k1 * ((1 - b) + b * (dl / avdl))

    # This part calculates relevance
    first = math.log(((r + 0.5) / (R - r + 0.5)) / ((n - r + 0.5) / (N - n - R + r + 0.5)))
    second = ((k1 + 1) * f) / (K + f)
    third = ((k2 + 1) * qf) / (k2 + qf)

    return first * second * third
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Indexing: The Heavy Lifting&lt;/strong&gt;&lt;br&gt;
The biggest challenge was speed. You can't scan 50,000 documents every time someone hits "Enter."&lt;/p&gt;

&lt;p&gt;The solution is an Inverted Index. Think of it like the index at the back of a textbook. Instead of reading the book to find "Algorithms," you look up "Algorithms" and see a list of page numbers.&lt;/p&gt;

&lt;p&gt;I wrote a script that pre-processes the raw data (stripping out punctuation, lowercasing everything) and builds this map in memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Simplified view of the indexing process
inverted_index = defaultdict(list)

for doc_id, text in corpus.items():
    tokens = preprocess(text) # Clean the text
    for term in tokens:
        # Map the term back to the document ID
        inverted_index[term].append(doc_id)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trade-off Alert&lt;/strong&gt;: I chose to keep this index in memory (RAM).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pro&lt;/strong&gt;: It’s blazing fast. Sub-millisecond lookup times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con&lt;/strong&gt;: It eats RAM. For a dataset this size (&amp;lt;100k docs), it's fine. For anything larger, you'd want to dump this to disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Frontend: Simple &amp;amp; Effective&lt;/strong&gt;&lt;br&gt;
Because this project focuses on the backend IR logic, I kept the frontend architecture simple.&lt;/p&gt;

&lt;p&gt;Instead of over-engineering with a complex framework like React or Vue, I built the interface using &lt;strong&gt;standard HTML, CSS, and Vanilla JavaScript&lt;/strong&gt;. This keeps the application lightweight and ensures that the "search" functionality remains the star of the show.&lt;/p&gt;

&lt;p&gt;The UI logic is handled by a simple script that fetches results from the backend API asynchronously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// A simple fetch function to query the Flask API
function search(query) {
    fetch(`/search?q=${query}`)
        .then(response =&amp;gt; response.json())
        .then(data =&amp;gt; {
            const resultsDiv = document.getElementById('results');
            resultsDiv.innerHTML = ''; // Clear old results

            data.forEach(paper =&amp;gt; {
                // Dynamically create HTML for each result
                let item = `
                    &amp;lt;div class="paper"&amp;gt;
                        &amp;lt;h3&amp;gt;${paper.title}&amp;lt;/h3&amp;gt;
                        &amp;lt;p&amp;gt;${paper.abstract}&amp;lt;/p&amp;gt;
                    &amp;lt;/div&amp;gt;
                `;
                resultsDiv.innerHTML += item;
            });
        });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Try It Yourself&lt;/strong&gt;&lt;br&gt;
If you want to poke around the code or run it locally, I have open sourced the whole thing.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>python</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
