如果一个模型在训练集上的表现要好于交叉验证集和测试集的化,这意味着模型是过拟合的。过拟合是指模型对训练集数据的特定观察值拟合的非常近,但训练集和测试集的真实数据分布并不一定是一致的,即模型存在较高的方差。产生过拟合的原因是建立在训练集上的模型过于复杂,而常用的降低泛化误差的方案有:
- 收集更多的训练数据;
- 通过正则化引入罚项(在逻辑回归中已论述);
- 选择一个参数较少的简单的模型;
- 降低数据的维度;
通过特征选择进行降维的方法对未经正则化处理的模型特别有效。降维技术主要分为两类:特征选择和特征提取。通过特征选择,可以选出原始特征的一个子集。对于特征提取,是通过对现有的特征信息进行推演,构造出一个新的特征子空间。
一、序列特征选择算法
序列特征选择算法是一种贪婪搜索算法,用于将原始的d维特征空间压缩到一个k维特征子空间,其中k<d。使用该算法出于对考虑是:能够剔除不相关特征或噪声,自动选出与问题最相关的特征子集,从而提高计算效率或是降低模型的泛化误差。这在模型不支持正则化时尤为有效。
一个经典的算法是序列向后选择算法(Sequential Backward Selection, SBS),其目的是在分类性能衰减最小的约束下,降低原始特征空间上的数据维度,以提高计算效率。某些情况下SBS可以在模型面临过拟合问题时提高模型的预测精度。
SBS算法理念:依次从特征集合中删除某些特征,直到新的特征子空间包含指定数量的特征。为了确定每一步需要删除的特征,我们定义一个需要最小化的标准衡量函数J,该函数的计算准则是:比较分类器的性能在删除某个特征前后的差异。因此每一步中待删除的特征就是那些能够使得标准衡量函数值尽可能大的特征,即使得模型损失性能最小的特征。
由于sklearn中没有现成的SBS算法,下面是手撸的版本:
1. from sklearn.base import clone
2. from itertools import combinations
3. import numpy as np
4. from sklearn.cross_validation import train_test_split
5. from sklearn.metrics import accuracy_score #衡量分类器的模型和评估器在特征空间上的性能
6.
7.
8. class SBS(object):
9.
10. def __init__(self, estimator, k_features, scoring=accuracy_score, test_size=0.25, random_state=1):
11. self.scoring = scoring
12. self.estimator = clone(estimator) # 模型深拷贝,不附加任何数据
13. self.k_features = k_features # 指定返回的特征数量
14. self.test_size = test_size
15. self.random_state = random_state # 设定随机数种子,数字对应的系统时间生成的随机数是相同的
16.
17. def fit(self, X, y):
18. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=self.test_size,
19. random_state=self.random_state)
20.
21. dim = X_train.shape[1] # 矩阵的行维度
22. self.indices_ = tuple(range(dim)) #最终特征子集的列标
23. self.subsets_ = [self.indices_]
24. score = self._calc_score(X_train, y_train, X_test, y_test, self.indices_)
25.
26. self.scores_ = [score]
27.
28. while dim > self.k_features:
29. scores = []
30. subsets = []
31.
32. for p in combinations(self.indices_, r=dim - 1): #combinations用来循环创建特征子集进行评估删减
33. score = self._calc_score(X_train, y_train, X_test, y_test, p)
34. scores.append(score)
35. subsets.append(p)
36.
37. best = np.argmax(scores)
38. self.indices_ = subsets[best]
39. self.subsets_.append(self.indices_)
40. dim -= 1
41.
42. self.scores_.append(scores[best])
43. self.k_score_ = self.scores_[-1] #得出k个特征下的最优分数
44.
45. return self
46.
47. def transform(self, X):
48. return X[:, self.indices_] #返回由选定的特征列构成的新数组
49.
50. def _calc_score(self, X_train, y_train, X_test, y_test, indices):
51. self.estimator.fit(X_train[:, indices], y_train)
52. y_pred = self.estimator.predict(X_test[:, indices])
53. score = self.scoring(y_test, y_pred)
54. return score
将SB