1. 为什么选择Matlab做深度学习?

很多朋友第一次接触深度学习都是从Python开始的,TensorFlow和PyTorch确实占据了主流市场。但如果你已经熟悉Matlab的工程计算环境,想快速验证算法原型,或者需要可视化调试网络结构,Matlab其实是个被低估的选择。

我最初用Matlab做深度学习是为了处理医学影像项目。当时团队里既有临床医生又有算法工程师,Matlab的 Deep Network Designer 工具让我们能直观地讨论网络结构,连不懂代码的医生都能指着网络图说"这个卷积层是不是应该再加个池化"。这种协作效率是命令行工具难以比拟的。

Matlab的独特优势主要体现在:

  • 零代码设计网络 :像搭积木一样拖拽网络层,特别适合教学演示和快速原型开发
  • 数据预处理全家桶 :内置图像增强、音频变换、信号滤波等工具,不用再装OpenCV等第三方库
  • 与Simulink无缝集成 :直接把训练好的模型部署到仿真系统
  • 自动生成代码 :一键转成C/C++或HDL代码部署到嵌入式设备

实测发现:当处理雷达信号、医学影像等专业领域数据时,Matlab现成的数据处理函数比用Python写循环要高效得多

2. 环境准备与数据导入

2.1 硬件软件配置

推荐使用 Matlab 2020b及以上版本 ,这个版本开始Deep Learning Toolbox支持了更多现代网络结构。我的开发环境是:

  • Windows 10 + Matlab 2022a
  • NVIDIA RTX 3060显卡(4GB显存够跑大多数分类任务)
  • CUDA 11.3 + cuDNN 8.2

如果只有CPU也不用慌,Matlab会自动调用MKL数学库加速。曾经在Intel i7上跑MNIST分类,一个epoch大概20秒,完全可以接受。

2.2 准备MNIST数据集

经典的MNIST手写数字集包含6万张28x28灰度图。Matlab其实内置了这个数据集,但为了演示完整流程,我们手动下载:

% 创建数据存储目录
datasetPath = fullfile(pwd,'MNIST');
if ~exist(datasetPath, 'dir')
    mkdir(datasetPath);
end

% 下载压缩包
url = 'http://yann.lecun.com/exdb/mnist/';
files = {'train-images-idx3-ubyte.gz', 'train-labels-idx1-ubyte.gz', ...
         't10k-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz'};
for i = 1:length(files)
    gunzip([url files{i}], datasetPath);
end

解压后需要转换文件格式。这是我常用的转换函数:

function [images, labels] = readMNIST(imageFile, labelFile)
    % 读取图像文件
    fid = fopen(imageFile, 'r', 'b');
    magicNum = fread(fid, 1, 'int32');
    numImages = fread(fid, 1, 'int32');
    numRows = fread(fid, 1, 'int32');
    numCols = fread(fid, 1, 'int32');
    
    images = fread(fid, inf, 'uint8');
    images = reshape(images, numCols, numRows, numImages);
    images = permute(images, [2 1 3]); % 调整维度顺序
    images = images./255; % 归一化
    fclose(fid);
    
    % 读取标签文件
    fid = fopen(labelFile, 'r', 'b');
    magicNum = fread(fid, 1, 'int32');
    numLabels = fread(fid, 1, 'int32');
    labels = fread(fid, inf, 'uint8');
    labels = categorical(labels);
    fclose(fid);
end

加载数据时建议使用 imageDatastore augmentedImageDatastore ,它们能自动处理批量读取和内存管理:

[XTrain, YTrain] = readMNIST(...
    fullfile(datasetPath,'train-images-idx3-ubyte'),...
    fullfile(datasetPath,'train-labels-idx1-ubyte'));

[XTest, YTest] = readMNIST(...
    fullfile(datasetPath,'t10k-images-idx3-ubyte'),...
    fullfile(datasetPath,'t10k-labels-idx1-ubyte'));

% 转换为4D数组 [高度 宽度 通道数 样本数]
XTrain = reshape(XTrain, [28,28,1,size(XTrain,3)]); 
XTest = reshape(XTest, [28,28,1,size(XTest,3)]);

3. 用Deep Network Designer构建CNN

3.1 可视化网络设计

在Matlab命令窗口输入 deepNetworkDesigner 打开可视化工具。界面分为三部分:

  • 左侧是层库(包含卷积层、全连接层等)
  • 中间是画布区域
  • 右侧是属性检查器

我们构建一个改进版LeNet-5:

  1. 从左侧拖拽 Image Input Layer ,设置InputSize为[28 28 1]
  2. 添加 Convolution 2D Layer ,设置NumFilters=6, FilterSize=[5 5]
  3. 添加 Batch Normalization Layer (原版LeNet没有这个)
  4. 添加 ReLU Layer (替换原版的tanh)
  5. 添加 Max Pooling 2D Layer ,设置PoolSize=[2 2], Stride=[2 2]
  6. 重复步骤2-5,第二卷积层设置NumFilters=16
  7. 添加 Fully Connected Layer ,设置OutputSize=120
  8. 添加 Dropout Layer ,设置Probability=0.5
  9. 添加 Fully Connected Layer ,OutputSize=84
  10. 最后添加 Softmax Layer Classification Layer

设计技巧:鼠标悬停在层与层之间的连线上,会显示特征图尺寸变化。如果看到尺寸意外缩小/放大,说明参数设置有问题

3.2 网络分析与导出

点击右上角的 Analyze 按钮,Matlab会检查网络结构的有效性。常见错误包括:

  • 层间尺寸不匹配(如池化后特征图变成非整数)
  • 最后一层输出尺寸与分类数不符
  • 缺失必要的层(如图像输入层或分类层)

确认无误后,点击 Export 生成代码:

layers = [
    imageInputLayer([28 28 1], 'Name', 'input')
    
    convolution2dLayer([5 5], 6, 'Padding', 'same', 'Name', 'conv1')
    batchNormalizationLayer('Name', 'bn1')
    reluLayer('Name', 'relu1')
    maxPooling2dLayer([2 2], 'Stride', [2 2], 'Name', 'pool1')
    
    convolution2dLayer([5 5], 16, 'Padding', 'same', 'Name', 'conv2')
    batchNormalizationLayer('Name', 'bn2')
    reluLayer('Name', 'relu2')
    maxPooling2dLayer([2 2], 'Stride', [2 2], 'Name', 'pool2')
    
    fullyConnectedLayer(120, 'Name', 'fc1')
    dropoutLayer(0.5, 'Name', 'dropout1')
    fullyConnectedLayer(84, 'Name', 'fc2')
    softmaxLayer('Name', 'softmax')
    classificationLayer('Name', 'output')];

4. 训练与评估模型

4.1 配置训练选项

关键参数解析:

options = trainingOptions('adam', ...       % 优化器
    'InitialLearnRate', 0.001, ...         % 初始学习率
    'LearnRateSchedule', 'piecewise', ...  % 分段学习率
    'LearnRateDropFactor', 0.1, ...        % 学习率衰减系数
    'LearnRateDropPeriod', 10, ...         % 每10个epoch衰减一次
    'MaxEpochs', 30, ...                   % 最大训练轮次
    'MiniBatchSize', 128, ...              % 批大小
    'Shuffle', 'every-epoch', ...          % 每轮打乱数据
    'ValidationData', {XTest, YTest}, ...  % 验证集
    'ValidationFrequency', 50, ...         % 每50次迭代验证一次
    'Plots', 'training-progress', ...      % 显示训练曲线
    'ExecutionEnvironment', 'auto');       % 自动选择CPU/GPU

4.2 启动训练

使用 trainNetwork 函数开始训练:

[net, info] = trainNetwork(XTrain, YTrain, layers, options);

训练过程中会实时显示:

  • 训练/验证准确率曲线
  • 损失值变化
  • 当前epoch和迭代进度

如果发现验证集准确率一直不升,可能是:

  1. 学习率太大导致震荡 → 调低InitialLearnRate
  2. 模型容量不足 → 增加卷积核数量
  3. 过拟合 → 加大Dropout概率

4.3 模型测试与部署

训练完成后,用测试集评估:

YPred = classify(net, XTest);
accuracy = sum(YPred == YTest)/numel(YTest);
disp(['测试准确率: ', num2str(accuracy*100), '%'])

保存模型有两种方式:

% 保存整个网络(包含权重)
save('mnist_model.mat', 'net'); 

% 导出为ONNX格式(可部署到其他框架)
exportONNXNetwork(net, 'mnist.onnx');

5. 实战技巧与避坑指南

5.1 数据增强策略

对于小样本数据集,可以用 imageDataAugmenter 增加多样性:

augmenter = imageDataAugmenter(...
    'RandRotation', [-20 20], ...  % 随机旋转
    'RandXTranslation', [-3 3], ... % 水平平移
    'RandYTranslation', [-3 3], ... % 垂直平移
    'RandScale', [0.9 1.1]);       % 随机缩放

augimds = augmentedImageDatastore([28 28], XTrain, YTrain, ...
    'DataAugmentation', augmenter);

然后在 trainNetwork 中使用augimds代替XTrain。

5.2 使用预训练模型

Matlab提供了ResNet、GoogLeNet等模型:

net = resnet18('Weights', 'imagenet');
layers = net.Layers(1:end-3); % 去掉最后三层
layers = [layers
    fullyConnectedLayer(10, 'Name', 'new_fc')
    softmaxLayer
    classificationLayer];

迁移学习时通常需要:

  1. 冻结前面层的权重:设置 WeightLearnRateFactor=0
  2. 降低学习率: InitialLearnRate=1e-4
  3. 使用更小的批大小: MiniBatchSize=32

5.3 常见错误排查

  • GPU内存不足 :减小MiniBatchSize,或在训练前执行 gpuDevice(1) 清理显存
  • 训练loss为NaN :检查数据是否有非法值(如Inf),或降低学习率
  • 验证集准确率波动大 :增加ValidationFrequency,确保验证时模型处于eval模式

最后分享一个调试技巧:用 activations 函数可视化中间层输出:

conv1_out = activations(net, XTest(:,:,:,1), 'conv1');
montage(rescale(conv1_out(:,:,:,1:6))) % 显示前6个特征图

更多推荐