62.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--自训练ML模型
在这一篇文章中,我们将在孢子记账项目中集成一个渐进式学习的机器学习系统,专注于应用中的类目预测功能。通过结合ASP.NET Core、ML.NET和MongoDB等技术栈,系统能够在用户使用过程中不断学习和优化预测效果,实现从单体应用向微服务架构的平滑过渡。简单来说,当用户输入一笔消费记录比如"星巴克咖啡"的时候,系统能够自动推荐可能的分类类目比如"餐饮"。并且系统会从用户的每次选择和纠正中学习,不断优化预测的准确性。
Tip:本文涉及的完整项目在专栏《.NET 8 + ML.NET LTR 智能类目匹配实战》中有详细介绍,欢迎查阅。
一、核心工作流程
整个系统的工作流程其实挺自然的,用户在记账的时候输入消费描述,系统会基于已有的数据给出一个类目推荐。如果用户接受了这个推荐,系统会把这次选择记录下来作为正面反馈,如果用户觉得推荐不对手动修改了类目,系统也会记录这次纠正,并且把错误的推荐标记为负面样本。
当积累的反馈达到一定数量后,系统会自动触发增量学习,用新的数据重新训练模型。这样模型就能够不断从用户的使用习惯中学习,预测也会越来越准确。特别是用户纠正预测结果的时候,系统会认为这是非常有价值的反馈,可能会立即触发模型重训练。
在模型还没有训练好或者训练数据不足的情况下,系统会使用基于规则的回退策略。这套规则匹配机制内置了常见消费场景的关键词,比如看到"地铁"、“公交"就会推荐"交通"类目,看到"星巴克”、"麦当劳"就会推荐"餐饮"类目。这样即使是新用户也能获得基本可用的预测结果。
二、核心组件介绍
系统主要由四个核心组件构成:机器学习核心CategoryMatcher
,渐进式学习管理器ProgressiveLearningManager
,反馈存储系统FeedbackStorage
,以及API接口层CategoryPredictionController
。下面我们逐一介绍这些组件的设计和实现细节。
2.1 机器学习核心:CategoryMatcher
CategoryMatcher
是整个系统的机器学习引擎,它基于LightGBM这个高效的梯度提升算法实现了Learning-to-Rank排序学习。为什么要用排序学习呢?因为类目预测本质上不是简单的分类问题,而是要从用户的多个自定义类目中找出最匹配的那个,这正是排序学习擅长的场景。
模型的特征工程设计得比较全面。首先是文本特征,包括用户输入的消费描述和候选类目的名称,系统会对这些文本进行标准化处理并转换成数值向量。然后是类别特征,比如用户ID和商户信息,通过OneHot编码转换成模型可以理解的格式。还有数值特征,包括金额分桶和消费时间,这些能够帮助模型学习到不同时间段和不同金额范围的消费模式。
训练过程中有个很重要的概念叫GroupId
。同一次查询的所有候选类目会被分配相同的GroupId
,这样LightGBM就知道要在这一组内进行排序学习。用户选择的正确类目标记为1,其他候选类目标记为0,模型通过学习这些正负样本来优化排序效果。
预测的时候,系统会为用户的每个类目都生成一个评分,然后按评分从高到低排序,评分最高的就是系统认为最匹配的类目。PredictTop1
方法返回最佳匹配,PredictTopK
方法可以返回前K个候选,这在需要给用户提供多个选项的场景很有用。
2.2 渐进式学习管理器:ProgressiveLearningManager
ProgressiveLearningManager
是整个系统的大脑,负责协调模型训练、反馈收集和智能预测等核心功能。它的设计是让机器学习真正融入到用户的日常使用中,而不是孤立的训练和预测两个阶段。
SmartPredict
智能预测方法是系统的核心策略。它会先判断模型是否可用和训练数据是否充足,如果条件满足就用机器学习模型预测,如果不满足就回退到规则匹配。即使使用机器学习预测,系统也会检查置信度是否达到阈值,如果置信度太低会提示用户需要确认。这样既保证了预测的可靠性,又能在数据不足时提供基本的功能。
用户反馈的记录和处理是渐进式学习的关键。RecordUserChoice
记录用户的主动选择,这种反馈会按照配置的频率触发模型更新,比如每5个反馈触发一次。RecordUserCorrection
记录用户的纠正行为,这种反馈的价值更高,因为它同时提供了错误样本和正确样本,能够让模型更快地学习到如何区分相似但不同的类目。
规则回退策略需要设计得很有层次感。第一层是精确关键词匹配,基于预定义的规则库直接匹配。第二层是语义相似度匹配,检查查询文本和类目名称的包含关系,还会使用同义词进行扩展匹配。第三层是通用关键词匹配,基于上下文推断可能的类目类型。如果前三层都没匹配到,系统会智能选择一个"其他"或"未分类"类目,避免错误的强制分类。这个保守策略确保了即使在机器学习模型不可用的情况下,系统也能给出合理的预测。
增量学习的机制也很有意思。当触发重训练时,如果是首次训练就使用全部数据从头训练,如果已经有模型了就获取最近的反馈数据进行增量更新。虽然叫"增量学习",但实际实现是把新数据加入历史数据后重新全量训练,这样能确保模型质量,代价是随着数据增长训练时间会变长。不过对于记账这种场景,数据增长速度是可控的,这种策略是合理的。
2.3 反馈存储系统:FeedbackStorage
FeedbackStorage
负责用户反馈数据的持久化,使用MongoDB作为存储引擎。选择MongoDB的原因很明显,文档型数据库非常适合存储这种半结构化的反馈数据,而且MongoDB的水平扩展能力也为未来的数据增长提供了保障。
UserFeedback
数据模型设计得很完整。除了基本的查询文本、选择类目这些核心字段,还包含了用户ID、商户信息、金额桶、消费时间等特征字段,这些都会作为机器学习的输入特征。特别是WrongCategory
字段,只有在用户纠正的场景下才会有值,用来标记系统预测错误的类目,这是负样本学习的重要数据来源。
系统会自动创建MongoDB索引来优化查询性能。Timestamp降序索引优化了时间范围查询和获取最新数据的操作,UserId索引支持按用户查询的场景,FeedbackType索引方便按反馈类型进行筛选和分析。这些索引设计考虑了系统的实际使用模式,既保证了写入性能又优化了读取效率。
ToTrainingData
方法负责把用户反馈转换成机器学习可用的训练数据。核心逻辑是为每个反馈生成一组查询-候选对,用户选择的类目标记为正样本Label=1,其他候选类目标记为负样本Label=0,所有候选项使用相同的GroupId
来保证LTR算法能正确学习排序关系。这种转换保证了训练数据的质量和一致性。
GetRecentFeedbacks
方法支持增量学习场景,通过时间戳降序排序获取最近的N个反馈。利用之前创建的索引,这个查询是非常高效的。这种设计让系统可以实现滑动窗口式的模型更新,只用最新的反馈数据进行增量训练,避免每次都处理全部历史数据。
2.4 API接口设计
CategoryPredictionController
提供了完整的RESTful API接口。Predict
预测接口接收用户的查询文本和类目列表,返回预测结果、置信度、预测方法和是否需要用户确认等信息。这个接口是整个系统对外的核心功能,前端调用这个接口就能获得智能的类目推荐。
反馈接口分为两类。FeedbackChoice
接口记录用户的正常选择,适用于用户接受系统推荐或者从列表中选择类目的场景。FeedbackCorrection
接口记录用户的纠正行为,需要同时传入错误类目和正确类目,这种反馈会被系统认为是高价值数据,可能立即触发模型重训练。
Stats
统计接口提供了系统运行状态的监控数据,包括总反馈数、训练数据量、模型是否存在、最近反馈数等信息。这对于了解系统的学习进度和健康状态非常有用。Retrain
重训练接口是管理员功能,可以手动强制触发增量学习,适用于收集到大量新反馈后立即优化模型的场景。
所有接口都使用DTO数据传输对象进行参数传递和结果返回,这样做的好处是API层和业务逻辑层解耦,而且DTO的设计也考虑了前端使用的便利性。比如PredictionResponse
不仅返回预测类目,还返回置信度和预测方法,前端可以据此给用户提供更丰富的交互体验。
三、数据模型设计
LtrRow
是Learning-to-Rank的核心数据结构,代表一个查询-候选对。它包含了所有用于训练和预测的特征字段,Query
是用户的消费描述,Candidate
是候选类目名称,Label
是训练标签(1表示正确匹配,0表示错误匹配),GroupId
用于LTR算法的分组排序。还有UserId
、Merchant
、AmountBucket
、HourOfDay
等辅助特征,让模型能够学习更复杂的模式。
UserCategory
表示用户自定义的类目,设计成不可变对象保证数据一致性。每个类目有唯一的ID和显示名称,ID用于系统内部引用和数据库存储,Name用于用户界面展示和文本匹配。这种设计既满足了技术需求又考虑了用户体验。
CategoryMatchResult
封装了预测结果,包含匹配的类目和对应的评分。这个结构在TopK预测中特别有用,可以返回按评分排序的多个候选结果,让用户有更多选择。评分的大小直接反映了模型对匹配度的判断,也可以用来设定置信度阈值进行过滤。
四、技术亮点
整个系统最大的亮点是真正实现了生产可用的渐进式学习。很多机器学习系统都是离线训练、在线预测的模式,模型一旦部署就固定不变。这个系统不一样,它能够在用户使用的过程中持续收集反馈并优化模型,实现了机器学习和用户体验的完美结合。
LightGBM排序学习的应用也很有技术含量。类目预测本质上是一个个性化排序问题,用户有自己的一套类目体系,系统需要在这些类目中找出最匹配的。传统的分类算法需要预定义类别,无法处理用户自定义类目的场景。Learning-to-Rank完美地解决了这个问题,而且训练和预测的效率都很高。
规则回退机制保证了系统的鲁棒性。机器学习模型虽然强大,但需要数据积累和训练时间。新用户刚开始使用的时候没有历史数据,这时候规则匹配就发挥作用了。而且即使模型已经训练好,如果预测置信度低也会回退到规则匹配或者提示用户确认,这种设计让系统在各种情况下都能提供可靠的服务。
MongoDB的使用也体现了技术选型的合理性。反馈数据是半结构化的,不同场景下可能有不同的特征字段,关系型数据库的固定schema不够灵活。MongoDB的文档模型非常适合这种场景,而且它的索引、聚合、分片等特性也为系统的扩展性提供了保障。
特征工程的设计也很全面。除了基本的文本匹配特征,还引入了用户ID支持个性化推荐,引入了商户信息增强上下文理解,引入了金额和时间特征捕捉消费模式。这些特征的组合让模型能够学习到更复杂的规律,比如某个用户在特定时间段的消费习惯,或者某个金额范围内的典型消费类型。
五、系统启动和运行
ProgressiveLearningManager
被注册为单例服务,保证了整个应用生命周期内模型和训练数据的一致性。配置参数从appsettings.json
读取,包括模型路径、MongoDB连接字符串、置信度阈值、训练频率等,这些都可以根据实际需求灵活调整。
系统启动时会尝试加载已有的模型文件,如果存在就直接使用,不存在就等待积累足够的反馈数据后进行首次训练。启动日志会显示当前的反馈数量、训练数据规模和模型状态,方便运维人员了解系统的健康状况。
CORS配置允许前端跨域调用API,Swagger UI提供了友好的接口测试界面,这些都是现代Web应用的标准配置。开发和生产环境的配置分离也做得很好,开发时启用Swagger方便测试,生产环境可以禁用以提高安全性。
六、总结
本文介绍了如何在孢子记账项目中集成一个渐进式学习的机器学习系统,实现类目预测功能。通过结合ASP.NET Core、ML.NET和MongoDB等技术栈,系统能够在用户使用过程中不断学习和优化预测效果。核心组件包括机器学习核心CategoryMatcher
,渐进式学习管理器ProgressiveLearningManager
,反馈存储系统FeedbackStorage
,以及API接口层CategoryPredictionController
。系统的设计充分考虑了实际应用场景,采用了LightGBM排序学习算法,并结合规则回退机制保证了鲁棒性。特征工程设计全面,涵盖文本、类别和数值特征,提升了模型的预测能力。通过持续收集用户反馈并进行增量学习,系统实现了从单体应用向微服务架构的平滑过渡,提升了用户体验和系统智能化水平。