百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

从零手搓中文大模型|Day02——Tokenizer和BPE

cac55 2024-09-19 16:56 42 浏览 0 评论

走过路过不要错过,先收藏+关注第一时间获取最新进度(或前来催更)

从零手搓中文大模型|Day02

原本是计划直接进入「数据处理」阶段的,但由于实在精力有限,就拆成两期:这次先说说Tokenizer,下一期说数据处理。

由于Tokenizer的训练算是一个相对独立的过程,且训练相对来说比较简单,因此我也打算偷懒先用国内大厂开源的,之后有时间再自己训练实现一个。

其实分词器这块的内容经常容易被大家忽略,但实际上是非常重要的,因为它直接决定了模型的输入,进而影响到模型的训练效果。

这部分内容我自己此前也没怎么深入研究过,所以这次也是一边学习一边写,权当补课了,如果有错误的地方,欢迎指正。

Tokenizer选择

tokenization是大模型训练的第一步,是将文本转换为模型可以理解的数字表示(后面也能反向decode回来)。

其中目前比较主流的是BPE(Byte Pair Encoding)[1](详细的介绍可以参考链接文章,下面通过例子简单介绍一下原理)。

BPE是一种简单的数据压缩形式,这种方法用数据中不存在的一个字节表示最常出现的连续字节数据。这样的替换需要重建全部原始数据。

BPE简介

假设我们要编码如下数据

aaabdaaabac

字节对“aa”出现次数最多,所以我们用数据中没有出现的字节“Z”替换“aa”得到替换表

Z <- aa

数据转变为

ZabdZabac

在这个数据中,字节对“Za”出现的次数最多,我们用另外一个字节“Y”来替换它(这种情况下由于所有的“Z”都将被替换,所以也可以用“Z”来替换“Za”),得到替换表以及数据

Z <- aa Y <- Za

YbdYbac

我们再次替换最常出现的字节对得到:

Z <- aa Y <- Za X <- Yb

XdXac

由于不再有重复出现的字节对,所以这个数据不能再被进一步压缩。

解压的时候,就是按照相反的顺序执行替换过程。

测试Tokenizer(以ChatGLM3-6B的tokenizer为例)

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm3-6b", trust_remote_code=True)

编码

print(tokenizer("这是一个测试"))
{'input_ids': [64790, 64792, 30910, 36037, 32882], 'attention_mask': [1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4]}

反编码

print(tokenizer.decode(tokenizer("这是一个测试")["input_ids"]))
'[gMASK] sop 这是一个测试'

??这里可以发现反向解码的时候,多出来了[gMASK]sop这两个「奇怪」的token,原因下面会讲到。

我们看看词表的大小:

print(tokenizer.vocab_size)
64798

这里我们写一个函数,针对数据集里的一行json文本做处理,得到整行中文文本的编码数组。

import numpy as np


def process_line(line, tokenizer, add_eos=True, dtype=np.uint16):
    js = json.loads(line)
    story = js["story_zh"]
    story = tokenizer.encode(story, add_special_tokens=False)
    if add_eos:
        story.append(tokenizer.eos_token_id)
    # 这里可以用np.unint16,因为我们的vocab_size是小于65536的
    arr = np.array(story, dtype=dtype)
    return arr

??这里有几个需要注意的点:

  • ? add_special_tokens参数的作用是添加特殊token。 是chatglm自定义的例如[gMASK]/sop,属于glm架构里特有的(可以参考这里[2])。由于我们后续并不使用glm的架构,因此这里不需要添加,直接设置为False
  • ? 需要在末尾加上eos标记对应的token_id。
  • ? chatglm3-6b使用的词表大小为64798,刚好在uint16的表示范围内,所以上面我们给numpy.array设置了dtype=np.uint16

拿一行测试一下:

import json

with open("../../Data/TinyStoriesChinese/train/data00_zh.jsonl", "r") as f:
    for line in f.readlines():
        data = process_line(line, tokenizer)
        print(data)
        print(tokenizer.decode(data))
        break
[30910 56623 56623 54542 50154 31761 31155 31633 31815 54534 32693 54662
 55409 31155 35632 31123 31633 34383 57427 47658 54578 34518 31623 55567
 55226 31155 56623 56623 54695 39887 32437 55567 55226 31155 54790 41309
 52624 31123 56856 32660 55567 55226 31155    13 30955 54834 54546 31123
 54613 31404 30955 36213 31155 54613 36660 54563 54834 43881 32024 31155
 56623 56623 32707 54657 33436 31155 54790 54937 56567 40714 31123 38502
 56653 55483 31155    13 54613 32984 56623 56623 31155 54572 31897 54790
 54657 35245 31155 36551 54695 56567 55567 55226 31155 33152 56623 56623
 51556 31797 39055 31155 31694 56623 56623 31631 51556 31155 54790 54937
 56567 54937 54929 31155 54790 55409 40915 34492 54537 31155    13 30955
 54546 32591 56567 55567 55226 55398 31123 56623 56623 31514 30955 54613
 54761 31155 56623 56623 54721 33906 31804 54887 31155 54790 46977 56567
 55567 55226 31155 54613 31897 32960 54597 31155 54572 54942 34675 31155
    13 56623 56623 56567 40915 54589 31123 36467 33501 31155 54790 54708
 55567 55226 54547 57456 32246 31123 36712 34245 31155 54790 56901 55328
 54537 55673 31155 54790 56399 37247 31155    13 30955 58394 56657 31123
 58394 56657 31123 58394 56657 31404 30955 36213 31155 35957 55227 54613
 31155 54790 31772 47554 31934 54790 31155 54688 54613 33551 33892 31155
 54572 34247 31155    13 56623 56623 32707 54657 52992 31155 54790 31772
 54790 54558 54542 54613 32097 55567 55226 31155 54790 31772 33152 33892
 37322 54790 31155 54790 54531 60337 54531 57635 54563 35220 52624 31155
 54790 31857 33277 32086 44829 49102 54547 31155 35328 43352 41147 31155
 54572 42393 32233 31155    13 56623 56623 40466 31155 54790 31897 54613
 33058 31155 54790 55947 32660 31804 41147 31155 54790 31772 38711 33857
 31155 54790 54695 37300 31155 54790 54695 32462 31705 31761 31155     2]
莉莉和本是朋友。他们喜欢在公园里玩。有一天,他们在一棵大树下看到了一个秋千。莉莉想试试那个秋千。她跑到树下,爬上了秋千。
"推我,本!"她说。本轻轻地推了她一下。莉莉感到很开心。她越荡越高,笑着喊叫。
本看着莉莉。他觉得她很可爱。他也想荡秋千。他在莉莉停下来之后等着。但是莉莉没有停下来。她越荡越快。她玩得太高兴了。
"我也可以荡秋千吗,莉莉?"本问。莉莉没听到他的话。她忙着荡秋千。本觉得很难过。他走开了。
莉莉荡得太高,失去了平衡。她从秋千上摔下来,落在地上。她扭伤了脚。她哭了起来。
"哎呀,哎呀,哎呀!"她说。她在找本。她希望他能帮助她。但本不在那里。他走了。
莉莉感到很抱歉。她希望她能和本分享秋千。她希望他在那里拥抱她。她一瘸一拐地走到树下。她看到有什么东西挂在树枝上。那是本的帽子。他留给她的。
莉莉笑了。她觉得本很好。她戴上了他的帽子。她希望他会回来。她想道歉。她想再次成为朋友。

选择ChatGLM3-6B的tokenizer的原因

该词表大小为64798,值得注意的是:这是一个很的数字,因为它刚好在uint16的表示范围(0~65535的无符号整数),每一个token只需要两个字节即可表示。

当我们的语料较大时候,相比常用的int32可以节省一半的存储空间

另外这里选择一个小尺寸的词表还有一个更重要的原因:我们后面的模型会选择一个小参数量的,如果词表过大,会导致大部分参数被embedding层占用,而无法训练出更好的模型。

小结

  1. 首先熟悉了一下BPE的原理
  2. 测试了一下ChatGLM3-6B的tokenizer
  3. 编写了一个函数,用于将一行json文本转换为token_id数组
  4. 解释了为什么选择ChatGLM3-6B的tokenizer

这期的内容就到这儿啦,有收获的话,走之前点个收藏吧。这也是我坚持更新的动力!

引用链接

[1] BPE(Byte Pair Encoding): https://zhuanlan.zhihu.com/p/424631681
[2] 这里:
https://github.com/THUDM/ChatGLM3/issues/183

相关推荐

14款健身APP蹿红 看看下载最多的是哪款?

Zombies,Run!($3.99,安卓,iOS)如果你的运动理念是:除非有人追,否则绝不跑起来,那么这款APP应该适合你。Zombies,Run!这款程序把单调的跑步过程变身为躲避僵尸的游戏...

微软官方彩蛋庆祝《回到未来》纪念日

2015年10月21日,是MartyMcFly和Brown博士回到未来的时间。现在,这一天真的到了,那么当时影片中展示的一些科技产品究竟有多少实现了呢?作为一家走在技术前沿的公司,日前,微软就在M...

时尚圈最潮同志情侣 帅到没朋友(同志情侣微信头像)

来源:MSN时尚综合|2015-03-0419:45:15男演员ZacharyQuinto(中)与男模MilesMcMillan(右)于纽约街头公开热吻。情人节这个拥有不同起源传说,最早可以...

IE浏览器阻止过期ActiveX控件或将影响网银的使用

IE浏览器网银IE浏览器网银如果经常使用IE浏览器浏览网页的用户,可能都有遇到过浏览器窗口提示安装ActiveX控件的情况,一般情况下用户也是会选择直接安装。ActiveX控件广义上是指微软公司的整...

如何使Microsoft Band连接到WP设备

如果你幸运地购买到了MicrosoftBand,那么恭喜你。现在我们(winbeta)推出了“帮助系列”,那些尚未买到MicrosoftBand的朋友可以了解设备的一些新功能,以及设备的其他关键特...

毕业生不得不看的五大骗局全揭秘(毕业生防骗)

目前,距离高校大学生毕业已不足100天,大部分毕业生都十分忙碌。论文定稿、答辩,参加招聘、面试等成了应届毕业生的头等大事。但随着毕业季的临近,不法分子专门针对毕业生的诈骗高发期也随之来临。360手机安...

菠萝觅生活是O2O应用流量入口最大的供应商

现在主流的传统O2O生活服务,他们其实都有一个共通点,那就是各行其道。打车有快的,滴滴,外卖有饿了么,买机票有去哪儿网…每个APP都有着自己的核心竞争力。而用户呢?既想拥有海量有趣应用,又担心占用过多...

WP8.1版MSN健康应用,现已支持锁屏计步

IT之家(www.ithome.com):WP8.1版MSN健康应用,现已支持锁屏计步@WP之家报道,微软今天已将必应系列应用品牌归为MSN,除此之外,WP8.1版MSN健康和天气应用也获得一些新的...

短信就能传播手机病毒?看完推理惊呆了!

很多人都收到过一种带网址的陌生短信,有的人会点击网址看看,有的还会在好奇心驱使下回复短信。近日《北京新发现》栏目报道了一起离奇的电信诈骗案,事主耿先生的银行卡从未离身,但是在收到一条带网址的陌生短信,...

微软OneClip:我承包了你的剪贴板(微软onedrive云空间)

不久前,Twitter用户WalkingCat曝光了微软一款名为OneClip的应用。这是一款剪贴板应用,根据描述这款应用将覆盖Windows10(包括桌面和移动)、iOS和Android平台,可以...

Windows 10手机应该是什么样?微博用户给出了概念图

随着Windows10发布的不断临近,WindowsPhone的用户对Windows10的旗舰手机的期望也越来越高,我们WP中文网也在微博上发出了同样的问题,搜集用户对Windows10的硬...

云管家出席武汉2015年支付宝O2O生态峰会

2月4日,蚂蚁金服O2O生态峰会在武汉启幕。此次峰会展现了2015年蚂蚁金服在O2O领域的开放思路和策略,以及合作伙伴对O2O的创新观念及思路分享,吸引了武汉近3000名企业大佬、众多创业者、第三方服...

微软将于下周开启Windows开发中心帐号迁移工作

自下周开始微软将启动Windows开发中心的帐号迁移工作。根据WindowsBuildingApps博客透露Windows开发中心帐号迁移工作将会分为几个阶段。首个阶段从下周开始持续到今年7月份...

如何解绑已经合并的MSN账户和Skype账户?

如果您绑定的账户已经充值,建议您把产品消耗完毕后,再进行解绑。当您需要解绑合并的账户时,可登入Skype点卡账户自助操作。输入Skype或MSN账号、密码登录账户:登录后,可在页面左下角选择语言"中文...

微博账号已显示所属MCN机构,成为目前第二个上线该功能的平台

7月25日,多位网友发现,部分微博大V的个人主页已经显示其所属的MCN机构名称,微博也成为目前第二个上线该功能的平台。【来源:中新经纬】声明:此文版权归原作者所有,若有来源错误或者侵犯您的合法权益,您...

取消回复欢迎 发表评论: