  
      //p Our next step is to cluster phrases. We will use the skip-grams with negative sampling algorithm, generally known as "word2vec". However, the vectors we'll learn will be keyed by larger units, discovered using linguistic annotations. Trask et al. describe an adaptation of word2vec that uses part-of-speech tagged keys as "sense2vec". I like the sound of "nlp2vec".
  
      
      
      //p In this post I'll describe how to adapt spaCy's named entity recogniser to recognise categories of your choosing, including a quick and dirty way to produce training data semi-automatically. The strategy is to define a handful of seed words or phrases, and then use word2vec to generate a more expansive list. Before using Gensim to train the word vectors, we merge base noun phrases and previously recognised entities into single tokens, using spaCy's annotations. This gives a vector space that includes phrases as well as words. To train our tagger, we can manually classify some of these phrases in context, or we can use an old data generation trick adapted from word sense disambiguation.
  
      //In this tutorial we'll look for video games, using comments posted to Reddit in 2015. The Reddit comment corpus is some of my favourite data. It's a good amount of text, and it's quite clean. We don't have to do any HTML or MediaWiki processing, and there's very little automatically generated text. Most of the comments are around the same length, and people mostly type in full sentences.
  
 
</s> remove     p Until machines can think for themselves, we have to either program them explicitly, or give them examples.  Let's say you want to write a program that tags mentions of a certain type of thing in text --- let's say, food.  You can't write this program without supplying some definition of what you consider to be food, and what you don't. That much is fundamental. But, you don't necessarily need to sit down with an arbitrary sample of text, and start tagging it manually, word-by-word. Sometimes you do --- but sometimes there are easier ways.
    
    p In this post I'll describe how to adapt spaCy's named entity recogniser to recognise categories of your choosing, including a quick and dirty way to produce training data semi-automatically. The strategy is to define a handful of seed words or phrases, and then use word2vec to generate a more expansive list. Before using Gensim to train the word vectors, we merge base noun phrases and previously recognised entities into single tokens, using spaCy's annotations. This gives a vector space that includes phrases as well as words. To train our tagger, we can manually classify some of these phrases in context, or we can use an old data generation trick adapted from word sense disambiguation.

 </s> add  </s> remove     p spaCy provides a named entity recogniser trained on OntoNotes 5, that recognises the usual classes --- person, location, organization, etc. In this tutorial we'll look for video games, using comments posted to Reddit in 2015. The Reddit comment corpus is some of my favourite data. It's a good amount of text, and it's quite clean. We don't have to do any HTML or MediaWiki processing, and there's very little automatically generated text. Most of the comments are around the same length, and people mostly type in full sentences.
 </s> add     p spaCy provides a named entity recogniser trained on OntoNotes 5, that recognises the usual classes --- person, location, organization, etc. In this tutorial I'll show how to:

    ol
        li Add classes to an existing entity recognition model;
        li Update the model to fix errors on your data;
        li Train a new named entity recognition model from scratch;

    h4 How the entity recognizer works

    p Named entity recognition is typically modelled as a problem of assigning labels to contiguous spans of text. The spans are usually encoded into structured tags that indicate the span boundaries and types, so that the entities can be assigned like part-of-speech tags, using a linear-chain CRF or a Hidden Markov Model.

    p All three parts of this design are bad. Named entities are not actually flat-structured. For instance, #[the University of Sydney] is an organization name that contains a city name. This is very common. Entities also don't fall into neat exclusive hierarchies. Finally, and of least significance, encoding the problem into per-word tags fails to make the right thing easy. It makes it tricky to write loss functions and features that refer to the actual structures we're trying to predict: the spans.

    p Addressing the first two problems would require us to depart significantly from most of the existing data and research.  We'll have richly labelled, internally structured entities eventually. But we can at least set up the learning problem better, without the pointless tag encoding.

    p Instead of a tagger, spaCy's entity recognizer is an incremental transition-based parser, with a simple transition system that matches the #[code BILOU] tagging scheme. We have the following actions: </s> remove     p It's important that we're able to do this over #[em phrases], not just individual words. We want to be tagging instances of "hot dog", not just "dog". A simple way to achieve this is to use spaCy's syntactic parser to identify base noun phrases. We then merge these noun phrases into single tokens, before we send the data to Gensim's word2vec implementation. I did this over all comments posted to Reddit in 2015.
 </s> add     p The model has learned that as a noun, #[em take] is similar to phrases like #[em personal stance] and #[em best guess]. Its similarity to the verbal sense is quite low: </s> remove     //p Note that the approach that I'm advocating here is actually fairly different from "active learning", where you try to get the classifier's uncertainty to prioritise a queue of examples to be annotated. This can minimise the total number of decisions you need to make to achieve some given accuracy. However, it makes the annotation task much more difficult, and introduces a whole new machine learning problem to tinker with. Active learning is well researched because it's generalisable: it doesn't require any specific insights about your annotation task. But this also makes it much less powerful.
 </s> add     pre
        code
            | >>> model.similarity('take|NOUN', 'take|VERB')
            | 0.13804844694430313

    p When we ask the model for entries similar to the verbal sense of take, the result is interesting:

    pre
        code
            | >>> w2v.most_similar(['take|VERB'])
            | [(u'taking|VERB', 0.8178646564483643), (u'taken|VERB', 0.7978687286376953), (u'takes|VERB', 0.7828329205513), (u'tale|VERB', 0.7796445488929749), (u'Take|VERB', 0.7672088146209717), (u'give|VERB', 0.7639298439025879), (u'take|ADJ', 0.7631379961967468), (u'Taking|VERB', 0.7620075345039368), (u'put|VERB', 0.7561365962028503), (u'took|VERB', 0.7524303793907166)]
            | >>> model.similarity('take|VERB', 'tale|VERB')
            | 0.49611725495669901

    p The first suggestion is #[em tale], tagged as a verb. This is surely a misspelling, that the part-of-speech tagger has managed to correctly tag, even though the actual word #[em tale] cannot be used as a verb.

    p A few bonus queries on the chunked model.

    p 1) The vector space seems like it'll give a good way to show compositionality:

    p "fair game" is not a type of game:

    pre
        code
            | >>> model.similarity('fair_game|NOUN', 'game|NOUN')
            | 0.034977455677555599
            | >>> model.similarity('multiplayer_game|NOUN', 'game|NOUN')
            | 0.54464530644393849
            | A "class action" is only very weakly a type of action:
            | >>> model.similarity('class_action|NOUN', 'action|NOUN')
            | 0.14957825452335169
            | But a class action lawsuit is definitely a type of lawsuit:
            | >>> model.similarity('class_action_lawsuit|NOUN', 'lawsuit|NOUN')
            | 0.69595765453644187
            | 2) Similarity between entities can be kind of fun.

    p Here's what Reddit thinks of Donald Trump:
    pre
        code
            | >>> model.most_similar(['Donald_Trump|PERSON'])
            | [(u'Sarah_Palin|PERSON', 0.5510910749435425), (u'Rick_Perry|PERSON', 0.5508972406387329), (u'Stephen_Colbert|PERSON', 0.5499709844589233), (u'Alex_Jones|PERSON', 0.5492554306983948), (u'Michael_Moore|PERSON', 0.5363447666168213), (u'Charles_Manson|PERSON', 0.5363028645515442), (u'Dick_Cheney|PERSON', 0.5348431468009949), (u'Mark_Zuckerberg|PERSON', 0.5258212089538574), (u'Mark_Wahlberg|PERSON', 0.5251839756965637), (u'Michael_Jackson|PERSON', 0.5229078531265259)]
    p Discussion of Bill Cosby makes some obvious (and some less obvious) comparisons:
    pre
        code
            | >>> model.most_similar(['Bill_Cosby|PERSON'])
            | [(u'Cosby|ORG', 0.6004706621170044), (u'Cosby|PERSON', 0.5874950885772705), (u'Roman_Polanski|PERSON', 0.5478169918060303), (u'George_Zimmerman|PERSON', 0.5398542881011963), (u'Charles_Manson|PERSON', 0.5387344360351562), (u'OJ_Simpson|PERSON', 0.5228893160820007), (u'Trayvon_Martin|PERSON', 0.514190137386322), (u'Adnan_Syed|PERSON', 0.49992451071739197), (u'rapist|NOUN', 0.49792540073394775), (u'srhbutts|NOUN', 0.49792492389678955)]

    p Some queries produce more confusing results:

    pre
        code
            | >>> model.most_similar(['Carrot_Top|PERSON'])
            | [(u'Kate_Mara|PERSON', 0.5347248911857605), (u'Andy_Samberg|PERSON', 0.5336876511573792), (u'Ryan_Gosling|PERSON', 0.5287898182868958), (u'Emma_Stone|PERSON', 0.5243821740150452), (u'Charlie_Sheen|PERSON', 0.5209298133850098), (u'Joseph_Gordon_Levitt|PERSON', 0.5196050405502319), (u'Jonah_Hill|PERSON', 0.5151286125183105), (u'Zooey_Deschanel|PERSON', 0.514430582523346), (u'Gerard_Butler|PERSON', 0.5115377902984619), (u'Ellen_Page|PERSON', 0.5094753503799438)]

    p I can't say the connection between Carrot Top and Kate Mara is obvious to me. I suppose this is true of most things about Carrot Top, so...Fair play.

    p 3) Reddit talks about food a lot, and those regions of the vector space seem very well defined:

    pre
        code
            | >>> model.most_similar(['onion_rings|NOUN'])
            | [(u'hashbrowns|NOUN', 0.8040812611579895), (u'hot_dogs|NOUN', 0.7978234887123108), (u'chicken_wings|NOUN', 0.793393611907959), (u'sandwiches|NOUN', 0.7903584241867065), (u'fries|NOUN', 0.7885469198226929), (u'tater_tots|NOUN', 0.7821801900863647), (u'bagels|NOUN', 0.7788236141204834), (u'chicken_nuggets|NOUN', 0.7787706255912781), (u'coleslaw|NOUN', 0.7771176099777222), (u'nachos|NOUN', 0.7755396366119385)]

    p Some of Reddit's ideas about food are kind of...interesting. It seems to think bacon and brocolli are very similar:

    pre
        code
            | >>> model.similarity('bacon|NOUN', 'broccoli|NOUN')
            | 0.83276615202851845

    p Reddit also thinks hot dogs are practically salad:

    pre
        code
            | >>> model.similarity('hot_dogs|NOUN', 'salad|NOUN')
            | 0.76765100035460465
            | >>> model.similarity('hot_dogs|NOUN', 'entrails|NOUN')
            | 0.28360725445449464

    p Just keep telling yourself that Reddit.
    //p Until machines can think for themselves, we have to either program them explicitly, or give them examples.  Let's say you want to write a program that tags mentions of a certain type of thing in text --- let's say, food.  You can't write this program without supplying some definition of what you consider to be food, and what you don't. That much is fundamental. But, you don't necessarily need to sit down with an arbitrary sample of text, and start tagging it manually, word-by-word. Sometimes you do --- but sometimes there are easier ways.
  
      //p Our next step is to cluster phrases. We will use the skip-grams with negative sampling algorithm, generally known as "word2vec". However, the vectors we'll learn will be keyed by larger units, discovered using linguistic annotations. Trask et al. describe an adaptation of word2vec that uses part-of-speech tagged keys as "sense2vec". I like the sound of "nlp2vec".
  
      
    //p Until machines can think for themselves, we have to either program them explicitly, or give them examples.  Let's say you want to write a program that tags mentions of a certain type of thing in text --- let's say, food.  You can't write this program without supplying some definition of what you consider to be food, and what you don't. That much is fundamental. But, you don't necessarily need to sit down with an arbitrary sample of text, and start tagging it manually, word-by-word. Sometimes you do --- but sometimes there are easier ways.
      
      //p In this post I'll describe how to adapt spaCy's named entity recogniser to recognise categories of your choosing, including a quick and dirty way to produce training data semi-automatically. The strategy is to define a handful of seed words or phrases, and then use word2vec to generate a more expansive list. Before using Gensim to train the word vectors, we merge base noun phrases and previously recognised entities into single tokens, using spaCy's annotations. This gives a vector space that includes phrases as well as words. To train our tagger, we can manually classify some of these phrases in context, or we can use an old data generation trick adapted from word sense disambiguation.
  
      //In this tutorial we'll look for video games, using comments posted to Reddit in 2015. The Reddit comment corpus is some of my favourite data. It's a good amount of text, and it's quite clean. We don't have to do any HTML or MediaWiki processing, and there's very little automatically generated text. Most of the comments are around the same length, and people mostly type in full sentences.
  
keep keep keep add keep keep keep keep keep
 * Write more notes about spaCy's NER 
website/src/jade/tutorials/bootstrap-ner-word2vec/index.jade