HanLP — HMM隐马尔可夫模型 -- 维特比(Viterbi)算法 --示例代码 - Java

简介: HanLP — HMM隐马尔可夫模型 -- 维特比(Viterbi)算法 --示例代码 - Java

Viterbi 维特比算法解决的是篱笆型的图的最短路径问题,图的节点按列组织,每列的节点数量可以不一样,每一列的节点只能和相邻列的节点相连,不能跨列相连,节点之间有着不同的距离,距离的值就不在

题目背景

从前有个村儿,村里的人的身体情况只有两种可能:健康、发烧

假设这个村儿的人没有体温计或者百度这种神奇东西,他唯一判断他身体情况的途径就是到村头我的偶像金正月的小诊所询问。月儿通过询问村民的感觉,判断她的病情,再假设村民只会回答正常、头晕或冷

有一天村里奥巴驴就去月儿那去询问了。

  • 第一天她告诉月儿她感觉正常。
  • 第二天她告诉月儿感觉有点冷。
  • 第三天她告诉月儿感觉有点头晕。
    那么问题来了,月儿如何根据阿驴的描述的情况,推断出这三天中阿驴的一个身体状态呢?

已知情况

省略训练过程,可参考 HanLP — HMM隐马尔可夫模型 -- 训练--归一化,计算概率

隐含的身体状态 = {健康,发烧}

可观察的感觉状态 = {正常,冷,头晕}

月儿预判的阿驴身体状态的概率分布(初始概率矩阵) = {健康:0.6,发烧:0.4}

月儿认为的阿驴身体健康状态的转换概率分布(转移概率矩阵) =

{
健康->健康: 0.7 ,
健康->发烧: 0.3 ,
发烧->健康:0.4 ,
发烧->发烧: 0.6
}

月儿认为的在相应健康状况条件下,阿驴的感觉的概率分布(发射概率矩阵) =

{
健康,正常:0.5 ,冷 :0.4 ,头晕: 0.1 ;
发烧,正常:0.1 ,冷 :0.3 ,头晕: 0.6
}

由上面我们可以发现,HMM的三要素都齐备了,下面就是解决问题了。

阿驴连续三天的身体感觉依次是: 正常、冷、头晕 。

过程:

第一天的时候,对每一个状态(健康或者发烧),分别求出第一天身体感觉正常的概率:P(第一天健康) = P(正常|健康)P(健康|初始情况) = 0.5 * 0.6 = 0.3 P(第一天发烧) = P(正常|发烧)P(发烧|初始情况) = 0.1 * 0.4 = 0.04

第二天的时候,对每个状态,分别求在第一天状态为健康或者发烧情况下观察到冷的最大概率。在维特比算法中,我们先要求得路径的单个路径的最大概率,然后再乘上观测概率。P(第二天健康) = max{0.30.7, 0.040.4}0.4=0.30.70.4=0.084 此时我们需要记录概率最大的路径的前一个状态,即0.084路径的前一个状态,我们在小本本上记下,第一天健康。 P(第二天发烧)=max{0.30.3, 0.040.6}0.3=0.027, 同样的在0.027这个路径上,第一天也是健康的。

第三天的时候,跟第二天一样。P(第三天健康)=max{0.0840.7, 0.0270.4}0.1=0.00588,在这条路径上,第二天是健康的。P(第三天发烧)=max{0.0840.3, 0.0270.6}0.6=0.01512,在这条路径上,第二天是健康的。

最后一天的状态概率分布即为最优路径的概率,即P(最优)=0.01512,这样我们可以得到最优路径的终点,是发烧

由最优路径开始回溯。请看我们的小本本,在求得第三天发烧概率的时候,我们的小本本上面写的是第二天健康,好了,第二天就应该是健康的状态,然后在第二天健康的情况下,我们记录的第一天是健康的。这样,我们的状态序列逆推出来了。即为:健康,健康,发烧

简略的画个图吧:

这儿的箭头指向就是一个回溯查询小本本的过程,我们在编写算法的时候,其实也得注意,每一个概率最大的单条路径上都要把前一个状态记录下来。

代码

Viterbi

package com.vipsoft.viterbi;
/**
 * 维特比算法
 * @author hankcs
 */
public class Viterbi
{
    /**
     * 求解HMM模型
     * @param obs 观测序列
     * @param states 隐状态
     * @param start_p 初始概率(隐状态)
     * @param trans_p 转移概率(隐状态)
     * @param emit_p 发射概率 (隐状态表现为显状态的概率)
     * @return 最可能的序列
     */
    public static int[] compute(int[] obs, int[] states, double[] start_p, double[][] trans_p, double[][] emit_p)
    {
        double[][] V = new double[obs.length][states.length];
        int[][] path = new int[states.length][obs.length];
        for (int y : states)
        {
            V[0][y] = start_p[y] * emit_p[y][obs[0]];
            path[y][0] = y;
        }
        for (int t = 1; t < obs.length; ++t)
        {
            int[][] newpath = new int[states.length][obs.length];
            for (int y : states)
            {
                double prob = -1;
                int state;
                for (int y0 : states)
                {
                    double nprob = V[t - 1][y0] * trans_p[y0][y] * emit_p[y][obs[t]];
                    if (nprob > prob)
                    {
                        prob = nprob;
                        state = y0;
                        // 记录最大概率
                        V[t][y] = prob;
                        // 记录路径
                        System.arraycopy(path[state], 0, newpath[y], 0, t);
                        newpath[y][t] = y;
                    }
                }
            }
            path = newpath;
        }
        double prob = -1;
        int state = 0;
        for (int y : states)
        {
            if (V[obs.length - 1][y] > prob)
            {
                prob = V[obs.length - 1][y];
                state = y;
            }
        }
        return path[state];
    }
}

DoctorExample

package com.vipsoft.viterbi;
import static com.vipsoft.viterbi.DoctorExample.Feel.cold;
import static com.vipsoft.viterbi.DoctorExample.Feel.dizzy;
import static com.vipsoft.viterbi.DoctorExample.Feel.normal;
import static com.vipsoft.viterbi.DoctorExample.Status.Fever;
import static com.vipsoft.viterbi.DoctorExample.Status.Healthy;
public class DoctorExample
{
    enum Status
    {
        /**
         * 健康
         */
        Healthy,
        /**
         * 发热
         */
        Fever,
    }
    enum Feel
    {
        /**
         *  正常
         */
        normal,
        /**
         * 冷
         */
        cold,
        /**
         * 头晕
         */
        dizzy,
    }
    static int[] states = new int[]{Healthy.ordinal(), Fever.ordinal()};
    /**
     * 初始概率矩阵
     * { 健康:0.6 , 发烧: 0.4 }
     */
    static double[] start_probability = new double[]{0.6, 0.4};
    /**
     * 转移概率矩阵
     * {
     *  健康->健康:0.7 ,
     *  健康->发烧:0.3 ,
     *  发烧->健康:0.4 ,
     *  发烧->发烧:0.6
     * }
     */
    static double[][] transititon_probability = new double[][]{
            {0.7, 0.3},
            {0.4, 0.6},
    };
    /**
     * 发射概率矩阵
     * {
     *   健康,正常:0.5 ,冷 :0.4 ,头晕: 0.1 ;
     *   发烧,正常:0.1 ,冷 :0.3 ,头晕: 0.6
     * }
     */
    static double[][] emission_probability = new double[][]{
            {0.5, 0.4, 0.1},
            {0.1, 0.3, 0.6},
    };
    public static void main(String[] args)
    {
        // 连续三天的身体感觉依次是: 正常、冷、头晕,推算出这三天的身体状态
        int[] observations = new int[]{normal.ordinal(), cold.ordinal(), dizzy.ordinal()};
        int[] result = Viterbi.compute(observations, states, start_probability, transititon_probability, emission_probability);
        for (int r : result)
        {
            System.out.print(Status.values()[r] + " ");
        }
        System.out.println();
    }
}


目录
相关文章
|
1月前
|
存储 算法 安全
探究‘公司禁用 U 盘’背后的哈希表算法与 Java 实现
在数字化办公时代,信息安全至关重要。许多公司采取“禁用U盘”策略,利用哈希表算法高效管理外接设备的接入权限。哈希表通过哈希函数将设备标识映射到数组索引,快速判断U盘是否授权。例如,公司预先将允许的U盘标识存入哈希表,新设备接入时迅速验证,未授权则禁止传输并报警。这有效防止恶意软件和数据泄露,保障企业信息安全。 代码示例展示了如何用Java实现简单的哈希表,模拟公司U盘管控场景。哈希表不仅用于设备管理,还在文件索引、用户权限等多方面助力信息安全防线的构建,为企业数字化进程保驾护航。
|
4天前
|
机器学习/深度学习 算法
扩散模型=进化算法!生物学大佬用数学揭示本质
在机器学习与生物学交叉领域,Tufts和Harvard大学研究人员揭示了扩散模型与进化算法的深刻联系。研究表明,扩散模型本质上是一种进化算法,通过逐步去噪生成数据点,类似于进化中的变异和选择机制。这一发现不仅在理论上具有重要意义,还提出了扩散进化方法,能够高效识别多解、处理高维复杂参数空间,并显著减少计算步骤,为图像生成、视频合成及神经网络优化等应用带来广泛潜力。论文地址:https://arxiv.org/pdf/2410.02543。
31 21
|
10天前
|
人工智能 算法 搜索推荐
单纯接入第三方模型就无需算法备案了么?
随着人工智能的发展,企业接入第三方模型提升业务能力的现象日益普遍,但算法备案问题引发诸多讨论。根据相关法规,无论使用自研或第三方模型,只要涉及向中国境内公众提供算法推荐服务,企业均需履行备案义务。这不仅因为服务性质未变,风险依然存在,也符合监管要求。备案内容涵盖模型基本信息、算法优化目标等,且需动态管理。未备案可能面临法律和运营风险。建议企业提前规划、合规管理和积极沟通,确保合法合规运营。
|
1月前
|
机器学习/深度学习 人工智能 算法
机器学习算法的优化与改进:提升模型性能的策略与方法
机器学习算法的优化与改进:提升模型性能的策略与方法
266 13
机器学习算法的优化与改进:提升模型性能的策略与方法
|
2月前
|
算法
基于模糊PI控制算法的龙格库塔CSTR模型控制系统simulink建模与仿真
本项目基于MATLAB2022a,采用模糊PI控制算法结合龙格-库塔方法,对CSTR模型进行Simulink建模与仿真。通过模糊控制处理误差及变化率,实现精确控制。核心在于将模糊逻辑与经典数值方法融合,提升系统性能。
|
2月前
|
存储 算法
基于HMM隐马尔可夫模型的金融数据预测算法matlab仿真
本项目基于HMM模型实现金融数据预测,包括模型训练与预测两部分。在MATLAB2022A上运行,通过计算状态转移和观测概率预测未来值,并绘制了预测值、真实值及预测误差的对比图。HMM模型适用于金融市场的时间序列分析,能够有效捕捉隐藏状态及其转换规律,为金融预测提供有力工具。
|
3月前
|
机器学习/深度学习 人工智能 算法
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
手写数字识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Flask框架,开发网页端操作平台,实现用户上传一张图片识别其名称。
179 0
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
|
3月前
|
机器学习/深度学习 人工智能 算法
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
蔬菜识别系统,本系统使用Python作为主要编程语言,通过收集了8种常见的蔬菜图像数据集('土豆', '大白菜', '大葱', '莲藕', '菠菜', '西红柿', '韭菜', '黄瓜'),然后基于TensorFlow搭建卷积神经网络算法模型,通过多轮迭代训练最后得到一个识别精度较高的模型文件。在使用Django开发web网页端操作界面,实现用户上传一张蔬菜图片识别其名称。
171 0
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
|
3月前
|
机器学习/深度学习 人工智能 算法
青否数字人声音克隆算法升级,16个超真实直播声音模型免费送!
青否数字人的声音克隆算法全面升级,能够完美克隆真人的音调、语速、情感和呼吸。提供16种超真实的直播声音模型,支持3大AI直播类型和6大核心AIGC技术,60秒快速开播,助力商家轻松赚钱。AI讲品、互动和售卖功能强大,支持多平台直播,确保每场直播话术不重复,智能互动和真实感十足。新手小白也能轻松上手,有效规避违规风险。
|
2天前
|
算法 数据安全/隐私保护 计算机视觉
基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证
本项目展示了256×256图像通过双线性插值放大至512×512的效果,无水印展示。使用Matlab 2022a和Vivado 2019.2开发,提供完整代码及详细中文注释、操作视频。核心程序实现图像缩放,并在Matlab中验证效果。双线性插值算法通过FPGA高效实现图像缩放,确保质量。

热门文章

最新文章