首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >由Tensorflow 2.0中的前身增加张量的每个元素

由Tensorflow 2.0中的前身增加张量的每个元素
EN

Stack Overflow用户
提问于 2020-03-08 17:46:06
回答 2查看 497关注 0票数 7

我是tensorflow 2.0的新手,除了从样板代码中设计和训练一些人工神经网络之外,我没有做什么。我正在尝试解决一个新加入新的tensorflow的练习。我创建了一些代码,但它不起作用。下面是问题定义

假设我们有有理数的张量M ( (a, b, c) )和标量p ∈ (0, 1) (记忆因子),让我们创建一个函数,返回张量N的形式为(a, b, c)。沿c轴移动的N张量的每一个元素都应增加前驱体的值乘以p

假设我们有张量:

代码语言:javascript
复制
T = [x1, x2, x3, x4]

(1, 1, 4)的形状中,我们希望得到向量:

代码语言:javascript
复制
[x1, x2+x1·p, x3+(x2+x1·p)·p, x4+(x3+(x2+x1·p)·p)*p] 

解决方案应该在Tensorflow 2.0中创建,并且应该集中于在CPU上交付最短的执行时间。生成的图应该允许有效地计算张量M和值p上的导数。

这是我到现在为止创建的代码

代码语言:javascript
复制
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

我查看了文档,我看到了像cumsumpolyeval这样的函数,但我不确定它们是否符合我的需求。据我所知,我需要编写自己的带有@tf.function注释的客户函数。我也不知道如何根据问题定义正确处理三维张量(添加前驱体应该发生在最后一个("c")轴上)。

我在文档(此处:https://www.tensorflow.org/tutorials/customization/performance)中看到,有一些方法可以度量生成的图形的大小。不过,我不知道“图”如何能有效地计算张量M和值p上的导数。ELI5的答案很受欢迎,或者至少有一些我可以阅读的材料来更好地教育自己。

非常感谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-03-11 11:59:03

我将给出几种不同的实现方法。我认为最明显的解决方案是使用tf.scan

代码语言:javascript
复制
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)

但是,如果构建了指数因子矩阵,也可以将其实现为特定的矩阵产品:

代码语言:javascript
复制
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

代码语言:javascript
复制
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)

这三项职能将以类似的方式使用:

代码语言:javascript
复制
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]]

使用矩阵积是更渐进的复杂,但它可以比扫描更快。以下是一个小小的基准:

代码语言:javascript
复制
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)

矩阵积似乎赢了。让我们看看这个比例是否:

代码语言:javascript
复制
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上有很大差异。

票数 5
EN

Stack Overflow用户

发布于 2020-03-13 07:15:51

以下是一个只提供了一些信息的答案,以及一个修复代码的简单解决方案--而不是实际的问题(请参考下面的原因)。

首先,TypeError是您早期尝试中的张量中不兼容类型的问题。有些张量包含浮点数(双),有些包含整数。它将有助于显示完整的错误信息:

代码语言:javascript
复制
TypeError: Input 'y' of 'Mul' Op has type int32 that does not match type float64 of argument 'x'.

它恰巧在正确的轨道上(尽管堆栈跟踪的血淋淋的细节)。

下面是一个简单的修复方法,可以让代码正常工作(针对目标问题的注意事项):

代码语言:javascript
复制
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 * x1p * x2等计算成一个标准的p * x1数组。注意,它在最后一个维度的最后一个元素之前停止,因为我们将移动数组。

  • ,它用tf.convert_to_tensor将数组转换为张量,因此将数组添加到计算图中。转置是匹配原始张量形状所必需的(我们可以避免它)。
  • 在每个维度的起始处沿最后一个轴附加零。
  • ,结果是原始张量和构造的张量之和。

这些值变成了x1 + 0.0 * p,然后是x2 + x1 * p等等。这说明了一些要查看的函数和问题(类型、形状),但我承认它是作弊的,并不能解决实际的问题。

而且,这段代码在任何硬件上都是无效的。这只是说明性的,需要(1)消除Python数组,(2)消除转置,(3)消除级联操作。希望伟大的训练:-)

额外注释:

  • 问题要求求形状张量(a,b,c)的解。您共享的代码在形状(a,b)的张量上工作,因此修复代码仍然不能解决问题。
  • 问题需要有理数。不知道目的是什么,这个答案把这个要求搁置一边。
  • T = [x1, x2, x3, x4]的形状实际上是(4,),假设xi是标量。
  • 为什么是tf.float64?默认情况下,我们得到tf.float32,而删除double将使代码工作。但是这个例子将失去类型的重要性,所以选择显式的非默认类型(和更丑陋的代码)。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60590333

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档