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:
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.