<?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://dimigeorgiou.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dimigeorgiou.github.io/" rel="alternate" type="text/html" /><updated>2026-08-27T21:22:42+00:00</updated><id>https://dimigeorgiou.github.io/feed.xml</id><title type="html">Dimitrios Georgiou</title><subtitle>Senior ML Engineer | AI Engineer | PhD Candidate — Calisthenics Athlete | Traveller</subtitle><author><name>Dimitrios Georgiou</name><email>dgeorgiou3@gmail.com</email></author><entry><title type="html">Voice Gender Identification</title><link href="https://dimigeorgiou.github.io/project/project_2/" rel="alternate" type="text/html" title="Voice Gender Identification" /><published>2020-02-18T00:00:00+00:00</published><updated>2020-02-18T00:00:00+00:00</updated><id>https://dimigeorgiou.github.io/project/project_2</id><content type="html" xml:base="https://dimigeorgiou.github.io/project/project_2/"><![CDATA[<p>Can we detect the gender of a voice using ML methods? I recently came across <a href="https://appliedmachinelearning.blog/2017/06/14/voice-gender-detection-using-gmms-a-python-primer/">this</a> article which I found quite interesting in the way it addresses Gender Identification from vocal recordings.</p>

<script type="text/javascript" async="" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML">
</script>

<p>Voice gender identification relies on three important steps:</p>
<ul>
  <li>Extracting from the training set MFCC features (13 per time window usually)</li>
  <li>Train two Gaussian Mixture Models (GMMs) on the feature matrices created (N_records x 13), one for each genre</li>
  <li>In prediction, compute the likelihood of each gender using the trained GMMs, and pick the most likely gender</li>
</ul>

<p>The Github repository for this article can be found <a href="https://github.com/maelfabien/VoiceGenderDetection/blob/master/README.md">here</a>.</p>

<p>The aim of this project is to build a web application using Streamlit in which a user is able to test the trained algorithm on his or her own voice.</p>

<h1 id="lets-build-it">Let’s build it</h1>

<h2 id="data-and-imports">Data and imports</h2>

<p>⚠️ The dataset has been extracted from <a href="https://research.google.com/audioset/dataset/index.html">AudioSet</a> and can be downloaded from <a href="https://drive.google.com/file/d/1g64EswaS5PtwIg-Y0ZmWwvSK1DgYvUuc/view?usp=sharing">here directly</a>.</p>

<p>Start by importing the libraries that we will need to build this application:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Data manipulation
</span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># Feature extraction
</span><span class="kn">import</span> <span class="nn">scipy</span>
<span class="kn">import</span> <span class="nn">librosa</span>
<span class="kn">import</span> <span class="nn">python_speech_features</span> <span class="k">as</span> <span class="n">mfcc</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">scipy.io.wavfile</span> <span class="kn">import</span> <span class="n">read</span>

<span class="c1"># Model training
</span><span class="kn">from</span> <span class="nn">sklearn.mixture</span> <span class="kn">import</span> <span class="n">GaussianMixture</span> <span class="k">as</span> <span class="n">GMM</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">preprocessing</span>
<span class="kn">import</span> <span class="nn">pickle</span>

<span class="c1"># Live recording
</span><span class="kn">import</span> <span class="nn">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>
<span class="kn">import</span> <span class="nn">soundfile</span> <span class="k">as</span> <span class="n">sf</span>
</code></pre></div></div>

<p>If you have not yet understood or seen the concept of Mel Frequency Cepstral Coefficients (MFCC), I recommend that you take a look at <a href="https://maelfabien.github.io/machinelearning/Speech9">the article I wrote on the topic of Sound Feature Extraction</a>.</p>

<h2 id="feature-extraction">Feature Extraction</h2>

<p>The concept behind this approach to gender detection is really simple. We first create a feature matrix from the training audio recordings. MFCCs are extracted on really small time windows (±20ms), and when you run an MFCC feature extraction using <code class="language-plaintext highlighter-rouge">python_speech_features</code> or Librosa, it automatically creates a matrix for the whole recording.</p>

<p>Knowing that, extracting the MFCC of a audio file is really easy:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">audio</span><span class="p">):</span>
    
    <span class="n">features</span> <span class="o">=</span> <span class="n">mfcc</span><span class="p">.</span><span class="n">mfcc</span><span class="p">(</span><span class="n">audio</span><span class="p">,</span> <span class="n">sr</span><span class="p">,</span> <span class="mf">0.025</span><span class="p">,</span> <span class="mf">0.01</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="n">appendEnergy</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span>
    <span class="n">features</span> <span class="o">=</span> <span class="n">preprocessing</span><span class="p">.</span><span class="n">scale</span><span class="p">(</span><span class="n">features</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">features</span>
</code></pre></div></div>

<p>I placed the training data in a folder called AudioSet, in which I have two sub-folders: male_clips and female_clips. We can extract the features of the training set simply by running the function above on all files in the training folder. The problem is however that for the moment, both the train and the test set are in the folder. We must, therefore, split these files in two, and run <code class="language-plaintext highlighter-rouge">get_MFCC</code> iteratively.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="n">source</span><span class="p">):</span>
    
    <span class="c1"># Split files
</span>    <span class="n">files</span> <span class="o">=</span> <span class="p">[</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">source</span><span class="p">,</span><span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">source</span><span class="p">)</span> <span class="k">if</span> <span class="n">f</span><span class="p">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'.wav'</span><span class="p">)]</span>
    <span class="n">len_train</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">files</span><span class="p">)</span><span class="o">*</span><span class="mf">0.8</span><span class="p">)</span>
    <span class="n">train_files</span> <span class="o">=</span> <span class="n">files</span><span class="p">[:</span><span class="n">len_train</span><span class="p">]</span>
    <span class="n">test_files</span> <span class="o">=</span> <span class="n">files</span><span class="p">[</span><span class="n">len_train</span><span class="p">:]</span>
    
    <span class="c1"># Train features
</span>    <span class="n">features_train</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">train_files</span><span class="p">:</span>
        <span class="n">sr</span><span class="p">,</span> <span class="n">audio</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
        <span class="n">vector</span> <span class="o">=</span> <span class="n">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">audio</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">features_train</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">features_train</span> <span class="o">=</span> <span class="n">vector</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">features_train</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">vstack</span><span class="p">((</span><span class="n">features_train</span><span class="p">,</span> <span class="n">vector</span><span class="p">))</span>
            
    <span class="c1"># Test features  
</span>    <span class="n">features_test</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">test_files</span><span class="p">:</span>
        <span class="n">sr</span><span class="p">,</span> <span class="n">audio</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
        <span class="n">vector</span> <span class="o">=</span> <span class="n">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">audio</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">features_test</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">features_test</span> <span class="o">=</span> <span class="n">vector</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">features_test</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">vstack</span><span class="p">((</span><span class="n">features_test</span><span class="p">,</span> <span class="n">vector</span><span class="p">))</span>
            
    <span class="k">return</span> <span class="n">features_train</span><span class="p">,</span> <span class="n">features_test</span>
</code></pre></div></div>

<h2 id="gmm-model-training">GMM model training</h2>

<blockquote>
  <p>“A Gaussian Mixture Model (GMM) is a parametric probability density function represented as a weighted sum of Gaussian component densities. (<a href="https://github.com/SuperKogito/Voice-based-gender-recognition">source</a>)</p>
</blockquote>

<p>GMMs are commonly used as a parametric model of the probability distribution of continuous measurements or features in a biometric system, such as vocal-tract related spectral features in a speaker recognition system. GMM parameters are estimated from training data using the iterative Expectation-Maximization (EM) algorithm or Maximum A Posteriori(MAP) estimation from a well-trained prior model.”</p>

<p>To apply it to the folder containing the Male recordings, simply use this function, extract the train features and train the Gaussian Mixture Model.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">source</span> <span class="o">=</span> <span class="s">"AudioSet/male_clips"</span>
<span class="n">features_train_male</span><span class="p">,</span> <span class="n">features_test_male</span> <span class="o">=</span> <span class="n">get_features</span><span class="p">(</span><span class="n">source</span><span class="p">)</span>
<span class="n">gmm_male</span> <span class="o">=</span> <span class="n">GMM</span><span class="p">(</span><span class="n">n_components</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span> <span class="n">max_iter</span> <span class="o">=</span> <span class="mi">200</span><span class="p">,</span> <span class="n">covariance_type</span> <span class="o">=</span> <span class="s">'diag'</span><span class="p">,</span> <span class="n">n_init</span> <span class="o">=</span> <span class="mi">3</span><span class="p">)</span>
<span class="n">gmm_male</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">features_train_male</span><span class="p">)</span>
</code></pre></div></div>

<p>We can repeat the process for Females:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">source</span> <span class="o">=</span> <span class="s">"AudioSet/female_clips"</span>
<span class="n">features_train_female</span><span class="p">,</span> <span class="n">features_test_female</span> <span class="o">=</span>  <span class="n">get_features</span><span class="p">(</span><span class="n">source</span><span class="p">)</span>
<span class="n">gmm_female</span> <span class="o">=</span> <span class="n">GMM</span><span class="p">(</span><span class="n">n_components</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span> <span class="n">max_iter</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span> <span class="n">covariance_type</span><span class="o">=</span><span class="s">'diag'</span><span class="p">,</span> <span class="n">n_init</span> <span class="o">=</span> <span class="mi">3</span><span class="p">)</span>
<span class="n">gmm_female</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">features_train_female</span><span class="p">)</span>
</code></pre></div></div>

<p>Are these features really differentiable for males and females?</p>

<p>We can plot the distribution over the MFCC features for random samples of males and females:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span><span class="mi">10</span><span class="p">))</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">430000</span><span class="p">,</span> <span class="mi">1000</span><span class="p">):</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">features_train_male</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">c</span><span class="o">=</span><span class="s">'b'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">features_train_female</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">c</span><span class="o">=</span><span class="s">'r'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">features_male</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span> <span class="n">c</span><span class="o">=</span><span class="s">'b'</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Male"</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">features_female</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span> <span class="n">c</span><span class="o">=</span><span class="s">'r'</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Female"</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"MFCC features for Males and Females"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://maelfabien.github.io/assets/images/mfcc_gender.png" alt="image" /></p>

<p>There seem to be slight differences in the features extracted, but the rest of the analysis will tell us more about the separability of these distributions.</p>

<h2 id="model-evaluation">Model Evaluation</h2>

<p>It is now time to evaluate the accuracy of the model on the test features that we kept untouched for the moment. The idea is simply that for a given recording, we estimate the likelihood of each time frame and sum it for the whole recording. Therefore, if the likelihood of a male voice is greater, we return 0 as an answer, and 1 otherwise.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">output</span> <span class="o">=</span> <span class="p">[]</span>

<span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">features_test_male</span><span class="p">:</span>

    <span class="n">log_likelihood_male</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">gmm_male</span><span class="p">.</span><span class="n">score</span><span class="p">([</span><span class="n">f</span><span class="p">])).</span><span class="nb">sum</span><span class="p">()</span>
    <span class="n">log_likelihood_female</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">gmm_female</span><span class="p">.</span><span class="n">score</span><span class="p">([</span><span class="n">f</span><span class="p">])).</span><span class="nb">sum</span><span class="p">()</span>
    
    <span class="k">if</span> <span class="n">log_likelihood_male</span> <span class="o">&gt;</span> <span class="n">log_likelihood_female</span><span class="p">:</span>
        <span class="n">output</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">output</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>The accuracy for the male test set can be computed as:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">accuracy_male</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="nb">sum</span><span class="p">(</span><span class="n">output</span><span class="p">)</span><span class="o">/</span><span class="nb">len</span><span class="p">(</span><span class="n">output</span><span class="p">))</span>
<span class="n">accuracy_male</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">0.63148</code></p>

<p>Similarly, the accuracy for the females reaches 0.63808.</p>

<p>Overall, the accuracy is not that high for such a task, and we might need to improve the approach in the next article.</p>

<h2 id="save-models">Save models</h2>

<p>We now suppose that our model is ready to move to production and we re-train it on the whole dataset and save the models:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="n">source</span><span class="p">):</span>
    
    <span class="n">files</span> <span class="o">=</span> <span class="p">[</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">source</span><span class="p">,</span><span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">source</span><span class="p">)</span> <span class="k">if</span> <span class="n">f</span><span class="p">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'.wav'</span><span class="p">)]</span>
    
    <span class="n">features</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
        <span class="n">sr</span><span class="p">,</span><span class="n">audio</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
        <span class="n">vector</span>   <span class="o">=</span> <span class="n">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">audio</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">features</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">features</span> <span class="o">=</span> <span class="n">vector</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">features</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">vstack</span><span class="p">((</span><span class="n">features</span><span class="p">,</span> <span class="n">vector</span><span class="p">))</span>

    <span class="k">return</span> <span class="n">features</span>

<span class="n">source_male</span> <span class="o">=</span> <span class="s">"test_data/AudioSet/male_clips"</span>
<span class="n">features_male</span> <span class="o">=</span> <span class="n">get_features</span><span class="p">(</span><span class="n">source_male</span><span class="p">)</span>

<span class="n">gmm_male</span> <span class="o">=</span> <span class="n">GMM</span><span class="p">(</span><span class="n">n_components</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span> <span class="n">max_iter</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span> <span class="n">covariance_type</span><span class="o">=</span><span class="s">'diag'</span><span class="p">,</span> <span class="n">n_init</span> <span class="o">=</span> <span class="mi">3</span><span class="p">)</span>
<span class="n">gmm_male</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">features_male</span><span class="p">)</span>

<span class="n">source_female</span> <span class="o">=</span> <span class="s">"test_data/AudioSet/female_clips"</span>
<span class="n">features_female</span> <span class="o">=</span> <span class="n">get_features</span><span class="p">(</span><span class="n">source_female</span><span class="p">)</span>

<span class="n">gmm_female</span> <span class="o">=</span> <span class="n">GMM</span><span class="p">(</span><span class="n">n_components</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span> <span class="n">max_iter</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span> <span class="n">covariance_type</span><span class="o">=</span><span class="s">'diag'</span><span class="p">,</span> <span class="n">n_init</span> <span class="o">=</span> <span class="mi">3</span><span class="p">)</span>
<span class="n">gmm_female</span><span class="p">.</span><span class="n">fit</span><span class="p">(</span><span class="n">features_female</span><span class="p">)</span>

<span class="c1"># Save models
</span><span class="n">pickle</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">gmm_male</span><span class="p">,</span> <span class="nb">open</span><span class="p">(</span><span class="s">"male.gmm"</span><span class="p">,</span> <span class="s">"wb"</span> <span class="p">))</span>
<span class="n">pickle</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">gmm_female</span><span class="p">,</span> <span class="nb">open</span><span class="p">(</span><span class="s">"female.gmm"</span><span class="p">,</span> <span class="s">"wb"</span> <span class="p">))</span>
</code></pre></div></div>

<h2 id="live-prediction">Live Prediction</h2>

<p>The next step, of course, is to build a live predictor that records 3-5 seconds of an audio sample and classifies it. We use sounddevice for this task, and particularly the rec option.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">record_and_predict</span><span class="p">(</span><span class="n">sr</span><span class="o">=</span><span class="mi">16000</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">duration</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s">'pred_record.wav'</span><span class="p">):</span>
    
    <span class="n">recording</span> <span class="o">=</span> <span class="n">sd</span><span class="p">.</span><span class="n">rec</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">duration</span> <span class="o">*</span> <span class="n">sr</span><span class="p">),</span> <span class="n">samplerate</span><span class="o">=</span><span class="n">sr</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="n">channels</span><span class="p">).</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    
    <span class="n">features</span> <span class="o">=</span> <span class="n">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">recording</span><span class="p">)</span>
    <span class="n">scores</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="n">log_likelihood_male</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">gmm_male</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">features</span><span class="p">)).</span><span class="nb">sum</span><span class="p">()</span>
    <span class="n">log_likelihood_female</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">gmm_female</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">features</span><span class="p">)).</span><span class="nb">sum</span><span class="p">()</span>

    <span class="k">if</span> <span class="n">log_likelihood_male</span> <span class="o">&gt;=</span> <span class="n">log_likelihood_female</span><span class="p">:</span>
        <span class="k">return</span><span class="p">(</span><span class="s">"Male"</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span><span class="p">(</span><span class="s">"Female"</span><span class="p">)</span>
</code></pre></div></div>

<p>To test it in your notebook, simply run :</p>

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

<p>Leave a comment and tell me how good it works! :)</p>

<p>Here’s what I noticed while using it. The accuracy on the test remains to improve (63%). When a user plays with his or her voice and tries to imitate the other gender, the GMM gets fooled and predicts the wrong gender. This is also due to the training data that it has seen so far which were extracted from AudioSet and Youtube.</p>

<h1 id="web-application">Web application</h1>

<p>Okay, playing with a notebook is quite easy. But we now need to build a dedicated application for this service. Hopefully, this became really easy with <a href="http://streamlit.io/">Streamlit</a>, a light framework to build interactive applications.</p>

<p>I won’t dive too much in how Streamlit works (this deserves a dedicated article, coming soon :) ), but here’s the code of the application that you should place in <code class="language-plaintext highlighter-rouge">app.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">streamlit</span> <span class="k">as</span> <span class="n">st</span>

<span class="c1"># Data manipulation
</span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># Feature extraction
</span><span class="kn">import</span> <span class="nn">scipy</span>
<span class="kn">import</span> <span class="nn">librosa</span>
<span class="kn">import</span> <span class="nn">python_speech_features</span> <span class="k">as</span> <span class="n">mfcc</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">scipy.io.wavfile</span> <span class="kn">import</span> <span class="n">read</span>

<span class="c1"># Model training
</span><span class="kn">from</span> <span class="nn">sklearn.mixture</span> <span class="kn">import</span> <span class="n">GaussianMixture</span> <span class="k">as</span> <span class="n">GMM</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">preprocessing</span>
<span class="kn">import</span> <span class="nn">pickle</span>

<span class="c1"># Live recording
</span><span class="kn">import</span> <span class="nn">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>
<span class="kn">import</span> <span class="nn">soundfile</span> <span class="k">as</span> <span class="n">sf</span>

<span class="n">st</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Voice Gender Detection"</span><span class="p">)</span>
<span class="n">st</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"This application demonstrates a simple Voice Gender Detection. Voice gender identification relies on three important steps."</span><span class="p">)</span>
<span class="n">st</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"- Extracting from the training set MFCC features (13 usually) for each gender"</span><span class="p">)</span>
<span class="n">st</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"- Train a GMM on those features"</span><span class="p">)</span>
<span class="n">st</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"- In prediction, compute the likelihood of each gender using the trained GMM, and pick the most likely gender"</span><span class="p">)</span>


<span class="n">st</span><span class="p">.</span><span class="n">subheader</span><span class="p">(</span><span class="s">"Ready to try it on your voice?"</span><span class="p">)</span>

<span class="n">st</span><span class="p">.</span><span class="n">sidebar</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Parameters"</span><span class="p">)</span>
<span class="n">duration</span> <span class="o">=</span> <span class="n">st</span><span class="p">.</span><span class="n">sidebar</span><span class="p">.</span><span class="n">slider</span><span class="p">(</span><span class="s">"Recording duration"</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">10.0</span><span class="p">,</span> <span class="mf">3.0</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">audio</span><span class="p">):</span>
    <span class="s">"""
    Extracts the MFCC audio features from a file
    """</span>
    <span class="n">features</span> <span class="o">=</span> <span class="n">mfcc</span><span class="p">.</span><span class="n">mfcc</span><span class="p">(</span><span class="n">audio</span><span class="p">,</span> <span class="n">sr</span><span class="p">,</span> <span class="mf">0.025</span><span class="p">,</span> <span class="mf">0.01</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="n">appendEnergy</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span>
    <span class="n">features</span> <span class="o">=</span> <span class="n">preprocessing</span><span class="p">.</span><span class="n">scale</span><span class="p">(</span><span class="n">features</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">features</span>

<span class="k">def</span> <span class="nf">record_and_predict</span><span class="p">(</span><span class="n">gmm_male</span><span class="p">,</span> <span class="n">gmm_female</span><span class="p">,</span> <span class="n">sr</span><span class="o">=</span><span class="mi">16000</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">duration</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s">'pred_record.wav'</span><span class="p">):</span>
    <span class="s">"""
    Records live voice and returns the identified gender
    """</span> 
    <span class="n">recording</span> <span class="o">=</span> <span class="n">sd</span><span class="p">.</span><span class="n">rec</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">duration</span> <span class="o">*</span> <span class="n">sr</span><span class="p">),</span> <span class="n">samplerate</span><span class="o">=</span><span class="n">sr</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="n">channels</span><span class="p">).</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    
    <span class="n">features</span> <span class="o">=</span> <span class="n">get_MFCC</span><span class="p">(</span><span class="n">sr</span><span class="p">,</span><span class="n">recording</span><span class="p">)</span>
    <span class="n">scores</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="n">log_likelihood_male</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">gmm_male</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">features</span><span class="p">)).</span><span class="nb">sum</span><span class="p">()</span>
    <span class="n">log_likelihood_female</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">gmm_female</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">features</span><span class="p">)).</span><span class="nb">sum</span><span class="p">()</span>

    <span class="k">if</span> <span class="n">log_likelihood_male</span> <span class="o">&gt;=</span> <span class="n">log_likelihood_female</span><span class="p">:</span>
        <span class="k">return</span><span class="p">(</span><span class="s">"Male"</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span><span class="p">(</span><span class="s">"Female"</span><span class="p">)</span>

<span class="n">gmm_male</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s">'male.gmm'</span><span class="p">,</span><span class="s">'rb'</span><span class="p">))</span>
<span class="n">gmm_female</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s">'female.gmm'</span><span class="p">,</span><span class="s">'rb'</span><span class="p">))</span>


<span class="k">if</span> <span class="n">st</span><span class="p">.</span><span class="n">button</span><span class="p">(</span><span class="s">"Start Recording"</span><span class="p">):</span>
    <span class="k">with</span> <span class="n">st</span><span class="p">.</span><span class="n">spinner</span><span class="p">(</span><span class="s">"Recording..."</span><span class="p">):</span>
        <span class="n">gender</span> <span class="o">=</span> <span class="n">record_and_predict</span><span class="p">(</span><span class="n">gmm_male</span><span class="p">,</span> <span class="n">gmm_female</span><span class="p">,</span> <span class="n">duration</span><span class="o">=</span><span class="n">duration</span><span class="p">)</span>
        <span class="n">st</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">"The identified gender is: "</span> <span class="o">+</span> <span class="n">gender</span><span class="p">)</span>
</code></pre></div></div>

<p>To launch the app, you must run the command line <code class="language-plaintext highlighter-rouge">streamlit run app.py</code>:</p>

<p><img src="https://maelfabien.github.io/assets/images/gender_app.png" alt="image" /></p>

<blockquote>
  <p><strong>Conclusion</strong> : I hope that you enjoyed this article and found the approach useful. It has some severe limitations in terms of accuracy and how the user can trick</p>
</blockquote>]]></content><author><name>Dimitrios Georgiou</name><email>dgeorgiou3@gmail.com</email></author><category term="project" /><summary type="html"><![CDATA[Can we detect the gender of a voice using ML methods? I recently came across this article which I found quite interesting in the way it addresses Gender Identification from vocal recordings.]]></summary></entry><entry><title type="html">Voice Activity Detection Application</title><link href="https://dimigeorgiou.github.io/project/project_1/" rel="alternate" type="text/html" title="Voice Activity Detection Application" /><published>2020-01-01T00:00:00+00:00</published><updated>2020-01-01T00:00:00+00:00</updated><id>https://dimigeorgiou.github.io/project/project_1</id><content type="html" xml:base="https://dimigeorgiou.github.io/project/project_1/"><![CDATA[<p>Voice activity detection is a field which consists in identifying whether someone is speaking or not at a given moment. It can be useful to launch a vocal assistant or detect emergency situations.</p>

<script type="text/javascript" async="" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML">
</script>

<p>In this article, we will cover the main concepts behind classical approaches to voice activity detection, and implement them in Python is a small web application using Streamlit. This article is inspired by the <a href="https://github.com/marsbroshok/VAD-python">following repository</a>.</p>

<h1 id="high-level-overview">High-level overview</h1>

<p>It can be useful at first to give a high level overview of the classical approaches to Voice Activity Detection:</p>
<ul>
  <li>Read the input file and convert is to mono</li>
  <li>Move a window of 20ms along the audio data</li>
  <li>Calculate for each window the ratio between energy of speech band and total energy for window</li>
  <li>If ratio is higher than a pre-defined threshold (e.g 60%), label windows as speech</li>
  <li>Apply median filter with length of 0.5s to smooth detected speech regions</li>
  <li>Represent speech regions as intervals of time</li>
</ul>

<p>The application we will build is the following:</p>

<p><img src="https://maelfabien.github.io/assets/images/app_speech_0.png" alt="image" /></p>

<h1 id="read-the-input-file-and-convert-it-to-mono">Read the input file and convert it to mono</h1>

<p>In this exercise, we will only consider the case of mono signals and not stereo, meaning that we must have a single series of values, not 2. We read the files using Scipy’s wavfile module, and convert it to mono if there are 2 lists of values returned (stereo) by applying a mean of both series.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">scipy.io.wavfile</span> <span class="k">as</span> <span class="n">wf</span>

<span class="n">filename</span> <span class="o">=</span> <span class="s">'test.wav'</span>

<span class="k">def</span> <span class="nf">_read_wav</span><span class="p">(</span><span class="n">wave_file</span><span class="p">):</span>
	<span class="c1"># Read the input
</span>	<span class="n">rate</span><span class="p">,</span> <span class="n">data</span> <span class="o">=</span> <span class="n">wf</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">wave_file</span><span class="p">)</span>
	<span class="n">channels</span> <span class="o">=</span> <span class="nb">len</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="n">filename</span> <span class="o">=</span> <span class="n">wave_file</span>

	<span class="c1"># Convert to mono
</span>	<span class="k">if</span> <span class="n">channels</span> <span class="o">==</span> <span class="mi">2</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">mean</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">data</span><span class="p">.</span><span class="n">dtype</span><span class="p">)</span>
		<span class="n">channels</span> <span class="o">=</span> <span class="mi">1</span>
	<span class="k">return</span> <span class="n">data</span>

<span class="n">read_file</span> <span class="o">=</span> <span class="n">_read_wav</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
</code></pre></div></div>

<p>You can plot the signal in order to see which regions should be detected. In my case, the sample file contains 2 to 3 speech regions.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span><span class="mi">8</span><span class="p">))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)),</span> <span class="n">data</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Raw audio signal"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://maelfabien.github.io/assets/images/audio_0.png" alt="image" /></p>

<p>You can already notice that there is a notion a threshold that appears. From what moment do we assume that someone is speaking? How do we split 2 regions? We’ll answer those questions as we dive deeper into the solution.</p>

<h1 id="rolling-window">Rolling Window</h1>

<p>The solution will take the form of a rolling window on the input data. We will determine the energy in the frequency range that usually is associated to speech, and the energy of the whole band. If the ratio is larger than a threshold, we can assume that someone is speaking.</p>

<p>We first need to define some constants that we will use:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SAMPLE_START</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">SPEECH_START_BAND</span> <span class="o">=</span> <span class="mi">300</span>
<span class="n">SPEECH_END_BAND</span> <span class="o">=</span> <span class="mi">3000</span>
<span class="n">SAMPLE_WINDOW</span> <span class="o">=</span> <span class="mf">0.02</span>
<span class="n">SAMPLE_OVERLAP</span> <span class="o">=</span> <span class="mf">0.01</span>
<span class="n">THRESHOLD</span> <span class="o">=</span> <span class="mf">0.6</span>
</code></pre></div></div>

<p>Here’s what the constants are used for:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">SAMPLE_START</code> : the start index,</li>
  <li><code class="language-plaintext highlighter-rouge">SPEECH_START_BAND</code> : the minimum frequency of a human voice</li>
  <li><code class="language-plaintext highlighter-rouge">SPEECH_END_BAND</code> : the maximum frequency of a human voice</li>
  <li><code class="language-plaintext highlighter-rouge">SAMPLE_WINDOW</code> : a 20 ms window size on which we run the algorithm</li>
  <li><code class="language-plaintext highlighter-rouge">SAMPLE_OVERLAP</code> : the amount by which we shift the window size at each step</li>
  <li><code class="language-plaintext highlighter-rouge">THRESHOLD</code> : the threshold for the energy ratio under which a sound is not tagged as a voice</li>
</ul>

<p>The rolling window will have the following format:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">(</span><span class="n">SAMPLE_START</span> <span class="o">&lt;</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="n">SAMPLE_WINDOW</span><span class="p">)):</span>
    
    <span class="c1"># Select only the region of the data in the window
</span>    <span class="n">SAMPLE_END</span> <span class="o">=</span> <span class="n">SAMPLE_START</span> <span class="o">+</span> <span class="n">SAMPLE_WINDOW</span>
    <span class="k">if</span> <span class="n">SAMPLE_END</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> 
        <span class="n">SAMPLE_END</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span>

    <span class="n">data_window</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">SAMPLE_START</span><span class="p">:</span><span class="n">SAMPLE_END</span><span class="p">]</span>
    
    <span class="c1"># Detect speech here
</span>    
    <span class="c1"># Increment 
</span>    <span class="n">SAMPLE_START</span> <span class="o">+=</span> <span class="n">SAMPLE_OVERLAP</span>
</code></pre></div></div>

<h1 id="speech-ratio">Speech Ratio</h1>

<p>Within this data window, we now need to determine the speech ratio:</p>

\[speech_{ratio} = \frac{\sum energy_{voice}}{\sum energy_{full}}\]

<p>To determine the voice energy, we will only consider frequencies between 300 and 3’000 Hz, as they correspond to human voice frequencies.</p>

<p>The first thing we need to do is to compute the range of possible frequencies at the defined rate and given the audio sequence:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_calculate_frequencies</span><span class="p">(</span><span class="n">audio_data</span><span class="p">):</span>
    <span class="n">data_freq</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">fftfreq</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">audio_data</span><span class="p">),</span><span class="mf">1.0</span><span class="o">/</span><span class="n">rate</span><span class="p">)</span>
    <span class="n">data_freq</span> <span class="o">=</span> <span class="n">data_freq</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="k">return</span> <span class="n">data_freq</span>
</code></pre></div></div>

<p>This will return regular values between -8’000 and 8’000. The energy transported by a wave is directly proportional to the square of the amplitude of the wave, which can be computed using a Fast Fourrier Transform.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_calculate_energy</span><span class="p">(</span><span class="n">audio_data</span><span class="p">):</span>
    <span class="n">data_ampl</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">fft</span><span class="p">(</span><span class="n">audio_data</span><span class="p">))</span>
    <span class="n">data_ampl</span> <span class="o">=</span> <span class="n">data_ampl</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="k">return</span> <span class="n">data_ampl</span> <span class="o">**</span> <span class="mi">2</span>
</code></pre></div></div>

<p>We then connect the energy with the frequency by creating a dictionary whose keys are the absolute value of the frequency, and values are the corresponding energy at that frequency.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_connect_energy_with_frequencies</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    
    <span class="n">data_freq</span> <span class="o">=</span> <span class="n">_calculate_frequencies</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="n">data_energy</span> <span class="o">=</span> <span class="n">_calculate_energy</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    
    <span class="n">energy_freq</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">freq</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">data_freq</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">freq</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">energy_freq</span><span class="p">:</span>
            <span class="n">energy_freq</span><span class="p">[</span><span class="nb">abs</span><span class="p">(</span><span class="n">freq</span><span class="p">)]</span> <span class="o">=</span> <span class="n">data_energy</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="mi">2</span>
    <span class="k">return</span> <span class="n">energy_freq</span>

<span class="n">energy_freq</span> <span class="o">=</span> <span class="n">_connect_energy_with_frequencies</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">sum_full_energy</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">energy_freq</span><span class="p">.</span><span class="n">values</span><span class="p">())</span>
</code></pre></div></div>

<p>The variable <code class="language-plaintext highlighter-rouge">energy_freq</code> should return :</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="mf">0.4166666666666667</span><span class="p">:</span> <span class="mf">388888371.0778143</span><span class="p">,</span>
 <span class="mf">0.8333333333333334</span><span class="p">:</span> <span class="mf">378650788.74457765</span><span class="p">,</span>
 <span class="mf">1.25</span><span class="p">:</span> <span class="mf">139749533.30109847</span><span class="p">,</span>
 <span class="mf">1.6666666666666667</span><span class="p">:</span> <span class="mf">703141467.1534827</span><span class="p">,</span>
 <span class="mf">2.0833333333333335</span><span class="p">:</span> <span class="mf">2622893493.5843244</span><span class="p">,</span>
 <span class="mf">2.5</span><span class="p">:</span> <span class="mf">2214362080.232078</span><span class="p">,</span>
 <span class="p">...</span>
</code></pre></div></div>

<p>As stated above, we suppose that a human voice will be anywhere between 300 and 3’000 Hz. Therefore, we sum the energy corresponding such frequencies in the time window, and we can compare it with the full sum of energies.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_sum_energy_in_band</span><span class="p">(</span><span class="n">energy_frequencies</span><span class="p">):</span>
    <span class="n">sum_energy</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">energy_frequencies</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span>
        <span class="k">if</span> <span class="n">SPEECH_START_BAND</span> <span class="o">&lt;</span> <span class="n">f</span> <span class="o">&lt;</span> <span class="n">SPEECH_END_BAND</span><span class="p">:</span>
            <span class="n">sum_energy</span> <span class="o">+=</span> <span class="n">energy_frequencies</span><span class="p">[</span><span class="n">f</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">sum_energy</span>

<span class="n">sum_voice_energy</span> <span class="o">=</span> <span class="n">_sum_energy_in_band</span><span class="p">(</span><span class="n">energy_freq</span><span class="p">)</span>
</code></pre></div></div>

<p>Finally, we can define the speech ratio as being the quotien between the sum of the speech energy in the time window and the sum of the total energy.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">speech_ratio</span> <span class="o">=</span> <span class="n">sum_voice_energy</span><span class="o">/</span><span class="n">sum_full_energy</span>
<span class="n">speech_ratio</span>
</code></pre></div></div>

<p>In this sample, it gave me : <code class="language-plaintext highlighter-rouge">0.68923</code>.</p>

<h1 id="combining-the-loop-and-the-speech-ratio">Combining the loop and the speech ratio</h1>

<p>So far, we estimated the speech ratio on the whole audio file, without using a rolling window. It is now time to combine both approaches. We will store in <code class="language-plaintext highlighter-rouge">speech_ratio_list</code> a list of all the speech ratios in the loop.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">speech_ratio_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">detected_voice</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">mean_data</span> <span class="o">=</span> <span class="p">[]</span>

<span class="n">SAMPLE_START</span> <span class="o">=</span> <span class="mi">0</span>

<span class="k">while</span> <span class="p">(</span><span class="n">SAMPLE_START</span> <span class="o">&lt;</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="n">SAMPLE_WINDOW</span><span class="p">)):</span>
    
    <span class="c1"># Select only the region of the data in the window
</span>    <span class="n">SAMPLE_END</span> <span class="o">=</span> <span class="n">SAMPLE_START</span> <span class="o">+</span> <span class="n">SAMPLE_WINDOW</span>
    <span class="k">if</span> <span class="n">SAMPLE_END</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> 
        <span class="n">SAMPLE_END</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span>

    <span class="n">data_window</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">SAMPLE_START</span><span class="p">:</span><span class="n">SAMPLE_END</span><span class="p">]</span>
    <span class="n">mean_data</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">data_window</span><span class="p">))</span>

    <span class="c1"># Full energy
</span>    <span class="n">energy_freq</span> <span class="o">=</span> <span class="n">_connect_energy_with_frequencies</span><span class="p">(</span><span class="n">data_window</span><span class="p">)</span>
    <span class="n">sum_full_energy</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">energy_freq</span><span class="p">.</span><span class="n">values</span><span class="p">())</span>
    
    <span class="c1"># Voice energy
</span>    <span class="n">sum_voice_energy</span> <span class="o">=</span> <span class="n">_sum_energy_in_band</span><span class="p">(</span><span class="n">energy_freq</span><span class="p">)</span>
    
    <span class="c1"># Speech ratio
</span>    <span class="n">speech_ratio</span> <span class="o">=</span> <span class="n">sum_voice_energy</span><span class="o">/</span><span class="n">sum_full_energy</span>
    <span class="n">speech_ratio_list</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">speech_ratio</span><span class="p">)</span>
    <span class="n">detected_voice</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">speech_ratio</span> <span class="o">&gt;</span> <span class="n">THRESHOLD</span><span class="p">)</span>
    
    <span class="c1"># Increment 
</span>    <span class="n">SAMPLE_START</span> <span class="o">+=</span> <span class="n">SAMPLE_OVERLAP</span>
</code></pre></div></div>

<p>We can now compare the speech ratio list with the threshold over time:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span><span class="mi">8</span><span class="p">))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">speech_ratio_list</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">axhline</span><span class="p">(</span><span class="n">THRESHOLD</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="s">'r'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Speech ratio list vs. threshold"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://maelfabien.github.io/assets/images/audio_1.png" alt="image" /></p>

<p>We can also compare the raw signal with moments we detected a voice:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span><span class="mi">8</span><span class="p">))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</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">mean_data</span><span class="p">),</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.4</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Not detected"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</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">detected_voice</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">mean_data</span><span class="p">),</span> <span class="n">label</span><span class="o">=</span><span class="s">"Detected"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Detected vs. non-detected region"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://maelfabien.github.io/assets/images/audio_2.png" alt="image" /></p>

<p>You can try to play with the speech ratio threshold and the window size to see how it affects the detection.</p>

<h1 id="smoothing-the-regions">Smoothing the regions</h1>

<p>The output is interesting but would require some smoothing if we want to detect smooth regions in which a user speaks. We’ll go for a median filter and apply it on the speech ratio’s list.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_median_filter</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
    <span class="k">assert</span> <span class="n">k</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"Median filter length must be odd."</span>
    <span class="k">assert</span> <span class="n">x</span><span class="p">.</span><span class="n">ndim</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="s">"Input must be one-dimensional."</span>
    <span class="n">k2</span> <span class="o">=</span> <span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
    
    <span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">((</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">k</span><span class="p">),</span> <span class="n">dtype</span><span class="o">=</span><span class="n">x</span><span class="p">.</span><span class="n">dtype</span><span class="p">)</span>
    <span class="n">y</span><span class="p">[:,</span><span class="n">k2</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span> <span class="p">(</span><span class="n">k2</span><span class="p">):</span>
        <span class="n">j</span> <span class="o">=</span> <span class="n">k2</span> <span class="o">-</span> <span class="n">i</span>
        <span class="n">y</span><span class="p">[</span><span class="n">j</span><span class="p">:,</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span><span class="p">[:</span><span class="o">-</span><span class="n">j</span><span class="p">]</span>
        <span class="n">y</span><span class="p">[:</span><span class="n">j</span><span class="p">,</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">y</span><span class="p">[:</span><span class="o">-</span><span class="n">j</span><span class="p">,</span><span class="o">-</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)]</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="n">j</span><span class="p">:]</span>
        <span class="n">y</span><span class="p">[</span><span class="o">-</span><span class="n">j</span><span class="p">:,</span><span class="o">-</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)]</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">median</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>We can the apply it to a region</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SPEECH_WINDOW</span> <span class="o">=</span> <span class="mf">0.5</span>

<span class="k">def</span> <span class="nf">_smooth_speech_detection</span><span class="p">(</span><span class="n">detected_voice</span><span class="p">):</span>
    <span class="n">window</span> <span class="o">=</span> <span class="mf">0.02</span>
    <span class="n">median_window</span><span class="o">=</span><span class="nb">int</span><span class="p">(</span><span class="n">SPEECH_WINDOW</span><span class="o">/</span><span class="n">window</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">median_window</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">:</span> 
        <span class="n">median_window</span> <span class="o">=</span> <span class="n">median_window</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">median_energy</span> <span class="o">=</span> <span class="n">_median_filter</span><span class="p">(</span><span class="n">detected_voice</span><span class="p">,</span> <span class="n">median_window</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">median_energy</span>
</code></pre></div></div>

<p>We can now apply this to the pipeline defined above:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">speech_ratio_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">detected_voice</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">mean_data</span> <span class="o">=</span> <span class="p">[]</span>

<span class="n">SAMPLE_START</span> <span class="o">=</span> <span class="mi">0</span>

<span class="k">while</span> <span class="p">(</span><span class="n">SAMPLE_START</span> <span class="o">&lt;</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="n">SAMPLE_WINDOW</span><span class="p">)):</span>
    
    <span class="c1"># Select only the region of the data in the window
</span>    <span class="n">SAMPLE_END</span> <span class="o">=</span> <span class="n">SAMPLE_START</span> <span class="o">+</span> <span class="n">SAMPLE_WINDOW</span>
    <span class="k">if</span> <span class="n">SAMPLE_END</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> 
        <span class="n">SAMPLE_END</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span>
    <span class="n">data_window</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">SAMPLE_START</span><span class="p">:</span><span class="n">SAMPLE_END</span><span class="p">]</span>
    <span class="n">mean_data</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">mean</span><span class="p">(</span><span class="n">data_window</span><span class="p">))</span>
    <span class="c1"># Full energy
</span>    <span class="n">energy_freq</span> <span class="o">=</span> <span class="n">_connect_energy_with_frequencies</span><span class="p">(</span><span class="n">data_window</span><span class="p">)</span>
    <span class="n">sum_full_energy</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">energy_freq</span><span class="p">.</span><span class="n">values</span><span class="p">())</span>
    
    <span class="c1"># Voice energy
</span>    <span class="n">sum_voice_energy</span> <span class="o">=</span> <span class="n">_sum_energy_in_band</span><span class="p">(</span><span class="n">energy_freq</span><span class="p">)</span>
    
    <span class="c1"># Speech ratio
</span>    <span class="n">speech_ratio</span> <span class="o">=</span> <span class="n">sum_voice_energy</span><span class="o">/</span><span class="n">sum_full_energy</span>
    <span class="n">speech_ratio_list</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">speech_ratio</span><span class="p">)</span>
    <span class="n">detected_voice</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">speech_ratio</span> <span class="o">&gt;</span> <span class="n">THRESHOLD</span><span class="p">))</span>
    
    <span class="c1"># Increment 
</span>    <span class="n">SAMPLE_START</span> <span class="o">+=</span> <span class="n">SAMPLE_OVERLAP</span>
    
<span class="n">detected_voice</span> <span class="o">=</span> <span class="n">_smooth_speech_detection</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">detected_voice</span><span class="p">))</span>
</code></pre></div></div>

<p>Finally, the detected regions are these ones :</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span><span class="mi">8</span><span class="p">))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</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">detected_voice</span><span class="p">),</span> <span class="n">label</span><span class="o">=</span><span class="s">"Detected"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Detected vs. non-detected region"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://maelfabien.github.io/assets/images/audio_3.png" alt="image" /></p>

<p>We can plot once again the regions on the raw signal in which the voice has been detected:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plt</span><span class="p">.</span><span class="n">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span><span class="mi">8</span><span class="p">))</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</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">mean_data</span><span class="p">),</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.4</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">"Not detected"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">plot</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">detected_voice</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">mean_data</span><span class="p">),</span> <span class="n">label</span><span class="o">=</span><span class="s">"Detected"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">legend</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="s">"Detected vs. non-detected region"</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://maelfabien.github.io/assets/images/audio_4.png" alt="image" /></p>

<h1 id="pros-and-cons">Pros and Cons</h1>

<p>The main advantages of this approach is that :</p>
<ul>
  <li>it runs really fast</li>
  <li>it is easily explainable</li>
  <li>it is simple to implement</li>
  <li>it does not take language into account</li>
</ul>

<p>The limits of such approach is that :</p>
<ul>
  <li>there are many hyperparameters to choose from</li>
  <li>we must specify manually the range of frequency corresponding to a human voice</li>
  <li>this range is not unique to humans, an an animal or a car could be interprete as a human</li>
</ul>

<blockquote>
  <p><em>Conclusion</em>: In this article, we introduced the concept of voice activity detection. In the next article, we’ll see how to create a web application to deploy our algorithm using Streamlit.</p>
</blockquote>]]></content><author><name>Dimitrios Georgiou</name><email>dgeorgiou3@gmail.com</email></author><category term="project" /><summary type="html"><![CDATA[Voice activity detection is a field which consists in identifying whether someone is speaking or not at a given moment. It can be useful to launch a vocal assistant or detect emergency situations.]]></summary></entry><entry><title type="html">The basis of Machine Learning</title><link href="https://dimigeorgiou.github.io/machinelearning/article_1/" rel="alternate" type="text/html" title="The basis of Machine Learning" /><published>2019-01-01T00:00:00+00:00</published><updated>2019-01-01T00:00:00+00:00</updated><id>https://dimigeorgiou.github.io/machinelearning/article_1</id><content type="html" xml:base="https://dimigeorgiou.github.io/machinelearning/article_1/"><![CDATA[<p>Machine learning (ML) has been a rising trend over the last years. ML includes a set of techniques that go beyond statistics. In this article, we’ll cover the most important concepts behind ML.</p>

<script type="text/javascript" async="" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML">
</script>

<h2 id="why-machine-learning">Why machine learning?</h2>

<p>Machine learning goes beyond statistics. Indeed, we face the following technical challenges :</p>
<ul>
  <li>the volume, complexity, and dimension of data to process</li>
  <li>the diversity of the context: supervised, unsupervised…</li>
</ul>

<p>The improvement of the computation power and cloud computing have also allowed machine learning to become a standard that goes beyond statistical inference.</p>

<h2 id="supervised-vs-unsupervised">Supervised vs. Unsupervised</h2>

<p>Suppose we are given a set of data points \(X_1, X_2, ... X_m\). These points might represent anything measurable you could think of (e.g humidity in the air, population density or even pixels of an image), and are the variables we will rely on.</p>

<p>There are 2 subcases :</p>
<ul>
  <li>either we have labels attached to the variables: \(y_1, y_2, ... y_m\). The labels are in some sense the output we want to be able to forecast (e.g the price of a house, a stock price..). This is called supervised learning.</li>
  <li>in some cases, we do not have the labels. In those cases, our aim should be to group observations that appear to be similar to clusters. This is called unsupervised learning.</li>
  <li>you might also encounter the term of semi-supervised data. This simply means that only some data points are labeled.</li>
</ul>

<p><img src="https://maelfabien.github.io/assets/images/supervised.jpg" alt="image" /></p>

<h2 id="regression-vs-classification">Regression vs. Classification</h2>

<p>In supervised learning, there are two types of prediction problems you will be solving. Either trying to classify a new observation to the class it belongs to (classification), or trying to predict a continuous outcome (regression). For example :</p>
<ul>
  <li>in fraud detection, you rely on some measured data to predict if a bank payment is fraudulent or not. If it is fraudulent, you should predict 1. Else, predict 0.</li>
  <li>in stock market analysis, you will be trying to predict the value of the stock itself, and this is a regression problem</li>
</ul>

<p><img src="https://maelfabien.github.io/assets/images/classification.jpg" alt="image" /></p>

<h2 id="train-vs-test">Train vs. Test</h2>

<p>Most of the time, to assess the efficiency of a classifier or a regressor, we split the data we have into a train and a test set. The idea is to train the model on the train set, and assess its efficiency on the test set. This allows us to prevent overfitting (i.e developing a model too complex that learns too much from the data we provide it).</p>

<h2 id="discriminative-vs-generative">Discriminative vs. Generative</h2>

<p>There are two main categories of models :</p>
<ul>
  <li>the discriminative model (most common) tries to estimate the probability of \(y\) given \(X\) directly. In classification, we try to estimate the decision boundary between the labels of the data without paying attention to the distribution of the labeled data themselves. Most classifiers are of this type.</li>
  <li>the generative model tries to estimate the probability of \(X\) given \(y\) to later on deduce the probability of \(y\) given \(X\). We learn the probability distribution of the data. Discriminant analysis and Naïve Bayes are examples of such classifiers.</li>
</ul>

<p><img src="https://maelfabien.github.io/assets/images/discr.jpg" alt="image" /></p>

<h2 id="error-rate-and-empirical-risk-minimization">Error rate and Empirical Risk Minimization</h2>

<p>We’ll consider a classification problem here. The aim of building a model is to be able to predict for a new observation the class it belongs to. If we had to define a metric for this, it would be the accuracy. We want to build a classifier \(C\) that minimizes the number of miss-classified examples among our data. The loss of the model, i.e the error measure we can define, is the following :</p>

\[L(C) = E( I (Y≠C(X) ) )\]

<p>An optimal classifier should meet the following criteria :</p>

\[C^* = {argmin}_{C ∈ G} L(C)\]

<p>It can quite easily be shown that the solution to this problem is :</p>

\[C^* = 2 * I( P(Y=1 | X = x) &gt; 1/2) - 1\]

<p>In practice, we have access to a limited number of observations. For such reason, we try to minimize the error on the test sample with the empirical risk minimization (ERM) :</p>

\[\hat{L_n}(C) = \frac {1} {n} \sum I (Y_i ≠ C(X_i) ) )\]

<p>Learning works if when \(n\) tends to infinity, \(\hat{L_n}(C)\) tends to \(L(C)\). The theory of Vapnik-Chervonenkis offers guarantees for the prediction as long as the underlying model is not too complex.</p>

<h2 id="how-to-assess-the-performance-of-a-model">How to assess the performance of a model?</h2>

<p>Several metrics can be used to assess how well a model is performing.</p>

<h3 id="classification">Classification</h3>

<p>For a classification problem, we first need to recall the following concepts :</p>

<p><img src="https://maelfabien.github.io/assets/images/tp.jpg" alt="image" /></p>

<p>Notation :</p>
<ul>
  <li>TP: True positive</li>
  <li>TN: True negative</li>
  <li>FP: False positive</li>
  <li>FN: False negative</li>
</ul>

<p>Then, we can define :</p>
<ul>
  <li>the accuracy, the most widely used metric in classification, useful when there is a certain balance between classes : 
\(Accuracy = \frac {TP + TN} {TP + TN + FP + FN}\)</li>
  <li>the precision :
\(Precision = \frac {TP} {TP + FP}\)</li>
  <li>the recall, or precision :
\(Recall = \frac {TP} {TP + FN}\)</li>
  <li>the specificity :
\(Specificity = \frac {TN} {TN + FP}\)</li>
  <li>the F1-Score :
\(F1-Score = 2 * \frac {Precision * Recall} {Precision + Recall}\)</li>
</ul>

<h3 id="regression">Regression</h3>

<p>Let’s define \(y\) as the true value, and \(\hat{y}\) as the predicted y-value :</p>

<p>The most important metrics are :</p>
<ul>
  <li>the Mean Absolute Error (MAE) : 
\(MAE = \frac {1}{n} \sum \mid {y - \hat{y}} \mid\)</li>
  <li>the Mean Square Error (MSE) : 
\(MSE = \frac {1}{n} \sum (y - \hat{y})^2\)</li>
  <li>Mean Absolute Percentage Error (MAPE) :
\(MAPE = \frac {1} {n} \sum \mid { \frac {y - \hat{y}} {y}} \mid * 100%\)</li>
  <li>Mean Percentage Error (MPE) :
\(MPE = \frac {1} {n} \sum { \frac {y - \hat{y}} {y}} * 100%\)</li>
</ul>

<h2 id="bias---variance-tradeoff">Bias - Variance tradeoff</h2>

<p>A model reaching a high accuracy in the training part, and much lower accuracy. In such a case, we face an overfitting issue. This means that our model is in some sense learning too much from the train set and creating a model that might be too complex.</p>

<p><img src="https://maelfabien.github.io/assets/images/overfitting.jpg" alt="image" /></p>

<p>There is a tradeoff to make between the complexity of the model and the predictive power. It can be expressed as :
\(E( (y - \hat{y})^2 ) = {Bias}( \hat{y})^2 + {Var}( \hat{y}) + \sigma^2\)</p>

<p>Where :</p>
<ul>
  <li>
\[{Bias}[ \hat{y}] = E(\hat{y}) - y\]
  </li>
  <li>
\[{Var}( \hat{y}) = E(\hat{y}^2) - E(\hat{y})^2\]
  </li>
  <li>\(\sigma\) is the variance of the noise of the underlying model</li>
</ul>

<blockquote>
  <p><strong>Conclusion</strong> : This brief introduction to machine learning should get you started for the next series of articles on the different algorithms.</p>
</blockquote>]]></content><author><name>Dimitrios Georgiou</name><email>dgeorgiou3@gmail.com</email></author><category term="machinelearning" /><summary type="html"><![CDATA[Machine Learning Basics]]></summary></entry></feed>