Kaggle教程 特征工程4 Feature Selection

《Kaggle教程 特征工程》系列课程目录
Kaggle教程 特征工程1 Baseline-Model
Kaggle教程 特征工程2 Categorical Encodings
Kaggle教程 特征工程3 Feature Generation
Kaggle教程 特征工程4 Feature Selection

1. 介绍

通常,在各种编码和特性生成之后,您将拥有数百或数千个特性。这可能导致两个问题。首先,您拥有的特性越多,就越有可能过度适应培训和验证集。这将导致模型在泛化新数据时性能下降。

其次,拥有的特性越多,训练模型和优化超参数所需的时间就越长。此外,在构建面向用户的产品时,您希望尽可能快地进行推理。使用更少的特性可以加快推断速度,但这是以预测性能为代价的。

为了帮助解决这些问题,您需要使用特性选择技术来为您的模型保留最有信息的特性。

这节课我们会讲到。首先,下面是到目前为止您看到的代码。

%matplotlib inline

import itertools
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.preprocessing import LabelEncoder
from sklearn import metrics

ks = pd.read_csv('../input/kickstarter-projects/ks-projects-201801.csv',
                 parse_dates=['deadline', 'launched'])

# Drop live projects
ks = ks.query('state != "live"')

# Add outcome column, "successful" == 1, others are 0
ks = ks.assign(outcome=(ks['state'] == 'successful').astype(int))

# Timestamp features
ks = ks.assign(hour=ks.launched.dt.hour,
               day=ks.launched.dt.day,
               month=ks.launched.dt.month,
               year=ks.launched.dt.year)

# Label encoding
cat_features = ['category', 'currency', 'country']
encoder = LabelEncoder()
encoded = ks[cat_features].apply(encoder.fit_transform)

data_cols = ['goal', 'hour', 'day', 'month', 'year', 'outcome']
baseline_data = ks[data_cols].join(encoded)

cat_features = ['category', 'currency', 'country']
interactions = pd.DataFrame(index=ks.index)
for col1, col2 in itertools.combinations(cat_features, 2):
    new_col_name = '_'.join([col1, col2])
    # Convert to strings and combine
    new_values = ks[col1].map(str) + "_" + ks[col2].map(str)
    label_enc = LabelEncoder()
    interactions[new_col_name] = label_enc.fit_transform(new_values)
baseline_data = baseline_data.join(interactions)

launched = pd.Series(ks.index, index=ks.launched, name="count_7_days").sort_index()
count_7_days = launched.rolling('7d').count() - 1
count_7_days.index = launched.values
count_7_days = count_7_days.reindex(ks.index)

baseline_data = baseline_data.join(count_7_days)

def time_since_last_project(series):
    # Return the time in hours
    return series.diff().dt.total_seconds() / 3600.

df = ks[['category', 'launched']].sort_values('launched')
timedeltas = df.groupby('category').transform(time_since_last_project)
timedeltas = timedeltas.fillna(timedeltas.max())

baseline_data = baseline_data.join(timedeltas.rename({'launched': 'time_since_last_project'}, axis=1))

def get_data_splits(dataframe, valid_fraction=0.1):
    valid_fraction = 0.1
    valid_size = int(len(dataframe) * valid_fraction)

    train = dataframe[:-valid_size * 2]
    # valid size == test size, last two sections of the data
    valid = dataframe[-valid_size * 2:-valid_size]
    test = dataframe[-valid_size:]
    
    return train, valid, test

def train_model(train, valid):
    feature_cols = train.columns.drop('outcome')

    dtrain = lgb.Dataset(train[feature_cols], label=train['outcome'])
    dvalid = lgb.Dataset(valid[feature_cols], label=valid['outcome'])

    param = {'num_leaves': 64, 'objective': 'binary', 
             'metric': 'auc', 'seed': 7}
    print("Training model!")
    bst = lgb.train(param, dtrain, num_boost_round=1000, valid_sets=[dvalid], 
                    early_stopping_rounds=10, verbose_eval=False)

    valid_pred = bst.predict(valid[feature_cols])
    valid_score = metrics.roc_auc_score(valid['outcome'], valid_pred)
    print(f"Validation AUC score: {valid_score:.4f}")
    return bst

Univariate Feature Selection (单变量特征选择)

最简单和最快的方法是基于单变量统计检验。对于每一个功能,测量强烈目标取决于使用统计测试χ2或方差分析等功能。

从scikit学习功能选择模块,feature_selection。SelectKBest返回给定评分函数的K个最佳特性。对于我们的分类问题,该模块提供了三种不同的得分函数:χ2,方差分析f值,以及互信息的得分。f值度量特征变量与目标之间的线性依赖关系。这意味着,如果特征和目标之间的关系是非线性的,那么得分可能会低估它们之间的关系。互信息评分是非参数的,因此可以捕捉非线性关系。

通过SelectKBest,我们定义了要保留的特性的数量,这些特性的数量基于来自计分函数的分数。使用.fit_transform(features, target),我们可以得到一个只包含选定功能的数组。

from sklearn.feature_selection import SelectKBest, f_classif

feature_cols = baseline_data.columns.drop('outcome')

# Keep 5 features
selector = SelectKBest(f_classif, k=5)

X_new = selector.fit_transform(baseline_data[feature_cols], baseline_data['outcome'])
X_new

'''
array([[2015.,    5.,    9.,   18., 1409.],
       [2017.,   13.,   22.,   31.,  957.],
       [2013.,   13.,   22.,   31.,  739.],
       ...,
       [2010.,   13.,   22.,   31.,  238.],
       [2016.,   13.,   22.,   31., 1100.],
       [2011.,   13.,   22.,   31.,  542.]])
'''

但是,我做错了一些事情。统计检验是使用所有的数据来计算的。这意味着来自验证和测试集的信息可能会影响我们保持的特性,从而引入泄漏源。这意味着我们应该只使用一个训练集来选择特性。

feature_cols = baseline_data.columns.drop('outcome')
train, valid, _ = get_data_splits(baseline_data)

# Keep 5 features
selector = SelectKBest(f_classif, k=5)

X_new = selector.fit_transform(train[feature_cols], train['outcome'])
X_new

'''
array([[2.015e+03, 5.000e+00, 9.000e+00, 1.800e+01, 1.409e+03],
       [2.017e+03, 1.300e+01, 2.200e+01, 3.100e+01, 9.570e+02],
       [2.013e+03, 1.300e+01, 2.200e+01, 3.100e+01, 7.390e+02],
       ...,
       [2.011e+03, 1.300e+01, 2.200e+01, 3.100e+01, 5.150e+02],
       [2.015e+03, 1.000e+00, 3.000e+00, 2.000e+00, 1.306e+03],
       [2.013e+03, 1.300e+01, 2.200e+01, 3.100e+01, 1.084e+03]])
'''

您应该注意到所选的特性与我使用整个数据集时的不同。现在我们有了选择的特性,但它只是训练集的特性值。要从验证和测试集中删除被拒绝的特性,我们需要找出数据集中哪些列是用SelectKBest保存的。为此,我们可以使用.inverse_transform返回具有原始数据形状的数组。

# 恢复我们保留的功能,去掉所有其他功能
selected_features = pd.DataFrame(selector.inverse_transform(X_new), 
                                 index=train.index, 
                                 columns=feature_cols)
selected_features.head()
goalhourdaymonthyearcategorycurrencycountrycategory_currencycategory_countrycurrency_countrycount_7_daystime_since_last_project
00.00.00.00.02015.00.05.09.00.00.018.01409.00.0
10.00.00.00.02017.00.013.022.00.00.031.0957.00.0
20.00.00.00.02013.00.013.022.00.00.031.0739.00.0
30.00.00.00.02012.00.013.022.00.00.031.0907.00.0
40.00.00.00.02015.00.013.022.00.00.031.01429.00.0

这将返回与训练集具有相同索引和列的DataFrame,但是所有删除的列都用0填充。我们可以通过选择方差非零的特征来找到所选的列。

# 被删除的列的值都是0,所以var是0,删除它们
selected_columns = selected_features.columns[selected_features.var() != 0]

# 获取具有所选特性的有效数据集。
valid[selected_columns].head()
yearcurrencycountrycurrency_countrycount_7_days
30289620151322311534.0
3028972013132231625.0
30289820145918851.0
30289920141322311973.0
302900201459182163.0

L1 regularization (L1正规化)

单变量方法在做选择决策时一次只考虑一个特征。相反,我们可以使用所有的特征来进行选择,方法是将它们包含在一个带有L1正则化的线性模型中。这种类型的正则化(有时称为Lasso)惩罚系数的绝对值,而L2 (Ridge)回归惩罚系数的平方。

随着正则化强度的增加,对预测目标不那么重要的特征被设为0。这允许我们通过调整正则化参数来执行特征选择。我们通过找出保留集的最佳性能来选择参数,或者提前决定保留多少特性。

对于回归问题,可以使用sklearn.linear_model。套索或sklearn.linear_model。LogisticRegression分类。这些可以与sklearn.feature_selection一起使用。SelectFromModel选择非零系数。否则,代码类似于单变量测试。

from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import SelectFromModel

train, valid, _ = get_data_splits(baseline_data)

X, y = train[train.columns.drop("outcome")], train['outcome']

# Set the regularization parameter C=1
logistic = LogisticRegression(C=1, penalty="l1", random_state=7).fit(X, y)
model = SelectFromModel(logistic, prefit=True)

X_new = model.transform(X)
X_new

'''
/opt/conda/lib/python3.6/site-packages/sklearn/linear_model/logistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning.
  FutureWarning)
array([[1.000e+03, 1.200e+01, 1.100e+01, ..., 1.900e+03, 1.800e+01,
        1.409e+03],
       [3.000e+04, 4.000e+00, 2.000e+00, ..., 1.630e+03, 3.100e+01,
        9.570e+02],
       [4.500e+04, 0.000e+00, 1.200e+01, ..., 1.630e+03, 3.100e+01,
        7.390e+02],
       ...,
       [2.500e+03, 0.000e+00, 3.000e+00, ..., 1.830e+03, 3.100e+01,
        5.150e+02],
       [2.600e+03, 2.100e+01, 2.300e+01, ..., 1.036e+03, 2.000e+00,
        1.306e+03],
       [2.000e+04, 1.600e+01, 4.000e+00, ..., 9.200e+02, 3.100e+01,
        1.084e+03]])
'''

与单变量测试类似,我们返回一个具有所选特性的数组。同样,我们希望将这些数据转换为DataFrame,以便获得所选的列。

# 以DataFrame的形式获取保留的特性,将删除的列作为所有的0
selected_features = pd.DataFrame(model.inverse_transform(X_new), 
                                 index=X.index,
                                 columns=X.columns)

# 删除的列的值都是0,保留其他列
selected_columns = selected_features.columns[selected_features.var() != 0]

在L1参数C=1的情况下,我们删除了time_since_last_project列。

一般来说,L1正则化的特征选择在单变量测试中更强大,但是当你有大量的数据和特征时,它也会非常慢。在大型数据集上,单变量测试将快得多,但也可能执行得更差。

===============================================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值