ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

鲸鱼优化算法+CNN:辐射源识别的超参数自动调优方案

鲸鱼优化算法+CNN:辐射源识别的超参数自动调优方案

简介:面向通信辐射源识别任务的Matlab实现方案,基于鲸鱼算法(WOA)优化卷积神经网络(CNN)分类模型,适合有一定Matlab基础、希望借助智能优化算法提升CNN分类性能的研究者与工程师。代码包共10个文件,以4个M脚本为主,含主函数Main.m、WOA.m、CostFunction.m与getData.m,另有4张分类效果图PNG、1个数据文件xlsx及1个备份文件asv,整体压缩包仅215KB,结构简洁,便于快速开展对照实验。项目在Matlab 2019b环境下可直接运行,通过WOA对CNN关键超参数进行寻优,完成通信辐射源识别,并输出可视化分类结果与优化收敛过程;代码模块划分清晰,主函数与子函数分离,方便替换数据或调整网络结构。资源已有664人学习,内容包含完整源码、效果图及数据样例,既可作为毕业设计、课程项目的完整参考,也可作为智能优化算法与深度学习结合的复现模板,用于算法对比或科研扩展。

1. 鲸鱼算法优化CNN做辐射源识别:一个不靠玄学、能落地的调参方案

通信辐射源识别说白了就是给信号“验指纹”——同一型号的两部电台,发射出来的信号在细微的相位噪声、载波偏移、瞬态特征上都有差异,这些差异足够让机器区分出“这是哪一部设备”。传统做法是人工提特征加上分类器,但特征提得好不好全看经验,换个频段、换个调制方式就得重新来。CNN能端到端自动提特征,省了人工设计特征的功夫,但代价是超参数多得让人头疼:卷积核个数、学习率、池化方式、全连接层节点数,每个都像开盲盒。鲸鱼优化算法(WOA)就是拿来治这个毛病的——把CNN的关键超参数当成鲸鱼种群里的个体,让算法自己迭代去找最优组合。这篇文章就从原理讲到Matlab落地,把WOA-CNN做通信辐射源识别的完整链路和踩过的坑一次说清楚,适合正在做信号分类、深度学习调参调到头秃的研究生和工程师。

2. 为什么是CNN加WOA:辐射源识别的问题结构和选型逻辑

2.1 辐射源识别任务里,CNN到底在学什么

通信辐射源识别的输入通常是IQ两路采样数据,或者经过变换后的时频图。如果用一维CNN直接吃IQ序列,网络学到的是信号的时序结构和瞬态特征;如果先做STFT或小波变换得到时频图,再用二维CNN,学到的是频率随时间演变的纹理特征。两种路线在公开数据集上都有验证过效果,但关键问题不在网络骨架,而在于“信号数据天然带噪声和信道畸变”,网络结构设计不当就很容易过拟合到某一次采集的噪声上。

我一般会选择混合结构——前面几层用一维卷积直接处理IQ序列,保留相位信息,后面接一个全局平均池化把特征图压成向量,再交给全连接层分类。这个结构参数少、不容易过拟合,而且给WOA优化的空间足够大。激活函数优先用ReLU,但最后一层用softmax输出每个辐射源类别的概率。Loss用交叉熵,这在多分类任务里是默认选择。

WOA在这里的价值不是替代CNN,而是替代人工试参。一个中等规模的CNN,要调的学习率、正则化系数、卷积核数量组合,暴力搜索几百组都很正常。WOA的全局搜索能力比网格搜索强,比贝叶斯优化更不容易陷入局部最优,而且在Matlab里实现非常顺手,优化过程可视化也直接。

2.2 鲸鱼算法为什么适合这个场景:三个理由

鲸鱼优化算法模拟的是座头鲸的泡泡网捕食行为,核心机制有三个:包围猎物、气泡网攻击、随机搜索。具体到超参数优化场景,每个“鲸鱼”就是一个候选超参数组合,种群在参数空间里游走,通过适应度函数(也就是验证集准确率)来判断当前位置好不好。

第一个理由是它不需要梯度信息。CNN的超参数不是什么连续可导函数,你没法对“卷积核数量应该从32变成多少”求梯度。WOA是群体智能算法,只靠适应度值就能迭代,跟梯度无关,天然适合这种黑盒优化问题。

第二个理由是探索和开发平衡做得好。WOA里有个随机数p和系数向量A,当p<0.5且|A|<1时做螺旋气泡网攻击(开发),其他情况做随机搜索(探索)。这个机制让算法前期不会扎堆、后期能精确收敛,对超参数这种多峰搜索空间很有效。

第三个理由是实现成本低。WOA不需要复杂的算子设计,位置更新公式就那么几个,Matlab代码量不会超过100行。我经常跟人说,如果你没时间研究算法细节,WOA是性价比最高的调参工具——它不要求你理解背后的数学机制也能用出效果。

2.3 方案整体架构:从信号输入到识别结果

整个WOA-CNN辐射源识别系统分成四个串行模块:数据预处理、CNN模型定义、WOA优化循环、模型验证。

数据预处理模块把采集到的辐射源信号切片成固定长度样本,做归一化,划分训练集和验证集。这一步看起来简单,但样本切多长、重叠率多少、归一化用全局统计量还是逐样本统计,都对后续识别准确率影响很大。CNN模型定义模块把网络结构封装成一个函数,接收一组超参数,返回模型对象。WOA优化循环是整个系统的心脏,每一轮迭代里,算法生成一组超参数,用这组参数训练CNN,得到验证准确率,再反馈给WOA指导下一轮搜索。模型验证模块在优化结束后训练最终模型,在测试集上评估泛化能力,并输出混淆矩阵。

三个关键点需要提前想清楚:一是适应度函数的计算代价,每次评估都要训练一轮CNN,网络深了、数据量大了,一次评估就要几分钟,整个优化可能几十个小时,所以CNN结构不能太重;二是验证集和测试集必须严格分开,否则WOA会过拟合到验证集上;三是超参数的搜索范围要设定合理范围,范围太小没意义,范围太大搜索效率低,这个后面对应参数表细说。

3. 用Matlab实现WOA-CNN:核心代码与逐步拆解

3.1 数据预处理:把辐射源信号切成能喂给CNN的样子

% 读取采集的IQ数据,按辐射源设备ID分组 % data结构: data{i}.iq为Nx2的复数IQ数据矩阵, data{i}.label为设备编号 fs = 50e6; % 采样率 50MHz,根据实际采集设备调整 sampleLen = 4096; % 每个样本的采样点数 overlapRate = 0.5; % 滑窗重叠率50%,数据增强用 allData = []; allLabel = []; for i = 1:length(data) iqData = data{i}.iq(:,1) + 1j*data{i}.iq(:,2); % 合成复信号 nSamples = floor(length(iqData) / (sampleLen*(1-overlapRate))) - 1; for j = 1:nSamples startIdx = (j-1) * sampleLen * (1-overlapRate) + 1; seg = iqData(startIdx : startIdx+sampleLen-1); % 逐样本归一化,避免不同设备发射功率差异造成干扰 seg = seg / (max(abs(seg)) + eps); allData(end+1, :, 1) = real(seg); allData(end, :, 2) = imag(seg); % 两通道分别放实部和虚部 allLabel(end+1) = data{i}.label; end end % 划分训练集、验证集、测试集 rng(42); cv = cvpartition(length(allLabel), 'Holdout', 0.2); idxTrain = training(cv); idxTest = test(cv); XTrain = allData(idxTrain,:,:); YTrain = categorical(allLabel(idxTrain)); XTest = allData(idxTest,:,:); YTest = categorical(allLabel(idxTest));

逻辑说明:这里把每段IQ数据同时保留实部和虚部作为两个输入通道,比只取幅度谱信息量更大,因为相位特征对辐射源个体差异非常敏感。归一化用的是逐样本最大幅度归一化,而不是全局归一化,原因在于通信辐射源识别要区分的是设备本身的指纹特征,而不同设备发射功率不同会导致整体幅值有差异,逐样本归一化能消掉这个干扰。

参数说明:sampleLen取4096点,在50MHz采样率下对应约82微秒的信号时长,这个长度既能覆盖多个符号周期,又不至于让样本数量太少。overlapRate取0.5是数据增强的常见做法,如果原始数据量不足可以提高到0.75,但训练时间也会相应变长。rng(42)固定随机种子保证结果可复现,这个习惯在调参时特别重要,否则你没法判断准确率变化是算法改进了还是数据划分变了。

3.2 WOA主循环:位置更新、边界处理、适应度评估

function [bestPos, bestFit, convergenceCurve] = WOA_CNN(...) % WOA-CNN超参数优化主函数 % 每个鲸鱼个体的位置向量 = [学习率lr, L2正则系数lambda, % 卷积核数量因子convFactor, 全连接层节点数fcNodes] nWhales = 8; % 种群大小:数据量不大时8-10足够 maxIter = 15; % 最大迭代次数:15-20次能收敛 dim = 4; % 超参数维度 lb = [1e-4, 1e-5, 0.5, 32]; % 下界 ub = [1e-2, 1e-2, 2.0, 256]; % 上界 % 初始化鲸鱼种群 positions = repmat(lb, nWhales, 1) + rand(nWhales, dim) .* (ub-lb); fitness = zeros(nWhales, 1); % 评估初始种群 for i = 1:nWhales fitness(i) = evaluateCNN(positions(i,:)); % 训练CNN并返回验证准确率(取负值) end [bestFit, bestIdx] = min(fitness); bestPos = positions(bestIdx, :); convergenceCurve = zeros(maxIter, 1); % WOA迭代主循环 for t = 1:maxIter a = 2 - 2*t/maxIter; % a从2线性递减到0,控制搜索步长 for i = 1:nWhales r1 = rand(); r2 = rand(); A = 2*a*r1 - a; % 系数A向量 C = 2*r2; % 系数C向量 p = rand(); % 随机数p决定捕食策略 if p < 0.5 if abs(A) < 1 % 包围猎物:向当前最优鲸鱼移动 D = abs(C*bestPos - positions(i,:)); positions(i,:) = bestPos - A*D; else % 随机搜索:选一个随机鲸鱼作为目标 randIdx = randi(nWhales); D = abs(C*positions(randIdx,:) - positions(i,:)); positions(i,:) = positions(randIdx,:) - A*D; end else % 气泡网攻击(螺旋更新):模拟座头鲸螺旋吐泡捕食 dist = abs(bestPos - positions(i,:)); positions(i,:) = dist .* exp(randn()*1) .* cos(2*pi*rand()) + bestPos; end % 边界处理:越界个体拉回边界并随机抖动 positions(i,:) = min(max(positions(i,:), lb), ub); positions(i,:) = positions(i,:) + 0.01 * (rand(1,dim)-0.5) .* (ub-lb); positions(i,:) = min(max(positions(i,:), lb), ub); % 计算新适应度 newFitness = evaluateCNN(positions(i,:)); if newFitness < fitness(i) fitness(i) = newFitness; if newFitness < bestFit bestFit = newFitness; bestPos = positions(i,:); end end end convergenceCurve(t) = bestFit; fprintf('Iter %d/%d, best acc = %.4f\n', t, maxIter, -bestFit); end end

逻辑说明:WOA的核心是那三个位置更新分支。p<0.5且|A|<1时,鲸鱼向当前最优个体靠拢,对应“包围猎物”;p<0.5且|A|>=1时,鲸鱼随机选一个同伴作为目标,对应全局探索;p>=0.5时做螺旋更新,让个体绕最优解旋转靠近,对应“气泡网攻击”。a从2线性递减到0是控制勘探和开发平衡的关键,前期大踏步搜索,后期小步收敛。

参数说明:种群大小8、迭代15次看起来数字很小,但注意每次适应度评估都要完整训练一轮CNN,8×15=120次训练,已经足够让算法找到不错的超参数组合。如果你GPU资源充足或者数据量较小,可以把种群加到12、迭代加到20;反过来数据量很大就减少迭代次数。lb和ub是搜索边界,学习率1e-4到1e-2覆盖了Adam优化器常用的有效区间,L2系数1e-5到1e-2覆盖了从几乎不正则化到强正则化的范围,卷积核数量因子0.5到2.0表示基础卷积核数乘以这个因子。

3.3 适应度函数设计:别把时间浪费在训练完整的网上

function val = evaluateCNN(params) % params = [lr, lambda, convFactor, fcNodes] % 返回验证集上的负准确率(WOA最小化) lr = params(1); lambda = params(2); convFactor = params(3); fcNodes = round(params(4)); % 节点数必须取整 % CNN网络结构和超参 numFilters = round(64 * convFactor); % 基础卷积核64个,按因子缩放 layers = [ sequenceInputLayer([4096 2]) % 4096点x2通道 convolution1dLayer(32, numFilters, 'Padding', 'same') batchNormalizationLayer reluLayer maxPooling1dLayer(4, 'Stride', 4) convolution1dLayer(16, numFilters/2, 'Padding', 'same') batchNormalizationLayer reluLayer maxPooling1dLayer(4, 'Stride', 4) fullyConnectedLayer(fcNodes) reluLayer fullyConnectedLayer(numClasses) softmaxLayer classificationLayer]; options = trainingOptions('adam', ... 'InitialLearnRate', lr, ... 'L2Regularization', lambda, ... 'MaxEpochs', 20, ... 'MiniBatchSize', 64, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 30, ... 'Verbose', false); net = trainNetwork(XTrain, YTrain, layers, options); YPred = classify(net, XVal); acc = mean(YPred == YVal); val = 1 - acc; % 适应度取错误率 end

逻辑说明:适应度函数是整个WOA-CNN方案里直接决定优化效果好坏的部分。这里选择验证集准确率作为评价指标,适应度取负值是因为WOA的更新逻辑是最小化问题,取错误率就能直接用原版算法。convFactor乘上基础卷积核数再加round取整,把连续搜索空间映射到离散的CNN结构参数,这是个关键映射技巧——WOA本来只能处理连续变量,通过这种方式就能优化离散结构。

参数说明:训练轮数固定20个epoch,这是平衡精度和速度的经验值。ValidationFrequency设为30意味着每30次迭代计算一次验证准确率,能监控训练过程但不会太频繁导致额外开销。这里没有用Plots参数开启训练曲线可视化,因为在优化循环里弹几百个图会直接把内存撑爆,想看训练过程就每轮选最优参数单独做一次带可视化的完整训练。

4. WOA-CNN参数配置与调试:一份能直接参考的设置清单

4.1 超参数搜索范围设置逻辑

参数搜索范围默认值/基础值设置依据
学习率lr1e-4 ~ 1e-21e-3Adam对学习率敏感,低于1e-4收敛太慢,高于1e-2容易震荡
L2正则系数1e-5 ~ 1e-21e-4辐射源数据往往样本量不大,正则化太小会过拟合
卷积核数量因子0.5 ~ 2.01.064个基础卷积核,因子0.5时32个、2.0时128个
全连接层节点数32 ~ 256128辐射源类别数一般在5-20之间,节点太少表达力不足,太多容易过拟合

这个表格的关键在于搜索范围的设定不能凭空拍脑袋,最好先手动跑一两次固定参数的训练,观察训练集和验证集准确率的关系。如果训练集很快到99%而验证集只有80%,说明过拟合了,把L2正则的上限再往上调大一些。如果两端都上不去,可能是学习率范围设置有问题或者网络结构本身表达能力不够。

4.2 WOA自身参数:种群大小和迭代次数的调试经验

WOA的参数不是越多越好。我见过有人把种群设到30、迭代设到50,结果一次优化跑了两天,效果跟种群10迭代20的差别其实不大。原因是CNN超参数优化问题的维度只有4-6个,搜索空间并不是特别高维,小种群配合适当的迭代次数就够了。

建议的调试策略是:第一轮先用种群6、迭代10跑通流程,验证代码没问题;第二轮把种群加到10、迭代加到20,看收敛曲线有没有明显下降;如果20次迭代后曲线还在下降,说明需要更多迭代,加到30到40。如果曲线在10次迭代就平坦了,说明种群大小是瓶颈,适当增加种群而不是迭代次数。

另外注意一个容易被忽略的点:WOA的随机性导致每次优化结果都有波动。同一个搜索范围跑三次,最优准确率可能差1%到2%。不要因为某次结果特别好就觉得是模型的功劳,多做几次试验取中位数才是客观评估。

4.3 训练选项里的坑:MiniBatchSize和ValidationFrequency的配合

训练选项里有个容易让新手翻车的地方:ValidationFrequency设置得太小,大批量验证会严重拖慢训练。比如MiniBatchSize=64,训练集有10000个样本,每个epoch约156次迭代,ValidationFrequency设为30就意味着每个epoch验证两次,如果验证集又比较大,额外开销不容小觑。

经验值是这样配合的:MiniBatchSize取32到128之间,ValidationFrequency设为floor(训练样本数/MiniBatchSize/2),也就是每个epoch验证两次。在WOA优化循环里,每轮训练的epoch数本来就不多,验证频率适中即可,重点确保每一轮训练能完整跑完且得到稳定的验证准确率。

还有个技巧是给训练选项加'OutputFcn', @(info)stopIfAccuracyNotImproving(info, 5),当验证准确率连续5个epoch不提升时就提前终止训练。这能把每轮适应度评估的时间缩短30%到50%,尤其当WOA搜索到接近最优超参数时,CNN的收敛速度变快,提前终止能省下大量时间。但这个函数要自己写,在info.ValidationAccuracy连续没有提升时返回true。

5. WOA-CNN通信辐射源识别避坑指南:五个值得记录的翻车现场

5.1 训练集和验证集混入了同一个设备的数据段,导致准确率虚高

现象:WOA优化过程中,验证集准确率能到百分之九十八九,但优化结束后用测试集一测只有百分之八十不到,差距大得离谱。

原因:数据预处理时滑窗切段没有按设备ID分组,同一个设备的相邻信号段可能同时出现在训练集和验证集里。CNN学到的不是辐射源设备的指纹特征,而是哪段信号跟哪段信号相邻的“作弊模式”。

解决:划分数据时先按设备ID把样本分组,再用cvpartition在设备级别做分层划分,确保同一个设备的样本不会同时出现在训练集和验证集。检查方法很简单:训练集和验证集的样本来源设备ID集合不能有交集,代码里加个断言就能防住。

5.2 适应度评估时CNN每次重头训练,优化周期长到让人怀疑人生

现象:种群8、迭代15,每轮适应度评估训练20个epoch,总训练次数120次,加上数据预处理和验证开销,整个优化跑了将近30个小时。

原因:每次训练都是冷启动随机初始化权重,前几个epoch都在做无用功。另外训练选项没有设置任何收敛加速机制。

解决:在WOA优化之前,先确定好CNN的基础结构并用小数据集跑通流程;每轮训练用OutputFcn做早停;训练数据量大的话考虑只在子集上做适应度评估,等WOA收敛后再用全量数据训练最终模型。还有一个常用的加速技巧是给WOA加记忆机制——记录已经评估过的超参数组合,避免重复训练相同或非常相似的组合,实现一个哈希表缓存能省掉大约10%到15%的冗余计算。

5.3 WOA位置更新后超参数变成负数或超出合理范围,CNN直接报错

现象:迭代到中途Error using trainNetwork,提示学习率为负或者全连接层节点数为0,整个优化中止。

原因:WOA的位置更新公式不保证新位置在搜索边界内,比如螺旋更新时cos(2*pi*rand())可能取负值,导致学习率变成负数。边界处理只做了截断,但如果某个维度触发了边界后又叠加了随机抖动,依然可能越界。

解决:边界处理要分成两步——第一步min(max(positions, lb), ub)做硬截断保证基本不越界;第二步做随机扰动后再次硬截断,也就是代码里写的那样做两次边界检查。全连接层节点数这类离散参数,必须在计算适应度时用round取整,同时在取值后检查是否小于2,太小就强制设为2或lb值。

5.4 CNN模型权重初始化随机性让适应度评估充满噪声,WOA难以稳步收敛

现象:WOA的收敛曲线不下降反而上下剧烈波动,相邻两次迭代的最优适应度差2%到3%,算法很难判断哪个位置真的更好。

原因:CNN每次训练都是随机初始化权重,同样的超参数跑两次验证准确率可能差1%到2%。WOA被这个噪声干扰,可能把一次运气好的位置当成最优解,导致搜索方向错误。

解决:一个可行方案是每次适应度评估时固定随机种子,但trainNetwork内部无法直接通过外部参数固定所有随机源,更实用的做法是每轮评估用两次训练取平均预测准确率,虽然训练时间翻倍,收敛稳定性显著提升。另一个治本方案是在WOA优化之前,先花少量时间手动测试几组超参数,估算CNN训练本身的随机波动幅度,如果波动超过1%,就优先增大验证集样本量而非调整WOA参数。某些开发者也建议同步固定rng种子,把数据打乱顺序固定下来,减少数据层面的随机性。

5.5 辐射源数据类别不平衡,WOA只优化“大多数类”的准确率

现象:五类辐射源设备中,有一类设备采集的样本量占了60%,WOA优化出来的模型对这类设备识别准确率很高,但对其他四类设备只有50%左右,整体准确率却看起来不错。

原因:CNN默认的交叉熵损失在类别不平衡时偏向样本量多的类,WOA的目标函数是整体准确率,无法感知小类别的表现。

解决:有两个层面的方案。数据层面用少数类过采样,让每个类别的样本数量基本一致;算法层面改一下适应度函数,把整体准确率换成宏平均F1分数,这样小类别的表现会被公平加权。如果设备实际工作频率差异很大,还可以考虑分频段单独建模,避免类别不平衡掩盖结构性问题。

6. 把WOA-CNN用得更顺手的三个进阶技巧

优化结束后别急着拿去跑测试集,先做一步“再训练”或者“微调”。WOA搜索到的最优超参数是在有限epoch下评估出来的,真实部署时可以用这个超参数组合,把训练epoch从20加到50或100,学习率按余弦退火从初始值降到初始值的十分之一。这个“优化阶段粗训+最终阶段精训”的两段式策略,通常能再提升1%到3%的准确率,几乎零成本。

第二个技巧是画“超参数敏感性热力图”。WOA收敛后,把每个超参数在其最优值附近上下浮动20%到30%,重新训练几次并记录准确率变化,能看出哪个参数对结果最敏感。如果学习率稍微一动准确率就掉1%以上,说明这个优化结果不可靠,需要扩大搜索范围重新跑;如果全连接层节点数从128变成80准确率变化不大,说明这个参数不重要,下次优化可以直接固定,降维度提高搜索效率。

第三个技巧是把WOA的收敛曲线和最终混淆矩阵保存下来做归档。辐射源识别项目通常要做多次迭代验证,有据可查才能说明方案的可靠性。每轮优化记录搜索范围、最优超参数、收敛曲线、测试集准确率、每个类别的精确率和召回率,形成一个参数档案表。当后续数据更新时,对比新旧结果能快速定位是数据漂移还是模型过时。

这几次做WOA-CNN辐射源识别的经历让我积累了一条血泪经验:不要一上来就把WOA的搜索范围设得很大,先用小范围验证整个pipeline通顺,再逐步扩大搜索空间。搜索范围大的确能找到更优的参数,但如果代码里有bug,大范围只会让bug的影响更难排查。先用小范围把流程跑通、确认能复现结果,再放开手脚做完整优化,这条路走起来最踏实。希望这些思路对你手头的辐射源识别项目能有点实际帮助。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进