word2vec------2019cs224N(Course One)

第一节课的主要内容是以下几个方面:

1,简单介绍了一下WordNet,以及NLTK。

2,介绍了一下ont-hot 编码方式,其最主要的问题是未给出词汇之间的内在联系,而且是稀疏矩阵的形式

     2005年google提出建立单词相似性表来解决这个没有内在联系的问题,但是表格太大。

3,由于one-hot的缺点,所以有了word2vec

4,利用2014年的一个语料库,和genism做了一些展示


one-hot编码方式

将整个文本分词后,作为一个整体考虑,词的个数就是词向量(word vector)的维度,每个词有一个特定的位置,其余位置为0。

假设文本中词个数:5

则女人这个词为:[1,0,0,0,0];男生这个词为:[0,1,0,0,0]....以此类推。

缺点:没有给出词与词之间的内在联系,稀疏性太大。

word2vec

核心思想:预测每个单词和它的上下文,这种表示方法叫做:distributed representation

起源:“You shall know a word by the company it keeps” (J. R. Firth 1957: 11) 

模型:(1)CBOW(continous bag of words):以词袋模型为基础产生的,给出上下文词去预测中间词的概率。cs224N利用的不是这种方法,所以暂时不仔细写。

(2)skip-gram(SG):给定一个词,去预测这个单词最有可能的上下文

两者正好是一个相反的过程。

skip-gram

思想过程:

(1)有一个很大的语料库,词个数为T

(2)在一个固定的词汇表中,每一个词都用一个向量表示

(3)每一个位置t,都有一个中心词:c 和他的上下文词:o,窗口大小为m(考虑中心词左右两边各m个词)

(4)利用c与o的相似性,计算p(o|c),在给定中心词的情况下,计算其上下文词出现的概率

(5)调整词向量,使概率最大(当概率最大时,中心词对于上下文的关联性最大,其效果越好)

数学阐释:

                     L(\theta)=\prod_{t=1}^{T} \prod_{-m \leq j \leq m \atop j \neq 0} P\left(w_{t+j} | w_{t} ; \theta\right)     (1)

         其中w_{t}是t位置的词,也就是中心词。中心词前后各m个单词。P\left(w_{t+j} | w_{t} ; \theta\right)表示给定中心词,其上下文词w_{t-m},w_{t-m+1},...,w_{t-1},w_{t+1},w_{t+2},...w_{t+m}出现的概率。即\prod_{-m \leq j \leq m \atop j \neq 0} P\left(w_{t+j} | w_{t} ; \theta\right)

T是文本长度,由于每个词都可能成为中心词,所以再计算每个位置的概率,因此最终的函数就是公式(1)。

L(\theta)也就是最终要优化的目标函数(也可以说是损失函数)。

上述过程,可以用下面 这张图体现,图来自:cs224ppt

由于要对L(\theta)最大化,但是一般情况下我们善于处于凸函数问题(最小化)问题。

因此将L(\theta)利用log函数,做一下转化。

J(\theta)=-\frac{1}{T} \log L(\theta)=-\frac{1}{T} \sum_{t=1}^{T} \sum_{-m \leq j \leq m \atop j \neq 0} \log P\left(w_{t+j} | w_{t} ; \theta\right)

只看-\frac{1}{T} \log L(\theta)这个部分,负号是为了让最大化问题转成最小化问题,除以T是为了努力达到平均水平(这块T具体含义我理解的还不够深入)。

J(\theta)叫做:(average) negative log likelihood。

说明\theta是目标函数中的唯一参数,这个参数也就是词向量,像m这些属于超参数(hyper parameter)。

有了损失函数之后,目标很明确就是最小化这个损失函数。但是还有一个问题,如何表示计算P\left(w_{t+j} | w_{t}\right), 因为w_{t}这些都是向量。

符号约定:V_{c}表示中心词,U_{w}表示上下文词,U_{o}表示上下文词中的某一个特定词。

答案就是:利用余弦函数来衡量相似性(中心词和上下文词),利用softmax来解决概率表示问题。

因此相似性就可以写成:U_{w}^{T}V_{c }(每个上下文词与中心词的相似性),U_{o}^{T}V_{c }上下文中的某一个词与中心词的相似性。

概率表示问题:softmax函数(将数值转成概率的一种方法),其基本结构为:\operatorname{softmax}\left(x_{i}\right)=\frac{\exp \left(x_{i}\right)}{\sum_{j=1}^{n} \exp \left(x_{j}\right)}=p_{i}

其中max做的是:将x_{i}中最大的概率放大,soft是:仍然分配一些概率给最小的x_{i}

因此有:

                                                     P(o | c)=\frac{\exp \left(u_{o}^{T} v_{c}\right)}{\sum_{w \in V} \exp \left(u_{w}^{T} v_{c}\right)}                      (2)

因此最终优化目标便成为:J(\theta)=-\frac{1}{T} \sum_{t=1}^{T} \sum_{-m \leqslant j \leqslant m \atop j \neq 0} \log \frac{\exp \left(u_{j}^{T} v_{t}\right)}{\sum_{w=1}^{2m} \exp \left(u_{w}^{T} v_{t}\right)}        (3)

做个解释:(2)中的v_c指的是所有的中心词的表示,u_o是围绕着所有中心词的某一个特定的上下文词,u_w同理,因此求和的时候w\in V也是一个总和的概念,P是关于概率的向量,而非某一个特定值;(3)中的v_t表示的是一个特定位置的中心词。u_w以及一些变量仅仅表示的是一个词,而且一类词。cs224在对参数求解的过程中,是将其当做一个整体考虑的,但是我认为应该当做一个单独的个体去考虑,所以有了(3)的表达方式,我觉得理解上更说得通。

接下来就是训练参数\theta,先说一下\theta的大小以及构成。

首先\theta是所有参数的集合,它是一个2dV的大小,d代表的是每个词向量的维度。V代表的是文章中所有单词个数。

                                        

类似于上图,其中从V_{aardvark},...,V_{zebra}是中心词下面的V_{aardvark},...,V_{zebra}表示的上下文词,因为每个单词它既可以是中心词,又可以是其他中心词的上下文词,因此是2dV。(这块我只能理解到这里,如果有更好地理解,请指教。)

\theta求解,就是常见的梯度下降法了,就是求导,找极值点,然后确定向着最优解方向,大小(学习率),然后重复这个过程。\theta中的每个词向量都是随机初始化的,在求导求解的构成中,逐步调节词向量。

在cs224课程中,只针对v_t做了求导,水平有限只能还原这个过程。

\frac{\partial}{\partial v_{t}} \log \frac{\exp \left(u_{j}^{T} v_{t}\right)}{\sum_{w=1}^{2 m} \exp \left(u_{w}^{T} v_{t}\right)}=\frac{\partial}{\partial v_{t}} \log \exp \left(u_{j}^{T} v_{t}\right)-\frac{\partial}{\partial v_{t}} \log \sum_{m=1}^{2 m} \exp \left(u_{m}^{T} v_{t}\right)

其中:

\begin{array}{l}{\frac{\partial}{\partial v_{t}} \log \exp \left(u_{j}^{T} v_{t}\right)} \\ {=\frac{\partial}{\partial v_{t}} \left(u_{j}^{T} v_{t}\right)} \\ {=u_{j}}\end{array}

【mathpix告诉我一个月只能用50次....所以剩下内容为手写版】

从图中最后求导结果可以看出,u_jv_t的一个上下文,后面那个部分其实是围绕着v_t的上下文的一个期望,可以算是围绕着v_t的关于上下文的平均程度。

利用u_j减去下面简称期望),可以看作是即将令v_t往哪个方向略微做一些倾斜,来优化目标函数。

这样理解比较抽象,用上面的“problems turing into  crises as ”来说明~~~~

调整过程如下:

 

说明一下图是什么意思,假设中心词还是 “banking”,turing v_1,intov_2,crisesv_3,asv_4

左图中:图中的蓝色的1,2,3,4,分别代表了v_1,...,v_4,然后虚线的平均表示的是期望,然后红色虚线表示的是每个值关于期望的差,也就是上面所提到的特定的某个词与期望的加法。(这是每一次调节的梯度大小)

右图表示的是中心词“banking”的一个调整过程,old代表原来的初始的向量,过程如下:

第一次先按照old与左图红色1(ruting),取一个学习率,进行调整,调整后到了右图蓝色1。

第二次按照右图蓝色1与左图红色2(into), 取一个学习率进行调整,调整后到了右图的蓝色2。

第三次按照右图蓝色2与左图红色3(crises),取一个学习率进行调整,到了右图蓝色3。

最后一次,按照右图蓝色3与左图红色额4(as),取一个学习率进行调整,得到了最终优化后的向量(new)。

这两个图应该放在一起,不太会画,所以就这样表示了。

以这样的过程,就可以得出最终的中心词的词向量。


代码部分

一共有三个小实验,没有做cs224的实验,而是利用水浒传的语料库做了一个实验(由于水平有限,可能效果不是很好)

(1)利用genism构建一个词向量,做词相关性展示(画图)。

词向量的维度是100维,如果画图,那么要映射到平面(二维)空间上,因此要利用PCA做降维处理。(cs224中讲到的思想)

前期处理:去标点符号->按照。?!等将句子分开->利用jieba分词->去停用词->保存文本

import jieba
import re
path_1 ='E:\\CODE\\shui.txt' 
data = open(path_1,encoding='utf-8').read() ###原始样本
path_2 = 'C:\\Users\\happy\\Desktop\\words.txt'
stopwords = [line.strip() for line in open(path,encoding='utf-8').readlines()] ###加载停用词

def change(en):
    uncn = re.compile(r'[\u4e00-\u9fa5]')
    en = "".join(uncn.findall(en.lower()))
    return en
def move_stopwords(sentence,stopwords):
    out_list = []
    for word in sentence:
        if word not in stopwords:
            out_list.append(word)
    return out_list
def participle(sentence):
    sentence = change(sentence) #将英文去掉
    sentence = jieba.lcut(sentence) #分词
    sentence = move_stopwords(sentence,stopwords) #去掉停用词
    for i in sentence:
        if len(i) == 1:
            sentence.remove(i)
    return ' '.join(sentence)
sentence = re.split('!|。|?|;',data) #将文本分成一句句的
path_3 = 'E:\\CODE\\shui_jieba.txt' ###按照句子分隔,每一行一个句子
with open(path_3,'a') as f:
    for j in sentence:
        string = participle(j)
        f.write(string+'\n')

利用genism训练

from sklearn.decomposition import PCA
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from gensim.models import word2vec

path = 'E:\\CODE\\shui_jieba.txt'
with open(path) as f:
    data = f.readlines()
s = []
for i in range(0,len(data)):
    string = data[i].replace('\n','')
    s.append(string.split(' '))   ####重新读一下

model = word2vec.Word2Vec(s,size=100,window=5,min_count=1,sg=1) ###模型,使用skip-gramw
model.save('E:\\CODE\\word2vec.model')

a = model['北宋','江西','宋江','陕西','江苏','洪太尉','汴梁','洪信','五台山','开封','江西','太尉','朝廷','关西','杨志',\
         '济州','郓城县','公孙胜','父母','宝珠寺','妻子','龙虎山','满县','史进','武松','武大郎']

pca  = PCA(n_components=2) ##PCA
newX = pca.fit_transform(a)
#print(newX)

######画图
x = [0 for i in range(0,len(newX))]
y = [0 for i in range(0,len(newX))]
for i in range(0,len(newX)):
    x[i] = newX[i][0]
    y[i] = newX[i][1]

fig,ax=plt.subplots(figsize=(10,8))
ax.scatter(x,y,c='r')

font = {'family': 'MicroSoft Yahei',
       'weight': 'bold',
       'size': 10}
matplotlib.rc("font", **font) ###为了使汉字能用

name=['北宋','江西','宋江','陕西','江苏','洪太尉','汴梁','洪信','五台山','开封','太尉','朝廷','关西','杨志',\
         '济州','郓城县','公孙胜','父母','宝珠寺','妻子','龙虎山','满县','史进','武松','武大郎']
for i in range(0,len(name)):
    ax.text(x[i],y[i],name[i])

结果图

(2)做词类比:类似于“king”-“man”+“woman”=“queen”

model.most_similar(positive=['及时雨','宋江'], negative=['豹子头'],topn=1)
output:[('军汉', 0.9928054809570312)]

因此(1)图中效果不是很好,因为从100维到了2维,然后衡量效果好坏用类比的方法比较好,然后我以为“豹子头”会对应出“林冲”,但是对应出了“军汉”(军汉:军人,也是林冲的一个身份),从这也说出了模型效果其实不太好。

(3)给出某两个词之间的相似性

model.similarity('及时雨','宋江')
out:0.997945

参考文献

[1]2019-cs224N

发布了56 篇原创文章 · 获赞 29 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/foneone/article/details/102864261
one