常识| 大模型收费计量单位之Token概念

大模型背后有大量的计算资源和开发人员支出, 厂商不得不思考商业化考量。现在通过API方式调用收费方式基本是按照token为基本单位进行收费。

image.png

Token是用来计量大模型输入、输出的基本单位,也可以直观的理解为“字”或“词”。但是目前并没有统一计量标准,各家大模型平台根据自己的偏好“随意”定义。如腾讯1token≈1.8个汉字,通义千问、千帆大模型等1token=1个汉字,对于英文文本来说,1个token通常对应3至4个字母, 不同的模型对相同的输入分词, 分词结果是不一样的。

image.png
01 什么是Token?

对于普通用户很难理解Token这个概念, 这个概念是隐藏在模型内部的, 对于普通使用者来说,这种计价方式无疑是致命的, 所以对于大部分普通使用者,还是采用包月方式偏多, Token计价方式针对的是开发者,希望通过API方式进行调用,封装自己的应用。即使很多开发者如果没有进行自然语言相关开发,也很难理解Token是什么?不懂这个概念,就贸然使用这种方式,有点“我为鱼肉,人为刀殂“的感觉。今天我就借这个机会和大家一起系统性了解Token这个词语在LLM领域的含义。

由于神经网络模型跟我们人类还是不一样,不能直接处理文本,因此我们需要先将文本转换为数字,这个过程被称为编码 (Encoding),其包含两个步骤:

(1)使用分词器 (tokenizer) 将文本按词、子词、字符切分为 tokens;

(2)将所有的 token 映射到对应的 token ID。

其中第一步利用分词器分成Tokens,就是我们今天的主角,也就是Token是编码过程中的一个产物。不同的分词器,拆分文本的规则也是不一样的,所以很多大模型的Token的标准也不一样。

02 分词策略

常见的分词策略,有按词切分 (Word-based)、按字符切分 (Character-based)、按子词切分 (Subword)三种方法。

3.1 按词拆分

image.png

例如直接利用 Python 的 split() 函数按空格进行分词:

tokenized_text = "let's do tokenization".split()
print(tokenized_text)

这种策略的问题是会将文本中所有出现过的独立片段都作为不同的 token,从而产生巨大的词表。而实际上很多词是相关的,例如 “dog” 和 “dogs”、“run” 和 “running”,如果给它们赋予不同的编号就无法表示出这种关联性。而且适用的语种不是很多,比如中文,词与词之间并没有空格或者特定的字符隔开。

3.2 按照字符隔开

image.png

这种策略把文本切分为字符而不是词语,这样就只会产生一个非常小的词表,并且很少会出现词表外的 tokens。

但是从直觉上来看,字符本身并没有太大的意义,因此将文本切分为字符之后就会变得不容易理解。这也与语言有关,例如中文字符会比拉丁字符包含更多的信息,相对影响较小。此外,这种方式切分出的 tokens 会很多,例如一个由 10 个字符组成的单词就会输出 10 个 tokens,而实际上它们只是一个词。

因此现在广泛采用的是一种同时结合了按词切分和按字符切分的方式—按子词切分 (Subword tokenization)。

3.3 按子词切分

高频词直接保留,低频词被切分为更有意义的子词。例如 “tokenization” 是一个低频词,可以切分为 “token” 和 “ization”,这两个子词不仅出现频率更高,而且词义也得以保留。下图展示了对 “Let’s do tokenization!“ 按子词切分的结果:

image.png

可以看到,“tokenization” 被切分为了 “token” 和 “ization”,不仅保留了语义,而且只用两个 token 就表示了一个长词。这种策略只用一个较小的词表就可以覆盖绝大部分文本,基本不会产生 unknown token。尤其对于土耳其语等黏着语,几乎所有的复杂长词都可以通过串联多个子词构成。这种方式类似抽取词语前缀。

03 分词实践

Transformer原理》文章中介绍了现在transformer库为我们提供了自然语言相关的工具(当然不止自然语言相关)。transformer使用 Tokenizer.from_pretrained()Tokenizer.save_pretrained() 函数。例如加载并保存 BERT 模型的分词器:

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
tokenizer.save_pretrained("./models/bert-base-cased/")

同样地,在大部分情况下我们都应该使用 AutoTokenizer 来加载分词器:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
tokenizer.save_pretrained("./models/bert-base-cased/")

调用 Tokenizer.save_pretrained() 函数会在保存路径下创建三个文件:

  • special_tokens_map.json:映射文件,里面包含 unknown token 等特殊字符的映射关系;

  • tokenizer_config.json:分词器配置文件,存储构建分词器需要的参数;

  • vocab.txt:词表,一行一个 token,行号就是对应的 token ID(从 0 开始)。

04 编码文本

前面说过,文本编码 (Encoding) 过程包含两个步骤:

(1)分词**:**使用分词器按某种策略将文本切分为 tokens;

(2)映射**:**将 tokens 转化为对应的 token IDs。

下面我们首先使用 BERT 分词器来对文本进行分词:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)
print(tokens)

然后,我们通过将切分出的 tokens 转换为对应的 token IDs:

ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)

以上两个步骤可以通过更便捷的方式进行:

# 将字符串转换为id序列,又称之为编码
ids = tokenizer.encode(sen, add_special_tokens=True)
print(ids)

05 快速分词器

前面我们已经介绍过如何使用分词器将文本编码为 token IDs。

实际上,Hugging Face 共提供了两种分分词器:

(1)慢速分词器**:**Transformers 库自带,使用 Python 编写(前面演示用的);

(2)快速分词器**:**Tokenizers 库提供,使用 Rust 编写。

快速分词器只有在并行处理大量文本时才能发挥出速度优势,在处理单个句子时甚至可能慢于慢速分词器。

我们一直推荐使用的 AutoTokenizer 类除了能根据 checkpoint 自动加载对应分词器以外,默认就会选择快速分词器,因此在大部分情况下都应该使用 AutoTokenizer 类来加载分词器。

推荐阅读:

研究篇| 一款深入浅出的微调框架

应用篇| 深入浅出LLM应用之RAG

【大模型应用篇7】新一代程序员需要掌握的智能体脚手架是?

【大模型应用篇3】LLM时代下的智能体
在这里插入图片描述

  • 32
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿阿三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值