写点什么

带你上手基于 Pytorch 和 Transformers 的中文 NLP 训练框架

  • 2023-08-28
    广东
  • 本文字数:7070 字

    阅读完需:约 23 分钟

带你上手基于Pytorch和Transformers的中文NLP训练框架

本文分享自华为云社区《全套解决方案:基于pytorch、transformers的中文NLP训练框架,支持大模型训练和文本生成,快速上手,海量训练数据》,作者: 汀丶 。

1.简介


目标:基于pytorchtransformers做中文领域的 nlp 开箱即用的训练框架,提供全套的训练、微调模型(包括大模型、文本转向量、文本生成、多模态等模型)的解决方案;


数据:从开源社区,整理了海量的训练数据,帮助用户可以快速上手;


同时也开放训练数据模版,可以快速处理垂直领域数据;


结合多线程、内存映射等更高效的数据处理方式,即使需要处理百GB规模的数据,也是轻而易举;


流程:每一个项目有完整的模型训练步骤,如:数据清洗、数据处理、模型构建、模型训练、模型部署、模型图解;


模型:当前已经支持gpt2clipgpt-neoxdollyllamachatglm-6bVisionEncoderDecoderModel等多模态大模型;


多卡串联:当前,多数的大模型的尺寸已经远远大于单个消费级显卡的显存,需要将多个显卡串联,才能训练大模型、才能部署大模型。因此对部分模型结构进行修改,实现了训练时推理时的多卡串联功能。


模型训练


2.文本分类模型


本部分,介绍中文的文本分类模型,适用于二分类、多分类等情况。使用 transformers 库。


  • 处理数据code_01_processdata.ipynb

  • 数据介绍本案例使用的是一个外卖平台的评论数据,对评论的文本做了分类(分为好评和差评)当你把code_01_processdata.ipynb文件跑完之后,就可以看到在📁data_all里面有一个📁data,里面有三个文件,样式都是像下面👇这样的



上图是一个batch的数据,或者所有的文本分类的数据样式:


  1. text下面的红色条,就是一个个句子。

  2. label里面有红色有绿色,就是表示标签分类。

  3. transformers包做分类的时候,数据要求就这两列。


注意点:


数据需要分为train_data.csv,test_data.csv,valid_data.csv,这三个csv文件注意是使用,分割开的。


数据不可以有缺失值


数据最好只含有两列:label,text


  • label:表示标签,最好为整型数值。0,1,2,3,4 等

  • text:表示文本,(看你需求,可以有符号,也可以没有标点符号)


train_data.csv,test_data.csv,valid_data.csv这三个数据里面,不要有数据相同的,不然会造成数据泄漏。


训练模型code_02_trainmodel.ipynb



数据训练流程


以一个 batch 为例:


  1. Tokenizer会将数据中的text转换成三个矩阵(或者叫三个Tensor),分别叫input_ids,token_type_ids,attention_mask,至于怎么转换的,我们先不做详细介绍(本仓库后续会介绍)。

  2. pretrained model在被加载之前,需要设置一大堆模型的参数,至于要设置什么参数,我们也不做详细介绍。

  3. Trainer就是一个训练器,也需要预先设置好一大堆参数。至于要设置什么参数,我们也不做详细介绍。

  4. Trainer会把input_ids,token_type_ids,attention_mask;还有数据自带的标签label;还有pretrained model都加载进来,进行训练;

  5. 当所有 batch 的数据更新完之后,最终就会生成一个模型。your new model就诞生了。

  6. 对于刚开始学习大模型做nlp分类的任务,其实不需要考虑那么多细节,只需要注意数据流程。


注意点:


  1. 这个步骤非常看显存大小。显卡显存越大越好。batch_size,eval_size大小取决于显存大小。

  2. 在实际工程中,会先使用Tokenizer把所有的文本转换成input_ids,token_type_ids,attention_mask,然后在训练的时候,这步就不再做了,目的是减少训练过程中 cpu 处理数据的时间,不给显卡休息时间。

  3. 在使用Tokenizer把所有的文本做转换的期间,如果设置的文本的长度上限为 64,那么会把大于 64 的文本截断;那些少于 64 的文本,会在训练的时候,在喂入模型之前,把长度补齐,这么做就是为了减少数据对内存的占用。


预测code_03_predict.ipynb


这个时候,就是搞个句子,然后丢给一个pipeline(这个就是把Tokenizer你的大模型放在一起了),然后这个pipeline就给你返回一个分类结果。


常见的就是使用pipeline,如果更加复杂的话,比如修改模型,这个时候,就比较复杂了(后面会再次介绍)。


部署


简单的部署相对于预测,其实就是再加一层 web 端口,fastapi 包就可以实现。


高级一点的部署相对于预测,就需要把模型从pytorch转换成onnx格式的,这样可以提高推理效率(也不一定,就是举个例子),可能也不会使用 web 端口(http 协议)了,会使用 rpc 协议等方法。这部分现在先不看。

3.中文 gpt2





本文,将介绍如何使用中文语料,训练一个 gpt2


可以使用你自己的数据训练,用来:写新闻、写古诗、写对联等


我这里也训练了一个中文 gpt2 模型,使用了612万个样本,每个样本有 512 个 tokens,总共相当于大约31亿个tokens


  • 安装包


需要准备好环境,也就是安装需要的包


pip install -r requirements.txt
复制代码


像是pytorch这种基础的包肯定也是要安装的,就不提了。


  • 数据来源

    获得数据:数据链接,关注公众号【统计学人】,然后回复【gpt2】即可获得。

    获得我训练好的模型(使用了 15GB 的数据(31亿个tokens),在一张 3090 上,训练了 60 多小时)

  • 数据格式


数据其实就是一系列文件夹📁,然后每一个文件夹里面有大量的文件,每一个文件都是.csv格式的文件。其中有一列数据是content


每一行的content就代表一句话,截图如下



虽然数据有 15GB 那么大,但是处理起来一点也不复杂,使用 datasets包,可以很轻松的处理大数据,而我只需要传递所有的文件路径即可,这个使用 glob 包就能完成。


  • 训练代码train_chinese_gpt2.ipynb


现在训练一个 gpt2 代码,其实很简单的。抛开处理数据问题,技术上就三点:tokenizergpt2_modelTrainertokenizer使用的是 bert-base-chinese,然后再添加一下bos_tokeneos_tokenpad_token


gpt2_model使用的是 gpt2,这里的 gpt2 我是从 0 开始训练的。而不是使用别人的预训练的gpt2模型。

Trainer


训练器使用的就是transformersTrainer模块。(支撑多卡并行,tensorboard 等,都写好的,直接调用就行了,非常好用)


  • 模型


模型已经上传到huggingface上了 https://huggingface.co/yuanzhoulvpi/gpt2_chinese



  • 推理代码infer.ipynb


这个是chinese-gpt2的推理代码


将代码中的model_name_or_path = "checkpoint-36000"里面的"checkpoint-36000",修改为模型所在的路径。


然后运行下面一个代码块,即可输出文本生成结果


可以参考这个代码,制作一个 api,或者打包成一个函数或者类。


  • 交互机器人界面chatbot.py


修改代码里面的第 4 行,这一行值为模型所在的位置,修改为我分享的模型文件路径。


model_name_or_path = "checkpoint-36000"
复制代码


运行


python chatbot.py
复制代码


点击链接,即可在浏览器中打开机器人对话界面



  • 更多


  1. 这个完整的项目下来,其实我都是全靠huggingface文档、教程度过来的.

  2. 我做的东西,也就是把Tokenizer改成中文的了,然后也整理了数据,别的大部分东西,都不是我做的了.

  3. 原文链接为 https://huggingface.co/course/zh-CN/chapter7/6?fw=pt.


其实,我更喜欢做应用,但是也要理解相关的背后原理,目前还在研究相关的 gpt2 原理还有相关的推理细节,这是我整理的链接,希望可以共同进步


  • https://huggingface.co/blog/how-to-generate

  • https://huggingface.co/gpt2

  • https://huggingface.co/gpt2-large

4.中文 clip 模型


本文将介绍,如何从 0 到 1 的训练一个中文 clip 模型。


在处理数据的过程中,训练的过程中,需要的注意事项。


从数据流的角度,看看 clip 模型是怎么处理数据的,模型是怎么构建的。image 和 text 的模型的差异性,两个模型是怎么合并起来计算 loss 的。


  • clip 模型介绍


CLIP 的英文全称是 Contrastive Language-Image Pre-training,即一种基于对比文本-图像对的预训练方法或者模型。


CLIP 是一种基于对比学习的多模态模型,与 CV 中的一些对比学习方法如 moco 和 simclr 不同的是,CLIP 的训练数据是文本-图像对:一张图像和它对应的文本描述,这里希望通过对比学习,模型能够学习到文本-图像对的匹配关系。


如下图所示,CLIP 包括两个模型:


  1. Text Encoder 和 Image Encoder,其中 Text Encoder 用来提取文本的特征,可以采用 NLP 中常用的 text transformer 模型;

  2. Image Encoder 用来提取图像的特征,可以采用常用 CNN 模型或者 vision transformer。



上面这段文字来源于https://zhuanlan.zhihu.com/p/493489688


从数据上看:之前相似度计算,都是两个文本对:text - text。只不过现在都是text - image了。

clip 是两个模型(具体长什么样子,后面再说)


  • text-model:负责把text转换成向量。

  • image-model:负责把image转换成向量。

  • 然后把上面两个向量,做交叉计算 loss,然后 loss 反向传播,这样两个模型的参数都会更新。


其实你想啊,这个image-model处理图像的,其实也可以改为处理视频、处理 3d 模型等。那简直是格局打开🫴了。我现在没有数据,后面也打算做一个。


你再想想,text-image => text-image-video-3d这样联合起来,是不是更好。没数据,没机器,做不了。


有些人可能感觉,你这人,就知道TMD吹牛,来来来,我带你研究研究 clip 模型的源码。


  • 数据


直接点击链接https://pan.baidu.com/s/1wGmXUNP021OWnW7Kik7q1A?pwd=gd3c

来获得。


把下载好的文件,也就是test-2.6w.csvtrain-137w.csv放在文件夹📁bigdata/raw_data里面。


以此运行processdta_01.ipynbprocessdta_02.ipynbprocessdta_02.ipynb用来处理数据。


  • processdta_01.ipynb:用来下载数据,大概下载了 10 多个小时。

  • processdta_02.ipynb:用来筛选数据,不是所有的图片数据都是可以用的,这一步非常坑。需要留意。如果图片没有筛选好,在你训练到中间的时候,突然一下因为图片无法加载导致错误,从而训练中断了。

  • processdta_03.ipynb:用来把数据干净的数据处理好,合并好,生成新的,漂亮的训练数据。


其实完整下来看,数据清洗,就是把符合格式的照片筛选出来,然后进行训练。


  • 数据总结


说到底,你的数据只要整理成这样的一个样式即可



text:这一列对应图片的标注,或者和图片相关的文本。


image_path:这一列对应图片所在你电脑本地上的路径。


是的,搞了半天,数据就是这么简单。


  • 数据预处理


这里的数据预处理,是我随便起的名字。说白了,就是这么会是:


  1. 使用tokenizertext转换成input_idsattention_mask.

  2. 使用processorimage转换成pixel_values.

  3. 处理text,那还是很快的。百万级别的数据,可能 2~3 分钟就行了。

  4. 因为image太大了,只能在训练的时候,每一 batch,才能去加载image,这就导致训练的时候特别慢。倒不是因为我的 3090 算力不行,全都TMD卡在计算机 IO 上了,非常让人难受。


  • 模型部分


终于讲解到 clip 的模型部分了。这个 clip 模型实在是太灵活了,你可以做很多个版本,这里我们挑几个比较常见的结构,来分享一下。


  • 常见的 clip 模型


这里值得是常见的 clip 模型,特指的是transformers包的 clip 模型。


clip 主要就是分为两个部分,一个是CLIPTextTransformer,一个是CLIPVisionTransformer,说白了就是一个处理 text,一个处理 image。


CLIPTextTransformerCLIPVisionTransformer的核心,都共用了一个模型结构CLIPEncoder

。也就是 CLIP 编码部分。(这里说的共用,值得是模型框架相同,而不是模型训练的时候,参数也相同。)


Q:有些人就问了,text 和 image 两个生成的数据都不一样,比如text转换成input_idsattention_maskimage转换成pixel_values;他们怎么可以使用一个模型结构CLIPEncoder


A:这个也是非常好回答的,因他俩又不是直接使用CLIPEncoder,前后都加了一些万金油的模型组件(比如embeddinglinear等),模型输出的时候,也是这么做的。还是应了那句话,就看你怎么吧数据转换成hidden_states,以及怎么把hidden_states输出出去。


Q:CLIPTextTransformerCLIPVisionTransformer输出的维度也不一定一样吧,怎么计算交叉损失?


A: 也很简单啦,加个linear对齐一下就行了。


看看CLIPTextTransformerCLIPVisionTransformer的内心:




  • 中文版本的 clip 模型


上面的常见的clip模型,确实是好,其实你只要换一个支持中文的新tokenizer,然后从 0️⃣开始训练即可。


但是这么搞,没什么创意呀。其实我第一次就是这么干的,直接支持中文的新tokenizer。但是训练了一天,loss 基本上没变化。我内心其实是崩溃的。


后来,我研究了一下transformers包里面的chinese-clip模型代码。我发现,chinese-clip相对于clip。就是把常规的CLIPTextTransformer换成了bert版本的。啊对,这就破案了。这个奉上代码截图。



  • 后续改进


因为训练 image 这类型的任务,非常吃资源,不管是我的显存还是我的磁盘。目前数据占用我硬盘100GB

针对 loss 不下降,下次如果再让我做,我打算先把clip模型的vit部分先固定住,然后训练 bert 来拟合vit-output


也可也固定 bert 模型,训练 vit 模型;


也可以拆开做,反正本质上都是Encoder,然后计算相似度。

5. 图生文 image-encoder-decoder



之前在 huggingfacehttps://huggingface.co/nlpconnect/vit-gpt2-image-captioning上看到这个模型.

感觉这个模型很有趣,想法很好。


发现这个模型关于中文的不多。


之前的clip训练其实挺失败的,loss没有下降。


主要也就是抱着学习的态度,把源码看懂,把流程跑通。分享中间的细节和踩坑经历。



  1. 使用vit来作为encoder部分,输出encoder_hidden_states绿色部分1

  2. 使用gpt2来作为decoder部分,接受encoder_hidden_states,绿色部分3

  3. 如果encoder输出的encoder_hidden_statesdecoder接受的encoder_hidden_states维度不一样,就加个linear,绿色部分2


模型训练需要的数据样式


训练的时候,模型需要的数据主要有两个维度:


  • pixel_valueimage通过processor生成

  • labeltext通过tokenizer生成的input_ids

  • 计算loss的时候,其实和gpt2一模一样的(自回归,本质上就是向后错位一下)。


目前已经把训练好的模型,发布在huggingface上了。https://huggingface.co/yuanzhoulvpi/vit-gpt2-image-chinese-captioning


本模块处理数据的方式和clip模型差不多,可以看隔壁文件夹,训练clip的数据处理思路。


  1. 只要把processdta_02.ipynb文件替换即可。

  2. 执行顺序依然按照着processdta_01.ipynbprocessdta_02.ipynbprocessdta_03.ipynb


训练部分train_encoder_decoder.ipynb


  1. 处理图像,使用的是"google/vit-base-patch16-224"模型。

  2. 处理文本,使用的是"yuanzhoulvpi/gpt2_chinese"模型。

  3. 最后就是把两个模型通过VisionEncoderDecoderModel粘起来。


训练的 loss


训练的信息


gpu 使用的是 3090,模型大概是 2.16 亿个参数。花了超过 20 个小时。但是大部分时间都是卡在 IO 上(加载图片上)



推理用你自己训练


参考infer_encoder_decoder.ipynb


直接用


from transformers import (VisionEncoderDecoderModel,                           AutoTokenizer,ViTImageProcessor)import torchfrom PIL import Imagevision_encoder_decoder_model_name_or_path = "yuanzhoulvpi/vit-gpt2-image-chinese-captioning"#"vit-gpt2-image-chinese-captioning/checkpoint-3200"
processor = ViTImageProcessor.from_pretrained(vision_encoder_decoder_model_name_or_path)tokenizer = AutoTokenizer.from_pretrained(vision_encoder_decoder_model_name_or_path)model = VisionEncoderDecoderModel.from_pretrained(vision_encoder_decoder_model_name_or_path)device = torch.device("cuda" if torch.cuda.is_available() else "cpu")model.to(device)
复制代码


max_length = 16num_beams = 4gen_kwargs = {"max_length": max_length, "num_beams": num_beams}def predict_step(image_paths):    images = []    for image_path in image_paths:        i_image = Image.open(image_path)        if i_image.mode != "RGB":            i_image = i_image.convert(mode="RGB")
images.append(i_image)
pixel_values = processor(images=images, return_tensors="pt").pixel_values pixel_values = pixel_values.to(device)
output_ids = model.generate(pixel_values, **gen_kwargs)
preds = tokenizer.batch_decode(output_ids, skip_special_tokens=True) preds = [pred.strip() for pred in preds] return predspredict_step(['bigdata/image_data/train-1000200.jpg'])
复制代码

6.vit 源码



之前都搞过clipimage-encoder-decoder。现在哪里还怕搞不懂vit


这里主要分享一下vit的最核心的部分。


  • vit 核心的数据内容


vit 想法非常牛,但是数据处理的思想更牛,之前都没提出来过。


载对于一个图片,将一个图片分割成 N 块。巧妙的使用nn.Conv2d


  • 初始化


import torchfrom torch import nn 
#base parameter
image_size=224 # 图片的width和heightpatch_size=16 # 将图片的分为块,每一块的大小为16x16,这样就有(224//16)^2 = 14 ^2 = 196个num_channels=3 # R,G, Bhidden_size=768 # 输出的hidden_sizebatch_size = 16 # 一批数据有多少
复制代码


  • 创建一个分块器和一个样本数据(一个batch)


#分块器project = nn.Conv2d(num_channels, hidden_size, kernel_size=patch_size, stride=patch_size)
#样本数据(一个`batch`) #batch_size, num_channels, height, width = pixel_values.shapepixel_values = torch.randn(batch_size, num_channels, image_size, image_size)
pixel_values.shape
复制代码


  • 输出分块的大小


project(pixel_values).shape 
#> torch.Size([16, 768, 14, 14])
复制代码


  • 数据再转换一下,image 的 embedding 就完成了。


image_embedding = project(pixel_values).flatten(2).transpose(1, 2)image_embedding.shape #> torch.Size([16, 196, 768]) # batch_size, seq_length, embedding_dim
复制代码


这个时候,就已经和文本的数据一样了。维度都是(batch_size, seq_length, embedding_dim),再向下推导,就是transformers了。没什么可介绍的了。


项目链接:https://github.com/yuanzhoulvpi2017/zero_nlp


点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 3
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
带你上手基于Pytorch和Transformers的中文NLP训练框架_人工智能_华为云开发者联盟_InfoQ写作社区