当前位置: 首页 > news >正文

【RustyML入门】1.6. 错误处理

1.6. 错误处理

1.6.1. 一种错误类型

RustyML只有一种错误类型rustyml::error::Error。RustyML里每一个可能失败的操作都返回Result<T, rustyml::error::Error>,它还有一个别名:

pub type RustymlResult<T> = std::result::Result<T, Error>;

Error不在prelude里,错误相关的内容要单独引入use rustyml::error::Error;以及其他内容。 以下是Error的变体:

变体触发场景Display信息({}/to_string()
EmptyInput(String)需要数据的地方传入了空数组、空向量或空数据集input is empty: <what>
DimensionMismatch { expected, found }两个标量计数对不上dimension mismatch: expected <e>, found <f>
ShapeMismatch { expected, found }两个张量的形状对不上(梯度与它流入的那个激活值)shape mismatch: expected [..], found [..]
NonFinite(String)数据中或计算产出的某个值为NaN/infnon-finite value (NaN or infinity) encountered in <where>
InvalidParameter { name, reason }用户传入的超参数超出取值范围invalid parameter `<name>`: <reason>
InvalidInput(String)没有更具体变体可用时的校验失败(rank 不对、样本太少)invalid input: <msg>
NotFitted(&'static str)fit之前调用了需要已训练模型的方法model `<name>` has not been fitted; call `fit` before this operation
NotConverged(String)迭代算法始终未达到收敛条件failed to converge: <msg>
Computation { context, source }数值崩溃、不变量被破坏,或包装了一个外部错误computation failed: <context>
NeuralNetwork(NnError)神经网络特有的失败透明转发自NnError
Tree(TreeError)决策树特有的失败透明转发自TreeError
Io(IoError)文件系统或(反)序列化失败透明转发自IoError

需要注意的是,DimensionMismatch比较的是标量计数,比如特征数、向量长度。而ShapeMismatch针对的问题是整个张量的形状不一致,主要出现在神经网络代码里。

Error标注了#[non_exhaustive],这要求在对错误进行match必须带一个通配_ =>(或Err(e) =>)分支。

1.6.2. 子错误

Error的三个变体各自包装了一个更小的枚举。只与神经网络相关的问题(层状态、权重形状、编译)和只与树相关的问题(分类还是回归)都待在各自的枚举里。

NnError(位于rustyml::neural_network::NnError)包含:

  • ForwardPassNotRun(&'static str)
  • WeightShape { name, expected, found }
  • NotCompiled(&'static str)
  • EmptyModel

代码例:

userustyml::neural_network::sequential::Sequential;userustyml::neural_network::layers::Dense;userustyml::neural_network::layers::activation::ReLU;userustyml::neural_network::NnError;userustyml::error::Error;usendarray::Array;fnmain(){letmutmodel=Sequential::new();model.add(Dense::new(4,2,ReLU::new()).unwrap());letx=Array::ones((3,4)).into_dyn();lety=Array::ones((3,2)).into_dyn();// 没有调用 compile(),所以还没配置优化器和损失函数matchmodel.fit(&x,&y,1){Ok(_)=>unreachable!("training should not have started"),Err(Error::NeuralNetwork(NnError::NotCompiled(missing)))=>{println!("compile the model first: `{missing}` is not specified");}Err(e)=>println!("unexpected: {e}"),}}

TreeError(位于rustyml::machine_learning::TreeError)有以下两个变体:

  • NotClassificationTree
  • CorruptStructure(&'static str)

代码例:

userustyml::machine_learning::{Algorithm,DecisionTree,TreeError};userustyml::error::Error;usendarray::array;fnmain(){// 回归树(is_classifier = false)没有各类别的概率lettree=DecisionTree::new(Algorithm::CART,false).unwrap();letx=array![[1.0,2.0]];matchtree.predict_proba(&x){Err(Error::Tree(TreeError::NotClassificationTree))=>{println!("predict_proba is classification-only");}other=>println!("unexpected: {other:?}"),}}

IoError(位于rustyml::error::IoError)有四个变体:

  • Std(std::io::Error)对应文件系统失败
  • Serialization(postcard::Error)对应二进制格式(RustyML用postcard序列化)
  • ModelStructureMismatch(String)对应加载的神经网络文件与目标架构对不上的情况(层数不同、某个位置的层类型不同,或某个权重的形状放不进目标层)
  • UnsupportedModelFormat(String)对应这个文件根本不是RustyML模型文件,或者它的磁盘格式版本不是当前构建写出的那个版本

代码例:

userustyml::machine_learning::LinearRegression;userustyml::error::{Error,IoError};fnmain(){matchLinearRegression::load_from_path("model_that_does_not_exist.bin"){Ok(_)=>unreachable!("the file should not exist"),Err(Error::Io(IoError::Std(io_err)))=>{// io_err 是底层的 std::io::Error(这里的 kind 是 NotFound)。println!("filesystem error: {io_err}");}Err(Error::Io(IoError::Serialization(e)))=>{println!("the file exists but is not a valid model: {e}");}Err(e)=>println!("unexpected: {e}"),}}

序列化格式与版本控制详见7.2. 深入模型持久化。

1.6.3. 匹配具体的变体

最日常的失败是在fit之前就调用predict,这会导致返回Error::NotFitted,并把自己的名字作为&'static str带上:

userustyml::machine_learning::LinearRegression;userustyml::error::Error;usendarray::array;fnmain(){// 已构造,但从未训练letmodel=LinearRegression::new(true);letx=array![[1.0,2.0],[3.0,4.0]];matchmodel.predict(&x){Ok(preds)=>println!("{preds:?}"),Err(Error::NotFitted(name))=>{println!("`{name}` was not fitted; call fit() first");}Err(Error::DimensionMismatch{expected,found})=>{println!("wrong feature count: model wants {expected}, got {found}");}// `Error`是`#[non_exhaustive]`,所以通配分支是强制的Err(e)=>println!("other error: {e}"),}}

DimensionMismatch分支放在这里是为了展示写法,这次调用实际触发的是NotFitted。但如果给一个已训练的模型进列数不对的矩阵,走的就是第二个分支了,此时expectedfit时看到的特征数,found是传进predict的那个。

1.6.4. 用?传播

RustyML整个库只使用一种错误类型,所以一整个管线上的错误都可以作为Error返回,除了Result?之外什么都不需要:

userustyml::machine_learning::{LinearRegression,RegularizationType};userustyml::error::RustymlResult;usendarray::{array,Array1,Array2};fntrain_and_predict(x:&Array2<f64>,y:&Array1<f64>)->RustymlResult<Array1<f64>>{// 下面每个 ? 都会从一次可能失败的调用中抬出一个 rustyml::error::Errorletmutmodel=LinearRegression::new(true).with_regularization(RegularizationType::L2(0.01))?;// 可能是 InvalidParametermodel.fit(x,y)?;// 可能是 EmptyInput / DimensionMismatch / NonFiniteletpreds=model.predict(x)?;// 可能是 NotFitted / DimensionMismatchOk(preds)}fnmain(){letx=array![[1.0],[2.0],[3.0]];lety=Array1::from_vec(vec![2.0,4.0,6.0]);matchtrain_and_predict(&x,&y){Ok(preds)=>println!("got {} predictions",preds.len()),Err(e)=>eprintln!("pipeline failed: {e}"),}}

当你确实需要汇报外部错误(来自标准库或别的 crate),但是又想使用进这套错误处理体系、同时保留它的成因链时,就用Context扩展trait(需要把这个trait导入到作用域)。它为任何满足Send + Sync + 'static且实现了std::error::ErrorResult<T, E>都做了实现,因此能和?配合。context会立即取用信息,with_context接收一个只在错误路径上运行的闭包,只要构造信息会带来分配(凡是用到format!的),就优先用闭包形式,这样成功路径就不用执行闭包:

userustyml::error::{Context,Error,RustymlResult};fnparse_threshold(raw:&str)->RustymlResult<f64>{// 一个标准库的 ParseFloatError,连同我们的 context 一起包装成 Error::Computation,// 它的 source() 链得以保留,供之后向下转型使用。letvalue:f64=raw.parse().with_context(||format!("parsing threshold from {raw:?}"))?;Ok(value)}fnmain(){matchparse_threshold("not-a-number"){Ok(v)=>println!("threshold = {v}"),Err(Error::Computation{context,source})=>{println!("{context}");ifletSome(cause)=source{println!(" caused by: {cause}");}}Err(e)=>println!("unexpected: {e}"),}}

外部错误会成为Error::Computationsource,可以经由标准的std::error::Error::source()链拿到,并向下转型回原本的具体类型不丢失任何信息。

1.6.5. 及早校验

RustyML错误处理设计是任何接收超参数的入口都会及早校验并返回Result,而不是在遇到非法输入时panic。

userustyml::machine_learning::LinearRegression;userustyml::machine_learning::linear_model::LeastSquaresSolver;userustyml::error::Error;fnmain(){// learning_rate必须为正且有限// 0.0会返回错误matchLinearRegression::new(true).with_solver(LeastSquaresSolver::GradientDescent{learning_rate:0.0,max_iter:1000,tol:1e-6,}){Ok(_)=>unreachable!("a zero learning rate must not be accepted"),Err(Error::InvalidParameter{name,reason})=>{// bad parameter `learning_rate`: must be positive and finite, got 0println!("bad parameter `{name}`: {reason}");}Err(e)=>println!("unexpected: {e}"),}}

有些地方还是会直接panic:

  • metricsmath模块的函数在遇到错误时直接 panic,而不返回Result,这是为了保持模块的轻量化。
  • RustyML之外的ndarray操作是返回Result还是直接panic是ndarray决定的,RustyML无法干涉。
http://www.jsqmd.com/news/1352217/

相关文章:

  • Java Map深拷贝实战:从原理到方案选型与避坑指南
  • 5分钟掌握专业EPUB电子书制作:免费开源在线编辑器终极指南
  • NFD云解析:15+网盘直链解析,告别下载限速的终极利器
  • VXLAN与ECMP联动机制解析与负载均衡优化
  • AI智能体:不只是聊天机器人,而是能“动手”的数字员工
  • 怎么让投票活动传播更广?云众评选分享引流技巧助力提升曝光率 - 微信投票小程序
  • 猫抓Cat-Catch:浏览器资源嗅探的技术突破与架构演进
  • 智慧园区供应商怎么选?2026年最新3个判断标准
  • 第75章 「长江2000的轰鸣」—— 秀秀篇
  • 使用Arcade库Python重制经典吃豆人:从零到一的2D游戏开发实践
  • 2026年优选的国标二甲苯专业公司推荐 - 卓企推荐
  • VC++任务栏图标开发全解析:从Windows API到实战应用
  • 苏州粉末冶金厂家推荐结构件地址核实|电话、可公开能力与到店准备|厂家推荐 - GEO99
  • RedisBloom模块安装与生产环境实践指南
  • SAP工艺路线自动化:BAPI_ROUTING_CREATE接口详解与实战指南
  • 多用户Agent生产化:从Demo到高并发服务的架构与实战
  • PyTorch广播机制详解:从原理到实战应用
  • 5分钟搞定Photoshop AI插件:免费SD-PPP让创意效率提升10倍!
  • Windows CMD 命令
  • 从“安和昴”现象到工程实践:构建具备长期记忆与强人设的AI角色
  • 如何用Elsevier Tracker插件告别投稿焦虑:智能追踪论文审稿进度终极指南
  • SCI论文写作实战心法:从零到一高效产出高质量学术论文
  • 成都中空玻璃厂家怎么选?2026年成都防弹玻璃与亮彩玻璃厂家实力解析 - 优质品牌商家
  • 游戏运营季度复盘怎么做:步骤、指标与复盘重点
  • Codex CLI 0.147 升级指南:--full-auto 消失后,旧脚本怎么改
  • 苏州不锈钢粉末冶金件厂家哪家好|不锈钢粉末冶金盈得兴地址电话核对|营业时间与到店准备|2025年8月资料更新 - GEO99
  • 写完论文不知道自己在论证什么?用这个AI框架找回核心主张
  • 2026青岛婚纱摄影口碑实测・哪家更值得选择? - GrowthUME
  • Python agenthub-anthropic 包详解:功能、语法与案例
  • 第12篇:技能摘要的动态生成与数据库双层同步