☰
MATLAB模式识别算法实战:从特征工程到分类器与降维评估
2026/9/27 6:28:42 网站建设 项目流程

简介:这是《模式识别与智能计算——MATLAB技术实现》第3版的完整PDF,融合统计学、机器学习、神经网络、数据挖掘与群体智能等思想,聚焦模式识别算法从理论到MATLAB落地的全过程,适合高校师生及相关工程技术人员参考。全书共14章,覆盖特征选择与优化、相似性测度、贝叶斯分类器、判别函数,以及BP、径向基、自组织竞争、概率、对向传播等神经网络分类器和决策树、粗糙集设计;聚类部分包括基本聚类、模糊聚类、禁忌搜索、遗传算法、蚁群与粒子群聚类分析,体系非常完整。书中以手写数字识别为实例,给出实现步骤、核心MATLAB代码及多种算法的效果对比,便于读者快速将理论转化为可运行程序,实践参考价值突出。资源为单个PDF文件,大小约17.87MB,已有311人学习下载,适合作为研究生教材、教学参考书或算法实践案头资料。

1. 模式识别算法MATLAB实现:不是调包,是想清楚决策面在哪儿

当你手里已经有一套带标签的样本,目标只是让模型学会区分新样本的类别时,模式识别算法MATLAB实现比大多数人想象中要直接得多。它不等于你非得从零推导二次判别函数的解析解,而是指一条能复现、能调试、能交付的完整路径:从特征矩阵出发,选一个合适的分类器,通过交叉验证调参,最后用测试集给效果下结论。我见过实验室同学一上来就打算用PyTorch做图像分类,结果导师给的基线代码全是MATLAB脚本,特征也已经是提取好的数值矩阵——这时候MATLAB的统计机器学习工具箱反而是最快的。这条路径适合两类人:一类是要快速出基线结果的研究生,另一类是在工业现场拿到带标签数据、想把识别逻辑做成可交付模块的工程师。下文所有代码都围绕“特征矩阵 + 分类器 + 交叉验证”这条主线展开,每段都能直接抄走。

2. 数据准备与特征矩阵:模式识别算法在MATLAB里跑起来的第一步

2.1 加载内置数据集与自定义数据:从iris到CSV文件

我习惯用fisheriris讲通全流程,因为特征矩阵和标签都是内置的,能排除读取格式带来的干扰。但如果要落地到实际项目,你手上更多是散落的CSV或Excel文件,所以这一节把两种入口都覆盖到。

% ===== 方式一:加载MATLAB内置鸢尾花数据集 ===== % meas 是150x4的数值矩阵,species 是150x1的分类变量 load fisheriris; % 把分类变量转成数值标签,便于后续算准确率、画混淆矩阵 Y = double(categorical(species)); % 类别1/2/3 % ===== 方式二:从CSV加载你自己的数据 ===== % data = readtable('your_data.csv'); % X = data{:, 1:end-1}; % 前若干列是特征 % Y = data{:, end}; % 最后一列是标签 % 固定随机种子,让数据划分可复现 rng(0); % 按3/7比例划分训练集与测试集 cv = cvpartition(Y, 'HoldOut', 0.3); idxTrain = training(cv); idxTest = test(cv); X_train = meas(idxTrain, :); Y_train = Y(idxTrain); X_test = meas(idxTest, :); Y_test = Y(idxTest);

最后三行是关键取数操作。cvpartition返回一个cvpartition对象,training和test两个方法分别返回逻辑索引向量,直接用矩阵下标就能把训练集和测试集分开。这样做的价值在于:训练集和测试集完全不重叠,且每次运行结果一致,不会出现“这次跑90分,下次跑70分”的复现困境。

参数说明:HoldOut后面的0.3表示30%样本留作测试,剩下70%参与训练;rng(0)不是随便写的,它把随机数生成器重置到固定状态,保证cvpartition每次切分结果一致。如果你把rng这行删掉,每次运行划分都会变,调参时根本无法判断效果变化来自参数还是来自数据划分。

2.2 归一化与数据划分:为什么训练集和测试集要分开处理

所有基于距离的分类器(KNN、SVM的某些核函数)都默认每个特征等权。如果特征A的量纲是0.01到1,特征B的量纲是1000到5000,距离计算会被B完全主导,特征A等同白给。归一化是解决这个问题的常规手段,但放在什么位置,直接决定你的评估结果是不是虚高。

% ===== 错误示范:先合并所有数据做zscore,再划分 ===== % zscore操作了包含测试集在内的全部数据,统计量已经泄露 % ===== 推荐写法:只在训练集上估计mu和sigma ===== mu = mean(X_train, 1); sigma = std(X_train, 0, 1); X_train_norm = (X_train - mu) ./ sigma; X_test_norm = (X_test - mu) ./ sigma;

每一行都不能省略:mean(X_train, 1)是按列求均值,std(X_train, 0, 1)是按列求标准差,第二个参数0表示除以n-1而不是n。先算统计量再用它变换测试集,是为了保证测试集在训练阶段不被“看见”。如果你图省事,拿全量数据跑zscore(meas)再划分,测试集的分布信息已经通过统计量进入缩放过程,分类器在测试集上的表现会系统性偏高,这个偏差在小样本场景下足以让你把一个不合适的模型误判为可用。

还有一类特征是类别型或二值型,比如性别、是否故障,这类特征不适合做zscore,保留0/1本身更合理。混合类型数据可以只对连续特征归一化,离散特征保持原值,不要把两者混在一个矩阵里统一处理。

2.3 用散点矩阵图和分类散点图先探一探数据的可分性

在跑任何分类器之前,先画图,这一步能省掉大量无用功。模式识别算法的第一步从来不是选模型,而是确认数据在特征空间里到底长什么样。

figure; gplotmatrix(meas, [], species, ['krb'], [], [], 'on', [], [], []); title('原始特征两两散点图');

gplotmatrix会把4个特征两两组合铺开成4x4的子图,每张子图展示某两个特征维度下的类别分布,['krb']指定三种颜色,'on'开启对角线上的直方图。我通常先花三分钟看这张图:如果某个组合里三个类别的点已经明显分成几团,说明线性分类器就有机会;如果所有子图都糊成一坨,说明原始特征的可分性不好,这时候应该先考虑特征降维或换特征,而不是盲目上复杂分类器。

iris数据集在花瓣长度和花瓣宽度两个维度上天然可分,所以后续每个分类器都能跑出不错的结果。真正容易翻车的是那些高维稀疏特征,比如文本TF-IDF矩阵或one-hot编码的类别特征,这种数据画散点图几乎永远看不出规律,需要靠降维手段把结构压缩到可视空间。下一节先落三个最常用的分类器,然后再讲降维怎么跟它们串联。

3. 实现三种经典分类器:贝叶斯、KNN、SVM的MATLAB代码与参数

3.1 朴素贝叶斯分类器:fitcnb的分布假设与后验概率输出

朴素贝叶斯的工作机制是假设特征在类别给定的条件下相互独立,然后通过贝叶斯公式计算后验概率。它在特征维数高但样本量不大的场景下往往比复杂模型更稳,因为它要估计的参数少,不容易过拟合。MATLAB里一句话就能建好模型:

% 训练朴素贝叶斯分类器 mdlNB = fitcnb(X_train_norm, Y_train, ... 'DistributionNames', 'kernel', ... % 用核密度估计,不假设高斯分布 'Prior', 'empirical'); % 先验概率按训练集各类别比例计算 % 预测并输出后验概率 [predNB, posterior] = predict(mdlNB, X_test_norm); % 计算准确率 accNB = sum(predNB == Y_test) / numel(Y_test); fprintf('朴素贝叶斯准确率: %.2f%%\n', accNB * 100);

DistributionNames有'normal'和'kernel'两个常用选项。默认是'normal',假设每个特征在各类别内服从高斯分布;如果你的数据分布明显不对称或有多峰,'kernel'会用核密度估计去拟合,效果通常更好但计算量也更大。Prior默认就是'empirical',即按训练集中各类别占比作为先验概率。

这里有一个值得注意的细节:predict返回的第二输出就是后验概率,你不仅能拿到类别标签,还能看到模型有多“确信”这个判断。实际项目里我常常把后验概率低于0.6的样本单独挑出来,归为“不确定类”,交给人工复核。比直接输出一个标签更有工程价值。

朴素贝叶斯不是黑匣子,它的决策边界可以被清晰地表达为各类别分布的交界处。如果某个特征维度在训练集上只有一种取值,fitcnb会报“类内方差为零”的警告,解决办法是在该维度上加一点点噪声,或者直接用'DistributionNames', 'kernel'回避高斯假设。

3.2 KNN最近邻:fitcknn的K值选择与KDTree切换

KNN的核心思路直接到近乎朴素:新样本的类别由特征空间里距离它最近的K个已知样本投票决定。没有显式训练过程,树结构本身就是为了加速查找。MATLAB实现如下:

% 训练KNN分类器(K=5,欧氏距离) mdlKNN = fitcknn(X_train_norm, Y_train, ... 'NumNeighbors', 5, ... 'Distance', 'euclidean', ... 'Standardize', false); % 注意:数据已经手动归一化,这里不能重复标准化 % 做5折交叉验证,评估K=5时的泛化能力 cvmdlKNN = crossval(mdlKNN, 'KFold', 5); knnLoss = kfoldLoss(cvmdlKNN); fprintf('KNN 5折交叉验证误差: %.2f%%\n', knnLoss * 100); % 用测试集最终验证 predKNN = predict(mdlKNN, X_test_norm); accKNN = sum(predKNN == Y_test) / numel(Y_test); fprintf('KNN测试集准确率: %.2f%%\n', accKNN * 100);

NumNeighbors是K值,Distance可选'euclidean'、'cityblock'、'chebychev'等。K值的选择没有理论最优解,3到10之间比较常见,K越小决策边界越碎、越容易过拟合,K越大边界越平滑、但可能把邻近的异类样本也卷进来。Standardize参数一旦设了true,fitcknn会在训练前自动做标准化,但我们前面已经手动归一化过,这里必须设false,否则等于做了两次尺度变换。

crossval加kfoldLoss是我最常用的快速评估组合。'KFold', 5表示把训练集再切成5份,轮流拿4份训练、1份验证,最后返回平均误差。这一步比直接跳去测试集靠谱得多,因为调参过程中反复使用测试集会让你在测试集上“记住答案”。

KNN在样本量超过几万条时会遇到性能瓶颈,默认的搜索方法会退化成穷举。此时可以显式指定搜索方法:'NSMethod', 'kdtree',但KDTree在高维数据下效率会急剧下降,一般超过20维我宁愿用'exhaustive'配合'Distance'设为快速计算的度量,或者干脆换SVM。

3.3 支持向量机:fitcsvm的核函数、BoxConstraint与多分类扩展

SVM找的是让两类样本间隔最大的超平面,在特征维数远大于样本数的时候尤其抗过拟合。fitcsvm本身只支持二分类,多分类要用fitcecoc包装成一对其余(one-vs-all)的组合。下面是完整的二分类及多分类实现:

% ===== 二分类示例:把iris的类别1设为正类,其余为负类 ===== Y_bin = double(Y_train == 1); % 类别1为1,类别2/3为0 mdlSVM = fitcsvm(X_train_norm, Y_bin, ... 'KernelFunction', 'rbf', ... % 高斯核,处理非线性边界 'BoxConstraint', 1, ... % 松弛变量惩罚系数 'KernelScale', 'auto'); % 核宽度由算法自动估计 predSVM1 = predict(mdlSVM, X_test_norm); accSVM1 = sum(predSVM1 == double(Y_test == 1)) / numel(Y_test); fprintf('二分类SVM准确率: %.2f%%\n', accSVM1 * 100); % ===== 多分类扩展:fitcecoc自动做一对余组合 ===== mdlSVM = fitcecoc(X_train_norm, Y_train, ... 'Learners', templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', 1, ... 'KernelScale', 'auto')); predSVM = predict(mdlSVM, X_test_norm); accSVM = sum(predSVM == Y_test) / numel(Y_test); fprintf('多分类SVM准确率: %.2f%%\n', accSVM * 100);

BoxConstraint是误分类样本的惩罚力度,值越大边界越硬,越容易过拟合;值越小边界越软,容忍更多误分类但泛化能力可能更好。我一般在小数据集上从0.1开始调,然后1、10、100各跑一遍,看交叉验证误差变化趋势。KernelScale设'auto'时算法会根据样本之间的距离自动估计高斯核的宽度,省去手动试探不同σ的麻烦。

fitcecoc的'Learners'必须传一个模板而不是直接传fitcsvm对象,templateSVM就是干这个事的。iris是三分类,fitcecoc会创建3个二分类器,每个负责区分“这一类”和“其余两类”,最后通过投票决定预测标签。多分类的准确率比二分类略低是正常现象,因为每个二分类器都面临类别不平衡的问题。如果你自己构造训练集,尽量保证每个类别样本数量接近。

核函数的选择上,'linear'适合特征维数高、样本量大的场景,训练快且可解释性强;'rbf'适合特征维数中等、边界非线性的场景,但它需要调的参数多一个,训练时间也长。只从准确率上看不到可解释性差异,但如果后续需要把模型部署到生产环境,线性核的权重向量可以直接导出作为特征重要性参考,RBF核就只能当黑匣子用。

4. 特征降维与可视化:PCA和LDA怎么选、怎么串联分类器

4.1 主成分分析PCA:累计方差贡献率与score矩阵的含义

特征维数太高会让KNN这类基于距离的分类器效果滑坡,也会让SVM训练时间暴涨。PCA是应用最广的无监督降维方法,它只考虑数据本身的方差结构,完全不看标签,找到的方向是“数据方差最大”的主成分方向。

% 在训练集上做主成分分析 % 注意:只在训练集上估计主成分方向,再变换测试集 [coeff, score, latent, ~, explained] = pca(X_train_norm); % 查看累计方差贡献率,决定保留几个主成分 cumsum(explained) % 按累计方差>95%选取维数,通常前2-3个主成分就够 X_train_pca = score(:, 1:2); % 用训练集的主成分系数变换测试集 X_test_pca = (X_test_norm - mean(X_train_norm, 1)) * coeff(:, 1:2);

pca返回的五个输出依次是:主成分系数矩阵coeff(每列是一个主成分方向)、投影后的得分score、各主成分方差latent、T²统计量和各主成分解释的方差百分比explained。cumsum(explained)就是你需要的累计贡献率,比如结果显示[72.5, 94.3, 99.0, 100.0],那么保留前两个主成分就能覆盖94.3%的信息量。维数的选择没有绝对标准,95%是一个常用阈值,但不是真理,信号与噪声接近时建议多保留一两个维度,让分类器自己去降权。

这里有一个我踩过的坑:X_test_pca的变换必须用mean(X_train_norm, 1)和coeff,而不是在测试集上单独跑一次pca。因为降维方向和数据均值都只能用训练集的信息来估计,测试集的作用是验证,一旦让它参与主成分估计,同样会产生信息泄露。

4.2 线性判别分析LDA:用Fisher判据找到最具判别力的投影方向

PCA不考虑类别标签,所以在分类任务里,PCA降维后的方向不一定是最能区分类别的方向。LDA正好反过来,它的优化目标是让投影后类间散度尽量大、类内散度尽量小,是有监督降维。对于三分类问题,LDA最多能给出类别数减一个判别方向。MATLAB里除了fitcdiscr可以直接做分类,也可以手动求解投影方向,方便大家理解内部发生了什么:

% 手动求解LDA投影方向 classes = unique(Y_train); % 初始化类内散度矩阵Sw和类间散度矩阵Sb Sw = zeros(size(X_train_norm, 2)); Sb = zeros(size(X_train_norm, 2)); % 全局均值 mu_all = mean(X_train_norm, 1); for i = 1:numel(classes) Xi = X_train_norm(Y_train == classes(i), :); % 当前类的所有样本 mu_i = mean(Xi, 1); % 当前类均值 Sw = Sw + (Xi - mu_i)' * (Xi - mu_i); % 累加类内散度 Sb = Sb + size(Xi, 1) * (mu_i - mu_all)' * (mu_i - mu_all); % 累加类间散度 end % 求解广义特征值问题:Sw^(-1) * Sb [V, D] = eig(Sb, Sw); [d, idx] = sort(diag(D), 'descend'); V = V(:, idx); % 按判别能力从大到小排列 % 取前两个判别方向做投影 W = V(:, 1:2); X_train_lda = X_train_norm * W; X_test_lda = X_test_norm * W;

eig(Sb, Sw)是LDA求解的核心,它在找一组方向,使投影后的类间散度除以类内散度达到最大。返回值D的对角线就是广义特征值,特征值越大代表该方向的判别能力越强,idx排序保证我们取到的是前两个最强方向。

手动实现这个公式会暴露一个问题:当特征维数大于样本数时,Sw往往是奇异矩阵,eig会报错。解决方法是给Sw加一个很小的正则项,比如Sw = Sw + 0.001 * eye(size(Sw)),或者改用fitcdiscr的'DiscrimType', 'pseudoLinear'选项自动处理奇异矩阵。

4.3 把PCA与分类器串成完整流程:训练集、验证集、测试集三段式

降维做完以后,必须把降维步骤和分类器打包成一个可复现的流程,否则线上使用时会因为少一步变换而出错。下面的代码是完整串联示例:

% ===== 第一阶段:训练集内部交叉验证选参数 ===== rng(0); % 固定随机种子 % 对训练集做PCA投影 [coeff, score, ~, ~, explained] = pca(X_train_norm); % 以累计贡献率95%为阈值确定保留维数 cumVar = cumsum(explained); numDim = find(cumVar >= 95, 1, 'first'); X_train_pca = score(:, 1:numDim); % 用PCA降维后的训练集训练SVM,并做5折交叉验证 mdl = fitcecoc(X_train_pca, Y_train, ... 'Learners', templateSVM('KernelFunction', 'rbf', 'BoxConstraint', 1)); cvmdl = crossval(mdl, 'KFold', 5); fprintf('训练集交叉验证误差: %.2f%%\n', kfoldLoss(cvmdl) * 100); % ===== 第二阶段:拿到最优模型后,变换测试集做最终评估 ===== X_test_pca = (X_test_norm - mean(X_train_norm, 1)) * coeff(:, 1:numDim); pred = predict(mdl, X_test_pca); acc = sum(pred == Y_test) / numel(Y_test); fprintf('测试集准确率: %.2f%%\n', acc * 100);

这段代码把整条链路浓缩成了三个关键动作:pca只跑在训练集上,降维矩阵coeff从训练集产生;交叉验证发生在降维之后,保证调参对象就是最终部署的对象;测试集的变换完全复用训练集的均值向量和主成分系数,不引入任何测试集统计量。这个三段式流程(训练-验证-测试)对任何分类器都成立,换成fitcknn或fitcnb只需替换中间那两行模型训练和预测代码,降维部分完全不用动。

关于PCA和LDA怎么选:如果标签质量高,LDA通常比PCA效果更好,因为它从设计上就偏向“区分”而非“表示”;如果标签可能有噪声或类别本身不均衡,PCA更稳健,毕竟它根本不需要标签信息,只压缩结构。

5. 模式识别算法MATLAB实现的避坑与常见问题排查

5.1 现象:分类器把所有样本都判为同一类,准确率却异常高

这在小数据集上非常容易遇到,尤其是二分类且正类样本只占5%的时候。模型学会“偷懒”,所有样本一律输出多数类,准确率照样95%,看起来很美,实际毫无用处。

原因在于多数分类器默认使用经验先验,训练集中正类样本太少,贝叶斯分类器计算后验概率时正类的先验乘出来始终小于负类;SVM决策面也会偏向多数类一侧。

解决方法是先看混淆矩阵,而不是只看准确率。确认问题后,给少数类加权重:fitcsvm的'Prior'参数可以直接设[0.5, 0.5]强行平衡先验,或者设置'Cost'矩阵,让少数类误分类的代价更高。对于fitcnb,指定'Prior', [0.5, 0.5]同样有效。更早期的做法是手动对少数类做上采样,但MATLAB内置参数已经能解决大部分场景,不建议平白引入SMOTE等重采样逻辑。

5.2 现象:测试集准确率比交叉验证高出好几个百分点,先别高兴

交叉验证误差是用来估计模型在没见过数据上的表现的。如果测试集结果明显高于交叉验证,通常是数据划分时出了问题。

最常见的原因是归一化泄露:先用zscore(meas)处理了全部数据再划分训练测试集,或者用全体数据估计了PCA的主成分方向。两种操作都让测试集的信息以统计量形式进入了训练过程,模型相当于提前“见过”了测试集的分布。另一种可能是随机划分刚好分到一组容易预测的测试样本,小样本下尤其常见。

解决方法是回到第2章和第4章的规范写法:均值、标准差、PCA系数三个统计量全部只在训练集上估计,测试集统一用训练集算好的统计量进行变换。要排查是否泄露,可以把交叉验证去掉,反复用不同随机种子跑50次,看测试集准确率是否稳定在一个窄区间内。如果波动超过5个百分点,多半是数据量太少,而不是算法问题。

5.3 现象:KNN分类器在训练集上准确率很高,测试集却表现拉胯

KNN的训练阶段几乎不做任何泛化处理,它记住的是训练样本本身的位置。当训练样本量少、特征维数高,或者K值设得太小,决策边界会过度贴合训练样本,新样本只要稍有偏移就被分到错误类别。

原因一是K太小,比如K=1,决策边界把所有训练样本都圈成孤岛,泛化能力极差;原因二是维数灾难,特征维数超过30维后,样本间的距离趋于同化,欧氏距离不再能反映真实的近邻关系;原因三是特征没有归一化,量纲大的维度主导了距离计算。

解决方法是先归一化,再对K值做5到20的网格搜索,每次用5折交叉验证打分,选交叉验证误差最小的K。特征维数过高时先做第4章的PCA/LDA降维,把有效维度压到10维以内再上KNN。若降维后KNN仍然表现差,果断换SVM或者随机森林,不要在一个算法上死磕。

5.4 现象:交叉验证误差很低,但模型对新数据预测一塌糊涂

这是最隐蔽的坑,原因是你在调参过程中反复使用交叉验证结果,交叉验证本身变成了“训练集”的一部分。当你用网格搜索跑了几百组参数,每次都挑交叉验证误差最低的那组,实际上已经过拟合到验证集上,跟直接用测试集调参没有任何区别。

解决方法是把数据集切成三段:训练集(用于训练)、验证集(用于选参数)、测试集(只做最终一次评估)。网格搜索和参数调整只在验证集上进行,测试集严格保持“一次机会”原则。如果样本量实在不够分三份,那就用嵌套交叉验证:内层交叉验证选参数,外层交叉验证评估泛化误差。虽然计算量翻了好几倍,但得到的误差估计才真正可信。

6. 进阶验证:用混淆矩阵和ROC曲线给分类器做一次全面体检

准确率只能告诉你一个笼统的比例,真正上线前还是要做一次全面体检。混淆矩阵能告诉你错误都发生在哪两类之间,ROC曲线能告诉你模型在不同决策阈值下的权衡。MATLAB里两行代码就能出结果。

% 以KNN为例,画混淆矩阵 figure; cm = confusionchart(Y_test, predKNN); cm.Title = 'KNN在测试集上的混淆矩阵'; cm.RowSummary = 'row-normalized'; % 显示每行百分比,便于看漏判 cm.ColumnSummary = 'column-normalized'; % 做ROC曲线:以类别1为正类,其余类别为负类 % 注意:perfcurve需要后验概率作为打分,KNN默认支持输出 [~, scoreKNN] = predict(mdlKNN, X_test_norm); [rocX, rocY, ~, auc] = perfcurve(Y_test, scoreKNN(:, 1), 1); figure; plot(rocX, rocY); xlabel('假正率'); ylabel('真正率'); title(sprintf('KNN ROC曲线,AUC=%.3f', auc));

confusionchart直接接收真实标签和预测标签,自动生成带颜色热力的矩阵。RowSummary设为'row-normalized'后,每个单元格显示的是“该类别中实际有百分之多少被预测成了某类”,可以快速定位哪些类别最容易相互混淆。如果发现类别2和类别3频繁互判,说明这两个类别在特征空间里本来就很接近,要么增加新特征区分它们,要么在业务上接受两类的合并。

perfcurve是画ROC的另一种路径,它需要的是样本属于正类的概率而不是硬标签。KNN的predict返回两个输出,第二输出就是后验概率,取第一列作为“类别1的概率打分”。曲线越接近左上角,AUC越接近1,模型判别力越强。如果AUC在0.5附近徘徊,说明该分类器在这个特征集上基本没有判别能力,换什么参数都救不了,回头重新构造特征才是正路。

这套体检流程我也不是一开始就想通的。早年间我拿到80%的准确率就急着给导师报结论,结果模型部署到新批次数据上直接被打回原形,那个翻车场面至今记忆犹新。后来养成了固定习惯:任何分类器跑完,先看混淆矩阵的行归一化百分比,再看AUC,最后才写报告。这两步花不了两分钟,却能挡住绝大多数过度乐观的误判。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询