美文网首页机器学习程序员
Ng机器学习第五周神经网络反向传播算法作业解析

Ng机器学习第五周神经网络反向传播算法作业解析

作者: Wleisure | 来源:发表于2017-11-27 11:11 被阅读541次

    在学完前面四周的视频课程后,我学会了如何利用matlab和python的spyder编辑器做线性回归拟合直线和逻辑回归分类两件事,而神经网络的反向传播算法的数学公式原理Ng老师没有做详细的推导和说明,在网上的各种资源的帮助下结合这一周的课后作业代码,小葵花妈妈课堂。


    这周的作业干了这么一件事情,小葵花妈妈在黑板上写了5000个阿拉伯数字,1到500个数字写的10,501到1000写的1,以此类推,最后4501到5000写的9,小葵花妈妈想要通过这5000个数字训练小葵花认识1到10这个数字,以后能通过它们的特征认出这10个数字,所以小葵花妈妈对每个数字提取出了400个特征点也就是用400个数来表示,这400个特征数共同决定在这个数字是1到10中的哪一个概率最大,概率最大就认为这个数字是几。


    好,现在题目给了我们一个5000乘以400的矩阵X,和一个5000乘以1的向量y(输出结果,代码中计算误差时,处理成了y_onehot),现在我们只看X其中一行的400个数怎么确定出最后的手写数字是几的。

    我们把这400个数叫这个神经网络的输入层,题目再告诉我们隐藏层的也只有1层,并且这一层有25个数,最后输出层就是10个数,这10个数代表这个阿拉伯数字分别为1到10的概率,选取概率最大的确定为结果y。

    这个决定过程是这样的,把我们原始有的400个特征数最前面加一个1,这个1先理解为除这400个特征数外的所有外界影响,把401个数写成一个1乘以401的矩阵a1(行向量),由题意得,设theta1为401乘以25的矩阵,a1乘以theta1得到1乘以25的矩阵z2(行向量),这个操作可以理解为通过theta1矩阵把400+1个特征数转化为离最终结果更近的25个特征数,不过这个z2刚出生,还没得到认可,不具有特征这个阿拉伯数字的权利,需要进行激活一下,所以a2=g(z2),函数g为sigmoid函数:1 / (1 + exp(-z)),这是上一周逻辑回归的内容,也就是说把z2中的25个数分别带入上面(1+e的(-z)次方)分之1这个函数来得到新的25个数,得到a2,这是1乘以25的行向量,代表激活后的25个特征数。

    image

    再重复上面步骤,在a2前加一个1,(嗯,你懂的),得到这个1乘以26的矩阵(行向量)a2,由题意设theta2为 26乘以10 的矩阵,z3=a2乘以theta2,z3就是1乘以10的矩阵(行向量)了,不要忘了激活一下,a3=g(z3),(嗯,这个你也懂的),好,激活完成了,a3(1*10的哈)这10个数就是最终这个阿拉伯数字分别为1到10的10个概率了,哪个概率最大,就是几了。

    image

    好了,题目背景(也叫正向传播)就是这样了,所以我们想要的就是我们设的未知数theta1和theta2了,求是不可能求出来的了,我们只能找到最好的她们俩让最终结果的误差最小,那这个误差长成什么样子呢,

    image

    Ng的课堂上给的是这个样子的,是一个戴着很多层口罩的妹子(天冷),脱这个口罩很麻烦,只有暴力点了。

    image

    现在应该理解误差(代价函数)是什么意思了,通过前几周的课程,知道还需要知道梯度,才能进行梯度下降,让误差最小,这里的梯度不能像高数里面求出关于未知数的导函数式子,而是求出在确定theta1和theta2位置处的梯度值(导函数值)。

    整个过程是这样的,计算机随机给出theta1和theta2的初始值,因为是随机给的,我们进行一遍正向传播,求出结果a3概率预测,和实际y_onehot概率(或者实际的y值)对比,发现基本都预测错了,计算误差J,发现误差J的值非常大。

    现在我们计算在确定的初始theta1和theta2处的梯度值,计算出的结果是两个和theta1,theta2维数一致的矩阵,代表对应位置参数的梯度值,在最后的代码中,将两个梯度矩阵,按行展开为一整个行向量,初始的theta输入也展开为一整个行向量,连同误差J的计算方法导入minimize优化函数中,进行迭代优化,算出使J计算最小的最优的theta(为一个行向量,最后按维数合成theta1和theta2)。(这一段需要结合代码理解,正则化项可以先不管,理解好了最好再处理正则化,不会的回去复习Ng前面讲正则化的部分)。

    image

    上面的推导过程结合后面的python代码理解,最后把最优的theta1和theta2进行一次正向传播计算a3(h)发现,预测基本准确,如果计算J也非常小,当我用5000组数据进行训练,最后检测这5000组预测 ,正确率有99.98%。当我只用间隔的2500组数据训练,检测整体的5000组数据,正确率还有95%左右。而只用2500数据训练,检测另外的2500组数据时,正确率跌倒91%。

    """
    Created on Tue Nov 21 16:30:57 2017
    
    @author: leisure
    """
    
    # -*- coding: utf-8 -*-
    
    import numpy as np                                  #引用numpy库,用np表示,方便矩阵运算
    from scipy.io import loadmat                        #引用loadmat,读取题目给的输入和正确输出的mat文件
    
    def sigmoid(z):
        return 1 / (1 + np.exp(-z))
    
    def forward_propagate(X, theta1, theta2):          #正向传播程序
        m = X.shape[0] #5000组数据
        
        a1 = np.insert(X, 0, values=np.ones(m), axis=1)  #在X最前 面加一列5000*1的向量
        z2 = a1 * theta1                                 #得到5000*25的矩阵,400个特征转化为25个隐藏特征
        a2 = np.insert(sigmoid(z2), 0, values=np.ones(m), axis=1) #取激活后的z2,再加一列5000*1的向量
        z3 = a2 * theta2                                #得到5000*10的矩阵,最后10个特征
        h = sigmoid(z3)                                 #激活后得到10个概率
        
        return a1, z2, a2, z3, h
    
    def sigmoid_gradient(z):
        return np.multiply(sigmoid(z), (1 - sigmoid(z)))  #s函数求导
    
    def backprop(params, input_size, hidden_size, num_labels, X, y,learning_rate): #反向传播算法计算梯度的函数,最后得到误差J和梯度grad(把两个梯度矩阵按行展开合成一行)
        m = X.shape[0]
        X = np.matrix(X)
        y = np.matrix(y)
        
        # reshape the parameter array into parameter matrices for each layer
        theta1 = np.matrix(np.reshape(params[:hidden_size * (input_size + 1)], ((input_size + 1),hidden_size)))
        theta2 = np.matrix(np.reshape(params[hidden_size * (input_size + 1):], ((hidden_size + 1),num_labels)))
        
        # run the feed-forward pass
        a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)
        
        # initializations
        J = 0
        delta1 = np.zeros(theta1.shape)  # (401, 25)
        delta2 = np.zeros(theta2.shape)  # (26, 10)
        
        # compute the cost
        for i in range(m):
            first_term = np.multiply(-y[i,:], np.log(h[i,:])) #y_onehot的每一行和输出概率h的每一行数据相乘
            second_term = np.multiply((1 - y[i,:]), np.log(1 - h[i,:]))
            J += np.sum(first_term - second_term) #乘出来的10*1的向量元素求和,再累加5000组的数据
        
        J = J / m
        
        # add the cost regularization term
        J += (float(learning_rate) / (2 * m)) * (np.sum(np.power(theta1[1:,:], 2)) + np.sum(np.power(theta2[1:,:], 2))) #加上正则化的项
        
        # perform backpropagation
        for t in range(m):
            a1t = a1[t,:]  # (1, 401)
            z2t = z2[t,:]  # (1, 25)
            a2t = a2[t,:]  # (1, 26)
            ht = h[t,:]  # (1, 10)
            yt = y[t,:]  # (1, 10)
            
            d3t = ht - yt  # (1, 10) 最后的误差向量
            
    #        z2t = np.insert(z2t, 0, values=np.ones(1))  # 补1为(1, 26)
            d2t = np.multiply((d3t * theta2.T)[:,1:], sigmoid_gradient(z2t))  # (1, 25)
            
            delta1 = delta1 + a1t.T * d2t  #401*25
            delta2 = delta2 + a2t.T * d3t    #26*10
            
        delta1 = delta1 / m
        delta2 = delta2 / m
        
        # add the gradient regularization term
        delta1[1:,:] = delta1[1:,:] + (theta1[1:,:] * learning_rate) / m
        delta2[1:,:] = delta2[1:,:] + (theta2[1:,:] * learning_rate) / m
        
        # unravel the gradient matrices into a single array
        grad = np.concatenate((np.ravel(delta1), np.ravel(delta2)))
        
        return J, grad
    
    from scipy.optimize import minimize
    
    # initial setup
    data = loadmat('ex4data1.mat') #字典形式的数据结构
    X = data['X']
    y = data['y']
    
    from sklearn.preprocessing import OneHotEncoder
    encoder = OneHotEncoder(sparse=False)
    y_onehot = encoder.fit_transform(y) # 将y转为y_onehot,每组数据中概率最大的为1,其余为0,5000*10
    
    input_size = 400 #输入层变量个数400
    hidden_size = 25 #隐藏层变量个数为25
    num_labels = 10  #输出层10个代表分别为1到10的概率
    learning_rate = 1#正则化的系数
    
    # randomly initialize a parameter array of the size of the full network's parameters
    params = (np.random.random(size=hidden_size * (input_size + 1) + num_labels * (hidden_size + 1)) - 0.5) * 0.25#随机给的初始参数theta1和theta2,现在为一个行向量,用时再按维数合成矩阵
    
    m = X.shape[0] #m为5000,组数据
    X = np.matrix(X)#转为矩阵方便运算
    y = np.matrix(y)
    
    # minimize the objective function
    fmin = minimize(fun=backprop, x0=params, args=(input_size, hidden_size, num_labels, X, y_onehot,learning_rate), #用minimize函数通过梯度grad优化J得到最优的theta1和theta2让J最小
                    method='TNC', jac=True, options={'maxiter': 250})
    
    theta1 = np.matrix(np.reshape(fmin.x[:hidden_size * (input_size + 1)], ((input_size + 1),hidden_size ))) #优化得到的行向量按维数合成theta1和theta2
    theta2 = np.matrix(np.reshape(fmin.x[hidden_size * (input_size + 1):], ((hidden_size + 1),num_labels )))
    
    a1, z2, a2, z3, h = forward_propagate(X, theta1, theta2)#最优的theta1和theta2正向传播一次,看看预测下效果
    y_pred = np.array(np.argmax(h, axis=1) + 1)#因为h索引从0开始,需要加1,得到y_pred为我们的预测结果
    
    correct = [1 if a == b else 0 for (a, b) in zip(y_pred, y)]#比较预测和实际的结果。正确为1,错误为0
    accuracy = (sum(map(int, correct)) / float(len(correct)))#累加求和,除以总个数,得到正确率
    print ('accuracy = {0}%'.format(accuracy * 100))
    

    由于python的主流性,只贴上python的代码,spyder编辑器也可和matlab一样以矩阵形式实时查看变量的值很方便,matlab的代码网上也有,更多机器学习资源可以加群:514649411。代码修改于群主黄博的共享,推导过程借鉴于博客园,初始数据mat文件可以在群里下载。
    我们都是初学者,希望更多人可以更快迈过这道坎,继续学习下去。

    相关文章

      网友评论

        本文标题:Ng机器学习第五周神经网络反向传播算法作业解析

        本文链接:https://www.haomeiwen.com/subject/noprbxtx.html