<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Alma Mohapatra - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Alma Mohapatra - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 16:10:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/almamohapatra/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How LLMs Work Under the Hood ]]>
                </title>
                <description>
                    <![CDATA[ Large Language Models (LLMs) like LLaMA 2 and Mistral are often described as “black boxes”. This means that you can see the text you give them and the responses they produce, but their inner workings remain hidden. Inside the model, billions of weigh... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-llms-work-under-the-hood/</link>
                <guid isPermaLink="false">68de8ecac4a4992466dd6d4b</guid>
                
                    <category>
                        <![CDATA[ LLM&#39;s  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ llm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alma Mohapatra ]]>
                </dc:creator>
                <pubDate>Thu, 02 Oct 2025 14:40:10 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759415587363/cc861698-598b-488a-bc79-58aeb99500ea.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Large Language Models (LLMs) like LLaMA 2 and Mistral are often described as “black boxes”. This means that you can see the text you give them and the responses they produce, but their inner workings remain hidden. Inside the model, billions of weights and neuron activations transform the input into output in ways we can’t directly interpret, so we see the results but not the step-by-step reasoning behind them. They generate text impressively well, but how do they actually represent meaning internally?</p>
<p>In this tutorial, you’ll run an open-source LLM locally on your machine and dig into its hidden activations — the internal neuron values produced while processing text. By visualizing these activations, you can see patterns that relate to sentiment, analogy, and bias.</p>
<p>This tutorial will help you:</p>
<ul>
<li><p>Understand how LLMs internally represent text</p>
</li>
<li><p>Experiment with embeddings and hidden states in Python</p>
</li>
<li><p>Build visualizations showing differences between words, phrases, or sentiments</p>
</li>
<li><p>Reflect on how bias and associations emerge in neural models</p>
</li>
</ul>
<p>Here is what we are going to cover in this tutorial, and yes — we’ll do all of this locally, with no cloud costs.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-0-create-amp-activate-a-virtual-environment">Step 0: Create &amp; Activate a Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-load-a-local-model-and-tokenizer">Step 1: Load a Local Model and Tokenizer</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-extract-hidden-states">Step 2: Extract Hidden States</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-visualize-sentiment-activations">Step 3: Visualize Sentiment Activations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-compare-two-sentences">Step 4: Compare Two Sentences</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-visualize-analogies-with-pca">Step 5: Visualize Analogies with PCA</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Python 3.10+</p>
</li>
<li><p>A machine with at least 8 GB RAM (16 GB recommended)</p>
</li>
<li><p>Basic familiarity with the command line and Python</p>
</li>
<li><p>Packages: <code>torch</code>, <code>transformers</code>, <code>matplotlib</code>, <code>scikit-learn</code></p>
</li>
</ul>
<h2 id="heading-step-0-create-amp-activate-a-virtual-environment">Step 0: Create &amp; Activate a Virtual Environment</h2>
<p>Why Use a Virtual Environment?</p>
<p>When you install Python libraries with <code>pip</code>, they normally go into your global Python setup. That can get messy fast:</p>
<ul>
<li><p>Different projects may need different versions of the same library (for example, <code>torch==2.0</code> vs <code>torch==2.2</code>).</p>
</li>
<li><p>Upgrading one project could accidentally break another.</p>
</li>
<li><p>Your system Python may get cluttered with packages you don’t actually need elsewhere.</p>
</li>
</ul>
<p>A virtual environment solves this by creating a self-contained “sandbox” just for your project.</p>
<ul>
<li><p>All installs (like <code>torch</code>, <code>transformers</code>, <code>matplotlib</code>) live inside your project folder.</p>
</li>
<li><p>When you’re done, you can delete the folder and nothing else on your computer is affected.</p>
</li>
<li><p>It’s the standard best practice for Python development — lightweight and safe.</p>
</li>
</ul>
<p>In short: a virtual environment keeps your project’s tools separate, so nothing breaks when you experiment.</p>
<h3 id="heading-windows-command-prompt-or-powershellmac-terminal">Windows (Command Prompt or PowerShell)/Mac (Terminal)</h3>
<ol>
<li><p>Create or navigate to your project folder (create one if needed):</p>
</li>
<li><p>Create the virtual environment: This creates a folder called <code>venv/</code> inside your project.</p>
</li>
<li><p>Activate it</p>
</li>
<li><p>Your terminal prompt will now look like step 4 in the code below</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment">#step 1</span>
mkdir llm_viz
<span class="hljs-built_in">cd</span> llm_viz

<span class="hljs-comment">#step 2</span>
python -m venv venv

<span class="hljs-comment">#step 3</span>
<span class="hljs-comment">#Window</span>
venv\Scripts\activate
<span class="hljs-comment">#Mac</span>
<span class="hljs-built_in">source</span> venv/bin/activate

<span class="hljs-comment">#step 4</span>
<span class="hljs-comment">#window</span>
(venv) C:\Users\YourName\llm_viz&gt;
<span class="hljs-comment">#mac</span>
(venv) your-macbook:llm_viz yourname$
</code></pre>
<h3 id="heading-install-dependencies">Install dependencies</h3>
<pre><code class="lang-bash">pip install torch transformers matplotlib scikit-learn
</code></pre>
<p>We’ll use DistilBERT (distilbert-base-uncased) since it’s small and easy to run locally. You can swap in larger models like LLaMA or Mistral if you have more powerful hardware.</p>
<h2 id="heading-step-1-load-a-local-model-and-tokenizer">Step 1: Load a Local Model and Tokenizer</h2>
<p>This step downloads <strong>DistilBERT</strong> (a small, free LLM) and prepares it to run locally.</p>
<p>In a file called <code>app.py</code>, paste the following code.</p>
<p><strong>Note:</strong> The first time you run it via <code>python app.py</code>, Hugging Face will automatically download the model (~250 MB). You only do this once.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> AutoTokenizer, AutoModel
<span class="hljs-keyword">import</span> torch

model_name = <span class="hljs-string">"distilbert-base-uncased"</span>
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name, output_hidden_states=<span class="hljs-literal">True</span>)
</code></pre>
<p>This code loads a small open-source language model so we can work with it on our own computer.<br>First, it imports the Transformers library and PyTorch, which provide the tools to download and run the model. Then it picks the model name (<code>distilbert-base-uncased</code>) and uses <code>AutoTokenizer</code> to turn text into tokens the model understands, while <code>AutoModel</code> downloads the pre-trained model itself and prepares it to return the hidden layer outputs we’ll visualize.</p>
<h2 id="heading-step-2-extract-hidden-states">Step 2: Extract Hidden States</h2>
<p>This feeds in text and grabs the “hidden activations” (the neuron outputs inside the model).</p>
<p>In the same <code>app.py</code>, add this function below the step 1 code.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_hidden_states</span>(<span class="hljs-params">text</span>):</span>
    inputs = tokenizer(text, return_tensors=<span class="hljs-string">"pt"</span>)
    <span class="hljs-keyword">with</span> torch.no_grad():
        outputs = model(**inputs)
    hidden = outputs.hidden_states[<span class="hljs-number">-1</span>][<span class="hljs-number">0</span>] <span class="hljs-comment"># Last hidden layer</span>
    tokens = tokenizer.convert_ids_to_tokens(inputs[<span class="hljs-string">"input_ids"</span>][<span class="hljs-number">0</span>])
    <span class="hljs-keyword">return</span> tokens, hidden

tokens, hidden = get_hidden_states(<span class="hljs-string">"I love pizza!"</span>)
print(tokens)
print(hidden.shape)
</code></pre>
<p>Now we can call <code>get_hidden_states("I love pizza!")</code> and it will return tokens like <code>["i", "love", "pizza", "!"]</code> and a big tensor of numbers.</p>
<p>You can use <code>python app.py</code> to run the code.</p>
<h2 id="heading-step-3-visualize-sentiment-activations">Step 3: Visualize Sentiment Activations</h2>
<p>This step plots how neuron values differ for happy vs. sad sentences. We’ll compare activations for positive and negative movie reviews.</p>
<p>In the same <code>app.py</code>, add this function below the step 2 code.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">plot_token_activations</span>(<span class="hljs-params">tokens, hidden, title, filename</span>):</span>
    plt.figure(figsize=(<span class="hljs-number">12</span>, <span class="hljs-number">4</span>))
    <span class="hljs-keyword">for</span> i, token <span class="hljs-keyword">in</span> enumerate(tokens):
        plt.plot(hidden[i].numpy(), label=token)
    plt.title(title)
    plt.xlabel(<span class="hljs-string">"Neuron Index"</span>)
    plt.ylabel(<span class="hljs-string">"Activation"</span>)
    plt.legend(loc=<span class="hljs-string">"upper right"</span>, fontsize=<span class="hljs-string">"x-small"</span>)
    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

<span class="hljs-comment"># Positive example</span>
tokens_pos, hidden_pos = get_hidden_states(<span class="hljs-string">"I love this movie, it is fantastic!"</span>)
plot_token_activations(tokens_pos, hidden_pos, <span class="hljs-string">"Positive Sentiment Example"</span>, <span class="hljs-string">"positive_sentiment.png"</span>)

<span class="hljs-comment"># Negative example</span>
tokens_neg, hidden_neg = get_hidden_states(<span class="hljs-string">"I hate this movie, it is terrible."</span>)
plot_token_activations(tokens_neg, hidden_neg, <span class="hljs-string">"Negative Sentiment Example"</span>, <span class="hljs-string">"negative_sentiment.png"</span>)
</code></pre>
<p>After running the code <code>python app.py</code>, check your folder — you’ll see two image files: <code>positive_sentiment.png</code> and <code>negative_sentiment.png</code>. They’ll look like line graphs showing activations for each token.</p>
<p>Figure 1: Activations for a positive review. Words like “love” and “fantastic” activate distinctive neuron patterns.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757910763307/5556cf9b-69a9-4f7b-b13e-76041d2d52b0.png" alt="Figure 1: Activations for a positive review. Words like “love” and “fantastic” activate distinctive neuron patterns." class="image--center mx-auto" width="1200" height="400" loading="lazy"></p>
<p>Figure 2: Activations for a negative review. Words like “hate” and “terrible” trigger different neuron curves.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757910816349/9aa6e149-3ff7-443e-96f9-7f86af0cc9bd.png" alt="Figure 2: Activations for a negative review. Words like “hate” and “terrible” trigger different neuron curves." class="image--center mx-auto" width="1200" height="400" loading="lazy"></p>
<h2 id="heading-step-4-compare-two-sentences">Step 4: Compare Two Sentences</h2>
<p>This step compares average neuron patterns between two sentences.</p>
<p>Now in the same <code>app.py</code>, add this function below the step 3 code.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compare_sentences</span>(<span class="hljs-params">s1, s2, filename</span>):</span>
    tokens1, hidden1 = get_hidden_states(s1)
    tokens2, hidden2 = get_hidden_states(s2)

    plt.figure(figsize=(<span class="hljs-number">10</span>,<span class="hljs-number">5</span>))
    plt.plot(hidden1.mean(dim=<span class="hljs-number">0</span>).numpy(), label=s1[:<span class="hljs-number">30</span>]+<span class="hljs-string">"..."</span>)
    plt.plot(hidden2.mean(dim=<span class="hljs-number">0</span>).numpy(), label=s2[:<span class="hljs-number">30</span>]+<span class="hljs-string">"..."</span>)
    plt.title(<span class="hljs-string">"Sentence Activation Comparison"</span>)
    plt.xlabel(<span class="hljs-string">"Neuron Index"</span>)
    plt.ylabel(<span class="hljs-string">"Mean Activation"</span>)
    plt.legend()
    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

compare_sentences(<span class="hljs-string">"I love coding."</span>, <span class="hljs-string">"I hate coding."</span>, <span class="hljs-string">"sentence_comparison.png"</span>)
</code></pre>
<p>After running the code <code>python app.py</code>, You’ll now get <code>sentence_comparison.png</code>, showing two curves — one for the happy sentence, one for the negative.</p>
<p>Figure 3: Comparing “I love coding” vs “I hate coding”. Even averaged across tokens, neuron profiles differ significantly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757910867868/824567dd-b390-4305-aa87-44d076205d3a.png" alt="Figure 3: Comparing “I love coding” vs “I hate coding”. Even averaged across tokens, neuron profiles differ significantly." class="image--center mx-auto" width="1000" height="500" loading="lazy"></p>
<h2 id="heading-step-5-visualize-analogies-with-pca">Step 5: Visualize Analogies with PCA</h2>
<p>We can check if embeddings encode semantic analogies like man → woman :: king → queen.</p>
<p>This step projects word embeddings like <em>man, woman, king, queen</em> into 2D space so you can see relationships.</p>
<p>Now in the same <code>app.py</code>, add this function below the step 4 code.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> sklearn.decomposition <span class="hljs-keyword">import</span> PCA

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_sentence_embedding</span>(<span class="hljs-params">text</span>):</span>
    inputs = tokenizer(text, return_tensors=<span class="hljs-string">"pt"</span>)
    <span class="hljs-keyword">with</span> torch.no_grad():
        outputs = model(**inputs)
    hidden = outputs.last_hidden_state.mean(dim=<span class="hljs-number">1</span>).squeeze()
    <span class="hljs-keyword">return</span> hidden

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">plot_embeddings</span>(<span class="hljs-params">words, embeddings, filename</span>):</span>
    pca = PCA(n_components=<span class="hljs-number">2</span>)
    reduced = pca.fit_transform(torch.stack(embeddings).numpy())

    plt.figure(figsize=(<span class="hljs-number">8</span>, <span class="hljs-number">6</span>))
    <span class="hljs-keyword">for</span> i, word <span class="hljs-keyword">in</span> enumerate(words):
        x, y = reduced[i]
        plt.scatter(x, y, marker=<span class="hljs-string">"o"</span>, s=<span class="hljs-number">100</span>)
        plt.text(x+<span class="hljs-number">0.02</span>, y+<span class="hljs-number">0.02</span>, word, fontsize=<span class="hljs-number">12</span>)
    plt.title(<span class="hljs-string">"Word Embeddings in 2D (PCA)"</span>)
    plt.xlabel(<span class="hljs-string">"PC1"</span>)
    plt.ylabel(<span class="hljs-string">"PC2"</span>)
    plt.grid(<span class="hljs-literal">True</span>)
    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

words = [<span class="hljs-string">"man"</span>, <span class="hljs-string">"woman"</span>, <span class="hljs-string">"king"</span>, <span class="hljs-string">"queen"</span>]
embeddings = [get_sentence_embedding(w) <span class="hljs-keyword">for</span> w <span class="hljs-keyword">in</span> words]
plot_embeddings(words, embeddings, <span class="hljs-string">"word_analogies.png"</span>)
</code></pre>
<p>After running the code <code>python app.py</code> , you’ll have <code>word_analogies.png</code> showing the famous <em>man→woman</em> and <em>king→queen</em> relationship as almost parallel lines.</p>
<p>Figure 4: PCA visualization of word embeddings. Man–woman and king–queen form parallel relationships, reflecting analogy structure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757910904012/758e3245-5ec7-4108-ae18-9fba8632f1a0.png" alt="Figure 4: PCA visualization of word embeddings. Man–woman and king–queen form parallel relationships, reflecting analogy structure." class="image--center mx-auto" width="800" height="600" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You’ve built a local toolkit to:</p>
<ul>
<li><p>Extract hidden activations from an LLM</p>
</li>
<li><p>Visualize neuron activity for positive vs. negative sentiment</p>
</li>
<li><p>Explore semantic analogies like “king → queen”</p>
</li>
<li><p>Inspect potential biases in role associations</p>
</li>
</ul>
<p>This helps demystify LLMs — showing they’re massive matrices of numbers encoding meaning, not magic.</p>
<p>Small models like DistilBERT run on any laptop. Larger models like LLaMA 2 can scale exploration further.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
