精华内容
下载资源
问答
  • 精通特征工程
    2022-03-17 11:03:01

    《精通特征工程》——速读学习1 基础特征工程

    该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

    time: 2022-02-08

    学习目标:“通过Python示例掌握特征工程基本原则和实际应用,增强机器学习算法效果”;

    目录

    • 第1章 机器学习流程:数值 模型 特征
    • 第2章 简单而奇妙的数值:数值型数据的基础特征工程,过滤 分箱 缩放 对数变换和幂次变换,以及交互特征
    • 第3章 文本数据:扁平化、过滤和分块:自然文本特征工程,研究词袋 n-gram 短语检测技术
    • 第4章 特征缩放的效果:从词袋到tf-idf:tf-idf(词频-逆文档频率),特征缩放
    • 第5章 分类变量:自动化时代的数据计数:分类变量编码技术,特征散裂化和分箱计数
    • 第6章 数据降维:使用PCA挤压数据:主成分分析PCA
    • 第7章 非线性特征化与-均值模型堆叠:k-均值聚类,模型堆叠
    • 第8章 自动特征生成:图像特征提取和深度学习:图像处理及其特征提取 手动提取技术——SIFT和HOG,最新的特征提取技术——深度学习
    • 第9章 回到特征:建立学术论文推荐器:实例
    • 附录A 线性建模与线性代数基础

    前言

    为了提取知识和做出预测,机器学习使用数学模型来拟合数据;输入即特征,指原始数据某些方面的数值表示(注意:原始数据不一定是数值型数据);

    特征工程:指从原始数据中提取特征并将其转换为适合机器学习模型的格式;是机器学习中的一个环节;

    建立机器学习流程的绝大部分时间都耗费在特征工程和数据清洗上;

    本书每章阐述了一个数据问题,结合这些问题,对特征工程的一些基本原则进行说明;

    • Numpy:数值向量和矩阵操作
    • Pandas:数据框,作为数据科学的基础数据结构
    • scikit-learn:是一个通用的机器学习包,包含大量模型和特征转换模型
    • Matplotlib和样式库Seaborn:提供绘图和可视化支持

    第1章 机器学习流程

    数据

    数据反应小部分现实,综合起来才能得到完整的描述;描述散乱,由千万小段组成,而且总是存在测量噪声和缺失值;

    任务

    数据处理流程往往是多阶段的迭代过程;

    两个构成机器学习基础的数学实体:模型和特征;

    模型

    统计模型:错误数据、冗余数据、缺失数据;

    特征

    特征是原始数据的数值表示;正确的特征应该适合当前的任务,并易于被模型所使用;

    特征工程就是在给定数据、模型和任务的情况下设计出最适合的特征的过程;

    模型评价

    模型和特征相辅相成,对其中一个的选择会影响另一个;好的特征可以使建模更容易;

    • 数据源N收集数据,作为原始数据;
    • 原始数据经过清洗和转换,得到特征(这一步就是特征工程);
    • 通过建模,从特征中提取知识;

    第2章 简单而又奇妙的数值

    数值型数据作为最简单的数据类型(相较于文本、图像而言),也最容易被数学模型所使用,但往往数值型数据也需要进行特征工程;

    好的特征既能表示出数据的主要特点,还应该符合模型的假设,因而通常需要进行数据转换;

    数值型数据的特征工程很基本,只要原始数据可以转换为数值型特征,就可以应用这些技术;

    合理性检测

    • 数据量级;
    • 特征尺度:最大值、最小值、跨度(是否跨多个数量级);

    模型是输入特征的平滑函数,那么它对输入的尺度很敏感:k-均值、最近邻、径向基核函数,以及所有使用欧式距离的方法都属于这种,对于这类模型和模型成分,通常需要对特征进行标准化,以便将输出控制在期望的范围内;

    逻辑函数则对输入特征的尺度并不敏感;无论输入如何,这种函数输出总是一个二值变量;另一个例子是阶梯函数(决策树模型中使用了输入特征的阶梯函数);

    基于空间分割树的模型(决策树、梯度提升机、随机森林)对尺度是不敏感的;但如果特征是某种累计值,最终可能会超出训练树的取值范围,因此需要定期对输入尺度进行调整;

    • 数值型特征的分布;

    这种分布表现出一个特征值出现的概率;输入特征的分布对于某些模型来说十分重要;

    线性回归模型的训练过程,需要假定预测误差近似地服从高斯分布;在预测目标分布在多个数量级中时,误差符合高斯分布的假定将不会被满足,一种解决方法是对输出目标进行转换,如对数变换就可以使变量的分布更加接近于高斯分布;

    除了将特征转换为模型所需或训练假设,还可以将多个特征进行组合;我们希望特征更具信息量(更简洁的捕获原始数据中的重要信息),这样可以使模型本身更简单,更容易训练和评价,也能做出更好的预测;

    极端情况,可以使用统计模型的输出作为复杂特征,这种思想称为模型堆叠

    标量、向量和空间

    • 标量:单独的数值型特征
    • 向量:标量的有序列表
    • 向量空间:向量位于向量空间中

    模型的输入通常表示为 数值向量

    在数据世界中,抽象的向量和它的特征维度具有实际意义,所有数据的集合可以在特征空间中形象地表示为一个点云;与在特征空间(使用特征维度)表示数据类似,也可以在数据空间(使用数据维度)中进行特增表示;

    处理计数

    • 当数据被大量且快速地生成时,很有可能包含一些极端值,因此需要检查数据尺度;以确定是保留原始数值,还是转成二值数据,或者进行粗粒度的分箱操作;

    二值化

    • 一个例子是百万歌曲数据集中的收听次数数据,多的接近1万,少的在0附近,从直方图上可以得到 收听次数越多的范围,记录数量越小,而且带有严重的偏置,如果视图使用实际的收听次数去预测歌曲的受欢迎程度,模型会被严重带偏;
    • 这可理解为,原始的收听次数并不是衡量用户喜好的强壮指标;(如 我们不能认为收听了某歌曲20次的人喜欢该歌的程度肯定是收听了10次的人的两倍)

    一种更强壮的用户偏好表示方法是 将收听次数二值化

    • 如把所有大于1的次数值设为1;
    • 二值目标变量是一个简单又强壮的用户偏好衡量指标;

    这是一个对模型目标变量处理的例子,值得注意的是,目标变量并不是特征,因为它不是输入;但为了正确地解决问题,有时确实需要修改目标变量

    区间量化(分箱)

    • 以商家评分数据集为例,点评数量是一个有用的输入特征,因为人气和高评分之间通常有很强的相关性;

    原始点评数量的直方图可视化:

    • 大多数商家的点评数量很少,但是有些商家具有几千条点评;
    • 原始点评数量横跨若干个数量级,这对很多模型来说都是问题;
    import pandas as pd
    import json
    
    biz_file - open("yelp_academic_dataset_business.json")
    biz_df = pd.DataFrame([json.loads(x) for x in biz_file.readlines()])
    biz.file.close()
    
    
    # 
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    # 绘制点评数量直方图
    sns.set_style('whitegrid')
    fig, ax = plt.subplots()
    biz_df["review_count"].hist(ax=ax, bins = 100)
    ax.set_yscale('log')
    ax.tick_params(labelsize=14)
    ax.set_xlabel("Review Count", fontsize=14)
    ax.set_ylabel("Occurrence", fontsize=14)
    
    

    一种解决方法是对计数值进行区间量化,然后在使用量化后的结果:

    • 将点评数分到多个箱子里面,去掉实际的计数值;
    • 将连续型数值映射为离散型数值,这种离散型数值可以看作一种有序的分箱序列,以表示对密度的测量

    对数据进行区间量化,首先需要确定每个分箱的宽度,通常有两种方法:

    • 固定宽度分箱
    • 自适应分箱

    固定宽度分箱

    • 每个分箱会包含一个具体范围内的数值;
    • 这些范围可以人工定制,也可以自动分段,可以是线性的,也可以是指数性的;
    • 如 10(0-9,10-19。。。)或10的幂(0-9,10-99。。。)(也可以是任何其他常数)进行分组;

    一个通过固定宽度分箱对计数值进行区间量化的代码段:

    import numpy as np
    
    small_counts = np.random.randint(0, 100, 20)
    
    # 通过除法随机映射到间隔均匀的分箱中,每个分箱取值范围为0~9
    np.floor_divide(small_counts, 10) # 整除 抛开余数
    
    # 横跨若干个数量级的技术数组
    large_counts = [296,8286,64011,80,3,725,867,2215,7689,11495,91897,...44,28,7971,926,122,22222]
    
    np.floor(np.log10(large_counts)) # 向下取整
    
    

    分箱操作实际是将原始数值 拉回同一量级 以便处理;

    分位数分箱

    • 固定宽度分箱容易计算,但如果计数值中有比较大的缺口,就会产生没有任何数据的空箱子;
    • 根据数据的分布特点,进行自适应的箱体定位,就可以解决这个问题,具体的可以使用数据分布的分位数实现;

    分位数是可以将数据划分为相等的若干份数的值;对应回直方图上,可以看出,分位数的数值是向较小的计数值偏斜的;

    点评数量十分位数的代码示例:

    import warnings
    
    warnings.filterwarnings('ignore')
    
    import numpy as np
    import pandas as pd
    
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    small_counts = np.random.randint(0, 10000, 200)
    small_counts_log = np.log10(small_counts)
    
    biz_df = pd.DataFrame({'review_count':small_counts,'log_review_count':small_counts_log})
    
    
    deciles = biz_df["review_count"].quantile([.1,.2,.3,.4,.5,.6,.7,.8,.9]) 
    
    sns.set_style("whitegrid")
    fig, ax = plt.subplots()
    biz_df["review_count"].hist(ax=ax, bins=10)
    # 在直方图上画出十个分位数
    for pos in deciles:
        print('pos', pos)
        handle = plt.axvline(pos, color="r")
    ax.legend([handle], ['deciles'], fontsize=14)
    # ax.set_yscale('log')
    # ax.set_xscale('log')
    ax.tick_params(labelsize=14)
    ax.set_xlabel("Review Count", fontsize=14)
    ax.set_ylabel("Occurence", fontsize=14)
    
    

    计算分位数并将数据映射到分位数分箱中,可以使用Pandas库:

    • pandas.DataFrame.quantilepandas.Series.quantile可以计算分位数;
    • pandas.qcut可以将数据映射为所需的分位数值;
    import pandas as pd 
    
    # 将计数值映射为分位数
    pd.qcut(large_counts, 4 , labels=False)
    
    # 计算实际的分位数值
    large_counts_series = pd.Series(large_counts)
    large_counts_series.quantile( [0.25,0.5,0.75] )
    
    
    

    对数变换

    之前简要提到了 通过取计数值的对数将数据映射到指数宽度分箱的方法,我们继续;

    对数函数是指数函数的反函数:㏒a(b) = c, 则 a^c = b

    • 对数函数可以将(0,1)这个小区间的数映射到(-∞,0)这个包括全部负数的大区间上;
    • ``㏒10(x)可以将区间[1,10]映射到[0,1],将[10,100]映射到[1,2]`;
    • 换言之,对数函数可以对大数值的范围进行压缩,对小数值的范围进行扩展

    对于重尾分布(质量更多的分布在尾部)的整数值的处理,对数变换是一个非常强大的工具;

    经过对数变换,评论数据的直方图在低计数值的集中趋势会被减弱:

    fig, (ax1, ax2) = plt.subplots(2,1)
    biz_df["review_count"].hist(ax=ax1, bins=10)
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel("review_count", fontsize=14)
    ax1.set_ylabel("Occurrence", fontsize=14)
    
    
    biz_df["log_review_count"].hist(ax=ax2, bins=10)
    ax2.tick_params(labelsize=14)
    ax2.set_xlabel('log10(review_count)', fontsize=14)
    ax2.set_ylabel('Occurrence', fontsize=14)
    
    

    实战过程:

    • 使用商家点评数量 来预测 商家的平均评分

    • 使用文章单词数量 来预测 文章流行程度

    • 预测输出是连续值,可以使用简单线性回归来构造模型;

    • 使用scikit-learn,对 进行了对数变换和未进行对数变换的特征上 进行10-折交叉验证的线性回归;

    • 使用R2分数评价模型,以衡量训练出来的回归模型预测新数据的能力(R方分数越大越好,完美模型的R方值为1,它也可以是负数,说明模型很糟糕);

    # 实例1
    
    import pandas as pd
    import numpy as np
    
    import json
    from sklearn import linear_model
    from sklearn.model_selection import cross_val_score
    
    # 提前计算对数变换值,且对点评数量+1,以避免当点评数为0时,对数运算结果得到负无穷大
    biz_df["log_review_count"] = np.log10(biz_df["review_count"] + 1)
    
    # 训练线性回归模型
    m_orig = linear_model.LinearRegression()
    scores_orig = cross_val_score(m_orig, biz_df[["review_count"]], biz_df["stars"], cv=10)
    
    m_log = linear_model.LinearRegression()
    scores_log = cross_val_score(m_log, biz_df[["log_review_count"]], biz_df["stars"], cv=10)
    
    
    print("R-squared score without log transform: %.5f (+/- %.5f)"%(scores_orig.mean(), scores_orig.std() * 2))
    print("R-squared score with log transform: %.5f (+/- %.5f)"%(scores_log.mean(), scores_log.std() * 2))
    
    # 平均星级(离散) 与 评价数量 的关系 原非线性
    
    
    # 实例2
    
    # 新闻流行程度预测问题 输入和输出相关性可视化
    fig2, (ax1, ax2) = plt.subplots(2,1)
    ax1.scatter(df["n_tokens_count"],df["shares"])
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel("Number of Words in Article", fontsize=14)
    ax1.set_ylabel('Number of Shares', fontsize=14)
    
    ax2.scatter(df['log_n_tokens_count'],df["shares"])
    ax2.tick_params(labelsize=14)
    ax1.set_xlabel("Log of the Number of Words in Article", fontsize=14)
    ax1.set_ylabel('Number of Shares', fontsize=14)
    
    

    值得关注的是 数据的可视化十分重要,比如 上面选择的线性回归模型 与 可视化的图形中得出数据的输入和目标之间关系是否相符;在构建模型时,使用可视化方法查看一下输入和输出之间以及各个输入特征之间的关系是一种非常好的做法;

    指数变换

    • 指数变换是一个变换族,对数变换只是它的一个特例;
    • 它们都是方差稳定化变换

    泊松分布(了解):

    • 是一种重尾分布,它的方差等于它的均值;其质心越大,方差就越大,重尾程度也越大;
    • 指数变换可以改变变量的分布,使得方差不再依赖于均值;

    λ表示泊松分布的均值,当其变大时,不仅整个分布向右移动,质量也更分散,方差随之变大;

    平方根变换和对数变换都可以简单推广为Box-Cox变换:

    • = (x^λ - 1)/λ , 当(λ != 0)

    • = ln(x) , 当(λ = 0)

    • λ=0 即对数变换,λ=0.25或0.5(对应的是平方根变换的一种缩放和平移形式);

    • λ值小于1时,可以压缩高端值,大于1时,起的作用相反;

    只有当数据为时,Box-Cox公式才有效;对非正数据,可以加一个固定常数,对数据进行平移;

    可以通过极大似然方法(找到能使变换后信号的高斯似然最大化的λ值)找到λSciPystats包中有Box-Cox变换的实现方法,并包括找到最优BoxCox变换参数的功能;

    from scipy import stats
    
    # 检查最小值 Box-Cox假定输入数据为正
    biz_df['review_count'].min()
    
    rc_log = stats.boxcos(biz_df['review_count'], lmbda=0) # 指定 λ = 0,此时对应的是对数变换
    
    # 默认 进行Box-Cox变换时 会找到使得输出最接近正太分布的λ值
    rc_bc, bc_params = stats.boxcox(biz_df['review_count'])
    bc_params # 即 最终的 λ 值
    
    

    概率图(probplot)

    • 是一种简单的可视化方法,用于比较数据的实际分布与理论分布;
    • 实际上是一种表示实测分位数和理论分位数的关系的散点图;

    原始点评数量的概率图具有明显的重尾特征,相比对数变换,最优Box-Cox变换对尾部的压缩更强:

    prob1 = stats.probplot(biz_df['review_count'], dist=stats.norm, plot=ax1)
    
    prob1 = stats.probplot(biz_df['rc_log'], dist=stats.norm, plot=ax2)
    
    prob1 = stats.probplot(biz_df['rc_bc'], dist=stats.norm, plot=ax3)
    
    

    更多的图形绘制信息 参考matplotlib

    特征缩放/归一化

    • 有些特征的值 具有明显的界限,有些则可以无限增加;
    • 有些模型的输入是平滑函数,比如线性回归、逻辑回归、或包含矩阵的模型;
    • 那些基于树的模型,不在乎输入的尺度有多大;

    如果模型对输入特征的尺度很敏感,就需要进行特征缩放;特征缩放可以改变特征的尺度,说是些人将之称为特征归一化

    特征缩放通常对每个特征独立进行,一下是集中常用的特征缩放,他们会产生不同的特征分布;

    min-max缩放:

    • min-max缩放 可以将所有特征值压缩(或扩展)到[0,1]之间;
    • 公式:x_n = (x-min)/(max-min)

    特征标准化/方差缩放

    • 公式:x_n = (x - mean)/sqrt(var);
    • 先减去均值,再除以方差;
    • 缩放后的特征均值为0,方差为1;如果初始特征服从高斯分布,那么缩放后的特征也服从高斯分布;

    在稀疏特征上执行min-max缩放和标准化的时候要慎重,他们都会从原始特征值中减去一个量;对于min-max缩放平移量是最小值,对于标准化,这个量是均值;
    如果平移量不是0,那么这两种变换会将一个多数元素为0的稀疏特征向量变成密集特征向量;
    词袋就是一种稀疏的表示方式,大多数分类算法的实现都针对稀疏输入进行了优化;

    归一化

    • 将初始特征值除以一个被称为l2范数的量,l2范数又称为欧几里得范数
    • 定义:x_n = x / (||x||2)

    l2范数是坐标空间中向量长度的一种测量;

    • l2 = ||x||2 = (x1^2 + x2^2 +... + xm^2)^(1/2)
    • 先对所有数据点中该特征的值进行平方求和,再计算平方根;
    • 经过l2规范化后,特征列的范数就是1,有时候这种处理也称为l2缩放;

    注意:除了可以对特征进行l2归一化,也可以对数据点进行l2归一化,最终会得到带有单位范数(1)的数据向量;

    特征缩放总是将特征除以一个常数(即归一化常数);因而他不会改变单特征分布的形状

    as_matrix方法的作用:
    很多时候在提取完数据后其自身就是数组形式(<class ‘numpy.ndarray’>),这只是习惯性的谨慎。很多时候取得的数据是DataFrame的形式,这个时候要记得转换成数组;
    x = datas.iloc[:,:].as_matrix() not predict
    x = datas.iloc[:,:].values predict

    特征缩放的示例代码:

    import pandas as pd
    import sklearn.preprocessing as preproc
    
    df = pd.read_csv("OnlineNewsPopularity.csv", delimiter=", ")
    
    print(df["n_token_content"].as_matrix())
    
    
    # min-max缩放
    df['minmax'] = preproc.minmax_scale(df[['n_token_content']])
    df['minmax'].as_matrix() # 或 .values
    
    # 标准化
    df['standardized'] = preproc.StandardScaler().fit_transform(df[['n_token_content']])
    df['standardized'].as_matrix() 
    
    # L2归一化
    df['l2_normalized'] = preproc.normalize(df[['n_token_content']], axis=0)
    df['standardized'].as_matrix() 
    
    

    当一组输入特征的尺度相差很大,就会对模型的训练算法带来数值稳定性方面的问题,因此需要对特征进行缩放;

    交互特征

    • 两个特征的乘积可以组成一对简单的交互特征,这种关系类比与逻辑运算AND;

    • 表示由一对条件行程的结果:地区A and 年龄B

    • 这种特征在决策树中极其常见,广义线性模型中也常用;

    • y = w1x1 + ... + wnxn

    • y = w1x1 + ... + wnxn + w11x1x1 + w12x1x2 + ...

    • 这样可以捕获特征间交互作用,这些特征对 就称为交互特征

    如果x1和x2是二值特征,那么他们的积就是逻辑与;

    如果我们的问题是基于客户档案信息来预测客户偏好,那么除了更具用户年龄或地点这些单独的特征来进行预测,还可以使用交互特征来根据用户位于某个年龄段并位于某个特征的地点来进行预测;

    # 交互特征示例
    
    from sklearn import linear_model
    from sklearn.model_selection import train_test_split
    import sklearn.preprocessing as preproc
    
    # 假设一个pandas数据框
    df.columns # 包含许多特征列
    
    # 选择与内容有关的特征作为模型的单一特征,忽略那些衍生特征
    features = ['n_token_title','n_token_content','num_videos']
    
    X = df[features]
    y = df[['shares']]
    
    # 创建交互特征 (跳过固定偏移项)
    X2 = preproc.PolynomialFeatures(include_bias=False).fit_transform(X)
    
    # 为两个特征集 创建 训练集和测试集
    X1_train, X1_test, X2_train, X2_test, y_train_, y_test = train_test_split(X,X2,y, test_size=0.3, random_state=123)
    
    def evaluate_feature(X_train, X_test, y_train, y_test):
      model = linear_model.LinearRegression().fit(X_train, y_train)
      r_score = model.score(X_test, y_test)
      return (model, r_score)
    
    (m1, r1) =   evaluate_feature(X1_train, X1_test,y_train_, y_test)
    (m2, r2) =   evaluate_feature(X2_train, X2_test,y_train_, y_test)
    
    print("R-squared score with singleton features: %.5f"% r1)
    print("R-squared score with pairwise features: %.10f"% r2)
    
    
    

    交互特征虽然构造简单,但代价并不低,如果线性模型中包含有交互特征,那么它的训练时间复杂度会从O(N)增加到O(N^2),其中n是单一特征的数量;

    精心设计的复杂特征需要昂贵的成本,所以数量不能太多,它们可以减少模型的训练时间,但特征本身会消耗很多计算能力,这增加了模型评分阶段的计算成本;(第8章会介绍若干复杂特征的示例)

    特征选择

    特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度;

    为了得到这样的模型,有些特征选择技术需要训练不止一个优选模型,换言之,特征选择不是为了减少训练时间,而是为了减少模型评分时间;

    粗略的特征选择技术可分为三类:

    • 过滤:过滤技术对特征进行预处理,以除去那些不太可能对模型有用的特征;
      • 计算每个特征与响应变量之间的相互性,然后过滤掉某个阀值之下的特征;
      • 过滤技术的使用需要谨慎,以免有些有用的特征在进入到模型训练阶段之前被不经意地删除;
    • 打包方法:
      • 打包方法将模型视为一个能对推荐的特征子集给出合理评分的黑盒子;
      • 它们可以试验特征的各个子集,这意味着我们不会意外地删除那些本身不提供什么信息但和其他特征组合起来却非常有用的特征;
      • 它们使用另外一种方法迭代地对特征子集进行优化;
    • 嵌入式方法:
      • 这种方法将特征选择作为模型训练过程的一部分;
      • 例如,特征选择是决策树与生俱来的一种功能,因为它在每个训练阶段都要选择一个特征来对树进行分割;

    嵌入式方法不如打包方法强大,但成本远不如打包方法那么高;相比于过滤技术,嵌入式方法可以选择出特别适合某种模型的特征;从这个意义上说嵌入式方法在计算成本和结果质量之间实现了某种平衡

    小结

    几种常用的数值型特征工程技术:区间量化、缩放(即归一化)、对数变换(指数变换的一种)和交互特征;
    并简要介绍了特征选择技术,它对于处理大量交互特征是必需的;

    在统计机器学习中,所有数据最终都会转化为数值型特征;因此,所有特征工程最终都会归结为某种数值型特征工程技术。

    更多相关内容
  • 精通特征工程》 / 《Feature Engineering for Machine Learning》书中的示例代码
  • 特征缩放归一化5.交互特征6.特征选择 1.二值化 # Echo Nest 品味画像数据集的统计 # 使 Million Song 数据集中听歌计数二进制化 import pandas as pd f = open(r'data/train_triplets.txt') listen_count = pd.read_...

    精通特征工程pdf:链接:https://pan.baidu.com/s/11AFe7LgjYnf56XcpI_wNKw 提取码:fvzo
    参考黄博代码实现:https://github.com/fengdu78/Data-Science-Notes/tree/master/9.feature-engineering

    1.二值化

    # Echo Nest 品味画像数据集的统计
    # 使 Million Song 数据集中听歌计数二进制化
    import pandas as pd
    f = open(r'data/train_triplets.txt')
    listen_count = pd.read_csv(f, header=None, delimiter='\t')
    listen_count[2] = 1
    
    listen_count.head()
    
    012
    0b80344d063b5ccb3212f76538f3d9e43d87dca9eSOAKIMP12A8C1309951
    1b80344d063b5ccb3212f76538f3d9e43d87dca9eSOAPDEY12A81C210A91
    2b80344d063b5ccb3212f76538f3d9e43d87dca9eSOBBMDR12A8C13253B1
    3b80344d063b5ccb3212f76538f3d9e43d87dca9eSOBFNSP12AF72A0E221
    4b80344d063b5ccb3212f76538f3d9e43d87dca9eSOBFOVM12A58A7D4941

    2.区间量化(分箱)

    # Yelp 数据集中的商家点评数量可视化 
    import pandas as pd
    import json
    # 加载商家数据
    biz_file = open('data/yelp_academic_dataset_business.json')
    biz_df = pd.DataFrame([json.loads(x) for x in biz_file.readlines()])
    biz_file.close()
    
    biz_df.head()
    
    business_idcategoriescityfull_addresslatitudelongitudenameneighborhoodsopenreview_countstarsstatetype
    0rncjoVoEFUJGCUoC1JgnUA[Accountants, Professional Services, Tax Servi...Peoria8466 W Peoria Ave\nSte 6\nPeoria, AZ 8534533.581867-112.241596Peoria Income Tax Service[]True35.0AZbusiness
    10FNFSzCFP_rGUoJx8W7tJg[Sporting Goods, Bikes, Shopping]Phoenix2149 W Wood Dr\nPhoenix, AZ 8502933.604054-112.105933Bike Doctor[]True55.0AZbusiness
    23f_lyB6vFK48ukH6ScvLHg[]Phoenix1134 N Central Ave\nPhoenix, AZ 8500433.460526-112.073933Valley Permaculture Alliance[]True45.0AZbusiness
    3usAsSV36QmUej8--yvN-dg[Food, Grocery]Phoenix845 W Southern Ave\nPhoenix, AZ 8504133.392210-112.085377Food City[]True53.5AZbusiness
    4PzOqRohWw7F7YEPBz6AubA[Food, Bagels, Delis, Restaurants]Glendale Az6520 W Happy Valley Rd\nSte 101\nGlendale Az, ...33.712797-112.200264Hot Bagels & Deli[]True143.5AZbusiness
    # 绘制点评数量直方图
    import matplotlib.pyplot as plt
    import seaborn as sns
    %matplotlib inline
    
    sns.set_style('whitegrid')
    fig, ax = plt.subplots()
    biz_df['review_count'].hist(ax=ax, bins=100)
    ax.set_yscale('log')
    ax.tick_params(labelsize=14)
    ax.set_xlabel('Review Count', fontsize=14)
    ax.set_ylabel('Occurrence', fontsize=14)
    
    Text(0, 0.5, 'Occurrence')
    

    在这里插入图片描述

    # 通过固定宽度分箱对计数值进行区间量化 
    import numpy as np
    
    # 生成20个随机整数,均匀分布在0-99之间
    small_counts = np.random.randint(0, 100, 20)
    small_counts
    
    array([13, 48, 98, 20, 58, 21, 92, 19, 48, 31, 46, 86, 23, 45, 65, 60, 66,
           42, 20,  9])
    
    # 通过出发映射到间隔均匀的分箱中,每个分箱取值范围0-9
    np.floor_divide(small_counts, 10)
    
    array([1, 4, 9, 2, 5, 2, 9, 1, 4, 3, 4, 8, 2, 4, 6, 6, 6, 4, 2, 0], dtype=int32)
    
    # 横跨若干数量级的计数值数组
    large_counts =  [296, 8286, 64011, 80, 3, 725, 867, 2215, 7689, 11495, 91897, 
                     44, 28, 7971, 926, 122, 22222] 
    # 通过对数函数映射到指数宽度分箱
    np.floor(np.log10(large_counts))
    
    array([ 2.,  3.,  4.,  1.,  0.,  2.,  2.,  3.,  3.,  4.,  4.,  1.,  1.,
            3.,  2.,  2.,  4.])
    
    # 计算 Yelp 商家点评数量的十分位数
    deciles = biz_df['review_count'].quantile([.1, .2, .3, .4, .5, .6, .7, .8, .9])
    deciles
    
    0.1     3.0
    0.2     3.0
    0.3     4.0
    0.4     5.0
    0.5     6.0
    0.6     8.0
    0.7    12.0
    0.8    23.0
    0.9    50.0
    Name: review_count, dtype: float64
    
    # 在直方图上画出十分位数
    sns.set_style('whitegrid')
    fig, ax = plt.subplots()
    biz_df['review_count'].hist(ax=ax, bins=100)
    for pos in deciles:
        handle = plt.axvline(pos, color='r')
    ax.legend([handle], ['deciles'], fontsize=14)
    ax.set_yscale('log')
    ax.set_xscale('log')
    ax.tick_params(labelsize=14)
    ax.set_xlabel('Review Count', fontsize=14)
    ax.set_ylabel('Occurence', fontsize=14)
    
    Text(0, 0.5, 'Occurence')
    

    [外链图片转存失败(img-Hh79VgPr-1565156805605)(output_11_1.png)]

    # 通过分位数对计数值进行分箱 
    # 使用large_counts
    pd.qcut(large_counts, 4, labels=False)  # 将数据映射为所需的分位数值
    
    array([1, 2, 3, 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 2, 1, 0, 3], dtype=int64)
    
    # 计算实际的分位数值
    large_counts_series = pd.Series(large_counts)
    large_counts_series.quantile([0.25, 0.5, 0.75])
    
    0.25     122.0
    0.50     926.0
    0.75    8286.0
    dtype: float64
    

    3.对数变换

    # 对数函数可以将大数值的范围压缩,对小数值的范围进行扩展
    # 对数变换前后的点评数量分布可视化
    fig, (ax1, ax2) = plt.subplots(2,1)
    fig.tight_layout(pad=0, w_pad=4.0, h_pad=4.0)
    biz_df['review_count'].hist(ax=ax1, bins=100)
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel('review_count', fontsize=14)
    ax1.set_ylabel('Occurrence', fontsize=14)
    biz_df['log_review_count'] = np.log(biz_df['review_count'] + 1)
    biz_df['log_review_count'].hist(ax=ax2, bins=100)
    ax2.tick_params(labelsize=14)
    ax2.set_xlabel('log10(review_count))', fontsize=14)
    ax2.set_ylabel('Occurrence', fontsize=14)
    
    Text(23.625, 0.5, 'Occurrence')
    

    [外链图片转存失败(img-5sR7eN4p-1565156805606)(output_15_1.png)]

    在线新闻流行度数据集的统计信息
    目标是使用这些特征来预测文章的流行度,流行度用社交媒体上的分享数表示,研究一个特征——文章中的单词个数

    df = pd.read_csv('data/OnlineNewsPopularity.csv', delimiter=', ')
    df.head()
    
    C:\Users\S2\Anaconda3\lib\site-packages\ipykernel\__main__.py:1: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.
      if __name__ == '__main__':
    
    urltimedeltan_tokens_titlen_tokens_contentn_unique_tokensn_non_stop_wordsn_non_stop_unique_tokensnum_hrefsnum_self_hrefsnum_imgs...min_positive_polaritymax_positive_polarityavg_negative_polaritymin_negative_polaritymax_negative_polaritytitle_subjectivitytitle_sentiment_polarityabs_title_subjectivityabs_title_sentiment_polarityshares
    0http://mashable.com/2013/01/07/amazon-instant-...731.012.0219.00.6635941.00.8153854.02.01.0...0.1000000.7-0.350000-0.600-0.2000000.500000-0.1875000.0000000.187500593
    1http://mashable.com/2013/01/07/ap-samsung-spon...731.09.0255.00.6047431.00.7919463.01.01.0...0.0333330.7-0.118750-0.125-0.1000000.0000000.0000000.5000000.000000711
    2http://mashable.com/2013/01/07/apple-40-billio...731.09.0211.00.5751301.00.6638663.01.01.0...0.1000001.0-0.466667-0.800-0.1333330.0000000.0000000.5000000.0000001500
    3http://mashable.com/2013/01/07/astronaut-notre...731.09.0531.00.5037881.00.6656359.00.01.0...0.1363640.8-0.369697-0.600-0.1666670.0000000.0000000.5000000.0000001200
    4http://mashable.com/2013/01/07/att-u-verse-apps/731.013.01072.00.4156461.00.54089019.019.020.0...0.0333331.0-0.220192-0.500-0.0500000.4545450.1363640.0454550.136364505

    5 rows × 61 columns

    df['log_n_tokens_content'] = np.log10(df['n_tokens_content'] + 1)
    
    # 新闻文章流行度分布的可视化,使用对数变换和不使用对数变换 
    fig, (ax1, ax2) = plt.subplots(2, 1)
    df['n_tokens_content'].hist(ax=ax1, bins=100)
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel('Number of Words in Article', fontsize=14)
    ax1.set_ylabel('Number of Article', fontsize=14)
    
    df['log_n_tokens_content'].hist(ax=ax2, bins=100)
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel('Number of Words in Article', fontsize=14)
    ax1.set_ylabel('Number of Article', fontsize=14)
    
    Text(0, 0.5, 'Number of Article')
    

    [外链图片转存失败(img-kWyGcolV-1565156805608)(output_19_1.png)]

    # 使用对数变换后的 Yelp 点评数量预测商家的平均评分 
    from sklearn import linear_model
    from sklearn.model_selection import cross_val_score
    
    # 使用前面加载的yelp点评数据,计算yelp点评数量的对数变换值
    # 注意:为原始点评数量加1,以免当点评数量为0时,对数运算结果得到负无穷大
    biz_df['log_review_count'] = np.log10(biz_df['review_count'] + 1)
    
    # 使用经过对数变换和未经过对数变换的review_count特征,训练线性回归模型预测
    # 一个商家的平均星级评分,比较两种模型的10折交叉验证得分
    m_orig = linear_model.LinearRegression()
    scores_orig = cross_val_score(m_orig, biz_df[['review_count']], 
                                 biz_df['stars'], cv=10)
    
    m_log = linear_model.LinearRegression()
    scores_log = cross_val_score(m_log, biz_df[['log_review_count']], 
                                 biz_df['stars'], cv=10)
    
    print("R-squared score without log transform: %0.5f (+/- %0.5f)" %
          (scores_orig.mean(), scores_orig.std() * 2))
    print("R-squared score with log transform: %0.5f (+/- %0.5f)" %
          (scores_log.mean(), scores_log.std() * 2))
    
    R-squared score without log transform: 0.00215 (+/- 0.00329)
    R-squared score with log transform: 0.00136 (+/- 0.00328)
    
    # 使用在线新闻流行度数据集中经对数变换后的单词个数预测文章流行度 
    # 加载数据集
    df = pd.read_csv('data/OnlineNewsPopularity.csv', delimiter=', ')
    # 对n_tokens_content特征进行对数变换,特征表示新闻文章中的单词
    df['log_n_tokens_content'] = np.log10(df['n_tokens_content'] + 1)
    
    # 训练两个线性回归模型来预测文章分享数(初始特征,对数变换后特征)
    m_orig = linear_model.LinearRegression()
    scores_orig = cross_val_score(
        m_orig, df[['n_tokens_content']], df['shares'], cv=10)
    m_log = linear_model.LinearRegression()
    scores_log = cross_val_score(
        m_log, df[['log_n_tokens_content']], df['shares'], cv=10)
    
    print("R-squared score without log transform: %0.5f (+/- %0.5f)" %
          (scores_orig.mean(), scores_orig.std() * 2))
    print("R-squared score with log transform: %0.5f (+/- %0.5f)" %
          (scores_log.mean(), scores_log.std() * 2))
    
    C:\Users\S2\Anaconda3\lib\site-packages\ipykernel\__main__.py:3: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.
      app.launch_new_instance()
    
    
    R-squared score without log transform: -0.00242 (+/- 0.00509)
    R-squared score with log transform: -0.00114 (+/- 0.00418)
    
    # 可视化新闻流程度预测问题中输入输出相关性
    fig2, (ax1, ax2) = plt.subplots(2, 1,figsize=(10, 4))
    fig.tight_layout(pad=0.4, w_pad=4.0, h_pad=6.0)
    ax1.scatter(df['n_tokens_content'], df['shares'])
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel('Number of Words in Article', fontsize=14)
    ax1.set_ylabel('Number of Shares', fontsize=14)
    
    ax2.scatter(df['log_n_tokens_content'], df['shares'])
    ax2.tick_params(labelsize=14)
    ax2.set_xlabel('Log of the Number of Words in Article', fontsize=14)
    ax2.set_ylabel('Number of Shares', fontsize=14)
    
    Text(0, 0.5, 'Number of Shares')
    

    [外链图片转存失败(img-dbWBfA56-1565156805609)(output_22_1.png)]

    # 对 Yelp 商家点评数量的 Box-Cox 变换 
    from scipy import stats
    # 假设bie_df包含yelp商家点评数据,Box_Cox变换假定输入数据都是正的,
    # 检查数据的最小值已确定满足假定
    biz_df['review_count'].min()
    
    3
    
    # 设置输入参数lmbda为0,使用对数变换
    rc_log = stats.boxcox(biz_df['review_count'], lmbda=0)
    # scipy在实现box-cox转换时,会找出使得输出最接近与正态分布的lmbda参数
    rc_bc, bc_params = stats.boxcox(biz_df['review_count'])
    bc_params
    
    -0.5631160899391674
    

    4.特征缩放归一化

    如果模型对输入特征的尺度很敏感,就需要进行特征缩放

    min-max缩放
    x ~ = x − min ⁡ ( x ) max ⁡ ( x ) − min ⁡ ( x ) \tilde{x}=\frac{x-\min (x)}{\max (x)-\min (x)} x~=max(x)min(x)xmin(x)

    标准化(方差缩放)
    x ~ = x − mean ⁡ ( x ) sqrt ⁡ ( var ⁡ ( x ) ) \tilde{x}=\frac{x-\operatorname{mean}(x)}{\operatorname{sqrt}(\operatorname{var}(x))} x~=sqrt(var(x))xmean(x)

    L2归一化
    x ~ = x ∥ x ∥ 2 \widetilde{x}=\frac{x}{\|x\|_{2}} x =x2x

    # 特征缩放实例
    import sklearn.preprocessing as preproc
    
    # 加载在线新闻流行度数据
    df['n_tokens_content'].as_matrix()
    
    array([ 219.,  255.,  211., ...,  442.,  682.,  157.])
    
    # min-max缩放
    df['minmax'] = preproc.minmax_scale(df[['n_tokens_content']])
    df['minmax'].as_matrix()
    
    array([ 0.02584376,  0.03009205,  0.02489969, ...,  0.05215955,
            0.08048147,  0.01852726])
    
    # 标准化
    df['standardized'] = preproc.StandardScaler().fit_transform(df[['n_tokens_content']])
    df['standardized'].as_matrix()
    
    array([-0.69521045, -0.61879381, -0.71219192, ..., -0.2218518 ,
            0.28759248, -0.82681689])
    
    # L2 归一化
    df['l2_normalized'] = preproc.normalize(df[['n_tokens_content']], axis=0)
    df['l2_normalized'].as_matrix()
    
    array([ 0.00152439,  0.00177498,  0.00146871, ...,  0.00307663,
            0.0047472 ,  0.00109283])
    
    # 绘制原始数据和缩放后数据的直方图
    fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1)
    fig.tight_layout()
    
    df['n_tokens_content'].hist(ax=ax1, bins=100)
    ax1.tick_params(labelsize=14)
    ax1.set_xlabel('Article word count', fontsize=14)
    ax1.set_ylabel('Number of articles', fontsize=14)
    
    df['minmax'].hist(ax=ax2, bins=100)
    ax2.tick_params(labelsize=14)
    ax2.set_xlabel('min-max word count', fontsize=14)
    ax2.set_ylabel('Number of articles', fontsize=14)
    
    df['standardized'].hist(ax=ax3, bins=100)
    ax3.tick_params(labelsize=14)
    ax3.set_xlabel('standardized word count', fontsize=14)
    ax3.set_ylabel('Number of articles', fontsize=14)
    
    df['l2_normalized'].hist(ax=ax4, bins=100)
    ax4.tick_params(labelsize=14)
    ax4.set_xlabel('l2_normalized word count', fontsize=14)
    ax4.set_ylabel('Number of articles', fontsize=14)
    
    Text(29.125, 0.5, 'Number of articles')
    

    [外链图片转存失败(img-C6owO3Nv-1565156805610)(output_33_1.png)]

    只有x轴尺度发生了变化,特征缩放后的分布形状不变

    5.交互特征

    # 预测中的交互特征实例
    from sklearn import linear_model
    from sklearn.model_selection import train_test_split
    import sklearn.preprocessing as preproc
    
    # df 包含UCI在线新闻流行度数据集
    df.columns
    
    Index(['url', 'timedelta', 'n_tokens_title', 'n_tokens_content',
           'n_unique_tokens', 'n_non_stop_words', 'n_non_stop_unique_tokens',
           'num_hrefs', 'num_self_hrefs', 'num_imgs', 'num_videos',
           'average_token_length', 'num_keywords', 'data_channel_is_lifestyle',
           'data_channel_is_entertainment', 'data_channel_is_bus',
           'data_channel_is_socmed', 'data_channel_is_tech',
           'data_channel_is_world', 'kw_min_min', 'kw_max_min', 'kw_avg_min',
           'kw_min_max', 'kw_max_max', 'kw_avg_max', 'kw_min_avg', 'kw_max_avg',
           'kw_avg_avg', 'self_reference_min_shares', 'self_reference_max_shares',
           'self_reference_avg_sharess', 'weekday_is_monday', 'weekday_is_tuesday',
           'weekday_is_wednesday', 'weekday_is_thursday', 'weekday_is_friday',
           'weekday_is_saturday', 'weekday_is_sunday', 'is_weekend', 'LDA_00',
           'LDA_01', 'LDA_02', 'LDA_03', 'LDA_04', 'global_subjectivity',
           'global_sentiment_polarity', 'global_rate_positive_words',
           'global_rate_negative_words', 'rate_positive_words',
           'rate_negative_words', 'avg_positive_polarity', 'min_positive_polarity',
           'max_positive_polarity', 'avg_negative_polarity',
           'min_negative_polarity', 'max_negative_polarity', 'title_subjectivity',
           'title_sentiment_polarity', 'abs_title_subjectivity',
           'abs_title_sentiment_polarity', 'shares', 'log_n_tokens_content',
           'minmax', 'standardized', 'l2_normalized'],
          dtype='object')
    
    # 选择与内容有关的特征作为模型单一特征,忽略衍生特征
    features = ['n_tokens_title', 'n_tokens_content',
                'n_unique_tokens', 'n_non_stop_words', 'n_non_stop_unique_tokens',
                'num_hrefs', 'num_self_hrefs', 'num_imgs', 'num_videos',
                'average_token_length', 'num_keywords', 'data_channel_is_lifestyle',
                'data_channel_is_entertainment', 'data_channel_is_bus',
                'data_channel_is_socmed', 'data_channel_is_tech',
                'data_channel_is_world']
    X = df[features]
    y = df['shares']
    
    # 创建交互特征对,跳过固定偏移项
    X2 = preproc.PolynomialFeatures(include_bias=False).fit_transform(X)
    X2.shape
    
    (39644, 170)
    
    # 为两个特征集创建训练集和测试集
    X1_train, X1_test, X2_train, X2_test, y_train, y_test = train_test_split(X, X2, y, test_size=0.3, random_state=123)
    
    def evaluate_feature(X_train, X_test, y_train, y_test):
        model = linear_model.LinearRegression().fit(X_train, y_train)
        r_score = model.score(X_test, y_test)
        return (model, r_score)
    
    # 在两个特征集上训练模型并比较R方分数
    (m1, r1) = evaluate_feature(X1_train, X1_test, y_train, y_test)
    (m2, r2) = evaluate_feature(X2_train, X2_test, y_train, y_test)
    print("R-squared score with singleton features: %0.5f" % r1)
    print("R-squared score with pairwise features: %0.10f" % r2)
    
    R-squared score with singleton features: 0.00924
    R-squared score with pairwise features: 0.0113280904
    

    6.特征选择

    • 过滤:对特征进行预处理
    • 打包方法:对某特征提供的方法
    • 嵌入式方法:模型训练
    
    
    展开全文
  • 精通特征工程】学习笔记Day1&1.26&D1-2章&P1-32页 1、机器学习流程&基本概念 数据–任务–模型–特征–模型评价 **数据:**是对现实世界的现象的观测 **特征:**就是原始数据某个方面的数值表示 ...

    【精通特征工程】学习笔记Day1&1.26&D1-2章&P1-32页

    1、机器学习流程&基本概念

    • 数据–任务–模型–特征–模型评价
    • **数据:**是对现实世界的现象的观测
    • **特征:**就是原始数据某个方面的数值表示
    • **特征工程:**是指从原始数据中提取特征并将其转换为适合机器学习模型的格式
    • 在统计机器学习中,所有数据最终都会转化为数值型特征。因此,所有特征工程最终都会归结为某种数值型特征工程技术

    2、简单而又奇妙的数值

    2.1 标量、向量、空间

    • 单独的数值型特征称为标量,标量的有序列表称为向量,向量位于向量空间中。

    2.2 处理计数

    2.2.1 二值化
    • 将大于等于某个次数的设为1,小于这个次数的设为0

    • 二值目标变量是一个既简单又强壮的用户偏好衡量指标

    • eg1:歌曲收听次数及人数的原始分布如下,会发现原始的收听次数并不是衡量用户喜好的强壮指标,因此可以进行二值化,如果用户收听了某首歌曲至少一次,那么就认为该用户喜欢该歌曲
      listen_count.png

    import pandas as pd
    listen_count = pd.read_csv('millionsong/train_triplets.txt.zip',header=None, delimiter='\t')# 表中包含有形式为“用户-歌曲-收听次数”的三元组。只包含非零收听次数。 # 因此,要二值化收听次数,只需将整个收听次数列设为1。
    listen_count[2] = 1
    
    2.2.2 区间量化(分箱)
    • 区间量化可以将连续型数值映射为离散型数值,可以将这种离散型数值看作一种有序的分箱序列,它表示的是对密度的测量

    • 为了对数据进行区间量化,必须确定每个分箱的宽度。有两种确定分箱宽度的方法:固定宽度分箱和自适应分箱

    • eg2:商家点评数量数据,点评数量及用户量的分布如下,会发现大多数商家的点评数量很少,但有些商家具有几千条点评,原始的点评数量横跨了若干个数量,一种解决方法是对计数值进行区间量化,然后使用量化后的结果。换言之,将点评数量分到多个箱子里面,去掉实际的计数值
      review_count.png

    (1)固定宽度分箱

    • 通过固定宽度分箱,每个分箱中会包含一个具体范围内的数值。这些范围可以人工定制, 也可以通过自动分段来生成,它们可以是线性的,也可以是指数性的

    • 例如,可以按 10 年为一段来将人员划分到多个年龄范围中:0~9 岁的在分箱 1 中、10~19 岁的在分箱 2 中

    • 要将计数值映射到分箱,只需用计数值除以分箱的宽度,然后取整数部分

    • eg3:通过固定宽度分箱对计数值进行区间量化

    import numpy as np
    small_counts = np.random.randint(0, 100, 20)# 生成20个随机整数,均匀分布在0~99之间
    small_counts# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是0~9
    

    1

    np.floor_divide(small_counts, 10)#通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是0~9
    

    2

    • 当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组,如:0-9、 10-99、100-999、1000-9999

    • 这时分箱宽度是呈指数增长的,要将计数值映射到分箱,需要取计数值的对数。指数宽度分箱与对数变换的关系非常紧密

    • eg4:通过固定宽度分箱对计数值进行区间量化

    large_counts = [296, 8286, 64011, 80, 3, 725, 867, 2215, 7689, 11495, 91897,44, 28, 7971, 926, 122, 22222]# 横跨若干数量级的计数值数组
    
    np.floor(np.log10(large_counts))# 通过对数函数映射到指数宽度分箱
    

    3

    (2)分位数分箱

    • 如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。根据数据的分布特点,进行自适应的箱体定位,可以使用数据分布的分位数来实现,分位数是可以将数据划分为相等的若干份数的值,如:中位数(即二分位数)、四分位数、十分位数等

    eg5:计算 Yelp 商家点评数量的十分位数

    >>> deciles = biz_df['review_count'].quantile([.1, .2, .3, .4, .5, .6, .7, .8, .9])
    >>> deciles
    0.1 3.0
    0.2 4.0
    0.3 5.0
    0.4 6.0
    0.5 8.0
    0.6 12.0
    0.7 17.0
    0.8 28.0
    0.9 58.0
    

    在直方图上画出十分位数
    3.png

    • 要计算分位数并将数据映射到分位数分箱中,可以使用 Pandas 库,如例 6所示。

      • pandas.DataFrame.quantile 可以计算分位数
      • pandas.Series.quantile 可以计算分位数
      • pandas.qcut 可以将数据映射为所需的分位数值
    • eg6:通过分位数对计数值进行分箱

    import pandas as pd # 继续使用例4中的large_couts
    pd.qcut(large_counts, 4, labels=False)# 将计数值映射为分位数
    

    0

    # 计算实际的分位数值
    large_counts_series = pd.Series(large_counts) 
    large_counts_series.quantile([0.25, 0.5, 0.75])
    

    1

    2.3 对数变换

    • 对数函数压缩高值区间并扩展低值区间
      2-6.png

    • eg7:对数变换前后的点评数量分布可视化。

      • 分析:比较对数变换前后的 Yelp 商家点评数量的直方图,如下所示,两幅图中的 y 轴都是正常(线性)标度。在下方的图形中,区间 (0.5, 1] 中的箱体间隔很大,是因为在 1 和 10 之间只有 10 个可能的整数计数值。请注意,初始的点评数量严重集中在低计数值区域,但有些异常值跑到了 4000 之外。经过对数变换之后,直方图在低计数值的集中趋势被减弱了,在 x 轴上的分布更均匀了一些

    2-7.png

    2.3.1 对数变换实战
    • 代码demo:使用对数变换后的 Yelp 点评数量预测商家的平均评分
    # 使用Yelp点评数据框,计算Yelp点评数量的对数变换值
    # 注意,我们为原始点评数量加1,以免当点评数量为0时,对数运算结果得到负无穷大。 
    >>> biz_df['log_review_count'] = np.log10(biz_df['review_count'] + 1)
    
    • 代码demo:使用在线新闻流行度数据集中经对数变换后的单词个数预测文章流行度
    # 从UCI下载在线新闻流行度数据集,对n_tokens_content特征进行对数变换,这个特征表示的是一篇新闻文章中的单词 # (token)数量。
    >>> df['log_n_tokens_content'] = np.log10(df['n_tokens_content'] + 1)
    

    从下图可以看出,对数变换重组了 x 轴,对于那些目标变量值异常巨大(>200000 个分享)的文章,对数变换将它们更多地拉向了 x 轴的右侧,这就为线性模型在输入特征空间的低值端争取了更多的“呼吸空间”。 如果不进行对数变换(下面上方的图),模型就会面临更大的压力,要在输入变化很小的情况下去拟合变化非常大的目标值。
    2-8.png

    2.3.2 指数变换:对数变换的推广
    • 指数变换是个变换族,对数变换只是它的一个特例。用统计学术语来说,它们都是方差稳定化变换

    • 泊松分布是一种 重尾分布,它的方差等于它的均值。因此,它的质心越大,方差就越大,重尾程度也越 大。指数变换可以改变变量的分布,使得方差不再依赖于均值

    • 例如,假设一个随机变量X 具有泊松分布,如果通过取它的平方根对它进行变换,那么它的平方根的方差就近似是一 个常数,而不是与均值相等。

    • 下图为泊松分布的粗略表示,这是一个方差随均值变大的分布示例。它表示出了 λ 对泊松分布的影响,λ 表示泊松分布的均值。当 λ 变大时,不仅整个分 布模式向右移动,质量也更加分散,方差随之变大。

    2-11.png

    • 平方根变换和对数变换都可以简单推广为 Box-Cox 变换

    • 下图展示了 λ = 0(对数变换)、λ = 0.25、λ = 0.5(平方根变换的一种缩放和平移形式)、 λ = 0.75 和 λ = 1.5 时的 Box-Cox 变换。λ 小于 1 时,可以压缩高端值;λ 大于 1 时,起的作 用是相反的。

    • 只有当数据为正时,Box-Cox 公式才有效。对于非正数据,我们可以加上一个固定的常数, 对数据进行平移。
      2-12.png

    • 代码demo:对 Yelp 商家点评数量的 Box-Cox 变换

    >>> from scipy import stats
    # 接上一个例子,假设biz_df包含Yelp商家点评数据。
    # Box-Cox变换假定输入数据都是正的。
    # 检查数据的最小值以确定满足假定。
    >>> biz_df['review_count'].min()
    3
    # 设置输入参数λ为0,使用对数变换(没有固定长度的位移)。
    >>> rc_log = stats.boxcox(biz_df['review_count'], lmbda=0)
    # 默认情况下,SciPy在实现Box-Cox转换时,会找出使得输出最接近于正态分布的λ参数。 
    >>> rc_bc, bc_params = stats.boxcox(biz_df['review_count'])
    >>> bc_params
    -0.4106510862321085
    

    下面为Box-Cox 变换后的 Yelp 商家点评数量直方图(下),以及初始点评数量直方图(上)和对数变换后的点评数量直方图(中)
    2-13.png

    • 概率图是一种非常简单的可视化方法,用以比较数据的实际分布与理论分布, 它本质上是一种表示实测分位数和理论分位数的关系的散点图。
    • 下图展示了 Yelp 点评 数据的两种概率图,分别是初始点评数量、变换后点评数量的概率图,并和正态分布进行了对比
    • 因为观测数据肯定是正的,而高斯分布可以是负的,所以在负数端,实测分位数和理论分位数不可能匹配。因此,我们只关注正数部分。于是,我们可以看出与正态分布相比,初始的点评数量具有明显的重尾特征。(排序后的值可以达到 4000 以 上,而理论分位数只能到达 4 左右。)普通对数变换和最优 Box-Cox 变换都可以将正尾部 拉近正态分布。
    • 根据图形明显可以看出,相比对数变换,最优 Box-Cox 变换对尾部的压缩更强,它使得尾部变平,跑到了红色等值斜线下面。

    2-14.png

    2.4 特征缩放/归一化

    • 有些特征的值是有界限的,比如经度和纬度
    • 但有些数值型特征可以无限制地增加,比如计数值
    • 有些模型是输入的平滑函数,比如线性回归模型、逻辑回归模型或包含矩阵的模型,它们会受到输入尺度的影响。
    • 相反,那些基于树的模型则根本不在乎输入尺度有多 大。
    • 如果模型对输入特征的尺度很敏感,就需要进行特征缩放。
    • 特征缩放会改变特征的尺度,有些人将其称为特征归一化
    • 特征缩放通常对每个特征独立进行
    2.4.1 min-max缩放
    • 令 x 是一个独立的特征值(即某个数据点中的特征值),min(x) 和 max(x) 分别为这个特征 在整个数据集中的最小值和最大值。min-max 缩放可以将所有特征值压缩(或扩展)到 [0, 1] 区间中
    • min-max 缩放的公式如下:
      x − m i n ( x ) m a x ( x ) − m i n ( x ) \frac {x-min(x)}{max(x)-min(x)} max(x)min(x)xmin(x)
      2-15.png
    2.4.2 特征标准化/方差缩放
    • 特征标准化:它先减去特征的均值(对所有数据点),再除以方差,因此又称为方差缩放。缩放后的特征均值为 0,方差为 1。如果初始特征服从高斯分布,那么缩放后的特征也服从高斯分布。
    • 特征标准化的公式如下:
      x ^ = x − m e a n ( x ) s q r t ( v a r ( x ) ) \hat x= \frac {x-mean(x)}{sqrt(var(x))} x^=sqrt(var(x))xmean(x)

    2-16.png

    2.4.3 l 2 l^2 l2归一化
    • 这种归一化技术是将初始特征值除以一个称为 l 2 l^2 l2范数的量, l 2 l^2 l2范数又称为欧几里得范数,它的定义如下:
      x ^ = x ∣ ∣ x ∣ ∣ 2 \hat x= \frac {x}{||x||_2} x^=x2x

    • l 2 l^2 l2范数是坐标空间中向量长度的一种测量。它的定义可以根据著名的毕达哥拉斯定理(给 定一个直角三角形两条直角边的长度,可以求出斜边的长度)导出:
      ∣ ∣ x ∣ ∣ 2 = x 1 2 + x 2 2 + ⋅ ⋅ ⋅ x m 2 {||x||_2}= \sqrt{x_1^2+x_2^2+···x_m^2} x2=x12+x22+xm2

    • l 2 l^2 l2范数先对所有数据点中该特征的值的平方求和,然后算出平方根。经过 l 2 l^2 l2 归一化后,特征列的范数就是1

    2-17.png

    • 不论使用何种缩放方法,特征缩放总是将特征除以一个常数(称为归一化常数)。因此,它不会改变单特征分布的形状

    • 代码demo:特征缩放示例

    # 加载在线新闻流行度数据集
    >>> df = pd.read_csv('OnlineNewsPopularity.csv', delimiter=', ')
    # 查看原始数据——文章中的单词数量
    >>> df['n_tokens_content'].as_matrix()
    array([ 219., 255., 211., ..., 442., 682., 157.])
    # min-max缩放
    >>> df['minmax'] = preproc.minmax_scale(df[['n_tokens_content']]) >>> df['minmax'].as_matrix()
    array([ 0.02584376, 0.03009205, 0.02489969, ..., 0.05215955,0.08048147,  0.01852726])
    # 标准化——注意根据标准化的定义,有些结果会是负的
    >>> df['standardized'] = preproc.StandardScaler().fit_transform(df[['n_tokens_ content']])
    >>> df['standardized'].as_matrix()
    array([-0.69521045, -0.61879381, -0.71219192, ..., -0.2218518 ,0.28759248, -0.82681689])
    # L2-归一化
    >>> df['l2_normalized'] = preproc.normalize(df[['n_tokens_content']], axis=0) 
    >>> df['l2_normalized'].as_matrix()
    array([ 0.00152439, 0.00177498, 0.00146871, ..., 0.00307663, 0.0047472 ,  0.00109283])
    

    下图为原始及缩放后的新闻文章单词数量。注意只有 x 轴的尺度发生了变化,特征缩放后的分布 形状保持不变

    2-18.png

    当一组输入特征的尺度相差很大时,就需要进行特征缩放。例如,一个人气很高的商业网 站的日访问量可能是几十万次,而实际购买行为可能只有几千次。如果这两个特征都被模 型所使用,那么模型就需要在确定如何使用它们时先平衡一下尺度。如果输入特征的尺度 差别非常大,就会对模型训练算法带来数值稳定性方面的问题。在这种情况下,就应该对 特征进行标准化。

    2.5 交互特征

    • 两个特征的乘积可以组成一对简单的交互特征

    • 这种相乘关系可以用逻辑操作符 AND 来 类比,它可以表示出由一对条件形成的结果:“该购买行为来自于邮政编码为 98121 的地区”AND“用户年龄在 18 和 35 岁之间”。

    • eg: UCI 在线新闻流行度数据集中的交互特征对来预测每篇新闻文章的分享数量

    >>> from sklearn import linear_model
    >>> from sklearn.model_selection import train_test_split
    >>> import sklearn.preprocessing as preproc
    # 假设df是一个Pandas数据框,其中包含了UCI在线新闻流行度数据集 >>> df.columns
    Index(['url', 'timedelta', 'n_tokens_title', 'n_tokens_content',
                'n_unique_tokens', 'n_non_stop_words', 'n_non_stop_unique_tokens','num_hrefs', 'num_self_hrefs', 'num_imgs', 'num_videos',
                'average_token_length', 'num_keywords', 'data_channel_is_lifestyle',
                'data_channel_is_entertainment', 'data_channel_is_bus',
                'data_channel_is_socmed', 'data_channel_is_tech',
                'data_channel_is_world', 'kw_min_min', 'kw_max_min', 'kw_avg_min',
                'kw_min_max', 'kw_max_max', 'kw_avg_max', 'kw_min_avg', 'kw_max_avg',
                'kw_avg_avg', 'self_reference_min_shares', 'self_reference_max_shares',
                'self_reference_avg_sharess', 'weekday_is_monday', 'weekday_is_tuesday',
                'weekday_is_wednesday', 'weekday_is_thursday', 'weekday_is_friday',
                'weekday_is_saturday', 'weekday_is_sunday', 'is_weekend', 'LDA_00',
                'LDA_01', 'LDA_02', 'LDA_03', 'LDA_04', 'global_subjectivity',
                'global_sentiment_polarity', 'global_rate_positive_words',
                'global_rate_negative_words', 'rate_positive_words',
                'rate_negative_words', 'avg_positive_polarity', 'min_positive_polarity',
                'max_positive_polarity', 'avg_negative_polarity',
                'min_negative_polarity', 'max_negative_polarity', 'title_subjectivity',
                'title_sentiment_polarity', 'abs_title_subjectivity',
                'abs_title_sentiment_polarity', 'shares'],
               dtype='object')
    # 选择与内容有关的特征作为模型的单一特征,忽略那些衍生特征 
    >>> features = ['n_tokens_title','n_tokens_content','n_unique_tokens', 'n_non_stop_words', 'n_non_stop_unique_tokens','num_hrefs', 'num_self_hrefs', 'num_imgs', 'num_videos', 'average_token_length','num_keywords','data_channel_is_lifestyle','data_channel_is_entertainment', 'data_channel_is_bus', 'data_channel_is_socmed', 'data_channel_is_tech', 'data_channel_is_world']
    >>> X = df[features]
    >>> y = df[['shares']]
    # 创建交互特征对,跳过固定偏移项
    >>> X2 = preproc.PolynomialFeatures(include_bias=False).fit_transform(X) >>> X2.shape
    (39644, 170)
    # 为两个特征集创建训练集和测试集
    >>> X1_train, X1_test, X2_train, X2_test, y_train, y_test = train_test_split(X, X2, y, test_size=0.3, random_state=123)
    >>> def evaluate_feature(X_train, X_test, y_train, y_test):
         ...     """Fit a linear regression model on the training set and
         ...     score on the test set"""
         ...     model = linear_model.LinearRegression().fit(X_train, y_train)
         ...     r_score = model.score(X_test, y_test)
         ...     return (model, r_score)
    # 在两个特征集上训练模型并比较R方分数
    >>> (m1, r1) = evaluate_feature(X1_train, X1_test, y_train, y_test)
    >>> (m2, r2) = evaluate_feature(X2_train, X2_test, y_train, y_test) 
    >>> print("R-squared score with singleton features: %0.5f" % r1) 
    >>> print("R-squared score with pairwise features: %0.10f" % r2) 
    R-squared score with singleton features: 0.00924
    R-squared score with pairwise features: 0.0113276523
    

    注:

    • 交互特征的构造非常简单,使用起来却代价不菲。如果线性模型中包含有交互特征对,那它的训练时间和评分时间就会从 O(n) 增加到 O(n2),其中 n 是单一特征的数量
    • 有若干种方法可以绕过高阶交互特征所带来的计算成本。我们可以在构造出所有交互特征之后再执行特征选择,或者,也可以更加精心地设计出少量复杂特征。

    2.6 特征选择

    • 特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度

    • 特征选择不是为了减 少训练时间,而是为了减少模型评分时间

    • 特征选择技术可以分为以下三类:
      (1) 过滤
      过滤技术对特征进行预处理,以除去那些不太可能对模型有用处的特征。例如,我们可以计算出每个特征与响应变量之间的相关性或互信息,然后过滤掉那些在某个阈值之下 的特征。过滤技术的成本比下面描述的打包技 术低廉得多,但它们没有考虑我们要使用的模型,因此,它们有可能无法为模型选择出正确的特征。我们最好谨慎地使用预过滤技术,以免在有用特征进入到模型训练阶段之 前不经意地将其删除。

      (2)打包方法
      这些技术的成本非常高昂,但它们可以试验特征的各个子集,这意味着我们不会意外地删除那些本身不提供什么信息但和其他特征组合起来却非常有用的特征。打包方法将模型视为一个能对推荐的特征子集给出合理评分的黑盒子。它们使用另外一种方法迭代地对特征子集进行优化。

      (3)嵌入式方法
      这种方法将特征选择作为模型训练过程的一部分。例如,特征选择是决策树与生俱来的 一种功能,因为它在每个训练阶段都要选择一个特征来对树进行分割。另一个例子是 l1 正则项,它可以添加到任意线性模型的训练目标中。l1 正则项鼓励模型使用更少的特 征,而不是更多的特征,所以又称为模型的稀疏性约束。嵌入式方法将特征选择整合为 模型训练过程的一部分。它们不如打包方法强大,但成本也远不如打包方法那么高。与过滤技术相比,嵌入式方法可以选择出特别适合某种模型的特征。从这个意义上说,嵌 入式方法在计算成本和结果质量之间实现了某种平衡。

    参考:《精通特征工程》爱丽丝·郑·阿曼达·卡萨丽

    展开全文
  • 只能说自己的英文太差,看翻译的作品,感觉还是会理解存在一定的...《精通特征工程》作者:[美]爱丽丝·郑,[美]阿曼达·卡萨丽,译者:陈光欣,出版社:人民邮电出版社,出版时间:2019-04,ISBN:9787115509680 ...

    只能说自己的英文太差,看翻译的作品,感觉还是会理解存在一定的偏差。
    或许是看的太粗略了,觉得自己有点似是而非,好似知晓了一些特征工程方面的东西,但又不甚了解。
    还是找书多看看,多动手实践吧,纸上觉来终觉浅,大概就是这个意思吧。


    《精通特征工程》作者:[美]爱丽丝·郑,[美]阿曼达·卡萨丽,译者:陈光欣,出版社:人民邮电出版社,出版时间:2019-04,ISBN:9787115509680
    在这里插入图片描述

    展开全文
  • 目标:通过思维导图快速建构特征工程思维体系 内容介绍:只有知识框架,在PCA部分不够详细,建议阅读原书。 机器学习流程 两句话理解特征工程特征工程其实就是用来探索对解决问题有意义的特征的步骤。这些特征...
  • 精通特征工程》读书笔记

    千次阅读 2019-10-05 19:59:48
    特征工程: 在给定数据、模型和任务的情况下设计出最合适的特征 二、数值型数据 合理性检查:正负等 考虑特征的尺度:kmeans、knn、径向基核函数、所有使用欧氏距离的方法均需要,对特征进行标准化、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,801
精华内容 5,120
关键字:

精通特征工程