通过挖掘源代码,我得出结论,这样做可能是为了方便和实现的极简主义 - 详情如下。
首先,没有“重塑”,只有扩展、挤压和重新排序 dims,这会产生很小的开销;实际上没有数组元素在内存中移动 - 只有张量对象的索引说明符被更改。
其次,所有conv 最终路由到tf.nn_ops.convolution_internal,然后路由到gen_nn_ops.conv2d 或gen_nn_ops.conv3d; conv1d 在 gen_nn_ops.py 中不存在。请注意,由于某种原因,您不会在 Git 存储库中找到该文件 - 但它应该在您的本地安装中,/python/ops/gen_nn_ops.py。
最后,要真正回答为什么没有专门的conv1d 实现,您需要询问gen_nn_ops.py 中卷积算法背后的 cuDNN 开发人员;他们可能没有发现性能改进,而conv2d 的工作速度也一样快。从低级别的角度来看,这是有道理的,因为在M x 1 输入上使用N x 1 元素滑动内核时的矩阵乘法数量与N 沿M 的矩阵乘法数量相同——同样,唯一的区别正在索引中。
不幸的是,开发人员决定封装最终调用,即_pywrap_tensorflow_internal.TFE_Py_FastPathExecute;该模块由.lib 和.pyd 文件组成——基本上,编译后的C(Cython)代码需要反汇编才能进行自省。
TL;DR (1) “重塑”的开销很小; (2) 每个备用冗余可能缺少专用的conv1d 实现,因为conv2d 的速度一样快; (3) 我不是 cuDNN 专家,所以如果你需要确定,最好在cuDNN 询问,或者阅读他们的SDK Documentation。或者,TF Github 的开发人员可能会有所帮助。我已经好几年没有看到 cuDNN 开发者回答 SO,所以在这里发帖可能不是最好的选择。
Dim 重新排序性能演示:
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