<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - Machine Learning</title>
    <link>https://emresahin.net/tags/machine-learning/</link>
    <description>Posts in the Machine Learning tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/machine-learning/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>devlog 32</title>
      <published>2025-07-12T16:10:36+00:00</published>
      <updated>2025-07-12T16:10:36+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 12 Jul 2025 16:10:36 +0000</pubDate>
      <link>https://emresahin.net/devlog-32/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-32/</guid>
      <description>This dialog is about converting ColQwen2 to the ONNX format. 🐢 Now, I have two classes. This one is a copy of the ONNX Patcher that’s used to convert ColQwen2 to ONNX format. But this one is valid only for the underlying model, that is Qwen2VLForConditionalGeneration , and ColQwen2 also has some ...</description>
      <category>devlog</category>
      <category>ONNX</category>
      <category>conversions</category>
      <category>ColQwen2</category>
      <category>machine-learning</category>
      <category>python</category>
      <category>torch</category>
      <content:encoded><![CDATA[<p><em>This dialog is about converting ColQwen2 to the ONNX format.</em></p>
<p>🐢 Now, I have two classes. This one is a copy of the ONNX Patcher that’s used to convert ColQwen2 to ONNX format. But this one is valid only for the underlying model, that is <code>Qwen2VLForConditionalGeneration</code>, and <code>ColQwen2</code> also has some modifications to call it.</p>
<p>🐇 What are these modifications?</p>
<p>🐢 The <code>forward</code> method looks something like this:</p>
<h2 id="colqwen2">ColQwen2</h2>
<pre><code class="language-python">    def forward(self, *args, **kwargs) -&gt; torch.Tensor:
        kwargs.pop("output_hidden_states", None)

        # Handle the custom "pixel_values" input obtained with `ColQwen2Processor` through unpadding
        if "pixel_values" in kwargs:
            offsets = kwargs["image_grid_thw"][:, 1] * kwargs["image_grid_thw"][:, 2]  # (batch_size,)
            kwargs["pixel_values"] = torch.cat(
                [pixel_sequence[:offset] for pixel_sequence, offset in zip(kwargs["pixel_values"], offsets)],
                dim=0,
            )

        position_ids, rope_deltas = self.get_rope_index(
            input_ids=kwargs["input_ids"],
            image_grid_thw=kwargs.get("image_grid_thw", None),
            video_grid_thw=None,
            attention_mask=kwargs.get("attention_mask", None),
        )
        last_hidden_states = self.inner_forward(
            *args, **kwargs, position_ids=position_ids, use_cache=False, output_hidden_states=True
        )  # (batch_size, sequence_length, hidden_size)

        proj = self.custom_text_proj(last_hidden_states)  # (batch_size, sequence_length, dim)

        # L2 normalization
        proj = proj / proj.norm(dim=-1, keepdim=True)  # (batch_size, sequence_length, dim)
        proj = proj * kwargs["attention_mask"].unsqueeze(-1)  # (batch_size, sequence_length, dim)

        if "pixel_values" in kwargs and self.mask_non_image_embeddings:
            # Pools only the image embeddings
            image_mask = (kwargs["input_ids"] == self.config.image_token_id).unsqueeze(-1)
            proj = proj * image_mask
        return proj
</code></pre>
<p>🐇 How about the <code>inner_forward</code> method? Does it just directly call <code>Qwen2VLForConditionalGeneration</code>’s <code>forward</code>?</p>
<p>🐢 No, it has some conditionals:</p>
<pre><code class="language-python">        if inputs_embeds is None:
            inputs_embeds = self.model.embed_tokens(input_ids)
            if pixel_values is not None:
                pixel_values = pixel_values.type(self.visual.get_dtype())
                image_embeds = self.visual(pixel_values, grid_thw=image_grid_thw)
                image_mask = (input_ids == self.config.image_token_id).unsqueeze(-1).expand_as(inputs_embeds)
                image_embeds = image_embeds.to(inputs_embeds.device, inputs_embeds.dtype)
                inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds)

            if pixel_values_videos is not None:
                pixel_values_videos = pixel_values_videos.type(self.visual.get_dtype())
                video_embeds = self.visual(pixel_values_videos, grid_thw=video_grid_thw)
                video_mask = (input_ids == self.config.video_token_id).unsqueeze(-1).expand_as(inputs_embeds)
                video_embeds = video_embeds.to(inputs_embeds.device, inputs_embeds.dtype)
                inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds)

            if attention_mask is not None:
                attention_mask = attention_mask.to(inputs_embeds.device)

        outputs = self.model(
            input_ids=None,
            position_ids=position_ids,
            attention_mask=attention_mask,
            past_key_values=past_key_values,
            inputs_embeds=inputs_embeds,
            use_cache=use_cache,
            output_attentions=output_attentions,
            output_hidden_states=output_hidden_states,
            return_dict=return_dict,
        )

        hidden_states = outputs[0]
        return hidden_states
</code></pre>
<p>🦊 The patcher only modifies the <code>past_key_values_args</code> and moves them to a <code>DynamicCache</code>. There are no other changes for patching.</p>
<p>🐢 But the return types are also different. <code>Qwen2VLForConditionalGeneration.forward</code> returns a <code>Union[Tuple, Qwen2VLCausalLMOutputWithPast]</code>, but <code>ColQwen2.forward</code> returns a <code>torch.Tensor</code>.</p>
<p>🐇 How is that <code>torch.Tensor</code> calculated from the return type of <code>Qwen2VLForConditionalGeneration</code>? ❓</p>
<p>🦊 There are multiple return types in <code>Qwen2VLForConditionalGeneration</code>. It’s determined by the <code>return_dict</code> argument.</p>
<pre><code class="language-python">
        if not return_dict:
            output = (logits,) + outputs[1:]
            return (loss,) + output if loss is not None else output

        return Qwen2VLCausalLMOutputWithPast(
            loss=loss,
            logits=logits,
            past_key_values=outputs.past_key_values,
            hidden_states=outputs.hidden_states,
            attentions=outputs.attentions,
            rope_deltas=self.rope_deltas,
        )
</code></pre>
<p>🐇 What’s that argument in <code>ColQwen2</code>?</p>
<p>🐢 It’s passed from the caller; it’s a <code>kwarg</code>.</p>
<p>🐇 Is there any modification to this in the ONNX patcher?</p>
<p>🐢 Nope.</p>
<p>🐇 Then we can consider it as the default. What’s the default?</p>
<p>🐢 The default is <code>None</code>. Hence it returns <code>(logits,) + outputs[1:]</code>.</p>
<p>🐇 Then, <code>logits</code> are the first parameter by default.</p>
<p>🐢 Yes, we can assume this.</p>
<p>🐇 What does <code>ColQwen2</code> do with these logits?</p>
<p>🦊 By the way, it calls <code>inner_forward</code> with <code>use_cache=False</code> and <code>output_hidden_states=True</code>. What do these change in <code>Qwen2VLForConditionalGeneration</code>?</p>
<p>🐢 These hidden states are actually output. In <code>ColQwen2.forward</code>, the <code>inner_forward</code> call is actually:</p>
<pre><code class="language-python">        last_hidden_states = self.inner_forward(
            *args, **kwargs, position_ids=position_ids, use_cache=False, output_hidden_states=True
        )  # (batch_size, sequence_length, hidden_size)
</code></pre>
<p>and the <code>output</code> is the hidden states.</p>
<p>🦊 It then runs:</p>
<pre><code class="language-py">        proj = self.custom_text_proj(last_hidden_states)  # (batch_size, sequence_length, dim)
</code></pre>
<p>and calculates a set of projections.</p>
<p>🐢 <code>custom_set_proj</code> is defined as:</p>
<pre><code class="language-py">        self.custom_text_proj = nn.Linear(self.model.config.hidden_size, self.dim)
</code></pre>
<p>hence these projections are fully connected layer calculations.</p>
<p>🦊 It returns these after L2 normalization and checks if there are <code>pixel_values</code> to consider.</p>
<p>🐢 In this case, the output from <code>ColQwen2</code> is this single tensor.</p>
<p>🐇 Ok. Then it’s actually simpler than wrapping up the whole <code>Qwen2VLForConditionalGeneration</code>.</p>
<p>🐢 It looks so, yes. But the example for <code>ColQwen2</code> also has a post-processing step:</p>
<pre><code class="language-py">scores = processor.score_multi_vector(query_embeddings, image_embeddings)
</code></pre>
<p>🐇 I think we can leave that post-processing for the time being. We only need multi-vector embeddings for FastEmbed.</p>
<p>🐢 Maybe it’s convertible to a <code>forward</code> method that we can use with ONNX.</p>
<p>🐇 Let’s see how this scoring works, then.</p>
<p>🐢 <a href="https://github.com/illuin-tech/colpali/blob/main/colpali_engine/utils/processing_utils.py#L68"><code>score_multi_vector</code></a> receives two tensors or tensor lists and compares query vectors with passage vectors. It’s a rather straightforward implementation that requires Torch, but not Transformers.</p>
<p>🐇 In theory, we can also convert this to an ONNX model.</p>
<p>🦊 We can also write a custom model for this.</p>
<p>🐢 It has two <code>for</code> loops for comparisons. Can we convert all of these?</p>
<p>🦊 The loop indices can be seen as <code>dynamic_axes</code>, and it’s possible to convert the whole thing as a Torch model, then use <code>torch.onnx.export</code> just as we do for the model itself.</p>
<p>🐢 I see, but I don’t think that’s what we must do now.</p>
<p>🐇 Yes, let’s skip that for the time being and convert the model itself. We’ll have multivectors for patches and queries at the end.</p>
<p>🐢 So, we’ll keep the processing part, send <code>BatchFeature</code> objects that are output from the processor, and send this to two models: one for images and one for text.</p>
<p>🐇 Yep, that’s the plan. In the end, we’ll have two ONNX models that require <code>BatchFeature</code>s.</p>
<p>🐢 Then, we’ll modify <code>past_key_values</code> in this argument to use <code>DynamicCache</code>.</p>
<p>🐇 Yes, that’s alright. We can start by moving <code>past_key_values_converter</code> to a method in <code>PatchedColQwen2</code>.</p>
<p>🐢 <code>past_key_values</code> wasn’t used much, so I completely removed it. I also began to use processor outputs as dummy input in the exporter. However, when using <code>BatchFeature</code>, we get:</p>
<blockquote>
<p>RuntimeError: Only tuples, lists and Variables are supported as JIT inputs/outputs. Dictionaries and strings are also accepted, but their usage is not recommended. Here, received an input of unsupported type: BatchFeature</p>
</blockquote>
<p>🐇 So, we can try Dynamo first, I think. If that doesn’t work, we can just collect items as tensors and build up a <code>BatchFeature</code> inside the patcher.</p>
<p>🐢 Let’s try <code>dynamo=True</code> for this first.</p>
<p>🦊 This time, it’s about <code>BatchFeature</code> again, but the error is different: <code>KeyError: 'Indexing with integers is not available when using Python based feature extractors'</code></p>
<p>🐇 In this case, we can just create <code>BatchFeature</code> inside the patcher.</p>
<h2 id="patchedcolqwen2">PatchedColQwen2</h2>
<pre><code class="language-python">
class PatchedColQwen2(ColQwen2):
    def forward(self, *args):
        (
            input_ids,
            inputs_embeds,
            attention_mask,
            position_ids,
            *past_key_values_args,
        ) = args
        # Convert past_key_values list to DynamicCache
        if len(past_key_values_args) == 0:
            past_key_values = None
        else:
            past_key_values = DynamicCache()
            for i in range(self.config.num_hidden_layers):
                key = past_key_values_args.pop(0)
                value = past_key_values_args.pop(0)
                past_key_values.update(key_states=key, value_states=value, layer_idx=i)

        breakpoint()
        o = super().forward(
            input_ids=input_ids,
            inputs_embeds=inputs_embeds,
            attention_mask=attention_mask,
            # position_ids=position_ids,
            past_key_values=past_key_values,
        )

        flattened_past_key_values_outputs = {
            "logits": o.logits,
        }
        output_past_key_values: DynamicCache = o.past_key_values
        for i, (key, value) in enumerate(
            zip(output_past_key_values.key_cache, output_past_key_values.value_cache)
        ):
            flattened_past_key_values_outputs[f"present.{i}.key"] = key
            flattened_past_key_values_outputs[f"present.{i}.value"] = value

        return flattened_past_key_values_outputs

</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Missing Variables</title>
      <published>2021-01-10T18:46:13+00:00</published>
      <updated>2021-01-10T18:46:13+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 10 Jan 2021 18:46:13 +0000</pubDate>
      <link>https://emresahin.net/missing-variables/</link>
      <guid isPermaLink="true">https://emresahin.net/missing-variables/</guid>
      <description>Using missing as a categorical variable It’s possible to use “missing” as a label for categorical variables when their frequency is high or relevant. Otherwise, it may be seen as adding another rare categorical variable to the dataset. Using random values for missing data Random sample imputation...</description>
      <category>ML</category>
      <category>Data Science</category>
      <category>Statistics</category>
      <category>Imputation</category>
      <category>Variables</category>
      <category>Random</category>
      <category>Missing Data</category>
      <category>Rare Variable</category>
      <category>Machine Learning</category>
      <category>Data Preprocessing</category>
      <content:encoded><![CDATA[<h2 id="using-missing-as-a-categorical-variable">Using <em>missing</em> as a categorical variable</h2>
<p>It’s possible to use “missing” as a label for categorical variables when their frequency is high or relevant. Otherwise, it may be seen as adding another rare categorical variable to the dataset.</p>
<h2 id="using-random-values-for-missing-data">Using random values for missing data</h2>
<p>Random sample imputation can be used to fill missing entries by selecting random values from those already present in the dataset.</p>
<p>Using this approach feels a bit unusual to me. However, when you are looking for a prediction and not all values are present in your sample, you can estimate a value based only on the supplied data.</p>
<p>For random variables, we need to generate values by seeding the random number generator with the given values. This ensures we obtain the same values for random variables whenever the same inputs are supplied, providing a degree of consistency.</p>]]></content:encoded>
    </item>
    <item>
      <title>Coursera Deep Learning Specialization Notes</title>
      <published>2018-10-29T21:25:17+00:00</published>
      <updated>2018-10-29T21:25:17+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 29 Oct 2018 21:25:17 +0000</pubDate>
      <link>https://emresahin.net/coursera-deep-learning-14336-24273/</link>
      <guid isPermaLink="true">https://emresahin.net/coursera-deep-learning-14336-24273/</guid>
      <description>Per the Honor Code, these notes do not contain answers to quizzes or assignments. If you are looking for those, please look elsewhere. Binary Classification Given an image, classify it as cat or non-cat. The result is $\hat{y} = P(y=1 | x)$. In other words, given $x$, we calculate the probability...</description>
      <category>Deep Learning</category>
      <category>Coursera</category>
      <category>Neural Networks</category>
      <category>Optimization</category>
      <category>CNN</category>
      <category>RNN</category>
      <category>YOLO</category>
      <category>Machine Learning</category>
      <category>Backpropagation</category>
      <category>Activation Functions</category>
      <content:encoded><![CDATA[<p><strong>Per the Honor Code, these notes do not contain answers to quizzes or assignments. If you are looking for those, please look elsewhere.</strong></p>
<h2 id="binary-classification">Binary Classification</h2>
<p>Given an image, classify it as <em>cat</em> or <em>non-cat.</em> The result is $\hat{y} = P(y=1 | x)$. In other words, given $x$, we calculate the probability that this data represents a cat.</p>
<h2 id="feature-vector-from-image">Feature Vector from Image</h2>
<p>We convert an image, e.g., a (64, 64, 3) image, into a (64 * 64 * 3, 1) feature vector.</p>
<h2 id="sigmoid">Sigmoid</h2>
<p>$$\hat{y} = \sigma(w^Tx + b)$$</p>
<p>$w$ represents the weights, $w^T$ is the transpose of $w$, $x$ is the input, and $b$ is the bias.</p>
<h2 id="loss-function">Loss Function</h2>
<p>The loss function measures the error between the real value of $y$ and our prediction $\hat{y}$ for a <em>single training example.</em></p>
<p>$$ L(\hat{y}, y) = \frac{1}{2}(\hat{y}-y)^2 $$</p>
<h2 id="cost-function">Cost Function</h2>
<p>The average of the <em>loss</em> across all training examples.</p>
<h2 id="learning-rate">Learning Rate</h2>
<p>The learning rate $\alpha$ is the coefficient applied to update weights:</p>
<p>$$w’ = w - \alpha \frac{dd(w)}{dw}$$</p>
<p>If it is too small, learning occurs slowly; if it is too large, it may overshoot the optimum point. Thus, it should be selected wisely.</p>
<h3 id="idea-can-we-use-a-vector-instead-of-a-single-value-for-the-learning-rate">IDEA: Can we use a vector instead of a single value for the learning rate?</h3>
<p>For Leaky ReLU activation functions, this seems possible, though likely overkill. Using $\alpha$ as a vector adds a layer of complexity and requires more computation to adjust (allowing some features to update more slowly than others).</p>
<h2 id="the-feature-vector">The Feature Vector</h2>
<p>$X$ is a feature matrix with $m$ columns, each representing a different training example. Each example has $n$ features. So, <code>X.shape = (n, m)</code>.</p>
<p><code>Y.shape = (1, m)</code>.</p>
<h2 id="the-computation-graph">The Computation Graph</h2>
<p>Any formula can be converted into a graph where operands are vertices and operations are edges.</p>
<figure><img src="https://emresahin.net/images/computation-graph.jpg" alt="" style="width: 300px"></figure>
<p>In neural networks, computation flows forward from inputs to output, while the derivatives are calculated from output back to input.</p>
<h2 id="the-notation-for-layers-and-training-examples">The Notation for Layers and Training Examples</h2>
<p>$X^{(i)}$ refers to the $i^{th}$ training example.</p>
<p>$z^{[i]}$ refers to the $z$ values in the $i^{th}$ layer.</p>
<p>$z^{[1]}$ is the $z$ value for layer 1.</p>
<p>$a^{[2]}$ is the $a$ value for layer 2.</p>
<p>$a^{[1]} = [a^{[1]}_1, a^{[1]}_2, a^{[1]}_3, a^{[1]}_4]^T$ for a neural net having 4 nodes in hidden layer 1.</p>
<figure><img src="https://emresahin.net/images/2-layer-nn.jpg" alt="" width="300"></figure>
<h2 id="steps-of-computation">Steps of Computation</h2>
<p>There are two steps of computation:</p>
<ol>
<li>$z^{[1]} = w^{[1]T}x + b^{[1]}$</li>
<li>$a^{[1]} = \sigma(z^{[1]})$</li>
</ol>
<p>In step 1, weights and inputs are dot-multiplied and the bias is added. In step 2, an activation function is applied to this result. While $\sigma$ is used here, other functions can also serve this purpose.</p>
<h2 id="backpropagation-algorithm">Backpropagation Algorithm</h2>
<p>This is the fundamental algorithm that updates weights to find a solution.</p>
<p>In the forward pass, the cost function $L(\hat{y}, y)$ is computed.</p>
<p>In backpropagation, weights and biases are adjusted based on their derivatives multiplied by the learning factor $\alpha$.</p>
<p>While the actual formulas can be complex, the general idea is to calculate derivatives from the last layer back to the first and adjust weights accordingly.</p>
<h2 id="vectorization-notation">Vectorization Notation</h2>
<p>Multiple training examples are denoted by the superscript $(i)$.</p>
<p>The vectorized representation of the $Z$ matrix for multiple inputs and multiple layers is:</p>
<p>$$Z = \begin{pmatrix}
z^{<a href="1">1</a>} &amp; z^{<a href="2">1</a>} &amp; z^{<a href="3">1</a>} &amp; \dots &amp; z^{<a href="m">1</a>} \
z^{<a href="1">2</a>} &amp; z^{<a href="2">2</a>} &amp; z^{<a href="3">2</a>} &amp; \dots &amp; z^{<a href="m">2</a>} \
z^{<a href="1">3</a>} &amp; z^{<a href="2">3</a>} &amp; z^{<a href="3">3</a>} &amp; \dots &amp; z^{<a href="m">3</a>} \
\dots      &amp; \dots      &amp; \dots      &amp; \dots &amp; \dots      \
z^{<a href="1">n</a>} &amp; z^{<a href="2">n</a>} &amp; z^{<a href="3">n</a>} &amp; \dots &amp; z^{<a href="m">n</a>} \
\end{pmatrix}$$</p>
<p>The $A$ matrix is structured similarly.</p>
<p>Since $w^{[i]T}x + b^{[i]}$ is a column vector, we concatenate these column vectors for multiple inputs.</p>
<h2 id="activation-functions">Activation Functions</h2>
<p>There are roughly four types of activation functions:</p>
<h3 id="sigmoid-1">Sigmoid</h3>
<p>This is the historic default. There is generally no need to use it except in the final output layer for binary classification (0 or 1).</p>
<h3 id="tanh">Tanh</h3>
<p>It is generally superior to the sigmoid function. It is asymptotic between 1 and -1, which often leads to better training behavior.</p>
<h3 id="relu">ReLU</h3>
<p>A simple function, $r = \max(0, x)$, which has become very popular.</p>
<h3 id="leaky-relu">Leaky ReLU</h3>
<p>Since ReLU is not differentiable for $x &lt; 0$, this version adds a small slope ($r = \max(0.01x, x)$) for negative values.</p>
<h2 id="training-set-and-test-set">Training Set and Test Set</h2>
<p>Traditionally, with datasets of 100 to 10,000 elements, splits like 70/30% or 60/20/20% were common.</p>
<p>However, in the era of Big Data™, where datasets may contain 10,000,000 elements, such percentages are unnecessary. It is more reasonable to keep a fixed number, such as 10,000 elements, for the dev and test sets to speed up development.</p>
<p>Crucially, this data should come from the same distribution. Dev and test sets are used to check for overfitting and evaluate real-world performance.</p>
<h2 id="bias-and-variance">Bias and Variance</h2>
<p><em>High bias</em> indicates the model is <em>underfitting.</em>
<em>High variance</em> indicates the model is <em>overfitting.</em></p>
<h3 id="high-bias">High Bias</h3>
<p>Example: Train Set Error Rate: 15%, Dev Set Error Rate: 16%.
The model is too simple to learn the data.
Solutions: Use a larger network, train longer, or change the NN architecture.</p>
<h3 id="high-variance">High Variance</h3>
<p>Example: Train Set Error Rate: 1%, Dev Set Error Rate: 14%.
The model overfits the training data and fails to generalize.
Solutions: Get more data, use regularization, or change the NN structure.</p>
<h3 id="high-bias-and-high-variance">High Bias and High Variance</h3>
<p>While classical models often face a bias/variance tradeoff, Deep Learning models can suffer from both simultaneously (e.g., 15% Train Set error and 30% Dev Set error).
Solutions: Larger network, more data, regularization, and architecture changes.</p>
<h2 id="regularization">Regularization</h2>
<p>Used to decrease variance and prevent overfitting.
Two main types: <em>L2 Regularization</em> (adds a factor to the weights) and <em>Dropout Regularization</em> (randomly sets some weights to zero).</p>
<h3 id="l2-regularization">L2 Regularization</h3>
<p>Without regularization: $$w^{[l]} \leftarrow w^{[l]} - \alpha (dw^{[l]})$$
With regularization, we add a decay factor: $$ -\alpha (\frac{\lambda}{2m} w^{[l]}) $$
The full formula becomes: $$w^{[l]} \leftarrow (1 - \alpha \frac{\lambda}{2m}) w^{[l]} - \alpha dw^{[l]}$$
This is called <em>weight decay</em> because it pulls $w$ closer to zero, effectively making the network “smaller.”</p>
<h3 id="dropout">Dropout</h3>
<p>Dropout is a technique where the algorithm randomly “knocks out” nodes during training. These nodes are temporarily ignored during weight updates.</p>
<p>In each iteration, a random subset of nodes is removed based on a <code>keep-prob</code>. For example, a <code>keep-prob</code> of 0.5 means roughly half the nodes are ignored.</p>
<h3 id="data-augmentation">Data Augmentation</h3>
<p>Generating variations of existing images (e.g., flipping, rotating, adding noise) is also a form of regularization.
Note: Changes shouldn’t alter the image’s meaning (e.g., a flipped “4” might not be a “4”).</p>
<h3 id="early-stopping">Early Stopping</h3>
<p>As training progresses, the cost function $J$ for the training set continues to decrease, but the test set $J$ eventually begins to increase, indicating overfitting. We can stop training at that inflection point.</p>
<h2 id="normalization">Normalization</h2>
<p>Normalization involves bringing all feature values $X$ into a similar range. If $x_1$ is between 1 and 1000 and $x_2$ is between 0 and 1, the network may struggle.
A common approach is $x_i = \frac{x_i - \mu_i}{\sigma_i}$, where $\mu_i$ is the mean and $\sigma_i$ is the standard deviation.</p>
<h2 id="exploding-and-vanishing-gradients">Exploding and Vanishing Gradients</h2>
<p>In deep networks, weight values may exponentially increase or decrease. If all weights are 2, the final activation in an $l$-layer network becomes $2^l$, leading to exploding gradients. Conversely, weights less than 1 can lead to vanishing gradients where the network fails to learn.</p>
<h2 id="weight-initialization-to-alleviate-explodingvanishing-gradients">Weight Initialization to Alleviate Exploding/Vanishing Gradients</h2>
<figure><img src="https://emresahin.net/images/node-weights.jpg" alt="" width="300"></figure>
<p>To prevent these issues, it is best to initialize weights with a variance of $\frac{1}{n}$, where $n$ is the number of nodes in a layer.</p>
<p>For ReLU:</p>
<pre><code class="language-python">W[i] = np.random.randn(shape) * np.sqrt(2 / n[i-1])
</code></pre>
<p>For <code>tanh</code>:</p>
<pre><code class="language-python">W[i] = np.random.randn(shape) * np.sqrt(1 / n[i-1])
</code></pre>
<p>Another option is <em>Xavier</em> initialization:</p>
<pre><code class="language-python">W[i] = np.random.randn(shape) * np.sqrt(2 / (n[i] + n[i-1]))
</code></pre>
<h3 id="initialization-by-zero">Initialization by Zero</h3>
<p>Initializing weights to all zeros fails to break symmetry, and the network will not learn. However, it is perfectly fine to initialize <em>biases</em> to zero.</p>
<h3 id="initialization-with-random-numbers">Initialization with Random Numbers</h3>
<pre><code class="language-python">W[i] = np.random.randn(layer_dim[l], layer_dim[l-1]) * FACTOR
</code></pre>
<p>If <code>FACTOR</code> is too large (e.g., 10), the network may converge slowly or suffer from exploding gradients.
Using <code>np.sqrt(2 / layer_dim[l-1])</code> is known as <em>He</em> initialization, while <code>np.sqrt(1 / layer_dim[l-1])</code> is <em>Xavier</em> initialization.</p>
<h2 id="mini-batches">Mini-Batches</h2>
<p>When dealing with massive datasets (e.g., 10 million images), we cannot process them all at once. Instead, we divide them into <em>mini-batches.</em></p>
<ul>
<li><strong>Stochastic Gradient Descent:</strong> Batch size of 1.</li>
<li><strong>Batch Gradient Descent:</strong> Batch size equals the entire training set.</li>
<li><strong>Mini-Batch Gradient Descent:</strong> Sizes typically range from 32 to 512.</li>
</ul>
<h2 id="optimization-momentum">Optimization: Momentum</h2>
<p>When training with mini-batches, it helps to track previous gradients to smooth out updates. A hyperparameter $\beta$ determines the influence of past gradients.</p>
<h2 id="optimization-rmsprop">Optimization: RMSprop</h2>
<p>Instead of simple updates, RMSprop uses the squared gradients to scale the updates:</p>
<p>$$s_{dW} \leftarrow \beta_2 s_{dW} - (1 - \beta_2) (dW)^2$$
$$ W \leftarrow W - \alpha \frac{dW}{\sqrt{s_{dW} + \epsilon}} $$</p>
<p>This reduces oscillations and speeds up convergence.</p>
<h2 id="optimization-adam">Optimization: Adam</h2>
<p>Adam combines Momentum and RMSprop. It uses two hyperparameters, $\beta_1$ and $\beta_2$, and is generally the most effective optimization algorithm, converging much faster than others.</p>
<h2 id="learning-rate-decay">Learning Rate Decay</h2>
<p>A fixed learning rate $\alpha$ can make convergence difficult. We can decay the learning rate over time:
$\alpha = \frac{1}{1 + d * t} \alpha_0$, where $d$ is the decay rate and $t$ is the epoch number.</p>
<h1 id="hyperparameter-tuning">Hyperparameter Tuning</h1>
<p>Key hyperparameters include the learning rate $\alpha$, optimization coefficients $\beta_1$ and $\beta_2$, number of layers, units per layer, activation functions, and mini-batch sizes.</p>
<p>Heuristics:</p>
<ul>
<li>Random search is often better than a uniform grid.</li>
<li>Search on a logarithmic scale for parameters like $\alpha$ or $\beta$ (e.g., searching $1-10^r$ for $\beta$).</li>
</ul>
<h1 id="batch-normalization">Batch Normalization</h1>
<p>Batch normalization normalizes layer activations using two learnable parameters, $\beta$ and $\gamma$. It helps information flow to deeper layers and makes the network less sensitive to initial weight scales.</p>
<h1 id="softmax-activation">Softmax Activation</h1>
<p>For multi-class classification, the final layer uses a <em>softmax</em> function to output a probability vector:
$$a_i^{[l]} = \frac{e^{z_i^{[l]}}}{\sum_{j=1}^C e^{z_j^{[l]}}}$$
where $C$ is the number of classes.</p>
<h1 id="metrics">Metrics</h1>
<p>Single-number metrics like F1 Score or mAP make evaluation much easier.</p>
<ul>
<li><strong>Optimizing Metric:</strong> The primary number you try to improve.</li>
<li><strong>Satisficing Metric:</strong> A threshold that must be met (e.g., running time &lt; 100ms).</li>
</ul>
<h1 id="error-analysis">Error Analysis</h1>
<p>Regularly inspect a subset of misclassified examples to understand where the model is failing.</p>
<h1 id="data-mismatch">Data Mismatch</h1>
<p>If your training and test data come from different distributions, use a <em>training-dev</em> set to identify if performance drops are due to the model failing to generalize or a fundamental difference in data.</p>
<h1 id="transfer-learning">Transfer Learning</h1>
<p>You can take a model trained on task A and repurpose it for task B. This is especially useful when task B has limited data.</p>
<h1 id="multi-task-learning">Multi-Task Learning</h1>
<p>One network can be trained to perform multiple tasks simultaneously (e.g., detecting pedestrians AND traffic signs), provided the network is large enough.</p>
<h1 id="end-to-end-learning">End-to-End Learning</h1>
<p>In end-to-end learning, the network maps raw input directly to the final output, bypassing traditional hand-engineered feature extraction steps. This requires significant amounts of data to be effective.</p>
<h1 id="convolutional-neural-networks-cnns">Convolutional Neural Networks (CNNs)</h1>
<p>CNNs are the standard for image processing. They apply filters (kernels) to images to extract features. The resulting matrix size is determined by the input size $n$ and filter size $f$ (result size: $n-f+1$).</p>
<h2 id="padding">Padding</h2>
<p>Padding adds a border of pixels (usually zeros) around the input to prevent the image from shrinking after each convolution layer.</p>
<h1 id="classic-networks">Classic Networks</h1>
<h2 id="lenet-5">LeNet-5</h2>
<p>A 1998 network designed for digit recognition (32x32x1). It is small (60k parameters) but foundational.</p>
<h2 id="alexnet-and-vgg-16">AlexNet and VGG-16</h2>
<p>AlexNet (2012) popularized Deep Learning. VGG-16 (138M parameters) used a very consistent architecture of 3x3 filters and max-pooling.</p>
<h1 id="resnets">ResNets</h1>
<p>Residual Networks use “shortcuts” or “skip connections” to allow gradients to flow through very deep networks (100+ layers) without vanishing.</p>
<h1 id="1x1-convolutions">1x1 Convolutions</h1>
<p>1x1 convolutions are used to reduce or increase the number of channels (depth) in a volume while adding non-linearity.</p>
<h1 id="inception-network">Inception Network</h1>
<p>Inception modules apply 1x1, 3x3, 5x5 convolutions and pooling in parallel, then concatenate the results.</p>
<h1 id="object-detection">Object Detection</h1>
<h2 id="yolo-algorithm">YOLO Algorithm</h2>
<p>“You Only Look Once” divides an image into a grid (e.g., 19x19) and predicts bounding boxes and class probabilities for each cell simultaneously.</p>
<h2 id="anchor-boxes">Anchor Boxes</h2>
<p>Anchor boxes allow a single grid cell to detect multiple objects (e.g., a person standing in front of a car).</p>
<h2 id="non-max-suppression">Non-Max Suppression</h2>
<p>NMS filters out overlapping bounding boxes, keeping only the most confident ones.</p>
<h1 id="face-recognition">Face Recognition</h1>
<h2 id="one-shot-learning">One-Shot Learning</h2>
<p>Learning to recognize a person from just one image. This is often solved using a Siamese network that learns a similarity metric.</p>
<h2 id="similarity-metric">Similarity Metric</h2>
<p>A function $f(A)$ that maps an image to a feature vector. The distance $d(A, B) = ||f(A) - f(B)||^2$ tells us how similar two faces are.</p>
<h1 id="visual-style-transfer">Visual Style Transfer</h1>
<p>Style transfer creates a new image $G$ that combines the content of image $C$ with the artistic style of image $S$. This is achieved by minimizing a cost function with both content and style components.</p>
<h1 id="word-embeddings">Word Embeddings</h1>
<p>Word embeddings represent words as dense vectors where similar words have similar vectors.</p>
<ul>
<li><strong>Analogies:</strong> $e_{king} - e_{man} + e_{woman} \approx e_{queen}$.</li>
<li><strong>Cosine Similarity:</strong> A common measure of vector similarity.</li>
</ul>
<h2 id="word2vec">word2vec</h2>
<p>A popular algorithm for learning word embeddings by predicting a word from its neighbors (or vice versa).</p>]]></content:encoded>
    </item>
    <item>
      <title>Probabilistic Graphical Models Course Notes</title>
      <published>2014-08-07T21:00:00+00:00</published>
      <updated>2014-08-07T21:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 07 Aug 2014 21:00:00 +0000</pubDate>
      <link>https://emresahin.net/pgm-course-notes/</link>
      <guid isPermaLink="true">https://emresahin.net/pgm-course-notes/</guid>
      <description>Preliminaries Distributions Suppose variable $A$ has 2, $B$ has 2, and $C$ has 3 possible values. Their joint probability distribution will contain $2 \times 2 \times 3 = 12$ values. We can condition the distribution by setting a variable to a specific value. We can also marginalize the distribut...</description>
      <category>Learning</category>
      <category>Data Science</category>
      <category>Probabilistic Graphical Models</category>
      <category>PGM</category>
      <category>Bayesian</category>
      <category>Machine Learning</category>
      <category>Statistics</category>
      <content:encoded><![CDATA[<h1 id="preliminaries">Preliminaries</h1>
<h2 id="distributions">Distributions</h2>
<p>Suppose variable $A$ has 2, $B$ has 2, and $C$ has 3 possible values. Their <strong>joint probability distribution</strong> will contain $2 \times 2 \times 3 = 12$ values.</p>
<p>We can <em>condition</em> the distribution by setting a variable to a specific value.</p>
<p>We can also <em>marginalize</em> the distribution over a subset of variables to examine the distribution of a single variable.</p>
<h1 id="factors">Factors</h1>
<p>A <em>factor</em> $\phi$ is a function that maps values of variables (e.g., $A$, $B$, and $C$) to a real number.</p>
<p>The set of variables that the factor takes as arguments is called the <em>scope</em> of the factor.</p>
<p>Both normalized and unnormalized measures are examples of factors, although their scopes may vary.</p>
<p>A <strong>Conditional Probability Distribution (CPD)</strong> is another common example of a factor.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
