TensorFlow中reshape操作差异及神经风格迁移作业代码疑问
tf.reshape(a, [m, n]) 与 tf.transpose(tf.reshape(a, [n, m])) 的区别 咱们直接拿实际例子说最清楚:
tf.reshape(a, [m, n])只是重新打包元素,完全不改变元素的存储顺序。比如你有个一维张量[1,2,3,4],reshape成[2,2]会得到[[1,2],[3,4]]——元素还是按原来的顺序排,只是换了个分组方式。- 而
tf.transpose(tf.reshape(a, [n, m]))是先reshape再转置,转置会直接交换元素的位置。还是用刚才的一维数组,先reshape成[2,2]得到[[1,2],[3,4]],转置后就变成[[1,3],[2,4]],元素的排列顺序彻底变了。
核心差异就是:reshape只改形状不改元素顺序,转置会打乱元素的原有布局,两者在内存里的存储结构完全不同。如果是高维张量,这个差异会更明显。
这个坑我当初做作业的时候也踩过!问题出在reshape的元素顺序和Gram矩阵的计算逻辑不匹配上,咱们一步步拆解:
首先明确风格损失里Gram矩阵的作用:要计算每个特征通道和其他通道的相关性,也就是把每个通道的所有像素值作为一个向量,然后计算向量之间的内积,最后得到[n_C, n_C]的相关性矩阵。
先看正确写法的逻辑:
a_S = tf.reshape(a_S, [n_H*n_W, n_C]) a_G = tf.reshape(a_G, [n_H*n_W, n_C]) GS = gram_matrix(tf.transpose(a_S)) GG = gram_matrix(tf.transpose(a_G))
原特征图是[n_H, n_W, n_C](TensorFlow默认通道最后),reshape成[n_H*n_W, n_C]后,每一列对应一个通道的所有像素。转置之后变成[n_C, n_H*n_W],这时候每一行就是一个完整的通道像素向量,传给gram_matrix计算行与行的内积,得到的就是正确的通道相关性矩阵。
再看错误写法:
a_S = tf.reshape(a_S, [n_C, n_H*n_W]) a_G = tf.reshape(a_G, [n_C, n_H*n_W]) GS = gram_matrix(a_S) GG = gram_matrix(a_G)
这里直接reshape成[n_C, n_H*n_W],但TensorFlow的reshape是行优先(C风格)遍历元素的,也就是说它会把原特征图里的元素按n_H→n_W→n_C的顺序依次填充到新形状里。这样得到的a_S的每一行,根本不是一个通道的所有像素,而是连续n_H*n_W/n_C个像素的所有通道值——完全不是我们需要的通道向量!
举个小例子:假设特征图是2×2×2的:
[[[1,2], [3,4]], [[5,6], [7,8]]]
正确写法里转置后的a_S是:
[[1,3,5,7], # 通道0的所有像素 [2,4,6,8]] # 通道1的所有像素
而错误写法里直接reshape得到的a_S是:
[[1,2,3,4], # 前2个像素的所有通道 [5,6,7,8]] # 后2个像素的所有通道
你看,这两个矩阵的行完全不是一回事!用错误的矩阵计算Gram矩阵,得到的自然不是通道之间的相关性,风格损失也就错了。
内容的提问来源于stack exchange,提问作者xinyue Liu

