Ich versuche, die Dokumentation zu create a double type in Theano zu folgen und Operationen auf diesem Typ wie beschrieben here zu implementieren. Der aktuelle Zustand kann unten gefunden werden:So definieren Sie benutzerdefinierte Theano Typen ermöglichen Differenzierung
import theano
import random
import unittest
from double import double, dadd
class TestDoubleOps(unittest.TestCase):
# the forward pass runs fine ...
def test_DoubleAddOpPerform(self):
x = double('x')
y = double('y')
z = dadd(x, y)
f = theano.function([x, y], z)
for i in range(100):
x_value = random.random()
y_value = random.random()
self.assertAlmostEqual(f(x_value, y_value), x_value + y_value)
# I am trying to get the gradient computation working here,
# this is what I have so far:
def test_DoubleAddOpGrad(self):
x = double('x')
y = double('y')
z = dadd(x, y)
gx = theano.tensor.grad(z, x) # <---
gy = theano.tensor.grad(z, y)
f = theano.function([x, y], [gx, gy])
for i in range(100):
x_value = random.random()
y_value = random.random()
print(f(x_value, y_value))
if __name__ == '__main__':
unittest.main()
Wenn jedoch der Gradient Berechnung Testen, ich die folgende Fehlermeldung an:
import theano
class Double(theano.gof.Type):
def filter(self, value, strict = False, allow_downcast = None):
if strict:
# we need to return a type, but if the value is incompatible raise an exception
if isinstance(value, float):
return value
else:
raise TypeError('Expected a float!')
elif allow_downcast:
return float(value)
else:
value_float = float(value)
if value_float == value:
return value_float
else:
raise TypeError('The double type cannot be accurately represent %s of type %s' % (value, type(value)))
def values_eq_approx(self, value_a, value_b, tolerance = 1e-6):
return abs(value_a - value_b)/(abs(value_a) + abs(value_b)) < tolerance
double = Double()
class DoubleAddOp(theano.Op):
__props__ =()
def make_node(self, x, y):
# check input types
if isinstance(x, (int, float)):
x = theano.gof.Constant(double, x)
if isinstance(y, (int, float)):
y = theano.gof.Constant(double, y)
if x.type != double or y.type != double:
raise TypeError('DoubleAddOp only works on doubles.')
return theano.gof.Apply(self, [x, y], [double()])
def perform(self, node, inputs, output_storage):
x = inputs[0]
y = inputs[1]
z = output_storage[0]
z[0] = x + y
def infer_shape(self, node, input_shapes):
return [input_shapes[0]]
def grad(self, inputs, output_grads):
return [output_grads[0]*1, output_grads[0]*1]
def __str__(self):
return 'DoubleAddOp'
dadd = DoubleAddOp()
Um den Code zu testen, habe ich ein paar Unit-Tests geschrieben die markierte Zeile:
Traceback (most recent call last):
File "~/theano/double-type-python/double_test.py", line 32, in test_DoubleAddOpGrad
gx = theano.tensor.grad(z, x)
File "~/.local/lib/python3.5/site-packages/theano/gradient.py", line 436, in grad
if cost is not None and cost.ndim != 0:
AttributeError: 'Variable' object has no attribute 'ndim'
Es scheint, dass dies ein Problem des oben definierten Doppeltyps ist. Der Typ selbst ist jedoch skaliert, daher sollte ich in der Lage sein, die Gradienten unter Verwendung von theano.tensor.grad
zu berechnen. Leider konnte ich kein Beispiel finden, das die Gradientenberechnung für benutzerdefinierte Typen demonstriert und nicht mehr über das ndim
Attribut erfahren konnte ...
Jede Hilfe wird geschätzt; Vielen Dank!
Aktualisierung. Wenn versucht wird, theano.tensor.grad
auszutricksen, z.B. durch explizites Setzen von z.ndim = 0
gehen die Probleme weiter, z.B.
Traceback (most recent call last):
File "~/theano/double-type-python/double_test.py", line 33, in test_DoubleAddOpGrad
gx = theano.tensor.grad(z, x)
File "/usr/local/lib/python3.4/dist-packages/theano/gradient.py", line 477, in grad
g_cost = _float_ones_like(cost)
File "/usr/local/lib/python3.4/dist-packages/theano/gradient.py", line 1340, in _float_ones_like
dtype = x.type.dtype
AttributeError: 'Double' object has no attribute 'dtype'
So scheint es, dass ich etwas Residualton hier und der definierte Doppeltyp mehr verschiedene typspezifische Informationen fehlen, die in der Dokumentation nicht erwähnt wird.
Aktualisierung. Nachdem Sie die Dokumentation erneut gelesen und den Quellcode von Theano betrachtet haben, ist die richtige Frage zu stellen: Ist es möglich, benutzerdefinierte (Nicht-Tensor-) Typen in Theano zu definieren, die eine Differenzierung ermöglichen?
Aktualisierung. Basierend auf nouiz‘Antwort, ich in die nächsten Probleme leite - das mir den Eindruck geben, dass Gradienten Berechnung nicht für Nicht-TensorType Typen gedacht:
Traceback (most recent call last):
File "~/theano/double-type-python/double_test.py", line 32, in test_DoubleAddOpGrad
gx = theano.tensor.grad(z, x)
File "~/.local/lib/python3.5/site-packages/theano/gradient.py", line 477, in grad
g_cost = _float_ones_like(cost)
File "~/.local/lib/python3.5/site-packages/theano/gradient.py", line 1344, in _float_ones_like
return tensor.ones_like(x, dtype=dtype)
File "~/.local/lib/python3.5/site-packages/theano/tensor/basic.py", line 2377, in ones_like
return fill(model, ret)
File "~/.local/lib/python3.5/site-packages/theano/gof/op.py", line 604, in __call__
node = self.make_node(*inputs, **kwargs)
File "~/.local/lib/python3.5/site-packages/theano/tensor/elemwise.py", line 577, in make_node
inputs = list(map(as_tensor_variable, inputs))
File "~/.local/lib/python3.5/site-packages/theano/tensor/basic.py", line 171, in as_tensor_variable
"Variable type field must be a TensorType.", x, x.type)
theano.tensor.var.AsTensorError: ('Variable type field must be a TensorType.', DoubleAddOp.0, <double.Double object at 0x7fb623a5b9b0>)
Danke für die Klarstellung. Also habe ich den obigen Fix angewendet und auch "dtype = 'float64'" hinzugefügt, was die beiden genannten Probleme umgeht. Ich habe jedoch nicht den Eindruck, dass Gradientenberechnung mit Nicht-TensorType-Typen bei einem as_tensor_variablen-Aufruf arbeiten soll. Ich werde meine Frage mit dem entsprechenden Stack-Trace aktualisieren. Könnten Sie auch einen Link zu den Implementierungen bereitstellen, in denen Sie etwas Ähnliches für Sparse-Variablen tun (wie Sie bereits erwähnt haben)? Vielen Dank! –
Ich habe vergessen, für Sparse-Matrix, es funktioniert nur, wenn die Kosten ein Tensor ist. Dies kann mit einem größeren Unterschied behoben werden, den ich meiner Antwort hinzugefügt habe. Sie müssen auch die Methode ones_like() zu Ihrer Klasse hinzufügen. Ich gebe auch dafür einen Hinweis. Die Implementierung einer neuen Unterklasse von Variable wäre besser, aber ich finde keine Dokumentation darüber. – nouiz
So wie ich es sehe, macht das neue diff nur Sinn, wenn ich den Doppeltyp aus demano.tensor.TensorType ableite. Aber wenn ich von theano.tensor.TensorType ableite, brauche ich das diff nicht, da es auch ohne arbeitet. Dann stoße ich jedoch auf Probleme, wenn z.B. Implementieren einer Multiplikationsgradientenoperation & ldquor; return [inputs [1] * output_grads [0], Eingaben [0] * output_grads [0]] ", da die Multiplikationen Probleme zu verursachen scheinen. Insgesamt scheint es kein sehr fruchtbarer Weg zu sein - vielleicht werde ich etwas aus dem Sparse Tensor Code lernen oder ohne Theano auskommen. Aber vielen Dank für Ihre Hilfe! –