sklearn-4特征工程与数据表示

1 分类变量

任务 根据美国人年龄,工作,教育水平等特征判定这人年收入比50000美元大还是小

问题 有的特征不是数值型,比如性别(男或女),工作类型等,而监督学习模型需要都是数值型的特征,可以通过one-hot编码解决

1.1 one-hot编码(虚拟变量)

举例,如何用one-hot表示星期几?创七个特征分别表示周一到周末,如果是周一,则周一特征值为1,其他全为0

如何使用one-hot 1pandas(更方便) 2sklearn

1.1.1 检查字符串编码的分类数据

调用pd.get_dummies(data)获取onehot处理的数据,然后用logistic回归模型进行预测,发现准确度只有80%,因为使用onehot后一个特征虽然分成多个0-1特征但很多0特征相同,导致结果不理想

解决方法 1在同时包含训练集和测试集的dataframe调用get_dummies 2保证分别调用tr,te的列名相同

1.1.2 数字编码分类变量

直接用onehot的问题 1拼写有误的属性可能会变成独立的特征 2onehot虽然用01表示但模型会将01识别为连续的数,而实际01只想表示两种不同的分类,而不是当作连续数值处理 3使用get_dummies只会处理字符串的值,不会处理其他数值,比如整数

解决 1可用sklearn的OneHotEncoder指定哪些变量连续哪些变量离散 2将df的整数类别列先转化类型为str,再给get_dummies参数传入comumns=[]指定要处理的列

    def test_onehot_classifier(self):
        demo_df = pd.DataFrame({'integer feature': [0,1,2,1], 'categorical feature': ['sock','box','fox','box']})
        display(demo_df)
        demo_df['integer feature'] = demo_df['integer feature'].astype(str)
        display(pd.get_dummies(demo_df, columns=['integer feature', 'categorical feature']))

sklearn-4特征工程与数据表示_第1张图片

2 分箱,离散化,线性模型和树

数据预测准度与使用的模型有关,比如线性模型,树模型(决策树)

决策树与输入结构强相关,且无法预测,线性模型无法构造复杂模型,比如非直线模型,此时可以使用特征分箱(binning),也叫离散化(discretization),将数据分为多个特征

2.1 分箱

比如wave数据集,是一个单特征数据集,我们加载特征在-3到3之间的数据,将-3到3分成十个区间,每个点在哪个区间作为特征,总共有10个特征,10个特征用onehot编码,然后对10个特征的分箱训练(分箱训练需要用OneHotEncoder)(这样,就把单特征的wave数据集转化为了10个特征的onehot数据集)

    def test_plot_binning(self):
        bins = np.linspace(-3, 3, 11)
        which_bin = np.digitize(self.wave[0], bins=bins)
        encoder = OneHotEncoder(sparse=False).fit(which_bin)
        line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
        x_bin, line_bin = encoder.transform(which_bin), encoder.transform(np.digitize(line, bins=bins))
        print(f'x_bin first 5: {x_bin[:5]}')
        reg = LinearRegression().fit(x_bin, self.wave[1])
        plot.plot(line, reg.predict(line_bin), label='linear regression binned')
        reg = DecisionTreeRegressor(min_samples_split=3).fit(x_bin, self.wave[1])
        plot.plot(line, reg.predict(line_bin), label='decision tree binned')
        plot.plot(self.wave[0], self.wave[1], 'o', c='k')
        plot.vlines(bins, -3, 3, linewidth=1, alpha=.2)
        plot.legend(loc='best')
        plot.ylabel('regression output')
        plot.xlabel('input feature')
        plot.show()

sklearn-4特征工程与数据表示_第2张图片

sklearn-4特征工程与数据表示_第3张图片

用分箱数据对wave数据集进行线性回归和决策树回归

结果 1决策树和线性模型完全重合

原因与解释 1因为每个分箱被看作一个特征,所有模型对单个特征的的预测都是相同的

总结 1分箱后,线性模型更加灵活(可预测非线性特征),决策树模型简化了(从过拟合向欠拟合发展) 2一般来说,分箱可以增加线性模型的预测效果(更灵活,可预测非线性模型) 3如果想拟合特征非线性可以考虑分箱

3 交互特征与多项式特征

如果想丰富线性模型的特征,可以给原始数据添加交互特征或多项式特征

用途 统计建模或机器学习一些基本分析

3.1 学习原始特征

给分箱的x数据添加原始x数据,可学到偏移和斜率

    def test_plot_bin_gradient(self):
        bins = np.linspace(-3, 3, 11)
        which_bin = np.digitize(self.wave[0], bins=bins)
        # sparse默认为True,会返回降维的系数矩阵,这里False返回完整矩阵,否则np.hstack会报错
        encoder = OneHotEncoder(sparse=False).fit(which_bin)
        line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
        x_bin, line_bin = encoder.transform(which_bin), encoder.transform(np.digitize(line, bins=bins))
        x_combined, line_combined = np.hstack([self.wave[0], x_bin]), np.hstack([line, line_bin])
        reg = LinearRegression().fit(x_combined, self.wave[1])
        print(f'line combine shape {line_combined.shape}')
        plot.plot(line, reg.predict(line_combined), label='linear regression combined')
        for bin in bins:
            plot.plot([bin, bin], [-3, 3], ':', c='k')
        plot.legend(loc='best')
        plot.ylabel('regression output')
        plot.xlabel('input feature')
        plot.plot(self.wave[0], self.wave[1], 'o', c='k')
        plot.show()

sklearn-4特征工程与数据表示_第4张图片

思考,为什么斜率是负的?

添加原始特征后,变成y=w1x1+w2。反推的话,斜率为负,即原始特征的系数w1为负,则分箱系数需比未添加原始特征时多。不算了,可能是线性代数解方程,大概算了下可能不对,算的w1小了w2就高,w2小了w1高,线性回归模型约束条件下可能w1为负最好

3.2 学习交互特征

学习交互特征,看下效果

    def test_plot_interaction_feature(self):
        bins, line = np.linspace(-3, 3, 11), np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
        which_bin = np.digitize(self.wave[0], bins=bins)
        encoder = OneHotEncoder(sparse=False).fit(which_bin)
        x_bin, line_bin = encoder.transform(which_bin), encoder.transform(np.digitize(line, bins=bins))
        x_prod, line_prod = np.hstack([x_bin, self.wave[0] * x_bin]), np.hstack([line_bin, line * line_bin])
        print(f'x prod shape: {x_prod.shape}')
        reg = LinearRegression().fit(x_prod, self.wave[1])
        plot.plot(line, reg.predict(line_prod), label='linear regression product')
        for bin in bins:
            plot.plot([bin, bin], [-3, 3], ':', c='k')
        plot.plot(self.wave[0], self.wave[1], 'o', c='k')
        plot.ylabel('regression output')
        plot.xlabel('input feature')
        plot.show()

sklearn-4特征工程与数据表示_第5张图片

3.3 学习多项式特征

利用了泰勒,将拟合曲线展开泰勒级数

添加的特征为输入特征的x次幂,可传参指定

因为一次幂等于x,所以不需要再np.hstack合并特征

    def test_plot_polynomial_feature(self):
        line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
        poly = PolynomialFeatures(degree=10, include_bias=False).fit(self.wave[0])
        x_poly = poly.transform(self.wave[0])
        reg = LinearRegression().fit(x_poly, self.wave[1])
        line_poly = poly.transform(line)
        plot.plot(line, reg.predict(line_poly), label='polynomial regression linear')
        plot.plot(self.wave[0], self.wave[1], 'o', c='k')
        plot.xlabel('input feature')
        plot.ylabel('regression output')
        plot.legend(loc='best')
        plot.show()

sklearn-4特征工程与数据表示_第6张图片

高次幂在边界会有极端表现

对比核svm,没有多项式这种极端表现

    def test_plot_svm_wave(self):
        line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
        for gamma in (1, 10):
            svr = SVR(gamma=gamma).fit(*self.wave)
            plot.plot(line, svr.predict(line), label=f'SVR gamma={gamma}')
        plot.plot(*self.wave, 'o', c='k')
        plot.xlabel('input feature')
        plot.ylabel('regression feature')
        plot.legend(loc='best')
        plot.show()

sklearn-4特征工程与数据表示_第7张图片

对比发现,svm结果比polynomial好,且不需要显式特征变换

3.4 小结

交互特征作为一种手段,可以应用于其他模型上,比如线性模型,随机森林

新增特征对不同模型来说效果不同,有的会提升准度,有的会降低准度,需取舍

4 单变量非线性变换

除了给数据集添加交互特征核多项式特征,还可以添加非线性特征,比如log,exp, 三角函数等特征

4.1 exp

将0-1的随机数按公式变换 y=10*exp(x),然后统计y分布情况,然后对比下原始数据

    def test_plot_random_distribute(self):
        r = np.random.RandomState(0)
        x_org, w = r.normal(size=(1000, 3)), r.normal(size=3)
        x = r.poisson(10 * np.exp(x_org))
        y = np.dot(x_org, w)

        bins = np.bincount(x[:, 0])
        print(f'bins shape: {bins.shape}, bins [:5]: {bins[:5]}')
        fig, axes = plot.subplots(2, 1, figsize=(10, 10))
        axes[0].bar(range(len(bins)), bins)
        axes[0].set_ylabel('number of appearances')
        axes[0].set_xlabel('x')
        axes[1].hist(x_org[:, 0], bins=30)
        axes[1].set_ylabel('number of appearances')
        axes[1].set_xlabel('y')
        plot.show()

sklearn-4特征工程与数据表示_第8张图片

np.poisson表示泊松分布。发现数据处理后变成泊松分布了,数据处理前是正态分布

线性模型无法很好的处理非线性数据,可以试着预测下

    def test_predict_poisson_linear(self):
        r = np.random.RandomState(0)
        x_org, w = r.normal(size=(1000, 3)), r.normal(size=3)
        x, y = r.poisson(10 * np.exp(x_org)), np.dot(x_org, w)
        xtr, xte, ytr, yte = train_test_split(x, y, random_state=0)
        score = Ridge().fit(xtr, ytr).score(xte, yte)
        print(f'ridge predict poisson distribute score: {score}')

将数据处理为线性模型,会对ridge性能有所提升,比如再用对数函数处理回来

4.2 小结

交互特征和多项式特征一定程度可以提高线性模型性能

对基于树的模型一般没有明显提升,因为树可以自己发现重要的交互特征,不需要再处理变换数据

其他模型一定程度也可受益于新增特征,但没线性模型明显

5 自动化特征选择

增加特征缺点 模型复杂化,更大可能过拟合

解决方法 新增最有用的特征,不要新增无用特征

如何判断特征的用处有多大? 1单变量统计 2基于模型选择 3迭代选择 三种方法都是监督方法

5.1 单变量统计

概念 计算每个特征和目标值之间是否存在统计显著性,选择具有最高置信度的特征。对分类问题讲,可以叫做方差分析。

特性 单变量的,即每次单独考虑每个特征。比如如果一个特征只有和另一个特征合并时才有意义,此特征将被舍弃

操作 1选处理单变量特征对应的测试,分类问题是f_classif,回归问题是f_regression 2根据测试确定的p值选择一种舍弃特征的方法 3根据阈值舍弃p值过大的特征(阈值需要计算,有SelectKBest和SelectPercentile,前者选固定k个特征,后者选固定百分比的特征)

任务 给原始数据添加一些噪声,通过单变量统计删除无用的特征

5.1.1 Percentile

使用SelectPercentile选择特征

    def test_feature_select_base(self):
        # show how to select features
        r = np.random.RandomState(42)
        noise = r.normal(size=(len(self.cancer.data), 50))
        cancer_noise = np.hstack([self.cancer.data, noise])
        xtr, xte, ytr, yte = train_test_split(cancer_noise, self.cancer.target, random_state=0, test_size=.5)
        select = SelectPercentile(percentile=50).fit(xtr, ytr)
        xtr_selected = select.transform(xtr)
        print(f'before select shape: {xtr.shape}, after select shape: {xtr_selected.shape}')

        # show which features are selected
        mask = select.get_support()
        print(f'feature selection mask: {mask}')
        plot.matshow(mask.reshape(1, -1), cmap='gray_r')
        plot.xlabel('sample index')
        plot.show()

发现大部分特征都是原始特征,但仍有部分特征来源噪声

将添加噪声特征的数据和选择特征后的数据用LogisticRegression分类模型训练,发现选择特征的约为94%,未选择特征的约为93%

用处 如果比较耗资源,可以用此方法删掉一些特征,但需要注意是否删除的是无用特征

5.2 基于模型选择

概念 通过监督学习模型选的特征,然后传入个阈值参数,将监督模型选的特征再用阈值过滤一遍

    def test_feature_select_from_model(self):
        noise = np.random.RandomState(42).normal(size=(len(self.cancer.data), 50))
        cancer_noise = np.hstack([self.cancer.data, noise])
        xtr, xte, ytr, yte = train_test_split(cancer_noise, self.cancer.target, random_state=0)
        select = SelectFromModel(RandomForestClassifier(n_estimators=100, random_state=42), threshold='median').fit(xtr, ytr)
        mask = select.get_support()
        plot.matshow(mask.reshape(1, -1), cmap='gray_r')
        plot.xlabel('sample index')
        plot.show()

相比于Percentile,原始特征多选了两个,用LogisticRegression训练,准度提高到约95%

5.3 迭代特征选择

对比

单变量统计 没用监督模型选择特征

基于模型选择 用单个模型选择特征

迭代特征选择 用很多模型选择特征,每个模型使用不同数量的特征(有两种方法,一种是从0开始逐一添加特征,另一种是先用所有特征然后逐一删除特征)

特点 成本高,因为构造了一系列模型并训练

递归特征消除(Recursive Feature Elimination, RFE) 是迭代特征选择的一种方法。先从所有特征建模,然后从模型舍弃一个最不重要特征,然后用剩下特征重新建模,再舍弃一个最不重要特征,循环往复知道剩余特征数量达到预期数量

看下RFE效果

    def test_feature_select_iter(self):
        noise = np.random.RandomState(42).normal(size=(len(self.cancer.data), 50))
        xtr, xte, ytr, yte = train_test_split(np.hstack([self.cancer.data, noise]), self.cancer.target, random_state=0, test_size=.5)
        select = RFE(RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=40).fit(xtr, ytr)
        plot.matshow(select.get_support().reshape(1, -1), cmap='gray_r')
        plot.xlabel('sample index')
        plot.show()

 

发现RFE只漏了一个原始特征,但训练周期更长,因为训练了40次随机森林

5.4 小结

当不确定用什么特征时,可以使用自动化特征选择

如果大概确定用什么特征,可以构造交互特征或多项式特征

6 利用专家知识

即在建模训练时用一些已知的先验经验增加模型准确度

例子 预测某人家门口租车情况,即每3小时内租车数量,数据集为mglearn.datasets.load_citibike()

数据集为单特征,特征为租车的时间

6.1 随机森林训练时间

将数据原始特征用随机森林训练,基本什么也没学到

因为决策树无法做预测

6.2 随机森林训练小时数

即使是未来时间租车,小时也总是在0-24范围内,假如用这个特征,那么可以使用随机森林学习

仅训练小时数的单特征准度约为60%

6.3 随机森林训练小时数和星期数

租车和工作日与否有关,可以添加星期几作为一个特征,和小时数作为两个特征训练随机森林模型

准确率大概达到84%

6.4 线性模型训练小时数和星期数

尝试用简单的线性模型训练

发现准确度大概只有13%,分析发现模型将整数编码的星期数和小时数视为连续变量,可以通过onehotEncoder编码将其转为分类变量再次训练

6.5 线性模型训练小时数和星期数onehotEncoder

准确率达到62%

6.6 添加交互特征和多项式特征

准确率达到85%,发现准确率和随机森林的差不多

和随机森林相比的优点是,可以看到特征对应的学习系数,而这在随机森林模型中是看不到的

你可能感兴趣的:(机器学习,人工智能)