我是tensorflow 2.0的新手,除了从样板代码中设计和训练一些人工神经网络之外,我没有做什么。我正在尝试解决一个新加入新的tensorflow的练习。我创建了一些代码,但它不起作用。下面是问题定义
假设我们有有理数的张量M ( (a, b, c) )和标量p ∈ (0, 1) (记忆因子),让我们创建一个函数,返回张量N的形式为(a, b, c)。沿c轴移动的N张量的每一个元素都应增加前驱体的值乘以p。
假设我们有张量:
T = [x1, x2, x3, x4]在(1, 1, 4)的形状中,我们希望得到向量:
[x1, x2+x1·p, x3+(x2+x1·p)·p, x4+(x3+(x2+x1·p)·p)*p] 解决方案应该在Tensorflow 2.0中创建,并且应该集中于在CPU上交付最短的执行时间。生成的图应该允许有效地计算张量M和值p上的导数。
这是我到现在为止创建的代码
import tensorflow as tf
@tf.function
def vectorize_predec(t, p):
last_elem = 0
result = []
for el in t:
result.append(el + (p * last_elem))
last_elem = el + (p * last_elem)
return result
p = tf.Variable(0.5, dtype='double')
m = tf.constant([[0, 1, 2, 3, 4],
[1, 3, 5, 7, 10],
[1, 1, 1, -1, 0]])
vectorize_predec(m, p)但它抛出了一个TypeError。
我查看了文档,我看到了像cumsum和polyeval这样的函数,但我不确定它们是否符合我的需求。据我所知,我需要编写自己的带有@tf.function注释的客户函数。我也不知道如何根据问题定义正确处理三维张量(添加前驱体应该发生在最后一个("c")轴上)。
我在文档(此处:https://www.tensorflow.org/tutorials/customization/performance)中看到,有一些方法可以度量生成的图形的大小。不过,我不知道“图”如何能有效地计算张量M和值p上的导数。ELI5的答案很受欢迎,或者至少有一些我可以阅读的材料来更好地教育自己。
非常感谢!
发布于 2020-03-11 11:59:03
我将给出几种不同的实现方法。我认为最明显的解决方案是使用tf.scan
import tensorflow as tf
def apply_momentum_scan(m, p, axis=0):
# Put axis first
axis = tf.convert_to_tensor(axis, dtype=tf.int32)
perm = tf.concat([[axis], tf.range(axis), tf.range(axis + 1, tf.rank(m))], axis=0)
m_t = tf.transpose(m, perm)
# Do computation
res_t = tf.scan(lambda a, x: a * p + x, m_t)
# Undo transpose
perm_t = tf.concat([tf.range(1, axis + 1), [0], tf.range(axis + 1, tf.rank(m))], axis=0)
return tf.transpose(res_t, perm_t)但是,如果构建了指数因子矩阵,也可以将其实现为特定的矩阵产品:
import tensorflow as tf
def apply_momentum_matmul(m, p, axis=0):
# Put axis first and reshape
m = tf.convert_to_tensor(m)
p = tf.convert_to_tensor(p)
axis = tf.convert_to_tensor(axis, dtype=tf.int32)
perm = tf.concat([[axis], tf.range(axis), tf.range(axis + 1, tf.rank(m))], axis=0)
m_t = tf.transpose(m, perm)
shape_t = tf.shape(m_t)
m_tr = tf.reshape(m_t, [shape_t[0], -1])
# Build factors matrix
r = tf.range(tf.shape(m_tr)[0])
p_tr = tf.linalg.band_part(p ** tf.dtypes.cast(tf.expand_dims(r, 1) - r, p.dtype), -1, 0)
# Do computation
res_tr = p_tr @ m_tr
# Reshape back and undo transpose
res_t = tf.reshape(res_tr, shape_t)
perm_t = tf.concat([tf.range(1, axis + 1), [0], tf.range(axis + 1, tf.rank(m))], axis=0)
return tf.transpose(res_t, perm_t)这也可以重写,以避免第一次转置(在TensorFlow中是昂贵的)与tf.tensordot。
import tensorflow as tf
def apply_momentum_tensordot(m, p, axis=0):
# Put axis first and reshape
m = tf.convert_to_tensor(m)
# Build factors matrix
r = tf.range(tf.shape(m)[axis])
p_mat = tf.linalg.band_part(p ** tf.dtypes.cast(tf.expand_dims(r, 1) - r, p.dtype), -1, 0)
# Do computation
res_t = tf.linalg.tensordot(m, p_mat, axes=[[axis], [1]])
# Transpose
last_dim = tf.rank(res_t) - 1
perm_t = tf.concat([tf.range(axis), [last_dim], tf.range(axis, last_dim)], axis=0)
return tf.transpose(res_t, perm_t)这三项职能将以类似的方式使用:
import tensorflow as tf
p = tf.Variable(0.5, dtype=tf.float32)
m = tf.constant([[0, 1, 2, 3, 4],
[1, 3, 5, 7, 10],
[1, 1, 1, -1, 0]], tf.float32)
# apply_momentum is one of the functions above
print(apply_momentum(m, p, axis=0).numpy())
# [[ 0. 1. 2. 3. 4. ]
# [ 1. 3.5 6. 8.5 12. ]
# [ 1.5 2.75 4. 3.25 6. ]]
print(apply_momentum(m, p, axis=1).numpy())
# [[ 0. 1. 2.5 4.25 6.125 ]
# [ 1. 3.5 6.75 10.375 15.1875]
# [ 1. 1.5 1.75 -0.125 -0.0625]]使用矩阵积是更渐进的复杂,但它可以比扫描更快。以下是一个小小的基准:
import tensorflow as tf
import numpy as np
# Make test data
tf.random.set_seed(0)
p = tf.constant(0.5, dtype=tf.float32)
m = tf.random.uniform([100, 30, 50], dtype=tf.float32)
# Axis 0
print(np.allclose(apply_momentum_scan(m, p, 0).numpy(), apply_momentum_matmul(m, p, 0).numpy()))
# True
print(np.allclose(apply_momentum_scan(m, p, 0).numpy(), apply_momentum_tensordot(m, p, 0).numpy()))
# True
%timeit apply_momentum_scan(m, p, 0)
# 11.5 ms ± 610 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit apply_momentum_matmul(m, p, 0)
# 1.36 ms ± 18.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit apply_momentum_tensordot(m, p, 0)
# 1.62 ms ± 7.39 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# Axis 1
print(np.allclose(apply_momentum_scan(m, p, 1).numpy(), apply_momentum_matmul(m, p, 1).numpy()))
# True
print(np.allclose(apply_momentum_scan(m, p, 1).numpy(), apply_momentum_tensordot(m, p, 1).numpy()))
# True
%timeit apply_momentum_scan(m, p, 1)
# 4.27 ms ± 60.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit apply_momentum_matmul(m, p, 1)
# 1.27 ms ± 36.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit apply_momentum_tensordot(m, p, 1)
# 1.2 ms ± 11.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# Axis 2
print(np.allclose(apply_momentum_scan(m, p, 2).numpy(), apply_momentum_matmul(m, p, 2).numpy()))
# True
print(np.allclose(apply_momentum_scan(m, p, 2).numpy(), apply_momentum_tensordot(m, p, 2).numpy()))
# True
%timeit apply_momentum_scan(m, p, 2)
# 6.29 ms ± 64.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit apply_momentum_matmul(m, p, 2)
# 1.41 ms ± 21.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit apply_momentum_tensordot(m, p, 2)
# 1.05 ms ± 26 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)矩阵积似乎赢了。让我们看看这个比例是否:
import tensorflow as tf
import numpy as np
# Make test data
tf.random.set_seed(0)
p = tf.constant(0.5, dtype=tf.float32)
m = tf.random.uniform([1000, 300, 500], dtype=tf.float32)
# Axis 0
print(np.allclose(apply_momentum_scan(m, p, 0).numpy(), apply_momentum_matmul(m, p, 0).numpy()))
# True
print(np.allclose(apply_momentum_scan(m, p, 0).numpy(), apply_momentum_tensordot(m, p, 0).numpy()))
# True
%timeit apply_momentum_scan(m, p, 0)
# 784 ms ± 6.78 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit apply_momentum_matmul(m, p, 0)
# 1.13 s ± 76.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit apply_momentum_tensordot(m, p, 0)
# 1.3 s ± 27 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# Axis 1
print(np.allclose(apply_momentum_scan(m, p, 1).numpy(), apply_momentum_matmul(m, p, 1).numpy()))
# True
print(np.allclose(apply_momentum_scan(m, p, 1).numpy(), apply_momentum_tensordot(m, p, 1).numpy()))
# True
%timeit apply_momentum_scan(m, p, 1)
# 852 ms ± 12.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit apply_momentum_matmul(m, p, 1)
# 659 ms ± 10.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit apply_momentum_tensordot(m, p, 1)
# 741 ms ± 19.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# Axis 2
print(np.allclose(apply_momentum_scan(m, p, 2).numpy(), apply_momentum_matmul(m, p, 2).numpy()))
# True
print(np.allclose(apply_momentum_scan(m, p, 2).numpy(), apply_momentum_tensordot(m, p, 2).numpy()))
# True
%timeit apply_momentum_scan(m, p, 2)
# 1.06 s ± 16.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit apply_momentum_matmul(m, p, 2)
# 924 ms ± 17 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit apply_momentum_tensordot(m, p, 2)
# 483 ms ± 10.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)现在已经不太清楚了。扫描仍然不是超快,但矩阵产品有时较慢。正如您可以想象的那样,如果您进入更大的张量,矩阵积的复杂性将主导时间。
因此,如果您想要最快的解决方案,并且知道您的张量不会太大,请使用矩阵产品实现之一。如果你的速度还不错,但你想确保你没有耗尽内存(矩阵解也需要更多的时间)和时间是可预测的,你可以使用扫描解决方案。
注:以上基准是在CPU上进行的,结果可能在GPU上有很大差异。
发布于 2020-03-13 07:15:51
以下是一个只提供了一些信息的答案,以及一个修复代码的简单解决方案--而不是实际的问题(请参考下面的原因)。
首先,TypeError是您早期尝试中的张量中不兼容类型的问题。有些张量包含浮点数(双),有些包含整数。它将有助于显示完整的错误信息:
TypeError: Input 'y' of 'Mul' Op has type int32 that does not match type float64 of argument 'x'.它恰巧在正确的轨道上(尽管堆栈跟踪的血淋淋的细节)。
下面是一个简单的修复方法,可以让代码正常工作(针对目标问题的注意事项):
import tensorflow as tf
@tf.function
def vectorize_predec(t, p):
_p = tf.transpose(
tf.convert_to_tensor(
[p * t[...,idx] for idx in range(t.shape[-1] - 1)],
dtype=tf.float64))
_p = tf.concat([
tf.zeroes((_p.shape[0], 1), dtype=tf.float64),
_p
], axis=1)
return t + _p
p = tf.Variable(0.5, dtype='double')
m = tf.constant([[0, 1, 2, 3, 4],
[1, 3, 5, 7, 10],
[1, 1, 1, -1, 0]], dtype=tf.float64)
n = tf.constant([[0.0, 1.0, 2.5, 4.0, 5.5],
[1.0, 3.5, 6.5, 9.5, 13.5],
[1.0, 1.5, 1.5, -0.5, -0.5]], dtype=tf.float64)
print(f'Expected: {n}')
result = vectorize_predec(m, p)
print(f'Result: {result}')
tf.test.TestCase().assertAllEqual(n, result)主要变化如下:
m张量得到一个dtype=tf.float64来匹配原始的double,所以类型错误消失了。N中的值是在更新之前还是之后计算出来的。以下是更新前的版本,要简单得多。要解决看似“真正”的问题,需要在函数上做更多的工作(请参阅其他答案,我可能在这里做得更多)。该功能是如何工作的:
p * x1、p * x2等计算成一个标准的p * x1数组。注意,它在最后一个维度的最后一个元素之前停止,因为我们将移动数组。,
tf.convert_to_tensor将数组转换为张量,因此将数组添加到计算图中。转置是匹配原始张量形状所必需的(我们可以避免它)。这些值变成了x1 + 0.0 * p,然后是x2 + x1 * p等等。这说明了一些要查看的函数和问题(类型、形状),但我承认它是作弊的,并不能解决实际的问题。
而且,这段代码在任何硬件上都是无效的。这只是说明性的,需要(1)消除Python数组,(2)消除转置,(3)消除级联操作。希望伟大的训练:-)
额外注释:
T = [x1, x2, x3, x4]的形状实际上是(4,),假设xi是标量。tf.float64?默认情况下,我们得到tf.float32,而删除double将使代码工作。但是这个例子将失去类型的重要性,所以选择显式的非默认类型(和更丑陋的代码)。https://stackoverflow.com/questions/60590333
复制相似问题