算法简介:
Viterbi 算法又叫维特比算法,其是HMM模型中在给出观测序列O,以及状态转移举证M 和 状态-观测矩阵Q之后,求解能够最佳解释序列O的状态序列S的一种动态规划算法。
具体如下图所示:
其中:
- 标记为O的 [0|1] 序列是观测序列,
- 标记为S的序列中横向的箭头即状态在根据转移矩阵M进行转移,
- 其中S序列与O序列之间向下的箭头表示根据状态生成观测的概率,也即Q矩阵。
假设我们已知观测序列O,即图片Bottom位置的0、1序列,则图片Top位置的用红框框起来的0、1序列就是对应的S序列,即我们所说的在给定矩阵M和Q之后,能最佳地解释O的序列。
算法实现:
算法的实现也很简单,主要包含两步:
1. 从前到后遍历O序列,根据观测生成的方式,计算每个观测的最大生成概率。
在计算过程中,当前观测只依赖于上一个观测的结果,即马氏性。
同时记录当前状态最有可能的上一个状态。
2. 从后向前回溯,得到序列S
根据1中在每个观测点记录的上一个最佳状态进行回溯。
具体实现如下:
/* ======================================================== * Copyright (C) 2014 All rights reserved. * * filename : viterbi.c * author : *** * date : 2014-12-19 * info : * ======================================================== */ #include <math.h> #include <string.h> #include "viterbi.h" /* **************************************** * @param l : input 0|1 vector * @param o : output[smoothed] 0|1 vector * @param n : vector length * @param r_t : rate for stat <--> stat * @param r_o : rate for stat <--> observ * ****************************************/ int viterbi(int * l, int * o, int n, int r_t, int r_o){ // check params if (!l || !o || r_t < 1 || r_o < 1){ // return fail return -1; } int i = 0; double r_t_ = log(r_t); double r_o_ = log(r_o); double ls0 = 0.0, ls1 = 0.0, cs0 = 0.0, cs1 = 0.0; double ps0 = 0.0, ps1 = 0.0; // prev_state for trace back int (*pre_s)[2] = (int(*)[2])malloc(sizeof(int[2]) * n); // init pre_state and output vector memset(pre_s, 0, sizeof(int[2]) * n); memset(o, 0, sizeof(int) * n); // init the begin stat distribute ls0 = (l[0] == 0 ? r_o_ : 0.0); ls1 = (l[0] == 1 ? r_o_ : 0.0); // pass through to calculate max prob for (i = 1; i < n ; i++){ ps0 = ls0 + r_t_ + (l[i] == 0 ? r_o_ : 0.0); ps1 = ls1 + (l[i] == 0 ? r_o_ : 0.0); if (ps0 >= ps1){ cs0 = ps0; pre_s[i][0] = 0; } else{ cs0 = ps1; pre_s[i][0] = 1; } ps0 = ls0 + (l[i] == 1 ? r_o_ : 0.0); ps1 = ls1 + r_t_ + (l[i] == 1 ? r_o_ : 0.0); if (ps0 >= ps1){ cs1 = ps0; pre_s[i][1] = 0; } else{ cs1 = ps1; pre_s[i][1] = 1; } ls0 = cs0; ls1 = cs1; } // end state with the max prob o[n - 1] = cs0 >= cs1 ? 0 : 1; // trace back for prev state for (i = n - 2; i >= 0; i--){ o[i] = pre_s[i + 1][o[i + 1]]; } // free the trace pre states free(pre_s); pre_s = NULL; // return success return 0; }
测试代码如下:
int main(){ int a[15] = {0,0,1,0,0,1,1,1,0,1,1,0,0,0,0}; int b[15] = {0,0,1,0,0,1,1,1,0,1,1,0,0,0,0}; int c = viterbi(a,b,15,10,5); int i = 0; printf("before smooth:\n"); for (; i < 15; i++){ printf("%d ", a[i]); } printf("\n"); printf("after smooth:\n"); for (i = 0; i < 15; i++){ printf("%d ", b[i]); } printf("\n"); }
结果如下:
before smooth: 0 0 1 0 0 1 1 1 0 1 1 0 0 0 0 after smooth: 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0
结果中显示原始序列中第3位置的“1”被平滑成“0”,原始序列中第9位置的“0”被平滑成"1"。
该算法在处理时序异常检测结果中的异常序列十分有用。
可以将平滑后的连续“1” 看成一个异常事件,而不用针对每个检测到的异常点单独处理。
相关推荐
嵌入式系统开发_STM32微控制器_ESP8266WiFi模块_心率传感器_加速度计_OLED显示屏_蓝牙40_低功耗设计_实时操作系统_智能手表_多功能健康监测_运动数据记录_
驾校自动化_网页自动化爬虫技术_Python27多线程HTTP请求模拟_龙泉驾校2014版约车系统自动预约助手_通过模拟登录和循环请求实现自动约车功能_支持失败自动递增车号重试_
Linux系统编程_操作系统内核_系统调用_进程线程_信号处理_文件IO_进程间通信_多线程同步_网络编程_UNIX环境编程_中文翻译勘误_错误修正_代码示例优化_技术文档校对_开
wanjunshe_Python-Tensorflow_12888_1745868924470
scratch少儿编程逻辑思维游戏源码-铅笔画.zip
即时通讯应用开发_基于LeanCloud云服务与Android原生开发_集成QQ第三方登录与即时聊天功能的社交应用_实现用户注册登录创建聊天室发送文字消息展示用户信息头像昵称并提供
scratch少儿编程逻辑思维游戏源码-伞兵大乱斗(云变量).zip
scratch少儿编程逻辑思维游戏源码-楼层酷跑.zip
scratch少儿编程逻辑思维游戏源码-零下之寒颤.zip
scratch少儿编程逻辑思维游戏源码-密室逃生.zip
少儿编程scratch项目源代码文件案例素材-爪猫足球.zip
命令行完成git本地仓库创建、将代码提交到暂存区、查看暂存区信息、将代码提交到本地仓库、将本地仓库关联到远程仓库、推送到远程仓库全过程的截图
少儿编程scratch项目源代码文件案例素材-纸.zip
scratch少儿编程逻辑思维游戏源码-日本冒险.zip
scratch少儿编程逻辑思维游戏源码-狼人杀跑酷.zip
scratch少儿编程逻辑思维游戏源码-史莱姆杀手.zip
少儿编程scratch项目源代码文件案例素材-粘粘世界.zip
scratch少儿编程逻辑思维游戏源码-米克 demo.zip
网络游戏开发_Netty4X框架_多协议支持_高性能分布式游戏服务器_基于TCP_UDP_HTTP_WebSocket协议栈_Protobuf自定义通信协议_RPC远程调用_My
前端开发_jQueryZepto_ES6_移动端_字母索引列表_联系人列表_轻量级插件_响应式设计_手机端优化_触摸交互_高性能滚动_右侧导航_字母提示_数据分组_列表渲染_动态