文末福利|特征工程与数据预处理的四个高级技巧

2019-08-26 16:36:19 浏览数 (1)

折磨数据,它会坦白任何事情。- 罗纳德科斯

用于创建新特征,检测异常值,处理不平衡数据和估算缺失值的技术可以说,开发机器学习模型的两个最重要的步骤是特征工程和预处理。特征工程包括特征的创建,而预处理涉及清理数据。

我们经常花费大量时间将数据精炼成对于建模有用的东西。为了使这项工作更有效,我想分享四个技巧,可以帮助你进行特征工程和预处理。用于创建新特征,检测异常值,处理不平衡数据和估算缺失值的技术可以说,开发机器学习模型的两个最重要的步骤是特征工程和预处理。特征工程包括特征的创建,而预处理涉及清理数据。

要注意到,领域知识是特征工程时最重要的事情之一(陈词滥调)。更好地了解你使用的特征可以帮助防止陷入欠拟合和过拟合的情况

本链接(https://github.com/MaartenGr/feature-engineering)是特征工程分析的笔记。

1. 重新采样不平衡数据

实际上,我们经常会遇到不平衡的数据。如果目标数据只有轻微的不平衡,这并不一定是一个问题。可以通过对数据使用适当的验证措施来解决这个问题,比如平衡精度(Balanced Accuracy)精确召回曲线(Precision-Recall Curves)F1分数(F1-score)

不幸的是,情况并非总是如此,目标变量可能非常不平衡(例如,10:1)。这种情况下,我们可以对该少数类(即样本数少的类别)进行过采样,以便使用一种称为SMOTE的技术来引入平衡。

SMOTE

SMOTE^SMOTE是合成少数类过采样技术 (Synthetic Minority Oversampling Technique)的缩写,是一种用于增加少数类样本的过采样技术。

它通过观察目标的特征空间和检测最近邻来生成新的样本。然后,在相邻样本的特征空间内,简单地选择相似的样本,每次随机地改变一列。

要实现SMOTE的模块可以在imbalanced-learn包^imbalanced-learn中找到。导入该包并使用fit_transform方法:

代码语言:javascript复制
import pandas as pd
from imblearn.over_sampling import SMOTE

# 导入数据创建x和y
df = pd.read_csv('creditcard_small.csv')
X = df.iloc[:,:-1]
y = df.iloc[:,-1].map({1:'Fraud', 0:'No Fraud'})

# 重新采样数据
X_resampled, y_resampled = SMOTE(sampling_strategy={"Fraud":1000}).fit_resample(X, y)
X_resampled = pd.DataFrame(X_resampled, columns=X.columns)

正如你所看到的,模型成功地对目标变量进行了过采样。当使用SMOTE进行过采样时,可以采用以下几种策略:

  • “少数类(minority)”:仅重采样少数类;
  • “非少数类(not minority)”:重新采样除少数类以外的其他类;
  • “非多数类(not majority)”:重新采样除了多数类的其他类;
  • '所有(all)':重新采样所有类;
  • "词典(dict)":键为目标类,值对应于每个目标类所需的样本数量。

我选择使用字典来指定我想要在多大程度上过采样数据。

附加提示1:如果数据集中有分类变量,那么可能会为那些不能发生的变量创建值。例如,如果有一个名为isMale的变量,它只能是0或1,SMOTE可能会创建0.365作为值。

这种情况下,可以使用SMOTENC,它考虑到了分类变量的性质。这个版本也可以在imbalanced-learn^imbalanced-learn包中找到。

附加提示2:确保在训练集与测试集分割之后进行过采样,并且只对训练数据进行过采样。因为通常不在合成数据上测试模型的性能。

2. 创建新的特征

为了提高模型的质量和预测能力,经常从现有变量中创建新特征。我们可以在每对变量之间创建一些交互(例如,乘法或除法),希望找到一个有趣的新特征。然而,这是一个漫长的过程,需要大量的编码。幸运的是,这可以使用深度特征综合(Deep Feature Synthesis)自动完成。

深度特征综合

深度特征综合(DFS)是一种能够快速创建具有不同深度的新变量的算法。例如,不仅可以对列进行相乘,你也可以选择先将列A与列B相乘,然后再添加列C。

首先,让我介绍将用于示例的数据。我选择使用人力资源分析数据^人力资源分析数据,因为这些特征很容易解释:

简单地根据我们的直觉,我们可以将 average_monthly_hours 除以number_project的值添加为一个新变量。然而,如果我们只跟着直觉,我们可能会错过很多的关系。

如果你的数据如果是一个简单的表格,你可以简单地按照下面的代码:

代码语言:javascript复制
import featuretools as ft
import pandas as pd

# 创建实体
turnover_df = pd.read_csv('turnover.csv')
es = ft.EntitySet(id = 'Turnover')
es.entity_from_dataframe(entity_id = 'hr', dataframe = turnover_df, index = 'index')

# 指定trans_primitives参数以执行深度特征综合
feature_matrix, feature_defs = ft.dfs(entityset = es, target_entity = 'hr',
                                      trans_primitives = ['add_numeric', 'multiply_numeric'], 
                                      verbose=True)

第一步是创建一个实体(entity),如果需要,可以从这个实体与其他表创建关系。接下来,我们可以简单地运行ft.dfs来创建新变量。我们指定参数trans_primitives来表示以什么方式创建变量。这里我们选择将数值变量相加或相乘。

正如你在上面的图中所看到的,我们仅使用几行代码就创建了另外668个特征。创建的一些特征示例:

  • last_evaluate乘以satisfaction_level
  • left乘以promotion_last_5years
  • average_monthly_hours乘以satisfaction_level加上time_spend_company

附加技巧1:注意这里的实现是比较简单基础的。DFS最大的优点是它可以进行表之间的聚合中创建新的变量。有关示例,请参见此链接^链接。

附加技巧2:运行ft.list_primitives(),以查看可以执行的聚合的完整列表。它甚至处理时间戳(timestamps)、空值(null values)和经纬度信息(long/lat information)。

3. 处理缺失值

像往常一样,没有一种处理缺失值的最佳方法。根据你的数据,只需用特定组的平均值或模式填充它们就足够了。然而,有一些高级技术使用数据的已知部分来估算(impute)缺失的值。 其中一种方法来自Scikit-Learn中的一个新包叫做Iterative Imputer,它是基于R语言(MICE包)来估算缺失的变量。

Iterative Imputer(迭代输入器)

虽然python是开发机器学习模型的一种很好的语言,但是仍然有很多方法在R中工作得更好。

Iterative Imputer是由Scikit-Learn开发的,它将每个缺失值的特征作为其他特征的函数来建模。它使用该函数的值作为估算。在每个步骤中,选择一个特征作为输出y,其他所有特征作为输入的X。然后在X和y上训练一个回归器,用来预测y的缺失值。

让我们看一个例子。我使用的数据是著名的titanic数据集。在这个数据集中,Age列缺少我们希望填充的值。代码总是很简单:

代码语言:javascript复制
# 显示的导入enable_iterative_imputer后
# 再从sklearn.impute 导入IterativeImputer
from sklearn.experimental import enable_iterative_imputer  
from sklearn.impute import IterativeImputer

from sklearn.ensemble import RandomForestRegressor
import pandas as pd

# 载入数据
titanic = pd.read_csv("titanic.csv")
titanic = titanic.loc[:, ['Pclass', 'Age', 'SibSp', 'Parch', 'Fare']]

# 使用随机森林估计器
imp = IterativeImputer(RandomForestRegressor(), max_iter=10, random_state=0)
titanic = pd.DataFrame(imp.fit_transform(titanic), columns=titanic.columns)

这种方法的优点是它允许使用你所选择的估计器(estimator)。我使用随机森林作为估计器来模拟在R中经常使用的missForest。

附加提示1:如果你有足够的数据,那么简单地删除缺少数据的示例可能是一个有吸引力的选项。但是,请记住,它可能会在你的数据中产生误差,因为丢失的数据可能遵循了你错过的模式。

附加提示2:Iterative Imputer允许使用不同的估计器。经过一些测试,我发现甚至可以使用Catboost作为估计器!不幸的是,LightGBM和XGBoost不能工作,因为它们的随机状态名(random state name)不同。

4. 异常值检测

如果不能很好地理解数据,就很难检测出异常值。如果你很了解数据,那么你可以很容易地指定数据还具有意义的阈值。

有时这是不可能的,因为很难完全理解数据。这种情况下,你可以使用离群点检测算法,比如流行的Isolation Forest

Isolation Forest

在Isolation Forest算法中,关键字是Isolation(孤立)。从本质上说,该算法检查了样本是否容易被分离。这样样本就产生了一个孤立编号,这个孤立编号由随机决策树中孤立该样本所需的分割数来计算。这个孤立编号在所有生成的树上进行平均。

如果算法只需要做几次分割就能找到一个样本,那么该样本更有可能是一个异常样本。分割本身也是随机划分的,这样异常样本在树中往往很浅(节点到根节点的路径长度短)。因此,当孤立编号在所有树中都很小时,该样本很可能是异常的。

为了展示一个例子,我再次使用了我们之前使用的信用卡数据集:

代码语言:javascript复制
from sklearn.ensemble import IsolationForest
import pandas as pd
import seaborn as sns

# 预测和可视化离群点
credit_card = pd.read_csv('creditcard_small.csv').drop("Class", 1)
clf = IsolationForest(contamination=0.01, behaviour='new')
outliers = clf.fit_predict(credit_card)
sns.scatterplot(credit_card.V4, credit_card.V2, outliers, palette='Set1', legend=False)

附加提示1:有一个扩展版的IsolationForest可用,它改进了一些缺点。然而,评论褒贬不一。

0 人点赞