关于 matlab-classify-tabular-data
此技能指导使用 MATLAB 比较表格数据集上的分类器,帮助用户识别准确率在统计上相当的模型层级。它通过规定的工作流涵盖数据准备、评估策略、模型选择和统计检验。
主要功能
- 01比较表格数据的分类器
- 02使用交叉验证或留出法评估
- 03依据数据集特征选择模型
- 04检验准确率差异是否显著
- 05导出可重放的分析工作流
适用场景
- 01比较带标签表格上的分类器
- 02为数据集选择评估方法
- 03判断准确率差异是否有意义
MCP 相关
该技能要求 Agent 通过 MATLAB MCP 服务运行 MATLAB 代码。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-classify-tabular-data安装后在 Claude Code 中输入/matlab-classify-tabular-data确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
manifest.yamlreferences/dataprep.mdhpo.mdREADME.mdsave-and-export.mdselect-classifiers-imbalanced.mdselect-classifiers.mdscripts/helpers/aggregate_nested_cv_loss.mapply_preproc.mbuild_model_definitions.mcompute_data_flags.mcompute_pairwise_pvalues_cv.mcompute_pairwise_pvalues_holdout.mcount_categorical_levels.pexport_workflow_script.mfind_zero_variance_columns.mis_bag_of_tokens.mmake_holdout_split.mplot_accuracy_bars.mplot_boosting_curve.mplot_pvalue_heatmap.mplot_training_time.mresolve_recipe.mresume_boosting_cv.mresume_boosting_holdout.mretrain_with_hpo.mretrain_with_resume.msanitize_model_name.msave_selected_models.mscore_holdout_test.m详细介绍
使用统计不确定性比较分类模型
比较用户数据集上的分类器,并找出准确率在统计上等效的顶级模型。
此技能包含 references/ 中的工作流(按需读取的分步 Markdown 说明)和 scripts/ 中的 MATLAB 代码。在 scripts/ 下,helpers/ 存放 Agent 直接调用的可复用计算和绘图函数,model_catalog/ 存放通过 run(...) 在内部由 build_model_definitions 调用的声明式分支表 .m 文件。请勿将 references/ 中的文件作为独立技能调用——这些文件仅在相应步骤运行时通过 Read 工具加载。有关目录结构,请参阅 references/README.md。
适用场景
- 用户希望对表格数据分类(预测变量矩阵或表格 + 分类响应变量)。
- 用户要求比较多个分类器、选出最佳模型或评估分类器准确率。
- 用户需要对分类器进行交叉验证、留出集评估或超参数优化。
- 用户需要统计检验(McNemar、5×2 cv、Friedman)来判断准确率差异是否显著。
不适用场景
- 响应变量是连续值——改用回归技能。
- 预测变量是图像、序列或时间序列——此技能假设输入为数值矩阵或由标量预测变量构成的表格。
- 用户希望设计或训练特定的神经网络架构——改用
matlab-train-network。此技能确实会将fitcnet作为常规分支数据上的候选模型之一,但不会调整网络层或超参数。 - 用户只希望在新数据上评估预训练模型——此技能用于训练和比较模型;评估现有模型不需要此技能。
- 用户希望进行代价敏感学习或使用自定义类别先验向量。 请明确拒绝并停止——不要尝试变通。此技能唯一支持的先验控制方式,是用于不平衡数据的内置均匀先验切换项(
'Prior','uniform',在步骤 5 中通过UseUniformPrior标志提供)。此技能不支持任意的'Prior',[...]向量和'Cost',C矩阵:模型定义助手和交叉验证/留出集评分助手都不会传递这些参数,分支表和 ECOC 扩展逻辑假设使用内置的先验/代价默认值,而且成对统计检验(testckfold、testcholdout)计算的是误分类率,而不是预期代价。尝试绕过助手注入自定义先验或代价属于必须拒绝的情况,不可自行判断——请告知用户:“此技能不支持自定义类别先验或代价矩阵。如果需要代价敏感学习或特定先验向量,请直接使用fitc*并传入'Prior'或'Cost'名称-值对;在这种情况下,此技能的统计比较工作流无法得出正确结果。”
运行 MATLAB
所有 MATLAB 代码都必须通过 MATLAB MCP 服务运行(脚本可使用 mcp__matlab__evaluate_matlab_code 或 mcp__matlab__run_matlab_file)。将 project_path 设为此技能的 scripts/helpers/ 目录,以便工作流助手在当前工作目录中正确解析,无需调用任何 addpath。
执行此技能时的沟通风格
与用户讨论他们的数据和结果,而不是技能的内部机制。references/ 和 scripts/(包括 helpers/ 和 model_catalog/)下的所有内容都是内部文件——用户查看运行过程时,不应需要询问某个文件名是什么意思。
具体来说,执行此技能时:
- 进度消息中不要提及内部文件或辅助函数。
references/select-classifiers.md、build_model_definitions、compute_pairwise_pvalues_cv、classifier_branches、resolve_recipe、modelDefs、cvFitFcn等都是内部内容。若必须提及(例如,要指出用户可以处理的错误),只需提及一次,并说明它们是什么。 - 不要按名称描述分支分派或筛选决策。“正在分派到宽数据分支”“正在应用类别不平衡叠加项”“
isSparse为 false,因此跳过稀疏分支”——这些都属于内部机制。用户只需要了解结果:“这个数据集很宽(200 个特征、40 个样本),因此我会使用线性模型。” - 不要朗读参考文件。 如果某一步要求停止并阅读
references/foo.md,这是给你的指令,而不是要播报的进度。请默读并继续。 - 在长时间训练循环或 HPO 运行前,务必告知用户他们选择运行的内容以及大致耗时。 用一句话说明。
- 在步骤指定的情况下,原样向用户提出问题(评估策略、可解释性、统一先验、HPO 选择)。这些问题就是技能与用户交互的界面。
- 若出现实际问题,请告知用户——例如检查关卡未通过、分支无法匹配或拟合调用出错。明确说明问题;只有在这种情况下,才可以提及需要修复问题的内部文件。
对比示例:
不要这样说:“正在读取
references/dataprep.md……通过compute_data_flags计算flags……isImbalanced=true,因此正在读取references/select-classifiers-imbalanced.md,并将IMBALANCED_BOOSTING_MODELS叠加到build_model_definitions上。modelDefs现在包含 8 个条目,其中有 RUSBoost-OVO 和 RUSBoost-OVA。”应该这样说:“类别比例为 9:1,因此我会使用能处理类别不平衡的提升模型。开始训练前,我需要询问你类别先验——[统一先验问题原文]。”
判断标准:如果一句话只有读过此技能的源文件的人才能理解,就不要说。
编写代码前
不要重复使用先前分析运行中的任何变量。 每次分析数据集时,都要从头执行完整流程并设置所有变量。每个步骤都必须定义自己的变量——不要假设工作区中保留着之前运行留下的内容。
预先定义 skillPath。 多个辅助函数将 skillPath 作为参数(技能的根目录,即 scripts/helpers/ 向上两级)。通过 evaluate_matlab_code 调用 MATLAB,并将 project_path 设置为此技能的 scripts/helpers/ 文件夹时,MATLAB 的当前工作目录就是 scripts/helpers/,因此 skillPath = fileparts(fileparts(pwd)); 会得到正确的值。在第一个需要它的代码块顶部(步骤 2 或步骤 3)设置此变量,之后沿用相同的值:
skillPath = fileparts(fileparts(pwd)); % skill root; used by build_model_definitions, THRESHOLDS load, imbalanced overlay, export_workflow_script
关于 MATLAB 代码编写的所有规则(使用内置函数、不要检查模板对象、训练时规则、图表规则),请参阅 scripts/README.md。
步骤 1:加载数据
- 加载数据集
- 判断是否提供了单独的测试集(例如,单独的训练表和测试表/矩阵)。
- 如果是:设置
hasHoldout = true,并赋值XTrain、YTrain、XTest、YTest。 - 如果否:从加载的数据中赋值
X、Y。暂时不要拆分数据,也不要询问是否要拆分。
- 如果是:设置
关于输入形状的假设。 如果任何预测变量都是分类变量,X 必须是一个表,其中的分类列存储为 MATLAB categorical(或 string/cellstr,compute_data_flags 也会将其视为分类变量)。矩阵输入默认全部为数值型。如果用户提供的是一组类型混合的独立变量(部分为数值型,部分为分类/字符串类型),请先用 table(...) 将它们组装成一个表再继续——不要将它们拼接成数值矩阵,因为这会静默地将分类变量强制转换为数值代码。本技能不会使用 fitc* 上的 CategoricalPredictors 名称-值对;分类变量完全根据表列的数据类型进行检测。
步骤 2:分析和清理数据
停止。使用 Read 工具读取 references/dataprep.md(相对于本技能的目录)。读取该文件之前,不要编写任何 MATLAB 代码。请严格遵循其中的说明。
如果 hasHoldout = true(数据集附带单独的测试集),则对 XTrain/YTrain 执行 dataprep 说明;否则对 X/Y 执行。返回后,工作区必须包含一个由 compute_data_flags 生成的 flags 结构体,包含以下字段:N、D、nClasses、classSize、smallestClassSize、classRatio、isBinary、isImbalanced、isWide、isHighD、isBig、hasManyMissing、isSparse、hasCategorical。
工作区还必须包含一个 preproc 结构体数组,按应用顺序记录对 X 或 Y 的每次变更。其操作目录——即导出的第 14 步工作流脚本可通过 apply_preproc 重放的仅有四种操作——如下:
.op | 发出的条件 | .payload 字段 |
|---|---|---|
omitColumns | 用户选择删除缺失率高或基数高的特征 | columnNames(表 X)或 columnIndices(矩阵 X) |
dropZeroVariance | 自动删除全局恒定特征 | columnNames(表 X)或 columnIndices(矩阵 X) |
removeClasses | 用户选择删除少数类 | classes(类别标签的元胞数组) |
mergeClasses | 用户选择将少数类合并为一个标签 | map.from(源标签)、map.to(目标标签) |
任何不在此目录中的操作名称都会导致 apply_preproc 在重放时出错——臆造新的操作名称属于错误,而不是扩展方式。
步骤 3:选择评估策略
如果 hasHoldout = true(数据集附带单独的测试集):跳过此步骤。
否则,加载阈值,并使用 flags.smallestClassSize(在步骤 2 中计算)给出建议:
run(fullfile(skillPath, 'scripts', 'model_catalog', 'classifier_thresholds.m')); % populates THRESHOLDS
如果 flags.smallestClassSize > THRESHOLDS.holdout_smallest_class,建议采用 70/30 留出集划分。通过 sprintf 将实际阈值插入提示词中——不要对数字进行硬编码:
此数据集有 [flags.N] 条观测记录,最小类别有 [flags.smallestClassSize] 条观测记录——超过了用于留出集评估的 [THRESHOLDS.holdout_smallest_class] 条观测记录阈值。我建议采用 70/30 分层训练集/测试集划分。这种方式速度更快,并且会使用未见过的数据进行评估。
或者,我也可以使用 5 折交叉验证。交叉验证会同时使用全部数据进行训练和评估,但速度较慢。
你更倾向哪种方式?(holdout / cv)
否则(flags.smallestClassSize <= THRESHOLDS.holdout_smallest_class),建议使用交叉验证:
此数据集有 [flags.N] 条观测记录,最小类别有 [flags.smallestClassSize] 条观测记录——不超过用于可靠留出集划分的 [THRESHOLDS.holdout_smallest_class] 条观测记录阈值。我建议采用 5 折交叉验证。
或者,我也可以使用 70/30 分层留出集划分,但测试集可能较小。
你更倾向哪种方式?(cv / holdout)
等待用户回复。如果用户选择 holdout,则使用 make_holdout_split 创建划分,并设置 hasHoldout = true。如果用户选择 cv,则设置 hasHoldout = false。
之后的所有步骤都使用训练数据(hasHoldout 为真时使用 XTrain/YTrain,否则使用 X/Y)。
步骤 4:询问对可解释性的要求
选择模型前,询问用户:
对于此任务,模型的可解释性有多重要?(low / high)
将回答记录为 interpretability,其值为 'low' 或 'high'。此评级会传递给 select-classifiers 参考内容,用于确定模型优先级:
| 评级 | 含义 |
|---|---|
| high | 预测结果必须能向人解释清楚(例如,受监管领域、临床场景)。优先选择树、判别分析、逻辑回归。 |
| low | 优先考虑准确率。所有模型系列都可作为候选项。 |
步骤 5:选择并定义模型模板
停止。使用 Read 工具读取 references/select-classifiers.md(相对于此技能目录)。在读取该文件之前,不要编写任何 MATLAB 代码。严格按照文件中的说明操作。
匹配的分支列表是完整的。不要添加分支中未列出的模型,即使是为了“多样性”或作为所列模型的相关变体也不行。唯一允许的修改是可解释性筛选、不平衡数据覆盖项,以及用户的明确请求。
如果 isImbalanced = true,还要读取 references/select-classifiers-imbalanced.md,了解 boosting 方法覆盖项和必须执行的均匀先验决策。在交互式会话中,Agent 必须原封不动地向用户提出均匀先验问题,并在任何 fit* 调用之前收到答复。在非交互式会话中(例如自动评估),如果已指示 Agent 不要提问,Agent 仍必须明确说明这一选择:列出两个选项(Prior='uniform' 与 Prior='empirical'),说明默认选择哪一个及其原因,并指出这通常应由用户决定。即使领域(例如欺诈检测、医学筛查)让某个选项看起来“显而易见”,也绝不能在不说明权衡的情况下悄悄采用默认值——这会导致不平衡工作流严重失败。
模型定义由 build_model_definitions 组装。不要从分支表中手动抄写配方——调用辅助函数。它会分派分支、应用所有筛选条件(binaryOnly / condition / interpretability)、解析函数值参数,并将每个 MulticlassECOC=true 配方扩展为两条条目(一条 -OVO,一条 -OVA)。手动编写配方列表会每次都漏掉 OVA 变体——使用辅助函数则不会出现这种情况。
% flags was populated in Step 2 via compute_data_flags(X, Y).
flags.interp = interpretability;
flags.totalCatLevels = count_categorical_levels(X);
% X and Y are only used to build the fitcnet placeholder on pre-R2026b
% MATLAB (regular branch); passing them is always safe.
modelDefs = build_model_definitions(flags, skillPath, 'X', XTrain, 'Y', YTrain);
% Imbalanced case only: read select-classifiers-imbalanced.md first,
% ask the uniform-prior question, then pass the overlay in. The
% 'UseUniformPrior' NV pair — set from the user's answer — bakes
% 'Prior','uniform' into every cvFitFcn (CV path). On the holdout path
% it is passed straight to train_and_score_holdout in Step 6 instead.
% run(fullfile(skillPath, 'scripts', 'model_catalog', 'imbalanced_boosting.m'));
% modelDefs = build_model_definitions(flags, skillPath, ...
% 'X', XTrain, 'Y', YTrain, ...
% 'ImbalancedOverlay', IMBALANCED_BOOSTING_MODELS, ...
% 'UseUniformPrior', useUniformPrior); % CV path only
modelDefs 是一个结构体数组。每个条目都有 .name、.template、.cvFitFcn、.fitFcn、.fnName、.hyperparameters、.innerLearner_fnName、.innerLearner_hyperparameters。.cvFitFcn 是 (X, Y, cv) -> cv model 闭包,由步骤 6 的 CV 路径使用;.fitFcn 是 (X, Y) -> trained model 闭包,由步骤 13 的 save_selected_models 使用,用于从 CV 封装模型生成可部署模型。多类别 ECOC 配方会出现两次:LinearSVM-OVO 和 LinearSVM-OVA、LogisticRegression-OVO 和 LogisticRegression-OVA 等。每个条目都要训练。
**步骤 6 中绝不能在任何位置编写 fitc* 或 fitensemble 调用。**正确的拟合签名——现代 fitcensemble 名称-值形式、将 'Learners' 设为内部模板的 fitcecoc,以及其他情况的 fitc*(X, Y, hyperparams...)——都已内置在 modelDefs(k).cvFitFcn(CV 路径)和 modelDefs(k).template(留出集路径)中。如果以下任何步骤看起来要求你自行编写 fitc* 调用,那就是此技能文件中的错误——请指出问题,不要自行变通。fitensemble(不带“c”)已弃用;Learners = templateEnsemble(...) 是错误的;fitcensemble('Method', ..., 'NumLearningCycles', ..., 'Learners', innerTmpl) 才是正确的——辅助函数已经生成了正确形式。
稀疏分支的 NaiveBayesMN 门控
如果匹配的分支是 sparse,NaiveBayesMN 始终会出现在 modelDefs 中。检查数据是否为非负整数词袋矩阵;如果是,询问用户是否要包含多项式 NB 模型。如果检查不通过或用户回答否,则移除该条目:
if is_bag_of_tokens(XTrain)
% Ask the user (yes/no); assume useNaiveBayesMN carries the answer.
else
useNaiveBayesMN = false;
end
if ~useNaiveBayesMN
modelDefs = modelDefs(~strcmp({modelDefs.name}, 'NaiveBayesMN'));
end
CV 路径(hasHoldout = false)——使用内置的 cvFitFcn
build_model_definitions 会将 modelDefs(k).cvFitFcn 设置为 (X, Y, cv) -> cv model 句柄,该句柄已针对配方封装了正确的 fitc* 签名(包括集成模型的现代 fitcensemble 名称-值形式,以及 ECOC 的正确 Learners/Coding)。不要手动重建此句柄——将其直接传递给 train_and_score_cv:
[cvModel, trainTime, acc, accCI, foldAcc] = ...
train_and_score_cv(modelDefs(k).cvFitFcn, X, Y, cvp);
留出集路径(hasHoldout = true)
将 .template 直接传递给 train_and_score_holdout(template, XTrain, YTrain, XTest, YTest)。无需 CV 封装。
步骤 6:获取基线准确率估计值
使用 tic/toc 记录每个模型的训练时间。对于 CV,计时 cvFitFcn 调用。对于留出法,计时 fit 调用。
静态检查门禁(强制,在任何训练调用之前)
在此步骤中执行任何 MATLAB 代码之前,先将该代码保存到 .m 文件中,并对其运行 mcp__matlab__check_matlab_code。然后在同一份源代码中搜索以下四种禁用模式;如果文件中任何位置出现其中之一,立即中止,不要执行:
| 禁用的令牌 / 模式 | 错误原因 |
|---|---|
fitensemble((没有“c”) | 已弃用的 API。请使用 modelDefs(k).cvFitFcn 或 modelDefs(k).template——两者都会为你封装现代的 fitcensemble 调用。 |
'Learners', templateEnsemble(任意大小写 / 空白形式) | 集成模板不能用作 ECOC 学习器。若要训练集成模型,请使用 fit(...) 调用集成模板,或者(更好的做法)使用 modelDefs(k).cvFitFcn / .template。 |
在步骤 6 中手动输入的 fitcensemble( | build_model_definitions.m 中的辅助函数已在 cvFitFcn 闭包中生成 fitcensemble(X, Y, 'Method', ..., 'NumLearningCycles', ..., 'Learners', innerTmpl)。手动输入会在十次中有九次重新引入已弃用的位置参数签名。 |
在步骤 6 中手动输入的任何其他 fitc*( | 原因相同:modelDefs(k).cvFitFcn 和 train_and_score_holdout(modelDefs(k).template, ...) 是此步骤中仅获准用于训练的入口。 |
如果检查门禁标记出上述任意一种情况,正确的做法不是重写拟合调用,而是将其替换为 train_and_score_cv(modelDefs(k).cvFitFcn, X, Y, cvp)(CV 路径)或 train_and_score_holdout(modelDefs(k).template, XTrain, YTrain, XTest, YTest, useUniformPrior)(留出法路径)。步骤 6 的训练循环中只能出现这两个调用。
不平衡数据的均匀先验
如果 useUniformPrior = true:
- **CV 路径:**在步骤 5 中向
build_model_definitions传入'UseUniformPrior', true。随后每个modelDefs(k).cvFitFcn都会在其拟合调用中追加'Prior','uniform'。请勿自行修改cvFitFcn——该闭包已经存在,此标志是唯一获支持的入口。 - **留出法路径:**将
useUniformPrior作为第 6 个参数传给train_and_score_holdout。
CV 路径(hasHoldout = false)
对每个模型调用 train_and_score_cv。该函数会运行模型的 cvFitFcn,并返回 cvModel、trainTime、acc、accCI(由 binofit 计算的 95% CI)和 perFoldAcc——每个模型按折划分的准确率向量(nFolds × 1)。
按如下方式在循环中汇总各模型的结果:
nModels = numel(modelDefs);
nFolds = cvp.NumTestSets;
trainTime = zeros(1, nModels);
acc = zeros(1, nModels);
accCI = zeros(nModels, 2);
foldAcc = zeros(nFolds, nModels); % preallocated as an nFolds × nModels MATRIX
cvModels = cell(1, nModels);
for k = 1:nModels
[cvModels{k}, trainTime(k), acc(k), accCI(k,:), perFoldK] = ...
train_and_score_cv(modelDefs(k).cvFitFcn, X, Y, cvp);
foldAcc(:, k) = perFoldK; % one column per model — REQUIRED for Step 7 friedman()
end
nFolds × nModels 的形状正是步骤 7 中 friedman(foldAcc, 1, 'off') 所需的格式——折是应用于每个主体-模型组合内的处理。请勿将 perFoldAcc 收集到元胞数组中,也不要沿错误的维度拼接。
报告包含 95% 置信区间的交叉验证准确率表。
留出法路径(hasHoldout = true)
对每个模型调用 train_and_score_holdout。该函数会处理 ClassificationNeuralNetwork 的例外情况(R2026a 及更早版本中,“模板”实际上是已训练模型),以及可选的 'Prior','uniform' 标志。它会返回已训练的 model、trainTime、acc、accCI 和 YHat(测试集上的预测向量)。
按如下方式在循环中汇总各模型的结果:
nModels = numel(modelDefs);
trainTime = zeros(1, nModels);
acc = zeros(1, nModels);
accCI = zeros(nModels, 2);
YHat = cell(1, nModels); % cell array — each entry is one model's prediction vector
trainedModels = cell(1, nModels);
for k = 1:nModels
[trainedModels{k}, trainTime(k), acc(k), accCI(k,:), YHat{k}] = ...
train_and_score_holdout(modelDefs(k).template, XTrain, YTrain, XTest, YTest, useUniformPrior);
end
YHat 必须是由预测向量构成的元胞数组——compute_pairwise_pvalues_holdout(步骤 8)会通过 YHat{i} 对其进行索引。请勿将预测堆叠成数值矩阵;类别标签是 categorical / string / cellstr 类型,在使用 [YHat{:}] 一类的方式拼接时会被强制转换为数值编码。
步骤 7:选择统计比较方法
-
CV 路径(
hasHoldout = false):先对foldAcc运行 Friedman 总体检验,然后在步骤 8 中进行成对testckfold检验。不要在 Friedman 检验后使用multcompare——成对比较由testckfold执行。[pFriedman, tbl, stats] = friedman(foldAcc, 1, 'off'); -
Holdout 路径(
hasHoldout = true):跳过 Friedman 检验(不存在折),直接在步骤 8 中进行成对testcholdout检验。
步骤 8:运行比较
- 对所有模型对进行成对比较(不只是与最佳模型比较),并将 p 值存储在对称矩阵中
- 应用 Bonferroni 校正:
alpha_corrected = 0.05 / nchoosek(nModels, 2) - 确定最高梯队:与最佳模型相比,经过 Bonferroni 校正后差异不显著的模型
- 将完整的成对 p 值矩阵报告为表格
不要手写成对循环——请针对当前路径调用随附的辅助函数。两个辅助函数都会将对角线设为 pvalMatrix(i,i) = 1(自身比较的 p 值),因此下游代码(最高梯队筛选、plot_pvalue_heatmap)无需针对 NaN 特殊处理即可正常工作。
CV 路径(hasHoldout = false)
内部使用 testckfold(5×2 F 检验):
[pvalMatrix, hMatrix] = compute_pairwise_pvalues_cv(modelDefs, X, Y, alpha_corrected);
Holdout 路径(hasHoldout = true)
对步骤 6 中保存的预测使用 testcholdout(McNemar 检验)。testcholdout 和 testckfold 的函数签名完全不同:testcholdout 不接受模板、训练好的模型或数据矩阵——它只比较两个预测标签向量与真实标签。传入 YHat(在步骤 6 中通过 predict 保存的预测向量元胞数组)和 YTest:
[pvalMatrix, hMatrix] = compute_pairwise_pvalues_holdout(YHat, YTest, alpha_corrected);
可视化成对 p 值(始终执行——CV 和 holdout 路径均适用)
无论使用哪条路径,都始终将成对 p 值矩阵显示为热图。此图在步骤 8 中生成,而不是步骤 9。请调用随附脚本:
% CV path:
plot_pvalue_heatmap(pvalMatrix, modelNames, sprintf('Pairwise testckfold p-values (Friedman p = %.4f)', pFriedman));
% Holdout path:
plot_pvalue_heatmap(pvalMatrix, modelNames, 'Pairwise testcholdout p-values (McNemar)');
该脚本位于此技能目录下的 scripts/helpers/plot_pvalue_heatmap.m。与其他所有辅助函数一样,它会通过当前工作文件夹解析路径——不要修改 MATLAB 路径。
步骤 9:可视化
调用 plot_accuracy_bars 和 plot_training_time 显示两张图。两者都会按准确率降序排列模型,并突出显示最高梯队的模型。
plot_accuracy_bars(modelNames, acc, accCI, topTierIdx, ...
sprintf('Accuracy with 95%% CI (path: %s)', evalPath));
plot_training_time(modelNames, trainTime, acc, topTierIdx);
步骤 10:提升学习曲线
针对每个提升模型(RUSBoost、AdaBoost、LogitBoost),绘制其学习曲线,并询问用户是否要继续训练。
首先,确定 modelDefs 中哪些条目是提升模型。提升模型是指 fnName == 'fitcensemble',且 hyperparameters.Method 是 RUSBoost、LogitBoost、AdaBoostM1、AdaBoostM2、GentleBoost 之一——不包括 Bag 或 Subspace。遍历这些 boostIdx 值;下面的每段代码片段都使用 boostIdx 作为当前模型在各模型数组(acc、accCI、foldAcc、trainTime)中的索引。
boostMethods = {'RUSBoost','LogitBoost','AdaBoostM1','AdaBoostM2','GentleBoost'};
isBoost = false(1, numel(modelDefs));
for k = 1:numel(modelDefs)
hp = modelDefs(k).hyperparameters;
isBoost(k) = strcmp(modelDefs(k).fnName, 'fitcensemble') ...
&& isfield(hp, 'Method') && ismember(hp.Method, boostMethods);
end
boostIndices = find(isBoost);
使用循环 for boostIdx = boostIndices,然后调用 plot_boosting_curve:
boostModelName = modelDefs(boostIdx).name;
% Holdout path: pass the trained model and test set
plot_boosting_curve(trainedModels{boostIdx}, boostModelName, true, XTest, YTest);
% CV path: pass the cross-validated model; XTest/YTest unused
plot_boosting_curve(cvModels{boostIdx}, boostModelName, false, [], []);
显示图表后,询问用户:
[modelName] 的学习曲线显示了树数量对应的准确率。请检查曲线。
是否要增加树的数量继续训练?如果要,请问要增加多少棵树?(no / number)
如果用户提供一个数字,请调用与当前路径对应的恢复脚本,并更新各模型的结果:
% Holdout path
[trainedModels{boostIdx}, resumeTime, acc(boostIdx), accCI(boostIdx,:), YHat{boostIdx}] = ...
resume_boosting_holdout(trainedModels{boostIdx}, nMoreTrees, XTest, YTest);
trainTime(boostIdx) = trainTime(boostIdx) + resumeTime;
% CV path
[cvModels{boostIdx}, resumeTime, acc(boostIdx), accCI(boostIdx,:), foldAcc(:, boostIdx), oofPreds] = ...
resume_boosting_cv(cvModels{boostIdx}, nMoreTrees);
trainTime(boostIdx) = trainTime(boostIdx) + resumeTime;
每次恢复训练后,报告该提升模型的最终平均准确率和累计训练时间(恢复前 + 恢复训练)。
完成所有恢复训练后,针对最终提升模型重新运行步骤 8 中的统计比较,并报告结果。
每次恢复训练后,都始终再次询问用户是否要增加树的数量继续训练——无论模型准确率是否仍远低于最高梯队。你可以指出剩余差距,但是否停止由用户决定。绝不要擅自断定继续训练已无意义。
步骤 11:超参数优化(可选)
首先,询问用户是否要运行 HPO;如果要运行,询问要在哪些模型上运行。将顶级模型列表和其余模型作为普通聊天文本呈现(与步骤 5 中相同的两步模式:先列出,再提问)。说明 CV 路径上的 HPO 使用嵌套交叉验证,可能会很慢。
如果用户拒绝(例如“都不选”“跳过”),直接继续步骤 12——不要读取 references/hpo.md。
如果用户想在一个或多个模型上运行 HPO:
停止。使用 Read 工具读取 references/hpo.md(相对于此技能所在目录)。在读取该文件之前,不要编写任何 MATLAB 代码。严格按照文件中的说明操作。
传入顶级模型列表、基线准确率、用户选定的模型以及所有相关的训练/评估数据。
步骤 12:摘要
报告:
- 选择的统计检验及原因
- 统计上等效的顶级模型
- 平均准确率最高的模型(即使其表现并无显著优势)
提供每个已训练模型的准确率和 95% 置信区间,让用户有足够的信息来决定在步骤 13 中保存哪些模型。
**指标命名保持一致:**选择准确率或错误率中的一种,并在整个摘要中保持一致。不要混用——绝不要将准确率数值称为“错误率”,也不要将错误率数值称为“准确率”。如果你说“平均错误率最低”,报告的数值必须是错误率(例如 5.1%);如果你说“平均准确率最高”,数值必须是准确率(例如 94.9%)。置信区间也遵循相同规则。
步骤 13 和 14:保存模型并导出重训练脚本(可选)
步骤 13(通过 save_selected_models 将选定的已训练模型保存到 .mat 文件)和步骤 14(通过 export_workflow_script 输出独立的重训练 .m 脚本)由同一个参考文件统一说明。这两个步骤都是可选的,且必须按此顺序提供——用户对“要保存哪些模型?”的回答取决于步骤 12 中提供的逐模型准确率表。
停止。使用 Read 工具读取 references/save-and-export.md(相对于此技能所在目录)。在读取该文件之前,不要为这些步骤编写任何 MATLAB 代码。严格按照文件中的说明操作。
如果用户拒绝保存任何模型,则工作流结束——不提供步骤 14。
行为准则
这些准则适用于面向用户的摘要和建议,不适用于 MATLAB 代码(代码层面的规则见 scripts/README.md)。
不要:
- 对模型属性(过拟合风险、鲁棒性)做出缺乏实际结果支持的泛泛教科书式断言
- 仅凭准确率就推荐某个模型用于生产环境
随附文件
参考资料(按需加载)
references/README.md—— 说明references/、scripts/helpers/和scripts/model_catalog/如何划分职责references/dataprep.md—— 分析并清理数据,计算特征标记(步骤 2)references/select-classifiers.md—— 根据数据集特征和可解释性评级选择有前景的模型(步骤 5)references/select-classifiers-imbalanced.md—— 针对不平衡数据的 boosting/均匀先验覆盖规则(从步骤 5 读取,条件为isImbalanced)references/hpo.md—— 对用户选定的模型进行超参数优化(步骤 11)references/save-and-export.md—— 将选定的已训练模型保存到.mat文件,并输出独立的重训练.m脚本(步骤 13 和 14)
模型目录(scripts/model_catalog/,通过 run(...) 调用链解析)
classifier_branches.m— 通过run(...)组装BRANCHES结构体数组(稀疏 / 缺失值较多 / 分类 / 宽型 / 常规);由build_model_definitions调用。Agent 不会直接调用。classifier_thresholds.m— 各分支表共用的声明式阈值(填充THRESHOLDS)。Agent 不会直接调用。branch_sparse.m、branch_many_missing.m、branch_categorical.m、branch_wide.m、branch_regular.m— 每个分支各占一个文件;每个文件通过model_recipe(...)将模型追加到BRANCHES。不会直接调用。imbalanced_boosting.m— 在isImbalanced为真时应用的 boosting 配方覆盖层;填充IMBALANCED_BOOSTING_MODELS。不会直接调用。model_recipe.m— 仅在分支.m文件中使用的配方构造函数。与分支文件放在同一目录,以便run(...)调用链解析它。
辅助脚本(scripts/helpers/,由 Agent 调用)
scripts/README.md— 代码级规则(使用内置功能、不要检查模板、训练时规则、图形规则)scripts/helpers/build_model_definitions.m— 根据flags和分支表组装modelDefs结构体数组;处理分支分派、配方筛选器、resolve_recipe和 OVO/OVA 展开。在 Step 5 中调用,并将project_path设为scripts/helpers/。scripts/helpers/resolve_recipe.m— 使用flags将函数形式的配方参数解析为具体标量。由build_model_definitions调用。scripts/helpers/find_zero_variance_columns.m— 稀疏安全的零方差列检测(由references/dataprep.md使用)scripts/helpers/compute_data_flags.m— 计算 Step 5 使用的数据集特征标志(由references/dataprep.md使用)scripts/helpers/is_bag_of_tokens.m— 稀疏安全地检查值是否为非负整数(由references/select-classifiers.md使用)count_categorical_levels— 计算表中的类别总数(由references/select-classifiers.md使用;采用scripts/.pcode中的 p-code)stratified_subsample— 按类别设置上限且保留类别比例的子样本(由references/select-classifiers.md使用;采用scripts/.pcode中的 p-code)scripts/helpers/make_holdout_split.m— 分层训练/测试集划分(Step 3)scripts/helpers/train_and_score_cv.m— CV 训练、准确率置信区间、各折准确率(Step 6,CV 路径)scripts/helpers/train_and_score_holdout.m— 留出集训练、准确率置信区间、预测;处理ClassificationNeuralNetwork特例(Step 6,留出集路径)scripts/helpers/compute_pairwise_pvalues_cv.m— 成对testckfoldp 值矩阵,对角线预置为 1(Step 8,CV 路径)scripts/helpers/compute_pairwise_pvalues_holdout.m— 成对testcholdoutp 值矩阵,对角线预置为 1(Step 8,留出集路径)scripts/helpers/plot_pvalue_heatmap.m— 成对 p 值热图(Step 8)scripts/helpers/plot_accuracy_bars.m— 带 95% 置信区间的准确率条形图(Step 9)scripts/helpers/plot_training_time.m— 训练时间条形图(Step 9)scripts/helpers/plot_boosting_curve.m— 提升学习曲线,支持留出集或 CV(Step 10)scripts/helpers/resume_boosting_holdout.m— 在留出集路径上为集成模型增加树并继续训练;重新计算准确率和预测(Step 10)scripts/helpers/resume_boosting_cv.m— 在 CV 路径上为交叉验证集成模型增加树并继续训练;通过kfoldLoss重新计算准确率和各折准确率(Step 10)scripts/helpers/score_holdout_test.m— 在留出测试集上计算最终准确率和 95% 置信区间(由references/hpo.md使用)scripts/helpers/aggregate_nested_cv_loss.m— 将折外预测汇总为准确率和 95% 置信区间,可选择使用均匀先验加权(由references/hpo.md使用)scripts/helpers/sanitize_model_name.m— 将模型显示名称(例如LinearSVM-OVO)转换为有效的 MATLAB 标识符(LinearSVM_OVO)。由save_selected_models和export_workflow_script生成的重新训练脚本共用,以确保两侧采用相同的清理方式。scripts/helpers/save_selected_models.m— 将用户选定的已训练模型保存到.mat文件;通过modelDefs(k).fitFcn在完整数据上重新训练 CV 包装模型,确保保存的模型可部署(Step 13)scripts/helpers/apply_preproc.m— 将记录的preproc操作应用于新数据(由导出的工作流脚本使用;Step 14)scripts/helpers/retrain_with_hpo.m— 在完整训练集上执行单次 HPO(由导出的工作流脚本使用;Step 14)scripts/helpers/retrain_with_resume.m— 重新训练集成模型,并将额外学习轮次写入NumLearningCycles(由导出的工作流脚本使用;Step 14)scripts/helpers/export_workflow_script.m— 生成自包含的重新训练工作流.m脚本(Step 14)
版权所有 2026 The MathWorks, Inc.