N-Gram+最短路径分词

PS:这篇文章中的代码,仅为一个简单的DEMO,并未进行过代码和算法的优化,参数也未进行过调整,仅仅是演示了一个从训练模型到应用的完整过程

简介

关于这种分词方法,网上的相关文章已经是相当相当少了,最出名的就是NLPIR分词中采用了这种方法(貌似最早也是这个分词工具的作者提出的)

相关文章

自然语言处理中的N-Gram模型详解
自然语言处理中N-Gram模型的Smoothing算法
HanLP作者对N-ShortPath分词理解的一个简单介绍
吕震宇先生对NLPIR分词的理解(这些文章里有些地方暂时持保留意见,或者说对于这个分词算法本身就有很多疑问)

代码

from nltk.util import bigrams, trigrams
from nltk.text import Text
from nltk import FreqDist
from functools import reduce
from bidict import bidict
import numpy as np

corpus = [
    '<begin> 小鸟 声音 不大 , 却 句 句 在理 , 全场 都 静静 恭听。 <end>',
    '<begin> 他 说 : “ 神 是否 创造 世界 ,即 神 对 世界 的 关系 如何 ,这个 问题 其实 就是 关于 精神 对 感性 一般 或 抽象 对 实在、类 对 个体 的 关系 如何 的 问题 ;这个 问题 是 属于 人类 认识 和 哲学 上 最 重要 又 最 困难 的 问题 之一 , 整个 哲学史 其实 只在 这个 问题 周围 绕 圈子 , 古代 哲学 中 斯多葛派 和 伊壁鸠鲁派 间 、 柏拉图派 和 亚里士多德派 间 、 怀疑派 和 独断派 间 的 争论 , 中古哲学 中 唯名论者 和 实在论者 间 的 争论 , 以及 近代 哲学 中 唯心主义者 和 实在论者 或 经验主义者 间 的 争论 , 归根结底 都是 关于 这个 问题 。 <end>”',
    '<begin> 讨论 法 的 本位 问题 , 应该 局限 于 实在 法效 用 的 实现 借助 于 何种 规范 手段 的 范围 内 , 它 主要 应 讨论 " 法 是 什么 " 的 问题 , 而 不是 " 法 应当 是 什么 " 的 问题 。 <end>',
    '<begin> 现在 , 你 已是 全班 第一名 了 , 我们 都要 向 你 学习 , 我们 还会 继续 帮助 你 。 <end>',
    '<begin> 他们 的 罪恶 行径 也 从 反面 教育 我们 , 革命 的 政治工作 对于 我们 党 的 各项 工作 , 对于 我们 军队 和 人民 来说 , 确实 是 不可以 须臾 离开 的 生命线 。 <end>',
    '<begin> 从 研究系 办 的 刊物 来看 , 确实 登载 过 大量 的 讨论 社会主义 的 文章 , 似乎 亦 拥护 社会主义 , 但 实际上 这 只是 假象 。 <end>',
    '<begin> 他 那些 舞台 下 、 剧场 外 的 事 的确 是 鲜为人知 的 。 <end>',
    # '<begin> 他 说 的 确实 在理 <end>'
]

# 单字切分,暂时没用到
def atomic_split(param1, param2):
    if isinstance(param1, list):
        return param1 + list(param2.replace(' ', ''))
    else:
        return atomic_split(atomic_split([], param1), param2)


atomics = reduce(atomic_split, corpus)

#对语料的切分
def word_split(param1, param2):
    if isinstance(param1, list):
        return param1 + param2.split()
    else:
        return word_split(word_split([], param1), param2)


words = reduce(word_split, corpus)

#计算词频,索引
fd = FreqDist(words)

index = bidict()
pos = 0
for k, c in fd.items():
    index[k] = pos
    pos = pos + 1

#=====利用nltk的biggrams函数,建立gram矩阵==========================
grams = list(bigrams(words))

gc = np.zeros((fd.B(), fd.B()), dtype=np.int32)

#统计gram次数
for p1, p2 in grams:
    gc[index[p1], index[p2]] += 1

#统计gram概率
gp = np.zeros((fd.B(), fd.B()))


#平滑系数
ratio = 0.9

for row in range(0, fd.B()):
    for col in range(0, fd.B()):
        gp[row, col] = ratio * (gc[row, col] / fd[index.inv[row]]) + (
            1 - ratio) * (fd[index.inv[col]] / len(words))

#======================模型训练完成=================================

#=============求最短路径(非N-最短路径,算法和原方法不同,一个是因为对原算法有疑问,另一个为了快速完成DEMO)==================
def split(s, pos=0):
    if len(s) <= pos: return [{'key': '<end>'}]
    result = []
    for k in fd.keys():
        end = pos + len(k)
        if len(k) > 1 and end <= len(s) and k == s[pos:end]:

            result.append({'key': k, 'childs': split(s, end)})

    result.append({'key': s[pos:pos + 1], 'childs': split(s, pos + 1)})

    return result


def split_to_tree(s):
    return {'key': '<begin>', 'childs': split(input)}


def segment(node):
    k = node['key']
    childs = node.get('childs')

    if not childs:
        return

    i1 = index.get(k)
    for child in childs:
        i2 = index.get(child['key'])
        child['score'] = gp[i1, i2] if (i1 and
                                        i2) else (1 - ratio) * 1 / len(words)
        segment(child)


def shortest(node):
    childs = node.get("childs")
    score = node.get('score', 0)
    key = node.get('key')

    if not childs:
        return score, [key]

    current_score, current_seq = -1, []
    for child in childs:
        _score, _seq = shortest(child)
        if _score > current_score:
            current_score, current_seq = _score, _seq

    return current_score + score, [key] + current_seq


#================分词函数完成=================

input = '他说的确实在理'

# 将输入转化成tree
root = split_to_tree(input)
# 根据上面的gram概率模型,计算得分
segment(root)

#求最大得分
score, seq = shortest(root)
print(seq, score)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容