从零构建属于自己的GPT系列3:模型训练2(训练函数解读、模型训练函数解读、代码逐行解读)

🚩🚩🚩Hugging Face 实战系列 总目录

有任何问题欢迎在下面留言
本篇文章的代码运行界面均在PyCharm中进行
本篇文章配套的代码资源已经上传

从零构建属于自己的GPT系列1:数据预处理
从零构建属于自己的GPT系列2:模型训练1
从零构建属于自己的GPT系列3:模型训练2
从零构建属于自己的GPT系列4:模型训练3
从零构建属于自己的GPT系列5:模型部署1
从零构建属于自己的GPT系列6:模型部署2

3 数据加载函数

def load_dataset(logger, args):
    logger.info("loading training dataset")
    train_path = args.train_path
    with open(train_path, "rb") as f:
        train_list = pickle.load(f)
    train_dataset = CPMDataset(train_list, args.max_len)
    return train_dataset
  1. 日志报告加载训练数据
  2. 训练数据路径
  3. 将以二进制形式存储的数据文件
  4. 使用 pickle 加载到内存中的 train_list 变量中
  5. 加载CPMDataset包,将train_list从索引转化为torch tensor
  6. 返回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.epochs
    optimizer = 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
    )# 设置warmup
    logger.info('start training')
    train_losses = []   # 记录每个epoch的平均loss
    for 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))
  1. 训练函数
  2. 制作Dataloader
  3. 制作Dataloader
  4. 制作Dataloader
  5. 制作Dataloader
  6. 日志添加信息Dataloader*epochs的数量
  7. 记录数据长度到t_total变量中
  8. 指定优化器
  9. 学习率衰减策略,从transformers包中调用现成的get_linear_schedule_with_warmup方法
  10. 设置warmup等参数
  11. 学习率衰减策略
  12. 日志添加信息开始训练
  13. 记录所有epoch的训练损失,以求每个epoch的平均loss
  14. 遍历每个epoch
  15. 指定一个我们自己写的train_epoch函数1
  16. train_epoch函数2
  17. train_epoch函数3
  18. train_epoch函数4
  19. 记录损失,只保存4位小数
  20. 记录日志信息训练损失
  21. 记录日志信息训练完成
  22. 最后一句是在日志中保存所有损失吗?

5 迭代训练函数train_epoch

def train_epoch(model, train_dataloader, optimizer, scheduler, logger, epoch, args):
    model.train()
    device = args.device
    ignore_index = args.ignore_index
    epoch_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.logits
            loss = outputs.loss
            loss = loss.mean()
            batch_correct_num, batch_total_num = calculate_acc(logits, labels, ignore_index=ignore_index)
            epoch_correct_num += batch_correct_num
            epoch_total_num += batch_total_num
            batch_acc = batch_correct_num / batch_total_num
            total_loss += loss.item()
            if args.gradient_accumulation_steps > 1:
                loss = loss / args.gradient_accumulation_steps
            loss.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, outputs
        except 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 exception
    epoch_mean_loss = total_loss / len(train_dataloader)
    epoch_mean_acc = epoch_correct_num / epoch_total_num
    logger.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 model
    model_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
  1. train_epoch函数
  2. 指定训练模式
  3. 训练设备
  4. 需要忽略的索引
  5. 当前epoch开启的具体时间
  6. 当前epoch的loss总和
  7. 当前epoch预测词正确的总数量
  8. 每个epoch需要预测的测的总数量
  9. for训练从train_dataloader遍历取数据
  10. 捕捉异常
  11. 输入词的索引数据进入训练设备
  12. 标签数据进入训练设备
  13. 输入数据经过前向传播得到输出
  14. 经过softmax后的输出
  15. 得到损失
  16. 平均损失
  17. 通过calculate_acc函数统计该batch的预测token的正确数与总数
  18. 统计该epoch的预测token的正确数
  19. 统计该epoch的预测token的总数
  20. 计算该batch的accuracy
  21. 获得损失值的标量累加到当前epoch总损失
  22. 如果当前的梯度累加步数大于1
  23. 对当前累加的损失对梯度累加步数求平均
  24. 损失反向传播
  25. 梯度裁剪:梯度裁剪的目的是控制梯度的大小,防止梯度爆炸的问题。在训练神经网络时,梯度可能会变得非常大,导致优化算法出现数值不稳定的情况。裁剪梯度就是将梯度的整体范数限制在一个特定的阈值之内
  26. 达到梯度累加的次数后
  27. 更新参数,达到6次batch才会更新一次参数
  28. 更新学习率
  29. 梯度清零
  30. 梯度累加次数为0时,也就是参数更新时
  31. 记录日志
  32. 记录的各个参数占位符
  33. 占位符对应的各个变量
  34. 删除两个变量,释放内存
  35. 捕捉到异常
  36. 如果异常的信息中的字符串包含内存不足的问题,也就是显卡内存不足
  37. 将该问题添加到日志信息
  38. 当显卡内存占用过多时
  39. 手动释放显卡内存
  40. 如果不是显卡内存不足
  41. 记录日志
  42. 返回异常
  43. 记录当前epoch的平均loss
  44. 记录当前epoch的平均accuracy
  45. 日志记录信息
  46. 记录的信息为当前epoch索引、损失、准确率
  47. 日志记录信息,当前保存的模型以及对于的epoch索引
  48. 保存模型的地址
  49. 如果地址不存在
  50. 创建该地址
  51. 确保得到不是外壳对象
  52. 保存模型
  53. 日志记录信息训练完成
  54. 记录完成时间
  55. 记录当前epoch训练所花费时间
  56. 返回epoch平均损失

从零构建属于自己的GPT系列1:数据预处理
从零构建属于自己的GPT系列2:模型训练1
从零构建属于自己的GPT系列3:模型训练2
从零构建属于自己的GPT系列4:模型训练3
从零构建属于自己的GPT系列5:模型部署1
从零构建属于自己的GPT系列6:模型部署2

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
训练自己的GPT模型是一个复杂且需要大量计算资源的过程。下面是一个基本的步骤概述: 1. 数据收集:收集与你想要训练的领域或主题相关的大量文本数据。这些数据可以是文章、博客、论文、小说、对话等。 2. 数据预处理:对收集到的数据进行预处理。这包括文本清洗、分词、去除停用词、剔除重复内容等步骤,以确保数据质量和一致性。 3. 构建训练集:将预处理后的数据划分为训练集和验证集。训练集用于模型训练,验证集用于评估模型的性能。 4. 模型架构选择:选择适合你任务的GPT模型架构。常见的选择包括GPT-2、GPT-3等。你可以根据你的需求和计算资源来选择合适的模型规模。 5. 模型训练:使用训练集对选定的GPT模型进行训练。这通常需要使用GPU或TPU等强大的计算资源,并可能需要长时间的训练。 6. 超参数调整:调整模型的超参数,如学习率、批次大小、训练轮数等,以优化模型的性能。这可以通过验证集的性能来指导。 7. 模型评估与调优:使用验证集评估训练模型性能,并根据需要进行调优和改进。这可以包括调整模型架构、数据增强、正则化等。 8. 部署和应用:在模型训练完成后,你可以将其部署到你的应用程序中,并使用它来生成文本、回答问题或执行其他自然语言处理任务。 请注意,训练自己的GPT模型需要大量的计算资源和专业知识。如果你没有足够的资源和经验,你也可以考虑使用已经训练好的预训练模型,并在其基础上进行微调来适应你的特定任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机器学习杨卓越

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值