自然语言处理 模型_CD模型

自然语言处理 模型_CD模型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[…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

CBOW一个用于快速训练得到词向量的神经网络模型,它的核心原理是中心词的前R个词和后R个词来预测中心词。

它的网络模型相比NNLM模型来说,最大的变化是直接去除隐层的非线性激活过程,以此来加速网络的训练速度。

CBOW的输入:
假设中心词 w i w_{i} wi的上下文 C ( w i ) = { w j ∣ 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)={
wjj
[iR,i)[i+1,i+R)}
,也就上文为中心词的前R个词,下文为中心词的后R个词。每次词的词向量为 e ( w i ) e(w_{i}) e(wi).那么输入到网络中的向量是这 2 R − 1 2R-1 2R1个上下文词向量的平均值。即:

X = 1 2 R − 1 ∑ w ′ ∈ C ( w i ) e ( w ′ ) X=\frac{1}{2R-1}\sum_{w'\in C(w_{i})}e(w') X=2R11wC(wi)e(w)
而其中的 e ( w i ) e(w_{i}) e(wi)则定义为从词向量矩阵 W ∣ V ∣ × ∣ D ∣ W_{|V|\times|D|} WV×D中取出词 w i w_{i} wi对应的那一行,或者那一列。 ∣ V ∣ |V| V是这个待研究的预料库的词典的大小,一般为4000~7000。 ∣ D ∣ |D| D是我们选择的词向量的长度,一般为50~500即可。

虽然我们知道 e ( w i ) e(w_{i}) e(wi)是将词 w i w_{i} wi对应的那一行词向量取出来,直观上也觉得很简单,然而这个“取”的过程并不好实现,尤其是GPU不好实现,GPU最愿意看到的是矩阵乘法那样子的东西。那么,能不能将这个”取”的过程,写成一个矩阵乘法的形式呢?

还真可以!

我们知道,某个单位正交基 e i ⃗ = ( 0 , 0 , 0 , … , 1 , … , 0 ) \vec{e_{i}}=(0,0,0,\dots,1,\dots,0) ei
=
(0,0,0,,1,,0)
,就是第i列的位置为1,其它位置为0的特殊向量,这个形式刚好又与我们常见到的one-hot向量一样。而 e i ⃗ \vec{e_{i}} ei
左乘一个矩阵 W W W,恰好就是将 W W W的第i行单独取出来。于是这个将 w i w_{i} wi的词向量从 W ∣ V ∣ × ∣ D ∣ W_{|V|\times|D|} WV×D“取”出的过程恰好就可以表示为 e i ⃗ × W ∣ V ∣ × ∣ D ∣ \vec{e_{i}}\times W_{|V|\times|D|} ei
×
WV×D
e i ⃗ \vec{e_{i}} ei
恰好也就是 w i w_{i} wi的one-hot向量。
我们再看刚刚的输入公式:
X = 1 2 R − 1 ∑ w ′ ∈ C ( w i ) e ( w ′ ) X=\frac{1}{2R-1}\sum_{w'\in C(w_{i})}e(w') X=2R11wC(wi)e(w)
把它具体化就是:
X = 1 2 R − 1 ∑ w ′ ∈ C ( w i ) ( w ′ 的 o n e − h o t 向 量 ) × W ∣ V ∣ × ∥ D ∣ X=\frac{1}{2R-1}\sum_{w'\in C(w_{i})}(w'的one-hot向量)\times W_{|V|\times\|D|} X=2R11wC(wi)(wonehot)×WV×D
而对于这个求和来说,里面有一个公共的因子 W ∣ V ∣ × ∥ D ∣ W_{|V|\times\|D|} WV×D。将它提取出来,原式变成:
X = 1 2 R − 1 ( ∑ w ′ ∈ C ( w i ) ( w ′ 的 o n e − h o t 向 量 ) ) × W ∣ V ∣ × ∥ D ∣ X=\frac{1}{2R-1}(\sum_{w'\in C(w_{i})}(w'的one-hot向量))\times W_{|V|\times\|D|} X=2R11(wC(wi)(wonehot))×WV×D
再做一点点变形:
X = ( 1 2 R − 1 ∑ w ′ ∈ C ( w i ) ( w ′ 的 o n e − h o t 向 量 ) ) × W ∣ V ∣ × ∥ D ∣ X=(\frac{1}{2R-1}\sum_{w'\in C(w_{i})}(w'的one-hot向量) ) \times W_{|V|\times\|D|} X=2R11wC(wi)(wonehot))×WV×D
我们发现,我们可以先计算上下文词的One-hot的平均向量,再用这个向量是左乘矩阵。这么做的好处是可以大大的减少计算量,变形前需要计算2R-1次矩阵乘法,变形后只需一次矩阵乘法!

另外,值得一提的是,为什么这个模型叫做词袋模型呢?我们看到,上面这个输入,其实会将 w i w_{i} wi上下文对应的词向量都加起来求平均向量。显然因为向量加法的交换性,导致这个计算过程中上下文词是可以打断顺序的!而这与我们对语言的理解也是吻合的,举个例子:

这一是个很好的明说词袋模型的例子。你会发现前一句话中有些词序是混乱的,但是你依然知道这段文字在表达什么意思。

CBOW的输出: p ( w ′ ∣ C ( w i ) ) p(w'|C(w_{i}) ) p(wC(wi)),
其中 p ( w ′ ∣ C ( w i ) ) p(w'|C(w_{i})) p(wC(wi))= s o f t m a x ( X 1 × ∣ D ∣ × W ∣ D ∣ × ∣ V ∣ ′ ) softmax(X_{1\times|D|} \times W'_{|D|\times|V|}) softmax(X1×D×WD×V)
这个输出 p ( w ′ ∣ C ( w i ) ) p(w'|C(w_{i})) p(wC(wi))是一个形状为 1 × ∣ V ∣ 1\times |V| 1×V的行向量,其实是给定一个上下文 C ( w i ) C(w_{i}) C(wi)后模型认为其中心为词典里面各个词 w ′ w' w的概率。然而,我们的目标是使得这个分布里面 w i w_{i} wi对应的概率最大。因些,我们将将 p ( w ′ ∣ C ( w i ) ) p(w'|C(w_{i})) p(wC(wi))的第i个数提取出来,同样的为了完成这个“提取”任务,我们只需要将 p ( w ′ ∣ C ( w i ) ) p(w'|C(w_{i})) p(wC(wi))左乘一个单位正交基 e ⃗ i \vec e_{i} e
i
,即可,这个正交基恰好又是 w i w_{i} wi的one-hot向量。

于是我们的目标函数就是:
L ( θ ) = ( w i 的 o n e − h o t 向 量 ) × s o f t m a x ( X 1 × ∣ D ∣ × W ∣ D ∣ × ∣ V ∣ ′ ) L(\theta)=(w_{i}的one-hot向量)\times softmax(X_{1\times|D|} \times W'_{|D|\times|V|}) L(θ)=(wionehot)×softmax(X1×D×WD×V)
最大化这个目标函数即可。一般 L ( θ ) L(\theta) L(θ)会比较小,于是我们会转而最大化 L ′ ( θ ) = l o g ( L ( θ ) ) L'(\theta)=log(L(\theta)) L(θ)=log(L(θ))。一般又是最小化某个函数,于是我们会转而最小化 L ′ ′ ( θ ) = − l o g ( 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 = 0
        self.batch_size = batch_size

        raw_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 += line
                for word in line:
                    if word in V:
                        V[word] +=1
                    else:
                        V.setdefault(word,1)

        #清除词频太小的词,同时为各个词建立下标到索引之间的映射
        self.V.setdefault('<UNK>',0)
        self.inverseV.setdefault(0,'<UNK>')
        cnt = 1
        for word in V:
            if V[word] <= min_frq:
                continue
            else:
                self.V.setdefault(word,cnt)
                self.inverseV.setdefault(cnt,word)
                cnt +=1
        self.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>']]
        #生成batches
        self.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  json

corpus_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 =10000
    for 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] ] += 1
            vec /= len(_x[i])
            x.append(vec)
            y_vec = np.zeros(shape=[data.vacb_size])
            y_vec[_y[i]] = 1
            y.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.32891735
  0.3287292   0.32835644 -0.32884252 -0.32896605 -0.32906595  0.32867116
  0.3286172   0.3290027  -0.32881436 -0.32877848  0.328693   -0.32874435
  0.3287963  -0.3290643  -0.3288444  -0.32919246 -0.32911804  0.3285764
 -0.3288233   0.32885128 -0.32878864 -0.32906076 -0.32880557  0.32889417
  0.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.32866135
  0.32829925  0.3286695  -0.3279959  -0.32863376 -0.32822555  0.32869092
  0.3287056   0.32847905 -0.328476   -0.3285315   0.3284099  -0.32846484
  0.32861754 -0.32874164 -0.32856745 -0.3281496  -0.32804772  0.32813182
 -0.32847726  0.32866627 -0.32860965 -0.32843712 -0.32851657  0.32848006
  0.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.33607063
  0.3360799   0.3374416  -0.33622378 -0.3356342  -0.33574662  0.33515957
  0.33587998  0.33657932 -0.3346068  -0.33668125  0.3373789  -0.33578864
  0.33600768 -0.3351044  -0.33618957 -0.33537337 -0.33528054  0.33497527
 -0.33659405  0.33648187 -0.33672735 -0.3356181  -0.33677348  0.3358079
  0.33754358  0.33513647]
you: [ 7.5410731e-05 -2.2068098e-05  1.5926472e-04 -3.7533080e-04
  1.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-05
  1.6925091e-04  3.0941452e-04  6.1381006e-06  8.4891668e-05
  3.2646640e-04  2.9357689e-04  1.1827118e-05 -3.3071122e-04
 -6.8303793e-06  1.6745779e-04 -3.6067510e-04  5.1347135e-05
  1.4563915e-04  1.7205811e-04  4.1834958e-04  2.2660226e-04
  5.4340864e-05  5.7893725e-05  4.7014220e-04  1.5608841e-05
  2.5751774e-04  2.9816956e-04 -3.2765078e-04  7.9145197e-05
 -1.4246249e-04  1.0391908e-04 -4.8424668e-06  1.1221454e-04
  3.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.27915266
  0.27918822  0.27295998 -0.28038228 -0.280024   -0.2794214   0.2795479
  0.2802325   0.2777838  -0.28295064 -0.27867603  0.27600077 -0.27959383
  0.2796351  -0.2815734  -0.27917543 -0.28051388 -0.2809812   0.28227493
 -0.2791555   0.28032318 -0.27791157 -0.28050876 -0.2775616   0.28203183
  0.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.3201145
  0.31962588  0.31499264 -0.32167593 -0.32168335 -0.32059893  0.3198868
  0.3216786   0.3199376  -0.32269898 -0.3208138   0.31806418 -0.32053798
  0.32106066 -0.3218284  -0.31995344 -0.32122964 -0.3216572   0.3230505
 -0.32118168  0.32176948 -0.31983265 -0.32104513 -0.31971234  0.32368666
  0.31785336  0.32170975]

看起来跟我们经验还是吻合的,哈哈哈
本blog的项目:
https://github.com/jmhIcoding/CBOW.git

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/196450.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • UART配置调试指南[通俗易懂]

    UART配置调试指南[通俗易懂]UART配置(硬件描述)1.根据原理图,查找相关的i2c引脚对应的GPIO值,以GPIO16作为UART1_TX,GPIO17作为UART1_RX为例。2.查找GPIO16与GPIO17对应的BLSP,以及检查GPIO16与GPIO17是否可以作为UART来使用。根据文档,GPIO16与GPIO17对应BLSP3。GPIONUMBERF

    2022年10月18日
  • 设计管理员表;webservice用于网络安全的高端内提供服务的

    设计管理员表;webservice用于网络安全的高端内提供服务的

  • WordPress 配置七牛云 CDN 具体操作

    WordPress 配置七牛云 CDN 具体操作

  • C++ 中的getline()函数用法详解

    C++ 中的getline()函数用法详解    遇到了要输入一行字符串的操作,我想除了fgets()的方法(fgets()用法链接),getline()也是可以的,但是我对getline的操作不熟悉,便查阅了很多资料,发现都说的很模糊,借这个机会我想彻底理清楚getline的用法;  网上有说getline有两种用法的,我在这总结一下,一、getline()用的比较多的用法 1) istrea…

  • DFS(深度优先搜索算法)「建议收藏」

    DFS(深度优先搜索算法)「建议收藏」基本概念深度优先搜索算法(DepthFirstSearch,简称DFS):一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。算法思想回溯法(探索与回溯法…

  • Java新手、小白入门。多敲练习代码!!!

    Java新手、小白入门。多敲练习代码!!!如果你喜欢Java,但是想学不会!我建议你没事的时候敲敲这些代码,希望对你有用!publicclassDemo{ publicstaticvoidmain(String[]args){ System.out.print(“你好\n世界”); System.out.println(“你好\tJava”); System.out.println(“1.电脑要求相对干…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号