人工智能 -- 循环神经网络day1 -- 自然语言基础、NLP基础概率、NLP基本流程、NLP特征工程、NLP特征输入
一、自然语言基础概念
1、自然语言处理的基本介绍
自然语言处理(Natural Language Processing,简称NLP)是人工智能和语言学领域的一个分支,它涉及到计算机和人类(自然)语言之间的相互作用。它的主要目标是让计算机能够理解、解释和生成人类语言的数据。NLP结合了计算机科学、人工智能和语言学的技术和理论,旨在填补人与机器之间的交流隔阂。
NLP 就是让机器学会处理我们的语言!
在定义NLP之前,先了解几个相关概念:
语言(Language):是人类用于沟通的一种结构化系统,可以包括声音、书写符号或手势。
自然语言(Natural Language):是指自然进化中通过使用和重复,无需有意计划或预谋而形成的语言。
计算语言学(Computational Linguistics):是语言学和计算机科学之间的跨学科领域,它包括:
a.计算机辅助语言学(Computer-aided Linguistics):利用计算机研究语言的学科,主要为语言学家所实践。
b.自然语言处理(NLP):使计算机能够解决以自然语言表达的数据问题的技术,主要由工程师和计算机科学家实践。
NLP的研究范围广泛,包括但不限于语言理解(让计算机理解输入的语言)、语言生成(让计算机生成人类可以理解的语言)、机器翻译(将一种语言翻译成另一种语言)、情感分析(分析文本中的情绪倾向)、语音识别和语音合成等。
在中文环境下,自然语言处理的定义和应用也与英文环境相似,但需要考虑中文的特殊性,如中文分词、中文语法和语义分析等,因为中文与英文在语言结构上有很大的不同,这对NLP技术的实现提出了特殊的挑战。自然语言处理使计算机不仅能够理解和解析人类的语言,还能在一定程度上模仿人类的语言使用方式,进行有效的沟通和信息交换。
2、自然语言理解
情感分析:对给定的文本输入,在给定的选项范围内分析文本的情绪是正面还是负面;
文本分类:对给定的文本输入,在给定的选项范围内对文本进行二分类或多分类;
信息检索:搜索引擎依托于多种技术,如网络爬虫技术、检索排序技术、网页处理技术、大数据处理技术、自然语言处理技术等,为信息检索用户提供快速、高相关性的信息服务;
抽取式阅读理解:对给定的文本输入,用文本中的内容回答问题;
语义匹配:对给定的两个文本输入,判断是否相似;
自然语言推理:对给定的两个文本输入,判断是蕴涵、矛盾还是无关;
命名实体识别:对给定的文本输入,返回含有命名实体及其对应标签的映射,例如{'糖尿病':'疾病'};
文本摘要:对给定的文本输入,用文本中的内容对文本进行摘要。
3、自然语言生成
应用方向又可以分为
同步序列到序列(Synchronous Seq2Seq)**
特点:输入序列和输出序列是严格对齐的,即在处理一个输入序列的元素时,模型会即时产生相应的输出序列的元素。
典型应用:通常应用在那些输入和输出之间存在一对一对齐关系的任务中。例如,逐词对齐、词性标注(POS tagging)、命名实体识别(NER)。
优点:处理速度快,因为输入和输出同步生成,不需要等待整个输入处理完毕。
缺点:要求输入和输出严格对齐,不适合处理输入输出之间存在较复杂映射关系的任务。
2. 异步序列到序列(Asynchronous Seq2Seq)
特点:输入序列和输出序列之间不需要严格对齐,模型可以在处理完整个输入序列后,再开始生成输出序列。大多数的Seq2Seq模型都是异步的。
典型应用:用于需要对整个输入序列进行全局上下文理解后,才能生成输出的任务。例如,机器翻译(句子级别)、文本摘要生成、问答系统等。经典的Seq2Seq模型,如基于RNN或Transformer的模型,通常都属于异步类型。
优点:模型可以利用输入序列的全局上下文信息进行更准确的输出生成。
缺点:生成速度较慢,因为需要等待整个输入序列处理完毕后才开始生成输出。
二、NLP基础概念
下面总结一些NLP中常用的概念名词,便于理解任务。
(1)词表/词库(Vocabulary):文本数据集中出现的所有单词的集合。
(2)语料库(Corpus):用于NLP任务的文本数据集合,可以是大规模的书籍、文章、网页等。
(3)词嵌入(Word Embedding):将单词映射到低维连续向量空间的技术,用于捕捉单词的语义和语法信息。
(4)停用词(Stop Words):在文本处理中被忽略的常见单词,如"a"、"the"、"is"等,它们通常对文本的意义贡献较 小。
(5)分词(Tokenization):将文本分割成一个个单词或标记的过程,为后续处理提供基本的单位。
(6) 词频(Term Frequency):在给定文档中,某个单词出现的次数。
(7)逆文档频率(Inverse Document Frequency):用于衡量一个单词在整个语料库中的重要性,是将词频取倒数并取 对数的值。
(8) TF-IDF(Term Frequency-Inverse Document Frequency):一种常用的文本特征表示方法,综合考虑了词频和逆文档频率。
(9) 词袋模型(Bag of Words):将文本表示为一个单词的集合,忽略了单词的顺序和语法结构。
(10)N-gram:连续的N个单词构成的序列,用于捕捉文本中的局部特征和上下文信息。
(11)序列:指的是一个按顺序排列的元素集合。这些元素可以是字符、单词、句子,甚至更抽象的结构。序列的每个元素都有特定的顺序和位置,这意味着它们不能随意重排,否则会影响其意义或功能。
序列的常见类型
字符序列:
一个字符串就是一个字符序列,每个字符按顺序排列。
例子:
"hello"
是一个由h
、e
、l
、l
、o
组成的字符序列。
单词序列:
一句话可以看作是一个单词序列,每个单词按照一定顺序排列。
例子:
"I love NLP"
是一个由I
、love
、NLP
组成的单词序列。
时序数据:
在时间序列中,元素是按时间顺序排列的,常用于预测问题。
例子:股票价格数据可以看作是随时间变化的数值序列。
语音序列:
在语音处理任务中,语音信号可以被分解为按时间顺序排列的帧序列(特征向量序列)。
其他序列:
序列还可以表示一些更抽象的结构,比如DNA序列(由碱基组成的序列)、事件序列等。
三、NLP基本流程
中文自然语言处理(NLP)的基本流程包含以下六个核心环节,与英文NLP相比,在文本预处理环节有显著差异:
1. 语料获取
来源:现有数据集/第三方语料库、网络爬虫获取、第三方合作购买
特点:中文领域特定语料往往需要专门采集
2. 语料预处理(中文特殊性强)
去除非文本内容:去除HTML标签、无关符号等
中文分词(关键区别):使用jieba等分词工具解决中文无空格分隔问题
词性标注:为词语标注名词、动词等词性
去停用词:移除虚词、代词等不影响语义的词
3. 文本向量化/特征工程
将文本转换为数值特征向量
常用方法:词袋模型、TF-IDF、n-gram、Word2Vec等
4. 模型构建
机器学习模型:SVM、朴素贝叶斯、决策树等
深度学习模型:TextCNN、RNN/LSTM、Transformer等
选择原则:平衡模型复杂度与效果,避免过度复杂
5. 模型训练
小批量数据试验后再全量训练
需要注意:过拟合、欠拟合、梯度消失/爆炸问题
持续优化:参数调优、应对分布漂移
6. 模型评价
评价指标:准确率、精确率、召回率、F1值、AUC等
业务适配:根据不同场景需求侧重不同指标
中文NLP的特殊性
分词需求:中文没有天然分词,必须依赖分词算法
编码处理:中文使用Unicode编码,需注意编码转换
语言特性:中文有丰富的成语、歇后语等特殊表达
这一流程为中文NLP项目提供了系统化的实施框架,确保了从原始文本到可用模型的完整处理链条。
四、NLP中特征工程
概念
在自然语言处理(NLP)中,特征工程是指将文本数据转换为适合机器学习模型使用的数值表示的过程。文本是一种非结构化数据,机器学习模型无法直接处理,因此必须通过特征工程来提取有用的信息。
通过特征工程能让机器学习到文本数据中的一些特征,比如词性、语法、相似度等。
在英文中可以学到:
我希望得到的特征工程训练后的结果,从向量空间里面的分布角度看
1. 词向量
基本定义:
词向量是词语的数值化表示,用一个稠密的浮点数向量(例如 [0.1, 0.2, 0.3])来捕捉词语的各种含义(包括字面意义和隐含意义)。
这个过程被称为“嵌入”(Embedding),即把单词从高维的离散表示(如 one-hot 编码)映射到低维的连续向量空间。
核心思想与目的:
核心思想:这些向量并非随机生成,它们能够体现词语之间的语义关系。语义相近的词(如“猫”和“狗”),其向量在空间中的距离也更近。
主要目的:将文字转换为可计算的数值形式,让机器能够像处理数学问题一样处理词语,从而进行逻辑推理和语义分析。
能做什么?(应用与能力):
词语相似度计算:通过计算向量之间的余弦相似度等方式,量化词语的相似性。
语义类比推理:进行向量运算来模拟逻辑关系。
经典例子:
向量(“国王”) - 向量(“男人”) + 向量(“女人”) ≈ 向量(“女王”)
首都例子:
向量(“中国”) + 向量(“首都”) ≈ 向量(“北京”)
提升NLP任务性能:作为下游任务(如文本分类、情感分析、机器翻译、命名实体识别等)的底层输入,可以显著提高模型的性能。
一个简单的例子:
词语“猫”可能被表示为向量
[0.1, 0.2, 0.3]
词语“狗”可能被表示为向量
[0.2, 0.2, 0.4]
(与“猫”向量相近)词语“爱情”可能被表示为向量
[-0.4, -0.5, -0.2]
(与动物类词语向量相距较远)映射
X{“猫”, “狗”, “爱情”} -> Y{[0.1,0.2,0.3], [0.2,0.2,0.4], [-0.4,-0.5,-0.2]}
的过程就是词嵌入。
2、传统NLP的特征工程
1.独热编码 one - hot
基本定义:
独热编码是一种将离散的类别型数据(如文字、标签)转换为数值型数据的特征表示方法。
其核心特点是:对于有 N 个不同类别的特征,每个类别都用一個 N 维向量 表示,在这个向量中,只有一位是 1(“热”的),其余位都是 0。
工作原理:
为数据中的所有类别创建一个唯一的二进制向量。
示例(颜色):
类别:{红色,绿色,蓝色}
红色 →
[1, 0, 0]
绿色 →
[0, 1, 0]
蓝色 →
[0, 0, 1]
在NLP中的应用:
将词汇表中的每个单词表示成一个庞大的向量,向量的维度等于词汇表的大小。
示例(句子):
词库:{time, fruit, flies, like, a, an, arrow, banana}(共8个词)
banana
的独热编码:[0, 0, 0, 0, 0, 0, 0, 1]
(第8维为1)like
的独热编码:[0, 0, 0, 1, 0, 0, 0, 0]
(第4维为1)
优点:
简单直观:易于理解和实现。
将类别转换为数值:满足了机器学习模型需要数值输入的要求。
致命缺点(尤其在NLP中):
维度灾难(Curse of Dimensionality):如果词汇表很大(例如5万个单词),每个向量就会变得非常长(5万维),导致计算和存储效率极低。
无法表示语义关系(“词汇鸿沟”):任何两个词之间的向量距离(如欧氏距离、余弦相似度)都相等。例如,
[1,0,0]
(猫)和[0,1,0]
(狗)的距离,与[1,0,0]
(猫)和[0,0,1]
(汽车)的距离完全相同。机器无法从这种表示中学到“猫和狗都是动物”这种语义信息。
2.词频 - 逆文档频率(TF - IDF)
TF-IDF是一种用于信息检索和文本挖掘的常用加权技术,用于评估一个单词对于一个文档集合(语料库) 中的某份文档的重要程度。
1. 核心思想
一个词的重要性随着它在单个文档中出现的次数(TF)而增加,但同时会随着它在整个文档集合中出现的频率(DF)而减少。其目的是抑制常见词(如“的”、“是”),保留重要的关键词语。
2. 两个组成部分
词频(TF - Term Frequency)
定义:衡量一个词在一份特定文档中出现的频繁程度。
计算公式:
TF(t, d) = (词t在文档d中出现的次数) / (文档d中的总词数)
作用:某词在单个文档中出现越频繁,其TF值越高,说明它对该文档越重要。
示例:词 "cat" 在一篇100词的文档中出现5次,则
TF("cat") = 5 / 100 = 0.05
。
逆文档频率(IDF - Inverse Document Frequency)
定义:衡量一个词的普遍重要性。如果一个词在很多文档中都出现,那么它的IDF值较低(如停用词);反之,如果一个词比较罕见,则IDF值较高。
计算公式:
IDF(t, D) = log( [总文档数N] / [包含词t的文档数df(t)] ) + 1
(公式中的+1
和log
是为了平滑和压缩数值范围)作用:降低常见词的权重,提升罕见但具有区分度词的权重。
示例:词 "cat" 在1000篇文档中的10篇出现过,则
IDF("cat") = log(1000/10) + 1 ≈ 3 + 1 = 4
。
3. TF-IDF计算
最终的TF-IDF值是TF和IDF的乘积: TF-IDF(t, d, D) = TF(t, d) \* IDF(t, D)
值越高,意味着词t对文档d越重要,越能代表该文档的特色。
值越低,意味着词t要么在该文档中不常见,要么在整个语料库中太常见(如功能词)。
4. 特性与结论
文档频率(DF) 与词语的语义贡献度成反比(越常见的词,区分度越低)。
文档频率(DF) 与逆文档频率(IDF) 成反比。
逆文档频率(IDF) 与词语的语义贡献度成正比(越罕见且出现在特定文档中的词,贡献度越高)。
5. 优点与局限性
优点:
计算简单,易于理解。
有效过滤掉常见停用词。
能够提取文档的关键词。
局限性:
本质上仍是基于词频统计的模型,无法捕捉词语的语义信息(无法理解同义词、多义词)。
生成的向量仍然是稀疏向量(维度为词汇表大小)。
总而言之,TF-IDF是对One-Hot编码的一种改进,它通过加权使向量表示更具意义,但它仍然无法解决语义鸿沟问题。它是传统文本处理中的强大工具,但为理解更深层的语义,仍需使用词向量(Word Embedding) 等现代技术。
3、n - grams
1. 基本定义
N-grams 是一种文本特征提取技术,它将文本中连续出现的 n 个词(或字符) 组合成一个单元(称为 gram),用于捕捉文本的局部上下文和顺序信息。
1-gram (Unigram):单个词(如 "I", "love", "NLP")。
2-gram (Bigram):两个连续的词(如 "I love", "love NLP")。
3-gram (Trigram):三个连续的词(如 "I love NLP")。
2. 核心作用
N-grams 的核心价值在于其能够捕捉词语之间的局部依赖关系和顺序信息。这使得模型能够理解像“机器学习”和“学习机器”这样词序不同则含义不同的短语。
3. 与 TF-IDF 的结合
N-grams 常与 TF-IDF 结合使用,形成 N-gram TF-IDF 特征,其流程如下:
生成 N-grams:从文本中提取所有连续的 N 词组合。
计算 TF:统计每个 N-gram 在单个文档中的频率。
计算 IDF:计算每个 N-gram 在整个语料库中的逆文档频率,惩罚常见组合(如 "this is"),提升稀有且重要的组合。
计算 TF-IDF:将 TF 和 IDF 相乘,得到最终的特征权重。
举例:句子 "I love NLP" 的 Bigram TF-IDF 特征会是 {"I love": 0.346, "love NLP": 0.346}
(具体值取决于语料库)。
4. 优点
简单有效:计算相对简单,且能显著提升模型对词序和局部语境的感知能力。
提升表现:在传统机器学习方法(如文本分类、情感分析)中,使用 N-gram TF-IDF 特征通常比只使用单词(Unigram)表现更好。
5. 缺点与局限性
尽管强大,N-grams 仍属于传统特征工程,存在固有缺陷:
维度爆炸:N 值增大时,可能的组合数量呈指数级增长,导致特征维度极高,计算和存储成本巨大。
无法捕捉语义:和之前的方法一样,它无法理解词语的深层语义。
同义词问题:"汽车" 和 "轿车" 会被视为完全不同的特征。
多义词问题:"苹果"公司和一个吃的"苹果"会被视为相同的特征。
数据稀疏性:许多可能的 N-gram 组合在训练数据中从未出现,但在测试数据中可能出现,导致模型无法处理。
总而言之,N-grams 是对 BoW 和 TF-IDF 模型的重要增强,通过引入上下文窗口解决了词序问题。然而,它依然无法解决语义鸿沟这个根本问题,并且会带来维度灾难。这正是促使研究者开发像 Word2Vec 这样的词嵌入(Word Embedding) 技术的核心动因,因为词向量能够在低维空间中自动学习语义和语法规律。
五、NLP特征输入
1、稠密编码
1. 基本定义与核心思想
定义:稠密编码(Dense Encoding)是一种将高维、稀疏的离散数据(如单词、类别)转换为低维、连续的实数向量表示的技术。这种技术也被称为特征嵌入(Feature Embedding),在NLP中特指词嵌入(Word Embedding)。
核心思想:不再使用像 one-hot 那样绝大部分元素为 0 的稀疏表示,而是为每个特征学习一个稠密的向量(所有维度都有有意义的实数值),并将其映射到一个低维的连续向量空间中。
2.关键特点
低维度(Low Dimensionality):
向量空间的维度
d
(通常为100、200、300维)远小于原始特征的数量(例如,4万个单词的词表)。这极大地降低了计算和存储成本,解决了 one-hot 编码的维度灾难问题。
语义相似性(Semantic Similarity):
在这个学习到的向量空间中,语义相近的词语,其向量在空间中的距离(如余弦相似度)也更接近。
例如,“猫”和“狗”的向量距离会远小于“猫”和“汽车”的距离。这使得模型能够捕捉到词语之间的语义和语法关系。
可微学习(Differentiable Learning):
这些嵌入向量不是预先计算好的固定值(如 TF-IDF),而是作为神经网络的参数,在模型训练过程中通过反向传播和梯度下降算法自动学习和优化的。
这意味着模型会为了最终任务(如分类、翻译)的目标,自动调整每个词的向量表示,使其最有效。
3. 优势(与传统方法对比)
对比 One-Hot:解决了维度灾难和无法表示语义两大核心缺陷。
对比 TF-IDF / N-gram:生成的向量是稠密的、低维的,并且能够捕捉深层次的语义关系(如同义词、多义词),而不仅仅是表面上的统计共现。
2、词嵌入算法
1.Embedding Layer
(1) 核心思想:从One-Hot到稠密向量
词嵌入算法的核心目标是解决One-Hot编码的稀疏性和无语义两大缺陷。它通过一个可学习的矩阵映射(Embedding Layer),将高维的One-Hot向量转换为低维、稠密且蕴含语义的连续向量。
One-Hot向量:
[0, 0, ..., 1, ..., 0]
(稀疏,高维,仅表示索引)词嵌入向量:
[0.26, 0.25, -0.39, ...]
(稠密,低维,蕴含语义)
(2) 实现机制:Embedding Layer
Embedding Layer(嵌入层) 是神经网络中负责实现这一转换的核心组件。
本质:它是一个可训练的查找表(Look-up Table) 或矩阵。
矩阵大小:
(词表大小V, 嵌入维度D)
。例如,一个有10000个词的词表,想用128维的向量表示每个词,则矩阵大小为10000 x 128
。工作流程:
输入一个词的索引(即其One-Hot向量中“1”的位置)。
Embedding Layer根据这个索引,从矩阵中查找(Lookup) 对应的那一行。
这一行向量就是该词的稠密向量表示。
训练方式:嵌入矩阵中的参数(即所有词向量)作为神经网络的一部分,通过反向传播算法在完成特定任务(如语言模型预测下一个词)的过程中被联合优化。
(3) 在PyTorch中的实现 (nn.Embedding
)
使用 torch.nn.Embedding
类可以轻松实现词嵌入层。
关键参数:
num_embeddings
: 词表的大小(V)。embedding_dim
: 嵌入向量的维度(D)。
输入:一批词语的索引(LongTensor)。
输出:对应索引词的稠密向量。
示例步骤(中文文本):
分词:使用
jieba
等工具将句子切分成词列表。构建词表:创建词到索引(
word2id
)和索引到词(id2word
)的映射字典。索引化:将句子中的每个词转换为对应的索引。
嵌入层转换:将索引张量输入
nn.Embedding
层,得到词的向量表示。
(4) 词向量的副产品:神经语言模型(NNLM)
词向量最初是训练神经语言模型(Neural Network Language Model, NNLM) 时的一个副产物。
NNLM任务:给定前
n-1
个词,预测第n
个词是什么。训练过程:为了更好地完成这个预测任务,模型必须学习到能很好表征词语语义和语法关系的向量表示。训练完成后,不仅得到了一个预测模型,更得到了宝贵的词向量矩阵。
(5)预训练模型(Pre-trained Models)
定义:指在大规模通用语料库上预先训练好的模型(如BERT, GPT)。其核心组成部分就是高质量的词嵌入(或更先进的上下文嵌入)。
思想:模型首先学习语言的通用特征(词汇、语法、语义),这个过程称为预训练。
优势:得到的词嵌入或模型可以迁移(Transfer) 到各种下游任务(如文本分类、问答),只需进行少量数据的微调(Fine-tuning),就能取得极佳的性能,解决了特定任务标注数据不足的问题。
示例:
import torch import torch.nn as nn import jieba text = '北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。' #分词 words = jieba.lcut(text) print(words) #构建词表 NLP任务数据处理的第一步 word2id = {} id2word = {} words_unique = list(set(words)) #去重 得到包含所有唯一词的列表 for i, word in enumerate(words_unique):word2id[word] = iid2word[i] = word print(id2word) print(word2id) #参数:第一个参数是词表大小,第二个参数是词向量的维度 超参数 hidden_size embed = nn.Embedding(len(word2id), 5) print(embed) for word in words:#获取词表的索引index = word2id[word]#获取词表中索引对应的词向量vector = embed(torch.LongTensor([index]))print(word,"的词向量:",vector)
2、word2id
1. 核心思想与目的
Word2Vec 的核心思想源于一个基本假设:“出现在相似上下文中的词语,其语义也相似”(Distributional Hypothesis)。
最终目的:它的首要目标不是训练一个完美的语言模型,而是为了得到训练完成后那个高质量的副产品——词向量矩阵。模型本身的预测任务只是学习这些向量的一种手段。
核心直觉:通过让模型学习预测一个词的上下文(或通过上下文预测中心词),迫使词向量捕捉到词语之间的语义和语法关系。例如,如资料中所说,因为“吴彦祖”和“我”出现在几乎相同的上下文中,所以模型会将它们的向量调整得非常接近,从而得出
vector("我") ≈ vector("吴彦祖")
的结论。
2. 两种训练模式
Word2Vec 提供了两种高效的模型架构来实现这一思想:
Skip-gram(跳字模型)
任务:给定一个中心词(如“吴彦祖”),预测其窗口大小内的上下文词(如“她们”、“夸”、“帅”、“没朋友”)。
特点:更适合处理少量训练数据,能很好地捕捉罕见词的表示。
CBOW(连续词袋模型)
任务:给定上下文词(如“她们”、“夸”、“帅”、“没朋友”),预测中心词(如“吴彦祖”)。
特点:训练速度更快,对高频词的准确度稍高。
3. 关键创新与优化
Word2Vec 之所以高效和流行,得益于其提出的优化方法,极大地提升了训练速度:
Negative Sampling(负采样):将复杂的多分类问题(从数万词汇中选一个)转化为一系列二分类问题(判断当前样本是真实样本还是噪声样本)。这是其训练速度快的核心原因。
Hierarchical Softmax(层次Softmax):使用哈夫曼树(Huffman Tree)来组织词汇表,将计算复杂度从 O(V) 降低到 O(log(V))(V是词汇表大小)。
4. 主要优点
高效:相比之前的模型(如NNLM),训练速度极快。
效果好:生成的词向量能够出色地捕捉语义和语法规律,支持经典的语义类比推理(如“国王 - 男人 + 女人 = 女王”)。
轻量级:得到的词向量模型文件很小,易于部署和应用。
5. 应用
学习到的词向量可以作为特征广泛应用于各种NLP任务中:
文本分类
命名实体识别
情感分析
推荐系统(用于物品和用户的嵌入表示)
机器翻译等
6. 局限性
静态表示:一个词无论上下文如何,其Word2Vec向量是固定不变的,无法解决一词多义问题(例如,“苹果”在公司名和水果名中的含义不同,但向量相同)。
上下文窗口限制:只能捕捉固定窗口大小内的局部上下文信息,无法获取更长距离的依赖关系或全局信息。
(1)Skip - gram模型
1. 核心思想
Skip-gram 是 Word2Vec 的两种模型之一。它的核心思想是:用一个词语(中心词)作为输入,来预测它周围一定窗口范围内的其他词语(上下文词)。
任务:给定一个词(如“吴彦祖”),让模型预测其周围最可能出现的词(如“夸”、“帅”)。
目标:通过完成这个预测任务,模型最终能学习到高质量的词向量,使得语义相近的词在向量空间中的位置也相近。
2. 模型架构与工作流程(基于你的代码)
你的代码实现了一个简化版的 Skip-gram 模型,其核心组件和工作流程如下:
嵌入层 (
nn.Embedding
)作用:将输入的中心词的索引转换为一个稠密的词向量。这是我们需要获取的最终目标。
参数:
vocab_size
(词表大小) 和embed_size
(词向量维度)。
线性输出层 (
nn.Linear
)作用:将嵌入层得到的词向量映射成一个长度等于词表大小的分数向量(logits)。
目的:为了计算每个词作为当前中心词的上下文词的概率。
训练过程
输入:中心词的索引。
输出:模型预测的上下文中某个词的分数分布。
损失函数:
nn.NLLLoss
(负对数似然损失)。通常与LogSoftmax
层结合使用,计算模型预测的概率分布与真实上下文词(one-hot 编码)之间的差异。优化:通过反向传播和优化器(如 SGD)更新嵌入层和线性层的参数。这个过程不断调整词向量,使得模型越来越擅长根据中心词预测上下文。
3. 优点(从你的描述和代码中体现)
利用中心词特征:模型直接学习中心词的向量表示,并用它来预测上下文,这使得中心词的向量能很好地捕捉其自身的语义和语法特征。
对低频词友好:每个中心词都会生成多个(窗口大小决定)训练样本(
(中心词, 上下文词1)
,(中心词, 上下文词2)
...),这大大增加了低频词的训练机会,使其能获得更好的向量表示。效果优异:正如你提到的,在处理大规模语料时,Skip-gram 模型的效果通常比 CBOW 更好,尤其是在学习高质量的低频词表示方面。
4. 可视化理解
代码最后的可视化部分至关重要:
model.parameters()
:获取模型训练好的参数,其中第一个就是嵌入矩阵(词向量矩阵)。降维绘图:将高维(例如100维)的词向量通过取前两维 (
x=W[i][0], y=W[i][1]
) 的方式进行降维,并在二维平面上绘制出来。意义:虽然损失了高维信息,但这种可视化能让我们直观地看到模型学习到的语义关系。理想情况下,语义相近的词(如“中国”和“北京”)在图上的点会聚集在一起。
示例:
import numpy as np import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt # 定义数据类型为浮点数 dtype = torch.FloatTensor # 语料库,包含训练模型的句子 sentences = ["i like dog", "i like cat", "i like animal","dog cat animal", "apple cat dog like", "cat like fish","dog like meat", "i like apple", "i hate apple","i like movie book music apple", "dog like bark", "dog friend cat"] # 将所有句子拼接为一个字符串并按空格分词 word_sequence = ' '.join(sentences).split() # 获取词汇表中的所有唯一词 word_list = list(set(word_sequence)) print(word_list) # 创建词典,词汇表中的每个词都分配一个唯一的索引 word2id = {w: i for i, w in enumerate(word_list)} # 创建跳字模型的训练数据 skip_grams = [] # 训练数据 for i in range(1, len(word_sequence) - 1):# 当前词对应的idtarget = word2id[word_sequence[i]]# 获取当前词的前后两个上下文词对应的idcontext = [word2id[word_sequence[i - 1]], word2id[word_sequence[i + 1]]] # 将目标词与上下文词配对,添加到训练数据中for w in context:skip_grams.append([target, w]) # print(skip_grams) # exit() # 定义随机批量生成函数 def random_batch(data, size):random_inputs = [] # 输入批次random_labels = [] # 标签批次# 从数据中随机选择size个索引random_index = np.random.choice(range(len(data)), size, replace=False) # 根据随机索引生成输入和标签批次for i in random_index:# 目标词one-hot编码# np.eye(voc_size)的作用是创建一个voc_size x voc_size的eye矩阵,其中对角线为1,其他元素为0random_inputs.append(np.eye(voc_size)[data[i][0]])# 上下文词的索引作为标签random_labels.append(data[i][1]) return random_inputs, random_labels # 定义嵌入维度(嵌入向量的大小)为2 embedding_size = 2 # 词汇表大小 voc_size = len(word_list) # 每次训练的批量大小 batch_size = 5 # 定义Word2Vec模型 class Word2Vec(nn.Module):def __init__(self):super(Word2Vec, self).__init__()# 定义词嵌入矩阵W,随机初始化,大小为(voc_size, embedding_size)self.W = nn.Parameter(torch.rand(voc_size, embedding_size)).type(dtype)# 定义上下文矩阵WT,随机初始化,大小为(embedding_size, voc_size)self.WT = nn.Parameter(torch.rand(embedding_size, voc_size)).type(dtype) # 前向传播def forward(self, x):# 通过乘以嵌入矩阵W得到词向量weight_layer = torch.matmul(x, self.W)# 通过上下文矩阵WT得到输出output_layer = torch.matmul(weight_layer, self.WT)return output_layer # 创建模型实例 model = Word2Vec() # 定义损失函数为交叉熵损失 criterion = nn.CrossEntropyLoss() # 使用Adam优化器 optimizer = optim.Adam(model.parameters(), lr=0.001) # 训练模型 for epoch in range(10000):# 获取随机的输入和目标inputs, labels = random_batch(skip_grams, batch_size) #转为张量input_batch = torch.Tensor(inputs)label_batch = torch.LongTensor(labels) optimizer.zero_grad() # 梯度清零output = model(input_batch)# 计算损失函数loss = criterion(output, label_batch) if (epoch + 1) % 1000 == 0:print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss)) loss.backward() # 反向传播optimizer.step() # 参数更新 #可视化词嵌入 for i, label in enumerate(word_list):W,WT = model.parameters() # 获取模型参数x,y = float(W[i][0]),float(W[i][1])plt.scatter(x, y) # 绘制散点图plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom') plt.show()
(2)CBOW模型
1. 核心思想
CBOW 是 Word2Vec 的另一种模型,其核心思想与 Skip-gram 相反:用上下文词语(包围目标词的词语)来预测中心的目标词。
任务:给定一个窗口内的上下文词(如“她们”、“夸”、“帅”、“没朋友”),让模型预测中间最可能出现的中心词(如“吴彦祖”或“我”)。
目标:通过完成这个预测任务,模型学习到的上下文词的向量表示能够共同推断出中心词的语义。
2. 模型架构与工作流程(关键特点)
CBOW 的架构相比早期的神经网络语言模型(NNLM)更为简化高效:
输入层:多个上下文词的 One-Hot 向量。
嵌入层 (
nn.Embedding
):将每个上下文词的索引转换为对应的词向量。
投影层(无隐藏层):
核心操作:将上下文中所有词的词向量求平均(或求和),得到一个综合的“上下文语义向量”。这是 CBOW 模型的一个关键简化步骤,它移除了 NNLM 中的隐藏层,大大减少了参数量和计算量。
这个“上下文向量”代表了当前窗口的整体语义环境。
输出层 (
nn.Linear
):将投影层得到的“上下文语义向量”映射成一个长度等于词表大小的分数向量(logits)。
最终通过 Softmax 计算每个词作为中心词的概率。
3. 优点
利用上下文信息:模型整合了整个上下文窗口的信息来预测中心词,这使得学习到的词向量能更好地反映词语在其常见语境中的含义。
训练速度快:由于模型结构更简单(去掉了隐藏层,参数更少),并且在求平均操作后只需要处理一个向量,因此训练速度通常比 Skip-gram 模型更快。
对高频词更有效:模型从多个上下文词中学习,对高频词的学习效果更好,因为它们的上下文出现得更频繁。
4. 缺点与适用场景
对低频词不友好:由于上下文被平均,一些低频但重要的词的信号可能会被高频词的信号所淹没。
更适用于小型数据库:正如你提到的,CBOW 在较小的语料库上往往表现得更合适、更稳定,因为它能更快地从有限的数据中收敛。
5. 与 Skip-gram 的对比总结
特性 | CBOW (连续词袋模型) | Skip-gram (跳字模型) |
---|---|---|
任务 | 用上下文预测中心词 | 用中心词预测上下文 |
训练速度 | 更快 | 较慢 |
对低频词效果 | 一般 | 更好 |
适用场景 | 较小的语料库 | 大型语料库 |
形象比喻 | 完形填空(给你周围词,猜中间词) | 给你一个词,让你说出它常见的邻居 |
6. 代码
关键点在于:
数据准备:需要构建
(context, target)
样本对,其中context
是窗口内所有上下文词的索引列表,target
是中心词的索引。前向传播:
通过
embedding
层将上下文词的索引转换为向量。对这些词向量求平均 (
torch.mean(embeddings, dim=0)
) 以生成投影层的输出向量。将投影向量送入线性层进行预测。
可视化:与 Skip-gram 类似,训练完成后,从
model.embedding.weight.data
中提取词向量矩阵并进行降维可视化,以观察词语在空间中的分布。
总而言之,CBOW 是一个高效、快速的词向量训练模型,它通过“用上下文预测中心词”的任务来学习词向量。它结构简单,训练速度快,特别适合在计算资源有限或语料库规模不是特别巨大的场景下使用。选择 CBOW 还是 Skip-gram 需要根据具体的任务、语料库大小和对性能的要求来决定。
示例:
import numpy as np import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt # 定义数据类型为浮点数 dtype = torch.FloatTensor # 语料库,包含训练模型的句子 sentences = ["i like dog", "i like cat", "i like animal","dog cat animal", "apple cat dog like", "cat like fish","dog like meat", "i like apple", "i hate apple","i like movie book music apple", "dog like bark", "dog friend cat"] # 构建词表以及字典 word_list = " ".join(sentences).split() word_list = list(set(word_list)) print(word_list) word2id = {word:i for i,word in enumerate(word_list)} id2word = {i:word for i,word in enumerate(word_list)} print(word2id) # 构建数据集 文本转索引 cbow_data = [] for i in range(1, len(word_list)-1):context = [word2id[word_list[i-1]], word2id[word_list[i+1]]]target = word2id[word_list[i]]cbow_data.append([context, target]) print(cbow_data) def random_batch(data, batch_size=3):random_inputs = []random_labels = []random_index = np.random.choice(range(len(data)), batch_size, replace=False) for i in random_index:random_inputs.append(data[i][0])random_labels.append(data[i][1]) return torch.LongTensor(random_inputs), torch.LongTensor(random_labels) input_batch, label_batch = random_batch(cbow_data) # 模型参数 vocab_size = len(word_list) embedding_size = 2 # x,y轴 class CBOW(nn.Module):def __init__(self):super(CBOW, self).__init__()self.embed = nn.Embedding(vocab_size, embedding_size)self.output = nn.Linear(embedding_size, vocab_size) def forward(self, input_batch):""":param input_batch: [batch,seq_len]:return:"""x = self.embed(input_batch) # [batch, seq_len, embedding_size]x = torch.mean(x, dim=1)x = self.output(x)return x # 训练流程 model = CBOW() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) for epoch in range(5000):optimizer.zero_grad()output = model(input_batch)loss = criterion(output, label_batch) loss.backward()optimizer.step()if (epoch+1)%1000 == 0:print(f"Epoch: {epoch+1}/5000, loss: {loss.item()}") #可视化词嵌入 for i, label in enumerate(word_list):# 获取模型参数 映射矩阵Ww = model.embed.weight.data.numpy()print(w.shape)x,y = float(w[i][0]), float(w[i][1])plt.scatter(x, y) # 绘制散点图plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom') plt.show()
(3)gensim
1. 核心目的
Gensim 库提供的 Word2Vec API 是一个高效、便捷且功能强大的工具,用于训练和使用 Word2Vec 模型,从而将单词转换为具有语义信息的向量(词嵌入),而无需从零开始实现模型。
2. 核心参数详解
使用 gensim.models.Word2Vec
训练模型时,以下几个参数至关重要:
参数 | 说明 |
---|---|
sentences | 训练的语料。 可以是一个单词列表的列表(list of list of words ),例如 [['我', '是'], ['谁']] 。对于大语料,建议使用 Gensim 提供的工具类(如 LineSentence 按行读取文件)。 |
vector_size | 词向量的维度。 通常设置为几十到几百维(如 100, 200, 300)。维度越大,能捕捉的信息越多,但需要更多的训练数据和计算资源。 |
window | 上下文窗口大小。 表示在当前词周围多大范围内的词会被视为其上下文。例如,window=5 意味着考虑左边和右边各5个词(总共10个词)。 |
min_count | 词频阈值。 词频低于此值的单词将被忽略,不会进入词汇表。这能有效过滤掉稀有词或错别字,减小模型大小并提升质量。常用值为 5。 |
sg | 训练算法选择。 sg=0 (默认)使用 CBOW 算法;sg=1 使用 Skip-gram 算法。根据任务和语料大小选择。 |
workers | 训练并行度。 设置使用多少个CPU核心进行训练,可以显著加快训练速度。 |
epochs | 迭代次数。 语料库被遍历的次数。默认是 5。 |
3. 常用方法(通过 model.wv
对象访问)
模型训练完成后,model.wv
(Word Vectors)对象提供了所有与词向量交互的方法:
方法 | 说明 | 示例 |
---|---|---|
model.wv[word] | 获取单个词的词向量。 | vector = model.wv['北京'] |
model.wv.most_similar(...) | 查找最相似的词。 用于词语类推、查找同义词等。 | sims = model.wv.most_similar('北京', topn=10) |
model.wv.similarity(word1, word2) | 计算两个词之间的余弦相似度。 | sim = model.wv.similarity('北京', '上海') |
model.save("path") | 将训练好的模型保存到磁盘。 | model.save("word2vec.model") |
Word2Vec.load("path") | 从磁盘加载一个已保存的模型。 | model = Word2Vec.load("word2vec.model") |
4. 工作流程总结
准备数据:将原始文本处理成
list of list of words
的格式(即分好词的句子列表)。初始化模型:使用
Word2Vec(sentences, vector_size=100, window=5, min_count=5, workers=4, sg=0)
等参数创建模型实例。训练模型:Gensim 会自动开始训练。
使用模型:通过
model.wv
对象查询词向量、计算相似度等。保存/加载模型:使用
save()
和load()
方法持久化模型,避免重复训练。
总而言之,Gensim 的 Word2Vec API 将复杂的词向量训练过程封装成了几行简单的代码,是学习和应用词嵌入技术的首选工具。通过调整关键参数(如 sg
, vector_size
, window
)并利用其丰富的查询方法,可以快速构建出适用于各种下游NLP任务的高质量词向量模型。
示例:
from gensim.models import Word2Vec from matplotlib import pyplot as plt # 语料库,包含训练模型的句子 sentences = ["i like dog", "i like cat", "i like animal","dog cat animal", "apple cat dog like", "cat like fish","dog like meat", "i like apple", "i hate apple","i like movie book music apple", "dog like bark", "dog friend cat"] data_list = [sentence.split() for sentence in sentences] # print(data_list) model = Word2Vec(data_list, vector_size=2, window=1, min_count=1, sg=0) # 获取词汇表 vocab = list(model.wv.index_to_key) print(vocab) # 可视化散点图 for i,lable in enumerate(vocab):w = model.wv.vectorsw_dim = model.wv[lable]print(w_dim)# print(w)x,y = float(w_dim[0]), float(w_dim[1])plt.scatter(x,y)plt.annotate(lable, xy=(x,y), xytext=(5,2), textcoords='offset points', ha='right', va='bottom') plt.show() print(model.wv.similarity('cat', 'dog')) print(model.wv.most_similar('cat'))