建模角度明白word embedding及tensorflow实现

好孩子

1、 前言

本篇首要介绍关键词的向量表示,也就是豪门熟稔的word embedding。自Google2013
年开源word2vec算法程序之后,它的简约、高效、实用,很快引起业界众人的关心和采用,为寻找引擎、[广告系统-谷歌的wide
& deep
learning][2]、[推荐系统][1]等互联网服务提供新的底蕴技术和思路。

肖肖

何为Embedding?

开篇以前率先需要了然一个定义何为Embedding?Embedding可以当作是数学上的一个上空映射(Mapping):map(
lambda y: f(x)
),该映射的特征是:单射(在数学里,单射函数为一函数,其将不同的引数连接至不同的值上。更确切地说,函数f被叫做是单射时,对每一值域内的y,存在至多一个定义域内的x使得f(x)
= y。)、映射前后结构不变,对应到word
embedding概念中得以清楚为寻找一个函数或映射,生成新的上空上的发表,把单词one-hot所表明的X空间音讯映射到Y的多维空间向量。
接下去,将以模型的角度分解embedding映射函数及新空间内表明的建模过程:

深夜在一家早点店吃油条豆浆,看到墙壁上贴着一张奖状,多看了一眼,发现,奖状的名目,竟然是“好孩子”!世间竟有诸如此类出尘绝艳之奖项!

非监督的“监督学习”

从利用角度,新空间内映射函数的读书方法不需要大量的人为标记样本就足以赢得质料还不错的embedding向量,没有实际的运用任务导向,从这些角度可以看作非监督的学习过程,而从建模角度,向量提取的建模过程是
个分类模型,又足以当做是督查学习,只是这一个监督没有实际的监控意义,当然后来部分应该将word2vec的前段表明格局喂给标注的过文本,形成真正意义上的监察学习,如非死不可的FastText

怎么才能收获这么一张奖状呢?上课不迟到不早退。坐姿端正,双手放在课桌上,左手在下右手在上地交叠着,双脚并拢踩在地板上,不跷二郎腿不伸长了本来放,写字一笔一画规规整整,上课回答问题又主动正确率又高。下课的时候安安静静的,不嬉戏玩耍,不搞恶作剧。打扫卫生的时候第一个冲到拖把后边,快速拿起拖把柄,冲洗,拖地。显而易见,就是教员觉得不错的该做的都积极主动去做去完成,好了,现在,你在师资的前头,就是个乖孩子,就是个好孩子。老师为了表明你成功地抓住了他的注目,便奖励你一张“好孩子”的奖状,不仅让您心中有收获称赞的欢喜,还让你的养父母可以有挂在墙壁上的纸张以供亲朋好友夸几句“呀你们家孩子真棒”!

2、一层隐层神经网络

饱含一个隐层的神经网络有以下普遍特性:理论上加以丰盛该隐层节点数,这个隐层可以接近拟合任意函数,本质上,隐层是上一层的停放(Embedding)函数的近乎表示,而且可以被用做lookup表(前面会介绍),word2vec也是遵照该层去找到输入word的内置向量表示,然后再建立下一层和脚下层的总是(connections),来控制目的函数的误差。【进一步抽象,假如从统计的角度,其实不同层之间的总括关系是一种递归的广义线性关系(递归广义线性模型),每一层通过线性组合对前一层开展转换,然后以部分非线性连接函数(不同函数对应output
label不同的总括分布,比如softmax对应多门类分布,sigmoid对应二项分布等)得到非线性结果喂给下一层,参见图rglm】

篮球 1

model_net.png

篮球 2

rglm.png

遥想我小学的时候,拿到“三好学生”奖状的时候,我二伯是无所谓的,他只是会说,假设奖励个文具盒啊铅笔啊什么的岂不是更好?一张纸有咋样看头?大概,岳父这么说,是因为他没钱给自身买文具盒?嗯,我岳父从来相比较务实。高校里拿了奖学金的证书,他也是说,仍旧奖金其实。我要被我爸笑哭了!

3、Embedding函数

从眼前的定义,大家期望在隐层中找到一个/组嵌入函数W(这里运用lookup
table的措施),使得![][3]实际的,要是指定固定的向量维度,W(“篮球”)=(0.2,
-0.4, 0.7, …),W(“苹果”)=(0.0, 0.6, -0.1,
…),W初叶化时方可赋值给每个维度一个任意数,并通过与output层连接建立学习模型/任务后得到有含义的向量。

实则,我是名教,喜欢名字被印在书本上的感到。不过,假诺给自家的小高校发一张“好孩子”的奖状,现在的自我看到,兴许会觉得这张纸的意义是避免?

4、建模

接下去来看看哪些树立和训练模型。

  • 数码准备
    为给模型准备数据,我们先是需要定义或取得n个样本:![][4]
    假使大家有一个句子“姚明 的 篮球 打得
    很不错”。
    正常方法是率先由总括语言模型,由中间词预测周围词(SKIP-GRAM),或由周围词预测中间词(CBOW)等办法,然后以指定的窗口向前推动,以SKIP-GRAM模式为例,倘若推进窗口为2,我们得以得到样本对:(“篮球”,”的”),(“篮球”,”姚明”),(“篮球”,”打得”),(“篮球”,”很不错”),X
    skip至”打得”时,拿到样本对
    :(“打得”,”篮球”),(“打得”,”的”),(“打得”,”很正确”),以此类推…我们得以拿到用于模型的锻炼样本。

  • 样本代表
    样本拆解出来了,接下去如何用数值来表述这些样本对吗?常用的章程是将持有的操练多少,即“word”对抽取出唯一不重复的单词来构建词典表(vocabulary),然后将样本数量中的“word”表明成one-hot编码,编码时只对有值的岗位上为1别样岗位均为0,以地点例子为例,“姚明
    的 篮球 打得
    很不错”。
    据悉这么些句子可以构建维度为5的词典表:{“姚明”:0,””:1,”的”:2,”篮球”:3,”打得”:4,”很不利”:5},那么锻炼样本(“篮球”,”姚明”)即可发挥为([0,0,1,0,0],0),看起来相比较像正常的多分类数据了,这里为了好了然Y表示成了职务编号,后续在模型中仍以one-hot向量表达。

  • 各层条件分布
    神经网络基于这一个磨炼样本将会输出一个概率分布,这么些概率代表着大家的词典中的每个词是output
    word的可能性。更相像的,尽管隐层有K个节点(即生成word对应vector向量的维度),对各类样本,我们需要做两件工作:

    • 加以隐层后预测output word的票房价值,即需要建个模型来猜度![][5]
    • 将观看到的input
      word喂给隐层嵌入函数,拿到隐层的概率分布,![][6]用连续函数表达即下面提到的(常见的相似会是K个关于x线性组合的方程组,前面会讲到为什么并非该措施)![][3]

    篮球,接下去我们需要构建完全的似然函数举行优化:

  • 对象函数
    各自创立input层-隐层及隐层-output层的总是函数(RGLM),input层和隐层的函数方面已交由,如若假如p(y|w)为正态分布,则
    log-likelihood loss便是(negative) L2
    loss:![][7],即使固然p(y|w)为多项分布,则likelihood
    loss便是softmax
    loss:![][8]从锻练样本可以阅览,output层为多分类,即隐层-output可利用softmax
    loss.
    为了规范预测output
    word,该网络需要遵照上述损失函数学习参数矩阵W和R(output层),实际上,对于我们来说,整个学习任务是为着学习隐层的W函数,即隐层节点参数。当然对于其他任务,比如神经网络推荐或法斯特(Fast)(Fast)text,网络布局过程看似,只是学习的任务是上学输出层的参数和结构。

  • 模型锻练
    常规优化方法会采纳梯度下降和反向传来,由地点的样书定义,大家的锻炼样本中input和output均以one-hot表示,向量极其稀疏(平常完整字典表会是几十万维,假使200000),仅有一个地方的数值为1,另外均为0,假使input到隐层的放置函数拔取大面积形式的话,假诺节点数即放置向量维度为200,则隐层参数矩阵每个样本的迭代将会是1×200000的向量和200000×200矩阵的相乘,分明会带来巨大总计资源的消耗,其实每个样本的隐层参数仅需要依照one-hot向量中数值为1的目录对应的隐层参数参数矩阵的该索引行对应的向量取出即可:

![](https://upload-images.jianshu.io/upload_images/5636599-9f66dd41decb78a6.png)

embedding.png



经过抽象后我们可以得到上面定义的Embedding函数/参数矩阵:



![](https://upload-images.jianshu.io/upload_images/5636599-c76958584ab10584.png)

embedding-abstract.png


这种方式其实联系上面提到的lookup
table就容易理解了,即模型中的隐层权重矩阵便成了一个”查找表“(lookup
table),进行矩阵计算时,只需要直接去查输入的one-hot向量中提取非零位置的索引,在隐层的对应行输出就是每个输入单词的“嵌入词向量”,该过程即完成了嵌入的动作。  
对于输出层:  
经过隐层的嵌入计算,input
word会被映射为1x200的dense向量,再喂给输出层经过softmax的分类器的计算,对随机给定任意output
word的嵌入向量计算其预测概率:!\[\]\[8\],这样基于同一input
word,替换不同的beta(output word的嵌入向量)得到不同output
word的预测概率。  

至此,数据的表示及目标损失函数的定义以及模型训练过程已拆解完毕。接下来,再看看训练性能提升和优化的方法。

从学会走路先河,我们就被引导,要遵从,要做个乖孩子,没有何人喜欢调皮捣蛋的子女,认为那样的子女不乖,是坏孩子,还不容许大家与这多少个不乖的男女接触,要远离。进了全校,大家学会了更多的规矩,手可以放在什么地方不可以置身啥地方,手要洗干净,无法去玩泥巴,不能够去抓虫子,不可以爬树,书本要保持清洁,甚至铅笔削到什么长度都会有个说法。我们学,我们做,大家听,大家看,不过,一直不曾人跟我们说,你干吗在做这多少个工作!你做的这些,不是为了博取一张“好孩子”的奖状,而是为了另外更要紧的事物啊!

5、抽样

据悉上边的拆卸,我们会发现实际训练过程涉及的参数数量会那一个巨大,以地点的200000个单词的字典表为例,隐层嵌入200维的词向量,那么每一回迭代的输入-隐层权重矩阵和隐层-输出层的权重矩阵都会有
200000 x 200 =
4000万个权重,在这样庞大的神经网络中开展梯度下降是一对一慢的,而且需要大量的教练多少来调整这多少个权重并且避免过拟合。所以对性能的渴求依旧很高,即便上边已经运用lookup
table的法门简化了一部分盘算,针对这个问题,Word2Vec的作者在舆论提议了实惠的措施,叫“negative
sampling”,每个磨炼样本的锻炼只会更新一小部分的模子权重,从而降低总括负担,甚至是词向量的身分。基于对假如是,大家的多寡中留存大气冗余和噪声,举例:对于“的”这种常用高频单词,大家会意识一些题材:当我们赢得成对的单词训练样本时,**(“的”,
“篮球”)
*这样的磨练样本并不会给我们提供有关“篮球”更多的语义信息,因为“的”这样的噪音词在大多数单词的光景文中几乎都相会世。由于在语料中“的”这样的常用词出现概率很大,由此我们将会有大气的(”的“,…)这样的训练样本,而这个样本数量远远领先了大家上学“的”这么些词向量所需的训练样本数。所以在统筹抽样情势的时候可以对这样的样本直接铲除在操练样本之外,对于其他样本对轻易抽取少量的负样本举办参数的换代,而不是对one-hot向量中拥有200000个职位对样本都进展测算,从而大大提高训练效能。
下面叙述的有点杂乱,总计起来就是在对给定input
word总结softmax时,不去改进具有词表中word的输出概率,而是从该样本的output
word之外擅自取样有限个(比如只抽样5个word)作为负样本总结其概率,进一步进展梯度和参数的换代。也就是说通过负样本抽样对于每一次训练只更新(5+1)个beta向量对应的参数,也就是200
6=1200个参数,这样与4000万个对照,需要改进的参数占比仅为0.003%,功效提高显而易见。

人格塑造,其实在15岁往日最着重。学会尊重,学会敬畏,学会珍爱,学会创造,学会干净卫生,学会思考,学会判断。我们的小学呢,在教我们,这几个问题的不错答案是XX,其它答案都是错的。分数高的儿女,可以获取什么的嘉奖,分数低的孩子……对不起,没有其他奖励!这样,潜意识的,孩子会认为,分数低,自己就不是个好孩子了,老师也不爱好自己了,回到家里都自卑了,家长问起分数来,像是一场埋在滚油里的煎熬。于是,有的孩子开头用笔更改分数了,只为了老人的几句赞赏或者某些吃食礼物上的嘉奖。变相地,教会了他们通过撒谎等招数拿到自己想要的东西。

6、基于tensorflow的实现

  • 数码加载

import os
def load_w2c_textcn_dataset(path='./data/'):
    """
    Returns
    --------
    word_list_all : a list
        a list of string (word).\n
     要求:中文语料需要先分词  
    """

    print("Load or Download chinese text corpus Dataset> {}".format(path))

    filename = 'wiki_cn.cut'
    word_list_all=[]
    with open(os.path.join(path, filename)) as f:
        for line in f:
            word_list=line.strip().split()
            for idx, word in enumerate(word_list):
                word_list[idx] = word_list[idx].decode('utf-8') 
                #print word_list[idx]
                word_list_all.append(word_list[idx])
    return word_list_all
words=load_w2c_textcn_dataset(path='./data/')
print len(words)
  • 字典构建

import collections
vocabulary_size = 200000
count = [['UNK', -1]]
count.extend(collections.Counter(words).most_common(vocabulary_size - 1))
dictionary = dict()

for word, _ in count:
    dictionary[word] = len(dictionary)
data = list()
unk_count = 0
for word in words:
    if word in dictionary:
        index = dictionary[word]
    else:
        index = 0  # dictionary['UNK']
        unk_count = unk_count + 1
    data.append(index)

count[0][1] = unk_count
reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys())) 
del words
  • batch数据生成器

data_index = 0

def generate_batch(batch_size, num_skips, skip_window):
    global data_index
    batch = np.ndarray(shape=(batch_size), dtype=np.int32)
    labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
    span = 2 * skip_window + 1  # [ skip_window target skip_window ]
    buf = collections.deque(maxlen=span)
    for _ in xrange(span):
        buf.append(data[data_index])
        data_index = (data_index + 1) % len(data)
    for i in xrange(batch_size // num_skips):
        target = skip_window  # target label at the center of the buffer
        targets_to_avoid = [ skip_window ]
        for j in xrange(num_skips):
            while target in targets_to_avoid:
                target = random.randint(0, span - 1)
            targets_to_avoid.append(target)
            batch[i * num_skips + j] = buf[skip_window]
            labels[i * num_skips + j, 0] = buf[target]
        buf.append(data[data_index])
        data_index = (data_index + 1) % len(data)
    return batch, labels
  • 模型构建

import tensorflow as tf
import collections
import numpy as np
batch_size = 128
embedding_size = 128  # 生成向量维度.
skip_window = 2       # 左右窗口.
num_skips = 2        # 同一个keyword产生label的次数.
num_sampled = 64      # 负样本抽样数.

graph = tf.Graph()

with graph.as_default(), tf.device('/cpu:0'):
    train_dataset = tf.placeholder(tf.int32, shape=[batch_size])
    train_labels  = tf.placeholder(tf.int32, shape=[batch_size, 1])

    embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
    softmax_weights = tf.Variable(
        tf.truncated_normal([vocabulary_size, embedding_size], stddev=1.0/np.sqrt(embedding_size)))
    softmax_biases = tf.Variable(tf.zeros([vocabulary_size]))

    embed = tf.nn.embedding_lookup(embeddings, train_dataset)
    loss = tf.reduce_mean(
        tf.nn.sampled_softmax_loss(weights=softmax_weights, biases=softmax_biases, inputs=embed,
                                   labels=train_labels, num_sampled=num_sampled, num_classes=vocabulary_size))

    optimizer = tf.train.AdagradOptimizer(1.0).minimize(loss)

    norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
    normalized_embeddings = embeddings / norm
  • 模型磨炼

num_steps = 500001
import random
with tf.Session(graph=graph) as session:
    tf.global_variables_initializer().run()
    average_loss = 0
    for step in range(num_steps):
        batch_data, batch_labels = generate_batch(batch_size, num_skips, skip_window)
        feed_dict = {train_dataset : batch_data, train_labels : batch_labels}
        _, l = session.run([optimizer, loss], feed_dict=feed_dict)
        average_loss += l
        if step % 100000 == 0 and step > 0:
            print('Average loss at step %d: %f' % (step, average_loss / 100000))
            average_loss = 0
    word2vec = normalized_embeddings.eval()
  • 最近邻

distances = -word2vec[dictionary[u'数据']].reshape((1, -1)).dot(word2vec.T)
inds = np.argsort(distances.ravel())[1:6]
print(' '.join([reverse_dictionary[i] for i in inds]))
----------------------------------------------
资料 统计 显示 信息 证据

[1] Peter McCullagh, John A Nelder, Generalized linear models., ,
1989
[2] The seminal paper, A Neural Probabilistic Language Model
(Bengio, et al.
2003)

has a great deal of insight about why word embeddings are powerful.
[3]:https://erikbern.com/2014/06/28/recurrent-neural-networks-for-collaborative-filtering.html
[4]:https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html?utm\_source=tuicool&utm\_medium=referral

本身小学的时候,其实也不会赢得一张“好孩子”的奖状的。我活泼好动,上课还会打瞌睡,爱跟同伴们七嘴八舌,甚至还会跟老师吵架,然后被老师打。这时候,其实真正好幸福。放学了在训练场上打打球在乒乓球台上找虐,去小河沟了捞六只小蝌蚪放在矿泉水瓶子里,搓多少个泥团假装是小玩偶变成过家庭的角色,去山顶找找野地瓜吃,拔几棵茅草放在嘴里嚼一嚼就当是甜食……野性,天然的我自己,在十分山沟里,并不指望一张奖状。

一经,我们的“好孩子”奖状,需要抑制好多属于孩子的童真的天性,那么,这种奖状如故少发几张的好,拿“创意”、“天分”、“稳定”等奖状来代表,岂不是更好?

自然,这张奖状,会贴在墙壁上很久,直到墙壁斑驳。