误差表面(或损失表面)是由神经网络的参数决定的损失函数在参数空间中的图像。当误差表面非常崎岖时,训练过程中的梯度下降算法容易陷入局部最优解,或者由于不同方向的斜率差异较大而导致训练速度非常缓慢。
批量归一化(Batch Normalization, BN) 的作用之一是减轻误差表面的崎岖程度,使得模型更容易优化。通过对每一层的输入数据进行归一化处理,BN可以使得参数更新更加稳定,从而有助于减少在不同方向上的斜率差异,也就是说,BN可以“铲平”误差表面的山峰和谷底,使得优化过程更加高效。
具体来说,BN通过将每一层输入的分布保持在一个固定的范围内,减少了因输入分布的变化(即“协变量偏移”)而导致的训练不稳定性。这不仅加速了训练过程,还可能提高模型的泛化能力。
该图展示了在深度神经网络中通过批量归一化来平滑和标准化每一层的特征,以确保不同维度的特征值在激活函数之前处于相对相似的范围,从而减少训练中的梯度消失或爆炸现象,提高网络的训练效果。批量归一化是深度学习中非常重要的技术,它能够加速训练收敛并提高模型的泛化能力。
卷积神经网络(Convolutional Neural Network, CNN)是深度学习中一种重要的网络结构,广泛应用于图像识别、自然语言处理等领域。
-
通道(Channel)
在图像处理中,通道指的是图像数据的不同颜色层或特征层。例如,彩色图像通常有三个通道(RGB通道),分别对应红色、绿色和蓝色。在卷积神经网络中,随着网络的深入,特征图(Feature Map)会有越来越多的通道,这些通道代表了通过卷积层提取到的不同特征。
-
全连接层(Fully Connected Layer)
全连接层是卷积神经网络中的一个重要部分,通常位于网络的末端。它的作用是将前面卷积层和池化层提取的高层次特征映射到最终的输出类别上。在全连接层中,每一个神经元都与上一层的所有神经元相连。这意味着全连接层可以组合来自不同特征图的信息,用于最后的分类或回归任务。
-
感受野(Receptive Field)
感受野指的是网络中的某个神经元所“看到”的输入区域。在卷积神经网络中,感受野的大小决定了该神经元能够感知的输入图像区域的范围。随着网络的层数增加,感受野会变得越来越大,这使得网络能够捕捉到图像中的全局信息。感受野的大小通常取决于卷积核的大小、步幅(stride)、以及池化操作的配置。
-
卷积核(Convolutional Kernel or Filter)
卷积核是卷积层中的一个小矩阵,用来在图像上滑动,执行卷积操作。卷积操作本质上是计算卷积核与输入图像的局部区域的点积,并将结果作为输出特征图中的一个元素。卷积核的大小通常较小(例如3x3或5x5),但它可以在整个图像上移动(滑动),提取局部特征。多个卷积核可以用于同一层,每个卷积核会提取不同类型的特征(如边缘、角点、纹理等)。
-
卷积层(Convolutional Layer)
卷积层是卷积神经网络的基础组件,它通过卷积操作提取输入图像的特征。每个卷积层由多个卷积核组成,这些卷积核可以捕捉到输入数据中的局部模式(如边缘、线条、颜色变化等)。卷积层通过叠加多个层次的特征图,逐渐从低级特征(如边缘)提取到高级特征(如物体的部分或形状)。
6.实际操作–HW4(Self-Attention)自注意力机制 -声音分类
#导入需要导库
import torch # 导入PyTorch库
from torch.utils.data import DataLoader, random_split # 导入DataLoader和random_split工具
from torch.nn.utils.rnn import pad_sequence # 导入pad_sequence工具,用于填充序列
def collate_batch(batch):
# 处理批次内的特征
"""将一批数据整理成一个批次。"""
mel, speaker = zip(*batch)
# 因为我们是逐批次训练模型,所以需要对同一批次中的特征进行填充,使它们的长度相同。
mel = pad_sequence(mel, batch_first=True, padding_value=-20) # 使用log 10^(-20)进行填充,这是一个非常小的值。
# mel: (批次大小, 长度, 40)
return mel, torch.FloatTensor(speaker).long()
def get_dataloader(data_dir, batch_size, n_workers):
"""生成数据加载器"""
# 创建数据集实例
dataset = myDataset(data_dir)
# 获取说话者数量
speaker_num = dataset.get_speaker_number()
# 将数据集分为训练集和验证集
trainlen = int(0.9 * len(dataset))
lengths = [trainlen, len(dataset) - trainlen]
# 随机划分数据集
trainset, validset = random_split(dataset, lengths)
train_loader = DataLoader( # 创建一个训练数据加载器
trainset, # 使用训练数据集
batch_size=batch_size, # 设置每个批次的大小
shuffle=True, # 在每个epoch开始时打乱数据顺序
drop_last=True, # 如果数据集大小不能被批次大小整除,则丢弃最后一个不完整的批次
num_workers=n_workers, # 设置用于数据加载的子进程数量
pin_memory=True, # 将数据存储在固定内存区域,以便更快地访问
collate_fn=collate_batch, # 自定义如何将样本组合成一个批次
)
valid_loader = DataLoader( # 创建一个验证数据加载器
validset, # 使用验证数据集
batch_size=batch_size, # 设置每个批次的大小
num_workers=n_workers, # 设置用于数据加载的子进程数量
drop_last=True, # 如果数据集大小不能被批次大小整除,则丢弃最后一个不完整的批次
pin_memory=True, # 将数据存储在固定内存区域,以便更快地访问
collate_fn=collate_batch, # 自定义如何将样本组合成一个批次
)
return train_loader, valid_loader, speaker_num
这段代码用于创建训练和验证的数据加载器,方便将语音数据集分成小批次并输入到模型中进行训练和评估。首先,通过定义一个 collate_batch 函数,对批次中的语音特征进行填充,使它们长度一致。然后,get_dataloader 函数使用 myDataset 类加载数据,将数据集随机划分为训练集和验证集,最后生成相应的 DataLoader 对象,用于在模型训练和验证过程中按批次高效地加载数据。
#导入需要导库
import torch # 导入torch库
import torch.nn as nn # 导入torch.nn模块,并将其命名为nn
import torch.nn.functional as F # 导入torch.nn.functional模块,并将其命名为F
class Classifier(nn.Module):
def __init__(self, d_model=80, n_spks=600, dropout=0.1):
super().__init__()
# 将输入特征的维度从40投影到d_model。
self.prenet = nn.Linear(40, d_model)
# TODO:
# 将Transformer更改为Conformer。
# https://arxiv.org/abs/2005.08100
self.encoder_layer = nn.TransformerEncoderLayer(
d_model=d_model, dim_feedforward=256, nhead=2
)
# self.encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=2)
# 将特征的维度从d_model投影到说话者数量。
self.pred_layer = nn.Sequential(
nn.Linear(d_model, d_model),
nn.Sigmoid(),
nn.Linear(d_model, n_spks),
)
def forward(self, mels):
"""
args:
mels: (batch size, length, 40)
return:
out: (batch size, n_spks)
"""
# out: (batch size, length, d_model)
# 输出 (batch size, length, d_model)
out = self.prenet(mels)
# out: (length, batch size, d_model)
#输出:(长度,批次大小,d_model)
out = out.permute(1, 0, 2)
# 编码器层期望特征的形状为(length, batch size, d_model)。
out = self.encoder_layer(out)
# out: (batch size, length, d_model)
#输出 (batch size, length, d_model)
out = out.transpose(0, 1)
# 平均池化
stats = out.mean(dim=1)
# out: (batch, n_spks)
out = self.pred_layer(stats)
return out
这段代码定义了一个基于PyTorch的神经网络模型 Classifier,用于语音分类任务。
Classifier 类:
继承自 nn.Module,这是所有神经网络模块的基类。
init 方法:初始化模型时,会执行以下步骤:
prenet:将输入的语音特征维度从 40(梅尔频谱图的默认维度)投影到 d_model,即模型的特征维度。
encoder_layer:定义了一个 TransformerEncoderLayer,它是 Transformer 编码器的基础组件,用于提取特征中的时序信息。当前模型只使用了一个编码器层,但可以扩展为多个层。
pred_layer:是一个全连接层的序列,先将 d_model 维度投影到相同维度,再通过一个 Sigmoid 激活函数,最后投影到 n_spks,即说话者的数量。
forward 方法:
mels:输入的语音特征,形状为 (batch size, length, 40)。
前向传播:
首先通过 prenet 投影输入特征。
然后调整特征的维度顺序以适应 TransformerEncoderLayer 的输入要求。
特征通过 encoder_layer 提取时序信息后,再调整维度顺序。
使用平均池化将时间维度的信息聚合。
最后通过 pred_layer 预测出每个样本属于哪一个说话者,输出的形状为 (batch size, n_spks)。
此模型的主要作用是接收预处理后的语音特征并预测说话者的身份。还可以将 Transformer 替换为 Conformer 以增强模型的性能,特别是在处理长序列时。
最后在阿里云服务器上实现模型的训练:
虽然这是最后一个task,但书中的内容仍有很多,不是寥寥数语可以表述清楚的,因此之空余时间仍会继续学习,也要谢datawhale夏令营提供的机会。
参考资料:
1.https://www.bilibili.com/video/BV1JA411c7VT/?p=5
2.https://github.com/datawhalechina/leedl-tutorial
3.Datawhale学习指南