foricee / foricee.github.io Goto Github PK
View Code? Open in Web Editor NEW博客
博客
读了几个月的论文,终于大致捋顺了word vector算法的脉络。文章里面有我对算法的一些直观理解,欢迎有想法的一起讨论。
语言模型遇到的问题:维度灾难——test数据集里面的词序列可能没有在train数据集里出现过。为什么这种情况叫做维度灾难呢?个人理解是:如果把词序列表示成one-hot的向量(向量的维度等于所有不同的词序列数量,当前词序列对应的位置是1,其他位置都是0),词序列长度越长,不同词序列的数量越大,向量长度即维数越大,所以,遇到的灾难就叫维度灾难了。
词序列太长,需要非常多的训练语料。而且就算有这么多的语料,参数的个数也太多了。例如词典大小10万(105),长度为10的词序列,参数个数就达到了1050(10个坑,每个坑有105个选择)
## 1.2 经典的解决方法n-gram——短词序列,互相覆盖。利用了词序,减少了参数个数。利用相邻n个词,限制了参数个数上限。缺点是只考虑了局部上下文。如果考虑更长的上下文,例如5以上,仍然会碰到维度灾难问题。
虽然实际应用中,这种方法很work,但是这种局部上下文的假设是有问题的。
## 1.3 新的解决方法词向量——为每个词引入分布表示,用向量表示词,它比n-gram更容易泛化(泛化的意思是把从训练数据中学到的知识用到测试数据里面)。
在LM角度上,词向量与n-gram的不同
n-gram | 词向量 | |
---|---|---|
泛化方法 | 1-n阶gram做插值 | 词表示为连续向量,自然泛化 |
上下文长度 | 2或3 | 5,甚至更多 |
词之间的相似 | 无(或者先聚类,用是否在同类别算二值相似度) | 有,相似度连续 |
《A Neural Probabilistic Language Model》 JMLR 03
这个图太经典,说到神经网络语言模型不得不贴上。这个NN公有3层,各层之间是全连接,输入wt的上文信息,输出生成wt的概率。假设上文长度为n,如下所示,_wt-n+1_到_wt-1_是_wt_的上文。
| wt-n+1 | ... | wt-2 | wt-1 | wt | ...
输入层:总的来说,包含了词wt的上文信息,说具体点,是词wt上文n-1个词的词向量,这n-1个词向量首尾拼接,构成了输入层节点。比如说:n=5,词向量维度100,那么,输入层共有400个节点。对这个结构我一直有个疑问:每个训练样例都对应一个上文,输入层不停的在更换不同的上文,但是输入层到隐含层的权值都是一套,那么输入层到隐含层的权值的物理意义是什么?或许,代表着上文的某个固定的位置对隐含层某个特征的影响。
tanh隐含层:为什么用tanh,而不是sigmoid,作者也没提,只说tanh也是常用的。
softmax输出层:节点数量=词表大小。比如说词表里有10万个词,那么就有10万个节点。在给定wt上文的情况下,输出10万个概率,这10万个概率分别代表这个上文生成10万个词的概率,生成wt的概率是其中的一个,目标就是要最大化的wt概率。因为输出层输出的是概率,所以就要对输出层的值做归一化,softmax就是干这个事儿的。
解释一下上图中的各个参数。C是词表中所有词向量构成的矩阵,C(wt)就是词wt的向量。f(i, wt-1,…,wt-n+1)是一个概率,表示在给定上文wt-1,…,wt-n+1的情况下,第i个词的概率。
### 2.1.3 形式化描述优化目标的类型是最大似然估计,T是训练样本的数量,最大化L,θ是NN和词向量的参数,R的意义我还不太懂。
上式就是一个softmax,表示在给定上文wt-1,…,wt-n+1的情况下,wt的概率。
y是softmax之前的值,x是上文wt-1,…,wt-n+1拼接成的向量,如下
H是输入层到隐含层的权值,隐含层激活函数是tanh,U是隐含层到输出层的权值,W是输入层到输出层的直连(目的是加快训练速度,why?),b,d分别是隐含层和输出层的偏置向量。
假设上文长度为n,词向量维度m,隐含层节点数量h,词表大小|V|,则H是h_(n-1)m的矩阵,U是|V|_h的矩阵,W是|V|*(n-1)m的矩阵,C是|V|*m的矩阵,b是维度|V|的向量,d是维度h的向量。
参数更新方法是SGA(Stochastic gradient ascent),为啥不是SGD?
### 2.1.4 变形:能量最小化的网络Bengio受Hinton 2000年的一个工作的启发(product of experts),提出了能量函数E。神经网络的隐含层和输出层之间的连接变了,之前是一个权值矩阵,现在是一个向量,隐含层的输出值与这个向量做点乘。
其中
H,b跟上面的一样,v是输出的权值向量。x不仅包含了上文的词向量,还包含了当前词的词向量。E越小,x越可能是一个正常的词序列(是人话);E越大,x越不是正常的词序列(不是人话)。
接下来,通过能量函数定义了概率:
优化目标还是最大似然估计,用的SGA方法。
## 2.2 Yoshua Bengio:层次softmax(二叉树) ### 2.2.1 简介《Hierarchical Probabilistic Neural Network Language Model》 aistats 2005
论文里面提到,词向量不是他发明的,是Hinton 1986年首次提出的。神经网络语言模型也不是他发明的,是一个叫Schmidhuber的人在1996年提出的。
本篇论文没有提出新的模型,只是对以往模型的加速,Bengio如是说:
大部分的计算量都在softmax,作者就是针对softmax做优化
这篇论文里面,Bengio对他的能量函数稍作了修改,能量的计算方法:
F是词向量矩阵,Fv'是词v向量的转置,W,F是权值矩阵,b是偏置向量,bv是词v的偏置,a是输出权值向量。x是首尾连接的上文词向量
算概率的公式:
### 2.2.2 优化方法一般的softmax为什么慢呢?假设词表里面有1万个词,对于每一个训练样本,都要把1万个词的网络输出值都算出来,然后归一化算目标词的概率,慢就慢在要算1万个值,运算量是|V|。
怎么优化呢?如果说,这1万个词属于100类,每一类100个词,那么,做两次归一化就可以算出概率了,第一次找出目标词的类,算100个值,第二次算这个类下面目标词的概率,再算100个值,算200次就够了,比1万次少多了,运算量是
如果进一步增加类目层次,比如说类目层次是一个完整的二叉树,那么,运算量就是log2|V|
按照这个思路,词v需要表示成位向量(向量的每一位可以理解成类别),(b1(v),…,bm(v)),m的值跟v相关。位向量可以通过建立词的层次结构得到。Bengio用的是WordNet加聚类的方法。b1(v)=1表示v属于顶层的类别1,b2(v)=0表示v输于顶层类别下面的第0类。词v的概率计算公式是:
可以看到m次就可以算出概率了,而不是|V|次。其中m=log2|V|,如果|V|=10000, log2|V|=13.3。Bengio提到了考虑词频可以更快提速,Mikolov利用Huffman树的灵感可能是他自己想出来的,也可能是看到了这个才有的。
Bengio的树结构用了Wordnet和聚类方法,他给出了内部节点为什么也可以用向量表示的说法:
预测词的位向量某一位为1的概率的公式:
### 2.2.3 树的构造方法Wordnet定义了IS-A关系,是一个层次结构,不过还不能拿来直接用,因为有两个问题:一词多义和非二叉树。
Wordnet的一个节点可能有多个节点。K-means方法,训练样本中的段落是“doc”,词表示成doc的向量,向量中元素的值是TFIDF。下面看一下指标:
Hinton和Mikolov的树结构对结果都有提升,但Bengio的方法却下降了,可能是因为k-means过程不精确的缘故,毕竟TFIDF不那么完美。
## 2.3 Hinton:Log-Bilinear model(LBL) ### 2.3.1 简介《Three New Graphical Models for Statistical Language Modelling》 ICML 07
形如xTMy的模型叫做Log-bilinear的,Hinton的模型也是基于能量函数的,从rbm开始,一步步修改能量函数,最后得到Log-bilinear的能量函数,公式如下:
R是所有词向量构成的矩阵,vi是one-hot的,viR相当于选出wi对应的词向量,Ci是方阵,行列数都等于词向量的维度,可以看做是wi到wn的线性变换。b是偏置向量。
直觉上,上面的模型是用上文词向量的线性组合来预测当前词的词向量,也可以看做是一个NN,有一个线性的隐含层和一个softmax输出层。算概率的方法跟以前一样:
## 2.4 Hinton:Log-Bilinear+层次softmax(HLBL) ### 2.4.1 简介《A Scalable Hierarchical Distributed Language Model》 NIPS 08
同样是层次方法,比Bengio的快,因为去掉了非线性隐含层
层次构造同样是聚类方法,比Bengio的指标高,因为他聚类的材料好,不是TFIDF向量
### 2.4.2 HLBL模型列一下这篇论文对LBL的描述,**没变
rwi是词wi的词向量,Ci是对角阵,可以看做是上文每个位置的权值。bw是偏置,是个标量,表示的是词w上下文无关的信息:词频。
Ci设置成对角阵是为了加速计算(虽然理论上损失了模型的表现力)
HLBL基于上面的LBL模型,它是层次的,也是要建立一棵树,赋予每个词一个编码。同时,Hinton实现了一词多义的解决方法(Bengio提出的,但没实现),
D(w)是词w所有的编码
### 2.4.3 聚类方法先把词表里的词表示成向量,然后用高斯混合模型聚类。
注意,这里没有直接用词本身的向量来聚类,而是用上文的词的向量来生成当前词的向量。方法是把上文n-1个词的向量相加,得到一个新的向量,如果一个词有多个上文,那么算个期望,把期望的向量作为当前词的向量,用于聚类,生成树结构。
算法会用Bootstrapping的方法,多次迭代,先随机生成向量,聚类成树,修改向量,再聚类成树。。。
为什么第一次迭代,用“随机词向量”聚类成的树做层次softmax,也可以得到差不多的结果呢?
先看看论文里面指标:
随机的树都能达到这样有意义的指标,为啥?我的理解是,虽然树是随机的,但是毕竟给每个词分配了不同的编码,训练的结果的确拟合了这个编码,所以结果是有意义的。只不过编码的质量不高,导致结果不是最好的。
为什么Bootstrapping的方法有效?更具体点说,为什么用“第一轮学出的词向量”聚类成的树要比“随机词向量”聚类成的树要好?
我的理解是:因为,聚类用的是上文融合成的向量,而不是当前词的向量,所以,这棵树其实是一个上文树,属于同一个父节点的两个节点有着相似的上文(同一类节点有相似的上文)。既然兄弟节点有如此相似的上文,那么一定不容易区分它们。如果模型还能准确区分的话,那么模型肯定是好模型。Bootstrapping的过程其实就是在区分越来越难的情况下还能区分准的过程。
所以,总的来说,层次softmax部分的树结构利用了语料本身的性质融入了一些限制,让模型去拟合这个限制。同理,Bengio Wordnet的方法加的限制是——兄弟节点的语义相似
## 2.5 Mikolov:层次softmax(Huffman树) ### 2.5.1 简介《Efficient Estimation of Word Representations in Vector Space》 ICLR 13
Huffman树的优点:
Mikolov随后做了很多实验,得到了很多有用的结论。
**增加训练数据量,同时增加向量维度,可提高词向量的质量。单独提升二者之一,词向量的质量提升有限。**数据如下表
CBOW在语法上比较厉害,Skip-gram在语义上比较厉害
向量维度足够大,训练数据量足够多,迭代一次训练数据就可以
词向量的用途:
《Distributed Representations of Words and Phrases and their Compositionality》 NIPS 13
层次softmax:没有啥新东西,就是树结构用的Huffman树
负采样:一种新的加速方法,代替层次softmax,不用树结构了。**是把当前词作为正例,在随机从词表中其他的词里面抽样一些词作为反例,正例反例都有了,就可以训练了。因为随机抽的数量不多,一般5个,避免了普通softmax的计算量,速度也很快。
负采样的语义实验效果好。
亚采样:一定概率的忽略掉高频词。语料中高频词出现的次数多,包含的信息相对少,可一定概率忽略之,从另一个角度看,高频词对应的词向量在训练了几百万次之后也不应该有较大的改变。忽略的概率如下:
t是常数,一般是10-5,f(wi)是词wi的词频。这个方法不仅提速了,而且还提高了效果,如下表,语义上有提升,语法上有下降。
论文中还给出了向量可加组合性(Additive Compositionality)的解释,不过只针对于Skip-gram模型得到的向量。可加组合性的实验结果如下:
为啥会有这种效果呢?Skip-gram是用当前词来预测其周围的词,也就是说这些词的词向量暗含了周围词的概率分布,由于softmax的存在,词向量相当于取log的概率。那么,两个向量的相加可以这样解释:i,j代表词表中词的下标
上面的等号代表的是一种相关性,两个词向量相加就是这两个词周围词概率分布的乘积。正是因为有这样的含义,与这两个词向量经常同时共现的词,就会与他俩最相似。
【参考文章】
是重新开始,也是新的开始。离开多少年后,还是无法完全放手,“她”还是我想搞定的那一个。。。
其实我说的是操作系统。
最近看了nginx的源代码,看了mit的jos。想要写文章总结并炫耀一下的时候,忽然发现无处下笔。因为只是在看,没有做什么有意义的事情。并且花的时间太集中,十一假期每时每刻都在看,这种强度当上班后肯定吃不消。后面定两个原则:
GO!
*aaa
如何用一句话描述OS的作用?xv6 book里这样说:
The job of an operating system is to share a computer among multiple programs and provide a more useful set of services than the hardware alone supports。
从应用程序的角度看,它们想要OS提供什么?
- 抽象的硬件,方便、可移植的
- 硬件复用,多应用之间多路复用
- 隔离,不让有bug的应用影响其他应用
- 通信,允许应用之间共享信息
OS通过interface给应用程序提供服务,设计这种interface很难。一方面,interface需要simple、narrow,既简单、不会用错;另一方面又要给应用程序提供复杂功能。解决这个矛盾的技巧就是设计一种机制让所有interfaces可以组合来提供通用性。
argv[0] = "echo";
argv[1] = "hello";
argv[2] = 0;
exec("/bin/echo", argv);
printf("exec error\n");
上面提到了interfaces的组合,redirect命令的实现就是组合的例子
argv[0] = "cat";
argv[1] = 0;
if(fork() == 0) {
close(0);
open("input.txt", O_RDONLY);
exec("cat", argv);
}
新建一个子进程执行另一个程序貌似只需要一个系统调用就行,但是fork和exec并没用合并到一起,而是分开,这样就可以在fork和exec之间设置子进程的状态。而且,cat的代码实现只需要写死从fd 0读就行,至于fd 0是什么(文件 or stdin)由父进程控制。所以,file descriptor的抽象能力很强。
PS:open总会使用最小的可用fd,dup,pipe也是
Two file descriptors share an offset if they were derived from the same original file descriptor by a sequence of fork and dup calls
一个pipe是kernel的一块内存,对进程来讲体现为一对fd,一个用来write,一个用来read。read操作或者等待write数据,或者当write端相关的fd都关闭时(read返回0,就像read到文件末尾一样)退出(fork或dup出的write端fd都要关闭)。
int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
exec("/bin/wc", argv);
} else {
write(p[1], "hello world\n", 12);
close(p[0]);
close(p[1]);
}
exec wc之前,最好close(p[1]),具体还不理解
mit 2002年才开始有OS课程,最初使用UNIX v6的源代码和John Lions的注释教学,后来老师们觉得这个代码用的C太古老,而且只适用于PDP 11机器架构,不适合intel x86机器上,学生们要在新老两种语言、两种架构之间切换,总之就是太坑学生,就重写了。2006年夏天开始,2006年秋天写完,就是xv6
reference:https://pdos.csail.mit.edu/6.828/2014/xv6.html
Version | Date | 简介 |
---|---|---|
First Edition Unix | 1971-11 | 纯汇编,包括shell。多任务,多user |
Second Edition Unix | 1972-06 | 开始使用C。更多系统调用 |
Third Edition Unix | 1973-02 | 最后一版使用汇编开发的UNIX。引入pipe |
Fourth Edition Unix | 1973-11 | 纯C,基本是3版的重写 |
Fifth Edition Unix | 1974-06 | kernel image大小26k,此时还没有stdio,bounce shell,编辑器是ed |
Sixth Edition Unix | 1975-05 | kernel image大小29k,加入Mike Lesk贡献的stdio |
Seventh Edition Unix | 1979-01 | kernel image大小51k。更大文件系统,更多user,更可移植,更慢。人们开始给他打补丁,这些补丁或者回到bell lab,或者去向BSD |
现在的Linux有几M了吧。
reference:http://minnie.tuhs.org/cgi-bin/utree.pl
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.