mcpField
M
matlab-classify-tabular-data

matlab-classify-tabular-data

github.com/matlab数据与数据库1.2k41 次安装1 天前更新
代码仓库

关于 matlab-classify-tabular-data

此技能指导使用 MATLAB 比较表格数据集上的分类器,帮助用户识别准确率在统计上相当的模型层级。它通过规定的工作流涵盖数据准备、评估策略、模型选择和统计检验。

主要功能

  1. 01比较表格数据的分类器
  2. 02使用交叉验证或留出法评估
  3. 03依据数据集特征选择模型
  4. 04检验准确率差异是否显著
  5. 05导出可重放的分析工作流

适用场景

  1. 01比较带标签表格上的分类器
  2. 02为数据集选择评估方法
  3. 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.yaml
references/
dataprep.md
hpo.md
README.md
save-and-export.md
select-classifiers-imbalanced.md
select-classifiers.md
scripts/helpers/
aggregate_nested_cv_loss.m
apply_preproc.m
build_model_definitions.m
compute_data_flags.m
compute_pairwise_pvalues_cv.m
compute_pairwise_pvalues_holdout.m
count_categorical_levels.p
export_workflow_script.m
find_zero_variance_columns.m
is_bag_of_tokens.m
make_holdout_split.m
plot_accuracy_bars.m
plot_boosting_curve.m
plot_pvalue_heatmap.m
plot_training_time.m
resolve_recipe.m
resume_boosting_cv.m
resume_boosting_holdout.m
retrain_with_hpo.m
retrain_with_resume.m
sanitize_model_name.m
save_selected_models.m
score_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 — 成对 testckfold p 值矩阵,对角线预置为 1(Step 8,CV 路径)
  • scripts/helpers/compute_pairwise_pvalues_holdout.m — 成对 testcholdout p 值矩阵,对角线预置为 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.

评论

支持 Markdown