<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Thomas Wolf on Medium]]></title>
        <description><![CDATA[Stories by Thomas Wolf on Medium]]></description>
        <link>https://medium.com/@Thomwolf?source=rss-167597463903------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*kQiyB3RRV3BUAq-EO1ur8w.jpeg</url>
            <title>Stories by Thomas Wolf on Medium</title>
            <link>https://medium.com/@Thomwolf?source=rss-167597463903------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 04 Apr 2026 05:22:21 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@Thomwolf/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[ From TensorFlow to PyTorch]]></title>
            <link>https://medium.com/huggingface/from-tensorflow-to-pytorch-265f40ef2a28?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/265f40ef2a28</guid>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Fri, 09 Aug 2019 13:05:31 GMT</pubDate>
            <atom:updated>2020-09-02T14:19:38.047Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KfD4ZOtBktmZcbBNNa6l4g.png" /><figcaption>By <a href="https://unsplash.com/@omairk">Omair Khan</a></figcaption></figure><p>Friends and users of our open-source tools are often surprised how fast 🚀 we reimplement the latest SOTA pre-trained TensorFlow models to make them accessible for everyone in our libraries like <a href="https://github.com/huggingface/pytorch-transformers">PyTorch-Transformers</a> 👾 or <a href="https://github.com/huggingface/pytorch-pretrained-BigGAN">PyTorch-pretrained-BigGAN</a> 🦋</p><p>In this post, you’ll<strong> learn the main recipe to convert a pretrained TensorFlow model in a pretrained PyTorch model</strong>, in just a few hours.</p><p>We’ll take the example of a simple architecture like <a href="https://github.com/openai/gpt-2">OpenAI GPT-2</a> 🦄</p><blockquote>Doing such a conversion assumes a good familiarity with both TensorFlow and PyTorch but it’s also one of the best ways to get to know better both frameworks!</blockquote><h3>Looking at the scope structure 🔎</h3><p>The first step is to retrieve the TensorFlow code and a pretrained checkpoint. Let’s get them from OpenAI GPT-2 <a href="https://github.com/openai/gpt-2">official repository</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a115aa6c9dcff38ed5e37376922a0bed/href">https://medium.com/media/a115aa6c9dcff38ed5e37376922a0bed/href</a></iframe><p>TensorFlow checkpoints are usually composed of three files named XXX.ckpt.data-YYY , XXX.ckpt.index and XXX.ckpt.meta :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*d0qkYPfPm4mJ7MvI1QvIsw.png" /></figure><blockquote>A trained NLP model should also be provided with a vocabulary to associate the tokens to the embeddings indices (here encoder.json and vocab.bpe). We won’t talk in too many details about vocabulary and tokenizer here since you can usually directly reuse their original python code with minor modifications.</blockquote><p>First, we can have a look at the hyper-parameters file: hparams.json. It contains a few hyper-parameters like the number of layers/heads and so on:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_us0guZALAeGzxUVrn4GLw.png" /><figcaption>We can reuse this JSON file in a configuration class for our model.</figcaption></figure><p>Now, let’s have a look at the structure of the model. Starting from now, you’ll need to have <a href="https://www.tensorflow.org/install/">TensorFlow installed</a> on your computer (can be the CPU version). Once TensorFlow is set up, open a python interpreter to load the checkpoint to inspect the saved variables:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ad038dfd38f57856a4408bac4c2346ce/href">https://medium.com/media/ad038dfd38f57856a4408bac4c2346ce/href</a></iframe><p>The result is a (long) list of all the variables stored in the checkpoint with their name and shapes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eq8EAGKXnz3tFfV0zeKyKA.png" /></figure><p>Variables are stored as Numpy arrays that you can load with tf.train.load_variable(name).</p><p>Now, what we are particularly interested in here are the path-like names of the variables like model/h0/ln_1/b which reflects the organization of TensorFlow variables in <a href="https://www.tensorflow.org/guide/variables#sharing_variables">scopes</a>.</p><p><strong>Here is our first secret:</strong></p><blockquote>To build our PyTorch model as fast as possible, we will <strong>reuse exactly the same organization</strong>: for each sub-scope in the TensorFlow model, we’ll create a sub-class under the same name in PyTorch.</blockquote><p>This will let us <strong>load weights</strong> easily by jointly iterating on scopes &amp; classes.</p><p>As you can see, GPT-2 has three modules at the root of the model (at the end of the list): model/wte, model/wpe and model/ln_f, and the rest of the model is composed of a series of identical modules hXX, each comprising a self-attention sub-module attn , a feed-forward module mlp and two layer-normalization modules ln_1 and ln_2 .</p><p>Now that we know how the model is organized, let’s build our PyTorch model with a hierarchy that reproduces this organization of scopes.</p><h3>Building the PyTorch model skeleton 👩‍🎨</h3><p>It’s time to have a look at the TensorFlow code it-self. We’ll start with the <a href="https://github.com/openai/gpt-2/blob/master/src/model.py#L147-L174">code for the main model</a> and reproduce the general organization in our PyTorch main model class:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6cdae8fad8b4cb5fb921355a05c65231/href">https://medium.com/media/6cdae8fad8b4cb5fb921355a05c65231/href</a></iframe><p>As you can see, we’ve given our main sub-modules names (wte, wpe, h, ln_f) that are identical to the first-level scopes of the variables we saw in the TensorFlow checkpoint.</p><p>We can also write the code for our forward pass by converting the <a href="https://github.com/openai/gpt-2/blob/master/src/model.py#L147-L174">code for the main model</a> from TensorFlow operations to PyTorch operations:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/89844e3a9294b6a2289ae9b683f71056/href">https://medium.com/media/89844e3a9294b6a2289ae9b683f71056/href</a></iframe><p>Now we dive deeper in the hierarchy, continuing to build our PyTorch model by adapting the rest of the TensorFlow code. Here is another example comparing the TensorFlow code for a “Block” module:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/763e6e6e7901d9c0cd69a2e031dd1f5c/href">https://medium.com/media/763e6e6e7901d9c0cd69a2e031dd1f5c/href</a></iframe><p>To the PyTorch equivalent nn.Module class:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/951393d1e27c986e99bd392b00ef4d81/href">https://medium.com/media/951393d1e27c986e99bd392b00ef4d81/href</a></iframe><p>Here again, the name of the class attributes containing the sub-modules (ln_1, ln_2, attn, mlp) are identical to the associated TensorFlow scope names that we saw in the checkpoint list above. Doing that ensures that the PT hierarchical attributes structure will be identical to the TF scope structure.</p><h3>Beware of the details — section I 🕵️</h3><h4>The computation flow</h4><p>When you convert TensorFlow code to PyTorch code, you have to be attentive to <strong>reproduce the exact computation workflow</strong> of the TensorFlow model in PyTorch. For instance, you should take care of reimplementing all the operations, even the ones not associated to a Variable (i.e. not visible in the checkpoint), add the dropout modules at same places than the original ones and carefully check how to convert each TensorFlow method in an equivalent PyTorch operation.</p><blockquote>It’s a good opportunity to <strong>dive in the internals of both frameworks</strong> to see how each operation is made under the hood. One example: TensorFlow &amp; PyTorch layer normalizations are slightly different from each other (go check them out!) so I usually reimplement layer normalization from scratch in PyTorch.</blockquote><h4>The initialization and defaults</h4><p>It’s also important to check default parameters of each module like epsilons and make sure you are using the same ones in PyTorch than the TensorFlow. Be especially careful about defaults values that may not be visible.</p><h3>Loading the weights 🏋️</h3><p>Once the code conversion step is finished and you can run a forward pass on dummy input without any errors with your newly defined PyTorch model, it’s time to load the TensorFlow weights in the newly created model 🐣</p><p>Having the same models&#39; organization make the loading very easy:</p><blockquote>We just jointly iterate on both the path-like names of TensorFlow variables &amp; our PyTorch model attributes.</blockquote><p>A commented <a href="https://github.com/huggingface/pytorch-transformers/blob/f2b300df6bd46ad16580f0313bc4b30ddde8515d/pytorch_transformers/modeling_gpt2.py#L45-L96">loading function</a> for GPT-2 looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ffdcab638e39bbe583c5ec1ba5193856/href">https://medium.com/media/ffdcab638e39bbe583c5ec1ba5193856/href</a></iframe><p>Let’s talk about a few things to keep in mind at this stage 👇</p><h3>Beware of the details — section II🕵️</h3><h4>Transposing tensors from TensorFlow to PyTorch</h4><p>Some TensorFlow operations operate on weights that are transposed with regards to their PyTorch counter-part (or vice-versa 😉). In this case, your weights loading method should take care of transposing the weights when loading them.</p><p>The main cases where this happens in practice are Keras modules like <a href="https://www.tensorflow.org/api_docs/python/tf/layers/dense">tf.layer.dense</a> whose kernel is the transposed of PyTorch’s <a href="https://pytorch.org/docs/stable/nn.html#torch.nn.Linear">nn.Linear</a> weights.</p><p>This transposition issue can be especially tricky to detect for <strong>square</strong> matrices which bring us to our last section 👇</p><h3>The final step —️ comparing the models 👭</h3><h4>Comparing hidden-states 🎼</h4><p>Now that your model runs and all the weights are initialized with their TensorFlow counterpart it is time for the most important operation:</p><blockquote>a careful comparison of both models!</blockquote><p>The way I usually do it is by starting from one script running the TensorFlow model provided by the authors of the original implementation and:</p><ul><li><strong>modify the TensorFlow model</strong> to output the hidden-states at regular locations along the depth of the model,</li><li><strong>modify our PyTorch model</strong> to output the hidden-states at the same regular locations along the depth of the model,</li><li>load the PyTorch model <strong>in parallel</strong> with the TensorFlow model and run them on the same inputs,</li><li><strong>compare their behaviors</strong> during a forward pass to detect where an error may have been made.</li></ul><p>You should take care of deactivating the DropOut modules and <strong>all nondeterministic</strong> modules to ensure maximal compatibility.</p><blockquote>If your script is a fine-tuning script and your model contains weights which are <strong>newly initialized</strong>, you should take care of fully initializing the PyTorch model from the newly initialized TensorFlow model for good comparison. <a href="https://github.com/thomwolf/xlnet/blob/master/run_classifier_gpu.py#L758-L767">Here</a> is an example of this process during the reimplementation of XLNet in <a href="https://github.com/huggingface/pytorch-transformers">pytorch-transformers</a> where the new TensorFlow model is saved and loaded in PyTorch.</blockquote><p>I usually compare the <strong>max absolute difference</strong> between the hidden-states after each layer of the models on a few real-life inputs:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3d9b2a6d1f493f1d68eeefa085d185da/href">https://medium.com/media/3d9b2a6d1f493f1d68eeefa085d185da/href</a></iframe><h4>Comparing on a down-stream task 🚣</h4><p>If your model is a pretrained model which can be <strong>fine-tuned</strong> on a down-stream task, you can further confirm the accuracy of the conversion by <strong>reproducing some results on a downstream task</strong>.</p><blockquote>This task can be quite long as you will need to reproduce the pre-processing, optimization and post-processing of the original author’s work.</blockquote><p>In our experience, a discrepancy at this stage, in pretty much every case, doesn’t come from a difference inside the models but from a discrepancy in the way the inputs are prepared, in the optimization parameters (one of the most often over-looked ones being the <strong>batch size</strong>) or in the post-processing and evaluation metrics.</p><h3>That’s all folks👭</h3><p>We’ve seen the main steps you can take to quickly and accurately reimplement a pretrained TensorFlow model in PyTorch.</p><p>This method has a few limits:</p><ul><li>the model may end up having a <strong>deeper hierarchy</strong> than necessary. In this case, you can rewrite the model to reduce the number of classes and use a mapping between the TensorFlow variables and the PyTorch attributes 🗺</li><li>the model is sometimes implemented with <strong>operations that are fast</strong><em> </em><strong>in TensorFlow</strong> or TPU (e.g. multiplication with one-hot matrices) but may be suboptimal in PyTorch. Here again, some rewriting and conversion afterward can help speed up the resulting model in some cases 🏎</li><li>You need <strong>access to the TensorFlow code</strong> for the conversion. It’s possible to convert a TensorFlow model without access to the code, e.g. a model only available on TensorFlow Hub but it’s a far more difficult process. In <a href="https://github.com/huggingface/pytorch-pretrained-BigGAN">PyTorch-pretrained-BigGAN</a> we did that by inspecting the raw computation graph and guessing the high-level operations involved 🙃</li></ul><p>👾 For detailed code examples of this process, you can have a look at the various models implemented in <a href="https://github.com/huggingface/pytorch-transformers">PyTorch-Transformers</a>.</p><p>… and if you feel like adding one of your own, we will probably be more than happy to <strong>welcome a Pull Request</strong> on the repository! Just ping us before to be sure we are not already working on it 😉</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=265f40ef2a28" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/from-tensorflow-to-pytorch-265f40ef2a28">🌓 From TensorFlow to PyTorch</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ How to build a State-of-the-Art Conversational AI with Transfer-Learning]]></title>
            <link>https://medium.com/huggingface/how-to-build-a-state-of-the-art-conversational-ai-with-transfer-learning-2d818ac26313?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/2d818ac26313</guid>
            <category><![CDATA[chatbots]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Thu, 09 May 2019 12:18:36 GMT</pubDate>
            <atom:updated>2020-09-02T14:16:33.566Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pVvFPi5RKjhua6vjv8VkYQ.jpeg" /><figcaption>By <a href="https://unsplash.com/@mahiruysal">Mahir Uysal</a></figcaption></figure><h3>🦄 How to build a State-of-the-Art Conversational AI with Transfer Learning</h3><p>A few years ago, creating a chatbot -as limited as they were back then- could take months 🗓, from designing the rules to actually writing thousands of answers to cover some of the conversation topics.</p><p>With the recent progress in deep-learning for NLP, we can now get rid of this petty work and build much more powerful conversational AI 🌟 in just a matter of hours 🍃 as you will see in this tutorial.</p><blockquote>We’ve set up a demo running the pretrained model we’ll build together in this tutorial at <a href="https://convai.huggingface.co/">convai.huggingface.co</a>. Be sure to check it out! 🎮</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Fn0bcNyyEyqpGq-nCPyoYw.gif" /><figcaption>Online demo of the pretrained model we’ll build in this tutorial at <a href="https://convai.huggingface.co/">convai.huggingface.co</a>. The “suggestions” (bottom) are also powered by the model putting itself in the shoes of the user.</figcaption></figure><p>Here is what we will learn and play with today:</p><ul><li>How you can use <strong>Transfer Learning</strong> to build a <strong>State-of-the-Art dialog agent</strong> based on <strong>OpenAI GPT</strong> and <strong>GPT-2 </strong>Transformer language models,</li><li>How you can <strong>reproduce</strong> the model we used in the NeurIPS 2018 dialog competition <a href="http://convai.io">ConvAI2</a> which <strong>won the automatic metrics track</strong>,</li><li>How we distilled 3k+ lines of competition code in less than <strong>250 lines of commented training code</strong> (with distributed &amp; FP16 options!), and</li><li>How you can train this model for <strong>less than $20</strong> on a cloud instance, or just use our open-sourced <strong>pre-trained model.</strong></li></ul><blockquote>Together with this post, we released a clean and commented code base with a pretrained model! Check the <a href="https://github.com/huggingface/transfer-learning-conv-ai">Github repo here</a> ✈️</blockquote><p>The story of this post began a few months ago in Montreal 🇨🇦 where <a href="http://huggingface.co">Hugging Face</a> finished 1st 🏆 in the automatic track of the Conversational Intelligence Challenge 2 <a href="http://convai.io">(ConvAI2</a>), a dialog competition at <a href="https://nips.cc/Conferences/2018">NeurIPS 2018</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U6_HGnynoAxO3Y0dTm86MA.png" /></figure><p>Our secret sauce was a <strong>large-scale pre-trained language model, </strong><a href="https://openai.com/blog/language-unsupervised/"><strong>OpenAI GPT</strong></a><strong>,</strong> combined with a <a href="http://arxiv.org/abs/1901.08149"><strong>Transfer Learning fine-tuning technique</strong></a>.</p><p>With the fast pace of the competition, we ended up with over 3k lines of code exploring many training and architectural variants.</p><p>Clearly, publishing such raw code would not have been fair.</p><p>In the meantime, we had started to build and open-source a repository of transfer learning models called <a href="https://github.com/huggingface/pytorch-pretrained-BERT">pytorch-pretrained-BERT</a> which ended up being downloaded more than 150 000 times and offered implementations of large-scale language models like OpenAI GPT and it’s successor GPT-2 🦄</p><p>A few weeks ago, I decided to re-factor our competition code in a clean and commented code-base built on top of <a href="https://github.com/huggingface/pytorch-pretrained-BERT">pytorch-pretrained-BERT</a> and to write a detailed blog post explaining our approach and code.</p><p>So here we are, let’s dive in 🚀</p><h3>An AI with a personality 🤠</h3><p>We’ll build a <strong>conversational AI </strong>with a <strong>persona</strong>.</p><p>Our dialog agent will have a <em>knowledge base</em> to store a few sentences describing who it is (<em>persona</em>) and a <em>dialog</em> <em>history.</em> When a <em>new utterance</em> will be received from a user, the agent will combine the content of this knowledge base with the newly received utterance to generate <em>a reply</em>.</p><p>Here is the general scheme:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sIRX4M3--Qrvo-RLqH-7GQ.png" /></figure><p>When we train a deep-learning based dialog agents, in an end-to-end fashion, we are facing a major issue:</p><blockquote>Dialog datasets are small and it’s hard to learn enough about language and common-sense from them to be able to generate fluent and relevant responses.</blockquote><p>Some approaches try to solve this by filtering the output of the model to improve the quality using smart beam search. Here we’ll take another path that gathered tremendous interest over the last months: <strong>Transfer Learning.</strong></p><p>The idea behind this approach is quite simple:</p><ul><li>start by <strong>pretraining</strong> a language model<strong> </strong>on a very large <strong>corpus</strong> of text to be able to generate long stretches of contiguous coherent text,</li><li><strong>fine-tune </strong>this language model to adapt it to our end-task: <strong>dialog</strong>.</li></ul><p>Pretraining a language model is an expensive operation so it’s usually better to start from a model that has already been pretrained and open-sourced.</p><blockquote>What would be a good pretrained model for our purpose?</blockquote><p><em>The</em> <em>bigger the better</em>, but we also need a model that <em>can generate text</em>. The most commonly used pretrained NLP model, <a href="http://arxiv.org/abs/1810.04805">BERT</a>, is pretrained on full sentences only and is not able to complete unfinished sentences. Two other models, open-sourced by OpenAI, are more interesting for our use-case: <strong>GPT</strong> &amp; <strong>GPT-2</strong>.</p><p>Let’s have a quick look at them 🔎</p><h3>🦄 OpenAI GPT and GPT-2 models</h3><p>In 2018 and 2019, Alec Radford, Jeffrey Wu and their co-workers at OpenAI open-sourced two language models trained on a very large amount of data: <strong>GPT</strong> and <strong>GPT-2</strong> (where <em>GPT</em> stands for <em>Generative Pretrained Transformer).</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YmND0Qj8O6b35J1yU_CPKQ.png" /><figcaption>A decoder/causal Transformer attends to the left context to generate next words</figcaption></figure><p>GPT and GPT-2 are two very similar <a href="http://arxiv.org/abs/1706.03762">Transformer</a>-based language models. These models are called <em>decoder</em> or <em>causal</em> models which means that they use the left context to predict the next word (see left figure).</p><p>Many papers and blog posts describe Transformers models and how they use attention mechanisms to process sequential inputs so I won’t spend time presenting them in details. A few pointers if you are not familiar with these models: <a href="https://people.cs.umass.edu/~strubell/doc/lisa-final.key">Emma Strubell’s EMNLP slides</a> are my personal favorite and Jay Alammar’s “<a href="https://jalammar.github.io/illustrated-transformer/">Illustrated Transformer</a>” is a very detailed introduction.</p><p>For our purpose, a language model will just be a model that takes as input a sequence of tokens and <strong>generates a probability distribution over the vocabulary for the next token following the input sequence</strong>. Language models are usually trained in a parallel fashion, as illustrated on the above figure, by predicting the token following each token in a long input sequence.</p><p>Pretraining these models on a large corpus is a costly operation, so we’ll start from a model and tokenizer pretrained by OpenAI. The tokenizer will take care of splitting an input string in tokens (words/sub-words) and convert these tokens in the correct numerical indices of the model vocabulary.</p><p>In <a href="https://github.com/huggingface/pytorch-pretrained-BERT">pytorch-pretrained-BERT</a> OpenAI GPT’s model and its tokenizer can be easily created and loaded from the pretrained checkpoint like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bfdfa85e951b2d80d66817db24fda451/href">https://medium.com/media/bfdfa85e951b2d80d66817db24fda451/href</a></iframe><p>You probably noticed we’ve loaded a model called <em>OpenAI GPT Double Heads Model</em> which sounds a bit more complex than the language model we’ve just talked about and you’re right!</p><p>This is because we need to adapt our model to <em>dialog</em>. Let’s see how this goes!</p><h3>👻 Adapting a language model to a dialog task</h3><p>Our language model is trained with a <strong>single</strong> input: a sequence of words<em>.</em></p><p>But as we saw earlier, in a dialog setting, our model will have to use <strong>several types</strong> of <em>contexts</em> to generate an output sequence:</p><ul><li>one or several <em>persona sentences</em>,</li><li>the <em>history of the dialog</em> with at least the last utterance from the user,</li><li>the <em>tokens of the output sequence</em> that have already been generated since we generate the output sequence word by word.</li></ul><blockquote>How can we build an input for our model from these various contexts?</blockquote><p>A simple answer is just to <strong>concatenate</strong> the context segments in a single sequence, putting the reply at the end. We can then <strong>generate a completion</strong> of the reply token by token by continuing the sequence:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RWEUB0ViLTdMjIQd61_WIg.png" /><figcaption>Input sequence: a concatenation of persona (blue), history (pink) and reply (green) with delimiters (light pink). Here we generate the word “you” to complete the reply.</figcaption></figure><p>There are two issues with this simple setup:</p><ul><li><em>Our transformer is color-blind!</em> The delimiter tokens only give it a weak idea of which segment each word belongs to. For example, the word “NYC” is indicated in blue (persona) in our illustration but our model will have a hard time extracting this information from the delimiters alone: <strong>we should add more information about the segments.</strong></li><li><em>Our transformer is position-blind!</em> Attention is a symmetrical dot-product so <strong>we should add position information for each token.</strong></li></ul><p>An easy way to add this information is to build three parallel input sequences for <em>word</em>, <em>position, </em>and <em>segments, </em>and fuse them in a single sequence, summing three types of embeddings: <em>word</em>, <em>position, </em>and <em>segments </em>embeddings:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r7vi6tho6sfpVx-ZQLPDUA.png" /><figcaption>Summing three types of inputs embeddings indicating words (grey), position (gradient) and segments (blue/pink/green)</figcaption></figure><p>How do we implement this?</p><p>First, we’ll add <em>special tokens </em>to our vocabulary for delimiters and segment indicators. These tokens were not part of our model’s pretraining so we will need to <strong>create </strong>and<strong> train new embeddings</strong> for them.</p><p>Adding special tokens and new embeddings to the vocabulary/model is quite simple with <a href="https://github.com/huggingface/pytorch-pretrained-BERT">pytorch-pretrained-BERT</a> classes. Let’s add five special tokens to our tokenizer’s vocabulary and model’s embeddings:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/54fb710ccd3651e9605d41899a4be0bb/href">https://medium.com/media/54fb710ccd3651e9605d41899a4be0bb/href</a></iframe><blockquote>These special-tokens methods respectively add our five special tokens to the vocabulary of the tokenizer and create five additional embeddings in the model.</blockquote><p>Now we have all we need to build our input sequence from the <em>persona</em>, <em>history, </em>and <em>beginning of reply </em>contexts. Here is a simple example:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eea69917fbab7fcc7323a3c717f4fa09/href">https://medium.com/media/eea69917fbab7fcc7323a3c717f4fa09/href</a></iframe><h3>👑 Multi-tasks losses</h3><p>We have now initialized our pretrained model and built our training inputs, all that remains is to choose a <strong>loss</strong> to <strong>optimize</strong> during the fine-tuning.</p><blockquote>We will use a multi-task loss combining language modeling with a next-sentence prediction objective.</blockquote><blockquote>The next-sentence prediction objective is a part of <a href="http://arxiv.org/abs/1810.04805">BERT pretraining</a>. It consists in randomly sampling distractors from the dataset and training the model to distinguish whether an input sequence ends with a gold reply or a distractor. It trains the model to look at the global segments meaning besides the local context.</blockquote><p>Now you see why we loaded a “<em>Double-Head”</em> model. One head will compute language modeling predictions while the other head will predict next-sentence classification labels. Let’s have a look at how losses are computed:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*945IpgUS9MGLB6gchoQXlw.png" /><figcaption>Multi-task training objective — the model is provided with two heads for language modeling prediction (orange) and next-sentence classification (blue)</figcaption></figure><p>The <strong>total loss</strong> will be the weighted sum of the<strong> language modeling loss </strong>and<strong> </strong>the <strong>next-sentence prediction loss</strong> which are computed as follow:</p><ul><li><strong>Language modeling</strong>: we project the hidden-state on the word embedding matrix to get logits and apply a <em>cross-entropy loss on the portion of the target</em> <em>corresponding to the gold reply</em> (green labels on the above figure).</li><li><strong>Next-sentence prediction</strong>: we pass the hidden-state of the <em>last</em> token (the<em> end-of-sequence</em> token) through a linear layer to get a score and apply a <em>cross-entropy loss to</em> <em>classify correctly a gold answer among distractor</em>s.</li></ul><p>Let’s see how we can code this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2357e65682d831f57c490082b4404095/href">https://medium.com/media/2357e65682d831f57c490082b4404095/href</a></iframe><p>We now have all the inputs required by our model and we can run a forward pass of the model to get the two losses and the total loss (as a weighted sum):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8bcb388af3f8f92e5e6895d9c2f9c63e/href">https://medium.com/media/8bcb388af3f8f92e5e6895d9c2f9c63e/href</a></iframe><p>We are ready to start the training 🎉</p><h3>🦊 Training on a dialog dataset</h3><p>The ConvAI2 competition used an interesting dataset released by Facebook last year: <a href="http://arxiv.org/abs/1801.07243">PERSONA-CHAT</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/661/1*VYFCi7ebNhPZy4iCIc7mOw.png" /></figure><p>It’s a rather large dataset of dialog (10k dialogs) which was created by crowdsourcing <em>personality sentences</em> and asking paired crowd workers to <em>chit-chat</em> while playing the part of a given character (an example is given on the left figure).</p><p>This dataset is available in raw tokenized text format in the nice <a href="https://parl.ai/">Facebook’s ParlAI</a> library. To bootstrap you, we also uploaded a JSON formatted version that you can download and tokenize using GPT’s tokenizer like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ca0661d64d6c726c6c00740968822bc7/href">https://medium.com/media/ca0661d64d6c726c6c00740968822bc7/href</a></iframe><p>The JSON version of PERSONA-CHAT gives quick access to all the relevant inputs for training our model as a nested dictionary of lists:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bGsPdkezu_sxkbsgImD4tg.png" /><figcaption>Organization of the <a href="https://s3.amazonaws.com/datasets.huggingface.co/personachat/personachat_self_original.json">JSON version</a> of PERSONA-CHAT</figcaption></figure><p>Using the awesome <a href="https://github.com/pytorch/ignite">PyTorch ignite</a> framework and the <a href="https://nvidia.github.io/apex/amp.html">new API</a> for Automatic Mixed Precision (FP16/32) provided by <a href="https://github.com/NVIDIA/apex">NVIDIA’s apex</a>, we were able to distill our +3k lines of competition code in less than <strong>250 lines of training code </strong>with distributed and FP16 options!</p><p>We’ve covered the essential parts of the code in the above gists so I’ll just let you read the commented code to see how it all fits together.</p><blockquote>The training (train.py) code is <a href="https://github.com/huggingface/transfer-learning-conv-ai">here <em>➱ 🎮</em></a></blockquote><p>Training this model on an AWS instance with 8 V100 GPU takes less than <strong>an hour</strong> (currently less than $25 on the biggest p3.16xlarge AWS instance) and gives results close to the SOTA obtained during the ConvAI2 competition with <strong>Hits@1 over 79, perplexity of 20.5 and F1 of 16.5.</strong></p><p>A few differences explain the slightly lower scores vs our competition model, they are detailed in the readme of the code repo <a href="https://github.com/huggingface/transfer-learning-conv-ai">here</a> and mostly consists in tweaking the position embeddings and using a different decoder.</p><h3>👻 Talking with the Model — the Decoder</h3><p>The amazing thing about dialog models is that you can talk with them 🤗</p><p>To interact with our model, we need to add one thing: a <strong>decoder </strong>that<strong> </strong>will build full sequences from the next token predictions of our model.</p><p>Now there have been very interesting developments in decoders over the last few months and I wanted to present them quickly here to get you up-to-date.</p><p>The two most common decoders for language generation used to be <strong>greedy-decoding</strong> and <strong>beam-search</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/386/0*idyUDtTYmOJ_hGLb.gif" /><figcaption>Generating a sentence word by word (<a href="https://edux.pjwstk.edu.pl/mat/2144/lec/wyklad6/w6.htm">source</a>)</figcaption></figure><p><strong><em>Greedy-decoding</em></strong> is the simplest way to generate a sentence: at each time step, we select the most likely next token according to the model until we reach end-of-sequence tokens. One risk with greedy decoding is that a <em>highly probable</em> token may be hiding after a <em>low-probability</em> token and be missed.</p><p><strong><em>Beam-search</em></strong> try to mitigate this issue by maintaining a beam of several possible sequences that we construct word-by-word. At the end of the process, we select the best sentence among the beams. Over the last few years, beam-search has been the <em>standard decoding algorithm</em> for almost all language generation tasks including dialog (see the recent <a href="#24e8">[1]</a>).</p><p>However several developments happened in 2018/early-2019. First, there was growing evidence that beam-search was strongly <em>sensitive to the length</em> of the outputs and best results could be obtained when the output length was <em>predicted</em> before decoding ([<a href="#7370">2</a>, <a href="#119d">3</a>] at EMNLP 2018). While this makes sense for <strong>low-entropy tasks </strong>like translation where the output sequence length can be roughly predicted from the input, it seems arbitrary for <strong>high-entropy tasks</strong> like dialog and story generation where outputs of widely different lengths are usually equally valid.</p><p>In parallel, at least two influential papers ([<a href="#ad18">4</a>, <a href="#fbf9">5</a>]) on high-entropy generation tasks were published in which greedy/beam-search decoding was replaced by <strong><em>sampling</em></strong> from the next token distribution at each time step. These papers used a variant of sampling called <strong><em>top-k sampling</em></strong> in which the decoder <em>sample only from the top-k most-probable tokens</em> (k is a hyper-parameter).</p><p>The last stone in this recent trend of work is the study recently published by Ari Holtzman et al. <a href="#7b60">[6]</a> which showed that the distributions of words in texts generated using <em>beam-search</em> and <em>greedy decoding</em> is very different from the distributions of words in human-generated texts. Clearly, <em>beam-search</em> and <em>greedy decoding</em> fail to reproduce some distributional aspects of human texts as it has also been noted in [<a href="#70eb">7</a>, <a href="#dcd5">8</a>] in the context of dialog systems:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yEX1poMDsiEBisrJcdpifA.png" /><figcaption>Left: Probability assigned to tokens generated by humans and beam search using GPT-2 (Note the strong variance in human text not reproduced by beam-search). Right: N-gram distributions in human and machine-generated texts (Note the complete separation between greedy/beam-search and sampling decoding methods).</figcaption></figure><p>Currently, the two most promising candidates to succeed beam-search/greedy decoding are <strong><em>top-k</em></strong> and <strong><em>nucleus (or top-p) sampling</em></strong>. The general principle of these two methods is to sample from the next-token distribution after having filtered this distribution to keep only the top k tokens (<em>top-k</em>) or the top tokens with a cumulative probability just above a threshold (<em>nucleus/top-p</em>).</p><p>Here is how we can decode using <strong><em>top-k</em></strong> and/or <strong><em>nucleus/top-p</em></strong> sampling:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e4ced14baefad690026d643514aa2a6a/href">https://medium.com/media/e4ced14baefad690026d643514aa2a6a/href</a></iframe><p>We are now ready to talk with our model 🚀</p><p>The interactive script is <a href="https://github.com/huggingface/transfer-learning-conv-ai">here</a> (interact.py) and if you don’t want to run the script you can also just play with our live demo which is here 🎮</p><p>Here is an example of dialog:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2dq-YSti7Ot1bwrOj80gGQ.png" /><figcaption>Example using the interactive scripts with default settings — Bot personality: <em>I read twenty books a year. I’m a stunt double as my second job. I only eat kosher. I was raised in a single parent household.</em></figcaption></figure><h3>👻 Conclusion</h3><p>We’ve come to the end of this post describing how you can build a simple state-of-the-art conversational AI using transfer learning and a large-scale language model like OpenAI GPT.</p><p>As we learned at <a href="http://huggingface.co/">Hugging Face</a>, getting your conversational AI up and running quickly is the best recipe for success so we hope it will help some of you do just that!</p><p>Be sure to check out the associated demo and code:</p><ul><li>the live demo is <a href="http://convai.huggingface.co">here</a> and</li><li>the open-sourced code and pretrained models are <a href="https://github.com/huggingface/transfer-learning-conv-ai">here</a>.</li></ul><p>As always, if you liked this post, give us a few 👏 to let us know and share the news around you!</p><h4>References:</h4><p>[1] <a href="#c7f2">^</a> <em>Importance of a Search Strategy in Neural Dialogue Modelling</em> by Ilya Kulikov, Alexander H. Miller, Kyunghyun Cho, Jason Weston (<a href="http://arxiv.org/abs/1811.00907">http://arxiv.org/abs/1811.00907</a>)</p><p>[2] <a href="#5aac">^</a> <em>Correcting Length Bias in Neural Machine Translation</em> by Kenton Murray, David Chiang (<a href="http://arxiv.org/abs/1808.10006">http://arxiv.org/abs/1808.10006</a>)</p><p>[3] <a href="#5aac">^</a> <em>Breaking the Beam Search Curse: A Study of (Re-)Scoring Methods and Stopping Criteria for Neural Machine Translation</em> by Yilin Yang, Liang Huang, Mingbo Ma (<a href="https://arxiv.org/abs/1808.09582">https://arxiv.org/abs/1808.09582</a>)</p><p>[4] <a href="#97f9">^</a> <em>Hierarchical Neural Story Generation</em> by Angela Fan, Mike Lewis, Yann Dauphin (<a href="https://arxiv.org/abs/1805.04833">https://arxiv.org/abs/1805.04833</a>)</p><p>[5] <a href="#97f9">^</a> <em>Language Models are Unsupervised Multitask Learners</em> by Alec Radford, Jeff Wu, Rewon Child, David Luan, Dario Amodei, and Ilya Sutskever (<a href="https://openai.com/blog/better-language-models/">https://openai.com/blog/better-language-models/</a>)</p><p>[6] <a href="#6ea4">^</a> <em>The Curious Case of Neural Text Degeneration</em> by Ari Holtzman, Jan Buys, Maxwell Forbes, Yejin Choi (<a href="https://arxiv.org/abs/1904.09751">https://arxiv.org/abs/1904.09751</a>)</p><p>[7] <a href="#6ea4">^</a> <em>Retrieve and Refine: Improved Sequence Generation Models For Dialogue</em> by Jason Weston, Emily Dinan, Alexander H. Miller (<a href="https://arxiv.org/abs/1808.04776">https://arxiv.org/abs/1808.04776</a>)</p><p>[8] <a href="#6ea4">^</a> <em>The Second Conversational Intelligence Challenge (ConvAI2)</em> by Emily Dinan et al. (<a href="https://arxiv.org/abs/1902.00098">https://arxiv.org/abs/1902.00098</a>)</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2d818ac26313" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/how-to-build-a-state-of-the-art-conversational-ai-with-transfer-learning-2d818ac26313">🦄 How to build a State-of-the-Art Conversational AI with Transfer-Learning</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU & Distributed setups]]></title>
            <link>https://medium.com/huggingface/training-larger-batches-practical-tips-on-1-gpu-multi-gpu-distributed-setups-ec88c3e51255?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/ec88c3e51255</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[nlp]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Mon, 15 Oct 2018 09:23:42 GMT</pubDate>
            <atom:updated>2020-09-02T14:15:17.823Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*f1Nz2zUOqpf5uUKk" /><figcaption>By <a href="https://unsplash.com/@davidmarcu">David Marcu</a></figcaption></figure><p>I’ve spent most of 2018 training neural networks that tackle the limits of my GPUs. Whether it was a 150 millions parameters language model like <a href="https://blog.openai.com/language-unsupervised/">OpenAI’s huge Generative Pre-trained Transformer</a> (or <a href="https://arxiv.org/abs/1810.04805">the recent and similar BERT model</a>) or a meta-learning neural net fed with 30 million element inputs like the one of <a href="https://medium.com/huggingface/from-zero-to-research-an-introduction-to-meta-learning-8e16e677f78a">our ICLR ‘18 paper</a>, I could barely fit more than a few training samples on a GPU.</p><p>But most of the time stochastic gradient descent algorithms require larger batches than just a handful of examples to get decent results.</p><blockquote>How can you train your model on large batches when your GPU can’t hold more than a few samples?</blockquote><p>There are several tools, tips and tricks you can use to do that and I thought it would be nice to gather all the things I use and learned in a post.</p><p>In this post I will mainly talk about <strong>the PyTorch framework</strong>. Some of these tools are not in PyTorch yet (as of 1.0) so I include some custom code as well.</p><p>In particular, we’ll talk about:</p><ul><li>How you can train a model on a single or multi GPU server with batches larger than the GPUs memory or when even a single training sample won’t fit (!),</li><li>How you can make the most efficient use of a multi-GPU machine, and</li><li>The simplest way to train a model using several machines in a distributed setup.</li></ul><p>Let’s start by the simplest trick: gradient accumulation.</p><h3>⌛️Large batches on one or several GPU(s)</h3><p>So, you’ve build a nice model that might be the new SOTA on this neat task but every time you try to stack more than a few samples in a batch you get a CUDA RuntimeError: <em>out of memory</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H_BbsrJdq-VMZ6rLFJCcYg.png" /><figcaption>Adam confirms your predicament! 😱Oh no!</figcaption></figure><p>But you’re pretty sure that doubling the batch size will improve the results.</p><blockquote>How can you do that?</blockquote><p>There is an easy solution to this problem: <strong>accumulating gradients</strong>. Here is a quick reminder on how stochastic gradient descent works from <a href="https://medium.com/huggingface/from-zero-to-research-an-introduction-to-meta-learning-8e16e677f78a">my earlier post on meta-learning</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ySxl2GQu0g07R7gWF4rizg.gif" /><figcaption>The 5-steps of a gradient descent optimization algorithm</figcaption></figure><p>The PyTorch code equivalent of these 5 steps can also be written in 5 lines:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b938012b325208cb9638949e19be71d5/href">https://medium.com/media/b938012b325208cb9638949e19be71d5/href</a></iframe><p>During the loss.backward() operation, gradients are computed for each parameter (in green on our animation) and stored in a tensor associated to each parameter: parameter.grad (the middle graph on our animation).</p><p><em>Accumulating gradients</em> just means that, before calling optimizer.step() to perform a step of gradient descent, we will sum the gradients of several backward operations in the parameter.grad tensors. This is straightforward to do in PyTorch as the gradient tensors are not reset unless we call model.zero_grad() or optimizer.zero_grad(). We’ll also need to divide by the number of accumulation steps if our loss is averaged over the training samples.</p><p>Here is a simple gist for training a model using gradient accumulation. In this example we can train with a batch size that is accumulation_steps-larger than the maximum size that fits on our GPU(s):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9625526c69f9c993da9d9a16a77f048c/href">https://medium.com/media/9625526c69f9c993da9d9a16a77f048c/href</a></iframe><p><a href="https://medium.com/u/4e69fb1d25b3">Grzegorz Chlebus</a> made a nice post describing how to do <strong>gradient accumulation in TensorFlow</strong>, <a href="https://gchlebus.github.io/2018/06/05/gradient-averaging.html">check it out here</a>.</p><h3>😱 Pushing that to the extreme</h3><p>Can you train a model for which <em>not even a single sample</em> can fit on a GPU?</p><p>Well if your architecture doesn’t have too-much skip connections, yes, it’s possible! The solution is to trade compute for memory using <strong>gradient-checkpointing.</strong></p><p>Basically, the idea is to back-propagate the gradients in small chunks along the model, trading the memory needed to store a full back propagation graph with the additional compute of a partial forward pass associated to each chunk. This is a rather slow method as we add additional compute to reduce the memory requirements but it can be interesting in some settings, e.g. to train RNN models over very long sequences (see for example my previous <a href="https://medium.com/huggingface/from-zero-to-research-an-introduction-to-meta-learning-8e16e677f78a">introduction to meta-learning</a>).</p><p>I won’t go into more details here and will just refer you to the relevant links:</p><ul><li>TensorFlow: <a href="https://github.com/openai/gradient-checkpointing">https://github.com/openai/gradient-checkpointing</a></li><li>PyTorch doc: <a href="https://pytorch.org/docs/stable/checkpoint.html">https://pytorch.org/docs/stable/checkpoint.html</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*udMSiPD0kZHum-sZ." /><figcaption>A “Memory-poor” strategy that needs O(1) memory (but requires O(n²) computation steps) — From <a href="https://medium.com/@yaroslavvb?source=post_header_lockup">Yaroslav Bulatov</a>’s nice post: <a href="https://medium.com/tensorflow/fitting-larger-networks-into-memory-583e3c758ff9">https://medium.com/tensorflow/fitting-larger-networks-into-memory-583e3c758ff9</a></figcaption></figure><h3>🕰 Making the best of a multi-GPU machine</h3><p>Now let’s talk more specifically about training model on multi-GPUs.</p><p>The go-to strategy to train a PyTorch model on a multi-GPU server is to use <a href="https://pytorch.org/docs/stable/nn.html#dataparallel-layers-multi-gpu-distributed">torch.nn.DataParallel</a>. It’s a container which parallelizes the application of a module by splitting the input across the specified devices, chunking along the batch dimension.</p><p><em>DataParallel</em> is very easy to use, we just add one line to encapsulate the model:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e91264ec8e4e49e45578c53cd79708d3/href">https://medium.com/media/e91264ec8e4e49e45578c53cd79708d3/href</a></iframe><p>However one issue can arise with DataParallel: <strong>unbalanced GPU usage</strong>.</p><blockquote>Under some settings GPU-1 will be used a lot more than the other GPUs.</blockquote><p>Where does this come from? I made an illustration to better explain what DataParallel does under the hood:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FpDHkWJhkLL7KxU01Lf9Lw.png" /><figcaption>Forward and Backward passes with torch.nn.DataParallel</figcaption></figure><p>During step 4 of the Forward pass (top-right), the results of <em>all the parallel computations</em> are gathered on GPU-1. This is fine for a lot of classification problems but it can become problematic when you train a language model on large batch for example.</p><p>Let’s quickly compute the size of the output for a language model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fSHTlvLwaPdOt08I38M9BQ.png" /><figcaption>Number of elements in the output of a language model</figcaption></figure><p>If we assume a 40k vocabulary, 250 tokens in our sequences, 32 samples per batch and 4 bytes to store each element in the memory, the output of our model takes<strong> </strong>about<strong> </strong>1,2 GB. We need to double that to store the associated gradient tensors, <strong>our model output thus requires 2,4 GB of memory!</strong></p><p>That’s a significant portion of a typical 10 GB GPU memory and means that GPU-1 will be over-used with regards to the other GPUs, limiting the effect of the parallelization.</p><p>We cannot easily reduce the number of elements in this output without tweaking the model and/or optimization scheme. But we can make sure the memory load is more evenly distributed among the GPUs.</p><h3>⚖️ Balanced load on a multi-GPU machine</h3><p>There are two main solution to the imbalanced GPU usage issue:</p><ul><li>computing the loss <strong>in the forward pass</strong> of your model,</li><li>computing the loss <strong>in a parallel</strong> fashion.</li></ul><p>The first option is the easiest but sometimes you can’t use it or it’s not practical for various reasons (e.g. your forward pass becomes too complicated and slow because of <a href="https://wiki.python.org/moin/GlobalInterpreterLock">Python’s GIL</a>) so let’s talk a bit about the second solution. Along the road we’ll learn interesting things about how PyTorch multi-GPU modules work.</p><p>In that case, the solution is to keep each partial output on its GPU instead of gathering all of them to GPU-1. We well need to distribute our loss criterion computation as well to be able to compute and back propagate our loss.</p><p>Thankfully for us, <a href="https://hangzhang.org/">Hang Zhang (张航)</a> has open-sourced a nice PyTorch package called <a href="https://github.com/zhanghang1989/PyTorch-Encoding">PyTorch-Encoding</a> which comprises these custom parallelization functions.</p><p>I’ve extracted and slightly adapted this module and you can <a href="https://gist.github.com/thomwolf/7e2407fbd5945f07821adae3d9fd1312">download here</a> a gist (parallel.py) to include and call from your code. It mainly comprises two modules: <em>DataParallelModel </em>and<em> DataParallelCriterion</em> which are made to be used as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/823ef640ddaf893fee7673b794c41e36/href">https://medium.com/media/823ef640ddaf893fee7673b794c41e36/href</a></iframe><p>The difference between <em>DataParallelModel </em>and<em> </em><a href="https://pytorch.org/docs/stable/nn.html#dataparallel-layers-multi-gpu-distributed"><em>torch.nn.DataParallel</em></a> is just that the output of the forward pass (predictions) is not gathered on GPU-1 and is thus <strong>a tuple</strong> of n_gpu tensors, each tensor being located on a respective GPU.</p><p>The <em>DataParallelCriterion</em> container encapsulate the loss function and <strong>takes as input</strong> the tuple of n_gpu tensors<strong> </strong>and the target labels tensor. It computes the loss function in parallel on each GPU, splitting the target label tensor the same way the model input was chunked by DataParallel.</p><p>I made an illustration of <em>DataParallelModel/DataParallelCriterion </em>internals<em>:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F6SXjBp6BCoFTZ26RKnz9A.png" /><figcaption><em>Using DataParallelModel and DataParallelCriterion</em></figcaption></figure><p>Here is how to handle two particular cases you may encounter:</p><ul><li><strong>Your model outputs several tensors:</strong> you likely want to disentangle them: output_1, output_2 = zip(*predictions)</li><li><strong>Sometimes you don’t want to use a parallel loss function</strong>: gather all the tensors on the cpu: gathered_predictions = parallel.gather(predictions)</li></ul><h3>⏰ Distributed training: training on several machines</h3><p>Now how can we harness the power of several servers to train on even larger batches?</p><p>The simplest option is to use PyTorch <a href="https://pytorch.org/docs/stable/nn.html#distributeddataparallel">DistributedDataParallel</a> which is meant to be <em>almost</em> a drop-in replacement for DataParallel discussed above.</p><p>But be careful: while the code looks similar, training your model in a <em>distributed setting</em> will change your workflow because you will actually have to start<strong> an independent python training script on each node</strong> (these scripts are all identical).<strong> </strong>As we will see, once started, these training scripts will be synchronized together by PyTorch distributed backend.</p><p>In practice, this<strong> </strong>means that each training script will have:</p><ul><li><strong>its own optimizer</strong> and performs a complete optimization step with each iteration, no parameter broadcast (step 2 in DataParallel) is needed,</li><li><strong>an independent Python interpreter:</strong> this will also avoid the <a href="https://wiki.python.org/moin/GlobalInterpreterLock">GIL-freeze</a> that can come from driving several parallel execution threads in a single Python interpreter.</li></ul><blockquote>Models that make heavy use of Python loops/call in their forward passes can be slowed down by the python interpreter’s GIL when several parallel forward calls are driven by a single interpreter. In these settings, <a href="https://pytorch.org/docs/stable/nn.html#distributeddataparallel">DistributedDataParallel</a> can advantageously replace DataParallel even on a single-machine setup.</blockquote><p>Now let’s just dive straight in the code and usage.</p><p>DistributedDataParallel is build on top of <a href="https://pytorch.org/docs/stable/distributed.html">torch.distributed</a> package which provide low-level primitives for synchronizing distributed operations and can make use of several backends (<em>tcp</em>, <em>gloo</em>, <em>mpi</em>, <em>nccl</em>) with different capabilities.</p><p>In this post I will select <strong>one simple way</strong> to use it out-of-the-box but you should <a href="https://pytorch.org/docs/stable/distributed.html">read the doc</a> and <a href="https://pytorch.org/tutorials/intermediate/dist_tuto.html">this nice tutorial</a> by <a href="http://seba1511.com/">Séb Arnold</a> to dive deeper in this module.</p><p>We will consider a simple but general setup with two 4-GPU servers (nodes):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/1*X23kPqRHuLM8hty-Y6bxyQ.png" /><figcaption>The main server (server 1) has an accessible IP and an open port for communication.</figcaption></figure><h4>🏃 Adapting our Python training script for distributed training</h4><p>First we need to adapt our script so that it can be run separately on each machine (node). We are actually going to go fully distributed and run a separate process for each GPU of each node, so 8 process in total.</p><p>Our training script is a bit longer as we need to initialize the distributed backend for synchronization, encapsulate the model and prepare the data to train each process on a separate subset of the data (each process is independent so we have to care of having each of them handle a different slice of the dataset ourselves). Here is the updated code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eb364572c28b87e9543a0f1db52e1d35/href">https://medium.com/media/eb364572c28b87e9543a0f1db52e1d35/href</a></iframe><h4>✨ Launching multiple instances of our Python training script</h4><p>We are almost done now. We just have to start an instance of our training script on each server.</p><blockquote>To run our script, we’ll use the <a href="https://pytorch.org/docs/stable/distributed.html#launch-utility">torch.distributed.<strong>launch</strong></a> utility of PyTorch. It will take care of setting the environment variables and call each script with the right local_rank argument.</blockquote><p>The first machine will be our master, it need to be accessible from all the other machine and thus have an accessible IP address (192.168.1.1 in our example) and an open port (1234 in our case). On this first machine, we run our training script using <a href="https://pytorch.org/docs/stable/distributed.html#launch-utility">torch.distributed.<strong>launch</strong></a>:</p><pre>python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=0 --master_addr=&quot;192.168.1.1&quot; --master_port=1234 OUR_TRAINING_SCRIPT.py (--arg1 --arg2 --arg3 and all other arguments of our training script)</pre><p>On the second machine we similarly start our script:</p><pre>python -m torch.distributed.launch --nproc_per_node=4 --nnodes=2 --node_rank=1 --master_addr=&quot;192.168.1.1&quot; --master_port=1234 OUR_TRAINING_SCRIPT.py (--arg1 --arg2 --arg3 and all other arguments of our training script)</pre><p>These two commands are identical excepted for the --node_rank argument which is set to 0 on the first machine and 1 on the second (and would be 2 on an additional server etc…)</p><p>The process of running a bunch of almost identical commands on a cluster of machine might looks a bit tedious. So now is probably a good time to learn about the magic of… <a href="https://www.gnu.org/software/parallel/"><strong>GNU parallel</strong></a><strong>:</strong></p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fvideoseries%3Flist%3DPL284C9FF2488BC6D1&amp;url=https%3A%2F%2Fwww.youtube.com%2Fplaylist%3Flist%3DPL284C9FF2488BC6D1&amp;image=http%3A%2F%2Fi.ytimg.com%2Fvi%2FOpaiGYxkSuQ%2Fdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="853" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/d51fb6fada94437833c863e671529d56/href">https://medium.com/media/d51fb6fada94437833c863e671529d56/href</a></iframe><p>One exciting improvement of the coming <a href="https://github.com/pytorch/pytorch/releases/tag/v1.0rc1">PyTorch v1.0</a> is the release of a new <a href="https://github.com/pytorch/pytorch/releases/tag/v1.0rc1#torchdistributed-new-c10d-library">c10d backend</a> for the distributed module. I will update this short introduction when v1.0 is released with more details on the new backend 🔥</p><p>This conclude our quick post on a few tips, tricks and tools to train your model on larger batches in a variety of settings.</p><p>I hope you enjoyed this more technical post!</p><p>Clap 👏 a couple of times if you liked it and want us to post more of these!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ec88c3e51255" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/training-larger-batches-practical-tips-on-1-gpu-multi-gpu-distributed-setups-ec88c3e51255">💥 Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU &amp; Distributed setups</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[⛵ Learning Meaning in Natural Language Processing - The Semantics Mega-Thread]]></title>
            <link>https://medium.com/huggingface/learning-meaning-in-natural-language-processing-the-semantics-mega-thread-9c0332dfe28e?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/9c0332dfe28e</guid>
            <category><![CDATA[twitter]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[meaning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Tue, 07 Aug 2018 15:31:15 GMT</pubDate>
            <atom:updated>2018-08-09T17:16:30.092Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4pIUBgHXsm4SkBdk" /><figcaption>Krabi, Thailand by <a href="https://unsplash.com/@playserious">Mo Baghdadi</a></figcaption></figure><h3>⛵ Learning Meaning in Natural Language Processing — The Semantics Mega-Thread</h3><h4>In which Twitter talked about meaning, semantics, language models, learning Thai and Java, entailment, co-reference — all in one fascinating thread.</h4><p>Last week <a href="https://twitter.com/jacobandreas/status/1023246560082063366">a tweet by Jacob Andreas</a><strong> </strong>triggered a huge discussion on Twitter that many people have called the <em>meaning/semantics mega-thread</em>.</p><p>Twitter is a great medium for having such a discussion, replying to any comment allows to revive the debate from the most promising point when it’s stuck in a dead-end.</p><p>Unfortunately Twitter also makes the discussion very hard to read afterwards so I made three entry points to explore this fascinating mega-thread:</p><ol><li>a summary of the discussion that you will find below,</li><li>an <a href="https://huggingface.co/meaning-mega-thread/">interactive view</a> to explore the trees of tweets, and</li><li>a <a href="https://huggingface.co/meaning-mega-thread/tree.png">commented map</a> to get an overview of the main points discussed:</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*em05OJ02V81HItUj.png" /><figcaption>Full size image <a href="https://huggingface.co/meaning-mega-thread/tree.png">here</a></figcaption></figure><blockquote>These map and summary are obviously selective/biased by my personal interests and familiarity with the topics discussed, so if you notice anything wrong or mis-represented please (i) go back to the actual tweets <strong>and</strong> (ii) send me a message <em>📨</em></blockquote><h3>🏎 A crash course on Lexical Meaning &amp; Semantics</h3><p>If you already know what we mean by “<em>Meaning/Semantics</em>” in NLP, you can skip this part and go straight to <a href="#c785">the debate 🔥</a></p><p>For the CS/ML folks out there here are a few words of introduction.</p><p>First, it’s important to state that Meaning in Natural Language is a <em>multi-facetted concept</em> with semantic, pragmatic, cognitive and social aspects. The discussion that happened on Twitter was mainly about lexical semantics and compositionality so I will focus on this sub-field for brevity. You will find additional links to broaden this view at the end of this section.</p><blockquote><a href="https://en.wikipedia.org/wiki/Meaning_(linguistics)">Meaning</a> is the information that a sender intends to convey, or does convey, to a receiver.</blockquote><p>Now, we know that <strong>strings are already a representation of meaning,</strong> so why should we go any further than just raw text?</p><p>Well there are several reasons we may want to distinguish meaning from raw text.</p><p>One reason is that the field of NLP/NLU aims at <strong>building systems</strong> that understand what you say to them, trigger actions based on that and convey back meaningful information. Let’s take a simple example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/444/1*9lcDx9SLPpdF4F0iS868CA.png" /></figure><p>Given some knowledge of math, we want our NLU system to produce an appropriate answer.</p><p>It’s difficult to (i) link raw text to a knowledge base of mathematical facts in our system and (ii) combine pieces of knowledge together to infer an answer. One solution is to define an <em>intermediate meaning representation</em> (sometimes called a Logical Form) that is more easy to manipulate.</p><p>For example in our case:</p><blockquote>Meaning representation: 𝑚𝑎𝑥(<em>𝚙𝚛𝚒𝚖𝚎𝚜</em> ∩(−∞; 𝟣𝟢))</blockquote><p>We can then execute this expression with respect to a model of the world, like our database of knowledge, to get an answer. This way, we have also factored out the understanding of language (called <em>semantic parsing</em>) from the world knowledge (the problem of <em>grounding</em> meaning of in the real word).</p><p>Advantageously, our <em>representation of the meaning of a sentence</em> can thus:</p><ol><li>provide a way to <strong>link language </strong>to external<strong> knowledge base</strong>, <strong>observations</strong>, and <strong>actions</strong>;</li><li>support <strong>computational inference</strong>, so that concepts can be <strong>combined</strong> to derive additional knowledge as human do during a conversation.</li></ol><p>Two other nice requirements for this representation:</p><ol><li><strong>unambiguous</strong>: one meaning per statement (unlike natural language);</li><li><strong>expressive</strong> enough to cover the full range of things that people talk about.</li></ol><p>Natural language as raw text doesn’t fulfill most of these criteria!</p><p>Arelated line of research is <strong>Formal Semantics</strong> which seek to <em>understand </em>linguistic meaning by constructing models of the principles that speakers use to convey meaning.</p><p>The tools of formal semantics are similar to NLU/NLP tools but the aim is to understand how people construct meaning more than any specific application.</p><p>Now there is a lot more to meaning than just logic forms and grounding. A few examples: “<strong>But I didn’t mean it literally!!”</strong> (speaker meaning ≠ literal meaning), “<a href="https://en.wikipedia.org/wiki/Buffalo_buffalo_Buffalo_buffalo_buffalo_buffalo_Buffalo_buffalo"><strong>Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo</strong></a><strong>.”</strong> (yes, this is a real sentence with a meaning but you need to find the right sense for each buffalo!) and so on…</p><blockquote>A few pointers: Our simple example came from <a href="https://cs.stanford.edu/~pliang/papers/executable-cacm2016.pdf">this nice article by Percy Liang</a>. As a quick overview of the field, I would recommend chapters 12 and 13 of J. Eisenstein’s book “<a href="https://github.com/jacobeisenstein/gt-nlp-class/blob/master/notes/eisenstein-nlp-notes.pdf">Natural Language Processing</a>”. They will take you through the main ideas, tools up to recent research on Meaning in NLP. <a href="http://faculty.washington.edu/ebender/100things-sem_prag.html">Emily M. Bender’s ACL 2018 tutorial</a> is a nice way to see how Meaning can be a multi-headed monster 🐍 to say the least!</blockquote><p>Now back to our mega-thread!</p><h3>🔥 Triggering a debate on Meaning</h3><p>As often, the discussion was sparked by a mention of sentence embeddings</p><h3>Jacob Andreas on Twitter</h3><p>@_shrdlu_ @emilymbender Are sentence embeddings &quot;meaning representations&quot;? Most of the work I&#39;ve seen is more about syntactic phenomena</p><h3>Emily M. Bender on Twitter</h3><p>@_shrdlu_ @jacobandreas What&#39;s the task the encodings are learned from? If it&#39;s language modeling, that&#39;s *not* a representation of meaning.</p><p>This argument was the main trigger for the mega-discussion that followed. Further in the thread, Emily M. Bender <a href="https://twitter.com/emilymbender/status/1024041833851015168">reformulates</a> her argument as:</p><blockquote>If all the learner gets is text the learner cannot learn meaning.</blockquote><h3><strong>Can a model </strong>trained only on raw text <strong>learn meaning?</strong></h3><p>This question was explored along two axes:</p><p><strong>What aspect of Meaning can a model learn?</strong></p><ul><li>Can it learn <em>meaning </em>or just learn <em>similarity in meaning </em>(i.e. learn that some expressions are similar without knowing what they mean. It is still very useful for transfer learning)?</li><li>Can it learn <em>grounded meaning</em> (learn the meaning of each expression as a state of the world state) or learn <em>lexical meaning</em> (e.g. learn how the meaning of sub-expressions compose together, as in our logical forms)?</li></ul><p><strong>How can the model Learn?</strong></p><ul><li>If the model cannot learn meaning from raw text alone, what would be the <em>minimal amount of additional supervision</em> needed? Should we add supervision from Logical Forms, Textual Entailment…?</li><li>Could we encode some <em>inductive bias</em> in the model so that it can learn aspects of meaning from raw text?</li></ul><h3>The Thai and Java Experiments</h3><p>Emily M. Bender proposed several interesting experiments which were discussed at length:</p><ul><li><a href="https://twitter.com/emilymbender/status/1024042044035985408">The Thai Room experiment</a>: “<em>Imagine [you] were given the sum total of all Thai literature in a huge library. (All in Thai, no translations.) Assuming you don’t already know Thai, you won’t learn it from that.”<br></em><strong>A real life example of trying to learn from raw text only.</strong></li><li><a href="https://twitter.com/emilymbender/status/1025002835467890689">The Java Code experiment</a>: “<em>Give your NN all well-formed java code that’s ever been written, but only the surface form of the code. Then ask it to evaluate (i.e. execute) part of it.”<br></em><strong>Can we learn execution semantics from raw text only?</strong></li></ul><h3>Investigating Programming Language Semantic</h3><p>The <a href="https://twitter.com/emilymbender/status/1025002835467890689">Java Code proposal</a> triggered an interesting discussion on the difference between trying to learn meaning from Programming Language (PL) code and from Natural Language (NL) text.</p><p>It actually seems more difficult to learn from PL than NL:</p><h3>Matt Gardner on Twitter</h3><p>@yoavgo @emilymbender @gneubig @gchrupala @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ Third try :) I want to learn code execution. I have java code (with no tests). There is no information about execution in the code itself, because I never actually see the output of a function. I see functions called, but this only ever gives me return types.</p><h3>Matt Gardner on Twitter</h3><p>@yoavgo @emilymbender @gneubig @gchrupala @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ In order to learn execution, I need to either see execution (for some kind of supervised learning), or some indirect result of an execution (for some kind of weak / unsupervised learning). I get neither in just code, unless I have tests.</p><p>Learning meaning from Java code is like having a text composed only of orders/commands and without any descriptions. But description are very important feedbacks for learning as they allow one to compare its internal world state with the real world state.</p><h3>Language Models</h3><p>The discussion circled around Language Models. A language model is a model which can <strong>predict the next word in a sentence given a context</strong> (past words, external knowledge). Recently, these models have given <a href="https://blog.openai.com/language-unsupervised/">interesting</a> <a href="http://arxiv.org/abs/1806.02847">results</a> in commonsense reasoning. Language models were examined in two settings:</p><ul><li><strong>Independently of any training dataset</strong>: having a <em>human-level language model</em> involves that the model has a human-like notion of meaning and world state. As <a href="https://twitter.com/yoavgo/status/1025485692347056128">Yolav Goldberg mentioned</a> “<em>it’s just (one of the many) trivial examples where perfect LM entails solving not only all of language but also all of AI.</em>”</li><li>More interesting is the case of a language model <strong>trained from raw text only:</strong> here the question is how much the model can learn in terms of semantics without being given access to explicit meaning information!</li></ul><h3>Textual Entailment</h3><p>One way to give some information about meaning without departing too much from raw text is to train a model on Textual Entailment, the task of <strong>predicting whether a sentence imply another</strong>.</p><p>In a series tweets, Sam Bowman explained his view that entailment could be used to learn “the meat of compositional” semantics almost from raw text:</p><h3>Sam Bowman on Twitter</h3><p>@jacobandreas @emilymbender @_shrdlu_ @tallinzen I&#39;d argue that it&#39;s possible to build a satisfying approach to semantics with only entailments and no grounding. This is a category fight, so I won&#39;t push to hard, but I&#39;m not the only one-have a look at work on natural logic for some very formal attempts.</p><p>And also made the suggestion that a learner might be able to learn entailment in a simpler way than curent setups, using a setup that could be close to LM:</p><h3>Sam Bowman on Twitter</h3><p>@emilymbender @tallinzen @IntuitMachine @jacobandreas @_shrdlu_ I&#39;m mostly on the same page, but I think it&#39;s plausible for a learner to learn to do something like RTE (which does get at something more meaning-like than bare forms) while learning almost entirely _from_ raw form.</p><h3>Sam Bowman on Twitter</h3><p>@emilymbender @tallinzen @IntuitMachine @jacobandreas @_shrdlu_ Something like language modeling, with some minimal additional guidance/data to teach it what the notion of entailment is (but not how any particular language works). I don&#39;t think it&#39;s something we can do now or soon, but I don&#39;t see a strong argument that it&#39;s impossible.</p><h3>An Inductive Bias Language Model</h3><p>Another way to learn meaning from a dataset as close as possible to raw text is to put a strong inductive bias in the model as discussed by Matt Gardner:</p><h3>Matt Gardner on Twitter</h3><p>@emilymbender @yoavgo @microth @gneubig @gchrupala @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ Step 1: design a model that explicitly tries to capture world state (&quot;meaning&quot;). Step 2: train that model using a LM signal. Yes, this is a weak signal, I know. But not zero. https://t.co/DgXKpSZtLN</p><p>One example is the <a href="https://arxiv.org/abs/1708.00781">Entity Language Model</a> which augments a classical LSTM by explicitly modeling an arbitrary number of entities, updating their representations along the hidden state, and using the mention representations to contextually generate the next word in the LM task.</p><blockquote>To read more about that, check <a href="https://sites.google.com/site/repl4nlp2018/">Yejin Choi’s talk at ACL 2018</a> and <a href="https://www.youtube.com/watch?v=7CcSm0PAr-Y">Percy Liang’s talk at AAAI 2018</a>.</blockquote><h3><strong>The Big Open Question</strong></h3><p>In the end, I feel like the main original question stayed open: can a model learn some aspects of lexical meaning from raw text alone?</p><p>Here is a discussion representative of the positions of the participants:</p><h3>Emily M. Bender on Twitter</h3><p>@gchrupala @yoavgo @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ First, yes, some constituent structure can be induced, but I&#39;m not convinced it all can. But even if it could be: How&#39;s the learner going to get active/passive equivalence? Dative alternation? Long distance dependencies? W/o these, you don&#39;t have predicate-argument structure.</p><h3>Matt Gardner on Twitter</h3><p>@emilymbender @gchrupala @yoavgo @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ By seeing &quot;She thought it was yummy&quot; after both the active and the passive versions. A stretch to actually learn this from a language modeling signal, yes, but not a priori impossible. Future utterances can give context that allows some induction of &quot;meaning&quot;.</p><h3>Emily M. Bender on Twitter</h3><p>@yoavgo @nlpmattg @gchrupala @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ Because I don&#39;t see how &quot;She thought it was yummy&quot; is going to help with mapping predicate argument structure for &quot;Kim at the cake&quot; and &quot;The cake was eaten by Kim&quot; without it.</p><h3>(((ل()(ل() &#39;yoav)))) on Twitter</h3><p>@emilymbender @nlpmattg @gchrupala @tallinzen @sleepinyourhat @jacobandreas @_shrdlu_ by seeing these two sentences in the exact same contexts, you could assume they share the meaning, and you also observe the symbols for cake and for Kim in different positions. a case-inflection system will make this harder.</p><p>We are done with our quick summary of the Meaning Megathread.</p><p>For more details you should check out the <a href="https://huggingface.co/meaning-mega-thread/tree.png">commented map</a> and <a href="https://huggingface.co/meaning-mega-thread/">navigate the tweet trees</a>!</p><p>As always, don’t hesitate give this post a few claps 👏 if you enjoyed it!</p><h3>A word on Searle’s Chinese Room</h3><p><a href="https://en.wikipedia.org/wiki/Chinese_room">Searle’s room argument</a> came back often in the discussion but the situation was a bit different.</p><p>Searle’s argument was made in the Strong versus Weak AI debate: does the computer has a mind or consciousness. Here the question is less philosophical: can we extract a representation of meaning from form alone.</p><p>Still, <a href="https://twitter.com/jeremyphoward/status/1026881686359822337">as Jeremy Howard detailed a bit later</a>, the Chinese room experiment of Searle goes far beyond the question of Strong/weak AI to the question of understanding/qualia, so please go <a href="https://twitter.com/jeremyphoward/status/1026881686359822337">check this thread</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9c0332dfe28e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/learning-meaning-in-natural-language-processing-the-semantics-mega-thread-9c0332dfe28e">⛵ Learning Meaning in Natural Language Processing - The Semantics Mega-Thread</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ 100 Times Faster Natural Language Processing in Python]]></title>
            <link>https://medium.com/huggingface/100-times-faster-natural-language-processing-in-python-ee32033bdced?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/ee32033bdced</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[optimisation]]></category>
            <category><![CDATA[chatbots]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Tue, 12 Jun 2018 06:46:20 GMT</pubDate>
            <atom:updated>2020-09-02T14:15:52.542Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hfwhejA1iwOt85ftb8gILQ.png" /><figcaption>SpaceX Falcon Heavy Launch — Credit <a href="https://www.flickr.com/photos/spacex">SpaceX</a></figcaption></figure><h4>How to take advantage of spaCy &amp; a bit of Cython for blazing fast NLP</h4><blockquote>I also published a <a href="https://github.com/huggingface/100-times-faster-nlp">Jupyter notebook</a> with the examples I describe in this post.</blockquote><p>When we published our Python <a href="https://medium.com/huggingface/state-of-the-art-neural-coreference-resolution-for-chatbots-3302365dcf30">coreference resolution package</a>✨ last year, we got an amazing feedback from the community and people started to use it for many applications 📚, some very different from our original dialog use-case 👥.</p><p>And we discovered that, while the speed was totally fine for dialog messages, it could be <em>really</em> slow 🐌 on larger news articles.</p><p>I decided to investigate this in details and the result is <a href="https://github.com/huggingface/neuralcoref/">NeuralCoref v3.0</a> which is about <strong>100 times faster</strong> 🚀 than the previous version (several thousands words per seconds) while retaining the same accuracy, and the easiness of use and eco-system of a Python library.</p><p>In this post I wanted to share a few lessons learned on this project, and in particular:</p><ul><li>How you can <strong>design a high-speed module </strong>in Python,</li><li>How you can <strong>take advantage of spaCy</strong>’s internal data structures to efficiently design <strong>super fast NLP functions</strong>.</li></ul><p>So I am a bit cheating here because we will be talking about Python, but also about some <strong>Cython</strong> magic — but, you know what? Cython is <a href="http://cython.org/">a superset of Python</a>, so don’t let that scares you away!</p><blockquote>Your current Python program is already a Cython program.</blockquote><p>There are several cases where you may need such speed-ups, e.g.:</p><ul><li>you are developing a <strong>production module</strong> for NLP using Python,</li><li>you are <strong>computing analytics</strong> on a large NLP dataset using Python,</li><li>you are <strong>pre-processing a large training set </strong>for a DeepLearning framework like pyTorch/TensorFlow, or you have a heavy processing logic in your DeepLearning <strong>batch loader</strong> that slows down your training.</li></ul><blockquote>One last thing before we start: I also <a href="https://github.com/huggingface/100-times-faster-nlp">published a Jupyter Notebook</a> with the working examples I talk about in this post. Try it out!</blockquote><h3>First step to rocket speed: Profiling</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6lC4bfxnSYHebgqP.png" /></figure><p>The first thing to know is that most of your code is probably just fine in pure Python but there can be <strong>a few bottlenecks functions</strong> that will get you orders of magnitude faster if you give them some love.</p><p>You should thus start by profiling your Python code and find where the slow parts are located. One option is to use <a href="https://docs.python.org/3/library/profile.html">cProfile</a> like that:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/25e8fee7b331fbfa23cdef2852f89d00/href">https://medium.com/media/25e8fee7b331fbfa23cdef2852f89d00/href</a></iframe><p>You’ll likely find that the slow parts are a few loops, and some Numpy arrays manipulations if you use neural networks (but I won’t spend time talking about NumPy here as there is already a <a href="http://cython.readthedocs.io/en/latest/src/userguide/numpy_tutorial.html">lot of information</a> written about that).</p><p>So, how can we speed up these loops?</p><h3>Fast Loops in Python with a bit of Cython</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*RA89oQ-0j3Rscipw.jpg" /></figure><p>Let’s work this out on a simple example. Say we have a large set of rectangles that we store as a list of Python objects, e.g. instances of a Rectangle class. The main job of our module is to iterate over this list in order to count how many rectangles have an area larger than a specific threshold.</p><p>Our Python module is quite simple and looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0b3be6080ecf6f7857d22fe639e8c206/href">https://medium.com/media/0b3be6080ecf6f7857d22fe639e8c206/href</a></iframe><p>The check_rectangles function is our bottleneck! It loops over a large number of Python objects and this can be rather slow as the Python interpreter does a lot of work under the hood at each iteration (looking for the area method in the class, packing and unpacking arguments, calling the Python API….).</p><blockquote>Here comes Cython to help us speed up our loop.</blockquote><p>The Cython language is a superset of Python that contains two kind of objects:</p><ul><li><strong>Python objects</strong> are the objects we manipulate in <em>regular Python</em> like numbers, strings, lists, class instances…</li><li><strong>Cython C objects </strong>are C or C++ objects like <em>double</em>, <em>int</em>, <em>float</em>, <em>struct, vectors</em> that can be compiled by Cython in super fast low-level code.</li></ul><blockquote>A fast loop is simply a loop in a Cython program within which we only access <strong>Cython C objects</strong>.</blockquote><p>A straightforward approach to designing such a loop is to define C structures that will contain all the things we need during our computation: in our case, the lengths and widths of our rectangles.</p><p>We can then store our list of rectangles in a C array of such structures that we will pass to our check_rectangle function. This function now has to accept a <em>C array</em> as input and thus will be defined as a <em>Cython function</em> by using the cdef keyword instead of def (note that cdef is also used to define Cython C <em>objects</em>).</p><p>Here is how the fast Cython version of our Python module looks like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/85795766c250d8f075fa85a96da49e47/href">https://medium.com/media/85795766c250d8f075fa85a96da49e47/href</a></iframe><p>Here we used a raw array of C pointers but you can also choose other options, in particular <a href="http://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html#standard-library">C++ structures like vectors, pairs, queues and the like</a>. In this snippet, I also used the convenient Pool() memory management object of <a href="https://github.com/explosion/cymem">cymem</a> to avoid having to free the allocated C array manually. When Pool is garbage collected by Python, it automatically frees the memory we allocated using it.</p><blockquote>A good reference on the practical usage of Cython in NLP is the <a href="https://spacy.io/api/cython#conventions">Cython Conventions</a> page of spaCy’s API.</blockquote><h3>👩‍🎨 Let’s Try that Code!</h3><p>There are many ways you can test, compile and distribute Cython code! Cython can even be used <a href="http://cython.readthedocs.io/en/latest/src/reference/compilation.html#compiling-notebook">directly in a Jupyter Notebook</a> like Python.</p><p>First install Cython with pip install cython</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/773/0*1MPkC8HxkRX_lHyl.png" /></figure><h4>First Tests in Jupyter</h4><p>Load the Cython extension in a Jupyter notebook with %load_ext Cython.</p><p>Now you can write Cython code like Python code by using <a href="http://cython.readthedocs.io/en/latest/src/reference/compilation.html#compiling-with-a-jupyter-notebook">the magic command</a> %%cython.</p><p>If you have a compilation error when you execute a Cython cell, be sure to check Jupyter terminal output to see the full message.</p><p>Most of the time you’ll be missing a-+ tag after %%cython to compile to C++ (for example if you use spaCy Cython API) or an import numpy if the compiler complains about NumPy.</p><p>As I mentioned in the beginning, check <a href="https://github.com/huggingface/100-times-faster-nlp">the Jupyter Notebook</a> accompanying this post, it has all the examples we discuss running in Jupyter.</p><h4>Writing, Using and Distributing Cython Code</h4><p>Cython code is written in .pyx files. These files are compiled to C or C++ files by the Cython compiler and then to byte-code level with the system’s C compiler. The byte-code level files can then be used by the Python interpreter.</p><p>You can load a .pyx file directly in Python by using pyximport:</p><pre>&gt;&gt;&gt; <strong>import</strong> <strong>pyximport</strong>; <strong>pyximport.install()</strong><br>&gt;&gt;&gt; <strong>import</strong> <strong>my_cython_module</strong></pre><p>You can also build your Cython code as a Python package and import/distribute it as a regular Python package as detailed <a href="http://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html#">here</a>. This can take some time to get working, in particular on all platforms. If you need a working example, <a href="https://github.com/explosion/spaCy/blob/master/setup.py">spaCy’s install script</a> is a rather comprehensive one.</p><p>Before we move to some NLP, let’s quickly talk about the def, cdef and cpdef keywords, because they are the main things you need to grab to start using Cython.</p><p>You can use three types of functions in a Cython program:</p><ul><li><strong>Python functions</strong>, which are defined with the usual keyword <strong>def</strong>. They take as input and output <em>Python objects</em>. Internally they can use <strong><em>both</em></strong><em> Python and C/C++ objects</em> and can call <strong><em>both</em></strong> <em>Cython and Python functions</em>.</li><li><strong>Cython functions</strong> defined with the <strong>cdef</strong> keyword. They can take as input, use internally and output <strong><em>both</em></strong> <em>Python and C/C++ objects. </em>These functions are <strong><em>not accessible</em></strong><em> from the Python-space</em> (i.e. the Python interpreter and other pure Python modules that would import your Cython module) but they can be imported by other Cython modules.</li><li><strong>Cython functions</strong> defined with the <strong>cpdef</strong> keyword are like the cdef Cython functions but they are also provided with a Python wrapper so they can be called <em>from the Python-space</em> (with Python objects as inputs and outputs) <strong><em>as well as</em></strong> <em>from other Cython modules</em> (with <em>C/C++ or Python objects</em> as inputs).</li></ul><p>The cdef keyword has another use which is to type <em>Cython</em> <em>C/C++ objects</em> in the code. Unless you type your objects with this keyword, they will be considered as Python objects (and thus slow to access).</p><h3>💫 Using Cython with spaCy to speed up NLP</h3><p>This is all nice and fast but… we are still not doing NLP here! No string manipulations, no unicode encodings, none of the subtleties we are lucky to have in Natural Language Processing 🙃.</p><p>And the official Cython documentation even <a href="http://cython.readthedocs.io/en/latest/src/tutorial/strings.html">advises against</a> the use of C level strings:</p><blockquote>Generally speaking: unless you know what you are doing, avoid using C strings where possible and use Python string objects instead.</blockquote><p>So how can we design fast loops in Cython when we work with strings?</p><blockquote>💫 spaCy got us covered.</blockquote><p>The way spaCy tackle this problem is quite smart.</p><h4>Convert all strings to <strong>64-bit </strong>hashes</h4><p>All the unicode strings in spaCy (the text of a token, its lower case text, its lemma form, POS tag label, parse tree dependency label, Named-Entity tags…) are stored in a single data structure called the <a href="https://spacy.io/api/stringstore">StringStore</a> where they are indexed by <strong>64-bit hashes</strong>, i.e. C level <a href="https://www.badprog.com/c-type-what-are-uint8-t-uint16-t-uint32-t-and-uint64-t">uint64_t</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nxvhI7mEc9A75PwMH-PSBg.png" /></figure><p>The <a href="https://spacy.io/api/stringstore">StringStore</a> object implements a look up between <strong>Python unicode strings</strong> and <strong>64-bit hashes</strong>.</p><p>It is accessible from everywhere in spaCy and every object (see on the left), for example as nlp.vocab.strings, doc.vocab.strings or span.doc.vocab.string.</p><p>When a module needs to perform fast processing on some tokens, it simply uses the C level 64-bit hashes instead of the strings. A call to the <a href="https://spacy.io/api/stringstore">StringStore</a> look up table will then give back the Python unicode strings associated to the hashes.</p><p>But spaCy does more than that and also gives us access to fully populated C level structures of the document and vocabulary, which we can use in Cython loops instead of having to build our own structures.</p><h4>SpaCy’s internal data structures</h4><p>The main data structure associated to a spaCy document is the <a href="https://spacy.io/api/cython-classes#section-doc">Doc</a> object which owns the sequence of tokens (“words”) of the processed string and all their annotations in a C level object called <a href="https://spacy.io/api/cython-classes#token_attributes">doc.c</a> which is an array of <strong>TokenC </strong>structures.</p><p>The <a href="https://spacy.io/api/cython-structs#section-tokenc"><strong>TokenC</strong></a><strong> </strong>structure contains all the informations we need about each tokens. This information is stored as<strong> 64-bit hashes</strong> that can be re-associated to unicode strings as we’ve just seen.</p><p>To see exactly what’s in these nice C structures, just have a look at the freshly created <a href="https://spacy.io/api/cython">Cython API doc</a> of spaCy 💫.</p><p>Let’s see that in action on a simple example of NLP processing.</p><h3>🚀Fast NLP Processing with spaCy and Cython</h3><p>Let’s say we have a dataset of text documents we need to analyse.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4a81bd7254df8769bc901df5bfa79877/href">https://medium.com/media/4a81bd7254df8769bc901df5bfa79877/href</a></iframe><p>On the left I wrote a script that builds a list of 10 documents parsed by spaCy, each with ~170k words. We could also have 170k documents with 10 words in each (like a dialog dataset) but that’s slower to create so let’s stick with 10 docs.</p><p>We want to perform some NLP task on this dataset. For example, we would like to count the number of times the word “<em>run”</em> is used as a noun in the dataset (i.e. tagged tagged with a “<em>NN</em>” Part-Of-Speech tag by spaCy).</p><p>A Python loop to do that is short and straightforward:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d3babdc283b837b18701aa8d8ebd5678/href">https://medium.com/media/d3babdc283b837b18701aa8d8ebd5678/href</a></iframe><p>But it’s also quite slow! On my laptop this code takes about 1.4 second to get the answer. If we had a million documents it would take <strong>more than a day</strong> to give us the answer.</p><p>We could use multiprocessing but <a href="https://youtu.be/yJR3qCUB27I?t=19m29s">it’s often not such a great solution in Python</a> because you have to deal with <a href="https://wiki.python.org/moin/GlobalInterpreterLock">the GIL</a> 😕 Also, note that Cython can also <a href="https://cython.readthedocs.io/en/latest/src/userguide/parallelism.html">use multi-threading</a>! And that may actually even be <strong>the best part of Cython</strong> because the GIL is released so we are at full speed 🏎 Cython basically directly call OpenMP under the hood. I won’t have time to talk about parallelism here so check <a href="https://cython.readthedocs.io/en/latest/src/userguide/parallelism.html">this link</a> for more details.</p><p>Now let’s try to speed up our Python code with spaCy and a bit of Cython.</p><p>First, we have to think about the data structure. We will need a C level array for the dataset, with pointers to each document’s TokenC array. We’ll also need to convert the test strings we use (“<em>run</em>” and “<em>NN</em>”) to 64-bit hashes.</p><p>When all the data required for our processing is in C level objects, we can then iterate at full C speed over the dataset.</p><p>Here is how this example can be written in Cython with spaCy:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4b7e6240e3f4fbed885fadbdb80ae010/href">https://medium.com/media/4b7e6240e3f4fbed885fadbdb80ae010/href</a></iframe><p>The code is a bit longer because we have to declare and populate the C structures in main_nlp_fast before calling our Cython function <a href="#a220">[*]</a>.</p><p>But it is also a lot faster! In my Jupyter notebook, this Cython code takes about 20 milliseconds to run which is about<strong> 80 times faster<em> </em></strong>than our pure Python loop.</p><p>The absolute speed is also impressive for a module written in a Jupyter Notebook cell and which can interface natively with other Python modules and functions: scanning ~1,7 million words in 20 ms means we are processing a whopping <strong>80 millions words per seconds</strong>.</p><p>This concludes our quick introduction on using Cython for NLP. I hope you enjoyed it.</p><p>There are a lot of other things to says on Cython but it would get us too far from this simple introduction. The best place to start from now is probably the <a href="http://cython.readthedocs.io/en/latest/src/tutorial/index.html">Cython tutorials</a> for a general overview and <a href="https://spacy.io/api/cython">spaCy’s Cython page</a> for NLP.</p><p>Don’t hesitate to give us a few claps 👏 if you want more content like that!</p><ul><li>. <a href="#c72f">^</a> If you use low level structures several times in your code, a more elegant option than populating C structures each time, is to design our Python code around the low level structures with <a href="http://cython.readthedocs.io/en/latest/src/userguide/extension_types.html">a Cython extension type</a> wrapping the C level structures. This is how most of spaCy is structured and it is a very elegant way to combine fast speed, low memory use and the easiness of interfacing with external Python libraries and functions.</li></ul><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ee32033bdced" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/100-times-faster-natural-language-processing-in-python-ee32033bdced">🚀 100 Times Faster Natural Language Processing in Python</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Current Best of Universal Word Embeddings and Sentence Embeddings]]></title>
            <link>https://medium.com/huggingface/universal-word-sentence-embeddings-ce48ddc8fc3a?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/ce48ddc8fc3a</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[chatbots]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Mon, 14 May 2018 15:13:53 GMT</pubDate>
            <atom:updated>2020-09-02T14:14:51.264Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4lAYlcSVrYmBzFcWg_2GlQ.png" /><figcaption>John Christian Fjellestad –<a href="https://www.flickr.com/photos/jkfjellestad/25509017594/in/photolist-W1hVoR-ES9odb-CL6HqD-Nc4e4C/">Distant road</a></figcaption></figure><blockquote>A Chinese version of this article can be <a href="https://www.jqr.com/article/000316">found here</a>, thanks to <a href="https://medium.com/u/c2d21d069ac">Jakukyo</a>.</blockquote><p>Word and sentence embeddings have become an essential part of any Deep-Learning-based natural language processing systems.</p><p>They encode words and sentences 📜 in fixed-length dense vectors 📐 to drastically improve the processing of textual data.</p><p>A huge trend is the quest for <strong>Universal Embeddings: </strong>embeddings that are pre-trained on a large corpus and can be plugged in a variety of downstream task models (sentimental analysis, classification, translation…) to automatically improve their performance by incorporating some general word/sentence representations learned on the larger dataset.</p><p>It’s a form of <em>transfer learning</em>. Transfer learning has been recently shown to drastically increase the performance of NLP models on important tasks such as text classification. Go check <a href="http://nlp.fast.ai/classification/2018/05/15/introducting-ulmfit.html">the very nice work of Jeremy Howard and Sebastian Ruder</a> (ULMFiT) to see it in action.</p><p>While unsupervised representation learning of sentences had been the norm for quite some time, the last few months have seen a shift toward supervised and multi-task learning schemes with a number of very interesting proposals in late 2017/early 2018.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZZrMm_-SnUhATja5a_z7tg.png" /><figcaption><strong>Recent trend in Universal Word/Sentence Embeddings. </strong>In this post, we describe the models indicated in black. Reference papers for all indicated models are listed at the end of the post.</figcaption></figure><p>This post is thus a brief primer on the current state-of-the-art in Universal Word and Sentence Embeddings, detailing a few</p><ul><li><strong>strong/fast baselines</strong>: <em>FastText, Bag-of-Words</em></li><li><strong>state-of-the-art models</strong>: <em>ELMo, Skip-Thoughts, Quick-Thoughts, InferSent, MILA/MSR’s General Purpose Sentence Representations &amp; Google’s Universal Sentence Encoder.</em></li></ul><p>If you want some background on what happened before 2017 😀, I recommend the <a href="http://ruder.io/word-embeddings-2017/">nice post on word embeddings</a> that Sebastian wrote last year and <a href="http://ruder.io/word-embeddings-1/">his intro posts</a>.</p><p>Let’s start with word embeddings.</p><h3>Recent Developments in Word Embeddings</h3><p>A wealth of possible ways to embed words have been proposed over the last five years. The most commonly used models are <a href="https://github.com/dav/word2vec/">word2vec</a> and <a href="https://nlp.stanford.edu/projects/glove/">GloVe</a> which are both unsupervised approaches based on the <a href="https://aclweb.org/aclwiki/Distributional_Hypothesis">distributional hypothesis</a> (<em>words that occur in the same contexts tend to have similar meanings</em>).</p><p>While <a href="https://arxiv.org/abs/1805.04032">several works</a> augment these unsupervised approaches by incorporating the supervision of semantic or syntactic knowledge, purely unsupervised approaches have seen interesting developments in 2017–2018, the most notable being <strong>FastText</strong> (an extension of word2vec) and <strong>ELMo</strong> (state-of-the-art contextual word vectors).</p><p><a href="https://github.com/facebookresearch/fastText"><strong>FastText</strong></a> was developed by the team of Tomas Mikolov who proposed the word2vec framework in 2013, triggering the explosion of research on universal word embeddings.</p><p>The main improvement of FastText over the original word2vec vectors is the inclusion of character n-grams, which allows computing word representations for words <em>that did not appear in the training data</em> (“out-of-vocabulary” words).</p><p><a href="https://github.com/facebookresearch/fastText">FastText vectors</a> are super-fast to train and are available in 157 languages trained on Wikipedia and Crawl. They are a great baseline.</p><p>The <a href="http://allennlp.org/elmo">Deep Contextualized Word Representations</a> (<strong>ELMo</strong>) have recently improved the state of the art in word embeddings by a noticeable amount. They were developed by the Allen institute for AI and will be <a href="https://arxiv.org/abs/1802.05365">presented at NAACL</a> 2018 in early June.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/302/0*RcKHZOLwtAm29QUE.jpg" /><figcaption>Elmo knows quite a lot about words context</figcaption></figure><p>In ELMo, each word is assigned a representation which is a function of the entire corpus sentences to which they belong. The embeddings are computed from the <em>internal states of a two-layers bidirectional Language Model (LM)</em>, hence the name “ELMo”: <strong><em>E</em></strong><em>mbeddings from </em><strong><em>L</em></strong><em>anguage </em><strong><em>Mo</em></strong><em>dels.</em></p><p>Specificities of ELMo:</p><ul><li><strong>ELMo’s inputs are characters</strong> rather than words. They can thus take advantage of sub-word units to compute meaningful representations even for out-of-vocabulary words (like FastText).</li><li><strong>ELMo are concatenations of the activations on several layers of the biLMs. </strong>Different layers of a language model encode different kind of information on a word (e.g. Part-Of-Speech tagging is well predicted by the lower level layers of a biLSTM while word-sense disambiguation is better encoded in higher-levels). Concatenating all layers allows to freely combine a variety of word representations for better performances on downstream tasks.</li></ul><p>Now, let’s turn to universal sentence embeddings.</p><h3>The Rise of Universal Sentence Embeddings</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*XxOiBMiB_Ac4fylp." /></figure><p>There are currently many competing schemes for learning sentence embeddings. While simple baselines like averaging word embeddings consistently give strong results, a few novel unsupervised and supervised approaches, as well as multi-task learning schemes, have emerged in late 2017-early 2018 and lead to interesting improvements.</p><p>Let’s go quickly through the four types of approaches currently studied: from <em>simple word vector averaging baselines </em>to <em>unsupervised/supervised</em> approaches and <em>multi-task learning schemes (as illustrated above)</em>.</p><p>There is a <a href="https://arxiv.org/abs/1804.07461">general</a> <a href="http://arxiv.org/abs/1805.01070">consensus</a> in the field that the simple approach of directly<strong> averaging a sentence’s word vectors</strong> (so-called Bag-of-Word approach) gives a strong baseline for many downstream tasks.</p><blockquote><em>A good algorithm for computing such a baseline is detailed in the work of Arora et al. published last year at ICLR, </em><a href="https://openreview.net/forum?id=SyK00v5xx"><em>A Simple but Tough-to-Beat Baseline for Sentence Embeddings</em></a><em>: </em>use a popular word embeddings of your choice, encode a sentence in a linear weighted combination the word vectors and perform a common component removal (remove the projection of the vectors on their first principal component). <em>This general method has deeper and powerful theoretical motivations that rely on a generative model which uses a random walk on a discourse vector to generate text (we won’t discuss the theoretical details here).</em></blockquote><p>A very recent implementation of a strong Bag-of-Word baseline (even stronger than Arora’s one) is the <em>Concatenated p-mean Embeddings</em> from the University of Darmstadt that you will find <a href="https://github.com/UKPLab/arxiv2018-xling-sentence-embeddings">here</a> (thanks <a href="https://medium.com/u/b1ef74fb3b24">Yaser</a> for pointing that work out).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*zJCFqVxNimR5TZqNLmZxqg.png" /><figcaption>A plot of HuggingFace’s dialogs Bag-of-Words. Bag-of-Words approaches loose words ordering but keep a surprising amount of semantic and syntactic content. Interesting insights in <a href="http://arxiv.org/abs/1805.01070">Conneau et al.</a> ACL 2018.</figcaption></figure><p>Going beyond simple averaging, the first major proposals were using <strong>unsupervised</strong> training objectives, starting with the <a href="https://arxiv.org/abs/1506.06726"><em>Skip-thoughts vectors</em></a> proposed by Jamie Kiros and co-workers in 2015.</p><p><em>Unsupervised</em> schemes learn sentence embeddings as a byproduct of learning to predict a coherent succession of sentences or a coherent succession of clauses inside a sentence. These approaches can (in theory) make use of any text dataset as long as it contains sentences/clauses juxtaposed in a coherent way.</p><p><a href="https://arxiv.org/abs/1506.06726"><strong>Skip-thoughts vectors</strong></a> is the archetypical example of learning unsupervised sentence embeddings. It can be though as the equivalent for sentences of the skip-gram model developed for word embeddings: <em>rather than predicting the words surrounding a word, we try to predict the surroundings sentences of a given sentence</em>. The model consists in an RNN-based encoder-decoder which is trained to reconstruct the surrounding sentences from the current sentence.</p><p>One interesting insight in the Skip-Thought paper was a <em>vocabulary expansion scheme</em>: Kiros et al. handled words not seen during training by learning a linear transformation between their RNN word embedding space and a larger word embedding such as word2vec.</p><p><a href="https://openreview.net/forum?id=rJvJXZb0W"><strong>Quick-thoughts vectors</strong></a><strong> </strong>are a recent development of the Skip-thoughts vectors, presented this year at ICLR. In this work, the task of predicting the next sentence given the previous one is reformulated as a classification task: <em>the decoder is replaced by a classifier which has to choose the next sentence among a set of candidates</em>. It can be interpreted as a discriminative approximation to the generation problem.</p><p>One strength of this model is its speed of training (an order of magnitude compared to Skip-thoughts model) making it a competitive solution to exploit massive dataset.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fU0pNrhPmk6N8QC4ShYAPg.png" /><figcaption>Quick-thoughts classification task. The classifier has to chose the following sentence from a set of sentence embeddings. Source: “An efficient framework for learning sentence representations” by Logeswaran et al.</figcaption></figure><p>For a long time, <strong>supervised</strong> learning of sentence embeddings was thought to give lower-quality embeddings than unsupervised approaches but this assumption has recently been overturned, in part following the publication of the <a href="https://arxiv.org/abs/1705.02364"><em>InferSent</em></a> results.</p><p>Unlike the <em>unsupervised </em>approaches detailed before, <em>supervised</em> learning requires a labelled dataset annotated for some task like Natural Language Inference (e.g. with pairs of entailed sentences) or Machine Translation (with pairs of translated sentences) which poses the question of the specific task to choose and the related question of the size of the dataset required for good quality embeddings. We talk more about these questions in the next and last section on Multi-task learning but before that, let’s see what’s behind the InferSent breakthrough that was published in 2017.</p><p><a href="https://arxiv.org/abs/1705.02364"><strong>InferSent</strong></a><strong> </strong>is an interesting approach by the simplicity of its architecture. It uses the <em>Stanford Natural Language Inference (SNLI)</em> <em>Corpus</em> (a set of of 570k pairs of sentences labelled with 3 categories: neutral, contradiction and entailment) to train a classifier on top of a sentence encoder. Both sentences are encoded using the same encoder while the classifier is trained on a pair representation constructed from the two sentence embeddings. Conneau et al. adopt a bi-directional LSTM completed with a max-pooling operator as sentence encoder.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/448/1*jwfxGxmOn42VMZMT6TOaKA.png" /><figcaption>A supervised sentence embeddings model (InferSent) to learn from a NLI dataset. Source: “Supervised Learning of Universal Sentence Representations from Natural Language Inference Data” by A. Conneau et al.</figcaption></figure><p>The success of InferSent lead poses the following question in addition to the usual quest for selecting the best neural net model:</p><blockquote><em>Which supervised training task would learn sentence embeddings that better generalize on downstream tasks?</em></blockquote><p><strong>Multi-task learning </strong>can be seen as a generalization of Skip-Thoughts, InferSent, and the related unsupervised/supervised learning schemes, that answer this question by trying to combine several training objectives in one training scheme.</p><p>Several recent proposals for multi-task learning were published in early 2018. Let’s quickly go through MILA/MSR’s <strong>General Purpose Sentence Representation </strong>and Google’s <strong>Universal Sentence Encoder.</strong></p><p>In the paper describing MILA &amp; Microsoft Montreal’s work and presented at ICLR 2018 (<a href="https://arxiv.org/abs/1804.00079">Learning General Purpose Distributed Sentence Representation via Large Scale Multi-Task Learning</a>), Subramanian et al observe that to be able to generalize over a wide range of diverse tasks, it is necessary to encode multiple aspects of the same sentence.</p><p>The authors thus leverage a <em>one-to-many multi-tasking learning framework</em> to learn a universal sentence embedding by switching between several tasks. The 6 tasks chosen (Skip-thoughts prediction of the next/previous sentence, neural machine translation, constituency parsing and natural language inference) share the same sentence embedding obtained by a bi-directional GRU. Experiments suggest that syntactic properties are better learned when adding a multi-language neural machine translation task, length and word order are learned with a parsing task and training a natural language inference encodes syntax information.</p><p><strong>Google’s Universal Sentence Encoder, </strong><a href="https://arxiv.org/abs/1803.11175">published in early 2018</a>, follows the same approach. Their encoder uses a transformer-network that is trained on a variety of data sources and a variety of tasks with the aim of dynamically accommodating a wide variety of natural language understanding tasks. A <a href="https://www.tensorflow.org/hub/modules/google/universal-sentence-encoder/2">pre-trained version</a> has been made available for TensorFlow.</p><p>This concludes our short summary on the current state of Universal Words and Sentence Embeddings.</p><p>The domain has seen a lot of interesting developments in the last few months together with great progresses in the ways we assess and probe the performance of these embeddings and their inherent bias/fairness (a real issue when you talk about Universal Embeddings). We didn’t have time to talk about these latest topics but you can find a few links in the references.</p><p>I hope you enjoyed this brief!</p><p>Clap 👏 a couple of times if you liked it and want us to post more of these!</p><p><strong>Some references</strong></p><ul><li>Very recently, C. Perone and co-workers published a nice and extensive comparison between ELMo, InferSent, Google Universal Sentence Encoder, p-mean, Skip-thought, etc. Here is a link to the paper: <a href="https://arxiv.org/abs/1806.06259">https://arxiv.org/abs/1806.06259</a></li><li>A nice ressource on traditional word embeddings like word2vec, GloVe and their supervised learning augmentations is the <a href="https://github.com/Hironsan/awesome-embedding-models">github repository of Hironsan</a>. More recent developments are <a href="https://fasttext.cc/">FastText</a> and <a href="http://allennlp.org/elmo">ELMo</a>.</li><li>Sentence embeddings papers: <a href="https://arxiv.org/abs/1506.06726"><em>Skip-Thoughts</em></a><em>, </em><a href="https://openreview.net/forum?id=rJvJXZb0W"><em>Quick-Thoughts</em></a><em>, </em><a href="https://arxiv.org/abs/1705.00557"><em>DiscSent</em></a><em>, </em><a href="https://arxiv.org/abs/1705.02364"><em>InferSent</em></a><em>, </em><a href="https://arxiv.org/abs/1804.00079"><em>MILA/MSR’s General Purpose Sentence Representations</em></a><em>, </em><a href="https://arxiv.org/abs/1803.11175"><em>Google’s Universal Sentence Encoder</em></a><em> &amp; </em><a href="http://arxiv.org/abs/1804.07754"><em>Google Input-Ouput Sentence learning on dialog</em></a><em>.</em></li><li>If you’re interested in the way we evaluate sentence embeddings, you should definitely check the recent work of Facebook on <a href="https://github.com/facebookresearch/SentEval">SentEval</a> and its <a href="https://github.com/facebookresearch/SentEval/tree/master/data/probing">probing tasks</a> as well as the recently published <a href="https://gluebenchmark.com/">GLUE benchmark</a> by NYU, UW and DeepMind researchers.</li></ul><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ce48ddc8fc3a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/universal-word-sentence-embeddings-ce48ddc8fc3a">📚The Current Best of Universal Word Embeddings and Sentence Embeddings</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ From zero to research — An introduction to Meta-learning]]></title>
            <link>https://medium.com/huggingface/from-zero-to-research-an-introduction-to-meta-learning-8e16e677f78a?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/8e16e677f78a</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[meta-learning]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Tue, 03 Apr 2018 15:29:12 GMT</pubDate>
            <atom:updated>2020-09-02T14:16:11.229Z</atom:updated>
            <content:encoded><![CDATA[<h3>🐣 From zero to research — An introduction to Meta-learning</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/1*d7V-bAzElJ2XjEO-T4_TnQ.gif" /></figure><p>Meta-learning is an exciting trend of research in the machine-learning community which tackles the problem of <em>learning to learn</em>.</p><p>The traditional paradigm in machine learning research is to get a huge dataset on a specific task, and train a model from scratch using this dataset. Obviously that’s very far from how humans leverage past experiences to learn very quickly a new task from only a handset of examples.</p><p>That’s because humans <em>learn to learn </em><a href="#9c51">[1]</a>.</p><p>Over the last months, I have been playing and experimenting quite a lot with meta-learning models for Natural Language Processing and will be presenting <a href="https://arxiv.org/abs/1803.10631">some of this work</a> at <a href="https://iclr.cc/">ICLR, next month in Vancouver</a> 🇨🇦 — come say hi! 👋</p><p>In this post, I will start by explaining what’s meta-learning in a very visual and intuitive way. Then, we will code a meta-learning model in PyTorch and I will share some of the lessons learned on this project.</p><h3>What’s learning in the first place?</h3><p>Let’s have a quick look at what happens when we train a simple neural net to classify images of dogs and cats. Let’s say we have a single training image of a cat together with a label indicating that this image represents a cat <a href="#3593">[2]</a>. I made a quick animation of a training step to save us a few thousand sentences.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T5ppr8fb0chwz0wC7oYPDA.gif" /><figcaption>Single step of the training process of a neural network. The neural net is trained to classify an image as representing a dog or a cat</figcaption></figure><p>The <em>backward pass</em> (“backprop”) is a key step when we train a neural net. Since the computations performed by the neural network and the loss are differentiable functions <a href="#5574">[3]</a>, we can compute the gradient that should be applied to each parameter of the neural net to reduce the difference between the label currently predicted by the neural net and the real/target label (this difference is measured by the loss function). After the backpropagation comes the <em>optimizer</em> which computes updated parameters for the model. This is where training a neural net becomes more of an art than a science as there are so many possible optimizers and optimization settings (hyper-parameters).</p><p>Let’s represent our single training step in a more compact way</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/378/1*xEnBZTYJXoTMO3gxfYt4bA.png" /></figure><p>The training image is now a 🐈 and the label indicating that the picture represents a cat is a 🔺. Large △s are our neural net with ■ parameters and gradients. The <em>loss function </em>is the L-box and the <em>optimizer </em>the O-box.</p><p>The learning process then simply consists in repeatedly applying the optimization step until we converge to good parameters for our neural net.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/1*d7V-bAzElJ2XjEO-T4_TnQ.gif" /><figcaption>3 steps of a neural net training process where the neural net (large △s) is trained to classify dogs/cats images.</figcaption></figure><h3>Let’s turn to meta-learning</h3><p>The idea of meta-learning is to <em>learn the learning process.</em></p><p>There are several ways to implement meta-learning <a href="#0f06">[4]</a> but the two I want to describe here are about learning a learning process that resemble the one we’ve just seen.</p><p>In our training process, there are two things in particular we can learn:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/272/1*Np91zhHmTqZ4d4j3rwaNvQ.png" /></figure><ul><li>the <strong>initial parameters </strong>of the neural net (blue ■) and</li><li>the <strong>parameters</strong> <strong>of the optimizer </strong>(pink ★)<strong>.</strong></li></ul><p>I will describe a combination of the two cases but each case is also very interesting on its own and can lead to simplifications, speedups and sound theoretical results <a href="#dfe6">[5]</a>.</p><p>So now, we have two modules to train:</p><ul><li>What I will call the<strong> model (M) </strong>which is our previous neural net. It can now be seen as a low-level<em> </em>network. It is sometimes called an <a href="https://arxiv.org/abs/1606.04474"><em>optimizee</em></a> or a <a href="https://openreview.net/forum?id=rJY0-Kcll"><em>learner</em></a>. The weights of the <strong>model</strong> are the ■ on the drawings.</li><li>The<strong> optimizer (O)</strong> or <strong>meta-learner </strong>is a <em>higher-level</em> <em>model</em> which is updating the weights of the lower-level network (the model). The weights of the <strong>optimizer</strong> are the ★ on the drawings.</li></ul><h4>How do we learn these meta-parameters?</h4><p>Well it turns out we can back-propagate a meta-loss gradient along the training process itself, back to the initial weights of the model and/or to the parameters of the optimizer <a href="#5d3c">[6]</a>.</p><p>We now have two, nested, training processes: the <em>meta-training process</em> of the optimizer/meta-learner in which the <em>(meta-)forward pass</em> includes <em>several</em> <em>training steps </em>of the model (with forward, backward and optimization steps as we saw previously).</p><p>Let’s take a look at the meta-training step:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AcaPiikZErVv_iFJzWekQg.gif" /><figcaption>A meta-training step (training the optimizer O) comprising with 3 steps of training the model M)</figcaption></figure><p>Here, a single step of <em>meta-training process</em> is represented horizontally. It includes two steps of <em>training process</em> of the model (vertically in the meta-forward and meta-backward boxes). The <em>training </em>process of the model is exactly the same training process that we’ve just seen.</p><p>As we can see, the input of the <em>meta-forward pass</em> is a list of examples/labels (or a list of batches) that are used successively during the model training pass.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/286/1*AOU2sGOniJvJTY8CrWoF8Q.png" /><figcaption>The input of a meta-training step is a list of examples (🐈, 🐕) with associated labels (🔺,🔻)</figcaption></figure><p>Now what <em>meta-loss</em> can we use to train the meta-learner? In the case of the model training we could simply compare the model prediction to the target label to get an error signal.</p><blockquote>In the case of the meta-learner, we would like a meta-loss that is indicative of how well the meta-learner is performing its task: training the model.</blockquote><p>One possibility is then to compute the loss of the model on some training data, the lower the loss, the better the training was. We can compute a meta-loss at the end or even just combine the losses of the model that we already compute during the training (e.g. by summing them).</p><p>We also need a <em>meta-optimizer</em> to update the weights of the optimizer. Here it starts to get very meta as we could use another meta-learner to optimize the meta-learner and so on, but in the end we will need a hand-defined optimizer like SGD or ADAM (it can’t be <a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down">turtles all the way down</a>).</p><p>There are a few important remarks regarding the implementation that we can as well discuss now:</p><ul><li><strong>Second-order derivatives</strong>: back propagating the meta-loss through the model’s gradients involves computing derivatives of derivative, i.e. second derivatives (when the green ▲ passes through the green ■ on the meta-backward pass of our last animation). We can compute that in modern frameworks like Tensorflow or PyTorch but in practice we often drop the second derivatives and only back propagate though the model weights (the yellow ■ of the meta-backward pass) to reduce the complexity.</li><li><strong>Coordinate sharing</strong>: a recent deep-learning model can have a very large number of parameters (easily around 30–200 millions in NLP). With current GPU memory, it is not possible to have such a number of parameters as separate inputs to the optimizer. What we often do is called <em>coordinate-sharing </em><a href="#43f0">[7]</a>, it means we design the optimizer for a single parameter of the model and duplicate it for all parameters (i.e. share it’s weights along the input dimension associated to the model parameters). This way the number of parameters of the meta-learner is not a function of the number of parameters of the model. When the meta-learner is a network with a memory like an RNN, we can still allow to have a separate hidden state for each model parameters to keep separate memories of the evolution of each model parameter.</li></ul><h3>Meta-learning in PyTorch 🔥</h3><p>Let’s try some code to see how this looks in practice.</p><p>So we have a model with a set of weights that we want to train and use for two tasks:</p><ul><li>during the <strong>meta-forward pass</strong>: we use our model to compute <em>gradients</em> (from the loss) that are feed as inputs to the optimizer to update the model parameters, and</li><li>during the <strong>meta-backward pass</strong>: we use our model as a <em>path for back propagating</em> the gradients of the optimizer’s parameters (computed from the meta-loss).</li></ul><p>The easiest way to do that in PyTorch is to have two duplicate modules that represent the model, one for each task. Let’s call <strong>forward model</strong> the module responsible for storing the model gradients used during the meta-forward pass and <strong>backward model</strong> the module responsible for keeping parameters as a continuous path for back propagating the optimizer gradients during the meta-backward pass.</p><p>The two modules will share their Tensors to avoid duplicating memory (tensors are the real meat in memory) but will keep separate Variables to cleanly separate the gradients of the model and the gradients used for the meta-learner.</p><h4>A simple meta-learner class in PyTorch</h4><p>Sharing Tensors in PyTorch is rather straight-forward: we just need to update the pointers in the <em>Variable</em> class to point to the same <em>Tensors</em>. One difficulty comes when our model is already a memory optimized model like an <a href="https://github.com/salesforce/awd-lstm-lm">AWD-LSTM or AWD-QRNN model</a> with shared Tensors (input and output embeddings). Then we need to be careful to keep the right pointers when we update the model parameters of the two modules.</p><p>One way to do that is to set a simple helper that will handle the task of looping through the parameters, send back all needed information to update the Parameters pointers (and not only the Tensors) and keep shared parameters synced. Here is such a function:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c142644ce3d6360ae6a8cbb377037e6/href">https://medium.com/media/2c142644ce3d6360ae6a8cbb377037e6/href</a></iframe><p>Using this function, we can plug any model and loop over the model parameters in our meta-learner in a clean way <a href="#039f">[8]</a>.</p><p>Now let’s draft a simple meta-learner class. Our optimizer is a module that will take as inputs during the forward pass, the <strong>forward model</strong> (with gradients) and the <strong>backward model</strong>, will loop over their parameters to update the backward model parameter in a way that allows meta-gradients to back propagate (by updating Parameters pointers and not only Tensors).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/32e7b3f283337dc35ca1777512dee961/href">https://medium.com/media/32e7b3f283337dc35ca1777512dee961/href</a></iframe><p>We can now train this optimizer as we saw in the first part. Here is a simple gist that illustrate the meta-training process that we have been describing:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c605cc20e0b5d0aacfd6b990601bb400/href">https://medium.com/media/c605cc20e0b5d0aacfd6b990601bb400/href</a></iframe><h4>Avoid memory blow-up — Hidden State Memorization</h4><p>Sometimes we want to learn an optimizer that can operate on very large models with several tens of millions of parameters and at the same time we would like to unroll the meta-training over a large number of steps to get good quality gradients <a href="#2373">[9]</a> like we did in <a href="https://arxiv.org/abs/1803.10631">our work</a>.</p><p>In practice, it means we want to include a long training process during the meta-forward pass, with many time-steps, and we’ll have to keep in memory the parameters (yellow ■) and gradients (green ■) data for each step that are used for the meta-backward pass.</p><p>How can we do that without blowing up our GPU’s memory?</p><p>One way is to trade some memory for computation by using <em>gradient checkpointing</em>, also called <em>hidden state memorization </em><a href="#19da">[10]</a>. In our case gradient checkpointing consists in slicing the meta-forward and meta-backward passes in segments that we compute successively.</p><p>A good introduction to gradient checkpointing is given in the nice blog post of <a href="https://medium.com/u/5511064b4364">Yaroslav Bulatov</a> of OpenAI. If you are interested in this, you should go and check it:</p><p><a href="https://medium.com/@yaroslavvb/fitting-larger-networks-into-memory-583e3c758ff9">Fitting larger networks into memory.</a></p><p>This post is already quite long so I won’t include a full gist of gradient checkpointing code. I’ll rather forward you to the nice PyTorch <a href="https://github.com/tshadley/examples/tree/master/word_language_model_bptt_hsm">implementation</a> of TSHadley and the <a href="https://github.com/pytorch/pytorch/pull/4594">current active work</a> to include gradient checkpointing natively in PyTorch.</p><h4>Other approaches in Meta-learning 🐣</h4><p>There are two other trends of research in meta-learning that I hadn’t time to cover but which are also very promising. I’ll just give you a few pointers so you can go check that for your-self now that you know the general idea:</p><ul><li><strong>Recurrent networks</strong>: We have built upon the standard training process of neural nets. An alternative is to consider the succession of task as a sequential series of input and build a recurrent model that can ingest and build a representation of this sequence for a new task. In this case we typically have a single training process with a recurrent network with memory or attention. This approach also gives good results, in particular when the embeddings are adequately designed for the task. A good example is the recent <a href="https://openreview.net/forum?id=B1DmUzWAW">SNAIL paper</a>.</li><li><strong>Reinforcement learning</strong>: The computation made by the optimizer during the meta-forward pass is very similar to the computation of a recurrent network: repeatedly apply the same parameters on a sequence of inputs (the succession of weights and gradients of the model during the learning). In practice this means we meet a usual issue with recurrent nets: the models have trouble returning to a safe path when they make errors as they are not trained to recover from training errors and the models have difficulties generalizing to longer sequences than the ones used during the meta-training. To tackle these issues, one can turn to <a href="http://bair.berkeley.edu/blog/2017/09/12/learning-to-optimize-with-rl/">reinforcement learning approaches</a> where the model learn an action policy associated to a current state of training.</li></ul><h3>Meta-learning in Natural Language Processing 🗣</h3><p>There is an interesting parallel between meta-learning and neural net models used in Natural Language Processing (NLP) like recurrent neural networks (RNN) that we have just started mentioning in the previous paragraph:</p><blockquote>A meta-learner optimizing a neural net model behaves similarly to a recurrent neural network.</blockquote><p>Like an RNN, the meta-learner ingests a series of parameters and gradients of the model during training, as an input sequence, and compute a sequential output (the series of updated model parameters) from this input sequence.</p><p>We develop this analogy in <a href="https://arxiv.org/abs/1803.10631">our paper</a> and study how a meta-learner can be used to implement a medium-term memory in a neural net language model: the meta-learner learns to encode a medium-term memory in the weights of a standard RNN like a LSTM (in addition to the way short-term memories are conventionally encoded in the hidden state of the LSTM).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/872/1*sYAdqp2x3sWcq4QIzpn2sA.png" /></figure><p>Our <em>meta-learning language model</em> has a hierarchy of memories with 3 levels, from bottom to top: a standard LSTM, a meta-learner updating the weights of the LSTM to store medium term memories and a long-term static memory.</p><p>We discovered that the meta-learning language model could be trained to encode memory of recent inputs, like the beginning of a Wikipedia article, that will be useful to predict the end of an article.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jsFquG2MF69elbtdAoJVQg.png" /><figcaption>The curves indicate how good the model is at predicting the words of a Wikipedia article given the beginning (A, …, H are successive Wikipedia articles), colored words indicate the same for single words, blue is better, red is worse. As the model reads through an article, it learns from the beginning and become better at predicting the end (for more details see <a href="https://arxiv.org/abs/1803.10631">our paper</a>).</figcaption></figure><p>Well I guess now you are ready to have a look at <a href="https://arxiv.org/abs/1803.10631">our paper</a> for more details on this story.</p><p>This concludes my introduction to Meta-Learning. Congratulation for reaching the end of this long post!</p><p>I hope you liked it!</p><p>Don’t forget to give us a few claps 👏 if you want more content like that!</p><ol><li><a href="#afeb">^</a> As such, meta-learning can be seen as a generalization of “transfer learning” and is related to the techniques for fine-tuning model on a task as well as techniques for hyper-parameters optimization. There was an interesting <a href="https://nips.cc/Conferences/2017/Schedule?showEvent=8767">workshop on meta-learning</a> at NIPS 2017 last December.</li><li><a href="#dc5a">^</a> Of course in a real training we would be using a mini-batch of examples.</li><li><a href="#e0bb">^</a> More precisely: “most of” these operations are differentiable.</li><li><a href="#d640">^</a> Good blog posts introducing the relevant literature are the BAIR posts: <a href="http://bair.berkeley.edu/blog/2017/07/18/learning-to-learn/">Learning to learn</a> by Chelsea Finn and <a href="http://bair.berkeley.edu/blog/2017/09/12/learning-to-optimize-with-rl/">Learning to Optimize with Reinforcement Learning</a> by Ke Li.</li><li><a href="#930c">^</a> Good examples of <em>learning the model initial parameters</em> are <a href="https://arxiv.org/abs/1703.03400">Model-Agnostic Meta-Learning</a> of UC Berkeley and its <a href="https://openreview.net/forum?id=BJ_UL-k0b">recent developments</a> as well as the <a href="https://blog.openai.com/reptile/">Reptile algorithm</a> of OpenAI. A good example of <em>learning the optimizer’s parameters</em> is the <a href="https://arxiv.org/abs/1606.04474">Learning to learn by gradient descent by gradient descent</a> paper of DeepMind. A paper combining the two is the work <a href="https://openreview.net/forum?id=rJY0-Kcll">Optimization as a Model for Few-Shot Learning</a> by Sachin Ravi and Hugo Larochelle. An nice and very recent overview can be found in <a href="https://arxiv.org/abs/1804.00222">Learning Unsupervised Learning Rules</a>.</li><li><a href="#d094">^</a> Similarly to the way we back propagate through time in an unrolled recurrent network.</li><li><a href="#725d">^</a> Initially described in DeepMind’s <a href="https://arxiv.org/abs/1606.04474">Learning to learn by gradient descent by gradient descent</a> paper.</li><li><a href="#4e23">^</a> We are using coordinate-sharing in our meta-learner as mentioned earlier. In practice, it means we simply iterate over the model parameters and apply our optimizer broadcasted on each parameters (no need to flatten and gather parameters like in L-BFGS for instance).</li><li><a href="#d029">^</a> There is a surprising under-statement of how important back-propagating over very long sequence can be to get good results. The recent paper <a href="https://arxiv.org/abs/1803.08240">An Analysis of Neural Language Modeling at Multiple Scales</a> from Salesforce research is a good pointer in that direction.</li><li><a href="#6c6f">^</a> Gradient checkpointing is described for example in <a href="https://arxiv.org/abs/1606.03401">Memory-Efficient Backpropagation Through Time</a> and the nice <a href="https://medium.com/@yaroslavvb/fitting-larger-networks-into-memory-583e3c758ff9">blog post</a> of Yaroslav Bulatov.</li></ol><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8e16e677f78a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/from-zero-to-research-an-introduction-to-meta-learning-8e16e677f78a">🐣 From zero to research — An introduction to Meta-learning</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[✨How to train a neural coreference model— Neuralcoref 2]]></title>
            <link>https://medium.com/huggingface/how-to-train-a-neural-coreference-model-neuralcoref-2-7bb30c1abdfe?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/7bb30c1abdfe</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[chatbots]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Fri, 23 Mar 2018 11:40:04 GMT</pubDate>
            <atom:updated>2018-06-16T23:01:03.146Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong>Links: </strong><a href="https://huggingface.co/coref/">Online demo</a> <strong>Github repo</strong>: <a href="https://github.com/huggingface/neuralcoref">https://github.com/huggingface/neuralcoref</a> and our <a href="https://medium.com/huggingface/state-of-the-art-neural-coreference-resolution-for-chatbots-3302365dcf30">previous Medium post</a>.</blockquote><p>The last months have been quite intense at HuggingFace 🤗 with crazy usage growth 🚀 and everybody hard at work to keep up with it 🏇, but we finally managed to free some time and update our open-source library ✨<a href="https://github.com/huggingface/neuralcoref">Neuralcoref</a> while publishing the training code at the same time.</p><p>Since we launched v1 last summer, more than ten million 💯 coreferences have been resolved on Hugging Face. Also, we are stoked that our library is now used in production by a few other companies and some really smart researchers, and our work was featured in the latest session of <a href="http://web.stanford.edu/class/cs224n/syllabus.html">Stanford’s NLP course</a>! 💪</p><p>The training code has been updated to work with the latest releases of both <a href="http://pytorch.org/">PyTorch (v0.3)</a> and <a href="https://spacy.io/usage/">spaCy v2.0</a> while the pre-trained model only depends on Numpy and spaCy v2.0.</p><blockquote>This release’s major milestone: You will now be able to train ✨ Neuralcoref on your own dataset — e.g., another language than English! — provided you have an annotated dataset.</blockquote><p>We have added a <a href="https://github.com/huggingface/neuralcoref/blob/master/readme.md#re-train-the-model--extend-to-another-language">special section</a> to the readme about training on another language, as well as <a href="https://github.com/huggingface/neuralcoref/blob/master/training.md">detailed instructions</a> on how to get, process and train the model on the English OntoNotes 5.0 dataset.</p><p>As before, ✨Neuralcoref is designed to strike a good balance between accuracy and speed/simplicity, using a rule-based mention detection module, a constrained number of features and a simple feed-forward neural network that can be implemented easily in Numpy.</p><p>In the rest of this blog post, I will describe how the coreference resolution system works and how to train it. Coreference resolution is a rather complicated NLP task 🐉 so bare with me, you won’t regret it!</p><h3>Let’s have a quick look at a (public) dataset 📚</h3><p>A good quality public dataset you can use to train the model on English is the CoNLL 2012 dataset. It is one of the largest freely available dataset with coreference annotations, having about 1.5M+ tokens spanning many fields like newswire, broadcast and telephone conversations as well as web data (blogs, newsgroups …).</p><p>In the repo we explain <a href="https://github.com/huggingface/neuralcoref/blob/master/training.md#get-the-data">how to download and prepare this dataset</a> if you want to use it. Once you are done with that, a typical CoNLL file will look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1017/1*kGyDWKCYp91HKSEaMWtIMA.png" /><figcaption>Extract of CoNLL 2012 dataset file “cctv_0005.v4_gold_conll”</figcaption></figure><p>This extract contains 2 sentences: “<em>Yes, I noticed that many friends, around me received it</em>” and “<em>It seems that almost everyone received this SMS</em>”</p><p>The sentences are tokenized and annotated with the tokens in column 4 and a large number of annotations: POS tags (col 5), parse tree (col 6), verbs lemma (col 7), speaker (col 10) and, what we are especially interested in, <strong>co-reference labels</strong> in the last column (labels 12, 119 on lines 5, 12, 14, 23 &amp; 24). In this extract, “<em>I</em>” is annotated as co-referring with “<em>me</em>” (they have the same entity label 12) and “<em>it</em>” as co-referring with “<em>this SMS</em>” (label 119).</p><p>You can also notice that <em>only the mentions that have at least one co-reference are labelled in the dataset </em>(i.e. at least a pair of mentions referring to the same entity)<em>.</em> Single mentions of an entity, with no other mention referring to the same entity, are not labelled.</p><p>This is somewhat annoying as it means we cannot fully evaluate (and easily train) the mention identification module of our coreference system (with precision, recall and F1 metrics). However, we can still have a look at the recall of co-referring mentions<em> </em><a href="https://github.com/huggingface/neuralcoref/blob/master/neuralcoref/train/training.md#some-details-on-the-training">as we mention in the github repo</a>.</p><h3>The coreference module workflow 🌊</h3><p>The first step of our process is to extract potential mentions. ✨Neuralcoref uses a <em>rule-based mention-extraction </em>function for this operation and get, in our two-sentences example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2NGtO-DQ2DPTQ5rD8-Zp9A.png" /></figure><p>Depending on the selectivity of the rule-based mention extractor and the parse-tree for our sentence, it may also capture a few bigger mentions like “<em>many friends, around me received it</em>” or “<em>almost everyone received this SMS</em>”. Let’s keep only the short mentions here for the sake of simplicity.</p><p>Each mention can co-refer with a various number of previous mentions. We can gather all the mentions in a mention-pairs table to highlight the co-referring pairs (in the table <strong>∅</strong> means that a mention doesn’t corefer with any previous mention).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-jpy11OAViGz2aYZais3Pg.png" /><figcaption>A table of coreferring mention-pairs for our two-sentences example (positive labels in red)</figcaption></figure><p>Note that, their may be more than one co-referring antecedent for a given mention (i.e. several red box on a single line on our table), forming clusters of co-referring mentions (co-referrence resolution is a clustering task).</p><p>We can already see some of the issues that will arise when training a model on such data, namely that (i) each mention will have a varying number of potential antecedents, complicating the batching (the size of our mention-pairs vector will span all the range from 0 to <em>N</em> the total number of mentions in a document), and (ii) the table of mention-pairs will typically scale as <em>cN</em> where <em>c</em> in the average number of mentions in each document of the dataset, and this can become quite large.</p><blockquote>In practice, our rule-based mention-extractor identifies about 1 million potential mentions on the CoNLL 2012 training set resulting in about 130 millions mention-pairs to train our model on.</blockquote><p>Once we have identified potential mentions and labels for them (the red box in our table), we can extract a set of features for each mention and each pair of mentions. Let’s see the features we extract:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*THl-UXeSLePo7bgE_HYkAg.png" /><figcaption>Extracted features for mentions and pairs of mentions. Span vectors are pre-computed average of word vectors.</figcaption></figure><p>It may seem like we need a lot of features but one of the advantage of ✨Neuralcoref is actually its reduced number of features — some coreference resolution systems uses up to +120 features! Another advantage is that most of these features don’t depend on the parser or additional databases (like word gender/number) and they are easy/fast to compute.</p><p>Practically, the features are a bunch of <em>real-valued vectors</em> (e.g. the span vectors which are average over word vectors and won’t be trained), <em>integers </em>(e.g. word indices in a dictionary, categorial indices), and <em>boolean</em> (e.g. “nested?” indicates whether a pair mention is contained in the other).</p><p>The mix of real values, integer and boolean features can give rise to large numpy arrays if we simply gather them in a single array (integer and boolean will be converted to floats). So we store them in separate arrays and build the features arrays on-time while we feed the neural net (see the DataLoader code in <a href="https://github.com/huggingface/neuralcoref/blob/master/neuralcoref/dataset.py">dataset.py</a>).</p><blockquote>We are done with the pre-processing steps. These steps are implemented in <a href="https://github.com/huggingface/neuralcoref/blob/master/neuralcoref/conllparser.py">conllparser.py</a> and <a href="https://github.com/huggingface/neuralcoref/blob/master/neuralcoref/document.py">document.py</a> in the code ✨Neuralcoref.</blockquote><p>Now, let’s use these features to train our model!</p><h3>A quick look at the neural net model 🔮</h3><p>As always, the neural net model is a pleasure to write in pyTorch so I copy it here in full (I just removed weights initialization/loading functions).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/74cdda70ddc8cc1b83b5590a280f9b44/href">https://medium.com/media/74cdda70ddc8cc1b83b5590a280f9b44/href</a></iframe><p>The model comprises a common embedding layer, <em>self.embed, </em>that<em> </em>transforms <em>words indices</em> in word vectors and feed two parallel feed-forward networks:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UwhXKcJ3O5vVI5cpzrhC-Q.png" /></figure><ul><li><em>self.single</em> takes as inputs <em>word vectors</em>, <em>spans</em> and <em>additional features </em>(see above) of a mention and compute the score that it has no other co-referring mention (score of ∅ as label),</li><li><em>self.pairs</em> takes as inputs <em>word vectors</em>, <em>spans</em> and <em>features</em> of a mention and an antecedent, together with <em>pair features,</em> and compute the score that the pair of mentions are co-referring.</li></ul><p>So, how do we train this beauty?</p><h3>Training the coreference neural net 🌀</h3><p>First, a word about mini-batching. We talked about the problem of having a varying number of pairs for each mention. One way to use mini-batching in such conditions is to pack mini-batches as follows:</p><ul><li>Sort the mentions in the training set by their number of potential antecedents (the length of each line in our pair table),</li><li>Define a maximum number of pairs P for a mini-batch, and</li><li>Slice the sorted training set in mini-batches of size ≅P, padding the mention-pairs in each mini-batch to the maximum number of pairs in the mini-batch (the longest line, i.e. the last one in our sorted dataset).</li></ul><p>In ✨Neuralcoref, this is done by the <a href="https://github.com/huggingface/neuralcoref/blob/master/neuralcoref/dataset.py"><em>dataset.py</em></a> module which load and construct a Dataset and a Dataloader with such padded mini-batches.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3dVcnGQq_iiLmltklFp8KQ.png" /><figcaption>Example of Neuralcoref evaluation metric during training</figcaption></figure><p>Once our mini-batches are ready, we can start training.</p><p>The training goes through three successive training phases: <em>All pairs</em>, <em>Top pairs</em> and <em>Ranking</em>.</p><p>We set up a very simple scheduling scheme to keep the training fast: each time our metric on the dev set stops increasing, we move on to the next stage.</p><p>The first two phases uses probabilistic loss (cross-entropy) while the last phase use a slack-rescaled ranking loss. More precisely, our losses for each training phase look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6ZfY_mW7tqs_X1K5Y_jVqg.png" /><figcaption>𝓣(m) is the set of true antecedents of a mention m, 𝓕(m) the false antecedents and 𝓐(m) all antecedents (including <strong>∅)</strong>.</figcaption></figure><p>The <em>All pairs loss</em> is a standard cross-entropy loss on the full set of mention-pairs. The <em>Top pairs</em> loss is also a cross-entropy but is restricted to the (currently) top-scoring true and false antecedents of a mention. Finally, the <em>Ranking loss</em> is a max-margin loss with a slack rescaled cost Δ.</p><p>To get more information, you should check the very nice work published in 2016 by Kevin Clark and Christopher Manning (see <a href="http://cs.stanford.edu/people/kevclark/resources/clark-manning-emnlp2016-deep.pdf">“Deep Reinforcement Learning for Mention-Ranking Coreference Models”</a> by Kevin Clark and Christopher D. Manning, EMNLP 2016, <a href="http://cs.stanford.edu/people/kevclark/resources/clark-manning-acl16-improving.pdf">Improving Coreference Resolution by Learning Entity-Level Distributed Representations</a> by Kevin Clark and Christopher D. Manning, ACL 2016, and the references therein), which our model is an adaptation of.</p><p>The full details and more are given in these publications which you should definitely read if you are interested in this model.</p><blockquote>This training is implemented in <a href="https://github.com/huggingface/neuralcoref/blob/master/neuralcoref/learn.py">learn.py</a> in the code ✨Neuralcoref.</blockquote><p>So I hope this gives you some good intuitions on how this rather uncommon beast works.</p><p>Most important, we setup a really nice and fast demo, so don’t hesitate to <a href="https://huggingface.co/coref/">try the coreference system for yourself</a>!</p><p>And don’t hesitate to <a href="https://github.com/huggingface/neuralcoref">fork the code</a> and use it in your projects. Hope you liked it and let us know how you use it 🚀</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7bb30c1abdfe" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/how-to-train-a-neural-coreference-model-neuralcoref-2-7bb30c1abdfe">✨How to train a neural coreference model— Neuralcoref 2</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding emotions — from Keras to pyTorch]]></title>
            <link>https://medium.com/huggingface/understanding-emotions-from-keras-to-pytorch-3ccb61d5a983?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/3ccb61d5a983</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[keras]]></category>
            <category><![CDATA[nlp]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Wed, 04 Oct 2017 15:06:28 GMT</pubDate>
            <atom:updated>2020-09-02T14:18:49.441Z</atom:updated>
            <content:encoded><![CDATA[<h4>Introducing torchMoji, a PyTorch implementation of DeepMoji</h4><p>Detecting emotions, sentiments &amp; sarcasm is a critical element of our natural language understanding pipeline at HuggingFace 🤗. Recently, we have switched to <a href="https://github.com/huggingface/torchMoji">an integrated system based on a NLP model from the MIT Media Lab</a>.</p><blockquote>Update: We’ve open sourced it! <a href="https://github.com/huggingface/torchMoji"><strong>Repo on GitHub</strong></a></blockquote><p>The model was initially designed in TensorFlow/Theano/Keras, and we ported it to pyTorch. Compared to Keras, pyTorch gives us more freedom to develop and test custom neural network modules and uses an easy to read numpy-style code. In this post, I will detail several interesting points that arose during the reimplementation:</p><ul><li>how to <strong>make a custom pyTorch LSTM </strong>with custom activation functions,</li><li>how the<strong> PackedSequence object works</strong> and is built,</li><li>how to <strong>convert an attention layer </strong>from Keras to pyTorch,</li><li>how to load your data in pyTorch: <strong>DataSets and</strong> <strong>smart Batching,</strong></li><li>how to reproduce Keras<strong> weights initialization</strong> in pyTorch.</li></ul><p>First, let’s look at the torchMoji/DeepMoji model. It is a fairly standard and robust NLP neural net with two bi-LSTM layers followed by an attention layer and a classifier:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/505/1*coSzKXLqaspA8f3B7rlWpg.png" /><figcaption>torchMoji/DeepMoji model</figcaption></figure><h3>How to build a custom pyTorch LSTM module</h3><p>A very nice feature of DeepMoji is that Bjarke Felbo and co-workers were able to train the model on a massive dataset of 1.6 billion tweets. The pre-trained model thus carries a very rich representation of the emotions and sentiments in the training set and we would like to use the pre-trained weights.</p><p>However, the model was trained with Theano/Keras’ default activation for the recurrent kernel of the LSTM: a <a href="https://www.tensorflow.org/api_docs/python/tf/contrib/keras/backend/hard_sigmoid">hard sigmoid</a>, while pyTorch is tightly modeled around <a href="https://devblogs.nvidia.com/parallelforall/optimizing-recurrent-neural-networks-cudnn-5/">NVIDIA’s cuDNN</a> library for efficient GPU acceleration which <a href="http://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html#cudnnRNNMode_t">natively supports only LSTM</a> with standard sigmoid recurrent activations:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vDnpd5RrhC9fdYgycHfpDw.png" /><figcaption>Keras default LSTM VS pyTorch default LSTM</figcaption></figure><p>I thus wrote a custom LSTM layer with hard sigmoid recurrent activation functions:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5a92520a29c0877015c2022048b830aa/href">https://medium.com/media/5a92520a29c0877015c2022048b830aa/href</a></iframe><p>This LSTM cell has to be integrated in a full module that can make use of all the pyTorch facilities (variable number of layers and directions, inputs as PackedSequences). This integration is quite long so I’ll refer you directly to <a href="https://github.com/huggingface/torchMoji/blob/master/torchmoji/lstm.py">the relevant file</a> of the repo.</p><p>Writing a custom LSTM cell also means that we lose some of the easy and fast GPU capabilities of cuDNN. As we mainly want to use the pre-trained model in production on a CPU and maybe fine-tune a small classifier on top of it, this is not a problem for us, but it means that the model should be further adapted to make use of the GPU more efficiently if you would like to re-train it from scratch.</p><h3>Attention layer: side-by-side Keras &amp; pyTorch</h3><p>The attention layer of our model is an interesting module where we can do a direct one-to-one comparison between the Keras and the pyTorch code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d221231a918f17069b6b8e4cc359d8e8/href">https://medium.com/media/d221231a918f17069b6b8e4cc359d8e8/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f2bc9526a04d2f61861439fe2c33b23e/href">https://medium.com/media/f2bc9526a04d2f61861439fe2c33b23e/href</a></iframe><p>As you can see, the general algorithm is roughly identical but most of the lines in the pyTorch implementation are comments while a Keras implementation requires you to write several additional functions and reshaping calls.</p><blockquote>When it comes to writing and debugging custom modules and layers, pyTorch is a faster option while Keras is clearly the fastest track when you need to quickly train and test a model built from standard layers.</blockquote><h3>How the PackedSequence object works</h3><p>Keras has a nice masking feature to deal with variable lengths sequences. How do we do that in pyTorch? We use <a href="http://pytorch.org/docs/master/nn.html#packedsequence">PackedSequences</a>! PackedSequence is not very detailed in the pyTorch doc so I will spend some time describing them in greater details.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/426/1*7qaPfwTbd6RBUWC5QNHxNw.png" /><figcaption>A typical NLP batch with five sequences and a total of 18 tokens</figcaption></figure><p>Let’s say we have a batch of sequences with variable lengths (as it is often the case in NLP application). To parallelize the computation of such a batch on the GPU we would like:</p><ul><li>to process the sequences in parallel as much as possible given that the LSTM hidden state need to depend from the previous time step of each sequence, and</li><li>to stop the computation of each sequence at the right time step (the end of each sequence).</li></ul><p>This can be done by using the <strong>PackedSequence</strong> pyTorch class as follow. We first sort the sequences by decreasing lengths and gather them in a (padded) tensor. Then we call the <a href="http://pytorch.org/docs/master/nn.html#pack-padded-sequence">pack_padded_sequence function</a> on the padded Tensor and the list of sequences lengths:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c1d647caa378911e9f222180f625661c/href">https://medium.com/media/c1d647caa378911e9f222180f625661c/href</a></iframe><p>The <strong>PackedSequence</strong> object comprises:</p><ul><li>a `<strong>data</strong>` object: a torch.Variable of shape (total # of tokens, dims of each token), in our simple case with five sequences of token (represented by integers): (18, 1)</li><li>a `<strong>batch_sizes</strong>` object: a list of the number of token per time-step, in our case: [5, 4, 3, 3, 2, 1]</li></ul><p>How the <a href="http://pytorch.org/docs/master/nn.html#pack-padded-sequence">pack_padded_sequence</a> function constructs this object is simple:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/668/1*WF93EuCOGU834ENSnnofZg.png" /><figcaption>How to construct a <strong>PackedSequence</strong> object (with batch_first=True)</figcaption></figure><p>One nice properties of the PackedSequence object is that we can perform many operations<strong> directly on the PackedSequence data variable</strong> without having to unpack the sequence (which is a slow sequential operation). In particular, we can perform any operation which is local in the tokens (i.e. insensitive to the tokens order/context). Of course, we can also apply any pyTorch Modules that accept PackedSequence inputs.</p><p>In our NLP model, we can, for example, <a href="https://github.com/huggingface/torchMoji/blob/master/torchmoji/model_def.py#L226">concatenate the outputs of the two LSTM modules without unpacking</a> the PackedSequence object and apply a LSTM on this object. We could also perform some operations of our attention layer without unpacking (like vector product, exponentiation).</p><p>Another thing to note is to be careful about the ordering of the label as you have now sorted the input sentence by length, you should sort the labels as well, using the permutation indices you got when you sorted the input:</p><p>labels = labels[perm_index]</p><h3>Smart data loading in pyTorch: DataSets &amp; Batches</h3><p>In Keras, data loading and batching are often hidden in the <a href="https://keras.io/models/sequential/#sequential-model-methods">fit_generator function</a>. Again, this is nice when you want to quickly test a model but it also means we don’t fully control what is happening in this –rather critical– part of the model.</p><p>In pyTorch, we will combine three nice classes to do this task:</p><ul><li>a DataSet to hold, pre-process and index the dataset,</li><li>a BatchSampler to control how the samples are gathered in batches, and</li><li>a DataLoader that will take care of feeding these batches to our model.</li></ul><p>Our DataSet class is very simple:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3679f8e013ac480840b5d79cbd0675ea/href">https://medium.com/media/3679f8e013ac480840b5d79cbd0675ea/href</a></iframe><p>Our BatchSampler is more interesting.</p><p>We have several small NLP datasets that we would like to use to fine-tune our model on emotion, sentiment and sarcasm detection. These datasets have <strong>varying lengths</strong> and sometimes <strong>unbalanced classes</strong> so we would like to design a batch sampler that could:</p><ul><li>gather batches in epochs of pre-defined number of samples so our training process can be independent of the batches lengths, and</li><li>be able to sample in a balanced way from the unbalanced datasets.</li></ul><p>In pyTorch, a BatchSampler is a class on which you can iterate to yield batches, each batch for the BatchSampler comprises a list with the indices of the samples to pick in the DataSet.</p><p>We can thus define a BatchSampler that will be initialized using a dataset class label vector to construct a list of batches fulfilling our needs:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b877c3bf87a2795b556f7f57bcc0f8ad/href">https://medium.com/media/b877c3bf87a2795b556f7f57bcc0f8ad/href</a></iframe><h3>From Keras to pyTorch: don’t forget the initialization</h3><p>One last thing you have to be careful when porting Keras/Tensorflow/Theano code in pyTorch is the initialization of the weights.</p><p>Another powerful feature of Keras in term of speed of development is that the layers come with default initialization that makes a lot of sense.</p><p>On the contrary, pyTorch does not initialize the weights but let you free to do as you please. To get consistent results when fine tuning the weights we thus copy the default Keras initialization of the weights as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/488c761dfad6f22a736ced56d8581660/href">https://medium.com/media/488c761dfad6f22a736ced56d8581660/href</a></iframe><h3>Conclusion</h3><p>Keras and pyTorch have differing philosophies and goals that we can feel when we compare the two frameworks directly on a single model.</p><p>In my opinion and experience :</p><ul><li>Keras is great for quickly testing various ways to combine standard neural network blocks on a given task,</li><li>pyTorch is great to quickly develop and test a custom neural network module with a great freedom and an easy to read numpy-style code.</li></ul><p>I took care to add a lot of comments in <a href="https://github.com/huggingface/torchMoji">my pyTorch code</a> and the original <a href="https://github.com/bfelbo/DeepMoji">Keras implementation</a> of DeepMoji is also well commented so don’t hesitate to walk through them, use them, and modify them.</p><p>Also, clap if you want us to share more of these! 🤗🤗🤗</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3ccb61d5a983" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/understanding-emotions-from-keras-to-pytorch-3ccb61d5a983">Understanding emotions — from Keras to pyTorch</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[State-of-the-art neural coreference resolution for chatbots]]></title>
            <link>https://medium.com/huggingface/state-of-the-art-neural-coreference-resolution-for-chatbots-3302365dcf30?source=rss-167597463903------2</link>
            <guid isPermaLink="false">https://medium.com/p/3302365dcf30</guid>
            <category><![CDATA[chatbots]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[natural-language]]></category>
            <dc:creator><![CDATA[Thomas Wolf]]></dc:creator>
            <pubDate>Fri, 07 Jul 2017 14:48:44 GMT</pubDate>
            <atom:updated>2020-09-02T14:16:54.485Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><strong>TL;DR, Links: </strong><a href="https://huggingface.co/coref/">Online demo</a> at <a href="https://huggingface.co/coref">https://huggingface.co/coref</a>, <a href="https://github.com/huggingface/neuralcoref">Github repo for Neuralcoref</a>: <a href="https://github.com/huggingface/neuralcoref">https://github.com/huggingface/neuralcoref</a></blockquote><p>At Hugging Face 🤗 we work on the most amazing and challenging subset of natural language: millennial language. Full of uncertainties 🤔, implicit references 👾, emojis 😭, jokes 😂 and constantly creating novel expressions…</p><p>To navigate these stormy waters 🚤, we have developed a number of specific NLP tools based on the latest research in the field. One of these tools is a <a href="https://huggingface.co/coref/?text=My%20sister%20has%20a%20friend%20called%20John.%20Really%2C%20tell%20me%20more%20about%20him%20%3F%20She%20think%20he%20is%20so%20funny%20%F0%9F%A4%A3%20!"><em>coreference system</em></a> we use to keep track of short term references already at the front end of our AI 🤗 brains.</p><p>We couldn’t find a tool we could easily integrate in our conversational agent so we decided to develop it internally and <a href="https://github.com/huggingface/neuralcoref">open-source it</a>.</p><p>You can also <a href="https://huggingface.co/coref/">try the coreference system for yourself</a> on the demo we setup!</p><blockquote>But, what is coreference?</blockquote><p>Let’s have a look at <strong>Bob</strong> 👦 who is talking with his AI friend <strong>Alice </strong>🤖</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qU9OSJPiWqzEG_yH3b1MlA.png" /></figure><p>There are several implicit references in the last message from Bob 👦</p><ul><li>“<strong>she</strong>” refers to the same entity as “<strong>My sister</strong>”: <em>Bob’s sister</em><strong>.</strong></li><li>“<strong>he</strong>” refers to the same entity as “<strong>a friend called John</strong>”: <em>Bob’s sister’s friend.</em></li></ul><p>The process of linking together mentions that relates to real world entities is called <em>coreference resolution </em><a href="#96fa">[1]</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YestCzcJ4zvlioClwmVNsQ.png" /><figcaption>Hugging face coreference system in operation. <a href="https://huggingface.co/coref/?text=My%20sister%20has%20a%20friend%20called%20John.%20Really%2C%20tell%20me%20more%20about%20him%20%3F%20She%20think%20he%20is%20so%20funny%20%F0%9F%A4%A3%20!">Try it for yourself</a>!</figcaption></figure><p>Humans naturally associate these references together — but for an AI 🤖 brain, it is much more difficult! And when we say it is hard, we mean it,</p><blockquote>Coreference resolution is the basis of the <a href="https://en.wikipedia.org/wiki/Winograd_Schema_Challenge">Winograd Schema Challenge</a>, a test of machine intelligence … build to <a href="https://motherboard.vice.com/en_us/article/wnjxxm/this-alternative-to-the-turing-test-aims-to-find-common-sense-in-ai">defeat the AIs who’ve beaten the Turing Test</a>! 🔥</blockquote><blockquote>So how do we solve this problem without creating a <a href="https://en.wikipedia.org/wiki/AI-complete">strong-AI</a>?</blockquote><p>Coreference is a rather old NLP research topic <a href="#96fa">[1]</a>. It<a href="#d0ca">*</a> has seen a revival of interest in the past two years as several research groups <a href="#06e5">[2]</a> applied cutting-edge deep-learning and reinforcement-learning techniques to it. It was published earlier this year that coreference resolution may be instrumental in improving the performances of NLP neural architectures like RNN and LSTM (see “<a href="https://arxiv.org/abs/1703.02620">Linguistic Knowledge as Memory for Recurrent Neural Networks</a>” by B. Dhingra, Z. Yang, W. W. Cohen, and R. Salakhutdinov).</p><p>A typical coreference resolution algorithm goes like this:</p><ul><li>We extract a series of <em>mentions –</em>words that are potentially referring to real world entities–</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8XT3PKiddmJ-o3lZ5rQrGQ.png" /><figcaption>In our previous dialogue, we can identify six mentions</figcaption></figure><ul><li>For each mentions and each pair of mentions<em>, </em>we compute a set of <em>features</em> (more on that soon).</li><li>Then, we find the most likely antecedent for each mention (if there is one) based on this set of features. This last step is called <em>pairwise ranking </em><a href="#7f43">[3]</a>.</li></ul><p>Traditionally the set of <em>features</em> was hand-engineered from linguistic features and it could be huge. Some high quality systems use 120+ features <a href="#ce28">[4]</a>!</p><p>Here comes the nice thing about modern NLP techniques like word vectors and neural nets. They allow us to automatically learn a lot of these hand-engineered features and reduce the set of hand-designed features by an almost an order of magnitude while keeping a good or even better accuracy.</p><p>Let’s see that in action on a simple example<em>.</em></p><p><em>Her</em> is a feminine pronoun and should have a higher probability to refer to <em>my sister </em>👧<em> </em>than to <em>my brother </em>👦.</p><p>We could encode that by hand, listing the gender and other properties of every word in our vocabulary — but we can also assign a <em>vector</em> to every word the vocabulary and let our model learn vectors that are adapted for our task on a training dataset.</p><p>So we trained our word embeddings on <a href="https://www.gabormelli.com/RKB/OntoNotes_Corpus">a large coreference annotated dataset</a> without supplying any information regarding word gender. And here is what we got.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WFvjhR8KbtSZ2g99tnvlgw.png" /><figcaption>Left: <strong>Initial</strong> word embeddings (PCA of pre-trained word2vec) — Right: <strong><em>trained</em></strong> word embeddings (PCA)</figcaption></figure><p>On the left, the original word2vec words vectors don’t specifically care about gender association <a href="#0bcf">[5]</a>. On the right side, after training, our word vectors shows feminine and masculine nouns nicely separated along the principal components of the vectors even though we didn’t supply any information regarding word gender (<em>gender</em> has become a direction of main variance of our trained word vectors).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/878/1*Cv82f8nm38u6326GZIiOug.png" /></figure><p>Obviously the quality of our trained embeddings depends a lot on the training dataset. Our embeddings are trained on the <a href="https://www.gabormelli.com/RKB/OntoNotes_Corpus">OntoNotes 5.0 dataset</a> (the largest coreference annotated corpus).</p><p>But clearly this dataset has its flaws. In particular, as often in NLP datasets, it is build mainly from news and web articles hence with a more formal language than the usual chatbot user.</p><p>We can see on our PCA projection that pairs of words like <em>dad/mum or brother/sister </em>seem less clearly separated than a pair of more formal words like <em>woman</em>/<em>man</em> (along the first components of the PCA).</p><p>And, in fact, you can construct sentences for which our coreference system will <a href="https://huggingface.co/coref/?text=My%20father%20and%20my%20mother%20are%20working%20hard.%20She%20is%20always%20nice%20but%20he%20is%20sometimes%20rude.">work nicely on some pairs</a> of mention but <a href="https://huggingface.co/coref/?text=My%20dad%20and%20my%20mum%20are%20working%20hard.%20She%20is%20always%20nice%20but%20he%20is%20sometimes%20rude.">fail on another pair of mentions which are less formal</a>. We give some hacks to circumvent that in a minute but the cleanest way is of course to gather a labeled data set that is more representative of your production data (like the over 10M messages already exchanged by our users with their Huggingface AIs).</p><p>So is that all? If we can learn every linguistic features relevant to our words, then how can coreference resolution be more difficult than the Turing Test?</p><p>Well, in the <a href="https://en.wikipedia.org/wiki/Winograd_Schema_Challenge">Winograd Schema Challenge</a>, your AI has to solve questions like:</p><blockquote>The trophy would not fit in the brown suitcase because <strong>it</strong> was too big. What was too big? the trophy or the suitcase?</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P_ZwcBaMoRS45onlFgta2A.png" /><figcaption>Terry Winograd (<a href="https://www.flickr.com/photos/lisap/379993291/in/photolist-4F45Jv-d83EV-aR1zYF-aR1A8c-aR1zWP-aR1AaP-b2hQb-aR1A4t-7Zq8vn-h4vGqR-7LsEwP-ekwg6y-ekquYR-9Hndsi-ekwg2N-t8i5X-t8ioz-t8hUJ-t8ix7-avu4tb-4vqpBz-zzyLt-68tjoE-68uE8x-2f7Jam">Flickr/Lisa Padilla</a>)</figcaption></figure><p>Our carefully gender-tuned-word embeddings will not be enough to help us solve these questions because we actually have to <em>take the context of our mentions into account </em>and maybe even <em>external common knowledge</em>!</p><p>In our model, we add some simple context information by averaging word vectors surrounding each mention but there are many way you can add some context information <a href="#06e5">[2]</a>. The good thing is, because we’re building an entertaining AI, we don’t need 100% accuracy for it to work for users. And a high accuracy can be very hard to reach: the best team competing for the <a href="https://en.wikipedia.org/wiki/Winograd_Schema_Challenge">Winograd Schema Challenge</a> last year reached only 58% of success!</p><p>Our model then goes very roughly as follow: we take word embeddings for several words inside and around each mention, average them if needed, add some simple integer features (length of the mention, speaker information, location of the mentions…) to obtain a features representation for each mention and it surroundings. Then we plug these representations into two neural nets. A first neural net gives us a score for each pair of a mention and a possible antecedent while a second neural net gives us a score for a mention having no antecedent (sometimes a mention is the first reference to an entity in a text). We can then simply compare all these scores together and take the highest score to determine whether a mention has an antecedent and which one it should be.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xcs9SyDOtUmKIvkTvHt9bw.png" /><figcaption>Rough sketch of the coreference scoring model.</figcaption></figure><p>The neural model is trained on a non-probabilistic slack-rescaled max-margin objective. It means that the system computes a score for each pair of mentions and a score for each individual mention but these scores are not probabilities, just scores in arbitrary unit (the highest the better).</p><p>This scoring system is an adaptation of the very nice work published last year by Kevin Clark and Christopher Manning (see <a href="http://cs.stanford.edu/people/kevclark/resources/clark-manning-emnlp2016-deep.pdf">“Deep Reinforcement Learning for Mention-Ranking Coreference Models”</a> by Kevin Clark and Christopher D. Manning, EMNLP 2016, <a href="http://cs.stanford.edu/people/kevclark/resources/clark-manning-acl16-improving.pdf">Improving Coreference Resolution by Learning Entity-Level Distributed Representations</a> by Kevin Clark and Christopher D. Manning, ACL 2016, and the references therein). The full details and even more are given in theses publications which you should definitely read if you are interested in this model.</p><p>The scoring model of Clark and Manning is <a href="https://github.com/clarkkev/deep-coref">open-source</a> and a full implementation (with mention detection and features computation) has been integrated in <a href="https://stanfordnlp.github.io/CoreNLP/">Stanford’s CoreNLP</a>.</p><p>We initially used this implementation in our system. However, we found that several important features were missing for our needs:</p><ol><li>Easy integration in our current NLP processing pipeline. CoreNLP is an extensive tool but it is also a large monolithic java bloc that is hard to integrate in a high throughput distributed system like ours.</li><li>Capability to evolve with our users language and take into account user-specific informations. New word vectors have to be dynamically learned to use the fact that “Kendall Jenner” is a woman and a model even though she is not mentioned in our training dataset.</li><li>Taking care of the speakers in a conversation. The quality of the coreference resolution depend for a large part of the speaker information associated to each mention.</li></ol><p>Here is how we solved that:</p><ol><li>Our current pipeline is based on a set of deep-learning python tools and the high speed parsing is done by <a href="https://spacy.io/">spaCy</a>. We are big fans of spaCy ultra-fast parser and of the work of Matthew and Ines at <a href="https://explosion.ai/">Explosion.ai</a>. The coreference system with mentions detection, features extraction and neural net computation is thus implemented on top of spaCy and Numpy (in the future we could easily switch to <a href="https://github.com/explosion/thinc">Thinc</a> when Thinc’s API is stabilized).</li><li>We make use of <a href="https://arxiv.org/abs/1706.00286">recent work on word embeddings</a> to compute embeddings for unknown words on the fly from definitions or information that you can provide (it’s very simple in fact: you can compute a word embedding for “Kendall Jenner” simply by averaging the vectors for “woman” and “model” for example).</li><li>We input and take care of the various speakers in the conversation when computing the features and resolving the coreferences.</li></ol><p>You can <a href="https://huggingface.co/coref/">try the coreference system for yourself</a>!</p><p>You can also <a href="https://github.com/huggingface/neuralcoref">fork the code</a> and use it in your projects. Hope you liked it and let us know how you use it 🚀</p><p>*. <a href="#116e">^</a> Coreference pun!</p><ol><li><a href="#c1eb">^</a> Good introductions of the subject can be found in <a href="http://web.stanford.edu/class/cs224n/">the great NLP class of Chris Manning</a> and in “<em>Speech and Language Processing: An introduction to natural language processing, computational linguistics, and speech recognition</em>” by Daniel Jurafsky &amp; James H. Martin, 2nd edition 2007 (Chapter 21 in particular). Linguistic is a fascinatingly huge domain so you may also have heard of <a href="https://en.wikipedia.org/wiki/Anaphora_(linguistics)">anaphors and cataphors</a>. <a href="https://en.wikipedia.org/wiki/Coreference">Coreferences</a>, anaphors and cataphors describes different relationships that sometimes overlap but not always. In particular <em>coreferences</em> are mentions that all relates to <em>the same object of the outside world</em>.</li><li><a href="#116e">^</a> See in particular: <a href="http://nlp.seas.harvard.edu/papers/corefmain.pdf">“Learning Global Features for Coreference Resolution”</a> by Sam Wiseman, Alexander M. Rush, and Stuart M. Shieber, NAACL 2016, <a href="http://cs.stanford.edu/people/kevclark/resources/clark-manning-emnlp2016-deep.pdf">“Deep Reinforcement Learning for Mention-Ranking Coreference Models”</a> by Kevin Clark and Christopher D. Manning, EMNLP 2016, and “<a href="http://www.aclweb.org/anthology/Q/Q15/Q15-1029.pdf">Latent Structures for Coreference Resolution</a>” by Sebastian Martschat and Michael Strube, ACL 2015<em>.</em></li><li><a href="#b027">^</a> There are other ways you can link entities (entity-mention models, , for e.g. by constructing cluster of mentions and considering global features (coreference is a clustering operation) but we won’t talk about them here. You should read the <a href="#4821">references above</a> of <a href="http://web.stanford.edu/class/cs224n/">the great NLP class of Chris Manning</a>, or refer to “<em>Speech and Language Processing: An introduction to natural language processing, computational linguistics, and speech recognition</em>” by Daniel Jurafsky &amp; James H. Martin, for example, if you want to know more about that.</li><li><a href="#7c50">^</a> See <a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43407.pdf">Modeling the lifespan of discourse entities with application to coreference resolution</a> by Marneffe et al. (2015) and <a href="http://www.aclweb.org/anthology/N16-1115.pdf">Search space pruning: A simple solution for better coreference resolvers</a> by Nafise Sadat Moosavi and Michael Strube (2016).</li><li><a href="#b9bf">^</a> well they do somehow but they also have to encode many other semantic/syntaxique features of the words — many of these being more important than gender— to predict the surrounding words (the objective in word2vec training)</li></ol><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC%3Ftypeform-embed%3Doembed%26format%3Djson&amp;display_name=Typeform&amp;url=https%3A%2F%2Fhuggingface.typeform.com%2Fto%2FP4EATC&amp;image=https%3A%2F%2Fimages.typeform.com%2Fimages%2FMpqBWPwLRZ7Z%2Fimage%2Fdefault&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=typeform" width="900" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href">https://medium.com/media/9028cd193efdc5a465b8ac91e4702628/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3302365dcf30" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/state-of-the-art-neural-coreference-resolution-for-chatbots-3302365dcf30">State-of-the-art neural coreference resolution for chatbots</a> was originally published in <a href="https://medium.com/huggingface">HuggingFace</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>