杂记

希望多年以后,看到之前记录的文章,会有所感触。

0%

最近突然想到一个问题:是不是大家都敢于消费了,物质被不断的交换,金钱不断转移,整个社会就会变得欣欣向荣,为此,跟AI讨论了一下,结果大概如下:

——关于消费、贫富差距、共产主义与经济运行逻辑的一些思考

一、一个看似简单的问题:如果所有人都敢消费,经济是不是就会越来越好?

最近,我一直在思考一个问题:

一个社会的经济,究竟是如何运转起来的?

我们经常听到“刺激消费”“扩大内需”“促进经济循环”这样的说法。那么,是不是只要每个人都有足够的钱,并且愿意把钱花出去,整个社会的经济就能够持续繁荣,所有人的生活也会越来越好?

直觉告诉我,似乎是这样的。

假设一个小镇有100个人。

我去饭店吃饭,花了100元。饭店老板收到这100元后,其中一部分用于支付员工工资,一部分用于采购食材,还有一部分成为利润。

饭店员工拿到工资,去理发店消费;理发店老板赚到钱,又去超市购买商品;超市赚到钱,再向供应商进货。

如此循环,最初的100元便在不同人之间流动,形成了一连串经济活动。

一个人的消费,往往就是另一个人的收入。

这意味着,如果所有人都不愿意消费,企业的收入就可能下降。企业收入减少,就会考虑降薪、裁员、缩减投资。而居民收入下降,又会进一步抑制消费。

经济由此陷入恶性循环。

反过来,如果居民敢于消费,企业便更有动力扩大生产、增加招聘,而新增的就业和收入又会进一步支持消费。

从这个角度看,消费确实是维持经济活力的重要动力。

但问题来了:

如果消费能够促进经济增长,为什么不直接给所有人发钱,让大家都变得富有?

二、金钱的增加,不等于社会财富的增加

假设一个国家有100个人,每个人拥有1万元。

这个社会每年能够生产1万件商品。

某一天,政府突然给每个人发放100万元。

所有人的账户余额都大幅增加了。

但这个社会能够生产的商品,依然只有1万件。

当所有人都拿着更多的钱争相购买商品时,如果生产能力无法同步提高,商品价格就可能大幅上涨。

最终,人们虽然拥有更多货币,却未必能购买更多东西。

这说明了一个基本问题:

金钱本身不是财富,金钱能够交换到的商品和服务,才是财富的重要体现。

一个社会真正的富裕,不能只依靠增加货币数量,而必须建立在生产能力的提高之上。

当然,这并不意味着增加货币和刺激消费毫无意义。

当经济中存在大量闲置产能、失业人口和未被充分利用的资源时,消费需求增加,可以促使企业扩大生产,提高资源利用率。

但如果整个社会的生产能力已经接近极限,继续刺激需求,就更容易表现为物价上涨。

因此,消费的重要性在于推动现有生产能力得到利用,并激励未来的投资和生产,而不是凭空创造物质财富。

至此,我们似乎能够得出一个结论:

生产创造物质财富,消费为生产提供需求,而货币则帮助整个经济体系完成交换和协调。

但这又引出了第二个问题。

如果社会生产力持续提高,创造出的财富越来越多,是否意味着所有人的生活都会同步改善?

三、社会一定需要穷人吗?

这是一个更加深刻的问题。

一个社会如果存在富人,是不是就必然需要存在穷人?

换句话说,贫富差距是否是经济运行不可避免的结果?

我认为,我们首先需要区分两个概念:相对贫困与绝对贫困。

假设存在两个社会。

在社会A中,普通工人每月收入2000元,高收入者每月收入2万元。

在社会B中,普通工人每月收入2万元,高收入者每月收入10万元。

暂时假设两个社会的物价水平相同。

我们会发现,两个社会都存在贫富差距,但是社会B的普通工人,生活水平可能已经超过社会A的高收入群体。

这意味着,即使收入差距长期存在,也不代表必须有人无法满足基本生活需求。

一个社会可以存在收入高低的差别,同时让绝大多数人拥有体面的生活。

那么,为什么现实中的财富差距又会不断产生?

其中一个重要原因,在于不同人获得收入的方式并不相同。

假设两个人最初各有100万元。

甲将这笔钱投入生产性资产,例如企业、工厂或者股票。如果投资成功,他不仅能够获得收益,还可以将收益继续投入,实现复利增长。

乙主要依靠工资收入维持生活,收入增长受到劳动时间、技能水平和就业市场的限制。

长期下来,甲可能逐渐积累更多资产,资产又不断产生新的回报。

这种机制有可能扩大两个人之间的财富差距。

当然,投资存在风险,劳动者也可以通过储蓄和投资积累资产,财富差距并非只由这一种机制造成。教育、家庭背景、技术变化、市场竞争、制度安排等因素,同样会影响财富分配。

但它至少说明了一件事情:

社会财富的增长,与社会财富如何分配,是两个不同的问题。

经济增长决定社会能够创造多少财富,分配机制则决定这些财富如何进入不同人的生活。

一个国家的GDP持续增长,并不自动意味着每个人都能以相同的速度变得富裕。

而这让我产生了一个更进一步的思考。

如果未来的生产力已经足够发达,能够满足所有人的基本物质需求,金钱是否还会像今天这样重要?

四、如果物质极度丰富,金钱会不会失去意义?

假设未来的人类社会,已经拥有高度发达的人工智能和自动化技术。

机器人负责种植粮食、生产工业品、建造房屋;人工智能负责物流、生产调度和大量技术工作。

整个社会只需要少量的人类劳动,就可以生产足够所有人使用的物资。

那么,金钱对人类生活的重要性,是否会大幅下降?

我认为,这是有可能的。

因为金钱之所以重要,一个根本原因在于资源稀缺。

我们必须通过劳动、交易等方式获得收入,再用收入交换有限的商品和服务。

但如果某些商品的生产成本已经极低,供应近乎无限,那么人们就不再需要为获取这些商品付出很高的经济代价。

然而,新的矛盾出现了:

如果每个人都不再为金钱发愁,那么谁还愿意主动劳动?

如果劳动不再能够带来明显的物质回报,是否会有越来越多的人选择停止工作?

当人们不再愿意生产,社会的商品供应就可能下降。

商品重新变得稀缺,金钱也随之重新变得重要。

这样一来,就似乎形成了一个循环:

物质极度丰富,使人们不再需要为生存劳动;劳动意愿下降,又导致物质生产减少;物质重新稀缺,人们不得不重新投入劳动。

如果这个循环无法打破,那么一种完全摆脱经济压力、依靠人们自觉劳动维持生产的社会,是否从理论上就很难成立?

这让我联想到了共产主义所设想的高级阶段。

五、共产主义面临的一个根本难题:人为什么劳动?

在马克思对共产主义高级阶段的设想中,生产力的充分发展,以及劳动逐渐成为人们主动追求的生活需要,是实现“各尽所能,按需分配”的重要前提。

这里有一个非常关键的变化:

人们不再仅仅为了生存而劳动,而是能够从劳动本身获得满足。

这并不是完全没有现实基础。

即使在今天,也有很多人在经济条件已经相当优越的情况下,仍然持续从事科学研究、艺术创作、企业经营或者其他工作。

因为人类追求的不只有物质财富,还有成就感、社会认同、自我实现等。

但问题在于:

人们愿意主动从事自己热爱的工作,并不代表人们愿意主动承担社会所需要的一切工作。

有人愿意研究科学,有人愿意创作艺术,但有多少人愿意长期从事高危险性、高重复性或者令人不适的劳动?

如果某些劳动是整个社会正常运行所必需的,却没有足够的人自愿参与,那么社会该如何解决?

可以提高这些工作的待遇,给予劳动者更多回报。

但这样一来,差异化的物质激励似乎又重新出现了。

也可以通过社会责任、荣誉等方式吸引人们参与。

但这些方式能否长期、稳定地满足社会的全部劳动需求,仍然是一个值得怀疑的问题。

还有第三种可能:利用自动化技术替代这些劳动。

这可能是解决劳动激励问题的一条重要路径。

如果未来机器能够承担绝大多数辛苦、危险和重复性的工作,人类就不必依靠大量不情愿的劳动来维持社会生产。

因此,物质丰富不必然导致生产崩溃,关键在于社会生产是否仍然高度依赖人类的经济激励。

但即使解决了劳动问题,我们仍然无法轻易消除另一个难题。

稀缺性不一定能够被彻底消灭。

例如,城市中心最好的土地、独特的自然景观、某位优秀医生有限的时间,这些资源很难通过扩大工业生产来无限复制。

如果不依靠价格进行分配,那么应该采用什么方式?

排队、抽签、按需要分配,还是由某个机构集中决定?

每种方式都有需要解决的公平与效率问题。

因此,我更倾向于把两个目标区分开来:

一是通过生产力进步,让每个人都能享有富足的基本生活。

二是彻底消除稀缺性、经济激励与资源分配中的所有矛盾。

前者在理论上可以想象,也有持续进步的空间;后者则远没有那么简单。

这并不能证明共产主义在理论上绝对不可能实现,但至少说明,生产力高度发达并不足以自动解决所有经济和社会问题。

六、回到现实:如果让所有中国人都敢消费,经济会不会好很多?

前面的讨论其实已经延伸到了相当遥远的未来。

但经济学最终还是要回到现实。

对于今天的中国,我们显然还没有达到物质可以无限供应的阶段。

因此,一个更加实际的问题是:

在现有生产能力下,如果能够让更多人敢于消费,中国经济是不是就会得到明显改善?

我认为,答案在很大程度上是肯定的,尤其是在消费需求不足、部分产能尚未充分利用的时候。

但问题是,为什么有些人明明有一定积蓄,却依然不愿意消费?

这涉及一个容易被忽略的因素:人们对未来收入的预期。

假设有两个人。

甲是一名公务员,每月收入8000元,工作相对稳定。

乙是一名民营企业员工,每月收入15000元,但是行业竞争激烈,存在裁员、降薪的风险。

虽然乙当前的收入更高,但甲可能更愿意安排旅游、购买汽车或者进行其他长期消费。

原因并不是甲拥有更多的钱,而是他对未来收入的确定性更有信心。

乙可能会考虑:

如果明年失业怎么办?

如果找不到同样收入的工作怎么办?

如果家庭突然出现医疗支出怎么办?

这种对未来的不确定性,会让人们主动增加储蓄,减少不必要的消费。

从经济学角度看,这与预防性储蓄有关。

人们不敢消费,并不一定是因为现在没有钱,也可能是因为他们不知道未来还能不能持续赚到钱。

这让我进一步提出了一个观点:

在中国,公务员等体制内群体由于就业和收入相对稳定,可能是消费预期比较稳定的一部分人。

那么,在当前希望促进消费的经济环境下,公务员的待遇是不是不应该轻易下降?

七、公务员待遇不应该下降,因为他们能够促进消费?

这个论点乍一看有些特殊,但背后存在合理的经济学逻辑。

公务员收入相对稳定,意味着他们能够对未来形成较为清晰的预期。

这种预期可能支持家庭进行长期消费规划。

例如购车、装修、旅游,以及其他需要持续收入支撑的支出。

而这些消费,又会成为当地企业和其他劳动者的收入来源。

假设某地一批收入稳定的居民突然面临降薪。

即使他们仍然有一定积蓄,也可能因为担心未来待遇继续下降,而主动减少消费。

餐饮、零售、旅游等行业可能受到影响。

从这个角度看,维持稳定就业群体的合理待遇,确实有助于维持消费需求。

但如果因此认定“公务员越有钱,越能促进消费”,就忽略了一个重要的经济学概念:

边际消费倾向。

简单来说,就是一个人每增加100元收入,其中有多少会被用于消费。

假设有两个人。

甲每月收入20000元,乙每月收入3000元。

现在,两个人都额外获得1000元。

甲可能觉得这笔钱对生活影响不大,于是直接存入银行。

乙却可能用这1000元改善饮食、购买生活用品,或者满足之前被迫推迟的消费需求。

因此,在其他条件相同的情况下,收入较低、消费需求尚未得到充分满足的人,可能拥有更高的边际消费倾向。

这意味着:

消费预期最稳定的群体,不一定是每增加一元收入就能够产生最多新增消费的群体。

还有一个更现实的问题。

政府的财政资金是有限的。

假设某个地方政府拥有10亿元可以自由安排的财政资金。

它既可以用于维持或提高部分公职人员的待遇,也可以用于改善公共医疗、支持低收入家庭、偿还拖欠企业的款项,或者进行其他公共支出。

这些资金流向不同的地方,所产生的经济效果也可能不同。

如果为了维持某一群体待遇,不得不减少其他更有价值的公共支出,甚至增加企业和居民的负担,那么整体效果就未必有利。

因此,公务员是否应该调整待遇,不能单纯从消费角度判断,还必须考虑财政可持续性、公共服务质量以及资金的其他用途。

我认为,比“公务员绝对不能降薪”更有说服力的结论是:

在经济需求不足的时期,不应忽视稳定就业群体收入下降带来的消费收缩效应;但更重要的目标,是让更多普通劳动者也拥有稳定的收入预期。

八、为什么我认为,比刺激消费更重要的是让人敢于消费?

讨论到这里,我发现,我们最初的问题其实需要重新表述。

一开始,我们问的是:

如果所有人都愿意花钱,经济是不是就会越来越好?

但现在,更值得追问的问题变成了:

为什么大家不愿意花钱?

一个普通家庭选择储蓄,往往并不是因为他们不理解消费能够促进经济增长。

而是因为他们必须面对未来的不确定性。

失业、疾病、养老、子女教育、住房,这些问题中的任何一个,都可能对家庭财务造成巨大压力。

因此,即使现在拥有一定收入,人们也可能选择控制消费,为未来留下足够的保障。

从个人角度看,这是一种理性的风险管理。

但如果社会上大量家庭同时增加储蓄、减少消费,企业可能面临需求不足,进而减少投资和招聘。

最终,居民对未来的担忧,反而可能通过经济循环变成现实。

这与凯恩斯经济学中著名的“节俭悖论”有相通之处。

对每个人单独而言合理的选择,汇总到整个社会层面,却未必带来最理想的经济结果。

这也说明,促进消费不能仅仅依靠鼓励居民花钱。

更重要的,是让人们相信未来收入具有一定稳定性,让家庭面对疾病、失业和养老等风险时不至于陷入严重的经济困境。

当人们不再需要为了应对未来风险而不断积累储蓄时,消费意愿才有可能真正改善。

对于低收入群体,提高实际可支配收入同样重要。毕竟,如果一个人连基本生活都存在压力,仅仅改善他的消费信心,也不可能创造出足够的购买力。

所以,消费问题实际上涉及三个层面:

第一,有没有钱消费。

这涉及收入水平和财富分配。

第二,敢不敢消费。

这涉及就业稳定性、未来收入预期和社会保障。

第三,有没有值得消费的东西。

这涉及产品与服务供给、质量、价格,以及社会是否能够持续创造新的消费价值。

这三个条件相互影响,缺少任何一个,都可能限制消费的增长。

九、结语:我们真正追求的,应该是什么样的经济?

从最初的“如果每个人都有钱花,经济是不是就会变好”,到后来讨论财富分配、劳动激励、共产主义,再回到中国当下的消费问题,我发现这些思考其实围绕着同一个核心:

经济发展的最终目的,究竟是让钱不断流动,还是让人的生活不断改善?

货币流动能够支持经济活动,但货币流动本身不是最终目的。

生产力进步能够创造更多财富,但财富增加也不意味着所有人都会自动受益。

收入差距可能长期存在,但不意味着必须有人无法维持体面的生活。

物质极度丰富可能降低人们对金钱的依赖,但也不会自动消除劳动激励和资源分配的难题。

回到现实,扩大消费固然重要,但一个社会不应该要求居民为了促进经济增长而牺牲自己的财务安全。

相反,更理想的经济循环应该是:

生产力提高,企业创造更多有价值的商品和服务;劳动者获得更好的收入和更稳定的预期;居民在满足储蓄与风险保障需要后,有能力也有意愿消费;稳定的消费需求又支持企业持续经营、投资和雇佣。

这才是值得追求的良性循环。

对于公务员等就业相对稳定的群体,合理的待遇具有支持消费预期的作用;但对于整个社会,更重要的不是保护少数人的消费能力,而是尽可能让更多人获得稳定、可持续的生活保障。

最终,我越来越倾向于认为:

一个健康的经济体,不是所有人都在拼命花钱,而是大多数人不必因为害怕未来,而被迫压抑今天合理的生活需求。

衡量一个社会是否真正富裕,也不应该只看它生产了多少商品,创造了多少GDP,或者有多少人拥有巨额财富。

更值得关注的是:普通人能否通过自己的劳动获得体面的生活,能否在遭遇风险时保有基本保障,能否对未来形成稳定的预期。

或许,经济发展的重要意义,就在于让越来越多的人拥有这样的选择权。

真正的繁荣,不只是让钱流动起来,更是让人能够安心地生活。

近期配置了电脑,咬牙买了5080显卡,为了自己的游戏需求也为了后面的AI短剧的需求,目前看来是买在了高点,但是不是最高点,后期如果6090出了,准备最快速度添置一台,毕竟直接是生产力,近期有些缺乏睡眠,睡眠时间越来越短,是个不好的迹象,需要慢慢纠正。好在正常运动,身体状况良好。上周末,进行了AI短剧的初次尝试,我发现短剧其实是有一定的门槛的,我跟我朋友合作进行,他进行了故事的编写和文本,我进行短剧的生产和剪辑,目前测算,一天最多生产7分钟左右的短剧,后续如果改成完全自动化,效率还能提升,就是不知道显卡能不能抗住,辛苦显卡了。

后续准备每周末制作几集,等质量满足自己的要求后,尝试发表,就当娱乐的副业了,做着玩了。感觉其中最主要的就是情节要带钩子,画面审美要在线,这样才会吸引人读下去。

最近我的工作主要围绕云端 ASR 的 L2 Router 展开,核心目标是在保证最终识别字准的前提下尽可能降低 LLM 调用率。在此之前的文章重点介绍了 ED、CTC、Beam Search、N-best、Token 置信度、FA、RAG 等 ASR 内部信息,并将 Router 的目标从“判断 ED 是否错误”逐步转向“判断 LLM 是否真正有收益”,以更贴近实际业务需求。补充一个细节信息,对于云端路由(LGB模型),即使训练集的比例不合理,也不适合进行过采样,直接进行1:1.5的下采样即可。同时,近期在训练集中加入了部分难集数据,训练的结果有所提升,所以感觉数据才是目前真正缺乏的东西。但是未来模型算法上线也是在现网数据集上进行测试,不知道现在的方法的子准能不能正常保持。
近期,我在探索基于灰度区的二级路由:对于一级模型高置信样本直接使用 ED,低置信样本直接调用 LLM,而在决策边界附近再结合特征进行二次筛选。模型方面,当前以LightGBM为主,也在对比 XGBoost、CatBoost、SVM 等方法,但相比单纯替换模型。整体来看,这项工作的本质并不是简单做一个二分类器,而是在 Accuracy、Latency 和 Compute Cost 之间进行风险估计与智能调度。下一步的任务将会变得困难,需要多次尝试,估计要炼丹很久,主要目的是在保持字准不变的前提下,使得模型的LLM调用率得到控制。

近期工作主要围绕云端 L2 Router 的识别效果优化与 LLM 调用率压降展开。当前整体云端识别的LLM调用率已经明显下降了,但是与此同时字准也发生了明显的下降,因此需要先提升字准,看在字准不变的情况下,如何保证LLM调用率的下降。围绕这一目标,近期主要从特征增强、训练目标优化、错误样本分析以及专项预筛几个方向进行了推进。

首先,在 Router 特征侧新增了 CTC32 与 ED 结果一致性特征。原有 Router 特征主要来源于 ED N-best、Decoder Beam 搜索过程、FA 声学匹配以及 RAG 检索结果,能够描述候选之间的文本、语义、声学以及搜索稳定性,但缺少不同识别分支之间的一致性信息。因此新增 ctc_ed_sim,用于衡量 CTC32 直接识别结果与 ED/Decoder 最终结果之间的相似程度。该特征本质上反映了模型内部不同解码分支之间是否达成一致:当 CTC 与 ED 结果高度一致时,通常表示识别结果较稳定;而当两者存在明显差异时,则说明模型内部对当前音频存在一定分歧,后续调用 LLM 可能具有更高收益。从实际 LightGBM 特征重要性结果来看,ctc_ed_sim 的 Gain 已进入前列,说明跨分支一致性信息确实能够为 Router 提供新的有效判别信息,也验证了这一特征设计方向的合理性。但是通过具体的实验结果看出,加入ctc32结果后,效果并不及预期,有所提高,但是不多,因此暂时舍弃了这个特征。

在阅读论文SR-CEM后,参考这个论文的思路:Decoder token-level score 特征。之前的 Decoder 特征主要包括 ed_top1_top2_avg_gap、ed_beam_dist_entropy 和 ed_top1_switch_rate,这些特征主要描述的是 Beam Search 层面的整体竞争情况,例如 Top1 与 Top2 的分数差距、多个 beam 之间的分布熵以及 Top1 路径切换频率。这些特征在当前模型中重要性一直较高,但它们更多反映的是“搜索空间整体稳定不稳定”,并没有直接描述最终 winning path 上每一个 token 的局部质量。因此参考 SR-CEM 中利用 token score 和路径上下文进行置信度估计的思路,在 ED_dec_simple_imp.cpp 中沿最终 getMaxPath() 的 winning path 回溯,通过 pPrePath_ 获取每一个真实 token 对应的 fStepScore_,并基于这些局部 step score 构造了三个句级特征:ts_score_mean、ts_score_min 和 ts_suffix_avg。其中,ts_score_mean 表示最终最优路径中所有真实 token 局部得分的平均值,用于衡量整句路径的总体质量;ts_score_min 用于捕获整句话中得分最低、最不稳定的 token,解决“整句平均很高但某一个字非常危险”的问题;ts_suffix_avg 则进一步统计各位置之后 token 的平均得分,并对整句进行聚合,用于刻画 winning path 在后续生成过程中的稳定性。相比原有的 Beam Gap、Entropy 等统计量,这一组特征把 Router 的信息粒度从 beam-level 进一步深入到了 token-level。

同时,对现有 Beam 特征也重新进行了分布与有效性分析。以 ed_beam_dist_entropy 为例,在 7-beam 场景下理论最大熵为:

Hmax⁡=ln⁡7≈1.946H_{\max}=\ln 7\approx1.946

实际数据中发现约 97% 的 entropy 都集中在 1.4~1.95 的高位区间。针对这一现象,进一步分析了是否需要将熵归一化到 0~1。结论是,对 LightGBM 来说,是否归一化并不是核心问题,因为树模型主要依赖特征排序和切分点,线性缩放并不会增加新的判别信息。真正需要关注的是高熵区间内部是否仍然能够区分 LLM Benefit 样本和 No-Benefit 样本。从当前特征 Gain 排名来看,ed_beam_dist_entropy 仍然处于非常靠前的位置,说明即使整体分布集中在高位区间,LightGBM 仍然可以在 1.4~1.95 内部找到有效切分点,因此目前没有必要为了数值范围强行归一化。

进一步分析错误 case 后,发现一个比较突出的专项问题来自 数字、符号和书写规范化场景。例如:

五点五英寸 → 5.5英寸
百分之零点三三 → 0.33%
九的立方 → 9³
一百除以零点五 → 100÷0.5
三十八点六摄氏度 → 38.6℃

这类 case 的特点是:从传统 ASR 声学识别角度看,ED 其实可能是正确的,甚至 CTC、Decoder、FA 等内部置信信号都非常稳定,但是最终 benchmark 或业务要求希望输出更规范的书写形式。因此,这类错误并不是传统 ASR confidence 特征能够很好区分的,本质上更接近 Normalization Risk,也就是“当前文本虽然声学识别正确,但存在较高概率需要数字、单位、符号、日期、时间、公式等书写规范化”。因此加入了预筛选的逻辑,将这些任务直接送到LLM中。

在现有模型效果分析方面,也对当前约 70% 的 LLM 调用率进行了进一步拆解。通过比较 ED 和 LLM 最终文本,发现仍然存在大量 ED 与 LLM 完全一致、或者仅存在标点差异的样本。这意味着当前仍然有相当一部分 LLM 调用实际上没有带来任何实质性收益。从统计角度看,这部分样本就是后续继续降低调用率的主要空间。另一方面,对当前已经被 Router 省掉的 LLM 调用进行分析后,也发现真正存在非标点实质文本差异的比例相对较低,说明当前 Router 在保证字准的前提下已经具备一定的安全降调用能力,但对“可无损省调用”样本的覆盖仍然不足。加入该类特征(3种)后,字准上升到96.68%已经超过了基线!但是LLM调用率升高到了70%左右(仍低于基线的95%左右)。

另外,对整个 ASR 引擎内部多个模块进行重新梳理,包括 ED Decoder、Simple Decoder、FA、FSA、LM、PGS 等模块之间的关系。对 FA 的理解进一步明确:FA 本质上是 Forced Alignment 强制对齐模块,在候选文本已经确定的前提下,利用 Encoder/声学头提供的帧级声学信息,在固定文本顺序约束下寻找最佳时间对齐路径,并输出对应的声学匹配分。因此 FA 更适合作为“候选文本是否与当前音频声学一致”的判断依据,而不是判断文本语义或者书写规范是否正确。FSA 则主要作为热词、领域词、通讯录等定制场景下的受约束搜索模块,通过预定义有限状态图产生候选,并与 ED、FA、LM 等结果进行 PK。通过这部分梳理,对当前 Router 各个特征的来源和物理含义也更加明确。

当前阶段的核心目标已经比较明确:云端整体字准已经达到要求,后续不再以单纯提高字准为主要目标,而是重点研究如何识别“LLM 无收益”的请求,在保证最终识别准确率不下降的前提下,将当前约 70% 的 LLM 调用率继续向更低水平压缩。

后续准备重点推进三项工作。第一,基于现有模型进行完整 threshold sweep,建立 Final Accuracy - LLM Call Rate 曲线,确认当前模型本身还有多少仅靠阈值调整即可释放的降调用空间;第二,基于新的 LLM Benefit Label 重构 Router V2 数据集并重新训练,重点提高对 ED≈LLM、LLM无收益 等样本的识别能力;第三,完成 Normalization Risk 预筛模型,对数字、单位、时间、日期、运算符、特殊符号等场景进行专项保护,考虑使用 Char TF-IDF + Logistic Regression 作为轻量文本分类模型,使主 Router 在普通样本上能够采用更激进的省调用策略。

目前计划的 L2 级路由整体采用两阶段决策机制。第一阶段为基于规则的预筛选阶段,主要针对数字、公式、实体以及部分特定业务场景进行识别。当输入命中预先配置的关键词、模式或场景规则时,认为该类样本具有较高的 LLM 纠错收益,因此不再经过后续置信度模型判断,而是直接调用 LLM 进行二次纠错。该阶段主要用于优先覆盖 LLM 收益较为明确的高价值场景,减少后续模型的决策压力。

对于未命中第一阶段规则的样本,则进入第二阶段的置信度路由模型。当前计划采用 LightGBM 作为主要分类模型,同时也可以进一步探索其他置信度估计模型。模型根据 ED 解码过程、N-best 候选、RAG 检索结果以及声学信息等多维特征,对当前 ED 识别结果的可靠程度进行综合打分。模型输出一个连续置信度分数,并通过预设阈值完成最终路由决策。当模型得分高于阈值时,认为当前 ED 结果已经具有较高可信度,无需继续调用 LLM,直接输出 ED 结果;当得分低于阈值时,则认为 ED 结果存在较大的潜在错误风险,进一步调用 LLM 进行纠错。

因此,整个 L2 路由的核心问题可以归纳为:如何尽可能准确地判断当前 ED 结果是否已经足够可靠,从而在保证识别准确率的前提下减少不必要的 LLM 调用。 在这一过程中,置信度模型所使用的特征质量将直接影响最终的路由效果。

现有 ASR 置信度估计方法通常会利用 Decoder、Beam Search 或 CTC 解码过程中产生的概率、得分以及候选分布信息,例如 Top1 与 Top2 的分数差、Beam 分布熵、token posterior、N-best 一致性等。这些信息能够从不同角度反映模型在解码过程中的确定程度。但仅依赖单一的 Decoder 得分往往难以完整描述一个识别结果是否可靠,因此当前 Router 在传统解码分数的基础上,进一步引入了文本、语义、声学、Beam Search 以及 RAG 检索等多源特征。

当前使用的特征主要包括 semantic_distance、text_distance 和 faNorm_distance,分别用于衡量 N-best 候选在语义、文本和声学层面的离散程度;EntityMissRate 用于描述关键实体信息的缺失情况;ed_top1_top2_avg_gap、ed_beam_dist_entropy 和 ed_top1_switch_rate 用于描述 Decoder Beam Search 过程中候选之间的竞争程度和搜索稳定性;同时利用 rag_top1_score、rag_top1_top2_gap、rag_recall_count、rag_score_mean、rag_score_std 和 rag_score_sum 等特征描述外部检索结果的置信度及分布情况。

后续还计划继续补充 CTC 与 Decoder 的跨分支一致性特征,以及 Decoder token-level 的 score、rank 等 SR-CEM 类特征,使 Router 不再只依赖最终 N-best 结果,而能够进一步利用解码过程中的动态置信度信息。例如,CTC 与 Decoder 最终结果的一致程度可以反映不同解码分支之间是否形成一致判断,而 token score、token rank 等特征则能够刻画 Decoder 在逐 token 生成过程中的局部稳定性。

最终希望形成一套由声学一致性、文本一致性、语义一致性、搜索稳定性、CTC 置信度以及外部知识检索置信度共同组成的多维特征体系,并基于这些特征训练 LGB Router。通过调整模型阈值,可以在最终识别准确率与 LLM 调用率之间取得合适的平衡,从而实现“容易样本直接使用 ED,困难样本调用 LLM”的自适应路由机制。

9月5日 9月6日 两天宝贵的周末进行了合肥市职工羽毛球比赛,结果略有遗憾–季军,在4进2的时候以19:21输给冠军了,前期输了太多分了,太遗憾了,我感觉我的水平,是能赢他的。就当积累比赛经验了,下次再加油。
反思一下:首先赛程太长,耽误了两天,身体状态没调整好,其次第二天淘汰赛前几轮冷启动后,冲的太猛,小腿抽筋了,体力没合理分配,或者说没有及时补充电解质。最后,比赛的时候,节奏不太对,没合理调整,后期才发现他正手较弱,头顶很强,但是为时已晚,而且最后关键时刻,换球,很影响我的节奏。19:19后,两个关键的失误,葬送了这场比赛,不想回忆,下次继续加油。
9月5日,打完第一天的小组赛后,去逛了山姆,村里人进城了,什么东西都想买,但是一看价格又感觉囊中羞涩了,衣服倒是便宜,买了一件轻羽绒,一件灰色外套,吃的东西买了点火锅肉片啥的,后面有机会涮个火锅吃一下。

这里主要介绍语音识别引擎的端云协同机制。在实际手机端/车机端语音识别产品具有用户量庞大的特点,这意味着,如果所有的音频数据都调用云侧LLM进行推理,那么占用的显卡资源将是巨大的。为了给企业降低成本的同时保证用户的体验,端云协同就是不可或缺的。
端云协同包括两级,L1:端侧和云侧的路由。L2:云侧的二级路由(使用LLM或者encoder-decoder(ED)的结果,不使用LLM)。现在主流的做法L1是使用是否存在网络作为一个硬性的过滤,因为绝大多数手机用户的手机的计算资源是及其有限的,因此对于L1基本没有其他软性过滤的条件,甚至很多软件不会提供边缘端计算的能力。因此主要的降低计算量的地方在于L2这一层次的路由,在保证字准的同时,要最大化的降低LLM调用的次数。
要减少LLM调用的次数,首先就想到:什么情况下ED的结果足以支撑起用户的需求呢?顺着这个思路向下,不难理出两点:1.ED结果足够好。2.不适合LLM处理。针对第一种情况,即 ED 的识别结果已经足够可靠,此时即使进一步调用 LLM,能够获得的字准提升也十分有限,反而会额外增加推理成本和响应时延。因此,L2 路由的核心问题之一,可以进一步转化为:如何在不知道真实转写结果的情况下,提前判断当前 ED 输出是否可信。

最直接的思路是利用 ED 模型自身输出的置信度信息。例如,可以统计 token 级概率、句级平均置信度、最低 token 置信度以及候选序列之间的得分差异。当模型对当前语音具有较高把握时,其输出概率通常更加集中,最优候选与次优候选之间也会存在较明显的分数间隔,此时可以直接返回 ED 结果,而无需继续调用 LLM。相反,当多个候选序列的得分十分接近时,说明 ED 模型对当前语音存在较大的识别不确定性,此类样本更适合交由能力更强的 LLM 进一步处理。

然而,仅依赖最终解码结果的置信度并不足以完整描述一次识别过程中的不确定性。对于 CTC 或 CTC-Attention 类语音识别模型,还可以进一步利用声学模型在帧级别的输出分布构造不确定性特征。例如,CTC 每一帧都会输出各 token 的后验概率分布,当概率集中在少数几个 token 上时,说明当前声学判断相对确定;而当概率分布较为均匀时,则意味着模型对该帧存在较强的不确定性。因此,可以进一步统计 CTC mean entropy、CTC max entropy 等指标,用于描述整段语音在声学层面的识别难度。

除了概率分布本身,还可以关注识别结果在时间维度上的稳定程度。对于流式 ASR,模型会随着新音频帧不断到来持续更新 partial hypothesis。如果某一段语音的中间识别结果频繁发生修改,通常意味着模型对该段语音的判断并不稳定。因此,可以引入 partial hypothesis revision rate、longest stable prefix ratio 等指标。前者用于统计中间识别结果的修改频率,后者用于衡量已经连续保持稳定的前缀长度占比。通常而言,revision rate 越高、stable prefix ratio 越低,说明当前识别结果的不确定性越大。

类似地,还可以直接从 CTC 输出序列的变化情况中提取特征。例如,统计相邻帧之间 top1 与 top2 token 的排序交叉次数,即 top1/top2 crossing rate;或者统计非 blank token 在连续帧之间发生改变的频率,即 nonblank token flip rate。当这些指标较高时,意味着模型在多个候选 token 之间反复摇摆,可以认为当前声学证据并不充分。相比单纯使用最终句级置信度,这类动态特征能够更直接地描述模型在整个解码过程中的“犹豫程度”。

因此,对于“ED 结果是否足够好”这一问题,可以将特征大致分为三个层次:第一类是最终结果置信度特征,例如平均 token probability、最小 token probability、N-best score margin 等;第二类是CTC 帧级不确定性特征,例如 mean entropy、max entropy、top1/top2 crossing rate 和 nonblank token flip rate;第三类是流式识别稳定性特征,例如 partial hypothesis revision rate 和 longest stable prefix ratio。通过这些特征,可以从不同角度刻画当前 ED 输出的可靠程度。

第二种情况则是 当前样本本身并不适合继续交给 LLM 处理。这里的核心并不是 ED 结果一定正确,而是即使调用 LLM,也很难获得足够的收益,甚至可能导致识别结果变差。例如,对于极短语音、单字或简单命令词,由于上下文信息非常有限,LLM 能够利用的语言信息较少;对于大量数字、字母、专有名词以及无明显语义关联的内容,LLM 还可能由于自身语言先验过强而主动修改原本正确的识别结果。在严重噪声、多人重叠语音或者非语音输入等情况下,如果前端声学信息本身已经严重缺失,LLM 同样很难恢复真实内容,并可能产生额外的幻觉结果。

因此,L2 路由实际上并不是简单地判断“ED 对不对”,而应当判断:

当前样本调用 LLM 后,是否能够带来足够大的预期收益。

从这个角度来看,可以将每条语音看作一个决策样本。对于第 ii 条语音,分别获得 ED 输出结果 YEDiY_{ED}^{i} 和 LLM 输出结果 YLLMiY_{LLM}^{i},并利用真实标注 YGTiY_{GT}^{i} 计算二者的字错误率:

WEREDi=WER(YEDi,YGTi)WER_{ED}^{i} = WER\left(Y_{ED}^{i}, Y_{GT}^{i}\right)

WERLLMi=WER(YLLMi,YGTi)WER_{LLM}^{i} = WER\left(Y_{LLM}^{i}, Y_{GT}^{i}\right)

进一步定义调用 LLM 所带来的收益:

Gaini=WEREDi−WERLLMiGain_i = WER_{ED}^{i} - WER_{LLM}^{i}

当 GainiGain_i 较大时,说明该样本调用 LLM 可以明显改善识别结果,应当路由至 LLM;当 Gaini≤0Gain_i \leq 0 或收益非常小时,则说明 LLM 对该样本没有明显帮助,此时直接采用 ED 结果更加合理。

这样一来,L2 路由问题就可以进一步建模为一个二分类问题或收益预测问题。模型输入可以由 ASR 解码过程中提取的多种特征组成,例如音频时长、SNR、ED confidence、CTC entropy、N-best margin、token flip rate、revision rate 和 stable prefix ratio 等;模型输出则可以是当前语音是否需要调用 LLM,或者直接预测调用 LLM 后可能获得的字准收益。

在实际工程中,仅以识别准确率作为优化目标仍然不够,因为端云协同本质上还需要同时考虑精度、计算成本和时延。假设一次 LLM 调用的平均成本为 CLLMC_{LLM},则可以将路由优化目标描述为:

min⁡P(LLM)⋅CLLM\min \quad P(\mathrm{LLM}) \cdot C_{LLM}

同时约束整体识别性能满足:

WERroute≤WERtargetWER_{route} \leq WER_{target}

也就是说,在保证系统整体字错误率不超过预定阈值的前提下,尽可能降低 LLM 的调用比例。这实际上才是 L2 路由真正需要解决的问题:不是单纯追求最高字准,而是在基本不损失用户体验的前提下,用尽可能少的 LLM 调用获得接近全量 LLM 的识别效果。

在具体系统实现时,可以采用“硬规则过滤 + 软路由模型”的两阶段策略。首先使用一些确定性较强的规则对明显无需调用 LLM 的样本进行过滤,例如极短语音、非语音片段、特定命令词或者部分特殊格式内容;随后,将剩余样本输入一个轻量级路由模型,根据 ED 置信度、CTC 不确定性以及流式稳定性等特征预测 LLM 的潜在收益。当预测收益高于一定阈值时调用 LLM,否则直接返回 ED 结果。

其中,路由阈值可以根据实际业务需求进行动态调整。例如,当服务器 GPU 负载较低时,可以适当降低阈值,让更多边界样本进入 LLM,以获得更高的整体识别准确率;而当 GPU 负载较高或者业务处于流量高峰时,则可以提高阈值,只将最困难、最有可能从 LLM 中获益的语音送入 LLM。这样,端云协同就不再是一个固定的二分类策略,而可以进一步演化为一个结合识别难度、LLM 收益、实时负载和推理成本的动态资源调度问题。

最终,整个端云协同链路可以概括为:

用户语音 → L1 端云路由 → 云侧 ED 推理 → L2 难度/收益评估 → ED 直接输出或调用 LLM → 最终识别结果。

其中,L1 解决的是“这条语音是否需要进入云侧”,而 L2 解决的是“进入云侧之后,这条语音是否值得继续消耗 LLM 计算资源”。相比 L1,L2 具有更大的优化空间,也是整个端云协同系统降低 GPU 资源消耗、控制推理成本,同时维持识别准确率的核心环节。
下一篇文章将会详细介绍当前主流的方法,以及未来我打算采用的方法。

最近一个星期进行了情感识别的AI大赛,比赛内容是进行10分类,指标看宏F1,不允许使用带有情感数据或者任务的预训练模型。最后结果不尽人意,没有取得前三名,看到前面人的方法,更是让我大吃一惊。
先说我的思路,既然是中英文的二分类问题,我是考虑训练一个语种分类的模型进行二分类(这个正确率一般都非常高,超过99%),后续用中文和英文训练集分别训练两个不同的模型,分别在各自的语种上面达到最好的效果,窃以为非常巧妙,实则效果不如其他人,百思不得其解,也尝试了很多模型,hubert wav2vec whisper wavlm等等等,在中文上可以达到很好的效果,但是英文上怎么样也不行,验证集正F1上不去。到了后面,实在没办法,选取了4个模型进行,对于难分类的问题进行了投票,结果0.55+,现在回想起来,或许不应该把中文的数据集冶金学投票,因为中文本身的F1就很高了,失误了。最后结束,问了别人,别人直接whisper+lora微调然后全量微调,就能到0.57以上了,哎,尴尬。
总结下来有两个失误,第一个失误:显卡资源不足。第二个失误:没有事先进行规划,应该先把主流的模型都跑一遍试一下的。第三个失误:最后专家投票时,不应该修改中文数据的标签。
雄关漫道真如铁,而今迈步从头越,靡不有初,鲜克有终。希望这股对生活的热情,和对工作的激情能一直保持下去。

整体结构:PCM音频->分帧->加窗->FFT->功率谱->Mel滤波器组->对数频谱
举一个简单的c++代码示范
MelFeatureExtractor
Compute()->包含:FrameSignal() ApplyWindow() FFT() MelFilterBank() Log()

头文件介绍:mel_feature.h 只是进行函数的声明不包含具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#pragma once  // 头文件保护指令,作用是防止该头文件被同一个编译单元重复包含。

#include <vector> // 引入动态数组容器


class MelFeatureExtractor
{

public:

MelFeatureExtractor(
int sample_rate,
int frame_length,
int frame_shift,
int n_fft,
int n_mels
);


// 输入PCM,输出Mel谱
std::vector<std::vector<float>>
Compute(
const std::vector<float>& waveform
); // 返回值为std::vector<std::vector<float>> 二维数组


private:

int sample_rate_;

int frame_length_;

int frame_shift_;

int n_fft_;

int n_mels_;


std::vector<float> window_; // 私有成员,构造时传入


// Mel滤波器组
std::vector<
std::vector<float>
> mel_filterbank_;



void InitWindow(); // 生成窗函数 存入windows_


void InitMelFilterbank(); // 生成梅尔三角滤波器组并存入mel_filterbank_


};

这个头文件声明了关键的几个函数,下面进行具体函数实现的介绍。

函数的具体实现:MelFeatureExtractor.cpp 梅尔特征的提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include "MelFeatureExtractor.h"
#include <cmath>
#include <algorithm>

using namespace std;

// 构造函数:初始化参数 + 预计算窗函数 + 预计算梅尔滤波器
MelFeatureExtractor::MelFeatureExtractor( // 类里面的成员函数实现
int sample_rate,
int frame_length,
int frame_shift,
int n_fft,
int n_mels
)
: sample_rate_(sample_rate),
frame_length_(frame_length),
frame_shift_(frame_shift),
n_fft_(n_fft),
n_mels_(n_mels)
{
InitWindow();
InitMelFilterbank(); //生成传函数和滤波器组
}

// 初始化汉明窗 Hamming Window
void MelFeatureExtractor::InitWindow()
{
window_.resize(frame_length_);
for (int i = 0; i < frame_length_; ++i) {
window_[i] = 0.54f - 0.46f * cosf(2.0f * (float)M_PI * i / (frame_length_ - 1));
} //窗函数代码
}

// 线性频率 → 梅尔频率
static float linear_to_mel(float f)
{
return 2595.0f * log10f(1.0f + f / 700.0f);
} // 人耳频率映射

// 梅尔频率 → 线性频率
static float mel_to_linear(float mel)
{
return 700.0f * (powf(10.0f, mel / 2595.0f) - 1.0f);
}

// 初始化梅尔三角滤波器组
void MelFeatureExtractor::InitMelFilterbank()
{
int num_bins = n_fft_ / 2 + 1;
float f_max = sample_rate_ / 2.0f;

// 梅尔轴上均匀分点
float mel_min = linear_to_mel(0.0f);
float mel_max = linear_to_mel(f_max);

// 生成 n_mels + 2 个中心点
vector<float> mel_points(n_mels_ + 2);
for (int i = 0; i < n_mels_ + 2; ++i) {
mel_points[i] = mel_min + (mel_max - mel_min) * i / (n_mels_ + 1);
}

// 转回频率
vector<float> freq_points(n_mels_ + 2);
for (int i = 0; i < n_mels_ + 2; ++i) {
freq_points[i] = mel_to_linear(mel_points[i]);
}

// 转换成 FFT bin 索引
vector<int> bin_points(n_mels_ + 2);
for (int i = 0; i < n_mels_ + 2; ++i) {
bin_points[i] = roundf(freq_points[i] * n_fft_ / sample_rate_);
}

// 构造每个三角滤波器
mel_filterbank_.resize(n_mels_, vector<float>(num_bins, 0.0f));
for (int m = 0; m < n_mels_; ++m) {
int left = bin_points[m];
int center = bin_points[m + 1];
int right = bin_points[m + 2];

// 上升沿
for (int k = left; k < center; ++k) {
mel_filterbank_[m][k] = (float)(k - left) / (center - left);
}
// 下降沿
for (int k = center; k < right; ++k) {
mel_filterbank_[m][k] = (float)(right - k) / (right - center);
}
}
}

// 最简单的复数结构用于 FFT(仅实现本代码所需)
struct Complex {
float re, im;
Complex(float r = 0, float i = 0) : re(r), im(i) {}
Complex operator+(const Complex& o) const { return Complex(re+o.re, im+o.im); }
Complex operator-(const Complex& o) const { return Complex(re-o.re, im-o.im); }
Complex operator*(const Complex& o) const { return Complex(re*o.re - im*o.im, re*o.im + im*o.re); }
};

// 极精简的 FFT 实现(仅用于演示,工程建议用 kissfft / FFTW )
static void fft(vector<Complex>& data, bool invert) {
int n = data.size();
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1)
j ^= bit;
j ^= bit;
if (i < j)
swap(data[i], data[j]);
}

for (int len = 2; len <= n; len <<= 1) {
float ang = 2 * M_PI / len * (invert ? -1 : 1);
Complex wlen(cos(ang), sin(ang));
for (int i = 0; i < n; i += len) {
Complex w(1);
for (int j = 0; j < len/2; j++) {
Complex u = data[i+j], v = data[i+j+len/2] * w;
data[i+j] = u + v;
data[i+j+len/2] = u - v;
w = w * wlen;
}
}
}
}

// 计算梅尔谱
vector<vector<float>> MelFeatureExtractor::Compute(const vector<float>& waveform)
{
int num_samples = waveform.size();
int num_frames = (num_samples - frame_length_) / frame_shift_ + 1;

if (num_frames <= 0)
return {};

vector<vector<float>> mel_frames(num_frames, vector<float>(n_mels_, 0.0f));
int num_bins = n_fft_ / 2 + 1;

for (int t = 0; t < num_frames; ++t) {
// 1. 取一帧
vector<float> frame(frame_length_);
int start = t * frame_shift_;
for (int i = 0; i < frame_length_; ++i)
frame[i] = waveform[start + i];

// 2. 加窗
for (int i = 0; i < frame_length_; ++i)
frame[i] *= window_[i];

// 3. 补零到 n_fft
vector<Complex> fft_in(n_fft_);
for (int i = 0; i < frame_length_; ++i)
fft_in[i] = Complex(frame[i]);

// 4. FFT
fft(fft_in, false);

// 5. 计算功率谱
vector<float> power(num_bins);
for (int i = 0; i < num_bins; ++i) {
float re = fft_in[i].re;
float im = fft_in[i].im;
power[i] = (re*re + im*im) / n_fft_;
}

// 6. 梅尔滤波 + 取 log
for (int m = 0; m < n_mels_; ++m) {
float sum = 0.0f;
for (int i = 0; i < num_bins; ++i)
sum += power[i] * mel_filterbank_[m][i];

// 对数梅尔谱(加小epsilon防 log(0))
mel_frames[t][m] = logf(sum + 1e-6f);
}
}

return mel_frames;
}

基本实现梅尔谱特征的提取,值得说明的是,在引擎工程中,特征提取并不是重点,资源的调度才是重点。