代码地址:https://github.com/liangyming/NLP-Word2Vec.git

1. 什么是Word2Vec

Word2vec是Google开源的将词表征为实数值向量的高效工具,其利用深度学习的思想,可以通过训练,把对词的处理简化为K维向量空间中的向量运算。简单来说,Word2Vec其实就是通过学习文本语料来用词向量的方式表示词的语义信息,即通过一个高维向量空间使得语义上相似的单词在该空间内距离很近。比如下图中的dog和cat同属于一类、tree和flower同属于一类,因此在语义上有关联的词在向量空间上也比较接近。

2. Skip-Gram模型

Word2Vec包括两种类型,分别是通过上下文去预测中间词的CBOW和通过中间词预测上下文的Skip-Gram。如下图分别为他们的模型原理图。Skip-Gram模型也是一种简单的神经网络结构,但此模型训练好后并不会直接用于具体任务,我们需要的是获取模型的隐藏层参数,这些参数即为词向量。

接下来举个例子看看算法运作流程:

  • 假设有句子I like nlp very much
  • 假设中心词为nlp,则模型的输入为nlp,设参数窗口大小windows=2,那么窗口内的上下文词,即背景词为[‘I’,‘like’,‘very’,‘much’]
  • 模型要做的就是通过中心词,计算窗口内的背景词的条件概率,即为: P ( P( P(“I”,“like”,“very”,“much” ∣ | “nlp” ) ) )
  • 假设给定中心词时,背景词之间相互独立,则可以进一步得到:
    P ( P( P(“I” ∣ | “nlp” ) ⋅ P ( )\cdot P( )P(“like” ∣ | “nlp” ) ⋅ P ( )\cdot P( )P(“very” ∣ | “nlp” ) ⋅ P ( )\cdot P( )P(“much” ∣ | “nlp” ) ) )

2.1 One-hot编码

因为计算机没法直接处理文本数据,因此我们需要将文本数据通过One-hot编码转换成数值型数据,还是以句子I like nlp very much为例,句子中每个词可以用5维(词表大小)的One-hot向量表示,比如nlp是词表中的第3个词,则表示为[0,0,1,0,0],编码后的矩阵为 n × n n\times n n×n维度, n n n为词表大小。

2.2 lookup查找表

为了将One-hot编码映射到词向量空间,我们需要通过lookup表建立这个映射过程,这个过程叫Word embedding,如下图的例子就是将词表中第4个词映射到对应词向量空间,此例子中词向量为3维,通过矩阵运算可以很容易建立对应映射关系,其中第二个矩阵就是我们的词向量矩阵,而映射过程其实就是词向量矩阵中的第几行就是词表中第几个词的词向量表示

2.3 负采样

负采样(negative sampling)是为了解决训练计算速度的问题,提出的策略。选为做负样本的词,一般叫做噪声词。噪声词的采样是根据词的概率来决定的,出现概率高的单词容易被选为负样本。为了增加一些出现频率少的词被选中的概率,减小常见词被选中的概率,通常按照以下公式采样。
P ( w i ) = f ( w i ) 3 / 4 ∑ j = 0 n ( f ( w j ) 3 / 4 ) P(w_i)=\frac{f(w_i)^{3/4}}{\sum_{j=0}^n(f(w_j)^{3/4})} P(wi)=j=0n(f(wj)3/4)f(wi)3/4
负采样的个数一般远远大于正样本的个数。

import torch
import torch.nn as nn
import torch.nn.functional as Fclass SkipGram(nn.Module):def __init__(self, vocab_size, embedding_dim):super(SkipGram, self).__init__()self.vocab_size = vocab_sizeself.embedding_dim = embedding_dimself.in_embedding = nn.Embedding(vocab_size, embedding_dim)self.out_embedding = nn.Embedding(vocab_size, embedding_dim)def forward(self, center, pos_words, neg_words):input_embedding = self.in_embedding(center) # [batch_size, embedding_dim]pos_embedding = self.out_embedding(pos_words) # [batch_size, windows*2, embedding_dim]neg_embedding = self.out_embedding(neg_words) # [batch_size, windows*2*neg_num, embedding_dim]input_embedding = input_embedding.unsqueeze(2) # [batch_size, embedding_dim, 1]# unsqueeze()增加维度,suqueeze()降低维度pos_loss = torch.bmm(pos_embedding, input_embedding).squeeze() # [batch_size, window*2, 1]neg_loss = torch.bmm(neg_embedding, -input_embedding).squeeze() # [batch_size, window*2*num, 1]pos_loss = F.logsigmoid(pos_loss).sum(1)neg_loss = F.logsigmoid(neg_loss).sum(1)loss = pos_loss + neg_lossreturn -lossdef get_weight(self):return self.in_embedding.weight.data.cpu().numpy()

3. 全局配置参数

新建config.py文件,定义常量超参数

import torchdevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
embedding_dim = 100
epochs = 150
batch_size = 64
windows = 3
neg_sam = 5
root_dir = './data'
result_dir = './result'
en_corpus = 'en.txt'
zh_corpus = 'zh.txt'
stopwords = 'stopwords.txt'
lr = 0.01

4. 数据处理模块

在数据处理部分,我们新建mydata.py文件,用于编写数据处理相关代码。首先导入相关包

import torch
from torch.utils import data
import os
import numpy as np
import config

定义Mydata类,继承torch工具包下的Dataset类,在此处读取语料数据并将其转换为需要的数据类型。首先,在构造方法中定义语料路径,初始化参数窗口大小、负采样数量以及根据2.3中的公式计算负采样频率。在__getitem__方法中根据索引取出对应中心词、背景词以及根据负采样频率获取负采样词语,将一组中心词、正样本的背景词、负采样词作为训练实例返回。

class Mydata(data.Dataset):def __init__(self, root, corpus_name, stop_file, windows=2, neg_sam=5):super(Mydata, self).__init__()# 语料库文件self.data_path = os.path.join(root, corpus_name)# Skip窗口大小self.windows = windows# 每个词的负采样数量self.neg_sams = neg_sam * 2 * windows# 中文停用词文件self.stop_file = os.path.join(root, stop_file)# -, -, 编码后的序列, 词频表self.word2id, self.id2word, self.sequence, self.word2count = self.get_data()# 负采样频率word_freq = np.array([count for count in self.word2count.values()], dtype=np.float32)word_freq = word_freq**0.75 / np.sum(word_freq**0.75)self.word_freq = torch.tensor(word_freq)def __getitem__(self, index):center = self.sequence[index]# 周围词pos_index = list(range(index-self.windows, index)) + list(range(index+1, index+1+self.windows))pos_index = [i%len(self.sequence) for i in pos_index]pos_words = self.sequence[pos_index]# 返回负采样词neg_words = torch.multinomial(self.word_freq, self.neg_sams, False)# 数据放入devicecenter = center.to(config.device)pos_words = pos_words.to(config.device)neg_words = neg_words.to(config.device)return center, pos_words, neg_wordsdef __len__(self):return len(self.sequence)

Mydata类中自定义一个get_data()方法应用于将文本数据编号并转化为tensor格式,定义字典word2id用于保存词到编号的映射、id2word用于保存编号到词语的映射、word2count用于保存每个词出现的次数。对原始语料数据首先经过停用词表的清洗,去除掉停用词以及标点符号后,得到训练语料,将训练语料加入词表并统计词频,同时将训练语料转换成词表中对应编号的tensor形式表示。

class Mydata(data.Dataset):'''省略'''def get_data(self):# 词表字典word2id = {}id2word = {}# 词频率word2count = {}# 词表编码后的语料sequence = []with open(self.stop_file, 'r', encoding='utf-8') as file:stopwords = file.read().split()with open(self.data_path, 'r', encoding='utf-8') as file:words = file.read().split()print("original corpus size: ", len(words))vocal = [word for word in words if word not in stopwords]print("new corpus size: ", len(vocal))for word in vocal:if word not in word2id:index = len(word2id)word2id[word] = indexid2word[index] = wordword2count[word] = word2count.get(word, 0) + 1sequence.append(word2id[word])# print("size: ", len(word2id), len(id2word), len(word2count))sequence = torch.tensor(sequence)return word2id, id2word, sequence, word2count

编写get_dataloader()函数,传入相应超参数,返回dataloader对象和dataset对象

def get_dataloader(root, corpus_name, stop_file, batch_size, shuffle=True, windows=2, neg_sam=5):dataset = Mydata(root=root,corpus_name=corpus_name,stop_file=stop_file,windows=windows,neg_sam=neg_sam)dataloader = data.DataLoader(dataset=dataset, batch_size=batch_size, shuffle=shuffle)return dataloader, dataset

5. 训练模块

新建main.py文件,首先导入相应包

import torch
import config
import numpy as np
from mydata import get_dataloader
from model import SkipGram
import os
import json
import tqdm

编写train()函数,传入模型、dataloader等参数训练模型,结束后保存模型,并返回loss值

def train(model, dataloader, learning_rate, epochs, save_name):model.train()optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)loss_list = []for epoch in tqdm.tqdm(range(epochs)):total_loss = 0for i, (center, pos_words, neg_words) in enumerate(dataloader):optimizer.zero_grad()loss = model(center, pos_words, neg_words).mean()loss.backward()optimizer.step()total_loss += loss.item()loss_list.append(total_loss)torch.save(model.state_dict(), os.path.join(config.result_dir, save_name + '_model.pth'))return loss_list

开始训练。在训练结束后获取模型的隐藏层权重参数,即我们需要的词向量,将词向量保存为json文件便于读取

### 选择语料类型: 'zh' | 'en' ###
corpus = 'en'
################################
corpus_name = config.zh_corpus if corpus == 'zh' else config.en_corpus
dataloader, dataset = get_dataloader(root=config.root_dir,corpus_name=corpus_name,stop_file=config.stopwords,batch_size=config.batch_size,shuffle=True,windows=config.windows,neg_sam=config.neg_sam)
vocab_size = len(dataset.word2id)
model = SkipGram(vocab_size, config.embedding_dim).to(config.device)
loss_list = train(model=model, dataloader=dataloader, learning_rate=config.lr, epochs=config.epochs, save_name=corpus)
np.savetxt(corpus + "_loss.csv", np.array(loss_list), delimiter=',')# 保存词向量
embedding_weights = model.get_weight()
dic = {word: embedding_weights[idx].tolist() for word, idx in dataset.word2id.items()}with open(os.path.join(config.result_dir, corpus + '_embed.json'), 'w', encoding='utf-8') as file:file.write(json.dumps(dic, ensure_ascii=False, indent=4))

6. 词向量工具模块

新建utils.py文件,在这个文件下,主要编写与可视化工具相关的代码,首先导入需要的包

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from scipy import spatial
import json
import seaborn as sns
import adjustText

编写plot_loss()函数,传入记录的loss列表数据,将loss数据可视化

def plot_loss(loss, name):length = len(loss)x = np.arange(1, length + 1)plt.plot(x, loss)plt.title(name)plt.xlabel('epochs')plt.ylabel('loss')plt.show()

创建Tool()工具类,处理词向量,在构造方法中设置pyplot能正常显示汉字,同时读取训练时保存的词向量文件,载入词向量数据

class Tool():def __init__(self, embedding_path):# 使得pyplot可输出汉字plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = Falsefile = open(embedding_path, 'r', encoding='utf-8')self.wordVec = json.load(file)

编写find_near_word()方法,根据指定词语,计算其与其他所有词的余弦相似度,选择相似度最大的前num个词作为结果返回

class Tool():def __init__(self, embedding_path):'''省略'''def find_near_word(self, word, num):''':param word: 选定的词:param num: 需要查找的近义词数量:return: num个近义词列表'''embedding = self.wordVec[word]cos_sim_list = []for key, value in self.wordVec.items():cos_sim = 1 - spatial.distance.cosine(value, embedding)cos_sim_list.append((cos_sim, key))cos_sim_list.sort(reverse=True)return cos_sim_list[0:num]

编写draw_heatmap()方法,通过热力图可视化一系列词向量

class Tool():def __init__(self, embedding_path):'''省略'''def find_near_word(self, word, num):'''省略'''def draw_heatmap(self, words):''':param words: 一列的词'''vectors = [self.wordVec[word] for word in words]f, ax = plt.subplots(figsize=(15, 9))sns.heatmap(vectors, ax=ax)ax.set_yticklabels(words)plt.show()

编写draw_scatter()方法,通过散点图可视化各个词在空间中的位置,由于词向量维度太高,因此我们需要使用主成分分析法将词向量降维到2维,才能正常显示在坐标轴上

class Tool():def __init__(self, embedding_path):'''省略'''def find_near_word(self, word, num):'''省略'''def draw_heatmap(self, words):'''省略'''def draw_scatter(self, words):''':param words: 一列词'''pca = PCA(n_components=2)vectors = [self.wordVec[word] for word in words]coordinates = pca.fit_transform(vectors)plt.figure(figsize=(13, 9))plt.scatter(coordinates[:, 0], coordinates[:, 1])text = [plt.text(coordinates[i, 0], coordinates[i, 1], words[i], fontsize=15) for i in range(len(words))]adjustText.adjust_text(text)plt.show()

7. 实验效果

损失值变化

查找与“中国”接近的5个词,结果为[(1, ‘中国’), (0.9770937577320784, ‘说’), (0.9704815043334012, ‘中’), (0.9691968812096485, ‘美国’), (0.966375220591473, ‘政府’)]

显示词义接近的20个词的热力图

可视化词的坐标

NLP入门之——Word2Vec词向量Skip-Gram模型代码实现(Pytorch版)相关推荐

  1. 【NLP】基于Word2Vec词向量的中文文本相似度匹配

    Word2Vec 词向量可以用于测量单词之间的相似度,相同语义的单词,其词向量也应该是相似的.对词向量做降维并可视化,可以看到如下图所示的聚类效果,即相近语义的词会聚在一.     文本或句子相似度问 ...

  2. 【NLP模型笔记】Intro || Word2vec词向量模型

    INTRO 自然语言处理(Natural Language Processing),简称NLP.这个领域是通过统计学.数学模型.机器学习等相关技术研究人类语言的特征,对其进行数学的表示,并基于这种表示 ...

  3. NLP—word2vec词向量简介

    NLP处理的数据都是文字,而文字是无法直接被计算机计算的,于是人们想出了使用独热编码的方式来表示单词. <span style="font-size:16px;">浙江 ...

  4. 系统学习NLP(十三)--词向量(word2vec原理)

    词向量简介 自然语言是一套用来表达含义的复杂系统.在这套系统中,词是表义的基本单元.在机器学习中,如何使用向量表示词? 顾名思义,词向量是用来表示词的向量,通常也被认为是词的特征向量.近年来,词向量已 ...

  5. NLP:Word Embedding词嵌入/word2vec词向量思想方法(一种主流的分布式表示)的简介、使用方法、案例应用之详细攻略

    NLP:Word Embedding词嵌入/word2vec词向量思想方法(一种主流的分布式表示)的简介.使用方法.案例应用之详细攻略 目录 词嵌入(一种主流的分布式表示)的简介 词嵌入的案例理解

  6. 自然语言处理(NLP):23 Word2Vec词向量研究以及实践

    本文主要同大家分享word2vec原理以及应用,通过文本相似度和新闻文本分类为案例进行展开,最后对词向量技术发展进行简述. 作者:走在前方 博客:https://wenjie.blog.csdn.ne ...

  7. [NLP] 深入浅出 word2vec 词向量详解

    Word2vec 词向量 前置知识:需要理解基本的MLP 多层感知机(全连接神经网络) 和DL.数学相关基础知识 One-hot encoding 独热编码 刚开始,人们用one-hot编码来表示词, ...

  8. 【NLP】word2vec词向量模型训练——基于tensorflow

    前言   维基百科中文数据训练word2vec词向量模型--基于gensim库   上文我们使用了 gensim 库中的 Word2vec 模块训练词向量模型,本篇我们通过 tensorflow 自己 ...

  9. 【NLP】维基百科中文数据训练word2vec词向量模型——基于gensim库

    前言   本篇主要是基于gensim 库中的 Word2Vec 模型,使用维基百科中文数据训练word2vec 词向量模型,大体步骤如下: 数据预处理 模型的训练 模型的测试 准备条件: Window ...

最新文章

  1. GNU Make chapter 2 —— Makefile 介绍
  2. 面试必问之【数组】篇
  3. 日常使用计算机工具软件,计算机常用工具软件的使用.doc
  4. ansible笔记(5):常用模块之文件操作(二)
  5. Non-zero CodeForces - 1300A
  6. 为什么你申请信用卡总是被拒绝?是银行跟你有仇吗?
  7. (剑指Offer)面试题4:替换空格
  8. centos mysql rpm re_CentOS 7 RPM 安装 MySQL5.7
  9. 关于物联网规则引擎技术,你想要知道的都在这儿!
  10. 跨域调用webapi web端跨域调用webapi
  11. 幻昼计算机弹奏,freepiano曲谱_Freepiano简谱
  12. iOS之线程(二)GCD
  13. 计算机视觉经典书籍推荐
  14. 最简单的输出四位数的每个位上的数字
  15. ABAP基本语法(一)附带思维导图学习材料等
  16. Android逆向:通过Xposed解密柠某直播本地数据
  17. 探索语言交互技术在政务数字化的应用
  18. 马斯克打了个响指,推特50%员工被裁....
  19. Compose 横竖屏切换时状态如何保存?rememberSaveable 实现原理分析
  20. 选对池塘钓大鱼([美]雷恩·吉尔森)第四章 想钓什么鱼?发现自己内在的需求...

热门文章

  1. (CSA 共识评估调查问卷)CSA Consensus Assessments Initiative Questionnaire
  2. 处处吻(粤语汉字英译)
  3. 【个人使用篇】github代码管理
  4. Java并发编程synchronized详解
  5. uniapp让ios软键盘-点击-非输入框地方-自动收起
  6. 洛谷P2141珠心算测验 C++解法
  7. Ubuntu卸载、安装
  8. c语言循环语句学习心得
  9. 威斯康星大学计算机科学教授,美国威斯康星大学周家振访问沈阳自动化所
  10. 管理故事216之025-山雀与知更鸟