4. 吴恩达机器学习课程-作业4-神经网络学习

奋斗吧
奋斗吧
擅长邻域:未填写

标签: 4. 吴恩达机器学习课程-作业4-神经网络学习 博客 51CTO博客

2023-05-10 18:24:05 32浏览

4. 吴恩达机器学习课程-作业4-神经网络学习,文章目录4.吴恩达机器学习课程-作业4-神经网络学习4.1题目介绍4.2可视化数据4.3代码介绍1)扁平化参数2)计算前馈3)代价函数4)Regularizedcostfunction正则化代价函数5)sigmod函数导数6)Randominitialization随机初始化7)计算反向传播8)梯度检查9)正则化神经网络10)优化参数11)可视化隐藏层4



文章目录

  • 4. 吴恩达机器学习课程-作业4-神经网络学习
  • 4.1 题目介绍
  • 4.2 可视化数据
  • 4.3 代码介绍
  • 1) 扁平化参数
  • 2) 计算前馈
  • 3) 代价函数
  • 4) Regularized cost function 正则化代价函数
  • 5) sigmod函数导数
  • 6) Random initialization 随机初始化
  • 7) 计算反向传播
  • 8) 梯度检查
  • 9) 正则化神经网络
  • 10) 优化参数
  • 11) 可视化隐藏层


4. 吴恩达机器学习课程-作业4-神经网络学习

参考代码

javascript:void(0)/article/details/104573555
https://blog.csdn.net/Cowry5/article/details/80399350

4.1 题目介绍

在前面的练习中,您实现了神经网络的前馈传播,并使用它来预测手写数字的权重提供。

在本练习中,您将实现反向传播算法学习神经网络的参数。

提供的脚本ex4.mat,会帮助你一步步完成这个练习。

4.2 可视化数据

def plot_100_images(self, X):
        """随机画100个数字"""
        index = np.random.choice(range(5000), 100)
        images = X[index]
        fig, ax_array = plt.subplots(10, 10, sharey=True, sharex=True, figsize=(8, 8))
        for r in range(10):
            for c in range(10):
                ax_array[r, c].matshow(images[r * 10 + c].reshape(20, 20), cmap='gray_r')
        plt.xticks([])
        plt.yticks([])
        plt.show()

    def main(self):
        pass

    def __init__(self):
        data = sio.loadmat('ex4data1.mat')  # return a dict
        X = np.array(data['X'])
        y = np.array(data['y']).flatten()
        print(X.shape)
        print(y.shape)
        # 随机画出100个图片
        self.plot_100_images(X)
        pass

结果如下

(5000, 400)
(5000,)

图片如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9l14lxy8-1645517925675)(picture/image-20211106171622354.png)]

4.3 代码介绍

1) 扁平化参数

当我们使用高级优化方法来优化神经网络时,我们需要将多个参数矩阵展开,才能传入优化函数,然后再恢复形状。

def serialize(self, a, b):
        """
        展开参数,扁平化参数
        :param a:
        :param b:
        :return: 拼接后的矩阵
        """
        # np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
        # 拼接矩阵的意思
        return np.r_[a.flatten(), b.flatten()]

使用的时候

theta = serialize(t1, t2)  # 扁平化参数,25*401+10*26=10285
theta.shape  # (10285,)

扁平化参数后,还得还原参数

def deserialize(self, seq):
        """
        提取参数,扁平化的参数,还原theta1,theta2
        :return:  theta1,theta2
        """
        return seq[:25 * 401].reshape(25, 401), seq[25 * 401:].reshape(10, 26)

2) 计算前馈

获取前馈值

def feed_forward(self, theta, X):
        """
        得到每层的输入和输出
        前置输出
        :param theta: 扁平化后的theta
        :param X:
        :return:
        """
        t1, t2 = self.deserialize(theta)
        # 前面已经插入过偏置单元,这里就不用插入了
        a1 = X
        z2 = a1.dot(t1.T)
        a2 = np.insert(self.sigmoid(z2), 0, 1, axis=1)
        z3 = a2.dot(t2.T)
        a3 = self.sigmoid(z3)

        return a1, z2, a2, z3, a3
      
def sigmoid(self, z):
        """
        sigmoid函数实现了将结果从R转化到0-1,用来表示概率。实现sigmoid函数
        :param z: z值
        :return:
        """
        return 1 / (1 + np.exp(-z))

3) 代价函数

回顾下神经网络的代价函数(不带正则化项)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KPBT29Dz-1645517925684)(picture/aHR0cHM6Ly91cGxvY.png)]

def cost(self, theta, X, y):
        """
        计算神经网络的代价函数(不带正则化项)
        :param theta: 扁平化的参数
        :param X:
        :param y:
        :return: 代价值
        """
        a1, z2, a2, z3, h = self.feed_forward(theta, X)
        J = 0
        for i in range(len(X)):
            first = - y[i] * np.log(h[i])
            second = (1 - y[i]) * np.log(1 - h[i])
            J = J + np.sum(first - second)
        J = J / len(X)
        return J

4) Regularized cost function 正则化代价函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNmSku1N-1645517925687)(picture/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8x.png)]

注意不要将每层的偏置项正则化。

def regularized_cost(self, theta, X, y, l=1):
        """
        正则化时忽略每层的偏置项,也就是参数矩阵的第一列'
        :param theta: 扁平化后的参数
        :param X:
        :param y:
        :param l: 惩罚力度
        :return: 正则化后的代价函数
        """
        t1, t2 = self.deserialize(theta)
        reg = np.sum(t1[:, 1:] ** 2) + np.sum(t2[:, 1:] ** 2)  # or use np.power(a, 2)
        return l / (2 * len(X)) * reg + self.cost(theta, X, y)

5) sigmod函数导数

4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化

def sigmoid_gradient(self,z):
        """
        这里可以手动推导,并不难。
        :param z:
        :return:
        """
        return self.sigmoid(z) * (1 - self.sigmoid(z))

6) Random initialization 随机初始化

当我们训练神经网络时,随机初始化参数是很重要的,可以打破数据的对称性。

一个有效的策略是在均匀分布(−e,e)中随机选择值,我们可以选择 e = 0.12 这个范围的值来确保参数足够小,使得训练更有效率。

def random_init(self,size):
        """
        从服从的均匀分布的范围中随机返回size大小的值
        :param size: 随机取出的数量
        :return: 
        """
        return np.random.uniform(-0.12, 0.12, size)

7) 计算反向传播

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MknSebQH-1645517925690)(picture/aHR0cHM6Ly91cGxvYWQtaW1hZ2Vz.png)]

4. 吴恩达机器学习课程-作业4-神经网络学习_神经网络_02表示第4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_03层的单元4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_04的损失

4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_05表示第4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_03层的梯度

  • 输出层产生的错误4. 吴恩达机器学习课程-作业4-神经网络学习_学习_07
  • 隐藏层传递的错误4. 吴恩达机器学习课程-作业4-神经网络学习_神经网络_08

假设一共有4层,1层输入,2层隐藏层,1层输出

4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_09

4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_10

4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_11

4. 吴恩达机器学习课程-作业4-神经网络学习_机器学习_12

4. 吴恩达机器学习课程-作业4-神经网络学习_机器学习_13

反向传播算法伪代码

训练集4. 吴恩达机器学习课程-作业4-神经网络学习_神经网络_14,

4. 吴恩达机器学习课程-作业4-神经网络学习_机器学习_15

  • 对于训练集中的每个样本x,设置输入层(Input layer)对应的激活值令4. 吴恩达机器学习课程-作业4-神经网络学习_正则化_16
  • 前向传播,计算出所有的4. 吴恩达机器学习课程-作业4-神经网络学习_学习_17
  • 计算输出层产生的错误4. 吴恩达机器学习课程-作业4-神经网络学习_扁平化_18
  • 反向传播错误4. 吴恩达机器学习课程-作业4-神经网络学习_机器学习_19
  • 使用梯度下降(gradient descent),训练参数
    4. 吴恩达机器学习课程-作业4-神经网络学习_神经网络_20
    4. 吴恩达机器学习课程-作业4-神经网络学习_神经网络_21
    4. 吴恩达机器学习课程-作业4-神经网络学习_机器学习_22
    4. 吴恩达机器学习课程-作业4-神经网络学习_机器学习_23
def gradient(theta, X, y):
    '''
    unregularized gradient, notice no d1 since the input layer has no error 
    return 所有参数theta的梯度,故梯度D(i)和参数theta(i)同shape,重要。
    '''
    t1, t2 = deserialize(theta)
    a1, z2, a2, z3, h = feed_forward(theta, X)
    d3 = h - y # (5000, 10)
    d2 = d3 @ t2[:,1:] * sigmoid_gradient(z2)  # (5000, 25)
    D2 = d3.T @ a2  # (10, 26)
    D1 = d2.T @ a1 # (25, 401)
    D = (1 / len(X)) * serialize(D1, D2)  # (10285,)
    
    return D

在你的神经网络,你是最小化代价函数J(Θ)。执行梯度检查你的参数,你可以想象展开参数Θ(1)Θ(2)成一个长向量θ。通过这样做,你能使用以下梯度检查过程。

8) 梯度检查

为了方便检测我们的梯度下降算法是否正确,我们采用近似计算的方式来对比

在你的神经网络,你是最小化代价函数J(Θ)。执行梯度检查你的参数,你可以想象展开参数Θ(1)Θ(2)成一个长向量θ。通过这样做,你能使用以下梯度检查过程。

4. 吴恩达机器学习课程-作业4-神经网络学习_正则化_24

def a_numeric_grad(self, plus, minus, X, y, e):
        """
        对每个参数theta_i计算数值梯度,即理论梯度。
        :param e:
        :param y:
        :param X:
        :param plus:
        :param minus:
        :return:
        """
        return (self.regularized_cost(plus, X, y) - self.regularized_cost(minus, X, y)) / (e * 2)

    def gradient_checking(self, theta, X, y, e):
        """
        荼毒检查
        :param theta:
        :param X:
        :param y:
        :param e:
        :return:
        """
        numeric_grad = []
        for i in range(len(theta)):
            plus = theta.copy()  # deep copy otherwise you will change the raw theta
            minus = theta.copy()
            plus[i] = plus[i] + e
            minus[i] = minus[i] - e
            grad_i = self.a_numeric_grad(plus, minus, X, y, e)
            numeric_grad.append(grad_i)
        numeric_grad = np.array(numeric_grad)
        analytic_grad = self.regularized_gradient(theta, X, y)
        diff = np.linalg.norm(numeric_grad - analytic_grad) / np.linalg.norm(numeric_grad + analytic_grad)

        print(
            'If your backpropagation implementation is correct,\nthe relative difference will be smaller than 10e-9 (assume epsilon=0.0001).\nRelative Difference: {}\n'.format(
                diff))

这个计算是非常耗时的,应用到一部分计算中对比结果,检测反向传播算法是否正确。之后要拿出。

9) 正则化神经网络

4. 吴恩达机器学习课程-作业4-神经网络学习_神经网络_25

4. 吴恩达机器学习课程-作业4-神经网络学习_正则化_26

def regularized_gradient(self, theta, X, y, l):
        """
        反向传播算法
        :param theta: ndarray,一维参数向量
        :param X: ndarray,输入层的输入值
        :param y: ndarray,数据的标签
        :param l: float,惩罚参数
        :return: ndarray,下降后一维参数向量
        """
        a1, z2, a2, z3, a3 = self.feed_forward(theta, X)
        theta1, theta2 = self.deserialize(theta)  # theta1(25,401), theta2(10,26)
        m = X.shape[0]
        d3 = a3 - y  # d3(5000,10)
        d2 = d3 @ theta2[:, 1:] * self.sigmoid_gradient(z2)  # d2(5000,25)
        theta1 = np.insert(np.delete(theta1, 0, axis=1), 0, 0, axis=1)
        theta2 = np.insert(np.delete(theta2, 0, axis=1), 0, 0, axis=1)
        D1 = (1 / m) * d2.T.dot(a1) + (l / m) * theta1  # D1(25,401)
        D2 = (1 / m) * d3.T.dot(a2) + (l / m) * theta2  # D2(10,26)
        return self.serialize((D1, D2))

10) 优化参数

def nn_training(self, X, y):
        """
        开始训练
        :param X:
        :param y:
        :return:
        """
        # 初始化参数
        print("========开始初始化参数,开始训练============")
        init_theta = self.random_init(10285)  # 25*401 + 10*26
        # 参数优化
        res = opt.minimize(fun=self.regularized_cost,
                           x0=init_theta,
                           args=(X, y, 1),
                           method='TNC',
                           jac=self.regularized_gradient,
                           options={'maxiter': 400})
        return res

使用代码

X = self.X
        y = self.y
        res = self.nn_training(X, y)
        print("========训练结束,开始预测===========")
        _, _, _, _, h = self.feed_forward(theta=res.x, X=X)
        y_pred = np.argmax(h, axis=1) + 1
        print(y_pred.shape)
        print(y_pred)
        # 开始预测,评价
        # self.raw_y (5000,) y_pred(5000,)
        print("self.raw_y.shape---", self.raw_y.shape)
        print("y_pred.shape", y_pred.shape)
        print("打印算法评估", classification_report(self.raw_y, y_pred))

11) 可视化隐藏层

def plot_hidden(self, theta):
        t1, _ = self.deserialize(theta)
        t1 = t1[:, 1:]
        fig, ax_array = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(6, 6))
        for r in range(5):
            for c in range(5):
                ax_array[r, c].matshow(t1[r * 5 + c].reshape(20, 20), cmap='gray_r')
                plt.xticks([])
                plt.yticks([])
        plt.show()

使用代码

self.plot_hidden(res.x)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7hwGLgFr-1645517925693)(picture/image-20211107210120947.png)]

所有代码

import scipy.io as sio
import numpy as np
from matplotlib import pyplot as plt
import scipy.optimize as opt
from sklearn.metrics import classification_report
from time import *


class BP:

    def regularized_gradient(self, theta, X, y, l=1):
        """
        反向传播算法
        :param theta: ndarray,一维参数向量
        :param X: ndarray,输入层的输入值
        :param y: ndarray,数据的标签
        :param l: float,惩罚参数
        :return: ndarray,下降后一维参数向量
        """

        # a1, z2, a2, z3, h = self.feed_forward(theta, X)
        D1, D2 = self.deserialize(self.gradient(theta, X, y))
        t1, t2 = self.deserialize(theta)
        t1[:, 0] = 0
        t2[:, 0] = 0
        reg_D1 = D1 + (l / len(X)) * t1
        reg_D2 = D2 + (l / len(X)) * t2
        return self.serialize(reg_D1, reg_D2)

    def gradient(self, theta, X, y):
        """
        获取整个网络代价函数的梯度。以便在优化算法中求解。
        不规则梯度,注意没有d1,因为输入层没有输入
        :param theta: 扁平化的参数,即权重w1,w2
        :param X:
        :param y:
        :return: 所有参数theta的梯度,故梯度D(i)和参数theta(i)同shape,重要。
        """
        # t1=theta1,t2=theta2
        t1, t2 = self.deserialize(theta)
        # 使用前向传播算法计算出所有的数值
        a1, z2, a2, z3, a3 = self.feed_forward(theta, X)
        # 均方误差的梯度(5000, 10)
        d3 = a3 - y  # (5000, 10)
        # 输出层误差,t2为w2的意思,是隐藏层到输入层的权值
        d2 = d3 @ t2[:, 1:] * self.sigmoid_gradient(z2)  # (5000, 25)

        # a2为隐藏层输出,D2为梯度
        D2 = d3.T @ a2  # (10, 26)
        # D1为损失函数值
        D1 = d2.T @ a1  # (25, 401)

        D = (1 / len(X)) * self.serialize(D1, D2)  # (10285,)
        return D

    def a_numeric_grad(self, plus, minus, X, y, e):
        """
        对每个参数theta_i计算数值梯度,即理论梯度。
        :param e:
        :param y:
        :param X:
        :param plus:
        :param minus:
        :return:
        """
        return (self.regularized_cost(plus, X, y) - self.regularized_cost(minus, X, y)) / (e * 2)

    def gradient_checking(self, theta, X, y, e):
        """
        荼毒检查
        :param theta:
        :param X:
        :param y:
        :param e:
        :return:
        """
        numeric_grad = []
        for i in range(len(theta)):
            plus = theta.copy()  # deep copy otherwise you will change the raw theta
            minus = theta.copy()
            plus[i] = plus[i] + e
            minus[i] = minus[i] - e
            grad_i = self.a_numeric_grad(plus, minus, X, y, e)
            numeric_grad.append(grad_i)
        numeric_grad = np.array(numeric_grad)
        analytic_grad = self.regularized_gradient(theta, X, y)
        diff = np.linalg.norm(numeric_grad - analytic_grad) / np.linalg.norm(numeric_grad + analytic_grad)

        print(
            'If your backpropagation implementation is correct,\nthe relative difference will be smaller than 10e-9 (assume epsilon=0.0001).\nRelative Difference: {}\n'.format(
                diff))

    def sigmoid_gradient(self, z):
        """
        这里可以手动推导,并不难。
        :param z:
        :return: sigmoid(x) 导数值
        """
        return self.sigmoid(z) * (1 - self.sigmoid(z))

    def random_init(self, size):
        """
        从服从的均匀分布的范围中随机返回size大小的值
        :param size: 随机取出的数量
        :return:
        """
        return np.random.uniform(-0.12, 0.12, size)

    def expand_y(self, y):
        """
        首先我们要将标签值(1,2,3,4,…,10)转化成非线性相关的向量,
        向量对应位置(y[i-1])上的值等于1,例如y[0]=6转化为y[0]=[0,0,0,0,0,1,0,0,0,0]。
        :param y:
        :return:
        """
        result = []
        # 把y中每个类别转化为一个向量,对应的lable值在向量对应位置上置为1
        for i in y:
            y_array = np.zeros(10)
            y_array[i - 1] = 1
            result.append(y_array)

        return np.array(result)

    def serialize(self, a, b):
        """
        展开参数,扁平化参数
        :param a:
        :param b:
        :return: 拼接后的矩阵
        """
        # np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
        # 拼接矩阵的意思
        return np.r_[a.flatten(), b.flatten()]

    def deserialize(self, seq):
        """
        提取参数,扁平化的参数,还原theta1,theta2
        :return:  theta1,theta2
        """
        return seq[:25 * 401].reshape(25, 401), seq[25 * 401:].reshape(10, 26)

    def feed_forward(self, theta, X):
        """
        得到每层的输入和输出
        前置输出
        :param theta: 扁平化后的theta
        :param X:
        :return:
        """
        t1, t2 = self.deserialize(theta)
        # 前面已经插入过偏置单元,这里就不用插入了
        # X (5000,401) t1 (25,401)
        a1 = X
        # z2 (5000,25)
        z2 = a1.dot(t1.T)
        # a2 (5000,26)
        a2 = np.insert(self.sigmoid(z2), 0, 1, axis=1)
        # a2 (5000,26) .t2(10,26) z3(5000,10)
        z3 = a2.dot(t2.T)
        # a3 (5000,10)
        a3 = self.sigmoid(z3)

        return a1, z2, a2, z3, a3

    def cost(self, theta, X, y):
        """
        计算神经网络的代价函数(不带正则化项)
        :param theta: 扁平化的参数
        :param X:
        :param y:
        :return: 代价值
        """
        a1, z2, a2, z3, h = self.feed_forward(theta, X)
        J = 0
        for i in range(len(X)):
            first = - y[i] * np.log(h[i])
            second = (1 - y[i]) * np.log(1 - h[i])
            J = J + np.sum(first - second)
        J = J / len(X)
        return J

    def regularized_cost(self, theta, X, y, l=1):
        """
        正则化时忽略每层的偏置项,也就是参数矩阵的第一列'
        :param theta: 扁平化后的参数
        :param X:
        :param y:
        :param l: 惩罚力度
        :return: 正则化后的代价函数
        """
        t1, t2 = self.deserialize(theta)
        reg = np.sum(t1[:, 1:] ** 2) + np.sum(t2[:, 1:] ** 2)  # or use np.power(a, 2)
        return l / (2 * len(X)) * reg + self.cost(theta, X, y)

    def sigmoid(self, z):
        """
        sigmoid函数实现了将结果从R转化到0-1,用来表示概率。实现sigmoid函数
        :param z: z值
        :return:
        """
        return 1 / (1 + np.exp(-z))

    def plot_100_images(self, X):
        """
        随机画100个数字
        :param X:
        :return:
        """
        index = np.random.choice(range(5000), 100)
        images = X[index]
        fig, ax_array = plt.subplots(10, 10, sharey=True, sharex=True, figsize=(8, 8))
        for r in range(10):
            for c in range(10):
                ax_array[r, c].matshow(images[r * 10 + c].reshape(20, 20), cmap='gray_r')
        plt.xticks([])
        plt.yticks([])
        plt.show()

    def plot_hidden(self, theta):
        t1, _ = self.deserialize(theta)
        t1 = t1[:, 1:]
        fig, ax_array = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(6, 6))
        for r in range(5):
            for c in range(5):
                ax_array[r, c].matshow(t1[r * 5 + c].reshape(20, 20), cmap='gray_r')
                plt.xticks([])
                plt.yticks([])
        plt.show()

    def nn_training(self, X, y):
        """
        开始训练
        :param X:
        :param y:
        :return:
        """
        # 初始化参数
        print("========开始初始化参数,开始训练============")
        init_theta = self.random_init(10285)  # 25*401 + 10*26
        # 参数优化
        res = opt.minimize(fun=self.regularized_cost,
                           x0=init_theta,
                           args=(X, y, 1),
                           method='TNC',
                           jac=self.regularized_gradient,
                           options={'maxiter': 400})
        return res

    def main(self):
        X = self.X
        y = self.y
        res = self.nn_training(X, y)
        print("========训练结束,开始预测===========")
        _, _, _, _, h = self.feed_forward(theta=res.x, X=X)
        y_pred = np.argmax(h, axis=1) + 1
        print(y_pred.shape)
        print(y_pred)
        # 开始预测,评价
        # self.raw_y (5000,) y_pred(5000,)
        print("self.raw_y.shape---", self.raw_y.shape)
        print("y_pred.shape", y_pred.shape)
        print("打印算法评估", classification_report(self.raw_y, y_pred))
        # 可视化参数
        self.plot_hidden(res.x)

    def __init__(self):
        self.data = sio.loadmat('ex4data1.mat')  # return a dict
        theta = sio.loadmat("ex4weights.mat")
        self.theta1 = theta["Theta1"]  # (25,401)
        self.theta2 = theta["Theta2"]  # (10,26)
        X = np.array(self.data["X"])
        self.raw_y = np.array(self.data["y"]).flatten()
        # 随机画出100个图片
        self.plot_100_images(X)
        y = self.expand_y(self.data["y"])
        self.y = np.array(y)
        self.X = np.insert(X, 0, 1, axis=1)  # 添加一列1,方便计算b

        # 扁平化参数,25*401+10*26=10285
        self.theta = self.serialize(self.theta1, self.theta2)


if __name__ == '__main__':
    begin_time = time()
    print("========程序开始============")
    obj = BP()
    obj.main()
    print("========程序结束============")
    end_time = time()
    run_time = end_time - begin_time
    print('该循环程序运行时间:', run_time)  # 该循环程序运行时间: 1.4201874732


好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695