機器學習可視化:模型評估和參數調優

hldirdsdjs 8年前發布 | 25K 次閱讀 數據挖掘 機器學習

本篇文章詳細闡述機器學習模型評估和參數調優。將主要圍繞兩個問題來闡述:

  1. “知其所以然”:當你選擇的一個機器學習模型運行時,你要知道它是如何工作的;

  2. “青出于藍”:更進一步,你得知道如何讓此機器學習模型工作的更優。

模型評估的方法

一般情況來說,F1評分或者R平方(R-Squared value)等數值評分可以告訴我們訓練的機器學習模型的好壞。也有其它許多度量方式來評估擬合模型。

你應該猜出來,我將提出使用可視化的方法結合數值評分來更直觀的評判機器學習模型。接下來的幾個部分將分享一些有用的工具。

首先想聲明的,單單一個評分或者一條線,是無法完全評估一個機器學習模型。偏離真實場景來評估機器學習模型('good' or 'bad')都是“耍流氓”。某個機器學習模型若可“駕馭”小樣本數據集生成最多預測模型(即,命中更多預測數據集)。如果一個擬合模型比其它擬合過的模型形式或者你昨天的預測模型能夠得到更好的結果,那即是好('good')。

下面是一些標準指標: confusion_matrix , mean_squared_error , r2_score ,這些可以用來評判分類器或者回歸的好壞。表格中給出的是 Scikit-Learn 中的函數以及描述:

評估分類模型 :

指標 描述 Scikit-learn函數
Precision 精準度 from sklearn.metrics import precision_score
Recall 召回率 from sklearn.metrics import recall_score
F1 F1值 from sklearn.metrics import f1_score
Confusion Matrix 混淆矩陣 from sklearn.metrics import confusion_matrix
ROC ROC曲線 from sklearn.metrics import roc
AUC ROC曲線下的面積 from sklearn.metrics import auc

評估回歸模型 :

指標 描述 Scikit-learn函數
Mean Square Error (MSE, RMSE) 平均方差 from sklearn.metrics import mean_squared_error
Absolute Error (MAE, RAE) 絕對誤差 from sklearn.metrics import mean_absolute_error, median_absolute_error
R-Squared R平方值 from sklearn.metrics import r2_score

下面開始使用 Scikit-Learn 的可視化工具來更直觀的展現模型的好壞。

評估分類模型

我們評估分類器是判斷預測值時否很好的與實際標記值相匹配。正確的鑒別出正樣本(True Positives)或者負樣本(True Negatives)都是True。同理,錯誤的判斷正樣本(False Positive,即一類錯誤)或者負樣本(False Negative,即二類錯誤)。

注意:True和False是對于評價預測結果而言,也就是評價預測結果是正確的(True)還是錯誤的(False)。而Positive和Negative則是樣本分類的標記。

通常,我們希望通過一些參數來告知模型評估如何。為此,我們使用混淆矩陣。

混淆矩陣

幸運的是, Scikit-Learn 提供內建函數( sklearn.metrics.confusion_matrix )來計算混淆矩陣。輸入數據集實際值和模型預測值作為參數,輸出即為混淆矩陣,結果類似這樣:

[[1238   19]   # True Positives = 1238, False Negatives = 19
 [   2  370]]  # False Positives = 2, True Negatives = 370

分類報告

分類報告除了包括混淆矩陣,也增加了其它優勢,比如,混淆矩陣會標示樣例是否被正確鑒別,同時也提供precision,recall和 F1 值三種評估指標。

from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred, target_names=target_names))

更進一步,可以對 Scikit-Learn 的內建函數做些加強,比如,使用帶顏色區分的熱力圖,它將幫助我們的眼睛更容易的辨別預測成功(橘黃色)和失敗(灰色)。代碼如下:

 from matplotlib import colors
from matplotlib.colors import ListedColormap
ddl_heat = ['#DBDBDB','#DCD5CC','#DCCEBE','#DDC8AF','#DEC2A0','#DEBB91',\
            '#DFB583','#DFAE74','#E0A865','#E1A256','#E19B48','#E29539']
ddlheatmap = colors.ListedColormap(ddl_heat)
def plot_classification_report(cr, title=None, cmap=ddlheatmap):
    title = title or 'Classification report'
    lines = cr.split('\n')
    classes = []
    matrix = []
    for line in lines[2:(len(lines)-3)]:
        s = line.split()
        classes.append(s[0])
        value = [float(x) for x in s[1: len(s) - 1]]
        matrix.append(value)
    fig, ax = plt.subplots(1)
    for column in range(len(matrix)+1):
        for row in range(len(classes)):
            txt = matrix[row][column]
            ax.text(column,row,matrix[row][column],va='center',ha='center')
    fig = plt.imshow(matrix, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    x_tick_marks = np.arange(len(classes)+1)
    y_tick_marks = np.arange(len(classes))
    plt.xticks(x_tick_marks, ['precision', 'recall', 'f1-score'], rotation=45)
    plt.yticks(y_tick_marks, classes)
    plt.ylabel('Classes')
    plt.xlabel('Measures')
    plt.show()
cr = classification_report(y_true, y_pred)
plot_classification_report(cr)

看起來挺容易,對不?發現分類熱力圖的另外一個好處,它可以讓我們看出一類錯誤 VS 二類錯誤。但有一個缺陷,它并不能垮模型進行比較,而這對評估擬合模型是相當重要的。因為這個原因,接下來將使用第二篇文章中的 classifyregress 代碼。下面的 get_preds 函數將輸出一個實際標記值和預測值的二元組,這個二元組將會使得后續的跨模型的可視化比較變得容易:

def get_preds(attributes, targets, model):
    '''
    Executes classification or regression using the specified model
    and returns expected and predicted values.
    Useful for comparison plotting!
    '''
    splits = cv.train_test_split(attributes, targets, test_size=0.2)
    X_train, X_test, y_train, y_test = splits
    model.fit(X_train, y_train)
    y_true = y_test
    y_pred = model.predict(X_test)
    return (y_true,y_pred)
 

ROC曲線

另一種評估分類模型的方法是ROC(Receiver Operating Characteristic)曲線。我們能從 Scikit-Learn 指標模塊中import roc_curve ,計算 true positive率和false positive 率的數值。我們也可以畫出ROC曲線來權衡模型的敏感性和特異性。下面的代碼將畫出ROC,Y軸代表true positive率,X軸代表false positive 率。同時,我們也可以增加同時比較兩種不同的擬合模型,這里看到的是 KNeighborsClassifier 分類器遠勝 LinearSVC 分類器:

 def roc_compare_two(y, yhats, models):
    f, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
    for yhat, m, ax in ((yhats[0], models[0], ax1), (yhats[1], models[1], ax2)):
        false_positive_rate, true_positive_rate, thresholds = roc_curve(y,yhat)
        roc_auc = auc(false_positive_rate, true_positive_rate)
        ax.set_title('ROC for %s' % m)
        ax.plot(false_positive_rate, true_positive_rate, \
                c='#2B94E9', label='AUC = %0.2f'% roc_auc)
        ax.legend(loc='lower right')
        ax.plot([0,1],[0,1],'m--',c='#666666')
    plt.xlim([0,1])
    plt.ylim([0,1.1])
    plt.show()
y_true_svc, y_pred_svc = get_preds(stdfeatures, labels, LinearSVC())
y_true_knn, y_pred_knn = get_preds(stdfeatures, labels, KNeighborsClassifier())
actuals = np.array([y_true_svc,y_true_knn])
predictions = np.array([y_pred_svc,y_pred_knn])
models = ['LinearSVC','KNeighborsClassifier']
roc_compare_two(actuals, predictions, models)
 

在ROC空間,ROC曲線越凸向左上方向效果越好;越靠近對角線,分類器越趨向于隨機分類器。

同時,我們也會計算曲線下的面積(AUC),可以結合上圖。如果AUC的值達到0.80,那說明分類器分類非常準確;如果AUC值在0.60~0.80之間,那分類器還算好,但是我們調調參數可能會得到更好的性能;如果AUC值小于0.60,那就慘不忍睹了,你得好好分析下咯。

評估回歸模型 對于混凝土數據集試驗一些不同的機器學習模型,然后評判哪種更好。在第二篇文章中,我們使用的平均方差和 R 平方值,比如:

Mean squared error = 116.268
R2 score = 0.606

這些數值是有用的,特別是對不同的擬合模型比較平均方差和 R 平方值。但是,這是不夠的,它不能告訴我們為什么一個模型遠勝于另外一個;也不能告訴我們如何對模型調參數提高評分。接下來,我們將看到兩種可視化的評估技術來幫助診斷模型有效性:預測錯誤曲線 和 殘差曲線。

預測錯誤曲線

為了知道我們的模型預測值與期望值到底有多接近,我們將拿混凝土數據集(混凝土強度)做例子,畫出其期望值和模型預測值曲線。下面是不同回歸模型的錯誤曲線: Ridge , SVR 和 RANSACRegressor 。

 def error_compare_three(mods,X,y):
    f, (ax1, ax2, ax3) = plt.subplots(3, sharex=True, sharey=True)
    for mod, ax in ((mods[0], ax1),(mods[1], ax2),(mods[2], ax3)):
        predicted = cv.cross_val_predict(mod[0], X, y, cv=12)
        ax.scatter(y, predicted, c='#F2BE2C')
        ax.set_title('Prediction Error for %s' % mod[1])
        ax.plot([y.min(), y.max()], [y.min(), y.max()], 'k--', lw=4, c='#2B94E9')
        ax.set_ylabel('Predicted')
    plt.xlabel('Measured')
    plt.show()
models = np.array([(Ridge(),'Ridge'), (SVR(),'SVR'), (RANSACRegressor(),'RANSAC')])
error_compare_three(models, features, labels)

從這里可以很清晰的看出預測值和期望值的關系。同時也發現線性回歸模型效果好。

殘差曲線 殘差是數據集每個實例的實際標記值和預測值之間的差值。通過畫出一系列實例的殘差,可以幫助我們檢測它們是否隨機錯誤。

def resids_compare_three(mods,X,y):
    f, (ax1, ax2, ax3) = plt.subplots(3, sharex=True, sharey=True)
    plt.title('Plotting residuals using training (blue) and test (green) data')
    for m, ax in ((mods[0], ax1),(mods[1], ax2),(mods[2], ax3)):
        for feature in list(X):
            splits = cv.train_test_split(X[[feature]], y, test_size=0.2)
            X_tn, X_tt, y_tn, y_tt = splits
            m[0].fit(X_tn, y_tn)
            ax.scatter(m[0].predict(X_tn),m[0].predict(X_tn)-y_tn,c='#2B94E9',s=40,alpha=0.5)
            ax.scatter(m[0].predict(X_tt), m[0].predict(X_tt)-y_tt,c='#94BA65',s=40)
        ax.hlines(y=0, xmin=0, xmax=100)
        ax.set_title(m[1])
        ax.set_ylabel('Residuals')
    plt.xlim([20,70])        # Adjust according to your dataset
    plt.ylim([-50,50])  
    plt.show()
models = np.array([(Ridge(),'Ridge'), (LinearRegression(),'Linear Regression'), (SVR(),'SVR')])
resids_compare_three(models, features, labels)

Bias VS Variance

每種評估器都有是有利有弊。

首先 Error = Bias + Variance。Error反映的是整個模型的準確度,Bias反映的是模型在樣本上的輸出與真實值之間的誤差,即模型本身的精準度,Variance反映的是模型每一次輸出結果與模型輸出期望之間的誤差,即模型的穩定性。

機器學習可視化調參

在文章開篇,我們提出了兩個問題:我們如何知道一個機器學習模型可以工作?我們如何讓這個模型工作(運行)的更好?

接下來,我們將回答第二個問題。如果你有注意,我們用的模型都是使用 Scikit-Learn 默認的參數。對于我們的大部分擬合模型來講,評分已經相當好了。但有時并沒有那么幸運,這時我們就得自己調參數。

可視化訓練和驗證模型

如何選擇最好的模型參數呢?一種方法是,用單一參數的不同值去驗證一個模型的評估分數。讓我們拿 SVC 分類器來試驗,通過調不同的gama值來畫出訓練值和測試紙的曲線。

我們的關注點是訓練值和測試值都高的點。如果兩者都低,那是欠擬合(underfit);如果訓練值高但是測試值低,那說明是過擬合(overfit)。下面的代碼畫出來的曲線是拿信用卡數據集來做例子,這里用的 6折交叉驗證。

 def plot_val_curve(features, labels, model):
    p_range = np.logspace(-5, 5, 5)
    train_scores, test_scores = validation_curve(
        model, features, labels, param_name='gamma', param_range=p_range,
        cv=6, scoring='accuracy', n_jobs=1
    )
    train_scores_mean = np.mean(train_scores, axis=1)
    train_scores_std = np.std(train_scores, axis=1)
    test_scores_mean = np.mean(test_scores, axis=1)
    test_scores_std = np.std(test_scores, axis=1)
    plt.title('Validation Curve')
    plt.xlabel('$\gamma$')
    plt.ylabel('Score')
    plt.semilogx(p_range, train_scores_mean, label='Training score', color='#E29539')
    plt.semilogx(p_range, test_scores_mean, label='Cross-validation score', color='#94BA65')
    plt.legend(loc='best')
    plt.show()
X = scale(credit[['limit','sex','edu','married','age','apr_delay']])
y = credit['default']
plot_val_curve(X, y, SVC())

Grid Search

對于超參數調優,大部分人使用的grid search。Grid search是一種暴力調參方法,即遍歷所有可能的參數值。對于信用卡數據集使用 SVC 模型,我們通過試驗不同內核系數gama來提高預測準確性:

from sklearn.grid_search import GridSearchCV
def blind_gridsearch(model, X, y):
    C_range = np.logspace(-2, 10, 5)
    gamma_range = np.logspace(-5, 5, 5)
    param_grid = dict(gamma=gamma_range, C=C_range)
    grid = GridSearchCV(SVC(), param_grid=param_grid)
    grid.fit(X, y)
    print(
        'The best parameters are {} with a score of {:0.2f}.'.format(
            grid.best_params_, grid.best_score_
        )
    )
features = credit[['limit','sex','edu','married','age','apr_delay']]
labels   = credit['default']
blind_gridsearch(SVC(), features, labels)

但是,grid search需要我們理解哪些參數是合適的,參數的意義,參數是如何影響模型的以及參數的合理的搜索范圍來初始化搜索。這里,我們使用 visual_gridsearch 代替 blind_gridsearch 函數:

 def visual_gridsearch(model, X, y):
    C_range = np.logspace(-2, 10, 5)
    gamma_range = np.logspace(-5, 5, 5)
    param_grid = dict(gamma=gamma_range, C=C_range)
    grid = GridSearchCV(SVC(), param_grid=param_grid)
    grid.fit(X, y)
    scores = [x[1] for x in grid.grid_scores_]
    scores = np.array(scores).reshape(len(C_range), len(gamma_range))
    plt.figure(figsize=(8, 6))
    plt.subplots_adjust(left=.2, right=0.95, bottom=0.15, top=0.95)
    plt.imshow(scores, interpolation='nearest', cmap=ddlheatmap)
    plt.xlabel('gamma')
    plt.ylabel('C')
    plt.colorbar()
    plt.xticks(np.arange(len(gamma_range)), gamma_range, rotation=45)
    plt.yticks(np.arange(len(C_range)), C_range)
    plt.title(
        "The best parameters are {} with a score of {:0.2f}.".format(
        grid.best_params_, grid.best_score_)
    )
    plt.show()
visual_gridsearch(SVC(), features, labels)

visual_gridsearch 的方法可以幫助我們理解不同的模型參數下的精確值。但是超參數調優的路程很長,好些人為此研究了幾十年。

結論

這是可視化機器學習部分的最后一篇,可視化在機器學習的過程占用重要的角色。許多工具都提供這個功能,比如,  Scikit-Learn , Matplotlib , Pandas , Bokeh 和 Seaborn 。
如果有表格排版不對的地方,請直接打開“閱讀原文”查看,謝謝。

 

 

 

 

來自:http://mp.weixin.qq.com/s?__biz=MzI0MDIxMDM0MQ==&mid=2247483752&idx=1&sn=0d8a84ba088565ee1d53379c971f2f24

 

 本文由用戶 hldirdsdjs 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!