2017-01-25 2 views
1

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>) 

Antwort

1

Die Antwort ist ja. Sie können. Wir machen das selbst für sparse Variable und GPU Variable.

Aber Sie schlagen Ecke Fälle theano.grad() wurde nicht gemacht, um zu unterstützen. Meistens erwartet es einen ndim Parameter und einen dtype Parameter. Das Hinzufügen des Parameters dtype = "float64" sollte das umgehen.

NDIM ist einfach mit diesem diff in Theano zu beheben:

diff --git a/theano/gradient.py b/theano/gradient.py 
index 6d6fbaf..3b4d706 100644 
--- a/theano/gradient.py 
+++ b/theano/gradient.py 
@@ -433,7 +433,7 @@ def grad(cost, wrt, consider_constant=None, 
          "cost is NaN because " + 
          cost.type.why_null) 

- if cost is not None and cost.ndim != 0: 
+ if cost is not None and getattr(cost, 'ndim', 0) != 0: 
     raise TypeError("cost must be a scalar.") 

    if isinstance(wrt, set): 

Für die dtype, es komplizierter ist, wie wir es an vielen Stellen für verifycation Zweck verwenden (Sie können die grad nicht nehmen kann Zahlen zum Beispiel) und auch die Kette der Steigung an init (oder Sie es über den known_grad Parameter)

UPDATE passieren können: der neue Fehler mit diesem größeren diff behoben werden kann:

diff --git a/theano/gradient.py b/theano/gradient.py 
index 6d6fbaf..6a9ec03 100644 
--- a/theano/gradient.py 
+++ b/theano/gradient.py 
@@ -433,7 +433,7 @@ def grad(cost, wrt, consider_constant=None, 
          "cost is NaN because " + 
          cost.type.why_null) 

- if cost is not None and cost.ndim != 0: 
+ if cost is not None and getattr(cost, 'ndim', 0) != 0: 
     raise TypeError("cost must be a scalar.") 

    if isinstance(wrt, set): 
@@ -1341,7 +1341,7 @@ def _float_ones_like(x): 
    if dtype not in tensor.float_dtypes: 
     dtype = theano.config.floatX 

- return tensor.ones_like(x, dtype=dtype) 
+ return x.ones_like(dtype=dtype) 


class numeric_grad(object): 
diff --git a/theano/tensor/var.py b/theano/tensor/var.py 
index 2ecb9f0..6b08a45 100644 
--- a/theano/tensor/var.py 
+++ b/theano/tensor/var.py 
@@ -727,6 +727,9 @@ class _tensor_py_operators(object): 
    def zeros_like(model, dtype=None): 
     return theano.tensor.basic.zeros_like(model, dtype=dtype) 

+ def ones_like(model, dtype=None): 
+  return theano.tensor.basic.ones_like(model, dtype=dtype) 
+ 
    def cumsum(self, axis=None): 
     return theano.tensor.extra_ops.cumsum(self, axis) 

Sie müssen die ones_like-Methode wie folgt zu Ihrer Variablen hinzufügen: def my_ones_like (Modell, dtype = Keine): return ... double.ones_like = my_ones_like

+0

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! –

+0

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

+0

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! –

Verwandte Themen