tensorflow与pytorch代码差异
[toc]
可能会长期更新,因为经常需要从pytorch
偷代码翻译成tensorflow
😑因此记录一下差异的地方.
1.
torch
中nn.Conv2d
的groups
参数
torch
中groups
控制输入和输出之间的连接,in_channels
和out_channels
必须都可以被组整除.
- groups=1
传统的卷积方式. - groups=2
等效于并排设置两个conv
层,每个conv
层看到一半的输入通道,并产生一半的输出通道,并且随后将它们都连接在一起.
- groups=in_channels
每个输入通道都有自己的滤波器.
等价写法: nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size,
stride=stride, padding=kernel_size//2, groups=expand_size, bias=False)
kl.DepthwiseConv2D(kernel_size=kernel_size,
strides=stride, padding='same', use_bias=False)
NOTE:
这里pytorch
生成的卷积核shape = [out_channel, 1, kh, kw]
这里tflite
生成的卷积核shape = [1, kh, kw, out_channel]
2.
nn.AdaptiveAvgPool2d
与kl.GlobalAveragePooling2D
当nn.AdaptiveAvgPool2d(1)
时和kl.GlobalAveragePooling2D()
相同,但是注意torch
的输出是保持4
维的,而tensorflow
不保持维度.
等价写法: x=nn.AdaptiveAvgPool2d(1)(x)
# -----------------------------
pool=kl.GlobalAveragePooling2D()
x=k.backend.expand_dims(k.backend.expand_dims(pool(x),1),1)
当然直接修改GlobalAveragePooling2D
里,添加keepdims=true
参数也可以.
tf.contrib.layers.layer_norm与tf.keras.LayerNorm与nn.LayerNorm
tf.contrib.layers.layer_norm
tf以前遗留代码还是挺蛋疼的。在tf.contrib.layers.layer_norm
中,对于输入为(4, 10, 10, 3)
的张量,是对(h,w,c)
进行归一化处理,但是他的仿射系数默认只对c
有效:
x = tf.reshape(tf.range(4 * 3 * 10 * 10, dtype=tf.float32), (4, 10, 10, 3))
xout = tf_contrib.layers.layer_norm(x,
center=True, scale=True,
scope='layer_norm')
mean.shape = (4, 1, 1, 1)
gamma.shape = (3,)
tf.keras.LayerNorm
tf.keras.LayerNorm
我就属实不懂了,讲道理他的归一化是对(h,w,c)
进行归一化处理,仿射系数对c
有效,但是输出归一化结果是400=4×10x10
,这就很奇怪了,他默认的特征维度是-1
,但是看起来却没有干LayerNorm
应该做的事情,反而把batch
维度也归一化了,但是在最终测试输出的时候发现结果是符合预期的。。属实不理解。
inputs_np = tf.convert_to_tensor( |
nn.LayerNorm
nn.LayerNorm
是对(c,h,w)
进行归一化处理,仿射系数对c,h,w
有效,但有个非常蛋疼的问题就是,他没有办法复现老版本tf
的行为,即只用c
作为仿射系数,如果开启仿射会导致参数非常大。。。
inputs = torch.tensor(np.arange(4 * 3 * 10 * 10).reshape((4, 3, 10, 10)), dtype=torch.float32) |
我继续检查他的源码,在aten/src/ATen/native/layer_norm.h
中,将输入维度分为M*N
,按照我们上面的做法即M=4,N=3*10*10
。
然后进入cuda代码aten/src/ATen/native/cuda/layer_norm_kernel.cu
利用RowwiseMomentsCUDAKernel
计算均值与方差:
template <typename T>
void LayerNormKernelImplInternal(
const Tensor& X,
const Tensor& gamma,
const Tensor& beta,
int64_t M,
int64_t N,
T eps,
Tensor* Y,
Tensor* mean,
Tensor* rstd) {
DCHECK_EQ(X.numel(), M * N);
DCHECK(!gamma.defined() || gamma.numel() == N);
DCHECK(!beta.defined() || beta.numel() == N);
const T* X_data = X.data_ptr<T>();
const T* gamma_data = gamma.defined() ? gamma.data_ptr<T>() : nullptr;
const T* beta_data = beta.defined() ? beta.data_ptr<T>() : nullptr;
T* Y_data = Y->data_ptr<T>();
T* mean_data = mean->data_ptr<T>();
T* rstd_data = rstd->data_ptr<T>();
cudaStream_t cuda_stream = at::cuda::getCurrentCUDAStream();
RowwiseMomentsCUDAKernel<T>
<<<M, cuda_utils::kCUDABlockReduceNumThreads, 0, cuda_stream>>>(
N, eps, X_data, mean_data, rstd_data);
LayerNormForwardCUDAKernel<T><<<M, kCUDANumThreads, 0, cuda_stream>>>(
N, X_data, mean_data, rstd_data, gamma_data, beta_data, Y_data);
AT_CUDA_CHECK(cudaGetLastError());
}
接下来我们检查一下group norm
,首先给定group
,他将模型输入分为N,C,HxW
。在aten/src/ATen/native/cuda/group_norm_kernel.cu
中,当group=1
的时候,D=C/G=C
,N×G=N
,也就是group=1
的是等同于layer norm
,并且此时他的可变化参数为C
,可以用来等效tf.contrib.layers.layer_norm
。
template <typename T> |