CS231N assignment2 SVM

 

CS231N Assignment2 Support Vector Machinepython

 

Beginapp


本文主要介紹CS231N系列課程的第一項做業,寫一個SVM無監督學習訓練模型。dom

課程主頁:網易雲課堂CS231N系列課程函數

語言:Python3.6學習

 

1線形分類器測試


 

        以圖像爲例,一幅圖像像素爲32*32*3表明長32寬32有3通道的衣服圖像,將其變爲1*3072的一個向量,即該圖像的特徵向量。ui

咱們若是須要訓練1000幅圖像,那麼輸入則爲1000*3072的矩陣X。spa

  咱們用X點乘矩陣W獲得一個計分矩陣以下所示,W乘以一幅圖像的特徵向量的轉置獲得一列表明分數。3d

       每一個分數對應表明一個類別,分數越高表明她所屬於此類別紀律越大,因此W實際上是一個類別權重的概念。blog

 注意:下圖爲CS231N中的一張圖,它是以一幅圖爲例,將X轉至爲3072*1,你們理解便可,在程序中咱們採用X*W來編寫。

 更多細節能夠參考CS231N做業1KNN詳解

 

 

 

2損失函數


 

       獲得每一幅圖像對應每個類別的分數以後,咱們須要計算一個損失,去評估一下W矩陣的好壞。

以下右側爲SVM損失函數計算公式。

        對每一幅圖像的損失用其錯誤類別的分數減去正確類別的分數,並與0比較求最大值

通常咱們應該正確類別的分數高就證實沒有損失,此時錯誤類別減去正確類別必定爲負值,比0小故取損失爲0.

爲了提升魯棒性,這裏給他加了一個1。

 

        計算全部的損失後,咱們把損失累加做爲最後的損失

        整理後咱們獲得以下的公式,可是其存在一個問題,沒有考慮W的影響,不一樣的W可能獲得一樣的損失,

 所以咱們引入一個正則,正則係數能夠調節W對整個損失的影響,W越分散固然越好

 

代碼以下:

def svm_loss_native(W,X,Y,reg):
    '''
    本函數用於計算SVM分類器的損失以及梯度
    輸入參數:
        W(D,C)表明權重
            D爲特徵向量的維度,C爲分類類別的數量
        X(N,D)表明訓練樣本的特徵,0維表明每個樣本,1維表明某同樣本的特徵向量
            對於32*32圖像,N表明有N個樣本,D=32*32*3全體像素值表明特徵向量
        Y(N,1)表明訓練樣本的標籤,0維表明每個樣本,1維表明某同樣本的標籤
    輸出參數:
        Loss損失

    '''
    #獲取基礎參數
    num_train = X.shape[0]#訓練樣本的數量
    num_classes = W.shape[1]#劃分的種類
    loss = 0.0#初始化損失
    dW = np.zeros(W.shape)#建立一個梯度
    for i in range(num_train):#分別求每個訓練樣本的損失
        score = X[i].dot(W)#計算每一個樣本的分數

        #計算損失
        for j in range(num_classes):
            if j == Y[i]:
                continue
            margin = score[j] - score[Y[i]] + 1
            #margin =  np.max(0,score[j] - score[Y[i]] + 1)#計算損失
            if margin > 0:
                loss += margin
    loss /= num_train
    #加入正則
    loss += reg * np.sum(W*W)
    return loss

  

 

 

  如此一套完整的損失函數就構造完成了,咱們經過看損失能夠知道這個W矩陣的好壞,那麼若是損失過大該怎麼調劑每個參數呢?

        此時咱們引入梯度降低法和梯度的概念

3梯度


 

梯度降低法:

        首先,咱們有一個可微分的函數。這個函數就表明着一座山。咱們的目標就是找到這個函數的最小值,也就是山底。根據以前的場景假設,最快的下山的方式就是找到當前位置最陡峭的方向,而後沿着此方向向下走,對應到函數中,就是找到給定點的梯度 ,而後朝着梯度相反的方向,就能讓函數值降低的最快!由於梯度的方向就是函數之變化最快的方向(在後面會詳細解釋)
        因此,咱們重複利用這個方法,反覆求取梯度,最後就能到達局部的最小值,這就相似於咱們下山的過程。而求取梯度就肯定了最陡峭的方向,也就是場景中測量方向的手段。

梯度如同求導同樣,以下圖所示,損失的導數反應着梯度情況

若是W向前變化一格,損失增大,則dW梯度應該爲正值,此時應該W向相反方向變化。

 

 
對於本例中對於損失函數,能夠改寫爲以下:

 

 

對於Lij,用其對Wj求偏導

 

 

CODE2 LOSS & 梯度 循環形式

 

def svm_loss_native(W,X,Y,reg):
    '''
    本函數用於計算SVM分類器的損失以及梯度
    輸入參數:
        W(D,C)表明權重
            D爲特徵向量的維度,C爲分類類別的數量
        X(N,D)表明訓練樣本的特徵,0維表明每個樣本,1維表明某同樣本的特徵向量
            對於32*32圖像,N表明有N個樣本,D=32*32*3全體像素值表明特徵向量
        Y(N,1)表明訓練樣本的標籤,0維表明每個樣本,1維表明某同樣本的標籤
    輸出參數:
        Loss損失

    '''
    #獲取基礎參數
    num_train = X.shape[0]#訓練樣本的數量
    num_classes = W.shape[1]#劃分的種類
    loss = 0.0#初始化損失
    dW = np.zeros(W.shape)#建立一個梯度
    for i in range(num_train):#分別求每個訓練樣本的損失
        score = X[i].dot(W)#計算每一個樣本的分數

        #計算損失
        for j in range(num_classes):
            if j == Y[i]:
                continue
            margin = score[j] - score[Y[i]] + 1
            #margin =  np.max(0,score[j] - score[Y[i]] + 1)#計算損失
            if margin > 0:
                loss += margin
                dW[:,Y[i]] += -X[i,:].T
                dW[:,j] += X[i,:].T
    loss /= num_train
    dW /= num_train
    #加入正則
    loss += reg * np.sum(W*W)
    dW += reg * W
    return loss,dW

 

  

 

CODE3 LOSS & 梯度 向量矩陣形式

 

def svm_loss_vectorized(W,X,Y,reg):

    loss = 0.0
    num_train = X.shape[0]
    dW = np.zeros(W.shape)
    scores = np.dot(X,W)
    correct_class_score = scores[np.arange(num_train),Y]
    correct_class_score = np.reshape(correct_class_score,(num_train,-1))
    margin = scores - correct_class_score + 1.0
    margin[np.arange(num_train),Y] = 0.0
    margin[margin<0] = 0.0
    loss += np.sum(margin)/num_train
    loss += 0.5*reg*np.sum(W*W)

    margin[margin>0] = 1.0
    row_sum = np.sum(margin,axis = 1)
    margin[np.arange(num_train),Y] = -row_sum
    dW = 1.0/num_train*np.dot(X.T,margin) + reg*W  # ** # 
    return loss,dW

 

 

4訓練函數


 

 

在獲得損失和梯度後咱們就能夠根據梯度去調節W矩陣,這裏須要引入TRAIN函數的一些參數。

通常須要有如下參數:

訓練次數:要循環訓練多少步。

學習率:每一次根據梯度去修正W矩陣的係數。

樣本數:每一次訓練可能不是選擇全部樣本,須要取樣必定樣本。

核心點在於在循環中不斷去計算損失以及梯度,而後利用下面公式去調節。

 

self.W = self.W - learning_rate * grade

 

 

CODE4 梯度降低法

def train(self,X,Y,learning_rate=1e-3,reg=1e-5,num_iters=100,batch_size=200,verbose=False):
        '''
        隨機梯度降低法訓練分類器
        輸入參數:
        -learning_rate學習率
        -reg正則化強度
        -num_iters步長值
        -batch_size每一步使用的樣本數量
        -verbose若爲真則打印過程
        輸出參數:
        list損失值
        '''
        num_train,dim = X.shape
        num_classes = np.max(Y) + 1
        
        #if self.W is None:
            #初始化W矩陣
        self.W = 0.001 * np.random.randn(dim,num_classes)
        loss_history = []
        #開始訓練num_iters步
        for it in range(num_iters):
            X_batch = None
            Y_batch = None
            ########################
            # 選取部分訓練樣本
            # 隨機生成一個序列
            batch_inx = np.random.choice(num_train,batch_size)
            X_batch = X[batch_inx,:]
            Y_batch = Y[batch_inx]
            #########################
            # 計算損失與梯度
            loss,grade = self.loss(self.W,X_batch,Y_batch,reg)
            loss_history.append(loss)

            ########################
            # 參數更新
            # 梯度爲正表示損失增大,應該減小,成負相關
            self.W = self.W - learning_rate * grade
            #打印結果
            if verbose and it % 100 == 0:
                print('iteration %d / %d : loss %f'%(it ,num_iters,loss))
        return loss_history

 

運行結果如

 

  

 

 5預測predict


 

 

在訓練完模型後會獲得一個較好的W矩陣,而後根據這個W去預測一下測試集看看模型的效果

    def predict(self,X_train):
        y_predict = np.zeros(X_train.shape[1])
        #根據訓練後的W矩陣計算分數
        scores = X_train.dot(self.W)
        #找到得分中最大的值做爲類別
        y_predict = np.argmax(scores,axis = 1)#計算每一行最大值
        return y_predict

在主函數中運行以下代碼觀察預測狀況

score1 = SVM1.predict(X_dev)
print('The predit result %f' %(np.mean(score1 == Y_dev)))
score1 = SVM1.predict(X_test)
print('The Test Data predit result %f' %(np.mean(score1 == Y_test)))

  

  

 

預測結果以下,用訓練集自己去預測獲得0.756,用測試集去預測才0.218,不是太好

 

 

 

6參數調整


 

述即完成了一總體的SVM模型庫,那麼咱們如何自動訓練出一個好的學習率和正則化強度參數呢?

 咱們須要不斷去測試每個參數的好壞,用下面一個程序能夠完成這個任務

 

#調參
#兩個參數,學習率;正則化強度
learning_rate = [2e-7,0.75e-7,1.5e-7,1.25e-7,0.75e-7]
regularization_strengths = [3e4,3.25e4,3.5e4,3.75e4,4e4]

results = {}
best_val = 0
best_svm = None
######################################
# 循環執行代碼
# 對不一樣的學習率以及正則化強度進行測試
#
for rate in learning_rate:
    for regular in regularization_strengths:
        SVM2 = SVM()
        #訓練
        SVM2.train(X_train,Y_train,learning_rate=rate,reg=regular,num_iters=1000)
        #預測
        Y1 = SVM2.predict(X_train)
        Y2 = SVM2.predict(X_val)
        accuracy_train = np.mean(Y1==Y_train)
        accuracy_val = np.mean(Y2==Y_val)
        #判斷優略
        if best_val < accuracy_val:
            best_val = accuracy_val
            best_svm = SVM2#保存當前模型
        #存儲數據
        results[rate,regular] = (accuracy_train,accuracy_val)
#打印數據
for lr,reg in sorted(results):
    accuracy_train,accuracy_val = results[(lr,reg)]
    print('lr:%e reg %e train accuracy: %f val val accuracy : %f'%(lr,reg,accuracy_train,accuracy_val))

  

運行結果以下:

 

7 可視化效果


 

在獲得最優W時,咱們有時要看一下W的可視化效果,從w的圖像能夠看出權重高低,相似於一個反應這個類別的模板。

#可視化結果數據
w = best_svm.W[:,:]
w=w.reshape(32,32,3,10)
w_min,w_max = np.min(w),np.max(w)
classes = ['plane','car','bird','cat','deer','dog','frog','hors','ships','truck']#類別劃分  列表
for i in range(10):
    plt.subplot(2,5,i+1)
    wimg = 255.0 * (w[:,:,:,i].squeeze()-w_min) / (w_max - w_min)

    plt.imshow(wimg.astype('uint8'))
    plt.axis('off')
    plt.title(classes[i])
plt.show()

  以下圖所示

不知我這圖爲啥和別人不同~~~~~~~看着不夠清晰呢?還望大神指點

 

 

在完成本案例過程當中參考了一些文章和帖子,因爲書寫過程當中沒有過多記錄,故沒有標註出來,若有侵權請聯繫備註!

相關文章
相關標籤/搜索