二、词向量

 

目录

 

一、什么是词向量

二、词向量的离散表示

  1、one-hot编码

  2、 Bag of  Words表示

3、TF-IDF表示

4、 Bi-gram和N-gram

5、离散表示的问题:

三、词的分布式表示(Distributed representation)

1、Skip-Gram模型

2、代码 


一、什么是词向量

     计算机倾向于和vector(向量)打交道,而不是一一个个单词,所以需要吧单词转化为词向量

二、词向量的离散表示

离散表示:

  1、one-hot编码

      词典中有10000个单词,则one-hot编码为10000维的向量,在正确单词表示处为1,其余为0

缺点:不能表示单词间语义相近性

如:

  2、 Bag of  Words表示

将每个单词在语料库中出现的次数加到one-hot编码中,即为单词的Bag of words表示。

缺点:单词的顺序没有被考虑,语义信息也没有被考虑

3、TF-IDF表示

为了表示单词的重要性,给词加上一个权重,罕见的单词给高一点的权重,常见的给低一点的权重。

缺点:还是没有考虑单词顺序和语义信息。

4、 Bi-gram和N-gram

本来是1个单词的单词表,又把两个单词拼在一起组成单词表。还是没有在根本上解决语义的相关性问题。

5、离散表示的问题:

无法衡量词向量之间的关系
词表维度随着语料库增长膨胀
n-gram 词序列随语料库膨胀更快
数据稀疏问题
 
 

如何保证词编码之间的相似性,比如一个许多不同品种的鸟,都属于鸟类,所以要保证它们之间的相关性。

三、词的分布式表示(Distributed representation)

想要知道一个单词什么意思,看它周围经常出现什么单词就可以了。

Word2Vec文章介绍了两个词向量的模型:本章主要学习单词怎样编码才能更好的表示其相似性(学习更好的词编码),初始编码是随机初始化的。

1、Skip-Gram模型

 用一个词附近的词定义该单词。即这个单词如果能够预测处它附近的单词,那这个单词的特征也就知道了,意思也大约能猜出来了。

 

总的损失函数如下:

 为了加快训练速度,采用负例采样(Negative Sampling):不要在所有吧的单词上预测概率,相当于做一个二分类任务,给定一个单词,预测这个单词是不是中心词周围的单词,是的话输出1,不是的话,输出0。即把50万分类问题变为一个二分类问题。不计算具体的概率,只关心是不是周围词,如果是,给一个高一点的概率就可以了,不是就给一个低一点的概率。

首先给一个中心词v_{c}和一个正确 的周围单词u_{w},还有若干个错误的周围词(可以从单词表中随机采样),希望前面的部分越大越好(第二个公式加号前面的公式),后面的饿部分越小越好(第二个公式,但是加了一个负号,所以此处也是越大越好)。

下面为模型目标函数:p(D=1|w,c,\theta )=\frac{1}{1+e^{-v_{c}^{T}v_{w}}}是典型的sigmoid函数:\sigma (x)=\frac{1}{1+e^{-x}},第二个公式即为先对输入进行sigmoid,然后做log操作,sigmoid是一个增函数,单词相近性越高(u_{c-m+j}^{T}*v_{c}),则得到的数值越高。采样的单词和中心词相近性越低(\tilde{u}_{k}^{T}*v_{c}),得到的结果越低,但是此处添加了一个负号,表示得到的结果越高越好。则整个第二个公式表示得到的结果越高越好。以此来训练,形成更好的词向量编码。需要训练的参数在词向量编码中。

2、代码 

'''中心思想:
在一个text(文章)中:

输入:一个中心词w(t),t表示这个单词的位置

得到其附近C位置上的单词,以及负采样单词(负采样是把单词出现频率作为一个list,然后在其中采样k*2*c次,返回其下标),返回这三种单词的编码center_input,pos_input,neg_input(神经网络输入的是数据,把单词变为数据/list下标进行输入)

神经网络再对其进行编码,得到center_encode,pos_encode,neg_encode

损失函数:对于center_encode*pos_encode,其乘积越高表示对center_encode的编码效果越好,-(center_encode*neg_encode)越高表示编码效果越好,再对乘积做logsigmoid操作,取负值即为loss。注意将两个乘积的第1维进行sum()操作(第0维表示有多少个中心词,第1维表示每个中心词产生多少个周围/负采样词,第2维表示每个词的编码长度)。

权重取值:取出对center_encode的权重(__init__()中nn.Embedding方法训练的权重,而不是forward中单个单词产生的权重),变为numpy数据类型。eg:self.in_encode.weight.data.numpy()
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as tud
from collections import Counter
import numpy as np
import random
import math
import pandas as pd
import sklearn
from sklearn.metrics.pairwise import cosine_similarity
#USE_CUDA=torch.cuda.is_available()
#模型每次训练得到的值都不一样,这是因为有些数据是随机初始化的,现在让这些数据初始化都一样
random.seed(1)
np.random.seed(1)
torch.manual_seed(1)
#设定一些超参数
c=3#定义中心词周围有3个单词
k=100#随机采样100个负例,如果训练数据够大的话,可以把k调到20以下,即没出现一个正确的词要出现100个错误的词
num_epoch=2#训练2个epoch
max_vocab_size=30000#词汇表有多少个单词,此处训练30000个常见的单词
batch_size=128
learning_rate=0.2
embedding_size=100#编码长度为100

#读进来的是一篇文章,希望把它变成一个一个单词
def word_tokenize(text):
    return text.split()
with open("text8.train.txt","r") as fin:
    text=fin.read()
    
#text[:100].split()#split()字符串自动切割,默认以空格为切割点
#将单词变为一张词汇表
text=text.split()
print(type(text))
vocab=dict(Counter(text).most_common(max_vocab_size-1))#Counter(text)能够把每个单词都数一遍,并计算出不同的单词出现多少遍,,然后把最常见的3000个单词取出来并变为字典

vocab["<unk>"]=len(text)-np.sum(list(vocab.values()))#vocab.values()表示每个vocab单词出现的次数(取出字典中的值),变成list,然后再求和,就是所有常见单词的出现次数,用所有单词的长度减去它,即所有不常见单词的出现次数

构建两张关于词汇的mapping
index_to_word=[word for word in vocab.keys()]#取出所有单词表中的单词
word_to_index={word:i for i,word in enumerate(index_to_word)}#把index_to_word中的内容反过来,并标上序号,enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
word_count=np.array([count for count in vocab.values()],dtype=np.float32)#为什么用numpy数据类型
word_freqs=word_count/np.sum(word_count)#得到每个单词出现的概率
#论文中提到要把概率都提到3/4次方
word_freqs=word_freqs**(3./4.)
word_freqs=word_freqs/np.sum(word_freqs)#再重新normalize一次(归一化)
vocab_size=len(index_to_word)#如果训练数据不够30000个,则更新单词数量
class WordEmbaddingDataset(tud.Dataset):
    def __init__(self,text,word_to_index,index_to_word,word_count,word_freqs):
        super(WordEmbaddingDataset,self).__init__()
        self.text_encode=[word_to_index.get(word,max_vocab_size-1) for word in text]
        self.text_encode=torch.tensor(self.text_encode).long()
        self.word_to_index=word_to_index
        self.index_to_word=index_to_word
        self.word_count=word_count
        self.word_freqs=word_freqs   
    
    def __len__(self):#数据集一共有多少个item()
        return len(self.text_encode)
    
    def __getitem__(self,idx):
        #给定一个index,返回其代表的哪一个数据,一般返回torch.tensor,然后把这些tensor 拼在一起,拼成一个batch
        center_word=self.text_encode[idx]
        pos_word=list(range(idx-c,idx))+list(range(idx+1,idx+c+1))
        pos_word=[i%len(self.text_encode) for i in pos_word]
        pos_word=self.text_encode(pos_word)
        neg_word=torch.multinorminal(self.word_freqs,K*pos_word.shape[0],True)
        return center_word,pos_word,neg_word
dataset=WordEmbaddingDataset(text,word_to_index,index_to_word,word_count,word_freqs)
dataloader=tud.DataLoader(dataset,batch_size=batch_size,shuffle=True,num_workers=0)
class EmbaddingModel(nn.Module):
        def __init__(self,vocab_size,embad_size):
            super(EmbaddingModel,self).__init__()
            self.vocab_size=vocab_size
            self.embad_size=embad_size
            self.in_embad=nn.Embedding(self.vocab_size,self.embad_size)
            self.out_embad=nn.Embedding(self.vocab_size,self.embad_size)
        
        def forward(self,input_lable,pos_lable,neg_lable):
            input_embadding=self.in_embad(input_lable)
            pos_embadding=self.out_embad(pos_lable)
            neg_embadding=self.out_embad(neg_lable)
            
            input_embadding=input_embadding.unsqueeze(2)
            pos_dot=torch.bmm(pos_embadding,input_embadding).squeeze(2)
            neg_dot=torch.bmm(neg_embadding,-input_embadding).squeeze(2)
            
            log_pos=F.logsigmoid(pos_dot ).sum(1)
            log_neg=F.logsigmoid(neg_dot).sum(1)
            
            loss=-(log_pos+log_neg)
            
            return loss
        
        def input_embadding(self):
            return self.in_embad.weight.data.numpy()
model=Embaddingmodel(vocab_size,embad_size)
optimizer=torch.optim.SGD(model.parameters(),lr=learning_rate)
for e in range(epoch):
    for i,(input_lable,pos_lable,neg_lable) in enumerate(dataloader):
        input_lable=input_lable.long()
        pos_lable=pos_lable.long()
        neg_lable=neg_lable.long()
        
        optimizer.zero_grad()
        loss=modle(input_lable,pos_lable,neg_lable)
        loss.backward()
        optimizer.step()
         
        if i%100==0:
            print("epoch",epoch,"iteration",i,loss.item())

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值