文章目录
- 一、实验介绍
- 二、实验环境
- 1. 配置虚拟环境
- 2. 库版本介绍
- 三、优化算法
- 0. 导入必要的库
- 1. 随机梯度下降SGD算法
- a. PyTorch中的SGD优化器
- b. 使用SGD优化器的前馈神经网络
- 2.随机梯度下降的改进方法
- a. 学习率调整
- b. 梯度估计修正
- 3. 梯度估计修正:动量法Momentum
- 4. 自适应学习率
- 5. Adam算法
- 四、参数初始化
- 五、数据预处理
- 1. 标准化
- 2. 归一化
- 3. 白化
- 4. 去除异常值
- 5. 处理缺失值
- 6. 代码整合
一、实验介绍
深度神经网络在机器学习中应用时面临两类主要问题:优化问题和泛化问题。
-
优化问题:深度神经网络的优化具有挑战性。
- 神经网络的损失函数通常是非凸函数,因此找到全局最优解往往困难。
- 深度神经网络的参数通常非常多,而训练数据也很大,因此使用计算代价较高的二阶优化方法不太可行,而一阶优化方法的训练效率通常较低。
- 深度神经网络存在梯度消失或梯度爆炸问题,导致基于梯度的优化方法经常失效。
-
泛化问题:由于深度神经网络的复杂度较高且具有强大的拟合能力,很容易在训练集上产生过拟合现象。因此,在训练深度神经网络时需要采用一定的正则化方法来提高网络的泛化能力。
目前,研究人员通过大量实践总结了一些经验方法,以在神经网络的表示能力、复杂度、学习效率和泛化能力之间取得良好的平衡,从而得到良好的网络模型。本系列文章将从网络优化和网络正则化两个方面来介绍如下方法:
- 在网络优化方面,常用的方法包括优化算法的选择、参数初始化方法、数据预处理方法、逐层归一化方法和超参数优化方法。
- 在网络正则化方面,一些提高网络泛化能力的方法包括ℓ1和ℓ2正则化、权重衰减、提前停止、丢弃法、数据增强和标签平滑等。
本文将介绍神经网络中的数据预处理方法
二、实验环境
本系列实验使用了PyTorch深度学习框架,相关操作如下:
1. 配置虚拟环境
conda create -n DL python=3.7
conda activate DL
pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
conda install matplotlib
conda install scikit-learn
2. 库版本介绍
软件包 | 本实验版本 | 目前最新版 |
---|---|---|
matplotlib | 3.5.3 | 3.8.0 |
numpy | 1.21.6 | 1.26.0 |
python | 3.7.16 | |
scikit-learn | 0.22.1 | 1.3.0 |
torch | 1.8.1+cu102 | 2.0.1 |
torchaudio | 0.8.1 | 2.0.2 |
torchvision | 0.9.1+cu102 | 0.15.2 |
三、优化算法
神经网络的参数学习是一个非凸优化问题.当使用梯度下降法来进行优化网络参数时,参数初始值的选取十分关键,关系到网络的优化效率和泛化能力.参数初始化的方式通常有以下三种:
0. 导入必要的库
from torch import nn
1. 随机梯度下降SGD算法
随机梯度下降(Stochastic Gradient Descent,SGD)是一种常用的优化算法,用于训练深度神经网络。在每次迭代中,SGD通过随机均匀采样一个数据样本的索引,并计算该样本的梯度来更新网络参数。具体而言,SGD的更新步骤如下:
- 从训练数据中随机选择一个样本的索引。
- 使用选择的样本计算损失函数对于网络参数的梯度。
- 根据计算得到的梯度更新网络参数。
- 重复以上步骤,直到达到停止条件(如达到固定的迭代次数或损失函数收敛)。
a. PyTorch中的SGD优化器
Pytorch官方教程
optimizer = torch.optim.SGD(model.parameters(), lr=0.2)
b. 使用SGD优化器的前馈神经网络
【深度学习实验】前馈神经网络(final):自定义鸢尾花分类前馈神经网络模型并进行训练及评价
2.随机梯度下降的改进方法
传统的SGD在某些情况下可能存在一些问题,例如学习率选择困难和梯度的不稳定性。为了改进这些问题,提出了一些随机梯度下降的改进方法,其中包括学习率的调整和梯度的优化。
a. 学习率调整
- 学习率衰减(Learning Rate Decay):随着训练的进行,逐渐降低学习率。常见的学习率衰减方法有固定衰减、按照指数衰减、按照时间表衰减等。
- Adagrad:自适应地调整学习率。Adagrad根据参数在训练过程中的历史梯度进行调整,对于稀疏梯度较大的参数,降低学习率;对于稀疏梯度较小的参数,增加学习率。这样可以在不同参数上采用不同的学习率,提高收敛速度。
- Adadelta:与Adagrad类似,但进一步解决了Adagrad学习率递减过快的问题。Adadelta不仅考虑了历史梯度,还引入了一个累积的平方梯度的衰减平均,以动态调整学习率。
- RMSprop:也是一种自适应学习率的方法,通过使用梯度的指数加权移动平均来调整学习率。RMSprop结合了Adagrad的思想,但使用了衰减平均来减缓学习率的累积效果,从而更加稳定。
b. 梯度估计修正
- Momentum:使用梯度的“加权移动平均”作为参数的更新方向。Momentum方法引入了一个动量项,用于加速梯度下降的过程。通过积累之前的梯度信息,可以在更新参数时保持一定的惯性,有助于跳出局部最优解、加快收敛速度。
- Nesterov accelerated gradient:Nesterov加速梯度(NAG)是Momentum的一种变体。与Momentum不同的是,NAG会先根据当前的梯度估计出一个未来位置,然后在该位置计算梯度。这样可以更准确地估计当前位置的梯度,并且在参数更新时更加稳定。
- 梯度截断(Gradient Clipping):为了应对梯度爆炸或梯度消失的问题,梯度截断的方法被提出。梯度截断通过限制梯度的范围,将梯度控制在一个合理的范围内。常见的梯度截断方法有阈值截断和梯度缩放。
3. 梯度估计修正:动量法Momentum
【深度学习实验】网络优化与正则化(一):优化算法:使用动量优化的随机梯度下降算法(Stochastic Gradient Descent with Momentum)
4. 自适应学习率
【深度学习实验】网络优化与正则化(二):基于自适应学习率的优化算法详解:Adagrad、Adadelta、RMSprop
5. Adam算法
Adam算法(Adaptive Moment Estimation Algorithm)[Kingma et al., 2015]可以看作动量法和 RMSprop 算法的结合,不但使用动量作为参数更新方向,而且可以自适应调整学习率。
【深度学习实验】网络优化与正则化(三):随机梯度下降的改进——Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)
四、参数初始化
【深度学习实验】网络优化与正则化(四):参数初始化及其Pytorch实现——基于固定方差的初始化(高斯、均匀分布),基于方差缩放的初始化(Xavier、He),正交初始化
五、数据预处理
除了参数初始化比较困难之外,不同输入特征的尺度差异比较大时,梯度下降法的效率也会受到影响。一般而言,样本特征由于来源以及度量单位不同,它们的尺度(Scale)(即取值范围)往往差异很大.以描述长度的特征为例,当用“米”作单位时令其值为𝑥,那么当用“厘米”作单位时其值为100𝑥.
对于尺度不同的特征,不同的机器学习模型对其敏感程度不同。尺度不变性是指机器学习算法在缩放全部或部分特征后仍能保持学习和预测性能的性质。例如,线性分类器具有尺度不变性,而最近邻分类器则对特征尺度敏感。当计算不同样本之间的欧氏距离时,尺度大的特征会起到主导作用。因此,对于尺度敏感的模型,需要对样本进行预处理,将各个特征转换到相同的取值区间,并消除不同特征之间的相关性,以获得更好的结果。
理论上,神经网络应该具有尺度不变性,可以通过参数的调整来适应不同特征的尺度。然而,尺度不同的输入特征会增加训练的难度。以一个只有一层的神经网络为例,如果输入特征的尺度差异很大,那么tanh函数在区间[-2, 2]上的导数会很敏感,而在其他区间上接近于0。因此,如果参数𝑤1𝑥1 + 𝑤2𝑥2 + 𝑏过大或过小,梯度会变得很小,导致难以训练。为了提高训练效率,可以将𝑤1设为较小的值(例如[-0.1, 0.1]之间),从而使𝑤1𝑥1 + 𝑤2𝑥2 + 𝑏在[-2, 2]区间内。然而,当数据维度很多时,手动选择每个参数变得困难。因此,如果每个特征的尺度相似(例如[0, 1]或[-1, 1]),就可以减少人工干预,简化训练过程。
数据预处理方法是在训练神经网络之前对数据进行处理,以提高模型的性能和训练效果。常见的数据预处理方法包括标准化、归一化、白化等操作。
1. 标准化
将数据按照特征列进行均值为0,标准差为1的标准化处理。这种方法可以使得数据的分布更加接近正态分布,有助于提高模型的训练效果。
import torch
from sklearn.preprocessing import StandardScaler
# 假设有一个名为data的张量,形状为(100, 10)
data = torch.randn(100, 10)
# 创建StandardScaler对象
scaler = StandardScaler()
# 对数据进行标准化
normalized_data = scaler.fit_transform(data)
# 打印标准化后的数据
print(normalized_data)
2. 归一化
将数据按照特征列进行线性变换,将数据的取值范围缩放到0到1之间。这种方法可以将不同特征的数据统一到同一个尺度上,避免不同特征之间的差异对模型的影响。
import torch
from sklearn.preprocessing import MinMaxScaler
# 假设有一个名为data的张量,形状为(100, 10)
data = torch.randn(100, 10)
# 创建MinMaxScaler对象
scaler = MinMaxScaler()
# 对数据进行最小最大值归一化
normalized_data = scaler.fit_transform(data)
# 打印归一化后的数据
print(normalized_data)
3. 白化
白化(Whitening)是一种重要的预处理方法,用来降低输入数据特征之间的冗余性。输入数据经过白化处理后,特征之间相关性较低,并且所有特征具有相同的方差。白化的一个主要实现方式是使用主成分分析(Principal Component Analysis,PCA)方法去除掉各个成分之间的相关性。
import numpy as np
from sklearn.decomposition import PCA
# 假设有一个名为data的数组,形状为(100, 10)
data = np.random.randn(100, 10)
# 计算数据的协方差矩阵
covariance_matrix = np.cov(data, rowvar=False)
# 使用PCA方法进行白化处理
pca = PCA(whiten=True)
whitened_data = pca.fit_transform(data)
# 打印白化后的数据
print(whitened_data)
4. 去除异常值
将数据中的异常值进行处理或去除,避免异常值对模型的影响,可以考虑以下几种常见方法:
-
删除异常值:将数据中的异常值直接删除或忽略。这种方法适用于异常值对整体数据影响较小的情况,但需要注意可能会导致数据的信息损失。
-
替换异常值:将异常值替换为合理的数值。可以使用均值、中位数、众数等统计量来替换异常值,或者使用插值法进行填充。这种方法适用于异常值数量较少且可以通过合理的替换来保持数据整体分布特征的情况。
-
离散化处理:将连续型的异常值转化为离散型数据,例如将连续的数值分桶为不同的类别。这种方法适用于异常值呈现明显分布特征的情况。
-
使用统计方法:使用统计方法来识别和处理异常值,例如Z-score方法或箱线图方法等。这些方法可以通过计算数据的偏差程度来确定异常值,并进一步进行处理。
5. 处理缺失值
对于数据中存在的缺失值,可以通过填充、删除或插值等方法进行处理。
- 填充是指用某个合理的数值替代缺失值,可以使用均值、中位数、众数等统计量来填充。
- 删除是指直接删除包含缺失值的样本或特征,但需要注意可能会导致数据的信息损失。
- 插值是指根据已有的数据推测缺失值,常用的插值方法有线性插值、多项式插值、样条插值等。
6. 代码整合
import torch
import pandas as pd
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.impute import SimpleImputer
import numpy as np
# 假设有一个名为data的张量,形状为(100, 10),其中含有缺失值和异常值
data = torch.randn(100, 10)
data[0, 0] = torch.tensor(float('nan')) # 添加一个缺失值
data[1, 1] = 1000 # 添加一个异常值
# 缩放
scaler = MinMaxScaler() # 最小最大值缩放
scaled_data = scaler.fit_transform(data)
scaler = StandardScaler() # 标准化
scaled_data = scaler.fit_transform(data)
# 去除异常值
std = torch.std(data)
mean = torch.mean(data)
data[(data - mean).abs() > 3 * std] = float('nan') # 使用3倍标准差去除异常值
# 处理缺失值
data = pd.DataFrame(data.numpy()) # 转换为DataFrame
imputer = SimpleImputer(strategy='mean') # 使用均值插补缺失值
imputed_data = imputer.fit_transform(data)
imputed_data = torch.tensor(imputed_data) # 转换为Tensor
# 打印处理后的数据
print(imputed_data)