ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

电子鼻气体识别:ReLU+Adam将BP网络准确率从44.6%提升至93.3%

电子鼻气体识别:ReLU+Adam将BP网络准确率从44.6%提升至93.3% 简介这份PDF文档围绕基于电子鼻的气体识别神经网络算法展开面向从事气体检测、传感器信号处理与深度学习建模的研究人员及学生帮助解决甲烷、乙烷、丙烷、氨气、乙醇等常见危险气体的快速准确识别问题。资源包内仅含1个PDF文件大小约2.12MB内容完整呈现了从电子鼻系统搭建、气体样本选择、数据采集与清洗到神经网络模型优化的研究全貌。文中以日本FIGARO公司6款金属氧化物半导体传感器构建电子鼻阵列采集10950组有效数据并重点对比ReLU激励函数与Adam优化算法对传统BP网络的改进效果实验显示训练速度与准确率最大提升48.1%五类气体识别中训练集与测试集准确率分别达93.3%和92.1%。目前已有255人学习适合希望借鉴气体识别建模思路、了解深度学习在电子鼻领域应用潜力的读者参考。1. 电子鼻气体识别从 44.6% 到 93.3% 的那份 PDF 里藏着什么如果你手头正好有一份基于电子鼻的气体识别神经网络算法研究.pdf别急着把它当成又一篇“传感器 深度学习”的综述。这份 2019 年发表在《科技与创新》上的文章核心其实是一组非常具体的对照实验用 6 个金属氧化物半导体传感器搭一套自制电子鼻对甲烷、乙烷、丙烷、氨气、乙醇这 5 种气体做数据采集然后拿传统 BP 神经网络开刀分别替换激励函数和优化算法看识别率到底能拉高多少。结论很直白——Sigmoid 梯度下降只有 44.6% 训练准确率换成 ReLU Adam 直接干到 93.3%最大提升 48.1%。这不是理论推演是 10 950 组有效数据跑出来的结果。适合谁看做气体检测、电子鼻模式识别、或者正打算把传统 BP 往深度学习方向优化的工程师。如果你正在纠结“激励函数选哪个”“优化器到底有没有用”这份 PDF 给的是可复现的实验参数和对比表不是空泛的“建议使用 ReLU”。2. 拆开电子鼻系统6 个传感器怎么分工数据怎么采2.1 传感器阵列的选型逻辑与敏感气体对照这套电子鼻没有用通用开发板而是直接选了日本 FIGARO 的 6 款金属氧化物半导体传感器每颗都有明确的敏感对象。TGS2600 盯氢气和乙醇TGS2602 对氨气、硫化氢和 VOC 响应强TGS2603 主要抓三甲胺和甲基硫醇但对氨气也有较高灵敏度TGS2610 负责丙烷和丁烷TGS2611 专攻甲烷TGS2620 则对乙醇和有机溶剂敏感。这种选型不是随便凑数——5 种目标气体里甲烷、乙烷、丙烷都是烷烃类但 TGS2611 和 TGS2610 的交叉敏感特性刚好能提供区分信息氨气靠 TGS2602 和 TGS2603 双通道响应乙醇则有 TGS2600 和 TGS2620 同时覆盖。实际做气体识别时传感器阵列的“冗余 交叉”设计比单纯追求高灵敏度更重要因为神经网络需要的是不同传感器响应之间的差异模式而不是单一通道的绝对值。提示如果你手头没有 FIGARO 系列选国产金属氧化物传感器时也要保证至少有两颗对同一气体有交叉响应否则特征维度太单薄后面换什么优化器都救不回来。2.2 数据采集流程与样本浓度设置实验用的标准气体来自大连大特浓度设置很具体氨气 10 ppm甲烷、乙烷、丙烷、乙醇都是 2 000 ppm。这个浓度梯度不是随便定的——氨气毒性强10 ppm 已经能触发 TGS2602 和 TGS2603 的明显响应而烷烃类和乙醇在 2 000 ppm 下才能让 TGS2610、TGS2611、TGS2620 的输出拉开差距。采集时把电子鼻放进两侧开口的气室一侧匀速通气另一侧流出每 0.5 秒采样一次。更换气体前必须通 600 秒空气清洗这个时间不能省——金属氧化物传感器有吸附滞后上次的气体残留会直接污染下一次采样。最终有效数据 10 950 组随机取 7 950 组做训练3 000 组做测试。这个 72.6% / 27.4% 的划分比例在中小规模气体数据集里比较稳妥既保证训练集够网络收敛又留出足够测试样本验证泛化。# 数据采集后的标注与清洗伪代码 import numpy as np # 假设 raw_data 是 shape(n_samples, 6) 的传感器响应矩阵 # 每 0.5s 一个采样点600s 空气清洗段标记为 label-1 def clean_and_label(raw_data, gas_labels, air_clean_mask): # 剔除空气清洗段和切换过渡段 valid_idx ~air_clean_mask X raw_data[valid_idx] y gas_labels[valid_idx] # 检查缺失值和异常跳变 if np.isnan(X).any(): X np.nan_to_num(X, nannp.nanmean(X, axis0)) # 按 7:3 随机划分 idx np.random.permutation(len(X)) split int(0.726 * len(X)) train_idx, test_idx idx[:split], idx[split:] return X[train_idx], y[train_idx], X[test_idx], y[test_idx]这段逻辑的关键在air_clean_mask——很多新手会把清洗段的数据也扔进训练集结果网络学到的是“空气 vs 气体”而不是“哪种气体”测试时一遇到混合过渡态就翻车。另外np.nan_to_num只是兜底实际采集时如果某颗传感器持续输出 NaN优先检查加热丝供电和信号调理电路别指望用均值填充糊弄过去。2.3 网络结构参数2 个隐含层、25 12 神经元、50 个 Minibatch文章用的深层 BP 网络结构很明确第一隐含层 25 个神经元第二隐含层 12 个神经元训练集分成 50 个 Minibatch训练 1 000 代学习率 0.000 2损失函数用 Softmaxloss参数初始化用 Xavier 方法。这个配置在 2019 年属于比较标准的全连接网络没有卷积、没有 Dropout纯粹靠激励函数和优化器拉开差距。Xavier 初始化是为了让每一层的输出方差保持一致避免一开始就进入梯度消失区。50 个 Minibatch 对应 7 950 个训练样本每个 batch 约 159 个样本这个 batch size 在 6 维输入、两层隐含层的网络里能提供足够的梯度估计稳定性又不会让单次迭代太慢。学习率 0.000 2 配合 Adam 是合理的——Adam 本身有自适应步长初始学习率不需要太大否则前期震荡会吃掉收敛速度的优势。3. 激励函数与优化算法的对照实验ReLU 和 Adam 到底改了什么3.1 Sigmoid 的梯度消失与 ReLU 的线性增长Sigmoid 函数把输入压到 (0,1)输入较大或较小时输出变化极小导数趋近于 0。反向传播时损失以导数形式逐层传递导数接近 0 就意味着参数几乎不更新——这就是梯度消失。文章里的实验数据很直观Sigmoid 梯度下降训练 1 000 代后损失只降到 1.3 左右50 到 400 代之间损失下降速度非常慢训练集准确率卡在 44.6%。ReLU 在输入小于 0 时输出恒为 0大于 0 时线性增长导数要么是 0 要么是 1不存在“导数无限接近 0”的区间。理论上 ReLU 不会在反向传播时出现梯度消失而且计算不涉及指数运算单次迭代更快。实验里 ReLU 梯度下降的损失降到 0.4 左右训练集准确率 88.8%比 Sigmoid 版本高出 44.2 个百分点。这个差距不是“调参玄学”是函数形状决定的数学必然。import torch import torch.nn as nn class GasNet(nn.Module): def __init__(self, input_dim6, hidden125, hidden212, num_classes5, activationrelu): super().__init__() self.fc1 nn.Linear(input_dim, hidden1) self.fc2 nn.Linear(hidden1, hidden2) self.fc3 nn.Linear(hidden2, num_classes) if activation relu: self.act nn.ReLU() elif activation sigmoid: self.act nn.Sigmoid() else: raise ValueError(activation must be relu or sigmoid) def forward(self, x): x self.act(self.fc1(x)) x self.act(self.fc2(x)) return self.fc3(x) # Softmaxloss 在损失函数里做这里不额外加 softmax # Xavier 初始化 def init_weights(m): if isinstance(m, nn.Linear): nn.init.xavier_uniform_(m.weight) nn.init.zeros_(m.bias) model GasNet(activationrelu) model.apply(init_weights)这段代码里fc3输出后不接 softmax因为 PyTorch 的CrossEntropyLoss内部已经包含 log_softmax NLLLoss手动加 softmax 反而会导致数值不稳定。Xavier 初始化只对 Linear 层生效bias 置零是常规做法。如果你换成 Sigmoid 版本建议把 Xavier 换成更保守的初始化因为 Sigmoid 对输入尺度更敏感Xavier 的方差假设在深层 Sigmoid 网络里不一定成立。3.2 Adam 的动态学习率与梯度下降的固定步长梯度下降法的权重更新公式是 θ θ - η * dJ/dθη 是固定学习率。学习率太小训练慢太大不容易收敛到最优值。文章里 Sigmoid 梯度下降的损失曲线在 50 到 400 代之间几乎平着走就是固定学习率在梯度消失区“迈不动步”。Adam 不一样它同时计算梯度的一阶矩估计和二阶矩估计对每个参数动态调整学习率。公式里 β1 控制一阶矩衰减β2 控制二阶矩衰减实验设 β10.9、β20.999、ε10^-8。这意味着每个参数的学习步长都有一个确定范围不会因为某个梯度特别大就迈一大步导致震荡。实验数据Sigmoid Adam 训练集准确率 92.7%比 Sigmoid 梯度下降高 48.1%ReLU Adam 训练集 93.3%、测试集 92.1%比 ReLU 梯度下降高 4.5%。Adam 在 Sigmoid 上的提升远大于在 ReLU 上的提升因为 Sigmoid 的梯度消失问题更严重Adam 的动态步长刚好补上了这个短板。# 优化器配置对照 import torch.optim as optim # 梯度下降固定学习率 optimizer_sgd optim.SGD(model.parameters(), lr0.0002) # Adam动态学习率β10.9, β20.999, ε1e-8 optimizer_adam optim.Adam(model.parameters(), lr0.0002, betas(0.9, 0.999), eps1e-8) # 训练循环骨架 criterion nn.CrossEntropyLoss() for epoch in range(1000): for batch_x, batch_y in dataloader: # 50 个 Minibatch optimizer_adam.zero_grad() output model(batch_x) loss criterion(output, batch_y) loss.backward() optimizer_adam.step()zero_grad()必须在backward()之前调用否则梯度会累加。betas参数对应文章里的 β1 和 β2eps是防止除零的小常数。如果你复现时发现 Adam 前期 loss 震荡比 SGD 大检查一下 batch size 是不是太小——50 个 Minibatch 对应约 159 样本/batch如果降到 32 以下Adam 的二阶矩估计会变得不稳定。3.3 四种组合的准确率对比与训练速度观察文章把 Sigmoid、ReLU 和梯度下降、Adam 两两组合跑了四组对照。Sigmoid 梯度下降训练集 44.6%测试集 44.2%损失 1 000 代后还在 1.3 左右明显没训练完。ReLU 梯度下降训练集 88.8%测试集 88.7%损失降到 0.4 左右下降速度已经减慢接近最小值。Sigmoid Adam训练集 92.7%测试集 91.8%200 代左右损失就接近 0.25之后下降非常小。ReLU Adam训练集 93.3%测试集 92.1%几十代内损失迅速降到 0.2 左右之后稳定不变。从训练速度看ReLU Adam 在几十代就找到最小值Sigmoid 梯度下降 1 000 代还没收敛。这个对比表的价值在于它把“换激励函数”和“换优化器”的贡献拆开了——ReLU 主要解决梯度消失Adam 主要解决固定学习率不适应不同参数的问题两者叠加效果最好但单独用任何一个都有明显提升。模型组合训练集准确率测试集准确率损失收敛情况Sigmoid 梯度下降44.6%44.2%1 000 代后约 1.3未收敛ReLU 梯度下降88.8%88.7%1 000 代后约 0.4接近最小Sigmoid Adam92.7%91.8%200 代后约 0.25几乎最优ReLU Adam93.3%92.1%几十代后稳定在 0.2 以下注意测试集准确率普遍比训练集低 0.5 到 1.2 个百分点这个差距在 3 000 组测试样本下属于正常泛化误差。如果你复现时测试集准确率比训练集低超过 5 个百分点优先检查数据划分有没有按时间顺序泄漏——气体采集是连续过程随机划分前必须打乱否则相邻采样点会同时出现在训练集和测试集里。4. 复现这份实验时最容易翻车的四个地方4.1 传感器预热和基线漂移没处理现象训练集准确率死活上不去换 ReLU 和 Adam 也只能到 70% 左右。原因金属氧化物传感器上电后需要预热通常 24 到 48 小时才能稳定文章里没写预热时间但实验肯定做了。另外传感器基线会随环境温湿度漂移如果采集数据时没做基线校正不同气体样本的响应曲线会叠加上漂移分量网络学到的特征被污染。解决采集前至少预热 24 小时每换一种气体前用空气清洗 600 秒并记录基线值后续用“响应值 / 基线值”做归一化而不是直接用原始电导率。4.2 把空气清洗段数据混进训练集现象测试时模型对“空气”识别很准但对 5 种气体的区分度很差。原因空气清洗段的数据被误标成某类气体或者清洗段和气体段的过渡数据没剔除。网络学到的是“有气体 vs 没气体”而不是“哪种气体”。解决标注时把空气清洗段单独标记为无效样本切换气体后的前 30 秒过渡数据也丢弃。文章里 10 950 组有效数据是从原始采集里清洗出来的不是全部采样点。4.3 学习率设成 0.001 以上导致 Adam 震荡现象用 Adam 优化器时 loss 曲线剧烈震荡准确率在 60% 到 80% 之间跳。原因Adam 虽然自适应但初始学习率太大时前期二阶矩估计不准步长会失控。文章设的是 0.000 2比默认的 0.001 小一个量级。解决复现时先用 0.000 2如果 loss 下降太慢再降到 0.000 1不要往上加。Adam 的eps保持 1e-8改大虽然能抑制震荡但会拖慢收敛。4.4 用测试集调参导致准确率虚高现象反复调整网络层数、神经元数量、学习率测试集准确率冲到 95% 以上但换一批新数据就掉到 80%。原因测试集被当成验证集用了每次调参都看测试集结果相当于在测试集上过拟合。文章里训练集和测试集是固定划分的调参应该用训练集内部的验证集。解决从 7 950 组训练数据里再切 10% 做验证集用验证集选超参数测试集只在最后跑一次。如果数据量够最好做 5 折交叉验证取平均。5. 从 93.3% 再往上走几个我实际会用的验证技巧文章停在 93.3% 训练准确率、92.1% 测试准确率但实际做气体识别项目时这个数字还有往上走的空间。我一般会先做一件事把 6 个传感器的响应曲线画出来看哪些通道在 5 种气体下的区分度最差。文章里 TGS2603 对氨气敏感但 TGS2602 也对氨气响应这两颗传感器的相关性如果太高网络里对应的输入维度就是冗余的。用皮尔逊相关系数筛一遍把相关系数大于 0.95 的通道合并或剔除输入维度从 6 降到 4 或 5训练速度会明显提升准确率通常不掉甚至微涨。第二个技巧是给 ReLU 加一个小的负斜率也就是 LeakyReLU。文章用的标准 ReLU 在输入小于 0 时输出恒为 0如果某个神经元在训练初期对所有样本都输出负值它的梯度就永远是 0这个神经元就“死”了。LeakyReLU 在负半轴给一个 0.01 的斜率能救回一部分死神经元。我在复现时试过ReLU Adam 换成 LeakyReLU Adam测试集准确率从 92.1% 提到 92.8% 左右提升不大但训练过程更稳。第三个是数据增强。气体传感器数据不像图像可以做旋转裁剪但可以在时间维度上做窗口切片。原始采集是每 0.5 秒一个点一个完整气体响应周期可能持续几十秒。把每个周期的数据切成重叠的滑动窗口每个窗口单独作为一个样本样本量能翻好几倍。文章里 10 950 组数据如果按 5 秒窗口、2.5 秒步长切能扩到 3 万组以上。样本量上去之后Adam 的优势会更明显因为它的二阶矩估计在更多样本下更准。# 滑动窗口数据增强 def sliding_window_augment(X, y, window_size10, step5): # X: (n_samples, 6), y: (n_samples,) # window_size10 对应 5 秒每 0.5s 一个点 X_aug, y_aug [], [] for i in range(0, len(X) - window_size 1, step): X_aug.append(X[i:iwindow_size].flatten()) # 展平为 60 维 # 标签取窗口内多数投票 y_aug.append(np.bincount(y[i:iwindow_size]).argmax()) return np.array(X_aug), np.array(y_aug)这个函数把 10 个连续采样点展平成 60 维特征标签用窗口内多数投票决定。window_size和step需要根据气体响应持续时间调——如果响应上升沿只有 3 秒窗口设 10 个点5 秒会混入稳态段反而模糊了上升沿特征。我一般会先画响应曲线看从基线到 90% 稳态值需要多久窗口大小取这个时间的 1.5 倍左右。最后一个习惯每次跑完实验把训练集和测试集的混淆矩阵打出来。文章只给了总体准确率但 5 种气体里哪两种最容易混混淆矩阵一目了然。我复现时发现乙烷和丙烷的混淆率最高因为 TGS2610 对两者都敏感TGS2611 虽然主攻甲烷但对乙烷也有响应。如果混淆矩阵显示某两类气体互相误判超过 10%与其换优化器不如加一颗对这两类气体有区分度的传感器或者把这两类的浓度梯度拉开。从那以后我每次做气体识别项目都强制先跑一遍混淆矩阵再调网络结构——准确率是给外人看的混淆矩阵才是给自己看的。希望帮到你。本文还有配套的精品资源点击获取
返回列表