TensorFlow转Pytorch在一维卷积上的差异

最近在迁移自己的 tensorflow 项目到 Pytorch,需要同步两边的一维卷积。我使用的函数分别是 tf.keras.layers.Conv1D 和 torch.nn.Conv1d。它们实现的功能相同,但使用方式存在差异。在开发过程中需要小心处理。为便于理解记忆,文本以最简单直观的情况分析记录。

1. 输出与输出

假定输入为单条文本(batch_size=1),共包含 8 个汉字(seq_length=8),每个汉字使用 128 维的词嵌入向量表示(embed_size=128)。即,输入矩阵的维度信息为 (1, 8, 128),记为 𝑋=[𝑥0,𝑥1,...,𝑥7],其中 𝑥0−7 均为 128 维向量。

卷积操作的步长为 1(stride=1),核为 3 (kernel_size=3),不做额外补齐(padding="valid"),输入通道数是 128(in_channels=128),输出通道数是 64(out_channels=64)。channel 可以理解为特征,N 维的特征向量,即包含 N 个通道。有几个输出通道,即有几组滤波器(filters)。

目标输出的序列长度为 seq_length-kernel_size+1=6,隐层维度和滤波器数量一致(hidden_size=64)。即,输出矩阵的维度信息为(1, 6, 64),记为 𝑌=[𝑦0,𝑦1,...,𝑦5],其中 𝑦0−5均为 64 维向量。

2. 卷积过程

[1]中认为,tensorflow的一维卷积是从上而下,不同于Pytorch中的从左到右。这个说法可能不是太准确。对于 NLP 文本任务,卷积操作只有沿着时序进行这一个方向。即 y0=Conv(x0, x1, x2),y1=Conv(x1,x2,x3),以此类推,如下图所示:

沿时序卷积

tensorflow 的 Conv1D() 的确和 Pytorch 的 Conv1d() 有区别,主要因为它是一个经过 keras 封装的层操作,如果扒进去看,内部的 tf.nn.convolution 和 Conv1d() 并没有不同。

a. tf.keras.layers.Conv1D

该函数的必要参数有两个,filters(即 out_channels)和 kernel_size。对于 X = (1, 8, 128),如下代码可以得到 Y = (1, 6, 64):

import tensorflow as tf
X = tf.random.normal((1, 8, 128))
X.shape
# TensorShape([1, 8, 128])
conv = tf.keras.layers.Conv1D(64, 3, padding='valid')
Y = conv(X)
Y.shape
# TensorShape([1, 6, 64])

keras 为了让整个 api 更加用户友好,隐藏了两个关键参数。第一个是 data_format,在默认值 “channels_last”下,X 的维度顺序为 [batch_size, seq_length, input_channels],更符合NLP任务的直观理解。如果修改为“channels_first”,X 需要满足 [batch_size, input_channels, seq_length]。第二个是 input_channels,在函数内部自动获得:

input_channel = self._get_input_channel(input_shape)

如果 X 和 data_format 不匹配,就得不到正确的 in_channels。这里就是和 Pytorch 显著差异的地方。

b. torch.nn.Conv1d

该函数的必要参数有三个,in_channels, out_channels 和 kernel_size。被 keras 隐藏的 in_channels 被直接暴露,并且也不支持 data_format 的设置,X 的维度顺序必须是 [batch_size, input_channels, seq_length]。因此,对于通常的使用习惯,必须要先对输入做一次维度转换,再对输出做一次。对于 X = (1, 8, 128),如下代码可以得到 Y = (1, 6, 64):

import torch
X = torch.randn(1, 8, 128)
X.shape
# torch.Size([1, 8, 128])
Xt = X.transpose(1,2)
Xt.shape
# torch.Size([1, 128, 8])
conv = torch.nn.Conv1d(128, 64, 3)
Yt = conv(Xt)
Yt.shape
# torch.Size([1, 64, 6])
Y = Yt.transpose(Yt)
Y.shape
# torch.Size([1, 6, 64])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值