第一节课的主要内容是以下几个方面:
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的相似性,计算,在给定中心词的情况下,计算其上下文词出现的概率
(5)调整词向量,使概率最大(当概率最大时,中心词对于上下文的关联性最大,其效果越好)
数学阐释:
(1)
其中是t位置的词,也就是中心词。中心词前后各m个单词。表示给定中心词,其上下文词出现的概率。即
T是文本长度,由于每个词都可能成为中心词,所以再计算每个位置的概率,因此最终的函数就是公式(1)。
也就是最终要优化的目标函数(也可以说是损失函数)。
上述过程,可以用下面 这张图体现,图来自:cs224ppt
由于要对最大化,但是一般情况下我们善于处于凸函数问题(最小化)问题。
因此将利用log函数,做一下转化。
只看这个部分,负号是为了让最大化问题转成最小化问题,除以T是为了努力达到平均水平(这块T具体含义我理解的还不够深入)。
把叫做:(average) negative log likelihood。
说明是目标函数中的唯一参数,这个参数也就是词向量,像m这些属于超参数(hyper parameter)。
有了损失函数之后,目标很明确就是最小化这个损失函数。但是还有一个问题,如何表示计算, 因为这些都是向量。
符号约定:表示中心词,表示上下文词,表示上下文词中的某一个特定词。
答案就是:利用余弦函数来衡量相似性(中心词和上下文词),利用softmax来解决概率表示问题。
因此相似性就可以写成:(每个上下文词与中心词的相似性),上下文中的某一个词与中心词的相似性。
概率表示问题:softmax函数(将数值转成概率的一种方法),其基本结构为:
其中max做的是:将中最大的概率放大,soft是:仍然分配一些概率给最小的。
因此有:
(2)
因此最终优化目标便成为: (3)
做个解释:(2)中的指的是所有的中心词的表示,是围绕着所有中心词的某一个特定的上下文词,同理,因此求和的时候也是一个总和的概念,P是关于概率的向量,而非某一个特定值;(3)中的表示的是一个特定位置的中心词。以及一些变量仅仅表示的是一个词,而且一类词。cs224在对参数求解的过程中,是将其当做一个整体考虑的,但是我认为应该当做一个单独的个体去考虑,所以有了(3)的表达方式,我觉得理解上更说得通。
接下来就是训练参数,先说一下的大小以及构成。
首先是所有参数的集合,它是一个2dV的大小,d代表的是每个词向量的维度。V代表的是文章中所有单词个数。
类似于上图,其中从是中心词下面的表示的上下文词,因为每个单词它既可以是中心词,又可以是其他中心词的上下文词,因此是2dV。(这块我只能理解到这里,如果有更好地理解,请指教。)
对求解,就是常见的梯度下降法了,就是求导,找极值点,然后确定向着最优解方向,大小(学习率),然后重复这个过程。中的每个词向量都是随机初始化的,在求导求解的构成中,逐步调节词向量。
在cs224课程中,只针对做了求导,水平有限只能还原这个过程。
其中:
【mathpix告诉我一个月只能用50次....所以剩下内容为手写版】
从图中最后求导结果可以看出,为的一个上下文,后面那个部分其实是围绕着的上下文的一个期望,可以算是围绕着的关于上下文的平均程度。
利用减去(下面简称期望),可以看作是即将令往哪个方向略微做一些倾斜,来优化目标函数。
这样理解比较抽象,用上面的“problems turing into crises as ”来说明~~~~
调整过程如下:
说明一下图是什么意思,假设中心词还是 “banking”,turing 为,into为,crises为,as为,
左图中:图中的蓝色的1,2,3,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