目 录

1 实验说明 1
2 数据集 1
3 特征选择 2
3.1 基本方法 2
3.2 单变量选择法 2
3.3 递归特征消除法 2
4 代码实现 2
4.1 获取数据 2
4.2 数据预处理 3
4.3 定义单变量选择函数 3
4.4 定义递归特征选择法函数 4
4.5 定义获得数据函数 4
4.6 定义评定函数 5
4.7 主函数 5
5 结果分析 6
6 优缺点分析 6
6.1 Filter 6
6.2 Wrapper 7
7 总结 7
A 作业代码 8

1 实验说明

本次实验的目标为利用提供的高光谱遥感数据集进行特征选择, 有以下实验要求:

  • 利用给定的数据集, 进行数据特征 (波段) 选择。
  • 具体选择方法和策略不限制。
  • 实验结果度量标准不少于 4 种, 结合课程学习中的指标。
  • 建议对比不同类型的选择方法, 给出各种方法的优缺点。
    数据集给出的数据维数较高, 其中存在了许多冗余的信息以及无关信息, 如果把这些实际的数据直接放到神经网络中则很难得出较好的结果, 而且计算量大大增加, 因此需要进行特征提取, 把影响实验效果的特征清除掉, 用较少的特征对实验结果进行分析。

2 数据集

在本次实验中, 提供了多种数据可以选择, 由于目标在于实现过程, 数据集选择无关,
此处选择了肯尼迪航天中心数据 KSC。
NASA AVIRIS (机载可见光/红外成像光谱仪) 仪器于 1996 年 3 月 23 日在佛罗里达州肯尼迪航天中心 (KSC) 上空获取数据。AVIRIS 采集 224 个波段的数据, 这些波段的宽度为 10 纳米, 中心波长为 400-2500 纳米。从大约 20 公里的高度获得的 KSC 数据具有 18 米的空间分辨率。去除吸水率和低信噪比波段后, 用 176 个波段进行分析。训练数据是利用肯尼迪航天中心提供的彩色红外摄影和陆地卫星专题制图仪 (TM) 图像绘制的土地覆盖图选择的。植被分类方案是由 KSC 人员制定的, 目的是确定在陆地卫星和这些 AVIRIS 数据的空间分辨率上可辨别的功能类型。由于某些植被类型的光谱特征具有相似性, 因此很难区分这种环境下的土地覆盖。为便于分类, 为现场定义了 13 个类别, 代表该环境中出现的各种土地覆盖类型。
数据集预览如下图所示:

图 1: KSC 数据预览图

数据集尺寸为 $512 \times 614 \times 176$, 大小为 $56.8\mathrm{MB}$, 相应的结果尺寸为 $512 \times 614$, 大小为 $3.2\mathrm{KB}$。

3 特征选择

3.1 基本方法

一个典型机器学习问题是通过样本特征预测对应的值, 如果样本特征少, 可以增加特征, 而有时候特征较多, 则需要较少一些特征, 较少过拟合, 提高模型泛化能力, 加快模型训练速度并获得更好的性能,
特征选择主要有三种选择方法:

  1. 过滤法 (Filter) : 按照发散性或者相关性对各个特征进行评分, 设定阈值或者待选择阈值的个数, 选择特征。
  2. 包裹法 (Wrapper): 根据目标函数, 每次选择若干特征或者排除若干特征, 直到选择出最佳的子集。
  3. 嵌入法 (Embedding): 先使用某些机器学习的算法和模型进行训练, 得到各个特征的权值系数, 根据系数从大到小选择特征。类似于 Filter 方法, 但是是通过训练来确定特征的优劣。

3.2 单变量选择法

单变量特征选择是通过选择那些基于单变量统计检验 (univariate statistical tests) 得出的最优特征来实现的, 这是 Filter 法的一种。它可以看作是估计器的一个预处理步骤。这里使用了 Sklearn 中的 Select KBest 对每个特征进行评分, 并选择出指定数目的特征, 从而达到选择的效果, 这种方法并未考虑到不同特征之间的相互关系, 本题中选择了 50 个特征。

3.3 递归特征消除法

递归特征消除 (Recursive feature elimination) 是 Wrapper 法中的一种, 其主要思想是反复构建模型, 然后选出最好的 (或者最差的) 特征 (根据系数来选), 把选出来的特征放到一边, 然后在剩余的特征上重复这个过程, 直到遍历了所有的特征。在这个过程中被消除的次序就是特征的排序, 本题中选择了 30 个特征的组合。
RFE 的稳定性很大程度上取决于迭代时, 底层用的哪种模型。比如 RFE 采用的是普通的回归 (LR), 没有经过正则化的回归是不稳定的, 那么 RFE 就是不稳定的。假如采用的是 Lasso/Ridge, 正则化的回归是稳定的, 那么 RFE 就是稳定的。

4 代码实现

4.1 获取数据

此处建立函数读取获取数据并将数据从三维降到二维, 将对应的标签从二维降到一维,
降维后数据的尺寸为 (314368 {\times} 176) 。
其中一个重要的问题是提供的大部分数据是无用的, 即数据对应的标签为 0 , 因此这里
仅提取出标签不为零的数据,提取后数据尺寸为 (5211 {\times} 176) ,相关代码如下:


def get_data():
dat = loadmat(‘./高光谱数据集/KSC.mat’) [‘KSC’]



(1\mathrm{ab} = 1) oadmat(’./高光谱数据集/KSC_gt.mat’) [‘KSC_gt’]
dat ( = ) dat.reshape (({-}1,176))
lab ( = ) lab.reshape (({-}1))
print(dat.shape)
data, label = [], []
for i in range(dat.shape[0]):
if lab[i].all() != 0:
data.append(dat[i, :])
label.append(lab[i])
data ( = ) np.array (data)
label ( = ) np.array(label)
return data, label


4.2 数据预处理

这里首先利用 sklearn 中的 processing 对数据进行标准化处理, 然后消除方差为 0 的
特征, 并利用中位数进行变量的选择。 def process(data):
data ( = ) preprocessing. StandardScaler (). fit_transform(data) print(‘shape={}’.format(data.shape))
selector ( = \operatorname{VarianceThreshold}()#) 实例化,不填参数默认方差为 0 data ( = ) selector.fit_transform(data) print(data.shape)
median_num = np.median(data)
data ( = ) VarianceThreshold (median_num).fit_transform(data) print(data.shape) return data

4.3 定义单变量选择函数

此处定义了单变量选择函数, 利用 SelectKBest 进行评分, 得到不同特征的得分以及
pvalue, 并得出是否选择, 并得到选出的 50 个特征的索引。


def select_k(data, label, k):
results ( = ) SelectKBest (f_classif,k=k).fit(data,label)
print(results)
features ( = ) pd.DataFrame({
“score”: results.scores_
“pvalue”: results.pvalues_,
“select”: results.get_support()



\})
features.sort_values(“score”, ascending=False)
print(features)
index ( = ) results.get_support(indices=True)
print(index)
return index


此处简要列出几种特征得分、pvalue 以及选择情况如下图所示。

Indexscorepvalueselect
1282727.830True
1292721.860True
1302713.150True
1313.106620.000214373False
1328.80466.66051e-17False
1331.562470.0951564False
1342085.420False
1352101.520False
1362107.90False
1372116.730False
1381.362640.17608False
1392165.66True
1402219.98®True
1411.066880.383812False

图 2: 特征得分预览图

4.4 定义递归特征选择法函数
此处同样获取索引, 在本题中, 选择了 30 个特征的组合进行特征选择, 代码如下。
def rfe(data, label, n):
results ( = ) RFE(estimator=LogisticRegression(),n_features_to_select=n) print(results)
results.fit(data, label)
index = results.get_support(indices=True) print(index) return index

4.5 定义获得数据函数

此函数目的为获取指定索引特征的数据并划分训练测试集, 即将上文中选出的指定特征索引数据选出来。

def select_index_data(index, data, label):
data_after ( = )
for i in index:
data_after.append(data[:, i])
data_after ( = ) np.array (data_after).transpose()
print(data_after.shape)
print(label.shape)
return train_test_split(data_after,label,test_size=0.3,
random_state=1)


4.6 定义评定函数

此处利用 SVM 来测定特征选择的效果, 并利用 sklearn 中的库函数来进行评定, 相关
代码如下。

def measure_feature(train_data, test_data, train_label, test_label, gamma,
c):
clf = sklearn.svm. SVC (kernel=’poly’, gamma=gamma, C=c) clf.fit(train_data, train_label) predict ( = ) clf.predict(test_data) clf.get_params(deep=True)
acc ( = ) sklearn.metrics.accuracy_score(test_label,predict)
(f1 = ) sklearn.metrics.f1_score(test_label,predict,average=’micro’) recall ( = ) metrics.recall_score(test_label,predict,average=’micro’) precision ( = ) metrics.precision_score(test_label,predict,
average=’micro’)
return acc, f1, recall, precision

4.7 主函数

这部分使用了上文中定义的函数, 首先读取数据, 然后进行数据预处理, 之后分别利用这两种方法, 并提取出特征对应的索引, 之后划分出训练集和测试集来对特征提取的结果进行测试, 最后分别展示出分别用分类准确率, f1 分数, 召回率, 精确度等指标来测试训练集和测试集的效果, 代码如下。


if _name == ‘main‘:
data, label = get_data()
data ( = ) process(data)
# rfc(data,label)
index ( = ) select_k(data,label,k=50)
# index = rfe(data, label,n=30)
train_data, test_data, train_label, test_label =
select_index_data(index, data, label)



print(train_data.shape, test_data.shape, train_label.shape,
test_label.shape)
gamma, (c = 0.125,60)
train_acc,train_f1,train_recall,train_precision ( = )
measure_feature(train_data, train_data, train_label, train_label,
gamma, c)
test_acc, test_f1, test_recall, test_precision =
measure_feature(train_data, test_data, train_label,
test_label, gamma, c)
print(train_acc, test_acc)
print(train_f1, test_f1)
print(train_recall, test_recall)
print(train_precision, test_precision)


5 结果分析

表 1: 训练测试结果

accuracyf1 scorerecallprecision
UFS 训练集0.9430.9430.9430.943
UFS 测试集0.9020.9020.9020.902
RFE 训练集0.9940.9940.9940.994
RFE 测试集0.9480.9480.9480.948

由上表可知, RFE 在选择的特征数少于 UFS 的情况下, 效果仍好于 UFS。在本数据集下, 四种指标结果相同。对于 UFS 法, 优点是直观, 可解释性更好, 但是最优的组合效果并不一定是最好的。对于 RFE 法, 计算量更大, 需要考虑不同组合的效果。递归式特征消除的主要思路是反复建立多种模型, 每一次根据系数的不挑出差的特征, 并去除挑出来的特征, 然后在剩余的特征上重复该过程, 直到遍历了所有的特征。

6 优缺点分析

6.1 Filter

过滤式特征选择的评价标准从数据集本身的内在性质获得, 与特定的学习算法无关, 因此具有具有较好的通用性。通常选择和类别相关度大的特征或者特征子集。过滤式特征选择的研究者认为, 相关度较大的特征或者特征自己会在分类器上获得较高的准确率, dash 和 liu 把过滤式特征选择的评价标准分为四种, 即距离度量, 信息度量, 关联度量以及一致性度量
优点: 算法的通用性强, 省去了分类器的训练步骤, 算法复杂性低, 因而适用于大规模
数据集, 可以快速去除大量不相关的特征, 作为特征的预筛选器非常合适的
缺点: 由于算法的评价标准独立于特定的学习算法, 所选的特征子集在分类准确率方
面通常低于 wrapper 方法。

6.2 Wrapper

封装式特征选择即 wrapper 方法利用学习算法的性能来评价特征自己的优劣, 因此, 对
于一个待评价的特征子集, wrapper 方法需要
训练一个分类器, 根据分类器的性能对该特征子集进行评价, wrapper 方法中用以评价特征的学习算法是多种多样的, 例如决策树、神经网路、贝叶斯分类器、近邻法以及支持向量机等等, 本文就使用的支持向量机来进行评价。
优点: 相对于 filter 方法, wrapper 方法找到的特征子集分类性能通常更好
缺点: wrapper 方法选出的特征通用性不强, 当改变学习算法时, 需要针对该学习算法重新进行特征选择, 由于每次对子集的评价都要进行分类器的训练和测试, 所以算法计算复杂度很高, 尤其对于大规模数据集来说, 算法的执行时间越长。

7 总结

在这次实验中, 开始在理解题意方面遇到了很多问题, 后来经过多方询问才明白特征提取的几种方法如何实现。这次实验中我通过广泛查询资料了解到了相关的知识, 也认真写代码来完成任务, 这份作业的完成确实比较艰巨, 一份顶多份, 但是我还是有很大的收获, 能力也得到了提升。

A 作业代码

程序 - 高光谱.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import pandas as pd
import numpy as np
from scipy.io import loadmat
import sklearn
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.feature_selection import SelectFromModel
from sklearn.feature_selection import VarianceThreshold
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import RFE
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn import metrics


def get_data():
dat = loadmat('./高光谱数据集/KSC.mat')['KSC']
lab = loadmat('./高光谱数据集/KSC_gt.mat')['KSC_gt']

dat = dat.reshape(-1, 176)
lab = lab.reshape(-1)
print(dat.shape)

data, label = [], []
for i in range(dat.shape[0]):
if lab[i].all() != 0:
data.append(dat[i, :])
label.append(lab[i])

data = np.array(data)
label = np.array(label)
return data, label


def process(data):
data = preprocessing.StandardScaler().fit_transform(data)
print('shape={}'.format(data.shape))
selector = VarianceThreshold() # 实例化,不填参数默认方差为0
data = selector.fit_transform(data)
print(data.shape)
median_num = np.median(data)
data = VarianceThreshold(median_num).fit_transform(data)
print(data.shape)
return data


# acc = cross_val_score(KNN(), data, label, cv=5).mean()
# print("accuracy:{},time:{}".format(acc,time.time()-start))

def select_k(data, label, k):
results = SelectKBest(f_classif, k=k).fit(data, label)
print(results)
features = pd.DataFrame({
"score": results.scores_,
"pvalue": results.pvalues_,
"select": results.get_support()
})
features.sort_values("score", ascending=False)
print(features)
index = results.get_support(indices=True)
print(index)
return index


def rfe(data, label, n):
results = RFE(estimator=LogisticRegression(), n_features_to_select=n)
print(results)
results.fit(data, label)
index = results.get_support(indices=True)
print(index)
return index


def rfc(data, label):
RFC_ = RFC(n_estimators=50, random_state=0)
X_embedded = SelectFromModel(RFC_, threshold=0.005).fit_transform(data, label)
result = sklearn.model_selection.cross_val_score(RFC_, X_embedded, label, cv=5).mean()
print(result)


def select_index_data(index, data, label):
data_after = []
for i in index:
data_after.append(data[:, i])
data_after = np.array(data_after).transpose()
print(data_after.shape)
print(label.shape)
return train_test_split(data_after, label, test_size=0.3, random_state=1)


def measure_feature(train_data, test_data, train_label, test_label, gamma, c):
clf = sklearn.svm.SVC(kernel='poly', gamma=gamma, C=c)
clf.fit(train_data, train_label)
predict = clf.predict(test_data)
clf.get_params(deep=True)
acc = sklearn.metrics.accuracy_score(test_label, predict)
f1 = sklearn.metrics.f1_score(test_label, predict, average='micro')
recall = metrics.recall_score(test_label, predict, average='micro')
precision = metrics.precision_score(test_label, predict, average='micro')
return acc, f1, recall, precision


if __name__ == '__main__':
data, label = get_data()
data = process(data)

# rfc(data,label)
index = select_k(data, label, k=50)
# index = rfe(data, label,n=30)
train_data, test_data, train_label, test_label = select_index_data(index, data, label)
print(train_data.shape, test_data.shape, train_label.shape, test_label.shape)
gamma, c = 0.125, 60
train_acc, train_f1, train_recall, train_precision = measure_feature(train_data, train_data, train_label,
train_label, gamma, c)
test_acc, test_f1, test_recall, test_precision = measure_feature(train_data, test_data, train_label, test_label,
gamma, c)

print(train_acc, test_acc)
print(train_f1, test_f1)
print(train_recall, test_recall)
print(train_precision, test_precision)
# print('训练集准确率为{:.4f},测试集准确率为{:.4f}'.format(train_acc, test_acc))