支持向量機的人臉識別

支持向量機(Support Vector Machine)是人工神經網絡出現之前最常用的算法
**支持向量機要解決的問題:**什麼樣的決策邊界纔是最好的呢?
在這裏插入圖片描述
優化的目標:找到一條線,使得離該線最近的點,比如二分類的兩種點,每個最近的點能夠離決策邊界最遠。
在這裏插入圖片描述
求什麼樣的w和b使得之前的等式最小,意思就是說找到離直線距離最近的點,然後在讓這個點到直線的距離求最大值。
化簡後:
在這裏插入圖片描述
第二步就是利用拉格朗日乘數法化簡後,什麼樣的阿爾法使得距離最大。
將其反轉後式子如上圖,轉換爲求最小值的問題。
下面是一個案例,以一個簡單實例來看,求解支持向量。
在這裏插入圖片描述
求解後得到的結果如上圖。真正發揮作用的點就是支持向量
在這裏插入圖片描述
最終的決策邊界是由阿爾法不等於0的樣本點構成,就那一兩個樣本,如果阿爾法等於0,那麼W所在的那一項的xy無論取什麼值,都等於0,所以這個點就沒有意義。
也就是說,對於支持向量機的機制,分類是由少數幾個樣本決定的。在這裏插入圖片描述
軟間隔:有時候數據中有一些噪音點,如果考慮這個不好的點,數據的線就不行了。爲了解決該問題,纔會引入鬆弛因子
在這裏插入圖片描述
C是我們可以指定的一個數。
爲了讓整體方程儘可能小,當C值大時,鬆弛因子必須小才能保證整體較小,意味着分類嚴格不能有錯誤。
而當C值小時,鬆弛因子可以稍微大一點,意味着可以有更大的錯誤容忍。
支持向量機的一個優點:
對於低維度不可分的問題,可以通過函數轉換成高維度,這樣對於一個平面內的不可分割問題,就會轉換成空間問題,然後用一個面就能分割了。
在這裏插入圖片描述
這種方法被稱爲核變換。
利用核函數進行核變換,時間複雜度是o(n^2)
常用的叫做高斯核函數,能把低維度變成高維度,把線性向量機變成非線性的。
在這裏插入圖片描述
核函數可以將一個不可分割的數據集變得可分割。

代碼實現:利用支持向量機分類數據,並且探索不同的核函數,不同的C值和不同的gamma對分類結果的影響。

%matplotlib inline
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_blobs
#模擬隨機產生個數據,數據的樣本數量是8,中心點是2,密集度是0.6
x,y= make_blobs(n_samples=80,centers=2,random_state=0,cluster_std=0.60)
#cluster_std的意思是,簇的分散程度,越大表示數據越分散,越小表示越集中。
plt.scatter(x[:,0],x[:,1],c=y,s=50)

畫圖如下:
在這裏插入圖片描述
x的部分數據:

array([[  1.65991049e+00,   3.56289184e+00],
       [  1.60841463e+00,   4.01800537e-01],
       [  2.77180174e-01,   4.84428322e+00],
       [  9.14338767e-01,   4.55014643e+00],
       [  2.15527162e+00,   1.27868252e+00],
       [  3.18515794e+00,   8.90082233e-02],
       [  1.81336135e+00,   1.63113070e+00],
       [  2.18023251e+00,   1.48364708e+00],
       [  2.42371514e+00,   1.45098766e+00],
       [  2.13141478e+00,   1.13885728e+00],
       [  4.88382309e-01,   3.26801777e+00],
       [  2.23421043e+00,   1.69349520e+00],

y的數據:

array([0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1,
       0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1,
       1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0,
       1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1])

建立模型

#導入向量機模型
from sklearn.svm import SVC #Support vector classifier
model = SVC(kernel='linear')#構造了一個最基本的線性支持向量機
model.fit(x,y)

繪圖函數

#繪圖函數
def plot_function(model,ax=None,plot_support=None):
    if ax is None:
        ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    x = np.linspace(xlim[0],xlim[1],30)
    y = np.linspace(ylim[0],ylim[1],30)
    Y,X = np.meshgrid(y,x)
    xy = np.vstack([X.ravel(),Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)
    ax.contour(X,Y,P,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--'])
    
    if plot_support:
        ax.scatter(model.support_vectors_[:,0],
                  model.support_vectors_[:,1],
                  s=300,linewidth=1,facecolors='none');
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)

畫圖:

plt.scatter(x[:,0],x[:,1],c=y,s=50)
plot_function(model)

結果如下:
在這裏插入圖片描述
從圖中可以看出,對於一個能夠分開有明確邊界的二維數據,可以用線性的進行分類,並且能夠構造出較好的支持向量

中間這條線是決策邊界,兩個邊界上的點就是支持向量, 在scikit-Learn中,他們存儲在supportvectors(一個屬性)

model.support_vectors_
#支持向量所在的位置
#只要支持向量沒有變,那麼決策邊界就不會變,那麼新的數據點肯定在這個分區裏

支持向量的輸出結果:

array([[ 2.33812285,  3.43116792],
       [ 0.44359863,  3.11530945],
       [ 1.35139348,  2.06383637],
       [ 1.53853211,  2.04370263]])

接下來是對於完全穿插的點,利用線性進行分類

from sklearn.datasets.samples_generator import make_circles
x,y = make_circles(100,factor=.1,noise=.1)
#做一個圓形的數據樣本
clf = SVC(kernel='linear').fit(x,y)
plt.scatter(x[:,0],x[:,1],c=y,s=50)
plot_function(clf,plot_support=False)

畫圖如下:
在這裏插入圖片描述
如圖可以看出,如果以這種樣本,線性支持向量機就不行了,分不開了,只能用核函數
核函數的畫圖理解:

from mpl_toolkits import mplot3d
r = np.exp(-(x**2).sum(1))
def plot_3D(elev=30,azim=30,x=x,y=y):
    ax = plt.subplot(projection='3d')
    ax.scatter3D(x[:,0],x[:,1],r,c=y,s=50)
    ax.view_init(elev=elev,azim=azim)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
plot_3D(elev=45,azim=45,x=x,y=y)

在這裏插入圖片描述
也就是說,對於低維度不可分割的樣本,轉換成更高的維度。

#加入徑向基函數
clf = SVC(kernel='rbf',C=1E6)
#高斯算法提升維度有很多種,rbf只是其中一種
clf.fit(x,y)
plt.scatter(x[:,0],x[:,1],c=y,s=50)
plot_function(clf)

畫圖如下:
在這裏插入圖片描述
高斯核函數能夠把線性變成非線性的決策邊界。
這樣就能夠把樣本的分的越明顯。
所以對於不容易分類的樣本,只要提高維度就行。
C值對支持向量的影響:

x,y= make_blobs(n_samples=80,centers=2,random_state=0,cluster_std=0.80)
#生成數據點
fig,ax = plt.subplots(1,2,figsize=(16,6))
fig.subplots_adjust(left=0.0625,right=0.95,wspace=0.1)
for axi,C in zip(ax,[10,0.1]):
    model = SVC(kernel='linear',C=C).fit(x,y)
    axi.scatter(x[:,0],x[:,1],c=y,s=50)
    plot_function(model,axi)
    axi.scatter(model.support_vectors_[:,0],
               model.support_vectors_[:,1],
               s=300,lw=1,facecolors='none');
    #axi.set_title('C = {0:.1f}',format(C),size=14)

在這裏插入圖片描述
如圖所示:如果C值過大容忍度就小,不能有錯誤點,反之亦然。
gamma值對最終結果的影響

x,y= make_blobs(n_samples=80,centers=2,random_state=0,cluster_std=1.1)
#cluster_std是離散程度,超過1,就完全接觸了
#生成數據點
fig,ax = plt.subplots(1,2,figsize=(16,6))
fig.subplots_adjust(left=0.0625,right=0.95,wspace=0.1)
for axi,gamma in zip(ax,[10,0.1]):
    model = SVC(kernel='rbf',gamma=gamma).fit(x,y)
    axi.scatter(x[:,0],x[:,1],c=y,s=50,cmap='autumn')
    plot_function(model,axi)
    axi.scatter(model.support_vectors_[:,0],
               model.support_vectors_[:,1],
               s=300,lw=1,facecolors='none');

畫圖如下:
在這裏插入圖片描述
gamma值越高,模型的映射維度越高,結果更加精確,但是曲線不規則,有可能過擬合,gamma值越小,模型映射的維度降低,但是不容易分開。
利用支持向量機對人臉識別的實現

from sklearn.datasets import fetch_lfw_people
#導入人臉識別數據
faces = fetch_lfw_people(min_faces_per_person=60)
#取出對於每一個人來說,最小人臉大於60的人
print(faces.target_names)#取出這些人的名字
print(faces.images.shape)#取出圖片個數,像素大小
#註釋:自動下載非常慢,先讓他自動下載成壓縮包,只要壓縮包出現就停止,然後網上下載lfw這個壓縮包和lfw-funnel.gz,建立一個名字爲lfw_funneled的文件夾,然後把lfw的照片全部拷貝過去

輸出如下:

['Ariel Sharon' 'Colin Powell' 'Donald Rumsfeld' 'George W Bush'
 'Gerhard Schroeder' 'Hugo Chavez' 'Junichiro Koizumi' 'Tony Blair']
(1348, 62, 47)

打印出人臉看看

fig,ax=plt.subplots(3,5)
for i,axi in enumerate(ax.flat):
    axi.imshow(faces.images[i],cmap='bone')#打印出一部分人臉看看
    axi.set(xticks=[],yticks=[],xlabel=faces.target_names[faces.target[i]])

照片的一個像素點表示一個維度,一共2914維度,需要降維。

from sklearn.svm import SVC
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline

pca = PCA(n_components=150,whiten=True,random_state=42)
#把維度降低到150維
svc = SVC(kernel='rbf',class_weight='balanced')
model = make_pipeline(pca,svc)
#捆綁在一起,意思是SVC和PCA合併在了一起,就是降維的svc

進行數據切分,劃分出訓練集和測試集。
直觀的觀察下數據:
faces.data.shape#數據的維度 打印結果(1348,2914)
faces.data#數據就是矩陣,行是圖片的個數,列是像素點的個數。類似於KNN的手寫識別

array([[ 114.        ,  122.33333588,  134.        , ...,    2.66666675,
           4.33333349,    5.        ],
       [  42.        ,   62.        ,   73.        , ...,  254.        ,
         252.        ,  249.        ],
       [  73.66666412,   78.66666412,   71.        , ...,  195.33332825,
         233.66667175,  233.33332825],
       ..., 
       [  29.66666603,   30.33333397,   30.        , ...,  135.33332825,
         141.        ,  145.        ],
       [  55.66666794,   60.        ,   69.66666412, ...,   22.        ,
          15.        ,    8.        ],
       [ 137.        ,   55.33333206,   43.        , ...,   42.33333206,
          46.        ,   47.        ]], dtype=float32)

faces.target()#姓名轉換成的數字,以方便做矩陣預算
輸出結果:array([1, 3, 3, …, 7, 3, 5], dtype=int64)
CV驗證,獲得最好的C和gamma

param_grid = {'svc__C':[1,5,10],'svc__gamma':[0.0001,0.0005,0.001]}#選取要循環的參數,就是要測試的參數
grid = GridSearchCV(model,param_grid=param_grid,cv=5)#選擇模型,選擇CV,就是交叉驗證,如果不進行結果不準確
grid.fit(x_train,y_train)
grid.grid_scores_, grid.best_params_, grid.best_score_

結果如下:

([mean: 0.19090, std: 0.16492, params: {'svc__C': 1, 'svc__gamma': 0.0001},
  mean: 0.59050, std: 0.10427, params: {'svc__C': 1, 'svc__gamma': 0.0005},
  mean: 0.78042, std: 0.01349, params: {'svc__C': 1, 'svc__gamma': 0.001},
  mean: 0.64985, std: 0.06542, params: {'svc__C': 5, 'svc__gamma': 0.0001},
  mean: 0.79426, std: 0.01482, params: {'svc__C': 5, 'svc__gamma': 0.0005},
  mean: 0.80712, std: 0.01576, params: {'svc__C': 5, 'svc__gamma': 0.001},
  mean: 0.79327, std: 0.01480, params: {'svc__C': 10, 'svc__gamma': 0.0001},
  mean: 0.78536, std: 0.01664, params: {'svc__C': 10, 'svc__gamma': 0.0005},
  mean: 0.79525, std: 0.01796, params: {'svc__C': 10, 'svc__gamma': 0.001}],
 {'svc__C': 5, 'svc__gamma': 0.001},
 0.80712166172106825)
from sklearn.metrics import confusion_matrix,classification_report,recall_score,accuracy_score
#CV驗證可以把最優的模型直接拿出來
model = grid.best_estimator_
#拿到最好的模型後,進行預測
y_pred=model.predict(x_test)
cnf_matrix =confusion_matrix(y_test,y_pred)
#把小數位數改爲2位
np.set_printoptions(precision=2)
print(accuracy_score(y_test,y_pred))
cnf_matrix

輸出結果和混淆矩陣如下

0.747774480712
Out[29]:
array([[ 13,   1,   1,   1,   0,   0,   0,   0],
       [  0,  51,   0,   2,   0,   0,   0,   1],
       [  1,   0,  23,   6,   3,   1,   0,   0],
       [  4,   4,   3, 105,   5,   4,   1,  10],
       [  0,   0,   0,   2,  18,   3,   1,   3],
       [  1,   1,   0,   4,   3,   7,   2,   0],
       [  1,   1,   0,   1,   1,   0,  10,   1],
       [  0,   5,   1,   2,   3,   1,   0,  25]], dtype=int64)

導入分類報告

from sklearn.metrics import classification_report
#導入分類報告
print(classification_report(y_test,y_pred,target_names=faces.target_names))

結果如下:

precision    recall  f1-score   support

     Ariel Sharon       0.65      0.81      0.72        16
     Colin Powell       0.81      0.94      0.87        54
  Donald Rumsfeld       0.82      0.68      0.74        34
    George W Bush       0.85      0.77      0.81       136
Gerhard Schroeder       0.55      0.67      0.60        27
      Hugo Chavez       0.44      0.39      0.41        18
Junichiro Koizumi       0.71      0.67      0.69        15
       Tony Blair       0.62      0.68      0.65        37

      avg / total       0.76      0.75      0.75       337

精度(precision) =正確預測的個數(TP)/被預測正確的個數(TP+FP) 召回率(recall)=正確預測的個數(TP)/預測個數(TP+FN)