当前位置: 首页 > news >正文

【大模型实战篇】大模型分词算法BPE(Byte-Pair Encoding tokenization)及代码示例

        词元化是针对自然语言处理任务的数据预处理中一个重要步骤,目的是将原始文本切分成模型可以识别和处理的词元序列。在大模型训练任务中,就是作为大模型的输入。传统的自然语言处理方法,如基于条件随机场的序列标注,主要采用基于词汇的分词方式,这与我们人类的语言认知更为契合。但是,这种分词方法在中文等语言中,可能会导致对同一输入产生不同的分词结果,从而生成包含大量低频词的庞大词表,并可能出现未登录词(OOV)的问题。因此,一些语言模型开始使用字符作为最小单位进行分词,例如,ELMo 使用了 CNN 词编码器。近年来,子词分词器在基于 Transformer 的语言模型中得到了广泛应用,常见的方法包括 BPE 分词、WordPiece 分词和 Unigram 分词,接下来我们将参考huggingface的材料【1】来分析这几类分词器,并展示每种模型使用的标记器类型的示例。本文首先对BPE进行分享。

1. 原理讲解

        字节对编码(BPE, Byte-Pair Encoding tokenization)【2】最初是作为一种文本压缩算法开发的,随后被OpenAI用于预训练GPT模型时的分词。许多Transformer模型都使用了该算法,包括GPT、GPT-2、RoBERTa、BART和DeBERTa。BPE 算法从一组基本符号(如字母和边界字符)开始,迭代地寻找语料库中的两个相邻词元,并将它们替换为新的词元,这一过程被称为合并。合并的选择标准是计算两个连续词元的共现频率,也就是每次迭代中,最频繁出现的一对词元会被选择与合并。合并过程将一直持续达到预定义的词表大小【3】。
        BPE训练开始于计算语料库中使用的唯一词汇集(在完成标准化和预分词步骤后),然后通过获取写这些词所用的所有符号来构建词汇表。举个非常简单的例子,假设我们的语料库使用了以下五个词:

"hug", "pug", "pun", "bun", "hugs"

        基础词汇表将是["b", "g", "h", "n", "p", "s", "u"]。在实际情况下,该基础词汇表至少会包含所有ASCII字符,并可能包括一些Unicode字符。如果一个例子使用了在训练语料库中不存在的字符,该字符将被转换为未知标记。许多NLP模型在分析包含表情符号的内容时表现不佳的原因可能这个就是其中一种。

        GPT-2和RoBERTa的分词器(相似性较高)有一种方法来处理这个问题:它们将词视为由字节组成,而不是Unicode字符。这样基础词汇表的大小就很小(256),但字符仍会被包含在内,而不会被转换为未知标记。这个技巧称为字节级BPE。 字节级别的 BPE(Byte-level BPE, B-BPE)是 BPE 算法的一种扩展,它将字节视为合并操作的基本符号,从而实现更精细的分割,并解决了未登录词的问题。代表性语言模型如 GPT-2、BART 和 LLaMA 都采用了这种分词方法。具体而言,如果将所有 Unicode 字符视为基本字符,基本词表的规模会非常庞大(例如,每个汉字都作为一个基本字符)。而使用字节作为基本字符,可以将词汇表的大小限制在 256,同时确保所有基本字符都包含在内。以 GPT-2 为例,其词表大小为 50,257,包含 256 个字节的基本词元、一个特殊的文末词元,以及通过 50,000 次合并学习到的词元。通过一些处理标点符号的附加规则,GPT-2 的分词器能够有效进行分词,而无需使用 “<UNK>” 符号。   

        在获得这个基础词汇表后,通过学习合并规则(将现有词汇表中的两个元素合并为一个新元素的规则)添加新标记,直到达到所需的词汇大小。因此,最开始这些合并将创建两个字符的标记,随着训练的进行,合并会生成更长的子词。在分词器训练的任何步骤中,BPE算法都会寻找最常见的现有标记对。最常见的那一对将被合并,然后重复下一步。

        回到之前的例子,假设这些词的频率如下:

("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)

        这意味着“hug”在语料库中出现了10次,“pug”5次,“pun”12次,“bun”4次,“hugs”5次。我们通过将每个词拆分为字符(构成我们初始词汇表的字符)开始训练,这样我们就可以将每个词视为标记的列表:

("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)

        然后查看标记对。标记对("h", "u")出现在“hug”和“hugs”中,因此在语料库中总共出现15次。但这不是最常见的标记对:最常见的对是("u", "g"),出现在“hug”、“pug”和“hugs”中,在词汇表中总共出现20次。因此,分词器学到的第一个合并规则是("u", "g") -> "ug",这意味着“ug”将被添加到词汇表中,并且在语料库中的所有词中都应合并该对。在这个阶段结束时,词汇表和语料库如下所示:

词汇表: ["b", "g", "h", "n", "p", "s", "u", "ug"]
语料库: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)

        现在有一些结果是超过两个字符的标记对:例如,标记对("h", "ug")(在语料库中出现15次)。然而,此时最常见的对是("u", "n"),在语料库中出现16次,因此第二个学习的合并规则是("u", "n") -> "un"。将其添加到词汇表并合并所有现有的出现结果如下:

词汇表: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
语料库: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)

        现在最常见的对是("h", "ug"),所以我们学习合并规则("h", "ug") -> "hug",这给我们带来了第一个三字符的标记。合并后,语料库如下:

词汇表: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
语料库: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)

        然后继续这样处理直到达到所需的词汇大小。

        分词紧密跟随训练过程,新输入的分词通过以下步骤完成:

  1. 标准化
  2. 预分词
  3. 将词拆分为单个字符
  4. 在这些拆分上按顺序应用学习到的合并规则

        以刚才使用的例子为例,三条学习到的合并规则为:

        ("u", "g") -> "ug"
        ("u", "n") -> "un"
        ("h", "ug") -> "hug"

        单词“bug”将被分词为["b", "ug"]。然而,“mug”将被分词为["[UNK]", "ug"],因为字母“m”不在基础词汇表中。同样,“thug”将被分词为["[UNK]", "hug"]:字母“t”不在基础词汇表中,应用合并规则后首先会合并“u”和“g”,然后合并“h”和“ug”。

2. BPE代码实现

2.1 创建jupyter实验环境

        为了快速方便验证代码,因此我们在服务器上安装了jupyter,来进行编程演示。这里记录下安装和部署的方式。

        首先服务器上下载安装jupyter:

pip install jupyter

        设置登录密码

jupyter notebook password

        启动jupyter

jupyter notebook --no-browser --ip=0.0.0.0 --allow-root

        访问jupyter-lab

一般域名就是:https://你的服务器ip地址:8888/lab

2.2 代码实现

示例语料库如下

corpus = ["This is the Hugging Face Course.","This chapter is about tokenization.","This section shows several tokenizer algorithms.","Hopefully, you will be able to understand how they are trained and generate tokens.",
]

        我们需要将该语料库预分词为单词。这里将使用gpt2分词器进行预分词,国内的话,为了方便,我们依然采用model scope的模型库来操作, 下载gpt2模型到本地并加载,下载大概要花个几分钟的样子:

import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os
from transformers import AutoTokenizermodel_dir = snapshot_download('AI-ModelScope/gpt2', cache_dir='/root/autodl-tmp', revision='master')
mode_name_or_path = '/root/autodl-tmp/AI-ModelScope/gpt2'
tokenizer = AutoTokenizer.from_pretrained(mode_name_or_path, trust_remote_code=True)

        然后,在预分词的同时,我们计算每个单词在语料库中的频率,并且计算基础词汇表,由语料库中使用的所有字符组成,将模型使用的特殊标记添加到词汇表的开头。对于GPT-2,唯一的特殊标记是"<|endoftext|>"。另外说明下:前面带有“Ġ”符号的词(例如“Ġis”、“Ġthe”)通常是在使用字节级别的BPE(Byte-level BPE)分词时生成的。这个符号的作用是标记一个单词是以空格开头的,即这个单词前面有一个空格。这种标记方式在处理文本时有助于保持词与词之间的分隔,使模型能够更好地理解上下文。

from collections import defaultdictword_freqs = defaultdict(int)for text in corpus:words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)new_words = [word for word, offset in words_with_offsets]for word in new_words:word_freqs[word] += 1print(word_freqs)
alphabet = []for word in word_freqs.keys():for letter in word:if letter not in alphabet:alphabet.append(letter)
alphabet.sort()print(alphabet)

        将每个单词拆分为单个字符,以便开始训练。计算每个标记对的频率。找到出现次数最多的词对。

splits = {word: [c for c in word] for word in word_freqs.keys()}
def compute_pair_freqs(splits):pair_freqs = defaultdict(int)for word, freq in word_freqs.items():split = splits[word]if len(split) == 1:continuefor i in range(len(split) - 1):pair = (split[i], split[i + 1])pair_freqs[pair] += freqreturn pair_freqspair_freqs = compute_pair_freqs(splits)for i, key in enumerate(pair_freqs.keys()):print(f"{key}: {pair_freqs[key]}")if i >= 5:break
best_pair = ""
max_freq = Nonefor pair, freq in pair_freqs.items():if max_freq is None or max_freq < freq:best_pair = pairmax_freq = freqprint(best_pair, max_freq)

        第一个合并学习的是 ('Ġ', 't') -> 'Ġt',我们将 'Ġt' 添加到词汇表中。接下来,在分割字典中应用这个合并。 

merges = {("Ġ", "t"): "Ġt"}
vocab.append("Ġt")
def merge_pair(a, b, splits):for word in word_freqs:split = splits[word]if len(split) == 1:continuei = 0while i < len(split) - 1:if split[i] == a and split[i + 1] == b:split = split[:i] + [a + b] + split[i + 2 :]else:i += 1splits[word] = splitreturn splits
splits = merge_pair("Ġ", "t", splits)
print(splits["Ġtrained"])

        循环,直到学习到我们想要的所有合并。设置目标词汇表大小为 50。

vocab_size = 50while len(vocab) < vocab_size:pair_freqs = compute_pair_freqs(splits)best_pair = ""max_freq = Nonefor pair, freq in pair_freqs.items():if max_freq is None or max_freq < freq:best_pair = pairmax_freq = freqsplits = merge_pair(*best_pair, splits)merges[best_pair] = best_pair[0] + best_pair[1]vocab.append(best_pair[0] + best_pair[1])print(merges)

        接下来为了对新文本进行分词,先进行预分词,再拆分,然后应用所有学习到的合并规则:

def tokenize(text):pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)pre_tokenized_text = [word for word, offset in pre_tokenize_result]splits = [[l for l in word] for word in pre_tokenized_text]for pair, merge in merges.items():for idx, split in enumerate(splits):i = 0while i < len(split) - 1:if split[i] == pair[0] and split[i + 1] == pair[1]:split = split[:i] + [merge] + split[i + 2 :]else:i += 1splits[idx] = splitreturn sum(splits, [])

3. 参考材料

【1】Summary of the tokenizers

【2】Byte-Pair Encoding tokenization

【3】RUC AI BOX 大预言模型

相关文章:

【大模型实战篇】大模型分词算法BPE(Byte-Pair Encoding tokenization)及代码示例

词元化是针对自然语言处理任务的数据预处理中一个重要步骤&#xff0c;目的是将原始文本切分成模型可以识别和处理的词元序列。在大模型训练任务中&#xff0c;就是作为大模型的输入。传统的自然语言处理方法&#xff0c;如基于条件随机场的序列标注&#xff0c;主要采用基于词…...

低功耗4G模组LCD应用示例超全教程!不会的小伙伴看这篇就够了!

希望大家通过本文的介绍&#xff0c;学会LCD显示屏与Air780E开发板结合使用的方法。利用LCD显示屏&#xff0c;你可以为你的项目增加丰富的显示内容&#xff0c;提升用户体验。记住&#xff0c;实践出真知&#xff0c;赶快动手尝试吧&#xff01;相信这篇教程对你有所帮助~ 本文…...

Java while语句练习 C语言的函数递归

1. /* public static void main(String[] args) {int[] arr {25, 24, 12, 98, 36, 45};int max arr[0];//不能写0for (int i 1; i < arr.length; i) {if (arr[i] > max) {max arr[i];}}System.out.println(max);}*//*public static void main(String[] args) {doubl…...

illustrator免费插件 截图识别文字插件 textOCR

随手可得的截图识别文字插件 textOCR&#xff0c;识别出来的文字可直接输入到illustrator的当前文档中&#xff1a; 执行条件 1、需截图软件支持&#xff0c;推荐笔记截图工具 2、截好图片直接拖入面板即可完成识别 ****后期可完成实现在illustrator选择图片对象完成文字识别。…...

提升数据管理效率:ETLCloud与达梦数据库的完美集成

达梦数据库的核心优势在于其强大的数据处理能力和高可用性设计。它采用先进的并行处理技术&#xff0c;支持大规模的数据操作&#xff0c;同时具备出色的事务处理能力和数据安全保障。此外&#xff0c;达梦数据库还提供了丰富的功能模块&#xff0c;如数据备份、恢复、监控等&a…...

头歌——人工智能(搜索策略)

文章目录 第1关&#xff1a;搜索策略第2关&#xff1a;盲目搜索第3关&#xff1a;启发式搜索 - 扫地机器人最短路径搜索第4关&#xff1a;搜索算法应用 - 四皇后问题 第1关&#xff1a;搜索策略 什么是搜索技术 人类的思维过程可以看作是一个搜索过程。从小学到现在&#xff0…...

gorm.io/sharding改造:赋能单表,灵活支持多分表策略(下)

背景 分表组件改造的背景&#xff0c;我在这篇文章《gorm.io/sharding改造&#xff1a;赋能单表&#xff0c;灵活支持多分表策略&#xff08;上&#xff09;》中已经做了详细的介绍——这个组件不支持单表多个分表策略&#xff0c;为了突破这个限制做的改造。 在上一篇文章中&…...

域渗透AD渗透攻击利用 MS14-068漏洞利用过程 以及域渗透中票据是什么 如何利用

目录 wmi协议远程执行 ptt票据传递使用 命令传递方式 明文口令传递 hash口令传递 票据分类 kerberos认证的简述流程 PTT攻击的过程 MS14-068 漏洞 执行过程 wmi协议远程执行 wmi服务是比smb服务高级一些的&#xff0c;在日志中是找不到痕迹的&#xff0c;但是这个主…...

C++进阶-->继承(inheritance)

1. 继承的概念及定义 1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要手段&#xff0c;他允许我们在保证原有类的特性基础上还进行扩展&#xff0c;通过继承产生的类叫做派生类&#xff08;子类&#xff09;&#xff0c;被继承的类叫做基类&a…...

可视化项目 gis 资源复用思路(cesium)

文章目录 可视化项目 gis 资源复用思路底图、模型替换思路具体操作 可视化项目 gis 资源复用思路 背景&#xff1a; A项目的底图、模型 是现在在做的 B项目所需要的&#xff0c;现在要把 B项目的底图之类的替换成 A系统的 底图、模型替换思路 观察可访问系统的 gis 相关网络请…...

SQL实战测试

SQL实战测试 &#xff08;请写下 SQL 查询语句&#xff0c;不需要展示结果&#xff09; 表 a DateSalesCustomerRevenue2019/1/1张三A102019/1/5张三A18 1. **用一条 ** SQL 语句写出每个月&#xff0c;每个销售有多少个客户收入多少 输出结果表头为“月”&#xff0c;“销…...

Java 基础教学:基础语法-变量与常量

变量 变量是程序设计中的基本概念&#xff0c;它用于存储信息&#xff0c;这些信息可以在程序执行过程中被读取和修改。 变量的声明 在Java中&#xff0c;声明变量需要指定变量的数据类型以及变量的名称。数据类型定义了变量可以存储的数据种类&#xff08;例如整数、浮点数…...

vue3使用element-plus手动更改url后is-active和菜单的focus颜色不同步问题

在实习&#xff0c;给了个需求做个新的ui界面&#xff0c;遇到了一个非常烦人的问题 如下&#xff0c;手动修改url时&#xff0c;is-active和focus颜色不同步 虽然可以直接让el-menu-item:focus为白色能解决这个问题&#xff0c;但是我就是想要有颜色哈哈哈&#xff0c;有些执…...

每天五分钟深度学习框架pytorch:从底层实现一元线性回归模型

本文重点 本节课程我们继续搭建一元线性回归模型,不同的是这里我们不使用pytorch框架已经封装好的一些东西,我们做这个目的是为了更加清楚的看到pytorch搭建模型的本质,为了更好的理解,当然实际中我们还是使用pytorch封装好的一些东西,不要重复造轮子。 模型搭建 #定义…...

编辑器加载与AB包加载组合

解释&#xff1a; 这个 ABResMgr 类是一个资源加载管理器&#xff0c;它用于整合 AB包&#xff08;Asset Bundle&#xff09;资源加载和 编辑器模式资源加载。通过这个管理器&#xff0c;可以根据开发环境选择资源加载方式&#xff0c;既支持 运行时使用Asset Bundle加载&…...

【c++】vector中的back()函数

nums.back() 是 C 中 std::vector 类的一个成员函数&#xff0c;用于获取数组&#xff08;向量&#xff09;中的最后一个元素。以下是一些关于 nums.back() 的详细解释和示例使用&#xff1a; 1. 功能 nums.back() 返回数组 nums 中的最后一个元素。如果数组为空&#xff0c;…...

[分享] SQL在线编辑工具(好用)

在线SQL编写工具&#xff08;无广告&#xff09; - 在线SQL编写工具 - Web SQL - SQL在线编辑格式化 - WGCLOUD...

element-ui隐藏表单必填星号

// 必填星号在前显示 去掉 .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before { content: !important; margin-right: 0px!important; } // 必填星号在结尾显示 .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__labe…...

自动驾驶系列—激光雷达点云数据在自动驾驶场景中的深度应用

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…...

C#删除dataGridView 选中行

关键在于&#xff1a;从最后一行开始删除。 从前往后删只能删除其中一半&#xff0c;我理解是再remove行的时候dataGridView内部行序列发生了变化&#xff0c;包含在选中行中的特定行会被忽略&#xff0c;从后往前删就可避免这个问题&#xff0c;最后一行的行号影响不到前面的…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...