<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://sungwon-kim.com/feed.xml" rel="self" type="application/atom+xml"/><link href="https://sungwon-kim.com/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-10-22T16:50:18+00:00</updated><id>https://sungwon-kim.com/feed.xml</id><title type="html">blank</title><subtitle>Sungwon&apos;s Personal website </subtitle><entry><title type="html">Old NLP Paper Reviews</title><link href="https://sungwon-kim.com/blog/2025/old-paper-reviews/" rel="alternate" type="text/html" title="Old NLP Paper Reviews"/><published>2025-11-21T00:00:00+00:00</published><updated>2025-11-21T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/old-paper-reviews</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/old-paper-reviews/"><![CDATA[<h1 id="premise">Premise</h1> <p>This is part of an ongoing series where I revisit influential papers in NLP. Each post will briefly summarize the core ideas, highlight what I found interesting (or confusing), and sometimes include small experiments. The goal isn’t to be exhaustive.</p> <h2 id="attention-is-all-you-need-vaswani-et-al-2017">Attention is All You need (Vaswani et al., 2017)</h2> <p>Written in Oct 30, 2025</p> <p>Many people consider this the landmark paper that ignited the Transformer revolution.</p> \[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V\] <p>This is the mechanism that started it all. Much have changed since then: I will focus on what I found interesting when reading the paper again.</p> <h3 id="about-attention-mechanism">About Attention Mechanism</h3> <blockquote> <p>The two most commonly used attention functions are additive attention [2], and dot-product (multiplicative) attention. Dot-product attention is identical to our algorithm, except for the scaling factor of \(\sqrt{d_k}\). Additive attention computes the compatibility function using a feed-forward network with a single hidden layer. While the two are similar in theoretical complexity, dot-product attention is much faster and more space-efficient in practice, since it can be implemented using highly optimized matrix multiplication code.</p> </blockquote> <p>This shows that the idea of <strong>attention</strong> itself was not new. Previous <a href="https://arxiv.org/pdf/1409.0473">paper</a> also introduced attention mechanism. It was used as a middle layer connecting encoder and decoder RNNs. The novelty of this paper was showing that attention mechanism alone can be used to replace RNNs.</p> <p>That said, I think that the title “Attention Is All You Need” is slightly misleading. When I first read the paper, I assumed attention parameters dominated model size. In reality, most parameters come from the feed forward layers (FFN) within each attention block. Many LLMs use 4 times dimension expansion in the <a href="https://self-supervised.cs.jhu.edu/fa2025/files/slides/transformers-language-models.pdf">FFN layer</a>, with some recent MoE models even more aggressively expanding the FFN dimensions. (95%+ of parameters in case of <a href="https://arxiv.org/pdf/2508.10925">OpenAI OSS models</a>).</p> <table> <thead> <tr> <th>Component</th> <th>Complexity</th> <th>Parameter Share</th> </tr> </thead> <tbody> <tr> <td>Attention</td> <td>(O(n^2 d))</td> <td>~30 %</td> </tr> <tr> <td>FFN (MLP)</td> <td>(O(n d^2))</td> <td>~70 %</td> </tr> </tbody> </table> <p>Parameter share numbers are estimates if we use 4x dimension expansion in FFN layers with standard attention.</p> <p>Furthermore, works have focused on the calculation of the attention weights, which is \((O(n^2 \cdot d))\). However, the feed-forward layers also have complexity of \((O(n \cdot d^2))\), Since in the earlier days of transformers, \(d\) was often larger than \(n\), the feed-forward layers were more computationally expensive on simple tasks.</p> <h3 id="about-positional-embeddings">About Positional Embeddings</h3> <p>The paper introduced sinusodial positional embedding. That is,</p> \[\text{PE}_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right)\] \[\text{PE}_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)\] <blockquote> <p>where pos is the position and i is the dimension. That is, each dimension of the positional encoding corresponds to a sinusoid. The wavelengths form a geometric progression from 2π to 10000 · 2π. We chose this function because we hypothesized it would allow the model to easily learn to attend by relative positions, since for any fixed offset k, \(PE_{pos+k}\) can be represented as a linear function of \(PE_{pos}\)</p> </blockquote> <p>So what does this mean? Simply, put, we can design a rotational matrix \(\mathbf{R}^k\) such that</p> \[\mathbf{R}^k \cdot PE_{pos} = PE_{pos+k}\] <p>Such rotational matrix can be created by the following block diagonal matrix.</p> \[R_i^k = \begin{pmatrix}\cos k / 10000^{2i/ d_{model}} &amp; -\sin k / 10000^{2i/ d_{model}}\\ \sin k / 10000^{2i/ d_{model}}&amp; \cos k / 10000^{2i/ d_{model}}\end{pmatrix}\] \[\mathbf{R}^k = \begin{pmatrix} R_0 &amp; &amp; &amp; \\ &amp; R_1 &amp; &amp; \\ &amp; &amp; \ddots &amp; \\ &amp; &amp; &amp; R_{d_{model}/2} \end{pmatrix}.\] <p>This also means position embedding difference between two tokens are determined by its relative distance.</p> <p>I personally find this formulation elegant but slightly lacking. We can express this as:</p> \[\mathbf{R}^k \times PE_{pos} + emb = PE_{pos + k} + emb\] <p>However, the attention mechanism is not additive. It uses dot product attention, and capturing additive positional difference seems unaligned to me. I would consider the following to be more intutive.</p> \[\mathbf{F}(P(x,pos), k) = P(x, pos + k)\] <p>\(P(x, pos)\) here is a function that encodes the position \(pos\) and token embedding \(x\) together. A position encoding should have some function \(\mathbf{F}\) that allows shifting the position by \(k\) when applied. This too, however, is counte to the linear <a href="https://transformer-circuits.pub/2021/framework/index.html">additive nature</a> of the transformers. Thus one might want a position embedding that keeps the latent representation additive while making attention mechanism rotational. Rotary Position Embeddings <a href="https://arxiv.org/pdf/2104.09864">(RoPE)</a> satisfy this property.</p> <p>The paper also notes that replacing the sinusoidal embedding with a <strong>learnable</strong> one barely affects performance.</p> <h3 id="simple-experiments">Simple Experiments</h3> <ol> <li>Sinusodial Embedding vs Learnable.</li> <li>Additive (Bahdanau-style) vs Dot product attention.</li> </ol> <p>For the base transformer implementation, I used the Andrej Karpathy’s <a href="https://www.youtube.com/watch?v=kCc8FmEb1nY">minGPT</a> and evaluated based on the perplexity of the Shakespeare dataset. The code can be found <a href="https://github.com/blindTissue/additive-attention-experiment">here</a>.</p> <p>There was a minor difficulty in implementing the Bahdanau Attention. The original attention mechanism in the paper (Which connects encoder with decoder) uses the following</p> \[\alpha_{ij} = \frac{\exp(e_{ij})}{\sum_{k=1}^{T_x} \exp(e_{ik})} \quad \\ e_{ij} = v_a^T \tanh(W_a s_{i - 1} + U_a h_j)\] <p>where \(v_a \in \mathbb{R}^n\), \(W_a \in \mathbb{R}^{n \times m}\), \(U_a \in \mathbb{R}^{n \times 2m}\). More detail can be found in the <a href="https://arxiv.org/pdf/1409.0473">original paper</a>.</p> <p>To fit this implementation to Transformers, I modified the equation a bit.</p> \[e_{ij} = vx_i\tanh(W_q x_i + W_k x_j)\] <p>Let head size be denoted \(h\). Then, we have \(v \in \mathbb{R}^{h}\), \(W_q \in \mathbb{R}^{h \times h}\), \(W_k \in \mathbb{R}^{h \times h}\).</p> <p>Notice that \(v\) here is a 1d vector instead of a matrix as in transformer. This was a design choice to be closer to the original Bahdanau attention. The \(q,k\) vectors are same as the original transformer.</p> <p>But this approach takes up too much memory in parallel computation. We need to materialize matrix of (B, T, T, h) tensor to parallelize adding the \(q,k\) vectors. So I used an alternative implementation.</p> \[e_{ij} = v(\tanh(W_q x_i)) + v(\tanh(W_k x_j))\] <p>This way, we do not need to materialize the full (B, T, T, h) tensor. The two implemtations are not equivalent, but I think this is a reasonable approximation.</p> <h3 id="results">Results</h3> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/attention_is_all_you_need/results-480.webp 480w,/assets/img/paper_review/attention_is_all_you_need/results-800.webp 800w,/assets/img/paper_review/attention_is_all_you_need/results-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/attention_is_all_you_need/results.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>All models produced similar perplexity curves — confirming that both learnable vs. sinusoidal embeddings and additive vs. dot-product attention behave comparably on small datasets.</p> <h3 id="takeaways">Takeaways</h3> <ul> <li><strong>Attention wasn’t new</strong> — the innovation was using attention-based blocks.</li> <li><strong>FFN layers dominate</strong> parameters, and sometimes, compute — attention is important, but not the whole story.</li> <li><strong>Sinusoidal embeddings</strong> are fixed position embedding which allow relative position encoding via rotation.</li> </ul> <h2 id="chain-of-thought-prompting-elicits-reasoning-in-large-language-models-wei-et-al-2022">Chain of Thought Prompting Elicits Reasoning in Large Language Models (Wei et al., 2022)</h2> <p>Written on Nov 1, 2025</p> <p>This paper is widely regarded as the beginning of the “reasoning” era in large language models. <strong>Chain-of-Thought (CoT)</strong> prompting demonstrated that simply adding step-by-step reasoning in prompts dramatically improves performance on arithmetic, commonsense, and symbolic tasks. CoT directly inspired later frameworks such as <a href="https://arxiv.org/pdf/2210.03629">ReAct</a> (reasoning + acting) and motivated the development of models explicitly trained on reasoning traces.<br/> Building on this, researchers began using reinforcement learning on reasoning processes to create “thinking” or “reasoning” models—systems that learn to generate intermediate reasoning steps before producing a final answer. OpenAI’s <a href="https://openai.com/index/learning-to-reason-with-llms/">o1</a> was one of the first marketed LLMs to incorporate these ideas.</p> <h3 id="core-idea">Core Idea</h3> <p>Prompting LLMs with few-shot step-by-step reasoning examples significantly improves performance on complex reasoning tasks. The authors showed that this works on arithmetic, commonsense, and symbolic reasoning benchmarks.</p> <p>This method worked better than standard few-shot prompting, especially as the model size increased. Models with size less than 10B often degraded in performance with CoT while larger models such as GPT-3 and PaLM 540B showed improved performance.</p> <p>The authors hypothesize that the ability to reason might be an emergent behavior with model size.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/chain_of_thought/chain_of_thought_results-480.webp 480w,/assets/img/paper_review/chain_of_thought/chain_of_thought_results-800.webp 800w,/assets/img/paper_review/chain_of_thought/chain_of_thought_results-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/chain_of_thought/chain_of_thought_results.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h3 id="additional-tests">Additional Tests</h3> <p>In order to see which aspect of the CoT prompting helped, the authors designed several prompt variants.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/chain_of_thought/different_methods-480.webp 480w,/assets/img/paper_review/chain_of_thought/different_methods-800.webp 800w,/assets/img/paper_review/chain_of_thought/different_methods-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/chain_of_thought/different_methods.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>For GSM8k, they provided models with</p> <ol> <li>Standard Prompting (few-shot)</li> <li>Equations only, no reasoning steps.</li> <li>Variable compute only, where the llm was given sequence of dots (…) equal to the original CoT.</li> <li>Chain of Thought After the Answer.</li> <li>Chain of Thought Before the Answer (original CoT).</li> </ol> <p>The results show that only the original CoT prompting significantly improves performance. This suggests that the step-by-step reasoning process itself is crucial.</p> <h3 id="personal-thoughts">Personal Thoughts</h3> <p>I wonder if their claim about model size and emergence still holds true in 2025. We have different training pipelines: training data, model architecture and SFT -&gt; RL is different from 2020. One major problem is that the comparison is not an apples to apples comparison, as recent training data often includes the reasoning rollout. I do think that retesting the claim with curated data, MoE model would be interesting.</p> <p>Since CoT and its variants are popular methods for prompting, I assumed that some organization would have kept a record of the results. With this, I would have been able to compare the accuracy increase claims on newer models. I couldn’t find it, and decided to test on smaller models.</p> <p>After being unhappy on my own CoT prompts and answer extractor, I used ElutherAI’s <a href="https://github.com/EleutherAI/lm-evaluation-harness">LM Evaluation Harness</a> to prompt and evalute LLM responses from GSM8k. As a side note, I think the uncentralized evaluation for LLMs is quite problematic: Some of the small improvements can be due to a more broad regex extractor, or from the randomness of the evaluative LLMs.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/chain_of_thought/prompting_diff-480.webp 480w,/assets/img/paper_review/chain_of_thought/prompting_diff-800.webp 800w,/assets/img/paper_review/chain_of_thought/prompting_diff-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/chain_of_thought/prompting_diff.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>The 0-shot Chain of Thought version here isn’t the popular <a href="https://arxiv.org/pdf/2205.11916">Zero shot CoT</a> (Often referred as just CoT in some context) but prompt with zero examplars. The left bars could be seen as standard prompting with 0-shot standard and Chain-of-Thought prompting can be seen as directly asking the question without any context. (The difference between the two should be the variance).</p> <p>We see that CoT prompting hurts performance for Llama 3.2 1B model, while slightly improving Llama 3.2 3B and Qwen3 4B model. Furthermore, we see that standard prompting outperform the CoT version when prompted with the Llama 3.2 1B and Qwen3 4B models. The model performance with and without CoT is model dependent in this experiment, and probably is dataset dependent.</p> <h3 id="closing-remarks">Closing Remarks</h3> <p>Chain of Thought had a great impact on LLMs, affecting the prompting strategies and fine tuning pipelines. This can be directly seen in LLMs. Earlier tended to output only the final answer for mathematical and reasoning tasks. Now, even small models tend to explain their steps and output the final answers.</p> <p>This is an instance where human understanding of the world have improved ML model. Prompting and training models on how humans think resulted in a better model performance, counter to the spirit of <a href="http://www.incompleteideas.net/IncIdeas/BitterLesson.html">bitter lesson</a>, which argues that progress in AI comes from building system that learn on that own, not from encoding what we know.</p> <p>Maybe Chain of Thought style training will hit a wall in the near future. Of course, there are alternatives such as teaching the models to <a href="https://arxiv.org/pdf/2412.06769">reason in latent space</a>.</p> <h2 id="roformer-enhanced-transformer-with-rotary-position-embedding"><a href="https://arxiv.org/abs/2104.09864">ROFORMER: ENHANCED TRANSFORMER WITH ROTARY POSITION EMBEDDING</a></h2> <p>Written in Nov 21, 2025</p> <p>Rotary Positional Embedding (RoPE) is practically used in all modern LLMs. Llama, Qwen, and even the recent <a href="https://www.datocms-assets.com/64837/1763662397-1763646865-olmo_3_technical_report-1.pdf">Olmo3</a> all use it. This paper introduced this embedding.</p> <h3 id="what-is-rope">What is RoPE?</h3> <p>Simply put, instead of adding a sinusodial position embedding to the \(x\) vector, as done in the original transformer paper, The rotation is <em>applied</em> to the \(q,k\) vectors.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/roformer/RoPE_banner-480.webp 480w,/assets/img/paper_review/roformer/RoPE_banner-800.webp 800w,/assets/img/paper_review/roformer/RoPE_banner-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/roformer/RoPE_banner.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>This creates some interesting properties.</p> <h3 id="theoretical-explanation-of-rope">Theoretical Explanation of RoPE</h3> <p>Section 3.4 explains the validity of the RoPE. It requires the unnormalized attention values \(q_m k_n\) to</p> <ol> <li>Be same if the distance (\(n-m\)) are the same.</li> <li>Be the original values of \(q,k\) if \(m, n\) are zero.</li> </ol> <p>The paper shows that their formulation of Rotary Position embedding satisfies this property.</p> <p>They also show an upper bound for this value to be \(\left( \max_i \lvert h_{i+1} - h_i \rvert \right) \sum_{i=0}^{\frac{d}{2}-1} \lvert S_{i+1} \rvert\), showing that the dot product reduces with more distance.</p> <h3 id="papers-performance-results-of-rope">Paper’s Performance Results of RoPE</h3> <p>The paper shows effectiveness of RoPE by comparing its performance with sinnusodial position embedding in the original attention paper. The result is as follows.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/roformer/loss-480.webp 480w,/assets/img/paper_review/roformer/loss-800.webp 800w,/assets/img/paper_review/roformer/loss-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/roformer/loss.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>The right graph compares RoFormer to a standard BERT baselline (using learned absolute embedding). The graph on the left uses <em>PerFormer</em>, a linear attention.</p> <p>This result is the one that surprised me the most when reading this paper. I expected the loss to be more drastic, as RoPE is currently used in virtually all of Transformers. The results show a measly increase in performance, an increase where in some eyes, might be just because of luck instead of actual improvement. Same can be said for result on downstream tasks, where learned positional embedding perform better than RoFormer (Their postional embedded transformer) in some tasks.</p> <h3 id="additional-experiment">Additional Experiment.</h3> <p>The paper argues that their method have a long distance decay. They argue that by proving that the expression is bounded above by \(\left( \max_i \lvert h_{i+1} - h_i \rvert \right) \sum_{i=0}^{\frac{d}{2}-1} \lvert S_{i+1} \rvert\). However, their graph only lasts up to around position ~260. What happens for longer sequencs? Does the relative upper bound go to zero, or does it stay stable? In some chance, the upper bound can increase after some time.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/paper_review/roformer/continued_upper-480.webp 480w,/assets/img/paper_review/roformer/continued_upper-800.webp 800w,/assets/img/paper_review/roformer/continued_upper-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/paper_review/roformer/continued_upper.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>The graph suggest that the upper bounda stays stable (for d = 64) from 2 to 6 for 2000+ positions.</p> <h3 id="personal-thoughts-1">Personal Thoughts</h3> <p>When I decided to read this paper, I was expecting a monumental increase in performance, or a quality, such as decreased performance degradation over time, for Rotary Position Embedding. In fact, there should be a reason behind why all transformers use RoPE. However, this wasn’t the case. The results showed that their embedding is extrapolatable to longer distance, but didn’t do a short context learning -&gt; long context training, or show a drastic increase in performance compared to previous embedding mechanism.</p>]]></content><author><name></name></author><category term="machine-learning"/><summary type="html"><![CDATA[A review of influential old papers in natural language processing.]]></summary></entry><entry><title type="html">Showcase of EMNLP Sycophancy Papers</title><link href="https://sungwon-kim.com/blog/2025/EMNLP-sycophancy-papers/" rel="alternate" type="text/html" title="Showcase of EMNLP Sycophancy Papers"/><published>2025-11-07T00:00:00+00:00</published><updated>2025-11-07T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/EMNLP-sycophancy-papers</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/EMNLP-sycophancy-papers/"><![CDATA[<p>The EMNLP 2025 papers are <a href="https://aclanthology.org/events/emnlp-2025/">out</a>! Congrats to everyone who had their papers accepted.</p> <p>As an accepted author myself, I decided to explore other papers that examined my paper’s topic: <strong>LLM Sycophancy</strong>. Below are a few bullet points for each paper.</p> <h2 id="self-augmented-preference-alignment-for-sycophancy-reduction-in-llms"><a href="https://aclanthology.org/2025.emnlp-main.625">Self-Augmented Preference Alignment for Sycophancy Reduction in LLMs</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/SAA_dataset-480.webp 480w,/assets/img/emnlp_sycophancy/SAA_dataset-800.webp 800w,/assets/img/emnlp_sycophancy/SAA_dataset-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/SAA_dataset.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>Created <strong>SAA</strong> dataset, which contains queries with user pressumption, correct answer, and incorrect alternatives. <ul> <li>The pressumptions are sometimes correct and sometimes incorrect.</li> </ul> </li> <li>Trained LLM with SAA using Supervised Fine Tuning and Proximal Policy Optimization. Results show that in both scenarios, the rate of sycophancy decreased.</li> <li>I wonder if model trained with SAA works with out of domain pressumptions. the examples in the dataset are simple: they are single turn and the prompts pertain to simple questions.</li> </ul> <h2 id="advancing-oversight-reasoning-across-languages-for-audit-sycophantic-behaviour-via-x-agent"><a href="https://aclanthology.org/2025.emnlp-main.654/">Advancing Oversight Reasoning across Languages for Audit Sycophantic Behaviour via X-Agent</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/x_agent-480.webp 480w,/assets/img/emnlp_sycophancy/x_agent-800.webp 800w,/assets/img/emnlp_sycophancy/x_agent-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/x_agent.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>Created a framework called <strong>X-Agent</strong>, an agent that checks for sycophancy in LLM responses.</li> <li>Their <strong>X-Agent</strong> successfully detects sycophantic responses, <ul> <li>Defined sycophancy as when LLM accepts incorrect user presupposition.</li> <li>Overall accuracy with X-Agent increased in most LLMs.</li> </ul> </li> <li>The models fine tuned on the X-Agent rollout also improved performance for both accuracy and sycophancy</li> </ul> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/x_agent_results-480.webp 480w,/assets/img/emnlp_sycophancy/x_agent_results-800.webp 800w,/assets/img/emnlp_sycophancy/x_agent_results-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/x_agent_results.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h2 id="sycophancy-mitigation-through-reinforcement-learning-with-uncertainty-aware-adaptive-reasoning-trajectories"><a href="https://aclanthology.org/2025.emnlp-main.661.pdf">Sycophancy Mitigation Through Reinforcement Learning with Uncertainty-Aware Adaptive Reasoning Trajectories</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/smart_framework-480.webp 480w,/assets/img/emnlp_sycophancy/smart_framework-800.webp 800w,/assets/img/emnlp_sycophancy/smart_framework-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/smart_framework.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>Created a <strong>SMART</strong> (Sycophancy Mitigate Through Adaptive Reasoning Trajectories) Framework <ul> <li>Smart uses UA-MCTS(Uncertainty-Aware Adaptive Monte Carlo Tree Search) to generate high quality reaoning trajectory</li> <li>In each reasoning step, future rollouts are created and scored based on the improvement of answer correctness probability compared to the last step.</li> </ul> </li> <li>The rollouts with scores are then used to train Large Language Models. <ul> <li>The implementation outperforms other approaches.</li> <li>Dense Reinforcement Learning on the rollouts outperforms SFT.</li> </ul> </li> </ul> <h2 id="pointing-to-a-llama-and-call-it-a-camel-on-the-sycophancy-of-multimodal-large-language-models"><a href="https://aclanthology.org/2025.emnlp-main.1020.pdf">Pointing to a Llama and Call it a Camel On the Sycophancy of Multimodal Large Language Models</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/SRT_framework-480.webp 480w,/assets/img/emnlp_sycophancy/SRT_framework-800.webp 800w,/assets/img/emnlp_sycophancy/SRT_framework-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/SRT_framework.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>Multimodal Large Language Models are more susceptible to Sycophancy compared to Large Language Models.</li> <li>Created a pipeline called <strong>SRT</strong> (Sycophancy Reflective Tuning). <ul> <li>This pipeline consists of image textualization -&gt; reflection -&gt; Summarization.</li> </ul> </li> <li>Fine tuning on the SRT rollout resulted in better overall score compared to original prompting. <ul> <li>Correction Rate (original answer incorrect, user suggestion correct) was reduced slightly, but Sycophancy Rate (original answer correct, user suggestion incorrect) increased considerably.</li> <li>Removing reasoning in SRT results in low Correction Rate.</li> </ul> </li> </ul> <h2 id="measuring-sycophancy-of-language-models-in-multi-turn-dialogues"><a href="https://aclanthology.org/2025.findings-emnlp.121.pdf">Measuring Sycophancy of Language Models in Multi-turn Dialogues</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/sycon_bench-480.webp 480w,/assets/img/emnlp_sycophancy/sycon_bench-800.webp 800w,/assets/img/emnlp_sycophancy/sycon_bench-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/sycon_bench.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>Authors proposed a benchmark called <strong>SYCON-BENCH</strong>, a benchmark for evaluating sycophantic behavior in multi-turn, free-form conversational settings</li> <li>Alignment Tuning increases sycophancy</li> <li>Model scaling reduces sycophancy</li> <li>Reasoning Models are More Robust</li> <li>Prompting in third person, creating a <em>Non-sycophantic</em> prompt can mitigate sycophancy</li> <li>Models can identify sycophancy when asked to, but are often sycophantic. This shows that sycophancy is not driven by ignorance.</li> </ul> <h2 id="challenging-the-evaluator-llm-sycophancy-under-user-rebuttal"><a href="https://aclanthology.org/2025.findings-emnlp.1222.pdf">Challenging the Evaluator: LLM Sycophancy Under User Rebuttal</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/challenge_evaluate-480.webp 480w,/assets/img/emnlp_sycophancy/challenge_evaluate-800.webp 800w,/assets/img/emnlp_sycophancy/challenge_evaluate-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/challenge_evaluate.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>Authors explored the discrepancy: LLMs are good as evaluative agents, but are sycophantic in follow-up questions. Since two actions are similar: choosing the better of the two different argument, what prompts an LLM to have this difference</li> <li>Found that conversational framework makes LLM more sycophantic</li> <li>More reasoning results in better persuasion, even if the reasoning is incorrect</li> <li>LLMs are more likely to be persuaded by casual prompts than by reasoned prompts.</li> <li>While casual prompt results in the best persuasion rate, the best correction rate is given when LLM is prompted as a judge.</li> </ul> <h2 id="echoes-of-agreement-argument-driven-sycophancy-in-large-language-models"><a href="https://aclanthology.org/2025.findings-emnlp.1241.pdf">Echoes of Agreement: Argument Driven Sycophancy in Large Language Models</a></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/emnlp_sycophancy/echos_agreement-480.webp 480w,/assets/img/emnlp_sycophancy/echos_agreement-800.webp 800w,/assets/img/emnlp_sycophancy/echos_agreement-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/emnlp_sycophancy/echos_agreement.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <ul> <li>LLMs are likely to echo user’s presented stance</li> <li>LLMs tend to agree more when supporting arguments were provided and disagree more when refuting arguments were provided.</li> <li>LLMs are likely to change their stance both in single and multi turn settings.</li> <li>LLMs are more likely to change their stance for arguments with “better” strength.</li> </ul>]]></content><author><name></name></author><category term="LLM"/><summary type="html"><![CDATA[The EMNLP 2025 papers are out! Congrats to everyone who had their papers accepted.]]></summary></entry><entry><title type="html">Torch MPS Basic Speedup Test</title><link href="https://sungwon-kim.com/blog/2025/torch-mps-speedup-test/" rel="alternate" type="text/html" title="Torch MPS Basic Speedup Test"/><published>2025-10-22T00:00:00+00:00</published><updated>2025-10-22T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/torch-mps-speedup-test</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/torch-mps-speedup-test/"><![CDATA[<h1 id="pytorch-mps-performance-tips">PyTorch MPS Performance Tips</h1> <p>Conducted quick experiments to find what actually speeds up PyTorch code on Apple Silicon (MPS).</p> <p>Based on <a href="https://www.youtube.com/watch?v=IAEysH-6VvM">this video</a>.</p> <h2 id="tldr">tl;dr</h2> <p>Keep tensors on GPU from the start, vectorize everything, avoid dynamic shapes. Code can be found <a href="https://github.com/blindTissue/torch_mps_speedup_test">here</a>.</p> <h2 id="whats-tested">What’s tested</h2> <ul> <li><strong>Creating tensors</strong>: CPU to GPU vs creating directly on GPU</li> <li><strong>Runtime constants</strong>: Dynamic tensor creation vs <code class="language-plaintext highlighter-rouge">register_buffer</code></li> <li><strong>Masking</strong>: Different masking approaches (indexing vs vectorized ops)</li> <li><strong>DataLoader</strong>: <code class="language-plaintext highlighter-rouge">pin_memory</code> and <code class="language-plaintext highlighter-rouge">non_blocking</code> flags</li> </ul> <h2 id="quick-findings">Quick findings</h2> <ol> <li> <p><strong>Create tensors directly on device</strong> - Up to 80x speedup for larger tensors for creation and basic ops.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Slow
</span><span class="n">tensor</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">randn</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">).</span><span class="nf">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>

<span class="c1"># Fast
</span><span class="n">tensor</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">randn</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">device</span><span class="o">=</span><span class="n">device</span><span class="p">)</span>
</code></pre></div> </div> </li> <li> <p><strong>Use <code class="language-plaintext highlighter-rouge">register_buffer</code> for constants</strong> - Up to 8x speedup.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">self</span><span class="p">.</span><span class="nf">register_buffer</span><span class="p">(</span><span class="sh">'</span><span class="s">constant</span><span class="sh">'</span><span class="p">,</span> <span class="n">torch</span><span class="p">.</span><span class="nf">tensor</span><span class="p">(...,</span> <span class="n">device</span><span class="o">=</span><span class="n">device</span><span class="p">))</span>
</code></pre></div> </div> </li> <li> <p><strong>Vectorize masking operations</strong> - Massive speedup (50-90x)</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Slow - creates irregular tensor
</span><span class="n">result</span> <span class="o">=</span> <span class="n">attention_map</span><span class="p">[</span><span class="n">attention_map</span> <span class="o">&gt;</span> <span class="p">.</span><span class="mi">5</span><span class="p">].</span><span class="nf">mean</span><span class="p">()</span>

<span class="c1"># Fast - stays vectorized
</span><span class="n">mask</span> <span class="o">=</span> <span class="p">(</span><span class="n">attention_map</span> <span class="o">&gt;</span> <span class="p">.</span><span class="mi">5</span><span class="p">).</span><span class="nf">float</span><span class="p">()</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">attention_map</span> <span class="o">*</span> <span class="n">mask</span><span class="p">).</span><span class="nf">sum</span><span class="p">()</span> <span class="o">/</span> <span class="n">mask</span><span class="p">.</span><span class="nf">sum</span><span class="p">().</span><span class="nf">clamp</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">1e-9</span><span class="p">)</span>
</code></pre></div> </div> </li> <li> <p><strong>Use <code class="language-plaintext highlighter-rouge">non_blocking=True</code></strong> - Small improvement (~5%)</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">to</span><span class="p">(</span><span class="n">device</span><span class="p">,</span> <span class="n">non_blocking</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div> </div> <p>Note: <code class="language-plaintext highlighter-rouge">pin_memory</code> doesn’t help on MPS (not supported yet)</p> </li> </ol>]]></content><author><name></name></author><category term="machine-learning"/><summary type="html"><![CDATA[A basic speedup test of PyTorch on MPS (Apple Silicon) vs CPU.]]></summary></entry><entry><title type="html">Random Cosine Similarity Distribution</title><link href="https://sungwon-kim.com/blog/2025/random-cosine-similarity/" rel="alternate" type="text/html" title="Random Cosine Similarity Distribution"/><published>2025-10-19T00:00:00+00:00</published><updated>2025-10-19T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/random-cosine-similarity</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/random-cosine-similarity/"><![CDATA[<h3 id="problem-with-uninterpretable-scores">Problem with Uninterpretable Scores</h3> <p>In machine learning, many scores or values are difficult to interpret in siolation. For example, take BERTScore. While we know that higher scores indicate greater similarity, the raw values lack intuitive meaning. Is the difference between 0.7 and 0.8 comparable to the difference between 0.8 and 0.9? What constitutes a “good” BERTScore?</p> <p>Furthermore, in Retrieval Augumented Generation (RAG) systems, consine similarity between the query and document embeddings are often used to determine relevant documents. However, the cosine similarity itself is not interpretable. A cosine similarity of 0.7 might seem reasonable, but how much better is 0.8? Without context, these numbers are difficult to assess.</p> <p>More fundamentally, matrix multiplication is simply a parallelized version of a dot product, which is scaled cosine similarity. I noticed that I lack the intuition of cosine similarity values in high dimensional spaces. This motivated me to investigate the distribution of cosine similarities between random vectors in a unit hypersphere.</p> <h3 id="experiment-starting-with-2d">Experiment Starting with 2D</h3> <p>I To build intuition, I started with a simple 2D unit circle. Before you read further, try to guess what the cosine similarity distribution would look like.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">math</span>
<span class="kn">import</span> <span class="n">random</span>
<span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="k">def</span> <span class="nf">get_random_point_on_circle</span><span class="p">():</span>
    <span class="n">angle</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="nf">uniform</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">math</span><span class="p">.</span><span class="n">pi</span><span class="p">)</span>
    <span class="n">x</span> <span class="o">=</span> <span class="n">math</span><span class="p">.</span><span class="nf">cos</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
    <span class="n">y</span> <span class="o">=</span> <span class="n">math</span><span class="p">.</span><span class="nf">sin</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
    <span class="nf">return </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>

<span class="n">random_points</span> <span class="o">=</span> <span class="p">[</span><span class="nf">get_random_point_on_circle</span><span class="p">()</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">)]</span>

<span class="n">first_item</span> <span class="o">=</span> <span class="n">random_points</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">cosine_similarities</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">random_points</span><span class="p">)):</span>
    <span class="n">dot_product</span> <span class="o">=</span> <span class="n">first_item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">random_points</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">first_item</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">random_points</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">cosine_similarities</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">dot_product</span><span class="p">)</span>

<span class="n">plt</span><span class="p">.</span><span class="nf">hist</span><span class="p">(</span><span class="n">cosine_similarities</span><span class="p">,</span> <span class="n">bins</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">title</span><span class="p">(</span><span class="sh">"</span><span class="s">Histogram of Cosine Similarities of Unit Vectors in 2D</span><span class="sh">"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">xlabel</span><span class="p">(</span><span class="sh">"</span><span class="s">Cosine Similarity</span><span class="sh">"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">ylabel</span><span class="p">(</span><span class="sh">"</span><span class="s">Frequency</span><span class="sh">"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
</code></pre></div></div> <p>The histogram looks like below.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/random-cosine-similarity/2d_cosine_similarities-480.webp 480w,/assets/img/random-cosine-similarity/2d_cosine_similarities-800.webp 800w,/assets/img/random-cosine-similarity/2d_cosine_similarities-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/random-cosine-similarity/2d_cosine_similarities.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>Cosine similarities are more likely to be around extreme values of -1 and 1 and less likely to be around 0! This is quite counter-intuitive. I asked three people about this and all three guessed that the distribution would be uniform.</p> <h3 id="understanding-the-2d-distribution">Understanding the 2D Distribution</h3> <p>Why does this happen? Let’s build geometric intuition.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/random-cosine-similarity/circle_line-480.webp 480w,/assets/img/random-cosine-similarity/circle_line-800.webp 800w,/assets/img/random-cosine-similarity/circle_line-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/random-cosine-similarity/circle_line.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>We can fix the first vector to \((1,0)\) without loss of generality. If we look at the first quadrant of the unit circle, we see that \(x = y\) line bisects the circumference of the quadrant. However, the cosine similarity of the midpoint is \(\frac{\sqrt{2}}{2} \approx 0.707\). Since cosine of 90 degrees is 0, and cosine of 0 degrees is 1, we expect to see the same number of points with cosine similarity between 0 and 0.707 and cosine similarity between 0.707 and 1. This explains why we see more points around extreme values.</p> <p>Another way to look at it would be to look at the slope. The slope of the circle at \((1,0)\) is perpendicular, while at \((0,1)\) it is horizontal. Thus a change in angle near \((1,0)\) results in a small change in cosine similarity, while a change in angle near \((0,1)\) results in a large change in cosine similarity.</p> <h3 id="higher-dimensions">Higher Dimensions</h3> <p>For higher dimensions, manually coding the angular rotations becomes error-prone. there exists an elegant approach which leverages the properties of the multivariate normal distribution: sample from a standard normal distribution, and then normalize. The reason is that multivariate normal distribution has spherically symmetric distribution, so normalizing the sampled vector results in a uniform distribution on the unit hypersphere.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="k">def</span> <span class="nf">random_point_on_unit_sphere</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">np</span><span class="p">.</span><span class="n">ndarray</span><span class="p">:</span>
    <span class="n">point</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">randn</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="n">point</span> <span class="o">=</span> <span class="n">point</span> <span class="o">/</span> <span class="n">np</span><span class="p">.</span><span class="n">linalg</span><span class="p">.</span><span class="nf">norm</span><span class="p">(</span><span class="n">point</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">point</span>
</code></pre></div></div> <p>Using this function, I plotted the histogram of cosine similarities in 3D, 10D, 100D, 1000D below.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/random-cosine-similarity/higher_dim_cos-480.webp 480w,/assets/img/random-cosine-similarity/higher_dim_cos-800.webp 800w,/assets/img/random-cosine-similarity/higher_dim_cos-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/random-cosine-similarity/higher_dim_cos.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>As dimension increases, the cosine similarities are more concentrated around 0. For 100D, there is no sample with cosine similarity greater than .3 among 10,000 samples.</p> <p>Another interesting observation is that in 3 dimension, the distribution looks uniform. GPT5 shows that it is indeed uniform in 3D case. In fact, it showed that the distribution of the cosine similarity of dimension \(d\) follows a Beta distribution with parameters \((\frac{d-1}{2}, \frac{d-1}{2})\) <a href="https://chatgpt.com/share/68f59365-b6c8-8009-8de4-b1cb8a46b5f5">link</a>.</p> <h3 id="implications-for-embeddings">Implications for Embeddings</h3> <p>Text embedding models often assign cosine similarities above 0.7 to similar documents. In high-dimensional spaces (common embeddings use 384, 768, or 1536 dimensions), such high cosine similarities are extremely rare between random vectors. This means that semantically similar texts must have embeddings that are highly aligned in the embedding space, far from the random baseline.</p>]]></content><author><name></name></author><category term="Random"/><summary type="html"><![CDATA[What is the distribution of cosine similarities between random vectors in a unit hypersphere?]]></summary></entry><entry><title type="html">Simple Attention Visualizer</title><link href="https://sungwon-kim.com/blog/2025/attention-visualizer/" rel="alternate" type="text/html" title="Simple Attention Visualizer"/><published>2025-10-14T00:00:00+00:00</published><updated>2025-10-14T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/attention-visualizer</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/attention-visualizer/"><![CDATA[<h2 id="description">Description</h2> <p>I have created a simple attention visualizer for transformer models. It is available at <a href="https://github.com/blindTissue/attention_visualization">this link</a>. It can</p> <ol> <li>Visualize all attention heads for a specific layer</li> <li>Show average attention for each layer.</li> <li>Single heatmap averaging all layers and heads.</li> </ol> <p>The code should work for any causal LLMs.</p> <p>More Details in the Repository.</p> <h2 id="visualization-examples">Visualization Examples</h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/attention_visualizer/all_attn-480.webp 480w,/assets/img/attention_visualizer/all_attn-800.webp 800w,/assets/img/attention_visualizer/all_attn-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/attention_visualizer/all_attn.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/attention_visualizer/layerwise_attn-480.webp 480w,/assets/img/attention_visualizer/layerwise_attn-800.webp 800w,/assets/img/attention_visualizer/layerwise_attn-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/attention_visualizer/layerwise_attn.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/attention_visualizer/added_by_layer-480.webp 480w,/assets/img/attention_visualizer/added_by_layer-800.webp 800w,/assets/img/attention_visualizer/added_by_layer-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/attention_visualizer/added_by_layer.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure>]]></content><author><name></name></author><category term="Random-Code"/><summary type="html"><![CDATA[Simple Attention Visualizer]]></summary></entry><entry><title type="html">Further Look into Cancer-Myth. Does the LLM Ignore False Presupposition Due to Lack of Knowledge or is it Sycophantic?</title><link href="https://sungwon-kim.com/blog/2025/knowledge-or-sycophancy/" rel="alternate" type="text/html" title="Further Look into Cancer-Myth. Does the LLM Ignore False Presupposition Due to Lack of Knowledge or is it Sycophantic?"/><published>2025-10-14T00:00:00+00:00</published><updated>2025-10-14T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/knowledge-or-sycophancy</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/knowledge-or-sycophancy/"><![CDATA[<h3 id="premise">Premise</h3> <p><a href="https://arxiv.org/pdf/2504.11373">Cancer-Myth: Evaluating Large Language Models on Patient Questions with False Presuppositions</a> showed that LLMs, when prompted with questions which have false medical presuppositions, does not readily detect these presuppositions. There are there are two possible points of failure.</p> <ol> <li>LLM does not know about the false medical presupposition.</li> <li>LLM blindly follows user input, sign of sycophancy.</li> </ol> <p>Problem (1) would indicate that LLM’s lack medical knowledge, while problem (2) would suggest sycophancy.</p> <h3 id="dataset">Dataset</h3> <p>The Cancer-Myth paper includes a public <a href="https://huggingface.co/datasets/Cancer-Myth/Cancer-Myth">dataset</a>. Sampled 20 first questions for this experiment. Each items in the dataset includes question, incorrect presuppostions, and other informations.</p> <h3 id="experiment">Experiment</h3> <p>Implemented a basic presupposition checker. Made LLMs</p> <ol> <li>Deconstruct user query into presumptions</li> <li>Check the factuality of each presumptions</li> <li>Condense the report to ones that are shown false</li> <li>Compare with the ground truth information</li> </ol> <p>Tested with Anthropic’s Claude 4 Sonnet, and 3.5 Haiku models.</p> <h3 id="results">Results</h3> <p>On the first 20 questions, Both LLMs almost always found the correct response. Using the scoring rubric of [-1, 0, 1] (Provided in the paper), we have</p> <table> <thead> <tr> <th>Model</th> <th>+1</th> <th>0</th> <th>-1</th> </tr> </thead> <tbody> <tr> <td>Claude 3.5 Haiku</td> <td>20</td> <td>0</td> <td>0</td> </tr> <tr> <td>Claude 4</td> <td>17</td> <td>2</td> <td>1</td> </tr> </tbody> </table> <h3 id="limitations">Limitations</h3> <ul> <li>Tested on only 20 questions.</li> <li>Didn’t compare the result to simple LLM QA (Might be the case that the chosen 20 questions were easy)</li> <li>Deconstructing and validating might be too over the board. <ul> <li>Simple QA of prompting LLM to detect incorrect presummptions might have been good enough.</li> </ul> </li> <li>evaluator, deconstructor, and presumption checker all used same model. Would have been better if different LLM were used for evaluator.</li> <li>Only tested on Anthropic’s Claude models. Would be interesting to see how other models behave.</li> </ul> <p><a href="https://github.com/blindTissue/presumption_checker">github link</a></p>]]></content><author><name></name></author><category term="Random"/><summary type="html"><![CDATA[Does the AI model know the incorrect answer or is it ignoring to answer the question?]]></summary></entry><entry><title type="html">Random Idea Exploration 01</title><link href="https://sungwon-kim.com/blog/2025/random-exploration-01/" rel="alternate" type="text/html" title="Random Idea Exploration 01"/><published>2025-01-22T00:00:00+00:00</published><updated>2025-01-22T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2025/random-exploration-01</id><content type="html" xml:base="https://sungwon-kim.com/blog/2025/random-exploration-01/"><![CDATA[<p>Do you ever have some idea that pops up in your head when you are doing something else? I have them from time to time. I am writing on some of those ideas that require more than a simple google search to answer.</p> <h3 id="how-many-phones-can-pyramid-of-giza-charge">How many phones can pyramid of Giza charge?</h3> <p>Today in class, as a ice breaker, we were to discuss the “best” invention in human history. The main point, I guess, other than a quick ice breaker, was to get us to think of the metric or a standard for “best”ness.</p> <p>After some discussion, I thought maybe, the metric of “best” can be described as how much movement the invention has caused. For example, the internet would have caused a lot of movement in the world as online shopping, international trade would have happened less in the counterfactual world.</p> <p>Then, the train of thought had led me to think of method to measure the movement. The easiest method (which is not really accurate in this example) would be to calculate the energy used to move item.</p> <p>The easiest calcuation of such energy would be potential energy. This made me think of potential energy of the pyramid of Giza.</p> <h4 id="calculation-of-potential-energy">Calculation of potential energy.</h4> <p>Potential energy is given by \(PE = mgh\). We would require mass, dimension of the pyramid to calculate this. From quick internet search</p> <ul> <li>Hieght: 146.7 meters</li> <li>Base: 230.6 meters</li> <li>Density (limestone): 2.7 g/cm^3</li> </ul> <p>source: <a href="https://en.wikipedia.org/wiki/Great_Pyramid_of_Giza#:~:text=The%20dimensions%20of%20the%20pyramid,6%20million%20tonnes%20in%20total.">wikipedia</a></p> <p>The height of the pyramid differ from source and time of measurement. I am using the one that is said to be the original dimension.</p> <p>Pyramid of Giza is mostly full of limestone. Thus we can use its shape to estimate the potential energy.</p> <p>For each \(h\) height, the area of the horizontal cross section would be given by \((230.6 - 230.6 \frac{h}{146.7})^2\). Thus we can calculate the potential energy by</p> \[PE = \int_0^{146.7} 2.7 \times 9.8 \times h \times (230.6 - 230.6 \frac{h}{146.7})^2 dh\] <p>Which, according to Wolfram Alpha is \(2.52 \times 10^9 kJ\).</p> <p>Now, an Iphone 16 has battery of 3561mAH with 3.85V. Thus the total energy inside the battery would be 13.7Wh. In Joules, that would be \(13.7 \times 3600 = 49320 J\). Thus the total number of phone charges would be \(2.52 \times 10^{12} / 49320 = 5.11 \times 10^7\). That is 51.1 million charges. That looks like a lot, but considering that there are around that many people in South Korea, <strong>the potential energy of the pyramid is equal to energy used to fully charge South Korean people’s phones once</strong>. That’s Kind of depressing.</p> <h3 id="what-is-the-lower-bound-of-land-required-to-feed-one-person">What is the lower bound of land required to feed one person?</h3> <p>I live around the Yangjae river in Seoul. During the summer, there is a small plot of land that is used to grow rice. (This place doubles as a ice rink in the winter.) I used to wonder how much rice grows from that plot of land.</p> <p>Furthermore, rice is known to be relatively calorie dense compared to other crops. It is more calorie dense than wheat, barley, and oats but less than potatoes. So having a rough estimate of amount of calories that can be produced from a unit of land could be used to estimate the lower bound of land required to feed one person. Since people don’t live on carbs alone, we would assume that lower bound would be higher than amount of calories that can be produced by rice.</p> <p>According to <a href="https://www.indexmundi.com/agriculture/?commodity=milled-rice&amp;graph=yield">United States Department of Agriculture</a>, the country with best milled rice yield per hectare is Australia, with 10MT / hectare. Northeast Asia, Japan, South Korea, and China, has yield of 7 MT / Hactare. For the sake of simplicity, let’s estimate using 7MT / Hactare.</p> <p>People typically consume 2000kcal per day. Also, milled rice has around <a href="https://en.wikipedia.org/wiki/White_rice">365Kcal per 100g</a>. Thus, we can see that</p> <ul> <li>1 hectare of rice creates \(7 \times 10^3 \times 3.65 \times 10^3 = 2.56 \times 10^7 kcal\)</li> <li>A Hectare of land can feed <strong>12800</strong> people per day.</li> <li>In order to sustain one person, we would need \((100000 / 12800) \simeq .78m^2\) of land.</li> </ul> <p>So in caloric perspective, we only need a little less than one square meter to feed a person per day. We need a little less than 300 square meters to feed a person annually.</p>]]></content><author><name></name></author><category term="Random"/><category term="Thoughts"/><summary type="html"><![CDATA[How many phones can Pyramid of Giza charge? How much plot of land is required to feed?]]></summary></entry><entry><title type="html">Llama Token Embedding to Model Head Experiment</title><link href="https://sungwon-kim.com/blog/2024/token-encoding-decoding/" rel="alternate" type="text/html" title="Llama Token Embedding to Model Head Experiment"/><published>2024-12-28T00:00:00+00:00</published><updated>2024-12-28T00:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2024/token-encoding-decoding</id><content type="html" xml:base="https://sungwon-kim.com/blog/2024/token-encoding-decoding/"><![CDATA[<h3 id="outline">Outline</h3> <p>With the help of <a href="https://huggingface.co/docs/transformers/index">Huggingface’s Transformers</a>, the model architecture and weights of open source models are available to the public.</p> <p>Methods on Mechanistic Interpretability by <a href="https://www.youtube.com/watch?v=UGO_Ehywuxc&amp;t=3s&amp;ab_channel=WelchLabs">Welch Labs</a> made me think of a feature in LLM that I was not aware of. The experiments done in the video presupposes that token embedder (tokens to vector translator) and the LM Head (final vector to token logits) are identical.</p> <p>I checked if this was the case for LMs as I was not familiar with this change. I thought that naturally, the LM head layer and the embedder layer were different. Turns out, it is a widely used method called <a href="https://paperswithcode.com/method/weight-tying">weight tying</a>.</p> <p>Because of this characteristic, we can “estimate” the token of the intermediate layer by multiplying intermediate layer’s output with the LM head’s weight matrix, then taking the argmax of the result. Pretty useful trick for mechanistic interpretability.</p> <p>So, I played around with this idea. The first question I had was if the LM head’s weights were L2 normalized. If the LM head’s weights are L2 normalized, then it follows that given token \(t\), and the head matrix \(W\), \(t = \text{argmax}(WW^T(t))\). If this were true, then it would give some solid baseline on future mechanistic interpretability experiments.</p> <h3 id="experiment-with-llama-32-on-l2-norm">Experiment with Llama 3.2 on L2 Norm</h3> <p>However, the LM heads are not L2 normalized for Llama 3.2 models.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tokenizer_1b</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">meta-llama/Llama-3.2-1B-Instruct</span><span class="sh">"</span><span class="p">)</span>
<span class="n">model_1b</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">meta-llama/Llama-3.2-1B-Instruct</span><span class="sh">"</span><span class="p">)</span>
<span class="n">tokenizer_3b</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">meta-llama/Llama-3.2-3B-Instruct</span><span class="sh">"</span><span class="p">)</span>
<span class="n">model_3b</span> <span class="o">=</span> <span class="n">AutoModelForCausalLM</span><span class="p">.</span><span class="nf">from_pretrained</span><span class="p">(</span><span class="sh">"</span><span class="s">meta-llama/Llama-3.2-3B-Instruct</span><span class="sh">"</span><span class="p">)</span>

<span class="n">model_1b_embedding</span> <span class="o">=</span> <span class="n">model_1b</span><span class="p">.</span><span class="n">model</span><span class="p">.</span><span class="n">embed_tokens</span>
<span class="n">lm_head_1b</span> <span class="o">=</span> <span class="n">model_1b</span><span class="p">.</span><span class="n">lm_head</span>

<span class="n">model_3b_embedding</span> <span class="o">=</span> <span class="n">model_3b</span><span class="p">.</span><span class="n">model</span><span class="p">.</span><span class="n">embed_tokens</span>
<span class="n">lm_head_3b</span> <span class="o">=</span> <span class="n">model_3b</span><span class="p">.</span><span class="n">lm_head</span>

<span class="k">def</span> <span class="nf">find_embedding_l2_info</span><span class="p">(</span><span class="n">model_embedding</span><span class="p">):</span>
    <span class="n">model_embedding_weights</span> <span class="o">=</span> <span class="n">model_embedding</span><span class="p">.</span><span class="n">weight</span>
    <span class="n">l2_norms</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">norm</span><span class="p">(</span><span class="n">model_embedding_weights</span><span class="p">,</span> <span class="n">p</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">l2_norms</span>

<span class="k">def</span> <span class="nf">find_max_min_var</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">):</span>
    <span class="n">max_val</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">max</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">)</span>
    <span class="n">min_val</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">min</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">)</span>
    <span class="n">variance</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">var</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">max_val</span><span class="p">,</span> <span class="n">min_val</span><span class="p">,</span> <span class="n">variance</span>

<span class="k">def</span> <span class="nf">find_max_min_indices</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">):</span>
    <span class="n">max_val</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">max</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">)</span>
    <span class="n">min_val</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">min</span><span class="p">(</span><span class="n">l2_norms</span><span class="p">)</span>
    <span class="n">max_indices</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="n">l2_norms</span> <span class="o">==</span> <span class="n">max_val</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nf">item</span><span class="p">()</span>
    <span class="n">min_indices</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="n">l2_norms</span> <span class="o">==</span> <span class="n">min_val</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nf">item</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">max_indices</span><span class="p">,</span> <span class="n">min_indices</span>

<span class="n">l2_norms_1b</span> <span class="o">=</span> <span class="nf">find_embedding_l2_info</span><span class="p">(</span><span class="n">model_1b_embedding</span><span class="p">)</span>
<span class="n">l2_norms_3b</span> <span class="o">=</span> <span class="nf">find_embedding_l2_info</span><span class="p">(</span><span class="n">model_3b_embedding</span><span class="p">)</span>

<span class="n">max_val_1b</span><span class="p">,</span> <span class="n">min_val_1b</span><span class="p">,</span> <span class="n">var_1b</span> <span class="o">=</span> <span class="nf">find_max_min_var</span><span class="p">(</span><span class="n">l2_norms_1b</span><span class="p">)</span>
<span class="n">max_val_3b</span><span class="p">,</span> <span class="n">min_val_3b</span><span class="p">,</span> <span class="n">var_3b</span> <span class="o">=</span> <span class="nf">find_max_min_var</span><span class="p">(</span><span class="n">l2_norms_3b</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Max value 1b: </span><span class="si">{</span><span class="n">max_val_1b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Min value 1b: </span><span class="si">{</span><span class="n">min_val_1b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Variance 1b: </span><span class="si">{</span><span class="n">var_1b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Max value 3b: </span><span class="si">{</span><span class="n">max_val_3b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Min value 3b: </span><span class="si">{</span><span class="n">min_val_3b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Variance 3b: </span><span class="si">{</span><span class="n">var_3b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="n">max_indices_1b</span><span class="p">,</span> <span class="n">min_indices_1b</span> <span class="o">=</span> <span class="nf">find_max_min_indices</span><span class="p">(</span><span class="n">l2_norms_1b</span><span class="p">)</span>
<span class="n">max_indices_3b</span><span class="p">,</span> <span class="n">min_indices_3b</span> <span class="o">=</span> <span class="nf">find_max_min_indices</span><span class="p">(</span><span class="n">l2_norms_3b</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Max indices 1b: </span><span class="si">{</span><span class="n">max_indices_1b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Min indices 1b: </span><span class="si">{</span><span class="n">min_indices_1b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Max indices 3b: </span><span class="si">{</span><span class="n">max_indices_3b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Min indices 3b: </span><span class="si">{</span><span class="n">min_indices_3b</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Max value 1b: 1.3203290700912476
Min value 1b: 0.5214595794677734
Variance 1b: 0.008283221162855625
Max value 3b: 1.4694581031799316
Min value 3b: 0.6221248507499695
Variance 3b: 0.012782144360244274
Max indices 1b: 58996
Min indices 1b: 72710
Max indices 3b: 58996
Min indices 3b: 81259
</code></pre></div></div> <p>As a fun note, the max, min index tokens are</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Max
1b: yourselves
3b: yourselves
</code></pre></div></div> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Min
1b: --&gt;
3b:


artisanlib
</code></pre></div></div> <p>Yes. The newlines and spaces are included in the tokens. Now, this means that there might be some tokens that does not map to itself after function \(\text{argmax}(WW^T())\).</p> <h3 id="does-tokens-map-to-itself">Does tokens map to itself?</h3> <p>So, I checked if the tokens map to itself.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">def</span> <span class="nf">find_mismatch_indices</span><span class="p">(</span><span class="n">tot_token_count</span><span class="p">,</span> <span class="n">model_embedding</span><span class="p">,</span> <span class="n">lm_head</span><span class="p">,</span> <span class="n">batch_size</span><span class="o">=</span><span class="mi">167</span><span class="p">):</span> <span class="c1">#set to 167 since 167 | 128256
</span>    <span class="n">original_different_indices</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">mismatched_indices</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">tqdm</span><span class="p">(</span><span class="nf">range</span><span class="p">(</span><span class="n">tot_token_count</span> <span class="o">//</span> <span class="n">batch_size</span><span class="p">)):</span>
        <span class="n">inputs</span> <span class="o">=</span> <span class="p">[</span><span class="n">j</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">batch_size</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">batch_size</span><span class="p">)]</span>
        <span class="n">inputs</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">tensor</span><span class="p">(</span><span class="n">inputs</span><span class="p">)</span>
        <span class="n">embedding_out</span> <span class="o">=</span> <span class="nf">model_embedding</span><span class="p">(</span><span class="n">inputs</span><span class="p">)</span>
        <span class="n">head_out</span> <span class="o">=</span> <span class="nf">lm_head</span><span class="p">(</span><span class="n">embedding_out</span><span class="p">)</span>
        <span class="n">returned_items</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nf">argmax</span><span class="p">(</span><span class="n">head_out</span><span class="p">,</span> <span class="n">dim</span><span class="o">=-</span><span class="mi">1</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">torch</span><span class="p">.</span><span class="nf">all</span><span class="p">(</span><span class="n">returned_items</span> <span class="o">==</span> <span class="n">inputs</span><span class="p">):</span>
            <span class="n">original_different_indices</span><span class="p">.</span><span class="nf">extend</span><span class="p">(</span><span class="n">inputs</span><span class="p">[</span><span class="n">returned_items</span> <span class="o">!=</span> <span class="n">inputs</span><span class="p">].</span><span class="nf">tolist</span><span class="p">())</span>
            <span class="n">mismatched_indices</span><span class="p">.</span><span class="nf">extend</span><span class="p">(</span><span class="n">returned_items</span><span class="p">[</span><span class="n">returned_items</span> <span class="o">!=</span> <span class="n">inputs</span><span class="p">].</span><span class="nf">tolist</span><span class="p">())</span>
    <span class="k">return</span> <span class="n">original_different_indices</span><span class="p">,</span> <span class="n">mismatched_indices</span>


<span class="n">original_different_indices_1b</span><span class="p">,</span> <span class="n">mismatched_indices_1b</span> <span class="o">=</span> <span class="nf">find_mismatch_indices</span><span class="p">(</span><span class="mi">128256</span><span class="p">,</span> <span class="n">model_1b_embedding</span><span class="p">,</span> <span class="n">lm_head_1b</span><span class="p">)</span>
<span class="n">original_different_indices_3b</span><span class="p">,</span> <span class="n">mismatched_indices_3b</span> <span class="o">=</span> <span class="nf">find_mismatch_indices</span><span class="p">(</span><span class="mi">128256</span><span class="p">,</span> <span class="n">model_3b_embedding</span><span class="p">,</span> <span class="n">lm_head_3b</span><span class="p">)</span>

<span class="n">embedding_1b_correct_rate</span> <span class="o">=</span> <span class="p">(</span><span class="mi">128256</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">original_different_indices_1b</span><span class="p">))</span> <span class="o">/</span> <span class="mi">128256</span>
<span class="n">embedding_3b_correct_rate</span> <span class="o">=</span> <span class="p">(</span><span class="mi">128256</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">original_different_indices_3b</span><span class="p">))</span> <span class="o">/</span> <span class="mi">128256</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">original_different_indices_1b</span><span class="p">))</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">original_different_indices_3b</span><span class="p">))</span>

<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">1b embedding correct rate: </span><span class="si">{</span><span class="n">embedding_1b_correct_rate</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">3b embedding correct rate: </span><span class="si">{</span><span class="n">embedding_3b_correct_rate</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>614
602
1b embedding correct rate: 0.9952126996007984
3b embedding correct rate: 0.9953062624750499
</code></pre></div></div> <p>Less than .5 percent of the tokens does not map to itself. Then, which tokens does the unmatched tokens map to?</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create_count_dict</span><span class="p">(</span><span class="n">mismatched_indices</span><span class="p">):</span>
    <span class="n">count_dict</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">mismatched_indices</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">count_dict</span><span class="p">:</span>
            <span class="n">count_dict</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">count_dict</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">count_dict</span>

<span class="n">count_dict_1b</span> <span class="o">=</span> <span class="nf">create_count_dict</span><span class="p">(</span><span class="n">mismatched_indices_1b</span><span class="p">)</span>
<span class="n">count_dict_3b</span> <span class="o">=</span> <span class="nf">create_count_dict</span><span class="p">(</span><span class="n">mismatched_indices_3b</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="n">count_dict_1b</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">count_dict_3b</span><span class="p">)</span>
</code></pre></div></div> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{122456: 477, 528: 1, 738: 1 ...}
{66325: 482, 528: 1, 738: 1 ...}
</code></pre></div></div> <p>Surprisingly, the majority of the mismatched tokens map to the same token. In the case of 1b, 477 tokens map to token 122456 and in the case of 3b, 482 tokens map to token 66325.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Token 122456: организа
Token 66325: ♪
</code></pre></div></div> <p>When I manually skimmed through the tokens mapped to 122456 and 66325, most of them were special tokens programming words, non-english words. Nevertheless, I found the result interesting. Why do the majority of the unmaatched tokens map to the same token?</p> <p>If you have any ideas, please let me know. Thanks for reading!</p> <p>Code can be found <a href="https://github.com/blindTissue/embedding_matrix_experiment">https://github.com/blindTissue/embedding_matrix_experiment</a></p>]]></content><author><name></name></author><category term="Machine-Learning"/><summary type="html"><![CDATA[Does Same Token Encoded by embeder and then decoded by LM Head Yield Same Result?]]></summary></entry><entry><title type="html">Louvain Clustering</title><link href="https://sungwon-kim.com/blog/2024/lovain-clustering/" rel="alternate" type="text/html" title="Louvain Clustering"/><published>2024-06-03T14:00:00+00:00</published><updated>2024-06-03T14:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2024/lovain-clustering</id><content type="html" xml:base="https://sungwon-kim.com/blog/2024/lovain-clustering/"><![CDATA[<p>Louvain and Leiden methods are popular for gene clustering. In this post, I will explain the Louvain method.</p> <p>The Louvain method can be broken into two phases:</p> <ol> <li><strong>maximization of modularity</strong>: The algorithm tries to maximize the modularity of the graph by moving nodes between communities.</li> <li><strong>aggregation</strong>: The algorithm aggregates the nodes in the same community into a single node and creates a new graph.</li> </ol> <p>The two phases are repeated until no further increase in modularity is possible.</p> <h3 id="first-phase-maximization-of-modularity">First Phase: Maximization of Modularity</h3> <p>The modularity of a graph is defined as</p> \[Q = \frac{1}{2m} \sum_{i=1}^N \sum_{j=1}^N \left( A_{ij} - \frac{k_i k_j}{2m} \right) \delta(c_i, c_j)\] <p>Where</p> <ul> <li>\(A_{ij}\) is the weight of the edge between node \(i\) and node \(j\)</li> <li>\(k_i\) and \(k_j\) are the sum of the weights of the edges attached to nodes \(i,j\) respectively</li> <li>\(m\) is the sum of all the edge weights in the graph</li> <li>\(c_i\) and \(c_j\) are the communities to which the nodes \(i\) and \(j\) belong</li> <li>\(\delta\) is the Kronecker delta function: <ul> <li>\(\delta(x,y) = 1\) if \(c_i\) and \(c_j\) are the same cluster and 0 otherwise</li> </ul> </li> </ul> <p>Modularity lies within range \([-1/2, 1]\).</p> <p>The modularity above can be written as sum of modularity in a cluster. A modularity of a cluster can be simplified into</p> \[Q_c = \frac{\sum_{in}}{2m} - \left( \frac{\sum_{tot}}{2m} \right)^2\] <p>and the total modularity is the sum of the modularity of all clusters.</p> \[Q = \sum_{c} Q_c\] <p>Let’s start with basic example of modularity to gain some intuition.</p> <p>For a graph without any edges, the modularity would be 0.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/simple_cluster-480.webp 480w,/assets/img/simple_cluster-800.webp 800w,/assets/img/simple_cluster-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets/img/simple_cluster.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>Consider the graph above. Say that we made two clusters, \((A, B, C, D)\) and \((E, F, G, H)\). Then modularity would be</p> \[(0 - (\frac{4}{8})^2) + (0 - (\frac{4}{8})^2) = -\frac{1}{2}\] <p>If we define cluster such into \((A,E), (D, H), (B,F), (C,G)\), Then the modularity would be</p> \[4 \cdot \left(\frac{2}{8} - \left(\frac{2}{8}\right)^2\right) = \frac{3}{4}\] <p>Now, If we say that the entire graph is one big cluster, we have</p> \[\frac{1}{8}(8) - \frac{1}{8}(64 \cdot \frac{1}{8}) = 0\] <p>Finding the optimal clustering is <a href="https://kops.uni-konstanz.de/server/api/core/bitstreams/283d523f-ebf3-4f1d-9bd0-96e4b96bc4eb/content">NP-complete hard</a>. so the Louvain method uses a greedy approach. The algorithm does not guarantee the optimal clustering, but it is fast and often gives good results.</p> <p>The algorithm is as follows:</p> <ol> <li>Start with each node in its own cluster.</li> <li>For each node, calculate the change in modularity if the node is moved to a neighboring cluster.</li> <li>Move the node to the cluster that gives the maximum increase in modularity.</li> <li>Repeat steps 2 and 3 until no further increase in modularity is possible.</li> </ol> <p>After the algorithm ends, it is time to move to the second step.</p> <h3 id="second-phase-aggregation">Second Phase: Aggregation</h3> <p>In the second phase, the algorithm aggregates the nodes in the same community into a single node and creates a new graph. The edges between the communities are the sum of the weights of the edges between the nodes in the communities.</p> <h3 id="code">Code</h3> <p>What better way to understand the algorithm than to implement it? I have created a simple implementation of the Louvain method in Python. You can find the code <a href="https://github.com/blindTissue/Louvain">here</a>.</p> <h3 id="souces">Souces</h3> <ul> <li><a href="https://en.wikipedia.org/wiki/Louvain_method">Louvain Method</a></li> <li><a href="https://kops.uni-konstanz.de/server/api/core/bitstreams/283d523f-ebf3-4f1d-9bd0-96e4b96bc4eb/content">On Modudularity Clustering</a></li> <li><a href="https://www.youtube.com/watch?v=0zuiLBOIcsw&amp;t=607s&amp;ab_channel=StanfordOnline">Machine Learning with Graphs Lecture 13.3</a></li> </ul>]]></content><author><name></name></author><category term="for-fun"/><summary type="html"><![CDATA[Maximize Modularity!]]></summary></entry><entry><title type="html">Induction as a Reduction</title><link href="https://sungwon-kim.com/blog/2024/induction-as-reduction-problem/" rel="alternate" type="text/html" title="Induction as a Reduction"/><published>2024-05-30T01:00:00+00:00</published><updated>2024-05-30T01:00:00+00:00</updated><id>https://sungwon-kim.com/blog/2024/induction-as-reduction-problem</id><content type="html" xml:base="https://sungwon-kim.com/blog/2024/induction-as-reduction-problem/"><![CDATA[<p>Induction is a powerful tool in mathematics. The general steps of induction are as follows:</p> <ol> <li><strong>Base case</strong>: Prove that the statement is true for the smallest possible value.</li> <li><strong>Inductive step</strong>: Assume that the statement is true for some value, and prove that the statement is true for the next value.</li> <li><strong>Conclusion</strong>: The statement is true for all values.</li> </ol> <p>A simple example is to prove that</p> \[\sum_{i=1}^{n} i = \frac{n(n+1)}{2}\] <ol> <li><strong>Base case</strong>: For \(n=1\), \(\sum_{i=1}^{1} i = 1 = \frac{1(1+1)}{2}\)</li> <li><strong>Inductive step</strong>: Assume that the statement is true for some \(n=k\). Then, \(\sum_{i=1}^{k} i = \frac{k(k+1)}{2}\). We want to prove that the statement is true for \(n=k+1\).</li> <li><strong>Conclusion</strong>: The statement is true for all \(n\).</li> </ol> <p>For 2 we see that</p> \[\begin{aligned} \sum_{i=1}^k i &amp;= \frac{k(k+1)}{2} \\ (\sum_{i=1}^{k} i) + (k+1) &amp;= \frac{k(k+1)}{2} + (k+1) \\ &amp;= \frac{k(k+1) + 2(k+1)}{2} \\ &amp;= \frac{(k+1)(k+2)}{2} \\ &amp;= \sum_{i=1}^{k+1} i \end{aligned}\] <p>So, the statement is true for all \(n\).</p> <p>For induction examples shown in elementary discrete math, it is often the case that you can start from the \(n = k\) case and show that the statement is true for \(n=k+1\). However, for some problems, viewing the inductive step as a reduction problem can conceptually simplify your reasoning</p> <h3 id="general-outline-of-reduction">General Outline of Reduction</h3> <p>In general, Induction can be used to prove that all elements of set \(S\) satisfies a mathematical statement with free variable. Let \(f\) be such mathematical statement that takes an element of set \(S\) and returns a boolean value. Then, induction can be used to prove that \(f(x)\) is true for all \(x \in S\). For simplicity say that \(S_k\) is a subset of \(S\). Similary, let’s say that \(f(S_k) = 1\) if and only if \(f(x) = 1\) for all \(x \in S_k\) and 0 otherwise.</p> <p>For induction, we divide the set \(S\) into indexable subsets \(S_1, S_2, \cdots\) such that their union is \(S\). Then we prove that \(f(S_k) = 1\) for all \(k\) by the induction step above.</p> <p>In our inductive step, mentally thinking the problem as a reduction is often easier. Instead of starting from items of \(S_i\), and expand to items of \(S_{i+1}\), we should treat the problem as finding reduction from items of \(S_{i+1}\) to items of \(S_i\). This is advantageous as for justification of expanding \(S_i\) to \(S_{i+1}\), we must show that the map from \(S_{i+1} spans all elements of\)S_{i+1}$$.</p> <p>This is often hard. There are edge cases that one has to consider. Below is a simple example.</p> <h4 id="problem">Problem</h4> <p><strong>A binary tree is a rooted tree in which each node has at most two children. Using induction, show that in any binary tree the number of nodes with two children is exactly one less than the number of leaves.</strong></p> <h2 id="proof">Proof</h2> <p>For this problem, the set \(S\) is the set of all binary trees. \(f\) is statement that in \(x\), the number of nodes with two children is exactly one less than the number of leaves.</p> <p>We would split the set by depth with \(S_k\) being binary trees with depth \(k\).</p> <p>For the base case, single node tree has 1 node and 0 edges, so the statement is true.</p> <p>Now look at case of \(S_{k+1}\). we look at nodes in depth \(k+1\). If the parent of the node has two children, then deleting the children of that parent would reduce the number of nodes and edges by 2. If the parent has a single child, then deleting the child would reduce the number of nodes and edges by 1. Deleting all parents of nodes of depth \(k+1\) would reduce the number of nodes and edges by same amount, and result in a tree of depth \(k\). So if \(f(S_k) = 1\), then \(f(S_{k+1}) = 1\).</p> <p>So, the statement is true for all binary trees.</p> <hr/> <p>We have shown the inductive step by reducing the problem. If we were to do the opposite, we would need to show that our “addition” of nodes from depth \(k\) spans all trees of depth \(k+1\). Using the reduction method, we can avoid this problem.</p> <p>Reduction is a strong tool in computer science. <a href="https://en.wikipedia.org/wiki/Dynamic_programming">Dynamic Programming</a> and <a href="https://en.wikipedia.org/wiki/Turing_reduction">Turing Reduction</a> also simplifies the problem by reducing it to a problem we already know.</p>]]></content><author><name></name></author><category term="for-fun"/><category term="math"/><category term="not-rigorous"/><summary type="html"><![CDATA[Generate or Reduce?]]></summary></entry></feed>