基于深度学习的多光谱图像语义分割 —— 从原理到实践(第五节)

前四节我们完成了数据准备、分块处理、预训练模型推理和应用分析。
现在,你将学习从头构建并训练自己的 U‑Net 模型,这样你可以自由调整网络结构、类别数量、损失函数,以适应你自己的多光谱数据集。
本节将深入讲解 U‑Net 架构、自定义损失函数的设计、训练超参数的含义,并给出完整的训练代码。


在这里插入图片描述

5. U‑Net 构建、训练与损失函数设计

5.1 回顾 U‑Net 网络架构

U‑Net 因形似字母 U 而得名,它最早由 Ronneberger 等人于 2015 年提出,用于生物医学图像分割,后来成为遥感分割的常用骨干网络。其核心思想是对称的编码器-解码器结构 + 跳跃连接

编码器(收缩路径)

  • 由多个卷积块(卷积 + ReLU + 批归一化)组成。
  • 每经过一个块,空间分辨率减半(通过最大池化),同时通道数翻倍(例如从 64 到 128)。
  • 这一步提取了图像的语义特征(“是什么”),但丢失了空间细节(“在哪里”)。

解码器(扩展路径)

  • 由多个上采样块(转置卷积或插值)和卷积块组成。
  • 每上采样一次,分辨率翻倍,通道数减半。
  • 关键点:跳跃连接——将编码器对应层的特征图与解码器上采样后的特征图拼接,再将融合后的特征继续卷积。这使得解码器能够保留低级细节信息,弥补下采样造成的空间损失。

最终输出层:一个 1×1 卷积,将通道数映射为类别数,再经过 softmax 得到每个像素的类别概率分布。

U‑Net 的优势在于:

  • 端到端:输入任意尺寸图像,输出同尺寸分割图。
  • 参数高效:跳跃连接避免了信息瓶颈,即使训练数据有限也能收敛。
  • 灵活性:可以自由调整深度、初始通道数、卷积核大小等。

5.2 使用 unet 函数构建网络

MATLAB 的 Computer Vision Toolbox 提供了 unet 函数,只需指定输入尺寸和类别数,即可自动生成 U‑Net 网络。我们还可以通过名称-值参数调整深度、卷积滤波器数量等,但本例使用默认设置。

输入图像块尺寸为 [256, 256],输入通道数为 6(光谱通道),输出类别数为 2:

inputTileSize = [256 256];
inputDepth = 6;                     % 使用前 6 个光谱通道
numClasses = 2;                     % 二分类
unetNetwork = unet([inputTileSize, inputDepth], numClasses);

unetNetwork 是一个 dlnetwork 对象,可以直接用于训练或推理。你可以通过 analyzeNetwork(unetNetwork) 查看网络层结构、参数数量和连接关系。

5.3 自定义损失函数——带掩膜的交叉熵

在语义分割中,常用的损失函数是像素级交叉熵(Cross‑Entropy),其公式为:

Loss = -Σ_i Σ_c y_i,c * log(p_i,c)

其中 y_i,c 是真实标签的 one‑hot 编码,p_i,c 是网络预测的概率。

但我们的数据中存在无效区域(掩膜外),这些像素不应参与损失计算。因此我们需要自定义损失函数,利用掩膜屏蔽无效位置。

我们定义如下函数(需要单独保存为 lossFcn.m 文件,或在脚本末尾以局部函数形式定义):

function loss = lossFcn(Y, T)
    % Y: 网络预测,尺寸 H×W×C(C为类别数)
    % T: 真实标签 one‑hot,尺寸 H×W×C,无效区域为 NaN
    mask = ~isnan(T);        % 有效像素位置(逻辑矩阵)
    T(isnan(T)) = 0;         % 临时替换 NaN 为 0,避免计算错误
    loss = crossentropy(Y, T, Mask=mask);
end

crossentropy 支持 Mask 参数,只会对 mask 为 true 的位置计算损失,其余位置忽略。这样我们就实现了只关注有效区域的学习。

5.4 训练选项(Training Options)详解

我们采用**带动量的随机梯度下降(SGDM)**优化器。以下是每个超参数的含义:

参数 含义
InitialLearnRate 0.05 初始学习率,控制参数更新步长。过大易震荡,过小收敛慢。
MaxEpochs 70 训练轮数,即遍历整个数据集的最大次数。
MiniBatchSize 16 每批处理的图像块数。受 GPU 显存限制,可调小或调大。
L2Regularization 0.001 L2 正则化系数(权重衰减),防止过拟合。
LearnRateDropPeriod 10 每 10 轮学习率乘以 LearnRateDropFactor(默认 0.1)。
LearnRateSchedule “piecewise” 分段衰减学习率策略。
GradientThreshold 0.05 梯度裁剪阈值,限制梯度的 L2 范数,防止梯度爆炸。
Shuffle “every-epoch” 每轮开始时随机打乱数据,增强随机性。
VerboseFrequency 200 每 200 次迭代输出一次训练信息。

设置代码如下:

maxEpochs = 70;
minibatchSize = 16;

options = trainingOptions("sgdm", ...
    InitialLearnRate=0.05, ...
    MaxEpochs=maxEpochs, ...
    MiniBatchSize=minibatchSize, ...
    L2Regularization=0.001, ...
    LearnRateDropPeriod=10, ...
    LearnRateSchedule="piecewise", ...
    GradientThreshold=0.05, ...
    Shuffle="every-epoch", ...
    VerboseFrequency=200);

5.5 启动训练

我们使用 trainnet 函数(Deep Learning Toolbox)进行训练。它支持自定义损失函数,并自动利用 GPU(如果可用)加速。

设置 doTraining = true 开始训练。训练结束后,将模型按时间戳保存,便于后续加载。

doTraining = false;   % 设为 true 以执行训练
if doTraining
    net = trainnet(dsTrain, unetNetwork, @lossFcn, options);
    modelDateTime = string(datetime("now", Format="yyyy-MM-dd-HH-mm-ss"));
    save(fullfile(dataDir, "multispectralUnet-"+modelDateTime+".mat"), "net");
end
  • dsTrain 是我们在第三节构建的 CombinedDatastore,每次迭代返回一个图像块和对应的标签块。
  • trainnet 会自动进行前向传播、损失计算、反向传播和参数更新,并实时输出损失值。

训练过程可能持续数小时(取决于 GPU 性能和数据量)。你可以通过 VerbosePlots 选项开启训练曲线图,监控损失下降趋势。

5.6 训练过程中的注意事项

  1. GPU 内存不足:如果报错 Out of Memory,可减小 MiniBatchSizeinputTileSize
  2. 学习率调整:若损失震荡剧烈,可降低 InitialLearnRate;若收敛缓慢,可适当提高。
  3. 早停策略:可以通过 ValidationDataValidationFrequency 选项添加验证集,当验证损失不再下降时提前终止训练,防止过拟合。
  4. 数据增强trainnet 本身不支持在线增强,但你可以自定义 transform 函数对数据存储进行扩充(例如随机旋转、翻转、色彩抖动等),提高泛化能力。

5.7 保存和加载模型

训练好的 net 变量保存在 .mat 文件中。之后使用时,只需:

load(fullfile(dataDir, "multispectralUnet-2026-07-17-10-30-00.mat"), "net");

即可恢复网络,用于后续推理(如第四节所示)。

5.8 本节完整代码汇总

以下是将本节所有代码组合在一起的脚本(训练部分默认关闭):

% 构建 U-Net
inputTileSize = [256 256];
inputDepth = 6;
numClasses = 2;
unetNetwork = unet([inputTileSize, inputDepth], numClasses);

% 定义损失函数(需单独保存为 lossFcn.m)
% function loss = lossFcn(Y,T)
%     mask = ~isnan(T);
%     T(isnan(T)) = 0;
%     loss = crossentropy(Y,T,Mask=mask);
% end

% 训练选项
maxEpochs = 70;
minibatchSize = 16;
options = trainingOptions("sgdm", ...
    InitialLearnRate=0.05, ...
    MaxEpochs=maxEpochs, ...
    MiniBatchSize=minibatchSize, ...
    L2Regularization=0.001, ...
    LearnRateDropPeriod=10, ...
    LearnRateSchedule="piecewise", ...
    GradientThreshold=0.05, ...
    Shuffle="every-epoch", ...
    VerboseFrequency=200);

% 启动训练(需先确保 dsTrain 已存在)
doTraining = false;
if doTraining
    net = trainnet(dsTrain, unetNetwork, @lossFcn, options);
    modelDateTime = string(datetime("now", Format="yyyy-MM-dd-HH-mm-ss"));
    save(fullfile(dataDir, "multispectralUnet-"+modelDateTime+".mat"), "net");
end

5.9 本节小结

在本节中,我们完成了以下重要工作:

  1. 深入理解了 U‑Net 的编码器-解码器结构和跳跃连接原理;
  2. 使用 unet 函数快速构建了适用于多光谱图像的分割网络;
  3. 设计了带掩膜的交叉熵损失函数,实现有效区域聚焦;
  4. 详细解释了每个训练超参数的作用,并设置了 SGDM 优化器;
  5. 给出了完整的训练代码,支持自定义开关和模型保存。

现在,你已经具备了从头训练一个多光谱语义分割模型的所有知识。如果训练条件允许,可以实际运行训练,对比预训练模型的效果。


最后预告:训练完成后,我们需要客观地评估模型的性能。第六节将介绍如何计算全局准确率、平均 IoU、F1‑score 等指标,并与真实标签进行对比,验证模型的泛化能力。敬请期待最后一节《模型评估与性能分析》。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐