深度学习 | 斯坦福cs231n编程作业#2 --- Linear_SVM

斯坦福cs231n(2017年版)的所有编程作业均采用iPython Notebooks实现,不熟悉的朋友可以提前使用一下Notebooks。编程作业#2主要是手写实现一个Linear_SVM分类器来对cifar-10图像数据集进行分类。

目录

1.实验综述

2.导入必要的包

3.加载CIFAR-10数据并预处理

3.LinearSVM分类器

4.mini-batch梯度下降

5.在验证集上调试超参数

6.在测试集上测试


cs231全部编程作业(英文原版带答案)

cs231n全部编程作业(英文原版不带答案)

编程作业#2(中文翻译版带答案)

1.实验综述

2.导入必要的包


import random
import numpy as np
from cs231n.data_utils import load_CIFAR10 #/cs231n/data_utils.py 加载数据集的函数
import matplotlib.pyplot as plt

from __future__ import print_function

#绘图设置
%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # 绘图默认大小
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'


# 更多重载外部Python模块的魔法命令查看 http://stackoverflow.com/questions/1907993/autoreload-of-modules-in-ipython
%load_ext autoreload
%autoreload 2

3.加载CIFAR-10数据并预处理

首先进入项目目录下的cs231n/datasets目录,有一个get_datasets.sh脚本文件:

# Get CIFAR10
wget http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
tar -xzvf cifar-10-python.tar.gz
rm cifar-10-python.tar.gz 

该脚本用于下载cifar-10数据集并解压,然后删除压缩包。Mac用户可能会报错,找不到wget命令,此时可以使用Mac包管理工具homebrew,在命令行输入 brew install wget 安装wget即可(如果没有安装homebrew的话自行百度安装)。

在当前目录下,打开命令行,执行该脚本文件./get_datasets.sh,得到解压后的数据集:

# 加载原始 CIFAR-10 数据.
cifar10_dir = 'cs231n/datasets/cifar-10-batches-py'
X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)

# 查看训练集和测试集的大小
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)

查看#cs231n/data_utils.py 中的load_CIFAR10函数,它用于加载CIFAR-10数据集:

def load_CIFAR10(ROOT):
  """ 
  加载所有的cifar batch
  
  input:
  ROOT:解压后cifar数据集的路径
  
  output:
  Xtr:训练集 四维数组(50000,32,32,3)
  Ytr:训练集图像的标签 一维数组 (50000,)  取值0-9 10个类别
  Xte:测试集 四维数组(10000,32,32,3)
  Yte:测试集图像的标签 一维数组 (10000,) 取值0-9 10个类别
  """
  
  xs = []  #列表 用于存储cifar训练集 各个batch的数据(四维数组)
  ys = []  #列表 用于存储cifar训练集 各个batch的标签数据(一维数组)
  for b in range(1,6):#b是 batch的编号
    f = os.path.join(ROOT, 'data_batch_%d' % (b, )) #得到各个bacth数据的完整路径
    X, Y = load_CIFAR_batch(f) #得到各个batch中的图片和标签
    xs.append(X) #将每个batch的图片 四维数组 追加到xs中
    ys.append(Y) #将每个batch的图片标签 一维数组 追加到ys中   
  Xtr = np.concatenate(xs)  #将列表中所有的四维数组拼接起来  得到完整的训练集图片
  Ytr = np.concatenate(ys)  #将列表中所有的一维数组拼接起来 得到完整的训练集标签
  del X, Y    #删除中间变量X,Y
  Xte, Yte = load_CIFAR_batch(os.path.join(ROOT, 'test_batch')) #得到测试集图片和标签
  return Xtr, Ytr, Xte, Yte

查看load_CIFAR_batch函数:

def load_CIFAR_batch(filename):
  """ 
  加载cifar一个batch的数据
  
  input:
  filename:batch的完整路径
  
  output:
  X:batch中的所有图片 四维数组(10000,32,32,3)
  Y:batch中所有图片标签 一维数组(10000,)
  """
  with open(filename, 'rb') as f: #打开文件 以二进制读取
    datadict = load_pickle(f)  #得到数据字典
    X = datadict['data']     #得到batch中的所有图片 
    Y = datadict['labels']   #得到batch中图片的标签 
    X = X.reshape(10000, 3, 32, 32).transpose(0,2,3,1).astype("float") #将X转型为(10000,3,32,32)四维数组,并调换一下各个轴  得到(10000,32,32,3)四维数组  数值类型为float(np.float64)
    Y = np.array(Y) #将Y变为一维数组
    return X, Y

查看load_pickle函数:

import platform
from six.moves import cPickle as pickle

def load_pickle(f):
    version = platform.python_version_tuple()#得到Python版本  
    if version[0] == '2': #针对Python2
        return  pickle.load(f)
    elif version[0] == '3':  #针对Python3
        return  pickle.load(f, encoding='latin1')
    raise ValueError("invalid python version: {}".format(version))

#可视化数据集中的一些样本/图像
#训练集中的每个类别都展示几张
classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
num_classes = len(classes) #类别数 10
samples_per_class = 7   #每个类别展示7张
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()

# 把数据集分为训练集、验证集和测试集.
#此外还设置了一个dev set,它是训练集的一个子集
#我们可以将它(dev set)用于测试我们编写的代码,速度更快
num_training = 49000 #训练集大小
num_validation = 1000 #验证集大小
num_test = 1000  #测试集大小
num_dev = 500   #开发集大小

#验证集中的样本是原始训练集中的num_validation(1000)个样本
mask = range(num_training, num_training + num_validation)
X_val = X_train[mask]
y_val = y_train[mask]

#训练集是原始训练集中的前num_training(49000)个样本
mask = range(num_training)
X_train = X_train[mask]
y_train = y_train[mask]

#我们还设置了开发集 它是训练集中的一个小子集 从49000个训练样本中随机不放回取500个
mask = np.random.choice(num_training, num_dev, replace=False)
X_dev = X_train[mask]
y_dev = y_train[mask]

#测试集是原始测试集中的前num_test(1000)个样本
mask = range(num_test)
X_test = X_test[mask]
y_test = y_test[mask]

print('Train data shape: ', X_train.shape)
print('Train labels shape: ', y_train.shape)
print('Validation data shape: ', X_val.shape)
print('Validation labels shape: ', y_val.shape)
print('Test data shape: ', X_test.shape)
print('Test labels shape: ', y_test.shape)

# 预处理 将每张图像(32,32,3)拉伸为一维数组(3072,)
#各个数据集从四维数组(m,32,32,3) 转型为2维数组(m,3072)
X_train = X_train.reshape((X_train.shape[0],-1))
X_val = X_val.reshape((X_val.shape[0],-1))
X_test = X_test.reshape((X_test.shape[0],-1))
X_dev = X_dev.reshape((X_dev.shape[0],-1))

'''
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))
X_dev = np.reshape(X_dev, (X_dev.shape[0], -1))
'''
# 各个数据集转型后的大小
print('Training data shape: ', X_train.shape)
print('Validation data shape: ', X_val.shape)
print('Test data shape: ', X_test.shape)
print('dev data shape: ', X_dev.shape)

#预处理 0均值化 减去图像每个像素/特征上的平均值
#基于训练数据 计算图像每个像素/特征上的平均值
mean_image = np.mean(X_train,axis=0) #(3072,) 
print(mean_image[:10])  #查看前10个元素  
plt.figure(figsize=(4,4))
plt.imshow(mean_image.reshape((32,32,3)).astype('uint8')) #可视化平均图像

#各个数据集减去基于训练集计算的各个像素/特征的平均值
#(m,3072) - (3072,)  广播运算
X_train -= mean_image
X_val -= mean_image
X_test -= mean_image
X_dev -= mean_image
#为各个数据集的特征矩阵加一列1  方便矩阵运算  
#将偏置参数加到权重参数中  只需要优化一个权重矩阵W即可
X_train = np.hstack([X_train,np.ones((X_train.shape[0],1))])
X_val = np.hstack([X_val, np.ones((X_val.shape[0], 1))])
X_test = np.hstack([X_test, np.ones((X_test.shape[0], 1))])
X_dev = np.hstack([X_dev, np.ones((X_dev.shape[0], 1))])
print(X_train.shape, X_val.shape, X_test.shape, X_dev.shape)

3.LinearSVM分类器

再加上正则化项产生的梯度即可。 

def svm_loss_naive(W, X, y, reg):
  """
  结构化SVM损失函数和梯度,使用循环进行实现.

  输入有D维,有C个类别,在包含N个样本的mini-batch上执行.

  Inputs:
  - W: 2维数组 (D, C) 包含权重.
  - X: 2维数组(N, D) 一个mini-batch的N个训练样本特征矩阵 每一行代表一个样本的特征向量.
  - y: 一维数组 (N,) 包含训练样本的标签; y[i] = c 意味着
    训练样本 X[i]的类别标签是 c, 0 <= c < C.
  - reg: (float)正则化惩罚系数/正则化强度

  Returns a tuple of:
  - loss: 损失
  - dW: 权重W的梯度 与W同维
  """
  dW = np.zeros(W.shape) # 初始化梯度为0

  # 计算损失和梯度
  num_classes = W.shape[1]  #类别数
  num_train = X.shape[0] #mini-batch中的训练样本数
  loss = 0.0
  for i in range(num_train):  #遍历mini-batch上的每个训练样本
    scores = X[i].dot(W)    #得到训练样本的得分向量
    correct_class_score = scores[y[i]] #正确类别的得分
    for j in range(num_classes): #遍历每个类别索引
      if j == y[i]:  #如果是正确类别索引 跳过
        continue
      #否则 计算损失 
      margin = scores[j] - correct_class_score + 1 # 注意 delta = 1
      if margin > 0:
        loss += margin
        dW[:,j] += X[i]
        dW[:,y[i]] += (-X[i])
  #loss是mini-batch上所有训练样本的数据损失之和
  #计算平均数据损失
  loss /= num_train
  
  # 加上正则化损失 得到完整损失
  #注意我们一般只惩罚权重参数  之前我们把偏置参数加到了权重参数中 所以统一当作权重处理
  #在神经网络中 我们会把他们分开
  #可以把W中的权重参数单独拿出来进行惩罚  当然我们统一对W权重+偏置参数进行惩罚也是可以的 对最终结果几乎没有影响
  loss += 0.5*reg * np.sum(W * W)  #使用L2惩罚 前面乘以0.5,可以约掉对平方求导时产生的2(笔记中没有加)
  
  #每个训练样本都会产生一个梯度  求平均梯度
  dW /= num_train
  dW += reg*W  #加上正则化项产生的梯度
  

  return loss, dW
# Evaluate the naive implementation of the loss we provided for you:
from cs231n.classifiers.linear_svm import svm_loss_naive
import time

# 随机初始化权重矩阵W 为很小的随机数
W = np.random.randn(3073, 10) * 0.0001 
#基于开发集计算损失
loss, grad = svm_loss_naive(W, X_dev, y_dev, 0.000005)
print('loss: %f' % (loss, ))

查看cs231n/gradient_check.py中的梯度检查函数 grad_check_sparse:

def grad_check_sparse(f, x, analytic_grad, num_checks=10, h=1e-5):
  """
  随机选取W中的num_checks个位置,计算该位置/维度的数值梯度  和该位置的解析梯度analytic_grad进行比较。
  """

  for i in range(num_checks):  #随机选择W中位置的数量
    ix = tuple([randrange(m) for m in x.shape]) #随机选择一个位置 元组形式(,)

    oldval = x[ix]  #得到W中该位置的原始值
    x[ix] = oldval + h # 增加h
    fxph = f(x) #计算f(x+h)  即在W的该位置上加上h后的损失值
    x[ix] = oldval - h # 减小h
    fxmh = f(x) # 计算 f(x - h)
    x[ix] = oldval # 恢复W中该位置的原始值

    grad_numerical = (fxph - fxmh) / (2 * h) #计算W中该位置的数值梯度
    grad_analytic = analytic_grad[ix]  #得到W中该位置的解析梯度
    #2者进行比较
    rel_error = abs(grad_numerical - grad_analytic) / (abs(grad_numerical) + abs(grad_analytic))
    print('numerical: %f analytic: %f, relative error: %e' % (grad_numerical, grad_analytic, rel_error))

#基于开发集计算损失和权重W的梯度.
loss, grad = svm_loss_naive(W, X_dev, y_dev, 0.0)

#在W中随机选几个位置 计算数值梯度 然后和该位置的解析梯度进行比较 
#梯度值应该是匹配的
#导入cs231n/gradient_check.py中的梯度检查函数 grad_check_sparse
from cs231n.gradient_check import grad_check_sparse
#定义匿名函数f  他的输入是不同的权重W,输出在当前W下的损失值  内部调用了之前计算loss的函数
f = lambda w: svm_loss_naive(w, X_dev, y_dev, 0.0)[0]
grad_numerical = grad_check_sparse(f, W, grad)

#在包含正则化的情况下 reg参数不为0  进行梯度检查 因为正则化部分也会产生梯度
loss, grad = svm_loss_naive(W, X_dev, y_dev, 5e1)
f = lambda w: svm_loss_naive(w, X_dev, y_dev, 5e1)[0]
grad_numerical = grad_check_sparse(f, W, grad)

实现 svm_loss_vectorized 向量化版本:

def svm_loss_vectorized(W, X, y, reg):
  """
  结构化SVM损失函数和梯度 向量化实现版本.
   
  输入输出同svm_loss_naive.
  """
  loss = 0.0
  dW = np.zeros(W.shape) # 初始化W的梯度
  num_classes = W.shape[1]  #类别数
  num_train = X.shape[0] #训练样本数

  scores = X.dot(W)  #(N,C)  每行对应一个训练样本的得分向量
  correct_class_scores = scores[np.arange(num_train),y].reshape((num_train,1)) #得到每个训练样本正确类别的得分  (N,1)
  margins = scores - correct_class_scores + 1 #delta=1 广播 (N,C)
  margins[np.arange(num_train),y] = 0
  loss = np.sum(margins[margins>0])/num_train #数据损失  各训练样本损失的均值
  loss += 0.5*reg*np.sum(W*W)  #加上正则化损失
  
  margins[margins>0] = 1  #不满足正确类别得分比不正确>delta 为1
  margins[margins<=0] = 0 #满足为0
  
  row_sum = np.sum(margins,axis=1) #计算对于每个样本来说不满足的数量  每行代表一个样本
  margins[np.arange(num_train),y] = -row_sum
  dW += np.dot(X.T,margins)  #(D,C)
  #每个训练样本都会产生一个梯度  求平均梯度
  dW /= num_train
  dW += reg*W  #加上正则化项产生的梯度
  
 
  return loss, dW
# 实现 svm_loss_vectorized 向量化版本; 
#现在仅仅计算loss  之后再计算梯度
#比较向量化版本和非向量化版本的时间
tic = time.time()
loss_naive, grad_naive = svm_loss_naive(W, X_dev, y_dev, 0.000005)
toc = time.time()
print('Naive loss: %e computed in %fs' % (loss_naive, toc - tic))

from cs231n.classifiers.linear_svm import svm_loss_vectorized
tic = time.time()
loss_vectorized, _ = svm_loss_vectorized(W, X_dev, y_dev, 0.000005)
toc = time.time()
print('Vectorized loss: %e computed in %fs' % (loss_vectorized, toc - tic))

# 两个版本计算的loss应该相同
print('difference: %f' % (loss_naive - loss_vectorized))

# 实现 svm_loss_vectorized 向量化版本, 计算梯度

# 比较向量化版本和非向量化版本的速度
tic = time.time()
_, grad_naive = svm_loss_naive(W, X_dev, y_dev, 0.000005)
toc = time.time()
print('Naive loss and gradient: computed in %fs' % (toc - tic))

tic = time.time()
_, grad_vectorized = svm_loss_vectorized(W, X_dev, y_dev, 0.000005)
toc = time.time()
print('Vectorized loss and gradient: computed in %fs' % (toc - tic))

#loss是一个实数所以很好比较
#W的梯度和W同维是一个矩阵  所以我们使用F范数对其进行比较
difference = np.linalg.norm(grad_naive - grad_vectorized, ord='fro')
print('difference: %f' % difference)

4.mini-batch梯度下降

#在cs231n/classifiers/linear_classifier.py中实现mini-batch梯度下降
#实现并调用LinearSVM类中的train()方法(父类中的方法)
#从cs231n/classifiers/linear_classifier.py中导入LinearSVM类
from cs231n.classifiers import LinearSVM
svm = LinearSVM() #实例化类对象
tic = time.time()
#调用train方法
loss_hist = svm.train(X_train, y_train, learning_rate=1e-7, reg=2.5e4,
                      num_iters=1500, verbose=True)
toc = time.time()
print('That took %fs' % (toc - tic))

编写父类train方法:

def train(self, X, y, learning_rate=1e-3, reg=1e-5, num_iters=100,
            batch_size=200, verbose=False):
    """
    使用mini-batch梯度下降 训练线性分类器.

    Inputs:
    - X: 完整训练集特征矩阵,2维数组 (N, D) 包含N个训练样本,每个样本是D维的 每行代表一个训练样本的特征向量
    - y: 一维数组 (N,) 包含训练样本的标签; y[i] = c
      意味着训练样本 X[i] 的类别标签是c 0 <= c < C .
    - learning_rate: (float) 学习率.
    - reg: (float) 正则化惩罚系数/正则化强度.
    - num_iters: (integer) 优化迭代次数 每次使用一个mini-batch的数据
    - batch_size: (integer) mini-batch大小/包含的训练样本数.
    - verbose: (boolean) true的话 每100次迭代打印一次损失.

    Outputs:
    列表,包含每次迭代的损失
    """
    num_train, dim = X.shape
    num_classes = np.max(y) + 1 #类别数 y:0~C-1  C
    if self.W is None:
      # 随机初始化权重W  (D,C)  很小的符合标准正态分布的随机数
      self.W = 0.001 * np.random.randn(dim, num_classes)

    # 运行mini-batch梯度下降来优化权重W
    loss_history = []
    for it in range(num_iters):  #num_iters次梯度下降迭代 优化loss,更新权重
      X_batch = None
      y_batch = None
      #每次从训练集X中随机有放回的取样batch_size个样本 作为一个mini-batch
      random_Index = np.random.choice(len(X),batch_size,replace=True)
      X_batch = X[random_Index]
      y_batch = y[random_Index]
      #基于该mini-batch的训练样本 计算loss 和梯度
      loss,grad = self.loss(X_batch,y_batch,reg)
      loss_history.append(loss) #保存每个mini-batch上的损失
      
      #使用梯度下降法更新权重
      self.W += (-learning_rate*grad)
      
      if verbose and it % 100 == 0:
        print('iteration %d / %d: loss %f' % (it, num_iters, loss))
    return loss_history

查看父类的loss方法:

  def loss(self, X_batch, y_batch, reg):
    """
    计算损失和梯度. 
    子类会重写这个方法.

    Inputs:
    - X_batch: 一个mini-batch训练数据的特征矩阵,2维数组 (N, D) 包含N个训练样本,每个样本是D维的 每行代表一个训练样本的特征向量
    - y_batch: 一维数组 (N,) 包含mini-batch训练样本的标签; y[i] = c
      意味着训练样本 X[i] 的类别标签是c 0 <= c < C .
    - reg: (float) 正则化惩罚系数/正则化强度.

    Returns: A tuple containing:
    - loss 实数损失值
    - gradient:self.W的梯度 与W同维
    """
    pass

查看LinearSVM子类:

class LinearSVM(LinearClassifier):
    
  """ LinearSVM类继承LinearClassifier类
  """
  #重写父类的loss方法  计算多分类SVM的损失
  def loss(self, X_batch, y_batch, reg):
    return svm_loss_vectorized(self.W, X_batch, y_batch, reg)
# 绘制loss随迭代次数的变化曲线  每次迭代使用一个mini-batch的数据
plt.plot(loss_hist)
plt.xlabel('Iteration number')
plt.ylabel('Loss value')
plt.show()

#实现并调用LinearSVM父类中的predict()方法
#计算训练好的模型在训练集和验证集上的准确率
y_train_pred = svm.predict(X_train)
print('training accuracy: %f' % (np.mean(y_train == y_train_pred), ))
y_val_pred = svm.predict(X_val)
print('validation accuracy: %f' % (np.mean(y_val == y_val_pred), ))

编写父类的predict方法:

def predict(self, X):
    """
    使用线性分类器训练好的权重 预测样本的标签

    Inputs:
    - X: 2维数组 (N, D) 待预测数据的特征矩阵 有N个样本,每个样本的特征向量是D维的  

    Returns:
    - y_pred: 为X中的样本预测的标签. 1维数组(N,), 其中的每一项是为该样本预测的类别标签索引 0~C-1
      
    """
    y_pred = np.zeros(X.shape[0])
    scores = X.dot(self.W) #(N,C)
    y_pred = np.argmax(scores,axis=1)
    
    return y_pred

5.在验证集上调试超参数

# 使用验证集调试超参数(学习率和正则化强度)/进行模型选择 
#接下来会尝试 不同的学习率和正则化强度的选择 如果顺利的话 应该可以在验证集上达到40%的准确率
learning_rates = [1.4e-7, 1.5e-7, 1.6e-7]
regularization_strengths = [8000.0, 9000.0, 10000.0, 11000.0, 18000.0, 19000.0, 20000.0, 21000.0]


# results 是一个字典
# 键:(learning_rate, regularization_strength) 
# 值:(training_accuracy, validation_accuracy). 
results = {}
best_val = -1   # 存储最高的验证集准确率
best_svm = None # 实现最高验证集准确率的linear_svm对象

'''
编写通过验证集调试超参数的代码,选择一组最好的超参数。 对于超参数的每个组合,在训练集上训练线性SVM
分类器,计算训练和验证集的准确性,将这些数据存储在结果字典中。 另外,在best_val存储最好的验证集准确率,
在best_svm存储实现最高验证集准确率的linear_svm对象
'''

'''
运行验证代码时可以先使用一个小的num_iters值,这样SVM分类器不会花很多时间去训练, 当你确信验证代码
正确工作(在当前超参数设置下,刚开始每次迭代后的loss在减小)时,再用一个大的num_iters值来运行验证代码。
'''
#当前共有3*8=24种组合
for lr in learning_rates:
    for reg in regularization_strengths:
        svm = LinearSVM() #实例化类对象
        #调用train方法
        loss_hist = svm.train(X_train, y_train, learning_rate=lr, reg=reg,
                      num_iters=1500, verbose=True)
        y_train_pred = svm.predict(X_train)
        train_accuracy = np.mean(y_train == y_train_pred)
        y_val_pred = svm.predict(X_val)
        val_accuracy = np.mean(y_val == y_val_pred)
        if val_accuracy > best_val:
            best_val = val_accuracy
            best_svm = svm
        results[(lr,reg)] = (train_accuracy,val_accuracy)
    
# 打印结果
for lr, reg in sorted(results):
    train_accuracy, val_accuracy = results[(lr, reg)]
    print('lr %e reg %e train accuracy: %f val accuracy: %f' % (
                lr, reg, train_accuracy, val_accuracy))
    
print('best validation accuracy achieved during cross-validation: %f' % best_val)

# 可视化验证/交叉验证结果
import math
x_scatter = [math.log10(x[0]) for x in results] 
y_scatter = [math.log10(x[1]) for x in results]

# 可视化训练集准确率
marker_size = 100
colors = [results[x][0] for x in results]
plt.subplot(2, 1, 1)
plt.scatter(x_scatter, y_scatter, marker_size, c=colors)
plt.colorbar()
plt.xlabel('log learning rate')
plt.ylabel('log regularization strength')
plt.title('CIFAR-10 training accuracy')


# 可视化验证集准确率
colors = [results[x][1] for x in results] 
plt.subplot(2, 1, 2)
plt.scatter(x_scatter, y_scatter, marker_size, c=colors)
plt.colorbar()
plt.xlabel('log learning rate')
plt.ylabel('log regularization strength')
plt.title('CIFAR-10 validation accuracy')
plt.show()

6.在测试集上测试

# 使用最好的SVM模型在测试集上进行测试
y_test_pred = best_svm.predict(X_test)
test_accuracy = np.mean(y_test == y_test_pred)
print('linear SVM on raw pixels final test set accuracy: %f' % test_accuracy)

# 可视化每个类别学到的权重

w = best_svm.W[:-1,:] # 从W中取出权重参数 去掉偏置参数 (D=32*32*3,C=10)  每一列对应一个类别
w = w.reshape(32, 32, 3, 10) #对w进行转型 把每一列转化为图像维度
w_min, w_max = np.min(w), np.max(w)
classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
for i in range(10):
    plt.subplot(2, 5, i + 1)
      
    # 把权重值变换到0-255取值范围   变成图像,进行可视化
    wimg = 255.0 * (w[:, :, :, i].squeeze() - w_min) / (w_max - w_min)
    plt.imshow(wimg.astype('uint8'))
    plt.axis('off')
    plt.title(classes[i])

猜你喜欢

转载自blog.csdn.net/sdu_hao/article/details/86617357