4.2 Scikit-Learn简介(机器学习篇)
第四章 机器学习
4.1 机器学习简介
机器学习是用数据科学的计算能力和算法能力去弥补统计学的不足。
基本统计学概念:偏差(bias)、方差(variance)、过拟合(overfitting)和欠拟合(underfitting)
4.1.1 机器学习分类
机器学习一般分为:有监督学习和无监督学习。
有监督学习分为:分类和回归任务,分类任务中,标签都是离散值;回归任务中标签都是连续值。
无监督学习分为:聚类和降维任何。聚类算法将数据分成不同的组别;降维算法追求更简洁的方式表现数据。
半监督学习,通常在标签数据不完整时使用。
4.2 Scikit-Learn简介
4.2.1 Scikit-Learn的数据表示
机器学习是从数据创建模型的学问,因此你首先需要了解怎样表示数据才能让计算机理解。sklearn认为数据表示最好的方法就是用数据表的形式。
1.数据表
# 基本的数据表示二维网格数据,其中每一行表示数据集的样本,而列表示构成每个样本的相关特征。
import seaborn as sns
iris = sns.load_dataset('iris')
iris.head()
sepal_length | sepal_width | petal_length | petal_width | species | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
2.特征矩阵
# 这个表格通过二维数组或矩阵的形式将信息清晰底表达出来,我们记此类矩阵为特征矩阵。特征矩阵通常被简记为X。
# 它是维度为[n_samples, n_features]的二维矩阵。samples--样本(行数),features--特征数(列数)
# 一、样本(即每一行)通常是指数据集中的每一个对象。
# 二、特征(即每一列)通常是指每个样本都具有的某种量化观测值。一般,特征都是实数值,但有时也可能是布尔值或离散值。
3.目标数组
# 除了特征矩阵x之外,还需要一个标签或标签数组,记为y。目标数组一般是一维数组,
# 长度就是样本总数n_samples,通常用一维的NumPy数组或者Pandas的Series表示。
# 目标数组可以是连续的数值类型,也可以是离散的类型/标签。
%matplotlib inline
sns.set()
sns.pairplot(iris,hue='species',size=1.5)
# 从iris数据集中抽取特征矩阵和目标数组
X_iris = iris.drop('species',axis=1)
print("X_iris:",X_iris.shape)
y_iris = iris['species']
print("y_iris:",y_iris.shape)
X_iris: (150, 4)
y_iris: (150,)
4.2.2 Scikit-Learn的评估器API
sklearn api主要遵循以下设计原则:
统一性:所有对象使用共同接口连接一组方法和统一的文档。
内省:所有参数都是公共属性。
限制对象层级:只有算法才能用Python类表示。数据集都用标准的数据类型(NumPy数组、Pandas DataFrame、SciPy稀疏矩阵)表示,参数名称用标准的Python字符串。
函数组合:许多机器学习任务都可以用一串基本算法实现,sklearn尽量支持这种可能。
明智的默认值:当模型需要用户设置参数时,sklearn预先设置合适的默认值。
sklearn中的所有机器学习算法都是用过评估器API实现的,它为机器学习应用提供统一的接口
1.API基础知识
sklearn评估器API的常用步骤如下:
(1)通过从sklearn中导入适当的评估器类,选择模型类。
(2)用合适的数值对模型类进行实例化,配置模型超参数。
(3)整理数据,获取特征矩阵和目标数组。
(4)调用模型实例的fit()方法对数据进行拟合。
(5)对新数据应用模型:
在有监督学习模型中,通常使用predict()方法预测新数据的标签;
在无监督学习模型中,通常使用transform()或predict()方法转换或推断数据的性质。
2.有监督学习示例:简单线性回归
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.RandomState(42)
x = 10 * rng.rand(50)
y = 2 * x - 1 + rng.randn(50)
# a.选择模型类
from sklearn.linear_model import LinearRegression
# b.选择模型超参数
# 问题:
# 我们想要拟合偏移量(即直线的截距)吗?
# 我们需要对模型进行归一化处理吗?
# 我们需要对特征进行预处理以提高模型灵活性吗?
# 我们打算在模型中使用哪种正则化类型?
# 我们打算使用多少模型组件?
# 有一些重要的参数必须在选择模型类时确定好。这些参数通常被称为超参数,即在模型拟合之前必须确定下来的参数。
model = LinearRegression(fit_intercept=True) # 拟合直线截距
print("模型超参数:\n",model)
# c.抽取特征矩阵和目标数据
X = x[:,np.newaxis]
print("特征矩阵X:\n",X)
print("目标数组y:\n",y)
# d.拟合数据
model.fit(X,y)
# fit()方法会在模型内部进行大量运算,运算结果将存储在模型属性中,供用户使用。
# 在sklearn中,所有通过fit()方法获得的模型参数都带一条下划线。如:线性模型中的模型参数如下:
print("模型的斜率:",model.coef_)
print("模型的截距:",model.intercept_)
# 模型训练效果评价:
# 原数据由y = 2 * x - 1 + rng.randn(50)获得,从中看出直线斜率为 2,截距为 1
# 拟合模型的直线斜率为 1.9776566,截距为 -0.903310725531 误差很小。
# e.预测新数据的标签
xfit = np.linspace(-1,11)
Xfit = xfit[:,np.newaxis]
yfit = model.predict(Xfit)
plt.scatter(x,y) # 散点图
plt.plot(xfit,yfit)
模型超参数:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
特征矩阵X:
[[ 3.74540119]
[ 9.50714306]
[ 7.31993942]
[ 5.98658484]
[ 1.5601864 ]
[ 1.5599452 ]
[ 0.58083612]
[ 8.66176146]
[ 6.01115012]
[ 7.08072578]
[ 0.20584494]
[ 9.69909852]
[ 8.32442641]
[ 2.12339111]
[ 1.81824967]
[ 1.8340451 ]
[ 3.04242243]
[ 5.24756432]
[ 4.31945019]
[ 2.9122914 ]
[ 6.11852895]
[ 1.39493861]
[ 2.92144649]
[ 3.66361843]
[ 4.56069984]
[ 7.85175961]
[ 1.99673782]
[ 5.14234438]
[ 5.92414569]
[ 0.46450413]
[ 6.07544852]
[ 1.70524124]
[ 0.65051593]
[ 9.48885537]
[ 9.65632033]
[ 8.08397348]
[ 3.04613769]
[ 0.97672114]
[ 6.84233027]
[ 4.40152494]
[ 1.22038235]
[ 4.9517691 ]
[ 0.34388521]
[ 9.09320402]
[ 2.58779982]
[ 6.62522284]
[ 3.11711076]
[ 5.20068021]
[ 5.46710279]
[ 1.84854456]]
目标数组y:
[ 7.22926896 18.18565441 13.52423055 10.67206599 0.64185082
1.4000462 -0.29896653 17.38064514 11.36591852 11.3984114
-0.26422614 18.01311476 14.97193082 3.8584585 3.66749887
3.59937032 4.24562734 9.18591626 7.9701638 5.80012793
10.75788366 1.60421824 3.736558 5.13103024 8.93392551
16.05975926 2.92146552 10.28822167 11.2099274 -0.7161115
11.51229264 3.94851904 0.26520582 19.5423544 15.69289556
15.98984947 5.17932245 0.65443493 12.77642131 5.81548096
1.22109281 9.26065077 1.16566447 16.66813782 3.36710603
11.74868864 6.14962364 9.73011153 9.40444538 3.21035654]
模型的斜率: [ 1.9776566]
模型的截距: -0.903310725531
[<matplotlib.lines.Line2D at 0x1e13980d1d0>]
3.有监督学习示例:鸢尾花数据分类
# 我们将使用高斯朴素贝叶斯方法来训练鸢尾花数据集,这个方法假设每个特征中属于每一类的观测值都符合高斯分布。
# 因为高斯朴素贝叶斯方法速度很快,而且不需要选择超参数,所以通常适合作为初步分类手段,在借助复杂模型进行优化之前使用。
from sklearn.naive_bayes import GaussianNB # 1.选择模型类
model = GaussianNB() # 2.初始化模型
from sklearn.cross_validation import train_test_split
Xtrain,Xtest,ytrain,ytest = train_test_split(X_iris,y_iris) # 3.分割数据集
model.fit(Xtrain,ytrain) # 4.拟合数据集
ypredict = model.predict(Xtest) # 5.预测数据集
from sklearn.metrics import accuracy_score
print(accuracy_score(ypredict,ytest)) # 6.模型准确率评估
print(accuracy_score(ytest,ypredict))
# 准确率高达97%
0.947368421053
0.947368421053
4.无监督学习示例:鸢尾花数据降维
# 对鸢尾花数据集进行降维,以便能更方便地对数据进行可视化
# 降维的任务是要找到一个可以保留数据本质特征的低维矩阵来表示高维数据。
# 降维通常用于辅助数据可视化的工作,毕竟二维数据画图比多维甚至更高维的数据画图更方便。
# 主成分分析(principal component analysis,PCA)方法,这一种快速线性降维技术。
from sklearn.decomposition import PCA # 1.选择模型类
model = PCA(n_components=2) # 2.设置超参数,初始化模型
model.fit(X_iris) # 3.拟合模型
x_2D = model.transform(X_iris) # 4.将数据转换为二维
iris['PCA1'] = x_2D[:,0]
iris['PCA2'] = x_2D[:,1]
sns.lmplot("PCA1","PCA2",hue='species',data=iris,fit_reg=False)
# 从二维数据标示图可以看出,虽然PCA算法根本不知道花的种类标签,但不同种类的花还是
# 被清晰地区分开来! 这表明用一种比较简单的分类方法就能够有效地学习这份数据集。
<seaborn.axisgrid.FacetGrid at 0x1e139168198>
5.无监督学习示例:鸢尾花数据聚类
# 聚类方法是要对没有标签的数据集进行分组。
# 使用一个强大的聚类方法----高斯混合模型(Guassian mixture model,GMM)。
# GMM模型试图将数据构造成若干服从高斯分布的概率密度函数镞。
from sklearn.mixture import GaussianMixture # 1.选择模型类
model = GaussianMixture(n_components=3,covariance_type='full') # 2.设置超参数,初始化模型 组件数量,协方差类型
model.fit(X_iris) # 3.拟合数据
y_predict = model.predict(X_iris) # 4.预测簇标签
iris['cluster'] = y_predict
sns.lmplot("PCA1","PCA2",data=iris,hue='species',col='cluster',fit_reg=False)
# 根据簇数量对数据进行分割,就会清晰地看出GaussianMixture算法的训练效果:setosa类的花在簇0中
# 被完美底区分出来,唯一的遗憾是第二幅图中的versicolor和virginical还有一点混淆。
# 这就说明,即使没有专家告诉完美每朵花的具体类型,但由于每种花的特征差异很大,
# 因此完美也可以通过简单的聚类算法自动识别出不同种类的花!
# 这种算法还可以帮助专家们探索观察样本之间的关联性。
<seaborn.axisgrid.FacetGrid at 0x1e13c455630>
4.2.3 应用:手写数字探索
1.加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()
digits.images.shape
# 这份图像数据是一个三维矩阵:共有1797个样本,每张图像都是8 × 8 像素。对前100张图进行可视化:
import matplotlib.pyplot as plt
fig,axes = plt.subplots(10,10,figsize=(8,8),
subplot_kw={'xticks':[],'yticks':[]},
gridspec_kw=dict(hspace=0.1,wspace=0.1))
for i,ax in enumerate(axes.flat):
ax.imshow(digits.images[i],cmap='binary',interpolation='nearest')
ax.text(0.05,0.05,str(digits.target[i]),
transform=ax.transAxes,color='green')
# 为了在sklearn中使用数据,需要一个维度为[n_samples, n_features]的二维特征矩阵
# 可以将每个样本图像的所有像素都作为特征,也就是将每个数字的8 × 8像素平铺成长度为64的一维数组。
# 另外,还需要一个目标数组,用来表示每个数字的真实值(标签)。这两份数据已经放在手写数字数据集
# 的data与target属性中,直接使用即可:
X = digits.data
y = digits.target
print("X的形状:",X.shape,"\n",X)
print("y的形状:",y.shape,"\n",y)
# 从上面可以看出,一共有1797个样本和64个特征。
X的形状: (1797, 64)
[[ 0. 0. 5. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 10. 0. 0.]
[ 0. 0. 0. ..., 16. 9. 0.]
...,
[ 0. 0. 1. ..., 6. 0. 0.]
[ 0. 0. 2. ..., 12. 0. 0.]
[ 0. 0. 10. ..., 12. 1. 0.]]
y的形状: (1797,)
[0 1 2 ..., 8 9 8]
2.无监督学习:降维
# 虽然我们想对具有64维参数控件的样本进行可视化,但是咋如此高纬度的控件中进行可视化十分困难。
# 因此,我们需要借助无监督学习方法将维度降到二维。
# 这次试试流行学学习算法中的isomap算法对数据域进行降维:
from sklearn.manifold import Isomap # 1.选择模型类
model = Isomap(n_components=2) # 2.设置超参数,模型初始化
model.fit(X) # 3.拟合模型
data_projected = model.transform(X) # 4.将数据转为二维
print(data_projected.shape)
# 现在数据已经投影到二维。把数据画出来,看看从结构中能发现什么:
plt.scatter(data_projected[:,0],data_projected[:,1],c=y,
edgecolor='none',alpha=0.5,
cmap=plt.cm.get_cmap('nipy_spectral',10))
plt.colorbar(label='digit label',ticks=range(10))
plt.clim(-0.5,9.5)
# 这幅图呈现除了非常直观的效果,让我们知道数字在64维空间中的分离(可识别)程度。
# 例如,在参数空间中,数字0和数字1基本不会重叠。
# 另外,从图中会发现,数字1和数字4好像有点儿混淆---也许有人写数字1时喜欢在上面加个“帽子”,因此看起来像数字4.
# 虽然有些瑕癖,但从总体上看,各个数字在参数空间中的分离程度还是令人满意的。
# 这其实告诉完美;用一个非常简单的有监督分类算法就可以完成任务。
(1797, 2)
4.有监督学习:数字分类
Xtrain,Xtest,ytrain,ytest = train_test_split(X,y,random_state=0)
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(Xtrain,ytrain)
y_predict = model.predict(Xtest)
from sklearn.metrics import accuracy_score
print(accuracy_score(y_predict,ytest))
# 可以看出,通过一个非常简单的模型,数字识别率就可以达到80%以上!但仅依靠这个指标,
# 我们无法知道模型哪里做的不好,解决这个问题的办法就是用混淆矩阵。
# 可以用sklearn计算混淆矩阵,然后用seaborn画出来:
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest,y_predict)
sns.heatmap(mat,square=True,annot=True,cbar=False)
plt.xlabel('predicted value')
plt.ylabel('true value')
# 从图中可以看出,误判的主要原因是许多数字2被误判成了数字1和数字8.
0.833333333333
<matplotlib.text.Text at 0x1e13b0dccc0>
# 另一种显示模型特征的直观方式是将样本画出来,然后把预测标签放在左下角,用绿色表示预测正确,用红色表示预测错误:
fig,axes = plt.subplots(10,10,figsize=(8,8),
subplot_kw={'xticks':[],'yticks':[]},
gridspec_kw=dict(hspace=0.1,wspace=0.1))
test_images = Xtest.reshape(-1,8,8)
for i,ax in enumerate(axes.flat):
ax.imshow(test_images[i],cmap='binary',interpolation='nearest')
ax.text(0.05,0.05,str(y_predict[i]),
transform=ax.transAxes,
color='green' if (ytest[i] == y_predict[i]) else 'red')
# 通过观察这部分样本数据,我们能自动模型哪里的学习不够好。如果希望分类准确率达到90%以上
# 可能需要借助更加复杂的算法,例如支持向量机、随机森林,或者其他分类算法。