sklearn学习(三)
簡述
從學習三開始,這個系列的blog會圍繞著scikit-learn上的實例進行。
General Examples (一般實例)
這些都是一些簡單的實例
這次只看一個(本來準備寫多個的。。但是看了下交叉驗證的數量。。)
Plotting Cross-Validated Predictions(畫出交叉驗證預測)
這是通過線性模型的交叉驗證,為了進一步的探索,點擊
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 svmiris = 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的數值都是固定了。
就是說,數據當中選做訓練集合,測試集的數據可能不是那么一樣,但是區分的數目倒是都是一樣的。
當評估不同的超參數評估器的時候,例如為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 timeif __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 ShuffleSplitif __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_splitif __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 ShuffleSplitif __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_validate 和cross_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 svmfrom sklearn.model_selection import cross_validateif __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 svmfrom 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 KFoldX = ["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 RepeatedKFoldX = ["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 LeaveOneOutX = "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 LeavePOutX = "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 ShuffleSplitX = "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允許一個關于生成的測試集和樣本數量的更好的控制。
基于標簽的分層交叉驗證
一些的分類問題,可能展示一個在目標類上表現一種非常大的不平衡。
例如,可能是負面的樣本是正面的好幾倍。
在這樣的情況下,推薦采用分層樣本。
一般采用StratifiedKFold和StratifiedShuffleSplit,來保證相關頻率的類,在每個測試集和驗證集之間都是近乎相等的。
比如:
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集合上
為分好組的數據準備的交叉驗證
當i.i.d(獨立,且具有相同的分布)這樣的條件不成立的情況下,底層生成過程會拋出分好組的獨立的樣本。
這樣的分組,是域特殊的。一個例子會是,當這個是收集自多個病人的醫學數據。每個病人都做了不同采樣。這樣的數據很可能是獨立的在每一個單獨的分組中。
在這樣的情況下,我們更想知道,當模型是在一個特定的組上訓練效果是不是會比沒分組之前更好。為了測量這個,我們需要去保證在驗證集中所有的樣本都來自于一點都沒有代表著配對訓練的折。
from sklearn.model_selection import GroupKFoldX = [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-fold和shuffleSplit假設數據都是獨立的,并且是具有相同的分布。但是這會導致難以解釋的在訓練集和測試集之前的相關性的情況(拋出一個在泛化誤差上不好的評估結果)
總結
以上是生活随笔為你收集整理的sklearn学习(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TensorFlow下载文件到当前目录
- 下一篇: 【解决方案】npm安装vue超时(ERR