Skip to content

Erimus-Koo/chinese_keywords

Repository files navigation

去年9月学了Python拿了点数据,国庆写了个不借助字典的中文分词方法,不同于网上流行的信息熵算法,更加直观易懂一些。
本来升级到py3就一直没用过这个脚本,前一阵看到知乎有人分析了红楼梦,手痒把脚本更新整理了一下。
然后今天刚好看到 Tartaglia 和 Cardano 的故事,决定还是发一下做个记录。

0. 题目

假设拿到任意一篇中文的文章,我们希望找出文中的 关键字,找出其中 出现频率最高词组,应该怎么做?
如果统计单字的出现频率,那非常地简单,但是毫无意义。
那么核心问题就是,如何找出 词组

对于这个问题,大致我会分两部分来说。
一部分是关于词组的特性。字和字如何凝聚成词。
另一部分是关于处理顺序。我觉得这个部分更加重要。


1. 词组的特性

数学老师为什么这么贱?
托起人类文明的数学
BBC 数学的故事
BBC 电的故事
哲学Van花筒

上面这几个视频标题里,出现最多的 词组 有哪些呢?【BBC】是英文我们就先不统计好了,【数学】出现了3次,【故事】出现了2次,其他字组合都只出现1次。

1.1 聚合规则

第一个问题,我们怎么知道【数学】是一个单词?
这里可以发现,【数】一共出现了3次,【学】一共出现了4次,【数学】一共出现了3次。
也就是说出现的所有【数】都被组成了【数学】,那么我们认为【数学】应该是一个词组,出现了3次。

把【数学】作为主词,【数】【学】作为因子,这里我们还能发现3条简单的规则:

  1. 主词的数量不可能超过因子的数量。
  2. 如果主词是一个词组,那么要扣除对应因子的数量。
  3. 如果主词不是一个词组,那么主词计量清零,因子数量不扣除。

这时候如果加入一条数据:

你学得好不好心里没点B数嘛?

现在源数据变成了【数】4次,【学】5次,【数学】3次。这时候大部分的【数】还是被组合成了【数学】,所以我们还是可以认为【数学】是个词组。

到这里,我们可以尝试列出一条公式。

min(A_times,B_times)*50% <= wordTimes  

说人话就是,两个因子中,出现次数少的那个因子,如果有50%以上(很大概率)都被父级占用了,那么父级很可能是一个词组,这个因子应该被聚合。

其中50%是一个可设定的阈值,这个我们留到后面再说。
但需要注意的是,这个公式不仅对二字词有效,对更多字的词也是有效的。

1.2 排除规则

但仔细审题,我们并没有限制词组只能是两个字,所以其实严格从长统计,不是【故事】出现2次,而是【的故事】出现了2次。

但显然,我们很容易知道【的故事】不是一个词组,【故事】才是,那怎么去除【的】呢?
因为还有【目的】【标的】这类含【的】的单词,也不能一杆子打死。

但在这个例子里,无法展现现实情况,不过我们可以想象一下,一般来说一篇文章,【的】【是】这类词出现的频率会非常非常地高。

那么我们假设一下这个情况:
【的故事】2次,【的】20次,【故事】2次。
那么我们隐约又能列出一个公式。

max(A_times,B_times)*10% <= wordTimes  

说人话就是,因子里出现次数多的那个,如果父级只占用了它的10%都不到,那这个因子很可能是一个常规字,所以不应该和父级组合为一个词。

这里的10%也是可调整的阈值。

1.3 合成规则小结

简单来说就是,我们可以把任意一个词都拆分成两部分,判断他们的出现次数,从而判断这个词是否是一个 词组

同时回到我们的例子,【数】4次,【数学】3次,我们一开始的例子里,统计是重复的。那么通过判断,我们如果判定聚合成功,那么确定【数学】3次。然后,我们要把【数】出现的次数从4变1,避免重复计数。也就是说

如果 合成成功:  
    父级不变。  
    两个因子出现次数,需要各自减去父级出现次数。  
反之:  
    父级出现次数归零。  
    因子出现次数不变。  

有了这三个基础,接下去我们要确定的是处理的具体顺序。


2. 文本预处理

一般文章都会含有标点符号、回车、英文数字等等,这些是天然的断句,我们可以利用一下。比如刚才的话很简单的切割一下,就会变成

同时回到我们的例子|数|次|数学|次|我们一开始的例子里|统计是重复的|那么通过一次是否合成的判断|我们判定合成成功|那么确定|数学|次|然后|我们要把|数|出现的次数从|变|避免重复计数

这样就把文章切割成了若干句子。然后,我们取出句子中的所有词的组合统计出现次数。考虑到长度,仅显示【避免重复计数】这一句的情况,词长限制到3个字。

1 避  
1 免  
2 重  
2 复  
2 计  
6 数  
1 避免  
1 免重  
2 重复  
1 复计  
1 计数  
1 避免重  
1 免重复  
1 重复计  
1 复计数  

根据我们前一节的方法,显然【重复】是一个词语。【避免】也算,但只有一次。【计数】就很微妙了,根据我们预设的那两个阈值,有可能算,也有可能不算。

当然这仅仅是不到80个字的统计结果,有很多可上可下的判断,实际例子会更清晰一些。下面我直接贴一张图。

绿色格子是各级长度的词,各自上面的灰色格子是出现的次数。各自下面的数字,是每一步判断后的结果。


3. 判断顺序

我们按照以下规则来处理。

  1. 从短的词开始聚合。
    比如先判断所有2字词是否是词组,判断完之后判断3字,4字。

  2. 同长度的词,从出现频率最高的开始。
    因为出现频率高的很可能是词组,如果先判断低频的,很可能抢夺高频词的因子,导致高频词聚合失败。

  3. 因子可以重叠。如果合成成功,扣除因子数量之外,还要退回重叠部分的次数。
    因子A和因子B有重复部分R,A合成时R作为因子被扣了一次,B合成又扣了一次。那么AB合成C,等于C占用了两次R,所以要退还一份。

  4. 如果合成成功,那相邻的词自然就不作为词组。
    比如前面图中的二字词,本来就是有重叠的部分的。如果【域名】作为一个词组成立了,那么【网域】就判断为非词组。


Sample

用刚才的图,我们实践一下。

从短到长,从2字词开始。
从多到少,【域名】出现19次,所以从它开始。
判断过程很简单,我就省略了。
聚合成功,【域名】下方记19(绿色),【域】剩余1,【名】剩余2。
然后把相邻的【网域】判断为非词组,次数清零。


【互联】【联网】都是16次,从【互联】开始。
聚合成功,然后把【联网】清零。


2字词判断完毕。开始3字。

【互联网】16次,从它开始。
【互|联网】合成失败,【互联|联网】合成失败,【互联|网】合成成功。
【互联网】记16次(蓝色),【互联】0,【网】剩6。
【联网域】【网域名】清零。


3字词完毕。开始4字。
四字合成全部失败。


开始5字。

【互联网域名】合成成功。
【互联网】剩10,【域名】剩13。


所以这部分最终按出现频率排序,取出现5次以上的,就是。

13    域名  
10    互联网  
 6    互联网域名  
 6    网  
 5    联  

最后剩下的词


再给两个例子

这个例子最后出现了重叠的部分因子归还

这个例子需要注意,在【侯变化】合成时,【侯】不够了,所以合成失败。


4. 代码

Github | chinese_keywords
其中detectKeywords3.py是我最后改的 python3 版本。

其他的都是 py2 的,懒得整了。
detectKeywords.py是 py2 的定稿,写3的时候发现有些地方没想明白,可能有不对的。
看看自己刚学的代码真的匪夷所思,说明还是稍微有点长进。虽然今年都没怎么写过。

这个算法可能含有的问题有:

  1. 因为因子的次数和上级词分离,所以如果有词长限制高的时候,短词的次数会被剥夺。比如【德云社】出现33次,但如果你词长选到5个字,再刚好满足了别的条件,那么【离开德云社】也会变成一个词,比如5次,那么【德云社】就统计为28次了。
  2. 聚合和排除的参数还是手动设的,所以参数有时候会有挺大影响,比如聚合参数0.8的时候,【相声】就会聚合失败,这个挺无解的。
  3. 还有出现次数也会影响结果。因为次数不满的,为了省计算会直接作聚合失败处理,而不是完全统计后再排除次数低的。可能有点影响。

5. 吐槽

  • 其实这个方法还是基于长文章比较管用,对短文本效果应该就不好。

  • 写完了发现我这个也不能算分词法,确切地说是关键字提取法。

  • 感觉py3改写之后速度还行,印象中py2下挺慢的。git也放了去年写的py2版本,有几个地方的构想好像还有点问题,那时候写了两天人都晕了。py3的版本应该是都改好了。

  • 这个方法对日文韩文等也适用。但实测日文碰到助词和词尾等会有点问题。

  • 现在如果让我选,我有点倾向于做个字典库了。去年刚学,根本不知道现成的库怎么用……

  • 相比信息熵的方法,我也说不上好不好。反正去年在网上看到过这个方法,后来也没跑通,就拉倒了。

PS:还是跑了一下信息熵的方法。
简单跑下来发现凝聚度有点小bug,按我看到那个现成代码的算法(P = 词频/总字数),出现次数多的词凝聚度反而低。比如20字的文章,A词和两个因子都出现5次,凝聚度4。B词和其因子都出现2次,凝聚度是10。我觉得可能是概率的算法的有问题吧。
而且自由度我还没很好地理解,但人工干预对结果影响也很大。
打算先补补概率的课,有空再看看。

Python 中文 无字典 关键字 单机 算法

About

无字典中文关键字提取法

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages