NLP词向量处理方法

NLP词向量处理方法

借鉴链接附上,如有侵权请联系我:
链接:
https://blog.csdn.net/weixin_55073640/article/details/123465943
https://zhuanlan.zhihu.com/p/114538417

onehot编码

NLP 中最直观,也是到目前为止最常用的词表示方法是 One-hot Representation,这种方法把每个词表示为一个很长的向量。这个向量的维度是词表大小,其中绝大多数元素为 0,只有一个维度的值为 1,这个维度就代表了当前的词。

优点

独热编码解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。它的值只有0和1,不同的类型存储在垂直的空间。

缺点

1.当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且one hot encoding+PCA这种组合在实际中也非常有用。
2.在文本特征表示上有些缺点就非常突出了。首先,它是一个词袋模型,不考虑词与词之间的顺序(文本中词的顺序信息也是很重要的);其次,它假设词与词相互独立(在大多数情况下,词与词是相互影响的);最后,它得到的特征是离散稀疏的。
在这里插入图片描述

Word Embedding

  • 通过一定的方式将词汇映射到指定维度(一般是更高维度)的空间.
  • 广义的word embedding包括所有密集词汇向量的表示方法,如后面要学习的word2vec,即可认为是word embedding的一种.
  • 狭义的word embedding是指在神经网络中加入的embedding层,对整个网络进行训练的同时产生的embedding矩阵(embedding层的参数),这个embedding矩阵就是训练过程中所有输入词汇的向量表示组成的矩阵.
    在这里插入图片描述
word embedding API

torch.nn.Embedding(num_embeddings , embedding_dim)参数介绍:

  1. num_embeddings:词典的大小
  2. embedding_dim : embedding的维度

使用方法:

embedding = nn.Embedding(vocab_size , 128)#实例化,对应下图词表为5000,嵌入维度为128。若使用onehot编码则初始输入向量为4*5000矩阵,经由变换成为了4*128的嵌入向量。
input_embeded = embedding(input)#进行embedding的操作

在这里插入图片描述

数据的形状变化

思考:每个batch中的每个句子有10个词语,经过形状为[20,4]的Word emebedding之后,原来的句子会变成什么形状?
每个词语用长度为4的向量表示,20是指词表大小,与后续变换无关。所以,最终句子会变为[batch_size,10,4]的形状。增加了一个维度,这个维度是embedding的dim。
ps://img-blog.csdnimg.cn/0ba7948884334c35a38a8f4386ad914a.png)

相比One-Hot编码词嵌入具有如下优势:
词嵌入将文本中的词通过一个低维向量来表达,例如128维相比上万维的One-Hot编码,效率上有了质的提升.

在这里插入图片描述

word2Vec方法

word2vec是谷歌提出一种word embedding 的工具或者算法集合。Word2Vec是轻量级的神经网络,其模型仅仅包括输入层、隐藏层和输出层,模型框架根据输入输出的不同,主要包括CBOW和Skip-gram模型。CBOW主要是利用上下文来预测当前词。而Skip-gram就是用当前词来预测上下文。
在这里插入图片描述

CBOW(连续词袋模型)

在这里插入图片描述
上图展现的是simple CBOW模型,即单输入单输出。

  1. Input Layer 是指单个词的词向量one-hot表示。考虑一个词表 V V V,里面每一个词 ω i \omega_i ωi,都有一个编号 i ∈ { 1 , . . . ∣ V ∣ } i\isin\{1,...|V|\} i{1,...∣V},那么此 ω i \omega_i ωi的one-hot表示一个维度为 ∣ V ∣ |V| V的向量,其中第 i i i个元素值非零,其余元素均为0。例如: ω 2 = [ 0 , 1 , 0.... , 0 ] T \omega_2=[0,1,0....,0]^T ω2=[0,1,0....,0]T;
  2. 输入层到隐藏层之间有一个权重矩阵 W W W,隐藏层得到的值是由输入X乘上权重矩阵得到的(细心的人会发现,0-1向量乘上一个矩阵,就相当于选择了权重矩阵的某一行,如图:输入的向量X是[0,0,1,0,0,0], W W W的转置乘上X就相当于从矩阵中选择第3行[2,1,3]作为隐藏层的值)。
  3. 隐藏层到输出层也有一个权重矩阵 W ′ W' W,因此,输出层向量y的每一个值,其实就是隐藏层的向量点乘权重向量 W ′ W' W的每一列,比如输出层的第一个数7,就是向量[2,1,3]和列向量[1,2,1]点乘之后的结果;
  4. 最终的输出需要经过softmax函数,将输出向量中的每一个元素归一化到0-1之间的概率,概率最大的,就是预测的词。
CBOW Multi-Word Context Model

了解了Simple CBOW model之后,扩展到CBOW就很容易了,只是把单个输入换成多个输入罢了(划红线部分)。
在这里插入图片描述
对比可以发现,和simple CBOW不同之处在于,输入由1个词变成了C个词,每个输入 X i k X_{ik} Xik
到达隐藏层都会经过相同的权重矩阵 W W W,隐藏层h的值变成了多个词乘上权重矩阵之后加和求平均值。下列是对网络每一层的分析:

  1. INPUT:输入的是上下文单词的one hot编码。假设单词向量空间的维度为V,即整个词库大小为V,上下文单词窗口的大小为C。所以输入大小 C * V。
  2. Hidden:假设hidden layer最终得到的词向量的维度大小为N,INPUT到Hidden的权值共享矩阵(“共享”即每个词乘的W一样)为W。W的大小为 V ∗ N,并且初始化。
    我们将C个1 * V大小的向量分别同一个上述所说的V ∗ N大小的W相乘,得到的是C个1 ∗ N 大小的向量。再将这C个1 ∗ N大小的向量取平均,得到一个1 ∗ N 大小的向量。
  3. OUTPUT:初始化输出权重矩阵大小为N ∗ V的W’将Hidden layer1 ∗ N 的向量与W’相乘,并且用softmax处理,得到1 ∗ V的向量,此向量的每一维代表词库中的一个词。概率中最大的index所代表的单词即为预测出的中间词。
代码
1.引入所需要的库
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
2.导入对应预测文本内容,并进行相应处理(创建词汇表,对单词建立索引,设定预测窗口并完成索引标记)

在此1.txt设置为
在这里插入图片描述``

path = "1.txt"
f = open(path, 'r', encoding='utf-8', errors='ignore')
text = []
piece = ''
for line in f:
    for uchar in line:
        if uchar == '\n':
            continue
        if uchar == '.' or uchar == '?' or uchar == '!':
            text.append(piece)
            piece = ''
        else:
            piece = piece + uchar

word_list = " ".join(text).split()

# 将分词后的结果去重
vab = list(set(word_list))

# 对单词建立索引
word_dict = {w: i for i, w in enumerate(vab)}  # 单词-索引

data = []
for i in range(2, len(word_list) - 2):
    context = [word_list[i - 2], word_list[i - 1],
               word_list[i + 1], word_list[i + 2]]
    target = word_list[i]
    data.append((context, target))

data打印出来是这样的,即前面数组中是上下文,后面的单个单词就是预测的单词。此处预测窗口是5 (4+1),一般还可以选择3。
在这里插入图片描述

3.定义CBOW网络模型
class CBOW(nn.Module):
    '''''
    word_size相当于V;
    embedding_dim:嵌入词向量的维度;
    context_size相当于一半上下文的大小。CBOW模型中使用的上下文单词数目/2,也可以理解为输入单词的窗口大小。即若为2,则窗口为5.
    '''''

    def __init__(self, word_size, embedding_dim, context_size):
        super(CBOW, self).__init__()
        self.embeddings = nn.Embedding(word_size, embedding_dim)
        self.linear1 = nn.Linear(2 * context_size * embedding_dim, 128)
        # N为128
        self.linear2 = nn.Linear(128, word_size)

    def forward(self, inputs):
        embeds = self.embeddings(inputs).view((1, -1))
        out = F.relu(self.linear1(embeds))
        out = self.linear2(out)
        probs = F.log_softmax(out, dim=1)
        return probs
4.定义相关参数以及网络模型训练
EMBEDDING_DIM = 10
CONTEXT_SIZE = 2
losses = []
loss_function = nn.NLLLoss()
model = CBOW(len(vab), EMBEDDING_DIM, CONTEXT_SIZE)
optimizer = optim.SGD(model.parameters(), lr=0.01)

for epoch in range(30):
    total_loss = 0
    for context, target in data:
        # 准备好进入模型的数据
        # 这段代码使用PyTorch将输入的上下文列表context中的每个单词转换为对应的整数索引,并将结果存储在列表idxs中。
        idxs = [word_dict[w] for w in context]
        context_idx = torch.tensor(idxs, dtype=torch.long)

        # 梯度置零
        model.zero_grad()

        # 进入模型训练
        log_probs = model(context_idx)

        # 计算损失函数
        loss = loss_function(log_probs, torch.tensor([word_dict[target]], dtype=torch.long))

        # 反向传播并更新梯度
        loss.backward()
        optimizer.step()

        total_loss += loss.item()
    print("Epoch:", epoch+1, " Loss:", total_loss)
    losses.append(total_loss)
print(losses)

输出结果

Epoch: 1  Loss: 5.777832865715027
Epoch: 2  Loss: 5.395304918289185
Epoch: 3  Loss: 5.041249632835388
Epoch: 4  Loss: 4.7081825733184814
Epoch: 5  Loss: 4.397132158279419
Epoch: 6  Loss: 4.102055191993713
Epoch: 7  Loss: 3.825270652770996
Epoch: 8  Loss: 3.561501145362854
Epoch: 9  Loss: 3.3132278323173523
Epoch: 10  Loss: 3.078549087047577
Epoch: 11  Loss: 2.8582298159599304
Epoch: 12  Loss: 2.651750326156616
Epoch: 13  Loss: 2.457363486289978
Epoch: 14  Loss: 2.2783106565475464
Epoch: 15  Loss: 2.1099923849105835
Epoch: 16  Loss: 1.9543667435646057
Epoch: 17  Loss: 1.8106426000595093
Epoch: 18  Loss: 1.6802804470062256
Epoch: 19  Loss: 1.5579476058483124
Epoch: 20  Loss: 1.4472983479499817
Epoch: 21  Loss: 1.3455210030078888
Epoch: 22  Loss: 1.2528588473796844
Epoch: 23  Loss: 1.1679846048355103
Epoch: 24  Loss: 1.0897139012813568
Epoch: 25  Loss: 1.0190409421920776
Epoch: 26  Loss: 0.9539474844932556
Epoch: 27  Loss: 0.894445538520813
Epoch: 28  Loss: 0.8403486013412476
Epoch: 29  Loss: 0.7909693568944931
Epoch: 30  Loss: 0.7449930906295776
[5.777832865715027, 5.395304918289185, 5.041249632835388, 4.7081825733184814, 4.397132158279419, 4.102055191993713, 3.825270652770996, 3.561501145362854, 3.3132278323173523, 3.078549087047577, 2.8582298159599304, 2.651750326156616, 2.457363486289978, 2.2783106565475464, 2.1099923849105835, 1.9543667435646057, 1.8106426000595093, 1.6802804470062256, 1.5579476058483124, 1.4472983479499817, 1.3455210030078888, 1.2528588473796844, 1.1679846048355103, 1.0897139012813568, 1.0190409421920776, 0.9539474844932556, 0.894445538520813, 0.8403486013412476, 0.7909693568944931, 0.7449930906295776]

Skip-Gram

Skip-gram model是通过输入一个词去预测多个词的概率。输入层到隐藏层的原理和simple CBOW一样,不同的是隐藏层到输出层,损失函数变成C个词损失函数的总和,权重矩阵W’还是共享的。
在这里插入图片描述
下列是对网络每一层的分析:

  1. INPUT:输入的是1 * V的one-hot向量x(其中V为词典大小)。
  2. Hidden:构建一个大小为V * N的嵌入矩阵W(其中N为词向量的维度),所以用这个W和x相乘,得到最终Hidden的输出,大小为1 * N。
  3. OUTPUT:构建一个大小为N * V的矩阵W’,用W’和Hidden的输出相乘得到最终的输出,大小为1 * V。C个向量就是C*V。

skip-gram的训练过程不是一次性用中心词预测窗口数个词,而是中心词和一个周围词组成一个训练样本。比如当我们指定窗口为2,那么当前词左右的周围词共有4个。有4个周围词的话就有4个样本,即[中心词,周围词1]、[中心词,周围词2]……这也决定了上述朴素skip_gram模型的输出是大小为1 * V。(就是这4个1 * V组成了1个当前词预测出上下文4个词)

代码
1.引入所需要的库
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import torch.utils.data as Data
2.文本预处理(1.txt与前文一样)
# 文本预处理
path = "1.txt"
f = open(path, 'r', encoding= 'utf-8', errors= 'ignore')
text = []
piece = ''
for line in f:
    for uchar in line:
        if uchar == '\n':
            continue
        if uchar == '.' or uchar == '?' or uchar == '!':
            text.append(piece)
            piece = ''
        else:
            piece = piece + uchar
#print(text)
word_list = " ".join(text).split()
print(word_list) #结果['I', 'love', 'you', 'My', 'mom', 'is', 'beautiful']
#将分词后的结果去重
vab = list(set(word_list))
print(vab)  #结果['I', 'is', 'My', 'mom', 'you', 'love', 'beautiful']
#对单词建立索引
word_dict = {w:i for i, w in enumerate(vab)} #单词-索引
print(word_dict)  #结果{'I': 0, 'is': 1, 'My': 2, 'mom': 3, 'you': 4, 'love': 5, 'beautiful': 6}
3.相关参数设定与数据预处理
# 相关参数
dtype = torch.FloatTensor
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
batch_size = 8
EMBEDDING_DIM = 4  # 词向量的维度是4
CONTEXT_SIZE = 2  # window size
voc_size = len(vab)

# 数据预处理
data = []
for idx in range(CONTEXT_SIZE, len(word_list) - CONTEXT_SIZE):
    center = word_dict[word_list[idx]]  # 中心词
    # 中心词和每个周围词组成一个训练样本
    for j in range(idx - CONTEXT_SIZE, idx + CONTEXT_SIZE + 1):
        if j == idx:
            continue
        data.append([center, word_dict[word_list[j]]])
print(data)  #结果[[4, 0], [4, 5], [4, 2], [4, 3], [2, 5], [2, 4], [2, 3], [2, 1], [3, 4], [3, 2], [3, 1], [3, 6]]


input_data = []
output_data = []
for i in range(len(data)):
    # input_data转换为one-hot形式,output_data合成一个list
    input_data.append(np.eye(voc_size)[data[i][0]])
    output_data.append(data[i][1])

input_data, output_data = torch.Tensor(input_data), torch.LongTensor(output_data)
dataset = Data.TensorDataset(input_data, output_data)
loader = Data.DataLoader(dataset, batch_size, True)

print(input_data,output_data)的结果是这样的,对照print(data) #结果[[4, 0], [4, 5], [4, 2], [4, 3], [2, 5], [2, 4], [2, 3], [2, 1], [3, 4], [3, 2], [3, 1], [3, 6]],帮助理解
在这里插入图片描述

4.构建Skip_gram模型
class Skip_gram(nn.Module):
    def __init__(self):
        super(Skip_gram, self).__init__()
        #self.W是一个形状为(voc_size, EMBEDDING_DIM)的可学习权重矩阵,表示输入词向量与隐藏层之间的转化。
        #voc_size代表词汇表的大小,EMBEDDING_DIM代表词向量的维度。
        self.W = nn.Parameter(torch.randn(voc_size, EMBEDDING_DIM).type((dtype)))
        #self.V是一个形状为(EMBEDDING_DIM, voc_size)的可学习权重矩阵,表示隐藏层与输出词向量之间的转化。
        self.V = nn.Parameter(torch.randn(EMBEDDING_DIM, voc_size).type((dtype)))

    #forward方法接受一个输入input,它是一个形状为(batch_size, input_size)的张量,其中input_size代表输入的词向量维度。
    #在forward方法中,首先将输入词向量与权重矩阵self.W相乘,得到隐藏层的表示hidden。这里使用了torch.matmul函数进行矩阵乘法运算。
    #接着,将隐藏层表示与权重矩阵self.V相乘,得到输出的词向量表示output。
    def forward(self, input):
        hidden = torch.matmul(input, self.W)
        output = torch.matmul(hidden, self.V)
        return output
5.开始训练啦
model = Skip_gram().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=0.01)


for epoch in range(1000):
    total_loss = 0
    for i , (x, y) in enumerate(loader):
        x = x.to(device)
        y = y.to(device)
        pred = model(x)
        loss = criterion(pred, y)
        # i表示当前的索引(即批次数),这里是两批,data=12,batch_size=8,(8+4)
        if (epoch + 1) % 50 == 0:
            print(epoch + 1, i, loss.item())
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print("Epoch:", epoch+1, " Loss:", total_loss)
输出结果
Epoch: 1  Loss: 8.472830057144165
Epoch: 2  Loss: 7.501566171646118
Epoch: 3  Loss: 8.09452748298645
Epoch: 4  Loss: 7.4987571239471436
Epoch: 5  Loss: 7.117967128753662
Epoch: 6  Loss: 6.324106812477112
Epoch: 7  Loss: 7.214094400405884
Epoch: 8  Loss: 6.785226345062256
Epoch: 9  Loss: 7.5581488609313965
Epoch: 10  Loss: 6.545481443405151
Epoch: 11  Loss: 5.821583271026611
Epoch: 12  Loss: 6.536340236663818
Epoch: 13  Loss: 5.82330584526062
Epoch: 14  Loss: 5.830262660980225
Epoch: 15  Loss: 5.894265174865723
Epoch: 16  Loss: 6.236735105514526
Epoch: 17  Loss: 5.859234809875488
Epoch: 18  Loss: 5.828025817871094
Epoch: 19  Loss: 5.150759935379028
Epoch: 20  Loss: 4.7668105363845825
Epoch: 21  Loss: 5.132047414779663
Epoch: 22  Loss: 4.6761651039123535
Epoch: 23  Loss: 4.985729455947876
Epoch: 24  Loss: 5.049295663833618
Epoch: 25  Loss: 4.684504270553589
Epoch: 26  Loss: 4.623528957366943
Epoch: 27  Loss: 4.766409873962402
Epoch: 28  Loss: 4.5114521980285645
Epoch: 29  Loss: 4.711355209350586
Epoch: 30  Loss: 4.440001010894775
Epoch: 31  Loss: 4.551765203475952
Epoch: 32  Loss: 4.397887468338013
Epoch: 33  Loss: 4.356009483337402
Epoch: 34  Loss: 4.176387310028076
Epoch: 35  Loss: 4.252211570739746
Epoch: 36  Loss: 4.081496477127075
Epoch: 37  Loss: 4.089355945587158
Epoch: 38  Loss: 4.025074005126953
Epoch: 39  Loss: 3.833372473716736
Epoch: 40  Loss: 3.8738949298858643
Epoch: 41  Loss: 3.959350824356079
Epoch: 42  Loss: 4.033707618713379
Epoch: 43  Loss: 3.8661465644836426
Epoch: 44  Loss: 3.8452646732330322
Epoch: 45  Loss: 3.6065515279769897
Epoch: 46  Loss: 3.665121912956238
Epoch: 47  Loss: 3.6360924243927
Epoch: 48  Loss: 3.7820838689804077
Epoch: 49  Loss: 3.993691563606262
50 0 1.8122646808624268
50 1 1.933388590812683
Epoch: 50  Loss: 3.74565327167511 #为一个批次的每个batch相加之和
......
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值