• 编程作业3.1:Multi-class classification(One-vs-all)


    题目:

    • 在本次练习中,你将使用逻辑回归神经网络来识别手写数字(从0到9)。
    • 今天,自动手写数字识别被广泛使用,从识别信封上的邮政编码到识别银行支票上的金额。这个练习将向你展示如何将你所学的方法用于此分类任务。
    • 在第一部分中,将扩展以前的逻辑回归,并将其应用于one-vs-all分类。
    • 关于数据:本次的数据是以.mat格式储存的,mat格式是matlab的数据存储格式,按照矩阵保存,与numpy数据格式兼容,适合于各种数学运算,因此这次主要使用numpy进行运算。
    • ex3data1中有5000个训练样例,其中每个训练样例是一个20像素×20像素灰度图像的数字,每个像素由一个浮点数表示,该浮点数表示该位置的灰度强度。每个20×20像素的网格被展开成一个400维的向量。这些每个训练样例都变成数据矩阵X中的一行。这就得到了一个5000×400矩阵X,其中每一行都是手写数字图像的训练样例。训练集的第二部分是一个包含训练集标签的5000维向量y,“0”的数字标记为“10”,而“1”到“9”的数字按自然顺序标记为“1”到“9”。

    编程实现

    1.Visualizing the data

    加载数据集。这里的数据为MATLAB的格式,所以要使用SciPy.io的loadmat函数。

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from scipy.io import loadmat
    
    # 这里的数据为MATLAB的格式,所以要使用SciPy.io的loadmat函数。
    def load_data(path):
        data = loadmat(path)
        X = data['X']
        y = data['y']
        return X,y
    
    X, y = load_data('D:BaiduNetdiskDownloaddata_setsex3data1.mat')
    
    # unique() 统计list中的不同值时,返回的是array;用来看下有几类标签
    print(np.unique(y)) 
    
    X.shape, y.shape
    

    其中有5000个训练样本,每个样本是20*20像素的数字的灰度图像。每个像素代表一个浮点数,表示该位置的灰度强度。20×20的像素网格被展开成一个400维的向量。在我们的数据矩阵X中,每一个样本都变成了一行,这给了我们一个5000×400矩阵X,每一行都是一个手写数字图像的训练样本。

    def plot_an_image(X):
        """
        随机打印一个数字
        """
        pick_one = np.random.randint(0, 5000) # np.random.randint(a,b):用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限,生成的随机数n:a<=n<b,即[a,b)
        image = X[pick_one, :]
        fig, ax = plt.subplots(figsize=(1, 1))
        ax.matshow(image.reshape((20, 20)), cmap='gray_r') # reshape()是数组对象中的方法,用于改变数组的形状。参数cmap='gray_r'是白底黑字的
        plt.xticks([])  # 去除刻度,美观
        plt.yticks([])
        plt.show()
        print('this should be {}'.format(y[pick_one]))
    
    plot_an_image(X)
    

    #定义可视化函数
    def plot_100_image(X):
        """ 
        随机画100个数字
        """
        sample_idx = np.random.choice(np.arange(X.shape[0]), 100)  # 随机选100个样本
        sample_images = X[sample_idx, :]  # (100,400)
        
        fig, ax_array = plt.subplots(nrows=10, ncols=10, sharey=True, sharex=True, figsize=(8, 8))
    
        for row in range(10):
            for column in range(10):
                ax_array[row, column].matshow(sample_images[10 * row + column].reshape((20, 20)),
                                       cmap='gray_r')
        plt.xticks([])
        plt.yticks([])        
        plt.show()
    
    plot_100_image(X)
    

    2.Regularized Cost function

    正则化逻辑回归的代价函数如下:

    [J( heta)=frac1m sum_{i=1}^m left( - y^{left(i ight)} log left( h_ heta left( x^{left( i ight)} ight) ight) - left( 1-y^{left( i ight)} ight) log left( 1- h_ heta left( x^{left( i ight)} ight) ight) ight) + frac {lambda}{2m} sum_{j=1}^n heta_j^2 ]

    注意: 不惩罚第一项( heta_0)

    def sigmoid(z):
        return 1 / (1 + np.exp(-z))
    
    # 定义带正则项的代价函数
    def regularized_cost(theta, X, y, l):
        """
        don't penalize theta_0
        args:
            X: feature matrix, (m, n+1) # 插入了x0=1
            y: target vector, (m, )
            l: lambda constant for regularization
        """
        thetaReg = theta[1:] # 将theta的第二列到最后一列的内容赋给theta
        first = (-y*np.log(sigmoid(X@theta))) + (y-1)*np.log(1-sigmoid(X@theta))
        reg = (thetaReg@thetaReg)*l / (2*len(X))
        return np.mean(first) + reg
    

    3.Regularized gradient

    因为我们未对({ heta }_{0})进行正则化,所以梯度下降算法将分两种情形:

    [frac{partial }{partial heta_0} J( heta)=frac1m sum_{i=1}^m left( h_ heta left( x^{ left(i ight) } ight) -y^{ left(i ight) } ight) x_{0}^{left(i ight)} ]

    [frac{partial }{partial heta_j} J( heta)=frac1m sum_{i=1}^m left( h_ heta left( x^{ left(i ight) } ight) -y^{ left(i ight) } ight) x_{j}^{left(i ight)} + frac lambda m heta_j,其中j=1,2,cdots,n ]

    # 定义正则化梯度值(导数值)
    def regularized_gradient(theta, X, y, l):
        """
        don't penalize theta_0
        args:
            l: lambda constant for regularization
        return:
            a vector of gradient
        """
        thetaReg = theta[1:]
        first = (1 / len(X)) * X.T @ (sigmoid(X @ theta) - y)
        
        # 这里插入一维0,使得对theta_0不惩罚,方便计算
        reg = np.concatenate([np.array([0]), (l / len(X)) * thetaReg]) # 将具有相同结构的array序列结合成一个array
        return first + reg
    

    4.One-vs-all Classification

    这部分我们将实现一对多分类通过训练多个正则化logistic回归分类器,每个对应数据集中K类中的一个。利用for循环对每种数字习得一个带正则的逻辑回归分类器,然后将10个分类器的参数组成一个参数矩阵all_theta返回。

    from scipy.optimize import minimize
    
    #计算K个类别分别训练出来的参数theta
    def one_vs_all(X, y, l, K):
        """generalized logistic regression
        args:
            X: feature matrix, (m, n+1) # with incercept x0=1
            y: target vector, (m, )
            l: lambda constant for regularization
            K: numbel of labels
        return: trained parameters
        """
        all_theta = np.zeros((K, X.shape[1]))  # (10, 401)
        
        for i in range(1, K+1):
            theta = np.zeros(X.shape[1])
            y_i = np.array([1 if label == i else 0 for label in y]) # 如果y对应的值和i相等就返回1,否则返回0
        
            # minimize是局部最优的解法
            # fun: 求最小值的目标函数
            # x0:变量的初始猜测值,如果有多个变量,需要给每个变量一个初始猜测值
            # args:是传递给优化函数的参数
            # method:求极值的方法
            # jac:计算梯度向量的方法
            # options:A dictionary of solver options. 
            ret = minimize(fun=regularized_cost, x0=theta, args=(X, y_i, l), method='TNC',
                            jac=regularized_gradient, options={'disp': True})
            all_theta[i-1,:] = ret.x
                             
        return all_theta
    

    对于这个任务,我们有10个可能的类,并且由于logistic回归只能一次在2个类之间进行分类,每个分类器在“类别 i”和“不是 i”之间决定。 我们将把分类器训练包含在一个函数中,该函数计算10个分类器中的每个分类器的最终权重,并将权重返回shape为(k, (n+1))数组,其中 n 是参数数量。

    def predict_all(X, all_theta):
        
        # compute the class probability for each class on each training instance   
        # 这里的h共5000行,10列,每行代表一个样本,每列是预测对应数字的概率。
        h = sigmoid(X @ all_theta.T)  # 注意的这里的all_theta需要转置
        
        # create array of the index with the maximum probability
        # Returns the indices of the maximum values along an axis.
        h_argmax = np.argmax(h, axis=1) # 返回h矩阵中每一行的最大索引
        
        # because our array was zero-indexed we need to add one for the true label prediction
        h_argmax = h_argmax + 1 #概率最大对应的index加1就是我们分类器最终预测出来的类别
        
        return h_argmax
    

    这里的h共5000行,10列,每行代表一个样本,每列是预测对应数字的概率,是一个5000乘10的预测概率矩阵。我们取概率最大对应的index加1就是我们分类器最终预测出来的类别。返回的h_argmax是一个array,包含5000个样本对应的预测值。

    5.Learning θ parameters

    raw_X, raw_y = load_data('D:BaiduNetdiskDownloaddata_setsex3data1.mat')
    
    X = np.insert(raw_X, 0, 1, axis=1) # 为X添加了一列常数项 1,(5000, 401)
    
    y = raw_y.flatten()  # 这里消除了一个维度,方便后面的计算 or .reshape(-1) (5000,)
    
    all_theta = one_vs_all(X, y, 1, 10)
    all_theta  # 每一行是一个分类器的一组参数
    

    6.Evaluating logistic regression

    y_pred = predict_all(X, all_theta)
    accuracy = np.mean(y_pred == y)
    print ('accuracy = {0}%'.format(accuracy * 100))
    

    总结

    有K个分类器,每个分类要针对其中一种情况进行训练。也就是每个分类分别当做1,不是这个分类的当做0,进行上次作业中的逻辑回归的二分类任务。

    参考资料

    minimize官方文档

    吴恩达机器学习作业Python实现(三):多类分类和前馈神经网络

    吴恩达|机器学习作业3.0.逻辑回归解决多元分类

  • 相关阅读:
    Linux系统操作问题汇总
    记录一些mysql数据库常用操作命令和问题汇总
    python学习之路-练习小程序02(模拟用户登录)
    python学习之路02(基础篇2)
    python学习之路-练习小程序01(猜年龄)
    python学习之路01(基础篇1)
    hashmap详解(基于jdk1.8)
    maven创建项目太慢怎么办
    CAS原理
    JUC原子类3-AtomicLongArray原子类
  • 原文地址:https://www.cnblogs.com/yangdd/p/12343592.html
Copyright © 2020-2023  润新知