
简介这套基于Python深度学习的光纤传感水声信号识别方法完整实现面向毕业设计、人工智能与信号处理方向的开发者解决光纤传感系统中水声信号准确分类与水下环境监测问题。方法采用最优聚类模型并以光纤传感系统自身固有噪声信号分解分量作为训练数据降低了对额外数据采集的依赖。资源共276个文件压缩包约6.93MB包含11个Python脚本、7个Jupyter Notebook、209张图片、14个CSV特征文件、12个PKL模型文件、4个PTH权重文件及说明文档覆盖数据处理、特征提取、模型训练与可视化全过程。目前已有46人学习。读者可获得完整可运行代码、特征数据集、训练好的模型权重以及结构清晰的目录便于直接复现实验、理解聚类与深度学习的结合方式并可在此基础上进行参数调整与扩展研究适用于水下目标探测、环境监测等场景。1. 为什么“固有噪声分解分量”可以作为水声信号识别的原始训练数据识别水声信号最难拿到的往往不是模型而是干净的正样本。光纤传感系统光纤水听器、分布式声学传感在现场会同时收到水流、平台振动、生物扰动和环境机械噪声真实水声片段要么标注成本高要么类别极不均衡。一个可行的突破口是先采集光纤传感系统自身固有噪声比如激光相对强度噪声、光电探测器散粒噪声、探头附近低频机械振动再用 VMD 或 EMD 这类方法把噪声分解成多个本征模态分量以这些分量作为原始训练数据去训练最优聚类模型。背后的判断是水声信号本质上是“不属于任何已知噪声分布”的异常模式。因此不需要事先准备大量真实水声样本只要把噪声基底建模得足够细就能在高维特征空间用距离把水声帧挑出来。这套做法尤其适合岸站长期监测和分布式光纤传感的入侵识别。2. 信号分解把固有噪声拆成可训练的分量与特征矩阵直接把原始时域波形送进深度网络不是不行但光纤传感信号的信噪比往往低于 -20 dB模型很容易把噪声的统计起伏当成“特征”。常见做法是在输入端先做模态分解把一帧噪声拆成一组有限带宽的分量再用这些分量构造训练矩阵。分解这一步决定了后面聚类能不能形成清晰边界值得先把参数和采集条件说清。2.1 为什么在光纤传感上优先用 VMD 而不是 EMD 或小波包EMD 的好处是不需要预设基函数但在光纤传感噪声上模态混叠和端点效应很明显同一频带在不同帧里可能被拆到不同分量导致后续聚类不稳定。小波包分解需要选小波基和分解层数选得不合适会把窄带噪声切碎。相比之下VMD变分模态分解把信号分解为 K 个具有中心频率的带限本征模态函数每个模态在频域上有明确位置更适合用来构造稳定的特征空间。分解方法是否需要预设参数在光纤噪声数据上的主要问题适用性EMD不需要模态混叠、端点效应分量不固定不适合直接喂聚类模型小波包需要小波基、层数基函数依赖经验分解结果解释性弱适合做滤波不适合做基底建模VMD需要 K 和 alphaK 选错会出现中心频率重叠中心频率稳定适合后续聚类VMD 也不是没有代价。它要求提前指定模态数量 K这个 K 和后面聚类的簇数不是同一个概念。VMD 的 K 是“把一帧信号切成几个频带”聚类簇数是“整个噪声库里有几种稳定状态”。两者如果混为一谈后面调参会很难收场。2.2 采集“干净固有噪声”的三个前提要让 VMD 分解出来的是系统自身噪声现场采集时要满足三个条件。第一选择没有人工声源和水流扰动的时段比如凌晨或设备检修窗口连续采集 5 到 10 分钟。第二如果环境噪声仍然躲不掉可以在探头表面加阻尼材料模拟“无水声激励”状态。第三原始解调相位数据先做预处理去掉直流分量、做 50 Hz 工频陷波、再按帧切分。帧长一般取 4096 或 8192 个采样点重叠 50%。如果采样率是 50 kHz4096 点对应约 82 ms能分辨到几十赫兹以上的水声信号。对更低频的水声需要把帧长加到 8192 甚至 16384否则 VMD 的低频模态实际中心频率会不稳定。2.3 VMD 分解的最小可用代码以下代码使用了常见的vmdpy封装负责把一维噪声帧分解成 K 个模态并返回每个模态的中心频率。from vmdpy import VMD import numpy as np def decompose_frame(frame, fs, K5, alpha2000): frame: 一维时间序列长度建议 4096 或 8192 fs : 采样率单位 Hz 返回: modes : (K, N) 的模态分量 center_freqs : (K,) 每个模态的物理中心频率 u, u_hat, omega VMD( frame, alpha, tau0, KK, DC0, init1, tol1e-7 ) # omega 最后一行为归一化中心频率范围 0 到 pi center_freqs omega[-1, :] * fs / (2 * np.pi) return u, center_freqs这段代码里VMD会内部完成维纳滤波和希尔伯特变换来估计瞬时频率。omega最后一行的值需要乘以fs / (2π)转成物理频率。tau0表示不启用噪声补偿项DC0表示不要把直流单独拆成一个模态init1表示初始中心频率均匀初始化。参数调试时可以这样看alpha 越大每个模态的带宽越窄抗混叠能力增强但过大的 alpha 会让模态丢失细节K 设得太大两个相邻模态的中心频率会挤在一起甚至出现镜像对称的假模态。一般先用 Welch 功率谱数一下明显峰值个数把峰值数作为 K 的起点。2.4 从分解分量构造固定维度的特征向量每一帧会得到 K 个模态直接把这些长度不一的模态序列平均起来不现实。我对每个模态提取 5 个标量特征这样一帧的特征维度可以固定为K * 5 1最后的 1 是原始帧的短时标准差。特征名计算对象说明RMS每个模态表示该频带幅度的强弱中心频率每个模态VMD 本身会输出直接保留峰值因子每个模态最大值除以 RMS反映脉冲成分排列熵每个模态衡量时序复杂度水声与机械噪声差异较大能量占比每个模态该模态 RMS 平方占全帧比例短时标准差原始帧补充未分解前的总能量信息如果 K5每帧特征向量维度就是 26。把这套特征按时间顺序排列就得到训练聚类模型的输入矩阵。需要强调在帧滑动时不要引入未来信息重叠部分必须只来自过去否则在线部署时特征分布会偏。3. 最优聚类模型从噪声分解分量中确定聚类数与噪声基底特征矩阵拿到之后下一步是训练一个“最优聚类模型”。这里的关键不是随便跑一个 K-Means 就结束而是要同时解决两个问题用深度网络对特征做非线性压缩以及在多个候选聚类数中选择真正符合噪声物理状态的 K。3.1 深度嵌入先用自编码器压缩特征VMD 得到的 5 个特征之间并不是独立的。能量占比和 RMS 高度相关排列熵和峰值因子也会互相影响。如果直接在 26 维空间上聚类轮廓系数会被冗余维度抬高很多看似分开的簇在物理上并不稳定。常见做法是先用一个自编码器把特征压到 3 到 6 维的隐空间再做聚类。import torch import torch.nn as nn class NoiseAE(nn.Module): def __init__(self, in_dim26, latent4): super().__init__() self.encoder nn.Sequential( nn.Linear(in_dim, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, latent), ) self.decoder nn.Sequential( nn.Linear(latent, 32), nn.ReLU(), nn.Linear(32, 64), nn.ReLU(), nn.Linear(64, in_dim), ) def encode(self, x): return self.encoder(x) def forward(self, x): z self.encoder(x) return self.decoder(z), z训练时用 Adam 优化器学习率 1e-3batch size 256MSE 损失跑 200 epoch。隐空间维度取 4 一般够用。隐空间太大会把 VMD 对噪声的敏感细节也保留下来聚类结果反而不稳定太小又会丢掉低频段差异。我一般会在训练集上观察重建误差同时把连续两天采集的噪声放在验证集里如果验证重建误差超过训练误差 2 倍说明噪声本身已经出现漂移需要重新采集。3.2 用 GMM 作为聚类模型并按 BIC 选择最优分量数相比硬聚类 K-MeansGMM高斯混合模型更适合光纤传感噪声基底。原因很简单同一噪声源在不同温度、不同偏置电流下幅度和中心频率会有缓慢漂移GMM 允许一个样本同时属于多个簇并输出概率。在深度聚类里常见的做法是先用 K-Means 初始化 GMM再用 EM 算法迭代拟合。import numpy as np from sklearn.mixture import GaussianMixture from sklearn.metrics import silhouette_score def select_gmm(Z, k_rangerange(2, 13)): best_gm None best_k 0 best_bic np.inf for k in k_range: gm GaussianMixture( n_componentsk, covariance_typediag, init_paramskmeans, random_state0, ).fit(Z) bic gm.bic(Z) if bic best_bic: best_bic bic best_k k best_gm gm labels best_gm.predict(Z) sil silhouette_score(Z, labels) return best_gm, best_k, sil这段代码把 BIC 作为主要选择标准。BIC 会惩罚参数过多的模型所以它通常比只看似然更不容易过拟合。但要注意BIC 在真实的连续噪声分布上可能随着 K 增加一直下降这时我们不能只选分数最大的 K。结合轮廓系数一起看比较稳轮廓系数大于 0.3 且 BIC 曲线出现明显拐点才说明聚类结构真的存在。3.3 聚类数的物理校验指标只是第一步。每个 GMM 聚类应该能被解释为一种典型噪声状态否则聚类再紧凑也没有工程价值。我在实际项目里会用下面这套校验表检查项通过标准每个聚类的样本占比不少于总样本数的 3%任意两个聚类中心的最小距离标准化后大于 0.5簇内样本的时域重构频谱形状一致不包含脉冲型突变各聚类对应的时间段与原始日志中的环境状态能对应上如果某个聚类只包含极少数样本而且这些样本在时间轴上随机散布那它大概率是 VMD 分解中的瞬时颤振不是稳定噪声模式。把它保留在模型里水声信号反而会被它吸收导致漏报。3.4 注意区分 VMD 的 K 和 GMM 的聚类数这是新手最容易绕晕的地方。VMD 的 K 是单帧频带切分数解决“一帧噪声怎么拆开”GMM 的聚类数是整个数据集的噪声模式数量解决“现场有哪几类典型噪声状态”。如果 VMD 的 K 设得太大会把一条宽带噪声拆成多个窄带分量这些分量在特征空间里很分散GMM 就会看到很多假簇如果 VMD 的 K 设得太小宽带水声信号会被压在某个模态里导致后续距离打分失效。先固定 VMD 的 K再调 GMM 的聚类数不要两个参数同时搜索。4. 水声信号识别用噪声聚类结果构造异常打分与分类器在最优聚类模型训练完成后整个系统已经知道了“光纤传感系统自身固有噪声”在隐空间中的分布。剩下的问题是如何用这个分布去判决一个全新帧是噪声还是水声。这里有两种路径可以同时用一种是直接计算新帧到已知噪声簇的距离另一种是把聚类结果作为伪标签去训练一个轻量级深度分类器。4.1 从 GMM 分量到距离打分对于 GMM 的每个高斯分量可以计算新样本隐向量 z 到该分量中心的马氏距离最后取所有分量中的最小值作为该帧的噪声偏离度。用马氏距离而不是欧氏距离是因为不同噪声簇在隐空间各方向上的方差差别很大欧氏距离会把高方差方向上的正常波动误判为异常。import numpy as np def noise_score(z, gmm): min_score np.inf for mu, cov in zip(gmm.means_, gmm.covariances_): inv_cov 1.0 / (cov 1e-6) delta z - mu mahal np.sqrt(np.sum(delta * delta * inv_cov, axis1)) min_score np.minimum(min_score, mahal) return min_score这段代码假设 GMM 使用diag协方差所以协方差矩阵可以直接按元素取倒数。如果换成full协方差就需要用np.linalg.solve解线性方程但光纤传感特征维度通常不高diag已经够用。4.2 阈值选择与连续帧去抖对训练噪声库里的每一帧都计算一个 score就可以得到噪声分数分布。把分布的 95%、99%、99.9% 分位数分别作为候选阈值具体效果参考下表阈值位置误报率响应速度适用阶段95% 分位数高几分钟内可能误报单帧即可触发只适合实验室验证99% 分位数中等适合初步上线需要结合连续帧判定推荐首版采用99.9% 分位数低漏掉小信号的风险高稳定但迟钝长期无人值守时配合日志复核单帧 score 超标不一定就是水声。光纤传感系统经常出现随机尖峰噪声持续时间只有几毫秒。我一般会加一条连续帧规则连续 3 帧中至少有 2 帧超标且超标状态持续 50 ms 以上才输出一次水声事件。这个规则能把大部分随机尖峰滤掉同时不会压低真正的脉冲型水声信号。4.3 用少量真实水声样本校正阈值尽管训练数据全部来自固有噪声系统部署时通常还会拿到一小批真实水声片段。这批数据量可能只有几十到几百条直接用来重新训练 VMD 和 GMM 会过拟合。正确做法是只拿它来搜索最佳阈值。from sklearn.metrics import f1_score train_scores ... # 训练噪声帧的 score test_scores ... # 测试帧的 score test_labels ... # 0 表示噪声1 表示水声 best_t, best_f1 0, 0 for p in [90, 95, 99, 99.5, 99.9]: t np.percentile(train_scores, p) pred test_scores t score f1_score(test_labels, pred) if score best_f1: best_t, best_f1 t, score注意这里计算 F1 时会遇到类别严重不平衡。如果测试集中水声帧只占 1%即使全部预测为噪声F1 也会因为低召回率而不合格。更实用的做法是把测试集按事件切分只要一个事件里有超过一半帧被检测到就算该事件命中而不是对每一帧求 F1。4.4 再用 VMD 分量训练一个一维 CNN 作为快速分类器距离打分在隐空间里工作对现场部署稍微有点麻烦因为每个新帧必须经过 VMD、特征提取、自编码器、GMM 打分四步。如果算力紧张还可以用 VMD 分解分量堆叠作为输入训练一个一维 CNN直接把“该不该报警”学进去。训练时噪声帧标为 0距离打分超标的候选帧标为 1。import torch.nn as nn class VMDNet(nn.Module): def __init__(self, K5, N4096): super().__init__() self.conv nn.Sequential( nn.Conv1d(K, 16, 5, padding2), nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(4), nn.Conv1d(16, 32, 5, padding2), nn.BatchNorm1d(32), nn.ReLU(), nn.AdaptiveAvgPool1d(16), nn.Flatten(), ) self.fc nn.Sequential( nn.Linear(32 * 16, 64), nn.ReLU(), nn.Linear(64, 2), )输入张量的形状是(batch, K, N)也就是一帧 VMD 分解后的 K 个模态直接作为通道。CNN 的作用是学习模态之间的频率拓扑关系。需要提醒的是伪标签中“标为 1”的帧本身可能包含部分强噪声所以 CNN 不能单独承担最终判决通常是final_score 0.6 * cnn_prob 0.4 * normalized_distance做软融合。5. 在线识别时的三个关键细节阈值、新噪声聚类与模型迭代噪声聚类模型上线之后最大的坑不是算法本身而是现场噪声分布的缓慢漂移。温度变化会让激光器中心波长偏移平台振动状态会改变低频噪声能量光纤链路衰减也会让解调相位噪声变大。面对这些变化最重要的是保持模型可以迭代而不是期望一个静态模型永远有效。5.1 漂移监控而不是频繁调阈值我会每天对当天所有噪声帧重新计算一次 score 序列并记录 99% 分位数。如果连续三天这个值比上线第一周上升超过 20%第一反应不应该是调高阈值而是先检查系统和环境。阈值一旦被调高真正的水声小信号也会跟着消失。更合理的做法是每天生成一份 score 分布曲线人工观察分布形状从中峰变成了多峰往往意味着出现了新的噪声源。5.2 新增噪声聚类的合并流程当监控发现大量未命中噪声簇但人工复核确认是环境噪声的样本时可以给它们执行一次独立聚类。步骤是把这些帧的隐向量 z 缓存下来用 DBSCAN 观察它们是否形成连续簇如果某个簇样本数超过 200 且中心与已有 GMM 分量距离较远就把它作为一个新的 GMM 分量加入模型。注意不要每个月都加否则水声信号会被逐渐吸收成“已知噪声”。5.3 每次告警都记录隐向量我一般会在日志系统里把每次告警的帧号、score、隐向量 z 一起保存。一周之后把 z 全部取出来再跑一次轮廓系数。如果发现新增簇的轮廓系数很高就直接把该簇加入噪声模型如果轮廓系数很低说明是区间噪声不需要处理。这样做的收益是可以随时回放上一次聚类是在什么数据上得到的模型漂移时也有一条可追溯的路径。在光纤传感系统里水声信号的声压级通常很微弱但它的时频纹波足以让 VMD 分解后的分量分布发生偏移。先用固有噪声把聚类基底做稳再谈识别精度远比重金标注真实水声样本、直接训练大模型更可持续。本文还有配套的精品资源点击获取