14-4-隐马尔可夫模型代码实现详细注释版一、完整注释版代码Jupyter Notebook格式数据准备# 导入numpy库用于数值计算矩阵定义、概率运算、数组操作等importnumpyasnp# 定义HMM的**隐藏状态**无法直接观测的状态复习状态# 3个隐藏状态0认真复习1简单复习2没有复习statenp.array([认真复习,简单复习,没有复习])# 定义HMM的**观测状态**可直接观测的结果考试成绩# 9个观测状态0A1A2A-3B4B5B-6C7C8C-gradenp.array([A,A,A-,B,B,B-,C,C,C-])# 计算隐藏状态的数量n_state3n_statelen(state)# 计算观测状态的数量m_grade9m_gradelen(grade)# 定义HMM三要素1初始概率矩阵pi初始时刻各隐藏状态的概率# 均匀分布初始时“认真/简单/没复习”的概率都是1/30.333...pinp.ones(n_state)/n_state# 定义HMM三要素2转移矩阵t隐藏状态之间的转移概率# 每行对应“当前隐藏状态”每列对应“下一刻隐藏状态”# 例如t[0] [0.4,0.3,0.3] → 当前“认真复习”下一刻仍认真复习的概率0.4简单复习0.3没复习0.3tnp.array([[0.4,0.3,0.3],# 认真复习 → 认真/简单/没复习的概率[0.3,0.4,0.3],# 简单复习 → 认真/简单/没复习的概率[0.3,0.3,0.4]# 没有复习 → 认真/简单/没复习的概率])# 定义HMM三要素3发射矩阵e隐藏状态→观测状态的概率# 形状[3,9]3个隐藏状态 × 9个观测状态初始化为全0enp.zeros([3,9])# 隐藏状态0认真复习所有成绩概率均等每个观测概率1/9≈0.111e[0,:9]1/9# 隐藏状态1简单复习仅B到C-索引3-8有概率每个观测概率1/6≈0.167e[1,3:9]1/6# 隐藏状态2没有复习仅B-到C-索引5-8有概率每个观测概率1/40.25e[2,5:9]1/4# 打印HMM三要素验证矩阵定义是否正确# 初始概率矩阵输出[0.333..., 0.333..., 0.333...]均匀分布print(初始概率矩阵\n,pi)# 转移矩阵输出3×3矩阵每行概率和为1符合概率矩阵要求print(转移矩阵\n,t)# 发射矩阵输出3×9矩阵每行概率和为1隐藏状态的观测概率总和为1print(发射矩阵\n,e)# 输出说明# - 认真复习行0所有成绩概率都是1/9# - 简单复习行1A/A/A-概率为0B到C-各1/6# - 没有复习行2A到B概率为0B-到C-各0.25# 符合“复习越充分高分概率越高”的逻辑hmmlearn# 安装hmmlearn库Python中实现HMM的常用库支持离散/连续观测的HMM# 注意Jupyter中用!pip installPyCharm中需在终端执行该命令pip install hmmlearn# 从hmmlearn的hmm模块导入CategoricalHMM类适用于**离散观测状态**的HMM# 若观测状态是连续的需用GaussianHMM等其他类fromhmmlearn.hmmimportCategoricalHMM# 初始化CategoricalHMM模型n_state3 → 隐藏状态数量为3hmmCategoricalHMM(n_state)# 设置HMM模型的初始概率矩阵对应pihmm.startprob_pi# 设置HMM模型的转移矩阵对应thmm.transmat_t# 设置HMM模型的发射矩阵对应ehmm.emissionprob_e# 设置观测状态的数量可选显式指定9个成绩等级hmm.n_feature9# 注意hmmlearn中CategoricalHMM的发射矩阵必须满足# 1. 每行概率和为12. 列数等于观测状态数量9# 定义观测序列datas对应grade的索引[0,4,2,6,1] → [A, B, A-, C, A]datasnp.array([0,4,2,6,1])# 转换形状hmmlearn要求观测序列为二维数组样本数×特征数这里特征数1# 原形状(5,) → 转换后(5,1)datasnp.expand_dims(datas,axis1)# 预测给定观测序列预测最可能的隐藏状态序列维特比算法stateshmm.predict(datas)# 输出预测的隐藏状态序列array([0, 0, 0, 2, 0])# 对应state的索引0认真复习2没有复习# 结果解释# 观测序列[A, B, A-, C, A]对应的隐藏状态为[认真复习, 认真复习, 认真复习, 没有复习, 认真复习]# 其中C索引6被预测为“没有复习”符合“没复习很难得高分但可能偶然得C”的逻辑states# 评分计算给定观测序列的**对数概率**log概率# 对数概率的好处避免小数相乘导致的下溢数值过小probhmm.score(datas)# 输出对数概率-14.00367...负数因为log(0~1)为负prob# 将对数概率转换为实际概率np.exp(prob)# 输出≈8.28e-07 → 该观测序列出现的概率约为8.28×10^-7极低# 原因观测序列包含A、A等高分也包含C低分段组合概率本身极低print(np.exp(prob))# 采样生成指定长度的观测序列和隐藏状态序列10000个样本# 返回值datas观测序列形状(10000,1)states隐藏状态序列形状(10000,)datas,stateshmm.sample(10000)# 基于采样结果统计**实际转移矩阵t_2**验证和原始t的一致性# 初始化转移矩阵t_2为3×3全0t_2np.zeros([3,3])# 遍历每个隐藏状态0认真1简单2没复习foriinrange(3):# 找到所有当前状态为i的索引currentnp.where(statesi)[0]# 下一个状态的索引当前索引1next_indexcurrent1# 去掉最后一个索引无下一个状态next_indexnext_index[:-1]# 获取下一个状态的取值tmpstates[next_index]# 统计当前状态i→下一个状态j的次数计算转移概率forjinrange(3):# 状态i→j的次数 / 状态i的总转移次数 转移概率t_2[i][j]np.where(tmpj)[0].shape[0]/np.shape(tmp)[0]# 输出统计的转移矩阵t_2# [[0.411..., 0.293..., 0.295...],# [0.288..., 0.409..., 0.301...],# [0.296..., 0.309..., 0.394...]]# 结果解释统计结果和原始转移矩阵t每行0.4,0.3,0.3高度接近验证了HMM模型的正确性print(t_2)# 基于采样结果统计**实际发射矩阵e_2**验证和原始e的一致性# 初始化发射矩阵e_2为3×9全0e_2np.zeros([3,9])# 遍历每个隐藏状态foriinrange(3):# 找到所有隐藏状态为i的索引currentnp.where(statesi)[0]# 注原代码中next_index无实际作用属于笔误保留原代码结构仅注释说明next_indexcurrent1next_indexnext_index[:-1]# 获取隐藏状态i对应的观测序列tmpdatas[current]# 统计隐藏状态i→观测状态j的次数计算发射概率forjinrange(9):# 状态i→观测j的次数 / 状态i的总观测次数 发射概率e_2[i][j]np.where(tmpj)[0].shape[0]/np.shape(tmp)[0]# 输出统计的发射矩阵e_2# 行0认真复习0-8列都有概率且接近1/9# 行1简单复习0-2列概率≈03-8列接近1/6# 行2没有复习0-4列概率≈05-8列接近1/4# 结果解释统计结果和原始发射矩阵e高度一致验证了HMM模型的发射概率设置正确print(e_2)二、核心知识点梳理1. 隐马尔可夫模型HMM核心三要素小白易懂版组件名称数学符号代码对应核心含义本案例解释初始概率矩阵π \piπpi初始时刻各隐藏状态的概率分布一开始“认真/简单/没复习”的概率都是1/3转移矩阵A AAt隐藏状态之间的转移概率当前→下一刻比如“认真复习”→“认真复习”的概率0.4→“没复习”0.3发射矩阵B BBe隐藏状态→观测状态的概率生成观测的规则“没复习”只能生成B-/C/C/C-且概率各0.252. HMM隐藏状态与观测状态映射表隐藏状态索引隐藏状态观测状态范围grade观测状态索引发射概率规则0认真复习A ~ C-全部0~8每个观测概率1/9≈0.1111简单复习B ~ C-3~8每个观测概率1/6≈0.1672没有复习B- ~ C-5~8每个观测概率1/40.253. hmmlearn库CategoricalHMM核心属性/方法名称类型作用输入/输出说明本案例示例startprob_属性设置初始概率矩阵输入形状为(n_state,)的数组和为1hmm.startprob_ pitransmat_属性设置转移矩阵输入形状为(n_state,n_state)的数组每行和为1hmm.transmat_ temissionprob_属性设置发射矩阵输入形状为(n_state,n_feature)的数组每行和为1hmm.emissionprob_ en_feature属性显式指定观测状态数量输入整数观测状态数hmm.n_feature 9predict()方法预测隐藏状态序列维特比算法输入观测序列(长度,1)输出隐藏状态序列(长度,)states hmm.predict(datas)score()方法计算观测序列的对数概率输入观测序列(长度,1)输出对数概率浮点数prob hmm.score(datas)sample()方法生成观测/隐藏状态序列输入序列长度输出(观测序列, 隐藏状态序列)datas, states hmm.sample(10000)4. 关键数值运算说明代码片段作用小白解释np.expand_dims(datas, axis1)调整观测序列形状把一维数组(5,)变成二维数组(5,1)满足hmmlearn的输入要求np.where(states i)[0]查找指定状态的索引找到所有隐藏状态为i的位置比如i0时找到所有“认真复习”的时刻np.exp(prob)对数概率转实际概率log概率是负数exp后还原为0~1之间的实际概率np.where(tmpj)[0].shape[0]统计指定状态的次数统计tmp数组中等于j的元素个数用于计算概率三、可直接运行的代码版本版本1Jupyter Notebook版已在上方完整给出可直接复制运行版本2PyCharm版无ifname ‘main’可直接复制运行# 14-4-隐马尔可夫模型代码实现 - PyCharm可直接运行版本# 适配Python 3.8hmmlearn 0.2.8无需额外配置需先安装hmmlearnpip install hmmlearn# 1. 导入库并定义HMM三要素 importnumpyasnp# 定义隐藏状态和观测状态statenp.array([认真复习,简单复习,没有复习])# 3个隐藏状态gradenp.array([A,A,A-,B,B,B-,C,C,C-])# 9个观测状态n_statelen(state)# 隐藏状态数3m_gradelen(grade)# 观测状态数9# 初始概率矩阵均匀分布pinp.ones(n_state)/n_state# 转移矩阵隐藏状态转移概率tnp.array([[0.4,0.3,0.3],# 认真复习 → 认真/简单/没复习[0.3,0.4,0.3],# 简单复习 → 认真/简单/没复习[0.3,0.3,0.4]# 没有复习 → 认真/简单/没复习])# 发射矩阵隐藏→观测概率enp.zeros([3,9])e[0,:9]1/9# 认真复习所有成绩概率1/9e[1,3:9]1/6# 简单复习B到C-概率1/6e[2,5:9]1/4# 没有复习B-到C-概率1/4# 打印HMM三要素验证定义print( HMM三要素 )print(初始概率矩阵\n,pi)print(转移矩阵\n,t)print(发射矩阵\n,e)# 2. 初始化hmmlearn模型 fromhmmlearn.hmmimportCategoricalHMM# 初始化离散观测HMM模型hmmCategoricalHMM(n_state)# 设置模型参数hmm.startprob_pi hmm.transmat_t hmm.emissionprob_e hmm.n_feature9# 显式指定观测状态数# 3. 预测隐藏状态 print(\n 观测序列预测隐藏状态 )# 定义观测序列对应grade索引A, B, A-, C, Adatasnp.array([0,4,2,6,1])datas_2dnp.expand_dims(datas,axis1)# 转换为hmmlearn要求的二维形状# 预测隐藏状态states_predhmm.predict(datas_2d)print(f观测序列{[grade[i]foriindatas]})print(f预测隐藏状态索引{states_pred})print(f预测隐藏状态{[state[i]foriinstates_pred]})# 4. 计算观测序列概率 # 对数概率prob_loghmm.score(datas_2d)# 实际概率指数还原prob_actualnp.exp(prob_log)print(\n 观测序列概率 )print(f观测序列的对数概率{prob_log:.6f})print(f观测序列的实际概率{prob_actual:.6e})# 科学计数法显示# 5. 采样并统计转移/发射矩阵 print(\n 采样并统计转移/发射矩阵 )# 采样10000个样本datas_sample,states_samplehmm.sample(10000)print(f采样结果形状观测序列{datas_sample.shape}隐藏状态序列{states_sample.shape})# 统计转移矩阵t_2np.zeros([3,3])foriinrange(3):currentnp.where(states_samplei)[0]next_indexcurrent1next_indexnext_index[:-1]tmpstates_sample[next_index]forjinrange(3):t_2[i][j]np.where(tmpj)[0].shape[0]/np.shape(tmp)[0]print(统计的转移矩阵\n,t_2)# 统计发射矩阵e_2np.zeros([3,9])foriinrange(3):currentnp.where(states_samplei)[0]tmpdatas_sample[current]# 原代码中next_index无作用此处修正forjinrange(9):e_2[i][j]np.where(tmpj)[0].shape[0]/np.shape(tmp)[0]print(统计的发射矩阵\n,e_2)# 结果总结 print(\n 实验结论 )print(1. 采样统计的转移矩阵与原始矩阵高度一致每行≈0.4,0.3,0.3)print(2. 采样统计的发射矩阵符合预期认真复习全成绩有概率没复习仅低分段有概率)print(3. HMM可有效预测观测序列对应的隐藏状态验证了模型的合理性)总结HMM核心逻辑隐马尔可夫模型通过初始概率、转移矩阵、发射矩阵三要素描述“隐藏状态→观测状态”的生成过程核心功能包括预测隐藏状态维特比算法、计算观测序列概率、生成采样序列。hmmlearn使用要点离散观测状态用CategoricalHMM连续观测用GaussianHMM观测序列必须转换为二维数组长度×1转移/发射矩阵需满足“每行概率和为1”的概率约束。工程验证方法通过sample()采样大量数据统计转移/发射矩阵若和原始矩阵高度一致说明HMM模型参数设置正确。小白易错点忘记将观测序列转为二维数组导致predict()/score()报错发射矩阵行/列数与观测/隐藏状态数不匹配需确保形状为(隐藏状态数, 观测状态数)。运行结果HMM三要素初始概率矩阵[0.333333330.333333330.33333333]转移矩阵[[0.40.30.3][0.30.40.3][0.30.30.4]]发射矩阵[[0.111111110.111111110.111111110.111111110.111111110.111111110.111111110.111111110.11111111][0.0.0.0.166666670.166666670.166666670.166666670.166666670.16666667][0.0.0.0.0.0.250.250.250.25]]观测序列预测隐藏状态观测序列[np.str_(A),np.str_(B),np.str_(A-),np.str_(C),np.str_(A)]预测隐藏状态索引[00020]预测隐藏状态[np.str_(认真复习),np.str_(认真复习),np.str_(认真复习),np.str_(没有复习),np.str_(认真复习)]观测序列概率观测序列的对数概率-14.003675观测序列的实际概率8.284786e-07采样并统计转移/发射矩阵采样结果形状观测序列(10000,1)隐藏状态序列(10000,)统计的转移矩阵[[0.408299480.29802670.29367382][0.316916490.38758030.29550321][0.305606340.297379650.39701402]]统计的发射矩阵[[0.11865390.103278210.107339720.115752830.109660570.111981430.114012180.105599070.11372208][0.0.0.0.161162080.169418960.171253820.170030580.161467890.16666667][0.0.0.0.0.0.253426740.254035940.244897960.24763935]]实验结论1.采样统计的转移矩阵与原始矩阵高度一致每行≈0.4,0.3,0.32.采样统计的发射矩阵符合预期认真复习全成绩有概率没复习仅低分段有概率3.HMM可有效预测观测序列对应的隐藏状态验证了模型的合理性 进程已结束退出代码为0