在光谱数据中,并非每个数据点都是有效的,因此,在构建模型时找到与大部分数据不符合的数据
点是一种必要的操作。这些数据我们称之为异常值。在这篇文章中,我们将使用PLS回归进行红外
光谱数据的异常值检测。
面对冗余繁杂的光谱数据,去除异常值毫无疑问成为一种必要的过程。一个疑问随之而来,我们如
何确定数据是异常值?很多异常样本是无法通过人眼观测的。
从宏观来讲,任何不遵循总体趋势的数据点我们都可以称之为异常点。
我们去一个比较直观的例子,如下图所示,散点图中的数据点。异常值游离于集体之外,模型无法
很好的描述这些数据点。
import numpy as np
import matplotlib.pyplot as plt
# 设置数据的参数
mean = 0
std = 1
num_samples = 100
num_outliers = 5
outlier_value = [10,6,6.4,8,10]
# 生成正常数据
normal_data = np.random.normal(mean, std, num_samples)
# 生成异常数据的索引
outlier_indices = np.random.choice(num_samples, num_outliers, replace=False)
# 将异常值插入到正常数据中
normal_data[outlier_indices] = outlier_value
# 绘制数据
plt.scatter(range(num_samples), normal_data)
plt.xlabel('Sample')
plt.ylabel('Value')
plt.title('Dataset with Outliers')
plt.show()
Y=U*Q.T+F
Y是响应;
U是原始光谱;
Q是每个波段在最终模型中的权重;
T称为分数,分数可以解释为模型对每个样本描述的重要程度的衡量标准。
E是误差,其中包含模型无法描述的 X 中所有光谱变化。
Q残差由误差矩阵推导而出。Q 残差考虑了模型无法解释的数据变化。
异常值是指不遵循大多数(或所有)点的趋势的点。这意味着对于任何给定模型,与其他点的相应
残差相比,异常值将具有较大的 Q 残差。
在实践中,Q 残差是通过取误差矩阵中每行的平方和来计算的。
T平方是检测异常值的第二个重要指标。Q 残差着眼于模型无法解释的变化,而T平方着眼于模型
本身的变化。模型中样本的好坏由分数来衡量。低分意味着非常适合。T平方可以直观地认为是每
个样本与完美拟合的“理想”零分情况之间的距离。
T平方是通过将分数矩阵的行的平方相加来计算的,然后按其标准差对每行行进行归一化。
ncomp=10
pls = PLSRegression(n_components=ncomp)
# 拟合数据
pls.fit(X2, Y)
# X的分数
T = pls.x_scores_
P = pls.x_loadings_
# 计算误差数组
Err = X2 - np.dot(T,P.T)
# 计算 Q 残差(误差数组行的总和)
Q = np.sum(Err**2, axis=1)
# 计算T平方(请注意,默认情况下数据是归一化的)
Tsq = np.sum((pls.x_scores_/np.std(pls.x_scores_, axis=0))**2, axis=1)
# 设置置信度
conf = 0.90
from scipy.stats import f
# 计算 T 平方的置信水平
Tsq_conf = f.ppf(q=conf, dfn=ncomp, \
dfd=(X2.shape[0]-ncomp))*ncomp*(X2.shape[0]-1)/(X2.shape[0]-ncomp)
# 估计 Q 残差的置信水平
i = np.max(Q)+1
while 1-np.sum(Q>i)/np.sum(Q>0)> conf:
i -= 1
Q_conf = i
ax = plt.figure(figsize=(8,4.5))
with plt.style.context(('Solarize_Light2')):
plt.plot(Tsq, Q, '*')
plt.plot([Tsq_conf,Tsq_conf],[plt.axis()[2],plt.axis()[3]], '--')
plt.plot([plt.axis()[0],plt.axis()[1]],[Q_conf,Q_conf], '--')
plt.xlabel("Hotelling's T-squared")
plt.ylabel('Q residuals')
plt.show()
每个点对应一个样品的光谱。具有较大 Q 残差的点是校准模型无法很好地解释的点。具有较大 T
平方值的点是那些在模型中显示偏差的点。
我们通过考虑交叉验证中的均方误差来估计模型的预测能力。这个想法是一次删除一个异常值,直
至达到最大值,并跟踪均方误差。然后,我们通过找到均方误差的最小值来估计要删除的最佳点
数。
# # RMS距离原点从高到低排序(最大在前)
plscomp=14
rms_dist = np.flip(np.argsort(np.sqrt(Q**2+Tsq**2)), axis=0)
# 根据降序 RMS 距离对校准光谱进行排序
Xc = X2[rms_dist,:]
Yc = y[rms_dist]
# 一次丢弃一个异常值,最多为 max_outliers
# 并计算 PLS 模型的 mse 交叉验证
max_outliers = 7
# 定义空 mse 数组
mse = np.zeros(max_outliers)
for j in range(max_outliers):
pls = PLSRegression(n_components=plscomp)
pls.fit(Xc[j:, :], Yc[j:])
y_cv = cross_val_predict(pls, Xc[j:, :], Yc[j:], cv=5)
mse[j] = mean_squared_error(Yc[j:], y_cv)
#MSE 中最小值的位置
msemin = np.where(mse==np.min(mse[np.nonzero(mse)]))[0][0]
为了测试代码的性能,让我们比较使用整个数据集构建的 PLS 模型,以及通过排除异常值构建的模型。
下面是包含整个数据集及其参数的模型:
这是在删除异常值之后。