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

news2024/11/17 0:01:23

打开深度学习的锁

  • 导言
  • PS:神经网络的训练过程
  • 一、数据集和包的说明
    • 1.1准备文件
    • 1.2 需要导入的包
  • 二、构建神经网络的架构
  • 三、初始化函数
  • 四、激活函数
    • 4.1 tanh(双曲正切函数)函数
  • 五,前向传播
  • 六、损失函数
  • 七、后向传播
  • 八、梯度下降
  • 九、构建预测
  • 十、聚合和主函数
  • 完整代码:
  • 总结

导言

本篇知识背景来源于吴恩达教授的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

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 需要导入的包

与上一篇相比,除了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 #它包含了许多线性模型,这些模型可以用于各种任务,如回归、分类和异常检测。

二、构建神经网络的架构

首先,我们要引入一个新的中间层,隐藏层。
让我们先不考虑损失的计算,也就是现忽略标签 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)#得到神经网络

三、初始化函数

现在,我们可以来构建初始化函数了,也就是对 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啦。

对应的公式其实和上一篇没有什么大太变化。

#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

四、激活函数

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

除了用于二分类任务最后的输出,否则不要用 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

五,前向传播

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

假设: 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"]))

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

六、损失函数

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

对于单个样本:
二元交叉熵损失函数(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)))

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

七、后向传播

所谓后向传播,就是计算 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)

代码:

#后向传播
#首先,我们需要损失函数想相较于最后一层输出的导数
#我们还需要使用链式法则计算每一层的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"]))

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

八、梯度下降

更新 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"]))

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

九、构建预测

这一步很关键,我们要使用 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)))

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

十、聚合和主函数

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

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 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)

完整代码:

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()   

输出结果:

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

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1033061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营第23期day3| 203.移除链表元素 ,707.设计链表,206.反转链表

目录 一、链表 基础操作 二、(leetcode 203)移除链表元素 1.使用原来的链表 2.设置虚拟头结点 三、(leetcode 707)设计链表 四、(leetcode 206)反转链表 1.双指针法 2.递归法 一、链表 单链表定义…

SpringMVC学习笔记——1

SpringMVC学习笔记——1 一、SpringMVC简介1.1、SpringMVC概述1.2、SpringMVC快速入门1.3、Controller中访问容器中的Bean1.4、SpringMVC关键组件的浅析 二、SpringMVC的请求处理2.1、请求映射路径配置2.2、请求数据的接收2.2.1、键值对方式接收数据2.2.2、封装JavaBean数据2.2…

JavaBean专项练习(学生信息管理系统)(增删改查)(每步详细解释和思路)

/* 题目: 定义一个长度为3的数组,数组存储1~3名学生对象作为初始数据学生: 1. 学号 2.姓名 3.年龄要求: 1.再次添加一个学生对象,并在添加的时候进行学号唯一的判断2. 添加完毕后,遍历所有学生信息3. 通过id删除学生信息,如果存在则删除,如果不存在则提示删除失败4. 删除完毕之…

tensorflow-卷积神经网络-图像分类入门demo

猫狗识别 数据预处理:图像数据处理,准备训练和验证数据集卷积网络模型:构建网络架构过拟合问题:观察训练和验证效果,针对过拟合问题提出解决方法数据增强:图像数据增强方法与效果迁移学习:深度…

网页版的 Redis 可视化工具来了,已开源?

轻量级Redis缓存图形化管理工具,包含redis的5种数据类型的CRUD操作 软件架构 后端 springboot 2.2.2.RELEASEJDK 1.8jedis 3.2.0commons-lang3 3.5hutool-core 5.1.1fastjson 1.2.62h2database 1.4.200 前端 vue-admin 1.0.5axios 0.15.3element-ui 2.13.0font-…

海外媒体发稿:海外汽车媒体推广9个方式解析

根据下列9个国外汽车媒体推广方式,企业能够在国际范围内突破边界,获得领域关心。这将帮助企业完成国际化发展发展战略,扩展市场占有率和提升盈利空间。【华媒舍】国外全媒体发表文章将会成为企业完成这一目标的重要方式,为企业带来…

Caton Media Xstream: 重新定义实时内容交付服务

// 编者按:随着公共互联网愈加复杂,best effort的基本原型已无法满足越来越多的有QoS保障需求的实时内容交付服务。而专线、卫星等传统解决方案存在部署成本高、周期长等问题,无法快速响应各类需求。LiveVideoStackCon邀请到了科腾科技的魏…

解决:Typora上传图片后本地显示不出来

在配置好PicGo、github以及Typora后,为了更好部署博客,将图片的偏好设置改为上传图片,会出现一个问题: github上图片已上传成功,但是本地Typora的图片不显示,这里进行配置: 文件——>偏好设…

Oracle 11g RAC部署笔记

搭了三次才搭好,要记录一下。 1. Oracle 11g RAC部署的相关步骤以及需要的包,可以参考这里。 Oracle 11g RAC部署_12006142的技术博客_51CTO博客Oracle 11g RAC部署,Oracle11gRAC部署操作环境:CentOS7.4Oracle11.2.0.4一、主机网…

安卓玩机搞机----不用刷第三方官改固件即可享受“高级设置”的操作 ChiMi安装使用步骤

很多玩友特别喜欢第三方作者修改的带有高级设置的官改包。因为他可以随意修改系统里面的有关设置选项。包括但不限于修改状态栏 显示日期 秒等等的操作。 第三方带高级设置的官改 一般官改带高级设置的类似与 今天给大家分享下不用刷这些官改包即可享受高级设置的操作。 红米…

Android面试题汇总(三)

Android 四大组件相关 1、Activity与Fragment之间常见的通讯方式 对于Activity与Fragment直接的相互调用: 1、Activity调用Fragment直接调用就好了,Activity一般是持有Fragment实例的。或者通过Fragment的id或者tag获取Fragment的实例 2、Fragment调用A…

CRM系统主要包括哪些功能?

CRM系统应该要包括的功能总结为3大方向—— 核心必须要具备的功能常见尽量要有的功能可选有了自然更好的功能 以我们公司用的简道云CRM系统模板为例:https://www.jiandaoyun.com 01 核心必须要具备的功能 核心功能决定了系统是否能够被纳入CRM类别,这些…

升级pip

升级pip 报错提示: WARNING: You are using pip version 19.1.1, however version 20.0.2 is available. You should consider upgrading via the ‘python -m pip install --upgrade pip’ command. 解决办法 py -m pip install --upgrade pip记得关掉梯子 如果…

全面重构存储系统,释放AI时代全新数据价值

《失控》作者凯文凯利做出判断: “不管你从事任何商业,你的生意现在就是数据生意。”一言蔽之,数据已是当今商业社会和经济发展的基石。企业的生产研发、经营管理和销售服务等所有环节愈发离不开数据,数实融合的加速为产业发展带来无限可能。…

视频编解码器H.264和H265有什么区别?

对于大型视频文件来说,视频编解码器至关重要,它可以将文件压缩为较小的尺寸,从而可以更轻松地存储和加快传输速度。而两种最常用的编解码器是H.264和H.265,那么它们两者之间有什么区别,哪一个更好呢? 1. 什…

https SSL证书使用 git bash 解密

申请域名证书后,有些证书下载时强制加密。 在使用时,比如在AWS ACM中使用时,不能用加密的证书。所以这里讲下怎么解密。 首先,加密一般加密的是公私钥中的私钥,即private.key。 填写密码,下载证书&#x…

玩转蓝牙墨水屏电子标签(一)点灯

对于垃圾佬的生活来说,每天逛海鲜市场是必不可少的生活片段,这不,手抖一下又刷到了一个东付的电子标价签。 价格合理,2块钱一个不包邮,直接买了N个。。。算了一下一个3.5,然后拿到群去炫耀了下,…

Xilinx FPGA 7系列 GTX/GTH Transceivers (4) Aurora 8b10b 递增数收发验证

第一节:Xilinx FPGA 7系列 GTX/GTH Transceivers (1)–了解了GTX硬件的基础知识 第二节:IBERT GTX --通过Ibert IP测试链路通信 第三节:aurora 8b10b single lane 4byte–学习官方历程 递增数验证 自行编写data_gen和data_check 验证aurora 8b10b SFP 1.25G 收发正确。 组…

用过lsof命令的,都竖起了大拇指!!!

lsof(list open files)是一个列出当前系统打开文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) …

webpack常用配置与性能优化插件

webpack是一个流行的前端项目构建工具(打包工具),可以解决当前web 开发中所面临的困境。 提供了友好的模块化支持,以及代码压缩混淆、处理js兼容问题、性能优化等强大的功能,从而让程序员把工作的重心放到具体的功能实…