• 打开深度学习的锁:(2)单隐藏层的神经网络


    导言

    本篇知识背景来源于吴恩达教授的DeepLearning课程作业–第三节,有兴趣的同学可以自行搜索。
    博客所用到的数据集和测试代码已经公开:GitHub

    这篇博客本质内容吴恩达教授的DeepLearning的课程作业第三节,是题解。

    有了上次的学习作为引导,这次的练习简单了很多。

    这次要做的任务,是构建一个只有一个隐藏层的神经网络。

    本次学习用到的数据集-那朵花,是使用随机数生成的。
    虽然是随机,不过在生成的时候使用了随机数种子,保证了数据的一致,所以在每一个模块的测试练习的时候,如果你的输出和我的不一样,那就说明代码中存在一些问题。

    它们必须一样

    好了,开始吧

    PS:神经网络的训练过程

    放此图在这里为了快速认识,也为了需要的时候快速查找。初次看的时候看不懂没关系,可以先看下面的内容。
    在这里插入图片描述

    一、数据集和包的说明

    1.1准备文件

    本次练习用给到的文件是两个.py文件,别无其他。分别是planar_utils.pytestCases.py。如果一些原因没办法访问Github,可以直接复制代码,如下:

    planar_utils.py主要作用就是生成一些随机数,以此来充当数据集。
    虽然是随机,不过在生成的时候使用了随机数种子,保证了数据的一致,所以在每一个模块的测试练习的时候,如果你的输出和我的不一样,那就说明代码中存在一些问题。

    planar_utils.py:

    import matplotlib.pyplot as plt
    import numpy as np
    import sklearn
    import sklearn.datasets
    import sklearn.linear_model
    
    def plot_decision_boundary(model, X, y):
        """
        绘制模型的决策边界。
    
        参数:
        - model: 用于预测数据集输出的函数。
        - X: 输入数据。
        - y: 实际标签。
        """
        # 设置最小和最大值并给予一些填充
        x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
        y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
        h = 0.01
        # 生成距离为h的点阵
        xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
        # 预测整个网格的函数值
        Z = model(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        # 绘制轮廓和训练样本
        plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
        plt.ylabel('x2')
        plt.xlabel('x1')
        plt.scatter(X[0, :], X[1, :], c=y, cmap=plt.cm.Spectral)
    
    def sigmoid(x):
        """
        计算x的sigmoid。
    
        参数:
        - x: 任意大小的标量或numpy数组。
    
        返回:
        s: sigmoid(x)
        """
        s = 1/(1+np.exp(-x))
        return s
    
    def load_planar_dataset():
        """
        加载2D平面数据集到X和Y。
    
        返回:
        - X: 表示数据点的numpy数组。
        - Y: 表示标签的numpy数组。
        """
        np.random.seed(1)
        m = 400  # 样本数量
        N = int(m/2)  # 每个类别的点数
        D = 2  # 维度
        X = np.zeros((m,D))  # 每行为一个单独的例子的数据矩阵
        Y = np.zeros((m,1), dtype='uint8')  # 标签向量 (0为红色, 1为蓝色)
        a = 4  # 花的最大半径
    
        for j in range(2):
            ix = range(N*j,N*(j+1))
            t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2  # 角度
            r = a*np.sin(4*t) + np.random.randn(N)*0.2  # 半径
            X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
            Y[ix] = j
            
        X = X.T
        Y = Y.T
    
        return X, Y
    
    def load_extra_datasets():  
        """
        加载额外的数据集。
    
        返回:
        - 不同的合成数据集作为元组 (数据, 标签)。
        """
        N = 200
        noisy_circles = sklearn.datasets.make_circles(n_samples=N, factor=.5, noise=.3)
        noisy_moons = sklearn.datasets.make_moons(n_samples=N, noise=.2)
        blobs = sklearn.datasets.make_blobs(n_samples=N, random_state=5, n_features=2, centers=6)
        gaussian_quantiles = sklearn.datasets.make_gaussian_quantiles(mean=None, cov=0.5, n_samples=N, n_features=2, n_classes=2, shuffle=True, random_state=None)
        no_structure = np.random.rand(N, 2), np.random.rand(N, 2)
    
        return noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    testCases.py的主要作用是提供了一些用于测试单元的工具,在需要的时候直接调用就可。
    testCases.py:

    import numpy as np
    
    def layer_sizes_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(5, 3)
        Y_assess = np.random.randn(2, 3)
        return X_assess, Y_assess
    
    def initialize_parameters_test_case():
        n_x, n_h, n_y = 2, 4, 1
        return n_x, n_h, n_y
    
    def forward_propagation_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
    
        parameters = {'W1': np.array([[-0.00416758, -0.00056267],
            [-0.02136196,  0.01640271],
            [-0.01793436, -0.00841747],
            [ 0.00502881, -0.01245288]]),
         'W2': np.array([[-0.01057952, -0.00909008,  0.00551454,  0.02292208]]),
         'b1': np.array([[ 0.],
            [ 0.],
            [ 0.],
            [ 0.]]),
         'b2': np.array([[ 0.]])}
    
        return X_assess, parameters
    
    def compute_cost_test_case():
        np.random.seed(1)
        Y_assess = np.random.randn(1, 3)
        parameters = {'W1': np.array([[-0.00416758, -0.00056267],
            [-0.02136196,  0.01640271],
            [-0.01793436, -0.00841747],
            [ 0.00502881, -0.01245288]]),
         'W2': np.array([[-0.01057952, -0.00909008,  0.00551454,  0.02292208]]),
         'b1': np.array([[ 0.],
            [ 0.],
            [ 0.],
            [ 0.]]),
         'b2': np.array([[ 0.]])}
    
        a2 = (np.array([[ 0.5002307 ,  0.49985831,  0.50023963]]))
    
        return a2, Y_assess, parameters
    
    def backward_propagation_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
        Y_assess = np.random.randn(1, 3)
        parameters = {'W1': np.array([[-0.00416758, -0.00056267],
            [-0.02136196,  0.01640271],
            [-0.01793436, -0.00841747],
            [ 0.00502881, -0.01245288]]),
         'W2': np.array([[-0.01057952, -0.00909008,  0.00551454,  0.02292208]]),
         'b1': np.array([[ 0.],
            [ 0.],
            [ 0.],
            [ 0.]]),
         'b2': np.array([[ 0.]])}
    
        cache = {'A1': np.array([[-0.00616578,  0.0020626 ,  0.00349619],
             [-0.05225116,  0.02725659, -0.02646251],
             [-0.02009721,  0.0036869 ,  0.02883756],
             [ 0.02152675, -0.01385234,  0.02599885]]),
      'A2': np.array([[ 0.5002307 ,  0.49985831,  0.50023963]]),
      'Z1': np.array([[-0.00616586,  0.0020626 ,  0.0034962 ],
             [-0.05229879,  0.02726335, -0.02646869],
             [-0.02009991,  0.00368692,  0.02884556],
             [ 0.02153007, -0.01385322,  0.02600471]]),
      'Z2': np.array([[ 0.00092281, -0.00056678,  0.00095853]])}
        return parameters, cache, X_assess, Y_assess
    
    def update_parameters_test_case():
        parameters = {'W1': np.array([[-0.00615039,  0.0169021 ],
            [-0.02311792,  0.03137121],
            [-0.0169217 , -0.01752545],
            [ 0.00935436, -0.05018221]]),
     'W2': np.array([[-0.0104319 , -0.04019007,  0.01607211,  0.04440255]]),
     'b1': np.array([[ -8.97523455e-07],
            [  8.15562092e-06],
            [  6.04810633e-07],
            [ -2.54560700e-06]]),
     'b2': np.array([[  9.14954378e-05]])}
    
        grads = {'dW1': np.array([[ 0.00023322, -0.00205423],
            [ 0.00082222, -0.00700776],
            [-0.00031831,  0.0028636 ],
            [-0.00092857,  0.00809933]]),
     'dW2': np.array([[ -1.75740039e-05,   3.70231337e-03,  -1.25683095e-03,
              -2.55715317e-03]]),
     'db1': np.array([[  1.05570087e-07],
            [ -3.81814487e-06],
            [ -1.90155145e-07],
            [  5.46467802e-07]]),
     'db2': np.array([[ -1.08923140e-05]])}
        return parameters, grads
    
    def nn_model_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
        Y_assess = np.random.randn(1, 3)
        return X_assess, Y_assess
    
    def predict_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
        parameters = {'W1': np.array([[-0.00615039,  0.0169021 ],
            [-0.02311792,  0.03137121],
            [-0.0169217 , -0.01752545],
            [ 0.00935436, -0.05018221]]),
         'W2': np.array([[-0.0104319 , -0.04019007,  0.01607211,  0.04440255]]),
         'b1': np.array([[ -8.97523455e-07],
            [  8.15562092e-06],
            [  6.04810633e-07],
            [ -2.54560700e-06]]),
         'b2': np.array([[  9.14954378e-05]])}
        return parameters, X_assess
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    1.2 需要导入的包

    与上一篇相比,除了Numpy和 Matplotlib以外,多了一个sklearn,#scikit-learn,提供了许多常见的机器学习算法的简单和高效的工具,如分类、回归、聚类和降维等。它还包括了用于模型选择和评估的工具,如交叉验证和各种性能指标。

    import numpy as np #Python科学计算的一些包
    import matplotlib.pyplot as plt #画图
    from Week_3_DataSet.testCases import * #测试示例
    from Week_3_DataSet.planar_utils import * #一些功能
    
    #导数数据挖掘和数据分析的一些包
    import sklearn
    import sklearn.datasets #它提供了一系列用于测试和学习的数据集。这个模块包含了多种类型的数据集
    import sklearn.linear_model #它包含了许多线性模型,这些模型可以用于各种任务,如回归、分类和异常检测。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二、构建神经网络的架构

    首先,我们要引入一个新的中间层,隐藏层。
    让我们先不考虑损失的计算,也就是现忽略标签 Y Y Y数据集合,单单来讨论 X X X Y ^ \hat Y Y^的关系
    那么我们现在知道的神经网络结构这样的:
    在这里插入图片描述
    总的来说,就是将特征输入到线性回归+激活函数函数中,得到预测,然后计算损失,然后调整模型参数,然后重复这一过程。

    现在,我们添加一个东西,顺便完善一下这个结构
    在这里插入图片描述

    现在,在中间添加了一个层,名字叫隐藏层

    从目前来看,这个层中的元素个数有四个,分别从 a [ 1 ] 1 a[1]^1 a[1]1 a [ 1 ] 4 a[1]^4 a[1]4,而这四个节点呢,每一个其实都是一个线性回归+激活函数线性回归+激活函数也就是逻辑回归

    我们可以看到,输入层中的每一个元素都于这四个元素进行了全链接,如果换个角度来看的话,就是:

    在隐藏层中,输入的样本被每一个逻辑回归单元进行了一次预测

    然后,我们将隐藏层中得到的预测再次进行拟合,也就是传入到输出层,得到一个最终的 y ^ \hat y y^

    所以,通俗的理解来说:添加了隐藏层就是为了对样本进行二次预测处理

    但是, 我可以这么说,但你不能这样想!这个话本是并不是很正确。但是我希望这句话可以让你有一个大概的感觉。
    如果你有了,一定要忘记这句话。

    那么,隐藏层到底是用来做什么的呢?

    在深度学习中,神经网络的隐藏层的作用可以从以下几个方面来理解:

    1.特征抽取: 隐藏层可以自动从数据中提取更高级和更复杂的特征。在卷积神经网络(CNN)中,例如,前几层可能会捕获边缘和纹理,而更深的层则可能会捕获更复杂的图案和结构。
    2.非线性映射: 通过在隐藏层使用非线性激活函数,神经网络能够学习并表示非线性函数。这使得网络能够逼近几乎任何复杂的函数。
    3.抽象表示: 在很多任务中,隐藏层提供了一种抽象表示的能力,这意味着每一层都在将数据转化为一种更加抽象和通用的表示。例如,在自然语言处理任务中,深层网络的早期层可能关注于单词和短语,而后续的层则可能捕获句子或段落的更高级含义。
    4.计算层: 隐藏层提供了更多的计算层,允许网络学习更多的参数和更复杂的结构。这使得网络能够逼近更为复杂的函数。

    如果搞不懂这些意义暂时也没关系。只需要知道,我们在编写神经网络对应的函数的时候,我们需要指定输入层、隐藏层、输出层

    #1.定义神经网络的结构
    def layer_sizes(X,Y):#传入数据集和标签
        # X:样本集合 维度(数据的特征数量,数据量)  每一个数据有两个特征,即:每一个点有X和Y两个坐标值
        # Y:标签的集合(标签数量,数据量) 每一个数据有一个标签,即:0/1 == 红/蓝色
        Input_layer=X.shape[0]#输入层的神经元数量,它等于数据的特征数量,每个数据点有两个特征(x ,y )坐标
        Hidden_layer=4 #隐藏层单元的数量,设置4
        Output_layer=Y.shape[0]#,表示输出层只有一个神经元来预测一个标签(0或1)。
        
        return (Input_layer,Hidden_layer,Output_layer)#得到神经网络
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三、初始化函数

    现在,我们可以来构建初始化函数了,也就是对 W W W b b b赋值了。这里有两个关键点:
    1:初始化最好用随机
    2:需要构建的 W W W b b b的个数。

    第一个我在第一章的时候解释过,随机的 W W W比全0的要好。
    但是第二个,为什么要构建 W W W b b b的个数呢?因为这个时候的 W W W b b b的不止一个。
    我们再看一下这张图:
    在这里插入图片描述

    在神经网络中,每一层对应需要 W W W b b b都不一样。

    当前有两个层:输入层和隐藏层会用到 W W W b b b,所以就定义两组。

    #2.初始化函数
    def init_parameters(Input_layer,Hidden_layer,Output_layer):
        np.random.seed(2) 
    
        #由于只有一层隐藏层,所以可以显式的写出初始化W1和W2
    
        W1=np.random.randn(Hidden_layer,Input_layer)*0.01 #W的维度(当前层的单元数,上一层的单元数)
        b1=np.zeros(shape=(Hidden_layer,1))  #b的维度(当前层的单元数,1)
        W2=np.random.randn(Output_layer,Hidden_layer)*0.01
        b2=np.zeros(shape=(Output_layer,1)) 
    
        #断言测试个格式
        assert(W1.shape==(Hidden_layer,Input_layer))
        assert(b1.shape==( Hidden_layer , 1))
        assert(W2.shape==(Output_layer,Hidden_layer))
        assert(b2.shape==(Output_layer,1))
    
        parameters ={
            "W1":W1,
            "b1":b1,
            "W2":W2,
            "b2":b2
        }
        return parameters
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    四、激活函数

    激活函数是做什么的呢?
    用一句话来说就是:
    转化线性函数的为非线性函数,使得模型具有更多的功能。 转化线性函数的为非线性函数,使得模型具有更多的功能。 转化线性函数的为非线性函数,使得模型具有更多的功能。
    对了,还有一句话,别问为什么,记住就行————

    除了用于二分类任务最后的输出,否则不要用 s i g m o i d 函数 除了用于二分类任务最后的输出,否则不要用sigmoid函数 除了用于二分类任务最后的输出,否则不要用sigmoid函数

    不用 s i g m o i d sigmoid sigmoid用什么呢?

    让我来介绍另外一种激活函数: t a n h tanh tanh函数

    4.1 tanh(双曲正切函数)函数

    关于tanh函数,它的图像是这样的:上趋近于1,下趋近于-1
    在这里插入图片描述
    由于 t a n h tanh tanh函数的输出值位于-1到1之间。这意味着它会将输入值归一化到这个范围内,所以如果我们想控制和规范神经元的输出值,我们可以选择这个函数。

    在这道题中,为了练习。对于第一个激活函数,我选择使用了 t a n h tanh tanh函数,而之后涉及到最终输出的激活函数,还是需要 s i g m o i d sigmoid sigmoid函数

    所以我们需要定义两个激活函数

    #3.激活函数
    #Numpy里面有thah函数,但是为了练习还是自己写一个吧
    def tanh(z): #传入一个线性的函数
        A=(np.exp(z)-np.exp(-z))/(np.exp(z)+np.exp(-z))
        return A #A是非线性输出
    
    #因为是二分类任务,所以对于最后的结果输出一定要用sigmoid函数
    def sigmoid(z):
         A=1/(1+np.exp(-z))
         return A
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    五,前向传播

    相较于上一篇文章提到的公式。有隐藏层的神经网络的公式由很大的改动:

    假设: N x Nx Nx =( N [ 0 ] N^{\left[0\right]} N[0] N [ 1 ] N^{\left[1\right]} N[1] N [ 2 ] N^{\left[2\right]} N[2]
    N [ 0 ] N^{\left[0\right]} N[0]:输入特征的个数;
    N [ 1 ] N^{\left[1\right]} N[1]:隐藏单元的个数;
    N [ 2 ] N^{\left[2\right]} N[2]:输出特征的个数;

    此外,给定单层神经网络的逻辑回归方程:

    隐藏层:
    Z [ 1 ] = W [ 1 ] X + b [ 1 ] Z^{\left[1\right]}=W^{\left[1\right]}X+b^{\left[1\right]} Z[1]=W[1]X+b[1]
    A [ 1 ] = g [ 1 ] ( Z [ 1 ] ) A^{\left[1\right]}=g^{\left[1\right]}(Z^{\left[1\right]}) A[1]=g[1](Z[1])

    // 其中 Z [ 1 ] Z^{\left[1\right]} Z[1]是输入的线性函数的输出, A [ 1 ] A^{\left[1\right]} A[1] Z [ 1 ] Z^{\left[1\right]} Z[1]进过 非线性函数变换 以后的 输出

    W [ 1 ] W^{\left[1\right]} W[1]的维度为( N [ 1 ] N^{\left[1\right]} N[1] N [ 0 ] N^{\left[0\right]} N[0]
    b [ 1 ] b^{\left[1\right]} b[1]的维度为( N [ 1 ] N^{\left[1\right]} N[1] 1 1 1

    输出层:
    Z [ 2 ] = W [ 2 ] A [ 1 ] + b [ 2 ] Z^{\left[2\right]}=W^{\left[2\right]}A^{\left[1\right]}+b^{\left[2\right]} Z[2]=W[2]A[1]+b[2]
    A [ 2 ] = g [ 2 ] ( Z [ 2 ] ) A^{\left[2\right]}=g^{\left[2\right]}(Z^{\left[2\right]}) A[2]=g[2](Z[2])

    W [ 2 ] W^{\left[2\right]} W[2]的维度为( N [ 2 ] N^{\left[2\right]} N[2] N [ 1 ] N^{\left[1\right]} N[1]
    b [ 2 ] b^{\left[2\right]} b[2]的维度为( N [ 2 ] N^{\left[2\right]} N[2] 1 1 1

    // 其中 Z [ 2 ] Z^{\left[2\right]} Z[2]非线性输出 A [ 1 ] A^{\left[1\right]} A[1] 添加上当前的 权重 W [ 1 ] W^{\left[1\right]} W[1]和偏差 b [ 1 ] b^{\left[1\right]} b[1] 所构成的线性输出

    A [ 2 ] A^{\left[2\right]} A[2] Z [ 2 ] Z^{\left[2\right]} Z[2]经过 非线性函数变换 以后的 最终输出,也就是预测结果 y ^ \hat y y^

    知道公式以后,代码很好写:

    #4.构建前向传播(不用Y)
    def propagate(X,parameters):
        W1=parameters["W1"]
        b1=parameters["b1"]
        W2=parameters["W2"]
        b2=parameters["b2"]
    
        Z1=np.dot(W1,X)+b1
        #使用tanh函数聚合值
        A1=tanh(Z1)
    
        #输出层计算
        Z2=np.dot(W2,A1)+b2
    
        #因为是二分类任务,所以对于最后的结果输出一定要用sigmoid函数
        A2=sigmoid(Z2)  #A2就是最后的输出
    
        cache={
            "Z1":Z1,
            "A1":A1,
            "Z2":Z2,
            "A2":A2
        }
    
        return (A2,cache)
    
    # #测试forward_propagation
    # print("=========================测试forward_propagation=========================") 
    # X_assess, parameters = forward_propagation_test_case()
    # A2, cache = propagate(X_assess, parameters)
    # print(np.mean(cache["Z1"]), np.mean(cache["A1"]), np.mean(cache["Z2"]), np.mean(cache["A2"]))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    测试结果:
    在这里插入图片描述

    六、损失函数

    根据任务的不同,所用到的损失函数也不同。
    关于当前这道题,我们处理的内容还是二分类问题。所以损失函数没有太大的变化

    对于单个样本:
    二元交叉熵损失函数(Binary Cross-Entropy Loss,也称为log loss)

    L o s s = L ( y , y ^ ) = − [ y log ⁡ ( y ^ ) + ( 1 − y ) log ⁡ ( 1 − y ^ ) ] Loss=L(y, \hat{y}) = -[y \log(\hat{y}) + (1 - y) \log(1 - \hat{y})] Loss=L(y,y^)=[ylog(y^)+(1y)log(1y^)]

    对于神经网络损失的计算:
    损失函数(Cost Function / Loss Function)

    J ( w [ 1 ] , b [ 1 ] , w [ 2 ] , b [ 2 ] ) = 1 m ∑ i = 1 m L ( y ( i ) , y ^ ( i ) ) J(w^{\left[1\right]}, b^{\left[1\right]},w^{\left[2\right]}, b^{\left[2\right]}) = \frac{1}{m} \sum_{i=1}^{m} \text{L}(y^{(i)}, \hat{y}^{(i)}) J(w[1],b[1],w[2],b[2])=m1i=1mL(y(i),y^(i))

    或者也可以使用 A [ 2 ] A^{\left[2\right]} A[2] 代替 y ^ ( i ) \hat{y}^(i) y^(i)

    J ( w [ 1 ] , b [ 1 ] , w [ 2 ] , b [ 2 ] ) = 1 m ∑ i = 1 m L ( y ( i ) , A [ 2 ] ( i ) ) J(w^{\left[1\right]}, b^{\left[1\right]},w^{\left[2\right]}, b^{\left[2\right]}) = \frac{1}{m} \sum_{i=1}^{m} \text{L}(y^{(i)}, A^{\left[2](i\right)}) J(w[1],b[1],w[2],b[2])=m1i=1mL(y(i),A[2](i))
    在深度学习中,我们需要找合适的 w w w b b b,尽可能的缩小 J ( w , b ) J(w,b) J(w,b)

    代码:

    #5.损失函数构建
    # 首先,A2是输出的结果,也就是预测,也就是Yhat,
    # 其次,我们需要真实的标签Y
    def Cost_Func(A2,Y):
          m=Y.shape[1] #总数据数
    
          sum_cost=-(
                     np.multiply(np.log(A2),Y)
                         +
                     np.multiply(np.log(1-A2),(1-Y))
                     )  
          cost=np.sum(sum_cost)/m
    
          #当前通过矩阵计算的得到的应该还是一个矩阵,这个不行,我们需要一个数
          cost=float(np.squeeze(cost))
    
          return cost
    
    # #测试compute_cost
    # print("=========================测试compute_cost=========================") 
    # A2 , Y_assess , parameters = compute_cost_test_case()
    # print("cost = " + str(Cost_Func(A2,Y_assess)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试结果:
    在这里插入图片描述

    七、后向传播

    所谓后向传播,就是计算 W W W b b b需要改变多少,只是计算,不改变。改变是梯度下降的事情。

    如果想玩后向传播就必须学会导数!

    可是我不会导数呀?不能玩吗?

    …也能, 这样吧,我给你导完以后的公式,你直接用,别问为什么就行。

    后向传播公式(其中的Y是(1,m)的标签集):
    d Z [ 2 ] = A [ 2 ] − Y dZ^{\left[2\right]}=A^{\left[2\right]}-Y dZ[2]=A[2]Y
    d W [ 2 ] = 1 m d Z [ 2 ] A [ 1 ] T dW^{\left[2\right]}=\frac{1}{m}dZ^{\left[2\right]}A^{\left[1\right]T} dW[2]=m1dZ[2]A[1]T
    d b [ 2 ] = 1 m n p . s u m ( d z [ 2 ] , a x i s = 1 , k e e p d i m s = T r u e ) db^{\left[2\right]}=\frac{1}{m}np.sum(dz^{\left[2\right]}, axis=1, keepdims=True) db[2]=m1np.sum(dz[2],axis=1,keepdims=True)
    d Z [ 1 ] = W [ 2 ] T d Z [ 2 ] ∗ g [ Z ] ′ dZ^{\left[1\right]}=W^{\left[2\right]T}dZ^{\left[2\right]}*g^{\left[Z\right]'} dZ[1]=W[2]TdZ[2]g[Z]
    d W [ 1 ] = 1 m d Z [ 1 ] X T dW^{\left[1\right]}=\frac{1}{m}dZ^{\left[1\right]}X^T dW[1]=m1dZ[1]XT
    d b [ 1 ] = 1 m n p . s u m ( d z [ 1 ] , a x i s = 1 , k e e p d i m s = T r u e ) db^{\left[1\right]}=\frac{1}{m}np.sum(dz^{\left[1\right]}, axis=1, keepdims=True) db[1]=m1np.sum(dz[1],axis=1,keepdims=True)

    7.1 激活函数的导数

    激活函数的导数,用符号表示: g [ Z ] ′ g^{\left[Z\right]'} g[Z]

    7.1.1 Sigmoid 函数

    Sigmoid 函数: σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+ez1
    导数: σ ′ ( z ) = σ ( z ) ⋅ ( 1 − σ ( z ) ) \sigma'(z) = \sigma(z) \cdot (1 - \sigma(z)) σ(z)=σ(z)(1σ(z))

    7.1.2 Tanh 函数

    Tanh 函数: tanh ⁡ ( z ) = e z − e − z e z + e − z \tanh(z) = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}} tanh(z)=ez+ezezez
    导数: tanh ⁡ ′ ( z ) = 1 − tanh ⁡ 2 ( z ) \tanh'(z) = 1 - \tanh^2(z) tanh(z)=1tanh2(z)

    7.1.3 ReLU 函数 (Rectified Linear Unit)

    ReLU 函数: R e L U ( z ) = max ⁡ ( 0 , z ) ReLU(z) = \max(0, z) ReLU(z)=max(0,z)
    导数:
    R e L U ′ ( z ) = { 1 if  z > 0 0 if  z ≤ 0 ReLU'(z) = {1if z>00if z0 ReLU(z)={10if z>0if z0

    7.1.4 Leaky ReLU 函数

    Leaky ReLU 函数导数:
    LeakyReLU ′ ( z ) = { 1 if  z > 0 α if  z ≤ 0 \text{LeakyReLU}'(z) = {1if z>0αif z0 LeakyReLU(z)={1αif z>0if z0

    Leaky ReLU 函数导数:
    LeakyReLU ′ ( z ) = { 1 if  z > 0 α if  z ≤ 0 \text{LeakyReLU}'(z) = {1if z>0αif z0 LeakyReLU(z)={1αif z>0if z0

    7.2 后向传播的代码

    #后向传播
    #首先,我们需要损失函数想相较于最后一层输出的导数
    #我们还需要使用链式法则计算每一层的W和b的梯度
    def back_propagtion(parameters,cache,X,Y):
         m=Y.shape[1] #总数据数
    
         W1=parameters["W1"]
         W2=parameters["W2"]
    
         A1=cache["A1"]
         A2=cache["A2"]
    
         #就算损失函数较于最后一层输出的导数dz
         #带入公式就好
         dZ2=A2-Y
         dW2=(1/m) *(np.dot(dZ2,A1.T))
         db2=(1/m) * np.sum(dZ2,axis=1,keepdims=True)
    
         #使用公式 g`(z)=1-a^2
         dZ1=np.dot(W2.T,dZ2)*(1-np.power(A1,2))
         dW1=(1/m)*np.dot(dZ1,X.T)
         db1=(1/m)*np.sum(dZ1,axis=1,keepdims=True)
    
         back_con={
              "dW1":dW1,
              "dW2":dW2,
              "db2":db2,
              "db1":db1
         }
         return back_con
    # #测试backward_propagation
    # print("=========================测试backward_propagation=========================")
    # parameters, cache, X_assess, Y_assess = backward_propagation_test_case()
    
    # grads = back_propagtion(parameters, cache, X_assess, Y_assess)
    # print ("dW1 = "+ str(grads["dW1"]))
    # print ("db1 = "+ str(grads["db1"]))
    # print ("dW2 = "+ str(grads["dW2"]))
    # print ("db2 = "+ str(grads["db2"]))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    测试结果:
    在这里插入图片描述

    补充说明:
    axis=1: 这个参数指定了求和应该沿着哪个轴进行。axis=1 意味着对每一行的元素进行求和。
    keepdims=True: 这个参数确保结果保持原始数组的维度。这意味着即使求和是沿着某个轴进行的,输出的数组仍将保持与原始数组相同的维数。

    八、梯度下降

    更新 W W W b b b,其中 α \alpha \frac{}{} α是学习率

    公式:
    w = w − α ∂ J ∂ w w = w - \alpha \frac{\partial J}{\partial w} w=wαwJ
    b = b − α ∂ J ∂ b b = b - \alpha \frac{\partial J}{\partial b} b=bαbJ

    代码:

    #梯度下降,也就是更新参数
    def update_parameter(parameters,back_con,learning_rate=1.2):
         #原数据
         W1,W2=parameters["W1"],parameters["W2"]
         b1,b2=parameters["b1"],parameters["b2"]
         #它们的导数
         dW1,dW2,db1,db2=back_con["dW1"],back_con["dW2"],back_con["db1"],back_con["db2"]
    
         W1=W1-learning_rate*dW1
         b1=b1-learning_rate*db1
    
         W2=W2-learning_rate*dW2
         b2=b2-learning_rate*db2
    
         update ={
            "W1":W1,
            "b1":b1,
            "W2":W2,
            "b2":b2
         }
    
         return update
    
    # #测试update_parameters
    # print("=========================测试update_parameters=========================")
    # parameters, grads = update_parameters_test_case()
    # parameters = update_parameter(parameters, grads)
    
    # print("W1 = " + str(parameters["W1"]))
    # print("b1 = " + str(parameters["b1"]))
    # print("W2 = " + str(parameters["W2"]))
    # print("b2 = " + str(parameters["b2"]))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    测试结果:
    在这里插入图片描述

    九、构建预测

    这一步很关键,我们要使用 X X X来生成预测,与它们的标签 Y Y Y来进行拟合。

    上一篇中没有把它们单独拿出来,看起来有点乱有点难理解,这一次单独提出来。

    #构建预测
    def predict(parameters,X):
    		#parameters - 包含参数的字典类型的变量。
    	    #X - 输入数据(n_x,m)
            A2,cache=propagate(X,parameters)
    
            predic=np.round(A2)
    
            return predic
    # #测试predict
    # print("=========================测试predict=========================")
    
    # parameters, X_assess = predict_test_case()
    # predictions = predict(parameters, X_assess)
    # print("预测的平均值 = " + str(np.mean(predictions)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试结果:
    在这里插入图片描述

    十、聚合和主函数

    好了,所有的功能都写完了。接下来写一个聚合它们的函数就可以了。

    def model(X,Y,num_iterations,print_cost=False):
        np.random.seed(3)
    
        #开始初始化
        Input_layer=layer_sizes(X,Y)[0] #导入输入层、隐藏层和输出层
        Hidden_layer=layer_sizes(X,Y)[1]
        Output_layer=layer_sizes(X,Y)[2]
    
        parameters=init_parameters(Input_layer,Hidden_layer,Output_layer)
    
        W1=parameters["W1"]
        W2=parameters["W2"]
        b1=parameters["b1"]
        b2=parameters["b2"]
    
        #初始化完成,开始神经网络
    
        for i in range(num_iterations):
             #前向传播,得到最后一个非线性输出结果和其它结果
             A2,cache=propagate(X,parameters)
             #计算损失
             cost=Cost_Func(A2,Y)
             #后向传播
             back_con=back_propagtion(parameters,cache,X,Y)
             #梯度下降/更新parameters
             parameters=update_parameter(parameters,back_con)
    
             #1000次打印
             if print_cost:
                  if i%1000 == 0:
                       print("第 ",i," 次循环,成本为:"+str(cost))
    
        #循环结束,返回最后的值
        return parameters
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    主函数:

    def main():
        X,Y=load_planar_dataset()
        parameters=model(X,Y,num_iterations=10000,print_cost=True)
        #draw graphe
        plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
        plt.title("Decision Boundary for hidden layer size " + str(4))
        plt.show()
        predictions = predict(parameters, X)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    完整代码:

    import numpy as np #Python科学计算的一些包
    import matplotlib.pyplot as plt #画图
    from Week_3_DataSet.testCases import * #测试示例
    from Week_3_DataSet.planar_utils import * #一些功能
    
    #scikit-learn提供了许多常见的机器学习算法的简单和高效的工具,如分类、回归、聚类和降维等。它还包括了用于模型选择和评估的工具,如交叉验证和各种性能指标。
    #导数数据挖掘和数据分析的一些包
    import sklearn
    import sklearn.datasets #它提供了一系列用于测试和学习的数据集。这个模块包含了多种类型的数据集
    import sklearn.linear_model #它包含了许多线性模型,这些模型可以用于各种任务,如回归、分类和异常检测。
    
    #1.定义神经网络的结构
    def layer_sizes(X,Y):
        # X:样本集合 维度(数据的特征数量,数据量)  每一个数据有两个特征,即:每一个点有X和Y两个坐标值
        # Y:标签的集合(标签数量,数据量) 每一个数据有一个标签,即:0/1 == 红/蓝色
     
        Input_layer=X.shape[0]#输入层的神经元数量,它等于数据的特征数量,每个数据点有两个特征(x ,y )坐标
        Hidden_layer=4 #隐藏层单元的数量,设置4
        Output_layer=Y.shape[0]#,表示输出层只有一个神经元来预测一个标签(0或1)。
    
        return (Input_layer,Hidden_layer,Output_layer)
    
    
    #2.初始化函数
    def init_parameters(Input_layer,Hidden_layer,Output_layer):
        np.random.seed(2) 
    
        #由于只有一层隐藏层,所以可以显式的写出初始化W1和W2
    
        W1=np.random.randn(Hidden_layer,Input_layer)*0.01 #W的维度(当前层的单元数,上一层的单元数)
        b1=np.zeros(shape=(Hidden_layer,1))  #b的维度(当前层的单元数,1)
        W2=np.random.randn(Output_layer,Hidden_layer)*0.01
        b2=np.zeros(shape=(Output_layer,1)) 
    
        #断言测试个格式
        assert(W1.shape==(Hidden_layer,Input_layer))
        assert(b1.shape==( Hidden_layer , 1))
        assert(W2.shape==(Output_layer,Hidden_layer))
        assert(b2.shape==(Output_layer,1))
    
        parameters ={
            "W1":W1,
            "b1":b1,
            "W2":W2,
            "b2":b2
        }
        return parameters
    
    #3.激活函数
    #Numpy里面有thah函数,但是为了练习还是自己写一个吧
    def tanh(z): #传入一个线性的函数
        A=(np.exp(z)-np.exp(-z))/(np.exp(z)+np.exp(-z))
        return A #A是非线性输出
    
    #因为是二分类任务,所以对于最后的结果输出一定要用sigmoid函数
    def sigmoid(z):
         A=1/(1+np.exp(-z))
         return A
         
    #4.构建前向传播(不用Y)
    def propagate(X,parameters):
        W1=parameters["W1"]
        b1=parameters["b1"]
        W2=parameters["W2"]
        b2=parameters["b2"]
    
        Z1=np.dot(W1,X)+b1
        A1=tanh(Z1)
    
        #输出层计算
        Z2=np.dot(W2,A1)+b2
    
        #因为是二分类任务,所以对于最后的结果输出一定要用sigmoid函数
        A2=sigmoid(Z2)  #A2就是最后的输出
    
        cache={
            "Z1":Z1,
            "A1":A1,
            "Z2":Z2,
            "A2":A2
        } #为什么要存呢?
    
        return (A2,cache)
    
    # #测试forward_propagation
    # print("=========================测试forward_propagation=========================") 
    # X_assess, parameters = forward_propagation_test_case()
    # A2, cache = propagate(X_assess, parameters)
    # print(np.mean(cache["Z1"]), np.mean(cache["A1"]), np.mean(cache["Z2"]), np.mean(cache["A2"]))
    
    
    #5.损失函数构建
    # 首先,A2是输出的结果,也就是预测,也就是Yhat,
    # 其次,我们需要真实的标签Y
    def Cost_Func(A2,Y):
          m=Y.shape[1] #总数据数
    
          sum_cost=-(
                     np.multiply(np.log(A2),Y)
                         +
                     np.multiply(np.log(1-A2),(1-Y))
                     )  
          cost=np.sum(sum_cost)/m
    
          #当前通过矩阵计算的得到的应该还是一个矩阵,这个不行,我们需要一个数
          cost=float(np.squeeze(cost))
    
          return cost
    
    # #测试compute_cost
    # print("=========================测试compute_cost=========================") 
    # A2 , Y_assess , parameters = compute_cost_test_case()
    # print("cost = " + str(Cost_Func(A2,Y_assess)))
    
    
    #后向传播
    #首先,我们需要损失函数想相较于最后一层输出的导数
    #我们还需要使用链式法则计算每一层的W和b的梯度
    def back_propagtion(parameters,cache,X,Y):
         m=Y.shape[1] #总数据数
    
         W1=parameters["W1"]
         W2=parameters["W2"]
    
         A1=cache["A1"]
         A2=cache["A2"]
    
         #就算损失函数较于最后一层输出的导数dz
         #带入公式就好
         dZ2=A2-Y
         dW2=(1/m) *(np.dot(dZ2,A1.T))
         db2=(1/m) * np.sum(dZ2,axis=1,keepdims=True)
    
         #使用公式 g`(z)=1-a^2
         dZ1=np.dot(W2.T,dZ2)*(1-np.power(A1,2))
         dW1=(1/m)*np.dot(dZ1,X.T)
         db1=(1/m)*np.sum(dZ1,axis=1,keepdims=True)
    
         back_con={
              "dW1":dW1,
              "dW2":dW2,
              "db2":db2,
              "db1":db1
         }
         return back_con
    # #测试backward_propagation
    # print("=========================测试backward_propagation=========================")
    # parameters, cache, X_assess, Y_assess = backward_propagation_test_case()
    
    # grads = back_propagtion(parameters, cache, X_assess, Y_assess)
    # print ("dW1 = "+ str(grads["dW1"]))
    # print ("db1 = "+ str(grads["db1"]))
    # print ("dW2 = "+ str(grads["dW2"]))
    # print ("db2 = "+ str(grads["db2"]))
    
    #梯度下降,也就是更新参数
    def update_parameter(parameters,back_con,learning_rate=1.2):
         #原数据
         W1,W2=parameters["W1"],parameters["W2"]
         b1,b2=parameters["b1"],parameters["b2"]
         #它们的导数
         dW1,dW2,db1,db2=back_con["dW1"],back_con["dW2"],back_con["db1"],back_con["db2"]
    
         W1=W1-learning_rate*dW1
         b1=b1-learning_rate*db1
    
         W2=W2-learning_rate*dW2
         b2=b2-learning_rate*db2
    
         update ={
            "W1":W1,
            "b1":b1,
            "W2":W2,
            "b2":b2
         }
    
         return update
    
    # #测试update_parameters
    # print("=========================测试update_parameters=========================")
    # parameters, grads = update_parameters_test_case()
    # parameters = update_parameter(parameters, grads)
    
    # print("W1 = " + str(parameters["W1"]))
    # print("b1 = " + str(parameters["b1"]))
    # print("W2 = " + str(parameters["W2"]))
    # print("b2 = " + str(parameters["b2"]))
    
    
    def model(X,Y,num_iterations,print_cost=False):
        np.random.seed(3)
    
        #开始初始化
        Input_layer=layer_sizes(X,Y)[0] #导入输入层、隐藏层和输出层
        Hidden_layer=layer_sizes(X,Y)[1]
        Output_layer=layer_sizes(X,Y)[2]
    
        parameters=init_parameters(Input_layer,Hidden_layer,Output_layer)
    
        W1=parameters["W1"]
        W2=parameters["W2"]
        b1=parameters["b1"]
        b2=parameters["b2"]
    
        #初始化完成,开始神经网络
    
        for i in range(num_iterations):
             #前向传播,得到最后一个非线性输出结果和其它结果
             A2,cache=propagate(X,parameters)
             #计算损失
             cost=Cost_Func(A2,Y)
             #后向传播
             back_con=back_propagtion(parameters,cache,X,Y)
             #梯度下降/更新parameters
             parameters=update_parameter(parameters,back_con)
    
             #1000次打印
             if print_cost:
                  if i%1000 == 0:
                       print("第 ",i," 次循环,成本为:"+str(cost))
    
        #循环结束,返回最后的值
        return parameters
    
    #构建预测
    def predict(parameters,X):
    		#parameters - 包含参数的字典类型的变量。
    	    #X - 输入数据(n_x,m)
            A2,cache=propagate(X,parameters)
    
            predic=np.round(A2)
    
            return predic
    # #测试predict
    # print("=========================测试predict=========================")
    
    # parameters, X_assess = predict_test_case()
    # predictions = predict(parameters, X_assess)
    # print("预测的平均值 = " + str(np.mean(predictions)))
    
    
    def main():
        X,Y=load_planar_dataset()
        parameters=model(X,Y,num_iterations=10000,print_cost=True)
        #draw graphe
        plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
        plt.title("Decision Boundary for hidden layer size " + str(4))
        plt.show()
        predictions = predict(parameters, X)
    
        # Y=Y*0  改变颜色玩一下
        # plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral) #绘制散点图
        # plt.show()
        # print(X.shape)#(2,400) 点的横坐标和纵坐标
        # print(Y.shape)#(1,400) 标签,红0蓝1
    
        # #原样测试:
        # plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral) #绘制散点图
        # plt.show()
    
        # #测试:使用自带的函数逻辑回归的线性分割
        # clf=sklearn.linear_model.LogisticRegressionCV()
        # clf.fit(X.T,Y.T)
        # plot_decision_boundary(lambda x: clf.predict(x), X, Y) #绘制决策边界
        # plt.title("Logistic Regression") #图标题、
        # plt.show()
        # LR_predictions  = clf.predict(X.T) #预测结果
        # print(Y.shape)
        # print(LR_predictions.shape)
        # print ("逻辑回归的准确性: %d " % float((np.dot(Y, LR_predictions) + 
        #         np.dot(1 - Y,1 - LR_predictions)) / float(Y.size) * 100) +
        #     "% " + "(正确标记的数据点所占的百分比)")
    
    if __name__ =="__main__":
         main()   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275

    输出结果:

    在这里插入图片描述
    在这里插入图片描述

    总结

    这个人太懒了,什么都没有写~~
    疑缺待补

  • 相关阅读:
    Java IO之网络的简介说明
    Elasticsearch介绍及插件head和kibana下载
    计算机丢失mfc140u.dll怎么办,mfc140u.dll丢失的解决方法分享
    新一代前端应该使用的“开发套餐“
    计算机毕业设计之垃圾分类公益回收管理系统
    为什么说学习的本质是泛化能力?如何理解偏差-方差窘境?
    YOLO系列概述(yolov1至yolov7)
    oracle10g 监听异常处理
    神经网络(十七)RCNN及其变体的概述
    [附源码]Python计算机毕业设计SSM酒店客户管理系统(程序+LW)
  • 原文地址:https://blog.csdn.net/HJS1453100406/article/details/132980690