1. 程式人生 > >機器學習--支援向量機(六)徑向基核函式(RBF)詳解

機器學習--支援向量機(六)徑向基核函式(RBF)詳解

前面講解了什麼是核函式,以及有效核函式的要求,到這裡基本上就結束了,很多部落格也是如此,但是呢這些只是理解支援向量機的原理,如何使用它講解的卻很少,尤其是如何選擇核函式更沒有人講,不講也是有原因的,因為核函式的選擇沒有統一的定論,這需要使用人根據不同場合或者不同問題選擇核函式,選擇的標準也沒有好的指導方法,一般都是嘗試使用,所以選擇核函式就需要看使用者的經驗了,研究者們也在一直研究這種方法,這方面的研究稱為核工程,因為核函式不僅僅使用在支援向量機中,只要滿足多維資料的內積即可使用核函式進行解決,因此核函式可以和很多演算法結合,能產生意想不到的效果,,但是核函式因為太過龐大,我在這裡只是引導的講解一下,以後有時間專門開一個欄目專講核函式的選擇,在這裡只簡單的講解一下核函式,同時深入講解徑向基核函式,通過講解這一個核函式,希望大家可以通過這種方法去學習如何選擇核函式,當然最後我會把sklearn的核函式的api介面也講解一下,雖然還沒使用過,但是當理解徑向基核函式後,還是可以很容易使用的,好,廢話不多說了,下面就開始進入正題:

在講徑向基核函式之前,先給大家講解一下,核函式構造方法:

為了利⽤核替換,我們需要能夠構造合法的核函式。⼀種⽅法是選擇⼀個特徵空間對映 ϕ(x) ,然後使⽤這個對映尋找對應的核,這⾥,⼀維空間的核函式被定義為:

其中 ϕ i (x) 是基函式。

上圖從對應的基函式集合構建核函式的例⼦。在每⼀列中,下圖給出了由上面公式定義的核函式 k(x,x ′ ) ,它是 x 的函式, x ′ 的值⽤紅⾊叉號表⽰,⽽上圖給出了對應的基函式,分別是多項式基函式(左列)、⾼斯基函式(中列)、 logistic sigmoid 基函式(右列)。 

另⼀種⽅法是直接構造核函式。在這種情況下,我們必須確保我們核函式是合法的,即它對應於某個(可能是⽆窮維)特徵空間的標量積。作為⼀個簡單的例⼦,考慮下⾯的核函式:

 如果我們取⼆維輸⼊空間 x = (x 1 ,x 2 ) 的特殊情況,那麼我們可以展開這⼀項,於是得到對應的⾮線性特徵對映:

我們看到特徵對映的形式為  ,因此這個特徵對映由所有的⼆階項組成,每個⼆階項有⼀個具體的係數。但是,更⼀般地,我們需要找到⼀種更簡單的⽅法檢驗⼀個函式是否是⼀個合法的核函式,⽽不需要顯⽰地建構函式 ϕ(x) 。核函式 k(x,x ′ ) 是⼀個合法的核函式的充分必要條件是 Gram 矩陣(元素由 k(x n ,x m ) 給出)在所有的集合 {x n } 的選擇下都是半正定的。注意,⼀個半正定的矩陣與元素全部⾮負的矩陣不同。
構造新的核函式的⼀個強⼤的⽅法是使⽤簡單的核函式作為基本的模組來構造。可以使⽤下⾯的性質來完成這件事。
給定合法的核 k 1 (x,x ′ ) 和 k 2 (x,x ′ ) ,下⾯的新核也是合法的 :

通過上面核函式的性質就可以構造出不同的核函數了,但是最好還是驗證一下是否是有效的核函式 ,即核矩陣是對稱的半正定矩陣,下面簡單證明一下徑向基核函式是有效的,然後再詳解徑向基核函式的來源和應用:

這個經常被稱為⾼斯核。但是注意,在我們現在的討論中,它不表⽰概率密度,因此歸⼀化係數被省略了。這是⼀個合法的核,理由如下。我們把平⽅項展開 :

從而:

 通過上面的性質以及線性核 的合法性,即可看到⾼斯核是⼀個合法的核。注意,對應於⾼斯核的特徵向量有⽆窮的維數。那麼你可以在此基礎繼續變化構造適合自己的核函式:

⾼斯核並不侷限於使⽤歐⼏⾥得距離。如果我們使⽤公式|x-x‘|中的核替換,將 x T x ′ 替換為⼀個⾮線性核 κ(x,x ′ ) ,我們有

 核觀點的⼀個重要的貢獻是可以擴充套件到符號化的輸⼊,⽽不是簡單的實數向量。核函式可以定義在多種物件上,例如圖⽚、集合、字串、⽂本⽂檔。例如,考慮⼀個固定的集合,定義⼀個⾮向量空間,這個空間由這個集合的所有可能的⼦集構成。如果 A 1 和 A 2 是兩個這樣的⼦集,那麼核的⼀個簡單的選擇可以是:

其中 A 1 ∩ A 2 表⽰集合 A 1 和 A 2 的交集, |A| 表⽰ A 的元素的數量。這是⼀個合法的核,因為可以證明它對應於⼀個特徵空間中的⼀個內積。 

當然還有其他的方法構造核函式例如從⼀個概率⽣成式模型開始構造,有興趣的同學可以研究一下.

下面就詳解什麼是徑向基核函式,將對他的由來和使用進行全面闡釋:

什麼是徑向基函式?

理解RBF網路的工作原理可從兩種不同的觀點出發:①當用RBF網路解決非線性對映問題時,用函式逼近與內插的觀點來解釋,對於其中存在的不適定(illposed)問題,可用正則化理論來解決;②當用RBF網路解決複雜的模式分類任務時,用模式可分性觀點來理解比較方便,其潛在合理性基於Cover關於模式可分的定理。下面闡述基於函式逼近與內插觀點的工作原理。
          1963年Davis提出高維空間的多變數插值理論。徑向基函式技術則是20世紀80年代後期,Powell在解決“多變數有限點嚴格(精確)插值問題”時引人的,目前徑向基函式已成為數值分析研究中的一個重要領域。
         考慮一個由N維輸人空間到一維輸出空間的對映。設N維空間有P個輸人向量平,P=1,2,....,P,它們在輸出空間相應的目標值為\large d^p,p=1,2,...,P,P對輸人一輸出樣本構成了訓練樣本集。插值的目的是尋找一個非線性對映函式F(X),使其滿足下述插值條件:

                                                               \large F(X) = d^p                \large p = 1,2,3,.....,P                         \large \left ( 1 \right )

式子中,函式F描述了一個插值曲面,所謂嚴格插值或精確插值,是一種完全內插,即該插值曲面必須通過所有訓練資料點。

那麼到底什麼是差值,在這裡簡單的解釋一下,就是通過訓練集資料,我找到一個曲面,這個曲面可以完全覆蓋這些訓練點,那麼找到這個曲面後就可以通過這個曲面取尋找其他的值了,下面畫個圖給大家看看:

畫圖不是很好啊,意思差不多,就是我通過一些資料樣本點 ,每個樣本都有目標值,通對映高維空間去找到一個曲面F(x),這個曲面需要經過所有的資料,一旦這個曲面確定以後,我就可以通過這個曲面去生成更多的資料目標值,就是這個意思了,好,我們繼續往下:

採用徑向基函式技術解決插值問題的方法是,選擇P個基函式個訓練資料,各基函式的形式為:
                                                 \large \varphi (\left \| x-x^p \right \|)                    \large p = 1,2,....,P                              \large \left ( 2 \right )
式中,基函式\large \varphi為非線性函式,訓練資料點\large x^p\large \varphi的中心。基函式以輸人空間的點x與中心\large x^p的距離作為函式的自變數。由於距離是徑向同性的,故函式被稱為徑向基函式。基於徑向基函式技術的差值函式定義為基函式的線性組合:

                                                  \large F(x) = \sum_{p=1}^{P}w_p\varphi (\left \| x-x^p \right \|)                                                        \large \left ( 3 \right )

 在這裡需要解釋一下\large \left \| x-x^p \right \|這是範數,在平面幾何的向量來說就是模,然而一旦維度很高就不知道是什麼東西了,可能是衡量距離的一個東西,那麼這個代表什麼意思呢?簡單來說就是一個圓而已,在二維平面,\large x^p就是圓心,x就是資料了,這個資料距離圓心的距離,因為和資料的位置和大小無關,只和到圓心的半徑有關,況且同一半徑圓上的點到圓心是相等的因此取名為徑向,代入對映函式就是徑向基函數了,我們看看徑向基函式有什麼特點:

將(1)式的插值條件代入上式,得到P個關於未知係數\large w^p,\large p = 1,2,....,P的線性方程組:

                                                    \large \sum_{p=1}^{P}w_p\varphi (\left \| \mathbf{x^1-x^p} \right \|)=d^1

                                                     \large \sum_{p=1}^{P}w_p\varphi (\left \| \mathbf{x^2-x^p} \right \|)=d^2

                                                                            \large \begin{matrix} \end{matrix}\large \wr

                                                     \large \sum_{p=1}^{P}w_p\varphi (\left \| \mathbf{x^P-x^p} \right \|)=d^P                                    \large \left ( 4 \right )

\large \varphi _{ip}=\varphi (\left \|\mathbf{ x^i-x^p} \right \|),i=1,2,...,P,p = 1,2,...,P,則上述方程組可改寫為:

                                   \large \begin{bmatrix} \varphi _{11},\varphi _{12},...,\varphi _{1P} &\\ \varphi _{11},\varphi _{12},...,\varphi _{1P} &\\ ......,......,...,..... &\\ \varphi _{11},\varphi _{12},...,\varphi _{1P} &\\ \end{bmatrix}\begin{bmatrix} w_1 &\\ w_2 &\\ ....&\\ w_P &\\ \end{bmatrix} = \begin{bmatrix} d^1 &\\ d^2 &\\ ....&\\ d^P &\\ \end{bmatrix}                                 \large \left ( 5 \right )

\large \Phi表示元素\large \varphi _{ip}的PxP階矩陣,\large w\large d分別表示係數向量和期望輸出向量,(5)式還可以寫成下面的向量形式:

                                                                 \large \Phi w =d                                                                 \large \left ( 6 \right )

式中,\large \Phi稱為插值矩陣,若\large \Phi為可逆矩陣,就可以從(6)式中解出係數向量\large w,即:

                                                                   \large w = \Phi ^{-1}d                                                           \large \left ( 7 \right )

通過上面大家可以看到為了使所有資料都在曲面還需要係數調節,此時求出係數向量就求出了整個的對映函數了,下面在看看幾個特殊的對映函式:

(1)高斯徑向基函式

                                                                   \large \varphi (r) = e^{-\frac{r^2}{2\delta ^2}}

 橫軸就是到中心的距離用半徑r表示,如上圖,我們發現當距離等於0時,徑向基函式等於1,距離越遠衰減越快,其中高斯徑向基的引數\large \delta在支援向量機中被稱為到達率或者說函式跌落到零的速度。紅色\large \delta=1,藍色\large \delta=5,綠色\large \delta=0.5,我們發現到達率越小其越窄。

(2) 反演S型函式

                                                          \large \varphi (r) = \frac{1}{1+e^{(\frac{r^2}{\delta ^2})}}

這個和徑向基類似,只是極值為0.5.前面乘上係數就好了 ,紅色\large \delta=1,藍色\large \delta=5,綠色\large \delta=0.5

(3)擬多二次函式

                                                 \large \varphi(r) = \frac{1}{(r^2+\delta ^2)^\frac{1}{2}}

                                                                

紅色\large \delta=1,藍色\large \delta=5,綠色\large \delta=0.5,橫軸為距離r。 

徑向基函式就講完了,後面就是如何使用徑向基函式進行機器學習分類呢?在這裡以sklearn為例進行講解:

核函式 可以是以下任何形式::

  • 線性: \langle x, x'\rangle.
  • 多項式: (\gamma \langle x, x'\rangle + r)^dd 是關鍵詞 degreer 指定 coef0
  • rbf: \exp(-\gamma \|x-x'\|^2)\gamma 是關鍵詞 gamma, 必須大於 0。
  • sigmoid (\tanh(\gamma \langle x,x'\rangle + r)), 其中 r 指定 coef0

此時大家應該能看懂徑向基中的函式\large \gamma是什麼意思吧,這裡我們著重講解徑向基核函式 

初始化時,不同核心由不同的函式名呼叫:

>>> linear_svc = svm.SVC(kernel='linear')
>>> linear_svc.kernel
'linear'
>>> rbf_svc = svm.SVC(kernel='rbf')
>>> rbf_svc.kernel
'rbf'

當用徑向基(RBF)核心去訓練SVM,有兩個引數必須要去考慮:C懲罰係數和gamma。引數C,通用在所有SVM核心,與決策表面的簡單性相抗衡,可以對訓練樣本的誤分類進行有價轉換。較小的C會使決策表面更平滑,同時較高的C旨在正確地分類所有訓練樣本。Gamma定義了單一訓練樣本能起到多大的影響。較大的gamma會更讓其他樣本受到影響。

直觀地,該gamma引數定義了單個訓練樣例的影響達到了多遠,低值意味著“遠”,高值意味著“接近”。所述gamma引數可以被看作是由模型支援向量選擇的樣本的影響的半徑的倒數。

C引數將訓練樣例的錯誤分類與決策表面的簡單性相對應。低值C使得決策表面平滑,而高度C旨在通過給予模型自由選擇更多樣本作為支援向量來正確地對所有訓練樣本進行分類。

程式碼就不貼了,後面有時間使用sklearn進行分類實踐其實主要想說的是如果使用我們如何選擇引數值,這個官方給了示例:

官方使用的資料是iris資料分類,通過交叉驗證給出了熱圖,下面分析一下,程式碼下面貼,或者直接去官網複製

../../_images/sphx_glr_plot_rbf_parameters_002.png

這個圖分別是徑向基兩個引數的關係是如何影響正確率的,右邊的顏色條書面正確率的,最下面即暗黑色正確率只有0.2,黃色接近0.98,而白色說明是1即百分百正確,大家可以看看一般引數在在哪些位置位置合適, 是c在1到100,gamma在0.001到0.1這些引數的的準確率為100%,具體的請參考官方文件。

我們再看這個資料圖:

一般我們選擇引數可以依靠引數優化工具幫我們選擇,或者拿來參考都是不錯的選擇,在這裡就詳細說了,感興趣的同學可以參考官方說明:sklearn官方網站

以上幾節是完全理論的分析下面就根據機器學習實戰實現一遍。

print(__doc__)

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize

from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.model_selection import GridSearchCV


# Utility function to move the midpoint of a colormap to be around
# the values of interest.

class MidpointNormalize(Normalize):

    def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
        self.midpoint = midpoint
        Normalize.__init__(self, vmin, vmax, clip)

    def __call__(self, value, clip=None):
        x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
        return np.ma.masked_array(np.interp(value, x, y))

# #############################################################################
# Load and prepare data set
#
# dataset for grid search

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

# Dataset for decision function visualization: we only keep the first two
# features in X and sub-sample the dataset to keep only 2 classes and
# make it a binary classification problem.

X_2d = X[:, :2]
X_2d = X_2d[y > 0]
y_2d = y[y > 0]
y_2d -= 1

# It is usually a good idea to scale the data for SVM training.
# We are cheating a bit in this example in scaling all of the data,
# instead of fitting the transformation on the training set and
# just applying it on the test set.

scaler = StandardScaler()
X = scaler.fit_transform(X)
X_2d = scaler.fit_transform(X_2d)

# #############################################################################
# Train classifiers
#
# For an initial search, a logarithmic grid with basis
# 10 is often helpful. Using a basis of 2, a finer
# tuning can be achieved but at a much higher cost.

C_range = np.logspace(-2, 10, 13)
gamma_range = np.logspace(-9, 3, 13)
param_grid = dict(gamma=gamma_range, C=C_range)
cv = StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=42)
grid = GridSearchCV(SVC(), param_grid=param_grid, cv=cv)
grid.fit(X, y)

print("The best parameters are %s with a score of %0.2f"
      % (grid.best_params_, grid.best_score_))

# Now we need to fit a classifier for all parameters in the 2d version
# (we use a smaller set of parameters here because it takes a while to train)

C_2d_range = [1e-2, 1, 1e2]
gamma_2d_range = [1e-1, 1, 1e1]
classifiers = []
for C in C_2d_range:
    for gamma in gamma_2d_range:
        clf = SVC(C=C, gamma=gamma)
        clf.fit(X_2d, y_2d)
        classifiers.append((C, gamma, clf))

# #############################################################################
# Visualization
#
# draw visualization of parameter effects

plt.figure(figsize=(8, 6))
xx, yy = np.meshgrid(np.linspace(-3, 3, 200), np.linspace(-3, 3, 200))
for (k, (C, gamma, clf)) in enumerate(classifiers):
    # evaluate decision function in a grid
    Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    # visualize decision function for these parameters
    plt.subplot(len(C_2d_range), len(gamma_2d_range), k + 1)
    plt.title("gamma=10^%d, C=10^%d" % (np.log10(gamma), np.log10(C)),
              size='medium')

    # visualize parameter's effect on decision function
    plt.pcolormesh(xx, yy, -Z, cmap=plt.cm.RdBu)
    plt.scatter(X_2d[:, 0], X_2d[:, 1], c=y_2d, cmap=plt.cm.RdBu_r,
                edgecolors='k')
    plt.xticks(())
    plt.yticks(())
    plt.axis('tight')

scores = grid.cv_results_['mean_test_score'].reshape(len(C_range),
                                                     len(gamma_range))

# Draw heatmap of the validation accuracy as a function of gamma and C
#
# The score are encoded as colors with the hot colormap which varies from dark
# red to bright yellow. As the most interesting scores are all located in the
# 0.92 to 0.97 range we use a custom normalizer to set the mid-point to 0.92 so
# as to make it easier to visualize the small variations of score values in the
# interesting range while not brutally collapsing all the low score values to
# the same color.

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=plt.cm.hot,
           norm=MidpointNormalize(vmin=0.2, midpoint=0.92))
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('Validation accuracy')
plt.show()