文章目录
前言
在自然语言处理领域,让计算机理解人类语言的语义一直是研究难点。传统的独热编码等词表示方法,仅能孤立标识单词,无法体现词间语义联系。
词向量转换与词嵌入技术的诞生打破了这一困局。它们将离散单词映射为连续向量,挖掘单词间语义关联,把单词嵌入低维空间,使语义相近的词自然聚类。如今,这项技术已深度融入文本分类、机器翻译等任务,成为 NLP 模型理解语义的核心基础。接下来,我们将深入解析其原理、方法及应用。
一、自然语言模型
语言模型是自然语言处理的关键技术,常见类型包括传统统计语言模型和神经网络语言模型。传统统计语言模型中的 N - gram 模型,基于统计计算文本中连续 N 个词出现的概率以预测下一词,如二元语法计算词对概率,其优势在于简单高效,但存在无法处理长距离语义依赖、数据稀疏等问题;神经网络语言模型中,递归神经网络语言模型(RNN - LM)能捕捉长期依赖关系,却面临梯度消失与爆炸难题,影响长序列处理效果,长短时记忆网络语言模型(LSTM - LM)作为 RNN 改进版,通过门控机制优化信息流动,可有效处理长序列,例如记住长句开头关键信息辅助后续处理,门控循环单元语言模型(GRU - LM)则简化 RNN 结构,合并部分门和状态,在部分任务中效果良好且计算效率更高。
统计语言模型存在的问题
1、由于参数空间的爆炸式增长,它无法处理(N>3)的数据。
举例理解:
中文有成千上万的词汇,四词组合的可能性超过100亿种(相当于给每个汉字搭配3个伙伴的所有可能性)。即使你每天背100个组合,也要273万年才能背完------这就是参数空间的爆炸式增长。统计语言模型就像一个只能死记硬背的学生,当需要记忆的组合超过三词时,它的"大脑"就完全装不下了。
2、没有考虑词与词之间内在的联系性。
假设你学过:
"小狗在公园奔跑"和"小猫在草地跳跃",但从未学过"小猫在公园奔跑"。
人类会自动联想:既然"小狗"和"小猫"都是宠物,"奔跑"和"跳跃"都是动作,那么新句子应该合理。但传统统计模型就像一个只认字面意思的机器人:
它认为"小狗"和"小猫"是完全不同的词,"奔跑"和"跳跃"也没有任何关联,只要没亲眼见过"小猫在公园奔跑"这个具体组合,就认为概率为零。
这就好比只允许用完全相同模具做蛋糕,稍微改动形状就被判定为"不可能",而不知道面粉、鸡蛋这些原料之间本来就有通用性。
总结:这两个问题的本质,第一个是"容量问题":模型记忆力有限;第二个是"理解力问题":模型缺乏抽象能力。
二、词向量转换
1.onehot编码
One-Hot 编码(独热编码)是一种将离散型分类变量(如文字、符号、类别标签)转换为数值型向量的方法。它的核心思想是为每个类别分配一个唯一的二进制向量,向量中只有一位是 1(表示当前类别),其余均为 0。机器学习模型(如神经网络、逻辑回归)只能处理数值数据,无法直接使用文本或类别标签。编码的目的是将非数值信息转化为数值形式。
假设你需要将"我爱中国"这些自然语言输入计算机,但计算机只能处理数字。如果想让计算机能够认识这些文字,则需要通过 One-Hot 编码后:
我 → [1, 0, 0,0]
爱 → [0, 1, 0,0]
中→ [0, 0, 1,0]
国→ [0,0, 0, 1]
每个汉字对应一个"独一无二"的二进制向量,就像为每个类别分配了一个"身份证号"。
编码过程
步骤 1:确定所有可能的类别,例如动物类别为 [猫, 狗, 鸟]。
步骤 2:为每个类别分配一个位置索引,例如:猫→0,狗→1,鸟→2。
步骤 3:生成二进制向量,向量的长度等于类别总数,仅在对应索引位置设为 1,其余为 0。
onehot独热编码存在的问题
1、维度灾难:当类别数量极大时(例如 10,000 个城市),编码后的特征维度爆炸,导致计算效率下降。
例子:假设我们有一个语料库,语料库总共有4960个汉字,将这4960个词转换为onehot向量,矩阵非常稀疏,出现维度灾难。例如有一句话为"我爱北京天安门",传入神经网络输入层的数据为:7x4960的矩阵,这个矩阵是由0和1组成的矩阵,随着语料库的增加,这个矩代表每一个汉字所需的维度也越多,出现了维度爆炸问题。
2、无法表达相似性:所有类别被视为完全独立,例如"猫"和"狗"的相似性无法体现(它们的向量距离相同)。
总结
One-Hot 编码是处理离散特征的基础方法,核心是通过二进制向量唯一标识每个类别。虽然简单有效,但在高基数(类别多)场景下需谨慎使用,此时可考虑嵌入(Embedding)或特征哈希(Hashing)等优化方案。
2.词嵌入
为了解决维度灾难,通过神经网络训练,将每个词都映射到一个较短的词向量上来。词向量的维度一般需要在训练时自己来指定。现在很常见的例如300维。
例如有一句话为"我爱北京天安门",通过神经网络训练后的数据为:
0.62,0.12,0.01,0,0,0,0,....,0
0.1,0.12,0.001,0,0,0,0,....,0
0,0,0.01,0.392,0.39, 0,....,0
0,0,0,1,0,0.01,0.123,....,0.11
"我爱北京天安门"这句话的矩阵由7x4960转换为7x300,可以看出矩阵中维度的数字已经不再是0和1,而是变为了浮点数。
这种将高维度的词表示转换为低维度的词表示的方法,我们称之为词嵌入(word embedding)。
(1)Word2Vec
Google研究团队里的Tomas Mikolov等人于2013年的《Distributed Representations ofWords and Phrases and their Compositionality》以及后续的《Efficient Estimation of Word Representations in Vector Space》两篇文章中提出的一种高效训练词向量的模型,也就是word2vec。
Word2Vec的两种模型结构,CBOW(Continuous Bag-of-Words):根据上下文预测中心词。Skip-Gram:根据中心词预测上下文。
CBOW模型
CBOW模型是以上下文词汇预测当前词,即用ω(t−2)、ω(t−1)、 ω(t+1)、 ω(t+2)预测ω(t)。
假设此时要预测"由",即通过"我命"和"我不"来预测,中间字"由"。
Skip_Gram模型
Skip_Gram模型是以当前词预测其上下文词汇,即用ω(t)预测ω(t−2)、ω(t−1)、 ω(t+1)、 ω(t+2)。

假设此时要使用"由",预测"我命"和"我不"的内容。
3、词嵌入模型的训练
(1)模型训练的过程
1、当前词的上下文词语的one-hot编码输入到输入层。
2、这些词分别乘以同一个矩阵WVN后分别得到各自的1 N 向量。
3、将多个这些1N 向量取平均为一个1 N 向量。
4、将这个1N 向量乘矩阵 W'V N ,变成一个1V 向量。
5、将1 V 向量softmax归一化后输出取每个词的概率向量1V
6、将概率值最大的数对应的词作为预测词。
7、将预测的结果1 V 向量和真实标签1V 向量(真实标签中的V个值中有一个是1,其他是0)计算误差
8、在每次前向传播之后反向传播误差,不断调整 WV N和 W'V*N矩阵的值。
假定语料库中一共有4960个词,则词编码为4960个01组合,现在压缩为300维。
当新来一个字时,我们可以将这个字的onehot向量与CBOW中训练得到的矩阵相乘就可以实现词嵌入,将词向量的维度由4960维降到300维。
(2)损失函数的选择

softmax交叉熵损失函数实现多分类,公式为:
三、CBOW模型实现词嵌入
1、导入必要的库
python
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim#
from tqdm import tqdm,trange#显示进度条
import numpy as np
2、读取文本,创建词库
python
CONTEXT_SIZE=2 #设置词左边和右边选择的个数(即上下文词汇个数)
raw_text ="""We are about to study the idea of a computational process.
Computational processes are abstract beings that inhabit computers.
As they evolve, processes manipulate other abstract things called data.
The evolution of a process is directed by a pattern of rulescalled a program.
People create programs to direct processes.
In effectwe conjure the spirits of the computer with our spells.""".split()#语料库
#中文的语句,你可以选择分词,也可以选择分字
vocab = set(raw_text)#集合。词库,里面内容独一无人
vocab_size = len(vocab)
word_to_idx = {word:i for i, word in enumerate(vocab)} #for循环的复合写法,第1次循环,i得到的索引号,word 第1个年
idx_to_word = {i: word for i, word in enumerate(vocab)}
3、构造CBOW网络的输入和输出
python
data =[]#获取上下文词,将上下文词作为输入,目标词作为输出。构建训练数据集。
for i in range(CONTEXT_SIZE, len(raw_text)-CONTEXT_SIZE):#(2,60)
context =(
[raw_text[i-(2-j)]for j in range(CONTEXT_SIZE)]
+[raw_text[i+j+ 1] for j in range(CONTEXT_SIZE)])
#获取上下文词(['we"'are"'to''study'])
target = raw_text[i] #获取目标词'about'
data.append((context,target)) #将上下文词和目标词保存到data中[((['we"'are 'to''study'])'about')]
4、定义文本转onehot的函数
python
def make_context_vector(context,word_to_idx):#将上下文词转换为one-hot
idxs =[word_to_idx[w] for w in context]
return torch.tensor(idxs,dtype=torch.long)#
5、定义CBOW网络
python
class CBOW(nn.Module):
def __init__(self,vocab_size,embedding_dim):
super(CBOW,self).__init__()
self.embeddings=nn.Embedding(vocab_size,embedding_dim)
self.proj=nn.Linear(embedding_dim,128)
self.output=nn.Linear(128,vocab_size)
def forward(self,inputs):
embeds=sum(self.embeddings(inputs)).view(1,-1)
out=F.relu(self.proj(embeds))
out=self.output(out)
nll_prob=F.log_softmax(out,dim=-1)
return nll_prob
6、优化器损失函数初始化,加载模型到设备中
python
device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
model=CBOW(vocab_size,10).to(device) #将模型加载到设备中,设置CBOW网络的输入和输出vocab_size是指语料库的大小,10是指你要将语料库压缩到多少维。
optimizer=optim.Adam(model.parameters(),lr=0.001) #创建优化器
loss_funcation=nn.NLLLoss() #损失函数初始化
7、模型的训练
python
losses=[]
model.train()
for epoch in tqdm(range(200)):
for context,target in data:
context_vector=make_context_vector(context,word_to_idx).to(device)
target=torch.tensor([word_to_idx[target]]).to(device)
train_predict=model(context_vector)
loss=loss_funcation(train_predict,target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
8、预测
python
context=['People','create','to','direct']
context_vector=make_context_vector(context,word_to_idx).to(device)
model.eval()
predict=model(context_vector)
max_idx=int(predict.argmax(1))
print(idx_to_word[max_idx])
完整代码展示
python
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim#
from tqdm import tqdm,trange#显示进度条
import numpy as np
#任务:已经有了语料库,
# 1、构造训练数据集,(单词,词库,)#真实的单词模型,
# 每一个单词的词性,你训练大量的输入文本,
CONTEXT_SIZE=2 #设置词左边和右边选择的个数(即上下文词汇个数)
raw_text ="""We are about to study the idea of a computational process.
Computational processes are abstract beings that inhabit computers.
As they evolve, processes manipulate other abstract things called data.
The evolution of a process is directed by a pattern of rulescalled a program.
People create programs to direct processes.
In effectwe conjure the spirits of the computer with our spells.""".split()#语料库
#中文的语句,你可以选择分词,也可以选择分字
vocab = set(raw_text)#集合。词库,里面内容独一无人
vocab_size = len(vocab)
word_to_idx = {word:i for i, word in enumerate(vocab)} #for循环的复合写法,第1次循环,i得到的索引号,word 第1个年
idx_to_word = {i: word for i, word in enumerate(vocab)}
data =[]#获取上下文词,将上下文词作为输入,目标词作为输出。构建训练数据集。
for i in range(CONTEXT_SIZE, len(raw_text)-CONTEXT_SIZE):#(2,60)
context =(
[raw_text[i-(2-j)]for j in range(CONTEXT_SIZE)]
+[raw_text[i+j+ 1] for j in range(CONTEXT_SIZE)])
#获取上下文词(['we"'are"'to''study'])
target = raw_text[i] #获取目标词'about'
data.append((context,target)) #将上下文词和目标词保存到data中[((['we"'are 'to''study'])'about')]
def make_context_vector(context,word_to_idx):#将上下文词转换为one-hot
idxs =[word_to_idx[w] for w in context]
return torch.tensor(idxs,dtype=torch.long)#
class CBOW(nn.Module):
def __init__(self,vocab_size,embedding_dim):
super(CBOW,self).__init__()
self.embeddings=nn.Embedding(vocab_size,embedding_dim)
self.proj=nn.Linear(embedding_dim,128)
self.output=nn.Linear(128,vocab_size)
def forward(self,inputs):
embeds=sum(self.embeddings(inputs)).view(1,-1)
out=F.relu(self.proj(embeds))
out=self.output(out)
nll_prob=F.log_softmax(out,dim=-1)
return nll_prob
device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
model=CBOW(vocab_size,10).to(device)
optimizer=optim.Adam(model.parameters(),lr=0.001)
loss_funcation=nn.NLLLoss()
losses=[]
model.train()
for epoch in tqdm(range(200)):
total_loss=0
for context,target in data:
context_vector=make_context_vector(context,word_to_idx).to(device)
target=torch.tensor([word_to_idx[target]]).to(device)
train_predict=model(context_vector)
loss=loss_funcation(train_predict,target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss+=loss.item()
losses.append(total_loss)
print(losses)
context=['People','create','to','direct']
context_vector=make_context_vector(context,word_to_idx).to(device)
model.eval()
predict=model(context_vector)
max_idx=int(predict.argmax(1))
print(idx_to_word[max_idx])
总结
统计语言模型受限于记忆容量与语义抽象能力,One-Hot 编码的稀疏性与语义孤立问题促使词嵌入技术诞生。Word2Vec 通过 CBOW 和 Skip-Gram 模型,基于上下文共现学习低维语义向量,并借助负采样等优化方法实现高效训练,完成了从符号表示到语义建模的跨越。然而,静态词嵌入难以捕捉序列数据的时序依赖与长期关联,而 ** 循环神经网络(RNN)** 及其改进变体 LSTM(长短期记忆网络)通过动态建模序列中的状态传递与记忆机制,弥补了这一缺陷,成为处理时序数据的核心技术。从词嵌入到 RNN/LSTM,自然语言处理逐步构建起从 "词级语义表示" 到 "序列动态建模" 的完整体系,为后续复杂任务奠定了关键基础。