一、介绍

内容

将接触现代 NLP 技术的基础:词向量技术。

第一个是构建一个简单的 N-Gram 语言模型,它可以根据 N 个历史词汇预测下一个单词,从而得到每一个单词的向量表示。

第二个将接触到现代词向量技术常用的模型 Word2Vec。在实验中将以小说《三体》为例,展示了小语料在 Word2Vec 模型中能够取得的效果。

在最后一个将加载已经训练好的一个大规模词向量,并利用这些词向量来做一些简单的运算和测试,以探索词向量中包含的语义信息。

知识点

  • N-Gram(NPLM) 语言模型
  • Word2Vec 词向量模型
  • 使用 Word2Vec 词向量进行语义运算

二、N-Gram 词向量模型

引入相关包

# 加载必要的程序包
# PyTorch的程序包
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim # 数值运算和绘图的程序包
import numpy as np
import matplotlib.pyplot as plt import matplotlib

值得注意的是 sklearn 包,在本次实验中将用到其中的 PCA 降维算法。

# 加载机器学习的软件包
from sklearn.decomposition import PCA #加载‘结巴’中文分词软件包 import jieba #加载正则表达式处理的包
import re %matplotlib inline

文本预处理

读入原始文件

下载数据 网盘链接:https://pan.baidu.com/s/1D1YI4h7k-xPpPbFYQx4E8A 提取码:osk5

#读入原始文件

f = open("三体.txt", 'r')
text = str(f.read())
f.close() text

接下来用“结巴(jieba)”分词工具来分词,并过滤掉所有的标点符号

# 分词
temp = jieba.lcut(text)
words = []
for i in temp:
#过滤掉所有的标点符号
i = re.sub("[\s+\.\!\/_,$%^*(+\"\'“”《》?“]+|[+——!,。?、~@#¥%……&*():]+", "", i)
if len(i) > 0:
words.append(i)
print(len(words))
words

N-Gram 词向量模型的原理是利用一个人工神经网络来根据前 N 个单词来预测下一个单词,从而得到每个单词的词向量。

# 构建三元组列表.  每一个元素为: ([ i-2位置的词, i-1位置的词 ], 下一个词)
# 我们选择的Ngram中的N,即窗口大小为2
trigrams = [([words[i], words[i + 1]], words[i + 2]) for i in range(len(words) - 2)]
# 打印出前三个元素看看
print(trigrams[:3])

将每个单词进行编码 构造词典

对上面包含所有词汇的列表 word 取一个独立集 set()

根据词典做两个速查表,一个是根据单词索引其编号,一个是根据编号索引单词

# 得到词汇表
vocab = set(words)
print(len(vocab))
# 两个字典,一个根据单词索引其编号,一个根据编号索引单词
# word_to_idx中的值包含两部分,一部分为id,另一部分为单词出现的次数
# word_to_idx中的每一个元素形如:{w:[id, count]},其中w为一个词,id为该词的编号,count为该单词在words全文中出现的次数
word_to_idx = {}
idx_to_word = {}
ids = 0 # 对全文循环,构建这两个字典
for w in words:
cnt = word_to_idx.get(w, [ids, 0])
if cnt[1] == 0:
ids += 1
cnt[1] += 1
word_to_idx[w] = cnt
idx_to_word[ids] = w word_to_idx

构造模型并训练

下面将构建一个三层的神经网络模型:

  • 1、输入层:embedding 层。

    • 这一层的作用是:先将输入单词的编号映射为一个 one hot 编码的向量,形如:001000,维度为单词表大小。
    • 然后,embedding 会通过一个线性的神经网络层映射出这个词的向量表示,输出为 embedding_dim。
  • 2、隐藏层:线性层 + 非线性 ReLU。
    • 从 embedding_dim 维度到128维度,然后经过非线性 ReLU 函数
  • 3、输出层:线性层 + Log Softmax。
    • 从 128 维度到单词表大小维度,然后 log softmax 函数,给出预测每个单词的概率。
class NGram(nn.Module):

    def __init__(self, vocab_size, embedding_dim, context_size):
super(NGram, self).__init__()
self.embeddings = nn.Embedding(vocab_size, embedding_dim) # 输入层
self.linear1 = nn.Linear(context_size * embedding_dim, 128) # 隐藏层
self.linear2 = nn.Linear(128, vocab_size) # 输出层 def forward(self, inputs):
#嵌入运算,嵌入运算在内部分为两步:将输入的单词编码映射为one hot向量表示,然后经过一个线性层得到单词的词向量
#inputs的尺寸为:1*context_size
embeds = self.embeddings(inputs)
#embeds的尺寸为: context_size*embedding_dim
embeds = embeds.view(1, -1)
#此时embeds的尺寸为:1*embedding_dim
# 线性层加ReLU
out = self.linear1(embeds)
out = F.relu(out)
#此时out的尺寸为1*128 # 线性层加Softmax
out = self.linear2(out)
#此时out的尺寸为:1*vocab_size
log_probs = F.log_softmax(out, dim=1)
return log_probs
def extract(self, inputs):
embeds = self.embeddings(inputs)
return embeds

NPLM 模型的训练是非常非常缓慢的,在训练代码的后面提供了预训练好的模型供大家加载使用。

losses = [] #纪录每一步的损失函数
criterion = nn.NLLLoss() #运用负对数似然函数作为目标函数(常用于多分类问题的目标函数)
model_ng = NGram(len(vocab), 10, 2) #定义NGram模型,向量嵌入维数为10维,N(窗口大小)为2 optimizer = optim.SGD(model_ng.parameters(), lr=0.001) #使用随机梯度下降算法作为优化器 # 因为模型的训练速度非常之慢
# 所以在这里把迭代训练的次数修改为0
# 如果你真的好奇有多慢,欢迎修改这个值,一定不会令你失望的
for epoch in range(0):
total_loss = torch.Tensor([0])
for context, target in trigrams:
# 准备好输入模型的数据,将词汇映射为编码
context_idxs = [word_to_idx[w][0] for w in context] # 包装成PyTorch的Variable
context_var = Variable(torch.LongTensor(context_idxs)) # 清空梯度:注意PyTorch会在调用backward的时候自动积累梯度信息,故而每隔周期要清空梯度信息一次。
optimizer.zero_grad() # 用神经网络做计算,计算得到输出的每个单词的可能概率对数值
log_probs = model_ng(context_var) # 计算损失函数,同样需要把目标数据转化为编码,并包装为Variable
loss = criterion(log_probs, Variable(torch.LongTensor([word_to_idx[target][0]]))) # 梯度反传
loss.backward() # 对网络进行优化
optimizer.step() # 累加损失函数值
total_loss += loss.data
losses.append(total_loss)
print('第{}轮,损失函数为:{:.2f}'.format(epoch, total_loss.numpy()[0]))

在这里加载预训练的模型。(数据在网盘中)

model_ng = torch.load('NPLM_Ready.mdl')

将向量投影到二维平面进行可视化

下面首先观察 NPLM 模型的网络结构,以便从 NPLM 的输入层中取出词向量。

print(model_ng)

下面将调用模型的 extract 函数提取出来所有单词的词向量。最后的词向量就存储到了 vec 中。

# 从训练好的模型中提取每个单词的向量
vec = model_ng.extract(Variable(torch.LongTensor([v[0] for v in word_to_idx.values()])))
vec = vec.data.numpy()

但此时获得的词向量仍然是高维度的,想要直观的观察到词汇的分布,还要对它进行降维。可以通过 PCA 降维的方法将 vec 中的向量展示在二维世界中。

# 利用PCA算法进行降维
X_reduced = PCA(n_components=2).fit_transform(vec) # 绘制所有单词向量的二维空间投影
fig = plt.figure(figsize = (30, 20))
ax = fig.gca()
ax.set_facecolor('white')
ax.plot(X_reduced[:, 0], X_reduced[:, 1], '.', markersize = 1, alpha = 0.4, color = 'black') # 绘制几个特殊单词的向量
words = ['智子', '地球', '三体', '质子', '科学', '世界', '文明', '太空', '加速器', '平面', '宇宙', '信息'] # 设置中文字体,否则无法在图形上显示中文
zhfont1 = matplotlib.font_manager.FontProperties(fname='./华文仿宋.ttf', size=16)
for w in words:
if w in word_to_idx:
ind = word_to_idx[w][0]
xy = X_reduced[ind]
plt.plot(xy[0], xy[1], '.', alpha =1, color = 'red')
plt.text(xy[0], xy[1], w, fontproperties = zhfont1, alpha = 1, color = 'black')

那么,获得的词向量好不好呢?我们通常是去查看在向量上相似的词是否具有相似的语义信息,来验证训练出的词向量是高质量的(包含正确的语义信息)。

# 定义计算cosine相似度的函数
def cos_similarity(vec1, vec2): norm1 = np.linalg.norm(vec1)
norm2 = np.linalg.norm(vec2)
norm = norm1 * norm2
dot = np.dot(vec1, vec2)
result = dot / norm if norm > 0 else 0
return result # 在所有的词向量中寻找到与目标词(word)相近的向量,并按相似度进行排列
def find_most_similar(word, vectors, word_idx):
vector = vectors[word_to_idx[word][0]]
simi = [[cos_similarity(vector, vectors[num]), key] for num, key in enumerate(word_idx.keys())]
sort = sorted(simi)[::-1]
words = [i[1] for i in sort]
return words # 与智子靠近的词汇
find_most_similar('智子', vec, word_to_idx)

似乎在词义上并没有什么相近性,这说明本次实验中的这个 NPLM 模型学出来的词向量并不好。实际上,训练好的词向量需要大规模语料,同时还要训练足够长的时间。在这两点上,实验中的小模型都没有做到,所以训练出来的词向量并不理想。

三、Word2Vec

引入 Word2Vec 相关包

Gensim 是一个面向自然语言处理领域的 Python 包,包含了 Word2Vec、LDA 主题模型等常用的自然语言处理功能的函数库。

#加载Word2Vec的软件包
import gensim as gensim
from gensim.models import Word2Vec
from gensim.models.keyedvectors import KeyedVectors
from gensim.models.word2vec import LineSentence

用小语料训练自己的词向量

首先载入语料库,并进行分词

# 读入文件、分词,形成一句一句的语料
# 注意跟前面处理不一样的地方在于,我们一行一行地读入文件,从而自然利用行将文章分开成“句子”
f = open("三体.txt", 'r')
lines = []
for line in f:
temp = jieba.lcut(line)
words = []
for i in temp:
#过滤掉所有的标点符号
i = re.sub("[\s+\.\!\/_,$%^*(+\"\'””《》]+|[+——!,。?、~@#¥%……&*():;‘]+", "", i)
if len(i) > 0:
words.append(i)
if len(words) > 0:
lines.append(words)

lines 是输入的已经变成列表的单词,size 是拟嵌入向量的维度;window 表示的是上下文窗口大小,也就是 N-gram 模型中的那个 N;min_count 为保留最少多少的低频词,如果它等于 0,那便意味着算法将计算所有词的词向量,无论它的出现次数是多少。

# 调用Word2Vec的算法进行训练。
# 参数分别为:size: 嵌入后的词向量维度;window: 上下文的宽度,min_count为考虑计算的单词的最低词频阈值 model = Word2Vec(lines, size = 20, window = 2 , min_count = 0)

观察小语料 Word2Vec 词向量的效果

首先对词向量进行降维,投影到二维空间

# 首先将词向量使用 PCA 降维,投影到二维空间
rawWordVec = []
word2ind = {}
for i, w in enumerate(model.wv.vocab):
rawWordVec.append(model[w])
word2ind[w] = i
rawWordVec = np.array(rawWordVec)
X_reduced = PCA(n_components=2).fit_transform(rawWordVec)

绘制出使用 Word2Vec 训练出的词向量星空

# 绘制星空图
# 绘制所有单词向量的二维空间投影
fig = plt.figure(figsize = (15, 10))
ax = fig.gca()
ax.set_facecolor('black')
ax.plot(X_reduced[:, 0], X_reduced[:, 1], '.', markersize = 1, alpha = 0.3, color = 'white') # 绘制几个特殊单词的向量
words = ['智子', '地球', '三体', '质子', '科学', '世界', '文明', '太空', '加速器', '平面', '宇宙', '进展','的'] # 设置中文字体,否则无法在图形上显示中文
zhfont1 = matplotlib.font_manager.FontProperties(fname='./华文仿宋.ttf', size=16)
for w in words:
if w in word2ind:
ind = word2ind[w]
xy = X_reduced[ind]
plt.plot(xy[0], xy[1], '.', alpha =1, color = 'red')
plt.text(xy[0], xy[1], w, fontproperties = zhfont1, alpha = 1, color = 'yellow')

下面找出与“智子”最相近的 20 个词

model.wv.most_similar('智子', topn = 20)

从上面打印出的相近词中可以观察到,使用小语料训练的 Word2Vec 词向量仍不能达到理想的效果。原因有可能还是语料太小,或者对于词语的预处理工作还需要加强

四、使用预训练的大规模 Word2Vec 词向量

加载大规模词向量

注:以下代码请在空闲内存大于3G的环境实验

下面实验要用到的词向量是由微博、人民日报、上海热线、汽车之家等多处的大量语料训练而成,包含 1366130 个词向量。规模如此巨大的中文词向量是非常难得的。感谢台湾第一家大数据公司 AsiaMiner 的联合创始人 尹相志 老师提供本词向量文件。大家可以自行搜索下载数据。

词向量文件的名字是:vectors.bin,首先将它加载到内存中,因为词向量较多所以加载需要一小点时间。

# 加载词向量
word_vectors = KeyedVectors.load_word2vec_format('vectors.bin', binary=True, unicode_errors='ignore')
len(word_vectors.vocab)

观察大规模词向量空间中的语义信息

仍然是使用 PCA 降维将词向量投影到 2 维空间

# PCA降维
rawWordVec = []
word2ind = {}
for i, w in enumerate(word_vectors.vocab):
rawWordVec.append(word_vectors[w])
word2ind[w] = i
rawWordVec = np.array(rawWordVec)
X_reduced = PCA(n_components=2).fit_transform(rawWordVec)

降维后将所有的词向量绘制在“星空”中

# 绘制星空图
# 绘制所有的词汇
fig = plt.figure(figsize = (30, 15))
ax = fig.gca()
ax.set_facecolor('black')
ax.plot(X_reduced[:, 0], X_reduced[:, 1], '.', markersize = 1, alpha = 0.1, color = 'white') ax.set_xlim([-12,12])
ax.set_ylim([-10,20]) # 选择几个特殊词汇,不仅画它们的位置,而且把它们的临近词也画出来
words = {'徐静蕾','吴亦凡','物理','红楼梦','量子'}
all_words = []
for w in words:
lst = word_vectors.most_similar(w)
wds = [i[0] for i in lst]
metrics = [i[1] for i in lst]
wds = np.append(wds, w)
all_words.append(wds) zhfont1 = matplotlib.font_manager.FontProperties(fname='./华文仿宋.ttf', size=16)
colors = ['red', 'yellow', 'orange', 'green', 'cyan', 'cyan']
for num, wds in enumerate(all_words):
for w in wds:
if w in word2ind:
ind = word2ind[w]
xy = X_reduced[ind]
plt.plot(xy[0], xy[1], '.', alpha =1, color = colors[num])
plt.text(xy[0], xy[1], w, fontproperties = zhfont1, alpha = 1, color = colors[num])

注意到,二维空间上看起来靠近的点不一定在高维空间上也靠近。因此我们不能简单的根据二维空间上的靠近程度来判断词语意思的相近程度,而更应该相信颜色,因为颜色是根据相似度表示出来的。

有趣的词向量语义运算

观察一下求近似词的效果

# 查看相似词
word_vectors.most_similar('物理', topn = 20)
# 女人-男人=?-国王
words = word_vectors.most_similar(positive=['女人', '国王'], negative=['男人'])
words
# 北京-中国=?-俄罗斯
words = word_vectors.most_similar(positive=['北京', '俄罗斯'], negative=['中国'])
words
# 自然科学-物理学=?-政治学
words = word_vectors.most_similar(positive=['自然科学', '政治学'], negative=['物理学'])
words
# 王菲-章子怡=?-汪峰
words = word_vectors.most_similar(positive=['王菲', '汪峰'], negative=['章子怡'])
words

PyTorch基础——词向量(Word Vector)技术的更多相关文章

  1. NLP之词向量

    1.对词用独热编码进行表示的缺点 向量的维度会随着句子中词的类型的增大而增大,最后可能会造成维度灾难2.任意两个词之间都是孤立的,仅仅将词符号化,不包含任何语义信息,根本无法表示出在语义层面上词与词之 ...

  2. AAAI 2018 论文 | 蚂蚁金服公开最新基于笔画的中文词向量算法

    AAAI 2018 论文 | 蚂蚁金服公开最新基于笔画的中文词向量算法 2018-01-18 16:13蚂蚁金服/雾霾/人工智能 导读:词向量算法是自然语言处理领域的基础算法,在序列标注.问答系统和机 ...

  3. 斯坦福NLP课程 | 第1讲 - NLP介绍与词向量初步

    作者:韩信子@ShowMeAI,路遥@ShowMeAI,奇异果@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www. ...

  4. lucene正向索引(续)——每次commit会形成一个新的段,段"_1"的域和词向量信息可能存在"_0.fdt"和"_0.fdx”中

    DocStoreOffset DocStoreSegment DocStoreIsCompoundFile 对于域(Stored Field)和词向量(Term Vector)的存储可以有不同的方式, ...

  5. PyTorch在NLP任务中使用预训练词向量

    在使用pytorch或tensorflow等神经网络框架进行nlp任务的处理时,可以通过对应的Embedding层做词向量的处理,更多的时候,使用预训练好的词向量会带来更优的性能.下面分别介绍使用ge ...

  6. 利用词向量进行推理(Reasoning with word vectors)

    The amazing power of word vectors | the morning paper (acolyer.org) What is a word vector? At one le ...

  7. Word Representations 词向量

    常用的词向量方法word2vec. 一.Word2vec 1.参考资料: 1.1) 总览 https://zhuanlan.zhihu.com/p/26306795 1.2) 基础篇:  深度学习wo ...

  8. 机器学习之路: python 实践 word2vec 词向量技术

    git: https://github.com/linyi0604/MachineLearning 词向量技术 Word2Vec 每个连续词汇片段都会对后面有一定制约 称为上下文context 找到句 ...

  9. pytorch中如何使用预训练词向量

    不涉及具体代码,只是记录一下自己的疑惑. 我们知道对于在pytorch中,我们通过构建一个词向量矩阵对象.这个时候对象矩阵是随机初始化的,然后我们的输入是单词的数值表达,也就是一些索引.那么我们会根据 ...

随机推荐

  1. windows Driver 查询指定键值

    NTSTATUS status; HANDLE hKey = NULL; OBJECT_ATTRIBUTES oa; UNICODE_STRING strPath = RTL_CONSTANT_STR ...

  2. 精选干货 在java中创建kafka

    这个详细的教程将帮助你创建一个简单的Kafka生产者,该生产者可将记录发布到Kafka集群. 通过优锐课的java学习架构分享中,在本教程中,我们将创建一个简单的Java示例,该示例创建一个Kafka ...

  3. jQuery下拉框联动(JQ遍历&JQ中DOM操作)

    1.下载jQuery,并导入:https://blog.csdn.net/weixin_44718300/article/details/88746796 2.代码实现: <!DOCTYPE h ...

  4. 【机器学习实战学习笔记(1-1)】k-近邻算法原理及python实现

    笔者本人是个初入机器学习的小白,主要是想把学习过程中的大概知识和自己的一些经验写下来跟大家分享,也可以加强自己的记忆,有不足的地方还望小伙伴们批评指正,点赞评论走起来~ 文章目录 1.k-近邻算法概述 ...

  5. 干货|微软远程桌面服务蠕虫漏洞(CVE-2019-1182)分析

    2019年8月,微软发布了一套针对远程桌面服务的修复程序,其中包括两个关键的远程执行代码(RCE)漏洞,CVE-2019-1181和CVE-2019-1182.与之前修复的"BlueKeep ...

  6. linux 用户与文件常用命令

    用户与文件 su :切换到超级用户 su - l chesney : 切换到chesney用户 sudo usermod -G sudo -a chesney:把chesney 加入到sudo组 su ...

  7. Unity 协程运行时的监控和优化

    我是快乐的搬运工: http://gulu-dev.com/post/perf_assist/2016-12-20-unity-coroutine-optimizing#toc_0 --------- ...

  8. C 的printf函数

    头文件 #include <stdio.h> printf函数是最常用的格式化输出函数,原型为:int printf(char *format,......); printf函数会根据参数 ...

  9. vue移动端点击一个元素缩小,松手的时候元素恢复正常

    active伪类解决 HTML代码 <div class='box'> </div> CSS代码 .box { width: 100px; height: 100px; bac ...

  10. python刷LeetCode:9. 回文数

    难度等级:简单 题目描述: 判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121输出: true示例 2: 输入: -121输出: fa ...