<?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[HuggingFace - Medium]]></title>
        <description><![CDATA[Stories @ Hugging Face - Medium]]></description>
        <link>https://medium.com/huggingface?source=rss----ba0dbdd23ac6---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>HuggingFace - Medium</title>
            <link>https://medium.com/huggingface?source=rss----ba0dbdd23ac6---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 15 Jun 2026 13:02:21 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/huggingface" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[ Simple considerations for simple people building fancy neural networks]]></title>
            <link>https://medium.com/huggingface/simple-considerations-for-simple-people-building-fancy-neural-networks-7abc3c0f0bd7?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/7abc3c0f0bd7</guid>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Victor Sanh]]></dc:creator>
            <pubDate>Tue, 22 Sep 2020 13:31:12 GMT</pubDate>
            <atom:updated>2020-09-22T14:50:12.900Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pXyq5Lalm4UlW15GcDwWbA.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@hngstrm?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Henry &amp; Co.</a> on <a href="https://unsplash.com/s/photos/builder?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>As machine learning continues penetrating all aspects of the industry, neural networks have never been so hyped. For instance, models like GPT-3 have been all over social media in the past few weeks and continue to make headlines outside of tech news outlets with fear-mongering titles.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sENCNdlC7zK4bg22r43KiA.png" /><figcaption><a href="https://www.theguardian.com/commentisfree/2020/sep/08/robot-wrote-this-article-gpt-3">An article</a> from The Guardian</figcaption></figure><p>At the same time, deep learning frameworks, tools, and specialized libraries democratize machine learning research by making state-of-the-art research easier to use than ever. It is quite common to see these almost-magical/plug-and-play 5 lines of code that promise (near) state-of-the-art results. Working at<a href="https://huggingface.co/"> Hugging Face</a> 🤗, I admit that I am partially guilty of that. 😅 It can give an inexperienced user the misleading impression that neural networks are now a mature technology while in fact, the field is in constant development.</p><h3></h3><p>When I was doing research on language models a decade ago, I could never in my wildest dreams imagine that MC Hammer would be debating Gary Marcus about the latest work in the field today.</p><p>In reality, <strong>building and training neural networks can often be an extremely frustrating experience</strong>:</p><ul><li>It is sometimes hard to understand if your performance comes from a bug in your model/code or is simply limited by your model’s expressiveness.</li><li>You can make tons of tiny mistakes at every step of the process without realizing at first, and your model will still train and give a decent performance.</li></ul><p><strong>In this post, I will try to highlight a few steps of my mental process when it comes to building and debugging neural networks.</strong> By “debugging”, I mean making sure you align what you have built and what you have in mind. I will also point out things you can look at when you are not sure what your next step should be by listing the typical questions I ask myself.</p><p><em>A lot of these thoughts stem from my experience doing research in natural language processing but most of these principles can be applied to other fields of machine learning.</em></p><h4><strong>1. 🙈 Start by putting machine learning aside</strong></h4><p>It might sound counter-intuitive but the very first step of building a neural network is to <strong>put aside machine learning and simply focus on your data</strong>. Look at the examples, their labels, the diversity of the vocabulary if you are working with text, their length distribution, etc. You should dive into the data to get a first sense of the raw product you are working with and focus on extracting general patterns that a model might be able to catch. Hopefully, by looking at a few hundred examples, you will be able to identify high-level patterns. A few standard questions you can ask yourself:</p><ul><li>Are the labels balanced?</li><li>Are there gold-labels that you do not agree with?</li><li>How were the data obtained? What are the possible sources of noise in this process?</li><li>Are there any preprocessing steps that seem natural (tokenization, URL or hashtag removing, etc.)?</li><li>How diverse are the examples?</li><li>What rule-based algorithm would perform decently on this problem?</li></ul><p>It is important to get a <strong>high-level feeling (qualitative) of your dataset along with a fine-grained analysis (quantitative)</strong>. If you are working with a public dataset, someone else might have already dived into the data and reported their analysis (it is quite common in Kaggle competition for instance) so you should absolutely have a look at these!</p><h4><strong>2. </strong>📚 Continue as if you just started machine learning</h4><p>Once you have a deep and broad understanding of your data, I always recommend <strong>to put yourself in the shoes of your old self when you just started machine learning</strong> and were watching introduction classes from Andrew Ng on Coursera. <strong>Start as simple as possible to get a sense of the difficulty of your task and how well standard baselines would perform. </strong>For instance, if you work with text, standard baselines for binary text classification can include a logistic regression trained on top of word2vec or fastText embeddings. With the current tools, running these baselines is as easy (if not more) as running BERT which can arguably be considered one of the standard tools for many natural language processing problems. If other baselines are available, run (or implement) some of them. It will help you get even more familiar with the data.</p><p>As developers, it easy to feel good when building something fancy but it is sometimes hard to rationally justify it if it beats easy baselines by only a few points, so it is central to make sure you have reasonable points of comparisons:</p><ul><li>How would a random predictor perform (especially in classification problems)? Dataset can be unbalanced…</li><li>What would the loss look like for a random predictor?</li><li>What is (are) the best metric(s) to measure progress on my task?</li><li>What are the limits of this metric? If it’s perfect, what can I conclude? What can’t I conclude?</li><li>What is missing in “simple approaches” to reach a perfect score?</li><li>Are there architectures in my neural network toolbox that would be good to model the inductive bias of the data?</li></ul><h4>3. 🦸‍♀️ Don’t be afraid to look under the hood of these 5-liners templates</h4><p>Next, you can start building your model based on the insights and understanding you acquired previously. As mentioned earlier, implementing neural networks can quickly become quite tricky: there are many moving parts that work together (the optimizer, the model, the input processing pipeline, etc.), and many small things can go wrong when implementing these parts and connecting them to each other. <strong>The challenge lies in the fact that you can make these mistakes, train a model without it ever crashing, and still get a decent performance…</strong></p><p>Yet, it is a good habit when you think you have finished implementing to <strong>overfit a small batch of examples</strong> (16 for instance). If your implementation is (nearly) correct, your model will be able to overfit and remember these examples by displaying a 0-loss (make sure you remove any form of regularization such as weight decay). If not, it is highly possible that you did something wrong in your implementation. In some rare cases, it means that your model is not expressive enough or lacks capacity. Again, <strong>start with a small-scale model</strong> (fewer layers for instance): you are looking to debug your model so you want a quick feedback loop, not a high performance.</p><blockquote>Pro-tip: in my experience working with pre-trained language models, freezing the embeddings modules to their pre-trained values doesn’t affect much the fine-tuning task performance while considerably speeding up the training.</blockquote><p>Some common errors include:</p><ul><li>Wrong indexing… (these are really the worst 😅). Make sure you are gathering tensors along the correct dimensions for instance…</li><li>You forgot to call `model.eval()` in evaluation mode (in PyTorch) or `model.zero_grad()` to clean the gradients</li><li>Something went wrong in the pre-processing of the inputs</li><li>The loss got wrong arguments (for instance passing probabilities when it expects logits)</li><li>Initialization doesn’t break the symmetry (usually happens when you initialize a whole matrix with a single constant value)</li><li>Some parameters are never called during the forward pass (and thus receive no gradients)</li><li>The learning rate is taking funky values like 0 all the time</li><li>Your inputs are being truncated in a suboptimal way</li></ul><blockquote>Pro-tip: when you work with language, have a serious <strong>look at the outputs of the tokenizers</strong>. I can’t count the number of lost hours I spent trying to reproduce results (and sometimes my own old results) because something went wrong with the tokenization.🤦‍♂️</blockquote><p>Another useful tool is <strong>deep-diving into the training dynamic</strong> and plot (in Tensorboard for instance) the evolution of multiple scalars through training. At the bare minimum, you should look at the dynamic of your loss(es), the parameters, and their gradients.</p><p>As the loss decreases, you also want to look at the model’s predictions: either by evaluating on your development set or, my personal favorite, <strong>print a couple of model outputs</strong>. For instance, if you are training a machine translation model, it is quite satisfying to see the generations become more and more convincing through the training. You want to be more specifically careful about overfitting: your training loss continues to decreases while your evaluation loss is aiming at the stars.💫</p><h4>4. 👀 Tune but don’t tune blindly</h4><p>Once you have everything up and running, you might want to tune your hyperparameters to find the best configuration for your setup. I generally stick with a random grid search as it turns out to be fairly effective in practice.</p><blockquote>Some people report successes using fancy hyperparameter tuning methods such as Bayesian optimization but in my experience, random over a reasonably manually defined grid search is still a tough-to-beat baseline.</blockquote><p>Most importantly, there is no point of launching 1000 runs with different hyperparameters (or architecture tweaks like activation functions): <strong>compare a couple of runs with different hyperparameters to get an idea of which hyperparameters have the highest impact </strong>but in general, it is delusional to expect to get your biggest jumps of performance by simply tuning a few values. For instance, if your best performing model is trained with a learning rate of 4e2, there is probably something more fundamental happening inside your neural network and you want to identify and understand this behavior so that you can re-use this knowledge outside of your current specific context.</p><h3></h3><p>The experience of the human setting the hyper-parameters of a deep learning method impacts the resulting performance significantly. This &quot;black magic&quot; has now been analyzed in a controlled study by Anand et al. in a #BMVC 2020 paper: https://t.co/MXVz0CIIqg pic.twitter.com/dDOCb2OOeb</p><p>To conclude, a piece of general advice that has helped me become better at building neural networks is to <strong>favor (as most as possible) a deep understanding of each component of your neural network instead of blindly (not to say magically) tweak the architecture</strong>. Keep it simple and avoid small tweaks that you can’t reasonably justify even after trying really hard. Obviously, there is the right balance to find between a “trial-and-error” and an “analysis approach” but a lot of these intuitions feel more natural as you accumulate practical experience. <strong>You too are training your internal model. </strong>🤯</p><p>A few related pointers to complete your reading:</p><ul><li><a href="https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p">Reproducibility (in ML) as a vehicle for engineering best practices</a> from Joel Grus</li><li><a href="https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21">Checklist for debugging neural networks</a> from Cecelia Shao</li><li><a href="https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765">How to unit test machine learning code</a> from Chase Roberts</li><li><a href="http://karpathy.github.io/2019/04/25/recipe/">A recipe for Training Neural Networks</a> from Andrej Karpathy</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=7abc3c0f0bd7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/simple-considerations-for-simple-people-building-fancy-neural-networks-7abc3c0f0bd7">🚧 Simple considerations for simple people building fancy neural networks</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[Sparse Neural Networks (2/N): GPU Performance.]]></title>
            <link>https://medium.com/huggingface/sparse-neural-networks-2-n-gpu-performance-b8bc9ce950fc?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/b8bc9ce950fc</guid>
            <category><![CDATA[nvidia]]></category>
            <category><![CDATA[hardware]]></category>
            <category><![CDATA[ampere]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[gpu]]></category>
            <dc:creator><![CDATA[François Lagunas]]></dc:creator>
            <pubDate>Thu, 28 May 2020 21:23:50 GMT</pubDate>
            <atom:updated>2020-05-28T21:23:50.389Z</atom:updated>
            <content:encoded><![CDATA[<h3>Sparse Neural Networks (2/N): Understanding GPU Performance.</h3><p><strong>NVIDIA Ampere A100 introduces fine-grained structured sparsity</strong></p><p>Welcome back for this series on Sparse Neural Networks. In case you have not read our first introductory episode, <a href="https://link.medium.com/In4bINyeO3">here it is</a>.</p><p>I told you last time that<strong> sparsity would a major topic in 2020</strong>, and it looks like it’s getting indeed some steam: <strong>Nvidia</strong> is announcing with the <strong>Ampere</strong> GPU generation that<strong> sparsity is directly baked into their GPU design.</strong></p><p>It’s quite a bold move: if you consider the time it takes to design and produce a new GPU line, they made this decision at least 2 years ago, and you need some vista to understand that it would be an important trend 2 years later.</p><figure><img alt="André Ampère" src="https://cdn-images-1.medium.com/max/337/1*M1ky4jno_MmGg5eR3tgGyg.png" /><figcaption>André Ampère, 1825 (from Wikipedia)</figcaption></figure><p>So that’s the perfect pretext to make a large digression on <strong>GPU architectures</strong> and why knowing better about them may matter for your daily Machine Learning jobs.</p><p>To be honest, this will more matter to you if you are working on some low-level code.</p><p>If you are using PyTorch or other libraries, and you are just using the extremely good tools it provides, you are probably fine.</p><p>But<strong> </strong><a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/"><strong>leaky abstractions</strong></a><strong> come back at you </strong>faster than you’d think<strong>.</strong> Your model got a bit heavier? Want to train faster? OK, let’s use a DataParallel PyTorch node, and we’ll be fine on 8 GPUs. But wait, why my GPU usage is down the gutter? And on 8 GPUs it’s only 3 times as fast as on a single one?</p><p>It especially matters to me, as I have been telling you last time that the <strong>performance of sparse matrices operations was not satisfactory</strong>. Today we’ll see why it can be hard to get good performance on GPUs, how it depends on your data structure and algorithms, and how you can overcome it, or at some times at least mitigate some issues.</p><p>And of course, all this is a good pretext to read about some mind-blowing GFlops numbers and killer optimizations, nothing to sneeze at…</p><h3>Some physics</h3><p>You may wonder why your PC/Mac is not significantly faster than a few years ago. That’s because most of the apps you are using are mostly sequential: they are doing only one thing at a time, or almost, and sequential performance has been stagnating for some years.</p><p>That’s because sequential performance is mostly limited by <strong>operating</strong> <strong>frequency</strong>, which is itself limited by:</p><ul><li>the<strong> size of the finest details</strong> that are drawn on the silicon, something that is getting harder and harder to improve,</li><li>the amount of <strong>heat</strong> that is created by the chips, a function of voltage and frequency. First, a <strong>transistor</strong> emits heat when <strong>changing state</strong>, so proportionally to frequency. Second, <strong>the higher the frequency, the higher the voltage</strong> you need. So in the end <strong>emitted heat is more than linear in the frequency</strong>, not something ideal.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0b-KUkO2e22UPpPKrRi9PQ.png" /><figcaption>From <a href="https://youtu.be/Knd-U-avG0c?t=109">https://youtu.be/Knd-U-avG0c</a></figcaption></figure><p>So if you could efficiently and cheaply remove heat from the chips, you could get higher frequencies, but only marginally, and it gets quickly impractical (water-cooling, you know, is <em>cool</em>, but <a href="https://www.avadirect.com/blog/leaking-liquid-cooler-whats-next/">not when it leaks</a>…).</p><p><strong>The recent ARM takeover is not an accident</strong>. When you work for years on <strong>low consumption and so low heat producing chips,</strong> when everybody hits the “heat wall”, you are in a <strong>good position to</strong><a href="https://9to5mac.com/2018/11/01/geekbench-ipad-pro-performance/"><strong> push performance higher</strong></a>, even if computers migrating to your pocket was the opportunity that made the difference.</p><h3>Chip design</h3><p>So people invented tricks to make use of the same amount of cycles to do more, to do almost any instruction in one single cycle, to forecast what’s the next instruction etc. Very different architectures to tackle the same issues were used (<a href="https://cs.stanford.edu/people/eroberts/courses/soco/projects/risc/risccisc/">RISC, CISC</a>). But the returns are diminishing, as always.</p><p>So what can you do to feed the hungry “Moore’s Law Beast”, and the marketing guys who keep asking why the numbers are flattening?</p><p>You look for problems that need to do the same kind of task a billion times, and each task does not need the result of another task, so all tasks can be computed at the same time. (the technical slang for this is “<a href="https://en.wikipedia.org/wiki/Embarrassingly_parallel"><strong>Embarrassingly Parallel</strong></a>” …).</p><p>Fortunately, there are a lot of them. <strong>Linear Algebra</strong>, for example, is highly parallel by nature, and <strong>machine learning</strong> is using it a lot, like lots of <strong>physics</strong> <strong>simulation</strong>, <strong>computer</strong> <strong>graphics</strong>, and so on.</p><p>So instead of increasingly complex single cores processors, we see much simpler (and smaller on silicon) cores but grouped by the hundreds or thousands. This way you are guaranteed that the ratio computation/silicon area is getting through the roof.</p><p>Great. That’s a simple idea. But of course, reality is more complex than that.</p><h3>Bottlenecks</h3><p><strong>If you have a lot of computing power available, you have to feed it with data. </strong>Memory is getting faster with time, but it’s harder than just duplicating cores. Because<strong> memory buses are basically 1D, and compute cores are 2D</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Fruof2lsq_v7hdrXZUhRjg.png" /><figcaption>From <a href="https://unsplash.com/photos/VEVfbQtyB8s">https://unsplash.com/photos/VEVfbQtyB8s</a></figcaption></figure><p>You can think about it as a <strong>city</strong> (the computing cores), and the suburban workers coming each morning in the city (the data). <strong>The city is 2D, the highways are 1D,</strong> and of course, you get some heavy traffic jams. So you add some new <strong>lanes</strong> on the <strong>highways</strong> (the width of the memory bus), but it’s always the <strong>bottleneck</strong></p><p>If you want to maximize the highway utility, you would have to use all day long, encouraging people to come to and leave from work earlier or later.</p><p>That’s the same thing for the memory bus: <strong>you have to make sure that you are balancing computation and memory transfers so you don’t waste time waiting without using the memory bus or the compute cores</strong>. That’s why it’s hard to reach peak performance for every task.</p><p>Some tasks even prefer to compute twice the same thing instead of transferring some data: <strong>compute is plentiful and memory bandwidth is scarce </strong>(and the gap is growing each year). In graphics, procedural texturing is used more and more for this exact reason: textures need bandwidth, and so if you can generate the same result with few memory transfers but some additional compute, it’s a win.</p><h3>GPU Architecture principles</h3><p>A lot of the complexities of GPU architectures exist to overcome those bottlenecks.</p><h4>Hierarchy</h4><p><strong>You don’t get the 1000s of cores in a GPU in a single bag: they are grouped at multiple levels. </strong>We’ll take the example of the new Ampere A100. Numbers change according to the generation, but the general principles are slowly evolving. (Numbers below come mostly from the <a href="https://devblogs.nvidia.com/nvidia-ampere-architecture-in-depth/">Nvidia blog</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i60_7GqIKxjgunaXe7CiCw.png" /><figcaption><em>The GA100 streaming multiprocessor (SM)</em></figcaption></figure><p>At the lower level you have a Streaming Processor (SP). He is part of a group of 16 SP which computes the same sequence of instructions at the same time.</p><p>(To be more precise, you have 16 FP32 cores, 8 FP64 cores, 16 INT32 cores, 1 Tensor Core, and 1 texture unit per group. More on tensor cores later)</p><p><strong>The first constraint is the following: the 16 SP in the group cannot diverge from a single sequence instruction. This is called SIMD: Same Instruction, Multiple Data.</strong> That’s not exactly true, the instructions can contain “if“ statement, but if different branches are taken, some compute will be lost because every processor will have to execute both branches, and throw the results that are not useful for its own work.</p><p>4 groups of 16 SPs form a Streaming Multiprocessor (SM). Each group executes the same kernel (=function), but not in a strictly synchronized way. Still, you’ll have at least 64 cores working on the same task, or you’ll lose some computing capacity.</p><p>Then, you group 2 SMs to form a “Texture Processing Cluster” (TPC), and you group 8 TPCs to form a GPC (GPU Processing Cluster). 8GPCs and you have an A100 GPU. Pfew!</p><p><strong>To sum it up, there are 128 SMs in an A100, so 8192 FP32 cores, but as you can see, we are far from getting a flat set of “8192” cores! </strong>(those are maximum numbers, first processors won’t have the full set of cores)<strong>.</strong></p><p>If you compare the A100 structure with the Volta V100, these structural numbers are almost the same, except for the PCs, and so for the grand total of course. The innards of the cores of course have changed too, but it looks like that the communication structure of the V100 was considered quite good for the kind of job it’s usually given. The Tensor Cores seems to be the area where the most innovation is taking place (more on this later).</p><p>You can see in the comparison below that all those numbers varied significantly with time, in search of the best performance :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MmPCD_ypvtHDhwAGUZhqHQ.png" /></figure><h4>Why so many levels? Performance</h4><p><strong>The main reason is of course to improve real-life performance. And in real life, you don’t have a single task to be done.</strong></p><p>First, there may be several processes using your GPU at the same time on your machine. Not sure if it’s a good idea to get some good performance, but it’s of course something very usual.</p><p><strong>In the new Ampere GPU, you can even partition your GPU to server multiple Virtual Machines with strong guarantees on your data security: the new feature is called “Multi-Instance GPU”.</strong></p><p>In a single process, if your network contains <strong>several layers</strong>, some linear, some non-linear, some embedding, each one will use one or <strong>several kernels</strong> to do its job.</p><p>You may think that they are executed one after the other. It’s true to some extent, but in order to<strong> keep your GPU busy</strong>, your <strong>CPU</strong> is sending a <strong>stream of tasks to be done</strong>, not a task after the other, and the GPU will do them without the CPU waiting for each one to complete.</p><p>The CPU will basically wait after a full batch has been processed, after the forward and backward pass, because he has to update the full model before starting a new batch.</p><p>There are several reasons to have this “stream of task” model:</p><ul><li>The first reason is that starting a task takes some time, so the GPU can prepare the next task before the previous is started: changing the active kernel on some part of the GPU takes some time, <strong>pipelining</strong> saves time.</li><li>Second, in the task stream, some tasks are not dependent on each other, so both can be executed in parallel in the GPU, so more work to be done, so less chance some part of the GPU is idling.</li></ul><p>Some <strong>networks</strong> are <strong>very, very parallel </strong>to compute, like <strong>Transformers</strong>, and so their efficiency is very good:</p><ul><li>there are only a <strong>few different layers</strong>, so <strong>few kernel changes</strong> and a lot of work for each kernel</li><li>there are only <strong>loose dependencies between computations</strong> (eg for each token), so the GPU has a lot of degrees of freedom when scheduling the different parts of the computation: if a kernel is waiting for some data, maybe another one can compute its result because it already has its own data available.</li></ul><h4>Why so many levels? Economics</h4><p>Another reason is that it’s hard to get zero-defect silicon at this level of detail.</p><p><strong>Ampere GPUs</strong> contain <strong>54 billion transistors</strong>. Any defective transistor, and you may have to throw the GPU to the bin. <strong>The fraction of chips that pass the test is called the <em>yield</em>.</strong> Those chips are huge, and silicon real estate costs a lot, so each failed chip is a big loss, just for a small defect on a single transistor somewhere in the silicon.</p><p>So instead of throwing the chip to the bin, <strong>you test some sub-parts of the chip, and you just disable the failing sub-parts</strong>. That means, for example, disabling a GPC (remember, there are 7 of them in a A100, instead of a theoretical 8). And you sell it in a lower-end card, <strong>with reduced specs</strong>. This process is called <strong><em>binning</em></strong>. If you are really good, and your chips are all perfect, you may even disable perfectly working parts of your chip, to segment your offer (and back in time, some users were able to re-enable those disabled parts of silicon to get the bang without the buck…)</p><h3>Developing for GPUs</h3><p>So what are the consequences of the GPU architecture choices on development?</p><h4>Kernels</h4><p>First, <strong>you have to write some kernels,</strong> using the primitives you get. It’s a quite specific exercise, as you have to manually manage caches, registers, the synchronization of the different cores, etc. For simple stuff like matrix products, or activation layers, it’s quite straightforward, as they are completely parallel by nature.</p><p>But for some algorithms, like sorting, it can be a lot trickier to have something efficient, because you will have some issues using all the cores all the time.</p><h4><strong>Grids and performance</strong></h4><p>That’s because the kernel is only a small part of the problem, the other is the way you distribute the work among cores. And the performance gains are often made more on the distribution than on an optimal kernel.</p><p>The way you distribute the work is usually done by<strong> partitioning your job into a 2D or 3D grid, </strong>then mapping each point of the grid to a thread, and finally mapping those threads to physical cores. Those dimensions will correspond for example to the dimensions of the output of a layer, plus the batch dimension.</p><p>As you have seen, in a GPU you get thousands of cores to work with, but with a really complex multi-layered structure. And this structure change according to the generation and model of the GPU. <strong>So it’s hard to find the right way to choose those mappings.</strong> You often have to make some benchmarks to find the right way to do a computation with given dimensions on a specific GPU, and that information will be used in the future to choose the best strategy at runtime.</p><h4>Memory</h4><p><strong>But the main and the most difficult hurdle a developer face while developing for GPU’s architecture is managing memory.</strong> And specifically memory transfers. The available memory bandwidth is huge, but the computing power is even larger. And just as you did not get a flat space of computing cores, you don’t get completely random access to the memory for free.</p><p><strong>If you want to access a float number stored in the main memory from a GPU core, you will wait literally for <em>ages </em>compared to the time it takes to compute a sum or a multiply.</strong> So you need to be able to start hundreds of computations at once, and when the data is finally available, you resume your kernel, you execute a few local operations, until you need some more data from the main memory.</p><p>Some special ops like “prefetch” exist, to declare that you will need some data in a few instructions, and the role of the compiler is to reorder the instructions so you keep the memory controllers busy while keeping the core busy too. And at runtime, a large part of the GPU silicon is devoted to handling all those threads that are “in flight” and their current memory requests.</p><p>But there are some low-level constraints that may cost you a lot. Just like the base computation unit is 16 cores doing the same job,<strong> you really get peak memory performance if you load memory by quite large contiguous blocks, </strong>for example, 16 floats = 64 bytes, by a group of threads (called warp in CUDA lingo). This is called <strong><em>coalesced</em></strong> access. This is another reason, and often the main one, why choosing the right grid to dispatch your task on is important.</p><p>So now, let’s unroll back to our initial issue if you still remember (I would forgive you, I can barely): <strong>why sparse matrices ops are slow?</strong></p><p><strong>If you look at the memory access pattern you need to make a sparse matrix/ matrix multiplication, you’ll see that by definition it’s hard to have those blocks of 16 floats when reading the matrix weights</strong>. And reading 16 contiguous floats is just a minimum, you’ll need to read more data at once to reach full performance.</p><p>That explains why a naive implementation can be at least an order of magnitude slower than the dense version.</p><p>Unless you make some compromise and use a <strong>block sparse matrix</strong>: each block, if large enough, will produce <strong>large contiguous accesses</strong>. 8x8 blocks is a minimum in <a href="https://openai.com/blog/block-sparse-gpu-kernels/">OpenAI implementation</a>, but you will get even better performance with 32x32 blocks.</p><p>But of course, you have to make sure that your model is working in a similar fashion with block sparse compared to pure sparse matrices. It can be the case if your matrices are large enough so block size is small in comparison, but you have to check.</p><p>The other way is to convince an executive at Nvidia to add some hardware sparse support into their next-gen GPU, and now it’s done. More on this below!</p><h4>Inter-GPU memory transfer</h4><p><strong>Memory bottlenecks exist within the GPU, but if you work with multiple GPUs sharing a single model, the available bandwidth is way lower than between memory and cores.</strong></p><p>The <a href="https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html"><strong>DataParallel</strong></a> node of <strong>PyTorch</strong> is convenient, but it is no magic: after each batch, the GPUs must send their gradients to a single GPU, and then this latter must broadcast the updated model to each GPU. <strong>If your model is big enough, this transfer can take very significant time, and the performance will suffer. </strong>Another point is that the transfers are synchronous, no GPU can work if the new model has not been received.</p><p>Another way to use multiple GPUs is to split a single model between the different GPUs, and then transfer only the “frontier” layers from a GPU to the next. Same thing for backpropagation. This may not be ideal either as the first layer will have to wait for the last to complete before the backpropagation can occur. The performance will depend heavily on the morphology of the network.</p><h3>Ampere Highlights</h3><p>Let’s finish where we started, with the latest Nvidia announcement.</p><h4>Tensor Cores</h4><p>With Volta, Nvidia introduced new “<strong>Tensor Core units</strong>”, and it looks like they are here to stay. Turing and now <strong>Ampere</strong> iterated on these new units.</p><p>You can see them as ultra-specialized units, with some significant dedicated silicon.</p><p>And this means a lot in terms of speed, especially quantized networks inference :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*e7SMvK3WdZmz32poFUQaJA.gif" /><figcaption>From <a href="https://youtu.be/yyR0ZoCeBO8?t=19">https://youtu.be/yyR0ZoCeBO8?t=19</a></figcaption></figure><p>For training, it was a bit more difficult on Volta, as working with FP16 was possible but a bit tricky (the 8x gain in speed was indeed tempting).</p><p><strong>But now with Ampere, Nvidia announces support for FP32 and even FP64 for Tensor Cores.</strong> <strong>And it looks like FP32 is now 20 times faster than on Volta with sparsity, and 10 times without sparsity.</strong> And this is for training and inference because it’s just big tensor ops, nothing special here.</p><p>It looks like we’ll be getting some nice toys to play with.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rBrF53VkNYC5gL6YJExXxQ.png" /></figure><h4>Sparsity</h4><p>From the <a href="https://devblogs.nvidia.com/nvidia-ampere-architecture-in-depth/#">Nvidia Blog</a> :</p><blockquote>NVIDIA has developed a simple and universal recipe for sparsifying deep neural networks for inference using this 2:4 structured sparsity pattern.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xq_MGzOOGVazlgAT8766uA.png" /></figure><p>If you have read the first part of this series, you should feel at home.</p><p><strong>The idea</strong> is simple: maybe using a fully dense matrix is not useful. And what Nvidia is claiming is that it’s true, <strong>keeping only half the weights has a minimal impact on precision.</strong></p><p>And so they propose a method to reduce the number of weights. <strong>But what is more interesting, is that the A100 GPU has new instructions to process efficiently these sparse matrices, at twice the speed of dense ones </strong>(no magic here, only half the multiply occurs of course).</p><p>So anyone can try its own method to sparsify the matrices and use the new instructions to speed things up. <strong>The only constraint is that the sparse pattern is fixed, as every 4 cells must have 2 sparse ones at most.</strong></p><p>You can compare this to the way textures are compressed to save memory but for floating computation and not just graphics.</p><p><strong>I see it mostly for inference at first, but I am sure some clever people will come with imaginative ways to use those new capabilities for training too, as it’s just some new compute ops.</strong></p><p>What about “sparse block sparse matrices”, by combining soon to be released OpenAI “block sparse matrices” with this? We’ll see.</p><h3>Conclusion</h3><p>I hope you enjoyed this second part of our trip to sparse land, even if it may have been a bit harder to digest.</p><p>I hope too this will help you to better understand th<strong>e level of mastery developers in the PyTorch or Keras team show</strong>: they manage to <strong>hide all this complexity </strong>and make it easy for mere mortals to use these supercomputer-on-a-chip to their full power, in just a <strong>few lines of python.</strong></p><p>Next time we will get back to more usual depths: we’ll see some<strong> techniques we can use to train sparse networks</strong>, and how performance is impacted.</p><p>By the way, congrats to Victor Sanh, Thomas Wolf, and Alexander M. Rush for their latest paper “<a href="https://arxiv.org/abs/2005.07683">Movement Pruning: Adaptive Sparsity by Fine-Tuning</a>”!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b8bc9ce950fc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/sparse-neural-networks-2-n-gpu-performance-b8bc9ce950fc">Sparse Neural Networks (2/N): GPU Performance.</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[A brief history of machine translation paradigms]]></title>
            <link>https://medium.com/huggingface/a-brief-history-of-machine-translation-paradigms-d5c09d8a5b7e?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/d5c09d8a5b7e</guid>
            <category><![CDATA[history-of-technology]]></category>
            <category><![CDATA[machine-translation]]></category>
            <category><![CDATA[neural-networks]]></category>
            <category><![CDATA[naturallanguageprocessing]]></category>
            <dc:creator><![CDATA[Teven Le Scao]]></dc:creator>
            <pubDate>Thu, 14 May 2020 12:41:14 GMT</pubDate>
            <atom:updated>2020-05-14T12:41:14.667Z</atom:updated>
            <content:encoded><![CDATA[<p>As a young European, having access to translation at any time just by pulling up my phone is an unprecedented luxury. There’s convenience in knowing I can order a kebab in a hundred languages, <em>cu de tuate und scharf, bez příliš mnoho cibule mais avec frites</em>, and there’s beauty in being able to do so even in countries where older generations remember being in conflict with mine. This made machine translation my first love of sorts among machine learning applications: it is why I originally went from applied mathematics to NLP. This week, Hugging Face is proud to release <a href="https://huggingface.co/models?search=Helsinki-NLP%2Fopus-mt">1000+ translation models</a> from University of Helsinki thanks to the hard work of Helsinki’s Jörg Tiedemann and our own Sam Shleifer. To accompany the release, here’s a short history of machine translation efforts over the last century. It’s written with a public familiar with modern NLP in mind, and I tried to draw connections with other fields throughout.</p><h3>1. Genesis (1933–1945)</h3><p>The first automated translation systems were independently created in 1933, by George Artsrouni in France¹ and Petr Troyanskii in the USSR². Unfortunately, neither really took hold in engineering or research circles, for different reasons. Artsrouni’s system, which was a mechanically automated retrieval system that could function as a dictionary, generated a lot of interest in the French administration but could not come to fruition before the start of the Second World War. Troyanskii’s system, which also started as an automated dictionary but grew to incorporate a memory as well as electronic components (those were at the time still mechanical computers !) was ignored by the Soviet scientific establishment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1p6kp8h-f6wJ7VoF" /><figcaption><em>Marian Rejewski’s statue with an Enigma machine in his hometown of Bydgoszcz³</em></figcaption></figure><p>Elsewhere in Europe, events that would prove (only slightly) more impactful were unfolding at the same time. From 1932 to 1933, the Polish Cipher Bureau — most notably Marian Rejewski, who the Marian NMT system is named after — broke the code of early German Enigma machines. During the Second World War itself, cryptography became a key topic and mobilized significant intellectual and financial resources. After the war ended, with the cold war rising, machine translation became a topic of interest to both superpowers’ intelligence communities. A key problem, for example, was automatically translating scientific articles from the other side, as scientific output out-scaled the number of competent translators.</p><p>In this context, the 1949 Weaver memorandum on translation⁴ was a landmark in the US, advocating that automated translation was becoming possible thanks to the newly created computer. It proposed several approaches, like storing the rules of language in the machine or learning statistical similarities between sentences, with even a mention of early efforts on perceptrons. Quite striking is the direct filiation it establishes between machine translation and wartime cryptography efforts: it opens with a war anecdote and raises the task of translating Russian as if it were code.</p><blockquote>When I look at an article in Russian, I say ‘This is really written in English, but it has been coded in some strange symbols. I will now proceed to decode.’</blockquote><p>Nothing like a good bout of great-power rivalry for research funding !</p><h3>2. Rule-based MT (1949–1984)</h3><h4>Early rule-based MT (1949–1967)</h4><p>After the publication of the Weaver memorandum, research in machine translation began in earnest in the United States, mostly focused on translating Russian scientific articles into English. Translation systems of the time can generally be placed on a scale between empiric and linguistically-grounded approaches. For example, on the empiric end of the scale, research at the RAND corporation proceeded in cycles of translation and editing. First, start from a few basic rules, observing the result on a predetermined corpus of Russian texts. Then, revise the glossary and grammatical rules of the system, and repeat the cycle, in a sort of expectation-maximization algorithm performed by humans (perhaps an ancestor of graduate student descent ?) On the other hand, academic research, especially at MIT, focused on finding intermediate representations between the source and target sentences. Sufficiently expressive representations, it was hoped, could allow for general-purpose translation. Another goal was building an interlingua, e.g. a representation of semantic meaning independent of language; Noam Chomsky was introducing universal grammar at the same time⁵.</p><p>A hybrid system to translate Russian technical documents was demonstrated in 1954 at Georgetown University. Deemed very impressive at the time, it spurred investment in the United States and seeded interest elsewhere, mostly in the Soviet Union and in Europe, where research concentrated on the theoretical approach. Systems at the time relied on the work of extensive teams of linguists: they translated human instructions into code rather than learning word correspondences on their own.</p><h4>Knowledge-based MT (1967–1984)</h4><p>The 1967 ALPAC report⁶ is generally held to have been the end of that first phase of machine translation hype, after it made the case that American research funding should be directed to machine-aided human translation rather than fully automated machine translation. After its publication, research funding dried up in the United States, leaving machine translation research efforts in Canada and Europe, and the Soviet Union.</p><blockquote>We have already noted that, while we have machine-aided translation of general scientific text, we do not have useful machine translation. Further, there is no immediate or predictable prospect of useful machine translation.</blockquote><p>Classic reviewer #2.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/496/0*kAqWMM4OiKudT4ZT" /><figcaption>The Vauquois Pyramid</figcaption></figure><p>One influential concept to understand the evolution of rule-based MT during that time is the Vauquois pyramid, reproduced here. First, the system attempts to understand the source text (analysis) and to represent this understanding. Then, it produces text in the target language (generation) from this representation. This was christened knowledge-based MT: the goal was to have ever more complete and general representations, moving up the pyramid, as opposed to earlier rule-based systems’ direct translation or only syntactically-informed translation. However, transfer machine translation, operating at a lower level, remained more effective and powered the systems of the time. Those were mostly domain-limited technical use cases like Canada’s Météo system.</p><h3>3. Data-driven MT (1984-present)</h3><h4>Example-based MT (1984–1993)</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/575/0*dRF8pFfojn1f-b3Z" /><figcaption><em>Looks like you’ve missed your daily French lesson today!</em></figcaption></figure><p>By the 1980s, computers had gotten a lot more powerful, especially in storage capacities. This allowed for larger databases of text, which remained yet to be systematically exploited. One early idea to do so was example-based MT, which was first proposed in 1984 in Japan⁷. Example-based systems made the observation that beginner-level foreign language speakers rely on sentences they already know to produce new ones: an example between French and English is shown in the figure⁸. Similarly, they relied on databases of known examples to produce new translations, querying the closest one. Although those ideas would eventually be subsumed in the broader framework of statistical MT, they were the first example of data-driven translation.</p><h4>Statistical MT (1993–2013)</h4><p>Underneath all this, a revolution was brewing, as a few outside developments came to fruition in the 90s. Statistical speech recognition started showing strong results on the back of advances in automata theory and hidden Markov models; computers became more powerful and accessible still; and high-quality, abundant datasets appeared, such as the Hansards accounts of the Canadian parliament. In 1988, IBM researchers had published the outline of modern statistical translation⁹, which proved controversial to say the least. As a famous anonymous review of the time states:</p><blockquote>The validity of a statistical (information theoretic) approach to MT has indeed been recognized, as the authors mention, by Weaver as early as 1949. And was universally recognized as mistaken by 1950 (cf. Hutchins, MT — Past, Present, Future, Ellis Horwood, 1986, p. 30ff and references therein). The crude force of computers is not science. The paper is simply beyond the scope of COLING.</blockquote><p>Reviewer #2 strikes again !</p><p>Nevertheless, the statistical approach quickly proved fruitful, as IBM’s models 1–5 became references in machine translation. Those were powered by the expectation maximization algorithm to learn both alignments between languages — which and how many words in the source and target correspond to each other — and a dictionary to translate after computing alignments. In a sense, they were direct descendants of the early RAND empirical approach: instead of being fed instructions by teams of linguists, the computer could learn all of the relationships from data on its own. By the 2010s, statistical methods had asserted their hegemony, as they powered virtually all of the internet-based translation services that comprise the bulk of translation use.</p><h4>Neural MT (2013-present)</h4><p>In his 1949 memorandum, Warren Weaver briefly touches upon early perceptron research as a promising avenue for machine translation. 60 years later, neural networks had made significant progress in other tasks, but had yet to be convincingly applied to translation. The first functional neural language models appeared in 2011, powered by recurrent neural networks¹⁰. Translation could then be reformulated as a conditional language modeling task: instead of predicting the most likely next word, predicting the most likely next word conditioned on the source text. The first modern machine translation paper appeared within a few years, in 2013. It consisted of an encoder model that produced a representation of the input with a convolutional neural network and of a decoder model that generated text from that representation with a vanilla recurrent neural network (RNN)¹¹.</p><p>At the time, neural MT was still underperforming compared to statistical MT, and it required two main developments from 2014 to eventually come out on top. First, vanilla RNNs were replaced with long short-term memory RNNs (LSTMs)¹². Then, learnable attention mechanisms were re-purposed from their computer vision roots and added to LSTMs¹³. By 2016, Google Translate had switched to neural MT. Transformer-based models¹⁴, which do away with the recurrent network part and only use iterated attention modules, have become the norm in recent years as they scale better than LSTMs with compute time and available data. The Helsinki models we’re releasing today all rely on this architecture. The power of transformers was quickly noticed outside of machine translation and, combined with pre-training, they now form the backbone of most modern NLP applications.</p><h3>Conclusion</h3><p>MT performance can be divided in 3 goals: human-level translation, general-purpose translation, and translation without human input. Older knowledge-based systems could do human-level translation without human input but only on very narrowly defined data. Human-in-the-loop systems had faster human-level translation but required manual intervention. Finally, statistical translation could handle any text without a human being, but not always at the level of human translators. If you’re lucky enough to have to translate between data-rich similar languages, Neural MT offers the best of all worlds. However, if the language pair you’re interested in is not data-rich, there is still quite a bit of work to do before we get there. An interesting project to realize the size of the task at hand is DARPA’s Lorelei program, which simulates a crisis in a region of the world whose language is underserved, and asks researchers to build a translation system in two weeks. Even for languages spoken by tens of millions of people, throwing teams of highly trained linguists at the problem is sometimes still the way to go!</p><h4>References</h4><p>[1] “La machine à traduire française aura bientôt trente ans”, Automatisme 5(3): 87–91, M. Corbé, 1960</p><p>[2] <a href="http://www.hutchinsweb.me.uk/PPF-2.pdf">Machine translation: past, present, future.</a> J. Hutchins, 1986</p><p>[3] Marian Rejewski statue photo from <a href="https://www.flickr.com/photos/petereed/5020796372">Peter Reed</a></p><p>[4] Reproduced in: Locke, W.N.; Booth, D.A., eds. (1955).<a href="http://www.mt-archive.info/Weaver-1949.pdf"> “Translation”</a> (PDF). <em>Machine Translation of Languages</em>.<a href="https://en.wikipedia.org/wiki/Cambridge,_Massachusetts"> Cambridge, Massachusetts</a>:<a href="https://en.wikipedia.org/wiki/MIT_Press"> MIT Press</a>. pp. 15–23.<a href="https://en.wikipedia.org/wiki/ISBN_(identifier)"> ISBN</a> <a href="https://en.wikipedia.org/wiki/Special:BookSources/0-8371-8434-7">0–8371–8434–7</a>.</p><p>[5] <a href="https://pdfs.semanticscholar.org/0af5/9a63ae47b9fcddddb761e8f9598518205109.pdf">Aspects of the Theory of Syntax</a>, Noam Chomsky, 1965</p><p>[6] <a href="http://www.mt-archive.info/ALPAC-1966.pdf">LANGUAGE AND MACHINES: COMPUTERS IN TRANSLATION AND LINGUISTICS</a>, ALPAC 1966</p><p>[7] <a href="https://pdfs.semanticscholar.org/bc43/f6bccb18a5a4892daa8e66756e0a684e7f5c.pdf">A framework for a mechanical translation between Japanese and English by analogy principle</a>, Nagao 1984</p><p>[8] EBMT figure from <a href="https://hal.archives-ouvertes.fr/hal-00260994/document">Purest ever example-based machine translation: detailed presentation and assessment</a>, Y. Lepage, E. Denoual, Machine Translation, Springer Verlag, 2007, pp.251–282. hal-00260994</p><p>[9] <a href="https://dl.acm.org/doi/10.3115/991635.991651">A Statistical Approach to Language Translation</a>, P. Brown, J. Cocke, S. Della Pietra, V. Della Pietra, F. Jelinek, R. Mercer, P. Roosin, COLING, 1988.</p><p>[10] <a href="http://www.fit.vutbr.cz/~imikolov/rnnlm/rnnlm-demo.pdf">RNNLM — Recurrent Neural Network Language Modeling Toolkit</a>, T. Mikolov, S. Kombrink, A. Deoras, L. Burget, J. Černocký, 2011</p><p>[11] <a href="https://www.aclweb.org/anthology/D13-1176.pdf">Recurrent Continuous Translation Models</a>, N. Kalchbrenner, P. Blunsom, 2013</p><p>[12] <a href="https://papers.nips.cc/paper/5346-sequence-to-sequence-learning-with-neural-networks.pdf">Sequence to Sequence Learning with Neural Networks</a>, I. Sutskever, O. Vinyals, Q. Le, 2014</p><p>[13] <a href="https://arxiv.org/abs/1409.0473">Neural Machine Translation by Jointly Learning to Align and Translate</a>, D. Bahdanau, K. Cho, Y. Bengio, 2014</p><p>[14] <a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a>, A. Vaswani, N. Shazeer, N. Parmar, J. Uszkoreit, L. Jones, A. Gomez, L. Kaiser, I. Polosukhin, 2017</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d5c09d8a5b7e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/a-brief-history-of-machine-translation-paradigms-d5c09d8a5b7e">A brief history of machine translation paradigms</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[Is the future of Neural Networks Sparse? An Introduction (1/N)]]></title>
            <link>https://medium.com/huggingface/is-the-future-of-neural-networks-sparse-an-introduction-1-n-d03923ecbd70?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/d03923ecbd70</guid>
            <category><![CDATA[transformers]]></category>
            <category><![CDATA[cuda]]></category>
            <category><![CDATA[sparse-matrix]]></category>
            <category><![CDATA[openai]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[François Lagunas]]></dc:creator>
            <pubDate>Tue, 04 Feb 2020 17:08:58 GMT</pubDate>
            <atom:updated>2020-02-04T17:09:54.360Z</atom:updated>
            <content:encoded><![CDATA[<h4>From principles to real-world library support.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7e9p9imPoRBYh5WYF3FFrQ.png" /><figcaption>TLDR: Yes</figcaption></figure><h4><strong>Hi, I am François Lagunas.</strong></h4><p>I am doing Machine Learning research, and I have been working for the last months on using sparse matrices, especially in Transformers. The recent <a href="https://openai.com/blog/openai-pytorch/"><strong>announcement</strong></a> that <strong>OpenAI</strong> is porting its <a href="https://openai.com/blog/block-sparse-gpu-kernels/"><strong>block sparse toolbox</strong></a> in <strong>PyTorch</strong> is really big news:</p><blockquote>“We are in the process of writing PyTorch bindings for our highly-optimized blocksparse kernels, and will open-source those bindings in upcoming months”</blockquote><p>I was talking about it with the outstanding <a href="https://huggingface.co/">Hugging Face</a> team, (I am one of their early investors), and I wanted to share with you my excitement!</p><h3>What is a Sparse Matrix?</h3><p>A <strong><em>sparse</em></strong> matrix is just a matrix with some zeros. Usually, a lot of them. So every place you are using a <strong><em>dense matrix</em></strong>, in a linear layer, for example, you could be using a sparse one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tf99LCAMrO70WAO4tkgFBw.png" /><figcaption>Matrices with increasing sparsity</figcaption></figure><p>The <strong><em>sparsity</em></strong> of the matrix is the fraction of zeros against the size of the matrix</p><p><strong>The pros?</strong> If you have a lot of zeros, you don’t have to compute some multiplications, and you don’t have to store them. So you <strong><em>may</em></strong> gain on size and speed, for training and inference (more on this today).</p><p><strong>The cons? </strong>Of course, having all these zeros will probably have an impact on network accuracy/performance. But to what extent? You may be surprised.</p><h3>Where are they from?</h3><p>The first researchers/engineers to use sparse matrices were <a href="https://en.wikipedia.org/wiki/Finite_element_method">Finite Elements</a> users.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/760/1*IvRIZEjC7kgUBuozUFlSng.png" /><figcaption>A 2D mesh (roof of Omni Coliseum, Atlanta) and its finite element matrix (<a href="https://www.cise.ufl.edu/research/sparse/matrices/HB/bcsstk14.html">source</a>).</figcaption></figure><p>When you have to deal with large physical simulations, you get a large graph of interconnected vertices.</p><p>Each vertex is a point of your system, and each edge connects two vertices. That means that these <strong>two points</strong> will have some <strong>influence</strong> on each other in the model. And so there is a <strong>non-zero</strong> value in the matrix that describes the graph.</p><p>This last sentence sums it up: you need non-zero values in the matrix when two dimensions are interacting in some way.</p><p><strong>Now getting back to ML, you should ask yourself the same question: are all the dimensions of my input vector interacting with all the others? </strong>Usually not. So going sparse maybe useful.</p><p>We have actually a very good, and famous, example of a successful trip to sparse-land: <strong>convolutional layers</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/627/1*3WLh11vam1ktq7kWJ9aKpg.jpeg" /><figcaption>Learned convolutional filters. From <a href="http://cs231n.github.io/convolutional-networks/">http://cs231n.github.io/convolutional-networks/</a></figcaption></figure><p>Convolutional layers are a smart and efficient way to implement a sparse transformation on an input tensor.</p><p>When processing images, it comes down to two things:</p><p><strong>Sparsity</strong>: the transformation is local → each output pixel should depend on a few neighboring input pixels.</p><p><strong>Invariance</strong>: the transformation does not depend on the position in the image</p><p>Then you just add the constraint that the transformation is linear: if you were to represent this transformation, you would get a HUGE matrix with only a few non-zeros. But of course, the right way to do this is to do a multiplication of the input tensor with a small set of small matrices (each square in the image before).</p><p>The importance of convolutions in today’s ML success is obvious. But you can see that <strong>finding a clever way to make things sparse sounds like a good recipe to save time and space.</strong></p><h3>Where are they useful?</h3><p>Convolutions are already an efficient form of sparsity, so you could try to make them <a href="https://arxiv.org/abs/1902.05967">even</a> more <a href="http://arxiv.org/abs/1907.04840">sparse</a>, but some other networks contain much larger matrices that may benefit from sparsity: Transformers.</p><p>And those are getting bigger and bigger. We have greatly exceeded the 1 billion parameters in 2019, and it’s not stopping here. The cost to train and to use those networks is getting unpractical, so every method to reduce their size will be welcome.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*m3oTlwLmwuXuBCVg.jpg" /><figcaption>From <a href="https://devblogs.nvidia.com/training-bert-with-gpus/">https://devblogs.nvidia.com/training-bert-with-gpus/</a></figcaption></figure><h3>Why the OpenAI announcement is so important?</h3><p>So, if everything is fine in sparse-land, we should all be trying sparse matrices, shouldn’t we?</p><p>Yes. But there is this stupid thing called <strong>implementation</strong>. It’s easy to see the theoretical improvements we could get with sparse compute. But the support in libraries is quite … sparse.</p><p>PyTorch <a href="https://github.com/soumith">developers</a>, for example, have done a <strong>significant</strong> <strong>effort</strong> to support sparse compute. But there is still a big gap in performance between dense and sparse matrices operations, which defeats the whole purpose of using them. Even memory usage is quite large: sparsity has to be more than 80% to save some room on sparse matrices (more on that in my next post). Even basic serialization was broken before version 1.4. The reason is that the underlying libraries (for example cuSPARSE) are not doing a great job because the problem is ill-suited to the way GPU works.</p><p>So the <strong>OpenAI</strong> <strong>announcement</strong> on their block sparse tools is <strong>very</strong> <strong>good</strong> <strong>news</strong> for those who want to use sparse ops without sacrificing training speed (and it looks like some <a href="https://github.com/openai/blocksparse/issues/2">people</a> have been waiting for some time now). And we are not talking about a few percents.</p><blockquote>“Our kernels typically performed <strong>one or two orders of magnitude faster</strong> in terms of GFLOPS.”</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/664/1*qXMoK7emiT7J6CA_3O29_A.png" /><figcaption>From OpenAI <a href="https://d4mucfpksywv.cloudfront.net/blocksparse/blocksparsepaper.pdf">blocksparse paper</a></figcaption></figure><p>(The worst thing is that the <a href="https://d4mucfpksywv.cloudfront.net/blocksparse/blocksparsepaper.pdf">paper</a> concludes that cuBLAS is faster that cuSPARSE even with very sparse matrices. How sad.)</p><p>The magic keyword here is “<strong>block</strong>”. <strong>It’s hard to implement general sparse matrice computations on GPUs in an efficient way</strong>. But it gets much easier if you add a “reasonable” constraint on the form of the matrices: their non-zeros should be grouped in small fixed-size blocks, and that makes GPU processing much easier to parallelize efficiently. Typically 8x8, 16x16 or 32x32 blocks, 16x16 already giving a very good performance, with 32x32 giving a slightly better one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/338/1*rHFMCfJ8Td-vhJi0e0zAQw.png" /><figcaption>A 8-block-sparse matrice</figcaption></figure><p>Of course, the “block” constraint may be crippling some sparsification algorithms, or at least it would require some changes to take it into account.</p><p>But at least we can play with large high sparsity matrices, and the block constraint may not be a big issue: if you think about it, it means that there is <strong>some locality in the dimensions</strong>, and that sounds a quite reasonable constraint. That’s the same reason band matrices have been useful in the past (finite difference, finite elements), and it was a much stronger constraint.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/338/1*zknKSiBQpsppvjDJFIFbqw.png" /><figcaption>Band matrix</figcaption></figure><h3>Conclusion</h3><p>I hope I have convinced you that 2020 will be the sparse network year (it already has two zeros, that’s a sign).</p><p><strong>Next time </strong>for those who are curious about what happens when they are using some CUDA based PyTorch code, we’ll dig a bit deeper in <strong>GPU internals</strong>, (and we will understand<strong> why block sparse code is outrunning sparse code by a large margin</strong>).</p><p><strong>This article series will continue on the different techniques that have been proposed to make sparse networks, and what are the potential long term benefits.</strong></p><h4>More reading</h4><p>First, here is a <a href="https://towardsdatascience.com/sparse-matrices-in-pytorch-part-2-gpus-fd9cc0725b71"><strong>study</strong></a><strong> of PyTorch sparse performance.</strong></p><p>If you want to have a very detailed review of <strong>different complementary approaches to network size reduction</strong>, and not just about sparse ones, you should definitely read <a href="http://mitchgordon.me/machine/learning/2020/01/13/do-we-really-need-model-compression.html">this article</a>.</p><p>And if you want to <strong>create illustrations like the header of this blog post</strong>, you will find the code I used on my <a href="https://github.com/madlag/medium_posts/tree/master/sparse_matrices_1"><strong>github</strong></a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d03923ecbd70" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/is-the-future-of-neural-networks-sparse-an-introduction-1-n-d03923ecbd70">Is the future of Neural Networks Sparse? An Introduction (1/N)</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[ Encoder-decoders in Transformers: a hybrid pre-trained architecture for seq2seq]]></title>
            <link>https://medium.com/huggingface/encoder-decoders-in-transformers-a-hybrid-pre-trained-architecture-for-seq2seq-af4d7bf14bb8?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/af4d7bf14bb8</guid>
            <category><![CDATA[naturallanguageprocessing]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Rémi Louf]]></dc:creator>
            <pubDate>Tue, 03 Dec 2019 13:10:39 GMT</pubDate>
            <atom:updated>2019-12-04T20:18:37.735Z</atom:updated>
            <content:encoded><![CDATA[<h4>How to use them with a sneak peak into upcoming features 🕵️‍♀️</h4><p>Our <a href="https://github.com/huggingface/transformers">Transformers</a> library implements many (11 at the time of writing) state-of-the-art transformer models. It is used by researchers and practitioners alike to perform tasks such as text classification, named entity recognition, question answering or text generation. Its API is compatible with both PyTorch and Tensorflow.</p><p>While many recent models have focused on single-stack architectures, encoder-decoders have come under the spotlight again recently, notably with Facebook’s <a href="https://arxiv.org/abs/1910.13461">BART</a> and Google’s <a href="https://arxiv.org/abs/1910.10683">T5</a>.</p><p>This post briefly goes through the (modern) history of transformers and the comeback of the encoder-decoder architecture. I will walk you through the implementation of encoder-decoders in the <a href="https://github.com/huggingface/transformers">transformers library</a>, show you can use them for your projects, and give you a taste of what is coming in the next releases.</p><h3>Hello 👾 Transformers</h3><p>The transformer storm began with <a href="https://arxiv.org/abs/1706.03762">“Attention is all you need”</a>, and the architecture proposed in the paper featured both an encoder and a decoder; it was originally aimed at translation, a Seq2Seq task. Its principal innovation compared to RNNs was to stack layers of bidirectional attention so every token can attend to every other token.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*tkC_NqylGH4hv2rTLPOf5A.png" /><figcaption>The original transformer architecture — that you have probably seen everywhere — has an encoder and decoder stack.</figcaption></figure><h3>🚀 The rise of single-stack architectures</h3><p>Following this, two papers came and further disrupted model architectures:</p><ol><li><a href="https://openai.com/blog/language-unsupervised/">GPT</a> from OpenAI</li><li><a href="https://arxiv.org/abs/1810.04805">BERT</a> from Google AI Language</li></ol><h4>👋 GPT</h4><p>The authors of GPT completely dropped the decoder of the original Transformer. They left us with this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/196/1*1V1a2hwijarJ9F7x2L3uDg.png" /><figcaption>Our poor transformer cut in half.</figcaption></figure><p>The authors trained the model by teaching it a language model, the probability distribution of possible sequences, in an unsupervised way. They did so by factorizing the distribution in a particular way:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/825/1*fw1WeE18wHYhYhQciUtpBg@2x.jpeg" /></figure><p>Which is mathematically trivially true: the probability of a sequence is the product of the probabilities of the tokens conditioned on the previous tokens. Note that this is not the <em>only </em>possible factorization, just one that seems particularly useful.</p><p>However, encoders are stacks of self-attention layers; everyone can attend to everyone and at the top of the encoder, the probability of each token will depend on every other token. How can the model learn the language model above?</p><p>The authors used a trick: the attention mask. Given a query <em>Q</em>, keys <em>K</em> and value <em>V</em> the output of (single-headed) attention layer reads:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/577/1*gdxAAz2fO7fs0-SUXOeBHg@2x.jpeg" /><figcaption>Attention mechanism with masking. The mask specifies which positions the output can attend to by forcing the output of the softmax to 0 if the position cannot be attended to.</figcaption></figure><p>The idea is to add a matrix that will “forbid” tokens (say words) to attend to one another. The following mask is used in GPT to prevent tokens to attend to tokens later in the sequence:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/712/1*6YqQivC_xJHNPeGTP-bjkg@2x.jpeg" /><figcaption>Left-to-right mask. For a given token in the sequence, we assign a mask value of 0 for this token and the preceding ones; a value of minus infinity for the later ones. As a result tokens can only attend to tokens preceding them in the sequence.</figcaption></figure><p>Using this mask you can train the model by making each token in the sequence predict the next one, and you can generate new sequences <a href="https://en.wikipedia.org/wiki/Autoregressive_model">in an auto-regressive way.</a> While the generation abilities are <a href="https://transformer.huggingface.co/doc/gpt">nothing short of amazing</a>, natural language understanding (NLU) is not GPT’s strong suit. That is where BERT entered the stage and took the NLP world by a storm.</p><h4>👋 BERT</h4><p>BERT, unlike GPT, does not use any mask trick during pre-training. It is the pre-training task that pulls all the weight.</p><p>Instead of teaching the model to predict the next word in a sentence, it masks a fixed proportion of tokens at random in a sequence and trains the model to recover these masked words (this is a <a href="https://en.wikipedia.org/wiki/Cloze_test">Cloze test</a> used, among other things, to evaluate people’s abilities in a foreign language). This pre-trained model can then be fine-tuned on many language understanding tasks such as named entity recognition, question answering and text classification. BERT thus achieved a qualitative jump in many NLU benchmarks.</p><p>As the figure below shows, many of the papers that followed are iterations on the foundations laid by BERT and GPT:</p><ul><li>A transformer encoder;</li><li>Various pre-training tasks and associated attention masks.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gBxbRUOOJPHIjPfxGagR-g.png" /><figcaption>Not all models implement the Encoder-Decoder architecture; they are actually only becoming popular now. Transformer-XL, GPT2, XLNet and CTRL approximate a decoder stack during generation by using the hidden state of the previous state as the key &amp; values of the attention module. Side note: all these ☝️ models are implemented in the <a href="https://github.com/huggingface/transformers">transformers library</a> or will be soon.</figcaption></figure><p>Yet every task cannot be reduced to solely a text generation task or a NLU task. Some tasks require <em>both understanding and generation capabilities.</em> For instance<em>:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/764/1*yxvvucHmjcLneIUn-rylKw@2x.jpeg" /><figcaption>Me reaching the limits of my drawing skills.</figcaption></figure><p>In these situations, what we would like the model to learn is not only the probability of the generated sequence, but the probability of this sequence given another sequence:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*BNleFKpOSHrFKPDY0eL9DQ@2x.jpeg" /><figcaption>Language model and Seq2Seq language models. Sometimes the distinction is pedantic, sometimes it’s not.</figcaption></figure><p>In a plot twist, the authors of <a href="https://arxiv.org/abs/1901.07291">XLM</a> and <a href="https://arxiv.org/abs/1905.03197">UniLM</a> managed to fit these two tasks <em>in a single encoder</em>. How? With a smart use of embeddings (XLM, for translation) or a clever mask trick (UniLM)!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/1*2LdzOzFyAEt-uRr8LEGJdQ@2x.jpeg" /><figcaption>The prefix mask as defined in the UniLM paper. Words in the first sequence can attend to any other word in this sequence; words in the second sequence can attend to every word in the first sequence and only the preceding words in their sequence.</figcaption></figure><h3>👋 The comeback of Encoder-decoder architectures</h3><p>So why should we care about Encoder-decoder architecture if one, smaller, architecture does the job very well? Can it even do what the smaller architecture does?</p><p>The authors of the <a href="https://arxiv.org/abs/1910.10683">T5</a> paper recently answered the last question with the affirmative; they even perform extremely well. Building on <a href="https://arxiv.org/abs/1806.08730">previous</a> <a href="https://openai.com/blog/better-language-models/">ideas</a>, they proposed a scheme to map any natural language understanding task to a text-to-text task. (read the paper if you have time, you won’t regret it).</p><p><em>To answer the first question, I would say that there is one thing that might be much easier to do with encoder-decoders: transfer learning on every task that can be mapped to a translation task.</em></p><p>(note: these are speculations)</p><p>Say you have a pre-trained model in language A, a pre-trained model in language B. You could theoretically use one as the encoder, the other as the decoder and fine-tune the model on a translation task.</p><p>This is not only true for natural language. Take the example of a data scientist bored from having to write simple SQL queries whenever asked, and a boss who couldn’t care less about using a frontend to answer their own questions. They could pre-train BERT on SQL, use a pre-trained weights for the English languages, finetune on a year worth of requests. Et voilà!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/645/1*sZkenLBh9_KYGtpTgAXxvA@2x.jpeg" /><figcaption>Boss2SQL (patent pending). The encoder is a Bert model pre-trained on the English language (you can even use pre-trained weights!), the decoder a Bert model pre-trained on the SQL language. Fine-tune the model on year’s worth of requests and you will never have to write a single line of SQL again.</figcaption></figure><p>Now imagine if we had a bank of BERTs pre-trained in many, many languages. Writing translators would become much easier, and thanks to transfer learning this would make the whole translation business easier to scale.</p><blockquote>Encoder-decoder architectures could theoretically allow us to compound pre-training efforts to do transfer learning on a vast number of translation tasks.</blockquote><h3>HuggingFace 🤗❤️ Seq2Seq</h3><p>When I joined HuggingFace, my colleagues had the intuition that the transformers literature would go full circle and that encoder-decoders would make a comeback. We thought that we should anticipate this move, and allow researchers to easily implement such models with our library.</p><p>Well, everything moves fast in NLP these days: within a few weeks <a href="https://arxiv.org/abs/1910.13461">BART</a> and <a href="https://arxiv.org/abs/1910.10683">T5</a> were published; both are encoder-decoder architectures showcasing all sorts of new state-of-the-art results.</p><p>Allowing the integration was fairly straightforward. All we needed to do was to modify the library to allow the existing models (encoders) to also act as decoders. Which meant:</p><ul><li>Adding a cross-attention layer, whose weights will be randomly initialized;</li><li>Transforming the attention mask on the decoder input as a left-to-right mask adapted for generation tasks.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/715/1*KlsFo8Kcbc46d0a6GED0sQ@2x.jpeg" /><figcaption>What happens schematically in our encoder-decoder architectures. The encoder has bi-directional layers of self attention; the decoder is in fact the same model to which we add layers of cross-attention and causal masks when it is used as a decoder. It allows us to leverage the models already implemented by the community with very little code.</figcaption></figure><h4>🔧 Use encoder-decoder architectures to build amazing things🔧</h4><p>We defined a simple API that allows you to initialize encoder-decoders with pre-trained encoders and decoders. We call these hybrid pre-trained architectures the <strong>combiners:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wvx2h1-JVvorZ8eYA_cBOg.png" /></figure><p>They allow you to combine, for instance, the NLU superpowers of BERT with the generation superpowers of GPT-2.</p><p>Thanks to transformers being central in the ecosystem and making state-of-the-art models available, encoder-decoder models benefit from a substantial compounding effect: <strong>11 models implemented in the library means 121 possible combinations for you to start building cool things. When you account for all the different languages the numbers become astronomical.</strong></p><blockquote>The combiners are where the open-source philosophy of Hugging Face and its amazing community start to really shine.</blockquote><p>Only need the superpowers of one model? No worries! We created a simpler API for you:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MkddHHjEsOvGRJ1c8V0qpw.png" /></figure><p>Knowing how to pass the arguments of the two models can be (the only) tricky (step), so here is a reference you can use for your implementation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MruApzwv38gkn98-ikkNNw.png" /><figcaption>To pass keyword arguments to the encoder and the decoder you need to respectively prefix them with `encoder_` and `decoder_`. Keyword arguments that are not prefixed will be passed to both models.</figcaption></figure><p>We recognize there are situations (notably for finetuning) in which you want to randomly initialize either the encoder or decoder. Easy:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sZH0UC8UDxR-eL_F3IhksA.png" /><figcaption>Initialize an encoder-decoder model with a pre-trained BERT encoder and a randomly initialized GPT2 XL</figcaption></figure><p>Finally, if you want to share the weights between the encoder and the decoder, you have access to both architecture via model.encoder and model.decoder. This is very application-specific, so we do not provide an API for this. Don’t hesitate to <a href="https://github.com/huggingface/transformers/issues">open an issue</a> if you need help.</p><p><strong>All this is all available since the 2.2.0 release of the </strong><a href="https://github.com/huggingface/transformers"><strong>transformers library</strong></a>. For the moment, only BERT has been adapted to work as a decoder, but we’re working our way through the other ones!</p><blockquote>What combiner would you like most to play with? Let us know in the comments 👇 or ping us on Twitter <a href="https://twitter.com/huggingface">@huggingface</a></blockquote><h4>⌨️ Generate text with Transformers ⌨️</h4><p>When we started working on an illustrative example, we realized that the text generation capabilities of the libraries were limited (although we do have an <a href="https://github.com/huggingface/transformers/blob/master/examples/run_generation.py">awesome example script</a> and an <a href="https://transformer.huggingface.co/">online demo</a> of text generation). Since they are essential for Seq2Seq tasks, we started working on a simple module for you to generate sequences. The API is subject to change, but you should be able to generate text as in the following:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*my6KF5Wa2AFa54tPnQp3Yw.png" /><figcaption>Sample sequences at various temperatures using k-filtering, nucleus sampling and applying repetition penalty.</figcaption></figure><p>It will include at the very least sampling for both single-stack (GPT, XLNet, CTRL, XLM, Transfo-XL, GPT2) and encoder-decoder stacks. The following example of transformers playing <a href="https://en.wikipedia.org/wiki/Exquisite_corpse"><em>exquisite corpse</em></a> was generated using an early version of this module. Look what 10 lines of code can do for you:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/976/1*NzMcdNc_bMZUpUYgl01n8Q.gif" /><figcaption>Transformers playing exquisite corps, a game invented by surrealists in the 1930s. Each algorithm is given the sequence written by the previous one, leading to an unexpected result.</figcaption></figure><p>Your GPU prefers beam search? We’ve got you covered:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z5wAujYSH6h1_LUx2iG56w.png" /></figure><p>And this is only scratching the surface of what is possible in text generation.</p><blockquote>If you would like to see more state-of-the art methods to generate text in the library, let us know in the comments 👇 or ping us on Twitter <a href="https://twitter.com/huggingface">@huggingface</a></blockquote><h4>📄 Abstractive summarization with Transformers 📄</h4><p>Abstractive summarization has a <a href="https://arxiv.org/abs/1908.08960">attracted</a> a <a href="https://arxiv.org/abs/1908.08345">lot</a> of <a href="https://arxiv.org/abs/1907.12461">attention</a> lately in the research literature. We have also had a substantial amount of feedback from the community. Users who are just curious about the current state-of-the-art but also practitioners who would be happy to use for it for their jobs.</p><p>We listened, so keep an eye on <a href="https://twitter.com/huggingface">Twitter</a> for the release 😉</p><blockquote>At 🤗 HuggingFace we care deeply about the needs and aspiration of our community. What are the applications of Seq2Seq models that you find most interesting? Let us know in the comments 👇 or ping us on Twitter <a href="https://github.com/huggingface">@huggingface</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af4d7bf14bb8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/encoder-decoders-in-transformers-a-hybrid-pre-trained-architecture-for-seq2seq-af4d7bf14bb8">🦄🤝🦄 Encoder-decoders in Transformers: a hybrid pre-trained architecture for seq2seq</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 Write With Transformer]]></title>
            <link>https://medium.com/huggingface/how-to-write-with-transformer-5ee58d6f51fa?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/5ee58d6f51fa</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[nlp]]></category>
            <dc:creator><![CDATA[Jamie Brew]]></dc:creator>
            <pubDate>Tue, 26 Nov 2019 16:56:51 GMT</pubDate>
            <atom:updated>2019-11-26T16:56:50.862Z</atom:updated>
            <content:encoded><![CDATA[<h3>How to Write With an Artificial Intelligence</h3><h4>Creative Writing 1010101</h4><p>Text-generating neural networks like OpenAI’s GPT-2 often raise questions about the dangers of fake text: Can a machine write text that’s <em>convincingly, deceptively human?</em></p><p>As a comedy writer, I’m more interested in the opposite question: Can a machine produce words that <em>no human would ever write? </em>Can it help me write things that <em>I would never write</em>?</p><p><a href="https://transformer.huggingface.co/">Write With Transformer</a> is a web app that lets you write in collaboration with a text-generating neural network. It’s a demo for <a href="https://github.com/huggingface/transformers">Transformers</a>, a state-of-the-art software library developed and maintained by <a href="https://huggingface.co/">Hugging Face</a>.</p><p>This post covers the basics of the app, a few strategies for using it as a writer and some more advanced controls.</p><h3>Basic controls</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LOp4A75dOYTJgBWDCN290g.png" /><figcaption><a href="https://transformer.huggingface.co/doc/gpt2-large">https://transformer.huggingface.co/doc/gpt2-large</a></figcaption></figure><p>Write With Transformer is a normal text editor with one twist: At any time, you can appeal to GPT-2 for suggestions.</p><p>To make them, the machine considers all possible next <em>words, </em>chooses one of those words, considers all possible next words after <em>that</em>, and repeats until it runs out of time. It does this in three different places at once, which is how it arrives at three different suggestions.</p><p>You can read more about what’s going on under the hood <a href="https://medium.com/huggingface/scaling-a-massive-state-of-the-art-deep-learning-model-in-production-8277c5652d5f">here</a>.</p><p>For now, here are the main predictive text commands to know:</p><ul><li>Press <strong>Tab </strong>to ask the neural network for 3 suggestions to continue what you have written so far.</li><li>Keep pressing <strong>Tab </strong>as many times as you like to repeatedly request three more suggestions.</li><li>Use the <strong>arrow keys </strong>and<strong> enter</strong> or <strong>click </strong>to select one of the suggestions.</li></ul><h3>Writing methods</h3><p>Here’s a list of just a few approaches you can take to using Write With Transformer.</p><h4>1. Blind devotion</h4><p>To remove yourself from the equation and see what the neural net might generate “on its own”, you can decide from the start that you’ll always take the first suggestion. If you start from a blank page, the first few words can be disorienting, like falling asleep and waking up in a random corner of the internet…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*gLAaO6Y_an4QGyDnXOwVTg.gif" /></figure><h4>2. Branching path</h4><p>Limit yourself to the three options supplied by the app, letting it tell you a choose-your-own-adventure tale about whatever the internet had on its mind when the training data was collected…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*CyGt9fsmOj2n6mYSF58aJQ.gif" /></figure><h4>3. Tag team</h4><p>Prompt the machine with a thought, then let its response prompt you. Go back and forth as cowriters, or warring Wikipedia editors…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*CYheiqRHJBDIwfnj6iBR8Q.gif" /></figure><h4>4. Rewrites</h4><p>Bring in familiar text from somewhere else, delete the end of it and see how Transformer would have completed it…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*2BfhKbZ5x0AhZOw2F6kGVA.gif" /><figcaption>Note: I got curious about the<em> second </em>option, which seems to be the start of a full-scale FAQ about chickens. So I opened the app again and kept going. You can read the FAQ <a href="https://transformer.huggingface.co/share/QJMQDroNaK">here</a>.</figcaption></figure><h4>5. Continuing lists</h4><p>Transformers are great at picking up patterns in series of items. This makes it especially fun to prompt them with incomplete lists.</p><p><strong><em>Try prompting with the start of a horizontal list, like this:</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1012/1*suLQ3yu0lNzE2FoZTxTKnw.png" /></figure><p><strong><em>Or the start of a vertical list, like this:</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/726/1*3AWM0VTi59WmG0rvA8iicA.png" /></figure><h4>6. Freeform</h4><p>The repetitive structure of lists lends itself to transformer writing. The same applies to any kind of writing with a recognizable, consistent structure. Try interviews, step-by-step instructions, or invent your own new format and see what patterns the neural net picks up.</p><h3>Advanced settings</h3><p>You can adjust four settings in the bottom left corner of the app, controlling <strong>Model size</strong>, <strong>Top-p</strong>, <strong>Temperature </strong>and <strong>Max time</strong>.</p><p>Let’s look at each of these in turn.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/278/1*X3H9IBZ0AxtctjBceXGw2w.png" /></figure><h4>Model size</h4><p>Larger models have more parameters, which roughly means they can remember patterns from their training set in greater detail. This means larger models offer suggestions that are <strong>more specifically related to the prompt</strong>.</p><p>Suggestions from larger models are also <strong>shorter. </strong>This is because the models run slower, so within the time window set by Max time (see below), they can generate fewer words.</p><h4>Temperature</h4><p>The most poetically named parameter, temperature controls how adventurous the algorithm is with its word choices. Turning the temperature up makes suggestions wilder and less predictable.</p><p><strong><em>Here’s a typical continuation at low temperature:</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/848/1*TcA_7-2TkdKTAg_ipJ7dqw.png" /></figure><p><strong><em>And here’s one at high temperature:</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zNZ5NKU7xS2_kLB5zpYg9g.png" /></figure><h4>Top-p</h4><p>This setting controls how broad a range of continuations are considered. Set it high to consider all continuations. Set it low to just consider likely continuations. The overall effect is similar to temperature, but more subtle.</p><h4>Max time</h4><p>This controls how long the suggestions are. The model will always generate as many words as it has time for. To ask for just a few words, set the maximum time to low. For longer suggestion blocks, choose a small model size and a high maximum time.</p><h3>Sharing your writing</h3><p>Write With Transformer has two built-in sharing mechanisms.</p><ol><li><strong>Screenshot</strong></li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/284/1*eJvhA84WTnT-lP_fZ4FUyg.png" /></figure><p>For short paragraphs, this button exports your document to an image, with <strong>Transformer-written text rendered in bold.</strong></p><p><strong>2. Save and publish</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/266/1*0gBV7OXYfMio6Zh2wGXRaA.png" /></figure><p>Ideal for longer documents. This option gives you links that let you return to editing a document later, or share with friends, who can read it or edit further themselves.</p><p><strong><em>For example: </em></strong><em>Here’s the </em><a href="https://transformer.huggingface.co/share/QJMQDroNaK"><em>Chicken FAQ</em></a><em> I created from the document that started “Why did the chicken cross the road?”</em></p><p><strong>3. Duplicate and edit</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/242/1*P2gBKMtuHK7YGd-iAyW5xQ.png" /></figure><p>Starting from a shared document (like the <a href="https://transformer.huggingface.co/share/QJMQDroNaK">Chicken FAQ</a>) click the Duplicate &amp; Edit button to do just that: create a copy that you can edit via any human-machine balance you choose.</p><p>Please, duplicate the FAQ and help me learn more about the chicken.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/592/1*KSZNvnJdYJ8PZ22o4peC_g.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5ee58d6f51fa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/how-to-write-with-transformer-5ee58d6f51fa">How To Write With Transformer</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[Benchmarking Transformers: PyTorch and TensorFlow]]></title>
            <link>https://medium.com/huggingface/benchmarking-transformers-pytorch-and-tensorflow-e2917fb891c2?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/e2917fb891c2</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[pytorch]]></category>
            <category><![CDATA[transformers]]></category>
            <category><![CDATA[nlp]]></category>
            <dc:creator><![CDATA[Lysandre Debut]]></dc:creator>
            <pubDate>Fri, 18 Oct 2019 15:02:09 GMT</pubDate>
            <atom:updated>2020-09-02T14:19:03.892Z</atom:updated>
            <content:encoded><![CDATA[<p>Our <a href="https://github.com/huggingface/transformers">Transformers library</a> implements several state-of-the-art transformer architectures used for NLP tasks like text classification, information extraction, question answering, and text generation. It is used by researchers and companies alike, offering PyTorch and TensorFlow front-ends.</p><p>Since the release of our TensorFlow implementation, we have been working on productionizing the models and making them available on TPU, slowly gearing ourselves towards performance.</p><p>This post compares the performance of our models in several environments. We compare them for <em>inference</em>, on CPU and GPU for PyTorch (1.3.0) as well as TensorFlow (2.0). As several factors affect benchmarks, <strong>this is the first of a series of blogposts concerning benchmarks and subsequent performance optimizations</strong>.</p><p>In addition to this post, we are creatingBenchmark section in our documentation, which will evolve as we further work on our models and benchmark them in different settings.</p><h3>Results</h3><p>The results are visible in this <a href="https://docs.google.com/spreadsheets/d/1sryqufw2D0XlUH4sq3e9Wnxu5EAQkaohzrJbd5HdQ_w/edit#gid=0">Google Spreadsheet</a>. The average results are visible in the table below. The results are detailed in the discussion section.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/870/1*9TuA4ymG0bUqi_t4j3zZCw.png" /><figcaption>Average inference time</figcaption></figure><p>The N/A entries in the spreadsheet indicate either an out-of-memory error or an inappropriate sequence length. Transformer-XL does not have TorchScript results as it is not currently serializable by TorchScript.</p><p>In most cases, the TensorFlow and PyTorch models obtain very similar results, both on GPU and CPU. Down below is a short discussion concerning the results, both as a comparison between PyTorch and TensorFlow as well as a comparison between models.</p><h3>Measuring inference</h3><p>Inference time is an important metric when putting a model in production. In order to evaluate the inference times of our models, we compare them with different batch sizes and different sequence lengths. We compare the reasonable batch sizes [1, 2, 4, 8] with the sequence lengths [8, 64, 128, 256, 512, 1024] . The batch sizes remain small as we are exclusively looking at an inference setup. BERT and other similar models have a maximum sequence length of 512 or 256 (for CTRL) and will therefore not be measured on the last sequence lengths.</p><p>We test the results in two different environments:</p><ul><li>on CPU, using a GCP n1-standard-32 which has 32 vCPUs and 120GB of RAM. The CPU model is an Intel Xeon @ 2.3GHz.</li><li>on GPU, using a custom GCP machine that has 12 vCPUs, 40GB of RAM and a single V100 GPU (16GB VRAM).</li></ul><h4>Experiment details &amp; best practices</h4><p>In order to maximize performance, further optimizations are made:</p><ul><li>The Intel Xeon CPU on which we measure the CPU inference comes with AVX and AVX2 extensions. <strong>As TensorFlow requires to be compiled from source to leverage those extensions, we do so.</strong></li><li>We make sure we are not using TensorFlow’s eager mode by using tf.function and tracing the models beforehand.</li><li>We compare the inference with and without the library-dependant tools: TorchScript for PyTorch, and XLA (Auto-clustering) for TensorFlow with GPUs. <em>These two tools are detailed below.</em></li><li>We use the native Python module <a href="https://docs.python.org/2/library/timeit.html">timeit</a> to measure the inference time. We run each of our experiments with repeat=30 and number=3 . We then average over the 30 values to get the expected average inference time. <strong>Averaging over 30 values yields very stable results.</strong></li><li>We do not make use of production environments such as TFX, and we measure the models’ callable method: nn.Module.forward for PyTorch and tf.keras.layers.Layer.call for TensorFlow</li><li>We are careful to use the appropriate CUDA versions for both TensorFlow and PyTorch.</li></ul><h3>Discussion</h3><h4>PyTorch and TensorFlow</h4><p>Both libraries obtain similar results in most cases, with TensorFlow generally being a bit slower on CPU compared to PyTorch, but a bit faster on GPU:</p><ul><li>Across all models, on CPU, PyTorch has an average inference time of 0.748s while TensorFlow has an average of 0.823s.</li><li>Across all models, on GPU, PyTorch has an average inference time of 0.046s whereas TensorFlow has an average inference time of 0.043s.</li></ul><p><em>These results compare the inference time across all models by averaging the results. </em><strong><em>As a consequence, the larger the input size, the larger the impact on the final result</em></strong><em>. PyTorch runs out of memory when the input sizes are too large; those results are removed from all measures when averaging as it would skew the results towards PyTorch.</em></p><p>The PyTorch models tend to run out of memory earlier than the TensorFlow models: apart from the Distilled models, PyTorch runs out of memory when the input size reaches a batch size of 8 and a sequence length of 1024.</p><h4>TorchScript</h4><p>TorchScript is PyTorch’s way of creating serializable models that can run on different runtimes, with no need for Python dependencies, such as C++ environments. <strong>Our tests were done by tracing the model in Python and re-using that traced model in the same environment</strong>. We make sure to trace the model before measuring its inference by executing a forward pass beforehand.</p><p><strong>Disclaimer</strong>: <em>while TorchScript does not seem to be inherently created for speed-up in a Python environment, our results show that tracing the model with TorchScript can yield performance improvements.</em></p><p>TorchScript seems to be very dependent on the models and the input size (batch size * sequence length); as an example, using TorchScript yields a permanent performance boost on XLNet whereas its use may be questionable on XLM, where it increases performance in smaller input sizes but decreases performance in larger input sizes.</p><p>On average, an inference with a model traced with TorchScript is 20% faster than an inference with the same PyTorch non-traced model.</p><h4>XLA</h4><p>XLA is a linear algebra compiler that can accelerate TensorFlow models. We’re using it solely on GPU where it is based on <a href="https://www.tensorflow.org/xla#auto-clustering">TensorFlow’s Auto-clustering </a>which compiles some of our models’ subgraphs.</p><blockquote>The results are improvements in speed and memory usage: most internal benchmarks run ~1.15x faster after XLA is enabled.</blockquote><p>We obtain an increase in performance with all of our models when XLA is enabled. In some extreme cases, we obtain a decrease of 70% in inference time, especially in lower input sizes</p><h4>Models and their distilled version</h4><p>Distilled models shine in this test as being very quick to benchmark. Both of the Hugging Face-engineered-models, DistilBERT and DistilGPT-2, see their inference times halved when compared to their teacher models.</p><h3>Contributing</h3><p>As benchmarking on all different setups, with every tool, isn’t achievable by a single organization, we welcome benchmarks from the community. The Github user @<a href="http://github.com/tlkh">tlkh</a> has already contributed by benchmarking performances that could be achieved using AMP, XLA and distributed strategies on our TensorFlow models. <em>It is currently being added to the benchmarking section of the documentation.</em></p><h4>How to contribute</h4><p>If you would like to contribute, we have set up issues templates on our Github to make it easier. Feel free to <a href="https://github.com/huggingface/transformers/issues/new/choose">open an issue with your results</a>, or to open a pull request with your additions to the benchmark section of the documentation.</p><h4>Benchmarking script</h4><p>Accompanying the release of this blog post and the Benchmark page on our documentation, we add a new script in our example section: <a href="https://github.com/huggingface/transformers/blob/master/examples/benchmarks.py">benchmarks.py</a> , which is the script used to obtain the results detailed below. It can run benchmarks on TensorFlow, on PyTorch, using XLA or TorchScript and save the results to a CSV file.</p><h3>What’s next?</h3><p>Benchmarking our models is but the first step on our road to speed performance. We believe this introductory article may be of help when looking to compare the current state of our models, especially when looking at the difference between PyTorch and TensorFlow. As we delve in the production aspects of <a href="https://github.com/huggingface/transformers">Transformers</a> , we are bound to work on performance-oriented improvements.</p><p>Automated scripts, new architectures and custom TPU training for PyTorch and TensorFlow: keep an eye out for future releases!</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=e2917fb891c2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/benchmarking-transformers-pytorch-and-tensorflow-e2917fb891c2">Benchmarking Transformers: PyTorch and TensorFlow</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[ Smaller, faster, cheaper, lighter: Introducing DilBERT, a distilled version of BERT]]></title>
            <link>https://medium.com/huggingface/distilbert-8cf3380435b5?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/8cf3380435b5</guid>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[transformers]]></category>
            <category><![CDATA[distillation]]></category>
            <category><![CDATA[bert]]></category>
            <dc:creator><![CDATA[Victor Sanh]]></dc:creator>
            <pubDate>Wed, 28 Aug 2019 14:43:24 GMT</pubDate>
            <atom:updated>2020-08-31T15:02:49.872Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CVGvdYELFWlWmmOdTuRF4A.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@shubhamsharan?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Shubham Sharan</a> on <a href="https://unsplash.com/search/photos/teacher?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><h3>🏎 Smaller, faster, cheaper, lighter: Introducing DistilBERT, a distilled version of BERT</h3><blockquote><strong>2019, October 3rd — Update: </strong>We are releasing <a href="https://arxiv.org/abs/1910.01108">our NeurIPS 2019 workshop paper</a> describing our approach on DistilBERT with improved results: 97% of BERT’s performance on GLUE (<strong>the results in the paper superseed the results presented here</strong>). The approach is slightly different from the one explained in this present blog post so this blog post should be a good entry point to the paper! We applied the same method to GPT2 and are releasing DistilGPT2! Training code and pre-trained weights for DistilBERT and DistilGPT2 are available <a href="https://github.com/huggingface/transformers/tree/master/examples/distillation">here</a>. 🤗</blockquote><p>In the last 18 months, transfer learning from<strong> large-scale language models </strong>has significantly improved upon the state-of-the-art on pretty much every Natural Language Processing task.</p><p>Usually based on the Transformer architecture of <a href="https://arxiv.org/abs/1706.03762">Vaswani et al.</a>, these pre-trained language models keep getting<strong> larger and larger </strong>and<strong> </strong>being trained on <strong>bigger datasets</strong>. The latest model from Nvidia has <a href="https://venturebeat.com/2019/08/13/nvidia-trains-worlds-largest-transformer-based-language-model/">8.3 billion parameters</a>: 24 times larger than BERT-large, 5 times larger than GPT-2, while <a href="https://arxiv.org/abs/1907.11692">RoBERTa</a>, the latest work from Facebook AI, was trained on 160GB of text 😵</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IFVX74cEe8U5D1GveL1uZA.png" /><figcaption>Some people in the community question the relevance of keeping on training larger and larger Transformer especially when you take into account the financial and environmental cost of training. Here’s are some of the latest large models and their <strong>size in millions of parameters</strong>.</figcaption></figure><p>At Hugging Face, we experienced first-hand the growing popularity of these models as our <a href="https://github.com/huggingface/pytorch-transformers">NLP library</a> — which encapsulates most of them — got installed more than 400,000 times in just a few months.</p><p>However, as these models were reaching a larger NLP community, an important and challenging question started to emerge. <strong>How should we put these monsters in production?</strong> <strong>How can we use such large models under low latency constraints?</strong> Do we need (costly) GPU servers to serve at scale?</p><blockquote>For many researchers and developers, these can be deal-breaking issues 💸</blockquote><p>To build more privacy-respecting systems, we noticed an increasing need to have <a href="https://github.com/huggingface/swift-coreml-transformers">machine learning systems operate <strong>on the edge</strong></a> rather than calling a cloud API and sending possibly private data to servers. Running models on devices like your smartphone 📲 also requires<strong> light-weight, responsive and energy-efficient models!</strong></p><p>Last but not least, we are more and more concerned about the environmental cost of scaling exponentially computing requirements of these models.</p><blockquote>So, how can we reduce the size of these monster models<em>⁉️</em></blockquote><p>There are many techniques available to tackle the previous questions. The most common tools include <strong>quantization</strong> (approximating the weights of a network with a smaller precision) and <strong>weights pruning </strong>(removing some connections in the network). For these technics, you can have a look at the excellent <a href="https://blog.rasa.com/compressing-bert-for-faster-prediction-2/">blog post of Rasa</a> on quantizing BERT.</p><p>We decided to focus on <strong>distillation</strong>: a technique you can use to compress a large model, called the teacher, into a smaller model, called the student.</p><h3>⚗️ Knowledge Distillation — Transferring generalization capabilities</h3><p><em>Knowledge distillation</em> (sometimes also referred to as <em>teacher-student learning</em>) is a <strong>compression technique in which a small model is trained to reproduce the behavior of a larger model</strong> (or an ensemble of models). It was introduced by <a href="https://www.cs.cornell.edu/~caruana/compression.kdd06.pdf">Bucila et al.</a> and generalized by <a href="https://arxiv.org/abs/1503.02531">Hinton et al.</a> a few years later. We will follow the latter method.</p><p>In supervised learning, a classification model is generally trained to predict a gold class by maximizing its probability (softmax of logits) using the log-likelihood signal. In many cases, a good performance model will predict an output distribution with the correct class having a high probability, leaving other classes with <strong>probabilities near zero</strong>.</p><blockquote><strong>But, some of these “almost-zero” probabilities are larger than the others, and this reflects, in part, the generalization capabilities of the model.</strong></blockquote><p>For instance, a <em>desk chair</em> might be mistaken with an <em>armchair</em> but should usually not be mistaken with a <em>mushroom</em>. This uncertainty is sometimes referred to as the <strong>“dark knowledge” 🌚</strong></p><p>Another way to understand distillation is that it prevents the model to be too sure about its prediction (similarly to label smoothing).</p><p>Here is an example to see this idea in practice. In language modeling, we can easily observe this uncertainty by looking at the distribution over the vocabulary. Here are the top 20 guesses by BERT for completing this famous quote from the <em>Casablanca</em> movie:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X3SZ5AL75_z29XyuR4rpeQ.png" /><figcaption>The top 20 guesses from BERT (base) for the masked token. The Language model identified two highly probable tokens (day &amp; life) followed by a long tail of valid tokens.</figcaption></figure><h3>👯‍♂️ How can we copy this dark knowledge?</h3><p>In the <strong>teacher-student training</strong>, we train a student network to mimic the <strong>full output distribution</strong> of the teacher network (its knowledge).</p><blockquote>We are training the student to generalize the same way as the teacher by matching the output distribution.</blockquote><p>Rather than training with a cross-entropy over the hard targets (one-hot encoding of the gold class), we transfer the knowledge from the teacher to the student with a cross-entropy over the soft targets (probabilities of the teacher). Our training loss thus becomes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/249/1*GZkQPjKC_Wqx1F4Uu3FdiQ.png" /><figcaption>With <strong><em>t</em></strong><em> the logits from the teacher and </em><strong><em>s</em></strong><em> the logits of the student</em></figcaption></figure><p><strong>This loss is a richer training signal since a single example enforces much more constraint than a single hard target.</strong></p><p>To further expose the mass of the distribution over the classes, Hinton et al. introduce a <strong>softmax-temperature</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/233/1*BaVyKMXRWaudFvcI9So8MQ.png" /><figcaption>T is the temperature parameter.</figcaption></figure><p>When <em>T → 0</em>, the distribution becomes a Kronecker (and is equivalent to the one-hot target vector), when <em>T →+∞</em>, it becomes a uniform distribution. <strong>The same temperature parameter is applied both to the student and the teacher at training time, further revealing more signals for each training example</strong>. At inference, <em>T</em> is set to 1 and recover the standard Softmax.</p><h3>🗜Hands-on coding in PyTorch — Compressing BERT</h3><p>We want to compress a large language model using distilling. For distilling, we’ll use the <a href="https://en.wikipedia.org/wiki/Kullback–Leibler_divergence">Kullback-Leibler loss</a> since the optimizations are equivalent:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/692/1*ipPFl15_nrY_jQ8QsAMC0w.png" /></figure><p>When computing the gradients with respect to <em>q</em> (the student distribution) we obtain the same gradients. It allows us to leverage PyTorch implementation for faster computation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sZSjyGEbxkQfEgHRxCZTaw.png" /><figcaption>A Knowledge distillation training step in PyTorch. Copy the gist from <a href="https://gist.github.com/VictorSanh/db90644aae5094654db87f9769c2e5ae">here</a>.</figcaption></figure><p>Using the teacher signal, we are able to train a <strong>smaller language model, </strong>we call<strong> DistilBERT, </strong>from the <strong>supervision of BERT </strong>👨‍👦 (we used the English bert-base-uncased version of BERT).</p><p>Following Hinton et al., the training loss is a linear combination of the <em>distillation loss</em> and the masked <em>language modeling loss</em>. Our student is a small version of BERT in which we <em>removed the token-type embeddings and the pooler</em> (used for the next sentence classification task) and kept the rest of the architecture identical while reducing the numbers of layers by a factor of two.</p><blockquote>Overall, our distilled model, <strong>DistilBERT,</strong> has <strong>about half</strong> the total number of parameters of BERT base and retains 95% of BERT’s performances on the language understanding benchmark GLUE.</blockquote><blockquote><strong><em>❓Note 1</em> — Why not reducing the hidden size as well?<br></strong>Reducing it from 768 to 512 would reduce the total number of parameters by ~2. However, in modern frameworks, most of the operations are highly optimized and variations on the last dimension of the tensor (hidden dimension) have a small impact on most of the operations used in the Transformer architecture (linear layers and layer normalisation). In our experiments, the number of layers was the determining factor for the inference time, more than the hidden size.<br>Smaller does not necessarily imply faster…</blockquote><blockquote><strong><em>❓Note 2</em> — Some works on distillation like </strong><a href="https://arxiv.org/pdf/1903.12136.pdf"><strong>Tang et al.</strong></a><strong> use the L2 distance as a distillation loss directly on downstream tasks</strong>.<br>Our early experiments suggested that the cross-entropy loss leads to significantly better performance in our case. We hypothesis that in a language modeling setup, the output space (vocabulary) is significantly larger than the dimension of the downstream task output space. The logits may thus compensate for each other in the L2 loss.</blockquote><p>Training a sub-network is <strong>not only about the architecture. </strong>It is also about <strong>finding the right initialization for the sub-network to converge</strong> (see <a href="https://arxiv.org/abs/1803.03635">The Lottery Ticket Hypothesis</a> for instance). We thus initialize our student, DistilBERT<em>,</em> from its teacher, BERT, by taking one layer out of two, leveraging the common hidden size between student and teacher.</p><p>We also used a few training tricks from the recent <a href="https://arxiv.org/abs/1907.11692">RoBERTa paper</a> which showed that <strong>the way BERT is trained is crucial for its final performance</strong>. Following RoBERTa, we trained DistilBERT<strong> </strong>on very <strong>large batches</strong> leveraging gradient accumulation (up to 4000 examples per batch), with <strong>dynamic masking</strong> and <strong>removed the next sentence prediction objective</strong>.</p><p>Our training setup is voluntarily limited in terms of resources. We train DistilBERT<strong> </strong>on <strong>eight 16GB V100 GPUs for approximately three and a half days </strong>using the concatenation of Toronto Book Corpus and English Wikipedia (same data as original BERT).</p><p>The code for DistilBERT is adapted in part from Facebook <a href="https://github.com/facebookresearch/XLM">XLM</a>’s code and in part from our PyTorch version of Google AI <a href="https://github.com/google-research/bert">Bert</a> and is available in our <a href="https://github.com/huggingface/pytorch-transformers/">pytorch-transformers library</a> 👾 along with several trained and fine-tuned versions of DistilBert and the code to reproduce the training and fine-tuning.</p><h3>🎢 Model performances — Testing <em>DistilBERT</em></h3><p>We compare the performance of DistilBERT on the development sets of the <a href="https://gluebenchmark.com"><strong>GLUE benchmark</strong></a><strong> </strong>against two baselines: BERT base (DistilBERT’s teacher) and a strong non-transformer baseline from NYU: two BiLSTMs on top of ELMo. We use the <a href="https://github.com/nyu-mll/jiant">jiant</a> library from NYU for ELMo baselines and <a href="https://github.com/huggingface/pytorch-transformers">pytorch-transformers</a> for the BERT baseline.</p><p>As shown in the following table, DistilBERT’s performances <strong>compare favorably with the baselines</strong> while having respectively about half and one third the number of parameters (more on this below). Among the 9 tasks, DistilBERT is <strong>always on par or improving over the ELMo baseline</strong> (up to 14 points of accuracy on QNLI). DistilBERT also <strong>compares surprisingly well to BERT</strong>: we are able to <strong>retain more than 95% of the performance</strong> while having 40% fewer parameters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hHwcSZEazpY_PwArgBzHtw.png" /><figcaption>Comparison on the dev sets of the GLUE benchmark. ELMo results as reported by the authors. BERT and DistilBERT results are medians of 5 runs with different seeds.</figcaption></figure><p>In terms of inference time, DistilBERT is more than <strong>60% faster and smaller </strong>than BERT and<strong> 120% faster and smaller </strong>than ELMo+BiLSTM<strong> 🐎</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1010/1*RLBWful7k50nV9zTJCuZ4Q.png" /></figure><p>To further investigate the speed-up/size trade-off of DistilBERT, we compare, in the left table, the number of parameters of each model along with the inference time needed to do a full pass on the STS-B dev set on CPU (using a batch size of 1).</p><h3><strong>🔮 Downstream task: Distillation &amp; transfer-learning</strong></h3><p>We further study the use of DistilBERT on downstream tasks under efficient inference constraints. We use our compact pre-trained language model by fine-tuning it a classification task. A nice way to actually <strong>mix distillation pre-training and transfer-learning</strong>!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/656/1*BtwpBJvHfdYUHCpt-wLvGw.png" /><figcaption>Extract from the IMDB Review dataset — Source: <a href="https://www.kaggle.com/desiredewaele/sentiment-analysis-on-imdb-reviews">Kaggle</a></figcaption></figure><p>We selected the <strong>IMDB Review Sentiment Classification </strong>which<strong> </strong>is composed of 50&#39;000 reviews in English labeled as positive or negative: 25&#39;000 for training and 25&#39;000 for test (and with balanced classes). We trained on a single 12GB K80.</p><p>First, we train bert-base-uncased on our dataset. Our dear BERT 💋 reaches <strong>an accuracy of 93.46%</strong> (average of 6 runs) without any hyper-parameters search.</p><p>We then train DistilBERT, using the same hyper-parameters. The compressed model reaches <strong>an accuracy of 93.07% </strong>(average of 6runs). An absolute difference of 0.4% in performances for a 60% reduction in latency and 40% in size 🏎!</p><blockquote><strong><em>❓Note 3</em></strong> — <a href="https://twitter.com/PiotrCzapla/status/1168120760201859072?s=20">As noted by the community</a>, you can reach comparable or better score on the IMDB benchmark with lighter methods (size-wise and inference-wise) like ULMFiT. We encourage you to compare on your own use-case! In particular, DistilBERT can give a sensible lower-bound on Bert’s performances with the advantage of faster training.</blockquote><p>Another common application of NLP is <strong>Question Answering. </strong>We compared the results of the bert-base-uncased version of BERT with DistilBERT on the SQuAD 1.1 dataset. On the development set, BERT reaches an F1 score of 88.5 and an EM (Exact-match) score of 81.2. We train DistilBERT on the same set of hyper-parameters and reach scores of <em>85.1 F1 and 76.5 EM, within 3 to 5 points of the full BERT</em>.</p><p>We also studied whether we could add another step of distillation during the adaptation phase by finetuning DistilBERT on SQuAD using the finetuned BERT model as a teacher with a knowledge distillation loss.</p><blockquote>Here we are finetuning by distilling a question answering model into a language model previously pre-trained with knowledge distillation! That a lot of teachers and students🎓</blockquote><p>In this case, we were able to reach interesting performances given the size of the network: <strong>86.2 F1 and 78.1 EM, ie. within 3 points of the full model!</strong></p><p>Other works have also attempted to accelerate question answering models. Notably, <a href="https://twitter.com/DebajyotiChat17">Debajyoti Chatterjee</a>, uploaded <a href="https://arxiv.org/abs/1904.00796">an interesting work on arXiv</a> which follows a similar method for the adaptation phase on SQuAD (initializing a student from its teacher, and training a question-answering model via distillation). His experiments present similar relative performances with regards to BERT (base uncased). The main difference with our present work is that we pre-train DistilBERT with a general objective (Masked Language Modeling) in order to obtain a model that can be used for transfer-learning on a large range of tasks via finetuning (GLUE, SQuAD, classification…).</p><h3>🙌 Less is more: smaller models also spark joy 🌟</h3><p>We are very excited about <strong>DistilBERT’s potential</strong>. The work we’ve presented is just the beginning of what can be done and raises many questions: How far can we compress these models with knowledge distillation? Can these technics be used to get further insights into the knowledge stored in the large version? What aspects of linguistic/semantics do we lose in this type of compression?…</p><p>One essential aspect of our work at HuggingFace is <strong>open-source </strong>and<strong> knowledge sharing</strong> as you can see from our <a href="https://github.com/huggingface">GitHub</a> and <a href="http://www.medium.com/huggingface">medium</a> pages. We think it is both the easiest and fairest way for everyone to participate and reap the fruits of the remarkable progress of deep learning for NLP.</p><p>Thus, together with this blog post, we release the code of our experiments 🎮 (in particular the code to reproduce the training and fine-tuning of DistilBERT) along with a trained version of DistilBERT in our <a href="https://github.com/huggingface/pytorch-transformers/">pytorch-transformers library</a>🔥.</p><p><em>Many thanks to Sam Bowman, Alex Wang and Thibault Févry for feedback and discussions!</em></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=8cf3380435b5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/distilbert-8cf3380435b5">🏎 Smaller, faster, cheaper, lighter: Introducing DilBERT, a distilled version of BERT</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 TensorFlow to PyTorch]]></title>
            <link>https://medium.com/huggingface/from-tensorflow-to-pytorch-265f40ef2a28?source=rss----ba0dbdd23ac6---4</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[Scaling a massive State-of-the-Art Deep Learning model in production]]></title>
            <link>https://medium.com/huggingface/scaling-a-massive-state-of-the-art-deep-learning-model-in-production-8277c5652d5f?source=rss----ba0dbdd23ac6---4</link>
            <guid isPermaLink="false">https://medium.com/p/8277c5652d5f</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[scaling]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Lysandre Debut]]></dc:creator>
            <pubDate>Mon, 24 Jun 2019 15:09:18 GMT</pubDate>
            <atom:updated>2019-06-26T19:01:13.443Z</atom:updated>
            <content:encoded><![CDATA[<p>Last week, at Hugging Face, we launched a new groundbreaking text editor app. It’s different from traditional text editors in that an NLP model can complete your sentences if you ask it to, bringing a new dimension to “writing with a machine”. It’s based on GPT-2, OpenAI’s language model that can generate syntactically accurate sentences and coherent paragraphs of text.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P7042_wRl6iE-STVPKzxNg.gif" /><figcaption>Telling a story with GPT-2’s help</figcaption></figure><blockquote>The demo is live on <a href="http://transformer.huggingface.co">https://transformer.huggingface.co</a> and you’re welcome to try it out! 🦄 <strong>Write with transformer is to writing what calculators are to calculus.</strong></blockquote><p>This model is part of the latest trends in NLP which revolve around creating very large language models that obtain excellent results on a variety of tasks when fine-tuned on those specific tasks. This results in models, “Transformers”, with large amounts of parameters (up to 1.5 billion parameters for GPT-2 Large, or Grover), which are difficult to handle because of their weight.</p><p>Our app allows the user to choose between two models: GPT-2 small, and GPT-2 medium. <strong>Loading them both in the computer’s RAM takes a total of 2.4GB of memory.</strong></p><p>Here we offer to show the approach we took in order to scale these models and respond to the 10,000 unique users and the equivalent of more than a <strong>hundred books written</strong> we got in the first few days. We explain the thoughts that went into it, define the best fitting architecture for optimal processing and discuss what we could have improved on.</p><h3>Issue at hand</h3><p><em>Disclaimer: our approach here is specific to models that cannot perform batch inference. For models that can do batch inference, like the one we used, the shown workaround may not be necessary.</em></p><p>This app has several constraints in order to be enjoyable by users. It must have the lowest possible response time and generate long-enough sentences. The system must offer several possible completions at each trigger so that the user may choose one of them, tripling the amount of data to be generated. The goal is, therefore, to optimize as best as possible the computation, creating a workflow taking advantage of the highly parallelizable aspect of GPUs.</p><h3>Setting up our workspace</h3><p>We’ll be building a server-side API to which our front-end app will connect. This API will be responsible for handling the computation needed to generate sentences. We’ll be using Python for this task as most NLP models are readily available. Other lower-level languages such as C++ or Rust would be more appropriate for performance-oriented backends, and we discuss their usage in the last part of this post.</p><p>We used <a href="https://falconframework.org/">falcon</a> for the web servers(any other http framework would have worked too) in conjunction with <a href="https://gunicorn.org/">gunicorn</a> to run our instances and balance the load. Our own <a href="https://github.com/huggingface/pytorch-pretrained-BERT">GPT-2 Pytorch implementation</a> is the backbone of this project. We have a few examples in our <a href="https://github.com/huggingface/pytorch-pretrained-BERT/tree/master/examples">examples directory</a> if you’re interested in doing something similar.</p><p>Gunicorn sets up “workers” which will independently run the application, efficiently balancing the load across different workers. You can check exactly how they work on the <a href="http://docs.gunicorn.org/en/stable/design.html">official gunicorn documentation</a>.</p><h3>3-way autocompletion</h3><p>On each autocompletion request, we want our API to return three different possible sentences. These sentences will be displayed to the end-user who will then choose one between the three. This is an essential part of our design, and our API must reflect that. The three sentences should appear at the same time and optimally a unique request should be sent to the server for each autocompletion.</p><p>The most naïve approach we could have is using a single worker with a model loaded behind:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/314/1*ZBbDLKhjWQrGhDHGnGS-6A.png" /><figcaption>Naïve API</figcaption></figure><p>Using this architecture, every request would be treated sequentially, and the model would be prompted to generate three different sentences before responding to the incoming request.</p><p>This infrastructure could be easily scaled up by adding more workers while keeping in mind that each worker loads the model in the RAM/VRAM according to GPU usage or not.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/924/1*3BgkBfBeR0oYw8kWvLYg0g.png" /><figcaption>Multi-worker naïve API</figcaption></figure><p>Using this approach implies that we have processes loading the model and operating on them, requesting three different sentences. If our model is able to perform batch inference, it can generate the three sentences at once. However, if it cannot, it needs to generate each sentence individually — resulting in three model iterations. We will be considering the case where batch inference is not available as it requires a slightly more engineered approach.</p><p>It would be better to parallelize the three iterations as we are looking for the lowest response time on autocompletion. Luckily for us, Python gives us access to several parallelization options that could be of use in our scenario:</p><ul><li>Multithreading (<a href="https://docs.python.org/3/library/threading.html">threading</a>)</li><li>Multiprocessing (<a href="https://docs.python.org/3/library/subprocess.html">subprocess</a> or <a href="https://docs.python.org/3.4/library/multiprocessing.html">multiprocessing</a>)</li><li>Distinct web servers as a form of multiprocessing (our approach)</li></ul><h4>Multithreading</h4><p>Multithreading in Python is usually done using the threading class, which allows the program to create several threads that will each go on about their respective operations. The problem with multithreading is the way the<em> Global Interpreter Lock</em> — or GIL — works in Python.</p><p>If a thread accesses our model object, then no other thread can access that object until the first thread has finished dealing with it. This approach is therefore similar in execution to not using any thread at all, as the three iterations will be treated sequentially. The only performance difference will be the additional time spent starting/joining each thread, which is detrimental to our objective.</p><p>If one really wanted to use threading, three different models could be loaded into the RAM, each being used by a separate thread. We did not choose to go this way as explained further below.</p><h4>Multiprocessing</h4><p>Multiprocessing can go two ways; either by booting up completely separate processes and connecting to their input/output (using the <em>subprocess</em> module) or by spawning python processes that can inherit the current Python interpreter process’ resources (bypassing the GIL issue, using the <em>multiprocessing</em> module).</p><p>A tricky part here is making sure the model doesn’t have to be loaded into the RAM every time it has to compute an inference; big models take a long time to load in memory.</p><p>We chose to take yet another, different approach.</p><h4>Our approach using gunicorn load balancing</h4><p>Our approach is slightly different in that we choose to use the power of gunicorn workers to parallelize our work. In order to do so, we add another layer to our previous model. The previously defined architecture can receive several requests and process them all at once on several workers. We will use that to our advantage. The final model is detailed below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VbccFWZ6fJk0nthv8pxuFg.png" /><figcaption>Final model with two different Falcon/Gunicorn servers</figcaption></figure><p>When a request is sent from the front-end app to our API, it is handled by our first web server. This web server has a single worker that runs our API. This API is responsible for sending three identical requests to the second web server. The requests sent from this API contain the current context (the previous sentences in the document) as well as some information regarding the parameters (small or medium model, specific top_k values, …).</p><p>This second web server has several workers which handle the requests separately. Three workers will handle each request received from the API, which can, therefore, be handled simultaneously. We use separate threads in the API so that requests can be sent to the second web server in parallel rather than sequentially (http requests -&gt; no GIL issue).</p><p>This architecture has several advantages that other, previously mentioned methods, do not have out-of-the-box:</p><ul><li><strong>We can generate as many workers as the number of models that can fit in our memory</strong>. We split the workers among the different GPUs if we have a distributed system.</li><li><strong>Each worker loads a single model in memory.</strong> Therefore, there may be more models loaded (more computing power) than if three models were loaded each time, such as for the threading approach.</li><li>Launched as webserver workers, the models will always stay loaded in memory.</li><li>We’re making use of gunicorn’s load balancing at every step in our architecture. We are not simply spawning processes running in parallel, <strong>we have a way to make sure each process handles loads relative to its computing capabilities</strong>. If we were to use two different GPUs of different computing power, the bottleneck created by the lower computing GPU wouldn’t impact the other one as much as it would in a purely multi-process program.</li></ul><p>Here is a GIF showing how the architecture behaves for memory management during initialization and when two concurrent requests are sent to the API.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5OGI43Tcpk2PgVG9SAo65A.gif" /><figcaption>Initialization and concurrency behavior</figcaption></figure><h3>Results</h3><p>Unsurprisingly, we obtain large improvements in response time when using a parallel system compared to the initial sequential system. Benchmarking on a single request which has to be broken up in three model iterations, we get a third of the initial response time, the actual local http request only taking a few hundred microseconds.</p><p><strong>This system is particularly adapted to vertical scaling as it adapts to the system’s memory and computing power.</strong> However, it does not compare to a model that can perform batch inference as this approach will store three models in the memory, versus a single one if using batch inference.</p><h3>Further improvements</h3><p>This system was designed to be run on a single machine, so we didn’t consider containerization or horizontal scaling. <strong>These are welcome and necessary in the case of a full-blown production system that needs to handle the 100,000s of users</strong>. This can be discussed in a future post.</p><p>An additional improvement could be the use of the TorchScript module. Since we used Pytorch for our model, we could see a TorchScript version of it that could be used to do inference in any programming language. We could, therefore, have optimized a better, more task-specific web server in a very low-level language if we wanted to optimize to the fullest.</p><p>This system has proven its worth as it held the load until now, handling more than 100,000 different requests in a week’s time while running on a single 4-GPU (K80) machine. If you would like to try out the app and see how our system responds to traffic, you’re welcome to try it out <a href="http://transformer.huggingface.co">here 🦄</a></p><p>This concludes this quick post on the system architecture we had to optimize for parallel computing, using our big Transformer model in production. All thoughts and claps are welcome!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8277c5652d5f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huggingface/scaling-a-massive-state-of-the-art-deep-learning-model-in-production-8277c5652d5f">Scaling a massive State-of-the-Art Deep Learning model in production</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>