从零构建属于自己的GPT系列2:模型训练1(预训练中文模型加载、中文语言模型训练、逐行代码解读)
🚩🚩🚩Hugging Face 实战系列 总目录
有任何问题欢迎在下面留言
本篇文章的代码运行界面均在PyCharm中进行
本篇文章配套的代码资源已经上传
从零构建属于自己的GPT系列1:数据预处理
从零构建属于自己的GPT系列2:模型训练1
从零构建属于自己的GPT系列3:模型训练2
从零构建属于自己的GPT系列4:模型训练3
0 运行参数
指定运行配置参数后运行 :
–epochs 5
–batch_size 8
–device 0
–train_path data/train_novel.pkl
–save_model_path ./model/novel
1 参数设置
def set_args():
parser = argparse.ArgumentParser()
parser.add_argument('--device', default='0,1', type=str, required=False, help='设置使用哪些显卡')
parser.add_argument('--no_cuda', action='store_true', help='不使用GPU进行训练')
parser.add_argument('--vocab_path', default='vocab/chinese_vocab.model', type=str, required=False, help='sp模型路径')
parser.add_argument('--model_config', default='config/cpm-small.json', type=str, required=False, help='需要从头训练一个模型时,模型参数的配置文件')
parser.add_argument('--train_path', default='data/train.pkl', type=str, required=False, help='经过预处理之后的数据存放路径')
parser.add_argument('--max_len', default=200, type=int, required=False, help='训练时,输入数据的最大长度')
parser.add_argument('--log_path', default='log/train.log', type=str, required=False, help='训练日志存放位置')
parser.add_argument('--ignore_index', default=-100, type=int, required=False, help='对于ignore_index的label token不计算梯度')
parser.add_argument('--epochs', default=100, type=int, required=False, help='训练的最大轮次')
parser.add_argument('--batch_size', default=16, type=int, required=False, help='训练的batch size')
parser.add_argument('--gpu0_bsz', default=6, type=int, required=False, help='0号卡的batch size')
parser.add_argument('--lr', default=1.5e-4, type=float, required=False, help='学习率')
parser.add_argument('--eps', default=1.0e-09, type=float, required=False, help='AdamW优化器的衰减率')
parser.add_argument('--log_step', default=10, type=int, required=False, help='多少步汇报一次loss')
parser.add_argument('--gradient_accumulation_steps', default=6, type=int, required=False, help='梯度积累的步数')
parser.add_argument('--max_grad_norm', default=1.0, type=float, required=False)
parser.add_argument('--save_model_path', default='model', type=str, required=False, help='模型输出路径')
parser.add_argument('--pretrained_model', default='model/zuowen_epoch40', type=str, required=False, help='预训练的模型的路径')
parser.add_argument('--seed', type=int, default=1234, help='设置随机种子')
parser.add_argument('--num_workers', type=int, default=0, help="dataloader加载数据时使用的线程数量")
parser.add_argument('--warmup_steps', type=int, default=4000, help='warm up步数')
args = parser.parse_args()
return args
由于这里很多地方,在help中已经解释过意思,我只解释部分内容
- ‘–device’,如果只有单卡设置成0,多卡设置成0,1…
- ‘–no_cuda’,不使用GPU进行训练
- ‘–vocab_path’,中文预训练分词模型路径,这个模型是用来分词的,不是用来
- ‘–vocab_path’,模型就是用的cpm现成的,完全没有改
- ‘–max_len’,文本中的一句话,可能是指逗号或者句号隔开是一句话,但是当前的NLP任务中,是换行符后才是一句话,所以可能等到换行符的时候已经有几十行了 ,这里的max_len就是不管一句话多长,都按照200个词进行分割,就和逗号句号没有关系了,到一句话结束时,如果不到50词就不要了,有50词就加上一句话再补上0
- ‘–ignore_index’,-100表示在任务中,有一些特殊字符和一些没用的东西是不想要的,要忽略的ID是多少
- ‘–seed’,设置随机种子,设置随机种子在机器学习和深度学习中是非常重要的,在训练模型时,如果不设置随机种子,每次运行代码得到的模型参数初始化、数据集划分等都可能不同,导致实验结果的差异
- ‘–gradient_accumulation_steps’,梯度累加步数,正常情况下是一次迭代更新,但是可以攒几次,在pytorch中每次迭代完成后都需要进行一次梯度清零,实际上就相当于间接增加了batch_size,
- warmup_steps,刚开始缓慢训练,然后逐步增加训练速度,再然后再平稳训练,最后再进行学习率的衰减。
2 main()函数
def main():
args = set_args()
os.environ["CUDA_VISIBLE_DEVICES"] = args.device
args.cuda = not args.no_cuda
logger = set_logger(args.log_path)
args.cuda = torch.cuda.is_available() and not args.no_cuda
device = 'cuda:0' if args.cuda else 'cpu'
args.device = device
logger.info('using device:{}'.format(device))
set_random_seed(args.seed, args.cuda)
tokenizer = CpmTokenizer(vocab_file="vocab/chinese_vocab.model")
args.eod_id = tokenizer.convert_tokens_to_ids("<eod>") # 文档结束符
args.pad_id = tokenizer.pad_token_id
if not os.path.exists(args.save_model_path):
os.mkdir(args.save_model_path)
if args.pretrained_model: # 加载预训练模型
model = GPT2LMHeadModel.from_pretrained(args.pretrained_model)
else: # 初始化模型
model_config = GPT2Config.from_json_file(args.model_config)
model = GPT2LMHeadModel(config=model_config)
model = model.to(device)
logger.info('model config:\n{}'.format(model.config.to_json_string()))
assert model.config.vocab_size == tokenizer.vocab_size
if args.cuda and torch.cuda.device_count() > 1:
model = BalancedDataParallel(args.gpu0_bsz, model, dim=0).cuda()
logger.info("use GPU {} to train".format(args.device))
num_parameters = 0
parameters = model.parameters()
for parameter in parameters:
num_parameters += parameter.numel()
logger.info('number of model parameters: {}'.format(num_parameters))
logger.info("args:{}".format(args))
# 加载训练集和验证集
# ========= Loading Dataset ========= #
train_dataset = load_dataset(logger, args)
train(model, logger, train_dataset, args)
- main函数
- 初始化参数(命令行中已经制定好了参数)
- 训练设备、显卡参数
- 训练设备、显卡参数
- 创建日志对象
- 训练设备、显卡参数
- 训练设备、显卡参数
- 训练设备、显卡参数
- 训练设备、显卡参数加入日志
- 设置随机种子
- 读进来CpmTokenizer
- end_id=7,索引为7代表一个句子的终止符
- 添加padding的id,padding索引是5
- 如果保持模型的文件路径不存在
- 新建一个路径
- 加载预训练模型
- 指定的是一个GPT2的模型
- 如果没有模型
- 从json文件中导入配置
- 加载gpt2模型(也就是你给了预训练模型,就直接加载模型,没有就需要下载模型)
- 模型放入训练设备中
- 内存开始占用
- 在命令行中可以看到日志信息了
- 多卡训练
- 多卡训练
- 多卡训练
- 计算模型参数的变量
- 导入计算参数的函数
- 用for循环变量层
- 累加参数量
- 记录参数日志信息
- 记录参数设置
- 通过加载数据函数加载数据
- 通过训练函数训练模型
从零构建属于自己的GPT系列1:数据预处理
从零构建属于自己的GPT系列2:模型训练1
从零构建属于自己的GPT系列3:模型训练2
从零构建属于自己的GPT系列4:模型训练3