手把手教你入门和实践特征工程 的全方位万字笔记,附代码下载

🙊 提及特征工程,都说是机器学习建模中最为重要并且费时的一项工做,并且它涉及的知识点会很是地多,经验老道的老司机天然是轻车熟路了,但对于刚刚入门的新手司机,学习到的知识点都是东一点西一点的,不够系统化,本篇文章是在阅读了一本评分极高的特征工程书籍 📚 《特征工程入门与实践》后的一篇笔记文,记录下相对比较系统的知识点以及可运行复现的代码,但愿对各位同行有所帮助哈。python

file

图:强力推荐这本书

🚗 目录

  • 🔍 特征理解
  • 🔋 特征加强
  • 🔨 特征构建
  • ✅ 特征选择
  • 💫 特征转换
  • 📖 特征学习

你们能够先看下思惟导图:算法

file

🔍 01 特征理解

在拿到数据的时候,咱们第一步须要作的是理解它,通常咱们能够从下面几个角度入手:网络

(注:本节用到了两个数据集,分别是Salary_Ranges_by_Job_Classification 和 GlobalLandTemperaturesByCity)数据结构

1. 区分结构化数据与非结构化数据

如一些以表格形式进行存储的数据,都是结构化数据;而非结构化数据就是一堆数据,相似于文本、报文、日志之类的。架构

2. 区分定量和定性数据

定量数据:指的是一些数值,用于衡量某件东西的数量;dom

定性数据:指的是一些类别,用于描述某件东西的性质。机器学习

其实区分了定量和定性数据,还能够继续细分下去,分为定类(nominal)、定序(ordinal)、定距(interval)、定比数据(ratio),下面咱们分别对这4类数据进行举例说明,加深你们对它们的印象。函数

1)定类(nominal)性能

也就是分类,好比:血型(A/B/O/AB型)、性别(男/女)、货币(人民币/美圆/日元),并且值得注意的是这些分类之间没有大小可比性。通常画图的话就只能看下分布占比,能够用条形图、饼图来表示。学习

file

2)定序(ordinal)

定序相比于定类,也就是多了一个“可排序”的属性,也就是说虽然它们是类别变量,可是它们的变量值之间是存在“大小”之分的。好比:期末绩点(A、B、C、D、E、F)、问卷答案(很是满意、满意、通常、不满意)。可视化方面,和定类同样,不过就是多了一个 箱体图 能够用(由于定序变量能够有中位数)。

file

3)定距(interval)

定距的话,就是变量值之间能够作加减法计算,也就是能够引入均值、方差之类的名词了,并且可以画的图也多了,包括先前的那些,还包括了直方图。

file

4)定比(ratio)

定比相比于定距更加严格,不只仅有定距的全部属性,同时,有一个 绝对零点 的概念,能够作加减乘除运算,好比说某个商品的价格是另外一个的2倍。值得注意的是,温度通常不纳入定比,而是定距,没有说20度是10度的两倍这种说法。

file

最后把上面的内容总结一下:

file

3. 关键代码聚集

如下的代码只是核心片断,完整代码可在公众号(SAMshare)后台输入关键字 特征工程 获取。

1)常见简易画图

# 绘制条形图
salary_ranges['Grade'].value_counts().sort_values(ascending=False).head(10).plot(kind='bar')

# 绘制饼图
salary_ranges['Grade'].value_counts().sort_values(ascending=False).head(5).plot(kind='pie')

# 绘制箱体图
salary_ranges['Union Code'].value_counts().sort_values(ascending=False).head(5).plot(kind='box')

# 绘制直方图
climate['AverageTemperature'].hist()

# 为每一个世纪(Century)绘制平均温度的直方图
climate_sub_china['AverageTemperature'].hist(by=climate_sub_china['Century'],
                                            sharex=True,
                                            sharey=True,
                                            figsize=(10, 10),
                                            bins=20)

# 绘制散点图
x = climate_sub_china['year']
y = climate_sub_china['AverageTemperature']

fig, ax = plt.subplots(figsize=(10,5))
ax.scatter(x, y)
plt.show()

2)检查缺失状况

# 移除缺失值
climate.dropna(axis=0, inplace=True)

# 检查缺失个数
climate.isnull().sum()

3)变量类别转换

# 日期转换, 将dt 转换为日期,取年份, 注意map的用法
climate['dt'] = pd.to_datetime(climate['dt'])
climate['year'] = climate['dt'].map(lambda value: value.year)

# 只看中国
climate_sub_china = climate.loc[climate['Country'] == 'China']
climate_sub_china['Century'] = climate_sub_china['year'].map(lambda x:int(x/100 +1))
climate_sub_china.head()

🔋 02 特征加强

这一步其实就是数据清洗了,虽然上一步中也有涉及到部分清洗工做(好比清除空值、日期转换之类的),但倒是分散的,这节重点讲讲数据清洗的一些技巧和实践代码,供你们在实际项目中去使用。

Step1: 进行EDA(Exploratory Data Analysis),思路以下:

(1)首先看看目标占比状况(针对二分类问题,也就是0和1的占比状况),直接 value_counts()就能够解决,看看样本是否失衡。

(2)接着看看有没有空值,直接统计 isnull().sum() 的个数,不过须要注意的是,可能统计出来没有缺失,并非由于真的没有缺失,并且缺失被人用某个特殊值填充了,通常会用 -九、blank、unknown、0之类的,须要注意⚠️识别,后面须要对缺失进行合理填充

(2.1)怎么识别缺失值呢?通常能够经过 data.describe() 获取基本的描述性统计,根据均值、标准差、极大极小值等指标,结合变量含义来判断。

file

(3)再接着看不一样类别之间的特征值分布状况,可经过画直方图(数值型变量)和计算变量值占比分布(类别变量)来观察。

(4)观察不一样变量之间的相关性状况,能够经过绘制 相关矩阵的热力图 来观察大致状况。

Step2: 处理数据缺失问题

缺失处理的办法有好多种,但最为经常使用的做者讲到有两种:填充和删除。

而在处理缺失前,咱们在上面的小节中识别出来了部分被人工填充的缺失,须要还原一下:

# 处理被错误填充的缺失值0,还原为 空(单独处理)
pima['serum_insulin'] = pima['serum_insulin'].map(lambda x:x if x !=0 else None)
# 检查变量缺失状况
pima['serum_insulin'].isnull().sum()


# 批量操做 还原缺失值
columns = ['serum_insulin','bmi','plasma_glucose_concentration','diastolic_blood_pressure','triceps_thickness']

for col in columns:
    pima[col].replace([0], [None], inplace=True)

# 检查变量缺失状况
pima.isnull().sum()

1) 删除含有缺失值的行

这里的话比较简单,就是使用 dropna() 来处理便可,同时咱们还能够检查下咱们到底删除了多少数据量:round(data.shape[0]-data_dropped.shape[0])/float(data.shape[0]) 就能够统计出来了。固然,删除以后,咱们还须要看看数据的分布,对比目标占比、特征分布与先前的是否存在明显差别,若是是的话,建议不要使用这种办法。

file

2) 缺失值合理填充

缺失填充,这里介绍的有均值填充、-9填充、中位数填充。这里会比较简单,咱们能够一般都是经过 sklearn的 Pipeline以及 Imputer来实现,下面是一个简单的完整Demo:

# 使用sklearn的 Pipeline以及 Imputer来实现缺失值填充
from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import Imputer

# 调参候选
knn_params = {'classify__n_neighbors':[1,2,3,4,5,6]}

# 实例化KNN模型
knn = KNeighborsClassifier()

# 管道设计
mean_impute = Pipeline([('imputer', Imputer(strategy='mean')),
                        ('classify',knn)
                       ])

x = pima.drop('onset_disbetes', axis=1) # 丢弃y
y = pima['onset_disbetes']

# 网格搜索
grid = GridSearchCV(mean_impute, knn_params)
grid.fit(x, y)

# 打印模型效果
print(grid.best_score_, grid.best_params_)
# 0.73177

Step3: 标准化和归一化

通过上面的处理,模型的精度能够达到0.73177,但咱们能够继续优化吗?那是确定的。

咱们能够先看看全部特征的分布(特征少的时候能够这么看):

pima_imputed_mean.hist(figsize=(15,15))

file

从上图中咱们能够看出一个问题,那就是每一个特征之间的量纲都是不同的,这对于knn这种基于距离的模型来讲是“致命”的bug,所以咱们须要进行标准化和归一化处理。

咱们重点关注3种方法:

1)Z分数标准化

最为经常使用的标准化技术,利用了统计学中的z分数思想,也就是将数据转换为均值为0,标准差为1的分布,其在python中的调用方法:

# z分数标准化(单一特征)
from sklearn.preprocessing import StandardScaler
# 实例化方法
scaler = StandardScaler()
glucose_z_score_standarScaler = scaler.fit_transform(pima[['plasma_glucose_concentration']].fillna(-9))
# 能够看看转换以后的均值和标准差是否为0和1
glucose_z_score_standarScaler.mean(), glucose_z_score_standarScaler.std()


# z分数标准化(所有特征)
from sklearn.preprocessing import StandardScaler
# 实例化方法
scaler = StandardScaler()
pima_imputed_mean_scaled = pd.DataFrame(scaler.fit_transform(pima_imputed_mean), columns=pima_columns)
# 看下标准化以后的分布
pima_imputed_mean_scaled.hist(figsize=(15,15), sharex=True)


# 在Pipeline中使用
model = Pipeline([
    ('imputer', Imputer()),
    ('standardize', StandarScaler())
])

2)min-max标准化

min-max标准化和z-score相似,其公式为:(X - Xmin)/(Xmax - Xmin)

在python中的调用方法:

# min-max标准化
from sklearn.preprocessing import MinMaxScaler
# 实例化方法
min_max = MinMaxScaler()
# 使用min-max标准化
pima_min_maxed = pd.DataFrame(min_max.fit_transform(pima.fillna(-9)), columns=pima_columns)

3)行归一化

行归一化针对的是每一行数据,不一样于上面的两种方法(针对列),对行进行处理是为了保证每行的向量长度同样(也就是单位范围,unit norm),有L一、L2范数。

在python中的调用方法:

# 行归一化
from sklearn.preprocessing import Normalizer
# 实例化方法
normalize = Normalizer()
# 使用行归一化
pima_normalized = pd.DataFrame(normalize.fit_transform(pima.fillna(-9)), columns=pima_columns)
# 查看矩阵的平均范数(1)
np.sqrt((pima_normalized**2).sum(axis=1)).mean()

🔨 03 特征构建

若是咱们对变量进行处理以后,效果仍不是很是理想,就须要进行特征构建了,也就是衍生新变量。

而在这以前,咱们须要了解咱们的数据集,先前两节中咱们了解到了能够经过 data.infodata.describe() 来查看,同时结合数据等级(定类、定序、定距、定比)来理解变量。

🙊 基础操做

本小节中咱们使用一个自定义数据集。

# 本次案例使用的数据集
import pandas as pd

X = pd.DataFrame({'city':['tokyo',None,'london','seattle','san fancisco','tokyo'],
                  'boolean':['y','n',None,'n','n','y'],
                  'ordinal_column':['somewhat like','like','somewhat like','like','somewhat like','dislike'],
                  'quantitative_column':[1,11,-.5,10,None,20]})
X

file

首先咱们须要对分类变量进行填充操做,类别变量通常用众数或者特殊值来填充,回顾以前的内容,咱们也仍是采起Pipeline的方式来进行,所以能够事先基于TransformMixin基类来对填充的方法进行封装,而后直接在Pipeline中进行调用,代码能够参考:

# 填充分类变量(基于TransformerMixin的自定义填充器,用众数填充)
from sklearn.base import TransformerMixin

class CustomCategoryzImputer(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def transform(self, df):
        X = df.copy()
        for col in self.cols:
            X[col].fillna(X[col].value_counts().index[0], inplace=True)
        return X
    
    def fit(self, *_):
        return self   
    
    
# 调用自定义的填充器
cci = CustomCategoryzImputer(cols=['city','boolean'])
cci.fit_transform(X)

file

又或者利用 scikit-learn 的 Imputer类来实现填充,而这个类有一个 Strategy的方法天然就被继承过来用了,包含的有mean、median、most_frequent可供选择。

# 填充分类变量(基于Imputer的自定义填充器,用众数填充)
from sklearn.preprocessing import Imputer
class CustomQuantitativeImputer(TransformerMixin):
    def __init__(self, cols=None, strategy='mean'):
        self.cols = cols
        self.strategy = strategy
        
    def transform(self, df):
        X = df.copy()
        impute = Imputer(strategy=self.strategy)
        for col in self.cols:
            X[col] = impute.fit_transform(X[[col]])
        return X
    
    def fit(self, *_):
        return self
    
    
# 调用自定义的填充器
cqi = CustomQuantitativeImputer(cols = ['quantitative_column'], strategy='mean')
cqi.fit_transform(X)

file

对上面的两种填充进行流水线封装:

# 所有填充
from sklearn.pipeline import Pipeline

imputer = Pipeline([('quant',cqi),
                    ('category',cci)
])

imputer.fit_transform(X)

完成了分类变量的填充工做,接下来就须要对分类变量进行编码了(由于大多数的机器学习算法都是没法直接对类别变量进行计算的),通常有两种办法:独热编码以及标签编码。

1)独热编码

独热编码主要是针对定类变量的,也就是不一样变量值之间是没有顺序大小关系的,咱们通常可使用 scikit_learn 里面的 OneHotEncoding来实现的,但咱们这里仍是使用自定义的方法来加深理解。

# 类别变量的编码(独热编码)
class CustomDummifier(TransformerMixin):
    def __init__(self, cols=None):
        self.cols = cols
        
    def transform(self, X):
        return pd.get_dummies(X, columns=self.cols)
    
    def fit(self, *_):
        return self
    

# 调用自定义的填充器
cd = CustomDummifier(cols=['boolean','city'])
cd.fit_transform(X)

file

2)标签编码

标签编码是针对定序变量的,也就是有顺序大小的类别变量,就好像案例中的变量ordinal_column的值(dislike、somewhat like 和 like 能够分别用0、一、2来表示),一样的能够写个自定义的标签编码器:

# 类别变量的编码(标签编码)
class CustomEncoder(TransformerMixin):
    def __init__(self, col, ordering=None):
        self.ordering = ordering
        self.col = col
        
    def transform(self, df):
        X = df.copy()
        X[self.col] = X[self.col].map(lambda x: self.ordering.index(x))
        return X
    
    def fit(self, *_):
        return self
    

# 调用自定义的填充器
ce = CustomEncoder(col='ordinal_column', ordering=['dislike','somewhat like','like'])
ce.fit_transform(X)

file

3)数值变量分箱操做

以上的内容是对类别变量的一些简单处理操做,也是比较经常使用的几种,接下来咱们就对数值变量进行一些简单处理方法的讲解。

有的时候,虽然变量值是连续的,可是只有转换成类别才有解释的可能,好比年龄,咱们须要分红年龄段,这里咱们可使用pandas的 cut函数来实现。

# 数值变量处理——cut函数
class CustomCutter(TransformerMixin):
    def __init__(self, col, bins, labels=False):
        self.labels = labels
        self.bins = bins
        self.col = col
        
    def transform(self, df):
        X = df.copy()
        X[self.col] = pd.cut(X[self.col], bins=self.bins, labels=self.labels)
        return X
    
    def fit(self, *_):
        return self
    

# 调用自定义的填充器
cc = CustomCutter(col='quantitative_column', bins=3)
cc.fit_transform(X)

file

综上,咱们能够对上面自定义的方法一并在Pipeline中进行调用,Pipeline的顺序为:

1)用imputer填充缺失值

2)独热编码city和boolean

3)标签编码ordinal_column

4)分箱处理quantitative_column

代码为:

from sklearn.pipeline import Pipeline

# 流水线封装
pipe = Pipeline([('imputer',imputer),
                 ('dummify',cd),
                 ('encode',ce),
                 ('cut',cc)
])

# 训练流水线
pipe.fit(X)

# 转换流水线
pipe.transform(X)

🙊 数值变量扩展

这一小节咱们使用一个新的数据集(人体胸部加速度数据集),咱们先导入数据:

# 人体胸部加速度数据集,标签activity的数值为1-7
'''
1-在电脑前工做
2-站立、走路和上下楼梯
3-站立
4-走路
5-上下楼梯
6-与人边走边聊
7-站立着说话

'''
df = pd.read_csv('./data/activity_recognizer/1.csv', header=None)
df.columns = ['index','x','y','z','activity']
df.head()

file

这边只介绍一种多项式生成新特征的办法,调用PolynomialFeatures来实现。

# 扩展数值特征
from sklearn.preprocessing import PolynomialFeatures

x = df[['x','y','z']]
y = df['activity']

poly = PolynomialFeatures(degree=2, include_bias=False, interaction_only=False)

x_poly = poly.fit_transform(x)
pd.DataFrame(x_poly, columns=poly.get_feature_names()).head()

file

还能够查看下衍生新变量后的相关性状况,颜色越深相关性越大:

# 查看热力图(颜色越深表明相关性越强)
%matplotlib inline
import seaborn as sns

sns.heatmap(pd.DataFrame(x_poly, columns=poly.get_feature_names()).corr())

file

在流水线中的实现代码:

# 导入相关库
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline

knn = KNeighborsClassifier()

# 在流水线中使用
pipe_params = {'poly_features__degree':[1,2,3],
               'poly_features__interaction_only':[True,False],
               'classify__n_neighbors':[3,4,5,6]}

# 实例化流水线
pipe = Pipeline([('poly_features',poly),
                 ('classify',knn)])

# 网格搜索
grid = GridSearchCV(pipe, pipe_params)
grid.fit(x,y)

print(grid.best_score_, grid.best_params_)
0.721189408065 {'classify__n_neighbors': 5, 'poly_features__degree': 2, 'poly_features__interaction_only': True}

🙊 文本变量处理

文本处理通常在NLP(天然语言处理)领域应用最为普遍,通常都是须要把文本进行向量化,最为常见的方法有 词袋(bag of words)、CountVectorizer、TF-IDF

1)bag of words

词袋法分红3个步骤,分别是分词(tokenizing)、计数(counting)、归一化(normalizing)

2)CountVectorizer

将文本转换为矩阵,每列表明一个词语,每行表明一个文档,因此通常出来的矩阵会是很是稀疏的,在sklearn.feature_extraction.text 中调用 CountVectorizer 便可使用。

3)TF-IDF

TF-IDF向量化器由两个部分组成,分别为表明词频的TF部分,以及表明逆文档频率的IDF,这个TF-IDF是一个用于信息检索和聚类的词加权方法,在 sklearn.feature_extraction.text 中调用 TfidfVectorizer 便可。

TF:即Term Frequency,词频,也就是单词在文档中出现的频率。

IDF:即Inverse Document Frequency,逆文档频率,用于衡量单词的重要度,若是单词在多份文档中出现,就会被下降权重。

✅ 04 特征选择

好了,通过了上面的特征衍生操做,咱们如今拥有了好多好多的特征(变量)了,所有丢进去模型训练好很差?固然是不行了🚫,这样子既浪费资源又效果不佳,所以咱们须要作一下 特征筛选 ,而特征筛选的方法大体能够分为两大类:基于统计的特征筛选 和 基于模型的特征筛选

在进行特征选择以前,咱们须要搞清楚一个概念:到底什么是更好的?有什么指标能够用来量化呢?

这大体也能够分为两大类:一类是模型指标,好比accuracy、F1-score、R^2等等,还有一类是元指标,也就是指不直接与模型预测性能相关的指标,如:模型拟合/训练所需的时间、拟合后的模型预测新实例所须要的时间、须要持久化(永久保存)的数据大小。

咱们能够经过封装一个方法,把上面说起到的指标封装起来,方便后续的调用,代码以下:

from sklearn.model_selection import GridSearchCV

def get_best_model_and_accuracy(model, params, x, y):
    grid = GridSearchCV(model, 
                        params,
                        error_score=0.)
    grid.fit(x,y)
    
    # 经典的性能指标
    print("Best Accuracy:{}".format(grid.best_score_))
    # 获得最佳准确率的最佳参数
    print("Best Parameters:{}".format(grid.best_params_))
    # 拟合的平均时间
    print("Average Time to Fit (s):{}".format(round(grid.cv_results_['mean_fit_time'].mean(), 3)))
    
    # 预测的平均时间
    print("Average Time to Score (s):{}".format(round(grid.cv_results_['mean_score_time'].mean(), 3)))
    
    
###############  使用示例  ###############
# 导入相关库
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline

knn = KNeighborsClassifier()

# 在流水线中使用
pipe_params = {'poly_features__degree':[1,2,3],
               'poly_features__interaction_only':[True,False],
               'classify__n_neighbors':[3,4,5,6]}

# 实例化流水线
pipe = Pipeline([('poly_features',poly),
                 ('classify',knn)])

# 网格搜索
get_best_model_and_accuracy(pipe, pipe_params, x, y)

经过上面的操做,咱们能够建立一个模型性能基准线,用于对比后续优化的效果。接下来介绍一些经常使用的特征选择方法。

1)基于统计的特征选择

针对于单变量,咱们能够采用 皮尔逊相关系数以及假设检验 来选择特征。

(1)皮尔逊相关系数能够经过 corr() 来实现,返回的值在-1到1之间,绝对值越大表明相关性越强;

(2)假设检验也就是p值,做为一种统计检验,在特征选择中,假设测试得原则是:” 特征与响应变量没有关系“(零假设)为真仍是假。咱们须要对每一个变量进行检测,检测其与target有没有显著关系。可使用 SelectKBestf_classif 来实现。通常P值是介于0-1之间,简而言之,p值越小,拒绝零假设的几率就越大,也就是这个特征与target关系更大

2)基于模型的特征选择

(1)对于文本特征,sklearn.feature_extraction.text里的 CountVectorizer有自带的特征筛选的参数,分别是 max_features、min_df、max_df、stop_words,能够经过搜索这些参数来进行特征选择,能够结合 SelectKBest 来实现流水线。

(2)针对🌲树模型,咱们能够直接调用不一样树模型算法里的 特征重要度 来返回特征重要度,好比 DecisionTreeClassifier里的feature_importances_,(除此以外还有RandomForest、GBDT、XGBoost、ExtraTreesClassifier等等)均可以直接返回每一个特征对于本次拟合的重要度,从而咱们能够剔除重要度偏低的特征,能够结合 SelectFromModel来实现流水线。

(3)使用正则化来筛选变量(针对线性模型)。有两种经常使用的正则化方法:L1正则化(Lasso)和L2正则化(岭)

总结一下,有几点作特征选择的方法经验:

(1)若是特征是分类变量,那么能够从SelectKBest开始,用卡方或者基于树的选择器来选择变量;

(2)若是特征是定量变量,能够直接用线性模型和基于相关性的选择器来选择变量;

(3)若是是二分类问题,能够考虑使用 SelectFromModel和SVC;

(4)在进行特征选择前,仍是须要作一下EDA。

💫 05 特征转换

通过了上面几个环节的“洗礼”,咱们来到特征转换的环节,也就是使用源数据集的隐藏结构来建立新的列,经常使用的办法有2种:PCA和LDA

✅ PCA:

PCA,即主成分分析(Principal Components Analysis),是比较常见的数据压缩的办法,即将多个相关特征的数据集投影到相关特征较少的坐标系上。也就是说,转换后的特征,在解释性上就走不通了,由于你没法解释这个新变量到底具备什么业务逻辑了。

PCA的原理这里就不展开来说了,太多的文章把它讲得十分透彻了。这里主要是复现一下PCA在sklearn上的调用方法,一来继续熟悉下Pipeline的使用,二来理解一下PCA的使用方法。

# 导入相关库
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
%matplotlib inline

from sklearn.decomposition import PCA

# 导入数据集
iris = load_iris()
iris_x, iris_y = iris.data, iris.target

# 实例化方法
pca = PCA(n_components=2)
# 训练方法
pca.fit(iris_x)
pca.transform(iris_x)[:5,]

# 自定义一个可视化的方法
label_dict = {i:k for i,k in enumerate(iris.target_names)}
def plot(x,y,title,x_label,y_label):
    ax = plt.subplot(111)
    for label,marker,color in zip(
    range(3),('^','s','o'),('blue','red','green')):
        plt.scatter(x=x[:,0].real[y == label],
                   y = x[:,1].real[y == label],
                   color = color,
                   alpha = 0.5,
                   label = label_dict[label]
                   )
        
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    
    leg = plt.legend(loc='upper right', fancybox=True)
    leg.get_frame().set_alpha(0.5)
    plt.title(title)

# 可视化
plot(iris_x, iris_y,"original iris data","sepal length(cm)","sepal width(cm)")
plt.show()

plot(pca.transform(iris_x), iris_y,"Iris: Data projected onto first two PCA components","PCA1","PCA2")

file

以上是PCA在sklearn上的简单调用和效果展现,另外,做者提出了一个颇有意思的问题:

通常而言,对特征进行归一化处理后会对机器学习算法的效果有比较明显的帮助,但为何在书本的例子倒是相反呢?

给出的解释是:在对数据进行缩放后,列与列之间的协方差会更加一致,并且每一个主成分解释的方差会变得分散,而不是集中在某一个主成分上。因此,在实际操做的时候,都要对缩放的未缩放的数据进行性能测试才是最稳妥的哦。

✅ LDA:

LDA,即线性判别分析(Linear Discriminant Analysis),它是一个有监督的算法(哦对了, PCA是无监督的),通常是用于分类流水线的预处理步骤。与PCA相似,LDA也是提取出一个新的坐标轴,将原始的高维数据投影到低维空间去,而区别在于LDA不会去专一数据之间的方差大小,而是直接优化低维空间,以得到最佳的类别可分性。

# LDA的使用
# 导入相关库
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# 实例化LDA模块
lda = LinearDiscriminantAnalysis(n_components=2)
# 训练数据
x_lda_iris = lda.fit_transform(iris_x, iris_y)
# 可视化
plot(x_lda_iris, iris_y, "LDA Projection", "LDA1", "LDA2")

file

📖 06 特征学习

来到最后一章了,这章的主题是“以AI促AI”。看起来还蛮抽象的,反正我是以为有点奇怪,特征学习算法是非参数方法,也就是不依赖数据结构而构建出来的新算法。

🙊 数据的参数假设

参数假设指的是算法对数据形状的基本假设。好比上一章的PCA,咱们是假设:

原始数据的形状能够被(特征值)分解,而且能够用单个线性变换(矩阵计算)表示。

而特征学习算法,就是要去除这个“假设”来解决问题,由于这算法不会依赖数据的形状,而是依赖于随机学习(Stochastic Learning),指的是这些算法并非每次输出相同的结果,而是一次次按轮(epoch)去检查数据点以找到要提取的最佳特征,而且能够拟合出一个最优的解决方法。

而在特征学习领域,有两种方法是比较经常使用的,也是下面来说解的内容:受限玻尔兹曼机(RBM)和词嵌入。

🙊 受限玻尔兹曼机(RBM)

RBM是一种简单的深度学习架构,是一组无监督的特征学习算法,根据数据的几率模型学习必定数量的新特征,每每使用RBM以后去用线性模型(线性回归、逻辑回归、感知机等)的效果极佳。

从概念上说,RBM是一个浅层(2层)的神经网络,属于深度信念网络(DBN,deep belief network)算法的一种。它也是一种无监督算法,能够学习到的 特征数量只受限于计算能力,它可能学习到比原始要少或者多的特征,具体要学习的特征数量取决于要解决的问题。

file

“受限”的说法是由于它只容许层与层之间的链接(层间链接),而不容许同一层内的节点链接(层内链接)。

file

在这里须要理解一下“重建”(Reconstruction),也就是这个操做,使得在不涉及更深层网络的状况下,可见层(输入层)和隐含层之间能够存在数次的前向和反向传播。

在重建阶段,RBM会反转网络,可见层变成了隐含层,隐含层变成了可见层,用相同的权重将激活变量a反向传递到可见层,可是误差不同,而后用前向传导的激活变量重建原始输入向量。RBM就是用这种方法来进行“自我评估”的,经过将激活信息进行反向传导并获取原始输入的近似值,该网络能够调整权重,让近似值更加接近原始输入。

在训练开始时,因为权重是随机初始化的(通常作法),近似值与真实值的差别可能会极大的,接下来就会经过反向传播的方法来调整权重,最小化原始输入与近似值的距离,一直重复这个过程,直到近似值尽量接近原始输入。(这个过程发生的次数叫 迭代次数

大体的原理就是上面的说法了,更加详细的解释能够自行百度哦。下面咱们来说讲RBM在机器学习管道中的应用,咱们仍是使用MNIST数据集,这个数据集在以前讲Keras的时候也用到了,就是一堆数字的像素点数据,而后用来识别数字。

# RBM的使用
# 咱们使用MNIST数据集来说解
# 导入相关库
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import BernoulliRBM
from sklearn.pipeline import Pipeline

# 导入数据集
images = np.genfromtxt('./data/mnist_train.csv', delimiter=',')
print(images.shape)
# 划分数据
images_x, images_y = images[:,1:], images[:,0]

# 缩放特征到0-1
images_x = images_x/255.

# 用RBM学习新特征
rbm = BernoulliRBM(random_state=0)
lr = LogisticRegression()

# 设置流水线的参数范围
params = {'clf__C':[1e-1, 1e0, 1e1],
          'rbm__n_components':[100, 200]
         }
# 建立流水线
pipeline = Pipeline([('rbm', rbm),
                     ('clf', lr)])
# 实例化网格搜索类
grid = GridSearchCV(pipeline, params)
# 拟合数据
grid.fit(images_x, images_y)
# 返回最佳参数
grid.best_params_, grid.best_score_

🙊 词嵌入

在NLP领域应用极为普遍了,它能够将字符串(单词或短语)投影到n维特征集中,以便理解上下文和措辞的细节,咱们可使用sklearn中的CountVectorizerTfidfVectorizer 来将这些字符串进行转为向量,但这只是一些单词特征的集合而已,为了理解这些特征,咱们更加要关注一个叫 gensim的包。

经常使用的词嵌入方法有两种:Word2vec和GloVe。

Word2vec: Google发明的一种基于深度学习的算法。Word2vec也是一个浅层的神经网络,含有输入层、隐含层和输出层,其中输入层和输出层的节点个数同样。

GloVe: 来自斯坦福大学的算法,经过一系列矩阵统计进行学习。

词嵌入的应用不少,好比信息检索,意思是当咱们输入关键词时,搜索引擎能够回忆并准确返回和关键词匹配的文章或者新闻。

本文由博客一文多发平台 OpenWrite 发布!

相关文章
相关标签/搜索