前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >特征选择

特征选择

作者头像
用户3577892
发布2020-06-11 09:29:02
1K0
发布2020-06-11 09:29:02
举报
文章被收录于专栏:数据科学CLUB数据科学CLUB

特征选择特征选择概述Filter 过滤法方差选择法相关系数法卡方检验互信息法Wrapper 包装法稳定性选择(Stability Selection)递归特征消除特征值排序选择Embedded 嵌入法线性模型正则化树模型类别标签不平衡处理欠采样过采样加权处理

特征选择

特征选择概述

概念及工作原理

  1. 从哪些方面来选择特征呢? 当数据处理好之后,我们需要选择有意义的特征输入机器学习的模型进行训练,通常来说 要从两个方面考虑来选择特征,如下: (1)特征是否发散 如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用。 (2)特征与目标的相关性 这点比较显见,与目标相关性高的特征,应当优先选择。 区别:特征与特征之间相关性高的,应当优先去除掉其中一个特征,因为它们是替代品。
  2. 为什么要进行特征选择? (1)减轻维数灾难问题 (2)降低学习任务的难度
  3. 处理高维数据的两大主流技术 特征选择和降维
  4. 特征选择有哪些方法呢? (1)Filter 过滤法 (2)Wrapper 包装法 (3)Embedded 嵌入法
代码语言:javascript
复制
# 加载 IRIS 数据集做演示
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import LabelEncoder
%matplotlib inline

df = sns.load_dataset('iris')
print(df.shape)
df['species'] = LabelEncoder().fit_transform(df.iloc[:, 4])
df.head()
代码语言:javascript
复制
(150, 5)

sepal_length

sepal_width

petal_length

petal_width

species

0

5.1

3.5

1.4

0.2

0

1

4.9

3.0

1.4

0.2

0

2

4.7

3.2

1.3

0.2

0

3

4.6

3.1

1.5

0.2

0

4

5.0

3.6

1.4

0.2

0

Filter 过滤法

它主要侧重于单个特征目标变量的相关性。 优点是计算时间上较高效,对于过拟合问题也具有较高的鲁棒性。 缺点就是倾向于选择冗余的特征,因为他们不考虑特征之间的相关性,有可能某一个特征的分类能力很差, 但是它和某些其它特征组合起来会得到不错的效果,这样就损失了有价值的特征。

方差选择法

为什么方差可以用于选择特征呢? 先从方差的概念说起,方差是衡量一个变量的离散程度(即数据偏离平均值的程度大小), 变量的方差越大,我们就可以认为它的离散程度越大,也就是意味着这个变量对模型的贡献和作用 会更明显,因此要保留方差较大的变量,反之,要剔除掉无意义的特征。

思路(先计算各个特征的方差,然后根据设定的阈值待选择阈值的个数,选择方差大于阈值的特征),公式如下:

(1)计算特征的方差。假设X=[x1,x2,…,xn],则方差为: ,其中 是平均值,一般样本方差选择n-1较好

  • (2)设定阈值,并筛选出大于阈值的特征或者筛选出待选择阈值的个数。
代码语言:javascript
复制
# 方差选择法
# 自己手写理论公式来实现功能
def VarianceThreshold(df, threshold=0.):
    dfc = df.iloc[:, :4].copy()
    print('>>>特征名:\n', dfc.columns.tolist())
    # 1 求方差
    var = np.sum(np.power(np.matrix(dfc.values)-np.matrix(dfc.mean()), 2), axis=0)/(dfc.shape[0]-1)
    T = []
    # 2 筛选大于阈值的特征
    for index, v in enumerate(var.reshape(-1, 1)):
        if v > threshold:
            T.append(index)
    dfc = dfc.iloc[:, T]
    return var, dfc

# 阈值设置为 0.6
var, dfc = VarianceThreshold(df, 0.60)
print('\n>>>原始特征对应的方差值:\n', var)
print('\n>>>方差阈值选择后的特征名:\n', dfc.columns)
dfc.head()
代码语言:javascript
复制
>>>特征名:
 ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']

>>>原始特征对应的方差值:
 [[0.68569351 0.18997942 3.11627785 0.58100626]]

>>>方差阈值选择后的特征名:
 Index(['sepal_length', 'petal_length'], dtype='object')

sepal_length

petal_length

0

5.1

1.4

1

4.9

1.4

2

4.7

1.3

3

4.6

1.5

4

5.0

1.4

代码语言:javascript
复制
# 调用 sklearn 模块 API 接口
from sklearn.feature_selection import VarianceThreshold

vt = VarianceThreshold(0.6)
x_vt = vt.fit_transform(df.iloc[:,:4])
print(vt.variances_)
x_vt[:5]
代码语言:javascript
复制
[0.68112222 0.18871289 3.09550267 0.57713289]





array([[5.1, 1.4],
       [4.9, 1.4],
       [4.7, 1.3],
       [4.6, 1.5],
       [5. , 1.4]])

相关系数法

第一种方法:计算特征与特征的相关系数

工作原理 通过计算特征与特征之间的相关系数的大小,可判定两两特征之间的相关程度。 取值区间在[-1, 1]之间,取值关系如下: corr(x1,x2)相关系数值小于0表示负相关((这个变量下降,那个就会上升)),即x1与x2是互补特征 corr(x1,x2)相关系数值等于0表示无相关 corr(x1,x2)相关系数值大于0表示正相关,即x1与x2是替代特征 原理实现:取相关系数值的绝对值,然后把corr值大于90%~95%的两两特征中的某一个特征剔除。

如果两个特征是完全线性相关的,这个时候我们只需要保留其中一个即可。 因为第二个特征包含的信息完全被第一个特征所包含。 此时,如果两个特征同时都保留的话,模型的性能很大情况会出现下降的情况

理论公式及推导 假设X=[x1,x2,…,xn],其中x1,x2…是列向量,即x1代表一个特征,公式推导如下: , 其中 表示各个特征的平均值

优缺点 优点:容易实现 缺点:只是根据特征与特征之间的相关度来筛选特征,但并没有结合与目标的相关度来衡量

应用场景 用于特征选择,以提取最有效的特征作为目标,剔除冗余特征

代码语言:javascript
复制
# 相关系数--特征与特征
# 自己手写理论公式实现功能
def corr_selector(df):
    dfc = df.copy().iloc[:,:4]
    CORR = np.zeros((dfc.shape[1], dfc.shape[1])) 
    delete, save = [], []
    for i in range(dfc.shape[1]):
        if dfc.columns.tolist()[i] not in delete:
            save.append(dfc.columns.tolist()[i])
        for j in range(i+1, dfc.shape[1]):
            # 计算特征与特征之间的相关系数
            cov = np.sum((dfc.iloc[:,i]-dfc.iloc[:,i].mean()) * (df.iloc[:,j]-df.iloc[:,j].mean()))
            std = np.sqrt(np.sum((df.iloc[:,i]-df.iloc[:,i].mean())**2)) * np.sqrt(np.sum((df.iloc[:,j]-df.iloc[:,j].mean())**2))
            corr = cov/std
            CORR[i][j] = corr
            # 筛选掉高线性相关两两特征中的某一个特征
            if (np.abs(corr) > 0.89) and (dfc.columns.tolist()[j] not in delete):
                delete.append(dfc.columns.tolist()[j])
    dfc_ = dfc[save].copy()
    return CORR, dfc_

corr,dfc_ = corr_selector(df)
print(corr)
dfc_.head()
代码语言:javascript
复制
[[ 0.         -0.11756978  0.87175378  0.81794113]
 [ 0.          0.         -0.4284401  -0.36612593]
 [ 0.          0.          0.          0.96286543]
 [ 0.          0.          0.          0.        ]]

sepal_length

sepal_width

petal_length

0

5.1

3.5

1.4

1

4.9

3.0

1.4

2

4.7

3.2

1.3

3

4.6

3.1

1.5

4

5.0

3.6

1.4

第二种方法:计算特征与目标的相关系数以及P值

原理依据 scipy.stats.pearsonr(x, y) 输出:(r, p) r:相关系数[-1,1]之间 p:相关系数显著性

相关性的强度确实是用相关系数的大小来衡量的,但相关大小的评价要以相关系数显著性的评价为前提 因此,要先检验相关系数的显著性,如果显著,证明相关系数有统计学意义,下一步再来看相关系数大小; 如果相关系数没有统计学意义,那意味着你研究求得的相关系数也许是抽样误差或者测量误差造成的,再进行一次研究结果可 能就大不一样,此时讨论相关性强弱的意义就大大减弱了。

原理实现:先计算各个特征对目标值的相关系数以及相关系数的P值

优缺点

Pearson相关系数的一个明显缺陷是,作为特征排序机制,他只对线性关系敏感。 如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0

应用场景及意义 应用于回归问题的特征选择,旨在选择出最有效的信息和减少内存占用空间

代码语言:javascript
复制
# 相关系数--特征与目标变量
# 自己手写理论公式实现功能
def corr_selector(df):
    X, y = df.iloc[:, :4], df.iloc[:, 4]
    cor_list = []
    for i in X.columns.tolist():
        cor = np.corrcoef(X[i], y)[0, 1]
        cor_list.append(cor)
    print(X.columns.tolist())
    print(cor_list)
    return cor_list

corr_selector(df)
代码语言:javascript
复制
['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
[0.7825612318100814, -0.4266575607811243, 0.9490346990083884, 0.9565473328764028]





[0.7825612318100814,
 -0.4266575607811243,
 0.9490346990083884,
 0.9565473328764028]
代码语言:javascript
复制
df.plot()
plt.savefig('corr.png')

从图像趋势来看:

  • petal_width、petal_length与目标变量species有着非常显著的相关性
  • sepal_length与目标变量species有着较强的相关性
  • sepal_width与目标变量species没有明显的相关性,sepal_width的数值在species各个类别中的数值几乎是不变的,因此它的贡献很小

从上面的方差选择法自变量与应变量的相关系数法可以验证上面图像趋势分析的结论。

代码语言:javascript
复制
# 调用 sklearn 模块 API 接口
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
from numpy import array

fun = lambda X, Y: tuple(map(tuple, array(list(map(lambda x: pearsonr(x, Y), X.T))).T))
sb = SelectKBest(fun, k=2)
x_sb = sb.fit_transform(df.iloc[:,:4], df.iloc[:, 4])
print('>>>检验统计值(相关系数):\n', sb.scores_)
print('\n>>>P值:\n', sb.pvalues_)
x_sb[:5]
代码语言:javascript
复制
>>>检验统计值(相关系数):
 [ 0.78256123 -0.42665756  0.9490347   0.95654733]

>>>P值:
 [2.89047835e-32 5.20156326e-08 4.20187315e-76 4.15531102e-81]


array([[1.4, 0.2],
       [1.4, 0.2],
       [1.3, 0.2],
       [1.5, 0.2],
       [1.4, 0.2]])

卡方检验

工作原理 卡方检验是检验定性自变量对定性因变量的相关性,求出卡方值,然后根据卡方值 匹配出其所对应的概率是否足以推翻原假设H0,如果能推翻H0,就启用备用假设H1。

理论公式及推导 假设检验: 假如提出原假设H0:化妆与性别没有关系; 备用假设H1:化妆与性别有显著关系。

卡方值(chi-square-value)计算公式 其中,Ai为i水平的观察(实际)频数,Ei为i水平的期望(理论)频数,n为总频数,pi为i水平的期望频率。 i水平的期望频数Ei等于总频数n×i水平的期望概率pi,k为单元格数(行数*列数)。

如何判定两个定性变量的卡方值在什么区间可以证明假设成不成立呢? 计算步骤如下:

  1. 计算 卡方值
  2. 计算自由度(df=(行数-1)*(列数-1))
  3. 置信度(根据卡方值结合表格和自由度查询而得到的置信度大小)

优缺点 优点:可以很好地筛选出与定性应变量有显著相关的定性自变量。

应用场景及意义 应用场景:适用于分类问题的分类变量

代码语言:javascript
复制
# 调用 sklearn 模块 API 接口
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

skb = SelectKBest(chi2, k=2)
x_skb = skb.fit_transform(df.iloc[:,:4], df.iloc[:,4])
print('>>>检验统计值(卡方值):\n', skb.scores_)
print('\n>>>P值:\n', skb.pvalues_)
x_skb[:5]
代码语言:javascript
复制
>>>检验统计值(卡方值):
 [ 10.81782088   3.7107283  116.31261309  67.0483602 ]

>>>P值:
 [4.47651499e-03 1.56395980e-01 5.53397228e-26 2.75824965e-15]

array([[1.4, 0.2],
       [1.4, 0.2],
       [1.3, 0.2],
       [1.5, 0.2],
       [1.4, 0.2]])

互信息法

工作原理 评价定性自变量对定性应变量的相关性

在处理分类问题提取特征的时候就可以用互信息来衡量某个特征和特定类别的相关性, 如果信息量越大,那么特征和这个类别的相关性越大。反之也是成立的。

理论公式及推导

互信息公式: 其中 ,,其中是总样本数

标准互信息公式: 其中信息熵的公式 ,而

优缺点

应用场景及意义 应用场景:因此非常适合于文本分类的特征和类别的配准工作

代码语言:javascript
复制
# 互信息法
# 自己手写理论公式实现功能
# np.where(condition, x, y)
# 满足条件(condition),输出x,不满足输出y。
# numpy.intersect1d(ar1, ar2, assume_unique=False) [资源]
# 找到两个数组的交集。
# 返回两个输入数组中已排序的唯一值。
# math.log(x[, base])
# x -- 数值表达式。
# base -- 可选,底数,默认为 e
def MI_and_NMI():
    import math
    from sklearn import metrics
    A = np.array([1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3])
    B = np.array([1,2,1,1,1,1,1,2,2,2,2,3,1,1,3,3,3])
    N, A_ids, B_ids = len(A), set(A), set(B)
    # print(N, A_ids, B_ids)

    # 互信息计算
    MI, eps = 0, 1.4e-45
    # 你说的这种应该是为了避免log0的结果
    # 所以添加一个非常小的数字,避免无穷的...log(0)的情况
    for i in A_ids:
        for j in B_ids:
            ida = np.where(A==i)  # 返回索引
            idb = np.where(B==j)
            idab = np.intersect1d(ida, idb)  # 返回相同的部分
            #print(ida,idb,idab)

            # 概率值
            px = 1.0*len(ida[0])/N  # 出现的次数/总样本数
            py = 1.0*len(idb[0])/N
            pxy = 1.0*len(idab)/N
            # MI 值
            MI += pxy * math.log(pxy/(px*py)+eps, 2)

    # 标准互信息计算
    Hx = 0
    for i in A_ids:
        ida = np.where(A==i)
        px = 1.0*len(ida[0])/N
        Hx -= px * math.log(px+eps, 2)
    Hy = 0
    for j in B_ids:
        idb = np.where(B==j)
        py = 1.0*len(idb[0])/N
        Hy -= py * math.log(py+eps, 2)
    NMI = 2.0*MI/(Hx+Hy)

    return MI, NMI, metrics.normalized_mutual_info_score(A,B)

MI_and_NMI()
代码语言:javascript
复制
(0.565445018842856, 0.3645617718571898, 0.36456177185718985)
代码语言:javascript
复制
from sklearn import metrics

A = [1, 1, 1, 2, 3, 3]
B = [1, 2, 3, 1, 2, 3]
result_NMI=metrics.normalized_mutual_info_score(A, B)
print("result_NMI:",result_NMI)
代码语言:javascript
复制
result_NMI: 0.3016631159962042

Wrapper 包装法

封装器用选取的特征子集样本(标签)集进行训练学习,训练的精度(准确率)作为衡量特征子集好坏的标准, 经过比较选出最好的特征子集。 常用的有逐步回归(Stepwise regression)、向前选择(Forward selection)和向后选择(Backward selection)。 它的优点是:考虑了特征之间组合以及特征与标签之间的关联性; 缺点是:当观测数据较少时容易过拟合,而当特征数量较多时,计算时间又会增长。

工作原理 包装法是指使用算法模型对特征子集与目标(标签)集进行训练评估,根据训练的精度(准确率)衡量特征子集的好坏,从而挑选出最好的特征子集。

理论公式及推导 步骤:

组合好特征子集 假设特征X=[A,B,C],目标(标签)Y,那么特征子集可以有组合方式: 其中要剔除掉空集,进一步扩展特征子集的表达方式如下: 特征子集,其中特征数

  1. 将每个特征子集Xi分别与标签Y进行训练学习 model.fit(Xi,Y),其中i=[1,2,…,()]

衡量特征好坏的不同方法 model.score(Xi,Y),可选择其他合理评估方法也可以 (1) 第一种(针对特征子集):从个特征子集中挑选出训练精度(评分)对应最好的特征子集 (2) 第二种(针对单个特征):比如把含有A特征的所有特征子集的评分/频率(频数)作为相对应的衡量指标 根据评分指标,挑选出前k个MeanScore值较大的特征作为最终的特征 ,N表示含有特征A的所有特征子集的次数,比如本例子含有A的特征子集有4次 根据频率指标,挑选出前k个F频率值较大的特征作为最终的特征 含有特征的特征子集被选为重要特征的次数含有特征的特征子集的数量 那么怎样才算被选为重要特征呢?对应指标大于80%(可根据具体情况调整)的特征子集可以被认为是重要特征

优缺点 优点:考虑了特征之间组合以及特征与标签之间的关联性。 缺点:由于要划分特征为特征子集并且逐个训练评分,因此当特征数量较多时,计算时间又会增长;另外在样本数据较少的时候,容易过拟合。

稳定性选择(Stability Selection)

工作原理 稳定性选择是一种基于二次抽样和选择算法(训练模型)相结合的方法,选择算法可以是回归、分类SVM或者类似算法。

原理实现:在不同的特征子集上运行训练模型,不断地重复,最终汇总特征选择的结果。比如可以统计某个特征被认为是重要特征的频率 (被选为重要特征的次数除以它所在的子集被测试的次数)。理想情况下,重要特征的得分会接近100%。稍微弱一点的特征得分会是非0的数, 而最无用的特征得分将会接近于0。

优缺点 优点:

  • 特征值下降的不是特别急剧,这跟纯lasso的方法和随机森林的结果不一样, 能够看出稳定性选择对于克服过拟合和对数据理解来说都是有帮助的。
  • 总的来说,好的特征不会因为有相似的特征、关联特征而得分为0。
  • 在许多数据集和环境下,稳定性选择往往是性能最好的方法之一。
代码语言:javascript
复制
from sklearn.svm import SVC

X1, X2, Y = df[['petal_width']], df[['sepal_width']], df['species']
svc = SVC()
for X in [X1, X2]:
    svc.fit(X,Y)
    print(svc.score(X,Y))
代码语言:javascript
复制
0.96
0.5533333333333333

从上面可以验证RandomizedLasso比RandomizedLogisticRegression更靠谱, 因此在日后使用过程中不要急着筛选,也要验证特征选择的合理性以及优越性。

递归特征消除

Recursive Feature Elimination,简称RFE 工作原理 主要思想是: (1)反复的构建模型(比如SVM或者回归模型) (2)接着选出最好(或者最差)的特征(可以根据系数来选),把选出来的特征放到一边 (3)然后在剩余的特征上重复上面(1)(2)步骤,直到遍历完所有特征。

通识来说: RFE算法的主要思想就是使用一个基模型(这里是S模型VM)来进行多轮训练, 每轮训练后,根据每个特征的系数对特征打分,去掉得分最小的特征, 然后用剩余的特征构建新的特征集,进行下一轮训练,直到所有的特征都遍历了。

这个过程中特征被消除的次序就是特征的排序,实际上这是一种寻找最优特征子集的贪心算法。

优缺点 RFE的稳定性很大程度上取决于在迭代选择的时候,选择哪一种模型。 (1)如果RFE采用的是普通的回归,没有经过正则化的回归是不稳定的,从而RFE也是不稳定的 (2)如果采用Ridge或Lasso模型,经过正则化的回归是稳定的,从而RFE是稳定的。

代码语言:javascript
复制
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston

boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

#use linear regression as the model
lr = LinearRegression()
#rank all features, i.e continue the elimination until the last one
rfe = RFE(lr, n_features_to_select=2)
rfe.fit(X,Y)

print("Features sorted by their rank:")
print(sorted(zip(rfe.ranking_, names)))
代码语言:javascript
复制
Features sorted by their rank:
[(1, 'NOX'), (1, 'RM'), (2, 'CHAS'), (3, 'PTRATIO'), (4, 'DIS'), (5, 'LSTAT'), (6, 'RAD'), (7, 'CRIM'), (8, 'INDUS'), (9, 'ZN'), (10, 'TAX'), (11, 'B'), (12, 'AGE')]
特征值排序选择

概念及工作原理 理论上来讲,如果某个特征进行排序或者打乱之后,会很明显的影响(无论正向影响还是负向影响)到模型(预测评分)效果评分, 那么可以说明这个特征对模型来说是重要的;反之,说明这个特征存不存在并不会影响到模型的效能。

基于这么个原理,我们可以提出: (1)特征在进行排序或者打乱之后,会很明显影响模型性能的特征,划定为重要特征。 (2)特征在进行排序或者打乱之后,对模型性能几乎没有影响,划定为不重要特征。

Embedded 嵌入法

集成法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权重值系数, 根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。 Regularization,或者使用决策树思想,Random Forest和Gradient boosting等

包装法与嵌入法的区别:包装法根据预测效果评分来选择,而嵌入法根据预测后的特征权重值系数来选择。 工作原理 先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。

有些机器学习方法本身就具有对特征进行打分的机制,或者很容易将其运用到特征选择任务中, 例如回归模型,SVM,树模型(决策树、随机森林)等等

5.4.1.线性模型

工作原理 越是重要的特征在模型中对应的系数就会越大,而跟目标(标签)变量越是无关的特征对应的系数就会越接近于0。

  1. 线性模型 在噪音不多的数据上,或者是数据量远远大于特征数的数据上,如果特征之间相对来说是比较独立的, 那么即便是运用最简单的线性回归模型也一样能取得非常好的效果

优缺点 缺点: (1)如果特征之间存在多个互相关联的特征,模型就会变得很不稳定 (2)对噪声很敏感,数据中细微的变化就可能导致模型发生巨大的变化

代码语言:javascript
复制
from sklearn.linear_model import LinearRegression

X = df.iloc[:,:4]
Y = df.iloc[:,4]
lr = LinearRegression()
lr.fit(X,Y)
print(X.columns.tolist())
print(lr.coef_)
代码语言:javascript
复制
['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
[-0.11190585 -0.04007949  0.22864503  0.60925205]
5.4.2.正则化

概念:正则化就是把额外的约束或者惩罚项加到已有模型(损失函数)上,以防止过拟合并提高泛化能力。

损失函数由原来的变为 , 是模型系数组成的向量(有些地方也叫参数parameter,coefficients), ∥⋅∥‖⋅‖一般是L1或者L2范数,α是一个可调的参数,控制着正则化的强度。 当用在线性模型上时,L1正则化和L2正则化也称为Lasso和Ridge。

工作原理 L1正则化Lasso(least absolute shrinkage and selection operator)将系数w的l1范数作为惩罚项加到损失函数上,由于正则项非零,这就迫使那些弱的特征所对应的系数变成0。因此L1正则化往往会使学到的模型很稀疏(系数w经常为0),这个特性使得L1正则化成为一种很好的特征选择方法。

L2正则化同样将系数向量的L2范数添加到了损失函数中。由于L2惩罚项中系数是二次方的,这使得L2和L1有着诸多差异,最明显的一点就是,L2正则化会让系数的取值变得平均。

优缺点 L1正则化缺点:L1正则化像非正则化线性模型一样也是不稳定的,如果特征集合中具有相关联的特征,当数据发生细微变化时也有可能导致很大的模型差异。

L2正则化优点:L2正则化对于特征选择来说一种稳定的模型,不像L1正则化那样,系数会因为细微的数据变化而波动。

总结:L2正则化和L1正则化提供的价值是不同的,L2正则化对于特征理解来说更加有用:表示能力强的特征对应的系数是非零。

代码语言:javascript
复制
from sklearn.linear_model import Lasso

X = df.iloc[:,:4]
Y = df.iloc[:,4]
lasso = Lasso(alpha=0.3)
lasso.fit(X,Y)
print(X.columns.tolist())
print(lasso.coef_)
代码语言:javascript
复制
['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
[ 0.        -0.         0.3435091  0.       ]

由上面情况来看,L1范数正则化效果非常差,几乎特征都被弱化了,不知道哪一些重要

代码语言:javascript
复制
from sklearn.linear_model import Ridge

X = df.iloc[:,:4]
Y = df.iloc[:,4]
ridge = Ridge(alpha=0.3)
ridge.fit(X,Y)
print(X.columns.tolist())
print(ridge.coef_)
代码语言:javascript
复制
['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
[-0.11327412 -0.03665944  0.23969479  0.58451798]

由上面情况来看,L2范数正则化从返回的回归系数就可以知道模型比较稳定,并且效果不错。

5.4.3.树模型

工作原理 随机森林具有准确率高、鲁棒性好、易于使用等优点,随机森林提供了两种特征选择的方法: (1)平均不纯度减少 (2)平均精确率减少

代码语言:javascript
复制
# 平均不纯度减少
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np

#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
# 训练随机森林模型,并通过feature_importances_属性获取每个特征的重要性分数。
rf = RandomForestRegressor()
rf.fit(X, Y)
print("Features sorted by their score:")
print(sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names),
             reverse=True))
代码语言:javascript
复制
Features sorted by their score:
[(0.4368, 'RM'), (0.3694, 'LSTAT'), (0.0655, 'DIS'), (0.0384, 'CRIM'), (0.0248, 'NOX'), (0.0156, 'PTRATIO'), (0.0141, 'TAX'), (0.0132, 'AGE'), (0.0117, 'B'), (0.0051, 'INDUS'), (0.0036, 'RAD'), (0.001, 'ZN'), (0.0008, 'CHAS')]
代码语言:javascript
复制
# 通过sklearn中的随机森林返回特征的重要性:

from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
import numpy as np
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['is_train'] = np.random.uniform(0, 1, len(df)) <= .75
df['species'] = pd.Categorical.from_codes(iris.target, iris.target_names)
df.head()
train, test = df[df['is_train']==True], df[df['is_train']==False]
features = df.columns[:4]
clf = RandomForestClassifier(n_jobs=2)
y, _ = pd.factorize(train['species'])
clf.fit(train[features], y)
preds = iris.target_names[clf.predict(test[features])]
pd.crosstab(test['species'], preds, rownames=['actual'], colnames=['preds'])
clf.feature_importances_
代码语言:javascript
复制
array([0.08850542, 0.0250167 , 0.39281297, 0.49366491])

类别标签不平衡处理

概念和工作原理 (1)类别标签不平衡问题是指: 在分类任务中,数据集中来自不同类别的样本数目相差悬殊。

举个例子: 假设类别 A 的样本数量有 M 个,类别 B 的样本数量有 N 个,并且 M >>> N(假设 M:N=9:1), 这种情况我们就可以判定此数据集存在严重的类别标签不平衡的问题,为了防止模型出现严重误差, 因此在建模前需要就样本不平衡问题处理。

(2)类别不平衡问题会造成这样的后果: 在数据分布不平衡时,其往往会导致分类器的输出倾向于在数据集中占多数的类别。 输出多数类会带来更高的分类准确率,但在我们所关注的少数类中表现不佳。

(3)常用方法: 欠采样、过采样及加权处理。

(4)类别标签不平衡情况下的评价指标: 准确率在类别不平衡数据上,说服力最差。应考虑精确率、召回率、F1 值、F-R 曲线和 AUC 曲线。

欠采样

所谓欠采样是指把占比多的类别 A 样本数量(M=900)减少到与占比少的类别 B 样本数量(N=100)一致,然后进行训练。

(1)第一种方法(随机欠采样): 随机欠采样是指通过随机抽取的方式抽取类别 A 中 100 个样本数据与类别 B 中的 100 个样本进行模型训练。 理论公式推导 个

随机欠采样的缺点:欠采样只是采取少部分数据,容易造成类别 A 的信息缺失

(2)第二种方法(代表性算法:EasyEnsemble 集成学习法): 算法思想:利用集成学习机制,将占比多的类别 A 样本数据划分为若干个样本子集供不同学习器使用, 这样对每个学习器来看都进行了欠采样,但在全局来看却不会丢失重要信息。

算法原理如下: 第一步:首先从占比多的类别 A 样本中独立随机抽取出若干个类别 A 样本子集。 第二步:将每个类别 A 的样本子集与占比少的类别 B 样本数据联合起来,训练生成多个基分类器。 第三步:最后将这些基分类器组合形成一个集成学习系统。集成可采用加权模型融合或者取所有基分类器总和的平均值。

EasyEnsemble 集成学习法优点:可以解决传统随机欠采样造成的数据信息丢失问题,且表现出较好的不均衡数据分类性能。

代码语言:javascript
复制
# 生成不平衡分类数据集
from collections import Counter
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=3000, n_features=2, n_informative=2,
                           n_redundant=0, n_repeated=0, n_classes=3,
                           n_clusters_per_class=1,
                           weights=[0.1, 0.05, 0.85],
                           class_sep=0.8, random_state=2018)
Counter(y)
代码语言:javascript
复制
Counter({2: 2532, 1: 163, 0: 305})
代码语言:javascript
复制
# 使用RandomOverSampler从少数类的样本中进行随机采样来增加新的样本使各个分类均衡
from imblearn.over_sampling import RandomOverSampler

ros = RandomOverSampler(random_state=0)
X_resampled, y_resampled = ros.fit_sample(X, y)
sorted(Counter(y_resampled).items())
代码语言:javascript
复制
Using TensorFlow backend.





[(0, 2532), (1, 2532), (2, 2532)]

过采样

所谓过采样是指把占比少的类别 B 样本数量(N=100)扩增到占比多的类别 A 样本数量(M=900)一致,然后进行训练。

第一种方法(随机过采样): 由于随机过采样采取简单复制样本的策略来增加少数类样本,这样容易产生模型过拟合的问题, 即使得模型学习到的信息过于特别(Specific)而不够泛化(General),因此很少使用这种方法。

经典代表性算法是 SMOTE 算法:

SMOTE 的全称是 Synthetic Minority Over-Sampling Technique 即“人工少数类过采样法”,非直接对少数类进行重采样, 而是设计算法来人工合成一些新的少数样本。

算法原理如下: (1)在占比少的类别 B 中随机抽取一个样本 a,从 a 的最近邻 k 个数据中又随机选择一个样本 b。 (2)在 a 和 b 的连线上(或者说[a,b]区间中)随机选择一点作为新的少数类样本。

代码语言:javascript
复制
# SMOTE: 对于少数类样本a, 随机选择一个最近邻的样本b, 然后从a与b的连线上随机选取一个点c作为新的少数类样本
from imblearn.over_sampling import SMOTE

X_resampled_smote, y_resampled_smote = SMOTE().fit_sample(X, y)

sorted(Counter(y_resampled_smote).items())
# [(0, 2532), (1, 2532), (2, 2532)]
代码语言:javascript
复制
[(0, 2532), (1, 2532), (2, 2532)]
代码语言:javascript
复制
# ADASYN: 关注的是在那些基于K最近邻分类器被错误分类的原始样本附近生成新的少数类样本
from imblearn.over_sampling import ADASYN

X_resampled_adasyn, y_resampled_adasyn = ADASYN().fit_sample(X, y)

sorted(Counter(y_resampled_adasyn).items())
# [(0, 2522), (1, 2520), (2, 2532)]
代码语言:javascript
复制
[(0, 2522), (1, 2520), (2, 2532)]
代码语言:javascript
复制
# RandomUnderSampler函数是一种快速并十分简单的方式来平衡各个类别的数据: 随机选取数据的子集.
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0)
X_resampled, y_resampled = rus.fit_sample(X, y)

sorted(Counter(y_resampled).items())
代码语言:javascript
复制
[(0, 163), (1, 163), (2, 163)]
代码语言:javascript
复制
# 在之前的SMOTE方法中, 当由边界的样本与其他样本进行过采样差值时, 很容易生成一些噪音数据. 因此, 在过采样之后需要对样本进行清洗. 
# 这样TomekLink 与 EditedNearestNeighbours方法就能实现上述的要求.
from imblearn.combine import SMOTEENN
smote_enn = SMOTEENN(random_state=0)
X_resampled, y_resampled = smote_enn.fit_sample(X, y)

sorted(Counter(y_resampled).items())
代码语言:javascript
复制
[(0, 2111), (1, 2099), (2, 1893)]
代码语言:javascript
复制
from imblearn.combine import SMOTETomek
smote_tomek = SMOTETomek(random_state=0)
X_resampled, y_resampled = smote_tomek.fit_sample(X, y)

sorted(Counter(y_resampled).items())
# [(0, 2412), (1, 2414), (2, 2396)]
代码语言:javascript
复制
[(0, 2412), (1, 2414), (2, 2396)]

加权处理

加权处理是指通过调整不同类型标签的权重值,增加占比少的类别 B 样本数据的权重,降低占比多的类别 A 样本数据权重, 从而使总样本占比少的类别 B 的分类识别能力与类别 A 的分类识别能力能够同等抗衡。

加权处理原理如下: 遍历每一个样本,设总样本占比多的类别 A 的权重为 W1(自定义),总样本占比少的类别 B 的权重为 W2(自定义),其中 W2 > W1。 其实这个也类似于对模型进行惩罚,从而影响各个类别标签的重要性。

代码语言:javascript
复制
# 使用SVM的权重调节处理不均衡样本 权重为balanced 意味着权重为各分类数据量的反比
from sklearn.svm import SVC  

svm_model = SVC(class_weight='balanced')
svm_model.fit(X, y)
代码语言:javascript
复制
SVC(C=1.0, break_ties=False, cache_size=200, class_weight='balanced', coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='scale', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)
代码语言:javascript
复制
# BalancedBaggingClassifier 允许在训练每个基学习器之前对每个子集进行重抽样. 简而言之, 该方法结合了EasyEnsemble采样器与分类器(如BaggingClassifier)的结果.
from sklearn.tree import DecisionTreeClassifier
from imblearn.ensemble import BalancedBaggingClassifier

bbc = BalancedBaggingClassifier(base_estimator=DecisionTreeClassifier(),
                                replacement=False,
                                random_state=0)
bbc.fit(X, y) 
代码语言:javascript
复制
BalancedBaggingClassifier(base_estimator=DecisionTreeClassifier(ccp_alpha=0.0,
                                                                class_weight=None,
                                                                criterion='gini',
                                                                max_depth=None,
                                                                max_features=None,
                                                                max_leaf_nodes=None,
                                                                min_impurity_decrease=0.0,
                                                                min_impurity_split=None,
                                                                min_samples_leaf=1,
                                                                min_samples_split=2,
                                                                min_weight_fraction_leaf=0.0,
                                                                presort='deprecated',
                                                                random_state=None,
                                                                splitter='best'),
                          bootstrap=True, bootstrap_features=False,
                          max_features=1.0, max_samples=1.0, n_estimators=10,
                          n_jobs=None, oob_score=False, random_state=0,
                          replacement=False, sampling_strategy='auto',
                          verbose=0, warm_start=False)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据科学CLUB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 特征选择
    • 特征选择概述
      • Filter 过滤法
        • 方差选择法
          • 相关系数法
            • 卡方检验
              • 互信息法
                • Wrapper 包装法
                  • 稳定性选择(Stability Selection)
                    • 递归特征消除
                    • 特征值排序选择
                  • Embedded 嵌入法
                    • 5.4.1.线性模型
                    • 5.4.2.正则化
                    • 5.4.3.树模型
                • 类别标签不平衡处理
                  • 欠采样
                    • 过采样
                      • 加权处理
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档