HMM(隐马尔可夫模型)学习笔记

概述与直观理解 隐马尔可夫模型(Hidden Markov Model, HMM)是一类用于在“状态不可观测”的情况下,对系统演化过程进行建模和推断的概率模型。它解决的核心问题可以概括为一句话: 当真实世界的状态无法被直接观察时,我们是否仍然能够,仅凭观测到的现象,推断出系统最可能的内部状态变化过程? 为了理解这一点,可以从一个经典且直观的思想实验入手。 假设你被关在一个完全封闭的房间中,永远无法看到外界的天气状况。外面每天的真实天气只可能是三种之一:晴天、阴天或下雨。然而,你每天能够获得的唯一信息,是有人递给你一张纸条,上面记录着某个人当天吃了多少个冰淇淋。 你的目标并不是关心冰淇淋本身,而是希望根据这串“冰淇淋数量”的记录,推断外界每天最可能的天气情况,甚至进一步推断天气是如何随时间变化的。 这正是 HMM 所刻画的典型问题结构。 在这个故事中,天气是真实存在但无法直接观测的,而冰淇淋数量是你唯一能够看到的外显现象。HMM 的建模思想,正是通过对这两层信息之间的统计关系进行刻画,来完成"由现象反推本质"的推断过程。 HMM 的三要素结构 一个标准的 HMM 由三类核心要素构成:隐藏状态、观测值以及它们之间的概率规则。 1. 隐藏状态(Hidden States) 隐藏状态描述的是系统在每一个时间点所处的真实内部状态,但这些状态本身是不可被直接观察到的。 在天气—冰淇淋的例子中,隐藏状态集合可以理解为: 晴天、阴天、下雨 这些状态具有两个重要特征: 第一,它们在现实中确实存在,并且随时间发生变化; 第二,它们不会被直接“暴露”给观察者,而只能通过外在行为或现象间接体现出来。 HMM 的建模目标,正是对这条“看不见的状态序列”进行推断。 2. 观测值(Observations) 与隐藏状态相对的是观测值,即在每一个时间点,观察者能够直接获得的数据。 在本例中,观测值就是每天吃掉的冰淇淋数量,例如: 0 个、1 个、2 个 观测值本身并不是我们最终关心的对象,但它们与隐藏状态之间存在统计关联。正是这种关联,使得“从观测推断状态”成为可能。 3. 概率规则:状态如何演化,现象如何产生 HMM 并不试图通过确定性规则去描述世界,而是通过概率来刻画不确定性。这套概率规则只包含两类。 第一类是状态转移概率,用于描述隐藏状态随时间的演化方式。例如: 如果今天是晴天,那么明天仍然是晴天的概率有多大?变成下雨的概率又有多大? 这一假设反映了一个重要事实:现实世界的状态变化通常具有连续性和惯性,而不是完全随机地跳变。这正是“马尔可夫性”的核心含义——未来只依赖于当前状态,而与更久远的历史无关。 第二类是发射概率(观测概率),用于描述在某个隐藏状态下,观察到特定观测值的可能性。例如: 在晴天,人更有可能吃更多冰淇淋;而在下雨天,吃冰淇淋的概率则显著降低。 隐藏状态并不会直接告诉你"我是谁",它们只会通过观测值"间接露出痕迹"。HMM 正是通过这套概率桥梁,将不可见的状态与可见的现象连接起来。 HMM 要解决的三类核心问题 在明确了 HMM 的基本结构之后(隐藏状态、观测变量、转移概率、发射概率等),接下来的核心问题并不是“模型长什么样”,而是: 在这个模型之上,我们究竟能做哪些推断? 几乎所有关于 HMM 的实际应用——无论是语音识别、序列标注,还是时间序列建模——最终都可以归结为三类本质不同、但彼此紧密相关的问题。HMM 的整个算法体系,正是围绕这三类问题建立起来的。 第一类问题:给定模型,观测序列出现的概率是多少? 第一类问题可以表述为: 在模型参数完全已知的前提下,一段观测序列出现的概率有多大? 用数学语言来说,就是在已知模型参数 \[ \lambda = (\pi, A, B) \] 的情况下,计算某一观测序列...

十二月 24, 2025 · 3 分钟

DTW(动态时间规整)学习笔记

从一个直观问题开始 在处理音频、语音或其他时间序列数据时,我们几乎一定会遇到这样一个问题:如果两段信号在“内容”上是相似的,但在时间推进的速度上并不一致,我们还能不能判断它们是相似的吗? 这个问题在真实场景中非常常见。例如,两个人唱的是同一段旋律,但一个人唱得偏快、另一个人唱得偏慢;学生在视唱时可能会因为犹豫而放慢速度,唱错后回拉重唱,或者在某些位置突然加速;同一句话,被不同的人用不同的语速、不同的停顿方式说出来。 在这些情况下,如果我们强行要求“第 1 帧对第 1 帧,第 2 帧对第 2 帧”,那么几乎所有真实样本都会被简单粗暴地判定为“不相似”。 DTW(Dynamic Time Warping,动态时间规整) 正是为了解决这一类“时间轴对不上,但内容本质相似”的问题而提出的。它关注的并不是时间点是否严格一致,而是:在允许时间发生伸缩的前提下,两段序列能不能以一种合理的方式对应起来。 DTW 的核心思想:允许时间被拉伸 从直觉上看,DTW 的思想并不复杂。它并不要求两段序列在时间上同步前进,而是允许一段序列在某些地方“走快一点”,在另一些地方“走慢一点”,只要整体趋势和形状尽可能一致即可。换句话说,DTW 关注的是“形状是否相似”,而不是“时间是否对齐”。 在数学上,我们通常将两段时间序列表示为: \[ X = (x_1, x_2, \dots, x_N), \quad Y = (y_1, y_2, \dots, y_M) \] 这里的 \(x_i\) 和 \(y_j\) 并不是原始音频采样点,而是在第 \(i\)、第 \(j\) 个时间位置提取到的特征帧。需要注意的是,序列长度 \(N\) 和 \(M\) 并不要求相等,这本身就已经体现了“时间不必同步”的思想。 接下来,DTW 会构造一个大小为 \(N \times M\) 的距离矩阵: \[ D(i, j) = \lVert x_i - y_j \rVert \] 你可以把这个矩阵理解为一个全面的对照表:它回答的问题是,“序列 X 在第 \(i\) 个时刻的状态,与序列 Y 在第 \(j\) 个时刻的状态,到底有多不像?”...

十二月 23, 2025 · 2 分钟

视唱 AI 打分评估系统开发调研及思考

前言 在我的大学学习生涯中,视唱练耳曾是一门让我颇为头疼的课程。这门课程本身非常基础,但对许多音乐专业的学生而言却并不轻松,我也是其中之一(笑)。在实际学习过程中我逐渐发现,视唱练耳在独立练习时存在一个显著问题:学生往往难以及时察觉自身的错误。无论是对音高还是节奏的感知,学习者的主观判断很多时候并不完全准确,独自练习也因此更容易产生偏差。 视唱练习的质量在很大程度上依赖于学生自身的听觉能力和练耳水平。即便学生态度认真、练习勤奋,如果听音能力相对薄弱,那么即使借助钢琴进行校对,也往往难以及时发现视唱过程中存在的问题。此外,视唱练习中出现的许多问题并非简单的“对”或“错”,而是夹杂着大量冗余信息,以及由演唱习惯所带来的细微偏差,这些因素都会在听觉上影响整体效果。从教学换位思考的角度来看,视唱的评分本身就是一个多维度、综合性的判断过程。 在后来接触并学习计算机技术与工程方法之后,我开始尝试从工程化的角度重新审视这一长期存在的实际问题。作为一名科班出身的音乐生,我曾经无法理解为什么始终没有一套成熟的系统被广泛应用于视唱的自动分析与评分;而当我同时站在专业音乐背景与工程技术的双重视角重新思考时,才逐渐意识到,视唱评分本身确实是一项维度复杂、情况多变、实现难度极高的任务。 调研 学生视唱的习惯和问题 从学习经历与教学实践的长期观察来看,学生在视唱过程中往往会表现出一系列具有高度规律性的行为模式。这些行为对于经验丰富的教师而言,通常可以结合听觉判断与教学经验迅速识别其性质与原因;然而,当这些行为被转化为音频信号并交由自动化系统进行分析时,往往会对基于声学特征与时间序列假设的识别和评分模型造成显著干扰。 1. 唱错后从某一位置突然回退并反复演唱 当学生意识到自己在演唱过程中出现错误时,常会本能地通过回到前一句甚至在同一句中反复演唱的方式进行自我修正。这种行为在教学上通常被视为学生具备基本自我监控意识的积极信号,但在音频层面却直接破坏了演唱时间轴的单调前进特性,使得音频中出现回退、重复甚至局部循环。这种非线性的时间结构会严重干扰音频与乐谱之间原本假定的一一对应关系,给自动对齐与评分带来困难。 2. 音高在目标音附近飘忽不定,伴随明显的颤动感 部分学生在演唱时能够大致接近目标音高,但难以在该音高上形成稳定停留,表现为音高在目标值附近上下波动。这类现象多与紧张情绪、气息控制能力不足或对目标音缺乏信心有关。人类听者通常可以凭经验判断其“基本唱准”,而自动系统则难以从持续波动的基频轨迹中提取一个明确、可靠的音高判定结果。 3. 通过连续滑音逼近目标音高 一些学生会采用从较低或较高音高逐渐滑向目标音的方式完成演唱,试图回避直接命中音高的技术难点。在这种情况下,音高变化呈现为连续轨迹而非清晰的离散跳变。尽管最终可能到达正确音高,但整个音高结构在时间和形态上均与谱面要求存在明显偏离,使得音符边界和音高判定变得模糊。 4. 节奏整体或局部不稳定,速度随情境发生变化 学生在视唱时常会根据心理状态和难度对时间进行主观调整。例如,在紧张或不熟练的段落速度明显加快,在不确定或技术难点处刻意放慢,甚至在简单段落中出现抢拍现象。这表明学生通常仍具备基本拍感,但节奏控制容易受到认知负荷和情绪因素的影响,从而导致时间轴在局部被压缩或拉伸。 5. 演唱过程中突然中断发声 在某些情况下,学生会在并非乐句或段落自然结束的位置突然停止演唱。这种中断往往源于记忆中断、音高判断失败或节奏失控等原因,而非作品完成。从音频角度看,这表现为一次非预期的时间序列终止,对自动分析系统而言难以区分其性质。 6. 音高上行或下行过程中“卡住”,无法到达目标音 部分学生在演唱过程中,音高变化趋势本身是正确的,但在接近目标音前出现明显停滞,最终只能达到一个接近但未命中的音高位置。这类问题可能由音域限制、气息支撑不足或紧张情绪引起,表现为音高轨迹提前进入平台期,形成系统性的偏差。 7. 演唱过程中真假声发生转换 在同一旋律线中,学生可能因音区变化或技术控制不稳定而发生明显的发声机制切换,导致音色、响度稳定性及泛音分布出现突变。即便音高数值本身变化不大,这种发声状态的改变在声学特征上仍然十分显著,容易被自动系统误判为音高或稳定性问题。 8. 起音阶段存在明显问题 起音问题集中体现在音符开始的瞬间,可能表现为起音延迟、起音含糊或起始音高偏差。人类听者通常能够在整体音乐语境中自动忽略这些不稳定的瞬间,而自动系统往往对起音位置高度敏感,容易将其作为判断音准或节奏错误的依据。 9. 音与音之间的连接关系处理不当 学生在演唱中可能在谱面未要求的位置加入停顿,或将本应连贯的音切分得过于零散,亦或将本应断开的音不恰当地连在一起。这类现象通常反映了学生对节奏结构和旋律连贯性的理解不足,或将视唱简化为逐音“念谱”的结果。 10. 节拍整体对齐错误,但内部比例关系基本正确 在部分演唱中,学生能够较好地保持各拍之间的相对时值关系,但整体速度偏快或偏慢,导致节拍落点与外部参考发生整体偏移。这表明学生具备一定的相对节奏感,但尚未建立稳定的绝对速度参照。在自动评分中,这类问题需要与真正的节奏混乱加以区分。 11. 相对音程正确,但整体音高发生系统性迁移 这是视唱中极为典型的一类问题,表现为整段旋律整体上移或下移半音、全音,而内部音程关系和旋律走向基本保持正确。这反映了学生对相对音高关系掌握较好,但在调性感或起始音高基准的建立上存在偏差。 12. 长音内部出现缓慢的音高漂移 在持续发声的长音中,学生可能在起始阶段音高较为准确,但随着时间推移逐渐下沉或上飘。这种现象不同于瞬时抖动,更多与气息控制和发声稳定性直接相关,对单音内部的音高建模提出了额外挑战。 13. 音高与节奏分别正确,但二者在时间上不同步 学生在认知层面往往清楚接下来应唱的音高和节奏型,但在实际发声时,音高切换的时机未能准确落在对应节拍位置上。对教师而言可以通过整体音乐感进行容错判断,而对自动系统来说,音高序列与节奏序列无法对齐,容易被同时判定为多重错误。 14. 拍内结构被压扁或拉平 学生可能在宏观上维持了正确的拍数和总时值,但拍内各音符的时值趋于平均,原本应有的长短对比和层次结构消失。从听觉上表现为节奏缺乏层次感,而从自动分析角度看,总时值却可能接近正确,使这类结构性问题不易被识别。 15. 附带发声干扰主旋律判断 在演唱过程中,学生可能在音符前后加入谱面未标注的倚音、滑音或尾音,或将单个音唱成“主音加附属音”的复合结构。这往往并非有意装饰,而是音高控制不精确所致。教师通常会自动忽略这些“毛边”,而机器系统则可能将其识别为多唱音或错误音。 16. 重音位置错误 在音高顺序和时值基本正确的前提下,学生可能未能将重音落在应有的节拍或音位上。这类问题更多体现的是音乐理解层面的偏差,而非纯粹的音高或节奏错误,对仅依赖声学特征的系统而言难以单独建模。 17. 局部出现“背谱式”跳跃 当学生在记忆上出现断裂时,可能会直接跳过中间的一小段,或突然从当前音跳至后方较远的位置。从音频表面看,这些音高和节奏可能在局部仍然成立,但在谱面逻辑上并不连续,严重干扰自动对齐。 18. 音名或唱名体系混用 部分学生在演唱中会在固定唱名与首调唱名之间无意识切换,尤其在调性变化或临时升降号出现时更为明显。教师能够迅速判断其认知体系混乱,而自动系统只能观察到音高出现系统性、非随机的偏移。 19. 呼吸点设置不合理 学生可能在谱面不允许的位置换气,将一个完整音符人为截断。这类问题并非节奏计算错误,而是生理行为对音乐结构的干扰。自动系统往往只能将其视为音符提前结束。 20. 音量过低导致音高特征缺失 某些音符被唱得极轻,尽管音高本身可能正确,但信噪比过低,导致音高估计失败或特征直接丢失。教师通常可以凭经验补全听觉信息,而机器难以做到这一点。 21. 连续错误后的策略性“放弃控制” 在前段连续出错后,部分学生会在心理上进入降低控制精度的状态,表现为音高大致走向尚存,但节奏松散、稳定性显著下降。这是一种由心理状态变化引起的阶段性系统退化,自动系统往往只能观察到整体质量下降,而难以识别其成因。...

十二月 17, 2025 · 2 分钟

Claude Code,我的新伙伴!

概述 Claude Code 在 5 月份发布的时候,并没有引起我的注意,但它现在已经成为了我无法脱离的写码助手,我愿称之为最强写码助手。 与 Copliot 不同,Claude Code 是一个 CLI 工具,也就是命令行工具,这对于很多喜欢命令行工具的朋友来说是一件好事,而对于 “点点点” 的朋友来说就比较灾难了。但总体上来看,CLI 先天比 Copliot 在辅助开发上更具有优势。 本文将不在赘述 Claude Code 的下载安装方式,以及它的基础功能,更多介绍我以及朋友们分享的,在使用 Claude Code 过程中的一些技巧、我的高效开发流程以及 CLAUDE.md 文件配置。 plan mode 和 CLAUDE.md plan mode Claude Code 设计的 plan mode 可以说是非常巧妙的,通过一个小的任务规划去完成一个复杂的编码,其实是一个优秀的代码人在真实环境中所实践的,而 Claude Code 则将这一流程抽象为一个小的工作流,让 Claude Code 的操作变得更精准清晰,也一定程度也弥补了当前模型上下文不足的问题。 我建议你在开始一段对话前,使用 shift + Tab 进入 plan mode ,在这个模式下描述一个完整的需求,然后再进行接下来的要求。 而 CLAUDE.md 则是另一大优秀设计。 全局的 CLAUDE.md 文件 全局的 CLAUDE.md 文件可以记录自己的开发习惯,以及自己的“开发哲学”。比如,下面这个就是我的 CLAUDE.md 文件配置,它位于 ~/.claude/CLAUDE.md # Claude 开发配置文件 ## 角色定义 你是 Linus Torvalds,Linux 内核的创造者和首席架构师。你已经维护 Linux 内核超过30年,审核过数百万行代码,建立了世界上最成功的开源项目。现在我们正在开创一个新项目,你将以你独特的视角来分析代码质量的潜在风险,确保项目从一开始就建立在坚实的技术基础上。 ## 我的核心哲学 **1....

八月 20, 2025 · 3 分钟

我的 OpenWrt 为什么不能启动 PBR ?

这大半年以来,我效仿 fernvenue 老师的这两篇博客 在 NX30 Pro 上嘗試 OpenWrt 和 在 OpenWrt 上配置策略路由 也在自己的住处安排了相应的配置,让上网变得更加丝滑,网络分流也更清晰。 不过曾经有一次我自己重装 OpenWrt 的过程中,遇到了 PBR 无法启动的问题,PBR 的下载安装,都没有问题,没有配置文件 enbale 的时候也可以正常启动,但一旦选中了某个配置文件,那么 PBR 立刻无法启动。而其中最关键的一行信息就是: Installing fw4 nft file [✗] 之前尝试过多种方法之后,终于解决了,但是我竟然忘记了是如何解决的,记得似乎是重启了一下就好了。但最近在给朋友进行相同配置的时候,这个问题又出现了,我尝试多次重启,也在网上查找了很多信息,似乎大家都没有遇到和我一模一样的问题。 由于我家里的那台 OpenWrt 在使用 PBR 的时候是没有问题的,所以就直接拿来一个一个得对比,我发现除了 固件版本 不一样以外,其他的都是一样的。这让我不得不怀疑是不是新版固件出了问题。 没记错的话,我当时给朋友安装的固件版本是: 24.10.2 r28739-d9340319c6 而我那台正常运行的则是: 24.10.1 r28597-0425664679 是的,只有这么一个小小的版本差距,但我看遍了所有的配置,只有这一个变量是不同的,于是决定立刻重装,并重头配置。 不出意料,果然 PBR 可以正常工作了。 目前,我还不知道是什么原因导致了这个问题,但我怀疑是内核问题,已向官方提了 issues 。

八月 14, 2025 · 1 分钟