🚩🚩🚩Hugging Face 实战系列 总目录
有任何问题欢迎在下面留言
本篇文章的代码运行界面均在PyCharm中进行
本篇文章配套的代码资源已经上传
从零构建属于自己的GPT系列1:数据预处理
从零构建属于自己的GPT系列2:模型训练1
从零构建属于自己的GPT系列3:模型训练2
从零构建属于自己的GPT系列4:模型训练3
3 数据加载函数
def load_dataset(logger, args):logger.info("loading training dataset")train_path = args.train_pathwith open(train_path, "rb") as f:train_list = pickle.load(f)train_dataset = CPMDataset(train_list, args.max_len)return train_dataset
- 日志报告加载训练数据
- 训练数据路径
- 将以二进制形式存储的数据文件
- 使用
pickle
加载到内存中的train_list
变量中 - 加载CPMDataset包,将train_list从索引转化为torch tensor
- 返回tensor
4 训练函数
def train(model, logger, train_dataset, args):train_dataloader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.num_workers, collate_fn=collate_fn,drop_last=True)logger.info("total_steps:{}".format(len(train_dataloader)* args.epochs))t_total = len(train_dataloader) // args.gradient_accumulation_steps * args.epochsoptimizer = transformers.AdamW(model.parameters(), lr=args.lr, eps=args.eps)scheduler = transformers.get_linear_schedule_with_warmup(optimizer, num_warmup_steps=args.warmup_steps, num_training_steps=t_total)# 设置warmuplogger.info('start training')train_losses = [] # 记录每个epoch的平均lossfor epoch in range(args.epochs):train_loss = train_epoch(model=model, train_dataloader=train_dataloader,optimizer=optimizer, scheduler=scheduler,logger=logger, epoch=epoch, args=args)train_losses.append(round(train_loss, 4))logger.info("train loss list:{}".format(train_losses))logger.info('training finished')logger.info("train_losses:{}".format(train_losses))
- 训练函数
- 制作Dataloader
- 制作Dataloader
- 制作Dataloader
- 制作Dataloader
- 日志添加信息Dataloader*epochs的数量
- 记录数据长度到t_total变量中
- 指定优化器
- 学习率衰减策略,从transformers包中调用现成的get_linear_schedule_with_warmup方法
- 设置warmup等参数
- 学习率衰减策略
- 日志添加信息开始训练
- 记录所有epoch的训练损失,以求每个epoch的平均loss
- 遍历每个epoch
- 指定一个我们自己写的train_epoch函数1
- train_epoch函数2
- train_epoch函数3
- train_epoch函数4
- 记录损失,只保存4位小数
- 记录日志信息训练损失
- 记录日志信息训练完成
- 最后一句是在日志中保存所有损失吗?
5 迭代训练函数train_epoch
def train_epoch(model, train_dataloader, optimizer, scheduler, logger, epoch, args):model.train()device = args.deviceignore_index = args.ignore_indexepoch_start_time = datetime.now()total_loss = 0 # 记录下整个epoch的loss的总和epoch_correct_num = 0 # 每个epoch中,预测正确的word的数量epoch_total_num = 0 # 每个epoch中,预测的word的总数量for batch_idx, (input_ids, labels) in enumerate(train_dataloader):try:input_ids = input_ids.to(device)labels = labels.to(device)outputs = model.forward(input_ids, labels=labels)logits = outputs.logitsloss = outputs.lossloss = loss.mean()batch_correct_num, batch_total_num = calculate_acc(logits, labels, ignore_index=ignore_index)epoch_correct_num += batch_correct_numepoch_total_num += batch_total_numbatch_acc = batch_correct_num / batch_total_numtotal_loss += loss.item()if args.gradient_accumulation_steps > 1:loss = loss / args.gradient_accumulation_stepsloss.backward()torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm)if (batch_idx + 1) % args.gradient_accumulation_steps == 0:optimizer.step()scheduler.step()optimizer.zero_grad()if (batch_idx + 1) % args.log_step == 0:logger.info("batch {} of epoch {}, loss {}, batch_acc {}, lr {}".format(batch_idx + 1, epoch + 1, loss.item() * args.gradient_accumulation_steps, batch_acc, scheduler.get_lr()))del input_ids, outputsexcept RuntimeError as exception:if "out of memory" in str(exception):logger.info("WARNING: ran out of memory")if hasattr(torch.cuda, 'empty_cache'):torch.cuda.empty_cache()else:logger.info(str(exception))raise exceptionepoch_mean_loss = total_loss / len(train_dataloader)epoch_mean_acc = epoch_correct_num / epoch_total_numlogger.info("epoch {}: loss {}, predict_acc {}".format(epoch + 1, epoch_mean_loss, epoch_mean_acc))logger.info('saving model for epoch {}'.format(epoch + 1))model_path = join(args.save_model_path, 'epoch{}'.format(epoch + 1))if not os.path.exists(model_path):os.mkdir(model_path)model_to_save = model.module if hasattr(model, 'module') else modelmodel_to_save.save_pretrained(model_path)logger.info('epoch {} finished'.format(epoch + 1))epoch_finish_time = datetime.now()logger.info('time for one epoch: {}'.format(epoch_finish_time - epoch_start_time))return epoch_mean_loss
- train_epoch函数
- 指定训练模式
- 训练设备
- 需要忽略的索引
- 当前epoch开启的具体时间
- 当前epoch的loss总和
- 当前epoch预测词正确的总数量
- 每个epoch需要预测的测的总数量
- for训练从train_dataloader遍历取数据
- 捕捉异常
- 输入词的索引数据进入训练设备
- 标签数据进入训练设备
- 输入数据经过前向传播得到输出
- 经过softmax后的输出
- 得到损失
- 平均损失
- 通过calculate_acc函数统计该batch的预测token的正确数与总数
- 统计该epoch的预测token的正确数
- 统计该epoch的预测token的总数
- 计算该batch的accuracy
- 获得损失值的标量累加到当前epoch总损失
- 如果当前的梯度累加步数大于1
- 对当前累加的损失对梯度累加步数求平均
- 损失反向传播
- 梯度裁剪:梯度裁剪的目的是控制梯度的大小,防止梯度爆炸的问题。在训练神经网络时,梯度可能会变得非常大,导致优化算法出现数值不稳定的情况。裁剪梯度就是将梯度的整体范数限制在一个特定的阈值之内
- 达到梯度累加的次数后
- 更新参数
- 更新学习率
- 梯度清零
- 梯度累加次数为0时,也就是参数更新时
- 记录日志
- 记录的各个参数占位符
- 占位符对应的各个变量
- 删除两个变量,释放内存
- 捕捉到异常
- 如果异常的信息中的字符串包含内存不足的问题,也就是显卡内存不足
- 将该问题添加到日志信息
- 当显卡内存占用过多时
- 手动释放显卡内存
- 如果不是显卡内存不足
- 记录日志
- 返回异常
- 记录当前epoch的平均loss
- 记录当前epoch的平均accuracy
- 日志记录信息
- 记录的信息为当前epoch索引、损失、准确率
- 日志记录信息,当前保存的模型以及对于的epoch索引
- 保存模型的地址
- 如果地址不存在
- 创建该地址
- 确保得到不是外壳对象
- 保存模型
- 日志记录信息训练完成
- 记录完成时间
- 记录当前epoch训练所花费时间
- 返回epoch平均损失
从零构建属于自己的GPT系列1:数据预处理
从零构建属于自己的GPT系列2:模型训练1
从零构建属于自己的GPT系列3:模型训练2
从零构建属于自己的GPT系列4:模型训练3