<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Posts on apsal</title>
        <link>/posts/</link>
        <description>Recent content in Posts on apsal</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <copyright>&lt;a href=&#34;https://creativecommons.org/licenses/by-nc/4.0/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CC BY-NC 4.0&lt;/a&gt;</copyright>
        <lastBuildDate>Fri, 05 Jun 2026 00:00:00 +0000</lastBuildDate>
        <atom:link href="/posts/index.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>Privacy accounting in DP-SGD</title>
            <link>/posts/2026/06/privacy-accounting-in-dp-sgd/</link>
            <pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate>
            
            <guid>/posts/2026/06/privacy-accounting-in-dp-sgd/</guid>
            <description>I was reproducing the Likelihood Ratio Attack (LiRA) from Carlini et al.&amp;rsquo;s Membership Inference Attacks From First Principles. It was an experiment I had started a long back and Eleuther AI&amp;rsquo;s Summer of Open AI Research (SOAR) insipired me to wrap it up before the application deadline. LiRA is a membership inference attack: given a trained model and a data point, can you tell whether that point was in the training set?</description>
            <content type="html"><![CDATA[<p>I was reproducing the Likelihood Ratio Attack (LiRA) from Carlini et al.&rsquo;s <a href="https://arxiv.org/abs/2112.03570">Membership Inference Attacks From First Principles</a>. It was an experiment I had started a long back and Eleuther AI&rsquo;s <a href="https://www.eleuther.ai/soar">Summer of Open AI Research (SOAR)</a> insipired me to wrap it up before the application deadline. LiRA is a membership inference attack: given a trained model and a data point, can you tell whether that point was in the training set? The attack works by training many shadow models (copies of the target model on different random subsets of the data) and using how the per-sample loss varies across those copies to answer the question. To reproduce the paper&rsquo;s DP-SGD experiments, I ran a grid of shadow models at different noise levels using Opacus on a WSL2 VM with 7.4 GB RAM.</p>
<p>The training runs kept dying around epoch 20. After some debugging I pinned it to a single <code>privacy_engine.get_epsilon()</code> call and specifically to which privacy accountant Opacus was using under the hood. That sent me into understanding how privacy accountants actually work. Here&rsquo;s what I found.</p>
<h2 id="what-differential-privacy-means">What differential privacy means</h2>
<p>Differential privacy is a mathematical guarantee that your model&rsquo;s behavior doesn&rsquo;t change noticeably based on whether any single training example was included.</p>
<p>Two datasets are called <em>adjacent</em> if they differ by exactly one example — one has Alice&rsquo;s data, the other doesn&rsquo;t. An algorithm M is (ε, δ)-differentially private if, for any two adjacent datasets D and D&rsquo; and any set of possible outputs S:</p>
<p>$$P[M(D) \in S] \leq e^\varepsilon \cdot P[M(D&rsquo;) \in S] + \delta$$</p>
<p>ε is the privacy budget. Smaller ε means the output distributions on adjacent datasets are closer together, which means an attacker watching the model has less power to infer who was in the training set. δ is a small probability that the guarantee fails entirely — in practice something like 10⁻⁵, a technical escape hatch the definition needs rather than a real risk in well-tuned settings.</p>
<p>The guarantee is about the worst case: over all possible outputs, over all possible adjacent datasets. It doesn&rsquo;t promise any individual&rsquo;s data is safe. It says the output distributions are close enough that statistical tests have limited power, regardless of who the attacker targets.</p>
<h2 id="dp-sgd">DP-SGD</h2>
<p>DP-SGD (Abadi et al., 2016) is the standard way to train neural networks with a differential privacy guarantee. Normal SGD averages gradients over a batch and applies the update. DP-SGD changes two things.</p>
<p>Per-sample gradient clipping. Instead of averaging raw gradients, the gradient is computed separately for each training example, clipped to a maximum L2 norm C, then averaged. This bounds how much any single example can move the update.</p>
<p>Gaussian noise. After clipping, Gaussian noise calibrated to C and a noise multiplier σ is added to the averaged gradient before the weight update:</p>
<p>$$\tilde{g} = \frac{1}{B} \left[ \sum_i \text{clip}(g_i, C) \right] + \mathcal{N}!\left(0,; \frac{\sigma^2 C^2}{B^2} I\right)$$</p>
<p>Higher σ means more noise, stronger privacy, and a worse model. C controls sensitivity — how much a single gradient can shift the update.</p>
<p>Opacus also uses Poisson subsampling instead of fixed-size batches. Each training step includes each example independently with probability q = batch_size / dataset_size, rather than sampling a fixed batch. Poisson subsampling amplifies privacy (a subsampled mechanism is more private than the same mechanism on the full batch), and this gets built into the accounting.</p>
<p>Now the accounting question: after T optimizer steps with noise σ and clipping norm C, what is the actual (ε, δ)?</p>
<h2 id="the-composition-problem">The composition problem</h2>
<p>Each optimizer step is one application of the Gaussian mechanism. T steps compose T times. The naive bound, basic composition, says if each step costs ε₀ then T steps cost T·ε₀. That bound is correct but useless in practice. At any practical noise level, T·ε₀ blows past a useful privacy budget within a few epochs.</p>
<p>The better approach is to exploit the specific shape of the Gaussian mechanism&rsquo;s privacy-loss distribution. Rather than bounding the worst case at each step independently, you track that distribution across compositions. RDP and PRV both do this, but through different methods with very different memory costs.</p>
<h2 id="rdp-accounting">RDP accounting</h2>
<p>Rényi DP (Mironov 2017) reframes the privacy guarantee using Rényi divergence. The Rényi divergence of order α between distributions P and Q measures how different they are, parameterized by how much weight you put on the tails. At α = 1 it recovers KL divergence; higher α emphasizes the heavy tail behavior. Formally:</p>
<p>$$D_\alpha(P | Q) = \frac{1}{\alpha - 1} \log \mathbb{E}_Q!\left[\left(\frac{P(x)}{Q(x)}\right)^\alpha\right]$$</p>
<p>A mechanism M is (α, ε)-RDP if D_α(M(D) || M(D&rsquo;)) ≤ ε for all adjacent D, D&rsquo;. Composition in Rényi terms is clean: T applications of an (α, ε)-RDP mechanism give (α, T·ε)-RDP. Still linear, but the per-step ε in Rényi terms is much smaller than in (ε, δ) terms for the Gaussian mechanism at practical noise levels, so the budget lasts.</p>
<p>To convert back to standard (ε, δ)-DP at the end:</p>
<p>$$\varepsilon(\delta) = \min_\alpha \left[ \mathrm{RDP}_\alpha - \frac{\log \delta}{\alpha - 1} \right]$$</p>
<p>The minimum over α matters because different orders are optimal at different step counts and δ targets. Opacus evaluates at 151 Rényi orders and takes the best.</p>
<p>Inside <code>RDPAccountant.get_privacy_spent</code>, <code>compute_rdp</code> is called once per segment in history, results are summed, then converted to (ε, δ)-DP:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#75715e"># opacus/accountants/rdp.py</span>
rdp <span style="color:#f92672">=</span> sum(
    compute_rdp(q<span style="color:#f92672">=</span>sample_rate, noise_multiplier<span style="color:#f92672">=</span>noise_multiplier,
                steps<span style="color:#f92672">=</span>num_steps, orders<span style="color:#f92672">=</span>alphas)
    <span style="color:#66d9ef">for</span> (noise_multiplier, sample_rate, num_steps) <span style="color:#f92672">in</span> self<span style="color:#f92672">.</span>history
)
eps, best_alpha <span style="color:#f92672">=</span> get_privacy_spent(orders<span style="color:#f92672">=</span>alphas, rdp<span style="color:#f92672">=</span>rdp, delta<span style="color:#f92672">=</span>delta)
</code></pre></div><p>The memory behavior becomes clear inside <code>compute_rdp</code>. It calls <code>_compute_rdp</code> once per Rényi order to get the per-step RDP value, then scales by <code>steps</code>:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#75715e"># opacus/accountants/analysis/rdp.py</span>

<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_compute_rdp</span>(q: float, sigma: float, alpha: float) <span style="color:#f92672">-&gt;</span> float:
    <span style="color:#66d9ef">if</span> q <span style="color:#f92672">==</span> <span style="color:#ae81ff">1.0</span>:
        <span style="color:#66d9ef">return</span> alpha <span style="color:#f92672">/</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> sigma<span style="color:#f92672">**</span><span style="color:#ae81ff">2</span>)      <span style="color:#75715e"># no subsampling: plain Gaussian</span>
    <span style="color:#66d9ef">return</span> _compute_log_a(q, sigma, alpha) <span style="color:#f92672">/</span> (alpha <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)

<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">compute_rdp</span>(q, noise_multiplier, steps, orders):
    rdp <span style="color:#f92672">=</span> np<span style="color:#f92672">.</span>array([_compute_rdp(q, noise_multiplier, order) <span style="color:#66d9ef">for</span> order <span style="color:#f92672">in</span> orders])
    <span style="color:#66d9ef">return</span> rdp <span style="color:#f92672">*</span> steps  <span style="color:#75715e"># steps is a scalar — output is always shape (151,)</span>
</code></pre></div><p><code>steps</code> never enters an array dimension. It scales the fixed-size output after the per-step computation is done. That&rsquo;s why memory doesn&rsquo;t grow with training length.</p>
<p>The per-step RDP at order α reduces to <code>_compute_log_a(q, σ, α) / (α - 1)</code>. For integer α, <code>_compute_log_a</code> evaluates the binomial expansion from Mironov et al. 2019 §3.3 in log-space:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_compute_log_a_for_int_alpha</span>(q, sigma, alpha):
    log_a <span style="color:#f92672">=</span> <span style="color:#f92672">-</span>np<span style="color:#f92672">.</span>inf
    <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(alpha <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>):
        log_coef_i <span style="color:#f92672">=</span> (
            math<span style="color:#f92672">.</span>log(special<span style="color:#f92672">.</span>binom(alpha, i))
            <span style="color:#f92672">+</span> i <span style="color:#f92672">*</span> math<span style="color:#f92672">.</span>log(q)
            <span style="color:#f92672">+</span> (alpha <span style="color:#f92672">-</span> i) <span style="color:#f92672">*</span> math<span style="color:#f92672">.</span>log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> q)
        )
        s <span style="color:#f92672">=</span> log_coef_i <span style="color:#f92672">+</span> (i <span style="color:#f92672">*</span> i <span style="color:#f92672">-</span> i) <span style="color:#f92672">/</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> sigma<span style="color:#f92672">**</span><span style="color:#ae81ff">2</span>)
        log_a <span style="color:#f92672">=</span> _log_add(log_a, s)  <span style="color:#75715e"># numerically stable log-space add</span>
    <span style="color:#66d9ef">return</span> log_a
</code></pre></div><p>The loop runs <code>alpha + 1</code> times — bounded by the Rényi order, not the step count. Calling this every epoch adds no incremental cost.</p>
<p>The cost of this approach is accuracy. The RDP-to-(ε, δ) conversion is an upper bound on top of the RDP bound, so the reported ε is somewhat larger than the true value. For the same mechanism and the same T, RDP will always report a looser ε than a tighter accountant would.</p>
<h2 id="prv-accounting">PRV accounting</h2>
<p>The Privacy Random Variable accountant (Gopi, Lee, Wutschitz, NeurIPS 2021) works with the privacy-loss distribution directly.</p>
<p>For adjacent datasets D and D&rsquo;, the privacy-loss random variable Z captures how much a single output reveals about which dataset was used:</p>
<p>$$Z = \log \frac{P[M(D) = o]}{P[M(D&rsquo;) = o]}$$</p>
<p>where o is drawn from M(D). The (ε, δ)-DP condition turns out to be equivalent to P[Z &gt; ε] ≤ δ. Computing (ε, δ) from Z is then a tail probability question: find ε such that the probability mass above ε equals δ.</p>
<p>Composing T steps means summing T independent copies of Z (Z₁ + Z₂ + &hellip; + Z_T gives the total privacy loss across T steps). Summing independent random variables means convolving their distributions. Convolution in frequency space is pointwise multiplication. Hence the FFT:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python">composed_pmf <span style="color:#f92672">=</span> irfft(rfft(dprv<span style="color:#f92672">.</span>pmf) <span style="color:#f92672">**</span> num_steps)
</code></pre></div><p>This is more accurate than RDP because it works with the actual composed distribution rather than a divergence bound.</p>
<p>But to do any of this numerically you have to discretize the distribution onto a finite grid, and that&rsquo;s where the memory cost comes in.</p>
<h3 id="the-grid">The grid</h3>
<p>The grid needs to cover the tails of the composed distribution. Gopi et al. derive the required domain half-width <code>L_max</code> from an RDP bound over all T compositions (Remark 5.6 of the paper). Because that RDP bound grows with T, <code>L_max</code> grows with T. The grid spacing <code>dt</code> is fixed by the requested error tolerances. So:</p>
<p>$$\text{grid_size} \approx \frac{2 \cdot L_{\max}}{dt}$$</p>
<p>Every call to <code>get_epsilon</code> at a larger T builds a bigger grid, runs a bigger FFT, and allocates more working memory. Here&rsquo;s what that looks like with no GPU — just the accounting math:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> gc<span style="color:#f92672">,</span> psutil
<span style="color:#f92672">from</span> opacus.accountants <span style="color:#f92672">import</span> create_accountant

<span style="color:#66d9ef">def</span> <span style="color:#a6e22e">watch</span>(mechanism, steps_per_epoch<span style="color:#f92672">=</span><span style="color:#ae81ff">98</span>, epochs<span style="color:#f92672">=</span><span style="color:#ae81ff">12</span>):
    acc <span style="color:#f92672">=</span> create_accountant(mechanism<span style="color:#f92672">=</span>mechanism)
    acc<span style="color:#f92672">.</span>history <span style="color:#f92672">=</span> [(<span style="color:#ae81ff">0.2</span>, <span style="color:#ae81ff">256</span><span style="color:#f92672">/</span><span style="color:#ae81ff">25000</span>, <span style="color:#ae81ff">0</span>)]
    rss <span style="color:#f92672">=</span> <span style="color:#66d9ef">lambda</span>: psutil<span style="color:#f92672">.</span>Process()<span style="color:#f92672">.</span>memory_info()<span style="color:#f92672">.</span>rss <span style="color:#f92672">/</span> <span style="color:#ae81ff">1024</span><span style="color:#f92672">**</span><span style="color:#ae81ff">2</span>
    <span style="color:#66d9ef">for</span> ep <span style="color:#f92672">in</span> range(<span style="color:#ae81ff">1</span>, epochs <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>):
        nm, sr, _ <span style="color:#f92672">=</span> acc<span style="color:#f92672">.</span>history[<span style="color:#ae81ff">0</span>]
        acc<span style="color:#f92672">.</span>history[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> (nm, sr, ep <span style="color:#f92672">*</span> steps_per_epoch)
        acc<span style="color:#f92672">.</span>get_epsilon(<span style="color:#ae81ff">1e-5</span>)
        <span style="color:#66d9ef">print</span>(f<span style="color:#e6db74">&#34;{mechanism} epoch {ep:2d}: steps={ep*steps_per_epoch:5d}  RSS={rss():.0f} MB&#34;</span>)
    gc<span style="color:#f92672">.</span>collect()

watch(<span style="color:#e6db74">&#34;prv&#34;</span>)
watch(<span style="color:#e6db74">&#34;rdp&#34;</span>)
</code></pre></div><pre><code>prv epoch  1: steps=196    RSS=552 MB
prv epoch  2: steps=392    RSS=755 MB
prv epoch  3: steps=588    RSS=1033 MB
prv epoch  4: steps=784    RSS=1685 MB
prv epoch  5: steps=980    RSS=2487 MB
prv epoch  6: steps=1176   RSS=3016 MB

rdp epoch  1: steps=196    RSS=401 MB
rdp epoch  6: steps=1176   RSS=402 MB
</code></pre><p>The <code>gc.collect()</code> after the PRV loop reclaims nothing. <code>scipy.fft</code> caches FFT plans keyed by transform size, and each new grid size adds a plan at the C level, below Python&rsquo;s GC. Glibc also typically doesn&rsquo;t return freed heap arenas to the OS when each successive allocation is larger than the previous one, since the old block can&rsquo;t be reused. The resident set ratchets up and stays.</p>
<h2 id="tradeoffs">Tradeoffs</h2>
<p>PRV gives a tighter ε. For the same mechanism and the same T, it reports a smaller ε than RDP. On long training runs this difference can be meaningful — PRV might report ε = 8 where RDP reports ε = 11. That accuracy comes from working with the actual privacy-loss distribution rather than a Rényi surrogate.</p>
<p>RDP stays at a fixed (151,) array throughout training. PRV&rsquo;s working memory grows with the step count, and called every epoch on a long run it can reach gigabytes. That&rsquo;s what killed my training runs.</p>
<p>For most experimental work, RDP is the right choice: the bound is correct, the memory is flat, and it&rsquo;s what the LiRA and TF-Privacy reference implementations use, so the numbers are directly comparable. If you&rsquo;re reporting ε in a paper where the tightness matters — comparing against PRV-reported numbers from another paper, or making a compliance argument — use PRV, but call it once at the end of training rather than every epoch:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#66d9ef">for</span> epoch <span style="color:#f92672">in</span> range(num_epochs):
    train_one_epoch(<span style="color:#f92672">...</span>)

epsilon <span style="color:#f92672">=</span> privacy_engine<span style="color:#f92672">.</span>get_epsilon(delta)  <span style="color:#75715e"># one call, at the end</span>
</code></pre></div><p>Worth knowing: that final-epoch PRV call is the most expensive one in the run, since the grid is at its largest. On a tight machine, using RDP throughout and doing one optional PRV call at the very end is the safest combination.</p>
<p>One last thing. Switching accountants does not change training at all. The noise multiplier, clipping norm, and gradient updates are identical between accountants. The accountant is purely a post-hoc measurement of the privacy cost. But ε values from RDP and PRV aren&rsquo;t on the same scale, so don&rsquo;t mix them in the same logs.</p>
<h2 id="references">References</h2>
<ul>
<li>M. Abadi et al., <em>Deep Learning with Differential Privacy</em>, CCS 2016.</li>
<li>I. Mironov, <em>Rényi Differential Privacy</em>, CSF 2017.</li>
<li>I. Mironov, K. Talwar, L. Zhang, <em>Rényi Differential Privacy of the Sampled Gaussian Mechanism</em>, 2019.</li>
<li>S. Gopi, Y. T. Lee, L. Wutschitz, <em>Numerical Composition of Differential Privacy</em>, NeurIPS 2021.</li>
<li>A. Yousefpour et al., <em>Opacus: User-Friendly Differential Privacy Library in PyTorch</em>, 2021.</li>
<li>N. Carlini et al., <em>Membership Inference Attacks From First Principles</em>, IEEE S&amp;P 2022.</li>
</ul>
<p>The LiRA reproduction this came out of is at <a href="https://github.com/apzl/LiRA-pytorch">github.com/apzl/LiRA-pytorch</a>, including shadow model training, DP-SGD grid, scoring pipeline, and a longer debugging writeup.</p>
]]></content>
        </item>
        
        <item>
            <title>How BERT makes sense</title>
            <link>/posts/2021/01/how-bert-makes-sense/</link>
            <pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate>
            
            <guid>/posts/2021/01/how-bert-makes-sense/</guid>
            <description>English has many words with multiple meanings and they are often used in very different context.
The bank will not be accepting cash on Saturdays. 
The river overflowed the bank
We humans can understand the context and relate the word with relevant meaning. This is a very basic requirement in natural language understanding. Let&amp;rsquo;s see how BERT, or contextual embedddings in general solve this problem, commonly know as word sense disambigution.</description>
            <content type="html"><![CDATA[<p>English has many words with multiple meanings and they are often used in very different context.</p>
<p><code>The bank will not be accepting cash on Saturdays. </code><br>
<code>The river overflowed the bank</code></p>
<p>We humans can understand the context and relate the word with relevant meaning. This is a very basic requirement in natural language understanding. Let&rsquo;s see how BERT, or contextual embedddings in general solve this problem, commonly know as word sense disambigution.</p>
<h2 id="word-sense-disambigution">Word Sense Disambigution</h2>
<p>Word Sense Disambiguation (WSD) is the task to identify the correct sense of the usage of a word in a sentence. The solution to this problem impacts other computer-related writing, such as discourse, improving relevance of search engines etc. Various approaches, both supervised and semi-supervised methods, with the use of dictionaries have been introduced but the introduction of transformers made a significant impact like in almost all other NLP problems.</p>
<h2 id="deep-contextual-embeddings">Deep Contextual Embeddings</h2>
<p>Representing text as vectors is the core principle behind natural language processing. Earlier models like word2vec, GloVe, and FastText were used to used to train to get such representations. But the drawback of these representations is that they don&rsquo;t aknowledge the disambiguity in meaning of words. Let&rsquo;s go back to example mentioned above.</p>
<p><code>The bank will not be accepting cash on Saturdays. </code><br>
<code>The river overflowed the bank</code></p>
<p>In embeddings produced by word2vec like models, the word <code>bank</code> will be having same representation for both the sentences even though they have very different  meaning. Also different words having different semantics will be having same representations.For example,in some sense, <code>arrive</code> and <code>arrival</code>, their semantics are almost the same, but they are different parts of speech: <code>arrive</code> is a verb and <code>arrival</code> is a noun, so they can appear in quite different places.Contextual embeddings solved these problems to an extent.</p>
<p>The concept of deep contextualized word embeddings was first introduced in ELMo.The main idea of the Embeddings from Language Models (ELMo) can be divided into two main tasks, first an LSTM-basedlanguage model is trained on a corpus and then the hidden embeddings are used to generate a vector representation for each word.<br>
<img src="http://www.davidsbatista.net/assets/images/2018-12-06-ELMo_language_models.png" alt="lstm"></p>
<p>ELMos train a multi-layer, bi-directional, LSTM-based language model, and extract the hidden state of each layer for the input sequence of words. Then, they compute a weighted sum of those hidden states to obtain an embedding for each word. The weight of each hidden state is task-dependent and is learned during training of the end-task.</p>
<h2 id="how-does-bert-do-that">How does BERT do that?</h2>
<p>Introduction of transformers revolutionised NLP domain. The Multi-layer bidirectional Transformer or Transformer was first introduced in the Attention is All You Need paper. It follows the encoder-decoder architecture of machine translation models, but it replaces the RNNs by a different network architecture.
The key concept behind the effectiveness of transformers is Multi-Head Attention.</p>
<p><img src="http://www.davidsbatista.net/assets/images/2018-12-06-attention_path_length.png" alt="multihead attention"></p>
<p>Several weighted sums are calculated for the same input using stacked multi-head attention models, with different linear transformations.<br>
another factor is that, BERT is a non-directional model as it is trained by masking 15% of the words in a sentence and then making the model to learn by predicting the masked words using the information from the non-masked words.<br>
All these factors help the contextual embeddings of bert to be efficient.</p>
]]></content>
        </item>
        
        <item>
            <title>Principal Component Analysis - A Brief Overview</title>
            <link>/posts/2020/11/principal-component-analysis-a-brief-overview/</link>
            <pubDate>Tue, 10 Nov 2020 00:00:00 +0000</pubDate>
            
            <guid>/posts/2020/11/principal-component-analysis-a-brief-overview/</guid>
            <description>Principal Component Analysis (PCA) is a very famous technique in Machine Learning. It is an unsupervised algorithm to reduce the number of dimensions in data, without losing the meaning of data. Reducing the number of dimensions makes it easier to understand and visualise the data and also compresses data, which in turn helps in faster processing. It has got a wide variety of applications in many fields like data analysis, computer vision and NLP.</description>
            <content type="html"><![CDATA[<hr>
<!-- raw HTML omitted -->
<!-- raw HTML omitted -->
<p>Principal Component Analysis (PCA) is a very famous technique in Machine Learning. It is an unsupervised algorithm to reduce the number of dimensions in data, without losing the meaning of data. Reducing the number of dimensions  makes it easier to understand and visualise the data and also compresses data, which in turn helps in faster processing. It has got a wide variety of applications in many fields like data analysis, computer vision and NLP. In this blogpost I&rsquo;d like to give a brief overview into PCA and the basic math in it.</p>
<p><br>
PCA is actually just a transformation method in linear algebra, which transforms the given data into combinations of original data in such a way that the new combinations will capture maximum variance in
data while reducing the correlation between variables. Data is at first centered by subtracting the mean from each dimensions and then the covariance between each dimensions are calculated and arranged into a matrix called covariance matrix. Eigen vectors and eigen values are found for the covariance matrix and which is then used to find the transformed data with requred number of dimensions. Ok, so now let&rsquo;s break this down into steps.</p>
<p>First step is to <strong>center data</strong>, that is to make the mean of data to zero. PCA without centering data might result in the first component corresponding with the mean of data rather than variance. For this all the values have the mean of the values subtracted from it.</p>
<p>Once the data is centered, no we need to find the covariance matrix. For that we need covariance between each dimensions. So what is <strong>covariance</strong>?  For that let&rsquo;s see what variance is.</p>
<p>Variance is a measure of spread of data in a dataset, with equation as:<br>
$$ s^{2}=\frac{  \sum_{i=1}^n   \sqrt{\big(  X_{i}- \overline{X}  \big)^{2} }    } { \big(n-1\big)} $$</p>
<p>Variance gives the spread of data in a single dimension, independent of other dimensions. Covariance gives how
much the dimensions vary from the mean <em>with respect to each other</em>. The formula for covariance is very similar to the formula for variance. The formula for variance could be written like this:
$$ var(X)=\frac{  \sum_{i=1}^n   \sqrt{\big(  X_{i}- \overline{X}  \big)\big(  X_{i}- \overline{X}  \big)}    } { \big(n-1\big)} $$
similarly, the covarience of two dimensions X and Y is calculated as,
$$ covar(X,Y)=\frac{  \sum_{i=1}^n   \sqrt{\big(  X_{i}- \overline{X}  \big)\big(  Y_{i}- \overline{Y}  \big)}    } { \big(n-1\big)} $$
Covarience actually shows how the dimensions are related to each other. For example, considere a 2-dimensional data, with dimension H has the number of hours studied by a group of students and dimension M as the marks they recieved. A positive covarience value indicates that both marks and hous of study increases together, or in other words, as the hours of study increases, marks increases. Negative value of covarience indicates that the as the value of a dimension increases, value of other decreases. A zero value of covarience indicates that both the dimensions are independent of each other.</p>
<p>Next step is to make a covariance matrix out of the covariance values. <strong>Coaviance matrix</strong> will be a square, symmetric matrix of dimension nxn, where n is the number of dimensions. For a two dimensional data, the covariance matrix will be like:
$$
\begin{bmatrix}covar(x,x) &amp; covar(x,y) \\covar(y,x) &amp; covar(y,y) \end{bmatrix}
$$
Diagonal values of the matrix will be variance value of each dimension. Because they are square and symmetrical, covariance matrixes are diagonalizable, which means an eigen decomposition can be calculated on the matrix. Next step is to find the eigen vectors and eigen values of the covariance matrix.<br>
Considere a square matrix A, v  is the eigen vector of if A[v] is a scalar multiple of v. ie.,
$$
A.v =  \lambda v
$$
where λ is the eigen value.</p>
<p>In context of PCA, eigen vectors of covariance matrix are the axes of principal components in a dataset. Eigen vectors show the direction of principal components calculated by PCA, where eigen values show how stretched the axes are.
<img src="https://miro.medium.com/max/700/0*fSfgExsETK72Si3-" alt="eigen-values and eigen vectors"></p>
<p>A covariance matrix of n dimensions will have n pairs of eigen vectors and eigen values.<br>
First eigen vector will be along the axis having the largest variance in data and the next one with lower variance and so on.<br>
The eigenvalues are important because they provide a ranking criterion for the newly found axes. The principal components (eigenvectors) are sorted by descending eigenvalue. The principal components with the highest eigenvalues are “picked first” as principal components because they account for the most variance in the data.</p>
<p>Next step is to select the required number of eigen vectors, typically n-1 vectors with the highest eigen value. The selected eigen vectors are arranged into a <strong>feature vector</strong>. Transpose of the feature vector
is then multiplied with transpose of the original data to get the new transformed dataset.</p>
<p><img src="https://miro.medium.com/max/700/0*A-CIBeVLj6Y8aS6_" alt="transformed data"></p>
<p>Yeah, that&rsquo;s it. we now have the new dataset with required number of dimensions. It is possible to get the old data back by just multiplying the the inverse of the feature vector with the new dataset, although there might a small loss in data.</p>
<p>PCA is a simple yet powerful tool. PCA is applied to compress images by reducing it&rsquo;s dimensions. Most of the aappplication of PCA is around data visualisation as we are limited to represent data by 2 dimensions. It is effective at filtering noise and decreasing interdependency of variables. Many programming libraries have methods to apply PCA, so it&rsquo;s not that important to know the programming side of PCA, but it is good to know theoretical side. There are many resources across internet, if you&rsquo;d like to dive deeper into the maths of coding side of PCA.</p>
<p><a href="https://towardsdatascience.com/principal-component-analysis-pca-from-scratch-in-python-7f3e2a540c51">Principal Component Analysis (PCA) from scratch in Python</a><br>
<a href="https://ourarchive.otago.ac.nz/bitstream/handle/10523/7534/OUCS-2002-12.pdf?sequence=1&amp;isAllowed=y">A Tutorial on Principal Component Analysis</a> has got detailed maths explanations.</p>
]]></content>
        </item>
        
    </channel>
</rss>
