在大多数tensorflow教程中,作者使用频道最后维度排序,例如:
input_layer = tf.reshape(features, [-1, 28, 28, 1])
其中最后一位代表通道数(https://www.tensorflow.org/tutorials/layers)。习惯于Theano和Numpy(都使用C-ordering,即row-major),我发现这很尴尬。此外,在张量流中读取了内存布局方案中的documentation,我认为通道最后布局会导致更多的缓存未命中,因为卷积是在各个通道上执行的,而在通道最后排序中这些通道是混合在线性存储器中,有效地将高速缓存缩小N(其中N是通道数),这在3D和4D卷积中效率特别低。我错了吗?
P.S。
我找到了一个密切相关的帖子(Tensorflow 3 channel order of color inputs)。接受回答的作者声明TF默认使用row-major,但鉴于我到目前为止发现的所有教程都显示了频道最后的排序,我发现这种说法具有误导性。
答案 0 :(得分:18)
以下是解释:
https://www.tensorflow.org/performance/performance_guide#use_nchw_image_data_format
图像数据格式是指批量图像的表示。 TensorFlow支持NHWC(默认为TensorFlow)和NCHW(默认为cuDNN)。 N表示批量中的图像数量,H表示垂直维度中的像素数量,W表示水平维度中的像素数量,C表示通道(例如1表示黑色和白色,3对于RGB等。)尽管cuDNN可以在两种格式上运行,但以默认格式运行速度更快。
最佳做法是构建适用于NCHW和NHWC的模型,因为在GPU上使用NCHW进行训练是常见的,然后在CPU上使用NHWC进行推理。
这两种格式的简短历史是TensorFlow使用NHWC开始,因为它在CPU上的速度要快一些。然后TensorFlow团队发现NCHW在使用NVIDIA cuDNN库时表现更好。目前的建议是用户在其模型中支持这两种格式。从长远来看,我们计划重写图表,使格式之间的切换变得透明。
此外,挖掘代码我们可以看到here当输入格式为NHWC时,tensorflow会将它转换为NCHW。
if (data_format == FORMAT_NHWC) {
// Convert the input tensor from NHWC to NCHW.
TensorShape nchw_shape =
ShapeFromFormat(FORMAT_NCHW, in_batch, in_rows, in_cols, in_depths);
if (in_depths > 1) {
Tensor transformed_input;
OP_REQUIRES_OK(ctx, ctx->allocate_temp(DataTypeToEnum<T>::value,
nchw_shape, &transformed_input));
functor::NHWCToNCHW<GPUDevice, T, 4>()(
ctx->eigen_device<GPUDevice>(),
const_cast<const Tensor&>(input).tensor<T, 4>(),
transformed_input.tensor<T, 4>());
input = transformed_input;
} else {
// If depth <= 1, then just reshape.
CHECK(input.CopyFrom(input, nchw_shape));
}
}
您可以指定要用于每个操作的数据格式,但默认的张量流不使用NCHW而是使用NHWC,这就是为什么即使TF defelopers仍然使用NHWC来避免在每个操作中指定格式
答案 1 :(得分:3)
你的问题是基于一种误解。
行主要与NHWC之间没有矛盾。行主要意味着最右边的索引是在更改时导致内存中最小跳转的索引,并且最左边索引中的更改会导致最大的跳转。在行主要中,最后一个维度是连续的,在列专业中,第一个维度是连续的。有关如何计算任意维数的内存偏移量,请参阅https://en.wikipedia.org/wiki/Row-_and_column-major_order#Address_calculation_in_general。
所以,TF的记忆是以行专业的形式排列的。索引的 order 的差异是微妙的(有些人甚至更喜欢CHWN - 见https://github.com/soumith/convnet-benchmarks/issues/66#issuecomment-155944875)。 NCHW很受欢迎,因为这是cudnn最擅长的。但基本上DL中的每个常见内存布局都是行主要的。