自然语言处理——CBOW模型
CBOW一个用于快速训练得到词向量的神经网络模型,它的核心原理是中心词的前R个词和后R个词来预测中心词。
它的网络模型相比NNLM模型来说,最大的变化是直接去除隐层的非线性激活过程,以此来加速网络的训练速度。
CBOW的输入:
假设中心词wiw_{i}wi的上下文C(wi)={wj∣j∈[i−R,i)∩[i+1,i+R)}C(w_{i})=\{w_{j}|j \in [i-R,i) \cap [i+1,i+R)\}C(wi)={wj∣j∈[i−R,i)∩[i+1,i+R)},也就上文为中心词的前R个词,下文为中心词的后R个词。每次词的词向量为e(wi)e(w_{i})e(wi).那么输入到网络中的向量是这2R−12R-12R−1个上下文词向量的平均值。即:
X=12R−1∑w′∈C(wi)e(w′)X=\frac{1}{2R-1}\sum_{w'\in C(w_{i})}e(w')X=2R−11w′∈C(wi)∑e(w′)
而其中的e(wi)e(w_{i})e(wi)则定义为从词向量矩阵W∣V∣×∣D∣W_{|V|\times|D|}W∣V∣×∣D∣中取出词wiw_{i}wi对应的那一行,或者那一列。∣V∣|V|∣V∣是这个待研究的预料库的词典的大小,一般为4000~7000。∣D∣|D|∣D∣是我们选择的词向量的长度,一般为50~500即可。
虽然我们知道e(wi)e(w_{i})e(wi)是将词wiw_{i}wi对应的那一行词向量取出来,直观上也觉得很简单,然而这个“取”的过程并不好实现,尤其是GPU不好实现,GPU最愿意看到的是矩阵乘法那样子的东西。那么,能不能将这个"取"的过程,写成一个矩阵乘法的形式呢?
还真可以!
我们知道,某个单位正交基ei⃗=(0,0,0,…,1,…,0)\vec{e_{i}}=(0,0,0,\dots,1,\dots,0)ei
我们再看刚刚的输入公式:
X=12R−1∑w′∈C(wi)e(w′)X=\frac{1}{2R-1}\sum_{w'\in C(w_{i})}e(w')X=2R−11w′∈C(wi)∑e(w′)
把它具体化就是:
X=12R−1∑w′∈C(wi)(w′的one−hot向量)×W∣V∣×∥D∣X=\frac{1}{2R-1}\sum_{w'\in C(w_{i})}(w'的one-hot向量)\times W_{|V|\times\|D|}X=2R−11w′∈C(wi)∑(w′的one−hot向量)×W∣V∣×∥D∣
而对于这个求和来说,里面有一个公共的因子W∣V∣×∥D∣W_{|V|\times\|D|}W∣V∣×∥D∣。将它提取出来,原式变成:
X=12R−1(∑w′∈C(wi)(w′的one−hot向量))×W∣V∣×∥D∣X=\frac{1}{2R-1}(\sum_{w'\in C(w_{i})}(w'的one-hot向量))\times W_{|V|\times\|D|}X=2R−11(w′∈C(wi)∑(w′的one−hot向量))×W∣V∣×∥D∣
再做一点点变形:
X=(12R−1∑w′∈C(wi)(w′的one−hot向量))×W∣V∣×∥D∣X=(\frac{1}{2R-1}\sum_{w'\in C(w_{i})}(w'的one-hot向量) ) \times W_{|V|\times\|D|}X=(2R−11w′∈C(wi)∑(w′的one−hot向量))×W∣V∣×∥D∣
我们发现,我们可以先计算上下文词的One-hot的平均向量,再用这个向量是左乘矩阵。这么做的好处是可以大大的减少计算量,变形前需要计算2R-1次矩阵乘法,变形后只需一次矩阵乘法!
另外,值得一提的是,为什么这个模型叫做词袋模型呢?我们看到,上面这个输入,其实会将wiw_{i}wi上下文对应的词向量都加起来求平均向量。显然因为向量加法的交换性,导致这个计算过程中上下文词是可以打断顺序的!而这与我们对语言的理解也是吻合的,举个例子:
这一是个很好的明说词袋模型的例子。你会发现前一句话中有些词序是混乱的,但是你依然知道这段文字在表达什么意思。
CBOW的输出:p(w′∣C(wi))p(w'|C(w_{i}) )p(w′∣C(wi)),
其中p(w′∣C(wi))p(w'|C(w_{i}))p(w′∣C(wi))=softmax(X1×∣D∣×W∣D∣×∣V∣′)softmax(X_{1\times|D|} \times W'_{|D|\times|V|})softmax(X1×∣D∣×W∣D∣×∣V∣′)
这个输出p(w′∣C(wi))p(w'|C(w_{i}))p(w′∣C(wi))是一个形状为1×∣V∣1\times |V|1×∣V∣的行向量,其实是给定一个上下文C(wi)C(w_{i})C(wi)后模型认为其中心为词典里面各个词w′w'w′的概率。然而,我们的目标是使得这个分布里面wiw_{i}wi对应的概率最大。因些,我们将将p(w′∣C(wi))p(w'|C(w_{i}))p(w′∣C(wi))的第i个数提取出来,同样的为了完成这个“提取”任务,我们只需要将p(w′∣C(wi))p(w'|C(w_{i}))p(w′∣C(wi))左乘一个单位正交基e⃗i\vec e_{i}e
于是我们的目标函数就是:
L(θ)=(wi的one−hot向量)×softmax(X1×∣D∣×W∣D∣×∣V∣′)L(\theta)=(w_{i}的one-hot向量)\times softmax(X_{1\times|D|} \times W'_{|D|\times|V|})L(θ)=(wi的one−hot向量)×softmax(X1×∣D∣×W∣D∣×∣V∣′)
最大化这个目标函数即可。一般L(θ)L(\theta)L(θ)会比较小,于是我们会转而最大化L′(θ)=log(L(θ))L'(\theta)=log(L(\theta))L′(θ)=log(L(θ))。一般又是最小化某个函数,于是我们会转而最小化L′′(θ)=−log(L(θ))L''(\theta)=-log(L(\theta))L′′(θ)=−log(L(θ))。
以上就是CBOW的核心原理。
接下来,我们就来实现之。
TextLoader类的实现:
TextLoader类主要实现对原始预料文本文件进行词典提取、生成批训练数据等功能。
class TextLoader(object):def __init__(self,input_data_path,Context_length=10,batch_size=10,min_frq = 3):self.Context_length = Context_length #定义上下文的长度self.V = {} # 将词映射到在词典中的下标self.inverseV ={} #将下标映射到词典中的某个词self.raw_text =list()self.x_data =list()self.y_data =list()self.number_batch = 0self.batch_size = batch_sizeraw_text = []#输入原始数据并统计词频V = dict() #{'word':frq}with open(input_data_path,"r",encoding="utf8") as fp:lines = fp.readlines()for line in lines:line =line.split(" ")line = ['<START>']+line+['<END>'] #为每句话加上<START>,<END>raw_text += linefor word in line:if word in V:V[word] +=1else:V.setdefault(word,1)#清除词频太小的词,同时为各个词建立下标到索引之间的映射self.V.setdefault('<UNK>',0)self.inverseV.setdefault(0,'<UNK>')cnt = 1for word in V:if V[word] <= min_frq:continueelse:self.V.setdefault(word,cnt)self.inverseV.setdefault(cnt,word)cnt +=1self.vacb_size = len(self.V)#将文本由字符串序列转换为词的下标的序列for word in raw_text:self.raw_text +=[self.V[word] if word in self.V else self.V['<UNK>']]#生成batchesself.gen_batch()def gen_batch(self):self.x_data =[]self.y_data =[]for index in range(self.Context_length,len(self.raw_text)-self.Context_length):#index的前Context,加上index的后Context个词,一起构成了index词的上下文x = self.raw_text[(index-self.Context_length):index]+ self.raw_text[(index+1):(self.Context_length+index)]y = [ self.raw_text[index] ]self.x_data.append(x)self.y_data.append(y)self.number_batch =int( len(self.x_data) / self.batch_size)def next_batch(self):batch_pointer = random.randint(0,self.number_batch-1)x = self.x_data[batch_pointer:(batch_pointer+self.batch_size)]y = self.y_data[batch_pointer:(batch_pointer+self.batch_size)]return x ,y
TextLoader每次返回的训练batch里面的词是各个词在词典中的下标,在训练阶段,我们需要将其转换为向量。
训练逻辑:
#coding:utf-8
__author__ = 'jmh081701'
import tensorflow as tf
import numpy as np
import jsoncorpus_path = "data\\input.en.txt"
embedding_word_path = corpus_path+".ebd"
vacb_path = corpus_path+".vab"
data = TextLoader(corpus_path,batch_size=300)embedding_word_length = 50
learning_rate =0.0001
#输入
input_x = tf.placeholder(dtype=tf.float32,shape=[None,data.vacb_size],name="inputx")
input_y = tf.placeholder(dtype=tf.float32,shape=[None,data.vacb_size],name='inputy')
W1 = tf.Variable(name="embedding_word",initial_value=tf.truncated_normal(shape=[data.vacb_size,embedding_word_length],stddev=1.0/(data.vacb_size)))
#W1其实就是 词向量矩阵
W2 = tf.Variable(tf.truncated_normal(shape=[embedding_word_length,data.vacb_size],stddev=1.0/data.vacb_size))
#计算过程
#累加所有上下文的one-hot向量,然后取平均值,再去乘以词向量矩阵;其效果就是相当于,选择出所有的上下文的词向量,然后取平均
hidden = tf.matmul(input_x,W1)
output = tf.matmul(hidden,W2) #batch_size * vacb_size的大小
output_softmax = tf.nn.softmax(output)
#取出中心词的那个概率值,因为iinput_y是一个one-hot向量,output_softmax左乘一个列向量,就是将这个列的第i行取出
output_y = tf.matmul(input_y,output_softmax,transpose_b=True)
loss = tf.reduce_sum(- tf.log(output_y)) #将batch里面的output_y累加起来
train_op = tf.train.AdamOptimizer(learning_rate).minimize(loss)with tf.Session() as sess:sess.run(tf.initialize_all_variables())max_epoch =10000for epoch in range(1,max_epoch):_x,_y = data.next_batch()#生成本次的输入x =[]y =[]for i in range(0,len(_x)):#将Context*2 -1 个向量,求和取平均为一个向量,用于将来和词向量矩阵相乘vec = np.zeros(shape=[data.vacb_size])for j in range(0,len(_x[i])):vec[ _x[i][j] ] += 1vec /= len(_x[i])x.append(vec)y_vec = np.zeros(shape=[data.vacb_size])y_vec[_y[i]] = 1y.append(y_vec)_loss,_ = sess.run([loss,train_op],feed_dict={input_x:x,input_y:y})if (epoch % 100 )==0 :print({'loss':_loss,'epoch':epoch})#保存词向量_W1 = sess.run(W1,feed_dict={input_x:[np.zeros([data.vacb_size])],input_y:[np.zeros([data.vacb_size])]})#每一行就是对应词的词向量np.save(embedding_word_path,_W1)with open(vacb_path,"w",encoding='utf8') as fp:json.dump(data.inverseV,fp)print("Some TEST:")print("<START>:",_W1[data.V['<START>']])print("<END>:",_W1[data.V['<END>']])print("<UNK>:",_W1[data.V['<UNK>']])print("you:",_W1[data.V['you']])print("are:",_W1[data.V['are']])print("is:",_W1[data.V['is']])print("Find Some Word pairs With high similarity")
一些输出:
Some TEST:
<START>: [-0.32865554 -0.32892272 -0.32865554 -0.32903448 -0.32862166 -0.3286371-0.32855278 -0.32865041 -0.3287456 0.32913685 0.3284275 0.3293942-0.32878074 0.32895386 -0.3293958 0.32897744 -0.3285544 0.32878345-0.32894143 -0.32877466 0.32910895 -0.32860583 -0.32861212 0.328917350.3287292 0.32835644 -0.32884252 -0.32896605 -0.32906595 0.328671160.3286172 0.3290027 -0.32881436 -0.32877848 0.328693 -0.328744350.3287963 -0.3290643 -0.3288444 -0.32919246 -0.32911804 0.3285764-0.3288233 0.32885128 -0.32878864 -0.32906076 -0.32880557 0.328894170.32867458 0.3288717 ]
<END>: [-0.32887468 -0.32819295 -0.32811585 -0.32842168 -0.32838833 -0.32807946-0.328573 -0.32846293 -0.32863003 0.32853135 0.3285702 0.3289579-0.32843226 0.32874456 -0.32906574 0.32823783 -0.328379 0.32890162-0.32847401 -0.32865068 0.32868966 -0.32853377 -0.32895073 0.328661350.32829925 0.3286695 -0.3279959 -0.32863376 -0.32822555 0.328690920.3287056 0.32847905 -0.328476 -0.3285315 0.3284099 -0.328464840.32861754 -0.32874164 -0.32856745 -0.3281496 -0.32804772 0.32813182-0.32847726 0.32866627 -0.32860965 -0.32843712 -0.32851657 0.328480060.3284792 0.32827806]
<UNK>: [-0.33541423 -0.33470714 -0.3370783 -0.33635023 -0.33554107 -0.33692467-0.3357885 -0.3362881 -0.33724156 0.3363039 0.3367789 0.33465096-0.33774552 0.334528 -0.33478507 0.3361384 -0.33670798 0.3359792-0.33699095 -0.3372743 0.33699647 -0.3349662 -0.3352877 0.336070630.3360799 0.3374416 -0.33622378 -0.3356342 -0.33574662 0.335159570.33587998 0.33657932 -0.3346068 -0.33668125 0.3373789 -0.335788640.33600768 -0.3351044 -0.33618957 -0.33537337 -0.33528054 0.33497527-0.33659405 0.33648187 -0.33672735 -0.3356181 -0.33677348 0.33580790.33754358 0.33513647]
you: [ 7.5410731e-05 -2.2068098e-05 1.5926472e-04 -3.7533080e-041.5225924e-04 5.7152174e-05 1.1864441e-04 1.8325352e-04-3.3901614e-04 1.2916526e-04 1.3833319e-05 2.6736190e-04-4.7140598e-05 2.6220654e-04 3.6094731e-04 -3.7336904e-051.6925091e-04 3.0941452e-04 6.1381006e-06 8.4891668e-053.2646640e-04 2.9357689e-04 1.1827118e-05 -3.3071122e-04-6.8303793e-06 1.6745779e-04 -3.6067510e-04 5.1347135e-051.4563915e-04 1.7205811e-04 4.1834958e-04 2.2660226e-045.4340864e-05 5.7893725e-05 4.7014220e-04 1.5608841e-052.5751774e-04 2.9816956e-04 -3.2765078e-04 7.9145197e-05-1.4246249e-04 1.0391908e-04 -4.8424668e-06 1.1221454e-043.8156973e-04 -1.1834640e-04 -4.6865684e-05 2.7329332e-04-3.1904834e-05 1.3008594e-04]
are: [-0.2812496 -0.2820716 -0.27766886 -0.2796223 -0.28174222 -0.2794273-0.28108445 -0.28041014 -0.27624518 0.27735004 0.2795439 0.28412956-0.27596313 0.28378895 -0.28383565 0.27873477 -0.27815878 0.27781972-0.2785313 -0.27764395 0.27687937 -0.28168035 -0.28165868 0.279152660.27918822 0.27295998 -0.28038228 -0.280024 -0.2794214 0.27954790.2802325 0.2777838 -0.28295064 -0.27867603 0.27600077 -0.279593830.2796351 -0.2815734 -0.27917543 -0.28051388 -0.2809812 0.28227493-0.2791555 0.28032318 -0.27791157 -0.28050876 -0.2775616 0.282031830.27543807 0.280927 ]
is: [-0.32220727 -0.32266894 -0.31880832 -0.32086748 -0.32186344 -0.3214381-0.32217908 -0.3221054 -0.3179382 0.31926474 0.32116815 0.32492614-0.31819957 0.32392636 -0.32451728 0.32015812 -0.3204411 0.3186195-0.3208769 -0.31925762 0.31855124 -0.32200468 -0.32246563 0.32011450.31962588 0.31499264 -0.32167593 -0.32168335 -0.32059893 0.31988680.3216786 0.3199376 -0.32269898 -0.3208138 0.31806418 -0.320537980.32106066 -0.3218284 -0.31995344 -0.32122964 -0.3216572 0.3230505-0.32118168 0.32176948 -0.31983265 -0.32104513 -0.31971234 0.323686660.31785336 0.32170975]
看起来跟我们经验还是吻合的,哈哈哈
本blog的项目:
https://github.com/jmhIcoding/CBOW.git
自然语言处理——CBOW模型相关推荐
- 改进版的CBOW模型
复习 首先复习一下之前的CBOW笔记. 采用推理的方法认知单词.CBOW模型这里面主要是: CBOW模型的核心思路:给出周围的单词(上下文)时,预测目标词处会出现什么单词. 要用神经网络处理单词,需要 ...
- Word2vec之CBOW 模型
Word2vec之CBOW 模型 CBOW模型流程举例 假设我们现在的语料库是这一个简单的只有四个单词的文本: Step 1. 得到上下文词的one-hot向量作为输入,同时得到预期的输出one-ho ...
- word2vec原理(五):skip-gram和CBOW模型代码实现
目录 代码一 代码二 第一部分代码对于skip-gram和CBOW模型是通用的,第二部分是实现skip-gram模型的代码. 代码一: import os from six.moves.urllib. ...
- CBOW模型的学习、Trainer类的实现
CBOW 模型的学习的实现:给神经网络准备好学习数据.然后求梯度,并逐步更新权重参数. Trainer类:学习的类. 初始化:类的初始化程序接收神经网络(模型)和优化器(SGD.Momentum.Ad ...
- CBOW模型正向传播、矩阵乘积层实现
把矩阵乘积称为MatMul节点: 下面这个图表示矩阵乘积y=xW的计算图 .因为考虑了mini-batch 处理,假设x中保存了N个数据.此时x .W.y 的形状分别是 N×D.D×H .N×H . ...
- 采用推理的方法认知单词、CBOW模型
基于计数的方法,根据一个单词周围的单词的出现频数来表示该单词.需要生成所有单词的共现矩阵,再对这个矩阵进行 SVD,以获得密集向量,如果语料库处理的单词数量非常大,将需要大量的计算资源和时间.基于计数 ...
- GPT-J 自然语言处理 AI 模型
GPT-J 是一个基于 GPT-3,由 60 亿个参数组成的自然语言处理 AI 模型.该模型在一个 800GB 的开源文本数据集上进行训练,并且能够与类似规模的 GPT-3 模型相媲美 2020 年, ...
- Word2Vec之Skip-Gram与CBOW模型
Word2Vec之Skip-Gram与CBOW模型 word2vec是一个开源的nlp工具,它可以将所有的词向量化.至于什么是词向量,最开始是我们熟悉的one-hot编码,但是这种表示方法孤立了每个词 ...
- 谈谈Word2Vec的CBOW模型
0x00 背景 最近在做毕设,需要使用Google的word2vec.查阅了相关资料,总结后,写下这篇. 注,本文大多取自以下内容: cbow例子,取自知乎,已取得答主同意 word2vec数学原理详 ...
最新文章
- MFC Initinstance中DoModal()返回-1
- 思科cisco解决方案:思科ACI解决方案和Nexus_9000交换机
- 计算机运行一会内存占用巨大,Win8电脑程序占用很大内存怎么办?
- eBPF技术应用云原生网络实践系列之基于socket的service | 龙蜥技术
- html span标签 不换行(有时span带中文时候是可以自动换行的)
- 【数据结构算法】二:上三角、下三角中求数组地址--【下标的计算】
- Eclipse CDT 编译wxWidgets
- ESXI上的新建虚机绑定已使用过的静态ip无法ping通网关的奇怪现象
- 35线性映射02—— 线性映射概念与运算、矩阵表示
- ArcFace免费人脸识别 Demo [Android]
- ASP.NET MVC 5 笔记
- python 文件写入多个参数_如何将多个参数写入txt文件(字符串和变量)
- Java爬虫学习——实例:获取起点中文网站小说并保存成txt文件
- Zeppelin0.8.1上操作hive(使用jdbc解释器)
- L9110H电机驱动模块-FPGA
- C语言alloc函数总结
- 中国全国地面站点数据(1981-2010)、月平均气象、月平均降水、月平均相对湿度、月最大降水、月最高温度最低温度、月最高温平均值最低温平均值、高低温站点基础数据,气候数据
- 计算机专业论文指导教师评语,指导老师论文评语
- 尚硅谷 尚医通学习笔记
- 付费入群怎么做_微信群怎么设置付费才可以进入