Home

Awesome

语言模型中文认知能力分析

跳到 所有模型的分析图

项目介绍

本项目的初衷是为了调查各个语言模型的中文认知能力,目前包括中文识字率分析输入端词向量分布分析输出端词向量分析。大规模语言模型非常复杂,难以一言以蔽之,只有从多个角度观察,才会得到更全面的认知,本分析是从一个角度一窥语言模型内部机理,并希望可以成为后续模型评估分析的参考。

中文识字率分析

为了分析模型的中文识字率,我们使用三个常用的字符集,《通用规范汉字表》、《常用國字標準字體表》以及《Unicode中日韩统一表意文字》,总共21267个汉字。

字符集

为了能够直观的反应模型对于不同字符集的识字率,我们使用不同的颜色来标识不同字符集的汉字。

识字率分析图颜色对应关系
#B04759 深红色:《通用规范汉字表》一级汉字<br>#E76161 红 色:《通用规范汉字表》二级汉字<br>#F99B7D 橘黄色:《通用规范汉字表》三级汉字<br>#146C94 深蓝色:《常用國字標準字體表》甲表汉字<br>#19A7CE 浅蓝色:《常用國字標準字體表》乙表汉字<br>#E893CF 粉 色:《Unicode中日韩统一表意文字》区段汉字

识字判定

如何判断一个模型是否认知某个汉字,要分几个层面来具体分析。

首先,我们要了解模型的 Tokenizer 是如何对汉字进行编码的。

我们使用的模型,从汉字角度来看,Tokenizer 主要有两种编码方式,一种是将一个汉字直接编码成一个 token,如 WordPiece;另一种是如 BBPE(Byte-level BPE)的方式,有可能将汉字按照它的 UTF-8 字节码,拆分成多个 token 的。

对于 WordPiece 这类按照字符编码,或者说按照 Unicode 码位编码的情况,如果其 Tokenizer 中的词表(vocab) 中没有对应的汉字,那么这个模型就是完全不认识这个汉字。对应的汉字将被编码为 unk_token_id,即词表中的 [UNK]。这类模型对于汉字的认知程度,可以用词表中汉字的数量来衡量,存在于词表则计数为 1,不存在则计数为 0

而对于可能将一个汉字拆分为多个 token 的 BBPE 这类编码方式,则需要更复杂一些的思考和应对。这类 Tokenizer 如果在训练的过程中,遇到过一些汉字,则可能会在词表中直接包含该汉字,从而产生单一的 token 与之对应;但如果在训练的过程中,没有遇到过某个汉字,或较少的遇到某些汉字,则该汉字将被拆分为多个 token。因此这类模型中,汉字对应的 token 数量,从1个到4个不等。

英文模型中,我们并不是在词表中存储26个字母,而是存储大量能够聚合的单词或子词,是因为一个单词被拆成单一字母,对语义的理解是有伤害的,这种伤害可能会消耗更多层的 Transformer 来弥补。同样的情况,一个汉字词语,或者一个汉字,被拆分成无语义的 UTF-8 字节码,这对于中文语义的理解也必然是有伤害的。因此,在识字判定中,我们将不计入一个汉字对应多个 token 的情况,而只对一个汉字对应单一 token 的情况计数为 1

但是,由于理论上 BBPE 这类 Tokenizer 是可以识别任意字符的,因此我们会在图中,用颜色来表示这种差别。对于词表中存在的汉字,我们用最深的颜色表示;对于一个汉字拆分成多个 token 的情况,拆分的越多,颜色越浅,用以表示其语义的损失程度。

在进行判断前,会先行去除前缀后缀的特殊token。

词向量分布分析

单纯的观察词表中汉字的数量,以及汉字对应的 token 数量,是不足以充分反应模型对于汉字的认知程度的。

模型对于一个汉字、或者一个汉字词汇的理解,并不因为这些词存在于词表,就天然的理解了其语义。模型需要对词表中的词所对应的词向量进行训练,让词向量具有语义关系。而在空间上的表现,则为相近语义的词向量,在空间上具有临近的关系。

所以,有些模型可能单纯的将很多数汉字加入词表(vocab),但是这些词的向量却没有被训练出语义,依旧保持着初始的随机状态,这种情况下,模型对于这些汉字的认知程度,也是较差的。

为了反应这种认知上的差异,我们对词表中的词,在向量空间的分布进行了分析。我们将词表中的词,计算出词向量,每个词向量就是高维空间中的一个点,然后,我们使用 t-SNE 对高维空间非线性降维,降维的同时尽量保持原有的空间关系,最终将高维空间中的点,映射到二维平面上,形成一个分布图。这个图中,每个点(放大后是文字)代表词表中的一个词,点与点之间的距离远近,代表了词向量之间的距离远近,即语义上的相关程度。我们将这个图,称为「词向量分布图」。

字词的分类

模型词表中的词并不一定都是中文字词,还有很多英文以及多语言字词,以及一些数字、符号。为了可以更好的观察不同的词向量分布,我们将词表中的词进行了分类,区分显示「中文」、「英文」、「日文」、「韩文」、「数字」和「其他」,并进行分色显示。

语言

由于本分析的重点在于中文认知能力的分析,因此我们将汉字进一步分为了「常用字」和「生僻字」两类。其中,常用字为属于《通用规范汉字表》一级和二级汉字,以及《常用國字標準字體表》甲表新增汉字;生僻字则为属于《通用规范汉字表》三级汉字,以及《常用國字標準字體表》乙表新增汉字和《Unicode中日韩统一表意文字》中的新增汉字。

「日文」是指日语中的平假名片假名,「韩文」是指韩语中的谚文。「英文」则包括了除中日韩文之外,包括英文在内的各种拉丁语言文字等。只包含 0-9 的词视为「数字」,其余的词视为「其他」。

词向量分布图颜色对应关系
#D50000 汉字(常用字)<br>#7B1FA2 汉字(生僻字)<br>#827717 日文<br>#FFA000 韩文<br>#2E7D32 英文<br>#01579B 数字<br>#212121 其他<br>
后缀子词

除了文字类别外,词表中的词还有普通词和后缀的区别,如,「dragon」和「##fly」,「dragon」为普通词,「##fly」是一个后缀子词,可以跟「dragon」组合为「dragonfly」。

英文这种设计是为了避免词表太大,从而将词划分为子词,多个子词(subwords)组成一个词(word),通过组合方式降低词表词汇量,在同等词汇量的情况下,支持更多词汇。这就像现代汉语中的多字词,通过组合既有汉字,避免造字以描述新鲜事物。

但是这种切分方式不一定对中文也适用,中文每个汉字都可能成为所谓后缀子词,而不是简单的修饰后缀,因此可能会有和单字同等规模的相同字的后缀子词出现,从而反而浪费了词表。

为了观察独立的普通词和后缀词的差异,于是,我们将这两种类别的词区分了颜色,「普通词」为本色,「后缀」为更浅一些的颜色。

下面的两个模型为例,左边的是英文的 BERT 模型,其中浅绿色的部分点为英文后缀子词;右边的是中文的 BERT 模型,淡粉色的部分点为中文后缀子词。他们的分布特征和普通词截然不同。

英文的「普通词」和「后缀词」中文的「普通词」和「后缀词」

输入端词向量、输出端词向量

对于一个模型而言,每个词所对应的向量其实分为两种情况。一种是在输入时所获得的 Token Embedding;另一种是在最后模型输出时对最后一层 hidden_state 进行计算得到的 embedding。我们将前者称为「输入端词向量」,后者称为「输出端词向量」。

输入端词向量分布图输出端词向量分布图

前者为词或token进入模型时,经过 embedding 层所得到的最初语义表达;而后者,则是经过模型多层计算之后,最终理解的语义表达。

一般来说,输入端的语义会更加基础,而输出端的语义会更加丰富。从向量分布图来看,输入端将会呈现更小的局部结构,而输出端则会呈现更复杂的局部结构,以及更大规模的全局结构。通过对比输入端词向量和输出端词向量的分布图,我们可以看到模型对于中文认知的变化,以及各种同基座模型不同微调后的变化。从而我们可以对模型于中文认知差异上有个更加直观的认识。

分析观察

下面是挑选出来的一些比较有特点的模型,并非完整模型列表,完成列表请查看 模型分析列表

如果图片无法显示,请访问项目 Github 页面:https://github.com/twang2218/vocab-coverage

BERT 类模型

这里对比了比较典型的3个 BERT 类模型。

bert-base-cased

颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
Token输入端
汉字输出端

bert-base-cased 是原始的英文 BERT 模型,使用的是 WordPiece Tokenizer,只使用英文语料训练,因此词表中基本都是英文,只有少数汉字的「漏网之鱼」。这个模型基本不认识汉字,绝大多数汉字在输入模型时,会被替换为 [UNK],这样的模型,就算经过微调,也不会对中文有什么认知。

从词向量分布图上看,可以很清晰的看到,「数字」连成了线条,并且和英文文字之间保持了一定的距离,这就说明数字之间具有很强的相关的关系,而数字和文字之间有明显的语义区别。

此外,还可以观察到「普通词」和「后缀子词」可以很好的区分开来,并且如果进一步观察词与词的连结关系,会发现「普通词」之间的连结更复杂,而「后缀子词」之间的连结更简单和稀疏。这说明「普通词」比「后缀子词」更容易被训练出语义。

如果观察输出端词向量分布图,会看到词与词之间的连结关系更加复杂,除了几个词语之间的聚集外,还出现了更大规模、更复杂的聚集和连结关系,这应该说明模型在输出端对于词语的语义理解更加丰富

roberta-large

颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
Token输入端
汉字输出端

此处的 roberta-large 就是典型的使用 BBPE Tokenizer英文模型,其词表中几乎没有汉字,但由于字节拆分的特征,不管是否对理解力有损伤,理论上它是可以接受所有字符的。在它的中文覆盖率的图上,可以看出,基本所有汉字都被拆分为多个 token,甚至绝大多数都被拆分为了 3 个 token。这样的模型,对于中文的认知有限,不仅仅是因为它没有使用中文语料训练,就算使用中文语料训练,如果不扩增词表,可能也会伤害其性能。此外,这个模型的输入端词向量分布图和输出端词向量分布度,也呈现了由简单连结关系,向复杂连结关系发展的变化。

bert-base-multilingual-cased

颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
Token输入端
汉字输出端

bert-base-multilingual-cased 则是一个典型的使用了 WordPiece多语言模型,为了支持多国语言,它将词表扩充到了12万,并且使用了多国语言的语料进行训练。因此它支持汉字的常用字,包括简繁体,但是稍微不常用一些的字,它认知的就非常少了。

观察该模型的输入端词向量分布图和输出端词向量分布图,会发现他们皆呈现出同一个语言抱团的现象,最典型的是中文、日文、韩文是独立的三团。绿色的部分实际涵盖了多种语言,在这个模型里,绿色部分语言之间的分界并不明显,放大后是可以看到同一种语言有聚集现象。

此外,输入端词向量分布图中,中文的「后缀子词」呈现出随机分布的形态,团成一团,并且互相之间几乎等距,不像汉字「普通词」那样,有明显的三五个词连成一线的简单结构。这说明中文的后缀子词在输入端没有训练出语义。而如果对比英文后缀子词,则会发现他们有3-5个词的小幅聚集情况,这说明英文的后缀子词在输入端训练出了一定的语义。因此,可能如之前所说的一样,后缀子词的用法可能并不适合中文

观察输出端词向量分布图,会发现,中文后缀子词,开始具有简单的小堆聚集连结的倾向,说明经过了完整的网络后,后缀子词的语义确实有被学习到。但是,这个结构的复杂和清晰程度,远远不如英文后缀子词,观察浅绿色的那部分,可以看到英文中更清晰的聚集结构。这些都有可能说明,子词(subword)的用法,更适合英文,而不一定适合中文。

bert-base-chinese 模型及其衍生的模型对比

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>bert-base-chinese</b>Token输入端
汉字输出端
<b>hfl<br/>/<br/>chinese-macbert-base</b>Token输入端
汉字输出端
<b>shibing624<br/>/<br/>text2vec-base-chinese</b>Token输入端
汉字输出端
<b>moka-ai<br/>/<br/>m3e-base</b>Token输入端
汉字输出端
<b>junnyu<br/>/<br/>wobert_chinese_plus_base</b>Token输入端
汉字输出端

这几个模型都是具有继承关系的,均是在前一个模型的基础上,调整了训练方法,用了更多的语料进行了训练,其继承关系如下:

bert-base-chinese 模型及其衍生的模型继承关系

因此我们可以看到这几个模型的中文识字率的结果是完全一样的。

在输入端词向量分布图上,我们可以看到只有最原始的 bert-base-chinese 外形最不一样,其他三个都几乎一致。最明显的是「中文后缀子词」部分,hfl 及其之后的模型,明显更有一些语义被训练出来,而原始的 bert-base-chinese 则基本保持初始化的均匀分布。

这恐怕是因为其中最大的变动是 hfl 的两个模型,均使用了同样的 5.4B tokens 的语料进行了训练,远超原版的 0.4B tokens 的语料,而后续模型的微调则都没有再用这么大的数据集训练了,所以这应该是引起输入端词向量变化的最主要因素。

当我们看向输出端词向量,则会发现每个模型的分布都截然不同。除了 moka-ai/m3e-base 外的三个模型,「普通词」和「后缀子词」都是明确分开的;而 moka-ai/m3e-base 却出现了 「普通词」和「后缀子词」混合在一起 的情况,甚至进一步放大去看,会发现,同一个汉字的「普通词」和「后缀子词」基本重叠,也就是二者对齐了。

由此可见,微调对于输出端词向量的影响,远大于对输入端词向量的影响。那么或许可以通过观察输出端的词向量分布变化,来观察不同的训练语料对于模型的影响。

ERNIE 模型及其衍生的模型对比

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>nghuyong<br/>/<br/>ernie-3.0-base-zh</b>Token输入端
汉字输出端
<b>shibing624<br/>/<br/>text2vec-base-chinese-sentence</b>Token输入端
汉字输出端
<b>shibing624<br/>/<br/>text2vec-base-chinese-paraphrase</b>Token输入端
汉字输出端

这一组基座模型和微调模型的对比很有意思,后一组两个模型都是基于 nghuyong/ernie-3.0-base-zh 模型微调而来,但是输出端的词向量分布却有很大的差异。而且这两个模型之间的微调其实非常接近。paraphrase 模型用来训练的语料库其实是在 sentence 模型的语料库基础上增加了 6051 个句子段落对,这部分里有4k左右的长文本。目前不太清楚是否是因为 这 4k 的长文本,导致了输出端词向量分布的巨大差异。

paraphrase-multilingual-MiniLM-L12-v2 模型及其衍生的模型对比

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>sentence-transformers<br/>/<br/>paraphrase-multilingual-MiniLM-L12-v2</b>Token输入端
汉字输出端
<b>shibing624<br/>/<br/>text2vec-base-multilingual</b>Token输入端
汉字输出端

xlm-roberta-base 模型及其微调后的模型对比

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>xlm-roberta-base</b>Token输入端
汉字输出端
<b>sentence-transformers<br/>/<br/>paraphrase-multilingual-mpnet-base-v2</b>Token输入端
汉字输出端

LLaMA 及衍生模型

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>decapoda-research<br/>/<br/>llama-7b-hf</b>Token输入端
汉字输出端
<b>lmsys<br/>/<br/>vicuna-7b-v1.3</b>Token输入端
汉字输出端
<b>togethercomputer<br/>/<br/>RedPajama-INCITE-7B-Chat</b>Token输入端
汉字输出端
<b>openlm-research<br/>/<br/>open_llama_7b</b>Token输入端
汉字输出端
<b>shibing624<br/>/<br/>chinese-alpaca-plus-7b-hf</b>Token输入端
汉字输出端

英文大语言模型

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>decapoda-research<br/>/<br/>llama-7b-hf</b>Token输入端
汉字输出端
<b>mosaicml<br/>/<br/>mpt-7b-instruct</b>Token输入端
汉字输出端
<b>tiiuae<br/>/<br/>falcon-7b-instruct</b>Token输入端
汉字输出端
<b>nomic-ai<br/>/<br/>gpt4all-j</b>Token输入端
汉字输出端
<b>OpenAssistant<br/>/<br/>oasst-sft-4-pythia-12b-epoch-3.5</b>Token输入端
汉字输出端

中文大语言模型

模型颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
<b>THUDM<br/>/<br/>chatglm-6b</b>Token输入端
汉字输出端
<b>THUDM<br/>/<br/>chatglm2-6b</b>Token输入端
汉字输出端
<b>fnlp<br/>/<br/>moss-moon-003-sft</b>Token输入端
汉字输出端
<b>baichuan-inc<br/>/<br/>baichuan-7B</b>Token输入端
汉字输出端

OpenAI 提供的模型

颗粒度完整性分析图位置子词向量分布汉字向量分布词语向量分布句子向量分布段落向量分布
Token输入端
汉字输出端

其他模型

请参见 模型分析列表

命令行工具 vocab-coverage 使用指南

vocab-coverage 是一个命令行工具,用于分析模型的汉字识字率。

安装

pip install vocab-coverage

由于图中有汉字,因此需要中文字体,这里我使用了 Noto Sans CJK 字体用于中文,以及 Anonymous Pro字体,建议安装该字体。

Linux

如 Ubuntu,可以通过以下命令安装:

sudo apt install fonts-noto-cjk fonts-anonymous-pro

Mac

如 MacOS,可以通过以下命令安装:

brew install font-noto-sans-cjk font-noto-serif-cjk font-anonymous-pro

Windows

如 Windows,可以通过以下命令安装:

choco install noto anonymouspro

使用

vocab-coverage 它有三个子命令:charsetcoverageembedding

charset 子命令

charset 子命令用于生成用以统计识字率的字表文件。

$ vocab-coverage charset --help
usage: vocab-coverage charset [-h] [--charset_file CHARSET_FILE]

options:
  -h, --help            show this help message and exit
  --charset_file CHARSET_FILE
                        用以统计识字率的字表文件(默认:charset.json)

coverage 子命令

coverage 子命令用于分析模型的汉字识字率。

$ vocab-coverage coverage --help
usage: vocab-coverage coverage [-h] [--model_name MODEL_NAME] [--charset_file CHARSET_FILE] [--output_dir OUTPUT_DIR] [--debug]

options:
  -h, --help            show this help message and exit
  --model_name MODEL_NAME
                        模型在 HuggingFace Hub 上的名称(默认为 shibing624/text2vec-base-chinese)
  --charset_file CHARSET_FILE
                        用以统计识字率的字表文件(默认为内置字符集文件)
  --output_dir OUTPUT_DIR
                        生成的图像文件的输出目录(默认为 images)
  --debug               是否打印调试信息

示例

$ vocab-coverage coverage --model_name=THUDM/chatglm-6b
检查模型 THUDM/chatglm-6b 的字表
字表《通用规范汉字表》一级汉字:3499/3500 (99.97%)
字表《通用规范汉字表》二级汉字:1724/3000 (57.47%)
字表《通用规范汉字表》三级汉字:48/1605 (2.99%)
字表《常用國字標準字體表》甲表(增):185/1749 (10.58%)
字表《常用國字標準字體表》乙表(增):14/4503 (0.31%)
字表《Unicode中日韩统一表意文字》(增):115/6910 (1.66%)

除了上述输出外,还会在 images 目录下生成一个图像文件,images/coverage/THUDM_chatglm-6b.coverage.png,为可视化的分析结果。

embedding 子命令

embedding 子命令用于分析模型词向量在空间中的分布情况。

usage: main.py embedding [-h] [--model_name MODEL_NAME] [--charset_file CHARSET_FILE] [--output_dir OUTPUT_DIR] [--is_detail] [--debug]

options:
  -h, --help            show this help message and exit
  --model_name MODEL_NAME
                        模型在 HuggingFace Hub 上的名称(默认为 shibing624/text2vec-base-chinese)
  --charset_file CHARSET_FILE
                        用以统计识字率的字表文件(默认为内置字符集文件)
  --output_dir OUTPUT_DIR
                        生成的图像文件的输出目录(默认为 images)
  --is_detail           是否对汉字进行详细分类(默认为 False)
  --debug               是否打印调试信息(默认为 False)

示例

$ vocab-coverage embedding --model_name=THUDM/chatglm-6b --debug
对模型 THUDM/chatglm-6b 的 embedding 进行可视化...
Loading checkpoint shards: 100%|█████████████████████████████████████████████████████████████| 8/8 [00:14<00:00,  1.79s/it]
reducing embeddings (130528, 4096) to 2D...
[t-SNE] Computing 91 nearest neighbors...
[t-SNE] Indexed 130528 samples in 0.223s...
...
[t-SNE] Computed conditional probabilities for sample 130528 / 130528
[t-SNE] Mean sigma: 0.251563
[t-SNE] Computed conditional probabilities in 1.189s
[t-SNE] Iteration 50: error = 111.8378067, gradient norm = 0.0000213 (50 iterations in 8.560s)
[t-SNE] Iteration 100: error = 111.7450027, gradient norm = 0.0002728 (50 iterations in 13.605s)
[t-SNE] Iteration 150: error = 111.7283707, gradient norm = 0.0000039 (50 iterations in 7.279s)
[t-SNE] Iteration 200: error = 111.7283707, gradient norm = 0.0000039 (50 iterations in 7.123s)
...
[t-SNE] Iteration 950: error = 3.7165585, gradient norm = 0.0030942 (50 iterations in 7.229s)
[t-SNE] Iteration 1000: error = 3.6922786, gradient norm = 0.0029739 (50 iterations in 7.451s)
[t-SNE] KL divergence after 1000 iterations: 3.692279
draw embeddings (130528, 2)...
...
draw embedding point: 130528
font size: 25, font: ('Noto Sans CJK JP', 'Regular')
...
save to images/embeddings/THUDM-chatglm-6b.embedding.jpg...

Star History

Star History Chart