计算机视觉与深度学习(北邮)---网课学习 线性分类器实现

正在代码理解中

softman.py

import numpy as np
from random import shuffle

def softmax_loss_naive(weight , X , y, reg):
    """用循环实现softmax损失函数 D,C,N分别表示数据维度,标签种类个数和数据批大小
     Inputs: - weight (D, C):weights. - X (N, D):data. - y (N,): labels - reg: (float) regularization strength
    Returns : - loss - gradient """
    loss = 0.0
    dweight = np.zeros_like(weight)
    num_classes = weight.shape[1]
    num_train = X.shape[0]
    for i in range(num_train):
        scores = np.dot(X[i],weight)
        shift_scores = scores-max(scores)
        dom = np.log(np.sum(np.exp(shift_scores)))
        loss_i = -shift_scores[y[i]]+dom
        loss += loss_i
        for j in range(num_classes):
            softmax_output = np.exp(shift_scores[j])/sum(np.exp(shift_scores))
            if j == y[i]:
                dweight[:, j] += (-1 + softmax_output) * X[i].T
            else:
                dweight[:,j] += softmax_output * X[i].T
                loss /= num_train
                loss += reg * np.sum(weight * weight)
                dweight = dweight/num_train + 2*reg*weight

    return loss, dweight

def softmax_loss_vectorized(weight, X, y, reg):
    """无循环的实现"""
    loss = 0.0
    dweight = np.zeros_like(weight)
    num_classes = weight.shape[1]
    num_train = X.shape[0]
    scores = np.dot(X,weight)
    shift_scores = scores-np.max(scores,axis=1).reshape(-1,1)
    softmax_output = np.exp(shift_scores)/np.sum(np.exp(shift_scores), axis = 1).reshape(-1,1)
    loss=np.sum(-np.log(softmax_output[range(num_train),y]))
    loss=loss/num_train+reg * np.sum(weight * weight)
    dweight = softmax_output.copy()
    dweight[range(num_train), y] -= 1
    dweight=np.dot(X.T,dweight)
    dweight = dweight/num_train + 2*reg*weight

    return loss, dweight

linear_svm.py

import numpy as np
from random import shuffle

def svm_loss_naive(weight, X, y, reg):
    """用循环实现的SVM loss计算 这里的loss函数使用的是margin loss
    Inputs: - weight (D, C): 权重矩阵. - X (N, D): 批输入 - y (N,) 标签 - reg: 正则参数
    Returns : - loss float - weight的梯度 """
    dweight =np.zeros(weight.shape)
    num_classes = weight.shape[1]
    num_train =X.shape[0]
    loss = 0.0
    for i in range(num_train):
        scores = X[i].dot(weight)
        correct_class_score =scores[y[i]]
        for j in range(num_classes):
            if j ==y[i]:
                continue
            margin = scores[j] - correct_class_score + 1
            if margin >0:
                loss +=margin
                dweight[:,j]+=X[i].T
                dweight[:,y[i]]-=X[i].T
        loss /=num_train
        dweight/=num_train
        loss += reg * np.sum(weight * weight)
        dweight+=2* reg * weight
    return loss, dweight

def svm_loss_vectorized(weight, X, y, reg):
    """不使用循环,利用numpy矩阵运算的特性实现loss和梯度计算51 """
    loss = 0.0
    dweight =np.zeros(weight.shape)
    #计算loss
    num_classes = weight.shape[1]
    num_train =X.shape[0]
    scores=np.dot(X,weight)#得到得分矩阵(N,C)
    correct_socre=scores[range(num_train), list(y)].reshape(-1,1)#得到每个输入的正确分类的分数
    margins=np.maximum(0,scores-correct_socre+1)
    margins[range(num_train), list(y)] =0
    loss=np.sum(margins)/num_train+reg * np.sum(weight * weight)
    #计算梯度
    mask=np.zeros((num_train,num_classes))
    mask[margins>0]=1
    mask[range(num_train),list(y)]-=np.sum(mask,axis=1)
    dweight=np.dot(X.T,mask)
    dweight/=num_train
    dweight+=2* reg * weight
    return loss, dweight

linear_classfication.py

import numpy as np
from linear_svm import *
from softmax import *

class LinearClassifier(object):#线性分类器的基类
    def __init__(self):
        self.weight =None
    def train(self, X, y, learning_rate=1e-3, reg=1e-5, num_iters=100, batch_size=200, verbose=False):
        """使用SGD优化参数矩阵
        Inputs: - X (N, D) - y (N,) - learning_rate: 学习率. - reg: 正则参数. - num_iters: (int) 训练迭代的次数
         - batch_size: (int) 每次迭代使用的样本数量. - verbose: (boolean) 是否显示训练进度
        Outputs: 返回一个list保存了每次迭代的loss """
        num_train, dim =X.shape
        num_classes = np.max(y) + 1 #假设有k类,y的取值为【0,k-1】且最大的下标一定会在训练数据中出现
        #初始化权重矩阵
        if self.weight is None:
            self.weight = 0.001 *np.random.randn(dim, num_classes)
        loss_history =[]
        for it in range(num_iters): #在每次迭代,随机选择batch_size个数据
            mask=np.random.choice(num_train,batch_size,replace=True)
            X_batch =X[mask]
            y_batch =y[mask]
            #计算损失和梯度
            loss, grad =self.loss(X_batch, y_batch, reg)
            loss_history.append(loss)
            #更新参数
            self.weight -= grad* learning_rate
            if verbose and it % 100 ==0:
                print('iteration %d / %d: loss %f' %(it, num_iters, loss))

        return loss_history
    def predict(self, X):
        """ 使用训练好的参数来对输入进行预测60
        Inputs: - X (N, D) Returns: - y_pred (N,):预测的正确分类的下标 """
        y_pred=np.dot(X,self.W)
        y_pred = np.argmax(y_pred, axis = 1)
        return y_pred
    def loss(self, X_batch, y_batch, reg):
        """这只是一个线性分类器的基类 不同的线性分类器loss的计算方式不同 所以需要在子类中重写 """
        pass

class LinearSVM(LinearClassifier):
    """使用SVM loss"""
    def loss(self, X_batch, y_batch, reg):
        return svm_loss_vectorized(self.weight, X_batch, y_batch, reg)
class Softmax(LinearClassifier):
    """使用交叉熵"""
    def loss(self, X_batch, y_batch, reg):
        return softmax_loss_vectorized(self.weight, X_batch, y_batch, reg)

data.utils.py

 #获取数据的部分
from six.moves import cPickle as pickle
import numpy as np
import os
from imageio import imread
import platform

def load_pickle(f):
    version =platform.python_version_tuple()
    if version[0] == '2':
        return pickle.load(f)
    elif version[0] == '3':
        return pickle.load(f, encoding='latin1')
    raise ValueError("invalid python version: {}".format(version))

def load_CIFAR_batch(filename):
    """CIRAR的数据是分批的,这个函数的功能是载入一批数据"""
    with open(filename, 'rb') as f:
         datadict = load_pickle(f) #以二进制方式打开文件
    X = datadict['data']
    Y = datadict['labels']
    X = X.reshape(10000, 3, 32, 32).transpose(0,2,3,1).astype("float")
    Y =np.array(Y)

    return X, Y

def load_CIFAR10(ROOT):
    """load 所有的数据"""
    xs =[]
    ys =[]
    for b in range(1,6):
        f = os.path.join(ROOT, 'data_batch_%d' %(b, ))
        X, Y =load_CIFAR_batch(f)
        xs.append(X)
        ys.append(Y)
    Xtr =np.concatenate(xs)
    Ytr =np.concatenate(ys)
    del X, Y

    Xte, Yte = load_CIFAR_batch(os.path.join(ROOT, 'test_batch'))

    return Xtr, Ytr, Xte, Yte

def get_CIFAR10_data (num_training=49000, num_validation=1000, num_test=1000, subtract_mean=True):
     """ Load the CIFAR-10 dataset from disk and perform preprocessing to prepare
     it for classifiers. These are the same steps as we used for the SVM, but condensed to a single function. """
     cifar10_dir = 'C:/Users/Dell/Desktop/cv/cifar-10-python/cifar-10-batches-py' #Load the raw CIFAR-10 data
     X_train, y_train, X_test, y_test =load_CIFAR10(cifar10_dir)
     #Subsample the data
     mask = list(range(num_training, num_training +num_validation))
     X_val =X_train[mask]
     y_val =y_train[mask]
     mask =list(range(num_training))
     X_train =X_train[mask]
     y_train =y_train[mask]
     mask =list(range(num_test))
     X_test =X_test[mask]
     y_test =y_test[mask]
     #Normalize the data: subtract the mean image
     if subtract_mean:
         mean_image = np.mean(X_train, axis=0)
     X_train -=mean_image
     X_val -=mean_image
     X_test -=mean_image
     #Transpose so that channels come first
     X_train = X_train.transpose(0, 3, 1, 2).copy()
     X_val = X_val.transpose(0, 3, 1, 2).copy()
     X_test = X_test.transpose(0, 3, 1, 2).copy()
     #Package data into a dictionary
     return { 'X_train': X_train, 'y_train': y_train, 'X_val': X_val, 'y_val': y_val, 'X_test': X_test, 'y_test': y_test,}

test.py

#实现hinge_loss和sotfmax_loss
import random
import numpy as np
from data_utils import load_CIFAR10
import matplotlib.pyplot as plt
from linear_svm import svm_loss_naive,svm_loss_vectorized
from softmax import softmax_loss_naive,softmax_loss_vectorized
import time
from linear_classfication import LinearSVM, Softmax

plt.rcParams['figure.figsize'] = (10.0, 8.0) #set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

###################################第一部分 载入数据并处理###############################
#载入CIFAR10数据.
cifar10_dir = 'C:/Users/Dell/Desktop/cv/cifar-10-python/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)
#每个分类选几个图片显示观察一下
classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
num_classes =len(classes)
samples_per_class = 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()
#把数据分为训练集,验证集和测试集。
#用一个小子集做测验,运行更快。
num_training = 49000
num_validation = 1000
num_test = 1000
num_dev = 500
#数据集本身没有给验证集,需要自己把训练集分成两部分
mask = range(num_training, num_training +num_validation)
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 = np.random.choice(num_training, num_dev, replace=False)
X_dev =X_train[mask]
y_dev =y_train[mask]
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)
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)
print(mean_image.shape)
plt.figure(figsize=(4,4))
plt.imshow(mean_image.reshape((32,32,3)).astype('uint8')) #可视化一下平均值
plt.show()
#第二步: 所有数据都减去刚刚得到的均值
X_train -=mean_image
X_val -=mean_image
X_test -=mean_image
X_dev -=mean_image
#第三步: 给所有的图片都加一个位,并设为1,这样在训练权重的时候就不需要b了,只需要w
#相当于把b的训练并入了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)

###################################第二部分 定义需要用到的函数###########################
def cmp_naiveANDvectorized(naive,vectorized):
    '''每个损失函数都用两种方式实现:循环和无循环(即利用numpy的特性)130 '''
    W = np.random.randn(3073, 10) * 0.0001
    #对比两张实现方式的计算时间
    tic =time.time()
    loss_naive, grad_naive = naive(W, X_dev, y_dev, 0.000005)
    toc =time.time()
    print('Naive computed in %fs' % ( toc -tic))
    tic =time.time()
    loss_vectorized, grad_vectorized = vectorized(W, X_dev, y_dev, 0.000005)
    toc =time.time()
    print('Vectorized computed in %fs' % ( toc -tic))
    #检验损失的实现是否正确,对于随机初始化的数据的权重,
    #softmax_loss应该约等于-log(0.1),svm_loss应该约等于9
    print('loss %f %f' %(loss_naive , loss_vectorized))
    #对比两种实现方式得到的结果是否相同
    print('difference loss %f' % (loss_naive -loss_vectorized))
    difference = np.linalg.norm(grad_naive - grad_vectorized, ord='fro')
    print('difference gradient: %f' %difference)

def cross_choose(Linear_classifier,learning_rates,regularization_strengths):
    '''选择超参数157 '''
    results = {} #存储每一对超参数对应的训练集和验证集上的正确率
    best_val = -1 #最好的验证集上的正确率
    best_model = None #最好的验证集正确率对应的svm类的对象
    best_loss_hist=None
    for rs in regularization_strengths:
        for lr in learning_rates:
            classifier =Linear_classifier
            loss_hist = classifier.train(X_train, y_train, lr, rs, num_iters=5)
            y_train_pred =classifier.predict(X_train)
            train_accuracy = np.mean(y_train ==y_train_pred)
            y_val_pred =classifier.predict(X_val)
            val_accuracy = np.mean(y_val ==y_val_pred)
            if val_accuracy >best_val:
                #print("1")
                best_val =val_accuracy
                best_model =classifier
                best_loss_hist=loss_hist
                results[(lr,rs)] =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)
    #可视化loss曲线
    plt.plot(best_loss_hist)
    plt.xlabel('Iteration number')
    plt.ylabel('Loss value')
    plt.show()
    return results, best_val, best_model

def show_weight(best_model):#看看最好的模型的效果
    #可视化学到的权重
    y_test_pred = best_model.predict(X_test)
    test_accuracy = np.mean(y_test ==y_test_pred)
    print('final test set accuracy: %f' %test_accuracy)
    w = best_model.weight[:-1,:] #去掉偏置参数
    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', '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])
        plt.show()

##########################第三部分 应用和比较svm_loss和softmax_loss######################
cmp_naiveANDvectorized(svm_loss_naive,svm_loss_vectorized)
learning_rates = [(1+i*0.1)*1e-7 for i in range(-3,5)]
regularization_strengths = [(5+i*0.1)*1e3 for i in range(-3,3)]
#正则参数的选择要根据正常损失和W*W的大小的数量级来确定,初始时正常loss大概是9,W*W大概是1e-6
#可以观察最后loss的值的大小来继续调整正则参数的大小,使正常损失和正则损失保持合适的比例
results,best_val,best_model=cross_choose(LinearSVM(),learning_rates,regularization_strengths)
show_weight(best_model)
print("--------------------------------------------------------")
cmp_naiveANDvectorized(softmax_loss_naive,softmax_loss_vectorized)
learning_rates = [(2+i*0.1)*1e-7 for i in range(-2,2)]
regularization_strengths = [(7+i*0.1)*1e3 for i in range(-3,3)]
results,best_val,best_model=cross_choose(Softmax(),learning_rates,regularization_strengths)
show_weight(best_model)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值