<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://kostyaev.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://kostyaev.github.io/" rel="alternate" type="text/html" /><updated>2025-11-05T19:26:11+00:00</updated><id>https://kostyaev.github.io/feed.xml</id><title type="html">Dmitry Kostiaev</title><subtitle>Machine Learning Research Engineer</subtitle><entry><title type="html">Nicer Causal Convs for Tensorflow 2</title><link href="https://kostyaev.github.io/2020/01/13/Better_causal.html" rel="alternate" type="text/html" title="Nicer Causal Convs for Tensorflow 2" /><published>2020-01-13T00:00:00+00:00</published><updated>2020-01-13T00:00:00+00:00</updated><id>https://kostyaev.github.io/2020/01/13/Better_causal</id><content type="html" xml:base="https://kostyaev.github.io/2020/01/13/Better_causal.html"><![CDATA[<p>Causal 1D convolution are quite useful when working with autoregressive models like WaveNet. To define this layer in tensorflow 2 we just pass “causal” padding in Conv1D layer arguments as is the following one liner:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">conv_layer</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Conv1D</span><span class="p">(</span>
    <span class="n">filters</span><span class="o">=</span><span class="mi">64</span><span class="p">,</span> 
    <span class="n">kernel_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
    <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> 
    <span class="n">padding</span><span class="o">=</span><span class="s">'causal'</span><span class="p">)</span>
</code></pre></div></div>

<p>Calling this layer will preserve the temporal dimension of the input by adding left padding which is not always desirable.  As we stack more and more layers with larger dilation rate padding will become a large portion of the input data. Also the default implementation of dilated convolution layer implicitly define some padding based on your first input restricting your choice of the input shape later on.</p>

<!--more-->

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x</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="n">randn</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="mi">96</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
<span class="n">conv_layer</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">shape</span>
<span class="o">--&gt;</span> <span class="n">TensorShape</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="mi">64</span><span class="p">])</span>
</code></pre></div></div>

<p>Now if you try to feed some other data with different shape you’ll get an error:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">y</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="n">randn</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">140</span><span class="p">,</span> <span class="mi">96</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
<span class="n">conv_layer</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
<span class="o">--&gt;</span> <span class="n">InvalidArgumentError</span><span class="p">:</span> <span class="n">padded_shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=</span><span class="mi">146</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">divisible</span> <span class="n">by</span> <span class="n">block_shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">=</span><span class="mi">4</span> <span class="p">[</span><span class="n">Op</span><span class="p">:</span><span class="n">SpaceToBatchND</span><span class="p">]</span>
</code></pre></div></div>

<p>We can handle dilation ourselves as we want in DilatedConv1D class that inherits from the built-in Conv1D layer.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">tensorflow</span> <span class="k">as</span> <span class="n">tf</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="nn">tensorflow.keras</span> <span class="kn">import</span> <span class="n">layers</span>

<span class="k">def</span> <span class="nf">time_to_batch</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">dilation</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">shape</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">shape</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="n">pad_elements</span> <span class="o">=</span> <span class="n">dilation</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="p">(</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">dilation</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">dilation</span>
    <span class="n">padded</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">pad</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="p">[[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="n">pad_elements</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]])</span>
    <span class="n">reshaped</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">padded</span><span class="p">,</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">dilation</span><span class="p">,</span> <span class="n">shape</span><span class="p">[</span><span class="mi">2</span><span class="p">]])</span>
    <span class="n">transposed</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="n">reshaped</span><span class="p">,</span> <span class="n">perm</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">tf</span><span class="p">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">transposed</span><span class="p">,</span> <span class="p">[</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">dilation</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">shape</span><span class="p">[</span><span class="mi">2</span><span class="p">]]),</span> <span class="n">pad_elements</span>


<span class="k">def</span> <span class="nf">batch_to_time</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">dilation</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">shape</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">shape</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="n">prepared</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="p">[</span><span class="n">dilation</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">shape</span><span class="p">[</span><span class="mi">2</span><span class="p">]])</span>
    <span class="n">transposed</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">transpose</span><span class="p">(</span><span class="n">prepared</span><span class="p">,</span> <span class="n">perm</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">tf</span><span class="p">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">transposed</span><span class="p">,</span>
                      <span class="p">[</span><span class="n">tf</span><span class="p">.</span><span class="n">divide</span><span class="p">(</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">dilation</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">shape</span><span class="p">[</span><span class="mi">2</span><span class="p">]])</span>


<span class="k">class</span> <span class="nc">DilatedConv1D</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Conv1D</span><span class="p">):</span>
    
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nb_filters</span><span class="p">,</span> <span class="n">kernel_size</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">(</span><span class="n">nb_filters</span><span class="p">,</span> <span class="n">kernel_size</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">custom_dilation</span> <span class="o">=</span> <span class="n">dilation_rate</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">dilation_rate</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">)</span> <span class="k">else</span> <span class="p">(</span><span class="n">dilation_rate</span><span class="p">,</span> <span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_tensor</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">custom_dilation</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">x</span><span class="p">,</span> <span class="n">pad</span> <span class="o">=</span> <span class="n">time_to_batch</span><span class="p">(</span><span class="n">input_tensor</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">custom_dilation</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">x</span> <span class="o">=</span> <span class="nb">super</span><span class="p">().</span><span class="n">call</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
            <span class="n">output</span> <span class="o">=</span> <span class="n">batch_to_time</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">custom_dilation</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">width</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">shape</span><span class="p">(</span><span class="n">output</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">pad</span>
            <span class="n">output</span> <span class="o">=</span> <span class="n">output</span><span class="p">[:,:</span><span class="n">width</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">output</span> <span class="o">=</span> <span class="nb">super</span><span class="p">().</span><span class="n">call</span><span class="p">(</span><span class="n">input_tensor</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">output</span>
</code></pre></div></div>

<p>Now we can implement CausalConv1D on top of DilatedConv1D so that we will be able to handle varying input shapes and control whether we want causal left padding or not with TF-like padding keywords: “valid” and “same”.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CausalConv1D</span><span class="p">(</span><span class="n">DilatedConv1D</span><span class="p">):</span>
    
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nb_filters</span><span class="p">,</span> <span class="n">kernel_size</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s">'valid'</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">allowed_paddings</span> <span class="o">=</span> <span class="p">[</span><span class="s">'same'</span><span class="p">,</span> <span class="s">'valid'</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">padding</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_paddings</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'Unknown padding, allowed: %s'</span> <span class="o">%</span> <span class="nb">str</span><span class="p">(</span><span class="n">allowed_paddings</span><span class="p">))</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">(</span><span class="n">nb_filters</span><span class="p">,</span> <span class="n">kernel_size</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">causal_padding</span> <span class="o">=</span> <span class="n">padding</span>

    <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_tensor</span><span class="p">,</span>  <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">pad</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">custom_dilation</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">kernel_size</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">causal_padding</span> <span class="o">==</span> <span class="s">'same'</span><span class="p">:</span>
            <span class="n">causal_padding</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="n">pad</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]]</span>
            <span class="n">padded_tensor</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">pad</span><span class="p">(</span><span class="n">input_tensor</span><span class="p">,</span> <span class="n">paddings</span><span class="o">=</span><span class="n">causal_padding</span><span class="p">)</span>
            <span class="n">output</span> <span class="o">=</span> <span class="nb">super</span><span class="p">().</span><span class="n">call</span><span class="p">(</span><span class="n">padded_tensor</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span> 
            <span class="n">out_width</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">shape</span><span class="p">(</span><span class="n">input_tensor</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">pad</span>
            <span class="n">output</span> <span class="o">=</span> <span class="nb">super</span><span class="p">().</span><span class="n">call</span><span class="p">(</span><span class="n">input_tensor</span><span class="p">)[:,</span> <span class="p">:</span><span class="n">out_width</span><span class="p">]</span>
            
        <span class="k">return</span> <span class="n">output</span>
    
</code></pre></div></div>

<p>Let’s try our new causal layer in action. We’ll create two identical architectures but with different causal layer implementations and since we set the same weights for both networks they should return the same result.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">OriginalNetwork</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">OriginalNetwork</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">layer_stack</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Conv1D</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s">'causal'</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s">'relu'</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
            <span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Conv1D</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s">'causal'</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s">'relu'</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span>
            <span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Conv1D</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s">'causal'</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s">'relu'</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">4</span><span class="p">),</span>
            <span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">Conv1D</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s">'causal'</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span>
        <span class="p">]</span>

    <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_tensor</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">input_tensor</span>
        <span class="k">for</span> <span class="n">layer</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">layer_stack</span><span class="p">:</span>
            <span class="n">x</span> <span class="o">=</span> <span class="n">layer</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">x</span>

    
<span class="k">class</span> <span class="nc">ModifiedNetwork</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">keras</span><span class="p">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="s">'valid'</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">ModifiedNetwork</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">layer_stack</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">CausalConv1D</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s">'relu'</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="n">padding</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
            <span class="n">CausalConv1D</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s">'relu'</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="n">padding</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span>
            <span class="n">CausalConv1D</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">activation</span><span class="o">=</span><span class="s">'relu'</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="n">padding</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">4</span><span class="p">),</span>
            <span class="n">CausalConv1D</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="n">padding</span><span class="p">,</span> <span class="n">dilation_rate</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span>
        <span class="p">]</span>
            

    <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_tensor</span><span class="p">,</span> <span class="n">training</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">input_tensor</span>
        <span class="k">for</span> <span class="n">layer</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">layer_stack</span><span class="p">:</span>
            <span class="n">x</span> <span class="o">=</span> <span class="n">layer</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">x</span>
    
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x</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="n">randn</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="mi">96</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
<span class="n">net1</span> <span class="o">=</span> <span class="n">OriginalNetwork</span><span class="p">()</span>
<span class="n">net2</span> <span class="o">=</span> <span class="n">ModifiedNetwork</span><span class="p">(</span><span class="n">padding</span><span class="o">=</span><span class="s">'same'</span><span class="p">)</span>
<span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">net1</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">net2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">net1</span><span class="p">.</span><span class="n">set_weights</span><span class="p">(</span><span class="n">net2</span><span class="p">.</span><span class="n">get_weights</span><span class="p">())</span>
<span class="n">output1</span><span class="p">,</span> <span class="n">output2</span> <span class="o">=</span> <span class="n">net1</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">net2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Shapes are equal: '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">((</span><span class="n">output1</span><span class="p">.</span><span class="n">shape</span> <span class="o">==</span> <span class="n">output2</span><span class="p">.</span><span class="n">shape</span><span class="p">)))</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Values are equal: '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">((</span><span class="n">output1</span> <span class="o">==</span> <span class="n">output2</span><span class="p">).</span><span class="n">numpy</span><span class="p">().</span><span class="nb">all</span><span class="p">()))</span>
</code></pre></div></div>

<p>Outputs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Shapes are equal: True
Values are equal: True
</code></pre></div></div>

<p>With valid padding the modified network should output reduced temporal dimension.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x</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="n">randn</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">150</span><span class="p">,</span> <span class="mi">96</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
<span class="n">net1</span> <span class="o">=</span> <span class="n">OriginalNetwork</span><span class="p">()</span>
<span class="n">net2</span> <span class="o">=</span> <span class="n">ModifiedNetwork</span><span class="p">(</span><span class="n">padding</span><span class="o">=</span><span class="s">'valid'</span><span class="p">)</span>
<span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">net1</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">net2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">net1</span><span class="p">.</span><span class="n">set_weights</span><span class="p">(</span><span class="n">net2</span><span class="p">.</span><span class="n">get_weights</span><span class="p">())</span>
<span class="n">output1</span><span class="p">,</span> <span class="n">output2</span> <span class="o">=</span> <span class="n">net1</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">net2</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Shapes are: '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">((</span><span class="n">output1</span><span class="p">.</span><span class="n">shape</span><span class="p">,</span> <span class="n">output2</span><span class="p">.</span><span class="n">shape</span><span class="p">)))</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Values are equal: '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">((</span><span class="n">output1</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">30</span><span class="p">:]</span> <span class="o">==</span> <span class="n">output2</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="n">numpy</span><span class="p">().</span><span class="nb">all</span><span class="p">()))</span>

</code></pre></div></div>

<p>Outputs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Shapes are: (TensorShape([1, 150, 1]), TensorShape([1, 120, 1]))
Values are equal: True
</code></pre></div></div>]]></content><author><name></name></author><category term="conv1d" /><category term="causal" /><category term="wavenet" /><category term="autoregressive" /><category term="tensorflow" /><category term="dilated" /><summary type="html"><![CDATA[Causal 1D convolution are quite useful when working with autoregressive models like WaveNet. To define this layer in tensorflow 2 we just pass “causal” padding in Conv1D layer arguments as is the following one liner: conv_layer = tf.keras.layers.Conv1D( filters=64, kernel_size=2, dilation_rate=4, padding='causal') Calling this layer will preserve the temporal dimension of the input by adding left padding which is not always desirable. As we stack more and more layers with larger dilation rate padding will become a large portion of the input data. Also the default implementation of dilated convolution layer implicitly define some padding based on your first input restricting your choice of the input shape later on.]]></summary></entry><entry><title type="html">Forward-Backward Algorithm</title><link href="https://kostyaev.github.io/2019/09/18/Forward-Backward.html" rel="alternate" type="text/html" title="Forward-Backward Algorithm" /><published>2019-09-18T00:00:00+00:00</published><updated>2019-09-18T00:00:00+00:00</updated><id>https://kostyaev.github.io/2019/09/18/Forward-Backward</id><content type="html" xml:base="https://kostyaev.github.io/2019/09/18/Forward-Backward.html"><![CDATA[<p>Forward-backward algorithm (FB) is a particular case of a dynamic programming. This algorithm is well known in the context of Hidden Markov Models (HMM) where it is used for training and inference, Kalman Smoothers and Connectionist Temporal Classification (<a href="http://www.cs.toronto.edu/~graves/icml_2006.pdf">CTC</a>). 
It consists of two passes: the first pass goes forward in time and second pass goes backward, hence the name.</p>

<p>FB algorithm heavily relies on Markov property and output independence assumptions from HMM. Actually a lot of terms here are inherited from HMM, so if you’re not familliar then this <a href="http://cs229.stanford.edu/section/cs229-hmm.pdf">HMM tutorial</a> might come in handy.</p>

<p>Suppose that \(z=\{z_1,z_2,..,z_n\}\) is hidden variable, $x={z_1,z_2,..,z_n}$ is observable variable and $k=1..n$ denotes timestep, then</p>

<ul>
  <li>\(p(x_k\vert z_k)\) is an Emission probability;</li>
  <li>$p(z_k\vert z_{k-1})$ is a Transition probability;</li>
  <li>$p(z_1)$ is an Initial probability.</li>
</ul>

<p>To simplify the notation let’s further assume that hidden state variable $z_i$ defined on an interger set ${1,..,m}$</p>

<p>The goal of FB algorithm is to compute $p(z_k\vert x)$.</p>

<p>The forward pass computes: $p(z_k,x_{1:k}) \forall k=1,..,n$</p>

<p>The backward pass computes: $p(x_{k+1:n} \vert  z_k) \forall k=1,..,n$</p>

<p>For now let’s assume that we’ve already computed forward and backward parts, in other words we know $p(z_k,x_{1:k})$ and $p(x_{k+1:n} \vert  z_k)$. How does it help us to get desired $p(z_k\vert x)$?</p>

\[\begin{align}
p(z_k\vert x) \propto p(z_k,x) &amp;= p(z_k, x_{1:k}, x_{k+1:n}) \\
&amp;= p(x_{k+1:n}\vert x_{1:k}, z_k) p(z_k, x_{1:k}) \\
&amp;= p(x_{k+1:n}\vert z_k) p(z_k, x_{1:k})
\end{align}\]

<p>The last step applies <a href="http://www.andrew.cmu.edu/user/scheines/tutor/d-sep.html">D-separation rule</a> ($x_{k+1:n}$ is conditionally independent of $x_{1:k}$ given $z_k$). After simplification we have:</p>

\[p(z_k\vert x) \propto p(x_{k+1:n}\vert z_k) p(z_k, x_{1:k})\]

<p>And we already know that $p(z_k, x_{1:k}), p(x_{k+1:n}\vert z_k)$ can be found with forward and backward algorithms. The proportionality is instead of equality is due to omitting normalizing constant $p(x)$ which we can compute by marginalizing $p(x,z_k)$ over all finite set of $z_k$:
\(p(x) = \sum_{z_{k}=1}^{m} p(x,z_k)\)</p>

<h2 id="forward-algorithm">Forward algorithm</h2>

\[\begin{align}
p(z_k, x_{1:k}) &amp;= \sum_{z_{k-1}=1}^{m} p(z_k,z_{k-1},x_{1:k}) \\
&amp;= \sum_{z_{k-1}=1}^{m} p(x_k\vert z_k,z_{k-1},x_{1:k-1})
                     p(z_k\vert z_{k-1},x_{1:k-1})
                     p(z_{k-1},x_{1:k-1}) \\
&amp;= \sum_{z_{k-1}=1}^{m} p(x_k\vert z_k) 
                     p(z_k\vert z_{k-1}) 
                     p(z_{k-1},x_{1:k-1})
\end{align}\]

<ul>
  <li>$p(x_k\vert z_k)$ is given as an emission probability</li>
  <li>$p(z_k\vert z_{k-1})$ is given as a transition probability</li>
  <li>$p(z_{k-1},x_{1:k-1})$ is unknown, but you can notice the recurrence here if you’ll look at what we are trying to compute: $p(z_k, x_{1:k})$</li>
</ul>

<p>To find $p(z_k, x_{1:k})$ we need to compute $p(z_{k-1},x_{1:k-1})$, for $p(z_{k-1},x_{1:k-1})$ you have to compute $p(z_{k-2},x_{1:k-2})$ and so on until $p(z_{1},x_{1})$. And $p(z_{1},x_{1})$ is just an emission probability that is known. Now we know all the parts to achieve our goal finding $p(z_k, x_{1:k})$.</p>

<p>What about time complexity? We have $n$ timesteps ($k=1..n$) and inside each timestep we iterate over $m$ values of $z_k$ and $m$ values of $z_{k-1}$, this gives us $O(nm^2)$ time complexity.</p>

<h2 id="backward-algorithm">Backward algorithm</h2>

\[\begin{align}
p(x_{k+1:n}\vert z_k) &amp;= \sum_{z_{k+1}=1}^{m} p(x_{k+1:n}, z_{k+1} \vert  z_k) \\
&amp;= \sum_{z_{k+1}=1}^{m} p(x_{k+2:n} \vert  x_{k+1}, z_{k+1}, z_k)  p(x_{k+1}\vert z_{k+1}, z_k) p(z_{k+1}\vert z_k) \\
&amp;= \sum_{z_{k+1}=1}^{m} p(x_{k+2:n} \vert  z_{k+1}) p(x_{k+1}\vert z_{k+1}) p(z_{k+1}\vert z_k)
\end{align}\]

<ul>
  <li>$p(x_{k+2:n} \vert  z_{k+1})$ can be computed recurrently</li>
  <li>$p(x_{k+1}\vert z_{k+1})$ is given as an emission probability</li>
  <li>$p(z_{k+1}\vert z_{k})$ is given as a transition probability</li>
</ul>

<p>We compute $p(x_{k+2:n} \vert  z_{k+1})$ recurrently until we reach $p(x_n\vert z_{n-1})$. We can do the same routine as previously:</p>

\[\begin{align}
p(x_n\vert z_{n-1}) &amp;= \sum_{z_n=1}^{m} p(x_n,z_n\vert z_{n-1}) \\
&amp;= \sum_{z_n=1}^{m} p(x_n\vert z_n,z_{n-1}) p(z_n\vert z_{n-1}) \\
&amp;= \sum_{z_n=1}^{m} p(x_n\vert z_n) p(z_n\vert z_{n-1}) 
\end{align}\]

<p>This formula is a special case of previous more general formula where the term $p(x_{k+2:n} \vert  z_{k+1})$ has become equal to 1, and if $k=n-1$ theh we’ll get $p(x_{n+1} \vert  z_n) = 1$.</p>

<p>Summarizing all that in the final formula:</p>

\[p(x_{k+1:n}\vert z_k) = \sum_{z_{k+1}=1}^{m} p(x_{k+2:n} \vert  z_{k+1}) p(x_{k+1}\vert z_{k+1}) p(z_{k+1}\vert z_k) \\
p(x_{n+1} \vert  z_n) = 1\]

<p>This is quite similar to forward part and time complexity is also $O(nm^2)$ because we have $k=1..n$ timesteps, $m$ values of $z_k$ and $z_{k+1}$ that we iterate through.</p>

<h2 id="naive-approach">Naive approach</h2>

<p>To better understand the value of FB algorithm let’s just compare it with the naive approach. With conditional probability formula we know that:</p>

\[p(z_k \vert x) = \frac{p(x,z_k)}{p(x)} \propto p(x,z_k)\]

<p>We can try directly compute $p(x,z_k)$ and for that we have to take into account all possible sequences of $z$ where $z_k$ might have been occured or in other words we should marginalize out all except $z_k$: $\hat{z} = z_1..z_{k-1},z_{k+1}..z_n$ variables:</p>

\[\begin{align}
p(x,z_k) &amp;= \sum_{\hat{z}} p(x,z) \\
&amp;= \sum_{z_1} \sum_{z_2} ... \sum_{z_{k-1}} \sum_{z_{k+1}} ... \sum_{z_{n}} p(x,z) 
\end{align}\]

<p>The number of sums is equal to number of possible permutations with repetitions of hidden state sequence $\hat{z}$ which is equal to $m^{n-1}$. This is an exponential time complexity, for $T = 101$ steps and $m = 10$ hidden states it will have $10^{100}$ terms to compute. While Forward-Backward algorithm will have $m^2 \times n = 100\times101 \approx 10^4$, that is $10^{96}$ times faster!</p>]]></content><author><name></name></author><category term="HMM" /><category term="forward-backward" /><summary type="html"><![CDATA[Forward-backward algorithm (FB) is a particular case of a dynamic programming. This algorithm is well known in the context of Hidden Markov Models (HMM) where it is used for training and inference, Kalman Smoothers and Connectionist Temporal Classification (CTC). It consists of two passes: the first pass goes forward in time and second pass goes backward, hence the name.]]></summary></entry><entry><title type="html">Playing with image embeddings</title><link href="https://kostyaev.github.io/computer%20vision/2017/04/17/CNN-embeddings.html" rel="alternate" type="text/html" title="Playing with image embeddings" /><published>2017-04-17T00:00:00+00:00</published><updated>2017-04-17T00:00:00+00:00</updated><id>https://kostyaev.github.io/computer%20vision/2017/04/17/CNN-embeddings</id><content type="html" xml:base="https://kostyaev.github.io/computer%20vision/2017/04/17/CNN-embeddings.html"><![CDATA[<p>Quite a while ago I worked on image retrieval and made a little experiment with algebraic operations on image embeddings extracted from convolutional network. I downloaded a set of publicly available photos, extracted feature vectors using pretrained ResNet 50 and applied cosine distance KNN search using linear combinations of some query vectors. 
<!--more-->
All documents and queries can be encoded with the following few lines of code using Caffe:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">encode</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="n">img</span> <span class="o">=</span> <span class="n">open_image</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    <span class="n">img</span> <span class="o">=</span> <span class="n">preprocess</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">asarray</span><span class="p">([</span><span class="n">img</span><span class="p">])</span>
    <span class="k">if</span> <span class="n">net</span><span class="p">.</span><span class="n">blobs</span><span class="p">[</span><span class="s">'data'</span><span class="p">].</span><span class="n">data</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">net</span><span class="p">.</span><span class="n">blobs</span><span class="p">[</span><span class="s">'data'</span><span class="p">].</span><span class="n">reshape</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">224</span><span class="p">,</span><span class="mi">224</span><span class="p">)</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">net</span><span class="p">.</span><span class="n">forward</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">net</span><span class="p">.</span><span class="n">blobs</span><span class="p">[</span><span class="s">'pool5'</span><span class="p">].</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">flatten</span><span class="p">().</span><span class="n">copy</span><span class="p">()</span>
</code></pre></div></div>

<p>Encode your documents and queries and perform nearest neighbor (NN) search. For fast NN search I put vectors into <a href="https://github.com/spotify/annoy">Annoy</a> index.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    
<span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">query_vector</span><span class="p">):</span>
    <span class="c1"># get 9 ids of nearest vectors from index
</span>    <span class="n">ids</span> <span class="o">=</span> <span class="n">index</span><span class="p">.</span><span class="n">get_nns_by_vector</span><span class="p">(</span><span class="n">query_vector</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
    <span class="c1"># load images by ids and show them
</span>    <span class="n">images</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="n">ids</span><span class="p">:</span>
        <span class="n">img</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">id2file</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
        <span class="k">if</span> <span class="n">img</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">img</span> <span class="o">=</span> <span class="n">centeredCrop</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">resize</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">128</span><span class="p">)),</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">128</span><span class="p">)</span>
        <span class="n">images</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="mi">10</span><span class="p">))</span>
    <span class="n">show</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">images</span><span class="p">))</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">sea</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_29_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">sea</span><span class="o">+</span><span class="n">woman</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_30_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">building</span> <span class="o">+</span> <span class="n">crowd</span><span class="o">*</span><span class="mf">0.5</span> <span class="o">+</span> <span class="n">sea</span><span class="o">*</span><span class="mf">1.5</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_31_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">woman</span> <span class="o">+</span> <span class="n">car</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="n">dress</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_32_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">coffee</span><span class="o">*</span><span class="mf">1.3</span> <span class="o">+</span> <span class="n">burger</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_33_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">man</span> <span class="o">+</span> <span class="n">dress</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_34_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">woman_in_dress</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_35_0.png" alt="png" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">search</span><span class="p">(</span><span class="n">woman_in_dress</span> <span class="o">-</span> <span class="n">dress</span><span class="p">)</span>
</code></pre></div></div>

<p><img src="/images/vectors/output_36_0.png" alt="png" /></p>]]></content><author><name></name></author><category term="Computer Vision" /><category term="computer vision" /><category term="features vectors" /><category term="linear operations" /><summary type="html"><![CDATA[Quite a while ago I worked on image retrieval and made a little experiment with algebraic operations on image embeddings extracted from convolutional network. I downloaded a set of publicly available photos, extracted feature vectors using pretrained ResNet 50 and applied cosine distance KNN search using linear combinations of some query vectors.]]></summary></entry><entry><title type="html">Semantic clustering of images</title><link href="https://kostyaev.github.io/computer%20vision/2016/03/16/Semantic-Clustering-of-Images.html" rel="alternate" type="text/html" title="Semantic clustering of images" /><published>2016-03-16T00:00:00+00:00</published><updated>2016-03-16T00:00:00+00:00</updated><id>https://kostyaev.github.io/computer%20vision/2016/03/16/Semantic%20Clustering%20of%20Images</id><content type="html" xml:base="https://kostyaev.github.io/computer%20vision/2016/03/16/Semantic-Clustering-of-Images.html"><![CDATA[<p>Recent studies in the field of computer vision have shown the abilities of deep convolutional neural networks to learn high level image representation. These representations have reach semantic and can be very handy in various visual tasks. One of such task that can make use of high level features is semantic clusterization. Let’s find some pictures in Google Web Search, feed them to CNN and apply k-means to the extracted feautures and take the biggest clusters, here is what I got for different queries:</p>

<p>Query “extreme”:
<img src="/images/semantic/extreme_grid.png" alt="extreme" /></p>

<p>On the left is the query result and on the right - some groups of images. It’s clear that pictures in the same group are closer in semantic than images across. Here we got two clusters, first for the American rock band and the second for some extreme activity.</p>

<p>Query “destruction”:</p>

<p><img src="/images/semantic/distruction_grid.png" alt="distruction" /></p>

<p>Now we have three clusters and arguably three slightly different meanings: an explosion, destroyed buildings and a forest fire. Let’s try a query that for sure should show images with different semantic.</p>

<p>Query “apple”:</p>

<p><img src="/images/semantic/apple_grid.png" alt="apple" /></p>

<p>As you might guess Google will return results containing some Apple products and we can automatically move images with different meaning to their semantic group by applying clustering algorithm on CNN codes. Having multiple images in top-k biggest clusters we can pull out centroids and use them as different representation of the same concept. Let’s do this for our examples:</p>

<p><img src="/images/semantic/centroids.png" alt="centroids" /></p>

<p>Here centroids of the “distruction” clusters can be thought as different representations of a destruction. There are many other applications, for example, semantic clusters can help to find the most popular meaning for some text term. We can also apply clustering to filter duplicates as they tend to fall into the same group, in this case we need to select images from different groups.</p>

<p>So coming to conclusion, deep convolutional neural networks can produce very good abstractions that can be further used in many visual recognition tasks that hardly be possible without them.</p>]]></content><author><name></name></author><category term="Computer Vision" /><category term="image" /><category term="semantic" /><category term="clustering" /><category term="kmeans" /><summary type="html"><![CDATA[Semantic clustering of images with a convolutional neural network]]></summary></entry><entry><title type="html">Why is top-5 error useful for evaluating ILSVRC models</title><link href="https://kostyaev.github.io/computer%20vision/2016/03/01/Why-top-5-error-is-more-fair-metric-than-top-1-for-ImageNet-classification-task.html" rel="alternate" type="text/html" title="Why is top-5 error useful for evaluating ILSVRC models" /><published>2016-03-01T00:00:00+00:00</published><updated>2016-03-01T00:00:00+00:00</updated><id>https://kostyaev.github.io/computer%20vision/2016/03/01/Why%20top-5%20error%20is%20more%20fair%20metric%20than%20top-1%20for%20ImageNet%20classification%20task</id><content type="html" xml:base="https://kostyaev.github.io/computer%20vision/2016/03/01/Why-top-5-error-is-more-fair-metric-than-top-1-for-ImageNet-classification-task.html"><![CDATA[<p>For the most publicly available models trained on ImageNet dataset <a href="http://stats.stackexchange.com/questions/156471/imagenet-what-is-top-1-and-top-5-error-rate">top-5</a> and <a href="http://stats.stackexchange.com/questions/156471/imagenet-what-is-top-1-and-top-5-error-rate">top-1</a> errors are reported. The best perfoming <a href="https://github.com/KaimingHe/deep-residual-networks">model</a> from <a href="http://image-net.org/challenges/LSVRC/2015/">ILSVRC 2015</a> has 6.7% top-5 error and 23% top-1 error evaluated using singe center crop. It’s pretty interesting to find out that human error turned out to be not much better, top-5 error is around 5.1%  according to the result of some experiments descibed in the <a href="http://karpathy.github.io/2014/09/02/what-i-learned-from-competing-against-a-convnet-on-imagenet/">article</a>.
<!--more-->
An ensembe of models show even better results, six models of different depth leads to 3.57% top-5 error (1st place in ILSVRC 2015). Top-1 error still seems to be very big and one may say that top-5 rule is very forgiving and rules should be tightened and only top-1 should be considered. But top-1 error doesn’t really give you an understanding of how good is you neural network in general, it can only be useful to compare the perfomance of different models. The reason is that ImageNet is a single label dataset containing images that actually can fall into several categories and the orded of those categories is ambiguous. To illustare this let’s look at some of the example images from the training set, starting from example easy to classify:</p>

<p>Sport cars 
<img src="/images/sport_cars.jpg" alt="race_cars" height="250px" width="750px" /></p>

<p>Race cars 
<img src="/images/race_cars.jpg" alt="race_cars" height="250px" width="750px" /></p>

<p>Car wheels 
<img src="/images/car_wheels.jpg" alt="car_wheels" height="250px" width="750px" /></p>

<p>Now if you look at the next examples, you will find that they are pretty ambigous if you are asked to assign only one label, let’s say, out of five. And for a number of categories there a lot of images with such ambiguity.</p>

<p><img src="/images/cars_collage.jpg" alt="cars" /></p>

<p>The major problem with the dataset is that for many of the images we can’t say precisely if a category A or a category B describes it the best and the model in such situation is penalized during the training for the the mismatch between the predicted A category and B category given as a ground truth. So even if the image from the evaluation set is better in the sense of an ambiguity but still can be described with multiple labels, it will be harder for the network to determine a pravailing label because the training dataset doesn’t train it to do so very well. And this explain such a big margin between top-1 and top-5 errors.</p>]]></content><author><name></name></author><category term="Computer Vision" /><category term="ILSVRC" /><category term="top-1" /><category term="top-5" /><category term="ImageNet" /><category term="dataset" /><summary type="html"><![CDATA[For the most publicly available models trained on ImageNet dataset top-5 and top-1 errors are reported. The best perfoming model from ILSVRC 2015 has 6.7% top-5 error and 23% top-1 error evaluated using singe center crop. It’s pretty interesting to find out that human error turned out to be not much better, top-5 error is around 5.1% according to the result of some experiments descibed in the article.]]></summary></entry><entry><title type="html">Preparing multilabel dataset for training ConvNet with Caffe</title><link href="https://kostyaev.github.io/caffe/2016/02/17/Multilabel-Dataset.html" rel="alternate" type="text/html" title="Preparing multilabel dataset for training ConvNet with Caffe" /><published>2016-02-17T00:00:00+00:00</published><updated>2016-02-17T00:00:00+00:00</updated><id>https://kostyaev.github.io/caffe/2016/02/17/Multilabel%20Dataset</id><content type="html" xml:base="https://kostyaev.github.io/caffe/2016/02/17/Multilabel-Dataset.html"><![CDATA[<p>Preparing multilabel training set for caffe framework is a bit nontrivial. So, if you have multiple, possibly varying number of ground truth labels for each training example then here is how you can do it using LMDB store. 
For LMDB data source you need to separate your data input and your labels by creating two LMDB (one for the data and the second one for the labels). You also have to define two data layers in your network definition, set the same batch size for both of them and disable shuffling for the alignment.</p>

<p>To share it I’ve created a small script available on <a href="https://github.com/kostyaev/ml-utils/blob/master/create_multilabel_lmdb.py">github</a>. You can run it as in this example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python create_multilabel_lmdb.py 
	--images /path/to/image_file/images.txt 
	--labels /path/to/labels_file/labels.npy 
	--imagesOut /path/to/image-lmdb 
	--labelsOut /path/to/label-lmdb 
	-n size_of_test_set 
	--maxPx 256 
	--minPx 227 
	--shuffle=true
	
</code></pre></div></div>

<p>What this script do is reads the images text file having the format like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> /path/to/dir/img1.jpg
 /path/to/dir/img2.jpg
 ...
</code></pre></div></div>

<p>Reads the labels file, which is just a 2d numpy array serialized using numpy, here is the example of an array:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[
	[1,0,1,0,1], 
	[0,1,0,0,1],
	[1,1,0,0,0]
]
</code></pre></div></div>

<p>The first row indicates that the first image (in the images text file) has labels 1,3 and 5. And the second row says that the second image has labels 2 and 5, the third row - labels 1 and 2.
This script also shuffles the data, resizes images preserving an aspect ratio and prints mean image values at the end of the work.
If error occures (e.g if some image file is corrupted or missing), the procedure skips the corrupted image and its label and continues the progress.</p>]]></content><author><name></name></author><category term="Caffe" /><category term="caffe" /><category term="lmdb" /><category term="multilabel" /><summary type="html"><![CDATA[Preparing multilabel training set for caffe framework is a bit nontrivial. So, if you have multiple, possibly varying number of ground truth labels for each training example then here is how you can do it using LMDB store. For LMDB data source you need to separate your data input and your labels by creating two LMDB (one for the data and the second one for the labels). You also have to define two data layers in your network definition, set the same batch size for both of them and disable shuffling for the alignment.]]></summary></entry><entry><title type="html">Sentiment analysis with CoreNLP</title><link href="https://kostyaev.github.io/nlp/2014/06/21/Sentiment-analysis.html" rel="alternate" type="text/html" title="Sentiment analysis with CoreNLP" /><published>2014-06-21T00:00:00+00:00</published><updated>2014-06-21T00:00:00+00:00</updated><id>https://kostyaev.github.io/nlp/2014/06/21/Sentiment%20analysis</id><content type="html" xml:base="https://kostyaev.github.io/nlp/2014/06/21/Sentiment-analysis.html"><![CDATA[<p>In the rise of social media customer’s opinions has become extremely valuable for businesses selling their products, financial markets and social researches. To extract opinions from customer’s reviews, comments or other kind of text data you might want to know what sentiment analysis is.</p>

<blockquote>
  <p>Sentiment analysis and opinion mining is the field of study that analyzes people’s opinions, sentiments, evaluations, attitudes, and emotions from written language.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>
</blockquote>

<p>So the the basic task of sentiment analysis is classifying text into some emotive categories. The most common set of categories are: positive, neutral and negative.</p>

<h3 id="methods-for-sentiment-analysis">Methods for sentiment analysis</h3>
<p>There is a number of methods for sentiment analysis that can be divided in two groups:</p>

<ul>
  <li>Lexicon-based methods</li>
  <li>Machine learning methods
    <ul>
      <li>NB (Naive Bayes classifier)</li>
      <li>biNB (Naive Bayes with bag of bigram features)</li>
      <li>SVM (Support Vector Machine)</li>
      <li>RNTN (Recursive Neural Tensor Network)</li>
      <li>RNN (Recursive Neural Network)</li>
      <li>MV-RNN (Matrix-Vector RNN)</li>
    </ul>
  </li>
</ul>

<p>The last method in the list is claimed to have the better performance than the others. <sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p>

<h3 id="corenlp-library">CoreNLP library</h3>
<p>As you may already know there is great open source library for natural language processing named <a href="http://nlp.stanford.edu/software/corenlp.shtml">CoreNLP</a> which includes RNTN for sentiment analysis. The library is written in Java, but there are also some wrappers for other languages.</p>

<h3 id="how-to-use-it">How to use it</h3>

<p>Let’s dive into details on how to use CoreNLP in Scala. Since Scala is compatible with Java, you can simply import CoreNLP library in your Scala project. To include library and models for sentiment component in your project add the following dependency to the sbt build file:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="n">libraryDependencies</span> <span class="o">+=</span> <span class="s">"edu.stanford.nlp"</span> <span class="o">%</span> <span class="s">"stanford-corenlp"</span> <span class="o">%</span> <span class="s">"3.3.1"</span> <span class="nf">artifacts</span> <span class="o">(</span><span class="nc">Artifact</span><span class="o">(</span><span class="s">"stanford-corenlp"</span><span class="o">,</span> <span class="s">"models"</span><span class="o">),</span> <span class="nc">Artifact</span><span class="o">(</span><span class="s">"stanford-corenlp"</span><span class="o">))</span></code></pre></figure>

<p>When doing analysis with CoreNLP you get fine grained predictions as a result (i.e. 5 classes: very positive, positive, neutral, negative, very negative) which are less accurate than Positive/Neutral/Negative ones. In the code below, we create three categories and map result into them in the end of <code class="language-plaintext highlighter-rouge">getSentiment</code> method.</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">import</span> <span class="nn">java.util.Properties</span>
<span class="k">import</span> <span class="nn">edu.stanford.nlp.pipeline.StanfordCoreNLP</span>
<span class="k">import</span> <span class="nn">edu.stanford.nlp.ling.CoreAnnotations</span>
<span class="k">import</span> <span class="nn">edu.stanford.nlp.sentiment.SentimentCoreAnnotations</span>
<span class="k">import</span> <span class="nn">edu.stanford.nlp.neural.rnn.RNNCoreAnnotations</span>

<span class="k">object</span> <span class="nc">SentimentCategory</span> <span class="k">extends</span> <span class="nc">Enumeration</span> <span class="o">{</span>
  <span class="k">type</span> <span class="kt">SentimentCategory</span> <span class="o">=</span> <span class="nc">Int</span>
  <span class="k">val</span> <span class="nv">Negative</span> <span class="k">=</span> <span class="mi">0</span>
  <span class="k">val</span> <span class="nv">Neutral</span> <span class="k">=</span> <span class="mi">1</span>
  <span class="k">val</span> <span class="nv">Positive</span> <span class="k">=</span> <span class="mi">2</span>
<span class="o">}</span>

<span class="k">trait</span> <span class="nc">SentimentTools</span> <span class="o">{</span>
    <span class="k">val</span> <span class="nv">props</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">Properties</span><span class="o">()</span>
    <span class="nv">props</span><span class="o">.</span><span class="py">setProperty</span><span class="o">(</span><span class="s">"annotators"</span><span class="o">,</span> <span class="s">"tokenize, ssplit, parse, sentiment"</span><span class="o">);</span>
    <span class="k">val</span> <span class="nv">pipeline</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">StanfordCoreNLP</span><span class="o">(</span><span class="n">props</span><span class="o">)</span>

    <span class="k">def</span> <span class="nf">getSentiment</span><span class="o">(</span><span class="n">text</span> <span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">SentimentCategory</span> <span class="o">=</span> <span class="o">{</span>
      <span class="k">var</span> <span class="n">mainSentiment</span> <span class="k">=</span> <span class="mi">0</span>
      <span class="nf">if</span> <span class="o">(</span><span class="n">text</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nv">text</span><span class="o">.</span><span class="py">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">var</span> <span class="n">longest</span> <span class="k">=</span> <span class="mi">0</span>
        <span class="k">val</span> <span class="nv">annotation</span> <span class="k">=</span> <span class="nv">pipeline</span><span class="o">.</span><span class="py">process</span><span class="o">(</span><span class="n">text</span><span class="o">)</span>
        <span class="k">val</span> <span class="nv">list</span> <span class="k">=</span> <span class="nv">annotation</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="n">classOf</span><span class="o">[</span><span class="kt">CoreAnnotations.SentencesAnnotation</span><span class="o">])</span>
        <span class="k">val</span> <span class="nv">it</span> <span class="k">=</span> <span class="nv">list</span><span class="o">.</span><span class="py">iterator</span><span class="o">()</span>
        <span class="nf">while</span> <span class="o">(</span><span class="nv">it</span><span class="o">.</span><span class="py">hasNext</span><span class="o">)</span>
        <span class="o">{</span>
          <span class="k">val</span> <span class="nv">sentence</span> <span class="k">=</span> <span class="nv">it</span><span class="o">.</span><span class="py">next</span><span class="o">()</span>
          <span class="k">val</span> <span class="nv">tree</span> <span class="k">=</span> <span class="nv">sentence</span><span class="o">.</span><span class="py">get</span><span class="o">(</span><span class="n">classOf</span><span class="o">[</span><span class="kt">SentimentCoreAnnotations.AnnotatedTree</span><span class="o">])</span>
          <span class="k">val</span> <span class="nv">sentiment</span> <span class="k">=</span> <span class="nv">RNNCoreAnnotations</span><span class="o">.</span><span class="py">getPredictedClass</span><span class="o">(</span><span class="n">tree</span><span class="o">)</span>
          <span class="k">val</span> <span class="nv">partText</span> <span class="k">=</span> <span class="nv">sentence</span><span class="o">.</span><span class="py">toString</span><span class="o">()</span>
          <span class="nf">if</span> <span class="o">(</span><span class="nv">partText</span><span class="o">.</span><span class="py">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">longest</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">mainSentiment</span> <span class="k">=</span> <span class="n">sentiment</span>
            <span class="n">longest</span> <span class="k">=</span> <span class="nv">partText</span><span class="o">.</span><span class="py">length</span><span class="o">()</span>
          <span class="o">}</span>
        <span class="o">}</span>
      <span class="o">}</span>
      <span class="k">import</span> <span class="nn">SentimentCategory._</span>
      <span class="nf">if</span> <span class="o">(</span><span class="n">mainSentiment</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="o">)</span>
        <span class="nc">Negative</span>
      <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">mainSentiment</span> <span class="o">==</span> <span class="mi">2</span><span class="o">)</span>
        <span class="nc">Neutral</span>
      <span class="k">else</span>
        <span class="nc">Positive</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="http://www.morganclaypool.com/doi/abs/10.2200/S00416ED1V01Y201204HLT016">Sentiment Analysis and Opinion Mining Synthesis Lectures on Human Language Technologies</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p><a href="http://nlp.stanford.edu/~socherr/EMNLP2013_RNTN.pdf">Recursive Deep Models for Semantic Compositionality Over a Sentiment Treebank</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="NLP" /><category term="sentiment analysis" /><category term="CoreNLP" /><category term="scala" /><summary type="html"><![CDATA[How to do sentiment analysis with CoreNLP library]]></summary></entry></feed>