2017-08-18 18:45:48 +00:00
|
|
|
from libc.stdint cimport int32_t, uint64_t
|
2017-06-05 10:32:08 +00:00
|
|
|
import numpy
|
|
|
|
from collections import OrderedDict
|
|
|
|
import msgpack
|
|
|
|
import msgpack_numpy
|
|
|
|
msgpack_numpy.patch()
|
2017-08-18 18:45:48 +00:00
|
|
|
cimport numpy as np
|
2017-06-05 10:32:08 +00:00
|
|
|
|
2017-08-18 18:45:48 +00:00
|
|
|
from .typedefs cimport attr_t
|
2017-06-05 10:32:08 +00:00
|
|
|
from .strings cimport StringStore
|
|
|
|
from . import util
|
2017-08-19 17:52:25 +00:00
|
|
|
from .compat import basestring_
|
2017-06-05 10:32:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
cdef class Vectors:
|
|
|
|
'''Store, save and load word vectors.'''
|
|
|
|
cdef public object data
|
|
|
|
cdef readonly StringStore strings
|
2017-08-19 02:33:03 +00:00
|
|
|
cdef public object key2row
|
2017-08-19 16:42:11 +00:00
|
|
|
cdef public object keys
|
2017-06-05 10:32:08 +00:00
|
|
|
|
|
|
|
def __init__(self, strings, data_or_width):
|
|
|
|
self.strings = StringStore()
|
|
|
|
if isinstance(data_or_width, int):
|
|
|
|
self.data = data = numpy.zeros((len(strings), data_or_width),
|
|
|
|
dtype='f')
|
|
|
|
else:
|
|
|
|
data = data_or_width
|
|
|
|
self.data = data
|
2017-08-19 02:33:03 +00:00
|
|
|
self.key2row = {}
|
2017-08-19 16:42:11 +00:00
|
|
|
self.keys = np.ndarray((self.data.shape[0],), dtype='uint64')
|
2017-06-05 10:32:08 +00:00
|
|
|
for i, string in enumerate(strings):
|
2017-08-19 16:42:11 +00:00
|
|
|
key = self.strings.add(string)
|
|
|
|
self.key2row[key] = i
|
|
|
|
self.keys[i] = key
|
2017-06-05 10:32:08 +00:00
|
|
|
|
|
|
|
def __reduce__(self):
|
2017-06-05 10:36:04 +00:00
|
|
|
return (Vectors, (self.strings, self.data))
|
2017-06-05 10:32:08 +00:00
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
if isinstance(key, basestring):
|
|
|
|
key = self.strings[key]
|
2017-08-19 02:33:03 +00:00
|
|
|
i = self.key2row[key]
|
2017-06-05 10:32:08 +00:00
|
|
|
if i is None:
|
|
|
|
raise KeyError(key)
|
|
|
|
else:
|
|
|
|
return self.data[i]
|
|
|
|
|
|
|
|
def __setitem__(self, key, vector):
|
|
|
|
if isinstance(key, basestring):
|
|
|
|
key = self.strings.add(key)
|
2017-08-19 02:33:03 +00:00
|
|
|
i = self.key2row[key]
|
2017-06-05 10:32:08 +00:00
|
|
|
self.data[i] = vector
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
yield from self.data
|
|
|
|
|
|
|
|
def __len__(self):
|
2017-08-19 17:52:25 +00:00
|
|
|
# TODO: Fix the quadratic behaviour here!
|
|
|
|
return max(self.key2row.values())
|
|
|
|
|
|
|
|
def __contains__(self, key):
|
|
|
|
if isinstance(key, basestring_):
|
|
|
|
key = self.strings[key]
|
|
|
|
return key in self.key2row
|
|
|
|
|
|
|
|
def add_key(self, string, vector=None):
|
|
|
|
key = self.strings.add(string)
|
|
|
|
next_i = len(self) + 1
|
|
|
|
self.keys[next_i] = key
|
|
|
|
self.key2row[key] = next_i
|
|
|
|
if vector is not None:
|
|
|
|
self.data[next_i] = vector
|
2017-06-05 10:32:08 +00:00
|
|
|
|
|
|
|
def items(self):
|
|
|
|
for i, string in enumerate(self.strings):
|
|
|
|
yield string, self.data[i]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def shape(self):
|
|
|
|
return self.data.shape
|
|
|
|
|
|
|
|
def most_similar(self, key):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2017-08-18 18:45:48 +00:00
|
|
|
def to_disk(self, path, **exclude):
|
|
|
|
serializers = OrderedDict((
|
2017-08-19 17:52:25 +00:00
|
|
|
('vectors', lambda p: numpy.save(p.open('wb'), self.data, allow_pickle=False)),
|
2017-08-19 16:42:11 +00:00
|
|
|
('strings.json', self.strings.to_disk),
|
2017-08-19 17:52:25 +00:00
|
|
|
('keys', lambda p: numpy.save(p.open('wb'), self.keys, allow_pickle=False)),
|
2017-08-18 18:45:48 +00:00
|
|
|
))
|
2017-08-19 16:42:11 +00:00
|
|
|
return util.to_disk(path, serializers, exclude)
|
2017-08-18 18:45:48 +00:00
|
|
|
|
|
|
|
def from_disk(self, path, **exclude):
|
2017-08-19 16:42:11 +00:00
|
|
|
def load_keys(path):
|
|
|
|
self.keys = numpy.load(path)
|
|
|
|
for i, key in enumerate(self.keys):
|
|
|
|
self.keys[i] = key
|
|
|
|
self.key2row[key] = i
|
|
|
|
|
|
|
|
def load_vectors(path):
|
|
|
|
self.data = numpy.load(path)
|
2017-08-18 18:45:48 +00:00
|
|
|
|
|
|
|
serializers = OrderedDict((
|
2017-08-19 16:42:11 +00:00
|
|
|
('keys', load_keys),
|
|
|
|
('vectors', load_vectors),
|
|
|
|
('strings.json', self.strings.from_disk),
|
2017-08-18 18:45:48 +00:00
|
|
|
))
|
2017-08-19 16:42:11 +00:00
|
|
|
util.from_disk(path, serializers, exclude)
|
|
|
|
return self
|
2017-06-05 10:32:08 +00:00
|
|
|
|
|
|
|
def to_bytes(self, **exclude):
|
|
|
|
def serialize_weights():
|
2017-08-18 18:45:48 +00:00
|
|
|
if hasattr(self.data, 'to_bytes'):
|
|
|
|
return self.data.to_bytes()
|
2017-06-05 10:32:08 +00:00
|
|
|
else:
|
2017-08-18 18:45:48 +00:00
|
|
|
return msgpack.dumps(self.data)
|
2017-06-05 10:32:08 +00:00
|
|
|
serializers = OrderedDict((
|
2017-08-19 16:42:11 +00:00
|
|
|
('keys', lambda: msgpack.dumps(self.keys)),
|
2017-06-05 10:32:08 +00:00
|
|
|
('strings', lambda: self.strings.to_bytes()),
|
2017-08-18 18:45:48 +00:00
|
|
|
('vectors', serialize_weights)
|
2017-06-05 10:32:08 +00:00
|
|
|
))
|
|
|
|
return util.to_bytes(serializers, exclude)
|
|
|
|
|
|
|
|
def from_bytes(self, data, **exclude):
|
|
|
|
def deserialize_weights(b):
|
2017-08-18 18:45:48 +00:00
|
|
|
if hasattr(self.data, 'from_bytes'):
|
|
|
|
self.data.from_bytes()
|
2017-06-05 10:32:08 +00:00
|
|
|
else:
|
2017-08-18 18:45:48 +00:00
|
|
|
self.data = msgpack.loads(b)
|
2017-06-05 10:32:08 +00:00
|
|
|
|
2017-08-19 16:42:11 +00:00
|
|
|
def load_keys(keys):
|
|
|
|
for i, key in enumerate(keys):
|
|
|
|
self.keys[i] = key
|
|
|
|
self.key2row[key] = i
|
|
|
|
|
2017-06-05 10:32:08 +00:00
|
|
|
deserializers = OrderedDict((
|
2017-08-19 16:42:11 +00:00
|
|
|
('keys', lambda b: load_keys(msgpack.loads(b))),
|
2017-06-05 10:32:08 +00:00
|
|
|
('strings', lambda b: self.strings.from_bytes(b)),
|
2017-08-18 18:45:48 +00:00
|
|
|
('vectors', deserialize_weights)
|
2017-06-05 10:32:08 +00:00
|
|
|
))
|
2017-08-19 16:42:11 +00:00
|
|
|
util.from_bytes(deserializers, exclude)
|
|
|
|
return self
|