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();
    }
}


目录
相关文章
|
8天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
34 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
3天前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
14 3
|
12天前
|
机器学习/深度学习 数据采集 算法
如何在一夜之间成为模型微调大师?——从零开始的深度学习修炼之旅,让你的算法功力飙升!
【10月更文挑战第5天】在机器学习领域,预训练模型具有强大的泛化能力,但直接使用可能效果不佳,尤其在特定任务上。此时,模型微调显得尤为重要。本文通过图像分类任务,详细介绍如何利用PyTorch对ResNet-50模型进行微调,包括环境搭建、数据预处理、模型加载与训练等步骤,并提供完整Python代码。通过调整超参数和采用早停策略等技巧,可进一步优化模型性能。适合初学者快速上手模型微调。
58 8
|
1月前
|
机器学习/深度学习 人工智能 算法
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
鸟类识别系统。本系统采用Python作为主要开发语言,通过使用加利福利亚大学开源的200种鸟类图像作为数据集。使用TensorFlow搭建ResNet50卷积神经网络算法模型,然后进行模型的迭代训练,得到一个识别精度较高的模型,然后在保存为本地的H5格式文件。在使用Django开发Web网页端操作界面,实现用户上传一张鸟类图像,识别其名称。
86 12
鸟类识别系统Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+ResNet50算法模型+图像识别
|
10天前
|
机器学习/深度学习 算法 搜索推荐
django调用矩阵分解推荐算法模型做推荐系统
django调用矩阵分解推荐算法模型做推荐系统
15 4
|
13天前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
33 2
|
1月前
|
存储 自然语言处理 算法
【算法精讲系列】MGTE系列模型,RAG实施中的重要模型
检索增强生成(RAG)结合检索与生成技术,利用外部知识库提升大模型的回答准确性与丰富性。RAG的关键组件包括文本表示模型和排序模型,前者计算文本向量表示,后者进行精细排序。阿里巴巴通义实验室推出的GTE-Multilingual系列模型,具备高性能、长文档支持、多语言处理及弹性向量表示等特性,显著提升了RAG系统的检索与排序效果。该系列模型已在多个数据集上展示出优越性能,并支持多语言和长文本处理,适用于各种复杂应用场景。
|
1月前
|
自然语言处理 监控 算法
【算法精讲系列】通义模型Prompt调优的实用技巧与经验分享
本文详细阐述了Prompt的设计要素,包括引导语、上下文信息等,还介绍了多种Prompt编写策略,如复杂规则拆分、关键信息冗余、使用分隔符等,旨在提高模型输出的质量和准确性。通过不断尝试、调整和优化,可逐步实现更优的Prompt设计。
|
1月前
|
算法
基于SIR模型的疫情发展趋势预测算法matlab仿真
该程序基于SIR模型预测疫情发展趋势,通过MATLAB 2022a版实现病例增长拟合分析,比较疫情防控力度。使用SIR微分方程模型拟合疫情发展过程,优化参数并求解微分方程组以预测易感者(S)、感染者(I)和移除者(R)的数量变化。![]该模型将总人群分为S、I、R三部分,通过解析或数值求解微分方程组预测疫情趋势。
|
1月前
|
机器学习/深度学习 数据采集 存储
一文读懂蒙特卡洛算法:从概率模拟到机器学习模型优化的全方位解析
蒙特卡洛方法起源于1945年科学家斯坦尼斯劳·乌拉姆对纸牌游戏中概率问题的思考,与约翰·冯·诺依曼共同奠定了该方法的理论基础。该方法通过模拟大量随机场景来近似复杂问题的解,因命名灵感源自蒙特卡洛赌场。如今,蒙特卡洛方法广泛应用于机器学习领域,尤其在超参数调优、贝叶斯滤波等方面表现出色。通过随机采样超参数空间,蒙特卡洛方法能够高效地找到优质组合,适用于处理高维度、非线性问题。本文通过实例展示了蒙特卡洛方法在估算圆周率π和优化机器学习模型中的应用,并对比了其与网格搜索方法的性能。
233 1