在上篇特征选择与提取最全总结之过滤法中已经介绍了特征选择的其中一大方法--过滤法。本篇将继续介绍特征选择与特征提取方法,其主要内容如下所示。
嵌入法
嵌入法是一种让算法自己决定使用哪些特征的方法,即特征选择和算法训练同时进行。在使用嵌入法时,我们先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小选择特征。
这些权值系数往往代表了特征对于模型的某种贡献或某种重要性,比如决策树和树的集成模型中的 feature_importances_
属性,可以列出各个特征对树的建立的贡献,我们就可以基于这种贡献的评估,找出对模型建立最有用的特征。
因此相比于过滤法,嵌入法的结果会更加精确到模型的效用本身,对于提高模型效力有更好的效果。
SelectFromModel
是一个元变换器,可以与任何在拟合后具有coef_,feature_importances_
属性或参数中可选惩罚项的评估器一起使用(比如随机森林和树模型就具有属性feature_importances_
,逻辑回归就带有l1和l2惩罚项,线性支持向量机也支持l2惩罚项)。
基于树的特征选择
代码语言:javascript复制from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier()
# X_embedded = SelectFromModel(clf,threshold=0.005).fit_transform(X,Y)
clf.fit(X,Y)
RFC = pd.DataFrame(clf.feature_importances_, columns = ["RFC"], index=features.columns)
RFC = RFC.reset_index()
RFC.sort_values(['RFC'],ascending=0)
极端随机树分类器
极端随机树ET或Extra-Trees(Extremely randomized trees)是由PierreGeurts等人于2006年提出。该算法与随机森林算法十分相似,都是由许多决策树构成。但该算法与随机森林有两点主要的区别:
- 随机森林应用的是Bagging模型,而ET是使用所有的训练样本得到每棵决策树,也就是每棵决策树应用的是相同的全部训练样本;
- 随机森林是在一个随机子集内得到最佳分叉属性,而ET是完全随机的得到分叉值,从而实现对决策树进行分叉的。
from sklearn.ensemble import ExtraTreesClassifier
# 特征提取
model = ExtraTreesClassifier()
# X_embedded = SelectFromModel(model,threshold=0.005).fit_transform(X,Y)
model.fit(X, Y)
print(model.feature_importances_)
代码语言:javascript复制[0.214 0.216 0.307 0.188 0.027 0.029 0.019]
代码语言:javascript复制ET = pd.DataFrame(model.feature_importances_, columns = ["Extra Trees"], index=features.columns)
ET = ET.reset_index()
ET.sort_values(['Extra Trees'],ascending=0)
L1正则化
使用惩罚项的模型的嵌入法。
而对于使用惩罚项的模型来说,正则化惩罚项越大,特征在模型中对应的系数就会越小。当正则化惩罚项大到一定的程度的时候,部分特征系数会变成0,当正则化惩罚项继续增大到一定程度时,所有的特征系数都会趋于0。
但是我们会发现一部分特征系数会更容易先变成0,这部分系数就是可以筛掉的。也就是说,我们选择特征系数较大的特征。
另外,支持向量机和逻辑回归使用参数C来控制返回的特征矩阵的稀疏性,参数C越小,返回的特征越少。Lasso回归,用alpha
参数来控制返回的特征矩阵,alpha
的值越大,返回的特征越少。
使用L1正则化作为惩罚项的线性模型会得到稀疏解:大部分特征对应的系数为0。特别指出,常用于此目的的稀疏预测模型有:
代码语言:javascript复制linear_model.Lasso(回归) linear_model.LogisticRegression svm.LinearSVC(分类)
from sklearn.svm import LinearSVC
from sklearn.feature_selection import SelectFromModel
lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X, Y)
model = SelectFromModel(lsvc,prefit=True)
l1 = pd.DataFrame(model.get_support(), columns = ["L1"], index=features.columns)
l1 = l1.reset_index()
岭回归
前面LASSO是L1正则化,而岭回归是L2正则化。
代码语言:javascript复制from sklearn.linear_model import Ridge
ridge = Ridge(alpha=1.0)
ridge.fit(X,Y)
def pretty_print_coefs(coefs, names = None, sort = False):
if names == None:
names = ["X%s" % x for x in range(len(coefs))]
lst = zip(coefs, names)
if sort:
lst = sorted(lst, key = lambda x:-np.abs(x[0]))
return " ".join("%s * %s" % (round(coef, 3), name)
for coef, name in lst)
print ("Ridge model:", pretty_print_coefs(ridge.coef_))
代码语言:javascript复制Ridge model:
-0.093 * X0 0.475 * X1
0.622 * X2 0.0 * X3 -0.013 * X4
0.203 * X5 -0.123 * X6
代码语言:javascript复制Ridge = pd.DataFrame(ridge.coef_, columns = ["Ridge"], index=features.columns)
Ridge = Ridge.reset_index()
Ridge.sort_values('Ridge',ascending=0)
包装法
包装法也是一个特征选择和算法训练同时进行的方法,与嵌入法十分相似,它也是依赖于算法自身的选择,比如coef_属性或feature_importances_ 属性来完成特征选择。
包装法在初始特征集上训练评估器,并且通过coef_ 属性或通过feature_importances_ 属性获得每个特征的重要性。然后,从当前的一组特征中修剪最不重要的特征。
在修剪的集合上递归地重复该过程,直到最终到达所需数量的要选择的特征。区别于过滤法和嵌入法的一次训练解决所有问题,包装法要使用特征子集进行多次训练,因此它所需要的计算成本是最高的。
递归特性消除
最典型的评估器是递归特征消除法 (Recursive feature elimination, 简写为RFE) 。它是一种贪婪的优化算法, 旨在找到性能最佳的特征子集。
它反复创建模型,并在每次迭代时保留最佳特征或剔除最差特征,下一次迭代时,它会使用上一次建模中没有被选中的特征来构建下一个模型,直到所有特征都耗尽为止。
然后,它根据自己保留或剔除特征的顺序来对特征进行排名,最终选出一个最佳子集。
另外还有一个RFECV通过交叉验证的方式执行RFE,以此来选择最佳数量的特征。
代码语言:javascript复制from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
# 特征提取
model = LogisticRegression()
rfe = RFE(model, 3)
rfe.fit(X, Y)
print("Num Features: %s" % (rfe.n_features_))
print("Selected Features: %s" % (rfe.support_))
print("Feature Ranking: %s" % (rfe.ranking_))
代码语言:javascript复制Num Features: 3
Selected Features: [True True False True
False False False]
Feature Ranking: [1 1 2 1 3 4 5]
代码语言:javascript复制features.columns
代码语言:javascript复制Index(['Open', 'High', 'Low', 'Volume',
'Increase_Decrease',
'Buy_Sell_on_Open', 'Buy_Sell'],
dtype='object')
代码语言:javascript复制RFE = pd.DataFrame(rfe.ranking_, columns = ["RFE"], index=features.columns)
RFE = RFE.reset_index()
RFE.sort_values('RFE',ascending=0)
多种方法整合
代码语言:javascript复制from functools import reduce
dfs = [Ridge,var_thd ,pearsonr, RFE, ET, RFC, chi_sq, l1, vif]
final_results = reduce(lambda left, right: pd.merge(left,
right,
on='index',
how='outer'),
dfs)
final_results
特征提取
特征提取(特征工程)是将原始数据转化为适合建模的特征。特征提取被用于所有的机器学习算法。
特征提取用于文本、图像、地理空间数据、日期和时间,以及时间序列。
特征提取从一组初始测量数据开始,并构建具有信息性和非冗余性的派生值(特征),促进后续的学习和泛化步骤,在某些情况下还会导致更好的人类解释。
特征提取是一个降维过程,原始变量的初始集合被简化为更易于处理的组(特征),同时仍然准确和完整地描述原始数据集。(维基百科)
特征工程是使用数据的领域知识来创建特征,使机器学习算法工作的过程。特征工程是机器学习应用的基础,既困难又昂贵。(维基百科)
数据准备
代码语言:javascript复制dataset = df.copy()
# Add more data
dataset['Increase_Decrease'] = np.where(dataset['Volume'].shift(-1) > dataset['Volume'],'Increase','Decrease')
dataset['Buy_Sell_on_Open'] = np.where(dataset['Open'].shift(-1) > dataset['Open'],1,0)
dataset['Buy_Sell'] = np.where(dataset['Adj Close'].shift(-1) > dataset['Adj Close'],1,0)
dataset['Returns'] = dataset['Adj Close'].pct_change()
dataset['Average'] = dataset[['Open','High','Low','Adj Close']].mean(axis=1)
dataset['Std'] = dataset[['Open','High','Low','Adj Close']].std(axis=1)
dataset['Month'] = dataset.index.month
dataset['Day'] = dataset.index.day
dataset['Year'] = dataset.index.year
dataset['Norm_Price'] = (dataset['Adj Close'] - dataset['Average']) / dataset['Std']
dataset['Date_Stamp'] = pd.to_datetime(dataset.index)
dataset = dataset.dropna()
dataset.head()
tsfresh特征提取
需要安装第三方模块:pip install tsfresh
TsFresh能自动地计算出大量的时间序列特征,即所谓的特征,这些特征描述了时间序列的基本特征,如峰数、平均值或最大值或更复杂的特征,如时间反转对称统计。
同时通过假设检验来将特征消减到最能解释趋势的特征,称为去相关性。然后,可以使用这些特征集在时间序列上构造统计或机器学习模型,例如在回归或分类任务中使用。
指定少量特征提取
代码语言:javascript复制from tsfresh import extract_features
import tsfresh
df2 = dataset.reset_index()
df2 = df2.dropna(how='all')
new_features = extract_features(df2[['Norm_Price', 'Day', 'Date_Stamp']],
column_id="Day", column_sort="Date_Stamp",
column_value="Norm_Price", n_jobs=0).dropna(axis=1)
new_features.head()
代码语言:javascript复制Feature Extraction: 100%|██████████|
31/31 [00:02<00:00, 15.23it/s]
对整个数据表提取特征
代码语言:javascript复制X = extract_features(df2, column_id='Month',
column_sort='Date',
column_value='Adj Close')
X.head()
代码语言:javascript复制Feature Extraction: 100%|██████████|
6/6 [00:00<00:00, 7.88it/s]
代码语言:javascript复制>>> tsfresh.feature_extraction.feature_calculators.abs_energy(dataset['Adj Close'])
2549098.5821893318
>>> tsfresh.feature_extraction.feature_calculators.absolute_sum_of_changes(dataset['Adj Close'])
858.289228439331