美文网首页
一个古老中文无词典抽词算法的MMA实现

一个古老中文无词典抽词算法的MMA实现

作者: Lohengriny | 来源:发表于2017-10-14 00:26 被阅读0次

    思路

    无词典抽词的主要问题就是如何判定某个序列是一个词。这里根据 matrix67 大佬的一篇老旧博文(最近访问不了,以后再补链接)选取了两个参数,一个我把它叫做结合度(combinity),定义如下:

    对于字符序列 a 和 b,
    cbt(a,b) = P("ab") / (P("a")*P("b"))
    即 ab 的频率除以 a 和 b 分别出现的频率之积。

    这样做的原理是:姑且认为 a 和 b 一起出现的频率越高就越可能是一个词。但是如果 a 或 b 本身就频率就很高,那么 a b 碰巧在一起的概率也会很高,因此要除以他们本身频率来修正。(比如一篇文章中“国的”出现的频率可能比“中国”高)。

    另一个参数为边界熵。定义为语料库中某一个字符串左边/右边第一个字符的熵。这是因为:如果一个字符串 a
    是一个词,那么 a 左右的字符都是自由的,边界熵应该较高。反之如果 a 不是一个词,那么 a 的左右应该经常出现能够和 a 组成词的字符, 边界熵较低。

    最后给结合度和边界熵设定一个阈值就完成了整个算法的思路。

    程序设计

    结合度的算法没有任何难度,直接计算频率即可,不过由于词可能不止由一个字组词,因此对于 abcdef 这样一个词,应该计算任意切分的结合度并取最小值,也就是计算 cbt(a,bcdef), cbt(ab,cdef) ……的最小值。

    这种计算 MMA 可以很好地完成。以“斗宗强者”为例
    先取得所有切分:
    s = "斗宗强者"; {StringTake[s, #], StringTake[s, {# + 1, -1}]} & /@ Range[StringLength@s - 1]
    输出为:{{"斗", "宗强者"}, {"斗宗", "强者"}, {"斗宗强", "者"}}, 然后依次计算结合度再取最小值即可。

    而边界熵的计算,首先要取得所有边界字符,用 StringCases 可以实现, 燃油 MMA 自带了求熵函数,直接求就行了……

    leftEntropy[corpus_, w_] := N@Entropy@StringCases[corpus, x_ ~~ w :> x]
    rightEntropy[corpus_, w_] := N@Entropy@StringCases[corpus, w ~~ x_ :> x]
    

    最后对一篇文章递归抽出所有词即可。全部代码:https://github.com/Lo-Aidas/MathematicaToys/blob/master/FindWords.nb

    充数的实例

    以小说《斗破苍穹》第300w~310w 字为语料库,绘制第 300w ~ 302w 的单词云:

    wordcloud.png

    不得不说确实中二度爆表……

    相关文章

      网友评论

          本文标题:一个古老中文无词典抽词算法的MMA实现

          本文链接:https://www.haomeiwen.com/subject/wpntuxtx.html