sklearn学习(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a19990412/article/details/82496572

简述

从学习三开始,这个系列的blog会围绕着scikit-learn上的实例进行。

General Examples (一般实例)

这些都是一些简单的实例

这次只看一个(本来准备写多个的。。但是看了下交叉验证的数量。。)
Plotting Cross-Validated Predictions(画出交叉验证预测)

from sklearn import datasets
from sklearn.model_selection import cross_val_predict
from sklearn import linear_model
import matplotlib.pyplot as plt

lr = linear_model.LinearRegression()
boston = datasets.load_boston()
y = boston.target

# cross_val_predict returns an array of the same size as `y` where each entry
# is a prediction obtained by cross validation:
predicted = cross_val_predict(lr, boston.data, y, cv=10)

fig, ax = plt.subplots()
ax.scatter(y, predicted, edgecolors=(0, 0, 0))
ax.plot([y.min(), y.max()], [y.min(), y.max()], 'k--', lw=4)
ax.set_xlabel('Measured')
ax.set_ylabel('Predicted')
plt.show()

这里写图片描述

这是通过线性模型的交叉验证,为了进一步的探索,点击
http://scikit-learn.org/stable/modules/cross_validation.html#cross-validation
开始对Crocs-validation做新的研究。

交叉验证

当训练和测试都是用同一个数据集的时候,往往都会出现过拟合的情况。

为了避免过拟合的情况,当使用一个监督的机器学习模型,选部分的数据作为测试集是很普遍的行为。

在scikit-learn中,一个随机的切分成训练和测试集,可以很快被 train_test_split 这个函数,使用起来还是比较简单的。

例如:

from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn import svm

iris = datasets.load_iris()

X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.4)

print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
print(clf.score(X_test, y_test))

由于,我这里跟官网给的代码不同在于我取消了那个随机数种子,所以,关于那个得分不一定是一样的(每次测试)。

其中一个输出是:
但是,前两个倒是一直都是一样的主要是,我test_size的数值都是固定了。
就是说,数据当中选做训练集合,测试集的数据可能不是那么一样,但是区分的数目倒是都是一样的。

(90, 4) (90,)
(60, 4) (60,)
0.9666666666666667

当评估不同的超参数评估器的时候,例如为SVM模型手动设置C,这任然有一种过拟合的风险存在。因为模型的系数可能会被扰动,直到评估器表现达到最优的时候。这样,测试集合的信息可能会泄露一点点到这个模型当中。评估矩阵的不再表现一个普遍的最优。(可能有些人不太理解这是什么意思。这两里,需要知道SVM中的这个C,其实就是加一个正则化之后的系数的,为了表面出现过拟合的情况。这里说的是,用一个集合来训练,同时也用这个集合来测试。那么就算是设置了这样的超参数,也有过拟合的风险问题。

其实说了这么一大堆的核心目的,就是说,需要分一个validation set(验证集合)。然后训练用训练集合,测试用测试集合。

当对集合做了这样的划分之后,其实是会大幅度地(drastically)减少了可以用于训练的集合数量。然后,结果也只是根据随机选取的部分数据。

对于这种问题的话,一种解决方式就是采用cross-validation(交叉验证),简称CV

测试数据集,依然被保留作为最后的评估集合,但是,当使用了CV之后,验证集合就不需要了。
最基本的方法,叫做,k-fold CV(K折交叉验证)。训练集合被分为k个小点的集合。(也有其他方法,但是基本的原理都是类似的。)下面这过程就被称为k折。

  • 模型是被k-1个折来训练。
  • 然后用剩下的一个那个集合作为评估集合。

这个效果评估,被称为k折交叉验证。是计算数值的平均。这个方法,在计算上耗费较多。但是不浪费太多的数据。就好像,是在填补任意测试集一样。这在反向推理(数据集合较为简单的情况下),是有非常大的优势的。

计算交叉验证指标

最简单的使用交叉验证的方法,cross_val_score

from sklearn.model_selection import cross_val_score
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)

print(scores )
  • cv:当是整数型的时候,表示的是fold的数目(前面的k折的
  • 如果为了计算快一点的话,可以添加参数 n_jobs,表示用多少个CPU来计算。如果使用-1就表示使用所有的CPU。

为什么对于Cross validation 来说,并行计算重要,要知道,这里的计算规模是关于cv数值(k-folds)线性增长的。当数据规模也很大的时候 k * N的规模就更大了。这样子的话,只有通过并行计算的方式来降低运算时间。

并行计算实例:

from sklearn import datasets
from sklearn import svm
from sklearn.model_selection import cross_val_score
import time

if __name__ == '__main__':
    iris = datasets.load_iris()

    clf = svm.SVC(kernel='linear', C=1)

    st = time.time()
    scores = cross_val_score(clf, iris.data, iris.target, cv=5)
    et = time.time()
    print(et - st)
    print(scores)

    st = time.time()
    scores = cross_val_score(clf, iris.data, iris.target, cv=5, n_jobs=-1)
    et = time.time()
    print(et - st)
    print(scores)

当然啦,由于这里的计算数量太少,所以,反而后者开了并行计算之后效果更差劲。

输出:

0.0
[0.96666667 1.         0.96666667 0.96666667 1.        ]
1.2504231929779053
[0.96666667 1.         0.96666667 0.96666667 1.        ]

当然,cv那其实也可以传进去一个切片模型。

比如:

from sklearn import datasets
from sklearn import svm
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import ShuffleSplit

if __name__ == '__main__':
    iris = datasets.load_iris()

    clf = svm.SVC(kernel='linear', C=1)

    n_samples = iris.data.shape[0]
    cv = ShuffleSplit(n_splits=3, test_size=0.3, random_state=0)
    scores = cross_val_score(clf, iris.data, iris.target, cv=cv)
    print(scores)

输出:

[0.97777778 0.97777778 1.        ]

标准化预处理:
要知道,使用svm的时候,只有使用了标准化的数据才能用,否则都是不行的…偏差太大了,会导致最后的预测结果会有偏差(甚至偏差的特别大。。)。

下面,就是使用sklearn上提供的预处理工具中的标准化 工具。

这个工具也是需要训练之后,才能使用的。(沿袭了sklearn的基本特点,获得性的语言,都是使用fit)

from sklearn import datasets
from sklearn import svm
from sklearn import preprocessing
from sklearn.model_selection import train_test_split

if __name__ == '__main__':
    iris = datasets.load_iris()

    clf = svm.SVC(kernel='linear', C=1)

    X_train, X_test, y_train, y_test = train_test_split(
        iris.data, iris.target, test_size=0.4, random_state=0)

    scaler = preprocessing.StandardScaler().fit(X_train)
    X_train_transformed = scaler.transform(X_train)

    clf = svm.SVC(C=1).fit(X_train_transformed, y_train)
    X_test_transformed = scaler.transform(X_test)

    print(clf.score(X_test_transformed, y_test))

当然,如果是要使用多个模型的话,完全可以设置一个多管道的东西,这样设置之后,代码量就会小很多了。

from sklearn import datasets
from sklearn import svm
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import ShuffleSplit

if __name__ == '__main__':
    iris = datasets.load_iris()
    clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
    cv = ShuffleSplit(n_splits=3, test_size=0.3, random_state=0)
    print(cross_val_score(clf, iris.data, iris.target, cv=cv))

输出:

[0.97777778 0.93333333 0.95555556]

这里的话:

  • 创建一个管道模型。这个模型,是先进行标准化的,然后在进行svm。
  • 再沿用之前的cv拆分方式。
  • 先用会这个模型来fit操作来连续的得到一个标准化的尺标模型。同时,还会有一个svm的模型通过fit模型,但是拿的是上一个预处理的模型来做好了的trans_form的数据。同时预测的时候也是做了类似的效果。

这个make_pipeline其实非常常用。而且,都是两个的。

具体可以参见这个url
http://scikit-learn.org/stable/modules/pipeline.html#combining-estimators

  • 一般的效果是,先进行预处理,再放到模型里面。
  • 常用的预处理方式有:正则化,二分,PCA(主成分分析)

cross_validate函数和多测度评估

cross_validatecross_val_score主要有两个不同:

  • cross_validate允许确定的多测度的评估
  • 它会返回一个dict,包含有 训练分数和匹配次数,还有评分次数

对于一个单测度的评估,评分的参数要么是,string,要么是一个callable的变量,要么就是None,这个键会是['test_score', 'fit_time', 'score_time']

但是对于多测度的评估,返回的值会是一个字典,然后会是['test_<scorer1_name>', 'test_<scorer2_name>', 'test_<scorer...>', 'fit_time', 'score_time']

如果不要的trian_score其实可以设置 return_train_score=False

from sklearn import datasets
from sklearn import svm

from sklearn.model_selection import cross_validate

if __name__ == '__main__':
    iris = datasets.load_iris()
    scoring = ['precision_macro', 'recall_macro']
    clf = svm.SVC(kernel='linear', C=1, random_state=0)
    scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,
                            cv=5, return_train_score=False)
    print(sorted(scores.keys()))
    print(scores['test_recall_macro'])
['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro']
[0.96666667 1.         0.96666667 0.96666667 1.        ]

或者是,

from sklearn import datasets
from sklearn import svm

from sklearn.metrics import recall_score
from sklearn.model_selection import cross_validate
from sklearn.metrics.scorer import make_scorer
if __name__ == '__main__':
    iris = datasets.load_iris()
    scoring = {'prec_macro': 'precision_macro',
               'rec_micro': make_scorer(recall_score, average='macro')}

    clf = svm.SVC(kernel='linear', C=1, random_state=0)
    scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,
                            cv=5, return_train_score=True)
    print(sorted(scores.keys()))

交叉验证迭代器

给独立且具有相似分布的数据的交叉验证

Assuming that some data is Independent and Identically Distributed (i.i.d.) is making the assumption that all samples stem from the same generative process and that the generative process is assumed to have no memory of past generated samples.
The following cross-validators can be used in such cases.

其实就是说,假设所有的数据都是独立且具有相同分布,也就是做这样的一个假设,假设所有的数据都是通过同样的一个生成过程而来的。并且这个生成的过程也是没有记忆的。
下面的交叉验证可以被用在上面这样的情况下。

i.i.d. data就是 独立而且具有相同的分布。在机器学习中,做出这样的假设是一个较为普遍的现象,但是在实践中却很少出现

如果有人知道样本都是通过时间独立的过程生成的,这最好还是采用 time-series aware cross-validation scheme 时序交叉验证模式。

同样的,当我们知道这个生成的过程,是组结构 样本被收集来自于不同的物体,实验,测量仪,最好还是使用group-wise cross-validation.

  • K-fold
  • Repeated K-Fold
  • Leave One Out (LOO)
  • Leave P Out (LPO)

K-fold将整个数据分为k份,当k=n的时候跟LOO策略是一样的。
数据分成同等规模的k份,然后让取k-1份作为测试集

例如:

from sklearn.model_selection import KFold

X = ["a", "b", "c", "d"]
kf = KFold(n_splits=3)
for train, test in kf.split(X):
    print("%s %s" % (train, test))

输出:

[2 3] [0 1]
[0 1 3] [2]
[0 1 2] [3]

n_splits=4时

[1 2 3] [0]
[0 2 3] [1]
[0 1 3] [2]
[0 1 2] [3]

n_splits=2时

[2 3] [0 1]
[0 1] [2 3]
  • n_splits表示的是被分的组数。
  • 从当n_splits为3的情况下的输出时候,我们可以看出,当不够的时候,会从k值更小的分法来找起。

RepeatedKFold:表示的是,将K-fold重复n次,

from sklearn.model_selection import RepeatedKFold

X = ["a", "b", "c", "d"]
rkf = RepeatedKFold(n_splits=2, n_repeats=2)
for train, test in kf.split(X):
    print("%s %s" % (train, test))

比如:

[0 1] [2 3]
[2 3] [0 1]
[2 3] [0 1]
[0 1] [2 3]

有趣的一点是,这里分组的时候,都是有序的分组。

Leave One Out (LOO):这其实是非常简单的分法。

from sklearn.model_selection import LeaveOneOut

X = "asdfghjkl"
loo = LeaveOneOut()
for train, test in loo.split(X):
    print("%s %s" % (train, test))

输出:

[1 2 3 4 5 6 7 8] [0]
[0 2 3 4 5 6 7 8] [1]
[0 1 3 4 5 6 7 8] [2]
[0 1 2 4 5 6 7 8] [3]
[0 1 2 3 5 6 7 8] [4]
[0 1 2 3 4 6 7 8] [5]
[0 1 2 3 4 5 7 8] [6]
[0 1 2 3 4 5 6 8] [7]
[0 1 2 3 4 5 6 7] [8]

使用LOO的用户,需要注意一些问题。
当对比K-fold的交叉验证,是建立在n个来自于n个数据的模型,而不是k个模型。(n>k时候)
此外,每一个都是在n-1个样本上训练,而不是在(k-1)*n/k上训练。

在两种方法中,假设k不是那么大的一个数。并且,保证了k小于n,LOO是需要更多的计算资源的。

考虑到准确性,LOO经常在高维上作为检查错误。

直觉上,因为n个样本中的n-1个被用来去建立各个模型,每个fold上的模型之间是基本类似的,并且跟用所有的数据训练的效果也是类似的。

然而,当,学习曲线比较陡的时候,那么5折或者是10折的交叉验证都会比较容易过分评估泛化误差(overestimate the generalization error)。

然后,一般来说5-fold或者是10-fold的交叉验证,是表现的比LOO更好的。

LPO,其实跟LOO有点类似,不过这里就是选P个,而不是像LOO一样,只选一个出去。

from sklearn.model_selection import LeavePOut

X = "asdfghjkl"
loo = LeavePOut(p=2)
for train, test in loo.split(X):
    print("%s %s" % (train, test))

输出:

[2 3 4 5 6 7 8] [0 1]
[1 3 4 5 6 7 8] [0 2]
[1 2 4 5 6 7 8] [0 3]
[1 2 3 5 6 7 8] [0 4]
[1 2 3 4 6 7 8] [0 5]
[1 2 3 4 5 7 8] [0 6]
[1 2 3 4 5 6 8] [0 7]
[1 2 3 4 5 6 7] [0 8]
[0 3 4 5 6 7 8] [1 2]
[0 2 4 5 6 7 8] [1 3]
[0 2 3 5 6 7 8] [1 4]
[0 2 3 4 6 7 8] [1 5]
[0 2 3 4 5 7 8] [1 6]
[0 2 3 4 5 6 8] [1 7]
[0 2 3 4 5 6 7] [1 8]
[0 1 4 5 6 7 8] [2 3]
[0 1 3 5 6 7 8] [2 4]
[0 1 3 4 6 7 8] [2 5]
[0 1 3 4 5 7 8] [2 6]
[0 1 3 4 5 6 8] [2 7]
[0 1 3 4 5 6 7] [2 8]
[0 1 2 5 6 7 8] [3 4]
[0 1 2 4 6 7 8] [3 5]
[0 1 2 4 5 7 8] [3 6]
[0 1 2 4 5 6 8] [3 7]
[0 1 2 4 5 6 7] [3 8]
[0 1 2 3 6 7 8] [4 5]
[0 1 2 3 5 7 8] [4 6]
[0 1 2 3 5 6 8] [4 7]
[0 1 2 3 5 6 7] [4 8]
[0 1 2 3 4 7 8] [5 6]
[0 1 2 3 4 6 8] [5 7]
[0 1 2 3 4 6 7] [5 8]
[0 1 2 3 4 5 8] [6 7]
[0 1 2 3 4 5 7] [6 8]
[0 1 2 3 4 5 6] [7 8]

ShuffleSplit:从翻译上看,就是洗牌切分

from sklearn.model_selection import ShuffleSplit

X = "asdfghjkl"
ss = ShuffleSplit(n_splits=3, test_size=0.25,
                  random_state=0)
for train, test in ss.split(X):
    print("%s %s" % (train, test))

注意一下,那个random_state是随机数种子。如果固定之后,会变成不随机的。

输出效果为:

[4 8 6 3 0 5] [7 2 1]
[6 8 0 4 2 5] [3 1 7]
[4 1 0 8 7 6] [5 2 3]

一般会认为,ShuffleSplite会是很好的对KFold的替代品。因为ShuffleSplite允许一个关于生成的测试集和样本数量的更好的控制。

基于标签的分层交叉验证

一些的分类问题,可能展示一个在目标类上表现一种非常大的不平衡。

例如,可能是负面的样本是正面的好几倍。
在这样的情况下,推荐采用分层样本。

一般采用StratifiedKFoldStratifiedShuffleSplit,来保证相关频率的类,在每个测试集和验证集之间都是近乎相等的。

比如:

from sklearn.model_selection import StratifiedKFold
import numpy as np
X = np.ones(10)
y = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
skf = StratifiedKFold(n_splits=3)
for train, test in skf.split(X, y):
    print("%s %s" % (train, test))

一开始看,还有点迷惑,后来你就会发现,在下面当中,其实,三个当中test中的对应的y为0的比例是基本上相同。
同理可以推到validation集合上

[2 3 6 7 8 9] [0 1 4 5]
[0 1 3 4 5 8 9] [2 6 7]
[0 1 2 4 5 6 7] [3 8 9]

为分好组的数据准备的交叉验证

当i.i.d(独立,且具有相同的分布)这样的条件不成立的情况下,底层生成过程会抛出分好组的独立的样本。
这样的分组,是域特殊的。一个例子会是,当这个是收集自多个病人的医学数据。每个病人都做了不同采样。这样的数据很可能是独立的在每一个单独的分组中。

在这样的情况下,我们更想知道,当模型是在一个特定的组上训练效果是不是会比没分组之前更好。为了测量这个,我们需要去保证在验证集中所有的样本都来自于一点都没有代表着配对训练的折。

from sklearn.model_selection import GroupKFold

X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10]
y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d"]
groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3]

gkf = GroupKFold(n_splits=3)
for train, test in gkf.split(X, y, groups=groups):
    print("%s %s" % (train, test))

其实就是按照组来分就好。把原来版本中那个元素的单位,换成这里的组就好了

为时序数据做交叉分析

时序数据,是指在相近的样本具有一定的相关联性。
然后,传统的交叉验证技术,像是k-foldshuffleSplit假设数据都是独立的,并且是具有相同的分布。但是这会导致难以解释的在训练集和测试集之前的相关性的情况(抛出一个在泛化误差上不好的评估结果)

猜你喜欢

转载自blog.csdn.net/a19990412/article/details/82496572
今日推荐