第4章:特征波段筛选之皮尔逊相关系数法
各位同学好,今天我们来聊聊光谱分析中一个非常基础但又极其重要的方法——皮尔逊相关系数法。说实话,我刚入行那会儿,总觉得这方法太简单,不就是算个相关系数嘛。直到有一次做近红外光谱的定量分析,数据预处理搞了一大堆,模型精度就是上不去。后来一位老工程师点醒我:「你先把那些跟目标变量八竿子打不着的波段去掉试试。」结果你猜怎么着?模型性能直接提升了15%。
嗯,从那以后,我再也不敢小看这个「简单」的方法了。
4.1 皮尔逊相关系数的数学原理
皮尔逊相关系数,说白了就是衡量两个变量之间线性相关程度的指标。它的取值范围在-1到1之间:
- r = 1:完全正相关,一个变量增大,另一个也增大
- r = -1:完全负相关,一个变量增大,另一个减小
- r = 0:没有线性相关关系
数学公式长这样:
r = Σ[(xi - x̄)(yi - ȳ)] / √[Σ(xi - x̄)² · Σ(yi - ȳ)²]
其中xi是第i个样本的光谱吸光度,yi是对应的目标变量值(比如浓度)。x̄和ȳ分别是它们的均值。
核心思想:如果某个波段的光谱值与目标变量高度相关,那么这个波段很可能包含了有用的化学信息。反之,如果相关系数接近0,这个波段大概率是噪声或无关信息。
我在项目中遇到过这样的情况:有个学生用全波段建模,模型R²只有0.82。我让他算一下每个波段与目标变量的相关系数,发现从1200nm到1400nm这段,相关系数普遍低于0.1。去掉这些波段后,模型R²直接跳到了0.91。你看,有时候不是模型不行,是数据本身有问题。
4.2 与目标变量的相关性分析
实际操作中,我们会计算每个光谱波段与目标变量的皮尔逊相关系数,然后绘制成一张「相关系数谱图」。这张图能直观地告诉我们:哪些波段是「好波段」,哪些是「垃圾波段」。
举个例子,假设我们有一个近红外光谱数据集,波长范围是800nm到2500nm,目标变量是某种成分的浓度。计算相关系数后,可能会得到这样的结果:
| 波长范围 (nm) | 相关系数范围 | 判断 |
|---|---|---|
| 800 - 1000 | 0.02 ~ 0.15 | 弱相关,可能是噪声 |
| 1000 - 1200 | 0.65 ~ 0.82 | 强相关,保留 |
| 1200 - 1400 | 0.10 ~ 0.25 | 弱相关,考虑剔除 |
| 1400 - 1600 | 0.70 ~ 0.88 | 强相关,保留 |
| 1600 - 2500 | 0.30 ~ 0.55 | 中等相关,视情况保留 |
为什么会这样?其实道理很简单。光谱中某些波段对应着特定的化学键振动,比如C-H、O-H、N-H等。如果目标变量恰好是含有这些化学键的成分,那这些波段自然就跟目标变量高度相关。反之,如果某个波段对应的是水分吸收,而你的目标变量跟水分没关系,那这个波段就是干扰项。
个人经验:我建议在做相关性分析之前,先对光谱进行简单的预处理,比如平滑去噪或标准正态变量变换(SNV)。否则噪声可能会干扰相关系数的计算,让你误判某些波段的价值。
4.3 Python实现
好了,理论讲完了,咱们直接上代码。我用Python实现一个完整的皮尔逊相关系数法特征筛选流程。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
# 假设我们有一个光谱数据矩阵 X (样本数 × 波段数)
# 和目标变量向量 y (样本数,)
# 这里生成模拟数据做演示
np.random.seed(42)
n_samples = 100
n_wavelengths = 200
X = np.random.randn(n_samples, n_wavelengths)
y = np.random.randn(n_samples)
# 计算每个波段与目标变量的皮尔逊相关系数
correlation_coefficients = []
p_values = []
for i in range(n_wavelengths):
corr, p_val = pearsonr(X[:, i], y)
correlation_coefficients.append(corr)
p_values.append(p_val)
correlation_coefficients = np.array(correlation_coefficients)
p_values = np.array(p_values)
# 绘制相关系数谱图
plt.figure(figsize=(12, 6))
plt.plot(range(n_wavelengths), correlation_coefficients, 'b-', linewidth=1.5)
plt.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
plt.xlabel('波段索引')
plt.ylabel('皮尔逊相关系数')
plt.title('各波段与目标变量的相关系数谱图')
plt.grid(True, alpha=0.3)
plt.show()
# 根据阈值筛选特征
threshold = 0.3 # 这个阈值怎么选?下一节讲
selected_indices = np.where(np.abs(correlation_coefficients) > threshold)[0]
selected_wavelengths = selected_indices # 实际应用中这里是波长值
print(f"原始波段数: {n_wavelengths}")
print(f"筛选后波段数: {len(selected_wavelengths)}")
print(f"筛选比例: {len(selected_wavelengths)/n_wavelengths*100:.1f}%")
这段代码的核心就两件事:一是算相关系数,二是根据阈值筛选。我习惯把相关系数谱图打印出来,肉眼看一下整体分布,再决定阈值。毕竟机器判断有时候会漏掉一些细节。
避坑指南:我曾经犯过一个错误——直接用p值小于0.05作为筛选标准。结果发现筛选出来的波段太多,模型反而过拟合了。后来才明白,p值只能说明相关性是否显著,不能说明相关性的强弱。对于特征筛选,我们更关心的是相关系数的绝对值大小。
4.4 阈值选择策略
阈值怎么选?这个问题其实没有标准答案。我根据自己的项目经验,总结了三种常用策略:
策略一:固定阈值法
直接设定一个绝对值阈值,比如0.3、0.5或0.7。这种方法简单粗暴,适合快速验证。我一般会先设0.3试试,如果筛选出来的波段太少(比如不到10%),就降到0.2;如果太多(超过50%),就升到0.4。
策略二:百分比法
按相关系数绝对值从大到小排序,取前N%的波段。比如保留前20%或前30%。这种方法的好处是能控制最终保留的波段数量,适合后续建模时对特征数量有要求的情况。
策略三:统计显著性法
结合p值,只保留p值小于某个阈值(比如0.05)且相关系数绝对值大于某个最小值的波段。这种方法更严谨,但要注意我前面说的——别只看p值。
我个人习惯的做法是:先用固定阈值法快速筛选一轮,然后用交叉验证评估模型性能,根据结果微调阈值。说白了,阈值不是算出来的,是试出来的。
总结一下:皮尔逊相关系数法虽然简单,但非常实用。它帮我们快速定位那些与目标变量高度相关的波段,剔除冗余信息。不过要注意,它只能捕捉线性关系,如果光谱与目标变量之间存在非线性关系,这个方法就不太灵了。那时候可以考虑互信息法或距离相关系数法,这些我们后面会讲到。
好了,这一章的内容就到这里。皮尔逊相关系数法虽然基础,但用好了能帮你省下大量时间。下一章我们会讲另一种特征筛选方法——互信息法,它能处理非线性关系,到时候咱们再对比一下两者的优劣。
公众号:蓝海资料掘金营,微信deep3321