<?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>Forem: HydraBytes</title>
    <description>The latest articles on Forem by HydraBytes (@thehydrabytes).</description>
    <link>https://forem.com/thehydrabytes</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%2F3873503%2F4d017dc0-77b3-4e5b-b8b7-6a3c71cbab08.jpg</url>
      <title>Forem: HydraBytes</title>
      <link>https://forem.com/thehydrabytes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thehydrabytes"/>
    <language>en</language>
    <item>
      <title>Building a 3-Class Lung Cancer Image Classifier with TensorFlow and Flask</title>
      <dc:creator>HydraBytes</dc:creator>
      <pubDate>Tue, 14 Apr 2026 17:26:35 +0000</pubDate>
      <link>https://forem.com/thehydrabytes/building-a-3-class-lung-cancer-image-classifier-with-tensorflow-and-flask-4615</link>
      <guid>https://forem.com/thehydrabytes/building-a-3-class-lung-cancer-image-classifier-with-tensorflow-and-flask-4615</guid>
      <description>&lt;p&gt;Medical imaging is one of the most rewarding spaces to apply deep learning. Pathologists spend years learning to distinguish subtle visual patterns in tissue samples, and even then, fatigue and caseload pressure can creep into decisions. A well-trained CNN does not replace that expertise, but it can serve as a useful second opinion, especially in triage workflows.&lt;/p&gt;

&lt;p&gt;In this post we will walk through how we built a lung cancer image classifier that sorts tissue images into three classes: &lt;strong&gt;Adenocarcinoma&lt;/strong&gt;, &lt;strong&gt;Benign&lt;/strong&gt;, and &lt;strong&gt;Squamous Cell Carcinoma&lt;/strong&gt;. The model runs behind a Flask API with a simple upload-and-predict web interface, so anyone can drop in an image and see the prediction in real time.&lt;/p&gt;

&lt;p&gt;Full code and dataset links are on &lt;a href="https://github.com/faizan-02/Lung-Cancer-Classification" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these three classes
&lt;/h2&gt;

&lt;p&gt;Lung cancer is commonly divided into small cell and non-small cell types. Within non-small cell lung cancer, adenocarcinoma and squamous cell carcinoma are the two most prevalent subtypes, together making up the majority of cases. Correctly separating them matters because treatment pathways can differ meaningfully.&lt;/p&gt;

&lt;p&gt;Adding a &lt;strong&gt;benign&lt;/strong&gt; class gives the model a "nothing to worry about" option so it does not force every input into a cancer label. That three-class setup reflects the kind of decision a real classifier would need to make in a triage tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dataset
&lt;/h2&gt;

&lt;p&gt;We used a public Kaggle lung cancer histopathology dataset, organized into three balanced classes with separate training and testing folders. The directory structure looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dataset/
├── train/
│   ├── adenocarcinoma/
│   ├── benign/
│   └── squamous_cell_carcinoma/
└── test/
    ├── adenocarcinoma/
    ├── benign/
    └── squamous_cell_carcinoma/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keras' &lt;code&gt;ImageDataGenerator&lt;/code&gt; made it trivial to load images directly from these folders and apply augmentation on the fly. Data augmentation matters a lot for medical imaging because real datasets are almost always smaller than what a fresh CNN would prefer. We used random flips, small rotations, and zoom to expand the effective training set without collecting new samples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model architecture
&lt;/h2&gt;

&lt;p&gt;We went with a custom CNN instead of a pretrained backbone like ResNet or VGG. The reasoning: histopathology images have different statistics from natural photographs (no sky, no faces, strong staining colors), so the features learned on ImageNet are not always the best starting point. A purpose-built network with fewer parameters also trains faster and is easier to reason about.&lt;/p&gt;

&lt;p&gt;The architecture is intentionally simple:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Conv2D + ReLU&lt;/td&gt;
&lt;td&gt;32 filters, 3x3 kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MaxPooling2D&lt;/td&gt;
&lt;td&gt;2x2 pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conv2D + ReLU&lt;/td&gt;
&lt;td&gt;64 filters, 3x3 kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MaxPooling2D&lt;/td&gt;
&lt;td&gt;2x2 pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flatten&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dense + ReLU&lt;/td&gt;
&lt;td&gt;128 units&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dropout&lt;/td&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dense + Softmax&lt;/td&gt;
&lt;td&gt;3 output units&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two convolutional blocks are enough to capture the low and mid-level texture patterns that distinguish tumor tissue from benign tissue. The dropout layer before the final dense block is doing heavy lifting: without it, the model happily memorized the training set and validation accuracy plateaued much earlier.&lt;/p&gt;

&lt;p&gt;Here is the model definition in Keras:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tensorflow.keras.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequential&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tensorflow.keras.layers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Conv2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MaxPooling2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Flatten&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dense&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dropout&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Sequential&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;Conv2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;relu&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nc"&gt;MaxPooling2D&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nc"&gt;Conv2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;relu&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;MaxPooling2D&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nc"&gt;Flatten&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nc"&gt;Dense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;relu&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Dense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;softmax&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;adam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categorical_crossentropy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Training ran for a modest number of epochs with early stopping watching validation loss. Adam as the optimizer, categorical cross-entropy as the loss, and a 70/15/15 train/validation/test split. Nothing exotic. The trained weights are saved to &lt;code&gt;models/lung_cancer_model.h5&lt;/code&gt; so the Flask app can load them at startup instead of retraining every time.&lt;/p&gt;

&lt;p&gt;A lesson we learned early: &lt;strong&gt;always shuffle within each class before splitting&lt;/strong&gt;. Our first split was sequential and it put nearly all adenocarcinoma images from one subfolder into the training set and the rest into validation, which tanked validation accuracy for that class. Shuffling fixed it in one line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flask integration
&lt;/h2&gt;

&lt;p&gt;The serving side is a tiny Flask app with a single route that handles both the GET (render upload page) and POST (accept image, run prediction) flows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tensorflow.keras.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_model&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;models/lung_cancer_model.h5&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;CLASSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Adenocarcinoma&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Benign&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Squamous Cell Carcinoma&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_and_predict&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RGB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_dims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;255.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;preds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;predicted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CLASSES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preds&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preds&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;predicted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Loading the model once at startup (instead of per request) is a small detail that matters a lot for response times. The first prediction warms up TensorFlow, and everything after that returns in well under a second on CPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  The front-end
&lt;/h2&gt;

&lt;p&gt;The user-facing interface is plain HTML, CSS, and a sprinkle of Bootstrap. No React, no framework overhead. A big drop zone for the image, a preview, and a result card that renders the predicted class with its confidence score. The goal was to keep the whole experience friction-free so someone without technical background can still use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After training, our numbers landed at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Training accuracy:&lt;/strong&gt; around 95%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation accuracy:&lt;/strong&gt; around 96%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test accuracy:&lt;/strong&gt; around 97%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The validation accuracy sitting slightly above training is a little unusual and usually a sign that dropout is doing its job, regularizing the model enough that it generalizes cleanly. We also checked per-class precision and recall to make sure the model was not gaming its accuracy by over-predicting the majority class. All three classes came back balanced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations and honest caveats
&lt;/h2&gt;

&lt;p&gt;A few things we want to be upfront about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;This is not a clinical tool.&lt;/strong&gt; Public histopathology datasets are carefully curated and do not capture the full range of tissue variation you would see in a real lab. High test accuracy on a clean dataset does not translate to clinical-grade reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stain variation is the biggest gap.&lt;/strong&gt; The model has not been tested against images with different staining protocols, scanners, or magnifications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three classes is a simplification.&lt;/strong&gt; Real pathology has many more subtypes and gradings. A production version would need a much deeper label space.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These caveats are part of the reason we picked a custom CNN instead of pretending a ResNet fine-tune on Kaggle data is "ready for deployment". The architecture, training pipeline, and Flask wrapper are all meant to be a solid starting point that a team could extend into a real diagnostic aid with the right dataset partnerships and regulatory path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Building this project was a great exercise in the full loop: curating data, designing a CNN small enough to train on a single GPU, wiring it up behind a web interface, and making predictions available in a form anyone could use. The accuracy numbers are strong for a public dataset, but the bigger win was shipping something end to end, from raw images to a working upload-and-predict app.&lt;/p&gt;

&lt;p&gt;If you want to try it yourself, clone the repo, drop in the dataset, train the model, and start the Flask server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python train_model.py
python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code, architecture diagrams, and screenshots are all on &lt;a href="https://github.com/faizan-02/Lung-Cancer-Classification" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Feedback and pull requests are welcome.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;HydraBytes&lt;/strong&gt;, we love projects like this one: real-world AI problems where the challenge is not just model accuracy but shaping the pipeline so the end result is useful. If you are exploring medical imaging, computer vision, or any ML use case, &lt;a href="https://www.hydrabytes.tech/contact" rel="noopener noreferrer"&gt;let's talk at Hydrabytes.tech&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>python</category>
      <category>tensorflow</category>
      <category>flask</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building an AI-Based Student Stress Management System with Python, ML, and RAG</title>
      <dc:creator>HydraBytes</dc:creator>
      <pubDate>Mon, 13 Apr 2026 23:04:32 +0000</pubDate>
      <link>https://forem.com/thehydrabytes/building-an-ai-based-student-stress-management-system-with-python-ml-and-rag-4nj5</link>
      <guid>https://forem.com/thehydrabytes/building-an-ai-based-student-stress-management-system-with-python-ml-and-rag-4nj5</guid>
      <description>&lt;p&gt;Student mental health has become a genuine crisis in universities worldwide. Stress, anxiety, and depression are among the leading causes of academic dropout yet most campuses lack accessible, real-time tools to help students recognize and address what they're experiencing.&lt;/p&gt;

&lt;p&gt;We built the &lt;strong&gt;AI-Based Student Stress Management System&lt;/strong&gt; to tackle exactly that. It's a full-stack web platform that uses machine learning to detect stress, anxiety, and depression from questionnaire inputs, then provides instant severity scoring, personalized coping recommendations, and a conversational AI chatbot all in one place.&lt;/p&gt;

&lt;p&gt;This post walks through the architecture, the ML pipeline, and the design decisions we made building it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Repo: &lt;a href="https://github.com/TheHydraBytes/AI-based-student-stress-mangement" rel="noopener noreferrer"&gt;TheHydraBytes/AI-based-student-stress-mangement&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;The standard approach to student mental health support is: fill out a form, wait for a counselor to follow up, get an appointment weeks later. By that point, a student in mild-to-moderate distress may have already spiraled.&lt;/p&gt;

&lt;p&gt;We wanted something that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gave students an immediate, private assessment of where they stood&lt;/li&gt;
&lt;li&gt;Offered actionable coping tools they could use right now&lt;/li&gt;
&lt;li&gt;Connected them to a professional when needed&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React Frontend
     |
  Flask Backend (Python)
     |          |           |
   SQL Server   ML Models   AI Chatbot
  (pyodbc)    (scikit-learn) (LangChain + Groq + Qdrant)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend is a Flask application. The ML models (three separate classifiers) run as in-process Python objects. The chatbot uses a RAG (Retrieval-Augmented Generation) pipeline backed by Qdrant as the vector store and Groq (LLaMA 3.1) as the LLM.&lt;/p&gt;




&lt;h2&gt;
  
  
  The ML Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Three Separate Models
&lt;/h3&gt;

&lt;p&gt;We trained three independent classifiers, one each for stress, anxiety, and depression. Each model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes a questionnaire response as input (Likert-scale answers covering behavioral, cognitive, and physiological symptoms)&lt;/li&gt;
&lt;li&gt;Outputs a severity label: &lt;strong&gt;Minimal / Mild / Moderate / Severe&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Was trained on a labeled clinical survey dataset (PSS, GAD-7, PHQ-9 style)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three follow the same training pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.linear_model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LogisticRegression&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LabelEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StandardScaler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;joblib&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stress.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;label_encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LabelEncoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stress Label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label_encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stress Label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stress Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stress Label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stress Label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;scaler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StandardScaler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;X_scaled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_scaled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LogisticRegression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_iter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;joblib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logistic_regression_stress.pkl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;joblib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scaler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scaler.pkl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;joblib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label_encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label_encoder.pkl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We chose Logistic Regression deliberately. For clinical classification tasks where interpretability and reliability matter more than marginal accuracy gains, it outperforms black-box alternatives like neural networks. We can understand exactly why a sample was classified the way it was.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Prediction
&lt;/h3&gt;

&lt;p&gt;When a student submits the questionnaire, all three models run simultaneously and the results are bundled into a single response object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;results_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stress_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stress_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stress_prediction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stress_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;anxiety_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anxiety_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;anxiety_prediction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anxiety_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;depression_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;depression_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;depression_prediction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;depression_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;demographic_info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;demographic_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results page shows all three severity scores at once, letting students see the full picture rather than a single-dimension reading.&lt;/p&gt;




&lt;h2&gt;
  
  
  The RAG Chatbot
&lt;/h2&gt;

&lt;p&gt;The conversational layer is where the system goes beyond a static form. Students can ask the chatbot anything related to their mental health — and it responds using actual clinical knowledge, not just generic LLM hallucinations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embeddings&lt;/strong&gt;: &lt;code&gt;sentence-transformers/all-mpnet-base-v2&lt;/code&gt; via HuggingFace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector store&lt;/strong&gt;: Qdrant (cloud-hosted)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM&lt;/strong&gt;: LLaMA 3.1 8B Instant via Groq API (fast, low-latency)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestration&lt;/strong&gt;: LangChain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The chatbot was trained on a curated mental health knowledge base (embedded as a PDF via &lt;code&gt;embed_documents.py&lt;/code&gt;). When a user sends a message, the system retrieves the top relevant chunks from Qdrant, then passes them as context to the LLM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crisis Detection
&lt;/h3&gt;

&lt;p&gt;One design decision we're particularly careful about: if a student types something that suggests a suicidal crisis, the chatbot does not attempt to respond as a therapist. It immediately surfaces emergency contact information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;crisis_triggers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i want to die&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i want to end it all&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t want to live&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;m done with everything&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ending my life&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i think about suicide&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i have no reason to live&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system detects these triggers before the RAG pipeline even runs. Automated AI responses should never substitute for human intervention in a genuine crisis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Features Beyond Assessment
&lt;/h2&gt;

&lt;p&gt;A questionnaire and a chatbot are the core, but the system includes several additional tools students can use day-to-day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mindfulness Games
&lt;/h3&gt;

&lt;p&gt;Two browser-based games built to reduce acute stress:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bubble Pop Game&lt;/strong&gt;: Timed focus exercise. Simple, but effective for grounding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Puzzle Game&lt;/strong&gt;: Cognitive engagement that shifts attention away from anxiety spirals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Game session data is stored and shown in the user's progress dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guided Breathing Exercises
&lt;/h3&gt;

&lt;p&gt;An animated breathing guide with session tracking. The &lt;code&gt;/breathing&lt;/code&gt; route stores per-session stats so students can track consistency over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Progress Dashboard
&lt;/h3&gt;

&lt;p&gt;Every student gets a personal dashboard showing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Historical assessment results (trend over time)&lt;/li&gt;
&lt;li&gt;Breathing and game session stats&lt;/li&gt;
&lt;li&gt;Upcoming counselor appointments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Doctor Appointment Booking
&lt;/h3&gt;

&lt;p&gt;Students can browse available counselors, check open slots via the &lt;code&gt;/api/available_slots&lt;/code&gt; endpoint, and book appointments directly through the platform. Admins can accept, reschedule, or cancel bookings through a separate admin dashboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  Admin Panel
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/admin_dashboard&lt;/code&gt; gives university mental health staff a full view of the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User management&lt;/strong&gt;: view/delete student accounts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assessment history&lt;/strong&gt;: aggregate submissions across all students&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chatbot ratings&lt;/strong&gt;: feedback on response quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Appointment management&lt;/strong&gt;: approve, reschedule, cancel appointments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because the platform is designed to integrate with, not replace, existing campus counseling infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Decisions
&lt;/h2&gt;

&lt;p&gt;A mental health platform handles particularly sensitive data. A few things we made sure to get right:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security headers on every response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.after_request&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;after_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-Frame-Options&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DENY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-Content-Type-Options&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nosniff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-XSS-Protection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1; mode=block&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Referrer-Policy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;strict-origin-when-cross-origin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cache prevention on protected routes:&lt;/strong&gt; The &lt;code&gt;@require_login&lt;/code&gt; decorator adds &lt;code&gt;no-cache, no-store, must-revalidate&lt;/code&gt; headers so assessment results cannot be retrieved from browser cache by a shared device user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Password reset via security questions&lt;/strong&gt; instead of email-only, reducing dependency on email deliverability for account recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Switch to PostgreSQL properly.&lt;/strong&gt; The app currently uses SQL Server via &lt;code&gt;pyodbc&lt;/code&gt; with a local SSMS connection string hardcoded for development. Moving to PostgreSQL on a hosted provider (Supabase, Neon) would make deployment portable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add model versioning.&lt;/strong&gt; The &lt;code&gt;.pkl&lt;/code&gt; files are committed directly to the repo. As the training data grows, we'd want MLflow or a similar registry to track model versions alongside accuracy metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade the chatbot to streaming.&lt;/strong&gt; The current Groq integration waits for the full response before sending it. Streaming tokens to the frontend would make the chatbot feel significantly more responsive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The full source is on GitHub under the HydraBytes organization:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TheHydraBytes/AI-based-student-stress-mangement" rel="noopener noreferrer"&gt;github.com/TheHydraBytes/AI-based-student-stress-mangement&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/TheHydraBytes/AI-based-student-stress-mangement.git
&lt;span class="nb"&gt;cd &lt;/span&gt;AI-based-student-stress-mangement
pip &lt;span class="nb"&gt;install &lt;/span&gt;flask scikit-learn pandas numpy langchain-huggingface langchain-groq langchain-qdrant
npm &lt;span class="nb"&gt;install
cd &lt;/span&gt;flask_app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to set &lt;code&gt;QDRANT_HOST&lt;/code&gt;, &lt;code&gt;QDRANT_API_KEY&lt;/code&gt;, and &lt;code&gt;GROQ_API_KEY&lt;/code&gt; environment variables for the chatbot to work. The ML models and Flask routes function independently without them.&lt;/p&gt;




&lt;p&gt;Built by &lt;a href="https://www.hydrabytes.tech" rel="noopener noreferrer"&gt;HydraBytes&lt;/a&gt; — a digital solutions agency based in Pakistan.&lt;/p&gt;

&lt;p&gt;If you're building something in the mental health tech space and want to talk architecture, drop a comment below.&lt;/p&gt;

</description>
      <category>python</category>
      <category>machinelearning</category>
      <category>ai</category>
      <category>flask</category>
    </item>
    <item>
      <title>How We Built an AI-Powered Retinal Disease Detector</title>
      <dc:creator>HydraBytes</dc:creator>
      <pubDate>Sat, 11 Apr 2026 12:50:33 +0000</pubDate>
      <link>https://forem.com/thehydrabytes/how-we-built-an-ai-powered-retinal-disease-detector-7fp</link>
      <guid>https://forem.com/thehydrabytes/how-we-built-an-ai-powered-retinal-disease-detector-7fp</guid>
      <description>&lt;p&gt;Early detection of retinal diseases like diabetic retinopathy can prevent blindness in millions of patients worldwide. Yet access to specialist ophthalmologists remains limited, especially in developing countries. That's the problem we set out to solve with OptiPro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- What is OptiPro?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OptiPro is an AI-powered retinal disease detection system that analyzes fundus images and classifies retinal conditions with high accuracy giving clinicians a fast, reliable second opinion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-  How It Works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core model is a convolutional neural network (CNN) trained on labeled fundus image datasets. The pipeline looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;  Image ingestion — fundus photos uploaded via web interface&lt;/li&gt;
&lt;li&gt;  Preprocessing — resizing, normalization, contrast enhancement&lt;/li&gt;
&lt;li&gt;  Inference — CNN classifies the image across multiple disease categories&lt;/li&gt;
&lt;li&gt;  Result — confidence score + condition label returned to the clinician&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;- Tech Stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Model: Python, TensorFlow, OpenCV&lt;/li&gt;
&lt;li&gt;  Backend: FastAPI&lt;/li&gt;
&lt;li&gt;  Frontend: Next.js&lt;/li&gt;
&lt;li&gt;  Deployment: Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;- What We Learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Training on imbalanced medical datasets is hard. We used weighted loss functions and aggressive augmentation (flips, rotations, brightness shifts) to prevent the model from overfitting to the majority class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-  What's Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OptiPro is currently in beta. We're working on expanding the disease categories and integrating it into clinical workflows.&lt;/p&gt;




&lt;p&gt;We build projects like this at &lt;a href="https://www.hydrabytes.tech" rel="noopener noreferrer"&gt;https://www.hydrabytes.tech&lt;/a&gt; - a web, mobile, and AI development agency based in Islamabad. If you're working on something ambitious, let's talk.&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%2Fbltoe3qbq4fwi0ysj5vw.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%2Fbltoe3qbq4fwi0ysj5vw.png" alt="OptiPro Dahboard" width="800" height="364"&gt;&lt;/a&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%2Fnt8dm2gaqjy9kor9mf37.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%2Fnt8dm2gaqjy9kor9mf37.png" alt="OptiPro Interface" width="800" height="360"&gt;&lt;/a&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%2F9k4nf5fwhhsjhr5pveuf.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%2F9k4nf5fwhhsjhr5pveuf.png" alt="OptiPro Interface" width="800" height="356"&gt;&lt;/a&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%2Fy5c5gohznjm3z2lj8avd.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%2Fy5c5gohznjm3z2lj8avd.png" alt="OptiPro Interface" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
