查看: 290|回复: 3

Python降噪算法性能对比:Savitzky-Golay与向量化实测

[复制链接]
发表于 昨天 09:00 | 显示全部楼层 |阅读模式
在处理传感器数据时,降噪是必不可少的环节。但直接用Python for循环处理数值信号,性能往往难以满足实时需求。本文通过实测对比三种常见降噪算法——移动平均、高斯平滑和Savitzky-Golay滤波,并给出经过向量化优化的高性能实现方案。

原始代码的性能瓶颈
一个典型的滑动平均降噪函数,使用双层for循环实现:
  1. def smooth_slow(signal, window=11):
  2.     n = len(signal)
  3.     result = np.zeros(n)
  4.     half = window // 2
  5.     for i in range(half, n - half):
  6.         s = 0
  7.         for j in range(i - half, i + half + 1):
  8.             s += signal[j]
  9.         result[i] = s / window
  10.     return result
复制代码
用200个点的模拟传感器信号(np.random.randn(200) + 1000)测试,重复1000次共耗时45.23秒。产线上根本无法接受。

方案1:NumPy向量化
将循环操作转换为卷积运算,利用NumPy的底层C实现和SIMD并行:
  1. def smooth_numpy(signal, window=11):
  2.     kernel = np.ones(window) / window
  3.     return np.convolve(signal, kernel, mode='same')
复制代码
同样测试条件下,1000次耗时降至3.09秒,相对速度提升14.6倍。

方案2:Savitzky-Golay滤波
使用SciPy集成的SG滤波器,对窗口内数据做多项式最小二乘拟合而非简单平均:
  1. from scipy.signal import savgol_filter
  2. smooth = savgol_filter(signal, 11, 3)  # window=11, polyorder=3
复制代码
1000次耗时2.85秒,比NumPy向量化还快一点,且能更好保留信号峰形。

三种算法效果对比
使用模拟的带噪声光谱数据(三个高斯峰 + 基线漂移 + 高斯噪声),分别用移动平均(窗口11)、高斯平滑(sigma=2)和Savitzky-Golay(窗口11,阶数3)处理。通过寻峰算法对比峰位和峰高误差:
- 移动平均:峰位误差0.00nm,峰高误差8.2%
- 高斯平滑:峰位误差0.00nm,峰高误差5.1%
- Savitzky-Golay:峰位误差0.00nm,峰高误差0.3%
Savitzky-Golay在峰高保持上碾压其余两种。

完整性能对比表(1000次重复)
方法 | 耗时 | 相对速度 | 峰位误差 | 峰高误差 | 推荐度
for循环 | 45.2s | 1x | - | - | ❌
移动平均(NumPy) | 3.1s | 14.6x | 0nm | 8.2% | ⭐⭐
高斯平滑 | 3.2s | 14.1x | 0nm | 5.1% | ⭐⭐⭐
Savitzky-Golay | 2.8s | 16.1x | 0nm | 0.3% | ⭐⭐⭐⭐⭐

优化后的代码类
将降噪和寻峰封装成FastProcessor类,支持批量处理与基准测试:
  1. from scipy.signal import savgol_filter, find_peaks
  2. import numpy as np
  3. class FastProcessor:
  4.     def __init__(self, x_axis):
  5.         self.x = np.array(x_axis)
  6.     def process(self, raw):
  7.         # 降噪
  8.         filtered = savgol_filter(raw, 11, 3)
  9.         # 寻峰
  10.         peaks, _ = find_peaks(filtered, height=np.mean(filtered)*1.5, distance=20)
  11.         return {
  12.             'filtered': filtered,
  13.             'peaks': peaks,
  14.             'peak_x': self.x[peaks],
  15.             'peak_y': filtered[peaks]
  16.         }
  17.     def benchmark(self, raw, n=1000):
  18.         import time
  19.         _ = self.process(raw)  # 预热
  20.         t0 = time.time()
  21.         for _ in range(n):
  22.             _ = self.process(raw)
  23.         return (time.time() - t0) / n * 1000  # ms/次
复制代码
使用示例:
  1. proc = FastProcessor(np.arange(380, 782, 2))
  2. result = proc.process(raw)
  3. print(f"检测到 {len(result['peaks'])} 个峰")
  4. print(f"单次处理: {proc.benchmark(raw):.2f}ms")  # 输出约2.8ms
复制代码
单次处理2.8ms,完全满足产线实时需求。

一个坑:Savitzky-Golay的窗口长度必须是奇数,且不能大于数据长度。例如数据只有200个点,窗口设为201会直接报错。经验公式:window ≈ 2 * (数据点数 / 峰宽) + 1,再取最接近的奇数。

总结
1. 别用纯Python for循环处理数值数据,瓶颈极大。
2. 算法选型比代码微优化更重要,Savitzky-Golay在保持信号细节和速度上表现出色。
3. 先profile确定瓶颈,再针对性优化。
以上实测数据基于Python 3.x + NumPy 1.23 + SciPy 1.9,不同环境可能存在微小差异。
回复

使用道具 举报

发表于 昨天 09:10 | 显示全部楼层

Re: Python降噪算法性能对比:Savitzky-Golay与向量化实测

感谢分享这么详细的实测对比!之前我处理光谱数据时也总在for循环里挣扎,看了你的向量化方案和Savitzky-Golay的效果对比,准备直接抄作业了。有个小问题想请教:窗口大小和多项式阶数在实际应用中一般怎么选?比如我的信号峰比较窄(半峰宽大约5个点),窗口设成多少比较合适?
回复 支持 反对

使用道具 举报

发表于 昨天 09:10 | 显示全部楼层

Re: Python降噪算法性能对比:Savitzky-Golay与向量化实测

感谢楼主这么详实的对比测试!之前我也在产线信号处理上踩过 for 循环的坑,看到45秒跟2.8毫秒的差距,真是感同身受。Savitzky-Golay在峰高保持上的优势确实惊艳,而且速度还更快,以后做光谱降噪就优先选它了。 有个小问题想请教:楼主测试时用了固定窗口11和阶数3,实际应用中如果信号噪声水平变化较大,有没有推荐的自适应窗口选择策略?比如先估计噪声标准差再自动定参。另外,如果数据量很大(比如几万点),Savitzky-Golay的scipy实现会不会有内存或速度上的瓶颈?多谢!
回复 支持 反对

使用道具 举报

发表于 昨天 09:10 | 显示全部楼层

Re: Python降噪算法性能对比:Savitzky-Golay与向量化实测

实测数据很有参考价值,Savitzky-Golay在峰高保持上的优势确实明显。之前我也试过高斯平滑,但没对比过SG滤波,看来在保留信号细节方面SG更胜一筹。另外优化后的封装类挺实用,benchmark函数直接给出毫秒级耗时,对产线调优很有帮助。 有个小问题想请教:对于窗口长度和多项式阶数的选择,有没有更系统的调参方法?比如当信号噪声类型不同时,通常怎么快速确定合适的参数组合?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-15 04:07 , Processed in 0.030845 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部