數據挖掘入門系列教程(三)之scikit-learn框架基本使用(以K近鄰算法為例)


數據挖掘入門系列教程(三)之scikit-learn框架基本使用(以K近鄰算法為例)

數據挖掘入門系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html

項目地址:GitHub

在上一篇博客中,我們使用了簡單的OneR算法對Iris進行分類,在這篇博客中,我將介紹數據挖掘通用框架的搭建以及使用(以scikit-learn框架為例子),並以K近鄰算法為例展示過程。

簡介

在介紹框架之前,有幾個重要的概念如下所示:

  • 估計器(Estimator):用於分類,聚類和回歸分析。
  • 轉換器(Transformer):用於數據的預處理和數據轉換。
  • 流水線(Pipeline):組合數據挖掘流程,便於再次使用。

至於這幾個具體是怎么回事,下面將以具體的例子來進行展示。

scikit-learn 估計器

估計器,根據上面概念的解釋,我們也知道估計器的作用是什么了,它主要是為了訓練模型,用於分類任務。主要有兩個主要的函數:

  • fit():訓練算法,里面接受訓練集類別兩個參數。
  • predict():里面的參數為測試集,預測測試集的類別,返回預測后的類別數組。

大多數的scikit-learn估計器接收和輸出的數據格式均為numpy數組或者類似的格式。

下面將介紹使用scikit-learn實現KNN算法,關於KNN算法的一些介紹,可以去參考一下我上一篇的博客

加載數據集

其中,在這里使用的數據集是叫做電離層(Ionosphere)。簡單點來說,通過采集的數據來判斷是否存在自由電子,存在則為"g",不存在則為'b'。下圖是一些數據的展示。


我們目標就是建立分類器,自動判斷數據的好壞。

數據集在這里:GitHub,該數據是CSV格式的。每一行數據有35個值,前34個為采集數據,最后一個表示該數據是否能夠判斷自由電子的存在。

接下來展示數據的導入。

import numpy as np

# 采集數據
x = np.zeros((351,34),dtype = "float")
# 類別數據
y = np.zeros((351),dtype = "byte")

# 數據文件名
file_name = "ionosphere.data"

with open(file_name,"r") as input_file:
    reader = csv.reader(input_file)
    for i,row in enumerate(reader):
        # 只遍歷前34個數據
        datas = [float(data) for data in row[:-1]]
        x[i] = datas
        y[i] = row[-1] == 'g'

此時我們就分別得到了采集的數據和類別數據。接下來就是創建訓練集和測試集。這一步在前面的博客有詳細說明,就不再解釋了。

from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(x,y,random_state = 14)

進行fit訓練

在前面我們說過,我們使用KNN算法實現數據的分類,但是需要我們實現KNN的函數嗎?盡管KNN的實現並不是很難,但是scikit-learn幫我們實現了。

通過下面的代碼,我們導入了一個K近鄰的分類器,其中里面默認$K = 5$,我們可以自己進行調整K的取值。

from sklearn.neighbors import KNeighborsClassifier
estimator = KNeighborsClassifier()

前面我們說過estimator有兩個重要的函數fit和predict,這里就讓我們來進行訓練吧!

estimator.fit(x_train,y_train)

在上面我們通過fit函數導入訓練集和訓練類別就完成了KNN算法的訓練,然后我們就可以進行預測了。其中使用predict函數,使用測試數據集作為參數,返回預測的類別。

y_predict = estimator.predict(x_test)
accuracy = np.mean(y_predict == y_test) * 100
print("精准度{0:.2f}%".format(accuracy))

最后的結果如下圖:


上面的結果與train_test_split的隨機數和測試集的大小有關。

拋開測試集的大小不談,通過改變隨機數,我們發現,精准度會隨着隨機數的改變而發生較大的變化。下圖是隨機種子為2的時候的精准度。


這個時候就會有一個疑問,如果我的運氣不好,隨機分割的訓練集沒有給好,即使再好的分類算法豈不是會得到一個不好的分類結果?對,臉黑的話的確會有這種情況。


那怎么解決呢?如果我們多進行幾次切分,並在在切分訓練集和測試集的時候每一次都與上一次不一樣就好了,並且每一條數據都被用來測試一次就更好了。算法描述如下。

  • 將整個大數據集分為幾個部分。
  • 循環執行以下操作:
    • 將其中一部分作為當前測試集
    • 用剩余部分訓練算法
    • 在當前測試集上測試算法
  • 記錄每次得分,然后得到平均分。
  • 每條數據只在訓練集出現一次。

以上的說法叫做交叉驗證。emm,夢想是好的,現實中更是好的,scikit-learn提供了一些交叉驗證的方法。我們導入即可:

# 進行交叉驗證
from sklearn.model_selection import cross_val_score

cross_val_score默認使用Stratified K Fold方法切分數據集,它大體上保
證切分后得到的子數據集中類別分布相同,以避免某些子數據集出現類別分布失衡的情況。這個默認做法很不錯,現階段就不再把它搞復雜了。

cross_val_score里面傳入估計器,數據集,類別集以及scoring。

這里簡單的說一下scoring的作用。簡單來說他是一個計分器,來決定計分的規則。而傳入accuracy代表預測的結果要完全的匹配測試集中的類別。官方的解釋如下圖。


scores = cross_val_score(estimator, x, y, scoring='accuracy') 
average_accuracy = np.mean(scores) * 100 
print("平均的精准度為{0:.2f}%".format(average_accuracy)) 

最終的結果如下圖:


設置參數

前面我們說過,默認的KNN估計器中$K = 5$,那么他是不是最好的呢?不一定!!K過小時,分類結果容易收到干擾,過大時,又會減弱近鄰的影響。因此我們希望設置一個合理的K,這個時候,我們可以使用一個for循環,遍歷K的取值,來選擇一個比較好的取值。

# 保存每一次的平均分數
avg_scores = []
# 遍歷k的取值從1到25
for i in range(1,25):
    estimator = KNeighborsClassifier(i)
    scores = cross_val_score(estimator, x, y, scoring='accuracy') * 100
    avg_scores.append(np.mean(scores))

為了更加的形象化,選擇使用matplotlib 進行畫圖。

from matplotlib import pyplot as plt
x_len = list(range(1,len(avg_scores)+1))
plt.plot(x_len,avg_scores, '-x',color = "r") 

最后的結果如下圖所示:


總的來說,隨着$K$值的增加,准確度逐漸降低。

預處理

數據挖掘的路不可能一帆風順,很可能我們就被第一波數據集的浪潮給拍死在岸邊上。因為我們的數據集很可能中間的某一些數據有問題,或者說有可能特征的取值千差萬別,也有可能某些特征沒有區分度等等原因。這個時候我們就需要對數據集進行預處理。

選擇具有區分度,創建新的特征,對數據進行清洗……這些都屬於預處理的范疇。在scikit-learn中,預處理工具叫做轉換器(Transformer),它接受原始數據集,返回轉換后的數據集。轉換器主要有以下三個函數:

  • fit():訓練算法,設置內部參數。
  • transform():數據轉換。
  • fit_transform():合並fit和transform兩個方法。

任然以上面的Ionosphere為例子,讓我們來對數據集進行一些破壞,然后進行訓練。

x_broken = np.array(x)
# 對numpy數組進行切片,對於每一列,每隔2個單位除以10
x_broken[:,::2] /= 10

estimator = KNeighborsClassifier()
scores = cross_val_score(estimator, x_broken, y, scoring='accuracy') 
average_accuracy = np.mean(scores) * 100 
print("平均的精准度{0:.2f}%".format(average_accuracy)) 


預處理有很多種方式,其中有一個方式稱之為歸一化,也就是說將特征值規范到01之間,最小值為0,最大值為1,其余值介於01之間。

接下來讓我們對x_broken進行歸一化預處理。在scikit-learn中提供了一系列的預處理器,通過調用預處理器的轉換函數,可以完成數據集的轉換。

  • sklearn.preprocessing.MinMaxScaler:使數據歸一化

  • sklearn.preprocessing.Normalizer:使每條數據各特征值的和為1

  • sklearn.preprocessing.StandardScaler:使各特征的均值為0,方差為1

  • sklearn.preprocessing.Binarizer:使數值型特征的二值化,大於閾值的為1,反之為0

# 歸一化
from sklearn.preprocessing import MinMaxScaler
x_formed = MinMaxScaler().fit_transform(x_broken)

# 然后進行訓練
estimator = KNeighborsClassifier()
scores = cross_val_score(estimator, x_formed, y, scoring='accuracy') 
average_accuracy = np.mean(scores) * 100 
print("平均的精准度{0:.2f}%".format(average_accuracy)) 

歸一化之后的精准度如下圖:


流水線

在上面我們看到,進行數據挖掘有很多步驟需要去做:

  • 預處理數據集
  • 對數據集進行切割
  • 尋找最好的參數
  • ……

而隨着數據集的增加以及精准度的要求,實驗的操作復雜度越來越大,如果中間某一個步驟出現問題,比如說數據轉換錯誤,拉下一個步驟……

流水線結構可以解決這個問題。讓我們來導入它吧:

from sklearn.pipeline import Pipeline

我們可以將Pipeline比喻成生產車間的流水線,原材料按順序一個一個的通過步驟加工,然后才能夠形成一個完整的產品。當然此pipeline非此流水線,但是哲學思想卻是差不多的。

首先,讓我們來看一看Pipeline對象一部分的介紹信:

Pipeline of transforms with a final estimator.

Sequentially apply a list of transforms and a final estimator.Intermediate steps of the pipeline must be transforms, that is, they must implement fit and transform methods.The final estimator only needs to implement fit.The transformers in the pipeline can be cached using memory argument.

The purpose of the pipeline is to assemble several steps that can be cross-validated together while setting different parameters.For this, it enables setting parameters of the various steps using their names and the parameter name separated by a '__', as in the example below. A step's estimator may be replaced entirely by setting the parameter with its name to another estimator, or a transformer removed by setting it to 'passthrough' or None.

簡單點來說,在這條流水線中,前面的步驟是一系列的transform,最后一個步驟必須是estimator。在transform中必須實現fittransform函數,而最后的estimator中必須實現fit函數。具體怎么使用,讓我們來看一看吧。

from sklearn.pipeline import Pipeline

mining_pipe = Pipeline(
    [
       ('預處理',MinMaxScaler()),
        ('估計器',KNeighborsClassifier())
    ],
    verbose=True
)

在pipeline中,步驟使用的是元組來表示:(“名稱”,步驟)。名稱只是一個稱呼,all is ok!后面的步驟則就是estimator或者transfrom。verbose代表是否查看每一個步驟的使用時間。

運行流水線很簡單:

# 傳入pipeline參數
scores = cross_val_score(mining_pipe, x_broken, y, scoring='accuracy') 

average_accuracy = np.mean(scores) * 100 
print("平均的精准度{0:.2f}%".format(average_accuracy)) 

結果如下圖:


時間為0s,這就很尷尬了。估計是數據集太小了,數據還沒反應過來就被處理了。結果是82.90%,與上一步的結果一樣。

結尾

這篇博客主要是介紹scikit-learn的使用流程和使用方法。

GitHub地址:GitHub

參考書籍:Python數據挖掘入門與實踐


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM