第3门:文本表示
第1章:文本词与句的表示
1.文本表示概述
文本表示,简单的说就是不将文本视为字符串,而视为在数学上处理起来更为方便的向量。而怎么把字符串变为向量,就是文本表示的核心问题。
1.1 为什么要进行文本表示
- 根本原因是计算机不方便直接对文本字符串进行处理,因此需要进行数值化或者向量化。
- 便于机器学习。不仅传统的机器学习算法需要这个过程,深度学习也需要这个过程。
- 良好的文本表示形式可以极大的提升算法效果。
1.2 文本表示分类(基于粒度)
- 文本表示
- 句子表示(短文本)
- 词表示
1.3 文本表示分类(基于表示方法)
- 离散表示
- one-hot表示
- multi-hot表示
- 分布式表示
- 基于矩阵
- 基于降维的方法
- 基于聚类的方法
- 基于神经网络
- CBOW
- Skip-gram
- NNLM
- C&W
2. 文本离散表示:词袋模型与TF-IDF
2.1 最简单的文本表示:词袋子模型(bag of words)
词袋子模型是一种非常经典的文本表示。顾名思义,它就是将字符串视为一个 “装满字符(词)的袋子” ,袋子里的 词语是随便摆放的。而两个词袋子的相似程度就以它们重合的词及其相关分布进行判断。
举个例子,对于句子:
“我们这些傻傻的路痴走啊走,好不容易找到了饭店的西门”。
我们先进行分词,将所有出现的词储存为一个词表。然后依据 “词语是否出现在词表中” 可以将这句话变为这样的向量:
[1,0,1,1,1,0,0,1,…]
词表:[我们,你们,走,西门,的,吃饭,旅游,找到了,…]
其中向量的每个维度唯一对应着词表中的一个词。可见这个向量的大部分位置是0值,这种情况叫作**“稀疏”**。为了减少存储空间,我们也可以只储存非零值的位置。
在实际应用中,这种方法非常常用。
2.1.1 词袋子模型的优点
- 简单,方便,快速
- 在语料充足的前提下,对于简单的自然语言处理任务效果不错。如文本分类。
2.1.2 词袋子模型的缺点
- 其准确率往往比较低。凡是出现在文本中的词一视同仁,不能体现不同词在一句话中的不同的重要性。
- 无法关注词语之间的顺序关系,这是词袋子模型最大的缺点。如“武松打老虎”跟“老虎打武松”在词袋子模型中是认为一样的。
2.2 对词袋子模型的改进:TF-IDF
2.2.1 不仅考虑词语是否出现,还考虑其出现的次数或者频率(TF)
[1,0,2,1,2,0,0,1,…]
词表:[我们,你们,走,西门,的,吃饭,旅游,找到了,…]
“的”这个次占了词频的很大的比重,而它对确定文本信息几乎没什么用。所以我们应该忽略掉这些词,取消掉它们的影响。一种方法是维护一个停用词表。但这种方式太粗暴。
改进方式:一个词预测主题的能力越强(与主题的关联程度),权重越大,反之,权重越小。在网页中看到“原子能”这个词,或多或少能够了解网页的主题,而看到“应用”一词,则对主题基本上还是一无所知。因此,“原子能”的权重应该比应用大。
容易发现,如果一个关键词只在很少的网页出现,通过它就容易锁定搜索目标,它的权重也就应该比较大。反正,如果一个词在大量的网页中出现,看到它仍然不清楚要找什么内容,因此它的权重应该小。(比如你在搜索“python gensim”,“python”这个关键词会在很多的网页中出现,内容可能是python入门介绍,python官网,python应用,而“gensim”却只会在相对比较少的网页中出现,一般所以gensim的官网,gensim的安装教程,gensim的学习笔记等,而后者是我们更倾向于看到的内容)。
2.2.2 统计逆文档频率——IDF
不仅考虑这个词在当下文本的出现的概率,还考虑出现该词语的文档占总文档出现的频率(DF)。其基本假设是如果一个词语在不同的文档中反复出现,那么它对于识别该文本并不重要。如高频词“我们”、“那么”之类。
严格来说,逆文档频率的公式为-log(出现该词语的文档占总文档出现的频率)
如关键字“python”在10万个网页中出现,而“gensim”只在1000个网页中出现,那么“gensim”的权重就会比“python”多,这样搜索出来的结果就与你想要的结果越贴近。比如,假定中文网页数是=10亿,停止词的在所有的网页中都出现,即D=10亿,那么它的IDF = log(10亿 / 10亿) = log(1) =0。假如专用词“原子能”在两百万个网页中出现,即Dw=200万,则它的权重IDF=log(500) =2.7。又假定通用词“应用”,出现在五亿个网页中,它的权重IDF = log(2)则只有 0.3。
TF-IDF的概念被公认为信息检索中最重要的发明。在搜索,文献分类,与其他相关领域有广泛的应用。
3.文本分布式表示:word2vec
- 参考笔记:斯坦福cs224d Lecture 1
3.1 词向量的one-hot表示
我们拿英文举例。
英语中大约有1300万个词组(token,自定义字符串,译作词组),不过他们全部是独立的吗?并不是哦,比如有一些词组,“Feline猫科动物”和“Cat猫”,“Hotel宾馆“和”Motel汽车旅馆”,其实有一定的关联或者相似性在。因此,我们希望用词向量编码词组,使它代表在词组的N维空间中的一个点(而点与点之间有距离的远近等关系,可以体现深层一点的信息)。每一个词向量的维度都可能会表征一些意义(物理含义),这些意义我们用“声明speech”来定义。例如,语义维度可以用来表明时态(过去与现在与未来),计数(单数与复数),和性别(男性与女性)。
说起来,词向量的编码方式其实挺有讲究的。咱们从最简单的看起,最简单的编码方式叫做one-hot vector:假设我们的词库总共有n个词,那我们开一个1*n的高维向量,而每个词都会在某个索引index下取到1,其余位置全部都取值为0.词向量在这种类型的编码中如下图所示:
w a a r d c a r k = [ 1 0 0 ⋮ 0 ] , w a = [ 0 1 0 ⋮ 0 ] w a t = [ 0 0 1 ⋮ 0 ] ⋯ w z e b r a = [ 0 0 0 ⋮ 1 ] w^{aardcark}= \begin{bmatrix} 1 \\ 0 \\ 0 \\ \vdots \\ 0 \end{bmatrix} , w^{a}= \begin{bmatrix} 0 \\ 1 \\ 0 \\ \vdots \\ 0 \end{bmatrix} w^{at}= \begin{bmatrix} 0 \\ 0 \\ 1 \\ \vdots \\ 0 \end{bmatrix} \cdots \\ w^{zebra}= \begin{bmatrix} 0 \\ 0 \\ 0 \\ \vdots \\ 1 \end{bmatrix} waardcark=⎣⎢⎢⎢⎢⎢⎡100⋮0⎦⎥⎥⎥⎥⎥⎤,wa=⎣⎢⎢⎢⎢⎢⎡010⋮0⎦⎥⎥⎥⎥⎥⎤wat=⎣⎢⎢⎢⎢⎢⎡001⋮0⎦⎥⎥⎥⎥⎥⎤⋯wzebra=⎣⎢⎢⎢⎢⎢⎡000⋮1⎦⎥⎥⎥⎥⎥⎤
这种词向量编码方式简单粗暴,我们将每一个词作为一个完全独立的个体来表达。遗憾的是,这种方式下,我们的词向量没办法给我们任何形式的词组相似性权衡。例如:
( w h o t e l ) T w m o t e l = ( w h o t e l ) T w c a t = 0 (w^{hotel})^Tw^{motel}=(w^{hotel})^Tw^{cat}=0 (whotel)Twmotel=(whotel)Twcat=0
(注:这里 W − 1 W^{-1} W−1是 W W W的逆矩阵,它们有关系: W − 1 ∗ W = 1 W^{-1}*W=1 W−1∗W=1,注意到hotel和motel是近义词)
究其根本你会发现,是你开了一个极高维度的空间,然后每个词语都会占据一个维度,因此没有办法在空间中关联起来。因此我们可能可以把词向量的维度降低一些,在这样一个子空间中,可能原本没有关联的词就关联起来了。
3.2 基于SVD降维的表示方法
这是一种构造词嵌入(即词向量)的方法,我们首先会遍历所有的文本数据集,然后统计词出现的次数,接着用一个矩阵 X X X来表示所有的次数情况,紧接着对X进行奇异值分解得到一个 U S V T USV^T USVT的分解。然后用 U U U的行(rows)作为所有词表中词的词向量。对于矩阵 X X X,我们有几种选择,咱们一起来比较一下。
3.2.1 词-文档矩阵
最初的想法是,我们猜测相互关联的词组同时出现在相同的文件中的概率很高。例如,“银行”、“债券”、“股票”、“钱”等都可能出现在一起。但是,“银行”、“章鱼”、“香蕉”和“曲棍球”可能不会一直一起出现。基于这个想法,我们建立一个词组文档矩阵 X X X,具体是这么做的:遍历海量的文件,每次词组i出现在文件j中时,将 X i j X_{ij} Xij的值加1。不过大家可想而知,这会是个很大的矩阵 R ∣ V ∣ × M R^{|V| ×M} R∣V∣×M,而且矩阵大小还和文档个数M有关系。所以咱们最好想办法处理和优化一下。
3.2.2 基于窗口的共现矩阵X
我们还是用一样的逻辑,不过换一种统计方式,把矩阵 X X X记录的词频变成一个相关性矩阵。我们先规定一个固定大小的窗口,然后统计每个词出现在窗口中次数,这个计数是针对整个语料集做的。可能说得有点含糊,咱们一起来看个例子,假定我们有如下的3个句子,同时我们的窗口大小设定为1(把原始的句子分拆成一个一个的词):
- I enjoy flying.
- I like NLP.
- I like deep learning.
由此产生的计数矩阵如下:
然后我们对X做奇异值分解,观察观察奇异值(矩阵的对角元素),并根据我们期待保留的百分比来进行截断(只保留前k个维度):
. . . . . . . . .
然后我们把子矩阵 U 1 : ∣ V ∣ , 1 : k U_{1:|V|,1:k} U1:∣V∣,1:k