前言
文章概述《深度学习详解》- 3.7
书中介绍了深度学习中常用的优化方法之一——批量归一化(BN)及其在训练过程中的作用机制。文章首先指出,在进行深度学习模型训练时,选择合适的损失函数对优化过程至关重要。相比于均方误差,交叉熵因其特性更适合用于分类任务。文章通过示意图说明了两者对损失函数的影响,并强调了交叉熵在优化过程中的优势。进一步地,文章探讨了批量归一化技术,旨在通过调整输入特征的分布,使得梯度下降过程更加平滑,从而加速模型的训练并提高准确性。文中不仅阐述了批量归一化的基本思想和原理,还讨论了其在深度神经网络中的具体实现细节,包括训练和测试阶段的处理方式以及如何通过移动平均来保持模型的稳定性。此外,文章还提及了关于批量归一化效果的其他理论解释,例如“内部协变量偏移”概念,并与其他归一化方法进行了比较。最终,通过一系列实验结果验证了批量归一化在优化深度学习模型方面的有效性,并提出了改进学习率的可能性。整体而言,本文深入分析了批量归一化的作用机理及其实现策略,为理解和应用该技术提供了全面的视角。
个人学习笔记以及概念梳理,望对大家有所帮助。
思维导图3.7
特征归一化与批量归一化
特征归一化通常在输入层应用,适用于所有类型的机器学习模型。
批量归一化主要应用于深层神经网络中,特别是在卷积神经网络(CNNs)和循环神经网络(RNNs)中。
涉及的一些术语
术语 | 解释 |
均方误差 (MSE) | 一种常用的损失函数,用于衡量预测值与真实值之间的差异的平方和的平均值。 |
交叉熵 | 一种损失函数,用于衡量两个概率分布之间的差异,特别是在分类任务中用于衡量预测概率分布与真实标签之间的差异。 |
Softmax 函数 | 一种激活函数,用于将多个数值转换为概率分布,通常用于多分类问题的最后一层。 |
批量归一化 (Batch Normalization, BN) | 一种正则化技术,用于标准化神经网络各层的输入,以减少内部协变量偏移,加速训练并提高模型性能。 |
内部协变量偏移 (Internal Covariate Shift) | 在训练过程中,由于前向传播的分布变化而导致的后续层参数更新时遇到的问题。 |
特征归一化 (Feature Normalization) | 对特征进行缩放和标准化的过程,使它们具有相似的尺度,有助于加速学习过程。 |
协变量偏移 (Covariate Shift) | 训练集与测试集或真实世界数据之间输入分布的变化。 |
层归一化 (Layer Normalization, LN) | 一种类似于批量归一化的技术,但它是在一个单独的样本的特征上进行归一化。 |
实例归一化 (Instance Normalization, IN) | 一种归一化技术,对每个样本的特征图独立进行归一化,常用于生成对抗网络 (GANs) 和风格迁移任务。 |
组归一化 (Group Normalization, GN) | 一种归一化技术,将特征图分成多个组并在每个组内进行归一化,适用于小型批次大小的情况。 |
权重归一化 (Weight Normalization, WN) | 一种技术,通过对权重矩阵进行归一化来简化网络的学习过程。 |
谱归一化 (Spectral Normalization, SN) | 一种正则化技术,通过对权重矩阵的最大奇异值进行约束来控制网络的 Lipschitz 常数,以提高泛化能力。 |
学习过程遇到的一些问题的理解:
1常见的归一化类型,分别的影响及效果
补充:
Min-Max归一化(线性归一化) 公式为
Z-Score归一化(标准差归一化) 公式为
当然,归一化方法的选择应基于数据的特点和模型的需求。例如,对于含有异常值的数据,Z-Score归一化可能不是最佳选择,而Min-Max归一化或非线性归一化可能更适合。另一方面,如果数据分布接近正态分布,Z-Score归一化会是一个好的选择。在深度学习领域,批归一化因其能够显著加速训练过程而被广泛采用。
2内部协变量偏移的看法
内部协变量偏移是指在神经网络训练过程中,每一层的输入分布会随着前面层参数的更新而发生变化的现象
影响
训练速度: 内部协变量偏移会增加训练的难度,导致训练速度变慢。
泛化能力: 输入分布的变化会影响模型的泛化能力,即模型在未见过的数据上的表现。
梯度问题: 分布变化可能导致梯度消失或梯度爆炸问题,从而影响训练的稳定性
延伸
而从生物学角度来看,生物神经系统也面临着类似的问题。生物神经元接收到的信号强度和模式会在不同的时间和条件下发生变化。内部协变量偏移可以类比为生物神经网络中的这种变化。批归一化等技术类似于生物体内的调节机制,帮助神经元适应输入信号的变化,以实现更高效的学习。
代码运行
不同归一化类型(最小-最大缩放(Min-Max Scaling)、Z-score标准化和批量归一化(Batch Normalization)以及非线性归一化这4个)的效果验证,实现对恶意软件的分类共9类。
Malicious Software Classification.csv数据集展示
其中有5万条数据,我们就简单点使用前1000条用于数据处理
代码如下
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler, StandardScaler, FunctionTransformer
import torch
import torch.nn as nn
from torch import optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
# 数据加载
data = pd.read_csv('Malicious Software Classification.csv', nrows=1000)
print(data.head())
# 删除不需要的列
y = data['label'].values
data = data.drop(['label', 'id'], axis=1)
X = data.values
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 归一化方法实现
# Min-Max Scaling
scaler = MinMaxScaler()
X_train_minmax = scaler.fit_transform(X_train)
X_test_minmax = scaler.transform(X_test)
# Z-Score Standardization
scaler = StandardScaler()
X_train_zscore = scaler.fit_transform(X_train)
X_test_zscore = scaler.transform(X_test)
# Batch Normalization
class BNLayer(nn.Module):
def __init__(self, input_dim):
super(BNLayer, self).__init__()
self.bn = nn.BatchNorm1d(input_dim)
def forward(self, x):
return self.bn(x)
X_train_bn_tensor = torch.tensor(X_train, dtype=torch.float)
X_test_bn_tensor = torch.tensor(X_test, dtype=torch.float)
# 应用 Batch Normalization
with torch.no_grad():
bn_model = BNLayer(X_train.shape[1])
X_train_bn = bn_model(X_train_bn_tensor).numpy()
X_test_bn = bn_model(X_test_bn_tensor).numpy()
# Non-linear normalization (e.g., using tanh)
def tanh_normalizer(x):
return np.tanh(x)
tanh_scaler = FunctionTransformer(tanh_normalizer)
X_train_tanh = tanh_scaler.fit_transform(X_train)
X_test_tanh = tanh_scaler.transform(X_test)
# 构建模型
class Classifier(nn.Module):
def __init__(self, input_dim, num_classes=9):
super(Classifier, self).__init__()
self.fc = nn.Sequential(
nn.Linear(input_dim, 64),
nn.ReLU(),
nn.Linear(64, num_classes)
)
def forward(self, x):
return self.fc(x)
# 定义训练和测试函数
def train(model, loader, criterion, optimizer, device):
model.train()
for inputs, labels in loader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
def test(model, loader, device):
model.eval()
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in loader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
return correct / total
# 设备设置
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam
# 确保张量的第一个维度相同
assert X_train_bn.shape[0] == y_train.shape[0], "Size mismatch between tensors in training set."
assert X_test_bn.shape[0] == y_test.shape[0], "Size mismatch between tensors in testing set."
# 转换为张量
train_tensors = [(torch.tensor(X_train_scaled, dtype=torch.float), torch.tensor(y_train, dtype=torch.long)) for
X_train_scaled in [X_train_minmax, X_train_zscore, X_train_bn, X_train_tanh]]
test_tensors = [(torch.tensor(X_test_scaled, dtype=torch.float), torch.tensor(y_test, dtype=torch.long)) for
X_test_scaled in [X_test_minmax, X_test_zscore, X_test_bn, X_test_tanh]]
# 训练和评估模型
results = {}
for i, (X_train_scaled, X_test_scaled) in enumerate(zip(train_tensors, test_tensors)):
train_dataset = TensorDataset(*X_train_scaled)
test_dataset = TensorDataset(*X_test_scaled)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
model = Classifier(X_train_scaled[0].shape[1]).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
for epoch in range(10): # 每种归一化方法训练10轮
train(model, train_loader, criterion, optimizer, device)
# 评估模型
acc = test(model, test_loader, device)
results[f"Method {i + 1}"] = acc
print(f"Accuracy of Method {i + 1}: {acc:.4f}")
# 绘制折线图
methods = ['Min-Max Scaling', 'Z-Score Standardization', 'Batch Normalization', 'Non-linear normalization']
accuracies = list(results.values())
plt.figure(figsize=(10, 6))
plt.plot(methods, accuracies, marker='o')
# 添加数值标签
for i, txt in enumerate(accuracies):
plt.annotate(f"{txt:.4f}", (methods[i], accuracies[i]), textcoords="offset points", xytext=(0,10), ha='center')
plt.title('Accuracy Comparison of Different Normalization Methods')
plt.xlabel('Normalization Method')
plt.ylabel('Accuracy')
# 修改y轴显示范围
plt.ylim(min(accuracies)-0.05, min(max(accuracies)+0.05, 1)) # 设置y轴范围
plt.grid(True)
plt.tight_layout()
plt.show()
运行结果
其中还是标准差归一化效果最好,线性归一化较差。
验证
当数据条数变大时(取10000或5万条数据),其他条件不变,四种方法的性能
还是标准差归一化效果最好,且好像min-max归一化随着数据集增加,精度提升较大。
小结(实验)
可得
- Z-Score标准化在本实验中表现最佳,尤其是在初步实验中。
- 最小-最大缩放虽然初始表现不佳,但随着数据量的增加,其准确率有显著提升。
- 批量归一化是一种有效的方法,尤其适合深层神经网络,能够加速训练并提高模型性能。
- 非线性归一化(如使用
tanh
函数)也能有效地改善模型的性能,尽管其效果可能不如Z-Score标准化或批量归一化。
推荐
- 对于大多数情况,Z-Score标准化是一个很好的起点,特别是当数据接近正态分布时。
- 如果需要处理大规模数据集,最小-最大缩放可能会带来更好的结果。
- 在深度学习模型中,考虑使用批量归一化来加速训练过程并提高模型的稳定性。