Wednesday, June 13, 2018

100 Times Faster Natural Language Processing in Python


 100 Times Faster Natural Language Processing in Python

How to take advantage of spaCy & a bit of Cython for blazing fast NLP

Published BY: Thomas Wolf

When we published our Python coreference resolution package last year, we got an amazing feedback from the community and people started to use it for many applications , some very different from our original dialog use-case .
And we discovered that, while the speed was totally fine for dialog messages, it could be really slow  on larger news articles.
I decided to investigate this in details and the result is NeuralCoref v3.0 which is about 100 times faster  than the previous version (several thousands words per seconds) while retaining the same accuracy, and the easiness of use and eco-system of a Python library.
In this post I wanted to share a few lessons learned on this project, and in particular:
  • How you can design a high-speed module in Python,
  • How you can take advantage of spaCy’s internal data structures to efficiently design super fast NLP functions.
So I am a bit cheating here because we will be talking about Python, but also about some Cython magic — but, you know what? Cython is a superset of Python, so don’t let that scares you away!
Your current Python program is already a Cython program.
There are several cases where you may need such speed-ups, e.g.:
  • you are developing a production module for NLP using Python,
  • you are computing analytics on a large NLP dataset using Python,
  • you are pre-processing a large training set for a DeepLearning framework like pyTorch/TensorFlow, or you have a heavy processing logic in your DeepLearning batch loader that slows down your training.

First step to rocket speed: Profiling



The first thing to know is that most of your code is probably just fine in pure Python but there can be a few bottlenecks functions that will get you orders of magnitude faster if you give them some love.
You should thus start by profiling your Python code and find where the slow parts are located. One option is to use cProfile like that:


You’ll likely find that the slow parts are a few loops, and some Numpy arrays manipulations if you use neural networks (but I won’t spend time talking about NumPy here as there is already a lot of information written about that).

So, how can we speed up these loops?

Fast Loops in Python with a bit of Cython



Let’s work this out on a simple example. Say we have a large set of rectangles that we store as a list of Python objects, e.g. instances of a Rectangle class. The main job of our module is to iterate over this list in order to count how many rectangles have an area larger than a specific threshold.
Our Python module is quite simple and looks like this:



The check_rectangles function is our bottleneck! It loops over a large number of Python objects and this can be rather slow as the Python interpreter does a lot of work under the hood at each iteration (looking for the area method in the class, packing and unpacking arguments, calling the Python API….).
Here comes Cython to help us speed up our loop.
The Cython language is a superset of Python that contains two kind of objects:
  • Python objects are the objects we manipulate in regular Python like numbers, strings, lists, class instances…
  • Cython C objects are C or C++ objects like doubleintfloatstruct, vectors that can be compiled by Cython in super fast low-level code.
A fast loop is simply a loop in a Cython program within which we only access Cython C objects.
Astraightforward approach to designing such a loop is to define C structures that will contain all the things we need during our computation: in our case, the lengths and widths of our rectangles.
We can then store our list of rectangles in a C array of such structures that we will pass to our check_rectangle function. This function now has to accept a C array as input and thus will be defined as a Cython function by using the cdefkeyword instead of def (note that cdef is also used to define Cython C objects).
Here is how the fast Cython version of our Python module looks like:


Here 

First install Cython with pip install cython

First Tests in Jupyter

Load the Cython extension in a Jupyter notebook with %load_ext Cython.
Now you can write Cython code like Python code by using the magic command %%cython.
If you have a compilation error when you execute a Cython cell, be sure to check Jupyter terminal output to see the full message.
Most of the time you’ll be missing a-+ tag after %%cythonto compile to C++ (for example if you use spaCy Cython API) or an import numpy if the compiler complains about NumPy.

Writing, Using and Distributing Cython Code

Cython code is written in .pyx files. These files are compiled to C or C++ files by the Cython compiler and then to byte-code level with the system’s C compiler. The byte-code level files can then be used by the Python interpreter.
You can load a .pyx file directly in Python by using pyximport:
>>> import pyximport; pyximport.install()
>>> import my_cython_module
You can also build your Cython code as a Python package and import/distribute it as a regular Python package as detailed here. This can take some time to get working, in particular on all platforms. If you need a working example, spaCy’s install script is a rather comprehensive one.
Before we move to some NLP, let’s quickly talk about the defcdef and cpdef keywords, because they are the main things you need to grab to start using Cython.
You can use three types of functions in a Cython program:
  • Python functions, which are defined with the usual keyword def. They take as input and output Python objects. Internally they can use bothPython and C/C++ objects and can call both Cython and Python functions.
  • Cython functions defined with the cdef keyword. They can take as input, use internally and output both Python and C/C++ objects. These functions are not accessible from the Python-space (i.e. the Python interpreter and other pure Python modules that would import your Cython module) but they can be imported by other Cython modules.
  • Cython functions defined with the cpdef keyword are like the cdefCython functions but they are also provided with a Python wrapper so they can be called from the Python-space (with Python objects as inputs and outputs) as well as from other Cython modules (with C/C++ or Python objects as inputs).
The cdef keyword has another use which is to type Cython C/C++ objects in the code. Unless you type your objects with this keyword, they will be considered as Python objects (and thus slow to access).

 Using Cython with spaCy to speed up NLP

This is all nice and fast but… we are still not doing NLP here! No string manipulations, no unicode encodings, none of the subtleties we are lucky to have in Natural Language Processing .
And the official Cython documentation even advises against the use of C level strings:
Generally speaking: unless you know what you are doing, avoid using C strings where possible and use Python string objects instead.
So how can we design fast loops in Cython when we work with strings?
spaCy got us covered.
The way spaCy tackle this problem is quite smart.

Convert all strings to 64-bit hashes

All the unicode strings in spaCy (the text of a token, its lower case text, its lemma form, POS tag label, parse tree dependency label, Named-Entity tags…) are stored in a single data structure called the StringStore where they are indexed by 64-bit hashes, i.e. C level uint64_t.


The StringStore object implements a look up between Python unicode strings and 64-bit hashes.
It is accessible from everywhere in spaCy and every object (see on the left), for example as nlp.vocab.stringsdoc.vocab.strings or span.doc.vocab.string.
When a module needs to perform fast processing on some tokens, it simply uses the C level 64-bit hashes instead of the strings. A call to the StringStore look up table will then give back the Python unicode strings associated to the hashes.
But spaCy does more than that and also gives us access to fully populated C level structures of the document and vocabulary, which we can use in Cython loops instead of having to build our own structures.

SpaCy’s internal data structures

The main data structure associated to a spaCy document is the Doc object which owns the sequence of tokens (“words”) of the processed string and all their annotations in a C level object called doc.c which is an array of TokenCstructures.
The TokenC structure contains all the informations we need about each tokens. This information is stored as 64-bit hashes that can be re-associated to unicode strings as we’ve just seen.
To see exactly what’s in these nice C structures, just have a look at the freshly created Cython API doc of spaCy .

Let’s see that in action on a simple example of NLP processing.

🚀Fast NLP Processing with spaCy and Cython

Let’s say we have a dataset of text documents we need to analyse.
A Python loop to do that is short and straightforward:


On the left I wrote a script that builds a list of 10 documents parsed by spaCy, each with ~170k words. We could also have 170k documents with 10 words in each (like a dialog dataset) but that’s slower to create so let’s stick with 10 docs.We want to perform some NLP task on this dataset. For example, we would like to count the number of times the word “run” is used as a noun in the dataset (i.e. tagged tagged with a “NN” Part-Of-Speech tag by spaCy).

But it’s also quite slow! On my laptop this code takes about 1.4 second to get the answer. If we had a million documents it would take more than a day to give us the answer.We could use multiprocessing but it’s often not such a great solution in Pythonbecause you have to deal with the GIL. Also, note that Cython can also use multi-threading! And that may actually even be the best part of Cythonbecause the GIL is released so we are at full speed 🏎 Cython basically directly call OpenMP under the hoodNow let’s try to speed up our Python code with spaCy and a bit of Cython.

First, we have to think about the data structure. We will need a C level array for the dataset, with pointers to each document’s TokenC array. We’ll also need to convert the test strings we use (“run” and “NN”) to 64-bit hashes.
When all the data required for our processing is in C level objects, we can then iterate at full C speed over the dataset.
Here is how this example can be written in Cython with spaCy:


The code is a bit longer because we have to declare and populate the C structures in main_nlp_fast before calling our Cython function [*].But it is also a lot faster! In my Jupyter notebook, this Cython code takes about 20 milliseconds to run which is about 80 times faster than our pure Python loop.The absolute speed is also impressive for a module written in a Jupyter Notebook cell and which can interface natively with other Python modules and functions: scanning ~1,7 million words in 30ms means we are processing a whopping 80 millions words per seconds.This concludes our quick introduction on using Cython for NLP. I hope you enjoyed it.There are a lot of other things to says on Cython but it would get us too far from this simple introduction. The best place to start from now is probably the Cython tutorials for a general overview and spaCy’s Cython page for NLP.*. ^ If you use low level structures several times in your code, a more elegant option than populating C structures each time, is to design our Python code around the low level structures with a Cython extension type wrapping the C level structures. This is how most of spaCy is structured and it is a very elegant way to combine fast speed, low memory use and the easiness of interfacing with external Python libraries and functions.

Saturday, June 9, 2018

First major AI patent filings revealed

First major AI patent filings revealed

DeepMind, a leading artificial intelligence (AI) research company, has filed a series of international patent applications, which have now been published for the first time. The applications relate to a number of the fundamental aspects of modern day machine learning, and are therefore of potential significance to anyone operating in the commercial AI sector. 

Background to DeepMind

DeepMind is a London based artificial intelligence (AI) research company, widely recognized as being at the forefront of the field. DeepMind was founded in 2010 and acquired by Google in 2014 for £400m. In 2017, DeepMind famously developed AI capable of defeating a world-champion at Go (Silver et al. Nature).

DeepMind's approach to AI is explained here by founder Dr Demis Hassabis (the article also serves as a useful introduction for the uninitiated to AI/Ml in general). Dr Hassabis is a popular guy, described as "a superhero of artificial intelligence" and "a genus" by the Guardian (here), a "modern polymath" by the FT and "London's megamind" by The Evening Standard (here). He has even been on the BBC's desert island discs (here).

AI has many uses including art! "Rose Hungry Cat"
DeepMind's patent applications

Deepmind's published patent applications so far include:
  • WO 2018/048934, "Generating Audio using neural networks", Priority date: 6 Sep 2016
  • WO 2018/048945, "Processing sequences using convolutional neural networks", Priority date: 6 Sep 2016
  • WO 2018064591, "Generating video frames using neural networks", Priority date: 6 Sep 2016
  • WO 2018071392, "Neural networks for selecting actions to be performed by a robotic agent", Priority date: 10 Oct 2016
  • WO 2018/081089, "Processing text sequences using neural networks", Priority date: 26 Oct 2016
  • WO 2018/083532, "Training action selection using neural networks", Priority date: 3 Nov 2016
  • WO 2018/083667, "Reinforcement learning systems", Priority date: 4 Nov 2016
  • WO 2018/083668, "Scene understanding and generation using neural networks", Priority date: 4 Nov 2016
  • WO 2018/083669, "Recurrent neural networks", Priority date: 4 Nov 2016
  • WO 2018083670, "Sequence transduction neural networks", Priority date: 4 Nov 2016
  • WO 2018083671, "Reinforcement learning with auxiliary tasks", Priority date: 4 Nov 2016
  • WO 2018/083672, "Environment navigation using reinforcement learning", Priority date: 4 Nov 2016
These applications represent a filing rate of almost 1 application per week in the period of September 2016 - December 2016. Patent applications are published 18 months from the priority date, so any applications filed after Dec 2016 will not yet have been published.

A quick perusal of the applications reveals that the claims generally fulfill promised broadness of the titles, and whilst not being directed to fundamental algorithms, claim ML platforms for solving general problems, without being restricted to a particular application. Claim 1 of WO 2018/048945, for example, relates to the application of convolutional networks (the majority of modern neural networks of significance are convolutional networks) to the processing of any sequences, i.e. any temporal data such as audio, and text. The claims of WO 2018/081089 apply this to text sequences for the purpose of translation. Whilst there are other ways of achieving similar results using alternative AI approaches, the application covers a fundamental class of approaches. 

DeepMind is, of course, not the first company to file applications in the AI field. As has been widely reported, AI is a large and rapidly expanding field of patent filings (for more on patenting AI see IPKat posts here and here). However, DeepMind's applications are significant in view of their position as a (if not the) leading AI research company, and for the broadness of the claims. These are not applications that reveal a clear commercial product or aim, but appear directed at covering broad swathes of AI technology. 

"Artificial neural style Cubism mirror cat"
Applications versus Patent

Contrary to popular belief on anti-patent blogs, a patent application is not equivalent to a granted, enforceable patent. The DeepMind applications are still at a very early stage of prosecution, and will be examined for patentability in each of the jurisdictions in which DeepMind seeks grant. As might be expected in a relatively new field, the International Search Reports (ISRs) (published for a number of the applications) do not cite reams of prior art. In fact, many of the documents cited for novelty and inventive step appear to be DeepMind's own arXiv papers. However, DeepMind will have to overcome Examiner objections based on these documents through argument or by limitation of the claims. Furthermore, patenting machine learning systems, whilst possible, is not always a straightforward matter in many jurisdictions (not least in Europe and the US).

"Don't be evil"

DeepMind researchers publish extensively in academic journals, and the company has the stated aim of supporting and accelerate research in the wider AI community. DeepMind founder Dr Hasabiss believes AI has the potential to "become a kind of meta-solution for scientists to deploy, enhancing our daily lives and allowing us to all work more quickly and effectively. If we can deploy these tools broadly and fairly, fostering an environment in which everyone can participate in and benefit from them, we have the opportunity to enrich and advance humanity as a whole" (FT article). At the PR level, DeepMind thus heavily emphasizes their collaborative attitude and openness. This approach is understandable, given the animosity of the AI academic community towards anything other than open source, and DeepMind's ongoing need to attract nothing less than the best researchers in the field.

"Ninja cat hiding in poppies"
It will be interesting to see how news of DeepMind's patent filings will go down in the AI community and the public at large; more commercially focused companies than DeepMind have encountered difficulties with public perception in this area. Google is of course well-known as a prolific patent filer, including within the field of AI. By contrast, the raison d'etre of DeepMind has hither-to been presented as the development of AI research for the good of the research community and humanity as a whole. Achieving such an aim may of course be facilitated by acquisition of the appropriate IP protection. But how does DeepMind's newly revealed patent filings fit with the mission statement that "AI should ultimately belong to the world" (quote source here)? Perhaps a topic of conversation for Dr Hassabis's next interview.