<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ronnie's Tech Blogs]]></title><description><![CDATA[Ronnie's Tech Blogs]]></description><link>https://adeptschneiderthedev.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 21 Sep 2026 14:49:11 GMT</lastBuildDate><atom:link href="https://adeptschneiderthedev.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Fine-tuning XLS-R Wav2Vec2 model for Swahili Automatic Speech Recognition]]></title><description><![CDATA[Understanding Wav2Vec2
Wav2Vec2 is a pre-trained model for Automatic Speech Recognition (ASR) and was released in September 2020 by Alexei Baevski, Michael Auli, and Alex Conneau. Soon after the superior performance of Wav2Vec2 was demonstrated on on...]]></description><link>https://adeptschneiderthedev.hashnode.dev/fine-tuning-xls-r-wav2vec2-model-for-swahili-automatic-speech-recognition</link><guid isPermaLink="true">https://adeptschneiderthedev.hashnode.dev/fine-tuning-xls-r-wav2vec2-model-for-swahili-automatic-speech-recognition</guid><category><![CDATA[Wav2Vec2]]></category><category><![CDATA[automatic-speech-recognition]]></category><category><![CDATA[Swahili]]></category><dc:creator><![CDATA[Ronnie Leon]]></dc:creator><pubDate>Sun, 31 Dec 2023 15:16:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704029180834/7c7c858c-4139-45eb-86c2-3e38d9da3fdb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-understanding-wav2vec2">Understanding Wav2Vec2</h3>
<p><strong>Wav2Vec2</strong> is a pre-trained model for Automatic Speech Recognition (ASR) and was released in <a target="_blank" href="https://ai.meta.com/blog/wav2vec-20-learning-the-structure-of-speech-from-raw-audio/">September 2020</a> by <em>Alexei Baevski</em>, <em>Michael Auli</em>, and <em>Alex Conneau.</em> Soon after the superior performance of Wav2Vec2 was demonstrated on one of the most popular English datasets for ASR, called <a target="_blank" href="https://huggingface.co/datasets/librispeech_asr">LibriSpeech</a>, <em>Facebook AI</em> presented a multi-lingual version of Wav2Vec2, called <a target="_blank" href="https://arxiv.org/abs/2006.13979">XLSR</a>. XLSR stands for <em>cross-lingual speech representations</em> and refers to a model's ability to learn speech representations that are useful across multiple languages.</p>
<p>XLSR's successor, simply called <strong>XLS-R</strong> (referring to the "<a target="_blank" href="https://ai.meta.com/blog/-xlm-r-state-of-the-art-cross-lingual-understanding-through-self-supervision/">XLM-R</a> for Speech"), was released in <a target="_blank" href="https://ai.meta.com/blog/xls-r-self-supervised-speech-processing-for-128-languages/">November 2021</a> by <em>Arun Babu, Changhan Wang, Andros Tjandra, et al.</em> XLS-R used almost <strong>half a million</strong> hours of audio data in 128 languages for self-supervised pre-training and comes in sizes ranging from 300 million up to <strong>two billion</strong> parameters. You can find the pre-trained checkpoints on the 🤗 Hub:</p>
<ul>
<li><p><a target="_blank" href="https://huggingface.co/facebook/wav2vec2-xls-r-300m">Wav2Vec2-XLS-R-300M</a></p>
</li>
<li><p><a target="_blank" href="https://huggingface.co/facebook/wav2vec2-xls-r-1b">Wav2Vec2-XLS-R-1B</a></p>
</li>
<li><p><a target="_blank" href="https://huggingface.co/facebook/wav2vec2-xls-r-2b">Wav2Vec2-XLS-R-2B</a></p>
</li>
</ul>
<p>Similar to <a target="_blank" href="https://jalammar.github.io/illustrated-bert/">BERT's masked language modeling objective</a>, XLS-R learns contextualized speech representations by randomly masking feature vectors before passing them to a transformer network during self-supervised pre-training (diagram below).</p>
<p>For fine-tuning, a single linear layer is added on top of the pre-trained network to train the model on labeled data of audio downstream tasks such as speech recognition, speech translation, and audio classification (diagram below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704030538857/236b79ff-6214-4bba-9418-a390dfe8d2a1.png" alt class="image--center mx-auto" /></p>
<p>XLS-R shows impressive improvements over previous state-of-the-art results on both speech recognition, speech translation, and speaker/language identification.</p>
<h3 id="heading-setup">Setup</h3>
<p>In this blog, I will give an in-detail explanation of how XLS-R - more specifically the pre-trained checkpoint <a target="_blank" href="https://huggingface.co/facebook/wav2vec2-xls-r-300m">Wav2Vec2-XLS-R-300M</a> - was fine-tuned to develop a Swahili automatic speech recognition model with a word error rate of 0.083 evaluated on a private dataset by Mozilla.</p>
<p>XLS-R is fine-tuned using Connectionist Temporal Classification (CTC), which is an algorithm that is used to train neural networks for sequence-to-sequence problems, such as ASR and handwriting recognition.</p>
<p>I highly recommend reading the well-written blog post <a target="_blank" href="https://distill.pub/2017/ctc/">Sequence Modeling with CTC (2017)</a></p>
<p>While there are several pre-trained speech recognition models available, including Nvidia NeMo, Whisper, and Coqui, my preference has strongly inclined towards Wav2Vec2 XLS-R. From the moment I encountered it, there was an immediate sense that it outshines other models. Perhaps, this confidence stems from my positive experiences with products developed by MetaAI.</p>
<p>We initiated the process by installing the necessary packages</p>
<pre><code class="lang-python">!pip install datasets==<span class="hljs-number">1.18</span><span class="hljs-number">.3</span>
!pip install transformers==<span class="hljs-number">4.11</span><span class="hljs-number">.3</span>
!pip install huggingface_hub==<span class="hljs-number">0.1</span>
!pip install torchaudio
!pip install librosa
!pip install jiwer
</code></pre>
<p><em>torchaudio</em> was used to load audio files and <em>jiwer</em> to evaluate our fine-tuned model using the word error rate (WER) metric. In the <a target="_blank" href="https://arxiv.org/pdf/2006.13979.pdf">paper</a>, the model was evaluated using the phoneme error rate (PER), but by far the most common metric in ASR is the word error rate (WER)</p>
<h2 id="heading-prepare-data-tokenizer-feature-extractor">Prepare Data, Tokenizer, Feature Extractor</h2>
<p>ASR models transcribe speech to text, which means that we both need a feature extractor that processes the speech signal to the model's input format, <em>e.g.</em> a feature vector, and a tokenizer that processes the model's output format to text.</p>
<p>In 🤗 Transformers, the XLS-R model is thus accompanied by both a tokenizer, called <a target="_blank" href="https://huggingface.co/transformers/master/model_doc/wav2vec2.html#wav2vec2ctctokenizer"><strong>Wav2Vec2CTCTokenizer</strong></a>, and a feature extractor, called <a target="_blank" href="https://huggingface.co/transformers/master/model_doc/wav2vec2.html#wav2vec2featureextractor"><strong>Wav2Vec2FeatureExtractor</strong></a>.</p>
<p>We started by creating the tokenizer to decode the predicted output classes to the output transcription.</p>
<h3 id="heading-create-wav2vec2ctctokenizer">Create Wav2Vec2CTCTokenizer</h3>
<p>A pre-trained XLS-R model maps the speech signal to a sequence of context representations as illustrated in the figure above. However, for speech recognition, the model has to to map this sequence of context representations to its corresponding transcription which means that a linear layer has to be added on top of the transformer block (shown in yellow in the diagram above). This linear layer is used to classify each context representation to a token class analogous to how a linear layer is added on top of BERT's embeddings for further classification after pre-training (<em>cf.</em> with the <em>'BERT'</em> section of the following <a target="_blank" href="https://huggingface.co/blog/warm-starting-encoder-decoder"><strong>blog post</strong></a>). after pretraining a linear layer is added on top of BERT's embeddings for further classification - <em>cf.</em> with the <em>'BERT'</em> section of this <a target="_blank" href="https://huggingface.co/blog/warm-starting-encoder-decoder"><strong>blog post</strong></a>.</p>
<p>The output size of this layer corresponds to the number of tokens in the vocabulary, which does <strong>not</strong> depend on XLS-R's pretraining task, but only on the labeled dataset used for fine-tuning.</p>
<p>We performed fine-tuning on XLS-R using the <a target="_blank" href="https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0/tree/main">Common Voice 13.0</a> Swahili dataset.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datasets <span class="hljs-keyword">import</span> load_dataset

cv_swahili_train = load_dataset(<span class="hljs-string">"mozilla-foundation/common_voice_13_0"</span>, <span class="hljs-string">"sw"</span>, split=<span class="hljs-string">"train"</span>, use_auth_token=<span class="hljs-literal">True</span>)
cv_swahili_validate = load_dataset(<span class="hljs-string">"mozilla-foundation/common_voice_13_0"</span>, <span class="hljs-string">"sw"</span>, split=<span class="hljs-string">"validation"</span>, use_auth_token=<span class="hljs-literal">True</span>)
cv_swahili_test = load_dataset(<span class="hljs-string">"mozilla-foundation/common_voice_13_0"</span>, <span class="hljs-string">"sw"</span>, split=<span class="hljs-string">"test"</span>, use_auth_token=<span class="hljs-literal">True</span>)
</code></pre>
<p>Many ASR datasets only provide the target text, <code>'sentence'</code> for each audio array <code>'audio'</code> and file <code>'path'</code>. Common Voice provides much more information about each audio file, such as the <code>'accent'</code>, etc. Keeping the notebook as general as possible, we only consider the transcribed text for fine-tuning.</p>
<pre><code class="lang-python">columns_to_remove = [<span class="hljs-string">"accent"</span>, <span class="hljs-string">"age"</span>, <span class="hljs-string">"client_id"</span>, <span class="hljs-string">"down_votes"</span>, <span class="hljs-string">"gender"</span>, <span class="hljs-string">"locale"</span>, <span class="hljs-string">"path"</span>, <span class="hljs-string">"segment"</span>, <span class="hljs-string">"up_votes"</span>]
cv_swahili_train = cv_swahili_train.remove_columns(columns_to_remove)
cv_swahili_validate = cv_swahili_validate.remove_columns(columns_to_remove)
cv_swahili_test = cv_swahili_test.remove_columns(columns_to_remove)
</code></pre>
<p>The transcriptions contained some special characters, such as <code>,.?!;:</code>. Without a language model, it is much harder to classify speech chunks into such special characters because they don't correspond to a characteristic sound unit. <em>For</em>, the letter <code>"s"</code> has a more or less clear sound, whereas the special character <code>"."</code> does not. Also to understand the meaning of a speech signal, it is usually not necessary to include special characters in the transcription.</p>
<p>We removed all characters that don't contribute to the meaning of a word and cannot be represented by an acoustic sound and normalized the text.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> re

<span class="hljs-comment"># Modify the chars_to_remove_regex pattern to include the additional symbols</span>
chars_to_remove_regex = <span class="hljs-string">r'[\,\?\.\!\-\;\:\"\“\%\‘\”\�\'\(\)\*\=\_`\[\]\/\*°ː’•…]'</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">remove_special_characters</span>(<span class="hljs-params">batch</span>):</span>
    batch[<span class="hljs-string">"sentence"</span>] = re.sub(chars_to_remove_regex, <span class="hljs-string">''</span>, batch[<span class="hljs-string">"sentence"</span>]).lower()
    <span class="hljs-keyword">return</span> batch
</code></pre>
<pre><code class="lang-python">cv_swahili_train = cv_swahili_train.map(remove_special_characters)
cv_swahili_validate = cv_swahili_validate.map(remove_special_characters)
cv_swahili_test = cv_swahili_test.map(remove_special_characters)
</code></pre>
<p>Before finalizing the pre-processing, it is always advantageous to consult a native speaker of the target language to see whether the text can be further simplified. Given that we were native speakers of the Swahili language, we replaced "hatted" characters with their equivalent "un-hatted" characters.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">replace_extra_characters</span>(<span class="hljs-params">batch, column_name=<span class="hljs-string">"sentence"</span></span>):</span>
    character_replacements = {
        <span class="hljs-string">'µ'</span>: <span class="hljs-string">'u'</span>,
        <span class="hljs-string">'á'</span>: <span class="hljs-string">'a'</span>,
        <span class="hljs-string">'â'</span>: <span class="hljs-string">'a'</span>,
        <span class="hljs-string">'ã'</span>: <span class="hljs-string">'a'</span>,
        <span class="hljs-string">'å'</span>: <span class="hljs-string">'a'</span>,
        <span class="hljs-string">'é'</span>: <span class="hljs-string">'e'</span>,
        <span class="hljs-string">'è'</span>: <span class="hljs-string">'e'</span>,
        <span class="hljs-string">'ë'</span>: <span class="hljs-string">'e'</span>,
        <span class="hljs-string">'í'</span>: <span class="hljs-string">'i'</span>,
        <span class="hljs-string">'ï'</span>: <span class="hljs-string">'i'</span>,
        <span class="hljs-string">'ñ'</span>: <span class="hljs-string">'n'</span>,
        <span class="hljs-string">'ó'</span>: <span class="hljs-string">'o'</span>,
        <span class="hljs-string">'ö'</span>: <span class="hljs-string">'o'</span>,
        <span class="hljs-string">'ø'</span>: <span class="hljs-string">'o'</span>,
        <span class="hljs-string">'ú'</span>: <span class="hljs-string">'u'</span>,
        <span class="hljs-string">'š'</span>: <span class="hljs-string">'s'</span>,
        <span class="hljs-string">'ū'</span>: <span class="hljs-string">'u'</span>,
        <span class="hljs-string">'μ'</span>: <span class="hljs-string">'u'</span>,
        <span class="hljs-string">'ụ'</span>: <span class="hljs-string">'u'</span>
    }

    <span class="hljs-keyword">for</span> original, replacement <span class="hljs-keyword">in</span> character_replacements.items():
        batch[column_name] = re.sub(re.escape(original), replacement, batch[column_name])

    <span class="hljs-comment"># Remove multiple dots and tabs</span>
    batch[column_name] = re.sub(<span class="hljs-string">r'\.\.\.+'</span>, <span class="hljs-string">''</span>, batch[column_name])
    batch[column_name] = re.sub(<span class="hljs-string">r'\t'</span>, <span class="hljs-string">''</span>, batch[column_name])

    <span class="hljs-keyword">return</span> batch
</code></pre>
<pre><code class="lang-python">cv_swahili_train = cv_swahili_train.map(replace_extra_characters)
cv_swahili_validate = cv_swahili_validate.map(replace_extra_characters)
cv_swahili_test = cv_swahili_test.map(replace_extra_characters)
</code></pre>
<p>In CTC, it is common to classify speech chunks into letters, so we did the same here. Let's extract all distinct letters of the training and test data and build our vocabulary from this set of letters.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">extract_all_chars</span>(<span class="hljs-params">batch</span>):</span>
  all_text = <span class="hljs-string">" "</span>.join(batch[<span class="hljs-string">"sentence"</span>])
  vocab = list(set(all_text))
  <span class="hljs-keyword">return</span> {<span class="hljs-string">"vocab"</span>: [vocab], <span class="hljs-string">"all_text"</span>: [all_text]}
</code></pre>
<pre><code class="lang-python">vocab_train = cv_swahili_train.map(extract_all_chars, batched=<span class="hljs-literal">True</span>, batch_size=<span class="hljs-number">-1</span>, remove_columns=cv_swahili_train.column_names)
vocab_validate = cv_swahili_validate.map(extract_all_chars, batched=<span class="hljs-literal">True</span>, batch_size=<span class="hljs-number">-1</span>, remove_columns=cv_swahili_validate.column_names)
vocab_test = cv_swahili_test.map(extract_all_chars, batched=<span class="hljs-literal">True</span>, batch_size=<span class="hljs-number">-1</span>, remove_columns=cv_swahili_test.column_names)
</code></pre>
<pre><code class="lang-python"><span class="hljs-comment"># Convert "vocab" column from each dataset to sets and union them</span>
vocab_set_train = set(vocab_train[<span class="hljs-string">"vocab"</span>][<span class="hljs-number">0</span>])
vocab_set_validate = set(vocab_validate[<span class="hljs-string">"vocab"</span>][<span class="hljs-number">0</span>])
vocab_set_test = set(vocab_test[<span class="hljs-string">"vocab"</span>][<span class="hljs-number">0</span>])

<span class="hljs-comment"># Merge vocabularies</span>
vocab_set = vocab_set_train | vocab_set_validate | vocab_set_test

<span class="hljs-comment"># Convert the result back to a list</span>
vocab_list = list(vocab_set)
</code></pre>
<p>The model has to learn to predict when a word is finished or else the model prediction would always be a sequence of chars which would make it impossible to separate words from each other.</p>
<p>One should always keep in mind that pre-processing is a very important step before training your model. E.g., we don't want our model to differentiate between <code>a</code> and <code>A</code> just because we forgot to normalize the data. The difference between <code>a</code> and <code>A</code> does not depend on the "sound" of the letter at all, but more on grammatical rules - <em>e.g.</em> use a capitalized letter at the beginning of the sentence. So it is sensible to remove the difference between capitalized and non-capitalized letters so that the model has an easier time learning to transcribe speech.</p>
<p>To make it clearer that <code>" "</code> has its token class, we give it a more visible character <code>|</code>. In addition, we also add an "unknown" token so that the model can later deal with characters not encountered in Common Voice's training set.</p>
<pre><code class="lang-python">vocab_dict[<span class="hljs-string">"|"</span>] = vocab_dict[<span class="hljs-string">" "</span>]
<span class="hljs-keyword">del</span> vocab_dict[<span class="hljs-string">" "</span>]
</code></pre>
<p>Finally, we also added a padding token that corresponds to CTC's "<em>blank token</em>". The "blank token" is a core component of the CTC algorithm. For more information, please take a look at the "Alignment" section <a target="_blank" href="https://distill.pub/2017/ctc/"><strong>here</strong></a>.</p>
<pre><code class="lang-python">vocab_dict[<span class="hljs-string">"[UNK]"</span>] = len(vocab_dict)
vocab_dict[<span class="hljs-string">"[PAD]"</span>] = len(vocab_dict)
len(vocab_dict)
</code></pre>
<p>We saved the vocabulary as a JSON file.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'vocab.json'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> vocab_file:
    json.dump(vocab_dict, vocab_file)
</code></pre>
<p>We used the JSON file to load the vocabulary into an instance of the <code>Wav2Vec2CTCTokenizer</code> class.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> Wav2Vec2CTCTokenizer

tokenizer = Wav2Vec2CTCTokenizer.from_pretrained(<span class="hljs-string">"./"</span>, unk_token=<span class="hljs-string">"[UNK]"</span>, pad_token=<span class="hljs-string">"[PAD]"</span>, word_delimiter_token=<span class="hljs-string">"|"</span>)
</code></pre>
<h3 id="heading-create-wav2vec2featureextractor">Create Wav2Vec2FeatureExtractor</h3>
<p>Speech is a continuous signal, and, to be treated by computers, it first has to be discretized, which is usually called <strong>sampling</strong>. The sampling rate hereby plays an important role since it defines how many data points of the speech signal are measured per second. Therefore, sampling with a higher sampling rate results in a better approximation of the <em>real</em> speech signal but also necessitates more values per second.</p>
<p>A pre-trained checkpoint expects its input data to have been sampled more or less from the same distribution as the data it was trained on. The same speech signals sampled at two different rates have a very different distribution. For example, doubling the sampling rate results in data points being twice as long. Thus, before fine-tuning a pre-trained checkpoint of an ASR model, it is crucial to verify that the sampling rate of the data that was used to pre-train the model matches the sampling rate of the dataset used to fine-tune the model.</p>
<p>XLS-R was pre-trained on audio data of <a target="_blank" href="http://www.reading.ac.uk/AcaDepts/ll/speechlab/babel/r"><strong>Babel</strong></a>, <a target="_blank" href="https://huggingface.co/datasets/multilingual_librispeech"><strong>Multilingual LibriSpeech (MLS)</strong></a>, <a target="_blank" href="https://huggingface.co/datasets/common_voice"><strong>Common Voice</strong></a>, <a target="_blank" href="https://arxiv.org/abs/2101.00390"><strong>VoxPopuli</strong></a>, and <a target="_blank" href="https://arxiv.org/abs/2011.12998"><strong>VoxLingua107</strong></a> at a sampling rate of 16kHz. Common Voice, in its original form, has a sampling rate of 48kHz, thus we will have to downsample the fine-tuning data to 16kHz in the following.</p>
<p>A <code>Wav2Vec2FeatureExtractor</code> object requires the following parameters to be instantiated:</p>
<ul>
<li><p><code>feature_size</code>: Speech models take a sequence of feature vectors as input. While the length of this sequence varies, the feature size should not. In the case of Wav2Vec2, the feature size is 1 because the model was trained on the raw speech signal 22.</p>
</li>
<li><p><code>sampling_rate</code>: The sampling rate at which the model is trained on.</p>
</li>
<li><p><code>padding_value</code>: For batched inference, shorter inputs need to be padded with a specific value</p>
</li>
<li><p><code>do_normalize</code>: Whether the input should be <em>zero-mean-unit-variance</em> normalized or not. Usually, speech models perform better when normalizing the input</p>
</li>
<li><p><code>return_attention_mask</code>: Whether the model should make use of a <code>attention_mask</code> for batched inference. In general, XLS-R model checkpoints should <strong>always</strong> use the <code>attention_mask</code>.</p>
</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> Wav2Vec2FeatureExtractor

feature_extractor = Wav2Vec2FeatureExtractor(feature_size=<span class="hljs-number">1</span>, sampling_rate=<span class="hljs-number">16000</span>, padding_value=<span class="hljs-number">0.0</span>, do_normalize=<span class="hljs-literal">True</span>, return_attention_mask=<span class="hljs-literal">True</span>)
</code></pre>
<p>For improved user-friendliness, the feature extractor and tokenizer are <em>wrapped</em> into a single <code>Wav2Vec2Processor</code> class so that one only needs a <code>model</code> and <code>processor</code> object.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> Wav2Vec2Processor

processor = Wav2Vec2Processor(feature_extractor=feature_extractor, tokenizer=tokenizer)
</code></pre>
<h2 id="heading-preprocess-data">Preprocess Data</h2>
<p>In addition to <code>sentence</code>, our datasets include two more column names <code>path</code> and <code>audio</code>. <code>path</code> states the absolute path of the audio file.</p>
<pre><code class="lang-python">cv_swahili_train[<span class="hljs-number">0</span>][<span class="hljs-string">"path"</span>]
</code></pre>
<p>XLS-R expects the input in the format of a 1-dimensional array of 16 kHz. This means that the audio file has to be loaded and resampled.</p>
<pre><code class="lang-python">cv_swahili_train[<span class="hljs-number">0</span>][<span class="hljs-string">"audio"</span>]
</code></pre>
<pre><code class="lang-python">    {<span class="hljs-string">'array'</span>: array([ <span class="hljs-number">0.0000000e+00</span>,  <span class="hljs-number">0.0000000e+00</span>,  <span class="hljs-number">0.0000000e+00</span>, ...,
            <span class="hljs-number">-8.8930130e-05</span>, <span class="hljs-number">-3.8027763e-05</span>, <span class="hljs-number">-2.9146671e-05</span>], dtype=float32),
     <span class="hljs-string">'path'</span>: <span class="hljs-string">'/root/.cache/huggingface/datasets/downloads/extracted/05be0c29807a73c9b099873d2f5975dae6d05e9f7d577458a2466ecb9a2b0c6b/cv-corpus-6.1-2020-12-11/tr/clips/common_voice_tr_21921195.mp3'</span>,
     <span class="hljs-string">'sampling_rate'</span>: <span class="hljs-number">48000</span>}
</code></pre>
<p>The audio data is loaded with a sampling rate of 48kHz whereas 16kHz is expected by the model. We set the audio feature to the correct sampling rate by making use of <a target="_blank" href="https://huggingface.co/docs/datasets/package_reference/main_classes.html?highlight=cast_column#datasets.DatasetDict.cast_column"><code>cast_column</code></a>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datasets <span class="hljs-keyword">import</span> load_metric, Audio
cv_swahili_train = cv_swahili_train.cast_column(<span class="hljs-string">"audio"</span>, Audio(sampling_rate=<span class="hljs-number">16</span>_000))
cv_swahili_validate = cv_swahili_validate.cast_column(<span class="hljs-string">"audio"</span>, Audio(sampling_rate=<span class="hljs-number">16</span>_000))
cv_swahili_test = cv_swahili_test.cast_column(<span class="hljs-string">"audio"</span>, Audio(sampling_rate=<span class="hljs-number">16</span>_000))
</code></pre>
<p>Looking at "audio" again, the sampling rate is now at 16kHz.</p>
<pre><code class="lang-python">cv_swahili_train[<span class="hljs-number">0</span>][<span class="hljs-string">"audio"</span>]
</code></pre>
<pre><code class="lang-python">    {<span class="hljs-string">'array'</span>: array([ <span class="hljs-number">0.0000000e+00</span>,  <span class="hljs-number">0.0000000e+00</span>,  <span class="hljs-number">0.0000000e+00</span>, ...,
            <span class="hljs-number">-7.4556941e-05</span>, <span class="hljs-number">-1.4621433e-05</span>, <span class="hljs-number">-5.7861507e-05</span>], dtype=float32),
     <span class="hljs-string">'path'</span>: <span class="hljs-string">'/root/.cache/huggingface/datasets/downloads/extracted/05be0c29807a73c9b099873d2f5975dae6d05e9f7d577458a2466ecb9a2b0c6b/cv-corpus-6.1-2020-12-11/tr/clips/common_voice_tr_21921195.mp3'</span>,
     <span class="hljs-string">'sampling_rate'</span>: <span class="hljs-number">16000</span>}
</code></pre>
<p>We leveraged <code>Wav2Vec2Processor</code> to process the data to the format expected <code>Wav2Vec2ForCTC</code> for training.</p>
<p>First, we loaded and resampled the audio data, simply by calling <code>batch["audio"]</code>. Second, we extracted the <code>input_values</code> from the loaded audio file. In our case, the <code>Wav2Vec2Processor</code> only normalizes the data. For other speech models, however, this step can include more complex feature extraction, such as <a target="_blank" href="https://en.wikipedia.org/wiki/Mel-frequency_cepstrum"><strong>Log-Mel feature extraction</strong></a>. Third, we encode the transcriptions to label IDs.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">prepare_dataset</span>(<span class="hljs-params">batch</span>):</span>
    audio = batch[<span class="hljs-string">"audio"</span>]

    <span class="hljs-comment"># batched output is "un-batched"</span>
    batch[<span class="hljs-string">"input_values"</span>] = processor(audio[<span class="hljs-string">"array"</span>], sampling_rate=audio[<span class="hljs-string">"sampling_rate"</span>]).input_values[<span class="hljs-number">0</span>]
    batch[<span class="hljs-string">"input_length"</span>] = len(batch[<span class="hljs-string">"input_values"</span>])

    <span class="hljs-keyword">with</span> processor.as_target_processor():
        batch[<span class="hljs-string">"labels"</span>] = processor(batch[<span class="hljs-string">"sentence"</span>]).input_ids
    <span class="hljs-keyword">return</span> batch
</code></pre>
<h2 id="heading-training">Training</h2>
<p>The data is processed so that we are ready to start setting up the training pipeline. We will make use of 🤗's <a target="_blank" href="https://huggingface.co/transformers/master/main_classes/trainer.html?highlight=trainer"><strong>Trainer</strong></a> for which we essentially need to do the following:</p>
<ul>
<li><p>Define a data collator. In contrast to most NLP models, XLS-R has a much larger input length than output length. <em>E.g.</em>, a sample of input length 50000 has an output length of no more than 100. Given the large input sizes, it is much more efficient to pad the training batches dynamically meaning that all training samples should only be padded to the longest sample in their batch and not the overall longest sample. Therefore, fine-tuning XLS-R requires a special padding data collator, which we will define below</p>
</li>
<li><p>Evaluation metric. During training, the model should be evaluated on the word error rate. We should define a <code>compute_metrics</code> function accordingly</p>
</li>
<li><p>Load a pre-trained checkpoint. We need to load a pre-trained checkpoint and configure it correctly for training.</p>
</li>
<li><p>Define the training configuration.</p>
</li>
</ul>
<p>After having fine-tuned the model, we will correctly evaluate it on the test data and verify that it has indeed learned to correctly transcribe speech.</p>
<h3 id="heading-set-up-trainer">Set-up Trainer</h3>
<p>We started by defining the data collator. The code for the data collator was copied from <a target="_blank" href="https://github.com/huggingface/transformers/blob/7e61d56a45c19284cfda0cee8995fb552f6b1f4e/examples/pytorch/speech-recognition/run_speech_recognition_ctc.py#L219"><strong>this example</strong></a>.</p>
<p>Without going into too many details, in contrast to the common data collators, this data collator treats the <code>input_values</code> and <code>labels</code> differently and thus applies to separate padding functions on them (again making use of the XLS-R processor's context manager). This is necessary because in speech input and output are of different modalities meaning that they should not be treated by the same padding function. Analogous to the common data collators, the padding tokens in the labels with <code>-100</code> so that those tokens are <strong>not</strong> taken into account when computing the loss.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch

<span class="hljs-keyword">from</span> dataclasses <span class="hljs-keyword">import</span> dataclass, field
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Any, Dict, List, Optional, Union

<span class="hljs-meta">@dataclass</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataCollatorCTCWithPadding</span>:</span>
    <span class="hljs-string">"""
    Data collator that will dynamically pad the inputs received.
    Args:
        processor (:class:`~transformers.Wav2Vec2Processor`)
            The processor used for proccessing the data.
        padding (:obj:`bool`, :obj:`str` or :class:`~transformers.tokenization_utils_base.PaddingStrategy`, `optional`, defaults to :obj:`True`):
            Select a strategy to pad the returned sequences (according to the model's padding side and padding index)
            among:
            * :obj:`True` or :obj:`'longest'`: Pad to the longest sequence in the batch (or no padding if only a single
              sequence if provided).
            * :obj:`'max_length'`: Pad to a maximum length specified with the argument :obj:`max_length` or to the
              maximum acceptable input length for the model if that argument is not provided.
            * :obj:`False` or :obj:`'do_not_pad'` (default): No padding (i.e., can output a batch with sequences of
              different lengths).
    """</span>

    processor: Wav2Vec2Processor
    padding: Union[bool, str] = <span class="hljs-literal">True</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, features: List[Dict[str, Union[List[int], torch.Tensor]]]</span>) -&gt; Dict[str, torch.Tensor]:</span>
        <span class="hljs-comment"># split inputs and labels since they have to be of different lengths and need</span>
        <span class="hljs-comment"># different padding methods</span>
        input_features = [{<span class="hljs-string">"input_values"</span>: feature[<span class="hljs-string">"input_values"</span>]} <span class="hljs-keyword">for</span> feature <span class="hljs-keyword">in</span> features]
        label_features = [{<span class="hljs-string">"input_ids"</span>: feature[<span class="hljs-string">"labels"</span>]} <span class="hljs-keyword">for</span> feature <span class="hljs-keyword">in</span> features]

        batch = self.processor.pad(
            input_features,
            padding=self.padding,
            return_tensors=<span class="hljs-string">"pt"</span>,
        )
        <span class="hljs-keyword">with</span> self.processor.as_target_processor():
            labels_batch = self.processor.pad(
                label_features,
                padding=self.padding,
                return_tensors=<span class="hljs-string">"pt"</span>,
            )

        <span class="hljs-comment"># replace padding with -100 to ignore loss correctly</span>
        labels = labels_batch[<span class="hljs-string">"input_ids"</span>].masked_fill(labels_batch.attention_mask.ne(<span class="hljs-number">1</span>), <span class="hljs-number">-100</span>)

        batch[<span class="hljs-string">"labels"</span>] = labels

        <span class="hljs-keyword">return</span> batch
</code></pre>
<p>Next, the evaluation metric was defined.</p>
<pre><code class="lang-python">wer_metric = load_metric(<span class="hljs-string">"wer"</span>)
</code></pre>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compute_metrics</span>(<span class="hljs-params">pred</span>):</span>
    pred_logits = pred.predictions
    pred_ids = np.argmax(pred_logits, axis=<span class="hljs-number">-1</span>)

    pred.label_ids[pred.label_ids == <span class="hljs-number">-100</span>] = processor.tokenizer.pad_token_id

    pred_str = processor.batch_decode(pred_ids)
    <span class="hljs-comment"># we do not want to group tokens when computing the metrics</span>
    label_str = processor.batch_decode(pred.label_ids, group_tokens=<span class="hljs-literal">False</span>)

    wer = wer_metric.compute(predictions=pred_str, references=label_str)

    <span class="hljs-keyword">return</span> {<span class="hljs-string">"wer"</span>: wer}
</code></pre>
<p>Next, we loaded the pre-trained checkpoint of <a target="_blank" href="https://huggingface.co/facebook/wav2vec2-xls-r-300m"><strong>Wav2Vec2-XLS-R-300M</strong></a>. The tokenizer's <code>pad_token_id</code> must define the model's <code>pad_token_id</code> or in the case of <code>Wav2Vec2ForCTC</code> also CTC's <em>blank token</em> 22. To save GPU memory, we enable PyTorch's <a target="_blank" href="https://pytorch.org/docs/stable/checkpoint.html"><strong>gradient checkpointing</strong></a> and also set the loss reduction to "<em>mean</em>".</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> Wav2Vec2ForCTC

model = Wav2Vec2ForCTC.from_pretrained(
    <span class="hljs-string">"facebook/wav2vec2-xls-r-300m"</span>, 
    attention_dropout=<span class="hljs-number">0.0</span>,
    hidden_dropout=<span class="hljs-number">0.0</span>,
    feat_proj_dropout=<span class="hljs-number">0.0</span>,
    mask_time_prob=<span class="hljs-number">0.05</span>,
    layerdrop=<span class="hljs-number">0.0</span>,
    ctc_loss_reduction=<span class="hljs-string">"mean"</span>, 
    pad_token_id=processor.tokenizer.pad_token_id,
    vocab_size=len(processor.tokenizer),
)
</code></pre>
<p>The first component of XLS-R consists of a stack of CNN layers that are used to extract acoustically meaningful - but contextually independent - features from the raw speech signal. This part of the model has already been sufficiently trained during pretraining and as stated in the <a target="_blank" href="https://arxiv.org/pdf/2006.13979.pdf"><strong>paper</strong></a> does not need to be fine-tuned anymore. Thus, we set the <code>requires_grad</code> to <code>False</code> for all parameters of the <em>feature extraction</em> part.</p>
<pre><code class="lang-python">model.freeze_feature_extractor()
</code></pre>
<p>In the final step, we defined all parameters related to training. To give more explanation on some of the parameters:</p>
<ul>
<li><p><code>group_by_length</code> makes training more efficient by grouping training samples of similar input length into one batch. This can significantly speed up training time by heavily reducing the overall number of useless padding tokens that are passed through the model</p>
</li>
<li><p><code>learning_rate</code> and <code>weight_decay</code> were heuristically tuned until fine-tuning has become stable. Note that those parameters strongly depend on the Common Voice dataset and might be suboptimal for other speech datasets.</p>
</li>
</ul>
<p>For more explanations on other parameters, one can take a look at the <a target="_blank" href="https://huggingface.co/transformers/master/main_classes/trainer.html?highlight=trainer#trainingarguments"><strong>docs</strong></a>.</p>
<p>During training, a checkpoint was uploaded asynchronously to the Hub every 400 training steps.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> TrainingArguments, get_linear_schedule_with_warmup

<span class="hljs-comment"># Check if you are running on a CUDA-enabled device before enabling FP16</span>
<span class="hljs-keyword">import</span> torch
<span class="hljs-keyword">if</span> torch.cuda.is_available():
    fp16_enabled = <span class="hljs-literal">True</span>
<span class="hljs-keyword">else</span>:
    fp16_enabled = <span class="hljs-literal">False</span>
    print(<span class="hljs-string">"CUDA device not available. Disabling FP16."</span>)

training_args = TrainingArguments(
  output_dir=repo_name,
  group_by_length=<span class="hljs-literal">True</span>,
  per_device_train_batch_size=<span class="hljs-number">16</span>,
  gradient_accumulation_steps=<span class="hljs-number">2</span>,
  evaluation_strategy=<span class="hljs-string">"steps"</span>,
  num_train_epochs=<span class="hljs-number">15</span>,
  gradient_checkpointing=<span class="hljs-literal">True</span>,
  fp16=fp16_enabled,  <span class="hljs-comment"># Enable FP16 only if CUDA is available</span>
  save_steps=<span class="hljs-number">400</span>,
  eval_steps=<span class="hljs-number">400</span>,
  logging_steps=<span class="hljs-number">400</span>,
  learning_rate=<span class="hljs-number">3e-4</span>,
  warmup_steps=<span class="hljs-number">500</span>,
  save_total_limit=<span class="hljs-number">2</span>,
  push_to_hub=<span class="hljs-literal">True</span>,
  remove_unused_columns=<span class="hljs-literal">False</span>
)

<span class="hljs-comment"># Define the scheduler parameters</span>
num_warmup_steps = int(training_args.max_steps * <span class="hljs-number">0.1</span>)
</code></pre>
<p>All instances were passed to the Trainer set for training.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> Trainer
<span class="hljs-keyword">from</span> torch.optim.lr_scheduler <span class="hljs-keyword">import</span> CosineAnnealingLR
<span class="hljs-keyword">from</span> torch.optim <span class="hljs-keyword">import</span> AdamW

trainer = Trainer(
    model=model,
    data_collator=data_collator,
    args=training_args,
    compute_metrics=compute_metrics,
    train_dataset=combined_train_validate,
    eval_dataset=cv_Swahili_test,
    tokenizer=processor.feature_extractor,
)

<span class="hljs-comment"># Create the learning rate scheduler</span>
optimizer = AdamW(model.parameters(), lr=training_args.learning_rate)
total_steps = <span class="hljs-number">1000</span>
scheduler = CosineAnnealingLR(
    optimizer,
    T_max=total_steps,
    eta_min=<span class="hljs-number">0</span>,  <span class="hljs-comment"># Minimum learning rate</span>
)

<span class="hljs-comment"># Define a function to update the learning rate during training</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_lr</span>():</span>
  scheduler.step()
</code></pre>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Learning_rate">Learning rate</a> is one of the most important <a target="_blank" href="https://en.wikipedia.org/wiki/Hyperparameter">hyperparameters</a> in the training of neural networks, impacting the speed and effectiveness of the learning process. A learning rate that is too high can cause the model to oscillate around the minimum, while a learning rate that is too low can cause the training process to be very slow or even stall. In the context of machine learning, the learning rate is a <a target="_blank" href="https://en.wikipedia.org/wiki/Hyperparameter">hyperparameter</a> that determines the step size at which an optimization algorithm (like gradient descent) proceeds while attempting to minimize the loss function. A learning rate scheduler is a method that adjusts the learning rate during the training process, often lowering it as the training progresses. This helps the model to make large updates at the beginning of training when the parameters are far from their optimal values, and smaller updates later when the parameters are closer to their optimal values, allowing for more fine-tuning. Several learning rate schedulers are widely used in practice:</p>
<ol>
<li><p>Step Decay</p>
</li>
<li><p>Exponential Decay</p>
</li>
<li><p>Cosine Annealing</p>
</li>
</ol>
<p>Cosine Annealing was used to adjust the learning rate during training. Cosine annealing reduces the learning rate using a cosine-based schedule. The form of the cosine annealing is defined as:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704035235047/0e6fb31a-85fa-4abd-80c1-2d2f7467e83a.png" alt class="image--center mx-auto" /></p>
<p>where:</p>
<ul>
<li><p><em>lr_min</em>​ is the minimum learning rate,</p>
</li>
<li><p><em>lr_max</em>​ is the maximum learning rate, and</p>
</li>
<li><p>epoch and max_epochs are the current and maximum number of epochs respectively.</p>
</li>
</ul>
<p>Learning rate schedulers are an important tool in the machine learning practitioner’s toolkit, providing a mechanism to adjust the learning rate over time, which can help to improve the efficiency and effectiveness of the training process. The best learning rate scheduler to use can depend on the specific problem and dataset, and it is often helpful to experiment with different schedulers to see which one works best. In our case, cosine annealing worked pretty well.</p>
<p>Checkout the following for further details:</p>
<ul>
<li><p><a target="_blank" href="https://colab.research.google.com/drive/1td6aXyUCTv_1iFFT5RZB23Kgr2LduxLB?usp=sharing">Google Colab Notebook with full implementation</a></p>
</li>
<li><p><a target="_blank" href="https://huggingface.co/spaces/Adeptschneider/Swahili-automatic-speech-recognition">Gradio app</a></p>
</li>
<li><p><a target="_blank" href="https://huggingface.co/Adeptschneider/wav2vec-large-swahili-asr-model-with-swahili-language-model">Fine-tuned Wav2Vec2 Swahili speech recognition model</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[What happens when you type 'www.google.com' in your browser and press enter]]></title><description><![CDATA[Practically everyone who has used the internet has at some point entered "https://www.google.com" into their web browser and pressed enter. Have you ever asked yourself what unfolds right after you press the enter key?
Or perhaps what transpires with...]]></description><link>https://adeptschneiderthedev.hashnode.dev/what-happens-when-you-type-wwwgooglecom-in-your-browser-and-press-enter</link><guid isPermaLink="true">https://adeptschneiderthedev.hashnode.dev/what-happens-when-you-type-wwwgooglecom-in-your-browser-and-press-enter</guid><category><![CDATA[Google]]></category><category><![CDATA[dns]]></category><category><![CDATA[dns resolver]]></category><category><![CDATA[Load Balancing]]></category><category><![CDATA[firewall]]></category><dc:creator><![CDATA[Ronnie Leon]]></dc:creator><pubDate>Wed, 18 Oct 2023 04:07:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1697403622257/3d1d99a3-d3b8-411e-a1fe-d60a527ea1ec.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Practically everyone who has used the internet has at some point entered "https://www.google.com" into their web browser and pressed enter. Have you ever asked yourself what unfolds right after you press the enter key?</p>
<p>Or perhaps what transpires within the few seconds after hitting the enter key to generate the list of search results that Google displays when you search for something. If you have ever been that curious, then allow me to break it down for you.</p>
<p>I presume you know what a browser is but if you don't, that is also fine. A browser is basically an application that you use to access the web. Common examples include: Chrome, Firefox, Safari, Edge, Brave, etc</p>
<p>Fasten your seatbelt, because we're diving in!</p>
<h2 id="heading-what-happens-when-you-type-a-url-into-a-web-browser">What happens when you type a URL into a web browser</h2>
<p>When you type a URL like "https://www.google.com" into a web browser and hit your enter key, there are a lot of things that go on before you finally get some output on your browser.</p>
<p>All the activities that transpire occur in a split second, so you hardly ever stop to think about them. Before we dive into a more in-depth explanation of all that happens, let me provide you with a general overview of everything that goes on within those few microseconds.</p>
<ol>
<li><p>Your computer sends a request to the domain name system (DNS) server which serves as an address book for all domain names. The DNS server then sends back the exact IP address of the server which <code>https://www.google.com</code> points to.</p>
</li>
<li><p>Knowing this IP, your computer then establishes a connection with the server through the IP address. The type of this connection is known as Transmission Control Protocol (TCP) and your computer is able to establish this connection through the Internet Protocol (IP). This whole process is known as a "handshake"</p>
</li>
<li><p>If your computer is behind a firewall, the firewall checks to ensure that the particular request you are making is allowed before permitting it. Also, if the server you are trying to access is behind a firewall, a similar check will be done before you are finally able to connect to the server.</p>
</li>
<li><p>After establishing the connection, your browser now sends a request for the webpage using an encryption protocol like Secure Sockets Layer (SSL) or Transport Layer Security (TLS) in order to encrypt the data that will be shared between your computer and the server. This type of encryption is what is responsible for the "s" in "https" which also implies that the connection is secure.</p>
</li>
<li><p>Companies like Google with high traffic maintain a host of servers and for that matter, they have a load balancer that receives most of the requests and sends them to a particular server. The request from your browser will therefore hit the load balancer first which will forward it to a specific server depending on the algorithm used by the load balancer.</p>
</li>
<li><p>The server that receives the request then sends a response back to the load balancer which also forwards the response back to your browser. This response will mostly include HTML, CSS, and JavaScript files that makeup Google's homepage.</p>
</li>
<li><p>The HTML files returned tell the browser how to render the content of the page. The CSS files tell the browser how to style the content while the JavaScript file adds interactivity to the page.</p>
</li>
<li><p>If there is a need for some dynamic content such as Google search results, then the web server will make a request to the application server, which in turn may make a request to a database server to get some data and send it back to the web server. The web server will then include these in the response that it sends back to the browser.</p>
</li>
<li><p>Finally, the browser will render the page and display it to you.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697597412549/36035f45-a555-4159-8484-9005591beae5.png" alt class="image--center mx-auto" /></p>
<p>Do you see the number of events that go on before you finally see anything in your browser? This is a high-level overview of what happens. Now let's take each part and discuss in detail what actually happens.</p>
<h2 id="heading-dns-request">DNS Request</h2>
<p>Anytime you use your browser to access any website or domain, the browser stores information concerning that domain name (DNS record) in its cache.</p>
<p>So, anytime you type a domain name like "google.com" into your web browser, the browser first checks its cache to see if it has a recent copy of the DNS record for that domain.</p>
<p>If there is a recent copy of the DNS records for that domain, it will use the IP address in the cache to send a request to the server. This speeds up the process of resolving the domain name to an IP address because it avoids the need to send a request to the DNS server.</p>
<p>If the browser cache does not contain a recent copy of the DNS record, or if the DNS record has changed since the last time it was cached, the browser will send a request to the DNS server to resolve the domain name to an IP address.</p>
<h3 id="heading-dns-lookup-process">DNS lookup process</h3>
<p>Here's how the DNS lookup process works:</p>
<ol>
<li><p>The browser sends a request to the local DNS resolver, which is usually provided by the internet service provider (ISP).</p>
</li>
<li><p>The local DNS resolver checks its cache to see if it has a recent copy of the DNS record for the domain. If it does, it sends the IP address back to the browser.</p>
</li>
<li><p>If the local DNS resolver does not have a recent copy of the DNS record, it sends a request to a root nameserver.</p>
</li>
<li><p>The root nameserver responds with the address of a top-level domain (TLD) nameserver, such as .com or .org.</p>
</li>
<li><p>The local DNS resolver sends a request to the TLD nameserver.</p>
</li>
<li><p>The TLD nameserver responds with the address of the authoritative nameserver for the domain.</p>
</li>
<li><p>The local DNS resolver sends a request to the authoritative nameserver.</p>
</li>
<li><p>The authoritative nameserver responds with the IP address for the domain.</p>
</li>
<li><p>The local DNS resolver sends the IP address back to the browser.</p>
</li>
<li><p>The browser sends a request to the server at the IP address to retrieve the webpage.</p>
</li>
</ol>
<p>This process may involve additional steps if the DNS record is not found at any of the nameservers, or if the DNS record has been configured to use a service such as DNS load balancing or content delivery networks (CDN)</p>
<p>Once the IP address has been resolved, it is cached by the local DNS resolver and the browser so that future requests for the same domain name can be resolved more quickly.</p>
<p>The length of time that the DNS record is cached ("TTL", or Time To Live) is determined by the authoritative nameserver and can be configured by the domain owner.</p>
<h2 id="heading-tcpip-connection">TCP/IP connection</h2>
<p>TCP (Transmission Control Protocol) and IP (Internet Protocol) are two of the main protocols that make up the Internet.</p>
<p>They work together to establish a connection between a client and a server and facilitate the transmission of data between them.</p>
<p>When you enter <code>google.com</code> into a browser, the browser uses <code>TCP/IP</code> to establish a connection with the server that hosts the website.</p>
<p>Here's what happens in more detail:</p>
<ol>
<li><p>The browser sends a request to the server using IP to establish a connection.</p>
</li>
<li><p>The server receives the request and sends back a message acknowledging the request to establish a connection. This is the handshake process.</p>
</li>
<li><p>Once the handshake is complete, the browser can send a request for the webpage it wants to access (in this case, the homepage of <strong>google.com)</strong> This request is sent using TCP, which ensures that the request is transmitted reliably and in the correct order.</p>
</li>
<li><p>The server receives the request and sends back the HTML code for the homepage of <strong>google.com</strong> to the browser. This response is also sent using TCP to ensure reliable transmission.</p>
</li>
<li><p>The browser receives the HTML code and uses it to render the webpage on your screen. Any resources (such as images) that the webpage needs are also requested and received using TCP/IP</p>
</li>
</ol>
<h2 id="heading-firewall">Firewall</h2>
<p>A firewall is a security system that monitors and controls incoming and outgoing network traffic based on predetermined security rules. Its primary purpose is to protect a network from external threats, such as hackers and malware.</p>
<p>When you type a URL like <strong>google.com</strong> into your browser, the request that your browser makes to Google's server passes through a firewall. The firewall checks the incoming request to make sure it is allowed based on its security rules.</p>
<p>There are two main types of security rules that a firewall uses to check incoming requests:</p>
<ol>
<li><p>Rules that allow or block traffic based on the source and destination of the request. For example, a firewall may be configured to block all traffic from certain countries or to allow only certain IP addresses to access the network.</p>
</li>
<li><p>Rules that allow or block traffic based on the type of traffic. For example, a firewall may be configured to block all traffic on certain ports (such as those used by malware) or to allow only certain types of traffic (such as HTTP or HTTPS)</p>
</li>
</ol>
<p>If an incoming request meets the security rules set by the firewall in front of Google's server, it is allowed through, and the browser is able to access the website.</p>
<h2 id="heading-httpsssl">HTTPS/SSL</h2>
<p>HTTPS (Hypertext Transfer Protocol Secure) is a secure version of the HTTP protocol used to transmit data on the Internet. It is used to encrypt the data transmitted between your browser and Google's server.</p>
<p>SSL (Secure Sockets Layer) and TLS (Transport Layer Security) are encryption protocols that are used to secure the data transmitted over HTTPS.</p>
<p>When your browser establishes a connection with Google's server using HTTPS, your browser and Google's server first agree on the version of SSL/TLS to use and then create a secure, encrypted channel for transmitting the data.</p>
<p>An easier way to understand HTTPS/SSL is to think of HTTPS to be a locked box that is used to send messages over the internet. When you want to send a message using HTTPS, you put the message in the locked box and send it to the person you want to receive the message. Only the person you are sending the message has the key to unlock the box and read the message.</p>
<p>SSL/TLS are special codes that are used to lock and unlock the box. When you want to send a message using HTTPS, you and the person you are sending the message to agree on the code to use to lock and unlock the box.</p>
<p>When you type <strong>google.com</strong> into your browser, the browser is like the person sending the message. The server that hosts <strong>google.com</strong> is like the person receiving the message. The browser sends a request for the webpage using HTTPS, which is like putting the request in a locked box and sending it to the server. The server then sends the webpage back to the browser using HTTPS, which is like putting the webpage in a locked box and sending it back to the browser.</p>
<h2 id="heading-load-balancer">Load balancer</h2>
<p>A load balancer is a device that distributes incoming network traffic across a group of servers or resources.</p>
<p>Its primary function is to ensure that the traffic is distributed evenly across the servers in order to avoid overloading any single server and to increase the overall capacity and reliability of the system.</p>
<p>A company like Google, which receives billions of website visitors a day, will need a lot of servers to serve all these users. Therefore, there will be a need for them to set up a load balancer to ensure that some of the servers are not overburdened while others are being underutilized.</p>
<p>In the case of a browser trying to access <strong>google.com,</strong> the load balancer would receive the incoming request from the browser and then forward it to one of the servers in the Google server network. The particular server chosen will depend on the type of load-balancing algorithm implemented.</p>
<h2 id="heading-web-server">Web server</h2>
<p>A web server is a computer program that is responsible for handling requests for web pages from clients (such as a browser trying to access <strong>google.com</strong>) When a client sends a request for a web page to a web server, the server processes the request, and returns the appropriate response to the client.</p>
<p>This means that when trying to access <strong>google.com,</strong> Google's server will receive a request from the load balancer.</p>
<p>The web server would then process the request and generate a response, which would typically include HTML, CSS, and JavaScript files that make up the web page.</p>
<p>The web server would then send this response back to the load balancer, which would forward it on to the browser. The browser would then use the HTML, CSS, and JavaScript files to render the web page for the user.</p>
<h2 id="heading-application-server-and-database">Application server and database</h2>
<p>Unlike the web server, the application server handles dynamic content. When using <strong>google.com,</strong> the application server will be responsible for generating the search results (which change based on the query you put into the search engine)</p>
<p>When you submit a search query to Google, the request is first sent to the load balancer, which forwards it to one of the web servers in the Google server network. This web server then sends the request to the application server, which processes the request and generates the search results.</p>
<p>Depending on the complexity of the search query, the application server may need to make a request to a <strong>database</strong> in order to retrieve the necessary data.</p>
<p>For example, if you are searching for a product on an e-commerce website, the application server may need to retrieve information about the product from a database.</p>
<p>Once the application server has obtained the necessary data, it sends it back to the web server, which includes it in the response that is sent back to the browser. The browser then uses the information to display the search results to you.</p>
<h2 id="heading-rendering-the-page">Rendering the page</h2>
<p>When a browser receives a response from a web server, it processes the HTML, CSS, and JavaScript files that are included in the response in order to render the web page.</p>
<p>The rendering process involves interpreting the HTML and CSS code, rendering any images or other media that are included on the page, and executing any JavaScript code that is present on the page.</p>
<p>In your case, your browser would receive the response from the web server, which includes the HTML, CSS, and JavaScript files that make up the Google web page.</p>
<p>The browser would then use these files to render the page and display it to you. This process typically involves the following:</p>
<ul>
<li><p>Displaying the text and images on the page in the appropriate positions</p>
</li>
<li><p>Formatting the text and layout according to the CSS styles</p>
</li>
<li><p>Executing any JavaScript code that is present on the page</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This blog post is my submission to a task focused on technical writing as part of the ALX Africa Software Engineering Program. I hope you enjoyed reading it and you now appreciate the engineering marvel that happens when you type <strong>google.com</strong> into your browser.</p>
]]></content:encoded></item></channel></rss>