Conv1D 是一种专门用于处理一维数据的卷积层。它通过滑动卷积核(滤波器)在输入序列上进行卷积操作,从而提取局部特征。与二维卷积(Conv2D)不同,Conv1D 只在一个维度上进行卷积,适合处理时间序列数据、音频信号和文本数据。

一、Conv1D 介绍

1.1 结构

  • 输入层:一维输入数据,通常为形状为 (batch_size, in_channels, sequence_length 的张量。
  • 卷积层:包含out_channels个可学习的​​卷积核​​,​​每个卷积核的形状​​为(in_channels, kernel_size)
    • 权重张量(out_channels, in_channels, kernel_size)
    • 偏置项(out_channels,)
  • 激活层:通常使用 ReLU 激活函数,引入非线性。

1.2 参数

  • in_channels​​ : 输入数据的通道数
  • out_channels​​:卷积核的数量,决定了输出特征图的深度。
  • kernel_size:卷积核的大小,决定了每次卷积操作覆盖的输入长度。
  • stride:步幅,卷积核在输入序列上滑动的步长,默认为 1。步长 > 1 相当于下采样。
  • padding:填充方式,可以是 ‘valid’(无填充)或 ‘same’(填充以保持输出大小与输入相同)。整数:指定在序列​​首尾​​各添加的填充数量。
  • activation:激活函数,通常使用 ReLU。
  • dilation​​:空洞率,控制卷积核元素的间距,用于扩大感受野而不增加参数数量。默认为 1。

1.3 输入输出维度

  • 输入数据维度
    (batch_size, in_channels, sequence_length)

  • 输出数据维度
    (batch_size, out_channels, new_sequence_length)

  • 输出序列长度new_sequence_length

    • 通用公式:
      new_sequence_length=⌊sequence_length+2×padding−dilation×(kernel_size−1)−1stride+1⌋ \text{new\_sequence\_length} = \left\lfloor \frac{\text{sequence\_length} + 2 \times \text{padding} - \text{dilation} \times (\text{kernel\_size} - 1) - 1}{\text{stride}} + 1 \right\rfloor new_sequence_length=stridesequence_length+2×paddingdilation×(kernel_size1)1+1

    • 使用valid填充时:
      new_length=sequence_length−kernel_sizestrides+1\text{new\_length} = \frac{\text{sequence\_length} - \text{kernel\_size}}{\text{strides}} + 1new_length=stridessequence_lengthkernel_size+1

    • 使用same填充时:
      new_length=sequence_length \text{new\_length} = \text{sequence\_length}new_length=sequence_length

1.4 计算过程

在卷积操作中,卷积核的权重与输入数据的对应区域进行逐元素相乘,然后求和,得到一个输出值。
单通道情况下的计算:
Y[i]=∑j=0k−1X[i+j]⋅W[j]+bY[i] = \sum_{j=0}^{k-1} X[i+j] \cdot W[j] + bY[i]=j=0k1X[i+j]W[j]+b
其中:

  • Y[i]Y[i]Y[i]:输出特征图的值。
  • XXX :输入序列。
  • WWW :卷积核的权重。
  • bbb :偏置项。
  • kkk :卷积核的大小。

多通道情况下的计算:
Y[c,i]=b[c]+∑d=0Cin−1∑j=0k−1X[d,i+j]⋅W[c,d,j]Y[c, i] = b[c] + \sum_{d=0}^{C_{in}-1} \sum_{j=0}^{k-1} X[d, i+j] \cdot W[c, d, j]Y[c,i]=b[c]+d=0Cin1j=0k1X[d,i+j]W[c,d,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]X[d, i+j]X[d,i+j]:输入序列在通道 ddd、位置 i+ji+ji+j的值
  • W[c,d,j]W[c, d, j]W[c,d,j]:卷积核在输出通道 ccc、输入通道ddd、位置jjj的权重

实际计算中还需要考虑步长(stride)和填充(padding)

Y[c,i]=b[c]+∑d=0Cin−1∑j=0k−1Xpadded[d,i×s+j]⋅W[c,d,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[c, d, j]Y[c,i]=b[c]+d=0Cin1j=0k1Xpadded[d,i×s+j]W[c,d,j]

其中:

  • sss:步长(stride)
  • XpaddedX_{padded}Xpadded:填充后的输入序列
  • iii:输出位置索引,范围从 0 到 Lout−1L_{out}-1Lout1
  • LoutL_{out}Lout:输出序列长度
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

二、代码示例

通过两层 Conv1D 处理一段音频,打印每层的输出形状、参数形状,并可视化特征图。

import librosa
import matplotlib.pyplot as plt
import torch
import torch.nn as nn


# 定义 Conv1D 模型
class Conv1DModel(nn.Module):
    def __init__(self):
        super(Conv1DModel, self).__init__()
        self.conv1 = nn.Conv1d(in_channels=1, out_channels=2, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv1d(in_channels=2, out_channels=4, kernel_size=3, stride=1, padding=1)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        return x


# 使用 librosa 读取音频文件
file_path = 'test.wav'
waveform, sample_rate = librosa.load(file_path, sr=None, 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}")

# 将音频信号调整为适合 Conv1D 的输入形状
# 输入形状为 (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 = Conv1DModel()

# 打印每一层卷积层的参数形状
print(f"Conv1 Layer 1 parameters shape: {model.conv1.weight.shape}")
print(f"Conv1 Layer 1 bias shape: {model.conv1.bias.shape}")
print(f"Conv1 Layer 2 parameters shape: {model.conv2.weight.shape}")
print(f"Conv1 Layer 2 bias shape: {model.conv2.bias.shape}")

# 进行前向传播以获取每一层的输出
output1 = model.conv1(audio_tensor)  # 第一层输出
output2 = model.conv2(output1)  # 第二层输出

# 打印每一层的输出形状
print(f"Input: {audio_tensor.shape}")
print(f"Output shape after Conv1D Layer 1: {output1.shape}")
print(f"Output shape after Conv1D Layer 2: {output2.shape}")

# 可视化原始音频信号
plt.figure(figsize=(12, 6))
plt.subplot(3, 1, 1)
plt.plot(audio_segment[0, 0, :])
plt.title("Audio")
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 Conv1D Layer 1 - Feature Map {i + 1}")
    plt.xlabel("Samples")
    plt.ylabel("Amplitude")

plt.tight_layout()
plt.show()

# 可视化第二层输出的所有特征图
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 Conv1D Layer 2 - Feature Map {i + 1}")
    plt.xlabel("Samples")
    plt.ylabel("Amplitude")

plt.tight_layout()
plt.show()
Original input shape: (44100,)
Conv1 Layer 1 parameters shape: torch.Size([2, 1, 3])
Conv1 Layer 1 bias shape: torch.Size([2])
Conv1 Layer 2 parameters shape: torch.Size([4, 2, 3])
Conv1 Layer 2 bias shape: torch.Size([4])
Input: torch.Size([1, 1, 44100])
Output shape after Conv1D Layer 1: torch.Size([1, 2, 44100])
Output shape after Conv1D Layer 2: torch.Size([1, 4, 44100])

在这里插入图片描述
在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐