☁️
☁️
☁️
☁️
✦✦✦✦✦✦✦✦
← all technical blogs

πŸ“Œ pinned Β· a tour of the build

The Data Science Hiding in My Portfolio

This site isn't just a static page. Most of it is generated, organized, and answered by models, and every piece ships with an eval. Here's the tour, with the concepts explained.
0%
chatbot accuracy
0
ML/LLM features
0.00
mood confidence
0
hallucinations
The content on this site is not only displayed, a lot of it is generated, organized, and answered by models. And because I care whether each piece actually works, almost every one ships with a small evaluation, so quality is measured, not assumed. Here is how each part works, with the concept behind it.

🧭The one idea everything is built on: embeddings

πŸ’‘ the concept

An embedding turns text (or an image) into a list of numbers, a vector. A good model places similar meanings close together, even with no shared words: "make LLMs faster" and "reduce inference latency" land near each other.

To measure "close" I use cosine similarity: the cosine of the angle between two vectors, from -1 (opposite) through 0 (unrelated) to 1 (identical). It cares about direction, not length, so a short phrase and a long paragraph on the same topic still match.

Once meaning becomes geometry, a lot gets simple: search is "find the nearest vectors," recommendations are "find neighbors," classification is "which label is closest." Almost everything below is a variation on that move.

πŸ’¬Ask-my-portfolio chatbot (RAG)

πŸ’‘ the concept

Retrieval-augmented generation fixes the fact that LLMs confidently make things up. Instead of asking the model to recall facts, you retrieve the relevant source text first (by embedding similarity), then ask it to answer using only that text. It becomes a careful summarizer of real sources, not a fuzzy memory.
Grounded in my bio, experience, every project's GitHub README, and my Substack posts (poems and photos are private and excluded). Answers stream token-by-token, cite sources, and refuse when out of scope.It also carries conversation memory: recent turns travel with each question, both into retrieval (the last exchange is embedded alongside the new question) and into the prompt, so a follow-up like "can I see a demo of it?" still knows what "it" is. And every project card has an ask about this button that opens the chat pre-loaded with that project, retrieval and grounding included.
Retrieval hit rate (right chunk in top-k)100%
Answer accuracy (contains the fact)100%
Refusal correctness (declines out-of-scope)100%
The refusals ("favorite poem?", "phone number?", "2024 Super Bowl?") are out of scope on purpose: it declines all three with zero hallucination.

🌌The embeddings galaxy (PCA)

πŸ’‘ the concept

Embeddings live in hundreds of dimensions, which you can't draw. PCA finds the directions of greatest variation and projects everything onto the top two, giving an x and y to plot while keeping as much real spread as possible. I compute it by hand via the Gram matrix and power iteration, no library.
Similar projects drift near each other, so the model roughly rediscovers my technical areas from text alone. An earlier version ran k-means and let an LLM name the clusters, but at near-zero separation it mislabeled things (a car-price project once landed in a "Computer Vision" cluster). So I color each dot by its real area instead. The layout shows the structure; the colors stay honest.The dots are interactive too: hover or tap one and a little card pops up with the project's area, domains, and actions, open the code, ask the chatbot about it, or find similar, which runs nearest-neighbors over the same cached vectors and filters the project grid above.You can also drop yourself into the map. Type an interest and it's embedded and projected onto the same two PCA axes as the projects, a proper out-of-sample projection (project the query onto the axes I already found, don't re-fit them), so your "you are here" star lands where it genuinely belongs. A line points to the single nearest project by cosine, and one click asks the chatbot about it. Semantic search, turned into a place on a map instead of a ranked list.

🏷️Auto-pulled blog with embedding zero-shot tagging

πŸ’‘ the concept

Zero-shot classification labels things with no training examples: describe each label in words, embed the descriptions and the post, and the closest label wins. Two refinements make it robust. Multi-prototype labels: average a few phrasings per category to cancel noise. And a confidence rule instead of blind argmax: a domain is attached only when it clears a floor and clearly beats the runner-up. When two are close, that's ambiguity, so it tags none. Precision over recall.
This replaced a brittle keyword system that once tagged an encryption post as "Food & Nutrition" because the intro mentioned my cat's empty food bowl. I weight the title 2x in the embedded text, and this very post tagged itself.The tags then earn their keep: the Technical Blogs page builds its topic filter pills from whatever tags exist across posts, so publishing a post on a new area grows a new filter on its own, no code change.The Work grid has a cheaper cousin for terse GitHub repo descriptions: a deterministic keyword classifier, now multi-label, so a project that is genuinely both IoT and Computer Vision wears both tags instead of whichever rule fired first. Matching the method to the input: embeddings for prose, rules for metadata.

πŸ“ΈPhoto clustering (k-means + silhouette + CLIP)

πŸ’‘ the concept

k-means groups without labels: place k centers, assign each point to its nearest, move centers to the mean, repeat. The silhouette score (-1 to 1) picks the best k by comparing in-cluster tightness to the nearest other cluster. CLIP puts images and captions in a shared space, so a CLIP image embedding captures visual content directly.
silhouette with caption-text embeddings0.076
silhouette with CLIP image embeddings0.143
Switching from caption text to CLIP image embeddings roughly doubled the silhouette and produced more meaningful groups. The pixels knew something the captions didn't.

✨The smaller LLM touches

πŸ’‘ the concept

A few features use an LLM as a function, not a chatbot: a strict instruction, temperature 0 (minimum randomness, repeatable output), and structured JSON. Constrained like that, it becomes a reliable little classifier or rewriter.
  • Poem mood classification into eight moods with a confidence score; average 0.894 across 8 poems, reported on the page.
  • AI poem art: a model distills each poem into one evocative prompt and an image model renders it. Cached so it is paid for once, but I can regenerate a fresh take, keep versions I like, or upload my own.
  • Auto-captioned photos via a vision model, low-detail to stay cheap.
  • ELI5 / expert toggle rewrites every blurb for a 10-year-old or a senior engineer, under a strict "keep every fact truthful" rule.
  • Guestbook moods: when someone signs the guestbook, the same temperature-0 JSON trick sorts their note into one of six moods (sweet, excited, curious, funny, kind, proud) and pins a matching little emoji to it.

πŸ› οΈThe engineering around it

  • Caching + ISR. Embeddings, art, captions, and rewrites are computed once; GitHub and Substack refresh hourly, so the site stays fresh without re-paying every request.
  • Batched calls. Labels and classifications go out in one batched call, not one-per-item.
  • Graceful fallback. The live embedding classifier has a deterministic keyword backup, so a flaky API degrades quality instead of breaking the page.
  • Evals as a habit. If I can't measure it, I try not to claim it.
That is the whole machine: it embeds, retrieves, clusters, classifies, and grades its own homework. Thanks for reading the tour. 🌸