科学数据处理统计学习指南¶
统计学习
机器学习是一项越来越重要的技术,因为实验科学面临的数据集规模正在迅速增长。 它所解决的问题包括建立链接不同观察值的预测功能,对观察值进行分类或学习未标记数据集中的结构。
本教程将探讨统计学习,以及将机器学习技术用于统计推断的目的:在手头的数据上得出结论。
Scikit-learn是一个Python模块,在紧密结合的科学Python程序包世界(NumPy,SciPy,matplotlib)中集成了经典的机器学习算法。
统计学习:scikit-learn中的设置和估计对象
数据集
Scikit-learn处理来自一个或多个以2D数组表示的数据集的学习信息。它们可以理解为多维观测的列表。我们说这些数组的第一个轴是样本轴,而第二个是特征轴。
scikit-learn附带的一个简单示例:iris数据集:
from sklearn import datasets
iris = datasets.load_iris()
data = iris.data
data.shape
输出:
(150,4)
它由150个鸢尾花的观察数据组成,每个观察都由4个特征描述:它们的萼片和花瓣的长度和宽度,详细信息请参见iris.DESCR。
当数据最初不是(n_samples,n_features)形状时,需要对其进行预处理,以供scikit-learn使用。
重塑数据的一个例子是数字数据集:
数字数据集由1797个8x8手写数字图像组成,如图所示:
digits = datasets.load_digits()
digits.images.shape
import matplotlib.pyplot as plt
plt.imshow(digits.images[-1], cmap=plt.cm.gray_r)
输出:
(1797, 8, 8)
<matplotlib.image.AxesImage object at ...>
为了将此数据集与scikit-learn一起使用,我们将每个8x8图像转换为长度为64的特征向量:
data = digits.images.reshape((digits.images.shape[0], -1))
估计对象
拟合数据:scikit-learn实现的主要API是估算器。估计器是从数据中学习的任何对象。它可以是分类,回归或聚类算法,也可以是从原始数据中提取/过滤有用特征的转换器。
所有估算器对象都公开一个带数据集(通常为二维数组)的fit方法:
estimator.fit(data)
估算器参数:可以在实例化估计器或通过修改相应属性来设置估计器的所有参数:
estimator = Estimator(param1 = 1,param2 = 2)
estimator.param1
输出:
1
估计出的参数:当数据装有估计器时,参数是根据现有数据估计的。所有估计的参数都是估计对象的属性,其下划线结尾:
estimator.estimated_param_
有监督的学习:从高维观测值预测输出变量
监督学习中解决的问题
监督学习包括学习两个数据集之间的链接:观测数据X和我们试图预测的外部变量y,通常称为“目标”或“标签”。 y通常是长度为n_samples的一维数组。
scikit-learn中所有受监督的估计器都执行fit(X,y)方法以拟合模型,并执行predict(X)方法,给定未标记的观察值X,该方法返回预测的标记y。
词汇:分类和回归
如果预测任务是将观察结果分类为一组有限标签,换句话说就是“命名”观察到的对象,则该任务称为分类任务。另一方面,如果目标是预测连续目标变量,则称其为回归任务。
在scikit-learn中进行分类时,y是整数或字符串的向量。
注意:有关scikit-learn中使用的基本机器学习词汇的快速指南,请参见scikit-learn机器学习简介教程。
最近邻和维度诅咒
分类鸢尾花数据集: 鸢尾花数据集是一项分类任务,包括从其花瓣和萼片的长度和宽度中识别出3种不同类型的鸢尾花(Setosa,Versicolour和Virginica):
import numpy as np
from sklearn import datasets
iris_X, iris_y = datasets.load_iris(return_X_y=True)
np.unique(iris_y)
输出:
array([0, 1, 2])
k近邻分类器
最简单的分类器是最近邻分类器:给定新的观测值X_test,在训练集中(即用于训练估计器的数据)找到具有最近特征向量的观测值。(有关这种类型的分类器的更多信息,请参见在线Scikit-learn文档的Nearest Neighbors部分。)
训练集和测试集
在尝试使用任何学习算法进行实验时,重要的是不要在用于拟合估算器的数据上测试估算器的预测,因为这不会评估新数据上估算器的性能。 这就是为什么数据集经常被分成训练和测试数据的原因。
KNN(k个最近邻)分类示例:
# 在训练和测试数据中分割鸢尾花数据
# 随机排列,随机分割数据
np.random.seed(0)
indices = np.random.permutation(len(iris_X))
iris_X_train = iris_X[indices[:-10]]
iris_y_train = iris_y[indices[:-10]]
iris_X_test = iris_X[indices[-10:]]
iris_y_test = iris_y[indices[-10:]]
# 创建并拟合最近邻分类器
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
knn.fit(iris_X_train, iris_y_train)
knn.predict(iris_X_test)
iris_y_test
输出:
KNeighborsClassifier()
array([1, 2, 1, 0, 0, 0, 2, 1, 2, 0])
array([1, 1, 1, 0, 0, 0, 2, 1, 2, 0])
维度诅咒
为了使估算器有效,您需要相邻点之间的距离小于某个值,这取决于问题。在一个维度上,这平均需要 点。在上述 -NN 示例的上下文中,如果仅通过一个值在0到1范围内的特征和训练观察来描述数据,则新数据将比距离远出。因此,与类间特征变化的规模相比,最近的邻居决策规则将很快变得有效。
如果要素数量为,则现在需要 个点。假设我们在一个维度上需要10个点:现在,在维度的空间上则需要个点才能铺平[0,1]空间。随着变得越来越大,一个好的估计量所需的训练点数呈指数增长。
例如,如果每个点仅是一个数字(8个字节),那么在大约的维度上有效的-NN估计量将需要比整个互联网的当前估计大小(±1000艾字节左右)更多的训练数据。
这称为维数诅咒,是机器学习解决的核心问题。
线性模型:从回归到稀疏
糖尿病数据集
糖尿病数据集包含442位患者的10个生理变量(年龄,性别,体重,血压),并显示一年后疾病进展的指标:
diabetes_X, diabetes_y = datasets.load_diabetes(return_X_y=True)
diabetes_X_train = diabetes_X[:-20]
diabetes_X_test = diabetes_X[-20:]
diabetes_y_train = diabetes_y[:-20]
diabetes_y_test = diabetes_y[-20:]
当前的任务是根据生理变量预测疾病的进展。
线性回归
LinearRegression最简单的形式是通过调整一组参数将线性模型拟合到数据集,以使模型的残差平方和尽可能小。
线性模型:
:数据
:目标变量
:系数
:观察噪声
from sklearn import linear_model
regr = linear_model.LinearRegression()
regr.fit(diabetes_X_train, diabetes_y_train)
print(regr.coef_)
# 均方误差
np.mean((regr.predict(diabetes_X_test) - diabetes_y_test)**2)
# 解释方差得分:1是完美预测,0表示X和y之间没有线性关系。
regr.score(diabetes_X_test, diabetes_y_test)
输出:
[ 0.30349955 -237.63931533 510.53060544 327.73698041 -814.13170937
492.81458798 102.84845219 184.60648906 743.51961675 76.09517222]
2004.56760268...
0.5850753022690...
紧缩
如果每个维度的数据点很少,则观测值中的噪声会引起高方差:
X = np.c_[ .5, 1].T
y = [.5, 1]
test = np.c_[ 0, 2].T
regr = linear_model.LinearRegression()
import matplotlib.pyplot as plt
plt.figure()
np.random.seed(0)
for _ in range(6):
this_X = .1 * np.random.normal(size=(2, 1)) + X
regr.fit(this_X, y)
plt.plot(test, regr.predict(test))
plt.scatter(this_X, y, s=3)
高维统计学习的一种解决方案是将回归系数缩小为零:任意两个随机选择的观察值很可能是不相关的。 这称为Ridge回归:
regr = linear_model.Ridge(alpha=.1)
plt.figure()
np.random.seed(0)
for _ in range(6):
this_X = .1 * np.random.normal(size=(2, 1)) + X
regr.fit(this_X, y)
plt.plot(test, regr.predict(test))
plt.scatter(this_X, y, s=3)
这是偏差/方差折衷的一个示例:脊alpha参数越大,偏差越大,方差越小。
我们可以选择α以最大程度地减少遗漏的错误,这次使用糖尿病数据集而不是我们的综合数据:
alphas = np.logspace(-4, -1, 6)
print([regr.set_params(alpha=alpha)
.fit(diabetes_X_train, diabetes_y_train)
.score(diabetes_X_test, diabetes_y_test)
for alpha in alphas])
输出:
[0.5851110683883..., 0.5852073015444..., 0.5854677540698...,
0.5855512036503..., 0.5830717085554..., 0.57058999437...]
注意:捕获阻止模型泛化为新数据的拟合参数噪声称为过拟合。 脊回归带来的偏差称为正则化。
稀疏性
注意:完整的糖尿病数据集的表示将涉及11个维度(10个特征维度和一个目标变量)。 很难就这种表示法建立一种直觉,但记住这将是一个相当空的空间,这可能会很有用。
我们可以看到,尽管特征2在整个模型中具有很强的系数,但是当与特征1一起考虑时,它传达的y信息很少。
为了改善问题的状况(即减轻维度诅咒),仅选择信息性特征并将非信息性特征(例如特征2设置为0)会很有趣。Ridge回归会减少其贡献,但不会设置 他们为零。 另一种惩罚方法称为Lasso(最小绝对收缩和选择算子),可以将一些系数设置为零。 这种方法称为稀疏方法,稀疏性可以看作是Occam剃刀的一种应用:更喜欢简单的模型。
regr = linear_model.Lasso()
scores = [regr.set_params(alpha=alpha)
.fit(diabetes_X_train, diabetes_y_train)
.score(diabetes_X_test, diabetes_y_test)
for alpha in alphas]
best_alpha = alphas[scores.index(max(scores))]
regr.alpha = best_alpha
regr.fit(diabetes_X_train, diabetes_y_train)
print(regr.coef_)
输出:
Lasso(alpha=0.025118864315095794)
[ 0. -212.43764548 517.19478111 313.77959962 -160.8303982 -0.
-187.19554705 69.38229038 508.66011217 71.84239008]
针对同一问题的不同算法
可以使用不同的算法来解决相同的数学问题。 例如,scikit-learn中的套索对象使用坐标下降法解决了套索回归问题,该方法在大型数据集上非常有效。 但是,scikit-learn还使用LARS算法提供了LassoLars对象,对于估计的权重向量非常稀疏的问题(即观察很少的问题)非常有效。
分类
对于分类,如在标记虹膜任务中一样,线性回归不是正确的方法,因为它将对远离决策边界的数据赋予过多的权重。 线性方法是拟合S型函数或逻辑函数:
输入:
log = linear_model.LogisticRegression(C=1e5)
log.fit(iris_X_train, iris_y_train)
输出:
LogisticRegression(C=100000.0)
这就是知名的逻辑回归:
多标签分类
如果您要预测多个类别,则经常使用的选择是对所有分类器进行拟合,然后使用投票启发法进行最终决策。
逻辑回归的收缩率和稀疏性
C参数控制LogisticRegression对象中的正则化量:C的值较大会导致正则化程度降低。 惩罚=“ l2”给出收缩率(即非稀疏系数),而惩罚=“ l1”给出稀疏性。
练习
尝试用最近的邻居和线性模型对数字数据集进行分类。 忽略最后的10%,并根据这些观察结果测试预测性能。
from sklearn import datasets, neighbors, linear_model
X_digits, y_digits = datasets.load_digits(return_X_y=True)
X_digits = X_digits / X_digits.max()
解决方案:https://scikit-learn.org/stable/_downloads/bfcebce45024b267e8546d6914acfedc/plot_digits_classification_exercise.py
支持向量机(SVM)
线性支持向量机
支持向量机属于判别模型家族:它们试图找到样本的组合以构建一个平面,以最大化两个类别之间的余量。 正则化是由C参数设置的:C的值较小意味着使用分隔线附近的许多或所有观测值来计算裕度(更正则化); C的值较大,表示在接近分隔线的观察值上计算裕度(较少的正则化)。
示例:
SVM可用于回归–SVR(支持向量回归)–或分类–SVC(支持向量分类)。
from sklearn import svm
svc = svm.SVC(kernel='linear')
svc.fit(iris_X_train, iris_y_train)
输出:
SVC(kernel='linear')
警告:归一化数据
对于包括SVM在内的许多估算器而言,对于每个要素而言,拥有具有单位标准差的数据集对于获得良好的预测至关重要。
使用核函数
在要素空间中,类并非总是线性可分离的。 解决方案是建立一个决策函数,该决策函数不是线性的,而是可以是多项式的。 这是使用内核技巧完成的,该技巧可以看作是通过将内核置于观察值上来创建决策能量:
互动范例
参见SVM GUI以下载svm_gui.py;。 使用左右按钮添加两个类别的数据点,拟合模型并更改参数和数据。
练习
尝试使用SVM对虹膜数据集中的第1类和第2类进行分类,并具有2个主要特征。 忽略每堂课的10%,并根据这些观察结果测试预测性能。
警告:课程是有序的,请不要遗漏最后的10%,您只能在一个课程上进行测试。
提示:您可以在网格上使用Decision_function方法来获取一些感觉。
输入:
iris = datasets.load_iris()
X = iris.data
y = iris.target
X = X[y != 0, :2]
y = y[y != 0]
解决方案见:https://scikit-learn.org/stable/_downloads/91f0cd01beb5b964a5e1ece5bdd15499/plot_iris_exercise.py
模型选择:选择估计量及其参数
分数和交叉验证的分数
如我们所见,每个估算器都公开一种计分方法,该方法可以判断新数据的拟合(或预测)质量。 越大越好。
from sklearn import datasets, svm
X_digits, y_digits = datasets.load_digits(return_X_y=True)
svc = svm.SVC(C=1, kernel='linear')
svc.fit(X_digits[:-100], y_digits[:-100]).score(X_digits[-100:], y_digits[-100:])
输出:
0.98
为了更好地衡量预测准确性(我们可以将其用作模型拟合优度的代理),我们可以将数据依次分成折叠,用于训练和测试:
import numpy as np
X_folds = np.array_split(X_digits, 3)
y_folds = np.array_split(y_digits, 3)
scores = list()
for k in range(3):
# We use 'list' to copy, in order to 'pop' later on
X_train = list(X_folds)
X_test = X_train.pop(k)
X_train = np.concatenate(X_train)
y_train = list(y_folds)
y_test = y_train.pop(k)
y_train = np.concatenate(y_train)
scores.append(svc.fit(X_train, y_train).score(X_test, y_test))
print(scores)
输出:
[0.934..., 0.956..., 0.939...]
这被叫做K折交叉验证。
交叉验证生成器
Scikit-learn具有一组类,这些类可用于生成流行的交叉验证策略的训练/测试索引列表。
他们公开了一个拆分方法,该方法接受要拆分的输入数据集,并为所选交叉验证策略的每次迭代生成训练/测试集索引。
本示例显示了split方法的示例用法。
输入:
from sklearn.model_selection import KFold, cross_val_score
X = ["a", "a", "a", "b", "b", "c", "c", "c", "c", "c"]
k_fold = KFold(n_splits=5)
for train_indices, test_indices in k_fold.split(X):
print('Train: %s | test: %s' % (train_indices, test_indices))
输出:
Train: [2 3 4 5 6 7 8 9] | test: [0 1]
Train: [0 1 4 5 6 7 8 9] | test: [2 3]
Train: [0 1 2 3 6 7 8 9] | test: [4 5]
Train: [0 1 2 3 4 5 8 9] | test: [6 7]
Train: [0 1 2 3 4 5 6 7] | test: [8 9]
然后可以轻松执行交叉验证:
[svc.fit(X_digits[train], y_digits[train]).score(X_digits[test], y_digits[test]) for train, test in k_fold.split(X_digits)]
输出:
[0.963..., 0.922..., 0.963..., 0.963..., 0.930...]
可以使用cross_val_score帮助器直接计算交叉验证得分。 给定一个估算器,交叉验证对象和输入数据集,cross_val_score将数据重复拆分为一个训练和一个测试集,使用该训练集训练该估算器,并根据测试集计算交叉算子的每次迭代的得分 验证。
默认情况下,估算器的得分方法用于计算各个得分。
请参阅指标模块以了解有关可用评分方法的更多信息。
cross_val_score(svc, X_digits, y_digits, cv=k_fold, n_jobs=-1)
输出:
array([0.96388889, 0.92222222, 0.9637883 , 0.9637883 , 0.93036212])
n_jobs = -1表示将在计算机的所有CPU上调度计算。
可替代地,可以提供计分参数以指定替代的计分方法。
cross_val_score(svc, X_digits, y_digits, cv=k_fold,
scoring='precision_macro')
输出:
array([0.96578289, 0.92708922, 0.96681476, 0.96362897, 0.93192644])
交叉验证生成器
KFold (n_splits, shuffle, random_state) |
StratifiedKFold (n_splits, shuffle, random_state) |
GroupKFold (n_splits) |
---|---|---|
将其拆分为K折,在K-1上训练,然后在左侧进行测试。 | 与K折相同,但保留每个折内的类分布。 | 确保同一组不在测试和培训集中 |
ShuffleSplit (n_splits, test_size, train_size, random_state) |
StratifiedShuffleSplit |
GroupShuffleSplit |
根据随机排列生成训练/测试索引。 | 与shuffle split相同,但保留每次迭代内的类分布。 | 确保同一组不在测试和培训集中。 |
LeaveOneGroupOut () |
LeavePGroupsOut (n_groups) |
LeaveOneOut () |
采用组数组对观察进行分组。 | 忽略P组 | 忽略指定的一个观察值。 |
LeavePOut (p) |
PredefinedSplit |
|
忽略P个观察值 | 根据预定义的拆分生成训练/测试索引。 |
练习
在数字数据集上,绘制具有线性核的SVC估计量的交叉验证得分作为参数C的函数(使用点的对数网格,范围为1到10)。
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn import datasets, svm
X, y = datasets.load_digits(return_X_y=True)
svc = svm.SVC(kernel='linear')
C_s = np.logspace(-10, 0, 10)
scores = list()
解决方案见链接:手写数据集上的交叉验证
网格搜索和交叉验证的估计量
网格搜索
scikit-learn提供了一个对象,该对象在给定数据的情况下,在参数网格上的估计器拟合期间计算分数,并选择参数以最大化交叉验证分数。 该对象在构造期间接受一个估算器,并公开一个估算器API:
from sklearn.model_selection import GridSearchCV, cross_val_score
Cs = np.logspace(-6, -1, 10)
clf = GridSearchCV(estimator=svc, param_grid=dict(C=Cs),
n_jobs=-1)
clf.fit(X_digits[:1000], y_digits[:1000])
clf.best_score_
clf.best_estimator_.C
# 测试集的预测性能不如训练集
clf.score(X_digits[1000:], y_digits[1000:])
输出:
GridSearchCV(cv=None,...)
0.925...
0.0077...
0.943...
默认情况下,GridSearchCV使用3倍交叉验证。但是,如果检测到通过了分类器而不是回归器,则使用分层的3倍。默认值将在版本0.22中更改为5倍交叉验证。
嵌套交叉验证
cross_val_score(clf, X_digits, y_digits)
输出:
array([0.938..., 0.963..., 0.944...])
并行执行两个交叉验证循环:一个由GridSearchCV估计器设置gamma,另一个由cross_val_score来测量估计器的预测性能。所得分数是对新数据的预测分数的无偏估计。
警告:不能嵌套使用并行计算的对象(n_jobs不同于1)。
交叉验证的估计量
可以在逐个算法的基础上更高效地完成交叉验证以设置参数。这就是为什么对于某些估计器,scikit-learn会显示交叉验证:评估通过交叉验证自动设置其参数的估计器性能估计器:
from sklearn import linear_model, datasets
lasso = linear_model.LassoCV()
X_diabetes, y_diabetes = datasets.load_diabetes(return_X_y=True)
lasso.fit(X_diabetes, y_diabetes)
# 估算器自动选择其lambda:
lasso.alpha_
输出:
LassoCV()
0.00375...
这些估算器的名称与对应估算器的名称类似,后缀“ CV”。
练习
在糖尿病数据集上,找到最佳正则化参数alpha。
奖励:您对选择出的alpha有多少信心?
from sklearn import datasets
from sklearn.linear_model import LassoCV
from sklearn.linear_model import Lasso
from sklearn.model_selection import KFold
from sklearn.model_selection import GridSearchCV
X, y = datasets.load_diabetes(return_X_y=True)
X = X[:150]
解决方案:糖尿病数据集上的交叉验证练习
无监督学习:寻求数据表示
聚类:将观察分组在一起
聚类中解决的问题:
在给定鸢尾花数据集的情况下,如果我们知道虹膜有3种类型,但没有分类学家来标记它们的话:我们可以尝试进行聚类任务:将观察结果分成分离良好的一组,称为聚类。
K均值聚类
请注意,存在许多不同的聚类标准和相关算法。 最简单的聚类算法是K-means。
输入:
from sklearn import cluster, datasets
X_iris, y_iris = datasets.load_iris(return_X_y=True)
k_means = cluster.KMeans(n_clusters=3)
k_means.fit(X_iris)
print(k_means.labels_[::10])
print(y_iris[::10])
输出:
KMeans(n_clusters=3)
[1 1 1 1 1 0 0 0 0 0 2 2 2 2 2]
[0 0 0 0 0 1 1 1 1 1 2 2 2 2 2]
警告:聚类绝对不能保证恢复基本事实(译者注:即找到真实标签)。 首先,很难选择正确数量的群集。 其次,尽管scikit-learn使用了几种技巧来缓解此问题,但是该算法对初始化很敏感,并且可以陷入局部最小值。
不要过度解读聚类结果。
应用示例:矢量量化
一般而言,尤其是KMeans,聚类可以看作是选择少量样本来压缩信息的一种方式。 该问题有时称为矢量量化。 例如,这可以用于后继图像:
import scipy as sp
try:
face = sp.face(gray=True)
except AttributeError:
from scipy import misc
face = misc.face(gray=True)
X = face.reshape((-1, 1)) # We need an (n_sample, n_feature) array
k_means = cluster.KMeans(n_clusters=5, n_init=1)
k_means.fit(X)
values = k_means.cluster_centers_.squeeze()
labels = k_means.labels_
face_compressed = np.choose(labels, values)
face_compressed.shape = face.shape
输出:
KMeans(n_clusters=5, n_init=1)
分层聚集聚类:Ward
层次聚类方法是一种聚类分析,旨在建立聚类的层次。通常,此技术的各种方法是:
集聚-自下而上的方法:每个观察都从其自己的簇开始,并且以最小化链接标准的方式迭代地合并簇。当感兴趣的簇仅由少量观察组成时,这种方法特别有趣。当簇数很大时,它的计算效率比k均值高得多。
分裂-自上而下的方法:所有观察都始于一个簇,当一个簇向下移动时,它将被迭代地拆分。对于估计大量聚类,此方法既缓慢(由于所有观察都是从一个聚类开始,然后递归拆分),而且在统计上是不适定的。
连接受限的集群
通过聚集聚类,可以通过提供连通性图来指定可以将哪些样本聚在一起。 scikit-learn中的图由它们的邻接矩阵表示。通常,使用稀疏矩阵。例如,在对图像进行聚类时,这对于检索连接的区域(有时也称为连接的组件)很有用。
from skimage.data import coins
from scipy.ndimage.filters import gaussian_filter
from skimage.transform import rescale
rescaled_coins = rescale(
gaussian_filter(coins(), sigma=2),
0.2, mode='reflect', anti_aliasing=False, multichannel=False
)
X = np.reshape(rescaled_coins, (-1, 1))
我们需要图像的矢量化版本。 'rescaled_coins'是硬币图像的缩小版本,以加快处理速度:
from sklearn.feature_extraction import grid_to_graph
connectivity = grid_to_graph(*rescaled_coins.shape)
定义数据的图形结构。连接到邻近的像素:
n_clusters = 27 #簇的数量
from sklearn.cluster import AgglomerativeClustering
ward = AgglomerativeClustering(n_clusters=n_clusters, linkage='ward',
connectivity=connectivity)
ward.fit(X)
label = np.reshape(ward.labels_, rescaled_coins.shape)
输出:
AgglomerativeClustering(connectivity=..., n_clusters=27)
功能集聚
我们已经看到稀疏性可以用来减轻维数的诅咒,即与特征数量相比,观测值不足。另一种方法是将相似的特征融合在一起:特征集聚。可以通过在特征方向上进行聚类(换句话说,对转置后的数据进行聚类)来实现此方法。
digits = datasets.load_digits()
images = digits.images
X = np.reshape(images, (len(images), -1))
connectivity = grid_to_graph(*images[0].shape)
agglo = cluster.FeatureAgglomeration(connectivity=connectivity,
n_clusters=32)
agglo.fit(X)
X_reduced = agglo.transform(X)
X_approx = agglo.inverse_transform(X_reduced)
images_approx = np.reshape(X_approx, images.shape)
输出:
FeatureAgglomeration(connectivity=..., n_clusters=32)
转换和逆转换(inverse_transform)方法
一些估计器会公开一种转换方法,例如以减少数据集的维数。
分解:从信号到分量和负载
组件(成分/特征)和负载
如果X是我们的多元数据,那么我们要解决的问题是在不同的观察基础上重写它:我们想学习载荷L和一组分量C,使得X = LC。存在选择的不同标准组件
主成分分析:PCA
主成分分析(PCA)选择解释信号中最大方差的连续成分。
上面观察到的点云在一个方向上非常平坦:三个单变量特征之一几乎可以使用另外两个来精确计算。 PCA寻找数据不一致的方向。
当用于转换数据时,PCA可以通过投影在主要子空间上来降低数据的维数。
#创建一个仅2个有用维度的信号
x1 = np.random.normal(size=100)
x2 = np.random.normal(size=100)
x3 = x1 + x2
X = np.c_[x1, x2, x3]
from sklearn import decomposition
pca = decomposition.PCA()
pca.fit(X)
print(pca.explained_variance_)
输出:
[ 2.18565811e+00 1.19346747e+00 8.43026679e-32]
如我们所见,只有前两个组件是有用的。
pca.n_components = 2
X_reduced = pca.fit_transform(X)
X_reduced.shape
输出:
(100, 2)
独立成分分析:ICA
独立组件分析(ICA)选择组件,以便它们的载荷分布具有最大数量的独立信息。 它能够恢复非高斯独立信号:
# 获取样本数据
import numpy as np
from scipy import signal
time = np.linspace(0, 10, 2000)
s1 = np.sin(2 * time) # 信号1 : 正弦信号
s2 = np.sign(np.sin(3 * time)) # 信号2:方波信号
s3 = signal.sawtooth(2 * np.pi * time) # 信号3: 锯齿信号
S = np.c_[s1, s2, s3]
S += 0.2 * np.random.normal(size=S.shape) # 增加噪音
S /= S.std(axis=0) # 标准化数据
# 混合数据
A = np.array([[1, 1, 1], [0.5, 2, 1], [1.5, 1, 2]]) # 混和矩阵
X = np.dot(S, A.T) # 获取观测值
# 计算ICA
ica = decomposition.FastICA()
S_ = ica.fit_transform(X) # 得到预测结果
A_ = ica.mixing_.T
np.allclose(X, np.dot(S_, A_) + ica.mean_)
输出:
True
全部放在一起
管道
我们已经看到,一些估计器可以转换数据,并且一些估计器可以预测变量。 我们还可以创建组合的估计量:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn import datasets
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
# 定义管道以搜索PCA截断和分类器正则化的最佳组合。
pca = PCA()
# 将公差设置为较大的值可以使示例更快
logistic = LogisticRegression(max_iter=10000, tol=0.1)
pipe = Pipeline(steps=[('pca', pca), ('logistic', logistic)])
X_digits, y_digits = datasets.load_digits(return_X_y=True)
# 可以使用__分隔的参数名称来设置管道的参数:
param_grid = {
'pca__n_components': [5, 15, 30, 45, 64],
'logistic__C': np.logspace(-4, 4, 4),
}
search = GridSearchCV(pipe, param_grid, n_jobs=-1)
search.fit(X_digits, y_digits)
print("Best parameter (CV score=%0.3f):" % search.best_score_)
print(search.best_params_)
# 绘制PCA色谱
pca.fit(X_digits)
fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True, figsize=(6, 6))
ax0.plot(np.arange(1, pca.n_components_ + 1),
pca.explained_variance_ratio_, '+', linewidth=2)
ax0.set_ylabel('PCA explained variance ratio')
ax0.axvline(search.best_estimator_.named_steps['pca'].n_components,
linestyle=':', label='n_components chosen')
ax0.legend(prop=dict(size=12))
特征脸的人脸识别
本示例中使用的数据集是“未经处理的、有标签的人脸”(也称为LFW)的预处理摘录:
http://vis-www.cs.umass.edu/lfw/lfw-funneled.tgz(233MB)
"""
===================================================
使用特征脸和SVM的脸部识别示例
===================================================
本示例中使用的数据集是“贴有标签的未经处理的面孔”,又名LFW_:
http://vis-www.cs.umass.edu/lfw/lfw-funneled.tgz(233MB)
.._ LFW:http://vis-www.cs.umass.edu/lfw/
数据集中排名前5名的人员的预期结果:
================================================== =
================== ============ ======= ========== =======
precision recall f1-score support
================== ============ ======= ========== =======
Ariel Sharon 0.67 0.92 0.77 13
Colin Powell 0.75 0.78 0.76 60
Donald Rumsfeld 0.78 0.67 0.72 27
George W Bush 0.86 0.86 0.86 146
Gerhard Schroeder 0.76 0.76 0.76 25
Hugo Chavez 0.67 0.67 0.67 15
Tony Blair 0.81 0.69 0.75 36
avg / total 0.80 0.80 0.80 322
================== ============ ======= ========== =======
"""
from time import time
import logging
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import fetch_lfw_people
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.decomposition import PCA
from sklearn.svm import SVC
print(__doc__)
# 在标准输出上显示进度日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
# #############################################################################
# 下载数据(如果尚未存储在磁盘上)并将其作为numpy数组加载
lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)
# 内省图像数组以找到形状(用于绘制)
n_samples, h, w = lfw_people.images.shape
# 对于机器学习,我们直接使用这2个数据(因为相对像素位置信息被该模型忽略)
X = lfw_people.data
n_features = X.shape[1]
# 要预测的标签是该人的身份证
y = lfw_people.target
target_names = lfw_people.target_names
n_classes = target_names.shape[0]
print("Total dataset size:")
print("n_samples: %d" % n_samples)
print("n_features: %d" % n_features)
print("n_classes: %d" % n_classes)
# #############################################################################
# 使用分层k折分为训练集和测试集
# 分割需练级与测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42)
# #############################################################################
# 在人脸数据集(视为未标记数据集)上计算PCA(特征脸):无监督特征提取/降维
n_components = 150
print("Extracting the top %d eigenfaces from %d faces"
% (n_components, X_train.shape[0]))
t0 = time()
pca = PCA(n_components=n_components, svd_solver='randomized',
whiten=True).fit(X_train)
print("done in %0.3fs" % (time() - t0))
eigenfaces = pca.components_.reshape((n_components, h, w))
print("Projecting the input data on the eigenfaces orthonormal basis")
t0 = time()
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)
print("done in %0.3fs" % (time() - t0))
# #############################################################################
# 训练SVM分类模型
print("Fitting the classifier to the training set")
t0 = time()
param_grid = {'C': [1e3, 5e3, 1e4, 5e4, 1e5],
'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.1], }
clf = GridSearchCV(
SVC(kernel='rbf', class_weight='balanced'), param_grid
)
clf = clf.fit(X_train_pca, y_train)
print("done in %0.3fs" % (time() - t0))
print("Best estimator found by grid search:")
print(clf.best_estimator_)
# #############################################################################
# 对测试集中的模型质量进行定量评估
print("Predicting people's names on the test set")
t0 = time()
y_pred = clf.predict(X_test_pca)
print("done in %0.3fs" % (time() - t0))
print(classification_report(y_test, y_pred, target_names=target_names))
print(confusion_matrix(y_test, y_pred, labels=range(n_classes)))
# #############################################################################
# 使用matplotlib对预测进行定性评估
def plot_gallery(images, titles, h, w, n_row=3, n_col=4):
"""Helper function to plot a gallery of portraits"""
plt.figure(figsize=(1.8 * n_col, 2.4 * n_row))
plt.subplots_adjust(bottom=0, left=.01, right=.99, top=.90, hspace=.35)
for i in range(n_row * n_col):
plt.subplot(n_row, n_col, i + 1)
plt.imshow(images[i].reshape((h, w)), cmap=plt.cm.gray)
plt.title(titles[i], size=12)
plt.xticks(())
plt.yticks(())
# 在测试集的一部分上绘制预测结果
def title(y_pred, y_test, target_names, i):
pred_name = target_names[y_pred[i]].rsplit(' ', 1)[-1]
true_name = target_names[y_test[i]].rsplit(' ', 1)[-1]
return 'predicted: %s\ntrue: %s' % (pred_name, true_name)
prediction_titles = [title(y_pred, y_test, target_names, i)
for i in range(y_pred.shape[0])]
plot_gallery(X_test, prediction_titles, h, w)
# 绘制最有意义的特征脸图库
eigenface_titles = ["eigenface %d" % i for i in range(eigenfaces.shape[0])]
plot_gallery(eigenfaces, eigenface_titles, h, w)
plt.show()
输出:
数据集中排名前5名的人员的预期结果:
precision recall f1-score support
Gerhard_Schroeder 0.91 0.75 0.82 28
Donald_Rumsfeld 0.84 0.82 0.83 33
Tony_Blair 0.65 0.82 0.73 34
Colin_Powell 0.78 0.88 0.83 58
George_W_Bush 0.93 0.86 0.90 129
avg / total 0.86 0.84 0.85 282
未解决的问题:股票市场结构
我们可以预测给定时间内Google的股价变化吗?
寻求帮助
项目邮件清单
如果您遇到scikit-learn的错误,或者需要在文档字符串或在线文档中进行说明,请随时在邮件列表中询问。
机器学习从业者问答社区
网站 | 说明 |
---|---|
Quora.com | Quora有一个与机器学习相关的问题的主题,其中还包含一些有趣的讨论:https://www.quora.com/topic/Machine-Learning |
Stack Exchange | Stack Exchange系列站点托管用于机器学习问题的多个子域。 |
– _“斯坦福大学的吴安德教授教授的一本优秀的免费在线机器学习课程”:https://www.coursera.org/learn/machine-learning
– _“另一种出色的免费在线课程,采用了更通用的人工智能方法”:https://www.udacity.com/course/intro-to-artificial-intelligence–cs271