1. 模型训练过程划分
if __name__ == '__main__' :
. . .
1.1. 定义过程
1.1.1. 全局参数设置
参数名
作用
learning_rate
控制模型参数的更新步长
device
指定模型训练使用的设备(CPU或GPU)
num_epochs
指定在训练集上训练的轮数
batch_size
指定每批数据的样本数
num_workers
指定加载数据集的进程数
prefetch_factor
指定每个进程预加载的批数
1.1.2. 模型定义
组件
作用
writer
定义tensorboard的事件记录器
net
定义神经网络结构
net.apply(init_weights)
模型参数初始化
criterion
定义损失函数
optimizer
定义优化器
1.2. 数据集加载过程
1.2.1. Dataset类:创建数据集
作用:定义数据集的结构和访问数据集中样本的方式。定义过程中通常需要读取数据文件,但这并不意味着将整个数据集加载到内存中 。
如何创建数据集
继承Dataset抽象类自定义数据集
TensorDataset类:通过包装张量创建数据集
1.2.2. Dataloader类:加载数据集
作用:定义数据集的加载方式,但这并不意味着正在加载数据集 。
数据批量加载 :将数据集分成多个批次(batches),并逐批次地加载数据。
数据打乱(可选) :在每个训练周期(epoch)开始时,DataLoader会对数据集进行随机打乱,以确保在训练过程中每个样本被均匀地使用。
主要参数
参数
作用
dataset
指定数据集
batch_size
指定每批数据的样本数
shuffle=False
指定是否在每个训练周期(epoch)开始时进行数据打乱
sampler=None
指定如何从数据集中选择样本,如果指定这个参数,那么shuffle必须设置为False
batch_sampler=None
指定生成每个批次中应包含的样本数据的索引。与batch_size、shuffle 、sampler and drop_last参数不兼容
num_workers=0
指定进行数据加载的进程数
collate_fn=None
指定将一列表的样本合成mini-batch的方法,用于映射型数据集
pin_memory=False
是否将数据缓存在物理RAM中以提高GPU传输效率
drop_last=False
是否在批次结束时丢弃剩余的样本(当样本数量不是批次大小的整数倍时)
timeout=0
定义在每个批次上等待可用数据的最大秒数。如果超过这个时间还没有数据可用,则抛出一个异常。默认值为0,表示永不超时。
worker_init_fn=None
指定在每个工作进程启动时进行的初始化操作。可以用于设置共享的随机种子或其他全局状态。
multiprocessing_context=None
指定多进程数据加载的上下文环境,即多进程库
generator=None
指定一个生成器对象来生成数据批次
prefetch_factor=2
控制数据加载器预取数据的数量,默认预取比实际所需的批次数量多2倍的数据
persistent_workers=False
控制数据加载器的工作进程是否在数据加载完成后继续存在
1.3. 训练循环
for epoch in trange( num_epochs) :
. . .
循环内部主要有以下模块:
for X, y in dataloader_train:
X, y = X. to( device) , y. to( device)
loss = criterion( net( X) , y)
optimizer. zero_grad( )
loss. mean( ) . backward( )
optimizer. step( )
def evaluate_loss ( dataloader) :
"""评估给定数据集上模型的损失"""
metric = d2l. Accumulator( 2 )
with torch. no_grad( ) :
for X, y in dataloader:
X, y = X. to( device) , y. to( device)
loss = criterion( net( X) , y)
metric. add( loss. sum ( ) , loss. numel( ) )
return metric[ 0 ] / metric[ 1 ]
2. 模型训练过程追踪
注意: 以下只区分变量、对象是在GPU还是在CPU内存中处理。实际处理过程使用的硬件是CPU、内存和GPU,其中CPU有缓存cache,GPU有显存。忽略具体的数据传输路径和数据处理设备。谈GPU包括GPU和显存,谈CPU内存包括CPU、缓存cache和内存 。
主过程
子过程
追踪情况
定义过程
全局参数设置
变量的定义都是由CPU完成的
模型定义
对象的定义都是由CPU完成的
模型参数和梯度信息可以转移到GPU
数据集加载过程
——
对象的定义都是由CPU完成的
训练循环
训练模型
每批数据的加载是由CPU完成的,先加载到CPU内存,然后可以转移到GPU
数据的前向传播可以由GPU完成
误差反向传播(包括梯度计算)可以由GPU完成的
模型参数更新可以由GPU完成的
评估模型
每批数据的加载是由CPU完成的,先加载到CPU内存,然后可以转移到GPU
数据的前向传播可以由GPU完成,此时可以禁用自动求导机制
由此提升硬件资源的利用率和训练效率,总体上有以下角度:
提升CPU的运算效率
提升数据从CPU转移到GPU的效率
数据传输未准备好也传输(即非阻塞模式):non_blocking=True
将张量固定在CPU内存 :pin_memory=True
提升GPU的运算效率
3. 优化分析
3.1. 定义过程
特点:每次程序运行只需要进行一次。
优化思路:将模型转移到GPU ,同时non_blocking=True
。
3.2. 数据集加载过程
特点:只是定义数据加载的方式,并没有加载数据。
优化思路:合理设置数据加载参数 ,如
batch_size
:一般取能被训练集大小整除的值。过小,则每次参数更新时所用的样本数较少,模型无法充分地学习数据的特征和分布,同时参数更新频繁,模型收敛速度提高,CPU到GPU的数据传输次数增加,CPU内存的消耗总量增加;过大,则每次参数更新时所用的样本数较多,模型性能更稳定,对GPU、CPU内存的单次消耗增加,对硬件配置要求更高,同时参数更新缓慢,模型收敛速度下降。
num_workers
:一般取CPU内核数。过小,则数据加载进程少,数据加载缓慢;过大,则数据加载进程多,对CPU要求高。
pin_memory
:当设置为True时,它告诉DataLoader将加载的数据张量固定在CPU内存中,使数据传输到GPU的过程更快。
prefetch_factor
:决定每次从磁盘加载多少个batch的数据到内存中,预先加载batch越多,在处理数据时,不会因为数据加载的延迟而影响整体的训练速度,同时可以让GPU在处理数据时保持忙碌,从而提高GPU利用率;过大,则会导致CPU内存消耗增加。
3.3. 训练循环
优化思路:
训练和评估过程分离或者减少评估的次数 :模型从训练到评估需要进行状态切换,模型评估过程开销很大。
尽量使用非局部变量 :减少变量、对象的创建和销毁过程
3.3.1. 训练模型
特点:训练结构固定
优化思路:
将数据转移到GPU ,同时non_blocking=True
。
优化训练结构 :比如使用自动混合精度(AMP,要求pytorch>=1.6.0),通过将模型和数据转换为低精度的形式(如FP16),可以显著减少内存使用,即
from torch. cuda. amp import autocast, GradScaler
grad_scaler = GradScaler( )
for epoch in range ( num_epochs) :
start_time = time. perf_counter( )
for X, y in dataloader_train:
X, y = X. to( device, non_blocking= True ) , y. to( device, non_blocking= True )
with autocast( ) :
loss = criterion( net( X) , y)
optimizer. zero_grad( )
grad_scaler. scale( loss. mean( ) ) . backward( )
grad_scaler. step( optimizer)
grad_scaler. update( )
3.3.2. 评估模型