提交 fc39f6ba authored 作者: abergeron's avatar abergeron

Merge pull request #4472 from nouiz/ifelse_grad

[CRASH, ENH] Ifelse grad and speed up perform
...@@ -213,17 +213,24 @@ class IfElse(PureOp): ...@@ -213,17 +213,24 @@ class IfElse(PureOp):
gpu=self.gpu, gpu=self.gpu,
name=nw_name_f) name=nw_name_f)
if_true = ([ins[0]] + grads + [theano.tensor.zeros_like(t) # The grads can have a different dtype then the inputs.
for t in ts]) # As inputs true/false pair must have the same dtype,
if_false = ([ins[0]] + [theano.tensor.zeros_like(f) # we must cast the zeros to the corresponding grad dtype
for f in fs] + grads) # and not the input dtype.
if_true = ([ins[0]] +
grads +
[theano.tensor.zeros_like(t, dtype=grads[i].dtype)
for i, t in enumerate(ts)])
if_false = ([ins[0]] +
[theano.tensor.zeros_like(f, dtype=grads[i].dtype)
for i, f in enumerate(fs)] +
grads)
condition = ins[0] condition = ins[0]
# condition does affect the elements of the output so it is connected. # condition does affect the elements of the output so it is connected.
# For the sake of making the gradient convenient we assume that # For the sake of making the gradient convenient we assume that
# condition + epsilon always triggers the same branch as condition # condition + epsilon always triggers the same branch as condition
condition_grad = condition.zeros_like().astype(theano.config.floatX) condition_grad = condition.zeros_like().astype(theano.config.floatX)
return ([condition_grad] + return ([condition_grad] +
if_true_op(*if_true, **dict(return_list=True)) + if_true_op(*if_true, **dict(return_list=True)) +
if_false_op(*if_false, **dict(return_list=True))) if_false_op(*if_false, **dict(return_list=True)))
......
...@@ -172,28 +172,43 @@ class MultinomialFromUniform(Op): ...@@ -172,28 +172,43 @@ class MultinomialFromUniform(Op):
unis.shape[0], pvals.shape[0], n_samples) unis.shape[0], pvals.shape[0], n_samples)
if z[0] is None or z[0].shape != pvals.shape: if z[0] is None or z[0].shape != pvals.shape:
z[0] = numpy.zeros(pvals.shape, dtype=node.outputs[0].dtype) z[0] = numpy.zeros(pvals.shape, dtype=node.outputs[0].dtype)
else:
z[0].fill(0)
nb_multi = pvals.shape[0] nb_multi = pvals.shape[0]
nb_outcomes = pvals.shape[1] # Original version that is not vectorized. I keep it here as
# it is more readable.
# For each multinomial, loop over each possible outcome # For each multinomial, loop over each possible outcome
# nb_outcomes = pvals.shape[1]
# for c in range(n_samples):
# for n in range(nb_multi):
# waiting = True
# cummul = 0
# unis_n = unis[c * nb_multi + n]
# for m in range(nb_outcomes):
# cummul += pvals[n, m]
# if c == 0:
# if (waiting and (cummul > unis_n)):
# z[0][n, m] = 1
# waiting = False
# else:
# # Only needed if we don't init the output to 0
# z[0][n, m] = 0
# else:
# if (cummul > unis_n):
# z[0][n, m] += 1
# break
# Vectorized version that is much faster as all the looping is
# done in C even if this make extra work.
for c in range(n_samples): for c in range(n_samples):
for n in range(nb_multi): for n in range(nb_multi):
waiting = True
cummul = 0
unis_n = unis[c * nb_multi + n] unis_n = unis[c * nb_multi + n]
# The dtype='float64' is important. Otherwise we don't
for m in range(nb_outcomes): # have the same answer as the c code as in the c code
cummul += pvals[n, m] # the cumul is in double precission.
if c == 0: cumsum = pvals[n].cumsum(dtype='float64')
if (waiting and (cummul > unis_n)): z[0][n, numpy.searchsorted(cumsum, unis_n)] += 1
z[0][n, m] = 1
waiting = False
else:
z[0][n, m] = 0
else:
if (cummul > unis_n):
z[0][n, m] += 1
break
class MultinomialWOReplacementFromUniform(MultinomialFromUniform): class MultinomialWOReplacementFromUniform(MultinomialFromUniform):
......
...@@ -482,6 +482,21 @@ class test_ifelse(unittest.TestCase, utt.TestOptimizationMixin): ...@@ -482,6 +482,21 @@ class test_ifelse(unittest.TestCase, utt.TestOptimizationMixin):
finally: finally:
theano.config.compute_test_value = backup theano.config.compute_test_value = backup
def test_grad_int_value(self):
w = theano.shared(numpy.random.rand(10))
b = theano.shared(numpy.random.rand())
params = [w, b]
x = tensor.vector()
y = tensor.scalar()
score = w.dot(x) + b
correct = (score * y > 0)
loss = ifelse(correct, 0, 1)
[(param, param - 0.5 * tensor.grad(cost=loss, wrt=param))
for param in params]
if __name__ == '__main__': if __name__ == '__main__':
print(' Use nosetests to run these tests ') print(' Use nosetests to run these tests ')
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论