尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于Matlab的多分类混淆矩阵绘制完整实现

基于Matlab的多分类混淆矩阵绘制完整实现 简介本资源面向计算机、电子信息工程及数学等专业的本科生聚焦多分类任务中混淆矩阵的可视化实现适用于课程设计、期末大作业或毕业设计阶段的算法验证与结果展示需求。压缩包共17个文件10个MATLAB脚本文件.m用于核心绘图与指标计算5个.mat数据文件存储真实标签、预测结果及分类置信度等关键变量2个.txt文档提供使用说明与类别映射信息整体仅51KB轻量易用。资源已获52人学习下载内容结构清晰包含主运行脚本main.m、多类精度/召回率计算模块、PG_Curve曲线绘制支持、以及预置的name_class.mat等标准化数据集可直接运行生成带类别标注、归一化选项与颜色热力映射的高可读性混淆矩阵图。读者可快速掌握Matlab下多分类评估结果的规范呈现方法并基于现有代码拓展F1-score、Kappa系数等衍生指标分析。 做分类任务这些年我见过太多“准确率95%”的模型真正拿去一测就露馅。多分类问题尤其如此光看Accuracy你根本不知道模型把A类错认成了B类还是把B类错认成了C类更不用说去定位到底是哪两个类别之间在互相打架。这时候就得靠混淆矩阵——一张NxN的计数表行是真实标签列是预测标签对角线全是分类正确的样本非对角线全暴露了错误去向。这个标题对应的压缩包就是一套“基于Matlab实现多分类问题中混淆矩阵画法”的完整源码和数据适合正在写论文、交课程设计、做科研汇报或者只是被老板要求“把结果画好看点”的Matlab用户。这套资源的核心其实是两样东西一个封装好的、可以反复调用的混淆矩阵绘图函数一份拿来就能跑的示例数据和演示脚本。你不需要从零去翻文档拿到手上跑一遍把自己的真实标签和预测标签替换进去就能出一张信息完整、配色正常、可以直接贴到论文或者PPT里的图。下面我把整个实现思路、关键代码、踩过的坑和几个很实用的扩展点一次性讲清楚。1. 为什么一定要会画混淆矩阵1.1 准确率会骗人混淆矩阵不会先举一个最简单的例子。三分类任务一共60个测试样本A类50个、B类5个、C类5个。一个“聪明”的模型啥也不学直接全部输出A类Accuruy算出来是83.3%。外行一看这模型不错啊83分内行一看就知道这模型根本就没学会区分类别B类和C类完全失效。这就是准确率在多分类场景下最典型的迷惑性——它把所有类别的正确率揉成了一个平均数类别不平衡越严重这个平均数越没有参考价值。混淆矩阵不存在这个问题。同样这个场景矩阵第一行大概率是[50 0 0]第二行是[5 0 0]第三行是[5 0 0]一眼就能看出B和C全军覆没问题出在哪一目了然。另外在多分类的很多真实场景里错误的代价是不对称的。比如工业质检把合格品误判为不良品损失可能只是返工成本把不良品误判成合格品流到客户手里就是客诉甚至赔偿。又比如医疗辅助诊断把良性判成恶性会引发不必要的焦虑和过度检查把恶性判成良性则会延误治疗。混淆矩阵能精确告诉你每一个类别的错到底错到了哪个类别身上。这种细粒度信息是Accuracy永远给不了的。1.2 多分类混淆矩阵到底在表达什么混淆矩阵的数学定义并不复杂。假设有N个类别矩阵C就是一个N行N列的方阵其中第i行第j列的元素C(i,j)表示真实标签是第i类、但模型预测成了第j类的样本个数。所以对角线元素C(i,i)是第i类被正确分类的样本数第i行的和是真实第i类的样本总数也就是“这一行应该有多少个”第j列的和是模型预测为第j类的样本总数也就是“模型觉得有多少个是这一类”。从这样一个矩阵里还能衍生出两个非常常用的指标召回率Recall也叫查全率对第i类来说等于 C(i,i) 除以第i行的和衡量的是“真实这一类里模型找回了多少”精确率Precision也叫查准率对第j类来说等于 C(j,j) 除以第j列的和衡量的是“模型预测成这一类的结果里有多少是真这一类的”。这两个概念在后面归一化绘图时非常重要因为行归一化的混淆矩阵对角线读出来就是召回率列归一化读出来就是精确率。很多人画图只图好看不理解这层对应关系画完也不知道自己到底看的是什么这是很可惜的。我随便给个小矩阵感受一下。三分类计数矩阵长这样预测A预测B预测C真实A3052真实B3251真实C2420整体准确率是(302520)/92大约81.5%。但从矩阵里你能看到更多信息真实A类有5个被错认成B、2个被错认成C真实B类和C类之间也有小规模互串。如果只看准确率你根本不知道“A和B之间存在系统性混淆”这个线索而这往往和特征重叠、样本标注质量、类别相似度都有关系。2. 画图之前先把数据格式理顺2.1 标签存储的两种常见姿势在Matlab里分类任务用到的标签无非两种存储形式。第一种是数值型向量比如[1;2;3;1;2]类别是1、2、3第二种是类别型或者字符串型cell比如{setosa;versicolor;virginica}。这两种形式对后续画图都友好只是处理方式稍有差异。如果你手头的是独热编码one-hot矩阵也就是每一行只有一个1、其余全是0那种那得先把它转回标签向量。我在实际项目里经常看到有人直接拿着one-hot矩阵就往confusionmat里塞结果报错或者画出来的图完全不对。转回去其实很简单假设onehot是一个n行k列的矩阵[~, y_label] max(onehot, [], 2);这样y_label就是1到k的标签向量。另一种情况是神经网络最后一层用了softmax输出的是概率矩阵同样用max取每行最大概率对应的列索引作为预测标签逻辑完全一样。还有一点要特别注意真实标签和预测标签的长度必须一致而且顺序一一对应。这听起来是废话但编程序时一旦涉及训练集测试集划分、数据洗牌、批量预测很容易出现排序不一致导致标签错位。我自己的习惯是在划分数据时就保存好索引最后预测完按照索引把预测结果填回原顺序再和真实标签对比。2.2 confusionmat三行代码拿到计数矩阵Matlab自带一个非常好用的统计函数叫confusionmat基本用法就一行C confusionmat(yTrue, yPred);这里yTrue是真实标签yPred是预测标签两个都必须是长度相同的向量。返回的C就是前面说的NxN计数矩阵。默认情况下confusionmat会根据标签在数据中出现的前后顺序自动排类别顺序这个特性有时省事有时却很坑——如果某个类在数据中只出现一次、又排在最前面类别顺序就可能和你想的不一样。解决办法是显式指定类别顺序classNames {setosa, versicolor, virginica}; C confusionmat(yTrue, yPred, Order, classNames);用Order参数把类别顺序固定下来后矩阵的行列顺序就稳定了后续在图上设置x轴和y轴标签时也不会发生错位。这是我要强调的第一个实用细节类别顺序必须在你脑子里是清晰的否则矩阵画出来你自己都读不明白。如果你用的是categorical类型标签也一样可以传进去confusionmat支持得很顺畅。万一遇到类型混用导致的报错最常见的是cell和categorical混在一起统一一下类型就行比如都转成string或者categorical。3. 核心绘图代码从能看到图到讲得清图3.1 直接用confusionchart不香吗很多人的第一反应是Matlab新版本不是自带confusionchart函数吗直接confusionchart(yTrue, yPred)就能出图为什么还要自己写confusionchart确实很方便适合快速检查模型效果。但它有个很实际的问题可定制性不够。论文里要统一字体、统一配色、统一坐标轴样式confusionchart能做一部分但改起来总感觉不够顺手而且不同Matlab版本渲染出来的样式有差异你在这台机器上调好的图换个版本打开可能又不一样了。另外如果要做多模型对比、把多个混淆矩阵拼在一张图上或者对颜色映射做细粒度控制confusionchart就更捉襟见肘了。所以我的建议是快速验证用confusionchart正式出图用自写函数。两者并不矛盾自写函数也不是让你从零造轮子底层还是靠imagesc、colormap、text这些最基本的绘图指令组合起来灵活性完全掌握在自己手里。3.2 第一步imagesc画出热力图拿到计数矩阵C之后最核心的绘图指令是imagesc它能把矩阵的数值大小映射成颜色深浅。先看一个最原始的版本C confusionmat(yTrue, yPred); figure; imagesc(C); axis equal tight; colorbar;这一跑窗口里出现一个热力图每个格子的颜色对应数值大小。但问题也很明显没有坐标轴标签、没有数字标注、配色是默认的完全不具备可直接展示的“成品感”。接下来一步一步补。这里提醒一下imagesc和常见的plot不一样它的坐标轴方向默认是从下往上也就是说矩阵第1行显示在最下面。但大家阅读混淆矩阵的习惯是第一行在最上面。所以需要在绘图后加上一句set(gca, YDir, reverse);加了这句矩阵的第1行才会显示在图像顶部读图时和书面表格完全一致。这个小细节非常重要我见过不少人第一次画出来发现矩阵“上下颠倒”其实就是漏了这一句。3.3 第二步在格子里填上数字一张只有色块没有数字的混淆矩阵信息量少了一大半。填数字用text函数遍历矩阵的每一个格子把计数显示出来[nRow, nCol] size(C); for i 1:nRow for j 1:nCol text(j, i, num2str(C(i, j)), ... HorizontalAlignment, center, ... FontSize, 11); end end这里最容易出错的点就是text函数的坐标参数顺序。text(x, y, str)中第一个参数是x坐标对应矩阵的列第二个参数是y坐标对应矩阵的行。所以当你想在第i行第j列格子里写字时写的是text(j, i, ...)而不是text(i, j, ...)。位置写反了数字就会全部错位对角线变得乱七八糟。很多初学者在这儿卡半天其实就是一个参数顺序的问题。另外如果某个格子的颜色很深文字还是黑色就很难看清颜色很浅白色又看不清。一个实用技巧是根据矩阵数值动态决定文字颜色if C(i, j) max(C(:)) * 0.5 textColor [1 1 1]; else textColor [0 0 0]; end阈值可以根据自己的配色调整核心思路就是“深底白字、浅底黑字”。如果嫌麻烦也可以统一用白色粗体再给文字加一点阴影效果但那样渲染成本略高动态判断更直接。3.4 第三步坐标标签、标题和配色矩阵画出来之后下一步是让坐标轴上的刻度显示成真实类别名。假设类别名存在classNames这个cell数组里直接用set(gca, XTick, 1:length(classNames), XTickLabel, classNames); set(gca, YTick, 1:length(classNames), YTickLabel, classNames); xlabel(预测标签); ylabel(真实标签); title(混淆矩阵);如果类别名太长横向排列会被截断常见做法是旋转刻度标签xtickangle(45);再来说配色。Matlab默认的配色其实不是最优选项。过去很多人喜欢用jet也就是那种红橙黄绿青蓝紫的彩虹色看着炫但在黑白打印时完全没法用而且彩虹色在感知上并不是线性均匀的浅色区域变化不明显、深色区域又过于跳跃。我现在画混淆矩阵基本只用parula或者自定义线性渐变色。parula是Matlab从R2014b开始的默认色图视觉上柔和、变化均匀对色弱人群也比较友好。直接一行colormap(parula);如果你想要更极简的风格比如“白色到蓝色”的渐变也可以自定义一个两行三列的色图矩阵cmap [1 1 1; 0.2 0.5 0.8]; colormap(cmap);这种白蓝渐变在论文里很常用看起来干净也不会抢内容的注意力。还有一个细节是colorbar的标注。如果矩阵是计数colorbar的标签可以写“样本数”如果是归一化后的比例就写“比例”或“百分比”不要留一个光秃秃的颜色条在那里。4. 归一化与颜色映射让细节真正看得见4.1 行归一化、列归一化还是全局归一化原始计数矩阵最直观但有一个问题当各类样本数量差异很大时大类的格子颜色普遍很深小类的格子颜色普遍很浅真正想看的“比例关系”反而被绝对数量掩盖了。解决办法是归一化。三种常见归一化方式对比如下方式计算方式对角线含义适用场景不归一化直接显示计数各类正确分类数样本均衡、想保留绝对数量行归一化每行除以该行总和各类召回率最推荐看漏检率最直观列归一化每列除以该列总和各类精确率想排查误检集中去向时全局归一化整体除以样本总数每格占总体比例多图对比时统一量纲我自己的默认选择是行归一化因为它的语义最贴合分类任务的核心诉求这一类里面我到底抓回来了多少画出来的矩阵每行加起来等于1对角线上直接就是召回率谁好谁坏一眼就能挑出来。行归一化在Matlab里写起来非常简单C_norm C ./ sum(C, 2);如果要列归一化改成C_norm C ./ sum(C, 1);注意Matlab的隐式扩展规则C是方阵时两种写法都成立但如果是长方形矩阵或者类别数不齐最好用./ sum(C, 2)和./ sum(C, 1)这种明确指定维度的写法。将归一化后的矩阵传入绘图函数时格子里的文字显示也要跟着变。你肯定不希望格子里还是“23”这种计数而是希望显示“76.7%”这种比例。显示格式可以由自己控制str sprintf(%.1f%%, C_norm(i, j) * 100); text(j, i, str, HorizontalAlignment, center);顺便建议如果你既要原始计数又想要比例可以两行字同时显示比如第一行写计数第二行写百分比。这种图在论文里信息量很足读者既能看到数量级又能看到比例关系。4.2 颜色范围如何统一归一化之后矩阵的数值范围变成了[0,1]但这不代表imagesc会自动把颜色范围设在[0,1]。如果你之前画过别的矩阵或者矩阵里存在异常值颜色映射的上下限可能会被拉偏导致大部分格子都呈现同一个颜色。解决办法是显式固定颜色范围。在Matlab较新版本中推荐用climclim([0 1]);在老版本里这个函数的名称是caxiscaxis([0 1]);为了兼容不同版本可以在自己的绘图函数里写一个try-catch兼容try clim([0 1]); catch caxis([0 1]); end这样做的好处不只是当前这张图正确更重要的是如果你要横向对比两个模型的混淆矩阵只要两张图都用clim([0 1])颜色深浅就有了可比性而不是各画各的、深浅没有统一标准。4.3 让大类别不淹没小类别有一类典型的画图翻车现场三个类别A类样本500个B类样本10个C类样本10个。直接画计数矩阵A类对应的行和列颜色深得发黑B和C肉眼几乎看不清。即使做全局归一化也还是大类的比例值更高小类容易淹没。根治办法就是行归一化。行归一化之后每一行都只看行内比例A类行内分布和B类行内分布在颜色上处于同一个量级小类别终于能够被看见。但行归一化也有个代价丢失了样本量信息。A类虽然每一格比例都不高但它总数500个绝对错误数可能比B类多很多。所以行归一化图旁边最好再配一张计数表或者直接标出每类样本总数。我在实际做汇报时会在图例或者脚注里写一句“括号内为该类别样本总数”既保证比例可读又不丢失数量级。多提一句如果你觉得只靠颜色深浅还不够可以用矩形边框把对角线格子框出来比如rectangle(Position, [i-0.5, i-0.5, 1, 1], EdgeColor, k, LineWidth, 1.5);这样对角线被明显强调读者第一眼就知道该看哪里。5. 封装成一个可以直接调用的绘图函数5.1 函数签名与参数表写了很多分散的命令最后一定要整理成一个可复用的函数否则每次画图都重新复制粘贴一大段代码迟早要出问题。这个压缩包里的核心函数plotConfusionMatrix.m就是干这个用的。函数签名大致如下function plotConfusionMatrix(yTrue, yPred, classNames, varargin)前三个输入是必填项真实标签、预测标签、类别名列表。后面用varargin接收各种可选参数方便灵活调整。我用inputParser来解析参数这样调用时既可以用默认参数快速出图也可以精细调整。几个常用的可选参数参数名默认值作用Normalizerows归一化方式可选none、rows、columns、allTitleConfusion Matrix图标题FontSize12格子文字和坐标轴字体大小ShowColorbartrue是否显示颜色条Colormapparula颜色映射函数FigSize[560 480]图像窗口大小Precision1百分比显示的小数位数用inputParser的好处是调用时的参数顺序没有硬性要求写起来一目了然。比如plotConfusionMatrix(yTrue, yPred, classNames, ... Normalize, rows, Title, KNN分类结果);5.2 核心实现框架函数的主体逻辑可以拆成四步统计计数、归一化、绘图填字、设置坐标标签。核心框架长这样function plotConfusionMatrix(yTrue, yPred, classNames, varargin) p inputParser; addRequired(p, yTrue); addRequired(p, yPred); addOptional(p, classNames, {}); addParameter(p, Normalize, rows); addParameter(p, Title, Confusion Matrix); addParameter(p, FontSize, 12); addParameter(p, ShowColorbar, true); addParameter(p, Colormap, parula); addParameter(p, FigSize, [560 480]); parse(p, yTrue, yPred, classNames, varargin{:}); C confusionmat(p.Results.yTrue, p.Results.yPred, ... Order, p.Results.classNames); if isempty(p.Results.classNames) classNames arrayfun((x) num2str(x), 1:size(C,1), ... UniformOutput, false); else classNames p.Results.classNames; end C_plot C; switch p.Results.Normalize case rows C_plot C ./ sum(C, 2); case columns C_plot C ./ sum(C, 1); case all C_plot C ./ sum(C(:)); end figure(Color, w, Position, [100 100 p.Results.FigSize]); imagesc(C_plot); colormap(p.Results.Colormap); try clim([0 1]); catch caxis([0 1]); end axis equal tight; set(gca, YDir, reverse, FontSize, p.Results.FontSize); set(gca, XTick, 1:length(classNames), XTickLabel, classNames); set(gca, YTick, 1:length(classNames), YTickLabel, classNames); xlabel(预测标签, FontSize, p.Results.FontSize 1); ylabel(真实标签, FontSize, p.Results.FontSize 1); title(p.Results.Title, FontSize, p.Results.FontSize 2); for i 1:size(C, 1) for j 1:size(C, 2) if strcmp(p.Results.Normalize, none) str num2str(C(i, j)); else str sprintf([%. num2str(p.Results.Precision) f%%], ... C_plot(i, j) * 100); end if C_plot(i, j) max(C_plot(:)) * 0.5 textColor [1 1 1]; else textColor [0 0 0]; end text(j, i, str, HorizontalAlignment, center, ... FontSize, p.Results.FontSize, Color, textColor); end end if p.Results.ShowColorbar colorbar; end end这段代码基本就是压缩包里那个函数的主体。你如果不想要那么多参数可以删减保留最核心的Normalize和Title两个参数其他都写死代码会更短。但我在实际项目里发现参数化做得越完整后面调整样式越省心所以还是建议保留这套接口。5.3 一段脚本把真值、预测值、类别名串起来有了函数之后调用非常简单。假设你已经有了yTrue和yPred还有一个类别名列表classNames那么画图只需要一行plotConfusionMatrix(yTrue, yPred, classNames, ... Normalize, rows, Title, 我的多分类结果);如果你想同时看计数和归一化两种视图可以画两遍或者把两种模式放在同一张图的两个子图里。我自己更习惯先画归一化视图用于看比例再在汇报PPT里放计数视图用于展示绝对数量两者互相补充。有一个调用细节需要说明classNames传入函数后confusionmat会严格按照这个顺序统计所以就算你的原始标签里没有某个类别矩阵里也会保留对应的全零行和全零列。有人会觉得全零行列碍眼我倒觉得这是好事说明模型在这个类别上完全失效这类信息值得被看到。6. 真实案例用内置鸢尾花数据跑一个分类并出图6.1 快速训练一个KNN分类器空讲理论没什么感觉我拿Matlab自带的fisheriris数据集跑一个完整流程。这个数据集150个样本、4个特征、3个类别非常适合演示。为了不让演示变成“用训练集当测试集”的过拟合笑话我用5折交叉验证的方式生成预测标签load fisheriris X meas; Y species; rng(1); cp cvpartition(Y, KFold, 5); pred cell(size(Y)); for k 1:cp.NumTestSets trIdx training(cp, k); teIdx test(cp, k); mdl fitcknn(X(trIdx, :), Y(trIdx), NumNeighbors, 5); pred(teIdx) predict(mdl, X(teIdx, :)); end pred categorical(pred);注意fitcknn返回的预测结果类型可能和species不完全一致统一转成categorical更稳妥。如果你想复现时减少变量把Y和pred都转成categorical再传给绘图函数。然后调用绘图函数classNames categories(Y); plotConfusionMatrix(Y, pred, classNames, ... Normalize, rows, Title, 5折交叉验证 KNN 混淆矩阵);画出来的图里setosa这一类几乎全对因为它的花萼和花瓣特征和其他两类分隔得非常干净versicolor和virginica之间会有少量互相误判这符合实际情况这两个类在特征空间里本来就有重叠区域。如果计算一下各个类别样本数会发现150个样本三等分每类50个数量均衡所以用不归一化和行归一化的差别在这份数据上不大。但换成你手头那种类别严重不均衡的数据归一化的价值立刻就会体现出来。6.2 加载压缩包里的数据直接画图压缩包里有一个data.mat文件里面预设了三个变量yTrue、yPred、classNames。这是我在某个实际小项目里保存下来的分类结果为了方便你快速验证绘图函数直接load进来就能用load(data.mat); plotConfusionMatrix(yTrue, yPred, classNames, ... Normalize, rows, Title, 示例数据混淆矩阵);这段脚本相当于一个“冒烟测试”能在几分钟内确认绘图函数在你的Matlab环境下正常工作。之后你把yTrue和yPred替换成自己的数据把classNames换成你的类别名图就出来了。自己动手替换时最容易忽略的是标签类型和类别名顺序。比如你的yTrue是1到10的数值向量classNames就写成{1,2,...}或者{类别1,类别2,...}保持一致即可。如果发现类别顺序和矩阵对不上百分之九十九是classNames的顺序和你预期不一致返回去检查confusionmat的Order参数。6.3 怎么从图上定位模型的“软肋”画图不是终点画完图要会读图。多分类混淆矩阵里有几类典型的“毛病”需要重点关注第一某个非对角线格子的数值明显偏高。这说明对应的两个类别之间出现了系统性混淆比如把第i类错认成第j类。这时候应该回头看看这两个类别的特征是否足够区分或者考虑增加针对性的特征、收集更多这两类的样本。第二某一行的对角线值很低整行颜色都偏浅。这代表第i类本身的召回率很差大概率是该类在训练集中数量太少或者特征分布过于分散。第三某一列的格子普遍偏深。这代表有很多其他类别被预测成了这一类也就是这个类被“过度预测”。常见原因是分类器偏向于这个类或者决策阈值设置不合理可以尝试调整分类阈值或者做后处理。第四如果矩阵整体呈现“对角线为深色、其他区域均匀浅色”那就说明模型整体表现不错没有特别明显的混淆模式可以放心继续下一步。读图能力比画图能力更值钱。很多人画了一张漂亮的混淆矩阵却不知道从哪看起那就失去了它存在的意义。我每次汇报模型效果时都会先指着矩阵里最高的非对角线值说一句“这里是我们下一步要优化的重点”比干巴巴地报一个Accuracy有说服力得多。7. 常见问题与排查技巧速查7.1 矩阵方向为什么画反了症状矩阵的对角线看起来是反对角线或者上下、左右明显颠倒。原因排查第一步检查confusionmat的两个输入参数顺序。confusionmat(yTrue, yPred)返回的是“真实标签为行、预测标签为列”的矩阵。如果你误传成confusionmat(yPred, yTrue)矩阵自动转置对角线内容虽然不变但行和列的语义就反了图的解读和标签完全对不上。原因排查第二步检查绘制时有没有设置set(gca, YDir, reverse)。imagesc默认y轴从下往上矩阵第1行在底部如果不加这句读图方向就和日常习惯相反。加上reverse后第1行显示在顶部看起来才自然。原因排查第三步检查text的坐标参数。text(j, i, ...)中的j是列坐标、i是行坐标写反了也会让数字错位。这个坑最隐蔽因为图像整体颜色块没变只是数字到处乱飞乍一看甚至不容易发现。7.2 格子里的文字位置乱了症状数字不在格子中心或者某些格子里的文字被边框截断。这种情况通常也是text坐标写反或者是文字对齐方式没设置。text函数默认的HorizontalAlignment是left如果没写HorizontalAlignment, center文字会从坐标点往右延伸看上去像偏了。另外如果矩阵数值太大比如成千上万字符串宽度超过格子宽度字体设置太大会被截断可以适当调小FontSize或者换用科学计数法显示。7.3 中文标签乱码症状坐标轴上或标题里的中文变成了一堆方块或者显示成问号。这是Matlab的著名老问题通常和系统的字体设置有关。简单粗暴的解决办法是把所有中文标签换成英文等论文定稿后再人工替换成中文或者用图和文字分离的方式在排版软件里加中文标签。如果一定要在Matlab里出中文图可以试试设置字体set(gca, FontName, SimHei);Windows上宋体、黑体、微软雅黑一般来说都能用Linux上经常缺中文字体这时候就老老实实用英文标签最省心。另外如果你用的是较新的Matlab版本fig文件里的中文显示可能没问题但导出成图片时反而乱掉这种情况多半也是字体问题全局设置一下DefaultAxesFontNameset(0, DefaultAxesFontName, Microsoft YaHei);7.4 颜色差异不明显症状整个矩阵几乎一个颜色深色和浅色拉不开差距。最常见的原因是矩阵数值范围太窄比如所有数值都集中在0到0.2之间像全局归一化后的小类比例颜色自然拉不开。解决方法是把颜色范围固定到实际数据范围或者改用行归一化再看一遍。另一个原因是色图选择不当。有些色图在低值区变化非常缓慢比如jet在深蓝色区域几乎看不出梯度变化。换用parula或者自定义白蓝渐变通常能明显改善。7.5 输出图片模糊症状把图保存下来后放到论文或PPT里放大就模糊。Matlab默认的打印分辨率不够高保存图片时需要用print指定高分辨率print(gcf, confusion_matrix.png, -dpng, -r300);这里-r300表示300dpi论文插图常用这个档位如果还想更高可以开到-r600。如果要矢量图方便后续在Illustrator里编辑可以导出PDF或EPSexportgraphics(gcf, confusion_matrix.pdf, ContentType, vector);exportgraphics是较新版本推荐的导出方式输出质量比print稳定优先使用它。7.6 版本差异caxis还是clim症状代码在旧版本跑得好好的换到新版本就弹出警告提示caxis即将被移除。从R2022a开始Matlab推荐用clim替代caxis。如果你写的代码要给别人用或者你自己经常在不同版本之间切换用try-catch兼容写法是最稳妥的try clim([0 1]); catch caxis([0 1]); end这样无论对方用的是新版本还是老版本都不会因为一行颜色范围设置报错。类似的版本兼容问题还出现在xtickangle、tiledlayout等函数上写通用代码时多做一步兼容处理能省去很多不必要的麻烦。8. 延伸除了画图混淆矩阵还能帮你算指标8.1 一个函数顺带算精确率、召回率、F1混淆矩阵的价值不止于可视化它还是很多分类指标的计算基础。我们在4.1里见过归一化矩阵的对角线含义但真要算指标时一定不要用归一化后的矩阵而是用原始计数矩阵C否则算出来的数字完全不对。一个非常简洁的指标计算代码块C confusionmat(yTrue, yPred); precision diag(C) ./ sum(C, 1); recall diag(C) ./ sum(C, 2); f1 2 * precision .* recall ./ (precision recall); acc sum(diag(C)) / sum(C(:)); T table(precision, recall, f1, ... RowNames, classNames); disp(T); fprintf(总体准确率: %.2f%%\n, acc * 100);跑完之后你会得到一个表格每一行一个类别包含精确率、召回率、F1。这张表配合混淆矩阵图一起展示既能看图又能看数非常实用。还有一个值得一提的指标是Kappa系数它衡量的是预测结果和随机分类相比的一致性程度取值一般在-1到1之间。多分类样本不均衡时Kappa比Accuracy更能反映模型真实水平。Matlab里可以直接调用kappa 1 - sum(sum(C, 1) .* sum(C, 2)) / (sum(C(:))^2); kappa (sum(diag(C)) / sum(C(:)) - ... sum(sum(C, 1) .* sum(C, 2)) / (sum(C(:))^2)) / ... (1 - sum(sum(C, 1) .* sum(C, 2)) / (sum(C(:))^2));这是简化版的多类别Kappa公式算出来就是一个0到1之间的数适合作为汇报时的一个补充数字。不过Kappa的计算方式有多个版本如果你在论文里用务必注明采用的计算公式。8.2 多模型对比与组合图我实际做实验时往往不会只跑一个模型而是要对比两三个。这时候可以把多个混淆矩阵拼在一张图里统一颜色范围方便横向对比。用subplot布局figure(Color, w, Position, [100 100 1200 480]); subplot(1, 2, 1); plotConfusionMatrix(yTrue, pred1, classNames, ... Normalize, rows, Title, KNN); subplot(1, 2, 2); plotConfusionMatrix(yTrue, pred2, classNames, ... Normalize, rows, Title, SVM);因为每个子图内部都固定了clim([0 1])所以两张图之间的颜色深浅可以直接比较。哪个模型在哪些类别上颜色更浅就是哪里的短板更明显。另一个有意思的扩展是类别重排。当分类数很多比如几十个类别时混淆矩阵的类别顺序往往不是最优展示顺序因为相似的类可能相隔很远看起来乱糟糟。一个常见的做法是根据混淆矩阵计算类别间的“距离”对类别做层次聚类然后按照聚类顺序重排行列让经常互相混淆的类别相邻整张图会呈现清晰的块状结构。这类重排在语音识别、图像细粒度分类等场景里非常常见配合热力图看能快速发现模型的“语义分组”倾向。最后分享一个我自己的使用习惯每次拿到一批数据我先用confusionchart快速扫一眼总体情况然后跑一个自写的函数出正式图。正式图我一般统一用行归一化、parula配色、白字深底黑字浅底、固定clim这样无论哪个模型、哪份数据画出来风格一致放进报告里整体感很强。这个小习惯看起来简单但真的能让汇报材料专业度提升不少。本文还有配套的精品资源点击获取
返回列表