深度学习基本模块:ConvTranspose1D 一维转置卷积层
ConvTranspose1D 是一种专门用于处理一维数据的转置卷积层。它通过反向卷积操作将低维特征图上采样到高维空间,从而恢复输入数据的空间结构。与一维卷积(Conv1D)不同,ConvTranspose1D 在反向传播过程中用于生成更高分辨率的特征图,适合处理时间序列数据、音频信号等。
本文参考了:https://zhuanlan.zhihu.com/p/679375638
一、ConvTranspose1D介绍
1.1 结构
- 输入层:接收一维输入数据,通常为形状是
(batch_size, in_channels, sequence_length)
的张量。 - 转置卷积层:核心计算层,包含
out_channels
个可学习的卷积核,每个卷积核的形状为(in_channels, kernel_size)
,负责执行上采样操作,将低分辨率特征映射转换为高分辨率特征映射。- 权重张量:
(in_channels, out_channels, kernel_size)
。 - 偏置项:
(out_channels,)
- 权重张量:
- 激活层:通常使用 ReLU 激活函数,引入非线性。
1.2 参数
- in_channels:输入数据的通道数。对于单通道音频信号,通常设置为1;对于多特征时间序列,可根据特征维度设置。
- out_channels:输出数据的通道数,即转置卷积层中卷积核的数量。这个参数决定了输出特征图的深度。
- kernel_size:卷积核的大小。如果是单个整数,表示使用相同的一维卷积核大小;也可以使用元组指定非对称核大小。
- stride:步幅,卷积核在输入序列上滑动的步长,默认为 1。当 stride=1 时,输出长度略有增加;当 stride>1 时,实现显著上采样效果。
- padding:输入序列的填充数量。填充会影响输出尺寸的计算,但不会改变上采样的基本比例。
- output_padding:门用于微调输出尺寸的额外参数。当 stride>1 时,可能需要在输出添加少量填充来精确控制输出长度。
- dilation:控制卷积核中元素的间距(空洞卷积)。增大 dilation 值可以增加感受野而不增加参数数量。
假设一个卷积核的原始权重为 [w1, w2, w3]:
dilation=1 (默认) [w1, w2, w3] [●, ●, ●] 3
dilation=2 [w1, 0, w2, 0, w3] [●, _, ●, _, ●] 5
dilation=3 [w1, 0, 0, w2, 0, 0, w3] [●, _, _, ●, _, _, ●] 7_ 表示被跳过的位置(实际计算中不存在的虚拟零值)。
● 表示卷积核的实际权重参数所在的位置。无论 dilation 设置为多少,可学习的权重参数仍然是且仅是 w1, w2, w3 这三个。
Dilation 只是改变了它们作用在输入序列上的相对距离。
1.3 输入输出维度
- 输入数据维度:
(batch_size, in_channels, sequence_length)
- 输出数据维度:
(batch_size, out_channels, new_sequence_length)
输出尺寸计算:
Lout=(Lin−1)×s−2×p+d×(k−1)+o+1L_{out} = (L_{in} - 1) \times s - 2 \times p + d \times (k - 1) + o + 1Lout=(Lin−1)×s−2×p+d×(k−1)+o+1
其中:
- LinL_{in}Lin:输入序列长度
- LoutL_{out}Lout:输出序列长度
- sss:步长(stride)
- ppp:填充(padding)
- ddd:扩张率(dilation)(控制卷积核元素间距)
- kkk:卷积核大小(kernel_size)
- ooo:输出填充(output_padding)
1.4 计算过程
ConvTranspose1D(一维转置卷积)的计算过程与常规卷积不同,其核心是通过在输入元素间插入零值并执行常规卷积来实现上采样。具体计算过程可分为三个关键步骤:
步骤一:输入扩展(Zero Insertion)
在输入序列的元素之间插入 (stride - 1) 个零值,从而扩展输入长度。
原始输入: [x₁, x₂, x₃]
扩展后: [x₁, 0, x₂, 0, x₃, 0]
步骤二:边缘填充(Padding Adjustment)
在扩展后的输入序列两侧添加填充,填充量为 (kernel_size - padding - 1)。
示例:kernel_size=3, padding=1 时的边缘填充
扩展后输入: [x₁, 0, x₂, 0, x₃, 0]
填充后: [0, x₁, 0, x₂, 0, x₃, 0, 0]
步骤三:执行常规卷积(Convolution Operation)
对经过扩展和填充的输入执行常规卷积操作,使用转置卷积层的权重参数。
单输入通道情况:
Y[i]=∑j=0k−1X~[i+j]⋅W[j]+bY[i] = \sum_{j=0}^{k-1} \widetilde{X}[i+j] \cdot W[j] + bY[i]=j=0∑k−1X[i+j]⋅W[j]+b
其中:
- Y[i]Y[i]Y[i]:输出特征图在位置 iii 的值
- X~\widetilde{X}X:经过零值插入和填充处理后的输入
- WWW:卷积核权重
- bbb:偏置项
- kkk:卷积核大小
多输入通道情况:
Y[c,i]=b[c]+∑d=0Cin−1∑j=0k−1X~[d,i+j]⋅W[d,c,j]Y[c, i] = b[c] + \sum_{d=0}^{C_{in}-1} \sum_{j=0}^{k-1} \widetilde{X}[d, i+j] \cdot W[d, c, j]Y[c,i]=b[c]+d=0∑Cin−1j=0∑k−1X[d,i+j]⋅W[d,c,j]
其中:
- Y[c,i]Y[c, i]Y[c,i]:输出特征图在通道 ccc、位置 iii 的值
- b[c]b[c]b[c]:通道 ccc 的偏置项
- CinC_{in}Cin:输入通道数
- X~[d,i+j]\widetilde{X}[d, i+j]X[d,i+j]:处理后的输入在通道 d、位置 i+j 的值
- W[d,c,j]W[d, c, j]W[d,c,j]:权重在输入通道 d、输出通道 c、位置 j 的值
实际计算中还需要考虑步长(stride)和填充(padding):
Y[c,i]=b[c]+∑d=0Cin−1∑j=0k−1Xpadded[d,i×s+j]⋅W[d,c,j]Y[c, i] = b[c] + \sum_{d=0}^{C_{in}-1} \sum_{j=0}^{k-1} X_{padded}[d, i \times s + j] \cdot W[d, c, j]Y[c,i]=b[c]+d=0∑Cin−1j=0∑k−1Xpadded[d,i×s+j]⋅W[d,c,j]
其中:
- sss:步长(stride)
- XpaddedX_{padded}Xpadded:经过零值插入和填充处理后的输入
1.5 卷积与转置卷积对比解析
ConvTranspose1D 被称为"转置"卷积是因为其计算过程在数学上可以表示为常规卷积的转置操作:
- 常规卷积可以表示为矩阵乘法:Y=W∗XY = W \ast XY=W∗X
- 转置卷积可以表示为:Y=WT∗XY = W^T \ast XY=WT∗X
其中 WWW是卷积操作对应的矩阵表示,WTW^TWT是其转置矩阵。
二、代码示例
通过两层ConvTranspose1D处理一段音频,打印每层的输出形状、参数形状,并可视化特征图。
import librosa
import matplotlib.pyplot as plt
import torch
import torch.nn as nn# 定义 ConvTranspose1D 模型
class ConvTranspose1DModel(nn.Module):def __init__(self):super(ConvTranspose1DModel, self).__init__()self.deconv1 = nn.ConvTranspose1d(in_channels=1, out_channels=2, kernel_size=3, stride=1, padding=1)self.deconv2 = nn.ConvTranspose1d(in_channels=2, out_channels=3, kernel_size=5, stride=2, padding=2,output_padding=1)def forward(self, x):x = self.deconv1(x)x = self.deconv2(x)return x# 1. 读取音频文件并处理
file_path = 'test.wav'
waveform, sample_rate = librosa.load(file_path, sr=16000, mono=True)# 提取 2-3 秒的数据
start_time = 2 # 开始时间(秒)
end_time = 3 # 结束时间(秒)
start_sample = int(start_time * sample_rate)
end_sample = int(end_time * sample_rate)
audio_segment = waveform[start_sample:end_sample]# 打印原始输入数据的维度
print(f"Original input shape: {audio_segment.shape}")# 将音频信号调整为适合 ConvTranspose1D 的输入形状
# 输入形状为 (batch_size, channels, sequence_length)
audio_segment = audio_segment.reshape(1, 1, -1) # batch_size=1, channels=1# 转换为 PyTorch 张量
audio_tensor = torch.tensor(audio_segment, dtype=torch.float32)# 创建模型实例
model = ConvTranspose1DModel()# 打印每一层卷积层的权重形状
print(f"ConvTranspose1D Layer 1 weights shape: {model.deconv1.weight.shape}")
print(f"ConvTranspose1D Layer 1 bias shape: {model.deconv1.bias.shape}")
print(f"ConvTranspose1D Layer 2 weights shape: {model.deconv2.weight.shape}")
print(f"ConvTranspose1D Layer 2 bias shape: {model.deconv2.bias.shape}")# 进行前向传播以获取每一层的输出
output1 = model.deconv1(audio_tensor) # 第一层输出
output2 = model.deconv2(output1) # 第二层输出# 打印每一层的输出形状
print(f"Input: {audio_tensor.shape}")
print(f"Output shape after ConvTranspose1D Layer 1: {output1.shape}")
print(f"Output shape after ConvTranspose1D Layer 2: {output2.shape}")# 可视化第一层输出的特征图
plt.figure(figsize=(12, 6))
plt.subplot(3, 1, 1)
plt.plot(audio_tensor[0, 0, :].detach().numpy())
plt.title("Original Audio Segment")
plt.xlabel("Samples")
plt.ylabel("Amplitude")for i in range(output1.shape[1]): # 遍历每个特征图plt.subplot(output1.shape[1] + 1, 1, i + 2) # 1个原始信号 + output1.shape[1]个特征图plt.plot(output1[0, i, :].detach().numpy())plt.title(f"Output after ConvTranspose1D Layer 1 - Feature Map {i + 1}")plt.xlabel("Samples")plt.ylabel("Amplitude")plt.tight_layout()# 可视化第二层输出的特征图
plt.figure(figsize=(12, 6))
for i in range(output2.shape[1]): # 遍历每个特征图plt.subplot(output2.shape[1], 1, i + 1) # 5个子图plt.plot(output2[0, i, :].detach().numpy())plt.title(f"Output after ConvTranspose1D Layer 2 - Feature Map {i + 1}")plt.xlabel("Samples")plt.ylabel("Amplitude")plt.tight_layout()
plt.show()
Original input shape: (16000,)
ConvTranspose1D Layer 1 weights shape: torch.Size([1, 2, 3])
ConvTranspose1D Layer 1 bias shape: torch.Size([2])
ConvTranspose1D Layer 2 weights shape: torch.Size([2, 3, 5])
ConvTranspose1D Layer 2 bias shape: torch.Size([3])
Input: torch.Size([1, 1, 16000])
Output shape after ConvTranspose1D Layer 1: torch.Size([1, 2, 16000])
Output shape after ConvTranspose1D Layer 2: torch.Size([1, 3, 32000])