1. 程式人生 > 其它 >【Python環境】使用 scikit-learn 進行機器學習的簡介

【Python環境】使用 scikit-learn 進行機器學習的簡介

概要: 該章節,我們將介紹貫穿scikit-learn使用中的“機器學習(Machine Learning)”這個詞彙,並給出一些簡單的學習示例。

一、機器學習:問題設定

通常,一個學習問題是通過分析一些資料樣本來嘗試預測未知資料的屬性。如果每一個樣本不僅僅是一個單獨的數字,比如一個多維的例項(multivariate data),也就是說有著多個屬性特徵

我們可以把學習問題分成如下的幾個大類:

  • (1)有監督學習 資料帶有我們要預測的屬性。這種問題主要有如下幾種:
    • ①分類 樣例屬於兩類或多類,我們想要從已經帶有標籤的資料學習以預測未帶標籤的資料。識別手寫數字就是一個分類問題,這個問題的主要目標就是把每一個輸出指派到一個有限的類別中的一類。另一種思路去思考分類問題,其實分類問題是有監督學習中的離散形式問題。每一個都有一個有限的分類。對於樣例提供的多個標籤,我們要做的就是把未知類別的資料劃分到其中的一種。
    • ②迴歸 去過預期的輸出包含連續的變數,那麼這樣的任務叫做迴歸。根據三文魚的年紀和中聯預測其長度就是一個迴歸樣例。
  • (2)無監督學習 訓練資料包含不帶有目標值的輸入向量x。對於這些問題,目標就是根據資料發現樣本中相似的群組——聚類。或者在輸入空間中判定資料的分佈——密度估計,或者把資料從高維空間轉換到低維空間以用於視覺化

訓練集和測試集 機器學習是學習一些資料集的特徵屬性並將其應用於新的資料。這就是為什麼在機器學習用來評估演算法時一般把手中的資料分成兩部分。一部分我們稱之為訓練集,用以學習資料的特徵屬性。一部分我們稱之為測試集,用以檢驗學習到的特徵屬性。

二、載入一個樣本資料集

scikit-learn帶有一些標準資料集。比如用來分類的iris資料集、digits資料集;用來回歸的boston house price 資料集。

接下來,我們我們從shell開啟一個Python直譯器並載入iris和digits兩個資料集。【譯註:一些程式碼慣例就不寫了,提示符>>>之類的學過Python的都懂】

$ python
>>>from sklearn import datasets
>>>iris = datasets.load_iris()
>>>digits = datasets.load_digits()

一個數據集是一個包含資料所有元資料的類字典物件。這個資料儲存在 ‘.data’成員變數中,是一個$n*n$的陣列,行表示樣例,列表示特徵。在有監督學習問題中,一個或多個響應變數(Y)儲存在‘.target’成員變數中。不同資料集的更多細節可以在dedicated section中找到。

例如,對於digits資料集,digits.data可以訪問得到用來對數字進行分類的特徵:

>>>print(digits.data)  
[[  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.]]

digits.target 就是數字資料集對應的真實數字值。也就是我們的程式要學習的。

>>>digits.target
array([0, 1, 2, ..., 8, 9, 8])

資料陣列的形狀 儘管原始資料也許有不同的形狀,但實際使用的資料通常是一個二維陣列(n個樣例,n個特徵)。對於數字資料集,每一個原始的樣例是一張(8 x 8)的圖片,也能被使用:

>>>digits.images[0]
array([[  0.,   0.,   5.,  13.,   9.,   1.,   0.,   0.],
       [  0.,   0.,  13.,  15.,  10.,  15.,   5.,   0.],
       [  0.,   3.,  15.,   2.,   0.,  11.,   8.,   0.],
       [  0.,   4.,  12.,   0.,   0.,   8.,   8.,   0.],
       [  0.,   5.,   8.,   0.,   0.,   9.,   8.,   0.],
       [  0.,   4.,  11.,   0.,   1.,  12.,   7.,   0.],
       [  0.,   2.,  14.,   5.,  10.,  12.,   0.,   0.],
       [  0.,   0.,   6.,  13.,  10.,   0.,   0.,   0.]])

三、學習和預測

對於數字資料集(digits dataset),任務是預測一張圖片中的數字是什麼。數字資料集提供了0-9每一個數字的可能樣例,可以用它們來對位置的數字圖片進行擬合分類。

在scikit-learn中,用以分類的擬合(評估)函式是一個Python物件,具體有fit(X,Y)和predic(T)兩種成員方法。

其中一個擬合(評估)樣例是sklearn.svmSVC類,它實現了支援向量分類(SVC)。一個擬合(評估)函式的建構函式需要模型的引數,但是時間問題,我們將會把這個擬合(評估)函式作為一個黑箱:

>>>from sklearn import svm
>>>clf = svm.SVC(gamma=0.001, C=100.)

選擇模型引數 我們呼叫擬合(估測)例項clf作為我們的分類器。它現在必須要擬合模型,也就是說,他必須要學習模型。這可以通過把我們的訓練集傳遞給fit方法。作為訓練集,我們使用其中除最後一組的所有影象。我們可以通過Python的分片語法[:-1]來選取訓練集,這個操作將產生一個新陣列,這個陣列包含digits.dataz中除最後一組資料的所有例項。

>>>clf.fit(digits.data[:-1], digits.target[:-1])  
SVC(C=100.0, cache_size=200, class_weight=None, coef0=0.0, degree=3,
gamma=0.001, kernel='rbf', max_iter=-1, probability=False,
random_state=None, shrinking=True, tol=0.001, verbose=False)

現在你可以預測新的數值了。我們可以讓這個訓練器告訴我們digits資料集我們沒有作為訓練資料使用的最後一張影象是什麼數字。

>>>clf.predict(digits.data[-1])
array([8])

相應的圖片如下圖:

正如你所看到的,這是一個很有挑戰的任務:這張圖片的解析度很低。你同意分類器給出的答案嗎?

這個分類問題的完整示例在這裡識別手寫數字,你可以執行並使用它。[譯:看本文附錄]

四、模型持久化

可以使用Python的自帶模組——pickle來儲存scikit中的模型:

>>>from sklearn import svm
>>>from sklearn import datasets
>>>clf = svm.SVC()
>>>iris = datasets.load_iris()
>>>X, y = iris.data, iris.target
>>>clf.fit(X, y)  
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0, degree=3, gamma=0.0,
  kernel='rbf', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False)

>>>import pickle
>>>s = pickle.dumps(clf)
>>>clf2 = pickle.loads(s)
>>>clf2.predict(X[0])
array([0])
>>>y[0]0

對於scikit,也許使用joblib的pickle替代——(joblib.dump&joblib.load)更有趣。因為它在處理帶資料時更高效。但是遺憾的是它只能把資料持久化到硬碟而不是一個字串(譯註:搬到string字串意味著資料在記憶體中):

>>>from sklearn.externals import joblib
>>>joblib.dump(clf, 'filename.pkl')

往後你就可以載入這個轉儲的模型(也能在另一個Python程序中使用),如下:

>>>clf = joblib.load('filename.pkl')

注意: joblib.dump返回一個檔名的列表,每一個numpy陣列元素包含一個clf在檔案系統上的名字,在用joblib.load載入的時候所有的檔案需要在相同的資料夾下

注意pickle有一些安全和可維護方面的問題。請參考Model persistent 獲得在scikit-learn中模型持久化的細節。

五、慣例約定

scikit-learn的各種擬合(評估)函式遵循一些確定的規則以使得他們的用法能夠被預想到(譯:使得各種學習方法的用法統一起來)

  • ①型別轉換 除非特別指定,輸入將被轉換為float64 import numpyfrom sklearn import random_projection rng = np.random.RandomState(0) X = rng.rand(10,2000) X = np.array(X,dtype ='float32')print x.dtype transformer = random_projection.GaussianRandomProjection() X_new = transformer.fit_transform(X)print X_new.dtype 在這個例子中,X是float32,被fit_transform(X)轉換成float64,迴歸被轉換成float64,分類目標維持不變.
from sklearn import datesetsfrom sklearn.svm import SVC
iris = datasets.load_iris()
clf =SVC()
clf.fit(iris.data,iris.target)print list(clf.predict(iris.data[:3]))
clf.fit(iris.data,iris.target_names[iris.target])print list(clf.predict(iris.data[:3]))

這裡第一個predict()返回一個整數陣列,是因為iris.target(一個整數陣列)被用於擬合。第二個predict()返回一個字串陣列,因為iris.target_names被用於擬合。

  • ②重擬合和更新引數 一個擬合(評估)函式的混合引數(超引數)能夠在通過sklearn.pipeline.Pipeline.set_params方法構造之後被更新。多次呼叫fit()能夠覆寫之前fit()學習的內容: import numpy as npfrom sklearn.svm import SVC rng = np.random.RandomState(0); X = rng.rand(100,10) Y = rng.binomial(1,0.5,100) X_test = rng.rand(5,10) clf = SVC() clf.set_params(kernel = 'linear').fit(X,Y) clf.predict(X_test) clf.set_params(kernel='rbf').fit(X,Y) clf.predict(X_test) 這裡,用SVC()構造之後,開始擬合(評估)函式預設的’rbf’核被改編成’linear’,後來又改回’rbf’去重擬合做第二次的預測。

附:

  • ①digits資料集:一個展示怎樣用scikit-learn識別手寫數字的樣例:繪製數字: # Code source: Gaël Varoquaux# Modified for documentation by Jaques Grobler# License: BSD 3 clausefrom sklearn import datasetsimport matplotlib.pyplot as plt#Load the digits datasetdigits = datasets.load_digits()#Display the first digitplt.figure(1, figsize=(3, 3)) plt.imshow(digits.images[-1], cmap=plt.cm.gray_r, interpolation='nearest') plt.show()
  • ②繪製數字分類 (plot_digits_classification.py) # Author: Gael Varoquaux <gael dot varoquaux at normalesup dot org># License: BSD 3 clause# Standard scientific Python importsimport matplotlib.pyplot as plt# Import datasets, classifiers and performance metricsfrom sklearn import datasets, svm, metrics# The digits datasetdigits = datasets.load_digits()# The data that we are interested in is made of 8x8 images of digits, let's# have a look at the first 3 images, stored in the `images` attribute of the# dataset. If we were working from image files, we could load them using# pylab.imread. Note that each image must have the same size. For these# images, we know which digit they represent: it is given in the 'target' of# the dataset.images_and_labels = list(zip(digits.images, digits.target))for index, (image, label) in enumerate(images_and_labels[:4]): plt.subplot(2, 4, index + 1) plt.axis('off') plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest') plt.title('Training: %i' % label)# To apply a classifier on this data, we need to flatten the image, to# turn the data in a (samples, feature) matrix:n_samples = len(digits.images) data = digits.images.reshape((n_samples, -1))# Create a classifier: a support vector classifierclassifier = svm.SVC(gamma=0.001)# We learn the digits on the first half of the digitsclassifier.fit(data[:n_samples / 2], digits.target[:n_samples / 2])# Now predict the value of the digit on the second half:expected = digits.target[n_samples / 2:] predicted = classifier.predict(data[n_samples / 2:]) print("Classification report for classifier %s:n%sn" % (classifier, metrics.classification_report(expected, predicted))) print("Confusion matrix:n%s" % metrics.confusion_matrix(expected, predicted)) images_and_predictions = list(zip(digits.images[n_samples / 2:], predicted))for index, (image, prediction) in enumerate(images_and_predictions[:4]): plt.subplot(2, 4, index + 5) plt.axis('off') plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest') plt.title('Prediction: %i' % prediction) plt.show()

資料人機器學習群:201696892,歡迎加入。