人到三十

或者是因为这个年纪更好, 却也更老了。
所以今年的生日过得非常的沉默, 除了两个朋友之外,其他人也都不记得了。

当然自己最近这种人畜勿近的样子, 记得和不记得也都一样。

时间笔记记到第十二篇, 确实感觉时间感比之前好了很多, 一天能做多少事情,一周能做多少事情。
前阵子有次在路上冥想,觉得时间笔记就是一种在时间中的冥想,时间变成一个连续体, 不再有一天天的间隔, 只有一个时间段一个时间段的区分。
在记时间笔记的过程中, 过去未来都被整合到了当下。

就像在冥想的时候, 至远和至近都被整合成身体的每一丝触动。

整合自己的方向是进一步的离弃。
手术, 工作, 感情,和家人的关系, 下一步的打算。
周六和母亲大吵了一架,表达了一些以为这辈子都不会表达的情绪。

依然是主动的离弃在时间之外, 不想被任何人看到,即自怜的自卑, 又可憎的傲慢。

有些若存若亡的故事, 若断若续的感情。

还好, 慢慢适应和这些相处。破碎和分裂的自己, 两个名字,跑反式的生活,稿子还没背熟时的舞台。
更加的会因为一时的情绪而表现的更好或是更坏。
更加的放肆和直接。

分明是一切都晦暗阴明的时刻。

VIM latex书写环境配置

用vim有一个好处, 就是任何的编辑工作全部可以用它来解决, 一个用IDE的人在学一门语言的时候第一反应是找一个这门语言的IDE,而用vim的人在学一门语言的时候第一反应是去github找几个最好用的插件。

记录一下自己的vim环境配置。
首先说下背景, 电脑为mac环境, 使用brew进行包管理。
vim为brew安装的macvim, 版本为8.0.136, 使用vim-plug来进行插件管理。

配置latex环境

使用brew cask来安装latex, 我选择的是mactex发行版的精简版, 也就是basictex. 好处是比较小只有70M,和完整版的2G精简太多, 坏处是很多包没有, 只能在需要的时候动态下载。

brew cask install basictex

注意使用这个命令安装后, 其实只是完成了basictex软件包的清理和下载工作, 一般下载到了类似
/usr/local/Caskroom/basictex/2017.0607/mactex-basictex-20170607.pkg
这样的地方,具体可以使用
mdfind -name mactex-basictex
来查看。

需要再进入此下载文件夹, 双击进行安装。中间需要输入密码。
安装完成。
由于这个安装包是精简版, 因此在稍后编译latex的时候会出现有些包缺失的时候, 那么就需要在缺失的时候自动地下载安装, 因此需要安装一个texliveonfly程序。其作用和latex一样进行编译, 但是与latex不同的是,当存在包缺失的时候, 会自动进行下载。
sudo tlmgr install texliveonfly

这就完成了latex环境的配置。

配置vim

首先安装vimtex插件.
在vimrc的plug列表区加入
Plug 'lervag/vimtex', {'for': ['tex', 'plaintex', 'bst']}
因为我的插件比较多, 因此选择动态加载,仅当文件类型是tex时, 才加载这个插件。

加入vimtex的插件配置:

"tex preview config
let g:texflavor = 'latex'
let g:texindentitems = 0
let g:TexDefaultTargetFormat = 'pdf'
let g:TexCompileRulepdf = 'pdflatex -src-specials -synctex=1 -interaction=nonstopmode $'
let g:TexFormatDependencypdf = 'pdf'
let g:vimtexviewmethod = 'general'
let g:vimtexenabled = 1
let g:vimtexcompleteimgusetail = 1
let g:vimtexviewgeneralviewer = 'open'
let g:vimtexviewgeneraloptions = '-r @line @pdf @tex'
vimtex默认使用latexmk来进行latex编译, 这也是basictex所缺失的包。因此还需要安装和配置latexmk

sudo tlmgr install latexmk
同时在根目录下建立.latexmkrc文件。其内容为:
$pdf_mode = 1;
$pdflatex = "pdflatex --shell-escape -src-specials -synctex=1 -interaction=nonstopmode %O %S";
$recorder = 1;

由于系统自带的pdf浏览器preview无法动态地更新latex生成的pdf内容,因此使用pdf-expert软件来代替, 安装pdf-expert后,整个工作就完成了。

测试

下载一个示例latex文件:
wget https://www.apptic.me/blog/sample-tex.zip
unzip sample-tex.zip
使用vim打开sample.tex, 敲\ll来触发即时生成pdf。
就可以好好地写latex了。

Word embedding综述与回顾(之一)---word2vec模型与PMI分解

适合读者:对word2vec有概念上的了解, 希望有细节上的掌握以及更深一步理解的研究人员, 工程师。

早先和人聊天,曾经聊过一次自然语言方面深度学习方法的发展和影响, 虽然从word2vec之外,nlp的神经网络方法也已经成为业界的主流, 但是与图像, 语音等领域相比, 始终还是差点意思,christopher maning在CS224N中提到语言是不同于图像, 语音的信号系统, 可以认为是一种符号\类别\离散系统。

想想也是,一般来讲都认为, 语言是人类独有高级认知系统, 语言之秘似乎就代表着人工智能中最艰深的问题。

在我自己看来,深度学习在自然语言领域最重大的贡献,word2vec算一个, 这个工作直接给后续一系列的任务奠定了基础, 甚至说是起到了改变研究范式的作用也不为过。

seq2seq算一个, 这个工作直接让机器翻译等等序列标注问题有了一个能用的框架, 而且将把这个问题推向了应用化。

除此之外, 虽然领域中各种模型层出不穷, 但是论影响力的广度和深度, 以及从思想上来讲的深刻性, 似乎都差着一星半点。

最近刷emnlp, 看到emnlp2017又有一些很有趣的在word embedding上作文章的paper, 于是就想把这个方向的一些成果和研究梳理一下。

word2vec.

word2vec由mikilov于2013年提出, 虽然之前已经有了一些关于词的向量表示的工作, 例如使用svd分解, 但是其效果都不太好。

word2vec包括两个算法, skip-gram和CBOW, 其基本思想是一个词的意思, 可以由这个词的上下文来表示。 相似词拥有相似的上下文, 这也就是所谓的离散分布假设(distributional hypothesis)。

术语定义, 例如一句话, “I want to go home”, 中心词为"to", 上下文词为"I",  "want",  "go", "home"。

对于skip-gram来说, 我们是通过中心词来预测上下文词的概率。对于CBOW来说, 我们是通过上下文词来预测中心词的概率 。

CBOW

初始化

拿到一篇文章, 先扫一篇文章, 建立辞典, 然后将辞典中的所有词进行初始化, 初始化为一个N维向量(实际使用中, 300到500不等), 这样得到一个d * |V| 的一个矩阵, 重复这个操作, 得到一个同样size, 但是不同值的矩阵。这是因为在word2vec中, 对于输入的embedding和输出的embedding使用不同的编码, 由于同一个词, 即可能是上下文, 又可能是中心词, 故而做此处理。

训练

输入: 以相临的5(即所谓的滑动窗口, 窗口大小为超参数 ,可设)个词为一个样本, 那么上下文就是第0, 1, 4, 5个词在词典中的索引, 假设这个索引为7, 12, 6, 8.

输出, 一个|V|* 1的概率向量, 每个位元表示中心词正好是这个辞典中这个位置的词的概率。例如在辞典中第三个词为"absorb", 那么p3=0.017, 就表示模型认为中心词为absorb的概率为0.017.

过程,拿到输入上下文的索引后,在输入embedding矩阵中查表, 得到第[7, 12, 6, 8]列,于是我们得到了四个向量,然后将这四个向量进行平均, 得到一个context vecter.

再取到输出embedding矩阵中中心词的向量, 在这个例子中, 假设中心词"to"在辞典中的第1487位, 那么就取输出embedding矩阵中第1487列。 求这个向量和content vector的乘积, 将这个乘积在所有词上做softmax.

如上图所示, 其中m为2, 也就是滑动窗口size的一半减1,wc代表中心心词, 第一行是指, 我们需要在给定上下文的情况下,最小化中心词的条件概率。
第二行, 我们将上下文使用输入embedding矩阵向量化并求平均, 将中心词使用输出embedding矩阵向量化。
第三行, 表示其在整个辞典上建模的softmax概率。

回顾一下整个思想, word2vec的想法是, 使用一个(准确来说是两个)高维向量空间来对词进行建模。 每个词即为向量空间中的一个点。 使用向量的dot product来衡量两个点之间的距离, 然后根据这个距离来最大化条件概率。

在这个模型中, 两个embedding矩阵为参数, 最后求一个最大似然, 也就是最小化其negative log likelihood。

skip-gram

skip-gram模型与CBOW相似,不同在于, skip-gram是使用中心词来预测上下文的词。
惟一值得注意的是, 中心词只有一个, 而上下文的词有四个, 那么如何建立这种一个对四个的对应关系, 在这里mikolov使用的是朴素贝叶斯假设, 也就是认为在给定中心词的情况下, 上下文词之间是相互独立的。
于是我们有这样的结果:

negative sampling

上面两个公式在实际计算的时候有一个问题,因为最后我们是对整个辞典上求softmax, 那也就意味将, 每过一个样本, 我们都要在整个辞典上算一次全部词向量和当前中心词的点积。 这个计算量太大了。
因此, 在论文中, 使用negative sampling这样一个技巧来处理这个问题。

其思想就是, 我们做一个负样本, 可以理解成随机语料, 于是每次训练的时候, 我们就有一个正样本和若干个负样本,我们让正样本的预测概率尽可能大, 而让负样本预测概率尽可能小, 通过负样本的引入, 将本来建立在整个辞典上的一个|V|分类问题, 转换成一个建模在正负样本上的一个二分类问题。 具体来说:

如上图, 当D等于1时, 即样本来自于真实语料, 当D等于0时, 样本来自于随机语料, 我们要使如果样本是真实样本, 那么概率就尽可能大, 如果样本来自随机语料, 那么概率就尽可以小(也就是D=0尽可能大)

这种转换中中涉及到一个很有意思的点, 就是softmax多分类概率和sigmoid二分类概率的等价性, softmax即是sigmoid, 参见CS229.

在生成负样本时, 我们使用Pn(w), 也就是决定要不要sample出某个词作为负样本,使用Pn(w)=freqency^(3/4)
即以词的unigram频率的3/4次方。
其意图是:

于是像is这样的高频词,在处理之后, 生成的概率还是较高, 但是变化不大, 而像bombastic这样的低频词, 其生成的概率就提高了三倍之多。

word2vec还使用Hierarchical Softmax来解决softmax计算量过大的问题, 思路是将词典挂在一棵哈夫曼树的叶结点上, 然后将一个词的softmax, 转换为这个词与这棵树上从根结点到词的父结点每个结点向量的点积的方向加权的sigmoid乘积。 这里就不多说了。

word embedding与矩阵分解的内在一致性

2014年, Yoav Goldberg在其《Neural Word Embedding as Implicit Matrix Factorization》 分析得出结论, word2vec其实质就是对语料的word-context的PMI矩阵进行分解 ,让我们来仔细看看这个过程:

上图是skip-gram同时进行negative-sampling之后的损失函数, 其中w为中心词, c为上下文, 这个损失函数是定义在整个语料上的(也就是跑完一个epoch), 因此要对中心词和上下文都进行求和符运算,k是指对于一个中心词进行多少次negative-sampling, 生成多少个负样本,Cn为negative-sample出来的负样本的上下文词。#(w, c)是指一个(w, c)的词对,在语料中出现的次数。

假设在真实语料由三句话组成, 分别是"what i am",   "fine. but what is that", “so Lucy, what are you doing about”, 那么what作为中心词就只有两次,分别是

样本1:  "."   "but"   "what"   "is"   "that"

样本2:"Lucy"  "."  "what"   "are"    "doing"

在以what作为中心词时, 我们negative sample出这样一个句子:

样本3:"about"   "am"   "what"  "fine"  "is"

作为上下文词,what出现1次, 分别为:

"what"   "are"   "you"   "doing"  "about"

公式1

我们进行第一次变换, 第一行只是简单地把求和符拆开, 第二行之所以可以进行如此变换的原因是因为在negative sampling的时候,是针对每一个中心词来做的, Cn的次数生成只与Pd也就是negative-distribution有关, 所以第二项对于c来说是个常数, 于是sum(#(w, c)) when c belong to Vc, 就等于#w, 这里#符号代表着在语料中的计数。
公式2

我们把注意力集中在公式1的第二部分, 进行再一步的变换, 在这次变换之后,negative-distribution的期望写成其分布原本定义的形式, 这里的Pd, 就是上面negative-sample小节中的Pn, 注意在这篇论文中为了简化起见,Pd, 直接用了基于频率的unigram distribution, 和mikolov的原文不太一样, 不过这里并不影响最终的结论, 只需要把这个#(cN)换成#(cN)^(3/4)就可以了。
这个变换的意思, 是指将negative-sample出来的上下文做一个更细的区分, 将那些正好在真实语料的上下文词集合中出现过的上下文词写出来做为公式的第一部分,在这个例子中, ("what", “is”)句对, 就属于即在真实上下文, 中心词对中出现过, 也在negative-sample中出现过的应该, 归到上面公式的第一种部分, 而(“what”, "fine")就属于只存在于negative-sample中的情况, 应该归到公式中的第二部分。

将上面的公式1和公式2合并, 得到如下的变换:

也就是说, 现在我们暂时只考虑在语料中出现过的(w, c)词对。对于这种词来把loss单独写出来。也就是只考虑("what", "is")这样的词对。令x = w . c, 然后再求偏导


就得到了上面的式子。
然后再令这个式子的偏导为零, 很容易能够求得:

最后, 把式子拆开, 就得到了。

这里就非常有意思了, 在第一个对数符号里包的那一堆东西, 恰恰就是广为人知的point-wise互信息公式。

最后我们来回溯一下, 在skip-gram中, 我们学到了两个矩阵, 输入embedding矩阵和输出embedding矩阵, 分别用来编码中心词和上下文词, 我们在skip-gram的原始形式中, 做向量的点积, 然后用这个点积来做softmax, 在做点积时, 我们实际就是取了, 这两个矩阵的某一行和某一列, 做点积, 那么如果把输入embedding矩阵表示为W, 输出embedding矩阵表示为C, 我们就得到了这样的结果:

也就是说, skip-gram模型的实质, 其实就等同于, 我们拿到了语料之后, 先进行了一个全局扫描, 然后建立了一个行为W, 列为C, 每个位元的值为PMI(w, c)的这么一个矩阵, 然后对这个矩阵进行全局减log(k), 这里k就是negative-sample时的次数(在这个例子中k=1),  就得到了一个矩阵M, 我们对这个M进行分解, 分解之后的W, C, 就是我们所谓的词嵌入矩阵, 或者称谓为lookup table.

 

参考文献:

Turney, P. D., & Pantel, P. (2010, March 5). From Frequency to Meaning: Vector Space Models of Semantics. arXiv.org. http://doi.org/10.1613/jair.2934

Levy, O., & Goldberg, Y. (2014). Neural Word Embedding as Implicit Matrix Factorization. Nips.

Mikolov, T., Sutskever, I., 0010, K. C., Corrado, G., & Dean, J. (2013). Distributed Representations of Words and Phrases and their Compositionality. CoRR Abs/1003.1141, cs.CL.

Mikolov, T., Chen, K., Corrado, G., & Dean, J. (2013, January 17). Efficient Estimation of Word Representations in Vector Space. arXiv.org.

https://web.stanford.edu/class/cs224n/lecture_notes/cs224n-2017-notes1.pdf