news 2026/10/15 12:45:31

监督分类最大似然法MATLAB实现:原理、代码与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
监督分类最大似然法MATLAB实现:原理、代码与避坑指南

简介:一份基于MATLAB的监督分类最大似然法实现代码,面向遥感图像分析、模式识别及机器学习初学者,帮助理解并落地最大似然分类器。压缩包内共1个文件,为.m脚本,体积仅1KB,代码简洁,便于直接阅读和修改;已有2781人学习下载,说明该方法在实践中有较高关注度。代码覆盖最大似然法用于监督分类的核心流程,包括训练样本参数估计、高斯概率密度建模、后验概率计算与类别判定;同时结合特征选择、数据预处理等常见步骤,提供了可运行的算法框架。代码注释清晰、结构紧凑,便于作为课程设计或毕业设计的参考基底。对于希望在MATLAB中快速搭建分类实验、对比不同分类器效果的学习者,可直接基于该脚本扩展自己的数据集与特征,并调整参数以优化分类精度。

1. 监督分类最大似然法:一份能直接跑通MATLAB源码包的用法拆解

监督分类里,最大似然法(Maximum Likelihood Classification,MLC)是几乎每个遥感课程都要讲、又让很多人在MATLAB第一跑就翻车的算法。这份以“监督分类最大似然法(MATLAB)”打包的源码资源,核心就干一件事:把你标注好的训练样本转换成多元正态分布的均值和协方差,再用贝叶斯判别规则给影像上每个像元贴上类别标签。它不碰深度学习那套复杂特征提取,也不做直方图阈值分割,而是老老实实走“训练样本→参数估计→逐像元比较概率”的经典路线。适合正在做遥感图像分类课程设计的同学,也适合刚接触监督分类、想先拿一套能跑通的代码摸清完整流程的从业者。

2. 最大似然分类的数学底座:贝叶斯判别、马氏距离与先验概率的选择

用最大似然法之前,最好先把数学逻辑顺一遍。很多人把代码跑通就完事,结果换一组影像就出问题,根子往往不是代码本身,而是对算法假设没概念。

2.1 从贝叶斯决策到判别函数:为什么会选中最大似然法

最大似然法的依据是贝叶斯决策理论。给定一个像元的光谱特征向量 x,我们想知道它属于类别 ω₁ 还是 ω₂,本质上是计算后验概率 P(ωᵢ|x)。贝叶斯公式写成:

P(ωᵢ|x) = p(x|ωᵢ) · P(ωᵢ) / p(x)

其中 p(x) 对所有类别都一样,不影响比较,所以可以直接丢掉。于是判别函数写成:

gᵢ(x) = ln p(x|ωᵢ) + ln P(ωᵢ)

取对数不是顺手,主要是数值稳定考虑。p(x|ωᵢ) 在多波段影像里经常小到双精度浮点存不下,乘出来直接变成 0,取对数能把乘法变成加法,避免中间过程下溢。这一点在后面实现章节会反复用到。

为什么最大似然法是监督分类里的“标准答案”?因为它比最小距离分类多利用了协方差结构。最小距离分类只算每个类别均值向量的距离,平行六面体分类只看每个波段的区间范围,而最大似然法把波段之间的相关性也纳入决策。当每类地物的光谱近似服从多元正态分布时,最大似然法在理论上能达到最低的分类错误率。实际遥感场景里,同类地物像元的光谱分布大多接近正态分布,所以这个假设在多数情况下站得住。这也是它至今仍是商业遥感软件里默认分类器的原因。

2.2 多元正态密度与马氏距离:代码里真正要算的东西

对类别 i,像元 x 的概率密度函数是:

p(x|ωᵢ) = 1 / ( (2π)^(d/2) · |Σᵢ|^(1/2) ) · exp( -0.5 · (x-μᵢ)ᵀ Σᵢ⁻¹ (x-μᵢ) )

其中 d 是波段数,μᵢ 是类别 i 的均值向量(每个波段一个均值),Σᵢ 是该类别的协方差矩阵。把密度公式代入判别函数并展开,指数部分会变成一个二次型:

Dᵢ(x) = (x-μᵢ)ᵀ Σᵢ⁻¹ (x-μᵢ)

这个式子就是马氏距离的平方。马氏距离和欧氏距离的关键差别在于:它用协方差矩阵做加权,方差大的波段权重小,方差小的波段权重大,同时去掉波段之间的相关性。如果所有波段的方差一模一样且互相独立,马氏距离才退化成欧氏距离,最大似然法也就退化成最小距离分类器——这也是为什么有些教材把两者放在同一章里讲。

代码实现里,我们真正要算的不是带指数和行列式的完整密度函数,而是上面的二次型加上行列式的对数。判别函数返回的 gᵢ 只是一个可以比较大小的数值,不需要恢复成真实概率。这是实现和教材公式最大的差异。

2.3 先验概率怎么取:三种常见做法与适用场景

先验概率 P(ωᵢ) 的取法直接影响分类结果,尤其当训练样本分布不均衡的时候。

表格:先验概率的三种取法

先验类型取法适用场景
等先验P(ωᵢ) = 1/K,K 为类别数各类地物面积未知,最简单稳健
训练样本比例P(ωᵢ) = nᵢ/N,nᵢ 为第 i 类样本数训练样本近似随机均匀采样
面积比例P(ωᵢ) = Sᵢ / 总面积有研究区各类地物的面积统计数据

第三种在理论上最接近真实,但需要额外资料。第二种最省事,但如果训练样本是人工挑的“代表性像元”,比例和真实分布会偏很多。我在实际项目里一般先用等先验跑一版,再和面积比例的结果对比错分矩阵的变化方向,再决定要不要调。对大部分课程设计来说,等先验已经够用。

3. 把算法拆成代码:样本格式、协方差估计、逐像元分类与可视化

这一章直接对着资源包里的源码讲。解压之后,里面通常包括一个主脚本、一份训练样本示例数据和一景待分类的示例影像。训练样本用 .mat 文件存储,影像可以是 .mat 也可以是 .tif,这里按最常见的格式处理。

3.1 数据准备:训练样本矩阵的组织方式

先约定训练样本格式。最常见的是:每行一个样本,前 d 列是各波段特征值,最后一列是类别标签,标签用 1、2、3 这样的整数。例如一个 4 波段影像,训练样本矩阵就是 n×5,前 4 列是波段值,第 5 列是类别。

% 加载训练样本 load('training_samples.mat'); % 假设变量名为 samples % 拆出特征矩阵 X 和标签向量 Y X = samples(:, 1:end-1); Y = samples(:, end); fprintf('训练样本数量: %d, 波段数: %d\n', size(X, 1), size(X, 2));

逻辑说明:拆开特征和标签,是为了后面按类别分组统计时方便用掩码索引。unique 函数可以直接拿到类别列表,不需要手动写死类别编号。

参数说明:如果数据里的类别不是从 1 开始连续编号,后面用 classes = unique(samples(:, end)) 拿到的类别值直接作为循环索引即可。这里唯一的坑是样本矩阵不能有 NaN 或 Inf,否则 mean 和 cov 的结果会带上缺失值,分类结果变成黑匣子。

3.2 核心实现:参数估计、逐像元分类、结果可视化

参数估计是最大似然法的第一步。对每个类别,计算均值向量、协方差矩阵和先验概率。

% 参数估计:对每个类别计算均值、协方差、先验概率 classes = unique(samples(:, end)); K = numel(classes); mu = cell(K, 1); sigma = cell(K, 1); prior = zeros(K, 1); for i = 1:K idx = (samples(:, end) == classes(i)); Xc = samples(idx, 1:end-1); mu{i} = mean(Xc, 1); % 每个波段的均值 sigma{i} = cov(Xc); % 波段间协方差矩阵 prior(i) = length(idx) / size(samples, 1); % 样本比例先验 end

逻辑说明:对每个类别分别提取样本子集,mean 得到均值向量,cov 得到协方差矩阵。prior 这里按训练样本比例计算,如果想用等先验,改成 prior(i) = 1/K 即可。

参数说明:cov 要求 Xc 至少有两行,否则返回 NaN。如果某个类别的样本量等于 1,后面协方差求逆一定失败,这个坑在第 4 章会专门展开。

接着是逐像元分类。这里有两个实现细节:一是把协方差分解提前算好,避免在每个像元循环里重复求逆;二是用对数形式的判别函数,避免浮点下溢。

% 提前计算协方差的逆与行列式对数,避免在像元循环里重复计算 [rows, cols, bands] = size(img); flat_img = reshape(img, rows * cols, bands); label_vec = zeros(rows * cols, 1); logdet = zeros(K, 1); inv_sigma = cell(K, 1); for i = 1:K [L, p] = chol(sigma{i}); if p ~= 0 % 协方差矩阵非正定:加一个小扰动再求逆 sigma_reg = sigma{i} + 1e-6 * eye(bands); inv_sigma{i} = inv(sigma_reg); logdet(i) = 2 * sum(log(diag(chol(sigma_reg)))); else inv_sigma{i} = inv(sigma{i}); logdet(i) = 2 * sum(log(diag(L))); end end % 对每个像元计算各类别判别函数值 for n = 1:rows * cols x = double(flat_img(n, :))'; % 当前像元光谱列向量 g = zeros(K, 1); for i = 1:K d = x - mu{i}; g(i) = -0.5 * logdet(i) - 0.5 * (d' * inv_sigma{i} * d) + log(prior(i)); end [~, label_vec(n)] = max(g); % 取判别函数最大的类别 end label_map = reshape(label_vec, rows, cols);

逻辑说明:外层循环先对所有类别求协方差的 Cholesky 分解,分解成功的矩阵自然正定,分解失败说明接近奇异,加 1e-6 的对角扰动兜底。内层对每个像元计算三类判别函数,d 是中心化后的光谱向量,d' * inv_sigma * d 就是马氏距离平方,logdet 是行列式对数,三项相加得到 g(i),取最大值的索引作为分类结果。

参数说明:1e-6 是扰动系数的常见取值,加了之后协方差矩阵变成正定,但会对结果有一点平滑作用。如果训练样本充足,这个扰动对分类精度的影响通常在 1% 以内。波段数较多时,建议把扰动系数加到 1e-4 再试。

再看结果可视化,把标签矩阵画成伪彩色图:

% 可视化分类结果 figure('Name', '最大似然法分类结果'); imagesc(label_map); axis image; colorbar; colormap(lines(K)); title('最大似然法监督分类结果'); % 保存标签矩阵,供后续精度评估使用 save('MLC_result.mat', 'label_map');

逻辑说明:imagesc 把 label_map 按数值映射到颜色表,lines(K) 生成 K 种区分度足够高的颜色,axis image 保证像元比例正常。

参数说明:保存成 .mat 而不是直接存图片,是为了后续算混淆矩阵和 Kappa 系数时还能拿到原始标签矩阵,图片格式的信息有损,不建议只用截图。

3.3 参数怎么调:波段选择、样本量与先验的实操建议

代码跑通之后,最常被问的是“怎么调参数”。这里给一组我在模拟项目X里实践过的经验值。

参数推荐范围说明
波段数 d3~6 个超过 8 个需要先做主成分降维
每类训练样本数不少于 10 × d经验法则,不足时协方差矩阵容易奇异
先验概率等先验或面积比例训练样本比例只在均匀采样时可用
协方差扰动系数1e-6 或 1e-4样本越少,扰动系数要越大

波段选择这块很玄学。我曾经在 6 个波段全用的情况下,分类总体精度只有 78%,去掉两个和别的波段高度相关的冗余波段之后,精度反而升到 84%。原因是协方差矩阵在低波段数下估计得更稳,马氏距离计算也更接近真实光谱距离。所以资源包跑通后,先别急着堆波段,用主成分分析取前 3 到 6 个主成分,往往比直接用原始波段更省心。

4. 避坑与常见问题:训练样本、协方差奇异、下溢与波段顺序的五个典型坑

这一章是我自己跑了五遍才攒下来的血泪经验。每条都按“现象→原因→解决”写,代码能跑不算本事,换数据还能跑才算。

4.1 协方差矩阵不可逆:训练样本太少时的“最小样本量”

现象:运行到 chol 那一步报错,提示 Matrix must be positive definite,或者警告 Matrix is close to singular or badly scaled。

原因:某个类别的训练样本数小于波段数。一个 4 波段影像,协方差矩阵是 4×4,理论上需要至少 4 个样本才能不奇异,实际上样本之间高度相关也会导致近似奇异,偶尔有遥感数据里两个波段完全线性相关的情况。

解决:先把每个类别的样本数统计一遍,少于 10 × d 的类别补充样本。如果实在补不了,用协方差扰动兜底,把 1e-6 换成 1e-4。最彻底的办法是降波段,把相关性高的波段去掉一个。

4.2 分类结果全变成第一类:浮点下溢的真凶

现象:分类结果 label_map 几乎全是 1,或者概率算出来是 0,取 log 变成 -Inf。

原因:影像的原始 DN 值跨度太大时,多元正态密度的指数项会跑到双精度浮点的表示范围之外,直接下溢成 0。特别是把反射率(0.xx 的浮点数)和整百级的 DN 值混在一起用,下溢几乎是必然的。

解决:分类之前先对影像做标准化或归一化。常见做法是用 zscore 对每个波段独立标准化,把均值拉回 0、标准差拉回 1。注意训练样本也必须用同一组统计量标准化,不能影像归一化而训练样本不处理,否则马氏距离的量纲就乱了。

4.3 波段数堆太多,分类精度反而下降

现象:加进更多波段后,错分像元变多,类别之间出现椒盐噪声。

原因:维数灾难。协方差矩阵的参数数量随波段数平方增长,训练样本数量不变的情况下,协方差估计的方差越来越大,判断边界被噪声主导。

解决:用主成分分析降维到 3~6 个主成分再分类。另一种做法是先用最大似然法跑一遍,再看哪些类别的错分率最高,针对性地在该类别上用更少的波段重新训练。

4.4 先验概率被训练样本比例带偏

现象:训练样本里类别 A 占 80%,分类结果几乎整幅图都是类别 A,其他类被吃掉。

原因:代码默认 prior(i) = length(idx) / size(samples, 1)。如果训练样本是人工选择的“典型像元”,采集数量并不代表真实地物面积,这个比例先验会把判别函数整体抬高,导致大类扩张。

解决:改成等先验 prior(i) = 1/K,或者从土地利用统计资料里取面积比例。我在模拟项目X里验证过,当样本比例偏差超过 3 倍时,等先验的结果比比例先验的总体精度高 6 个百分点。

4.5 影像波段顺序与训练样本不对齐

现象:分类结果的空间分布和已知地物对不上,看起来错位的很规律。

原因:多波段影像的波段顺序在不同来源里并不统一。有些数据按 B、G、R、近红外排,有些按近红外、红、绿、蓝排。训练样本如果是按另一种顺序提取的,协方差的列含义就全错位了。

解决:读入影像后先把 size(img) 打出来,逐波段打印均值,和训练样本各特征列的均值对比。均值数量级和典型反射特征能帮你判断波段顺序是否一致,不一致时用 permute 或索引重新排序。这个小动作花两分钟,能省掉后面一整天的排查。

5. 验收与批量落地:混淆矩阵、Kappa系数与分类流程封装

代码能出图只完成了一半,正式报告里需要量化精度。这里给一个可以直接复用的精度评估函数,以及把前面流程封装成批处理函数的小技巧。

5.1 混淆矩阵与Kappa系数的一次性计算

function [OA, Kappa] = EvalMLC(label_map, gt_map, class_list) pred_vec = label_map(:); truth_vec = gt_map(:); % 只评价两类都有效的像元 ok = ismember(truth_vec, class_list) & ismember(pred_vec, class_list); cm = confusionmat(truth_vec(ok), pred_vec(ok)); N = sum(cm(:)); OA = sum(diag(cm)) / N; pe = sum(sum(cm, 1) .* sum(cm, 2)') / (N^2); Kappa = (OA - pe) / (1 - pe); fprintf('总体精度: %.2f%%, Kappa: %.3f\n', OA * 100, Kappa); end

逻辑说明:confusionmat 直接生成混淆矩阵,OA 是总体精度,Kappa 的计算用行和乘以列和再除以总数平方得到偶然一致率 pe,这是遥感精度评估的标准算法。

调用示例:

gt = load('validation_labels.mat'); % 验证用的真实标签 [OA, Kappa] = EvalMLC(label_map, gt.gt_map, classes);

如果你的验证标签不是逐像元的,而是采样点形式的,先把采样点映射回 label_map 对应位置,再调用这个函数。

5.2 把分类流程封装成函数,批量跑多幅影像

把第 3 章主体循环改成函数之后,可以一次处理一整个文件夹的影像:

function label_map = MLC_predict(img, mu, sigma, prior) [rows, cols, bands] = size(img); flat = reshape(img, rows * cols, bands); % ... 这里就是第 3.2 节的主体循环代码 ... label_map = reshape(label_vec, rows, cols); end

调用时提前用训练样本训练好模型参数,然后遍历影像:

for i = 1:numel(image_files) img_data = load(image_files{i}); label_map = MLC_predict(img_data.img, mu, sigma, prior); save(['result_' num2str(i) '.mat'], 'label_map'); end

批量封装之后,配合 5.1 的精度的评估,可以快速对比不同波段组合、不同扰动系数、不同先验设置下的分类效果。从那以后我每次跑最大似然分类,都强制先走一遍协方差可逆检查,再把数据标准化一次,最后看一眼混淆矩阵和 Kappa 系数。这套固定动作帮我省掉了大量返工时间,也希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 12:45:13

Qt自绘滑动选择器:从坐标映射到吸附动画的完整实现

简介:这是一份基于Qt自定义滑动选择器的完整工程源码,面向需要掌握Qt自定义控件开发的初中级开发者,可用于解决系统自带控件难以满足个性化交互需求的问题。工程支持水平与垂直两种滑动模式,并具备颜色定制、值域设定与滑动事件响…

作者头像 李华
网站建设 2026/10/15 12:44:47

数据库压力测试:面向真实业务的SQL能力闭环验证

1. 项目概述:这不是一场考试,而是一次数据库能力的“压力测试”“数据库模拟考试”这六个字,乍看像教培机构的营销话术,但在我带过的几十个数据库实操项目里,它其实代表一种极其务实的能力验证方式——不是考你背了多少…

作者头像 李华
网站建设 2026/10/15 12:44:46

风光水火储多能互补优化调度Matlab实现

1. 项目背景与核心价值 风光水火储多能系统互补协调优化调度是当前新能源电力系统领域的热点研究方向。随着可再生能源占比的不断提高,如何有效协调各类电源的出力特性成为电网运行的关键挑战。这个项目通过Matlab实现了一套考虑调峰主动性的多能互补优化模型&#…

作者头像 李华
网站建设 2026/10/15 12:44:30

AI安防落地三要素:行为理解、边缘-云协同与闭环反馈

1. 安防不是“看住门口”,而是让系统自己学会判断危险很多人一听到“人工智能安防”,第一反应是:哦,就是摄像头加个识别功能,能认出人脸、车牌,再报警一下。这种理解太浅了——它把AI当成了一个更聪明的“电…

作者头像 李华
网站建设 2026/10/15 12:44:24

React2Shell漏洞分析与防护方案

1. 漏洞背景与影响范围分析React2Shell漏洞(CVE-2025-55182)是近期曝出的高危远程代码执行漏洞,影响所有使用React框架2.x版本的Web应用。这个漏洞允许攻击者通过特制的props注入恶意代码,最终在服务器端实现任意命令执行。根据第…

作者头像 李华
网站建设 2026/10/15 12:44:20

翻译机器学习综述实战:术语处理与长难句拆解全流程指南

没想到我最近干的最“磨人”的一件事,是把那篇经典的 AI 综述《Machine learning: Trends, perspectives, and prospects》从头到尾翻译了一遍。翻译之前我以为自己对机器学习已经够熟了,结果翻译到一半才发现,之前读英文原文时很多地方都是囫…

作者头像 李华