2017-01-25 9 views
1

Я пытаюсь выполнить документацию до create a double type в Theano и выполнить операции над этим типом, как описано here. Текущее состояние можно найти ниже:Как определить пользовательские типы термонов, допускающие дифференциацию

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

Для того, чтобы проверить код, я написал несколько модульных тестов:

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

Однако при тестировании вычисления градиента, я получаю следующее сообщение об ошибке в обозначенная строка:

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' 

Похоже, что это проблема двойного типа, определенного выше. Однако сам тип является масштабом, поэтому я должен иметь возможность вычислять градиенты с использованием theano.tensor.grad. К сожалению, я не смог найти пример, демонстрирующий вычисление градиента на пользовательских типах и не смог узнать больше об атрибуте ndim ...

Любая помощь приветствуется; благодаря!

Обновление. При попытке обмануть theano.tensor.grad, например. явно указывая z.ndim = 0, проблемы продолжаются, например.

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' 

Таким образом, кажется, что я пропускаю что-то фундаментальное здесь и определяется тип Double отсутствует несколько различную информацию типоспецифической, которая не упоминается в документации.

Обновление. После повторного чтения документации и изучения исходного кода Theano, правильный вопрос: можно ли определить пользовательские (не тензорные) типы в Theano, которые допускают различие?

Обновление. На основании ответа nouiz», я бег в следующие проблемы - они дают мне ощущение, что градиент вычисление не предназначено для неконтактного типа TensorType:

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

ответ

1

Ответ да. Ты можешь. Мы делаем это для разреженной переменной и переменной GPU.

Но вы попадаете в угловые шкалы, чтобы не использовать anano.grad(). В основном, он ожидает параметр ndim и параметр dtype. Добавление параметра dtype = "float64" должно обойти это.

ndim один легко исправить в Теано с этим дифф:

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

Для DTYPE, он более сложен, поскольку мы используем его во многих местах для verifycation цели (вы не можете взять на себя град из целые числа, например), а также инициализировать цепочку градиента (или вы можете передать его с помощью параметра known_grad)

UPDATE: новая ошибка может быть исправлена ​​с этим большим дифф:

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) 

Вам нужно добавить метод one_like к вашей переменной следующим образом: def my_ones_like (model, dtype = None): return ... double.ones_like = my_ones_like

+0

Спасибо за разъяснение. Поэтому я применил исправление выше, а также добавил «dtype = 'float64», который работает по двум упомянутым проблемам. Однако у меня нет впечатления, что вычисление градиента предназначено для работы с типами без TensorType при вызове as_tensor_variable. Я обновляю свой вопрос с помощью соответствующей трассировки стека. Не могли бы вы также предоставить ссылку на реализации, где вы делаете что-то подобное для разреженных переменных (как вы упомянули)? Благодаря! –

+0

Я забыл, что для разреженной матрицы это работает, только если стоимость является тензором. Это может быть исправлено с большим различием, которое я добавил к моему ответу. Вам также необходимо добавить метод one_like() в ваш класс. Я тоже даю указание на это. При внедрении нового подкласса Variable было бы лучше, но я не нахожу документацию об этом. – nouiz

+0

Так как я вижу это, новый diff имеет смысл, если я получаю двойной тип от theano.tensor.TensorType. Но когда вы получаете от theano.tensor.TensorType, мне не нужен diff, поскольку он также работает без него. Однако, тогда я сталкиваюсь с проблемами, когда, например, реализуя операцию градиента умножения 'return [input [1] * output_grads [0], входы [0] * output_grads [0]]', поскольку умножения, похоже, вызывают проблемы. В целом, это, кажется, не очень плодотворный путь - может быть, я кое-что узнаю из кода разреженного тензора или уйду без Теано. Но большое спасибо за вашу помощь! –