transform

old TransE-like models
git clone https://esimon.eu/repos/transform.git
Log | Files | Refs | README

commit 92227335bd42489fe0a25e7670b36d05fb4a519f
parent 051a8e243443b93520a4c6bd506abe1220550802
Author: Étienne Simon <esimon@esimon.eu>
Date:   Thu, 17 Apr 2014 11:53:53 +0200

Fix ranking & Speed up by abusing broadcasting

Diffstat:
Mdataset.py | 9++-------
Mmodel.py | 45++++++++++++++++++++++++++++++---------------
Mrelations/translations.py | 6+++++-
3 files changed, 37 insertions(+), 23 deletions(-)

diff --git a/dataset.py b/dataset.py @@ -57,18 +57,13 @@ class Dataset(object): relation = train_relation[f:t] yield (relation, left_positive, right_positive, left_negative, right_negative) - def iterate(self, name, batch_size): - def repeat_csr(matrix, size): - data = list(matrix.data)*size - indices = list(matrix.indices)*size - indptr = range(size+1) - return scipy.sparse.csr_matrix((data, indices, indptr), shape=(size, matrix.shape[1]), dtype=theano.config.floatX) + def iterate(self, name): N = getattr(self, name+'_size') relation = getattr(self, name+'_relation') left = getattr(self, name+'_left') right = getattr(self, name+'_right') for i in xrange(N): - yield (repeat_csr(relation[i], batch_size), repeat_csr(left[i], batch_size), right[i]) + yield (relation[i], left[i], right[i]) def universe_minibatch(self, batch_size): N = len(self.embeddings) diff --git a/model.py b/model.py @@ -78,14 +78,23 @@ class Model(object): inputs = tuple(S.csr_matrix() for _ in xrange(5)) positive_left, positive_right = self.embeddings.embed(inputs[1]), self.embeddings.embed(inputs[2]) negative_left, negative_right = self.embeddings.embed(inputs[3]), self.embeddings.embed(inputs[4]) - positive_score = self.hyperparameters['similarity'](self.relations.apply(positive_left, inputs[0]), positive_right) - negative_score = self.hyperparameters['similarity'](self.relations.apply(negative_left, inputs[0]), negative_right) + relation = self.relations.lookup(inputs[0]) + positive_score = self.hyperparameters['similarity'](self.relations.apply(positive_left, relation), positive_right) + negative_score = self.hyperparameters['similarity'](self.relations.apply(negative_left, relation), negative_right) score = self.hyperparameters['margin'] + positive_score - negative_score violating_margin = score>0 criterion = T.mean(violating_margin*score) self.train_function = theano.function(inputs=list(inputs), outputs=[criterion], updates=self.updates(criterion)) - self.scoring_function = theano.function(inputs=list(inputs[0:3]), outputs=[positive_score]) + + relation = T.addbroadcast(relation, 0) + broadcasted_left = T.addbroadcast(positive_left, 0) + broadcasted_right = T.addbroadcast(positive_right, 0) + left_score = self.hyperparameters['similarity'](self.relations.apply(broadcasted_left, relation), positive_right) + right_score = self.hyperparameters['similarity'](self.relations.apply(positive_left, relation), broadcasted_right) + + self.left_scoring_function = theano.function(inputs=list(inputs[0:3]), outputs=[left_score]) + self.right_scoring_function = theano.function(inputs=list(inputs[0:3]), outputs=[right_score]) def updates(self, cost): """ Compute the updates to perform a SGD step w.r.t. a given cost. @@ -117,18 +126,24 @@ class Model(object): """ Compute the mean rank and top 10 on a given data. """ batch_size = self.hyperparameters['test batch size'] count, mean, top10 = 0, 0, 0 - for (relation, left, right) in self.dataset.iterate(name, batch_size): # TODO Test symmetric - scores = None + for (relation, left, right) in self.dataset.iterate(name): + left_scores, right_scores = None, None for entities in self.dataset.universe_minibatch(batch_size): - if left.shape != entities.shape: - left = left[0:entities.shape[0]] - relation = relation[0:entities.shape[0]] - batch_result = self.scoring_function(relation, left, entities) - scores = numpy.array(batch_result, dtype=theano.config.floatX) if scores is None else numpy.concatenate((scores, batch_result), axis=1) - rank = numpy.asscalar(numpy.where(numpy.argsort(scores)==right.indices[0])[1]) # FIXME Ugly - mean = mean + rank - count = count + 1 - top10 = top10 + (rank<=10) + left_batch_result = self.left_scoring_function(relation, left, entities) + right_batch_result = self.right_scoring_function(relation, entities, right) + if left_scores is None: + left_scores = numpy.array(left_batch_result, dtype=theano.config.floatX) + else: + left_scores = numpy.concatenate((left_scores, left_batch_result), axis=1) + if right_scores is None: + right_scores = numpy.array(right_batch_result, dtype=theano.config.floatX) + else: + right_scores = numpy.concatenate((right_scores, right_batch_result), axis=1) + left_rank = numpy.asscalar(numpy.where(numpy.argsort(left_scores)==right.indices[0])[1]) # FIXME Ugly + right_rank = numpy.asscalar(numpy.where(numpy.argsort(right_scores)==left.indices[0])[1]) # FIXME Ugly + count = count + 2 + mean = mean + left_rank + right_rank + top10 = top10 + (left_rank<=10) + (right_rank<=10) mean = float(mean) / count top10 = float(top10) / count return (mean, top10) @@ -141,7 +156,7 @@ class Model(object): if self.hyperparameters['validate on training data']: (train_mean, train_top10) = self.error('train') print >>sys.stderr, 'train mean: {0:<15} train top10: {1:<15}'.format(train_mean, train_top10) - else + else: print >>sys.stderr, '' def test(self): diff --git a/relations/translations.py b/relations/translations.py @@ -31,9 +31,13 @@ class Translations(object): self.parameters = [self.R] + def lookup(self, relations): + """ Embed given relations. """ + return S.dot(relations, self.R) + def apply(self, inputs, relations): """ Apply the given relations to a given input. """ - return S.dot(relations, self.R)+inputs + return relations + inputs def updates(self, cost, learning_rate): """ Compute the updates to perform a SGD step w.r.t. a given cost.