Fork me on GitHub

NLP札记3-信息抽取

信息抽取

信息抽取是个宽泛的概念,指的是从非结构化的文本中提取出结构化的信息来的一种技术。信息抽取(information extraction),即从自然语言文本中,抽取出特定的事件或事实信息,帮助我们将海量内容自动分类、提取和重构。信息通常包括实体(entity)、关系(relation)、事件(event)

新词提取

新词

新词指的是词典之外的词语。新词的提取分为:

  1. 提取大量的文本(生语料)中的词语,无论新旧
  2. 用词典过滤掉已有的词语,得到新的词语

信息熵

信息熵指的是某条消息所含的信息量。不确定性越大,信息量越大。计算方法如下:
$$
H(x)=-\sum_xp(x)logp(x)
$$
如果对数函数的底数是2,信息上的单位就是
比特

具体到新词提取中,给定字符串S作为词语选取,X定义为左边可能出现的字符(左邻字),则成H(X)为S的左信息熵

互信息

互信息指的是两个离散型随机变量XY之间的相关程度的度量。
$$
\begin{align}
I(X;Y)
& =\sum_{x,y}p(x,y)log \frac{p(x,y)}{p(x)p(y)} \
& = E_{p(x,y)} log\frac{p(x,y)}{p(x)p(y)}
\end{align}
$$
在韦恩图中,

  • 并集:联合分布的信息熵$H(X,Y)$
  • 差集:条件熵$H(X|Y)$
  • 交集:互信息$H(X;Y)$

互信息越大,两个变量的关系越密切。同时发生的可能性也就越大。

提取新词

有了信息熵和互信息,将两个指标低于一定阈值的片段过滤掉,剩下的片段按照频次降序排列,截取最高频次的N个片段,完成词语的提取流程。具体模块在com.hankcs.hanlp.mining.word.NewWordDiscover相应的接口com.hankcs.hanlp.HanLP#extractWords

1
word_life_list = HanLP.extractWords(IOUtil.newBufferedReader(corpus), 100)   # 提取100个关键词

接口的参数为

  • reader 从reader中获取文本,逐行读取和处理,内存更省

  • size 需要提取词语的数量

  • NewWordsOnly 是否只提取词典中没有的词语,返回OOV

  • max_word_len 词语最长长度,默认是4

  • min_freq 词语最低频率

  • min_entropy 词语最低熵。0.5左右,该值越大,越短的词语越容易被提取出来。

  • min_aggregation 词语最低互信息,一般50-200。该值越大,越长的词语越容易被提取出来

关键词提取

提取文章中重要的单词,而不是限于词语的新鲜程度,成为关键词提取

在进行提取的过程中,根据一份还是多份文档,提取算法分为单文档和多文档算法

  • 单文档:词频和TextRank
  • 多文档:TF-IDF

词频

文章中作者反复提及到的词语,通过统计文章每种词语的词频并排序,获取关键词。但是比如某些词语,比如“的”反复出现,但是并不是关键词。词频统计的步骤:

  • 分词
  • 停用词过滤
  • 按词频统计前n个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
counter = TermFrequencyCounter()
counter.add("加油加油中国队")
counter.add("中国观众在给中国队加油")
for termFrequency in counter:
print("%s = %d" %(termFrenquency.getTrem(), termFrequency.getFrequency()))
print(counter.top(2))

# 结果
中国=2
中国队=1
加油=3
观众=1
高呼=1
[加油=3,中国=1]
[女排,观众,欢呼]
  • 遍历counter默认是按照字典顺序遍历
  • top(N)返回的是词频最高的前N个单词及词频,降序排列
  • 标点符号等停用分词已经去除了,分词结果以语境为主
  • 索引模式:激活内置分词器的索引模式
1
2
counter.getSegment().enableIndexMode(true);  # 激活索引模式
counter.setSegment(new PerceptronLexicalAnalyzer().enableIndexMode(true)); # 切换分词器

笔记:使用词频统计的缺陷是,高频词语不等价于关键词。比如所有报道“奥运会”的文章中,将“奥运会”作为关键词并不合适,用户希望看到的是每篇文章的特色之处,而不是共性。

TF-IDF

TF-IDF(Term Frequency-Inverse Document Frequency, 词频-倒排文档频次)是信息检索中一个重要的词语重要程度的统计指标,广泛用于搜索引擎中,它属于多文档提取方法。综合考虑词语的稀有程度:

  • 正比于它在文中的频次

  • 反比于有多少文档包含它:包含该词语的文档越多,越不能体现文档的特色。

  • 计算方法有多种,如下一种

$$
\begin{align}TF-IDF(t,d)& = \frac{TF(t,d)}{DF(t)} \ & = TF(t,d)\cdot IDF(t)\end{align}
$$

上面每个参数的解释

  • t代表的是term
  • d代表的是document
  • TF(t,d)代表的是td中的出现频次
  • DF(t)代表的是多少篇文档包含t
  • DF的倒数成为$IDF$
1
2
3
4
5
6
7
counter = TfIdfCounter()   # 调用方法的类
counter.add("<id1>, <内容>") # 输入多篇文章
counter.add("<id2>, <内容>")
counter.add("<id3>, <内容>")
counter.compute() # 输入完毕
for id in counter.documents():
print(id + ":" + counter.getKeywordsOf(id, 3).toString()) # 根据每篇文章的TF-IDF提取关键词
  • add函数接受两个参数:文档id和文档内容
  • documents方法返回所有的文档id,供用户遍历
  • getKeywordsOf(id, 3)的两个参数:文档id和提取的关键词个数

TextRank

如果没有大型的语料库或者存储IDF的内存,又想改善瓷片统计的效果,使用TextRank方法。TextRank实际上就是谷歌的PageTank文本上的应用

PageRank是一种用于排序网页的随机算法。基本原理如下

  • 互联网看作是有向图,网页视作为节点
  • 节点$V_i$到节点$V_j$的超链接视作有向边
  • 初始化每个节点的权重$S(V_i)$都是1,通过迭代的方式更新每个节点的权重,更新的表达式为

$$
S(V_i)=(1-d)+d*\sum_{V_j \in In(V_i)}\frac{1}{|Out(V_j)|}S(V_j)
$$

  1. d是0-1之前的常数因子,用户点击链接跳出当前链接当前网站的概率
  2. $In(V_i)$表示链接$V_i$的集合
  3. $Out(V_j)$表示从$v_j$出发链接到的集合
  4. 外链权重和外链总数成反比,与提供外链的网站权重成正比

笔记:并不是外链越多,PR就越高。外链越多,权重就越低。

PageRank应用到关键词提取中,将单词看做是节点。每个单词的外链是来自前后固定大小的窗口内的所有单词。

1
keyword_list = HanLP.extractKeyWord(content, 5)

短语提取

固定多词表达串的识别。常用于搜索引擎的自动推荐,文档的简介生成等。

1
pharse_list = HanLP.extractPharse(text, 5)  # 两个参数是文档的内容和所需短语个数

关键句提取

BM25

一般的PageRank在句子颗粒度上行不通的,因为一篇文章中几乎不会出现两句完全相同的句子。引入了BM25算法衡量句子的相似度,改进连接的权重计算。

在搜索引擎中,查询串query是由多个词语构成的。BM25解决的是衡量多个词语与文档的关联程度。几个定义如下:

  • Q为查询语句,由关键字$q_1,…,q_n$组成
  • D是被检索的文档
  • $k_l$和$d$是两个常量,$avgDL$是所有文档的平均长度

BM25的定义如下:
$$
\mathrm{BM} 25(D, Q)=\sum_{i=1}^{n} \operatorname{IDF}\left(q_{i}\right) \cdot \frac{\operatorname{TF}\left(q_{i}, D\right) \cdot\left(k_{1}+1\right)}{\operatorname{TF}\left(q_{i}, D\right)+k_{1} \cdot\left(1-b+b \cdot \frac{|D|}{\operatorname{avg} D L}\right)}
$$
上面式子的特点

  1. 先对查询语句中所有单词的IDF加权求和,两个参数和TF可以看做是调整IDF权重的参数
  2. $k_1$越大,TF对正面文档得分的正面影响就越大;b越大,TF负面文档得分的正面影响就越大
  3. TF-IDF 中,当IDF固定时,得分正比于TF,长文档占据优势;BM25中,TF对得分的影响还必须考虑文档长度

TF-IDF的表达式再现:

$$
\begin{align}TF-IDF(t,d)& = \frac{TF(t,d)}{DF(t)} \ & = TF(t,d)\cdot IDF(t)\end{align}
$$

TextRank

在上面的BM25算法中,将句子视作一个查询语句,相邻的句子视作待查询的文档,得到它们之间的相似度。将该相似度看做是PageRank链接的权重,得到了TextRank算法。
$$
\mathrm{WS}\left(V_{i}\right)=(1-d)+d \times \sum_{V_{j} \in \ln \left(V_{i}\right)} \frac{\mathrm{BM} 25\left(V_{i}, V_{j}\right)}{\sum_{V_{k} \in O u\left(V_{j}\right) \mathrm{BM} 25\left(V_{k}, V_{j}\right)}} \mathrm{WS}\left(V_{j}\right)
$$
TextRank关键句提取模块的实现为TextRankSentence。核心的Java代码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for (int _ = 0; _ < max_iter; ++_)
{
double[] m = new double[D];
double max_diff = 0;
for (int i = 0; i < D; ++i)
{
m[i] = 1 - d;
for (int j = 0; j < D; ++j) /*上面表达式中的第一个求和式子*/
{
if (j == 1 || weight_sum[j] == 0) continue;
m[i] += (d * weight[j][i] / weight_sum[j] * vertex[j]);
}
double diff = Math.abs(m[i] - vertex[i]);
if (diff > max_diff)
{
max_diff = diff;
}
}
vertex = m;
if (max_diff <= min_diff) break;
}

当两次迭代间权重最大变化量小于阈值min_diff,或者总的迭代次数达到max_iter时算法终止。

1
sentence_list = HanLP.extractSummary(document,3)  # 两个参数:文档和所需要的句子数量

本文标题:NLP札记3-信息抽取

发布时间:2019年12月18日 - 10:12

原始链接:http://www.renpeter.cn/2019/12/18/NLP%E6%9C%AD%E8%AE%B03-%E4%BF%A1%E6%81%AF%E6%8A%BD%E5%8F%96.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea