PyTorch学习笔记——词向量简介
引言
本系列文章是七月在线的<PyTorch入门与实战>课程的一个笔记。
本文用的PyTorch版本是1.7.1
。
- 上一篇:PyTorch学习笔记——PyTorch简介
- 下一篇:PyTorch学习笔记——语言模型
为什么需要词向量
为了便于计算机处理,我们需要把文档、单词向量化。
而且除了向量化之后,还希望单词的表达能计算相似词信息。
向量化单词,最早的方法是one-hot表示法,但是这种表示没有包含语义信息,并且也不知道某个单词在某篇文章中的重要性。
后来有人提出了TF-IDF方法,这种词袋模型能考虑到单词的重要性,但是语义的相似性还是捕捉不到。
所谓语义信息,就是代表各种青蛙的单词,向量化之后,这些向量的距离越接近越好。距离越近则表示它们的意思越近。
后来有人提出了分布式表示。假设你想知道某个单词的含义,你只要知道这个单词与哪些词语同时出现。即一个单词可以用周围的单词来表示。
这就是Word2Vec,原理可以点进去看看。这里补充下Skip-Gram模型的目标函数:
1T∑t=1T∑−c≤j≤c,j≠0logp(wt+j∣wt)\frac{1}{T} \sum_{t=1}^T \sum_{ -c \leq j \leq c , j \neq 0} \log p(w_{t+j}|w_t) T1t=1∑T−c≤j≤c,j=0∑logp(wt+j∣wt)
其中TTT代表文本长度。 wt+jw_{t+j}wt+j是wtw_twt附近的单词。
就是给定中心词,它周围单词出现的概率越大越好。
本文的重点是学习PyTorch,用到的数据 见百度网盘: 密码:v2z5 。
接下来基于PyTorch实现Word2Vec:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as tudfrom collections import Counter
import numpy as np
import random
import mathimport pandas as pd
import scipy
import sklearn
from sklearn.metrics.pairwise import cosine_similarityUSE_CUDA = torch.cuda.is_available()seed = 53113
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)if USE_CUDA:torch.cuda.manual_seed(seed)
# 实现Skip-gram模型C = 3 # 窗口大小
K = 100 # 负采样个数
NUM_EPOCHS = 2
MAX_VOCAB_SIZE = 30000
BATCH_SIZE = 128
LEARNING_RATE = 0.2
EMBEDDING_SIZE = 100def word_tokenize(text):return text.split()
首先是设置好参数。
然后读取训练数据:
with open('./datasets/text8/text8.train.txt','r') as fin:text = fin.read()text = text.split()
text[:200] # 看200个单词
然后构造词典和相应的映射。
# 词典
vocab = dict(Counter(text).most_common(MAX_VOCAB_SIZE-1))
vocab['<unk>'] = len(text) - np.sum(list(vocab.values())) # 整个文本的单词数,减去词典中的对应的单词数 得到未知单词数
id_2_word = [word for word in vocab.keys()]
word_2_id = {word:i for i,word in enumerate(id_2_word)}
# 得到每个单词出现的次数
word_counts = np.array([count for count in vocab.values()],dtype=np.float32)
# 计算每个单词的频率
word_freqs = word_counts / np.sum(word_counts)
word_freqs = word_freqs ** (3./4.)
word_freqs = word_freqs / np.sum(word_freqs)
VOCAB_SIZE = len(id_2_word)
PyTorch提供了Dataset
结合DataLoader
可以实现训练数据的加载以及本文的负采样。
继承Dataset
需要提供以下两个方法的实现:
__len__
返回数据集元素数量__getitem__
支持索引操作,比如dataset[i]
能获得第i
个元素
class WordEmbeddingDataset(tud.Dataset):def __init__(self, text, word_2_id, id_2_word, word_freqs, word_counts):'''text: 单词列表,训练集中所有单词word_2_id : 单词到id的字典id_2_word: id到单词的映射word_freqs: 每个单词的频率word_counts: 每个单词出现的次数'''super(WordEmbeddingDataset,self).__init__()# 将每个单词转换为idself.text_encoded = [word_2_id.get(word, word_2_id['<unk>']) for word in text]# 转换成Tensorself.text_encoded = torch.Tensor(self.text_encoded)# 保存word_2_id和id_2_worself.word_2_id = word_2_idself.id_2_word = id_2_word# 转换成tensor并保存self.word_freqs = torch.Tensor(word_freqs)self.word_counts = torch.Tensor(word_counts)def __len__(self):# 数据集大小就是text_encoded的长度return len(self.text_encoded)def __getitem__(self,idx):'''负采样,用于训练返回:中心词中心词附近的positive单词随机采样K个单词作为negative样本'''# 中心词center_word = self.text_encoded[idx]# 上文下单词的索引pos_indices = list(range(idx - C,idx)) + list(range(idx+1,idx+C+1)) # 可能会超出文本长度pos_indices = [i % len(self.text_encoded) for i in pos_indices]pos_words = self.text_encoded[pos_indices]# 负采样neg_words = torch.multinomial(self.word_freqs, K * pos_words.shape[0],True)return center_word, pos_words, neg_words # 形状依次是: [] [6] [600]
其中用到的torch.multinomial
返回一个tensor,每行包含从input相应行中定义的多项分布(概率)中抽取的num_samples个样本,返回的是索引。
下面基于Dataset
来构造DataLoader
。
dataset = WordEmbeddingDataset(text, word_2_id, id_2_word, word_freqs, word_counts)
dataloader = tud.DataLoader(dataset,batch_size=BATCH_SIZE,shuffle=True,num_workers=0)
然后就开始定义模型了:
# 定义PyTorch模型
# 实现的是Skip-gram模型
class EmbeddingModel(nn.Module):def __init__(self,vocab_size,embed_size):super(EmbeddingModel,self).__init__()self.vocab_size = vocab_sizeself.embed_size = embed_sizeinitrange = 0.5 / self.embed_sizeself.output_embed = nn.Embedding(self.vocab_size, self.embed_size, sparse=False)# 对权重进行随机初始化self.output_embed.weight.data.uniform_(-initrange, initrange)self.input_embed = nn.Embedding(self.vocab_size, self.embed_size, sparse=False)self.input_embed.weight.data.uniform_(-initrange, initrange)def forward(self,input_labels, pos_labels, neg_labels):'''input_labels: [batch_size]pos_labels: [batch_size,(window_size * 2)]neg_labels: [batch_size,(window_size * 2 * K)]'''batch_size = input_labels.size(0)input_embedding = self.input_embed(input_labels) #[batch_size,embed_size]pos_embedding = self.output_embed(pos_labels) #[batch_size,(window_size * 2),embed_size]neg_embedding = self.output_embed(neg_labels) #[batch_size,(window_size * 2 * K),embed_size]#input_embedding.unsqueeze(2) # unsqueeze在指定的位置插入1个维度,变成[batch_size, embed_size,1]pos_dot = torch.bmm(pos_embedding,input_embedding.unsqueeze(2)).squeeze() # [batch_size,window_size * 2 ] squeeze()neg_dot = torch.bmm(neg_embedding,-input_embedding.unsqueeze(2)).squeeze() # [batch_size,window_size * 2 * K]log_pos = F.logsigmoid(pos_dot).sum(1)log_neg = F.logsigmoid(neg_dot).sum(1)loss = log_pos + log_negreturn -lossdef input_embeddings(self):return self.input_embed.weight.data.cpu().numpy()
先来看一下nn.Embedding
,说的是存储了单词的嵌入向量。实际上是根据指定的维度初始化了一个权重矩阵,本例可以理解为初始化了self.vocab_size
个大小为self.embed_size
的tensor,每个tensor就是一个单词的词嵌入向量。
torch.bmm
做的是批次内的矩阵乘法。
pos_dot = torch.bmm(pos_embedding,input_embedding.unsqueeze(2)).squeeze() # [batch_size,window_size * 2 ] squeeze()neg_dot = torch.bmm(neg_embedding,-input_embedding.unsqueeze(2)).squeeze() # [batch_size,window_size * 2 * K]log_pos = F.logsigmoid(pos_dot).sum(1)log_neg = F.logsigmoid(neg_dot).sum(1)loss = log_pos + log_neg
以上代码实现的是论文1中的公式(4)(4)(4)。
其中vwIv_{wI}vwI是输入词向量input_embedding
;vwi′v^\prime_{wi}vwi′是基于Pn(w)P_n(w)Pn(w)生成的负采样单词词向量neg_embedding
;vwO′v^\prime_{wO}vwO′是输出词向量pos_embedding
。
下面定义模型:
# 定义一个模型以及把模型移动到GPU
model = EmbeddingModel(VOCAB_SIZE, EMBEDDING_SIZE)
if USE_CUDA:model = model.cuda()
通过PyTorch这种框架,我们只需要实现好前向传播,它能帮我进行反向传播。
# 训练模型
optimizer = torch.optim.SGD(model.parameters(),lr=LEARNING_RATE)
for e in range(NUM_EPOCHS):for i, (input_labels,pos_labels,neg_labels) in enumerate(dataloader):input_labels,pos_labels,neg_labels = input_labels.long(),pos_labels.long(),neg_labels.long()if USE_CUDA:input_labels,pos_labels,neg_labels = input_labels.cuda(),pos_labels.cuda(),neg_labels.cuda()optimizer.zero_grad()loss = model(input_labels,pos_labels,neg_labels).mean()loss.backward()optimizer.step()if i % 100 == 0:print('epoch ',e ,' iteration ', i , loss.item())
在我本机上要跑2个小时,基于使用GPU的情况。
下面我们测试得到的结果:
embedding_weights = model.input_embeddings()
def find_nearest(word):index = word_2_id[word]embedding = embedding_weights[index]cos_dis = np.array([scipy.spatial.distance.cosine(e, embedding) for e in embedding_weights])return [id_2_word[i] for i in cos_dis.argsort()[:10]]for word in ["good", "fresh", "monster", "green", "like", "america", "chicago", "work", "computer", "language"]:print(word, find_nearest(word))
可以看到,确实学到了一些相关的语义信息。
参考
Distributed Representations of Words and Phrases and their Compositionality ↩︎
PyTorch学习笔记——词向量简介相关推荐
- PyTorch学习笔记(二):PyTorch简介与基础知识
往期学习资料推荐: 1.Pytorch实战笔记_GoAI的博客-CSDN博客 2.Pytorch入门教程_GoAI的博客-CSDN博客 本系列目录: PyTorch学习笔记(一):PyTorch环境安 ...
- 【NLP】CS224N课程笔记|词向量I: 简介, SVD和Word2Vec
NewBeeNLP原创出品 公众号专栏作者@Ryan 知乎 | 机器学习课程笔记 CS224N课程笔记系列,持续更新中 课程主页: http://web.stanford.edu/class/cs2 ...
- Pytorch学习笔记总结
往期Pytorch学习笔记总结: 1.Pytorch实战笔记_GoAI的博客-CSDN博客 2.Pytorch入门教程_GoAI的博客-CSDN博客 Pytorch系列目录: PyTorch学习笔记( ...
- PyTorch学习笔记(五):模型定义、修改、保存
往期学习资料推荐: 1.Pytorch实战笔记_GoAI的博客-CSDN博客 2.Pytorch入门教程_GoAI的博客-CSDN博客 本系列目录: PyTorch学习笔记(一):PyTorch环境安 ...
- PyTorch学习笔记(三):PyTorch主要组成模块
往期学习资料推荐: 1.Pytorch实战笔记_GoAI的博客-CSDN博客 2.Pytorch入门教程_GoAI的博客-CSDN博客 本系列目录: PyTorch学习笔记(一):PyTorch环境安 ...
- Pytorch学习笔记-第十章
Pytorch学习笔记-第十章图像描述 model data_preprocess data feature_extract main 记录一下个人学习和使用Pytorch中的一些问题.强烈推荐 &l ...
- PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call
您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...
- 深度学习入门之PyTorch学习笔记:卷积神经网络
深度学习入门之PyTorch学习笔记 绪论 1 深度学习介绍 2 深度学习框架 3 多层全连接网络 4 卷积神经网络 4.1 主要任务及起源 4.2 卷积神经网络的原理和结构 4.2.1 卷积层 1. ...
- 深度学习入门之PyTorch学习笔记:多层全连接网络
深度学习入门之PyTorch学习笔记 绪论 1 深度学习介绍 2 深度学习框架 3 多层全连接网络 3.1 PyTorch基础 3.2 线性模型 3.2.1 问题介绍 3.2.2 一维线性回归 3.2 ...
- PyTorch学习笔记(七):PyTorch可视化
PyTorch可视化 往期学习资料推荐: 1.Pytorch实战笔记_GoAI的博客-CSDN博客 2.Pytorch入门教程_GoAI的博客-CSDN博客 本系列目录: PyTorch学习笔记(一) ...
最新文章
- Go 学习笔记(64)— Go error.New 创建接口错误对象、fmt.Errorf 创建接口错误对象、errors.Is 和 errors.As
- 小猿圈web前端之移动端Vue+Vant实现上传压缩旋转图片功能
- Leetcode 64 最小路径和 (每日一题 20210721)
- 别人家的程序员是如何使用 Java 进行 Web 抓取的?
- 学习Java软件开发该从何入手
- 史上最全的MSSQL笔记
- Harbo1.5.2离线搭建
- UDT源代码下载链接
- swift项目 9.3以前版本模拟器运行出错
- 1000道Python题库系列分享
- 与IP地址有关的那些点
- 前端商城vue项目案例1
- 服务器SNMP协议测试
- getinfo怎么用php,PHP的函数curl-curl_getinfo
- 一步教你轻松实现--Word方括号打勾☑
- C++ priority_queue的使用及模拟实现
- 增加点赞手势图及提交按钮图标
- 亚马逊买家秀视频怎么上传?上传买家秀视频的作用是什么
- Linux文件系统--文件类型
- 从0到1的CTF之旅————Web(1)
热门文章
- ORACLE进制转换函数
- 【排序算法】堆排序——常规方法
- Altium Designer(六):Make Library
- php 判断设备是手机还是平板还是pc
- python3----如何简单地理解Python中的if __name__ == '__main__'
- 【转】linux常用命令:find、grep
- youphp学习整理
- [NOIP2009 最优贸易]
- 纯JS日历控件自动输入日期到TextBox、文本框当中
- 转:百度又开始踢新浪屁股了