我们知道,在深度学习中,神经网络模型只能处理数值张量,所以在处理文本时,不能直接将原始文本作为输入,我们需要一种将文本表示为数值张量的方法,而这转换的过程,就是文本向量化。
《Python深度学习》笔记
文本向量化
文本向量化有许多种形式,但都遵循相同的流程:文本规范化、词元化、建立词汇索引。
文本规范化
文本规范化旨在消除你不希望模型处理的那些编码差异,从而把文本统一格式。通常的做法有:把所有字母转小写、全角符号转半角符号、去掉多余空格和特殊字符等。
规范化后,模型将需要更少的训练数据,并且具有更好的泛化效果。比如,在没有规范化前,模型会把”Love”、”love”等当成 3 个不同的词,每个词分到的训练样本会变少,训练的效果也会产生差异。
1 | """ |
词元化
文本完成规范化后,需要将文本拆分成能够向量化的单元(token),这一步也叫词元化。词元化一般有以下 3 种方式:
**字符级:**每个字符都是一个词元,词表极小(中文仅几千个汉字),序列很长,单字符语义弱。
**词级:**对于英文,词元是以空格(或标点)分隔的子字符串;对于中文,则需要基于词典实现。词汇量大,词表会难以管理,且模型无法处理生僻词(OOV)。典型实现有 jieba 等。
**子词级:**将常见的词保留为完整的词元,同时将不常见的词拆分成多个有意义的子词片段。这样既控制了词表的大小,又能让模型通过组合子词来理解和生成新词。主流的实现有 BPE 算法等。
BPE 算法
BPE 的核心思想非常简单:反复把语料中出现频率最高的相邻符号对合并成一个新符号,直到词表达到预设大小。以下是采用 BPE 算法进行分词的简单示例。
语料:
我爱AI
我爱深度学习
| 步骤 | 当前字符序列(每个字符用空格分隔) | 统计相邻字符对 | 最高频对 → 合并 | 合并后序列 |
|---|---|---|---|---|
| 初始词表 | {我, 爱, A, I, 机, 器, 学, 习} | — | — | — |
| 统计相邻对 | 计数 (我, 爱)=2、(爱, A)=1、(A, I)=1、(机, 器)=1、(学, 习)=1 … |
(我, 爱) (2 次) |
合并为 我爱 |
我爱 A I 我爱 机 器 学 习 |
| 合并最高频对 | 计数 (我爱, A)=1、(A, I)=1、(机, 器)=1、(学, 习)=1 … |
任选(这里选 (A, I)) |
合并为 AI |
我爱 AI 我爱 机 器 学 习 |
| 继续统计、合并 | 计数 (我爱, AI)=1、(机, 器)=1、(学, 习)=1 … |
任选(这里选 (机, 器)) |
合并为 深度 |
我爱 AI 我爱 深度 学 习 |
| 最终结果 | 计数 (学, 习)=1 → 合并为 学习 |
合并后得到最终子词序列 | 我爱 AI 我爱 深度 学习 |
{我爱, AI, 深度, 学习} |
最终子词词表(仅列出现的子词)
{我爱, AI, 深度, 学习}
建立词汇索引
得到词元后,需要将每个词元分配一个唯一整数 ID。词表在训练开始前就固定,之后不再改变。
如上面得到子词词表后,得到的词汇表如下:
1 | {"我爱": 0, "AI": 1, "深度": 2, "学习": 3} |
得到词汇表后,下一步我们要思考的是,如何表示一个句子。
编码
Multi-hot
表示一个句子最简单的做法是把一段文本看成一个词的集合,完全忽略顺序,这就是词袋模型。它用一个长度为 V(词表大小)的向量表示,每个维度对应词表中的一个词,出现了就标 1,没出现就标 0。
1 | 文本: "我爱深度学习" |
但这种方式有很大的问题,它舍弃了词序,对同一个句子,把词序换一下,往往表示不同的含义。
比如,对 Multi-hot 来说,下面两句话是完全一样的:
1 | "电影 不 好" → {电影, 不, 好} |
但实际上,这两个句子有着完全不同的含义。
One-hot
既然词袋丢了顺序,那我们把文本保持为一个有序的词元序列,每个词元用 One-hot 向量表示。
给定词表大小 V,token id 为 i 的词被编码为长度为 V 的向量,第 i 位为 1,其余为 0:
1 | 文本: "我爱深度学习" |
这里可能有人会有疑问,为什么不直接使用整数序列?直接输入整数序列会引入错误的数值假设(而实际上我们的词汇表索引大小可以认为是随机的,不含任何的意义),转成向量后可以有更大的表示空间。
经过 One-hot 编码后,词序保留了,但是问题来了,一条长度 20、词表 20000 的文本,经过 One-hot 编码后变成 (20, 20000) 的矩阵—— 包含 400 万个浮点数,其中 99.997% 是 0。
- 内存浪费巨大,效率极低;
- 任意两个词元的向量是相互独立的,不存在任何语义相似性,但在实际情况中,两个词向量之间的几何关系应该反映这两个单词之间的语义关系——比如,“好”和“棒”这两个词的“距离”应该相近。
我们需要更加聪明、更加高效的表示。
词嵌入
词嵌入是 One-hot 的高效实现:One-hot 编码得到的向量是二进制的、稀疏的(大部分元素是0)、高维的(维度大小等于词表中的单词个数),是硬编码的;而词嵌入是低维的浮点向量(密集向量,与稀疏向量相对),另外最重要的一点是,它是和模型一起训练、并且从数据中学习得到的。
如何得到词嵌入呢?有以下两种方法可以得到词嵌入。
在完成主任务(比如文档分类或情感预测)的同时学习词嵌入。在这种情况下,神经网络中会有一个词嵌入层,嵌入层本质上就是一个查表操作,其参数在初始化时随机,在训练中更新,它的学习方式与学习神经网络权重相同,在学习过程中,语义相近的词元会逐渐靠近,语义相反的会远离。
加载别人在大规模语料上预训练好的词向量。这种词嵌入通常是利用词频统计计算得到的,具有通用特征,如Word2Vec、GloVe等。
以下是训练一个词嵌入层的简单示例:
1 | inputs = keras.Input(shape=(None,), dtype="int64") # 整数序列 |
小结
本文介绍了深度学习中文本数据的向量化流程与主要编码方式。
文本向量化分为三个步骤:
文本规范化:统一文本格式(如转小写、全角转半角、去除噪声),减少无意义的编码差异,提升模型泛化能力。
词元化:将文本拆分为可向量化的最小单元。常见粒度有字符级、词级和子词级,其中子词级(如 BPE 算法)兼顾了词表大小与生僻词处理能力,是当前主流方案。
建立词汇索引:为每个词元分配唯一的整数 ID,构建固定词表。
编码方式的演进体现了对语义与效率的不断权衡:
- Multi-hot(词袋):实现简单,但完全丢失词序,同义不同序的句子无法区分。
- One-hot:保留了词序,但向量极度稀疏、维度极高,且无法表达词与词之间的语义相似性。
- 词嵌入:用低维稠密向量表示词元,可与模型联合训练或使用预训练向量,不仅大幅降低内存开销,还能在几何空间中自然地编码词语间的语义关系,是目前最实用的文本表示方式。