C231n Assignment 1 作業(2) SVM線性分類器實現

目錄

 

一、Assignment 1 課程作業要求

二、數據集下載及處理 

三、SVM線性分類器實現

3.1 計算SVM損失值和梯度

3.2定義SVM線性分類器

3.2.1 定義初始化和訓練函數

3.2.2 定義測試函數和損失值函數

3.2.3 定義SVM線性分類器

四、調用SVM分類器進行圖片分類

4.1 數據集分割,及歸零化處理圖片

4.2調用SVM訓練數據並預測


一、Assignment 1 課程作業要求

具體原版要求見鏈接http://cs231n.github.io/assignments2019/assignment1/  給不能FQ的同志搬運下

對cifar10 數據進行圖像數據的10分類,分別有以下要求

1: k-Nearest Neighbor classifier (20 points)

2: Training a Support Vector Machine (25 points)

3: Implement a Softmax classifier (20 points)

4: Two-Layer Neural Network (25 points)

5: Higher Level Representations: Image Features (10 points)

 

本文僅完成SVM 線性分類器部分 

 

二、數據集下載及處理 

基本的數據集下載,數據簡單批處理,參考https://github.com/Halfish/cs231n/blob/master/assignment1/knn.ipynb中,將下載後的cifar10數據集解壓

data_utils.py:

# -*- coding: utf-8 -*-
"""
Created on Sat Feb 29 12:57:18 2020
data_utils.py
@author: acer
"""
import pickle
import numpy as np 
import os 

def load_cifar_batch(filename): 
    with open(filename,'rb') as f : 
        datadict=pickle.load(f,encoding='bytes') 
        x=datadict[b'data'] 
        y=datadict[b'labels'] 
        x=x.reshape(10000,3,32,32).transpose(0,2,3,1).astype('float') 
        y=np.array(y) 
        #以數組形式輸出
        return x,y 
#root 爲cifar文件根目錄
def load_cifar10(root_path): 
    xs=[] 
    ys=[] 
    # 訓練集1-5
    for b in range(1,2): #這裏只選10000張作爲訓練集
        # 讀取訓練集圖片
        f=os.path.join(root_path,'data_batch_%d' % (b,)) 
        #每批讀取
        x,y=load_cifar_batch(f) 
        #累加 x y 將5批數據合併
        xs.append(x) 
        ys.append(y) 
        #數組拼接
        Xtrain=np.concatenate(xs) 
        Ytrain=np.concatenate(ys) 
    del x ,y 
    # 讀取測試集圖片
    Xtest,Ytest=load_cifar_batch(os.path.join(root_path,'test_batch'))   
    return Xtrain,Ytrain,Xtest,Ytest

建立讀取數據訓練的主文件main.py,調用函數讀取並顯示cifar10圖片

main.py:

import numpy as np
from data_utils import load_cifar10
import matplotlib.pyplot as plt

#提取數據
x_train,y_train,x_test,y_test = load_cifar10('.\cifar-10-python\cifar-10-batches-py')
x_test = x_test[0:2000,:]
y_test = y_test[0:2000]
print('training data shape:',x_train.shape) 
print('training labels shape:',y_train.shape) 
print('test data shape:',x_test.shape) 
print('test labels shape:',y_test.shape)

#載入數據
classes=['plane','car','bird','cat','deer','dog','frog','horse','ship','truck'] 
num_classes=len(classes) 
samples_per_class=3
for y ,cls in enumerate(classes): 
    idxs=np.flatnonzero(y_train==y)
    idxs=np.random.choice(idxs,samples_per_class,replace=False) 
    for i ,idx in enumerate(idxs): 
        plt_idx=i*num_classes+y+1 
        plt.subplot(samples_per_class,num_classes,plt_idx) 
        plt.imshow(x_train[idx].astype('uint8'))
        plt.axis('off') 
        if i==0: 
            plt.title(cls) 
plt.show()

三、SVM線性分類器實現

我們要做的就是建立一個SVM線性分類器,具體在python中以類的形式實現,原理見具體SVM原理

3.1 計算SVM損失值和梯度

輸入圖片X:N*D,輸出標籤爲y:N*C,線性權值矩陣 W:D*C

線性得分score = f(X,W) = X\cdot W

多類svm分類器輸出,預測(X_i,y_i)X_i的樣本爲y_i的損失值爲

L_i=\sum_{j\neq y_i} max(0,S_j-S_{y_i}+\Delta)=\sum_{j\neq y_i} \begin{cases} & 0,\text{ if } S_{y_i}>S_j+\Delta \\ & S_j-S_{y_i}+\Delta,\text{ otherwise } \end{cases}

加入正則換項,取SVM間隔爲1,並替換線性表達式

L_i=\sum_{j\neq y_i} max(0,W_j^Tx_i-W_{y_i}^Tx_i+1)+\lambda\cdot W_{y_i}^2

取梯度

\frac{\partial L_i}{\partial W_{y_i}}=-x_i\sum_{j\neq y_i} 1\cdot (W_j^Tx_i-W_{y_i}^Tx_i+1>0)+2\lambda\cdot W_{y_i}

定義svm.py,定義SVM損失值和梯度的向量形式求解

#定義SVM損失值
def loss_svm_vectorized(X,y,W,reg):
    #SVM 訓練中 
    #  Inputs:
    #  W: A numpy array of shape (D, C) containing weights.
    #  X: A numpy array of shape (N, D) containing a minibatch of data.
    #  y: A numpy array of shape (N, 1) containing training labels; y[i] = c means
    #  that X[i] has label c, where 0 <= c < C.

    # 得分 Score = f(x,W)= xW   s:N*C  x:N*D  w:D*C  N-圖像數 D-每幅圖維數 C-類別數
    # loss_i = Sum_i max(0,Sj-Syj+1)    
    #輸出類別
    n_class = W.shape[1]
    #通道數
    n_dimension = X.shape[1]
    #樣本數
    n_sample = X.shape[0]   
    #定義 損失值loss 和 梯度dW
    dW = np.zeros(W.shape)
    total_loss = [0.0]
    #前向通道
    score = np.dot(X,W)  # N*C
    loss = []
    #將y_score轉化成與score同型
    n0 = range(n_sample)
    ls = list(y)
    y_score = score[range(n_sample), list(y)]  # 1*N
    y_score = y_score.reshape(-1,1)  # N*1
    margins = np.maximum(0,score-y_score+1) # N*C
    margins[range(n_sample), list(y)] = 0
    loss = np.sum(margins)
    #加入正則化
    total_loss =loss/n_sample + 0.5*reg*np.sum(W*W)
    
    #SGD 方法求解梯度
    #dLoss/dWi:
    # 當Sj+1>Syi時margins>0 dLoss/dW_yi=-xi+lamda*W
    # 當Sj+1<Syi時margins=0 dLoss/dW_yi=0+lamda*W
    #建立delta矩陣
    Dt = np.zeros((n_sample,n_class)) #N*C
    Dt[margins>0]  = 1
    Dt[range(n_sample), list(y)] = 0
    Dt[range(n_sample), list(y)] = -np.sum(Dt,axis = 1)

    dW = np.dot(X.T,Dt)+reg*W
    return total_loss,dW

3.2定義SVM線性分類器

先定義廣義的線性分類器 LinearClassifier.py,loss值的計算先不定義,後續SVM分類器和softmax分類器均可在此基礎上繼承

3.2.1 定義初始化和訓練函數

# 線性分類器類 設置初始化 訓練 預測三個函數
class LinearClassifier:
    def _init_(self):
        pass
        #self.W = None #D*C
    # 其中訓練函數:數據批處理 迭代輪數 計算損失和梯度 更新權值函數    
    def train(self,X,y,learning_rate,reg,train_iters,batch_size):
        #輸出類別
        n_class = np.max(y)+1
        #通道數
        n_dimension = X.shape[1]
        #樣本數
        n_sample = X.shape[0] 
        #if self.W==None:
        self.W = 0.001*np.random.randn(n_dimension,n_class) #D*C
        #批處理數據,計算損失值 梯度 並更新權值
        loss_history = []
        for it in range(train_iters):
            #batch_gen = get_batch([X,y],batch_size,True)
            #x_batch, y_batch = next(batch_gen)
            #在N中選batch_size個數組成數組
            batch_idx = np.random.choice(n_sample,batch_size,replace=True)
            x_batch = X[batch_idx]
            y_batch = y[batch_idx]
            #print(x_batch, y_batch)
            #計算loss grad
            loss,grad = self.loss(x_batch,y_batch,reg)
            loss_history.append(loss)
            #更新權值
            self.W += - learning_rate*grad
            
            if it%5 ==0:
                print('after %d/%d iterations, the algorithm gets %f loss value'%(it,train_iters,loss))

3.2.2 定義測試函數和損失值函數

其中loss函數先不定義,後續專門在線性分類器的繼承類SVM分類器中調用之前定義的loss_svm_vectorized

    def predict(self,X):
        #前向通道 輸出score
        y_pred = np.zeros(X.shape[1])
        score = np.dot(X,self.W)
        y_pred = np.argmax(score,axis=1)
        
        return y_pred
    
    def loss(self,x_batch,y_batch,reg):
        pass
        

3.2.3 定義SVM線性分類器

#SVM 繼承類
class LinearSVMClassifier(LinearClassifier):
    def loss(self,x_batch,y_batch,reg):
        return loss_svm_vectorized(x_batch,y_batch,self.W,reg)

四、調用SVM分類器進行圖片分類

main.py 補充以下代碼

4.1 數據集分割,及歸零化處理圖片

#截斷數據訓練
num_training = 3000 #訓練集
num_val = 100      #驗證集
num_test=100       #測試集

mask=range(num_training,num_training+num_val) 
x_val=x_train[mask]
y_val=y_train[mask] 

mask=range(num_training) 
x_train=x_train[mask]
y_train=y_train[mask] 


mask=range(num_test) 
x_test=x_test[mask] 
y_test=y_test[mask]

#爲了歐氏距離的計算,我們把得到的圖像數據拉長成行向量32*32*3 -> 1*3072,代碼如下:
x_train = np.reshape(x_train,(x_train.shape[0],-1)) 
x_val   = np.reshape(x_val,(x_val.shape[0],-1)) 
x_test  = np.reshape(x_test,(x_test.shape[0],-1)) 

print(x_train.shape,x_val.shape,x_test.shape)

4.2調用SVM訓練數據並預測

#linear SVM
#所有圖片減去均值 進行批歸一化處理
mean_image = np.mean(x_train,axis = 0)
x_train-= mean_image
x_val  -= mean_image
x_test -= mean_image
# 選用模型 開始訓練
print('init...')
classifier = LinearSVMClassifier()
classifier.train(x_train,y_train,learning_rate=1e-8,reg=1e-5,train_iters=100,batch_size = 250)

print('predicting...')
y_test_pred=classifier.predict(x_test) 

print(y_test_pred)
print(y_test)

#計算準確度
num_correct = np.sum(y_test_pred == y_test)
accu = float(num_correct)/num_test
print('classifier: %f accuracy in %d image test' %(accu,num_test))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章