viterbi算法linux下C++实现

来源:互联网 发布:铁塔倾斜度的算法 编辑:程序博客网 时间:2024/06/11 02:56

算法介绍见博客:http://www.cnblogs.com/tornadomeet/archive/2012/03/24/2415889.html,实现结果与他一样,下面是我的实现细节:

一 程序整体布局

分为loadmodel、viterbi搜索、print输出结果三个部分。主体程序如下:

#include "Viterbi.h"#include <fstream>#include <string>int main(){    Viterbi viterbi_1;    viterbi_1.loadModel();    viterbi_1.viterbi();    viterbi_1.print();    return 0;}

二 头文件的定义

头文件中主要用来确定需要用那些函数和数据,这里面使用什么样的数据结构尤其需要考虑,我的实现中

二维数组: vector < vector<type> > , 第一维表示帧等,第二维表示状态数;

观测序列与发射矩阵中第二维的对应: 用map容器;

最后的结果反向遍历得到,然后又正向输出结果: 用stack容器适配器;

具体代码如下:

/*  * File:   Viterbi.h * Author: junan * * Created on December 3, 2014, 4:16 PM */#include <iostream>#include <vector>#include <fstream>#include <map>#include <stack>#define DBL_MIN 2.2250738585072014e-308#ifndef VITERBI_H#defineVITERBI_H#endif/* VITERBI_H */using namespace std;class Viterbi{public:    // default construct function    Viterbi(): max_pro(0), forward_path(0){        initProbF = "/home/junan/Documents/C++/viterbi/ArrayIniprob_3.txt";        transF = "/home/junan/Documents/C++/viterbi/ArrayTranmat_3.txt";        emitF = "/home/junan/Documents/C++/viterbi/ArrayRadmat_3.txt";        observF = "/home/junan/Documents/C++/viterbi/ArrayObseque.txt";        char2orderF = "/home/junan/Documents/C++/viterbi/ArrayChar2order_3.txt";    };        fstream& open_file(fstream&, string&);    // loadModel: 将模型从文件读入缓存    void loadModel();    // state2observ: 求状态观测序列    void state2observ();    // 开始viterbi搜索    void viterbi();    // 输出结果    void print();        typedef vector< vector<double> >::iterator iter_mat;    private:    int N, len, forward_path; // N:存储状态数, len: 存储帧数, forward_path: 前向路径    int ord, fra; // ord: 字符对应的序列,f: 第f帧    double tmp, max_pro;    vector<double> tmp_vec;        // 下面的string用来存储文件的绝对路径    string initProbF, transF, emitF, observF, char2orderF;        /*     * initProb_vec[i]: 存储状态先验概率, i表示第i个状态的先验概率     * trans_mat[i][j]: 从状态i转移到状态j的转移概率     * emit_mat[i][c]: 用状态i产生字符c的发射概率     * observ_vec[f]: 存储状态序列, f表示第f帧     * backPath[i][f]: 表示第f帧的第i个状态的前一个状态     */    vector<double> initProb_vec;    vector< vector<double> > trans_mat;    vector< vector<double> > emit_mat;    vector<char> obser_vec;    vector< vector<int> > backPath;        // ifstream: 定义文件流    fstream fin;        // 定义关联容器,使观测字符到序列对应    map<char, int> char2order_map;        // 定义每个字符在所有状态下产生概率的矩阵    vector< vector<double> > Prob_mat;          // 存储最佳状态序列    stack<int> state_stack;    };

三 函数的定义

避免代码的重复书写,比如定义了open_file函数,具体代码如下:

#include <fstream>#include <vector>#include <stack>#include <iostream>#include "Viterbi.h"using namespace std;//void Viterbi::loadModel(string initProF, string transF, string emitF){void Viterbi::loadModel(){    // 载入初始矩阵    open_file(fin, initProbF);    while (fin >> tmp)        initProb_vec.push_back(tmp);        N = initProb_vec.size(); // 状态数        // 载入状态转移矩阵    open_file(fin, transF);    trans_mat.resize(N);    for (int i=0; i<N; ++i)        for (int j=0; j<N; ++j){            if (fin >> tmp)                trans_mat[i].push_back(tmp);        }        // 载入发射矩阵    open_file(fin, emitF);    while (!fin.eof()) {         for (int i = 0; i<N; ++i) {            if (fin >> tmp)                tmp_vec.push_back(tmp);        }        emit_mat.push_back(tmp_vec);        tmp_vec.clear();    }    // 载入观测序列    open_file(fin, observF);    char ch;    while (fin >> ch)        obser_vec.push_back(ch);    len = obser_vec.size();        // 载入观测字符与序号之间的对应    open_file(fin, char2orderF);    int order;    while (fin >> ch) {        fin >> order;        char2order_map[ch] = order;    }        // 初始化概率矩阵    state2observ();} fstream& Viterbi::open_file(fstream& filein, string& filename) {    filein.close();    filein.clear();    filein.open(filename.c_str(), ios::in);    if (!filein)        cerr << "fail to open file!" << flush;    return filein;}/* 对状态矩阵进行初始化 * 1 由字符找到序号  * 2 根据序号返回字符在该状态下的发射概率 * 3 得到整个矩阵 */void Viterbi::state2observ(){    Prob_mat.resize(len);    vector<char>::const_iterator iter = obser_vec.begin();    for (; iter!=obser_vec.end(); ++iter) {        fra = iter - obser_vec.begin();        ord = char2order_map[*iter];        for (int i=0; i<N; ++i) // N是状态数            Prob_mat[fra].push_back(emit_mat[ord][i]);    }}void Viterbi::viterbi() {    backPath.resize(len);    iter_mat iter = Prob_mat.begin();    for (; iter!=Prob_mat.end(); ++iter) {        fra = iter - Prob_mat.begin();        // 计算第一帧        if (fra==0){            for (int j=0; j<N; ++j){                Prob_mat[fra][j] = Prob_mat[fra][j] * initProb_vec[j];                backPath[fra].push_back(0); // 第一帧前面没有状态了,先设为0            }            }        // 计算其他帧        else {            for (int j=0; j<N; ++j) {                max_pro = 0;                forward_path = 0; // 使用之前记得重置!                for (int i=0; i<N; ++i){                    tmp = Prob_mat[fra-1][i] * trans_mat[i][j];                    if (tmp>max_pro) {                        max_pro = tmp;                        forward_path = i;                    }                }                 backPath[fra].push_back(forward_path);                Prob_mat[fra][j] = max_pro * Prob_mat[fra][j];            }        }    }    // 所有帧计算完毕,找最大概率,然后找最优路径    max_pro = Prob_mat[fra][0];    int last_state(0);    for (int i=0; i<N; ++i) {        if (Prob_mat[fra][i]>max_pro) {            max_pro = Prob_mat[fra][i];            last_state = i;        }    }    state_stack.push(last_state);    len = obser_vec.size();    for (fra=len-1; fra>0; --fra) {        last_state = backPath[fra][last_state];        state_stack.push(last_state);       }   }void Viterbi::print() {    cout << "最佳状态序列是:" << endl;    while (!state_stack.empty()) {        cout << state_stack.top() << " " << nounitbuf; // 刷新所有输出        state_stack.pop();    }    cout << endl;    cout << "最佳状态序列对应的概率为: " << max_pro << endl;}

还有些改进的地方,比如将矩阵归一化等,需待后续完善。


0 0
原创粉丝点击