tensorflow - 为什么TensorFlow在调用1D卷积时要计算2D卷积?
问题描述
在 tf.nn.conv1d 的文档中,指出
在内部,此操作会重塑输入张量并调用 tf.nn.conv2d。例如,如果data_format不以“NC”开头,则将形状为[batch, in_width, in_channels]的张量reshape为[batch, 1, in_width, in_channels],并且filter被reshape为[1, filter_width, in_channels, out_channels]。然后将结果重新整形回 [batch, out_width, out_channels](其中 out_width 是步幅和填充的函数,如 conv2d 中一样)并返回给调用者。
我知道这些操作是等效的,但我对这个实现细节的含义有点困惑。
重塑是否会产生一些计算开销?3D 卷积有它自己的实现,那么为什么不是 1D 卷积呢?
感谢任何解释,帮助我和其他人理解 TensorFlow 的这个实现细节!
解决方案
通过挖掘源代码,我得出结论,它可能是为了方便和实现的极简主义而完成的——细节如下。
首先,没有“重塑”,只有扩展、挤压和重新排序 dims,这会产生很小的开销;实际上没有数组元素在内存中移动 - 只有张量对象的索引说明符被更改。
其次, allconv
最终路由到tf.nn_ops.convolution_internal
,然后路由到gen_nn_ops.conv2d
or gen_nn_ops.conv3d
;aconv1d
中不存在gen_nn_ops.py
。请注意,由于某种原因,您不会在 Git 存储库中找到该文件 - 但它应该在您的本地安装中,/python/ops/gen_nn_ops.py
.
最后,要真正回答为什么没有专门的conv1d
实现,您需要询问卷积算法背后的 cuDNN 开发人员gen_nn_ops.py
;他们可能没有发现性能改进,而且conv2d
效果也一样快。从低级别的角度来看,这是有道理的,因为沿输入滑动带有N x 1
元素的内核时的矩阵乘法的数量与沿输入M x 1
的相同- 同样,唯一的区别在于索引。N
M
不幸的是,开发人员决定封装最终调用,即_pywrap_tensorflow_internal.TFE_Py_FastPathExecute
; 该模块由一个文件.lib
和一个.pyd
文件组成 - 基本上,编译的 C (Cython) 代码需要反汇编以进行自省。
TL;DR(1)“重塑”的开销很小;conv1d
(2)每个备用冗余都可能缺乏专门的实现conv2d
,速度也一样快;(3) 我不是 cuDNN 专家,所以如果您需要确定,最好在cuDNN询问,或阅读他们的SDK 文档。或者,TF Github的开发人员可能会有所帮助。多年来,我还没有看到 cuDNN 开发人员对 SO 的回答,所以在这里发帖可能不是最好的选择。
昏暗的重新排序性能演示:
import numpy as np
from time import time
x = np.random.randn(700, 800, 900) # 504,000,000 elements
t0 = time()
for i in range(1000):
if i % 2 == 0:
x = x.reshape(700, 900, 800)
else:
x = x.reshape(700, 800, 900)
print(time() - t0)
0.0009968280792236328