0x0. 前言 rwkv社区在huggingface上放了rwkv-4-world和rwkv-5-world相关的一系列模型,见:https://huggingface.co/blinkdl/rwkv-4-world & https://huggingface.co/blinkdl/rwkv-5-world ,然而这些模型的格式是以pytorch的格式进行保存的即*.pt文件,并没有将其转换为标准的huggingface模型。后来了解到这里还有一个问题是rwkv的世界模型系列的tokenizer是自定义的,在huggingface里面并没有与之对应的tokenizer。没有标准的huggingface模型就没办法使用tgi进行部署,也不利于模型的传播以及和其它模型一起做评测等等。
让rwkv world系列模型登陆huggingface社区是必要的,这篇文章介绍了笔者为了达成这个目标所做的一些努力,最后成功让rwkv-4-world和rwkv-5-world系列模型登陆huggingface。大家可以在 https://huggingface.co/rwkv 这个空间找到目前所有登陆的rwkv模型:
在这里插入图片描述 本系列的工作都整理开源在 https://github.com/bbuf/rwkv-world-hf-tokenizer ,包含将 rwkv world tokenizer 实现为 huggingface 版本,实现 rwkv 5.0 的模型,提供模型转换脚本,lambda数据集ppl正确性检查工具 等等。
0x1. 效果 以 rwkv/rwkv-4-world-3b 为例,下面分别展示一下cpu后端和cuda后端的执行代码和效果。
cpu from transformers import automodelforcausallm, autotokenizermodel = automodelforcausallm.from_pretrained(rwkv/rwkv-4-world-3b)tokenizer = autotokenizer.from_pretrained(rwkv/rwkv-4-world-3b, trust_remote_code=true)text = in a shocking finding, scientist discovered a herd of dragons living in a remote, previously unexplored valley, in tibet. even more surprising to the researchers was the fact that the dragons spoke perfect chinese.prompt = f'question: {text.strip()}answer:'inputs = tokenizer(prompt, return_tensors=pt)output = model.generate(inputs[input_ids], max_new_tokens=256)print(tokenizer.decode(output[0].tolist(), skip_special_tokens=true)) 输出:
question: in a shocking finding, scientist discovered a herd of dragons living in a remote, previously unexplored valley, in tibet. even more surprising to the researchers was the fact that the dragons spoke perfect chinese.answer: the dragons in the valley spoke perfect chinese, according to the scientist who discovered them. gpu import torchfrom transformers import automodelforcausallm, autotokenizermodel = automodelforcausallm.from_pretrained(rwkv/rwkv-4-world-3b, torch_dtype=torch.float16).to(0)tokenizer = autotokenizer.from_pretrained(rwkv/rwkv-4-world-3b, trust_remote_code=true)text = 你叫什么名字?prompt = f'question: {text.strip()}answer:'inputs = tokenizer(prompt, return_tensors=pt).to(0)output = model.generate(inputs[input_ids], max_new_tokens=40)print(tokenizer.decode(output[0].tolist(), skip_special_tokens=true)) 输出:
question: 你叫什么名字?answer: 我是一个人工智能语言模型,没有名字。 我们可以在本地通过上述代码分别运行cpu/gpu上的wkv-4-world-3b模型,当然这需要安装transformers和torch库。
0x2. 教程 下面展示一下在 https://github.com/bbuf/rwkv-world-hf-tokenizer 做的自定义实现的rwkv world tokenizer的测试,rwkv world模型转换,检查lambda数据集正确性等的教程。
使用此仓库(https://github.com/bbuf/rwkv-world-hf-tokenizer)的huggingface项目 上传转换后的模型到huggingface上时,如果bin文件太大需要使用这个指令 transformers-cli lfs-enable-largefiles 解除大小限制.
rwkv/rwkv-5-world-169m rwkv/rwkv-4-world-169m rwkv/rwkv-4-world-430m rwkv/rwkv-4-world-1b5 rwkv/rwkv-4-world-3b rwkv/rwkv-4-world-7b rwkv world模型的huggingface版本的tokenizer 下面的参考程序比较了原始tokenizer和huggingface版本的tokenizer对不同句子的编码和解码结果。
from transformers import automodelforcausallm, autotokenizerfrom rwkv_tokenizer import trie_tokenizertoken_path = /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizerorigin_tokenizer = trie_tokenizer('/users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_vocab_v20230424.txt')from transformers import autotokenizerhf_tokenizer = autotokenizer.from_pretrained(token_path, trust_remote_code=true)# 测试编码器assert hf_tokenizer(hello)['input_ids'] == origin_tokenizer.encode('hello')assert hf_tokenizer(s:2)['input_ids'] == origin_tokenizer.encode('s:2')assert hf_tokenizer(made in china)['input_ids'] == origin_tokenizer.encode('made in china')assert hf_tokenizer(今天天气不错)['input_ids'] == origin_tokenizer.encode('今天天气不错')assert hf_tokenizer(男:听说你们公司要派你去南方工作?)['input_ids'] == origin_tokenizer.encode('男:听说你们公司要派你去南方工作?')# 测试解码器assert hf_tokenizer.decode(hf_tokenizer(hello)['input_ids']) == 'hello'assert hf_tokenizer.decode(hf_tokenizer(s:2)['input_ids']) == 's:2'assert hf_tokenizer.decode(hf_tokenizer(made in china)['input_ids']) == 'made in china'assert hf_tokenizer.decode(hf_tokenizer(今天天气不错)['input_ids']) == '今天天气不错'assert hf_tokenizer.decode(hf_tokenizer(男:听说你们公司要派你去南方工作?)['input_ids']) == '男:听说你们公司要派你去南方工作?' huggingface rwkv world模型转换 使用脚本scripts/convert_rwkv_world_model_to_hf.sh,将huggingface的blinkdl/rwkv-4-world项目中的pytorch格式模型转换为huggingface格式。这里,我们以0.1b为例。
#!/bin/bashset -xcd scriptspython convert_rwkv_checkpoint_to_hf.py --repo_id blinkdl/rwkv-4-world --checkpoint_file rwkv-4-world-0.1b-v1-20230520-ctx4096.pth --output_dir ../rwkv4-world4-0.1b-model/ --tokenizer_file /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer --size 169m --is_world_tokenizer truecp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer/rwkv_vocab_v20230424.json ../rwkv4-world4-0.1b-model/cp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer/tokenization_rwkv_world.py ../rwkv4-world4-0.1b-model/cp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer/tokenizer_config.json ../rwkv4-world4-0.1b-model/ 使用脚本 scripts/convert_rwkv5_world_model_to_hf.sh,将来自 huggingface blinkdl/rwkv-5-world 项目的 pytorch 格式模型转换为 huggingface 格式。在这里,我们以 0.1b 为例。
#!/bin/bashset -xcd scriptspython convert_rwkv5_checkpoint_to_hf.py --repo_id blinkdl/rwkv-5-world --checkpoint_file rwkv-5-world-0.1b-v1-20230803-ctx4096.pth --output_dir ../rwkv5-world-169m-model/ --tokenizer_file /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer --size 169m --is_world_tokenizer truecp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_v5.0_model/configuration_rwkv5.py ../rwkv5-world-169m-model/cp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_v5.0_model/modeling_rwkv5.py ../rwkv5-world-169m-model/cp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer/rwkv_vocab_v20230424.json ../rwkv5-world-169m-model/cp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer/tokenization_rwkv_world.py ../rwkv5-world-169m-model/cp /users/bbuf/工作目录/rwkv/rwkv-world-hf-tokenizer/rwkv_world_tokenizer/tokenizer_config.json ../rwkv5-world-169m-model/ 另外,您需要在生成文件夹中的 config.json 文件开头添加以下几行:
architectures: [ rwkvforcausallm],auto_map: { autoconfig: configuration_rwkv5.rwkv5config, automodelforcausallm: modeling_rwkv5.rwkvforcausallm}, 运行huggingface的rwkv world模型 run_hf_world_model_xxx.py演示了如何使用huggingface的automodelforcausallm加载转换后的模型,以及如何使用通过autotokenizer加载的自定义rwkv world模型的huggingface版本的tokenizer进行模型推断。请看0x1节,这里就不赘述了。
检查lambda 如果你想运行这两个脚本,首先需要下载一下 https://github.com/blinkdl/chatrwkv ,然后cd到rwkv_pip_package 目录做pip install -e . ,然后再回到chatrwkv的v2目录下面,运行这里提供的lambda_pt.py和lambda_hf.py。
check_lambda文件夹下的lambda_pt.py和lambda_hf.py文件分别使用rwkv4 world 169m的原始pytorch模型和huggingface模型对lambda数据集进行评估。从日志中可以看出,他们得到的评估结果基本上是一样的。
lambda_pt.py lambda评估日志 # check lambada...# 100 ppl 42.41 acc 34.0# 200 ppl 29.33 acc 37.0# 300 ppl 25.95 acc 39.0# 400 ppl 27.29 acc 36.75# 500 ppl 28.3 acc 35.4# 600 ppl 27.04 acc 35.83...# 5000 ppl 26.19 acc 35.84# 5100 ppl 26.17 acc 35.88# 5153 ppl 26.16 acc 35.88 lambda_hf.py lambda评估日志 # check lambada...# 100 ppl 42.4 acc 34.0# 200 ppl 29.3 acc 37.0# 300 ppl 25.94 acc 39.0# 400 ppl 27.27 acc 36.75# 500 ppl 28.28 acc 35.4# 600 ppl 27.02 acc 35.83...# 5000 ppl 26.17 acc 35.82# 5100 ppl 26.15 acc 35.86# 5153 ppl 26.14 acc 35.86 从lambda的输出结果可以验证原始基于chatrwkv系统运行的模型和转换后的huggingface模型是否精度是等价的。
0x3. 实现 tokenizer的实现 tokenizer的实现分为两步。
因为目前社区的rwkv world模型tokenizer文件是一个txt文件:https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/rwkv_vocab_v20230424.txt 。我们需要将其转换为huggingface的autotokenizer可以读取的json文件。这一步是通过 https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/scripts/convert_vocab_json.py 这个脚本实现的,我们对比一下执行前后的效果。
在这里插入图片描述 转换为json文件后:
在这里插入图片描述 这里存在一个转义的关系,让gpt4解释一下u0000和x00的关系:
在这里插入图片描述 有了这个json文件之后,我们就可以写一个继承pretrainedtokenizer类的rwkvworldtokenizer了,由于rwkv world tokenzier的原始实现是基于trie树(https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/rwkv_tokenizer.py#l5),所以我们实现 rwkvworldtokenizer 的时候也要使用 trie 树这个数据结构。具体的代码实现在 https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/rwkv_world_tokenizer/tokenization_rwkv_world.py 这个文件。
需要注意 https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/rwkv_world_tokenizer/tokenization_rwkv_world.py#l211 这一行,当解码的token id是0时表示句子的结束(eos),这个时候就停止解码。
rwkv world 5.0模型实现 实现了rwkv world tokenizer之后就可以完成所有rwkv4 world模型转换到huggingface模型格式了,因为rwkv4 world模型的模型结构和目前transformers里面支持的rwkv模型的代码完全一样,只是tokenzier有区别而已。
但是如果想把 https://huggingface.co/blinkdl/rwkv-5-world 这里的模型也转换成 huggingface模型格式,那么我们就需要重新实现一下模型了。下面红色部分的这个模型就是rwkv5.0版本的模型,剩下的都是5.2版本的模型。
在这里插入图片描述 我对照着chatrwkv里面rwkv world 5.0的模型实现完成了huggingface版本的rwkv world 5.0版本模型代码实现,具体见:https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/rwkv_world_v5.0_model/modeling_rwkv5.py ,是在transformers官方提供的实现上进一步修改得来。
实现了这个模型之后就可以完成rwkv world 5.0的模型转换为huggingface结果了,教程请看上一节或者github仓库。成品:https://huggingface.co/rwkv/rwkv-5-world-169m
0x4. 踩坑 在实现tokenizer的时候,由于rwkv world tokenzier的eos是0,我们没办法常规的去插入eos token,所以直接在tokenzier的解码位置特判0。
rwkvworldtokenizer的初始化函数在 super().__init__ 的时机应该是在构造self.encoder之后,即:
在这里插入图片描述 否则会在当前最新的transformers==4.34版本中造成下面的错误:
在这里插入图片描述 在模型实现部分踩了一个坑,在embedding的时候第一次需要需要对embedding权重做一个pre layernorm的操作:
在这里插入图片描述 这个操作只能在第一次输入的时候做一般就是prefill,我没注意到这一点导致后面decoding的时候也错误做了这个操作导致解码的时候乱码,后面排查了不少时间才定位到这个问题。
另外,在做check lambda的时候,如果不开启torch.no_grad上下文管理器禁用梯度计算,显存会一直涨直到oom:https://github.com/bbuf/rwkv-world-hf-tokenizer/blob/main/check_lambda/lambda_hf.py#l48 。
0x5. 总结 这件事情大概花了国庆的一半时间加一个完整的周六才搞定,所以这篇文章记录一下也可以帮助有相关需求的小伙伴们少踩点坑。
长征二号F火箭总设计师叫容易 让“天和”带你看太空的日落有多美
用于水下环境实时3D场景重建的单光子激光雷达成像技术
XESS X3无机三原色量子点电视 在技术领先性及成本控制方面取得重大突破
食用油酸价快速检测仪HM-J12的功能介绍
外媒盘点2016年科技突破 之一为人人都能玩转VR
CPU后端和CUDA后端的执行代码和效果
水喷射引导的激光技术
伺服电机抖动原因及处理
鱼虾食品安全快速检测系统的操作原理介绍
Andes晶心科技和Parasoft合力为汽车功能安全应用提供无缝的软件测试工具
SIMPLE SWITCHER真的那么好用吗?
更新换代心中有数 5G手机究竟值不值得买?
比特币如何在会计领域提供简单却具备革命性的进步
OPPO将面向日本市场推出5G智能手机
Loco—一个为全栈开发者提供的Web框架
HER608整流二极管 国产品牌 东沃电子
中国联通提出了“智慧冬奥”的核心目标
LM35温度传感器应用及特性
Flyme进化史,魅蓝X/Flyme6发布在即,能结合出怎样的火花?
厄瓜多尔智能手机出货量小米位居榜首