目录
3.1 移除低方差的特征 (Removing features with low variance)
3.2. 单变量特征选择 (Univariate feature selection)
3.2.2 Pearson相关系数 (Pearson Correlation)
3.3 递归特征消除 (Recursive Feature Elimination)
3.4 基于L1的特征选择 (L1-based feature selection)
“特征决定了模型的上限, 而算法只是逼近这个上限”,由此可见特征工程在风控建模中的重要程度。 特征工程的本质是基于原始数据的信息提炼, 风控场景中的很多数据源, 单独来看可能和风险表现关联性并不强,但是加工成特征后, 却会与我们想要预测的目标产生紧密的联系。
下面我们将从特征预处理,特征生成,特征筛选三个模块对特征工程进行拆解。
一、数据预处理
1.1 缺失值
一般来说,未经处理的原始数据中通常会存在缺失值,因此在建模训练之前需要处理好缺失值。
1)缺失数据占比小于 20%。可以通过直接填充法,连续特征一般取均值填充,离散特征可以取众数填充;可以模型预测法,通过随机森林或者决策树进行预测结果填充;也可以通过插值法填充。
2)缺失数据占比大于 20% 小于 50%,这个时候可以把缺失值构建为新的特征,增加一列离散特征,即有缺失数据和无缺失数据。
3)缺失数据占比大于 50%,因为信息量较少,模型可能会学到大量噪音建议直接删除该特征。
像 xgboost 模型自带缺失值处理功能,可以不进行缺失值处理。
缺失值填充方法:
1). 如果是连续性,就使用平均值插补,如果是离散性,就使用众数来插补。 当然也可以用特殊值、中位数等代替。 其中采用均值填充的缺点:大大降低数据的方差
2). 随机插补法----从总体中随机抽取某个样本代替缺失样本
3). 引入预测模型,可考虑辅助回归,通过变量间的关系来预测缺失数据
下面代码就是均值填充的两种方案:
df_train['Age'].fillna(value=df_train['Age'].mean()).sample(10)
from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
df_train.loc[:,'Age'] = df_train['Age'].fillna(value=df_train['Age'].mean()).copy()
df_train.head(10)
1.2 异常值处理
异常值,即在数据集中存在不合理的值,又称离群点、极值等。可以通过统计分析、箱线图、聚类、3σ 原则、孤立森林等方法进行检查。
一般的处理方法如下:
1)直接删除含有异常值的样本。
2)视为缺失值。利用缺失值处理的方法进行处理。
3)最近值修正。可以用相近的观测值修正该异常值。
4)不处理。可以直接在具有异常值的数据集上进行数据建模。
大部分建模时,通过画图的方式进行看一下即可。
提供的方法:
统计分析,看四分位数,看数据分布。
箱线图:使用画图工具比如seaborn库 调用boxplot,distplot看一下。
聚类:使用sklearn中的聚类函数。
3σ: 需要自己同统计,类似看四分位数。
1.3 样本不均衡处理
推荐包:imbalance learn (https://imbalanced-learn.org/stable/references/index.html)
后面章节会对样本不均衡做详细的介绍,暂时可以先简单了解一下。一般应用场景中,常用样本不均衡的是解决方案为1,2。
样本不均衡现象是指正样本数目与负样本数目比列相差很大。处理方法如下:
1)下采样/欠采样(under sampling):从多数类中随机抽取样本从而减少多数类别样本数据,使数据达到平衡的方式。比如本来样本正负例的比例是 10:1,可以对正样本进行下采样构建 10 个正负样本比例为 1:1 的模型,回归结果取平均值,分类结果进行投票。
2)上采样/过采样(Over Sampling):和欠采样采用同样的原理,通过抽样来增加少数样本的数目,从而达到数据平衡的目的。同样比如本来样本正负例的比例是 10:1,可以对负样本进行上采样构建正负样本比例为 1:1 的模型。
3)Smote 算法:Smote 算法属于上采样的一种,通过人工合成的方法来生成少类别的样本。方法也很简单,对于某一个缺少样本的类别,它会随机找出几个该类别的样本,再找出最靠近这些样本的若干个该类别样本,组成一个候选合成集合,然后在这个集合中不停的选择距离较近的两个样本,在这两个样本之间,比如中点,构造一个新的该类别样本。举个例子,比如该类别的候选合成集合有两个样本(x1,y),(x2,y)(x1,y),(x2,y),那么Smote采样后,可以得到一个新的训练样本(x1+x22,y)(x1+x22,y),通过这种方法,我们可以得到不改变训练集分布的新样本,让训练集中各个类别的样本数趋于平衡。
4)Focal loss :主要解决分类样本不平衡问题,通过修改交叉熵损失函数,通过增加类别权重 α 和样本难度权重调因子(modulating factor)(1−pt)γ(1−pt)γ,来减缓上述问题。
5)设置损失函数的权重:使得少数类别数据判断错误的损失大于多数类别数据判断错误的损失,即当我们的少数类别数据预测错误的时候,会产生一个比较大的损失值,从而导致模型参数往让少数类别数据预测准确的方向偏。
二、特征生成
2.1 特征归一化(or 标准化)
应用于数值类特征。
归一化(标准化),就是要把你需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。其目的一是把不同量纲的东西放在同一量纲下,保正程序运行时收敛加快,大部分模型归一化后收敛速度会加快。但像树模型不受特征归一化影响,所以不需要特征归一化。
归一化处理:最大最小值归一化或者叫 0-1 归一化,取值范围在 [0,1] 处理,max 为样本最大值, min 为样本最小值。
标准化处理:这里只介绍一种经常使用的 z-score 标准化,经过处理后的数据均值为 0,标准差为 1,符合标准的正态分布。其中 mean 为平均值,б 为标准差。
# 幅度缩放,最大最小值缩放到[0,1]区间内
from sklearn.preprocessing import MinMaxScaler
mm_scaler = MinMaxScaler()
fare_trans = mm_scaler.fit_transform(df_train[['Fare']])
# 幅度缩放,将每一列的数据标准化为正态分布的
from sklearn.preprocessing import StandardScaler
std_scaler = StandardScaler()
fare_std_trans = std_scaler.fit_transform(df_train[['Fare']])
#中位数或者四分位数去中心化数据,对异常值不敏感
from sklearn.preprocessing import robust_scale
fare_robust_trans = robust_scale(df_train[['Fare','Age']])
#将同一行数据规范化,前面的同一变为1以内也可以达到这样的效果
from sklearn.preprocessing import Normalizer
normalizer = Normalizer()
fare_normal_trans = normalizer.fit_transform(df_train[['Age','Fare']])
fare_normal_trans
2.2 特征放缩(统计信息+ 简单加减乘除)
应用于数值类特征,可以引入log非线性进行放缩。
import numpy as np
log_age = df_train['Age'].apply(lambda x:np.log(x))
df_train.loc[:,'log_age'] = log_age
# 最大最小值
max_age = df_train['Age'].max()
min_age = df_train["Age"].min()
# 分位数,极值处理,我们最粗暴的方法就是将前后1%的值抹去
age_quarter_01 = df_train['Age'].quantile(0.01)
print(age_quarter_01)
age_quarter_99 = df_train['Age'].quantile(0.99)
print(age_quarter_99)
df_train.loc[:,'family_size'] = df_train['SibSp']+df_train['Parch']+1
df_train.head() df_train.loc[:,'tmp'] = df_train['Age']*df_train['Pclass'] + 4*df_train['family_size']
df_train.head()
2.3 哑变量
哑变量用于类别,一般如性别,分为男女两列。
embarked_oht = pd.get_dummies(df_train[['Embarked']])
embarked_oht.head()
2.3 分桶
应用于数值。
可以等频分桶,也可以自行指定区间分桶。非线性变化。
# 等频切分
df_train.loc[:,'fare_qcut'] = pd.qcut(df_train['Fare'], 10)
df_train.head()
df_train = df_train.sort_values('Fare')
alist = list(set(df_train['fare_qcut']))
badrate = {}
for x in alist:
a = df_train[df_train.fare_qcut == x]
bad = a[a.label == 1]['label'].count()
good = a[a.label == 0]['label'].count()
badrate[x] = bad/(bad+good)
f = zip(badrate.keys(),badrate.values())
f = sorted(f,key = lambda x : x[1],reverse = True )
badrate = pd.DataFrame(f)
badrate.columns = pd.Series(['cut','badrate'])
badrate = badrate.sort_values('cut')
print(badrate.head())
badrate.plot('cut','badrate')
2.4 日期类
(用于生成时间序列的衍生特征,相对重要)时间点获取等
car_sales.loc[:,'date'] = pd.to_datetime(car_sales['date_t'])
car_sales.info()
# 取出几月份
car_sales.loc[:,'month'] = car_sales['date'].dt.month
car_sales.head()
# 取出来是几号
car_sales.loc[:,'dom'] = car_sales['date'].dt.day
# 取出一年当中的第几天
car_sales.loc[:,'doy'] = car_sales['date'].dt.dayofyear
# 取出星期几
car_sales.loc[:,'dow'] = car_sales['date'].dt.dayofweek
2.5 组合特征
简单组合通知,比如两个特征都等于某个值。等。
df_train.loc[:,'alone'] = (df_train['SibSp']==0)&(df_train['Parch']==0)
2.6 文本型(风控场景中应用比较少,传统nlp应用较多)
2.6.1 词袋模型+Ngram
#词袋模型
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
corpus = [
'This is a very good class',
'students are very very very good',
'This is the third sentence',
'Is this the last doc',
'PS teacher Mei is very very handsome'
]
X = vectorizer.fit_transform(corpus)
vec = CountVectorizer(ngram_range=(1,3))
X_ngram = vec.fit_transform(corpus)
vec.get_feature_names()
X.toarray(), X_ngram.toarray()
2.6.2 tf-idf
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vec = TfidfVectorizer()
tfidf_X = tfidf_vec.fit_transform(corpus)
tfidf_vec.get_feature_names()
tfidf_X.toarray()
2.6.3 word2vev/fastext
对文本数据建模,采用cbow, skip等方式对一句话,一段文章生成固定大小的embedding。两个都可以直接引用库既可生成。
2.7 embedding
采用深度学习生成的embedding信息。 比如 autoencoder, dnn 这类算法进行生成。
2.8 特征分解 (应用较少)
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2)
df_train[['SibSp','Parch']].head()
poly_fea = poly.fit_transform(df_train[['SibSp','Parch']])
poly_fea
三、特征筛选
下面的方法基本参考sklearn筛选函数。
API Reference — scikit-learn 1.1.2 documentation
介绍四种方法。这四种方法又分为了三类,分别是filter,wrapper,embeded。
①Filter(过滤法,按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征。)
- 移除低方差的特征 (Removing features with low variance)
- 单变量特征选择 (Univariate feature selection)
②Wrapper(包装法,根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征。)
- 递归特征消除 (Recursive Feature Elimination)
③Embedded(嵌入法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。)
- 使用SelectFromModel选择特征 (Feature selection using SelectFromModel)
特征选择主要有两个目的:
-
减少特征数量、降维,使模型泛化能力更强,减少过拟合;
-
增强对特征和特征值之间的理解。
3.1 移除低方差的特征 (Removing features with low variance)
假设某特征的特征值只有0和1,并且在所有输入样本中,95%的实例的该特征取值都是1,那就可以认为这个特征作用不大。如果100%都是1,那这个特征就没意义了。当特征值都是离散型变量的时候这种方法才能用,如果是连续型变量,就需要将连续变量离散化之后才能用。而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。
from sklearn.feature_selection import VarianceThreshold
X = [[1, 2, 0, 3], [0, 1, 4, 3], [0, 1, 1, 3]]
selector = VarianceThreshold(threshold=1)
selector.fit_transform(X)
结果:array([[0], [4], [1]])
from sklearn.feature_selection import VarianceThreshold
X = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]]
sel = VarianceThreshold(threshold=(.8 * (1 - .8)))
sel.fit_transform(X)
结果:array([[0, 1], [1, 0], [0, 0], [1, 1], [1, 0], [1, 1]])
3.2. 单变量特征选择 (Univariate feature selection)
单变量特征选择的原理是分别单独的计算每个变量的某个统计指标,根据该指标来判断哪些变量重要,剔除那些不重要的变量。
对于分类问题(y离散),可采用:卡方检验,f_classif,mutual_info_classif,互信息
对于回归问题(y连续),可采用:皮尔森相关系数,f_regression,mutual_info_regression,最大信息系数
这种方法比较简单,易于运行,易于理解,通常对于理解数据有较好的效果(但对特征优化、提高泛化能力来说不一定有效)。
- SelectKBest 移除得分前 k 名以外的所有特征(取top k)
- SelectPercentile 移除得分在用户指定百分比以后的特征(取top k%)
- 对每个特征使用通用的单变量统计检验: 假正率(false positive rate) SelectFpr, 伪发现率(false discovery rate) SelectFdr, 或族系误差率 SelectFwe.
- GenericUnivariateSelect 可以设置不同的策略来进行单变量特征选择。同时不同的选择策略也能够使用超参数寻优,从而让我们找到最佳的单变量特征选择策略。
3.2.1 卡方(Chi2)检验
经典的卡方检验是检验定性自变量对定性因变量的相关性。比如,我们可以对样本进行一次chi2 测试来选择最佳的两项特征:
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
iris = load_iris()
X, y = iris.data, iris.target
X.shape
X_new = SelectKBest(chi2, k=2).fit_transform(X, y)
X_new.shape
3.2.2 Pearson相关系数 (Pearson Correlation)
我们使用Pearson相关系数主要是为了看特征之间的相关性,而不是和因变量之间的。这个例子中,我们比较了变量在加入噪音之前和之后的差异。当噪音比较小的时候,相关性很强,p-value很低。
import numpy as np
from scipy.stats import pearsonr
np.random.seed(0)
size = 300
x = np.random.normal(0, 1, size)
# pearsonr(x, y)的输入为特征矩阵和目标向量,能够同时计算 相关系数 和p-value.
print("Lower noise", pearsonr(x, x + np.random.normal(0, 1, size)))
print("Higher noise", pearsonr(x, x + np.random.normal(0, 10, size)))
3.3 递归特征消除 (Recursive Feature Elimination)
递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,移除若干权值系数的特征,再基于新的特征集进行下一轮训练。
对特征含有权重的预测模型(例如,线性模型对应参数coefficients),RFE通过递归减少考察的特征集规模来选择特征。首先,预测模型在原始特征上训练,每个特征指定一个权重。之后,那些拥有最小绝对值权重的特征被踢出特征集。如此往复递归,直至剩余的特征数量达到所需的特征数量。
RFECV 通过交叉验证的方式执行RFE,以此来选择最佳数量的特征:对于一个数量为d的feature的集合,他的所有的子集的个数是2的d次方减1(包含空集)。指定一个外部的学习算法,比如SVM之类的。通过该算法计算所有子集的validation error。选择error最小的那个子集作为所挑选的特征。
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
rf = RandomForestClassifier()
iris=load_iris()
X,y=iris.data,iris.target
print(X.shape)
rfe = RFE(estimator=rf, n_features_to_select=3)
X_rfe = rfe.fit_transform(X,y)
X_rfe.shape
3.4 基于L1的特征选择 (L1-based feature selection)
使用L1范数作为惩罚项的线性模型(Linear models)会得到稀疏解:大部分特征对应的系数为0。当你希望减少特征的维度以用于其它分类器时,可以通过 feature_selection.SelectFromModel 来选择不为0的系数。
特别指出,常用于此目的的稀疏预测模型有 linear_model.Lasso(回归), linear_model.LogisticRegression 和 svm.LinearSVC(分类)
from sklearn.feature_selection import SelectFromModel
from sklearn.svm import LinearSVC
lsvc = LinearSVC(C=0.01, penalty="l1", dual=False)
model = SelectFromModel(lsvc,max_features=4).fit(X,y) # ,threshold=0
X_embed = model.transform(X)
X_embed.shape
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
X = [[ 0.87, -1.34, 0.31 ],
[-2.79, -0.02, -0.85 ],
[-1.34, -0.48, -2.55 ],
[ 1.92, 1.48, 0.65 ]]
y = [0, 1, 0, 1]
selector = SelectFromModel(estimator=LogisticRegression()).fit(X, y)
selector.estimator_.coef_
3.5 xgboost, lightGBM
使用xgboost,lightGBM训练模型,根据特征重要性排序,选择靠前的特征。
本章没有代码,实例都是基于pandas,numpy,seaborn, sklearn, matplotlib常用库写的。不懂得,可以搜索对应库的官网进一步更全面的学习。
下一章,我们会关注风控领域下,除了本章之外的常用特征工程。
参考文献
- https://blog.csdn.net/a8689756/article/details/117329331
- 七月在线之金融风控实战入门