[數據分析] 踏上 Kaggle 之路 – 使用 kNN 預測 Digit Recognizer

Photo by Robby McCullough on Unsplash

因某次誤打誤撞闖入成大數據分析的圈子,在完全沒有底子的狀態下,完全無法吸收內容,只是簡單吸收了一些 KeyWord,當時聽到的包含 Kaggle、CNN,當下聽到 CNN 還以為是管媒體的那個單位,經過簡單的講解也才初步了解演算法,以及演算法目的,不過今天文章要寫的無關 CNN,而是分群演算法,以及預測 Kaggle 上的一到題目 Digit Recognizer。

前言

由於網路上已經有太多 Kaggle 的介紹文了,所以本篇僅會簡單帶過,並且使用前幾篇起手式的概念完成 Kaggle 上的一到題目 Digit Recognizer。

[Python] 跌入數據分析的坑 – 談談機器學習入門 (三)
[Python] 跌入數據分析的坑 – 談談起手式 Pandas (二)
[Python] 跌入數據分析的坑 – 談談起手式 NumPy (一)

Kaggle

Kaggle 就如同 ITSA、ACM 等等的程式競賽一般,但 Kaggle 是專門針對數據建模和數據分析的競賽平台,從練習的題目、競賽,到企業徵才都有相當的規模,新手也可以在 Kaggle 討論區上,透過閱讀別人分享的分析方法及經驗學習。

Kaggle

Digit Recognizer

Digit Recognizer 是一到圖形識別的題目,以 MNIST 手寫資料集抽樣改編後做為訓練、測試資料,透過演算法對訓練資料機器學習之後,並且預測測試資料的真實數字為何。

Kaggle Digit Recognizer

kNN 近鄰演算法

kNN 全名為 K Nearest Neighbor,關於 kNN 可以參考文章「kNN算法的原理與實現」,內文寫得很清楚,雖然文中有實作 Knn 演算法,但本篇文章所採用的是 Scikit-learn 套件中的 neighbors.KNeighborsClassifier 來實作 kNN。

開始預測

題目 Digit Recognizer 提供三個檔案,分別為提交範例資料:「sample_submission.csv」、測試資料:「test.csv」、訓練資料:「train.csv」。

起手式

如同之前的起手式文章一樣,先把必要的函式庫讀取進來。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

載入演算法套件

關於 train_test_split 在先前起手式系列文章中已經有介紹過了,另外的 KNeighborsClassifier 也就是本次要用來預測的演算法 kNN 套件。

from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

讀取相關資料

接著要將訓練資料、測試資料一一讀進來。

train = pd.read_csv("train.csv")
test  = pd.read_csv("test.csv")

透過 shape 可以看出 train 有 42000 列、785 欄,其中第一欄為所有訓練資料集的對應數字,也就是訓練資料的答案。

資料前處理

上頭讀取資料已經說明,第一欄所代表的資料為訓練資料的對應數字,所以要先將其分割出來,並且將測試資料從 DataFrame 轉為 numpy.Array

O_X_train = train.values[0:,1:]
O_y_train = train.values[0:,0]
test = test.values

為了讓預測結果可以更接近現實,所以將資料在各別複製一份出來,並且做 Nomalizing,非 0 即 1 的處理。

import copy
nomalizing_X_train = copy.deepcopy(O_X_train)
nomalizing_y_train = copy.deepcopy(O_y_train)
nomalizing_X_train[nomalizing_X_train > 0] = 1
nomalizing_y_train[nomalizing_y_train > 0] = 1

效果可以參考下圖。

接著將 Nomalizing 的資料與原本的訓練資料集做整合,使用 concatencate 方法,要注意到的是僅使用到第一個 argv,所以使用小括號把第一個 argv 框住。

O_X_train = np.concatenate((O_X_train, nomalizing_X_train))
O_y_train = np.concatenate((O_y_train, nomalizing_y_train))

之後就是從訓練資料測試資料中,在切分成「訓練資料:X_train」、「訓練資料答案:y_train」、「測試資料:X_test」、「測試資料答案:y_test」,train_test_split 的相關用法也有在前幾篇起手式的文中提過,本次取樣比為 8:2

X_train, X_test, y_train, y_test = train_test_split(O_X_train,
                                                    O_y_train,
                                                    test_size = 0.2,
                                                    random_state = 87)

使用 kNN 演算法預測

在使用 kNN 預測之前,要先得知道最佳的 k 值為何,由於預測會花費大部分的時間,可以透過迴圈,並藉由 accuracy_score 方法,來計算預測的分數,並且建構一個計時器,來計算預測一次 k 值的時間。

from sklearn.metrics import accuracy_score
import time

解釋以下程式碼,首先宣告 k_score 來紀錄每一次計算結果的成績,test_range 為測試的範圍,由於全部資料有 48000 筆,抽樣 8:2 之後,也有 33600 筆資料,如果要全測的話,會相當耗時,所以將 test_range 調整為 10000,接著計算 1 到 6 的 k 值。

k_score = []
test_range = 10000
for k in range(1, 6 + 1):
    print("k = {} Training.".format(k))
    start = time.time()
    knn = KNeighborsClassifier(n_neighbors = k)
    knn.fit(X_train, y_train)
    predict_result = knn.predict(X_test[:test_range])
    predict_score = accuracy_score(y_test[:test_range], predict_result)
    k_score.append(predict_score)
    end = time.time()
    print("Score: {}.".format(predict_score))
    print("Complete time: {} Secs.".format(end - start))
print(k_score)

以下為輸出結果:

k = 1 Training.
    Score: 0.9803.
    Complete time: 911.3254759311676 Secs.
k = 2 Training.
    Score: 0.9779.
    Complete time: 908.3969151973724 Secs.
k = 3 Training.
    Score: 0.9814.
    Complete time: 911.6351885795593 Secs.
k = 4 Training.
    Score: 0.9803.
    Complete time: 910.7894451618195 Secs.
k = 5 Training.
    Score: 0.9807.
    Complete time: 908.5058484077454 Secs.
k = 6 Training.
    Score: 0.9805.
    Complete time: 910.5136153697968 Secs.
[0.9803, 0.9779, 0.9814, 0.9803, 0.9807, 0.9805]

也可以將,輸出的結果以 plt 繪製出來,程式碼如下:

print (k_score)
plt.plot(range(1, 6 + 1), k_score)
plt.xlabel('k')
plt.ylabel('k_score')
plt.show()

觀察輸出的圖形,可以筆單純看數字結果來得清楚,可以清楚得知 k = 3 的時候分數最高。

所以採用 k = 3 來當作預測基準,並用來預測 test.csv 的資料,首先將所有測試資料訓練資料(包括 Nomalizing 的資料),程式碼如下:

k = 3
print("k = {} Training.".format(k))
start = time.time()
knn = KNeighborsClassifier(n_neighbors = k)
knn.fit(O_X_train, O_y_train)
predict_result = knn.predict(test)
end = time.time()
print("Complete time: {} Secs.".format(end - start))

輸出訓練時間為 1602.5 Secs 相當於 26 分鐘:

k = 3 Training.
Complete time: 1602.5110640525818 Secs.

要將預測結果資料打包成範例 sample_submission.csv 的形式,可以藉由 pandas 套件來處理,首先有 ImageIdLabel 兩個欄位,ImageId 列的值為測試資料的總數 28000,Label 則存放我們的預測結果,程式碼如下:

pd.DataFrame(
             {
              "ImageId": list(range(1,len(predict_result)+1)),
              "Label": predict_result
             }
).to_csv('Digit_Recogniser_Result_Double.csv', index=False, header=True)

將答案送出之後,預測結果為 0.96814,。

由於會顯示 Your submission scored 0.21514, which is not an improvement of your best score. Keep trying!是因為站長最後一次送的時候,是將 test 的資料也做 Nomalizing,顯然效果不彰,但 0.96814 的結果仍為本次文章中的預測方法

結論

第一次參與這種競賽,拿到預測率高達九成六的成績也已經心滿意足了,至於要如何做資料的前處理、採用什麼演算法,多多少少都會影響預測的結果,這就是此領域的學習精華,關於該題目 Digit Recognizer,討論區有不少人直接使用 MNIST 的資料集來進行訓練,也得到了很高,甚至預測精準度 1.0 的結果,但有違該題目的精神。

如果有網友們剛踏入此領域,希望這篇文章能夠幫助到你們,同時也歡迎各位一同討論、學習,互相成長。


發佈留言

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料