中文分词之HMM模型详解

2023-11-11

关于HMM模型的介绍,网上的资料已经烂大街,但是大部分都是在背书背公式,本文在此针对HMM模型在中文分词中的应用,讲讲实现原理。

尽可能的撇开公式,撇开推导。结合实际开源代码作为例子,争取做到雅俗共赏,童叟无欺

没有公式,就没有伤害。

模型介绍

第一次听说HMM模型是从李开复的博文论文中听说的:

李开复1988年的博士论文发表了第一个基于隐马尔科夫模型(HMM)的语音识别系统Sphinx,被《商业周刊》评为1988年美国最重要的科技发明。

出处点我

乍一听似乎很玄妙,但是其实很简单。下面是相关参数介绍,也是第一眼觉得很抽象,但是慢慢看下去随着具体含义的解释就渐渐清晰。

HMM(Hidden Markov Model): 隐式马尔科夫模型。

HMM模型可以应用在很多领域,所以它的模型参数描述一般都比较抽象,以下篇幅针对HMM的模型参数介绍直接使用它在中文分词中的实际含义来讲:

HMM的典型介绍就是这个模型是一个五元组:

  • StatusSet: 状态值集合
  • ObservedSet: 观察值集合
  • TransProbMatrix: 转移概率矩阵
  • EmitProbMatrix: 发射概率矩阵
  • InitStatus: 初始状态分布

HMM模型可以用来解决三种问题:

  1. 参数(StatusSet,TransProbMatrix,EmitRobMatrix,InitStatus)已知的情况下,求解观察值序列。(Forward-backward算法)
  2. 参数(ObservedSet,TransProbMatrix,EmitRobMatrix,InitStatus)已知的情况下,求解状态值序列。(viterbi算法)
  3. 参数(ObservedSet)已知的情况下,求解(TransProbMatrix,EmitRobMatrix,InitStatus)。(Baum-Welch算法)

其中,第三种问题最玄乎也最不常用,第二种问题最常用,【中文分词】,【语音识别】, 【新词发现】, 【词性标注】 都有它的一席之地。所以本文主要介绍第二种问题,即【viterbi算法求解状态值序列】的方法。

五元组参数在中文分词中的具体含义

接下来我们讲实的,不讲虚的,针对中文分词应用,直接给五元组参数赋予具体含义:

StatusSet & ObservedSet

状态值集合为(B, M, E, S): {B:begin, M:middle, E:end, S:single}。分别代表每个状态代表的是该字在词语中的位置,B代表该字是词语中的起始字,M代表是词语中的中间字,E代表是词语中的结束字,S则代表是单字成词。

观察值集合为就是所有汉字(东南西北你我他...),甚至包括标点符号所组成的集合。

状态值也就是我们要求的值,在HMM模型中文分词中,我们的输入是一个句子(也就是观察值序列),输出是这个句子中每个字的状态值。 比如:

小明硕士毕业于中国科学院计算所

输出的状态序列为

BEBEBMEBEBMEBES

根据这个状态序列我们可以进行切词:

BE/BE/BME/BE/BME/BE/S

所以切词结果如下:

小明/硕士/毕业于/中国/科学院/计算/所

同时我们可以注意到:

B后面只可能接(M or E),不可能接(B or E)。而M后面也只可能接(M or E),不可能接(B, S)

没错,就是这么简单,现在输入输出都明确了,下文讲讲输入和输出之间的具体过程,里面究竟发生了什么不可告人的秘密,请看下文:

上文只介绍了五元组中的两元【StatusSet, ObservedSet】,下文介绍剩下的三元【InitStatus, TransProbMatrix, EmitProbMatrix】。

这五元的关系是通过一个叫Viterbi的算法串接起来,ObservedSet序列值是Viterbi的输入,而StatusSet序列值是Viterbi的输出,输入和输出之间Viterbi算法还需要借助三个模型参数,分别是InitStatus, TransProbMatrix, EmitProbMatrix,接下来一一讲解:

InitStatus

初始状态概率分布是最好理解的,可以示例如下:

#B
-0.26268660809250016
#E
-3.14e+100
#M
-3.14e+100
#S
-1.4652633398537678

示例数值是对概率值取对数之后的结果(可以让概率相乘的计算变成对数相加),其中-3.14e+100作为负无穷,也就是对应的概率值是0。下同。

也就是句子的第一个字属于{B,E,M,S}这四种状态的概率,如上可以看出,E和M的概率都是0,这和实际相符合,开头的第一个字只可能是词语的首字(B),或者是单字成词(S)。

TransProbMatrix

转移概率是马尔科夫链很重要的一个知识点,大学里面学过概率论的人都知道,马尔科夫链最大的特点就是当前T=i时刻的状态Status(i),只和T=i时刻之前的n个状态有关。也就是:

{Status(i-1), Status(i-2), Status(i-3), ... Status(i - n)}

更进一步的说,HMM模型有三个基本假设(具体哪三个请看文末备注)作为模型的前提,其中有个【有限历史性假设】,也就是马尔科夫链的n=1。即Status(i)只和Status(i-1)相关,这个假设能大大简化问题。

回过头看TransProbMatrix,其实就是一个4x4(4就是状态值集合的大小)的二维矩阵,示例如下:

矩阵的横坐标和纵坐标顺序是BEMS x BEMS。(数值是概率求对数后的值,别忘了。)

-3.14e+100 -0.510825623765990 -0.916290731874155 -3.14e+100
-0.5897149736854513 -3.14e+100 -3.14e+100 -0.8085250474669937
-3.14e+100 -0.33344856811948514 -1.2603623820268226 -3.14e+100
-0.7211965654669841 -3.14e+100 -3.14e+100 -0.6658631448798212

比如TransProbMatrix[0][0]代表的含义就是从状态B转移到状态B的概率,由TransProbMatrix[0][0] = -3.14e+100可知,这个转移概率是0,这符合常理。由状态各自的含义可知,状态B的下一个状态只可能是ME,不可能是BS,所以不可能的转移对应的概率都是0,也就是对数值负无穷,在此记为-3.14e+100

由上TransProbMatrix矩阵可知,对于各个状态可能转移的下一状态,且转移概率对应如下:

#B
#E:-0.510825623765990,M:-0.916290731874155
#E
#B:-0.5897149736854513,S:-0.8085250474669937
#M
#E:-0.33344856811948514,M:-1.2603623820268226
#S
#B:-0.7211965654669841,S:-0.6658631448798212

EmitProbMatrix

这里的发射概率(EmitProb)其实也是一个条件概率而已,根据HMM模型三个基本假设(哪三个请看文末备注)里的【观察值独立性假设】,观察值只取决于当前状态值,也就是:

P(Observed[i], Status[j]) = P(Status[j]) * P(Observed[i]|Status[j])

其中P(Observed[i]|Status[j])这个值就是从EmitProbMatrix中获取。

EmitProbMatrix示例如下:

#B
耀:-10.460283,涉:-8.766406,谈:-8.039065,伊:-7.682602,洞:-8.668696,...
#E
耀:-9.266706,涉:-9.096474,谈:-8.435707,伊:-10.223786,洞:-8.366213,...
#M
耀:-8.47651,涉:-10.560093,谈:-8.345223,伊:-8.021847,洞:-9.547990,....
#S:-10.005820,涉:-10.523076,唎:-15.269250,禑:-17.215160,洞:-8.369527...

虽然EmitProbMatrix也称为矩阵,这个矩阵太稀疏了,实际工程中一般是将上面四行发射转移概率存储为4个Map,详见代码HMMSegment

到此,已经介绍完HMM模型的五元参数,假设现在手头上已经有这些参数的具体概率值,并且已经加载进来,(也就是有该模型的字典了,详见HMMDict里面的hmm_model.utf8),那么我们只剩下Viterbi这个算法函数,这个模型就算可以开始使用了。所以接下来讲讲Viterbi算法。

HMM中文分词之Viterbi算法

输入样例:

小明硕士毕业于中国科学院计算所

Viterbi算法计算过程如下:

定义变量

二维数组 weight[4][15],4是状态数(0:B,1:E,2:M,3:S),15是输入句子的字数。比如 weight[0][2] 代表 状态B的条件下,出现'硕'这个字的可能性。

二维数组 path[4][15],4是状态数(0:B,1:E,2:M,3:S),15是输入句子的字数。比如 path[0][2] 代表 weight[0][2]取到最大时,前一个字的状态,比如 path[0][2] = 1, 则代表 weight[0][2]取到最大时,前一个字(也就是)的状态是E。记录前一个字的状态是为了使用viterbi算法计算完整个 weight[4][15] 之后,能对输入句子从右向左地回溯回来,找出对应的状态序列。

使用InitStatus对weight二维数组进行初始化

已知InitStatus如下:

#B
-0.26268660809250016
#E
-3.14e+100
#M
-3.14e+100
#S
-1.4652633398537678

且由EmitProbMatrix可以得出

Status(B) -> Observed(小)  :  -5.79545
Status(E) -> Observed(小)  :  -7.36797
Status(M) -> Observed(小)  :  -5.09518
Status(S) -> Observed(小)  :  -6.2475

所以可以初始化 weight[i][0] 的值如下:

weight[0][0] = -0.26268660809250016 + -5.79545 = -6.05814
weight[1][0] = -3.14e+100 + -7.36797 = -3.14e+100
weight[2][0] = -3.14e+100 + -5.09518 = -3.14e+100
weight[3][0] = -1.4652633398537678 + -6.2475 = -7.71276

注意上式计算的时候是相加而不是相乘,因为之前取过对数的原因。

遍历句子计算整个weight二维数组

//遍历句子,下标i从1开始是因为刚才初始化的时候已经对0初始化结束了
for(size_t i = 1; i < 15; i++)
{
    // 遍历可能的状态
    for(size_t j = 0; j < 4; j++) 
    {
        weight[j][i] = MIN_DOUBLE;
        path[j][i] = -1;
        //遍历前一个字可能的状态
        for(size_t k = 0; k < 4; k++)
        {
            double tmp = weight[k][i-1] + _transProb[k][j] + _emitProb[j][sentence[i]];
            if(tmp > weight[j][i]) // 找出最大的weight[j][i]值
            {
                weight[j][i] = tmp;
                path[j][i] = k;
            }
        }
    }
}

如此遍历下来,weight[4][15] 和 path[4][15] 就都计算完毕。

确定边界条件和路径回溯

边界条件如下:

对于每个句子,最后一个字的状态只可能是 E 或者 S,不可能是 M 或者 B。

所以在本文的例子中我们只需要比较 weight[1(E)][14] 和 weight[3(S)][14] 的大小即可。

在本例中:

weight[1][14] = -102.492;
weight[3][14] = -101.632;

所以 S > E,也就是对于路径回溯的起点是 path[3][14]

回溯的路径是:

SEBEMBEBEMBEBEB

倒序一下就是:

BE/BE/BME/BE/BME/BE/S

所以切词结果就是:

小明/硕士/毕业于/中国/科学院/计算/所

到此,一个HMM模型中文分词算法过程就阐述完毕了。

也就是给定我们一个模型,我们对模型进行载入完毕之后,只要运行一遍Viterbi算法,就可以找出每个字对应的状态,根据状态也就可以对句子进行分词。

模型的训练问题

以上讲的前提是基于模型来进行切词,也就是假设我们手头上的HMM模型已经是被训练好了的(也就是InitStatus, TransProbMatrix, EmitProbMatrix这三个模型的关键参数都是已知的),没有涉及到这三个参数是如何得到的。 这三个参数其实也是基于已分词完毕的语料进行统计计算,计算出相应的频率和条件概率就可以算出这三个参数。具体在此就不讲了。

备注

HMM模型的三个基本假设如下:

  • 有限历史性假设:
P(Status[i]|Status[i-1],Status[i-2],... Status[1]) = P(Status[i]|Status[i-1])
  • 齐次性假设(状态和当前时刻无关):
P(Status[i]|Status[i-1]) = P(Status[j]|Status[j-1])
  • 观察值独立性假设(观察值只取决于当前状态值):
P(Observed[i]|Status[i],Status[i-1],...,Status[1]) = P(Observed[i]|Status[i])

源码参考

HMMSegment


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

中文分词之HMM模型详解 的相关文章

  • 字节跳动第五届青训营后端练习题——分割ip(Java版)

    题目 有效 IP 地址正好由四个整数 每个整数位于 0 到 255 之间组成 且不能含有前导 0 整数之间用 分隔 例如 0 1 2 201 和 192 168 1 1 是有效 IP 地址 但是 0 011 255 245 192 168
  • 归并排序,自顶向下,自底向上

    http blog csdn net cjf iceking article details 7920153
  • Nvidia-docker运行错误- Nvidia-docker : Unknown runtime specified nvidia

    使用nvidia docker的时候 出现了上面的bug 故总结一下 所使用的环境为ubuntu系统64位 GPU2080ti command1 sudo nvidia docker run e NVIDIA VISIBLE DEVICES
  • 动态规划算法的优化技巧

    关键词 动态规划 时间复杂度 优化 状态 摘要 动态规划是信息学竞赛中一种常用的程序设计方法 本文着重讨论了运用动态规划思想解题时时间效率的优化 全文分为四个部分 首先讨论了动态规划时间效率优化的可行性和必要性 接着给出了动态规划时间复杂度
  • 程序员面试题精选100题(41)-把数组排成最小的数

    程序员面试题精选100题 41 把数组排成最小的数 题目 输入一个正整数数组 将它们连接起来排成一个数 输出能排出的所有数字中最小的一个 例如输入数组 32 321 则输出这两个能排成的最小数字32132 请给出解决问题的算法 并证明该算法
  • 【NOIP 2004 提高组】合并果子

    题就自己找啦 各大OJ上应该都有 题目 题目描述 在一个果园里 多多已经将所有的果子打了下来 而且按果子的不同种类分成了不同的堆 多多决定把所有的果子合成一堆 每一次合并 多多可以把两堆果子合并到一起 消耗的体力等于两堆果子的重量之和 可以
  • 算法基础--递归与回溯、递推、迭代关系

    递归的优缺点 优点 代码更简洁清晰 可读性更好 实际上递归的代码更清晰 但是从学习的角度要理解递归真正发生的什么 是如何调用的 调用层次和路线 调用堆栈中保存了什么 可能是不容易 但是不可否认递归的代码更简洁 缺点 由于递归需要系统堆栈 所
  • 编程珠玑第三章习题5——英语中的连字符问题

    编程珠玑第三章习题5 英语中的连字符问题 问题 本问题将处理一小部分用连字符连接的英语单词方面的问题 下面的规则列表描述了一些以字母c结尾的单词的有效连字符连接 et ic al is tic s tic p tic lyt ic ot i
  • 程序员面试题精选100题(44)-数值的整数次方

    程序员面试题精选100题 44 数值的整数次方 题目 实现函数double Power double base int exponent 求base的exponent次方 不需要考虑溢出 方法一 由于输入的exponent是个int型的数值
  • 编写一个"banner"函数,该函数的输入为大写字母

    编写一个 banner 函数 该函数的输入为大写字母 题目 编写一个 banner 函数 该函数的输入为大写字母 输出为一个字符数组 该数组以图像化的方式表示该字母 编程 珠玑 上提到当要 输入 的 数据 很多 且没有 规律 时 可以 考虑
  • 迁移学习概述

    1 迁移学习的背景 在有监督的机器学习和尤其是深度学习的场景应用中 需要大量的标注数据 标注数据是一项枯燥无味且花费巨大的任务 关键是现实场景中 往往无法标注足够的数据 而且模型的训练是极其耗时的 因此迁移学习营运而生 传统机器学习 主要指
  • strassen矩阵乘法

    Strassen矩阵乘法简要解析 Strassen矩阵乘法具体描述如下 两个n n 阶的矩阵A与B的乘积是另一个n n 阶矩阵C C可表示为假如每一个C i j 都用此公式计算 则计算C所需要的操作次数为n3 m n2 n 1 a 其中m表
  • 程序员面试题精选100题(40)-扑克牌的顺子

    程序员面试题精选100题 40 扑克牌的顺子 题目 从扑克牌中随机抽5张牌 判断是不是一个顺子 即这5张牌是不是连续的 2 10为数字本身 A为1 J为11 Q为12 K为13 而大小王可以看成任意数字 分析 这题目很有意思 是一个典型的寓
  • 蓝桥题解(不定期更新)

    597 跑步锻炼 import math if name main moth 0 31 28 31 30 31 30 31 31 30 31 30 31 day 6 ans 0 for year in range 2000 2021 if
  • 机器学习(machine learning)之AdaBoost算法

    转载自 http blog csdn net haidao2009 article details 7514787 菜鸟最近开始学习machine learning 发现adaboost 挺有趣 就把自己的一些思考写下来 主要参考了http
  • 程序员面试题精选100题(48)-二叉树两结点的最低共同父结点

    程序员面试题精选100题 48 二叉树两结点的最低共同父结点 题目 二叉树的结点定义如下 struct TreeNode int m nvalue TreeNode m pLeft TreeNode m pRight 输入二叉树中的两个结点
  • Java算法基础:使用递归算法实现,平方相加1^2 + 2^2 + 3^2 +...+ n^2。

    package algorithm recursion public class RecursionTest public static void main String args int m 5 int sumOfSquares sumO
  • 贪心算法——排队打水问题

    6 3 排队打水问题 有n个人排队到r个水龙头去打水 他们装满水桶的时间为t1 t2 tn为正整数且个不相等 应如何安排他们打水顺序才能使他们花费的时间最少 算法分析 时间总和 等待时间 装水时间 采用贪心思想 先sort 默认将装水时间从
  • 快速排序之“采取“尾递归”和“三数取中”技术的快速排序”

    快速排序之 采取 尾递归 和 三数取中 技术的快速排序 下面针对快速排序进行一些优化 QUICKSORT算法包含两个对其自身的递归调用 即调用PARTITION后 左边的子数组和右边的子数组分别被递归排序 QUICKSORT中的第二次递归调
  • CRC-模2除法

    在循环冗余校验码 CRC 的计算中有应用到模2除法 模2除法的特点就是 每一位除的结果不影响其它位 即不向上一位借位 模2除法原则 1 被除数的首位为1 商为1 2 被除数的首位为0 商为0 3 模2除法等同于按位异或 要保证每次除完首位都

随机推荐

  • 怎么修改游戏内存服务器,修改游戏服务器内存

    修改游戏服务器内存 内容精选 换一换 当您成功创建私有镜像后 镜像的状态为 正常 您可以使用该镜像创建服务器实例或云硬盘 也可以将镜像共享给其他帐号 或者复制镜像到其他区域 私有镜像的生命周期如图1所示 通过华为云创建的ECS服务器默认使用
  • mysql客户端小海豚_MySQL基础

    1 数据库概述 1 1数据的存储方式 第一种存储方式是创建对象 实际上new出来的对象不就是用来存数据的嘛 创建对象就是在堆内存中为对象请求了一个空间 相当于是将对象存入堆内存 第二种方式存文件中 这个在IO流部分我们就是这么处理的 但是缺
  • Python批量改文件名

    对以下路径中的文件名批量修改 文章目录 一 读取指定路径中的文件名 二 正则表达式提取需要保留的部分 1 介绍re库 2 re库中函数的用法 1 re findall 最常用 2 re sub pattern repl string cou
  • 数仓知识点

    传统数仓知识 1 数据仓库分层 ODS 数据准备层 该区为数据仓的准备区 直接输入源数据 如业务库 埋点日志和消息队列等 DWD 数据细节层 该层为业务层和数据层的隔离层 保持和ODS层相同的颗粒度 该层还进行了数据清洗和规范化操作 例如去
  • 阿里巴巴笔试-2020.7.27-第二题 藏宝架

    题目 有个藏宝架有n层 每层的宝物数量不一 每个宝物都有其价值 现在要求拿出m个宝物 并且需要遵守规则 每次只能拿选定层的两端的宝物 要拿出的m个宝物的总价值是各种方案里最大的 输入 第一行是 n 和 m 后面每一行是各层的数据 n m 下
  • WebSocket 基于JAVA Spring boot Spring Colud 的使用

    先上代码再看调试结果 package com qiang user util import com alibaba fastjson JSONObject import org springframework stereotype Comp
  • 软考网络工程师-最新最全小白攻略

    一 前言 最近Beau 博主本人 也是考取了2023年上半年的软考网络工程师 这里也准备给小白们做一些避坑流程 这里附上通过图 二 考前准备 1 报考条件 无 无年龄 资质 学历限制 无需通过软考初级才能报考 是中国守法公民即可报名 2 考
  • webpack 保存文件后自动打包_自动打包插件webpack-dev-server的安装、配置及使用

    1 介绍 webpack dev server插件可以实现Webpack的自动打包编译 这样 就不需要每次修改完代码都重新手动输入webpack打包了 2 安装 在项目的根路径下输入 cnpm i webpack dev server D
  • Python----模块(Module)和包(Package)

    Python 包 包 定义 为了组织好模块 会将多个模块分为包 Python 处理包也是相当方便的 简单来说 包就是文件夹 但该文件夹下必须存在 init py 文件 常见的包结构如下 最简单的情况下 只需要一个空的 init py 文件即
  • uniapp中使用网页录音并上传声音文件(发语音)——js-audio-recorder的使用【伸手党福利】

    uniapp中上传音频只能在app或小程序当中实现 如何使用网页完成语音的录制和上传则成为了困扰前端童鞋的重点 本文着重解决 js audio recorder报 error 浏览器不支持getUserMedia 的问题 js audio
  • qt使用socket连续发图片,服务端使用qt或者python接受图片

    首先客户端是用qt 不能用python这种 首先在pro里面 QT network 然后引入头文件 include
  • 2023最新AI创作商用ChatGPT源码分享+支持AI绘画

    一 SparkAI智能创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统 本期针对源码系统整体测试下来非常完美 可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统 那么如何搭建部
  • Vue 中使用 Upload 组件上传 Excel

    vue 中使用 Element 的 upload 组件上传 Excel 大致可以分两种情况 使用 action 上传到服务器 使用 axios 上传到服务器 注意 上传文件可能由于前后端格式不统一导致上传失败 application x w
  • openTLD算法在opencv3的PatchGenerator

    由于opencv3的各种版本相对于opencv2的版本已经改变了很多内容 openTLD跟踪算法所依赖的一些函数在opencv3中已经消失了 为此需要对openTLD进行适当修改才能使之在opencv3的各种版本中运行 加入如下文件 并在对
  • C++一本通基础算法:深度优先搜索(DFS)

    算法概述 搜索 类似于枚举 从头结点开始 搜索发现有一些路可以走 先选择一条 走到一个结点处 重复上述过程 一直走到不能走为止 然后返回上一个结点 选择第二条路 一直检索知道将头结点所有的路走完 算法图像 如图所示 从1开始 发现可以到达2
  • 06:TIM定时器功能------编码器接口功能

    目录 1 简历 2 正交编码器 3 编码器接口基本结构 4 编码器的工作模式 5 极性反转 A 编码器接口测速 1 连接图 2 函数介绍 3 步骤 4 代码 B 编码器接口计次 1 连接图 2 代码 1 简历 Encoder Interfa
  • 2020-12-03 PMP 群内练习题 - 光环

    1 完工估算 EAC 是下面哪一项的定期评估 A 已完成工作的成本 B 已完成工作的价值 C 预测项目完成时的总成本 D 预测完成项目还将需要的成本 如果挣值 EV 350 实际成本 AC 400 计划值 PV 300 那么成本偏差 CV
  • 怎么更改wifi频段_【wifi信号频率】wifi频率怎么设置 wifi2.4g和5g哪个更好

    wifi频率怎么设置 1 打开浏览器 输入192 168 1 1 进入路由设置界面 2 单击左侧的设置向导 然后单击下一步 3 一般情况 选择让路由器自动选择上网方式 4 输入你从运营商那里获得上网账号 密码 5 弹出无线频段选择界面 可按
  • __declspec用法详解

    dllimport dllexport 格式 declspec dllimport declarator declspec dllexport declarator 分别用来从dll导入函数 数据 或对象以及从dll中导出函数 数据 或对象
  • 中文分词之HMM模型详解

    关于HMM模型的介绍 网上的资料已经烂大街 但是大部分都是在背书背公式 本文在此针对HMM模型在中文分词中的应用 讲讲实现原理 尽可能的撇开公式 撇开推导 结合实际开源代码作为例子 争取做到雅俗共赏 童叟无欺 没有公式 就没有伤害 模型介绍