好久没更新这个专栏的文章了,今天抽空写了一篇。————2023.12.28
摘要:文体包括新闻,法律文书,公告,广告等,每种文体的书写风格不一样,如果拥有自己的数据集,想针对特定文体来训练一个内容生成的工具,来帮助自己写点文章,如果没接触过AIGC,可能一开始会觉得无所入手,那么希望本文能够帮助到你。本文将基于llama2来教大家如何训练一个内容生成工具,即训练属于自己的AIGC(Artificial Intelligence Generated Content)。 这里需要训练两个模型,一个是tokenizer,一个是llama2模型,我们一个一个来。
看这篇文章之前可以看下以下两篇文章:
相关github: tokenizer: GitHub - google/sentencepiece: Unsupervised text tokenizer for Neural Network-based text generation. llama2.c: GitHub - karpathy/llama2.c: Inference Llama 2 in one file of pure C
如果没有显卡,可使用kaggle,kaggle的P100 gpu 足矣
可直接运行的kaggle:llama2-c-chinese
加载中文数据集:
数据来源: https://github.com/eatmop/MNBVC https://huggingface.co/datasets/liwu/MNBVC
简单的加载方式:
由于law_judgement数据集太大了,要下载很久,所以可以下载小一点的数据集,比如news_peoples_daily
需要把下载的数据集进行处理,才能用来训练。
下载后的数据集如下: 获取到的中文数据集需要转换成对应的格式
首先我们借用训练英文时的数据集(TinyStories_all_data),来看下训练llama2时的数据格式,如下(我们取一条数据集出来看看)
训练时,读取了"story"里面的内容进行训练,因此我们需要将news_peoples_daily的格式进行转换,news_peoples_daily的json格式如下:
有用的部分也就是[”段落”][”内容”],但可以看到文章是被分为多段了,所以要把这些段落整合到一起,作为一篇新闻,然后再把它放到”story”的字段下:
文章每一段的内容也就是用下面的结构体表示
代码如下:
由于有多个json文本文件,然后也保存了多个txt文件,所以将这些txt文件合并为一个文件,保存为"data/mergedatas.txt”:
合并后的数据集"data/mergedatas.txt”,就可以用来训练tokenizer了,训练过程参考下面的文章:
[玩转AIGC]sentencepiece训练一个Tokenizer(标记器)
我们还需要对数据集进行处理,使得其符合train.py的输入数据格式,也就是转为带key为"story"的json数据,保存为txt文件:
转换后如下:
[{“story”: “### 对外友协举行酒会 庆祝蒙古人民革命六十二周年 1983-07-07 第4版() 专栏: 对外友协举行酒会 庆祝蒙古人民革命六十二周年 新华社北京7月5日电 为庆祝蒙古人民革命六十二周年,对外友协今天下午在这里举行酒会。 应邀出席酒会的有蒙古人民共和国驻中国大使彭茨克·沙格达尔苏伦,以及大使馆外交官员。 对外友协副会长陆璀主持了酒会。酒会结束后放映了中国彩色故事片《快乐的单身汉》。 ”}, {“story”: “### 今日兄弟报纸要目 1983-07-08 第4版() 专栏:今日兄弟报纸要目 今日兄弟报纸要目 《天津日报》△国务院委托水电部在天津召开的引滦工程管理工作会议提出,不但要把引滦工程建设成为第一流的工程,而且要努力创造第一流的管理水平 《经济日报》△一些地区和单位措施不力,心存观望,关停计划外烟厂进展迟缓 △社论:执行国务院决定不能打折扣 《四川日报》△四川省政府决定计划外烟厂一律关停 《湖北日报》△武汉钢铁公司主动清查乱涨价问题,从7月1日起停止加收计划外协作钢材“管理费” 《文汇报》△进一步加强关于统一祖国方针政策的宣传教育,《上海市对台宣传展览》昨日开幕 《报》△北京部队某炮团坚持原则,退回12名不符合规定的汽车驾驶员 《人民铁道》△特约评论员文章:杜绝野蛮装卸的根本措施在于加强基础工作 《南方日报》△广东省地质局水文一队在雷州半岛地表以下500米深度内查明有“地下海”,地下水资源总量每日为1,471万吨 《陕西日报》△平利县农民积极发展香菇生产,全县有1,100多户和外贸公司签订合同 《解放日报》△上海造船工业今年上半年创历史最好水平,已完成船舶20艘,计17.7万多吨位,其中出口船11艘,共15.6万多吨位,总产值4亿多 《北京日报》北京市政府通知:严格控制企业职工加班加点,制止滥发加班加点工资 ”}]
总之:这里我们准备了2种数据,一种用于训练tokenizer,一种用于训练llama2模型,并分别简单介绍了数据结构
训练操作可以参考博文:[玩转AIGC]sentencepiece训练一个Tokenizer(标记器)
训练完成后可以查看相关词汇
查看词汇个数:
打开训练好的文件tokenizer.vocab,就可以看到个数,可看到一共是8000
将tokenizer转为C++可读的bin,运行:
可看到:
tokenizer.bin
在进行train之前,先对训练集进行处理,即使用训练好的tokenizer进行编码:
先修改tinystories.py的pretokenize()方法里面的数据集路径:
必要时修改主路径:
然后运行:
训练之前需要对一些参数进行修改,这一步很重要:
vocab_size设置为总文字数的个数,可以看到原代码为32000,所以这里将32000改为8000,否则在运行https://blog.csdn.net/qq_27149279/article/details/run model.bin的时候,会在下面画框那句return了,因为数组越界。
注意:run.c里面的config是从train出来的model.bin读取的,也就是里面的checkpoint
如果训练时忘记改了,那就直接在run.c里面直接把config.vocab_size改过来即可,上面划线部分就是直接把32000改为8000
max_seq_len:推理生成的句子长度,会直接影响生成的故事长度,默认为256,能人为在run.c里面去修改长度(但是长度最好不超过训练时的max_seq_len,否则运行run.c时运行到越界了会报错),在run.c里面的变量为steps,训练时max_seq_len不能太大,要不然会报显存不足,训练时候会看到提示: 代码里面默认为64256,也就是batch size为64,max_seq_len为256,这边我为了增长推理输出的句子长度(max_seq_len),把训练时的batch_size减少了,要不然内存要不足了,也就是改为161024
run.c中int token = 1,表示从头开始生成,设置为0会不知道从哪开始,随便生成的,也就是开头不知道从哪开始,所以建议token采用默认值,也就是token=1
训练之前需修改数据集加载的路径
先来看看训练时数据集是怎么加载的: 先来看看训练时数据集是怎么加载的:
可以看到调用了Task.iter_batches,Task是在tinystories.py里面定义的,来看看tinystories.py里面的Task:
可看到调用了PretokDataset,仔细看PretokDataset,发现了数据集路径,修改即可,也就是修改PretokDataset下的"news_peoples_daily"
修改好之后训练模型:
1)直接可跑的代码:
下载数据集之后放在data目录,依次运行:
1、python3 news_peoples_daily.py 2、python3 mergedatas.py 3、python3 processTrainDataSets.py 4、python3 tinystories.py pretokenize 5、python3 train.py
放在kaggle里面的代码,需要创建data/news_peoples_daily文件,然后把编码好的数据集.bin文件放到里面,直接训练训练即可:
训练之后,在out里我们可以得到两个模型: model.bin模型是可以用来进行C代码推理的
1>逐层手写转换(占用内存少)
loadModelPt.py
2>参考llama2.py的转换(占用内存大一些)
github:https://github.com/tairov/llama2.py/tree/master
需对原来的代码做小修改,改为从.pt读取参数,然后也要修改输入:
修改后的代码为:
export_meta_llama_bin.py
如果你想改输入输出的路径,那么要修改代码export_meta_llama_bin.py里面的:
然后直接运行:
代码与模型在run.zip里面 可以看到主要为上图框中的4个文件,其中.bin文件均为模型文件,一个是文本编码模型,一个是llama模型
进行编译
运行推理
int token = 0时生成的内容,开头随便生成
int token = 1,=从头生成,且max_seq_len=1024
修改:
去掉下面两句(读取tokenizer.bin时):
bpe_encode也需要做修改(添加中文支持):
参考:https://github.com/chenyangMl/llama2.c-zh/blob/main/run.c
运行:
AVX2指的是使用 AVX2 指令集的内嵌函数(intrinsics)来执行矩阵乘法(matmul)操作,当然也包含了原始的矩阵乘法方法
将tokenizer.model拷贝到代码根目录下,运行:
导出的模型:tokenizer.bin
可见比master分支下的模型还要大一些,内容更丰富
跟1、可自定义参数运行(运行master旧tokenizer.bin模型) 一样,但是只需要修改bpe_encode 使得代码能够兼容中文,不一样的地方是不需要修改tokenizer.bin模型的读取,也就是不需要去掉:
不同的地方在export
master的export
feature/avx2的export
把写入文件那里摘出来:
多写了max_token_length,与score:
把f.write(struct.pack(“fI”, score, len(bytes)))改为f.write(struct.pack(“I”, len(bytes)))
把**f.write(struct.pack(“I”, max_token_length))**去掉,两者就一样了
对于大部分常见的中文字符,UTF-8 编码使用 3 个字节来表示。每个字节都有 8 位,因此一个中文字符在 UTF-8 编码中所占用的总位数是 3 × 8 = 24 位。
比如用下面的输入,带了prompt
输入的prompt为“中国特色社会主义”,会通过bpe_encode这个函数进行处理,结合分数来处理,
训练的tokenizer的词汇表,我们可以看到:
在tokenizer词汇表里面能找到的就是以下的词汇: 可见中国是分数得分最高的,因此第一轮的:
best_score = -6.41448; best_id = 48; best_idx = 0;
把“中”,“国”,组合为“中国”,因此tokens变为以下的 接着再进行组合,那么就是
再来查看tokenizer词汇表 得分最高的是社会,将“社”,“会”,两个词组合到一起,因此输入的tokens变为:
将tokens两两前后合并,得到:
得到主义得分最低,因此tokens就变为: 然后再进行两两前后组合:
查看tokenizer词汇表 因此,tokens变为:
最后再进行组合:
在tokenizer词汇表里面已经找不到相应词汇了,此时就结束while(1)的死循环
上面可以看到最后的tokens就变成了:
tokens[0] = 中国 tokens[1] = 特色 tokens[2] = 社会主义
也就是说原本为:“中”,“国”,“特”,“色”,“社”,“会”,“主”,“义”,经过bpe_encode的处理,就变成了“中国”,“特色”,“社会主义”,原本看起来没关系的独个词汇,变成有关联
最终得到的tokens就赋值给了prompt_tokens,即变为:[48, 2953, 274],然后再补一些padding,使得输入shape一致。
**steps:**不是表示词汇个数,而是生成的token个数,有个token包含了多个词汇,有的token是标点符号,比如:“社会主义”,“,”
注:很多没细看,以后有空再补充
https://github.com/google/sentencepiece/blob/9cf136582d9cce492ba5a0cfb775f9e777fe07ea/python/add_new_vocab.ipynb
打印new_token会得到下面的内容
采用UTF-8编码的,可恢复为:
打出来是“诽”
输出所加载模型的所有token:
本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕,E-mail:xinmeigg88@163.com
本文链接:http://www.dbeile.cn/news/1755.html