<?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: Mbonimpa Kelia</title>
    <description>The latest articles on DEV Community by Mbonimpa Kelia (@mbonimpa_kelia_9).</description>
    <link>https://dev.to/mbonimpa_kelia_9</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4151839%2F7316dfc3-fddd-4a0a-b62a-79a3ad7aa784.png</url>
      <title>DEV Community: Mbonimpa Kelia</title>
      <link>https://dev.to/mbonimpa_kelia_9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mbonimpa_kelia_9"/>
    <language>en</language>
    <item>
      <title># What I Learned About Computer Vision as a Beginner: My kLab TekHer AI Journey</title>
      <dc:creator>Mbonimpa Kelia</dc:creator>
      <pubDate>Tue, 13 Oct 2026 14:03:25 +0000</pubDate>
      <link>https://dev.to/mbonimpa_kelia_9/-what-i-learned-about-computer-vision-as-a-beginner-my-klab-tekher-ai-journey-155f</link>
      <guid>https://dev.to/mbonimpa_kelia_9/-what-i-learned-about-computer-vision-as-a-beginner-my-klab-tekher-ai-journey-155f</guid>
      <description>&lt;p&gt;A beginner's reflection on the kLab TekHer AI Program — Cohort 2&lt;/p&gt;

&lt;p&gt;When I started the "Introduction to Computer Vision" notebook, I thought I was going to learn how to make computers "see." I was half right. What I actually learned is that computers don't see anything at all — they just do math on grids of numbers. And once that clicked, everything else made sense.&lt;/p&gt;

&lt;p&gt;Here's what I learned, the challenges I ran into, and how I got past them.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An image is just numbers — lots of them
The first lesson was the most surprising. A photo isn't a picture to a computer. It's a giant grid of numbers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My photo of the bus was 1080 rows × 810 columns × 3 channels (R, G, B) = 2,624,400 numbers. That's it. No "bus," no "people," no "sunlight" — just over 2.6 million numbers between 0 and 255.&lt;/p&gt;

&lt;p&gt;Each pixel has three values:&lt;/p&gt;

&lt;p&gt;Red, Green, Blue (in that order)&lt;/p&gt;

&lt;p&gt;0 = none of that color, 255 = full of that color&lt;/p&gt;

&lt;p&gt;White = (255, 255, 255). Black = (0, 0, 0).&lt;/p&gt;

&lt;p&gt;That was my first "aha" moment. When I removed the red channel from an image, all the white areas turned cyan. Why? Because white − red = green + blue = cyan. Simple arithmetic, but it changed how I think about photos.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Filters: how a computer finds edges
Next, I learned about filters (also called kernels). A filter is a tiny grid of numbers — usually 3×3 or 5×5 — that slides across the image. At each position, it multiplies the neighboring pixels by the filter's weights and adds the results. That becomes the new value of the center pixel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two classic filters I built by hand:&lt;/p&gt;

&lt;p&gt;Filter  What it does&lt;br&gt;
Box blur (all weights = 1/25)   Averages each pixel with its 25 neighbors → softens the image&lt;br&gt;
Sobel x ([-1,0,1], [-2,0,2], [-1,0,1])  Subtracts left side from right side → finds vertical edges&lt;br&gt;
Sobel y (Sobel x transposed)    Finds horizontal edges&lt;br&gt;
When I ran Sobel x on the bus photo, I could see exactly where the vertical edges were — the sides of the bus, the traffic sign, the people's arms. But the horizontal edges (like the roof of the bus) were invisible until I ran Sobel y. Combining both with sqrt(x² + y²) gave me every edge.&lt;/p&gt;

&lt;p&gt;This is what a CNN does. The difference is that a CNN doesn't use hand-picked weights — it learns them from thousands of labeled photos.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Classification: one label for a whole image
I loaded a pretrained YOLO classifier (trained on ImageNet — 1.2 million photos, 1,000 classes) and ran it on the bus photo. It said:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;minibus — 57.1%&lt;/p&gt;

&lt;p&gt;police_van — 33.7%&lt;/p&gt;

&lt;p&gt;trolleybus — 4.2%&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Top guess was correct! But the notebook asked me to test it on something I know well: a Rwandan agaseke (traditional woven basket).&lt;/p&gt;

&lt;p&gt;The result? "vase" at 74.4%.&lt;/p&gt;

&lt;p&gt;The model had never seen an agaseke. It looked at the shape, decided it most resembled a vase, and gave me a confident but wrong answer.&lt;/p&gt;

&lt;p&gt;This was the single most important lesson in the whole notebook.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Object Detection: what and where
I moved on to object detection — a YOLO model (yolo11n.pt) trained on COCO (80 everyday Western classes: person, car, bus, dog, chair, laptop...).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the bus photo, it worked beautifully:&lt;/p&gt;

&lt;p&gt;bus — 94%&lt;/p&gt;

&lt;p&gt;person — 89%, 88%, 86%, 62%&lt;/p&gt;

&lt;p&gt;5 boxes, correctly drawn around each object.&lt;/p&gt;

&lt;p&gt;On my agaseke, it drew 2 boxes:&lt;/p&gt;

&lt;p&gt;vase — 45% (covering the whole basket)&lt;/p&gt;

&lt;p&gt;bird — 58% (around the pointed tip of the lid)&lt;/p&gt;

&lt;p&gt;Both wrong. But the boxes themselves were reasonably placed. The model could see where the object was — it just couldn't name it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Segmentation: pixel-by-pixel
Segmentation is the most detailed level. Instead of a rectangle, the model predicts a mask — a grid of 0s and 1s where 1 means "this pixel belongs to the object."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the bus photo, the segmenter produced clean outlines of the bus and each person. On my agaseke, the mask traced the shape of the basket beautifully — but again, the label was "vase."&lt;/p&gt;

&lt;p&gt;Key insight: the AI could see the shape perfectly. It just had never learned the word "agaseke."&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When models fail
The last section was the most eye-opening. I took the same photo and made it harder in 5 ways:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Variant Result&lt;br&gt;
Original    5 objects, 4 people — perfect&lt;br&gt;
Very dark   6 objects (with a false "skateboard")&lt;br&gt;
Upside down 1 object, mislabeled as "screen"&lt;br&gt;
Low resolution  0 objects — total failure&lt;br&gt;
Blurry  A few objects, some misses&lt;br&gt;
The upside-down image crushed the model. Why? Because the model's internal filters were trained on photos where "up" is sky and "down" is ground. Flip it and everything breaks.&lt;/p&gt;

&lt;p&gt;The low-resolution image also crushed it. Fine details = the entire basis of detection.&lt;/p&gt;

&lt;p&gt;Lesson: AI is not magic. It's pattern matching. If the pattern is unfamiliar, it fails.&lt;/p&gt;

&lt;p&gt;The Challenges I Faced&lt;br&gt;
Challenge 1: "NameError: name 'np' is not defined"&lt;br&gt;
What happened: I ran a cell that used np (NumPy) and Image (PIL), but the notebook didn't know what those were. The cell crashed.&lt;/p&gt;

&lt;p&gt;Why: Google Colab doesn't remember variables between sessions. When I restarted the runtime (or closed and reopened the notebook), all the import statements were forgotten. The setup cell had to be re-run.&lt;/p&gt;

&lt;p&gt;How I fixed it: I learned the golden rule — always run cells from the top down, in order, one at a time. If you see any NameError, scroll up and look for the cell that defines the missing name.&lt;/p&gt;

&lt;p&gt;Challenge 2: "NameError: name 'convolve' is not defined"&lt;br&gt;
What happened: I added a new cell to test new kernels (sharpen, big blur, Sobel y), but the convolve function — defined in the previous cell — wasn't in memory anymore.&lt;/p&gt;

&lt;p&gt;Why: Same issue as Challenge 1. I had skipped running the earlier cell.&lt;/p&gt;

&lt;p&gt;How I fixed it: Re-ran the cell that starts with def convolve(gray, kernel):. Immediately, my new test cell worked.&lt;/p&gt;

&lt;p&gt;Challenge 3: Colab couldn't find my image&lt;br&gt;
What happened: I wrote IMAGE_PATH = r"C:\Users\PC\Documents\klab\computer science.jpg" and got a FileNotFoundError.&lt;/p&gt;

&lt;p&gt;Why: Google Colab runs on a cloud server. It has no access to your C: drive. Your files are on your laptop; Colab is in Google's data center.&lt;/p&gt;

&lt;p&gt;How I fixed it: I used the correct upload workflow:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
from google.colab import files&lt;br&gt;
uploaded = files.upload()&lt;br&gt;
Then clicked "Choose Files," selected my image, and let Colab save it locally. I could then access it by filename (e.g., "Agaseke (1).jpg").&lt;/p&gt;

&lt;p&gt;Bonus lesson: Colab sometimes renames your file — for example, Agaseke.jpg → Agaseke (1).jpg. To avoid hard-coding a wrong name, I used IMAGE_PATH = list(uploaded.keys())[0] to grab whatever filename Colab assigned.&lt;/p&gt;

&lt;p&gt;Challenge 4: The upload widget disappeared&lt;br&gt;
What happened: A message said: "Upload widget is only available when the cell has been executed in the current browser session."&lt;/p&gt;

&lt;p&gt;Why: The runtime had disconnected in the background, so the "Choose Files" button became inactive.&lt;/p&gt;

&lt;p&gt;How I fixed it: Re-ran the upload cell (Shift+Enter). The button came back. If it still didn't work, I could use IMAGE_PATH = "Agaseke (1).jpg" to load the file directly.&lt;/p&gt;

&lt;p&gt;Challenge 5: Not sure whether to edit the existing cell or add a new one&lt;br&gt;
What happened: I wasn't sure whether to change code inside an existing cell or add a new one.&lt;/p&gt;

&lt;p&gt;How I fixed it: I asked, and the answer was:&lt;/p&gt;

&lt;p&gt;If you're learning or experimenting → add a new cell. Safe, reversible.&lt;/p&gt;

&lt;p&gt;If you're fixing a bug → edit the existing cell.&lt;/p&gt;

&lt;p&gt;For the "Try it yourself" tasks → a new cell is usually cleanest.&lt;/p&gt;

&lt;p&gt;Challenge 6: The domain gap — a lesson disguised as a bug&lt;br&gt;
What happened: When I ran the classifier on my agaseke, it said "vase, 74.4%". I thought I had done something wrong.&lt;/p&gt;

&lt;p&gt;Why: I hadn't. The model simply had never been trained on agaseke. It was doing exactly what it was supposed to — finding the closest thing it knew.&lt;/p&gt;

&lt;p&gt;How I "fixed" it: I didn't. I learned from it. This is one of the most important unsolved problems in AI: a model trained on American photos will fail on Rwandan objects, even when it's technically "working."&lt;/p&gt;

&lt;p&gt;What I Would Do Differently Next Time&lt;br&gt;
Always run cells from the top before starting work.&lt;/p&gt;

&lt;p&gt;Keep a "session check" cell at the top of my notebook that prints which image is loaded:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
print("IMAGE_PATH =", IMAGE_PATH)&lt;br&gt;
print("img shape =", img.shape)&lt;br&gt;
Save intermediate copies of variables (like my_img = img.copy()) so I don't lose my original image when experimenting.&lt;/p&gt;

&lt;p&gt;Predict before running. Every "Try it yourself" became more interesting when I stopped and thought about what would happen first.&lt;/p&gt;

&lt;p&gt;Final Reflection&lt;br&gt;
This notebook took me from "an image is a picture" to "an image is 2,624,400 numbers, filtered and classified by learned patterns." Along the way, I:&lt;/p&gt;

&lt;p&gt;Manipulated RGB channels&lt;/p&gt;

&lt;p&gt;Built blur and edge-detection filters&lt;/p&gt;

&lt;p&gt;Ran a real ImageNet classifier&lt;/p&gt;

&lt;p&gt;Ran a real COCO object detector&lt;/p&gt;

&lt;p&gt;Ran a real segmentation model&lt;/p&gt;

&lt;p&gt;Discovered the domain gap in person, using an agaseke.&lt;/p&gt;

&lt;p&gt;The hardest part wasn't the code. It was realizing that the AI is doing exactly what I tell it to — I just have to give it training data that represents my world.&lt;/p&gt;

&lt;p&gt;My biggest takeaway: If we want AI that works in Rwanda, we need to build it with Rwandan data. And that's the next thing I want to learn.&lt;/p&gt;

&lt;p&gt;— Written after completing the "Introduction to Computer Vision" notebook, kLab TekHer AI Program, Cohort 2.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>beginners</category>
      <category>learning</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>What I Learned About Word Embeddings While Building a FAQ Chatbot</title>
      <dc:creator>Mbonimpa Kelia</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:26:50 +0000</pubDate>
      <link>https://dev.to/mbonimpa_kelia_9/what-i-learned-about-word-embeddings-while-building-a-faq-chatbot-350c</link>
      <guid>https://dev.to/mbonimpa_kelia_9/what-i-learned-about-word-embeddings-while-building-a-faq-chatbot-350c</guid>
      <description>&lt;p&gt;When I set out to build a simple FAQ chatbot for Python programming questions, I thought I'd be done in a weekend. I was wrong — but in the best way. Along the way, I ran into a concept that changed how I think about text: word embeddings.&lt;/p&gt;

&lt;p&gt;This post is a lessons-learned write-up of what I discovered. If you're a developer getting into NLP, this might save you a few hours of confusion.&lt;/p&gt;

&lt;p&gt;What Are Word Embeddings, Anyway?&lt;br&gt;
Computers don't understand words. They understand numbers. So any NLP system has to convert text into numbers before it can do anything useful.&lt;/p&gt;

&lt;p&gt;The naive way to do this is one-hot encoding or bag-of-words: assign each word a number, and represent a sentence as a list of word counts. This works, but it has a big problem — it treats every word as completely independent. To a bag-of-words model, "car" and "automobile" are as unrelated as "car" and "banana."&lt;/p&gt;

&lt;p&gt;Word embeddings solve this. An embedding represents each word as a dense vector of real numbers — usually 100 to 300 dimensions — where the position of the vector in space reflects the word's meaning. Words that appear in similar contexts end up close together in this vector space. "Dog" and "puppy" end up near each other. "King" and "queen" end up near each other. "Python" and "programming" end up near each other.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because once words are vectors, you can do math on them. You can measure similarity, find clusters, and even solve analogies.&lt;/p&gt;

&lt;p&gt;The Method That Clicked for Me: Word2Vec&lt;br&gt;
The assignment notes I was working from mentioned Word2Vec, and it's the one that finally made embeddings click.&lt;/p&gt;

&lt;p&gt;Word2Vec comes in two flavors:&lt;/p&gt;

&lt;p&gt;CBOW (Continuous Bag of Words): predicts a word from its surrounding context&lt;/p&gt;

&lt;p&gt;Skip-gram: predicts the surrounding context from a word&lt;/p&gt;

&lt;p&gt;The key insight is the distributional hypothesis: words that appear in similar contexts have similar meanings. If "cat" and "dog" both frequently appear near "pet," "feed," and "vet," Word2Vec learns that they're semantically related.&lt;/p&gt;

&lt;p&gt;Training happens by sliding a window over a large corpus. For each word, the model tries to predict its neighbors. Over millions of examples, the weights of a small neural network adjust until each word has a useful vector. What surprised me is that nobody labels this data — the text itself is the supervision. You just throw a lot of text at it and it figures out meaning on its own.&lt;/p&gt;

&lt;p&gt;The Analogy That Blew My Mind&lt;br&gt;
The classic Word2Vec demo is this:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
king - man + woman ≈ queen&lt;br&gt;
Translated: take the vector for "king," subtract "man," add "woman" — and the closest vector to the result is "queen." The model wasn't told that kings and queens are related, or that man and woman are a gender pair. It learned both facts from raw text alone.&lt;/p&gt;

&lt;p&gt;I tried to reproduce this with a Python example using gensim:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
from gensim.models import Word2Vec&lt;/p&gt;

&lt;h1&gt;
  
  
  Assume &lt;code&gt;sentences&lt;/code&gt; is a list of tokenized sentences
&lt;/h1&gt;

&lt;p&gt;model = Word2Vec(sentences, vector_size=100, window=5, min_count=5)&lt;/p&gt;

&lt;h1&gt;
  
  
  Find words most similar to "python"
&lt;/h1&gt;

&lt;p&gt;similar = model.wv.most_similar("python", topn=5)&lt;br&gt;
print(similar)&lt;/p&gt;

&lt;h1&gt;
  
  
  [('programming', 0.71), ('java', 0.68), ('code', 0.65), ...]
&lt;/h1&gt;

&lt;p&gt;The fact that a model trained on raw Wikipedia text knows "python" is close to "programming" still amazes me.&lt;/p&gt;

&lt;p&gt;What I Struggled With&lt;br&gt;
Here's the honest part: I did not use word embeddings in my final FAQ chatbot.&lt;/p&gt;

&lt;p&gt;My project used TF-IDF, which is a sparse, frequency-based vectorization method — not an embedding. TF-IDF is fast, interpretable, and works great for small FAQ datasets. But it has a real limitation: it can't handle paraphrases.&lt;/p&gt;

&lt;p&gt;For example, if a user asks "How do I set up Python?" and my FAQ has "How do I install Python?", TF-IDF sees "set up" and "install" as completely different words. It might miss the match. A word embedding model would recognise that "set up" and "install" mean the same thing and would match them correctly.&lt;/p&gt;

&lt;p&gt;So I learned embeddings as theory but implemented TF-IDF in practice. If I were to expand this project to hundreds or thousands of FAQs, the very first upgrade I'd make is swapping TF-IDF for sentence embeddings like Sentence-BERT, which can encode entire questions into a single dense vector and handle paraphrasing gracefully.&lt;/p&gt;

&lt;p&gt;Other Embedding Methods Worth Knowing&lt;br&gt;
While learning, I also came across:&lt;/p&gt;

&lt;p&gt;GloVe (Global Vectors): similar to Word2Vec but trained on global word co-occurrence statistics rather than a sliding window.&lt;/p&gt;

&lt;p&gt;FastText: extends Word2Vec by treating words as bags of character n-grams. This means it can generate vectors for words it has never seen — very handy for typos or rare words.&lt;/p&gt;

&lt;p&gt;Contextual embeddings (BERT, ELMo): the same word gets different vectors in different sentences. "Bank" in "river bank" vs. "money bank" gets two different vectors. This is the foundation of modern LLMs.&lt;/p&gt;

&lt;p&gt;What I'd Tell Another Developer&lt;br&gt;
If you're just starting with NLP:&lt;/p&gt;

&lt;p&gt;Learn TF-IDF first. It's simpler, faster, and good enough for many tasks.&lt;/p&gt;

&lt;p&gt;Learn Word2Vec next. It builds the intuition for why embeddings work.&lt;/p&gt;

&lt;p&gt;Then try sentence embeddings (SBERT). This is the practical modern approach for semantic search.&lt;/p&gt;

&lt;p&gt;Don't skip the theory. Understanding why embeddings work — the distributional hypothesis — makes it much easier to know when to use them.&lt;/p&gt;

&lt;p&gt;Embeddings turned text into geometry. That's the mental shift that changed how I look at NLP problems.&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;p&gt;Mikolov et al., "Efficient Estimation of Word Representations in Vector Space" (2013)&lt;/p&gt;

&lt;p&gt;Pennington et al., "GloVe: Global Vectors for Word Representation" (2014)&lt;/p&gt;

&lt;p&gt;Bojanowski et al., "Enriching Word Vectors with Subword Information" (FastText, 2017)&lt;/p&gt;

&lt;p&gt;Reimers &amp;amp; Gurevych, "Sentence-BERT" (2019)&lt;/p&gt;

&lt;p&gt;Dev.to community posts on TF-IDF vs. embeddings&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
  </channel>
</rss>
