什麼是超參數

超參數:算法運行前須要決定的參數

模型參數:算法運行過程當中學習的參數

咱們常說的「調參工程師」調試的基本都是超參數,超參數選擇的好與壞在必定程度上決定了整個算法的好壞。

就拿KNN算法中的超參數K來講,雖然sklearn中對於KNN算法有默認的K=5,但這僅僅是在經驗中獲得的較爲理想的值,在實際應用中卻並不必定是這樣。

咱們就拿KNN算法來對手寫數字數據集進行分類這件事來講,試圖從[1-20]之間找到最合適的那個K

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

digits_df = datasets.load_digits()
X = digits_df.data
y = digits_df.target

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.3, random_state=666)

best_K = 0
best_scope = -1

for k in range(1, 21):
    KNN_cls = KNeighborsClassifier(n_neighbors=k)
    KNN_cls.fit(X_train, y_train)
    temp_scope = KNN_cls.score(X_test, y_test)
    if temp_scope > best_scope:
        best_scope = temp_scope
        best_K = k
print(best_K)
print(best_scope)

最終的運行結果是 best_K = 3 best_scope = 0.9761526232114467 因而可知,算法中封裝的默認超參數並不必定是最好的。

有一個問題咱們須要注意一下: 好比說咱們從[1-20]之間來尋找K,若是最終結果顯示最優K爲20,那麼咱們應該繼續向上拓展,繼續尋找,好比[20-30]

緣由是:一般來說,不一樣的超參數決定不一樣的分類準確率,且這個變化是連續的,若是咱們最終找到的超參數位於邊界,那麼理論上還存有更優解咱們仍未找到。

隱藏的超參數

就拿KNN算法來講,若是咱們的K=3,且恰巧這個待分類樣本週圍最近的3個樣本卻分屬不一樣的三個分類(以下圖所示),那麼就會形成平局。

因此說,只是考慮K這一個超參數是不穩妥的,如上圖所示,雖然待分類的小球最近的3個小球分屬不一樣的分類,可是它離沒一個小球的距離卻不同,咱們能夠認爲,離得近的權重大,離得遠的權重小,這樣就很好的解決了平票的問題。

咱們來看看sklearn框架中封裝的kNN算法類,它有一個參數weights='uniform',這個參數用來控制是否在分類時是否考慮距離的權重(默認uniform是不考慮的,weights='distince'則考慮權重)

下邊咱們考慮權重再來查找一次最優的K

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

digits_df = datasets.load_digits()
X = digits_df.data
y = digits_df.target

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.3, random_state=666)

best_K = 0
best_scope = -1
basic_weights = ''
for i in ['uniform', 'distance']:
    for k in range(1, 21):

        KNN_cls = KNeighborsClassifier(n_neighbors=k, weights=i)
        KNN_cls.fit(X_train, y_train)
        temp_scope = KNN_cls.score(X_test, y_test)
        if temp_scope > best_scope:
            best_scope = temp_scope
            best_K = k
            basic_weights = i
print(best_K)
print(best_scope)
print(basic_weights)

最終結果:best_K = 4 best_scope = 0.9785373608903021 basic_weights = 'distance'

從上邊咱們根據是否考慮距離的權重計算出了更高的分類準確率,在這裏咱們不由想到,不一樣的特徵樣本之間的距離是怎樣算出來的呢?計算距離有多種方法,如歐拉距離、曼哈頓距離,不一樣的方法計算出的距離也不同,這樣就又引出了KNN算法中又一個超參數: KNeighborsClassifier類中的參數p

KNN算法中計算距離使用的是明可夫斯基距離,從上邊的推導式能夠看出,不一樣的o值對應不一樣的公式,當p=1時,計算距離用的就是曼哈頓距離,當p=2時,用的就是歐拉距離,在KNN算法中默認p=2,使用的是歐拉距離。

接下來咱們再根據不一樣的計算距離公式來找出最優的K

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

digits_df = datasets.load_digits()
X = digits_df.data
y = digits_df.target
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.2, random_state=666)

best_K = -1
best_scope = -1
best_p = -1

for i in range(1, 6):
    for k in range(1, 21):

        KNN_cls = KNeighborsClassifier(n_neighbors=k, weights='distance', p=i)
        KNN_cls.fit(X_train, y_train)
        temp_scope = KNN_cls.score(X_test, y_test)
        if temp_scope > best_scope:
            best_scope = temp_scope
            best_K = k
            best_p = i
print(best_K)
print(best_scope)
print(best_p)

咱們看到 best_K=4 best_scope=0.9770514603616134 best_p=4

爲何上邊的代碼中,當考慮參數p的狀況下,weights蠶食必定是distince呢?由於若是weights='uniform',就不會考慮路徑,也就更加不用關心距離計算公式了,也就是說,超參數之間還有可能存在相互依賴的關係。

根據上邊的代碼 咱們能看到,在參數p和參數k兩層循環下,咱們至關於在x軸爲k,y軸爲p的平面中遍歷每個點,來尋找其中的最優解,這中搜索策略其實有一個名字,叫作網格搜索。

上邊幾個例子都是咱們來控制代碼尋找不一樣的超參數,實際上,sklearn爲這種網格搜索方式封裝了一個專門的函數:Grid Search,下邊咱們使用Grid Search來執行一邊函數就找到最優的幾個超參數解。

先來看這個參數,它是一個list格式的參數,裏邊的每個元素都是json類型,仔細看,這每個json元素就是咱們定義的一個針對超參數的搜索策略

param_grid = [
    {
        "weights":["uniform", "distinct"], 
        "n_neighbors":[ i for i in range(1, 20)]
    },
    {
        "weights":["distinct"],
        "n_neighbors":[ i for i in range(1, 20)],
        "p":[ i for i in range(1, 20)]
    }
]

下邊調用Grid Search函數來找出最優解

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

digits_df = datasets.load_digits()
X = digits_df.data
y = digits_df.target
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.4, random_state=666)


from sklearn.model_selection import GridSearchCV  # 這個類用來進行網格搜索

knn_clf = KNeighborsClassifier()  # 實例化KNN類
grid_search = GridSearchCV(knn_clf, param_grid)  # 傳入兩個參數:模型的類,前邊制定的網格搜索策略
grid_search.fit(X_train, y_train)  # 先讓這個類基於咱們設定好的搜索策略從訓練數據種找出最優的參數
grid_search.best_estimator_  # 它用來返回找到的最優超參數

這是運行結果

咱們能夠看到,程序已經幫咱們找到了 最優的n_neighbors=4, p=7, weights='distance',咱們還能夠經過調用grid_search.best_score_方法來看到使用這些超參數後的分類準確度

咱們如何才能獲取到找到的這幾個超參數呢? 使用 grid_search.best_params_ 就能夠獲取到經過網格搜索找到的這幾個超參數

像這幾個屬性:grid_search.best_estimator_

grid_search.best_score_

grid_search.best_params_

上邊這幾個幾個屬性都有一個下劃線(這是一種代碼規則:若是某個參數是根據用戶傳入的參數計算出來的,那麼就會帶個下劃線)

上邊咱們就已經計算出了幾個最優超參數,如今咱們就用最優超參數來計算一下分類準確度吧

使用sklearn爲咱們封裝的GridSearchCV先進行網格搜索尋找最佳超參數,而後使用找到的超參數來計算進行分類並獲得分類準確度:

from sklearn import datasets
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier

digits_df = datasets.load_digits()
X = digits_df.data
y = digits_df.target

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.3, random_state=666)
knn_clf = KNeighborsClassifier()

params_grid = [
    {
        "weights":["uniform", "distance"],
        "n_neighbors":[i for i in range(1, 20)]
    },
    {
        "weights":["distance"],
        "n_neighbors":[i for i in range(1, 20)],
        "p":[j for j in range(1, 6)]
    }
]
grid_search = GridSearchCV(estimator=knn_clf, param_grid=params_grid)
grid_search.fit(X_train, y_train)
knn_clf = grid_search.best_estimator_  # 它返回的就是一個使用了最佳超參數的KNN分類器,能夠直接對測試數據集進行預測
print(knn_clf)
knn_clf.score(X_test, y_test)  # 直接進行預測

這是運算結果

上邊咱們使用了 GridSearchCV 這個類來使用網格搜索來尋找超參數,咱們向這個類中傳遞了兩個參數estimatorparam_grid,estimator標識你要使用的算法類,param_grid標識要使用的網格搜索策略,其實GridSearchCV類還有一些其餘的參數也是比較有用的:

由於咱們使用的網格搜索有多條策略,在尋找最優超參數時就須要一條策略一條策略的去計算,這無疑是比較慢的,n_jobs參數正式爲了改善這一狀況的,n_jobs參數默認爲None,表示只用一個cpu核心去計算,你輸入數字幾,它就用幾核心,當n_jobs=-1時表示使用cpu的所有核心,這樣就加快了計算速度。

並且在進行網格搜索時,默認是沒有任何log打出的,這不利於咱們觀察和監督,verbose=0參數用來控制log的打印長度,數字越大,log打印的越詳細

上邊咱們使用sklearn的GridSearchCV類找到了KNN算法經常使用的超參數,但這不是所有,其實KNN算法還有好多超參數,若是咱們不是用明可夫斯基距離計算樣本之間的距離,還有其餘的一些選擇:

相關文章
相關標籤/搜索