序列比对(十六)——Baum-Welch算法估算HMM参数

2023-10-14 07:50

本文主要是介绍序列比对(十六)——Baum-Welch算法估算HMM参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原创:hxj7

本文介绍了如何用Baum-Welch算法来估算HMM模型中的概率参数。

Baum-Welch算法应用于HMM的效果

前文《序列比对(15)EM算法以及Baum-Welch算法的推导》介绍了EM算法Baum-Welch算法的推导过程。Baum-Welch算法是EM算法的一个特例,用来估算HMM模型中的概率参数。其具体步骤如下:
在这里插入图片描述

图片引自《生物序列分析》

本文给出了Baum-Welch算法的C代码,还是以投骰子为例,估算出了转移概率以及发射概率。

具体效果如图:
(下面几张图中的 Real 表示真实的转移概率以及发射概率,而Baum-Welch表示用Baum-Welch算法估算的转移概率以及发射概率。)
首先是当若干条序列总长度300时:
在这里插入图片描述
在这里插入图片描述
然后是当若干条序列总长度30000时:
在这里插入图片描述
在这里插入图片描述
可以看出总长度为30000时已经很接近真实值了。但是,Baum-Welch算法的结果时一个局部最优值,很依赖初始值的设定。所以,当初始值不同时,也有可能会出现这种结果:
在这里插入图片描述
在这里插入图片描述
小结一下:

  • Baum-Welch算法通过多次迭代来估算HMM模型中的概率参数。
  • 本文代码设定了迭代的终止条件:当“归一化后的平均对数似然”的变化小于预先设定的阈值时或者迭代次数超出最大迭代次数时,迭代终止。
  • Baum-Welch算法的最终结果非常依赖初始值的设定。
  • 本文代码中的初始值是随机值。
  • 在计算期望次数时,使用了伪计数。

代码中所用公式及其推导

其中的 A k l A_{kl} Akl指的是 a k l a_{kl} akl在所有训练序列中出现的期望次数,而 E k ( b ) E_k(b) Ek(b)指的是 e k ( b ) e_k(b) ek(b)在所有训练序列中出现的期望次数。用符号表示就是(其中 x j x^j xj表示第j条符号序列):
(1.1) A k l = ∑ j ∑ π P ( π j ∣ x j , θ ) A k l ( π j ) = ∑ j ∑ i P ( π i j = k , π i + 1 j = l ∣ x j , θ ) \begin{aligned} \displaystyle A_{kl} & = \sum_{j} \sum_{\pi} P(\pi^j|x^j,\theta) A_{kl}(\pi^j) \\ & = \sum_{j} \sum_i P(\pi_i^j=k, \pi_{i+1}^j=l|x^j,\theta) \tag{1.1} \end{aligned} Akl=jπP(πjxj,θ)Akl(πj)=jiP(πij=k,πi+1j=lxj,θ)(1.1)
(1.2) E k ( b ) = ∑ j ∑ π P ( π j ∣ x j , θ ) E k ( b , π j ) = ∑ j ∑ i P ( π i j = k , x i j = b ∣ x j , θ ) = ∑ j ∑ { i ∣ x i j = b } P ( π i j = k ∣ x j , θ ) \begin{aligned} \displaystyle E_k(b) & = \sum_j \sum_\pi P(\pi^j|x^j, \theta) E_k(b, \pi^j) \\ & = \sum_{j} \sum_i P(\pi_i^j=k, x_i^j=b|x^j,\theta) \\ & = \sum_{j} \sum_{\{i|x_i^j=b\}} P(\pi_i^j=k|x^j,\theta) \tag{1.2} \end{aligned} Ek(b)=jπP(πjxj,θ)Ek(b,πj)=jiP(πij=k,xij=bxj,θ)=j{ixij=b}P(πij=kxj,θ)(1.2)

我们可以推导出,对某一条序列 x j x^j xj有如下结论:
(2.1) P ( π i = k , π i + 1 = l ∣ x , θ ) = f ~ k ( i ) a k l e l ( x i + 1 ) b ~ l ( i + 1 ) P(\pi_i=k, \pi_{i+1}=l|x,\theta) = \tilde{f}_k(i) a_{kl} e_l(x_{i+1}) \tilde{b}_l(i+1) \tag{2.1} P(πi=k,πi+1=lx,θ)=f~k(i)aklel(xi+1)b~l(i+1)(2.1)
(2.2) P ( π i = k ∣ x , θ ) = f ~ k ( i ) b ~ k ( i ) s i P(\pi_i=k|x,\theta) = \tilde{f}_k(i) \tilde{b}_k(i) s_i \tag{2.2} P(πi=kx,θ)=f~k(i)b~k(i)si(2.2)

其中 f ~ k ( i ) \tilde{f}_k(i) f~k(i) b ~ k ( i ) \tilde{b}_k(i) b~k(i) 以及 s i s_i si 的定义在前文《序列比对(12):计算后验概率》中已经给出(下文给出了计算公式)。

公式(2.1)的推导如下:
P ( π i = k , π i + 1 = l ∣ x , θ ) = P ( π i = k , π i + 1 = l , x ∣ θ ) P ( x ∣ θ ) = P ( π i = k , π i + 1 = l , x 1 , . . . , x i , x i + 1 , . . . , x L ∣ θ ) P ( x ∣ θ ) = P ( x 1 , . . . , x i , π i = k ∣ θ ) P ( x i + 1 , . . . , x L , π i + 1 = l ∣ x 1 , . . . , x i , π i = k , θ ) P ( x ∣ θ ) = f k ( i ) P ( x i + 1 , . . . , x L , π i + 1 = l ∣ x 1 , . . . , x i , π i = k , θ ) P ( x ∣ θ ) = f k ( i ) P ( x i + 1 , . . . , x L , π i + 1 = l ∣ π i = k , θ ) P ( x ∣ θ ) = f k ( i ) P ( x i + 2 , . . . , x L , π i + 1 = l ∣ x i + 1 , π i + 1 = l , π i = k , θ ) P ( x i + 1 , π i + 1 = l ∣ π i = k , θ ) P ( x ∣ θ ) = f k ( i ) P ( x i + 2 , . . . , x L , π i + 1 = l ∣ π i + 1 = l , θ ) P ( x i + 1 , π i + 1 = l ∣ π i = k , θ ) P ( x ∣ θ ) = f k ( i ) b l ( i + 1 ) P ( x i + 1 , π i + 1 = l ∣ π i = k , θ ) P ( x ∣ θ ) = f k ( i ) b l ( i + 1 ) P ( π i + 1 = l ∣ π i = k , θ ) P ( x i + 1 ∣ π i = k , π i + 1 = l , θ ) P ( x ∣ θ ) = f k ( i ) b l ( i + 1 ) a k l P ( x i + 1 ∣ π i + 1 = l , θ ) P ( x ∣ θ ) = f k ( i ) b l ( i + 1 ) a k l e l ( x i + 1 ) P ( x ∣ θ ) \begin{aligned} & P(\pi_i=k, \pi_{i+1}=l|x,\theta) \\ & = \frac {P(\pi_i=k, \pi_{i+1}=l,x|\theta)} {P(x|\theta)} \\ & = \frac {P(\pi_i=k, \pi_{i+1}=l, x_1, ..., x_i, x_{i+1},...,x_L|\theta)} {P(x|\theta)} \\ & = \frac {P(x_1,...,x_i,\pi_i=k|\theta) P(x_{i+1},...,x_L,\pi_{i+1}=l|x_1,...,x_i,\pi_i=k,\theta)} {P(x|\theta)} \\ & = \frac {f_k(i) P(x_{i+1},...,x_L,\pi_{i+1}=l|x_1,...,x_i,\pi_i=k,\theta)}{P(x|\theta)} \\ & = \frac {f_k(i) P(x_{i+1},...,x_L,\pi_{i+1}=l|\pi_i=k,\theta)}{P(x|\theta)} \\ & = \frac {f_k(i) P(x_{i+2},...,x_L,\pi_{i+1}=l|x_{i+1},\pi_{i+1}=l,\pi_i=k,\theta) P(x_{i+1},\pi_{i+1}=l|\pi_i=k,\theta)}{P(x|\theta)} \\ & = \frac {f_k(i) P(x_{i+2},...,x_L,\pi_{i+1}=l|\pi_{i+1}=l,\theta) P(x_{i+1},\pi_{i+1}=l|\pi_i=k,\theta)} {P(x|\theta)} \\ & = \frac {f_k(i) b_l(i+1) P(x_{i+1},\pi_{i+1}=l|\pi_i=k,\theta)} {P(x|\theta)} \\ & = \frac {f_k(i) b_l(i+1) P(\pi_{i+1}=l|\pi_i=k,\theta) P(x_{i+1}|\pi_i=k,\pi_{i+1}=l,\theta)} {P(x|\theta)} \\ & = \frac {f_k(i) b_l(i+1) a_{kl} P(x_{i+1}|\pi_{i+1}=l,\theta)} {P(x|\theta)} \\ & = \frac {f_k(i) b_l(i+1) a_{kl} e_l(x_{i+1})} {P(x|\theta)} \end{aligned} P(πi=k,πi+1=lx,θ)=P(xθ)P(πi=k,πi+1=l,xθ)=P(xθ)P(πi=k,πi+1=l,x1,...,xi,xi+1,...,xLθ)=P(xθ)P(x1,...,xi,πi=kθ)P(xi+1,...,xL,πi+1=lx1,...,xi,πi=k,θ)=P(xθ)fk(i)P(xi+1,...,xL,πi+1=lx1,...,xi,πi=k,θ)=P(xθ)fk(i)P(xi+1,...,xL,πi+1=lπi=k,θ)=P(xθ)fk(i)P(xi+2,...,xL,πi+1=lxi+1,πi+1=l,πi=k,θ)P(xi+1,πi+1=lπi=k,θ)=P(xθ)fk(i)P(xi+2,...,xL,πi+1=lπi+1=l,θ)P(xi+1,πi+1=lπi=k,θ)=P(xθ)fk(i)bl(i+1)P(xi+1,πi+1=lπi=k,θ)=P(xθ)fk(i)bl(i+1)P(πi+1=lπi=k,θ)P(xi+1πi=k,πi+1=l,θ)=P(xθ)fk(i)bl(i+1)aklP(xi+1πi+1=l,θ)=P(xθ)fk(i)bl(i+1)aklel(xi+1)
同时,由我们知道:
f k ( i ) = f ~ k ( i ) ∏ r = 1 i s r b k ( i ) = b ~ k ( i ) ∏ r = i L s r P ( x ) = ∏ r = 1 L s r \displaystyle f_k(i) = \tilde{f}_k(i) \prod_{r=1}^{i} s_r \\ \displaystyle b_k(i) = \tilde{b}_k(i) \prod_{r=i}^{L} s_r \\ \displaystyle P(x) = \prod_{r=1}^L s_r fk(i)=f~k(i)r=1isrbk(i)=b~k(i)r=iLsrP(x)=r=1Lsr
所以:
P ( π i = k , π i + 1 = l ∣ x , θ ) = f k ( i ) b l ( i + 1 ) a k l e l ( x i + 1 ) P ( x ∣ θ ) = [ f ~ k ( i ) ∏ r = 1 i s r ] [ b ~ l ( i + 1 ) ∏ r = i + 1 L s r ] a k l e l ( x i + 1 ) ∏ r = 1 L s r = f ~ k ( i ) a k l e l ( x i + 1 ) b ~ l ( i + 1 ) \begin{aligned} P( & \pi_i=k, \pi_{i+1}=l|x,\theta) \\ & = \frac {f_k(i) b_l(i+1) a_{kl} e_l(x_{i+1})} {P(x|\theta)} \\ & = \frac {\bigg[ \tilde{f}_k(i) \displaystyle \prod_{r=1}^{i} s_r \bigg] \bigg[ \tilde{b}_l(i+1) \prod_{r=i+1}^{L} s_r \bigg] a_{kl} e_l(x_{i+1})} {\displaystyle \prod_{r=1}^L s_r} \\ & = \tilde{f}_k(i) a_{kl} e_l(x_{i+1}) \tilde{b}_l(i+1) \end{aligned} P(πi=k,πi+1=lx,θ)=P(xθ)fk(i)bl(i+1)aklel(xi+1)=r=1Lsr[f~k(i)r=1isr][b~l(i+1)r=i+1Lsr]aklel(xi+1)=f~k(i)aklel(xi+1)b~l(i+1)
公式(2.2)的证明已经在前文《序列比对(12):计算后验概率》中给出过了。

由式子(1.1)、(1.2)、(2.1)、(2.2),我们可以得到:
(3.1) A k l = ∑ j ∑ i f ~ k j ( i ) a k l e l ( x i + 1 j ) b ~ l j ( i + 1 ) \displaystyle A_{kl} = \sum_{j} \sum_i \tilde{f}^{j}_k(i) a_{kl} e_l(x^j_{i+1}) \tilde{b}^j_l(i+1) \tag{3.1} Akl=jif~kj(i)aklel(xi+1j)b~lj(i+1)(3.1)
(3.2) E k ( b ) = ∑ j ∑ { i ∣ x i j = b } f ~ k j ( i ) b ~ k j ( i ) s i j \displaystyle E_k(b) = \sum_{j} \sum_{\{i|x_i^j=b\}} \tilde{f}^j_k(i) \tilde{b}^j_k(i) s^j_i \tag{3.2} Ek(b)=j{ixij=b}f~kj(i)b~kj(i)sij(3.2)

实际上,代码中使用了状态0,构建了初始概率向量。假设以B代表初始向量的“转移”期望次数,那么它是 A k l A_{kl} Akl当k=0时的一个特例:
(3.3) B 0 l = ∑ j b ~ l j ( 1 ) a 0 l e l ( x 1 j ) \displaystyle B_{0l} = \sum_j \tilde{b}^j_l(1) a_{0l} e_l(x^j_1) \tag{3.3} B0l=jb~lj(1)a0lel(x1j)(3.3)

由于我们使用了伪计数 r k l r_{kl} rkl 以及 r k ( b ) r_k(b) rk(b),所以:
(4.1) A k l ′ = A k l + r k l A'_{kl} = A_{kl} + r_{kl} \tag{4.1} Akl=Akl+rkl(4.1)
(4.2) E k ′ ( b ) = E k ( b ) + r k ( b ) E'_{k}(b) = E_{k}(b) + r_{k}(b) \tag{4.2} Ek(b)=Ek(b)+rk(b)(4.2)
(4.3) B 0 l ′ = B 0 l + r 0 l B'_{0l} = B_{0l} + r_{0l} \tag{4.3} B0l=B0l+r0l(4.3)

最终,我们可以估算转移概率和发射概率:
(5.1) a k l = A k l ′ ∑ l ′ A k l ′ ′ a_{kl} = \frac {A'_{kl}} {\displaystyle \sum_{l'} A'_{kl'}} \tag{5.1} akl=lAklAkl(5.1)

(5.2) e k ( b ) = E k ′ ( b ) ∑ b ′ E k ′ ( b ′ ) e_k(b) = \frac {E'_k(b)} {\displaystyle \sum_{b'} E'_k(b')} \tag{5.2} ek(b)=bEk(b)Ek(b)(5.2)

本文代码实际使用的计算公式就是(5.1)和(5.2)。

具体代码

具体代码如下:
(本文代码利用结构体重新梳理了过程,与之前文章中的代码相比,更工整了。)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>typedef char State;
typedef char Symbol;
struct MarkovChain {double* b;   // 初始概率向量double** a;double** e;Symbol* symb;State* st;int* idx;    // 每个符号向量所对应的序号int L;      // 符号向量的长度double** fscore;double** bscore;double* scale;double logScaleSum;
};
typedef struct MarkovChain* MChain;State state[] = {'F', 'L'};   // 所有的可能状态
Symbol symbol[] = {'1', '2', '3', '4', '5', '6'};   // 所有的可能符号
double init[] = {0.9, 0.1};    // 初始状态的概率向量
double emission[][6] = {   // 发射矩阵:行对应着状态,列对应着符号1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6,0.1, 0.1, 0.1, 0.1, 0.1, 0.5
};
double trans[][2] = {   // 转移矩阵:行和列都是状态0.95, 0.05,0.1, 0.9
};
const int nstate = 2;
const int nsymbol = 6;MChain create(const int n);
int random(double* prob, const int n);  // 根据一个概率向量随机生成一个0 ~ n - 1的整数
void randSeq(MChain mc);
void getSymbolIndex(MChain mc);
void forward(MChain mc);
void backward(MChain mc);
void printState(State* st, const int n);
void printSymbol(Symbol* symb, const int n);
void printMChain(MChain mc);
void destroy(MChain mc);
void toz(double* a, const int n);   // 将概率数组除以其和,使得新的概率的和为1
void BaumWelch(MChain* amc, const int n);int main(void) {int nchain = 3;int initLen = 80;int step = 20;int i;MChain* amc;MChain mc;if ((amc = (MChain*) malloc(sizeof(MChain) * nchain)) == NULL) {fputs("Error: out of space!\n", stderr);exit(1);}for (i = 0; i < nchain; i++) {mc = create(initLen + step * i);randSeq(mc);getSymbolIndex(mc);//printMChain(mc);amc[i] = mc;}BaumWelch(amc, nchain);for (i = 0; i < nchain; i++)destroy(amc[i]);free(amc);return 0;
}MChain create(const int n) {int k;MChain mc;if ((mc = (MChain) malloc(sizeof(struct MarkovChain))) == NULL) {fputs("Error: out of space!\n", stderr);exit(1);        }mc->L = n;if ((mc->symb = (Symbol*) malloc(sizeof(Symbol) * mc->L)) == NULL || \(mc->st = (State*) malloc(sizeof(State) * mc->L)) == NULL || \(mc->idx = (int*) malloc(sizeof(int) * mc->L)) == NULL || \(mc->fscore = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(mc->bscore = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(mc->scale = (double*) malloc(sizeof(double) * mc->L)) == NULL) {fputs("Error: out of space!\n", stderr);exit(1);}for (k = 0; k < nstate; k++) {if ((mc->fscore[k] = (double*) malloc(sizeof(double) * mc->L)) == NULL || \(mc->bscore[k] = (double*) malloc(sizeof(double) * mc->L)) == NULL) {fputs("Error: out of space!\n", stderr);exit(1);        }}return mc;
}int random(double* prob, const int n) {int i;double p = rand() / 1.0 / (RAND_MAX + 1);for (i = 0; i < n - 1; i++) {if (p <= prob[i])break;p -= prob[i];}return i;
}void randSeq(MChain mc) {int i, ls, lr;srand((unsigned int) time(NULL));ls = random(init, nstate);lr = random(emission[ls], nsymbol);mc->st[0] = state[ls];mc->symb[0] = symbol[lr];for (i = 1; i < mc->L; i++) {ls = random(trans[ls], nstate);lr = random(emission[ls], nsymbol);mc->st[i] = state[ls];mc->symb[i] = symbol[lr];}
}void getSymbolIndex(MChain mc) {int i;for (i = 0; i < mc->L; i++)mc->idx[i] = mc->symb[i] - symbol[0];
}void forward(MChain mc) {int i, l, k, idx;double logpx;// 缩放因子向量初始化for (i = 0; i < mc->L; i++)mc->scale[i] = 0;// 计算第0列分值idx = mc->idx[0];for (l = 0; l < nstate; l++) {mc->fscore[l][0] = mc->e[l][idx] * mc->b[l];mc->scale[0] += mc->fscore[l][0];}for (l = 0; l < nstate; l++)mc->fscore[l][0] /= mc->scale[0];// 计算从第1列开始的各列分值for (i = 1; i < mc->L; i++) {idx = mc->idx[i];for (l = 0; l < nstate; l++) {mc->fscore[l][i] = 0;for (k = 0; k < nstate; k++) {mc->fscore[l][i] += mc->fscore[k][i - 1] * mc->a[k][l];}mc->fscore[l][i] *= mc->e[l][idx];mc->scale[i] += mc->fscore[l][i];}for (l = 0; l < nstate; l++)mc->fscore[l][i] /= mc->scale[i];}// P(x) = product(scale)// P(x)就是缩放因子向量所有元素的乘积logpx = 0;for (i = 0; i < mc->L; i++)logpx += log(mc->scale[i]);mc->logScaleSum = logpx;/*// 打印结果printf("forward: logP(x) = %f\n", logpx);for (l = 0; l < nstate; l++) {for (i = 0; i < mc->L; i++)printf("%f ", mc->fscore[l][i]);printf("\n");}*/
}void backward(MChain mc) {int i, l, k, idx;double tx, logpx;// 计算最后一列分值for (l = 0; l < nstate; l++)mc->bscore[l][mc->L - 1] = 1 / mc->scale[mc->L - 1];// 计算从第n - 2列开始的各列分值for (i = mc->L - 2; i >= 0; i--) {idx = mc->idx[i + 1];for (k = 0; k < nstate; k++) {mc->bscore[k][i] = 0;for (l = 0; l < nstate; l++) {mc->bscore[k][i] += mc->bscore[l][i + 1] * mc->a[k][l] * mc->e[l][idx];}}for (l = 0; l < nstate; l++)mc->bscore[l][i] /= mc->scale[i];}/*// 计算P(x)tx = 0;idx = mc->idx[0];for (l = 0; l < nstate; l++)tx += mc->b[l] * mc->e[l][idx] * mc->bscore[l][0];logpx = log(tx) + mc->logScaleSum;// 打印结果printf("backward: logP(x) = %f\n", logpx);for (l = 0; l < nstate; l++) {for (i = 0; i < mc->L; i++)printf("%f ", mc->bscore[l][i]);printf("\n");}*/
}void printState(State* st, const int n) {int i;for (i = 0; i < n; i++)printf("%c", st[i]);printf("\n");
}void printSymbol(Symbol* symb, const int n) {int i;for (i = 0; i < n; i++)printf("%c", symb[i]);printf("\n");
}void printMChain(MChain mc) {int k;int ll = 60;int nl = mc->L / ll;int nd = mc->L % ll;for (k = 0; k < nl; k++) {printf("Rolls\t");printSymbol(mc->symb + k * ll, ll);printf("Die\t");printState(mc->st + k * ll, ll);printf("\n");}if (nd > 0) {printf("Rolls\t");printSymbol(mc->symb + k * ll, nd);printf("Die\t");printState(mc->st + k * ll, nd);printf("\n"); }printf("\n\n");
}void destroy(MChain mc) {int i;free(mc->symb);free(mc->st);free(mc->idx);free(mc->scale);for (i = 0; i < nstate; i++) {free(mc->fscore[i]);free(mc->bscore[i]);}free(mc->fscore);free(mc->bscore);free(mc);
}void toz(double* a, const int n) {int i;double sum;for (i = 0, sum = 0; i < n; i++)sum += a[i];if (sum == 0) {for (i = 0; i < n; i++)a[i] = 1.0 / n;} else {for (i = 0; i < n; i++)a[i] /= sum;}
}void BaumWelch(MChain* amc, const int n) {int i, k, j, l;double* b;   // 初始概率向量double** e;double** a;double* B;double** A;double** E;double* rb;   // 伪计数double** ra;double** re;int maxIter = 500;   // 最大迭代次数int niter;int totalLen;     // 序列总长度double minLogDiff = 1e-6;      // 终止阈值double loglh1, loglh2;   // log likelyhooddouble tmp, sum;// 初始化空间if ((b = (double*) malloc(sizeof(double) * nstate)) == NULL || \(e = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(a = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(B = (double*) malloc(sizeof(double) * nstate)) == NULL || \(A = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(E = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(rb = (double*) malloc(sizeof(double) * nstate)) == NULL || \(ra = (double**) malloc(sizeof(double*) * nstate)) == NULL || \(re = (double**) malloc(sizeof(double*) * nstate)) == NULL) {fputs("Error: out of space!\n", stderr);exit(1);     }for (k = 0; k < nstate; k++) {if ((e[k] = (double*) malloc(sizeof(double) * nsymbol)) == NULL || \(a[k] = (double*) malloc(sizeof(double) * nstate)) == NULL || \(E[k] = (double*) malloc(sizeof(double) * nsymbol)) == NULL || \(A[k] = (double*) malloc(sizeof(double) * nstate)) == NULL || \(re[k] = (double*) malloc(sizeof(double) * nsymbol)) == NULL || \(ra[k] = (double*) malloc(sizeof(double) * nstate)) == NULL) {fputs("Error: out of space!\n", stderr);exit(1);        }}// 序列总长度for (i = 0, totalLen = 0; i < n; i++)totalLen += amc[i]->L;// 初始化参数值,概率使用随机数,次数使用伪计数srand((unsigned int) time(NULL));for (k = 0; k < nstate; k++) {rb[k] = 0;b[k] = rand() / (float) RAND_MAX;}toz(b, nstate);  // 将概率向量的和转换为1for (k = 0; k < nstate; k++) {for (l = 0; l < nstate; l++) {ra[k][l] = 1;a[k][l] = rand() / (float) RAND_MAX;}toz(a[k], nstate);}for (k = 0; k < nstate; k++) {for (i = 0; i < nsymbol; i++) {re[k][i] = 1;e[k][i] = rand() / (float) RAND_MAX;}toz(e[k], nsymbol);    }// 开始迭代过程for (j = 0, loglh2 = 0; j < n; j++) {amc[j]->e = e;amc[j]->a = a;amc[j]->b = b;forward(amc[j]);backward(amc[j]);loglh2 += amc[j]->logScaleSum;}loglh2 = loglh2 * 1000 / totalLen;    // 用序列总长度归一化,得到每个符号的平均log-likelyhoodloglh1 = loglh2 - minLogDiff - 1;for (niter = 0; niter < maxIter && loglh2 - loglh1 > minLogDiff; niter++) {loglh1 = loglh2;// 使用伪计数赋值给初始次数for (k = 0; k < nstate; k++)B[k] = rb[k];for (k = 0; k < nstate; k++) {for (l = 0; l < nstate; l++) A[k][l] = ra[k][l];}for (k = 0; k < nstate; k++) {for (i = 0; i < nsymbol; i++) E[k][i] = re[k][i];   }// 利用旧参数计算期望次数for (j = 0; j < n; j++) {for (k = 0; k < nstate; k++) {B[k] += amc[j]->bscore[k][0] * b[k] * e[k][amc[j]->idx[0]];}for (k = 0; k < nstate; k++)for (l = 0; l < nstate; l++)for (i = 0; i < amc[j]->L - 1; i++)A[k][l] += amc[j]->fscore[k][i] * amc[j]->bscore[l][i + 1] * a[k][l] * e[l][amc[j]->idx[i + 1]];for (k = 0; k < nstate; k++)for (i = 0; i < amc[j]->L; i++)E[k][amc[j]->idx[i]] += amc[j]->fscore[k][i] * amc[j]->bscore[k][i] * amc[j]->scale[i];} // 利用期望次数计算新参数for (k = 0, sum = 0; k < nstate; k++)sum += B[k];for (k = 0; k < nstate; k++)b[k] = B[k] / sum;for (k = 0; k < nstate; k++) {for (l = 0, sum = 0; l < nstate; l++)sum += A[k][l];for (l = 0; l < nstate; l++)a[k][l] = A[k][l] / sum;}for (k = 0; k < nstate; k++) {for (i = 0, sum = 0; i < nsymbol; i++)sum += E[k][i];for (i = 0; i < nsymbol; i++)e[k][i] = E[k][i] / sum;}// 计算新的log-likelyhoodfor (j = 0, loglh2 = 0; j < n; j++) {amc[j]->e = e;amc[j]->a = a;amc[j]->b = b;forward(amc[j]);backward(amc[j]);loglh2 += amc[j]->logScaleSum;}loglh2 = loglh2 * 1000 / totalLen;}// 输出结果printf("num_of_seq = %d\n", n);printf("total_seq_len = %d\n", totalLen);printf("max_iter_num = %d\n", maxIter);printf("num_of_iter = %d\n", niter);printf("min_log_diff = %f\n", minLogDiff);printf("final_log_diff = %f\n", loglh2 - loglh1);printf("\n");printf("Real trans:\n");for (k = 0; k < nstate; k++) {printf("  ");for (l = 0; l < nstate; l++)printf("%f ", trans[k][l]);printf("\n");}  printf("Baum-Welch trans:\n");for (k = 0; k < nstate; k++) {printf("  ");for (l = 0; l < nstate; l++)printf("%f ", a[k][l]);printf("\n");}printf("\n");printf("Real emission:\n");for (k = 0; k < nstate; k++) {printf("  ");for (i = 0; i < nsymbol; i++)printf("%f ", emission[k][i]);printf("\n");}printf("Baum-Welch emission:\n");for (k = 0; k < nstate; k++) {printf("  ");for (i = 0; i < nsymbol; i++)printf("%f ", e[k][i]);printf("\n");}printf("\n");    // 释放空间free(b);free(B);free(rb);for (k = 0; k < nstate; k++) {free(ra[k]);free(re[k]);free(A[k]);free(E[k]);free(a[k]);free(e[k]);}free(ra);free(re);free(A);free(E);free(a);free(e);
}

(公众号:生信了)

这篇关于序列比对(十六)——Baum-Welch算法估算HMM参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/209134

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

4B参数秒杀GPT-3.5:MiniCPM 3.0惊艳登场!

​ 面壁智能 在 AI 的世界里,总有那么几个时刻让人惊叹不已。面壁智能推出的 MiniCPM 3.0,这个仅有4B参数的"小钢炮",正在以惊人的实力挑战着 GPT-3.5 这个曾经的AI巨人。 MiniCPM 3.0 MiniCPM 3.0 MiniCPM 3.0 目前的主要功能有: 长上下文功能:原生支持 32k 上下文长度,性能完美。我们引入了