mirror of https://github.com/explosion/spaCy.git
* Move spacy.pos tagger to spacy.tagger, and generalize it so that it can take on other tagging tasks, given a different set of feature templates.
This commit is contained in:
parent
2420d944cb
commit
abbe3e44b0
|
@ -0,0 +1,32 @@
|
||||||
|
from cymem.cymem cimport Pool
|
||||||
|
|
||||||
|
from thinc.learner cimport LinearModel
|
||||||
|
from thinc.features cimport Extractor
|
||||||
|
from thinc.typedefs cimport atom_t, feat_t, weight_t, class_t
|
||||||
|
|
||||||
|
from .tokens cimport Tokens
|
||||||
|
|
||||||
|
|
||||||
|
cdef enum TagType:
|
||||||
|
POS
|
||||||
|
ENTITY
|
||||||
|
SENSE
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Tagger:
|
||||||
|
cpdef int set_tags(self, Tokens tokens) except -1
|
||||||
|
cpdef class_t predict(self, int i, Tokens tokens) except 0
|
||||||
|
cpdef int tell_answer(self, class_t gold) except -1
|
||||||
|
|
||||||
|
cpdef readonly Pool mem
|
||||||
|
cpdef readonly Extractor extractor
|
||||||
|
cpdef readonly LinearModel model
|
||||||
|
|
||||||
|
cpdef readonly TagType tag_type
|
||||||
|
cpdef readonly list tag_names
|
||||||
|
|
||||||
|
cdef class_t _guess
|
||||||
|
cdef atom_t* _context
|
||||||
|
cdef feat_t* _feats
|
||||||
|
cdef weight_t* _values
|
||||||
|
cdef weight_t* _scores
|
|
@ -0,0 +1,91 @@
|
||||||
|
# cython: profile=True
|
||||||
|
from os import path
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import random
|
||||||
|
import codecs
|
||||||
|
import gzip
|
||||||
|
import json
|
||||||
|
import cython
|
||||||
|
|
||||||
|
|
||||||
|
from thinc.features cimport ConjFeat
|
||||||
|
|
||||||
|
NULL_TAG = 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Tagger:
|
||||||
|
"""Assign part-of-speech, named entity or supersense tags, using greedy
|
||||||
|
decoding. The tagger reads its model and configuration from disk.
|
||||||
|
"""
|
||||||
|
def __init__(self, model_dir):
|
||||||
|
self.mem = Pool()
|
||||||
|
cfg = json.load(path.join(model_dir, 'config.json'))
|
||||||
|
templates = cfg['templates']
|
||||||
|
self.tag_names = cfg['tag_names']
|
||||||
|
self.tag_type = cfg['tag_type']
|
||||||
|
self.model = LinearModel(len(self.tag_names))
|
||||||
|
if path.exists(path.join(model_dir, 'model')):
|
||||||
|
self.model.load(path.join(model_dir, 'model'))
|
||||||
|
self.extractor = Extractor(templates, [ConjFeat] * len(templates))
|
||||||
|
|
||||||
|
self._feats = <feat_t*>self.mem.alloc(self.extractor.n+1, sizeof(feat_t))
|
||||||
|
self._values = <weight_t*>self.mem.alloc(self.extractor.n+1, sizeof(weight_t))
|
||||||
|
self._scores = <weight_t*>self.mem.alloc(len(self.cfg.tags), sizeof(weight_t))
|
||||||
|
self._guess = NULL_TAG
|
||||||
|
|
||||||
|
cpdef int set_tags(self, Tokens tokens) except -1:
|
||||||
|
"""Assign tags to a Tokens object.
|
||||||
|
|
||||||
|
>>> tokens = EN.tokenize(u'An example sentence.')
|
||||||
|
>>> assert tokens[0].pos == 'NO_TAG'
|
||||||
|
>>> EN.pos_tagger.set_tags(tokens)
|
||||||
|
>>> assert tokens[0].pos == 'DT'
|
||||||
|
"""
|
||||||
|
cdef int i
|
||||||
|
for i in range(tokens.length):
|
||||||
|
tokens.set_tag(i, self.tag_type, self.predict(i, tokens))
|
||||||
|
|
||||||
|
cpdef class_t predict(self, int i, Tokens tokens) except 0:
|
||||||
|
"""Predict the tag of tokens[i]. The tagger remembers the features and
|
||||||
|
prediction, in case you later call tell_answer.
|
||||||
|
|
||||||
|
>>> tokens = EN.tokenize(u'An example sentence.')
|
||||||
|
>>> tag = EN.pos_tagger.predict(0, tokens)
|
||||||
|
>>> assert tag == EN.pos_tagger.tag_id('DT') == 5
|
||||||
|
"""
|
||||||
|
#if self.tag_type == POS:
|
||||||
|
# _pos_feats.fill_context(self._context, i, tokens)
|
||||||
|
self.extractor.extract(self._feats, self._values, self._context, NULL)
|
||||||
|
self._guess = self.model.score(self._scores, self._feats, self._values)
|
||||||
|
return self._guess
|
||||||
|
|
||||||
|
cpdef int tell_answer(self, class_t gold) except -1:
|
||||||
|
"""Provide the correct tag for the word the tagger was last asked to predict.
|
||||||
|
During Tagger.predict, the tagger remembers the features and prediction
|
||||||
|
for the example. These are used to calculate a weight update given the
|
||||||
|
correct label.
|
||||||
|
|
||||||
|
>>> tokens = EN.tokenize('An example sentence.')
|
||||||
|
>>> guess = EN.pos_tagger.predict(1, tokens)
|
||||||
|
>>> JJ = EN.pos_tagger.tag_id('JJ')
|
||||||
|
>>> JJ
|
||||||
|
7
|
||||||
|
>>> EN.pos_tagger.tell_answer(JJ)
|
||||||
|
"""
|
||||||
|
cdef class_t guess = self._guess
|
||||||
|
if gold == guess or gold == NULL_TAG:
|
||||||
|
self.model.update({})
|
||||||
|
return 0
|
||||||
|
counts = {guess: {}, gold: {}}
|
||||||
|
self.extractor.count(counts[gold], self._feats, 1)
|
||||||
|
self.extractor.count(counts[guess], self._feats, -1)
|
||||||
|
self.model.update(counts)
|
||||||
|
|
||||||
|
def tag_id(self, object tag_name):
|
||||||
|
"""Encode tag_name into a tag ID integer."""
|
||||||
|
tag_id = self.tag_names.index(tag_name)
|
||||||
|
if tag_id == -1:
|
||||||
|
tag_id = len(self.tag_names)
|
||||||
|
self.tag_names.append(tag_name)
|
||||||
|
return tag_id
|
Loading…
Reference in New Issue