spaCy/spacy/_ml.py

208 lines
7.2 KiB
Python
Raw Normal View History

2017-05-06 21:38:12 +03:00
from thinc.api import add, layerize, chain, clone, concatenate, with_flatten
2017-05-06 17:47:15 +03:00
from thinc.neural import Model, Maxout, Softmax, Affine
2017-05-04 14:31:40 +03:00
from thinc.neural._classes.hash_embed import HashEmbed
2017-05-05 21:12:03 +03:00
from thinc.neural._classes.convolution import ExtractWindow
from thinc.neural._classes.static_vectors import StaticVectors
2017-05-06 19:24:38 +03:00
from thinc.neural._classes.batchnorm import BatchNorm
2017-05-07 04:57:26 +03:00
from thinc.neural._classes.resnet import Residual
2017-05-05 21:12:03 +03:00
2017-05-06 18:37:36 +03:00
from .attrs import ID, LOWER, PREFIX, SUFFIX, SHAPE, TAG, DEP
2017-05-04 14:31:40 +03:00
def get_col(idx):
def forward(X, drop=0.):
2017-05-06 21:38:12 +03:00
assert len(X.shape) <= 3
output = Model.ops.xp.ascontiguousarray(X[:, idx])
2017-05-06 21:38:12 +03:00
def backward(y, sgd=None):
dX = Model.ops.allocate(X.shape)
dX[:, idx] += y
return dX
return output, backward
2017-05-04 14:31:40 +03:00
return layerize(forward)
2017-05-05 20:20:39 +03:00
def build_model(state2vec, width, depth, nr_class):
with Model.define_operators({'>>': chain, '**': clone}):
model = (
state2vec
>> Maxout(width, 1344)
>> Maxout(width, width)
2017-05-06 17:47:15 +03:00
>> Affine(nr_class, width)
)
2017-05-05 20:20:39 +03:00
return model
2017-05-04 14:31:40 +03:00
2017-05-06 17:47:15 +03:00
def build_debug_model(state2vec, width, depth, nr_class):
with Model.define_operators({'>>': chain, '**': clone}):
model = (
state2vec
2017-05-07 04:57:26 +03:00
>> Maxout(nr_class)
2017-05-06 17:47:15 +03:00
)
return model
def build_debug_state2vec(width, nr_vector=1000, nF=1, nB=0, nS=1, nL=2, nR=2):
ops = Model.ops
def forward(tokens_attrs_vectors, drop=0.):
tokens, attr_vals, tokvecs = tokens_attrs_vectors
2017-05-06 21:38:12 +03:00
2017-05-06 17:47:15 +03:00
orig_tokvecs_shape = tokvecs.shape
tokvecs = tokvecs.reshape((tokvecs.shape[0], tokvecs.shape[1] *
tokvecs.shape[2]))
vector = tokvecs
def backward(d_vector, sgd=None):
d_tokvecs = vector.reshape(orig_tokvecs_shape)
return (tokens, d_tokvecs)
return vector, backward
model = layerize(forward)
return model
2017-05-06 21:38:12 +03:00
def build_state2vec(nr_context_tokens, width, nr_vector=1000):
ops = Model.ops
with Model.define_operators({'|': concatenate, '+': add, '>>': chain}):
2017-05-07 04:57:26 +03:00
hiddens = [get_col(i) >> Maxout(width) for i in range(nr_context_tokens)]
model = get_token_vectors >> add(*hiddens)
2017-05-06 21:38:12 +03:00
return model
def print_shape(prefix):
def forward(X, drop=0.):
return X, lambda dX, **kwargs: dX
return layerize(forward)
2017-05-07 04:57:26 +03:00
2017-05-06 21:38:12 +03:00
@layerize
def get_token_vectors(tokens_attrs_vectors, drop=0.):
ops = Model.ops
tokens, attrs, vectors = tokens_attrs_vectors
def backward(d_output, sgd=None):
return (tokens, d_output)
return vectors, backward
2017-05-05 21:09:50 +03:00
def build_parser_state2vec(width, nr_vector=1000, nF=1, nB=0, nS=1, nL=2, nR=2):
embed_tags = _reshape(chain(get_col(0), HashEmbed(16, nr_vector)))
embed_deps = _reshape(chain(get_col(1), HashEmbed(16, nr_vector)))
2017-05-05 21:09:50 +03:00
ops = embed_tags.ops
def forward(tokens_attrs_vectors, drop=0.):
tokens, attr_vals, tokvecs = tokens_attrs_vectors
2017-05-05 21:09:50 +03:00
tagvecs, bp_tagvecs = embed_deps.begin_update(attr_vals, drop=drop)
depvecs, bp_depvecs = embed_tags.begin_update(attr_vals, drop=drop)
orig_tokvecs_shape = tokvecs.shape
2017-05-05 21:09:50 +03:00
tokvecs = tokvecs.reshape((tokvecs.shape[0], tokvecs.shape[1] *
tokvecs.shape[2]))
2017-05-04 14:31:40 +03:00
2017-05-05 20:20:39 +03:00
shapes = (tagvecs.shape, depvecs.shape, tokvecs.shape)
assert tagvecs.shape[0] == depvecs.shape[0] == tokvecs.shape[0], shapes
vector = ops.xp.hstack((tagvecs, depvecs, tokvecs))
2017-05-05 20:20:39 +03:00
def backward(d_vector, sgd=None):
d_tagvecs, d_depvecs, d_tokvecs = backprop_concatenate(d_vector, shapes)
assert d_tagvecs.shape == shapes[0], (d_tagvecs.shape, shapes)
assert d_depvecs.shape == shapes[1], (d_depvecs.shape, shapes)
assert d_tokvecs.shape == shapes[2], (d_tokvecs.shape, shapes)
2017-05-05 20:20:39 +03:00
bp_tagvecs(d_tagvecs)
bp_depvecs(d_depvecs)
d_tokvecs = d_tokvecs.reshape(orig_tokvecs_shape)
return (tokens, d_tokvecs)
2017-05-05 20:20:39 +03:00
return vector, backward
model = layerize(forward)
model._layers = [embed_tags, embed_deps]
2017-05-04 14:31:40 +03:00
return model
2017-05-05 20:20:39 +03:00
def backprop_concatenate(gradient, shapes):
grads = []
start = 0
for shape in shapes:
end = start + shape[1]
grads.append(gradient[:, start : end])
start = end
return grads
2017-05-05 21:09:50 +03:00
2017-05-05 20:20:39 +03:00
def _reshape(layer):
'''Transforms input with shape
(states, tokens, features)
into input with shape:
(states * tokens, features)
So that it can be used with a token-wise feature extraction layer, e.g.
an embedding layer. The embedding layer outputs:
(states * tokens, ndim)
But we want to concatenate the vectors for the tokens, so we produce:
(states, tokens * ndim)
We then need to reverse the transforms to do the backward pass. Recall
the simple rule here: each layer is a map:
inputs -> (outputs, (d_outputs->d_inputs))
So the shapes must match like this:
shape of forward input == shape of backward output
shape of backward input == shape of forward output
'''
def forward(X__bfm, drop=0.):
b, f, m = X__bfm.shape
B = b*f
M = f*m
X__Bm = X__bfm.reshape((B, m))
y__Bn, bp_yBn = layer.begin_update(X__Bm, drop=drop)
n = y__Bn.shape[1]
N = f * n
y__bN = y__Bn.reshape((b, N))
def backward(dy__bN, sgd=None):
dy__Bn = dy__bN.reshape((B, n))
dX__Bm = bp_yBn(dy__Bn, sgd)
if dX__Bm is None:
return None
else:
return dX__Bm.reshape((b, f, m))
return y__bN, backward
2017-05-05 20:20:39 +03:00
model = layerize(forward)
model._layers.append(layer)
2017-05-04 14:31:40 +03:00
return model
@layerize
def flatten(seqs, drop=0.):
ops = Model.ops
2017-05-07 04:57:26 +03:00
lengths = [len(seq) for seq in seqs]
def finish_update(d_X, sgd=None):
2017-05-07 04:57:26 +03:00
return ops.unflatten(d_X, lengths)
X = ops.xp.vstack(seqs)
return X, finish_update
def build_tok2vec(lang, width, depth=2, embed_size=1000):
2017-05-06 18:37:36 +03:00
cols = [ID, LOWER, PREFIX, SUFFIX, SHAPE, TAG]
2017-05-05 21:12:03 +03:00
with Model.define_operators({'>>': chain, '|': concatenate, '**': clone}):
#static = get_col(cols.index(ID)) >> StaticVectors(lang, width)
2017-05-06 18:37:36 +03:00
lower = get_col(cols.index(LOWER)) >> HashEmbed(width, embed_size)
2017-05-05 21:12:03 +03:00
prefix = get_col(cols.index(PREFIX)) >> HashEmbed(width, embed_size)
suffix = get_col(cols.index(SUFFIX)) >> HashEmbed(width, embed_size)
shape = get_col(cols.index(SHAPE)) >> HashEmbed(width, embed_size)
2017-05-06 18:37:36 +03:00
tag = get_col(cols.index(TAG)) >> HashEmbed(width, embed_size)
2017-05-05 21:12:03 +03:00
tok2vec = (
doc2feats(cols)
>> with_flatten(
#(static | prefix | suffix | shape)
2017-05-06 18:37:36 +03:00
(lower | prefix | suffix | shape | tag)
2017-05-06 21:38:12 +03:00
>> Maxout(width, width*5)
2017-05-07 04:57:26 +03:00
>> Residual((ExtractWindow(nW=1) >> Maxout(width, width*3)))
>> Residual((ExtractWindow(nW=1) >> Maxout(width, width*3)))
>> Residual((ExtractWindow(nW=1) >> Maxout(width, width*3)))
)
2017-05-05 21:12:03 +03:00
)
return tok2vec
def doc2feats(cols):
def forward(docs, drop=0.):
feats = [doc.to_array(cols) for doc in docs]
feats = [model.ops.asarray(f, dtype='uint64') for f in feats]
return feats, None
model = layerize(forward)
return model