简介:《模式识别与智能计算——MATLAB技术实现》(第3版)电子书,面向计算机、信息工程、生物医学工程、智能机器人及自动化等专业的学生、研究人员与工程技术人员,帮助读者在掌握模式识别理论的同时获得可运行的MATLAB实现思路。全书14章,从特征选择与优化、相似性测度讲起,覆盖贝叶斯分类器、判别函数、BP/RBF/概率/自组织竞争等神经网络、决策树与粗糙集分类器,并延伸到模糊聚类、禁忌搜索、遗传算法、蚁群与粒子群等群智能聚类方法;以手写数字识别贯穿实例,给出实现步骤、示范代码与效果图示,便于对照复现和比较不同算法。资源包为1个PDF文件,约17.87MB,适合按章节系统阅读或作为课程教材、项目参考。目前已有313人学习,可作为模式识别课程学习与算法选型的案头资料。
1. 零基础也能上手的模式识别 MATLAB 入门:先弄明白机器是怎么「认字」的
刚接触模式识别 MATLAB 入门的人,十个里有八个会先卡在同一个念头上:数学基础不够,是不是根本学不了。其实这份资料干的事特别朴素——教机器把看到的东西归到某个类别里,比如看一眼手写数字就说出它是 0 还是 7。这件事你每天都在做,只是从没意识到。换个说法,模式识别就是把「人一眼能看出来的事」拆成能一步步算的流程,再用 MATLAB 写成能跑的代码。这份资源里 14 章算法全都围着同一个例子转:手写数字识别,每个算法都配了理论、实现步骤和可以直接运行的程序。适合谁?刚上手、要交课程作业、要做课题、想搞懂算法本身而不是只会调包的人。看完这篇,你至少能把第一个例子跑起来。
2. 入门先弄懂三个概念:样本、特征、相似度
很多新手一上来就翻到神经网络那一章,结果看两页就放弃,问题不在脑子笨,在于前三个概念没落地。这一章只干一件事:把「一张手写数字」翻译成 MATLAB 能处理的数字,再把「像不像」翻译成一个能算出来的数。这两步通了,后面 14 章你都会觉得是同一件事的不同做法。
2.1 样本和特征向量:一张手写数字怎么变成一串数
先统一叫法。被观测的每一个对象叫样本,比如你写在纸上的一百个数字,就是一百个样本;描述样本的每一个 measurable 的指标叫特征,比如这个数字占了多少像素、笔画有多长、上下左右四块的黑点密度分别是多少;一个样本的全部特征排成一列,就是特征向量;所有可能的特征向量张成的空间,叫特征空间。
举个能算的例子。假设你要区分数字 0 和 1,别一上来就用 28×28=784 个像素,先粗化成 8×8 的网格,数每个格子里黑点的比例,得到 64 个特征。那么一个手写的「0」在 MATLAB 里就是:
% 一个样本:8x8 的图像先展平成 1x64 的行向量,再转成列向量 img = double(imresize(imread('digit_0.png'), [8 8])); % 读图并缩到 8x8 x = img(:) / 255; % 展平成 64x1,顺便把 0-255 的灰度压到 0-1 size(x)img(:)是 MATLAB 里最常用的展平手法,把二维矩阵按列顺序拉成一维;/255是把像素值归一化到 0 到 1 之间,这一步不做,后面算距离时像素值会压过其他特征。跑完你会看到64 1,这就是一个 64 维的特征向量。
一百个样本摞起来,就是一个 100×64 的矩阵,每一行是一个样本,每一列是一个特征。这份资料的原书里用的是另一种写法——n 行 N 列,每一列是一个样本。两种写法数学上完全等价,但 MATLAB 的习惯是「行是样本、列是特征」,因为几乎所有内置函数都这么吃数据。这是新手第一个容易翻车的地方:照着书上的表格敲进 MATLAB,维度反了,报错信息还特别难懂。我第一次学的时候就在这里卡了半天,后来养成的习惯是,读入数据后立刻size(X)打印一次,心里默念「行是样本」。
还有一件事得提前说:特征不是越多越好。784 维的像素直接丢进分类器,样本量不够时效果往往比 64 维还差,这叫维数灾难。资料第 2 章专门讲特征的选择与优化,包括主成分分析降维、特征评估、样本筛选,那一章值得在动手之前先翻一遍,哪怕只看懂结论。
2.2 相似性测度:机器判断「像不像」的那把尺子
机器不会「看」,它只会算。判断一个手写数字是 0 还是 1,本质是算它跟已知的 0 模板、1 模板分别差多少,差得少的那个赢。这个「差多少」就是相似性测度,是整个模式识别的基础。资料第 3 章把常用的几种都列了出来,新手先认识四个就够用。
| 测度 | 怎么算 | 适合什么数据 | 新手要注意什么 |
|---|---|---|---|
| 欧氏距离 | 两个向量对应元素差的平方和开根号 | 连续特征,比如像素灰度、长度 | 各维量纲差距大时必须先归一化,否则数值大的特征会主导结果 |
| 马氏距离 | 欧氏距离再除以协方差矩阵 | 特征之间有相关性、量纲不统一 | 样本数必须大于特征维数,否则协方差矩阵不可逆 |
| 夹角余弦 | 两个向量的点积除以模长乘积 | 关心方向不关心长度,文本、二值图像 | 全零向量没法算,要先处理 |
| Tanimoto 测度 | 交集除以并集 | 纯二值数据,比如黑白像素、有无某个属性 | 只认 0 和 1,连续值必须先二值化 |
拿欧氏距离举例,两个 64 维向量 x 和 y 的距离在 MATLAB 里就一行:
% 两个 64 维特征向量之间的欧氏距离 d = sqrt(sum((x - y).^2));.^2是逐元素平方,sum把 64 个数加起来,sqrt开根号。注意这里是x - y,如果 x、y 维度不一致,MATLAB 的新版本会做隐式扩展而不报错,算出来的结果是错的——这种错最难查,因为程序不崩,只是结果离谱。
为什么值得花时间理解测度?因为后面所有算法都可以看成「换个方式算距离」。贝叶斯分类器算的是概率意义上的接近程度,K 均值聚类算的是到簇中心的欧氏距离,神经网络算的是加权之后的相似度。你把这一层看透了,14 章里至少一半的内容会突然变得眼熟。
2.3 第一个能跑的例子:二十行代码走完一次完整分类
理论先放一放,先把流程跑通。零基础最怕的是「看懂了但不知道从哪下手」,所以这个例子刻意做到零依赖、不需要任何工具箱、复制粘贴就能跑。数据集用 MATLAB 自带的鸢尾花数据,150 个样本、4 个特征、3 个类别,小到能一眼看明白,又真实到能算出有意义的准确率。
% 第一步:载入自带数据。meas 是 150x4 的特征矩阵,species 是 150x1 的类别标签 load fisheriris X = meas; % 每行一个样本,每列一个特征 y = species; % 每个样本属于哪一类 % 第二步:划分训练集和测试集,隔一个取一个,各占一半 idxTrain = 1:2:150; idxTest = 2:2:150; XTrain = X(idxTrain,:); yTrain = y(idxTrain); XTest = X(idxTest,:); yTest = y(idxTest); % 第三步:最近邻分类。测试样本和每个训练样本算欧氏距离,抄最近那个的标签 pred = cell(size(yTest)); % 预分配,用来存预测结果 for i = 1:size(XTest,1) d = sqrt(sum((XTrain - XTest(i,:)).^2, 2)); % 当前样本到所有训练样本的距离 [~, k] = min(d); % 距离最小的那个的下标 pred{i} = yTrain{k}; % 抄它的类别 end % 第四步:算准确率,确认流程真的跑通了 acc = mean(strcmp(pred, yTest)); fprintf('最近邻分类准确率:%.2f%%\n', acc*100);逐段解释一下。load fisheriris是 MATLAB 自带的数据,不需要联网也不需要额外工具箱;1:2:150是从 1 开始每隔 2 取一个,得到 75 个训练样本,剩下 75 个做测试,这种分法简单但够用。第三步里的sqrt(sum((XTrain - XTest(i,:)).^2, 2)),第二个参数2表示沿着第 2 维(列方向)求和,所以结果是 75×1 的列向量,每个元素是当前测试样本到一个训练样本的距离。[~, k] = min(d)里那个波浪号表示「第一个返回值我不需要」,只要最小值的下标。最后strcmp逐元素比较两个 cell 数组里的字符串,返回逻辑数组,mean一算就是准确率。
跑完你大概会看到 94% 左右。这个数字不用太当真,它的意义是证明流程走通了:数据 → 划分 → 训练 → 预测 → 评估,五步一个不少。
参数想改的话,看这张表:
| 想改什么 | 改哪里 | 影响 |
|---|---|---|
| 训练测试比例 | idxTrain、idxTest的取值规则 | 训练样本太少学不好,测试样本太少评估不准 |
| 换成别的数据 | 替换X和y两行 | 特征矩阵保持「行样本列特征」,标签保持同样行数 |
| 距离换成余弦 | 把sqrt(sum(...))换成点积除以模长 | 适合关心方向、不关心绝对大小的场景 |
| 最近邻改成 K 近邻 | min换成排序后取前 K 个,再投票 | K 大了更稳,但类别边界会糊 |
现在把它换成手写数字,你只需要动两行:X换成你算出来的特征矩阵(比如 N×64),y换成对应的数字标签。流程一模一样,只是特征从 4 维变成 64 维,类别从 3 类变成 10 类。等你把这一步走顺了,就是真正在入门了,而不只是看别人入门。
提示:第一次跑的时候,在
XTrain后面加一句size(XTrain),把维度打在屏幕上。养成这个习惯,能省掉后面一半的报错。
3. 分类器怎么挑:从贝叶斯到神经网络的上手顺序
流程跑通之后,下一个问题自然冒出来:最近邻这么简单,为什么还要学那么多分类器?答案是,不同数据有不同的坑,没有一个方法通吃。这一章按「新手最容易上手」的顺序来排:先概率统计,再神经网络,最后才是可解释性优先的那几个。顺序反了会很痛苦。
3.1 概率统计路线:贝叶斯和判别函数到底在赌什么
贝叶斯分类器的思路特别像人做判断。医生看病,先看这个病在人群里有多常见(先验概率),再看病人的症状在这个病里出现的概率(类条件概率),两者一乘,算出「得了这个病」的可能性(后验概率),谁大判谁。资料第 4 章讲的就是这套东西,分最小错误率和最小风险两个版本:前者只管「判对的概率最大」,后者还会考虑「判错要付多大代价」——比如把有病判成没病,代价远大于反过来,这时就该用最小风险版本。
判别函数则是另一个思路:不绕概率,直接找一条线或者一个曲面,把特征空间切成几块,落在哪块就是哪类。资料第 5 章列了一长串:线性判别函数、感知器算法、增量校正、LMSE、Fisher 分类、基于核的 Fisher、势函数法、支持向量机。名字吓人,其实核心动作只有一个——找一个让类间距离大、类内距离小的方向。Fisher 就是干这个的,特别直观。
为什么把这两个放在最前面学?因为参数少、结果可解释、失败时能看出原因。贝叶斯只有先验和类条件概率要估,Fisher 主要是求一个投影方向,你调完之后画个图就知道为什么对了或者为什么错了。神经网络就不一样,二十个隐层节点调成二十一个,结果可能完全不同,新手很难判断是自己错了还是模型就这样。
真上手的时候,建议你拿鸢尾花数据把贝叶斯跑一遍,再换 Fisher 跑一遍,对比两者的决策边界。资料里对每种方法都给了理论基础、实现步骤、编程代码三段式,按这个顺序读不会乱。看得懂公式就推一遍,看不懂就先跑代码看效果,回头再补——这是我自己觉得最省时间的读法。
3.2 神经网络路线:六种网络分别适合什么场景
资料第 6 章一口气讲了六种神经网络,新手容易在这里迷路。先给一句总结:它们都是在学一个从特征到类别的映射,差别在于网络结构、学习规则和擅长的数据规模。
| 网络 | 一句话理解 | 适合什么场景 | 新手注意 |
|---|---|---|---|
| BP 神经网络 | 最通用的多层前馈网络,靠反向传播调权重 | 中小规模分类,入门首选 | 隐层节点数、学习率要试,训练不收敛很常见 |
| RBF 径向基网络 | 用一堆「局部响应」的基函数拼出决策面 | 小样本、要求训练快的场合 | 中心点的选取影响很大 |
| 自组织竞争网络 | 神经元互相竞争,谁响应强谁赢 | 无监督聚类、特征拓扑 | 输出要先想清楚怎么解读 |
| 概率神经网络 PNN | 把贝叶斯决策塞进网络结构里 | 分类任务,训练极快 | 样本一多网络就膨胀,内存吃紧 |
| 对向传播网络 CPN | 竞争层加输出层,两段式 | 模式联想、分类 | 资料里篇幅不大,先了解思路即可 |
| Hopfield 反馈网络 | 有反馈回路,能「回忆」出完整模式 | 联想记忆、带噪声图像复原 | 容易陷入局部极小,稳定性要分析 |
入门顺序我的建议是:BP 先跑通,因为资料里 BP 的篇幅最长、示例最全,而且它的训练流程(前向计算、算误差、反向调权重)是理解其它网络的地基;然后看 PNN,因为它把上一节学的贝叶斯直接搬进网络结构,学起来有承上启下的感觉;RBF 放在第三,理解「局部响应」这个概念;剩下三个当扩展阅读,先知道它们解决什么问题就够了,不用一开始就死磕。
3.3 跑通第一个神经网络分类器
接着第 2 章的流程往下走,这次把最近邻换成 BP 网络。需要 Deep Learning Toolbox,如果你的 MATLAB 没有装,先用上一章的代码把流程跑熟,不影响理解。
% 前提:安装了 Deep Learning Toolbox load fisheriris X = meas'; % 神经网络工具箱的约定是每列一个样本,所以这里转置 y = zeros(3, 150); % 独热编码,3 个类别各占一行 classes = {'setosa','versicolor','virginica'}; for i = 1:150 y(:,i) = strcmp(classes, species{i})'; % 属于哪一类,哪一行就是 1,其余是 0 end net = patternnet(10); % 建一个隐含层含 10 个神经元的分类网络 net.trainParam.showWindow = false; % 关掉训练弹窗,命令行看结果就够了 net = train(net, X, y); % 开始训练 out = net(X); % 拿训练好的网络把全部数据回头预测一遍 [~, idxPred] = max(out); % 输出值最大的那一行就是预测类别 [~, idxTrue] = max(y); acc = mean(idxPred == idxTrue); fprintf('网络在全部数据上的准确率:%.2f%%\n', acc*100);几个关键点。meas'这个转置非常关键,神经网络工具箱和统计工具箱的数据约定是反的,一个要列是样本,一个要行是样本,转错方向不会报错但结果会很难看。patternnet(10)里的 10 是隐含层神经元个数,可以改成 5、20、50 试,改完重新跑,看准确率怎么变,这个过程比看十页理论更直观。独热编码那一段,是把'setosa'这种字符串标签转成[1;0;0]的向量,因为网络只会处理数字。max(out)取每列最大值的下标,就是预测类别。
这里必须提醒一句:上面算的是「在全部数据上的准确率」,网络在训练时已经见过这些样本了,所以这个数字虚高,不代表真实水平。资料里强调过训练与学习的区别,你要真评估效果,就得像第 2 章那样留出测试集。这个坑我后面第 5 章还会再讲一次,因为它实在太常见了。
3.4 决策树和粗糙集:需要给结论找理由时先学这两个
有些场景不光要分类结果,还要说清楚「为什么这么判」。比如某类样本被判成故障,你得告诉别人是哪几个指标触发的。这时候神经网络就不太合适了,它像个黑匣子,输出很准但你解释不清。
决策树(资料第 7 章)的好处是天生可解释:它把判断过程变成一串「如果某个特征大于某个值,就往左走」的问句,最后落到叶子上就是类别。你把这个树画出来,任何人都能顺着走一遍,看得明明白白。
粗糙集(第 8 章)解决的是另一类问题:数据里有冗余属性、有不精确的信息,能不能在不损失分类能力的前提下把属性砍掉一些。它的核心是「属性约简」,思路是不引入额外假设,直接从上近似、下近似这些集合概念出发来判断哪些属性是必要的。
这两个方法新手不用一上来就深挖,但值得知道它们存在。等你做课题时被老师问「你这个模型凭什么这么判」,你会庆幸自己翻过这两章。
4. 聚类入门:没有答案的时候,让机器自己分组
前面讲的都是有标签的情况:你知道每个样本是几,让机器学。但现实里更多时候你手里只有一堆数据,没人告诉你该分几类。这时候就该聚类上场了,资料从第 9 章一直讲到第 14 章,占了全书三分之一还多,值得认真对待。
4.1 K 均值:无监督学习最该先跑通的一个
K 均值是整个聚类里最经典、最容易理解的算法,思路四步:随便挑 K 个点当簇中心,把每个样本分给最近的中心,重新算每簇的均值当新中心,重复直到中心不再动。就这么简单。
下面这个版本不依赖任何工具箱,纯基础语法,方便你逐行看懂每一步在干什么:
% 不依赖工具箱的 K 均值:把数据粗分成 K 组 load fisheriris X = meas(:,3:4); % 只取后两个特征,方便在平面上看效果 K = 3; % 想分成几类 rng(1); % 固定随机种子,保证每次跑结果一样,便于对比 centers = X(randperm(size(X,1), K), :); % 随机挑 K 个样本当初始中心 for iter = 1:20 d = zeros(size(X,1), K); % 存每个样本到每个中心的距离 for k = 1:K d(:,k) = sqrt(sum((X - centers(k,:)).^2, 2)); end [~, assign] = min(d, [], 2); % 每行取最小值,得到归属的簇编号 newCenters = centers; for k = 1:K if any(assign == k) % 防止某一簇空了导致 mean 出错 newCenters(k,:) = mean(X(assign==k, :), 1); end end if isequal(newCenters, centers) % 中心不再移动就提前收工 break end centers = newCenters; end fprintf('迭代 %d 次后收敛,各簇样本数:%s\n', iter, mat2str(accumarray(assign,1)'));参数说明:K是你想分几类,这个数得自己给,K 均值不会告诉你该分几组;rng(1)是固定随机种子,不固定的话每次跑出来的初始中心都不一样,结果也跟着变;iter控制最多迭代多少轮,实际大多十几轮就收敛;assign是每个样本的簇编号,后面画图、算指标都靠它。那个if any(assign == k)判断容易被忽略,但没有它,某一簇变空时mean会返回 NaN,整个结果就废了,这是我自己踩过的坑。
min(d, [], 2)里的[]是占位符,表示「第二个参数跳过」,2表示沿着列方向找最小值,也就是对每个样本找最近的中心。这种写法在 MATLAB 里出现频率极高,记住了能省很多查文档的时间。
4.2 类别数说不清怎么办:层次聚类和 ISODATA 的思路
K 均值最大的软肋是 K 得你自己定。可很多时候你根本不知道数据该分几类,这时候有两个思路可以试。
第一个是层次聚类(资料第 9 章)。它不求一步到位,而是先把每个样本当成独立的一簇,然后不断把最近的两簇合并,一直到只剩一簇,整个过程画出来是一棵树。你在树上横着切一刀,切出几枝就是几类。资料里列了最短距离法、最长距离法、中间距离法、重心法、类平均距离法五种合并规则,差别在于「两簇之间的距离怎么定义」,最短距离看最近的一对点,最长距离看最远的一对点。新手先用类平均距离,它比较稳,不容易被个别离群点带偏。
第二个是 ISODATA(迭代自组织数据分析算法)。它在 K 均值的基础上加了「分裂」和「合并」两个动作:某一簇太散就拆成两簇,两簇中心太近就并成一簇,类别数在迭代过程中自己调整。这个算法参数多一些,但对「我不知道该分几类」这种情况非常友好。
再补一个思路:基于试探的聚类,比如最大最小距离算法,先随便挑一个样本当第一簇中心,找出离它最远的点当第二簇中心,再找离这两个都足够远的点当第三簇中心,一直加到没有点满足条件为止。整个过程不需要预设 K,思路很符合直觉,代码也短,适合作为层次聚类和 K 均值之外的一个补充练习。
4.3 遗传、蚁群、粒子群:为什么聚类还要学这些算法
翻到资料后半部分,你会看到模拟退火、禁忌搜索、遗传算法、蚁群算法、粒子群算法,全是聚类的变体,名字一个比一个玄。新手的第一反应通常是:K 均值几行代码就搞定了,为什么还要搞这么复杂?
原因就一个:K 均值是局部搜索。它从初始中心出发,每次都往「当前看起来更好」的方向走,走到走不动为止。问题是它可能停在一个并不理想的局部最优上——你换个初始中心,结果就完全变了。这个现象我第一次遇到时以为是程序写错了,反复检查才发现是算法本身的特性。
这些「玄学」算法的共同点是引入了跳出局部的机制:模拟退火允许以一定概率接受更差的解,相当于给算法一个后悔药;禁忌搜索记住最近走过的路,短期内不回头;遗传算法把解编码成染色体,通过选择、交叉、变异一代代进化;蚁群靠信息素正反馈逐步找到好的分组;粒子群让一群「粒子」在解空间里飞,互相参考彼此的历史最优位置。
真上手的话,建议按这个顺序:先跑遗传算法聚类,因为它的框架最清晰(编码、适应度、选择、交叉、变异五个部件),资料里也给了完整的构成要素讲解;再跑粒子群,代码短、参数少,比较容易看到效果;蚁群放在后面,它的参数最多,调起来最像玄学,第一遍跑不出好结果很正常,别灰心。模拟退火和禁忌搜索更多是作为一种「改进策略」出现在资料里,比如用模拟退火改进 K 均值,理解它们的思路比会默写代码更重要。
5. 避坑指南:新手最容易翻车的五个地方
这一章可能是全篇最值钱的部分。下面五条,全部来自实际跑代码时会遇到的情况,按「现象 → 原因 → 解决」写,遇到问题时对着排一遍,能省下大量时间。
5.1 五个高频坑,一条一条对着排
坑一:准确率高得离谱,换一批数据就崩。
现象:训练完一测,准确率 99%,兴冲冲拿去跑新样本,结果惨不忍睹。原因八成是评估方式错了——你把训练用过的数据又拿来测试,模型当然全对。这种情况在神经网络上尤其常见,因为它的拟合能力太强。解决办法很简单:一定要留出模型没见过的数据做测试,第 2 章那种隔一个取一个的做法虽然粗糙但有效,讲究一点就做三份:训练、验证、测试。
坑二:训练半天,损失不降,或者所有样本都判成同一类。
现象:网络跑了几百轮,误差曲线一条水平线,或者预测结果清一色是某一类。原因最常见的是特征没归一化——比如某个特征是 0 到 1000 的像素值,另一个是 0 到 1 的比例,前者会把距离和权重完全主导。解决:训练前用zscore做标准化,注意标准化用的均值和标准差必须来自训练集,再原样应用到测试集,反过来做就是数据泄漏。
坑三:K 均值每次跑出来的结果都不一样。
现象:同一份数据,连着跑三次,分出来的簇完全不同。原因有两个:初始中心是随机挑的,而且算法本身容易停在局部最优。解决:先用rng(1)固定种子,保证结果可复现;再试试多次随机重启,取簇内距离之和最小的那次;如果数据维度高,先做主成分分析降到二维或三维再聚,效果往往好很多。
坑四:把所有像素直接丢进去,跑得又慢又不准。
现象:用 784 维的原始像素做特征,训练时间长得离谱,准确率还不高。原因是维数灾难——特征维度上去了,样本就显得稀疏,距离度量失去区分度,同时计算量暴涨。解决:先做特征提取再分类。资料第 2 章讲的主成分分析就是干这个的,把 784 维压到几十维,往往不掉点甚至还涨点。
坑五:整体准确率 90%,但某一类几乎全错。
现象:看总体准确率挺好看,细看发现有个类别基本没识别对。原因是类别不平衡——多数类样本多,模型只要全判成多数类就能拿到不错的分数。解决:别只看总体准确率,去看混淆矩阵;再补上每一类的召回率,哪个类别拖后腿一眼就清楚。
5.2 每次跑完实验,先看这四样东西
比「不犯错」更实际的,是养成一套固定的检查习惯。我自己每次跑完分类实验,会强制看四样:整体准确率、混淆矩阵、每类召回率、以及换一组随机种子后结果稳不稳。前三个用一小段代码就能算出来:
% 在第 2 章最近邻例子的基础上,自己算一个混淆矩阵,不依赖工具箱 labels = unique([yTest; pred]); % 所有出现过的类别 C = zeros(numel(labels)); % 行是真实类别,列是预测类别 for i = 1:numel(yTest) r = find(strcmp(labels, yTest{i})); % 真实类别对应的行号 c = find(strcmp(labels, pred{i})); % 预测类别对应的列号 C(r,c) = C(r,c) + 1; % 对应格子加一 end disp(array2table(C, 'RowNames', labels, 'VariableNames', labels')) recall = diag(C) ./ sum(C, 2); % 每一类的召回率:对角线除以该行总数 disp(array2table(recall, 'RowNames', labels, 'VariableNames', {'recall'}))混淆矩阵的对角线是判对的,非对角线是判错的。哪一类总是被误判成哪一类,看非对角线哪一列数大就知道了。sum(C, 2)是沿行求和,得到每一类的真实样本总数;diag(C)取对角线;两者相除就是召回率。array2table只是为了让输出好读,不加也照样能跑。
注意:换随机种子重跑这件事看着多余,但它是判断「模型真有效」还是「这次运气好」最便宜的办法。同一个模型跑三次,结果差十几个点,那说明它不稳定,别急着写报告。
6. 下一步学什么:从跑通示例到做出自己的小项目
跑通第一个例子之后,最容易出现的状态是「会跑了,但不知道下一步干什么」,然后就在原地打转。给一条我验证过、对新手比较友好的路线,按周来排,四周下来你能有一个能拿得出手的小东西。
| 周次 | 重点 | 具体做什么 | 检验标准 |
|---|---|---|---|
| 第 1 周 | 打地基 | 吃透样本、特征、相似度三个概念,把第 2 章的最近邻例子换成自己造的两类数据重跑一遍 | 能说清每一行代码在干什么,能自己改距离公式 |
| 第 2 周 | 分类器入门 | 跑通贝叶斯和 Fisher,再跑通 BP 网络,对比三者在同一份数据上的表现 | 能画出决策边界,能解释谁为什么更好 |
| 第 3 周 | 聚类入门 | K 均值、层次聚类各跑一遍,再用遗传算法聚类跑一遍,比较结果稳定性 | 能解释 K 均值为什么会卡在局部最优 |
| 第 4 周 | 完整小项目 | 选一个自己感兴趣的识别问题,从特征提取做到评估,写出完整流程 | 有训练测试划分,有混淆矩阵,有失败分析 |
具体到每天怎么分配,我的建议是把「读」和「跑」的比例控制在三比七。资料里每种方法都给了理论基础、实现步骤、编程代码三段,理论基础那部分第一遍别死磕,看懂「它在解决什么问题」就够了,剩下的时间全部用来改代码、换参数、看结果怎么变。等你对结果有直觉了,再回头补理论,效率会高得多。
如果四周之后你还想继续深入,有三个方向可以挑:一是往特征工程走,把第 2 章的主成分分析、特征评估吃透,这是提升效果最实在的地方;二是往集成走,把多个分类器的结果合起来投票,比死磕单个模型调参划算;三是往可解释性走,决策树和粗糙集那两章值得细读,做课题时特别有用。
最后说一句我自己的教训。刚学那会儿,我总想一口气把所有算法都跑一遍,结果每个都只跑了个皮毛,遇到问题完全不知道从哪查。后来我改了个习惯:每跑一个新数据集,先强制走一遍「看维度、看分布、留测试集、看混淆矩阵」这四步,哪怕数据再小也不跳过。这个习惯帮我挡掉了后面大量的返工。希望帮到你。
本文还有配套的精品资源,点击获取