Я пытаюсь выполнить документацию до 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>)
Спасибо за разъяснение. Поэтому я применил исправление выше, а также добавил «dtype = 'float64», который работает по двум упомянутым проблемам. Однако у меня нет впечатления, что вычисление градиента предназначено для работы с типами без TensorType при вызове as_tensor_variable. Я обновляю свой вопрос с помощью соответствующей трассировки стека. Не могли бы вы также предоставить ссылку на реализации, где вы делаете что-то подобное для разреженных переменных (как вы упомянули)? Благодаря! –
Я забыл, что для разреженной матрицы это работает, только если стоимость является тензором. Это может быть исправлено с большим различием, которое я добавил к моему ответу. Вам также необходимо добавить метод one_like() в ваш класс. Я тоже даю указание на это. При внедрении нового подкласса Variable было бы лучше, но я не нахожу документацию об этом. – nouiz
Так как я вижу это, новый diff имеет смысл, если я получаю двойной тип от theano.tensor.TensorType. Но когда вы получаете от theano.tensor.TensorType, мне не нужен diff, поскольку он также работает без него. Однако, тогда я сталкиваюсь с проблемами, когда, например, реализуя операцию градиента умножения 'return [input [1] * output_grads [0], входы [0] * output_grads [0]]', поскольку умножения, похоже, вызывают проблемы. В целом, это, кажется, не очень плодотворный путь - может быть, я кое-что узнаю из кода разреженного тензора или уйду без Теано. Но большое спасибо за вашу помощь! –