05-06 模型選擇

更新、更全的《機器學習》的更新網站,更有python、go、數據結構與算法、爬蟲、人工智能教學等着你:http://www.javashuo.com/article/p-vozphyqp-cm.htmlpython

模型選擇

機器學習是在某種假設上對數據的分析,基於該假設便可構造多個模型得到預測值,經過比較多個模型間真實值與預測值之間的偏差便可得到一個較優的模型。算法

因爲機器學習不是預言而是預測。所以機器學習可能會出現欠擬合和過擬合的現象,即若是模型擬合效果很差,則是欠擬合,對於欠擬合問題一般增大訓練數據量便可;可是若是模型的擬合效果很好,也並不必定是一件好事,由於訓練集中每每含有噪聲,致使模型可能出現過擬合,過擬合將會是學習機器學習路上的攔路虎之一。數據結構

在將來很長的一段路中首先要考慮的就是模型會不會過擬合,所以本文給出瞭解決過擬合的幾種方法,解決過擬合問題的同時就是在進行模型選擇。dom

除了過擬合問題以外,模型可能還會由於其餘的問題形成模型不優的狀況,一般迴歸問題能夠經過誤差和方差度量模型性能,二分類問題則能夠經過精準度和查全率的考慮或描繪學習曲線ROC來度量模型性能。機器學習

總而言之,總能經過某種工具的應用讓咱們找到一個最優的模型來預測將來新數據。函數

1、模型選擇學習目標

  1. 損失函數和目標函數
  2. 過擬合
  3. 解決過擬合的四種方法
  4. 誤差與方差
  5. 查準率、查全率和F1
  6. ROC和AUC

2、機器學習基本假設

2.1 損失函數

機器學習模型一般分爲機率模型和非機率模型,機率模型由條件機率分佈\(p(\hat{y_i}|x_i)\)表示;非機率模型由決策函數\(\hat{y_i}=f(x_i)\)表示,其中\(\hat{y_i}\)爲模型的輸出值即預測值。工具

一般狀況下使用損失函數度量模型對單個樣本預測的好壞,即度量真實值與預測值之間的偏差,通常使用下面給出的\(4\)種損失函數。性能

給定一個樣例\((x_i,y_i)\),假設模型的預測值爲\(\hat{y_i}\),把損失函數記做\(L(yi,\hat{y_i})\).一般有如下4種損失函數的表現形式。學習

  1. 0-1損失函數:
    \[ L(y_i,\hat{y_i})= \begin{cases} 1, &{y_i\not=\hat{y_i}} \\ 0, &{y_i=\hat{y_i}} \end{cases} \]
  2. 平方損失函數:
    \[ L(y_i,\hat{y_i})=(y_i-\hat{y_i})^2 \]
  3. 絕對值損失函數:
    \[ L(y_i,\hat{y_i})=|y_i-\hat{y_i}| \]
  4. 對數損失函數或對數似然損失函數:
    \[ L(y_i,p(\hat{y_i}|x_i))=-logp(\hat{y_i}|x_i) \]

2.2 目標函數

經過損失函數便可獲得模型的目標函數,目標函數是數據集的平均損失,它表示訓練集的平均訓練偏差,有時候也被稱爲訓練偏差,對於測試集則稱爲測試偏差。

有一個\(n\)個鳶尾花樣本的訓練集\(T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_i,y_i),\cdots,(x_n,y_n)\}\),能夠給出目標函數的公式:
\[ J={\frac{1}{n}}\sum_{i=1}^nL(y_i,\hat{y_i}) \]
當損失函數是0-1損失函數時,目標函數將變成偏差率或準確率,\(I(y_i \not= \hat{yi})\)是指示函數,即\(y_i \not= \hat{yi}\)時爲1,不然爲0。
\[ J_e={\frac{1}{n}} \sum_{i=1}^n I(y_i\not=\hat{y_i}) \text{偏差率}\\ J_a={\frac{1}{n}} \sum_{i=1}^n I(y_i = \hat(y_i)) \text{準確率}\\ J_e + J_a = 1 \]

3、參數模型和非參數模型

3.1 參數模型

參數模型是經過訓練大量的訓練集獲得一個的帶有參數的函數,預測將來新數據時只須要把特徵值輸入函數便可得到預測值。參數模型的典型例子有感知機、邏輯迴歸。

3.2 非參數模型

非參數模型沒法用一組固定的參數來描述,一般狀況下利用數據自己進行訓練。非參數模型的典型例子有決策樹、k-近鄰算法。

4、過擬合

上一節講了經過訓練偏差選擇最優模型,也許已經找到了\(0\)偏差的模型,可是這就是最好的嗎?

事實上\(0\)偏差的模型也許並非最好的,由於模型是經過訓練集獲得的,因爲訓練集可能存在噪聲,所以訓練集並不必定能表明測試集,更不必定能表明將來新數據。雖然這樣的模型可能很好的擬合訓練數據,可是對將來數據可能並無較好的擬合能力,這種現象成爲過擬合。

5、過擬合解決方法

  1. 收集更多訓練數據
  2. 選擇參數較少的簡單模型
  3. 經過正則化引入對複雜性的懲罰
  4. 切割訓練數據集交叉驗證
  5. 減小數據的維度或者刪除掉無心義的特徵(會有單獨的文章介紹數據降維)

5.1 收集更多訓練數據

因爲尚未實戰經驗,你可能沒法想象。

可是你能夠這樣假想當訓練數據多的可以窮盡將來新數據的時候,那麼用該訓練數據獲得的模型去測試將來新數據,那麼對將來新數據的預測必定可以獲得一個很好的預測值;訓練數據過多也會適當減輕噪聲的影響。

5.2 選擇簡單模型

給定一個訓練集\(T=\{(x_1,y_1),(x_2,y_2),\cdots,(x_i,y_i),\cdots,(x_n,y_n)\}\),第\(i\)個樣本\(x_i\)能夠由一個\(m\)次多項式函數獲得一個預測值\(\hat{y_i}\)。假設\(m\)次多項式爲
\[ \hat{y_i}=f(x,\omega)=\omega_1x_i^{(1)}+\omega_2x_i^{(2)}+\cdots+\omega_nx_i^{(n)} \]

5.2.0.1 示例

# 過擬合圖例
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
font = FontProperties(fname='/Library/Fonts/Heiti.ttc')
%matplotlib inline

# 自定義數據並處理數據
data_frame = {'x': [2, 1.5, 3, 3.2, 4.22, 5.2, 6, 6.7],
              'y': [0.5, 3.5, 5.5, 5.2, 5.5, 5.7, 5.5, 6.25]}
df = pd.DataFrame(data_frame)
X, y = df.iloc[:, 0].values.reshape(-1, 1), df.iloc[:, 1].values.reshape(-1, 1)

# 線性迴歸
lr = LinearRegression()
lr.fit(X, y)


def poly_lr(degree):
    """多項式迴歸"""
    poly = PolynomialFeatures(degree=degree)
    X_poly = poly.fit_transform(X)
    lr_poly = LinearRegression()
    lr_poly.fit(X_poly, y)
    y_pred_poly = lr_poly.predict(X_poly)

    return y_pred_poly


def plot_lr():
    """對線性迴歸生成的圖線畫圖"""
    plt.scatter(X, y, c='k', edgecolors='white', s=50)
    plt.plot(X, lr.predict(X), color='r', label='lr')
    # 噪聲
    plt.scatter(2, 0.5, c='r')
    plt.text(2, 0.5, s='$(2,0.5)$')

    plt.xlim(0, 7)
    plt.ylim(0, 8)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()


def plot_poly(degree, color):
    """對多項式迴歸生成的圖線畫圖"""
    plt.scatter(X, y, c='k', edgecolors='white', s=50)
    plt.plot(X, poly_lr(degree), color=color, label='m={}'.format(degree))
    # 噪聲
    plt.scatter(2, 0.5, c='r')
    plt.text(2, 0.5, s='$(2,0.5)$')

    plt.xlim(0, 7)
    plt.ylim(0, 8)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()


def run():
    plt.figure()
    plt.subplot(231)
    plt.title('圖1(線性迴歸)', fontproperties=font, color='r', fontsize=12)
    plot_lr()
    plt.subplot(232)
    plt.title('圖2(一階多項式迴歸)', fontproperties=font, color='r', fontsize=12)
    plot_poly(1, 'orange')
    plt.subplot(233)
    plt.title('圖3(三階多項式迴歸)', fontproperties=font, color='r', fontsize=12)
    plot_poly(3, 'gold')
    plt.subplot(234)
    plt.title('圖4(五階多項式迴歸)', fontproperties=font, color='r', fontsize=12)
    plot_poly(5, 'green')
    plt.subplot(235)
    plt.title('圖5(七階多項式迴歸)', fontproperties=font, color='r', fontsize=12)
    plot_poly(7, 'blue')
    plt.subplot(236)
    plt.title('圖6(十階多項式迴歸)', fontproperties=font, color='r', fontsize=12)
    plot_poly(10, 'violet')
    plt.show()


run()

png

如上圖所示每張圖都有相同分佈的8個樣本點,紅點明顯是一個噪聲點,接下來將講解上述8張圖。暫時不用太關心線性迴歸和多項式迴歸是什麼,這兩個之後你都會學習到,此處引用只是爲了方便舉例。

  • 圖1:線性迴歸擬合樣本點,能夠發現樣本點距離擬合曲線很遠,這個時候通常稱做欠擬合(underfitting)
  • 圖2:一階多項式迴歸擬合樣本點,等同於線性迴歸
  • 圖3:三階多項式迴歸擬合樣本點,表現還不錯
  • 圖4:五階多項式迴歸擬合樣本點,明顯過擬合
  • 圖5:七階多項式迴歸擬合樣本點,已經擬合了全部的樣本點,毋庸置疑的過擬合
  • 圖7:十階多項式迴歸擬合樣本點,擬合樣本點的曲線和七階多項式已經沒有了區別,能夠想象十階以後的曲線也相似於七階多項式的擬合曲線

從上圖能夠看出,過擬合模型將會變得複雜,對於線性迴歸而言,它可能須要更高階的多項式去擬合樣本點,對於其餘機器學習算法,也是如此。這個時候你也能夠想象,過擬合雖然對擬合的樣本點的偏差接近0,可是對於將來新數據而言,若是新數據的\(x=2\),若是使用過擬合的曲線進行擬合新數據,那麼會給出\(y=0.5\)的預測值,也就是說把噪聲的值給了新數據,這樣明顯是不合理的。

5.3 正則化

針對過擬合有時候能夠減小模型的參數,還有一個典型的方法是對目標函數正則化(regularization),即在目標函數上加上一個正則化項(regularizer)或懲罰項(penalty term),即新的目標函數變成
\[ J(\omega)=\frac{1}{m} \sum_{i=1}^m L(y_i,f_{\omega_i}(x_i)) + \lambda(R(f)) \]
其中\(\lambda\geq0\)爲超參數,相似於參數,可是參數能夠經過算法求解,超參數須要人工手動調整;\(\lambda(R(f))\)爲正則化項。

正則化項通常是一個單調遞增的函數,模型越複雜,正則化值越大,懲罰越大。

5.3.1 L1正則化

L1正則化(Lasso)是在目標函數上加上L1正則化項,通常用於特徵選擇,也能夠防止過擬合,即新的目標函數爲
\[ J(\omega) =\frac{1}{m} \sum_{i=1}^m L(y_i,f_{\omega_i}(x_i)) + \lambda||\omega||_1 \]
其中\(||\omega||_1\)爲參數向量\(\omega\)的1範數。

假設樣本有\(n\)特徵,則\(\omega\)\(n\)維向量,1範數爲
\[ ||\omega||_1=\sum_{j=1}^n|\omega_j| \]

5.3.2 L2正則化

L2正則化(Ridge)是在目標函數上加上L2正則化項,通常只用於過擬合,即新的目標函數爲
\[ J(\omega)=\frac{1}{m} \sum_{i=1}^m L(y_i,f_{\omega_i}(x_i)) + \frac{\lambda}{2}||\omega||_2^2 \]
其中\(||\omega||_2^2\)爲參數向量\(\omega\)的2範數的平方。

假設樣本有\(n\)特徵,則\(\omega\)\(n\)維向量,2範數爲
\[ ||\omega||_2=\sqrt{\sum_{j=1}^n{\omega_j}^2} \]

多說一嘴,假設樣本有\(n\)特徵,則\(\omega\)\(n\)維向量,p範數爲
\[ ||\omega||_p=\sqrt[p]{\sum_{j=1}^n{\omega_j}^p} \]

5.4 交叉驗證

對訓練數據集切割作交叉驗證也是防止模型過擬合的一個很好的方法。

通常會把數據按照某種比例分爲訓練集、測試集。訓練集用來訓練模型,把測試集當作將來新樣本的樣本集用來評估模型。而後交叉驗證能夠認爲就是不斷地重複訓練模型、測試模型。

5.4.1 簡單交叉驗證

把數據集按照某種比例,將數據集中的數據隨機的分爲訓練集和測試集。而後不斷的改變模型參數訓練出一組模型,每訓練完一個模型就用測試集測試,最後獲得性能最好的模型。

  1. 初始值\(c=1\)
  2. 訓練模型
  3. 測試模型,\(c+1\)
  4. 若是\(c<11\)改變模型參數,跳轉到步驟1;反之,中止訓練
  5. 從模型集\(\{c_1,c_2,\cdots,c_{10}\}\)中選擇性能最優的模型
# 簡單交叉驗證
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

# 導入鳶尾花數據
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target

# random_state=1能夠確保結果不隨機,stratify=y能夠確保每一個分類的結果都有相同的比例
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=1, stratify=y)

print('不一樣類別全部樣本數量:{}'.format(np.bincount(y)))
print('不一樣類別訓練數據數量:{}'.format(np.bincount(y_train)))
print('不一樣類別測試數據數量:{}'.format(np.bincount(y_test)))
不一樣類別全部樣本數量:[50 50 50]
不一樣類別訓練數據數量:[35 35 35]
不一樣類別測試數據數量:[15 15 15]

5.4.2 k折交叉驗證

模型選擇-10折交叉驗證

將數據隨機的分爲\(k\)個子集(\(k\)的取值範圍通常在\([1-20]\)之間),而後取出\(k-1\)個子集進行訓練,另外一個子集用做測試模型,重複\(k\)次這個過程,獲得最優模型。

  1. 將數據分爲\(k\)個子集
  2. 選擇\(k-1\)個子集訓練模型
  3. 選擇另外一個子集測試模型
  4. 重複2-3步,直至有\(k\)個模型
  5. 選擇\(k\)個模型中性能最優的模型
# k折交叉驗證
import numpy as np
from sklearn import datasets
from sklearn.model_selection import StratifiedKFold

# 導入鳶尾花數據
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target

# n_splits=10至關於k=10
kfold = StratifiedKFold(n_splits=10, random_state=1)
kfold = kfold.split(X, y)

for k, (train_data, test_data) in enumerate(kfold):
    print('迭代次數:{}'.format(k), '訓練數據長度:{}'.format(
        len(train_data)), '測試數據長度:{}'.format(len(test_data)))
迭代次數:0 訓練數據長度:135 測試數據長度:15
迭代次數:1 訓練數據長度:135 測試數據長度:15
迭代次數:2 訓練數據長度:135 測試數據長度:15
迭代次數:3 訓練數據長度:135 測試數據長度:15
迭代次數:4 訓練數據長度:135 測試數據長度:15
迭代次數:5 訓練數據長度:135 測試數據長度:15
迭代次數:6 訓練數據長度:135 測試數據長度:15
迭代次數:7 訓練數據長度:135 測試數據長度:15
迭代次數:8 訓練數據長度:135 測試數據長度:15
迭代次數:9 訓練數據長度:135 測試數據長度:15

5.4.3 留一法交叉驗證

\(k\)折交叉驗證相似,屬於\(k\)折交叉驗證的特例,即一個數據集\(T\)中有\(n\)個數據,當\(k=n-1\)時,\(k\)折交叉驗證即爲留一法交叉驗證。

# 留一法交叉驗證
import numpy as np
from sklearn import datasets
from sklearn.model_selection import LeaveOneOut

# 導入鳶尾花數據
iris_data = datasets.load_iris()
X = iris_data.data[:, [0, 1]]
y = iris_data.target

loo = LeaveOneOut()
loo
LeaveOneOut()
loo.get_n_splits(X)
150
count = 0
for train_index, test_index in loo.split(X):
    if count < 10:
        print("訓練集長度:", len(train_index), "測試集長度:", len(test_index))
    count += 1
    if count == loo.get_n_splits(X)-1:
        print('...\n迭代次數:', count)
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
訓練集長度: 149 測試集長度: 1
...
迭代次數: 149

5.4.4 時間序列分割

時間序列分割通常對時間序列算法作測試,他切割的原理是:測試集的數據和上幾個數據會有必定的聯繫。

from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [2, 4], [3, 2], [2, 4], [1, 2], [3, 2]])
y = np.array([1, 3, 3, 4, 5, 4])
# max_train_size指訓練數據個數,n_splits指切割次數
tscv = TimeSeriesSplit(n_splits=5, max_train_size=3)
tscv
TimeSeriesSplit(max_train_size=3, n_splits=5)
for train_index, test_index in tscv.split(X):
    print("訓練數據索引:", train_index, "測試數索引:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
訓練數據索引: [0] 測試數索引: [1]
訓練數據索引: [0 1] 測試數索引: [2]
訓練數據索引: [0 1 2] 測試數索引: [3]
訓練數據索引: [1 2 3] 測試數索引: [4]
訓練數據索引: [2 3 4] 測試數索引: [5]

5.5 交叉驗證和模型一塊兒使用

若是隻是對交叉驗證有必定的瞭解,那麼問題則是,咱們如何把使用交叉驗證的思想,訓練模型呢?使用for循環嗎?不,咱們可使用sklearn自帶的交叉驗證評分方法。

5.5.1 cross_val_score

交叉驗證中的cross_val_score,即最普通的交叉驗證和模型一塊兒使用的方法,該方法須要指定模型、訓練集數據和評分方法,而後能夠得出每一次測試模型的分數。

from sklearn.metrics import SCORERS

# 可使用的評分方法
SCORERS.keys()
dict_keys(['explained_variance', 'r2', 'neg_median_absolute_error', 'neg_mean_absolute_error', 'neg_mean_squared_error', 'neg_mean_squared_log_error', 'accuracy', 'roc_auc', 'balanced_accuracy', 'average_precision', 'neg_log_loss', 'brier_score_loss', 'adjusted_rand_score', 'homogeneity_score', 'completeness_score', 'v_measure_score', 'mutual_info_score', 'adjusted_mutual_info_score', 'normalized_mutual_info_score', 'fowlkes_mallows_score', 'precision', 'precision_macro', 'precision_micro', 'precision_samples', 'precision_weighted', 'recall', 'recall_macro', 'recall_micro', 'recall_samples', 'recall_weighted', 'f1', 'f1_macro', 'f1_micro', 'f1_samples', 'f1_weighted'])
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data
y = iris.target

clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000)
scores = cross_val_score(clf, X, y, cv=10, scoring='accuracy')
scores
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=1000, multi_class='auto',
          n_jobs=None, penalty='l2', random_state=None, solver='lbfgs',
          tol=0.0001, verbose=0, warm_start=False)
print('準確率:{:.4f}(+/-{:.4f})'.format(scores.mean(), scores.std()*2))

5.5.2 cross_validate

交叉驗證中cross_validate方法,相比較cross_val_score方法能夠指定多個指標,而且cross_validate方法會返回模型fit_time訓練和score_time評分的時間。

from sklearn.model_selection import cross_validate
from sklearn.linear_model import LogisticRegression
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data
y = iris.target

clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000)
cross_validate(clf, X, y, cv=10, scoring=[
    'accuracy', 'recall_weighted'], return_train_score=True)
{'fit_time': array([0.03741813, 0.03649879, 0.0418241 , 0.03404689, 0.02642822,
        0.02773309, 0.02451205, 0.02093697, 0.03865075, 0.06034207]),
 'score_time': array([0.00159192, 0.00155306, 0.00085807, 0.00101495, 0.00089979,
        0.000772  , 0.00068307, 0.00121713, 0.00101519, 0.00123286]),
 'test_accuracy': array([1.        , 0.93333333, 1.        , 1.        , 0.93333333,
        0.93333333, 0.93333333, 1.        , 1.        , 1.        ]),
 'train_accuracy': array([0.97037037, 0.97777778, 0.97037037, 0.97037037, 0.97777778,
        0.97777778, 0.98518519, 0.97037037, 0.97037037, 0.97777778]),
 'test_recall_weighted': array([1.        , 0.93333333, 1.        , 1.        , 0.93333333,
        0.93333333, 0.93333333, 1.        , 1.        , 1.        ]),
 'train_recall_weighted': array([0.97037037, 0.97777778, 0.97037037, 0.97037037, 0.97777778,
        0.97777778, 0.98518519, 0.97037037, 0.97037037, 0.97777778])}

5.5.3 cross_val_predict

交叉驗證中的cross_val_predict方法能夠獲取每一個樣本的預測結果,即每個樣本都會被做爲測試數據。

from sklearn.model_selection import cross_val_predict
from sklearn.linear_model import LogisticRegression
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data
y = iris.target

clf = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=1000)
cross_val_predict(clf, X, y, cv=10)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
from sklearn.metrics import accuracy_score

accuracy_score(y, per_sample)
0.9733333333333334

6、誤差與方差

一般把模型對將來新數據的預測能力稱爲泛化能力,而模型對位置新數據預測的偏差稱爲泛化偏差。誤差則表達了模型的指望預測與真實結果的偏離程度,即模型的擬合能力;方差表達了一樣大小的訓練集變更所致使的學習性能的變化,即數據擾動形成的影響;噪聲表達了指望泛化偏差的下限,即對將來預測新數據的難度。

迴歸任務中泛化偏差經過某種計算能夠分解爲誤差、方差與噪聲之和,如何分解不在本文討論範圍內。噪聲不可避免,所以主要關注誤差與方差,誤差越大,預測值與真實結果的偏離程度越大,模型的擬合能力越弱,泛化偏差越大;方差越大,對數據擾動形成的影響越強,模型的擬合能力越差,泛化偏差越大。如此看來,誤差與方差越小越好,可是誤差和方差一般狀況下是有衝突的,稱爲誤差-方差窘境。

6.1 誤差-方差窘境

誤差-方差窘境:訓練初期,因爲訓練不足,會形成欠擬合,數據擬合效果不好,誤差較大,數據集的擾動也沒法使模型形成較大影響;隨着訓練程度的加深,數據擬合效果很好,即過擬合,誤差變小,可是數據集輕微的擾動都會使模型發生較大的影響,方差偏大。簡而言之,欠擬合狀態,誤差大,方差小;過擬合狀態,誤差小,方差大。

模型選擇-方差與誤差窘境

7、查準率、查全率和F1

二分類問題中根據樣例的真實類別和模型預測類別的組合劃分爲真正例(true positive)、假正例(false positive)、真反例(true negative)、假反例(false negative)四種情形,令TP、FP、TN、FN分別表示對應的樣例數,\(樣例總數 = TP+FP+TN+FN\)

TP——將正類預測爲正類數

FP——將負類預測爲正類數

TN——將負類預測爲負類數

FN——將正類預測爲負類數

7.0.1 準確度

準確度(accuracy_socre)定義爲
\[ P = {\frac{TP+FN}{TP+FP+TN+FN}} = \frac{正確預測的樣本數}{樣本總數} \]

# 查準率示例
from sklearn import datasets
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression

iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target

lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)

y_pred = lr.predict(X)
print('準確度:{:.2f}'.format(
    accuracy_score(y, y_pred)))
準確度:0.97

7.1 查準率

查準率定義爲
\[ P = {\frac{TP}{TP+FP}} = \frac{正確預測爲正類的樣本數}{預測爲正類的樣本總數} \]

# 查準率示例
from sklearn import datasets
from sklearn.metrics import precision_score
from sklearn.linear_model import LogisticRegression

iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target

lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)

y_pred = lr.predict(X)
print('查準率:{:.2f}'.format(
    precision_score(y, y_pred, average='weighted')))
查準率:0.97

7.2 查全率

查全率定義爲
\[ R = {\frac{TP}{TP+FN}} = \frac{正確預測爲正類的樣本數}{正類總樣本數} \]

# 查全率示例
from sklearn.metrics import recall_score
from sklearn import datasets
from sklearn.linear_model import LogisticRegression

iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target

lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)

y_pred = lr.predict(X)
print('查全率:{:.2f}'.format(recall_score(y, y_pred, average='weighted')))
查全率:0.97

7.3 F1值

一般狀況下經過查準率和查全率度量模型的好壞,可是查準率和查全率是一對矛盾的度量工具,查準率高的時候查全率低;查全率高的時候查準率低,所以工業上對不不一樣的問題對查準率和查全率的側重點會有所不一樣。

例如癌症的預測中,正類是健康,反類是患有癌症。較高的查準率可能會致使健康的人被告知患有癌症;較高的查全率可能會致使患有癌症的患者會被告知健康。

\(F_1\)值定義爲
\[ F_1 = {\frac{2*P*R}{P+R}} = {\frac{2*TP}{2TP+FP+FN}} = {\frac{2*TP}{樣例總數+TP-TN}} \]
\(F_\beta\)定義爲:
\[ F_\beta = {\frac{(1+\beta^2)*P*R}{\beta^2*P+R}} \]
\(F_\beta\)是在\(F_1\)值的基礎上加權獲得的,它能夠更好的權衡查準率和查全率。

  1. \(\beta<1\)時,\(P\)的權重減少,即\(R\)查準率更重要
  2. \(\beta=1\)時,\(F_\beta = F_1\)
  3. \(\beta>1\)時,\(P\)的權重增大,即\(P\)查全率更重要。
# F1值示例
from sklearn import datasets
from sklearn.metrics import f1_score
from sklearn.linear_model import LogisticRegression

iris_data = datasets.load_iris()
X = iris_data.data
y = iris_data.target

lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)

y_pred = lr.predict(X)
print('F1值:{:.2f}'.format(f1_score(y, y_pred, average='weighted')))
F1值:0.97

7.4 ROC曲線

下圖即ROC曲線的圖形,對於如下三條ROC曲線,通常狀況能夠看過\((0,0)\)的虛線與ROC曲線的交點,若是交點越靠外面,則模型性能越好。可是通常狀況是經過判斷ROC曲線圍成的面積AUC面積來判斷模型的性能。

模型選擇-ROC曲線和AUC面積

一般狀況下也會使用ROC(receiver operating characteristic,ROC)曲線度量模型性能的好壞,ROC曲線顧名思義是一條曲線,它的橫軸是假正例率(false positive rate,FPR),縱軸是真正例率(true positive rate,TPR),假正例率和真正例率分別定義爲:
\[ FPR = {\frac{FP}{TN+FP}} \text{假正例率} \\ TPR = {\frac{TP}{TP+FN}} \text{真正例率} \]

# ROC示例
from sklearn import datasets
from sklearn.metrics import roc_curve
from sklearn.linear_model import LogisticRegression

iris_data = datasets.load_iris()
X = iris_data.data[0:100, :]
y = iris_data.target[0:100]

lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)

y_pred = lr.predict(X)
fpr, tpr, thresholds = roc_curve(y, y_pred)
plt.xlabel('FPR', fontsize=15)
plt.ylabel('TPR', fontsize=15)
plt.title('FPR-TPR', fontsize=20)
plt.plot(fpr, tpr) 
plt.show()

png

7.5 AUC面積

因爲ROC曲線有時候沒法精準度量模型的好壞,所以會使用ROC曲線關於橫縱軸圍成的面積稱爲AUC(area under ROC curve,AUC)來度量模型的好壞,AUC值越大的模型,則模型越優。

# AUC示例
from sklearn import datasets
from sklearn.metrics import roc_auc_score
from sklearn.linear_model import LogisticRegression

iris_data = datasets.load_iris()
X = iris_data.data[0:100, :]
y = iris_data.target[0:100]

lr = LogisticRegression(solver='lbfgs', multi_class='auto', max_iter=200)
lr = lr.fit(X, y)

y_pred = lr.predict(X)
# 計算AUC值
print('AUC值:{:.2f}'.format(roc_auc_score(y, y_pred, average='weighted')))
AUC值:1.00

8、小結

在訓練模型的時候,總會有各類各樣的緣由致使咱們的模型可能有一些較大的偏差。又因爲偏差是沒法避免的,只能減少,所以咱們須要懂得如何去面對並解決這些偏差,而不能讓偏差致使咱們在構造模型以前的努力功虧一簣。

本文介紹了許多種針對偏差度量與解決的工具,也但願你在將來的探索途中可以找到適合你面對的問題的最優的模型,可是前提是你得須要有模型。既然你已經得到了通往機器學習世界大門的鑰匙,下一篇將帶你深刻機器學習的世界尋找適合你的最優模型,即學習第一個機器學習算法——感知機。

相關文章
相關標籤/搜索