• 深度学习入门基于python的理论与实现-第三章神经网络(个人向笔记)


    激活函数

    激活函数是连接感知机和神经网络的桥梁

    阶跃函数

    阶跃函数是在感知机中使用的激活函数。
    h ( x ) = { 0 x < 0 1 x > = 0 h(x)=\begin{cases} 0 \quad x<0\\ 1 \quad x>=0\\ \end{cases} h(x)={0x<01x>=0

    图像:
    image

    实现

    import numpy as np
    
    
    def step_function(x):
        y = x > 0
        return y.astype(np.int)
    
    
    print(step_function(np.array([1, -1, 1, 3, -3, -4])))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    sigmoid函数

    h ( x ) = 1 1 + e − x h(x)=\frac{1}{1+e^{-x}} h(x)=1+ex1
    其中 e = 2.7182... e=2.7182... e=2.7182...是纳皮尔常数。
    图像:
    image

    实现

    import numpy as np
    import matplotlib.pyplot as plt
    
    
    # numpy array具有广播功能
    def sigmoid(x):
        return 1 / (1 + np.exp(-x))
    
    
    # x = np.arange(-5, 5, 0.1)
    # y = sigmoid(x)
    # plt.plot(x, y)
    # plt.ylim(-0.1, 1.1)
    # plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在感知机中使用的是阶跃函数,在神经网络中我们引入了sigmoid函数。
    可以先比较两种函数
    从两方面去比较
    不同点:

    1. 平滑性:阶跃函数是不平滑的,sigmoid函数是平滑的
    2. 返回值:阶跃函数只能返回两种值0|1,sigmoid可以返回的值是[0,1]之间的实数
      相同点:
    3. 从宏观上去看,两者的趋势比较一致或者说形状吧
    4. 两者均属于非线性函数

    神经网络中的激活函数必须使用非线性函数。
    为什么呢?
    可以考虑使用线性函数,我们知道神经网络是一层一层这样叠加下去,使用线性函数的话,无论叠加多少层,到最后我们的函数都可以等价为只使用一个等价的线性函数

    ReLU函数

    h ( x ) = { 0 x < 0 x x > = 0 h(x)=\begin{cases} 0 \quad x<0\\ x \quad x>=0\\ \end{cases} h(x)={0x<0xx>=0

    图像
    image
    实现

    import numpy as np
    from matplotlib import pyplot as plt
    
    
    def relu(x):
        return np.maximum(0, x)
    
    # x = np.arange(-5, 5, 0.3)
    # y = relu(x)
    # plt.plot(x, y)
    # plt.ylim(-5, 10)
    # plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    三层神经网络的实现

    第0层到第1层

    X = np.array([1.0, 0.5])
    W1 = np.array([[0.1, 0.3, 0.5],
                   [0.2, 0.4, 0.6]])
    B1 = np.array([0.1, 0.2, 0.3])
    
    # print(W1.shape)
    # print(X.shape)
    # print(B1.shape)
    
    # 通过矩阵运算,使得信息向前传递
    A1 = np.dot(X, W1) + B1
    # 激活函数
    Z1 = sigmoid(A1)
    
    # print(A1)
    # print(Z1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第1层到第2层

    W2 = np.array([[0.1, 0.4],
                   [0.2, 0.5],
                   [0.3, 0.6]])
    
    B2 = np.array([0.1, 0.2])
    
    A2 = np.dot(Z1, W2) + B2
    Z2 = sigmoid(A2)
    # print(A2)
    # print(Z2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第2层到输出层

    # 第2层到输出层
    W3 = np.array([[0.1, 0.3],
                   [0.2, 0.4]])
    B3 = np.array([0.1, 0.2])
    
    A3 = np.dot(Z2, W3) + B2
    
    # 恒等函数,将第2层结果传递到输出层
    Y = identity_function(A3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    将上面代码整合一下就可以得到下面代码

    
    def init_network():
        network = {}
        network['W1'] = np.array([[0.1, 0.3, 0.5],
                                  [0.2, 0.4, 0.6]])
        network['B1'] = np.array([0.1, 0.2, 0.3])
        network['W2'] = np.array([[0.1, 0.4],
                                  [0.2, 0.5],
                                  [0.3, 0.6]])
        network['B2'] = np.array([0.1, 0.2])
        network['W3'] = np.array([[0.1, 0.3],
                                  [0.2, 0.4]])
        network['B3'] = np.array([0.1, 0.2])
    
        return network
    
    
    def forward(network, x):
        W1, W2, W3 = network['W1'], network['W2'], network['W3']
        b1, b2, b3 = network['B1'], network['B2'], network['B3']
    
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
    
        a2 = np.dot(z1, W2) + b2
        z2 = sigmoid(a2)
    
        a3 = np.dot(z2, W3) + b3
        y = identity_function(a3)
    
        return y
    
    
    network = init_network()
    x = np.array([1.0, 0.5])
    y = forward(network, x)
    print(y)
    
    • 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

    init_network()中做的工作是,初始化权重和偏置,并用一个字典存储作为返回值
    forward,是将输入信号进行计算得出输出信号,[forward的含义是表示从输入到输出方向传递]

    输出层设计

    我们知道神经网络的运用一般是分类和回归,根据不同的任务,我们可以设计不同的输出,不同的输出可以通过不同的输出层激活函数来实现。
    具体而言,回归问题使用恒等函数 ( i d e n t i t y f u n c t i o n ) (identity_function) (identityfunction),分类问题使用 s o f t m a x softmax softmax函数

    恒等函数和softmax函数

    恒等函数顾名思义就是恒等函数

    import numpy as np
    
    def identity_function(x):
        return x
    
    • 1
    • 2
    • 3
    • 4

    softmax函数
    y k = e a k ∑ i = 1 n e a i y_k=\frac{e^{a_k}}{\sum_{i=1}^{n}{e^{a_i}}} yk=i=1neaieak

    import numpy as np
    
    
    def softmax(x):
        exp_x = np.exp(x)
        sum_exp_x = np.sum(exp_x)
        y = exp_x / sum_exp_x
        return y
     
    # a = np.array([0.3, 2.9, 4.0])
    # print(softmax(a))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    softmax函数的一些小优化防止溢出

    import numpy as np
    
    
    def softmax(x):
        c = np.max(x)
        exp_x = np.exp(x - c)
        sum_exp_x = np.sum(exp_x)
        y = exp_x / sum_exp_x
        return y
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    softmax函数的性质,观察softmax函数的式子可以发现,softmax函数值在[0,1]内,同时所有的函数值加起来为1,这个性质和概率的的定义比较像,所以我们可以把它解释为概率

    输出层的神经元数量

    在分类问题中输出层神经元的数量由分类的类别确定

    手写数字识别

    机器学习解决问题一般分为两个步骤,一是学习,而是推理。
    学习的过程就是最优化我们权重的过程,推理的过程是通过学习到的权重对问题进行处理。
    推理的过程也被称之为前向传播 ( f o r w a r d p r o p a g a t i o n ) (forward propagation) (forwardpropagation)

    MINIST数据集

    MINIST数据集通常是指手写数字识别数据集,其中包含大量的手写数字图像和相应的标签。这个数据集通常用于机器学习和深度学习领域的算法测试和验证。MINIST数据集包含了来自高中学生和美国人口普查局员工的手写数字样本,涵盖了数字0到9。
    MINIST数据集通常包含以下几个部分:
    训练集:用于训练机器学习模型的数据集,包含许多手写数字图像及其对应的标签。
    测试集:用于评估模型性能的数据集,也包含手写数字图像及其标签,但是这些数据不用于模型的训练,只用于测试。
    每个图像通常都是28x28像素的灰度图像,标签是0到9之间的数字,表示相应图像中的手写数字。MINIST数据集是一个相对较小的数据集,适合用于快速验证和测试机器学习模型的原型。

    MINIST数据集是很多深度学习入门书上的例子,很经典。
    有很大的学习价值。

    神经网络的推理处理

    # coding: utf-8
    import pickle
    import sys, os
    from sigmoid import *
    from softmax import *
    sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
    import numpy as np
    from dataset.mnist import load_mnist
    
    def get_data():
        (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
        return x_test, t_test
    
    
    def init_network():
        with open("sample_weight.pkl", 'rb') as f:
            network = pickle.load(f)
    
        return network
    
    
    def predict(network, x):
        W1, W2, W3 = network['W1'], network['W2'], network['W3']
        b1, b2, b3 = network['b1'], network['b2'], network['b3']
        a1 = np.dot(x, W1)
        z1 = sigmoid(a1)
    
        a2 = np.dot(z1, W2)
        z2 = sigmoid(a2)
    
        a3 = np.dot(z2, W3)
        y = softmax(a3)
    
        return y
    
    
    # 输入数据
    x, t = get_data()
    
    # 创建网络
    
    network = init_network()
    
    # 精度(准确度的计算)
    accuracy_cnt = 0
    for i in range(len(x)):
        y = predict(network, x[i])
        # 获取概率最高的索引
        p = np.argmax(y)
        if p == t[i]:
            accuracy_cnt = accuracy_cnt + 1
    
    print("Accuracy:" + str(float(accuracy_cnt / len(x))))
    
    • 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

    批处理

    1. 向量化运算: 在现代的深度学习框架中,通常会利用高度优化的线性代数库(如NumPy、TensorFlow、PyTorch等)来执行批处理操作。这些库能够利用硬件加速器(如GPU)进行并行化操作,从而高效地进行向量化运算。相比逐个样本处理,批处理可以充分发挥这些库的优势,使得计算速度大大提高。

    2. 减少数据传输开销: 在训练过程中,数据的传输开销可能成为瓶颈之一,尤其是当数据量较大时。通过批处理,可以减少数据传输的次数,因为一次传输可以处理多个样本。这样可以显著减少由数据传输引起的延迟。

    3. 优化内存访问: 当处理一批数据时,可以利用现代计算机架构的缓存机制,从而更有效地利用内存。在批处理中,连续的数据通常会被放置在内存中相邻的位置,这有助于减少内存访问的随机性,提高数据访问效率。

    4. 并行计算: 许多深度学习框架支持在GPU上并行计算,批处理可以更充分地利用GPU的并行计算能力。通过一次处理多个样本,可以同时执行多个神经网络计算步骤,从而加快整体训练速度。

    
    x, t = get_data()
    network = init_network()
    
    batch_size = 100  # 批数量
    accuracy_cnt = 0
    
    # range(start, end, step),start表示起始下标,end表示结束下标的下一个,step为步长[start, end)
    for i in range(0, len(x), batch_size):
    	# numpy中的切片操作
        x_batch = x[i:i + batch_size]
        y_batch = predict(network, x_batch)
        # axis=1表示确定第0维求,第二维最大值的下标
    	p = np.argmax(y_batch, axis=1)
        accuracy_cnt += np.sum(p == t[i:i + batch_size])
    
    print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    Android AMS——进程优先级更新(十八)
    面对中小型机房动力环境该如何实现监控?
    java Arrays类
    机器学习+CFD的后续
    vue锚点链接
    基于JAVA智慧物业管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    【人见人爱报错系列】GIt常见问题解决大全
    40.【C++最全文件操作,少一个你打我】
    用于光栅仿真的非偏振光–实例讨论
    【06】基础知识:typescript中的泛型
  • 原文地址:https://blog.csdn.net/weixin_61426225/article/details/136748927