用 Python 从零开始创建神经网络(三):添加层级(Adding Layers)

添加层级(Adding Layers)

  • 引言
  • 1. Training Data
  • 2. Dense Layer Class

引言

我们构建的神经网络变得越来越受人尊敬,但目前我们只有一层。当神经网络具有两层或更多隐藏层时,它们变成了“深度”网络。目前我们只有一层,实际上是一个输出层。为什么我们想要两层或更多隐藏层的原因将在后面的章节中变得明显。目前,我们没有隐藏层。隐藏层不是输入层或输出层;作为科学家,你看到的数据是输入到输入层的数据以及来自输出层的结果数据。这些端点之间的层具有我们不一定处理的值,因此得名“隐藏”。不过,别让这个名字让你认为你不能访问这些值。你通常会使用它们来诊断问题或改进你的神经网络。为了探索这个概念,让我们给这个神经网络增加另一层,并且,现在,让我们假设我们将要拥有的这两层是隐藏层,我们还没有编写我们的输出层。

在我们添加另一层之前,让我们思考一下即将到来的内容。对于第一层,我们可以看到我们有一个包含4个特征的输入:


在这里插入图片描述

将包含 4 个特征的输入层转化为包含 3 个神经元的隐藏层。


样本(特征集数据)通过输入传递,输入本身不会以任何方式改变它,传递到我们的第一个隐藏层,我们可以看到这一层有3组权重,每组包含4个值。

这3组独特的权重集与它们各自的神经元相关联。因此,由于我们有3组权重,这第一个隐藏层就有3个神经元。每个神经元都有一组独特的权重,我们有4个(因为这一层有4个输入),这就是为什么我们的初始权重有一个形状为 ( 3 , 4 ) (3,4) (3,4) 的原因。

现在,我们希望增加另一层。为此,我们必须确保该层的预期输入与前一层的输出匹配。我们通过设置每层有多少组权重和偏差来设定层中的神经元数量。前一层对当前层的权重集的影响是每个权重集需要有每个输入的单独权重。这意味着前一层的每个神经元(或如果我们谈论输入的话,每个特征)都有一个独特的权重。前一层有3组权重和3个偏差,所以我们知道它有3个神经元。这意味着,对于下一层,我们可以有尽可能多的权重集(因为这是这个新层将拥有的神经元数量),但这些权重集中的每一个都必须有3个独立的权重。

为了创建这个新层,我们将复制并粘贴我们的权重和偏差到 weights2 和 biases2,并将它们的值改为新的假设集。这里有一个例子:

import numpy as np

inputs = [[1, 2, 3, 2.5],
          [2., 5., -1., 2],
          [-1.5, 2.7, 3.3, -0.8]]

weights = [[0.2, 0.8, -0.5, 1],
           [0.5, -0.91, 0.26, -0.5],
           [-0.26, -0.27, 0.17, 0.87]]
biases = [2, 3, 0.5]

weights2 = [[0.1, -0.14, 0.5],
            [-0.5, 0.12, -0.33],
            [-0.44, 0.73, -0.13]]
biases2 = [-1, 2, -0.5]

接下来,我们将调用输出 layer1_ouputs

layer1_outputs = np.dot(inputs, np.array(weights).T) + biases
print("layer1_outputs:", layer1_outputs)
>>>
layer1_outputs: [[ 4.8    1.21   2.385]
				 [ 8.9   -1.81   0.2  ]
				 [ 1.41   1.051  0.026]]

正如之前所述,层的输入要么是来自您正在训练的实际数据集的输入,要么是来自前一层的输出。这就是为什么我们定义了两个版本的权重和偏差,但只有一个输入的原因 —— 因为第二层的输入将是前一层的输出:

layer2_outputs = np.dot(layer1_outputs, np.array(weights2).T) + biases2
print("layer2_outputs:", layer2_outputs)
>>>
layer2_outputs: [[ 0.5031  -1.04185 -2.03875]
				 [ 0.2434  -2.7332  -5.7633 ]
				 [-0.99314  1.41254 -0.35655]]

完整:

import numpy as np

inputs = [[1, 2, 3, 2.5],
          [2., 5., -1., 2],
          [-1.5, 2.7, 3.3, -0.8]]

weights = [[0.2, 0.8, -0.5, 1],
           [0.5, -0.91, 0.26, -0.5],
           [-0.26, -0.27, 0.17, 0.87]]
biases = [2, 3, 0.5]

weights2 = [[0.1, -0.14, 0.5],
            [-0.5, 0.12, -0.33],
            [-0.44, 0.73, -0.13]]
biases2 = [-1, 2, -0.5]

layer1_outputs = np.dot(inputs, np.array(weights).T) + biases
print("layer1_outputs:", layer1_outputs)

layer2_outputs = np.dot(layer1_outputs, np.array(weights2).T) + biases2
print("layer2_outputs:", layer2_outputs)

此时,我们的神经网络可以直观地表示为:
在这里插入图片描述


1. Training Data

训练数据


接下来,我们将使用一个可以创建非线性数据的函数,而不是手工输入随机数据。什么叫非线性数据?线性数据可以用直线拟合或表示。


在这里插入图片描述

可用直线(绿线)表示(拟合)的数据(橙色点)示例


非线性数据无法用直线很好地表示:


在这里插入图片描述

直线拟合不理想的数据(橙色点)示例。


如果你要绘制形式为 ( x , y ) (x, y) (x,y) 的数据点,其中 y = f ( x ) y = f(x) y=f(x),并且看起来像是一条具有明显趋势或斜率的线,那么很可能,这些是线性数据!线性数据可以非常容易地被比神经网络简单得多的机器学习模型近似。其他机器学习算法不容易近似的是非线性数据集。为了简化这一点,我们创建了一个可以通过 pip 安装的Python包,名为 nnfs:

pip3 install nnfs

nnfs 软件包包含一些函数,我们可以用它们来创建数据。例如:

from nnfs.datasets import spiral_data

螺旋数据(spiral_data)函数是从 https://cs231n.github.io/neural-networks-case-study/ 上稍加修改的,它是本主题的一个很好的补充资源。


通常你不会为你的神经网络从函数生成训练数据。你会有一个实际的数据集。这种方式生成数据集纯粹是为了目前阶段的便利性。我们还将使用这个包来确保每个人的重复性,使用 nnfs.init(),在导入NumPy之后:

import numpy as np
import nnfs

nnfs.init()

nnfs.init()执行三件事:它将随机种子设置为0(默认情况下),创建一个 float32 数据类型的默认值,并覆盖NumPy的原始点积函数。所有这些都是为了确保后续操作的可重复结果。

spiral_data函数允许我们创建一个数据集,可以有我们想要的任意多的类别。该函数有参数可以选择类别数和每个类别中点/观测的数量,来生成结果中的非线性数据集。例如:

import numpy as np
import nnfs
from nnfs.datasets import spiral_data
import matplotlib.pyplot as plt

nnfs.init()

X, y = spiral_data(samples=100, classes=3)

plt.scatter(X[:,0], X[:,1])
plt.show()

在这里插入图片描述

未着色的螺旋数据集


如果从中心开始追踪,就可以分别确定所有 3 个类别,但对于机器学习分类器来说,这是一个非常具有挑战性的问题。在图表中添加颜色可以让这一点更加清晰:

import numpy as np
import nnfs
from nnfs.datasets import spiral_data
import matplotlib.pyplot as plt

nnfs.init()

X, y = spiral_data(samples=100, classes=3)

plt.scatter(X[:,0], X[:,1])
plt.show()

plt.scatter(X[:, 0], X[:, 1], c=y, cmap='brg')
plt.show()

在这里插入图片描述

在这里插入图片描述

按类别着色的螺旋数据集


请记住,神经网络不会意识到颜色的差异,因为数据没有类别编码。这只是作为对读者的一种指导。在上述数据中,每个点都是特征,其坐标是构成数据集的样本。该点的 “分类 ”与它属于哪种螺旋有关,如上图中的蓝色、绿色或红色。然后,这些颜色将被分配一个类别编号,供模型拟合,如 0,1和2。


2. Dense Layer Class

密集层级


既然我们不再需要手工输入数据,我们就应该为各种类型的神经网络层创建类似的功能。到目前为止,我们只使用了所谓的密集层或全连接层。这些层在论文、文献和代码中通常被称为 “密集 ”层,但你偶尔也会在代码中看到它们被称为全连接层,简称 “FC”。我们的密集层类将以两个方法开始:

class Layer_Dense:
    # Layer initialization
    def __init__(self, n_inputs, n_neurons):
        # Initialize weights and biases
        pass # using pass statement as a placeholder
        
    # Forward pass
    def forward(self, inputs):
        # Calculate output values from inputs, weights and biases
        pass # using pass statement as a placeholder

如前所述,模型的权重通常是随机初始化的,但不总是这样。如果你希望加载一个预训练的模型,你将把参数初始化为预训练模型完成时的状态。也有可能即使是对于一个新模型,你也有一些除了随机之外的其他初始化规则。现在,我们将坚持使用随机初始化。接下来,我们有前向方法。当我们从头到尾通过模型传递数据时,这被称为前向传递。然而,就像其他一切一样,这并不是唯一的做事方式。你可以让数据回环并做其他有趣的事情。我们将保持通常的做法,执行一个常规的前向传递。

为了继续 Layer_Dense 类的代码,让我们添加权重和偏置的随机初始化:

import numpy as np

class Layer_Dense:
    # Layer initialization
    def __init__(self, n_inputs, n_neurons):
        # Initialize weights and biases
        self.weights = 0.01 * np.random.randn(n_inputs, n_neurons)
        self.biases = np.zeros((1, n_neurons))
        
    # Forward pass
    def forward(self, inputs):
        # Calculate output values from inputs, weights and biases
        pass # using pass statement as a placeholder

在这里,我们将权重设置为随机,偏置设置为0。请注意,我们将权重初始化为(输入,神经元),而不是(神经元,输入)。我们提前这样做,而不是每次执行前向传递时都转置,如上一章所解释的。为什么偏置为零?在特定情况下,例如当许多样本包含0值时,偏置可以确保神经元最初被激活。有时将偏置初始化为某个非零数可能是合适的,但偏置最常见的初始化是0。然而,在这些情况下,你可能会发现以另一种方式做事会成功。这将根据你的用例而有所不同,并且只是你在尝试改进结果时可以调整的许多事情之一。一个你可能想尝试其他方法的情况是所谓的死神经元。我们还没有在实践中讨论激活函数,但想象一下我们之前提到的阶跃函数:

在这里插入图片描述

权重 · 输入 + 偏置没有达到阶跃函数的阈值时,可能会导致神经元输出0。单独来看,这不是一个大问题,但如果每一个输入样本都导致这个神经元发生这种情况,就会成为一个问题(一旦我们讲解反向传播,这将变得清晰)。因此,这个神经元的0输出就成为另一个神经元的输入。任何与零相乘的权重都将是零。随着输出0的神经元数量增加,更多的输入将传递给下一个神经元这些0,使得网络基本上无法训练,或者“死亡”。


接下来,让我们更详细地探讨 np.random.randnnp.zeros 这些方法。这些方法是初始化数组的便捷方式。np.random.randn 产生一个均值为0和方差为1的高斯分布,这意味着它将生成以0为中心的随机数,正负都有,平均值接近0。一般来说,神经网络在 -1 到 +1 之间的值下工作最好,我们将在即将到来的章节中讨论这一点。因此,np.random.randn 生成围绕这些数字的值。我们将乘以 0.01 的高斯分布用于权重,以生成比较小的数值。否则,模型在训练过程中将需要更多时间来适应数据,因为开始的值与训练过程中进行的更新相比过大。这里的想法是用足够小的非零值开始一个模型,这些值不会影响训练。这样,我们就有一堆值可以开始使用,但希望没有太大或为零的值。如果你愿意,可以尝试除0.01之外的其他值。最后,np.random.randn 函数接受维度大小作为参数,并创建具有此形状的输出数组。

这里的权重将是第一维度的输入数量和第二维度的神经元数量。这类似于我们之前虚构的权重数组,只是随机生成的。每当有一个你不确定的函数或代码块时,你总是可以打印它出来。例如:

import numpy as np
import nnfs

nnfs.init()

random_test = np.random.randn(2,5)
zero_array = np.zeros((2,5))

print("random_test:", random_test)
print("zero_array:", zero_array)
>>>
random_test: [[ 1.7640524   0.4001572   0.978738    2.2408931   1.867558  ]
			  [-0.9772779   0.95008844 -0.1513572  -0.10321885  0.41059852]]
zero_array: [[0. 0. 0. 0. 0.]
 			 [0. 0. 0. 0. 0.]]

np.random.randn(2,5)函数调用返回了一个2x5的数组(我们也可以说它的“形状是 ( 2 , 5 ) (2,5) (2,5)”),数据是从均值为0的高斯分布中随机抽样得到的。

在调用 np.random.randn(2, 5) 时:参数 2 和 5 指定了生成数组的形状。 2 表示数组有行。 5 表示数组有列。

接下来,np.zeros 函数接受一个期望的数组形状作为参数,并返回一个填充了零的该形状数组。


我们将以行向量的形式初始化偏置,其形状为 ( 1 , n _ n e u r o n s ) (1, n\_{neurons}) (1,n_neurons),这将使我们能够在以后轻松地将其加到点积的结果上,无需进行转置等额外操作。

看一个我们方法初始化权重和偏置的例子:

import numpy as np
import nnfs

nnfs.init()

n_inputs = 2
n_neurons = 4

weights = 0.01 * np.random.randn(n_inputs, n_neurons)
biases = np.zeros((1, n_neurons))

print("weights:", weights)
print("biases:", biases)
>>>
weights: [[ 0.01764052  0.00400157  0.00978738  0.02240893]
		  [ 0.01867558 -0.00977278  0.00950088 -0.00151357]]
biases: [[0. 0. 0. 0.]]

接下来是我们的正演方法——我们需要用点积+偏差计算来更新它:

import numpy as np

class Layer_Dense:
    # Layer initialization
    def __init__(self, n_inputs, n_neurons):
        # Initialize weights and biases
        self.weights = 0.01 * np.random.randn(n_inputs, n_neurons)
        self.biases = np.zeros((1, n_neurons))
        
    # Forward pass
    def forward(self, inputs):
        # Calculate output values from inputs, weights and biases
        self.output = np.dot(inputs, self.weights) + self.biases

我们已经准备好使用这个新类来代替硬编码计算,因此让我们使用讨论过的数据集创建方法生成一些数据,然后使用我们的新层来执行前向传递:

import numpy as np
import nnfs
from nnfs.datasets import spiral_data

nnfs.init()

class Layer_Dense:
    # Layer initialization
    def __init__(self, n_inputs, n_neurons):
        # Initialize weights and biases
        self.weights = 0.01 * np.random.randn(n_inputs, n_neurons)
        self.biases = np.zeros((1, n_neurons))
        
    # Forward pass
    def forward(self, inputs):
        # Calculate output values from inputs, weights and biases
        self.output = np.dot(inputs, self.weights) + self.biases


# Create dataset
X, y = spiral_data(samples=100, classes=3)

# Create Dense layer with 2 input features and 3 output values
dense1 = Layer_Dense(2, 3)

# Perform a forward pass of our training data through this layer
dense1.forward(X)

# Let's see output of the first few samples:
print(dense1.output[:5])
>>>
[[ 0.0000000e+00  0.0000000e+00  0.0000000e+00]
 [-1.0475188e-04  1.1395361e-04 -4.7983500e-05]
 [-2.7414842e-04  3.1729150e-04 -8.6921798e-05]
 [-4.2188365e-04  5.2666257e-04 -5.5912682e-05]
 [-5.7707680e-04  7.1401405e-04 -8.9430439e-05]]

在输出结果中,你可以看到我们有 5 行数据,每行有 3 个值。这 3 个值中的每一个都是dense1层中 3 个神经元输入每个样本后的值。好极了!我们有了一个神经元网络,所以我们的神经网络模型几乎名副其实了,但我们还缺少激活函数,所以接下来让我们完成激活函数!

本章的章节代码、更多资源和勘误表:https://nnfs.io/ch3

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在jupyter notebook切换python环境

目录 参考链接 首先确保conda已经正常安装 conda --version 或者conda -V 以下请将“myenv”替换成自己的命名!!! 1-查看虚拟环境目录 conda env list 2-创建虚拟环境命令 conda create -n myenv 或者 conda create --name myenv 3-激活虚拟环…

C#从入门到放弃

C#和.NET的区别 C# C#是一个编程语言 .NET .NET是一个在window下创建程序的框架 .NET框架不仅局限于C#,它还可以支持很多语言 .NET包括了2个组件,一个叫CLR(通用语言运行时),另一个是用来构建程序的类库 CLR 用C写一个程序,在一台8688的机器…

STM32 低功耗模式详解

目录 一、什么是低功耗 二、低功耗的核心思想 三、STM32的3种低功耗模式 1、睡眠模式 (Sleep Mode) 2、停止模式 (Stop Mode) 3、 待机模式 (Standby Mode) 四、相关电源管理寄存器 1、PWR_CR (Power Control Register, 电源控制寄存器) 2、PWR_CSR (Power Control/St…

数位DP学习

数位 DP - OI Wiki 引入 主要变量及函数 变量: L, R: 所求区间边界 limit:边界限制,主要在记忆化搜索里用 len:所求数的位数 pos:当前所求位置 lead: 前导零 DP[N][M] :第一维是当前的pos&…

WP网站如何增加文章/页面的自定义模板

通过Wordpress我们后台在发布文章或者页面的时候其实可以看到有些主题 他有选择使用的页面模板,可以自定义模板,但是有些主题却没有选择主题这个功能,那这个自定义模板的功能是如何实现的呢?以下分两种情况:Page页面和…

Python学习从0到1 day27 Python 高阶技巧 ③ 设计模式 — 单例模式

此去经年,再难同游 —— 24.11.11 一、什么是设计模式 设计模式是一种编程套路,可以极大的方便程序的开发最常见、最经典的设计模式,就是我们所学习的面向对象了。 除了面向对象外,在编程中也有很多既定的套路可以方便开发,我们称之为设计模…

DAY112代码审计PHP开发框架POP链利用Yii反序列化POP利用链

一、pop1链的跟踪 1、路由关系 2、漏洞触发口unserialize(base64_decode($data)); 2、__destruct(),魔术法方法调用close函数方法 3、未找到利用链,尝试__call魔术方法 4、逆推找call_user_func 函数 第一部分 namespace yii\db; class BatchQueryResu…

Flink新版Source接口源码解析

目录 1. 前言 2. Source解析 2.1 Source类图 2.2 接口和方法说明 2.2.1 Source,> 3. SplitEnumerator解析 3.1 SplitEnumetator类图 3.2 类和方法说明 3.2.1 SplitEnumerator 3.2.2 SimpleVersionedSerializer 4. SourceReader解析 4.1 SourceReader类图 4.2 类…

SpringBoot后端解决跨域问题

1.全局方式 新建一个conifg配置类,内容如下: Configuration public class CorsConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**")//是否发送Cookie.allowCrede…

qt QUndoCommand 与 QUndoStack详解

1、概述 QUndoCommand 和 QUndoStack 是 Qt 框架中用于实现撤销/重做(undo/redo)功能的两个核心类。QUndoCommand 是表示单个可撤销操作的基类,而 QUndoStack 则负责管理这些命令的堆栈,提供撤销和重做操作的接口。 QUndoCommand…

Struts源码阅读——三个常用的辅助类DispatchAction

Struts源码阅读——三个常用的辅助类 紧接前文,我们来阅读org.apache.struts.actions包中三个常用类的源码。 DispatchAction、LookupDispatchAction 和 MappingDispatchAction 是 Struts 1 框架中的三个常用的辅助类,用来简化 Action 类中的请求分发。…

C++中的栈(Stack)和堆(Heap)

在C中,堆(heap)和栈(stack)是两种用于存储数据的内存区域。理解它们的原理和区别,对于优化代码性能和确保代码的安全性至关重要。以下是对C中堆栈的详细解析,包括它们的分配方式、优缺点、应用场…

群控系统服务端开发模式-应用开发-前端登录接口开发

一、修改验证方法 1、修改验证器 loginRules: {username: [{required: true, trigger: blur, validator: validateUsername}],password: [{required: true, trigger: blur, validator: validatePassword}],captcha_code: [{required: true, trigger: blur, validator: validat…

游戏引擎学习第10天

视频参考:https://www.bilibili.com/video/BV1LyU3YpEam/ 介绍intel architecture reference manual 地址:https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html RDTS(读取时间戳计数器)指令是 x86/x86_64 架构中的…

MySQL查询某个数据库中特定表的空间占用大小

如果您也想要查询某个数据库中特定表的空间占用大小,包括数据和索引的大小,那么您可以使用以下SQL查询。这个查询将显示特定表在数据库中的数据大小、索引大小以及总大小。 SELECT table_name AS Table,ROUND(((data_length index_length) / 1024 / 10…

SystemVerilog学习笔记(十一):接口

在Verilog中,模块之间的通信是使用模块端口指定的。 Verilog模块连接的缺点 声明必须在多个模块中重复。存在声明不匹配的风险。设计规格的更改可能需要修改多个模块。 接口 SystemVerilog引入了 interface 结构,它封装了模块之间的通信。一个 inter…

el-input 正则表达式校验输入框不能输入汉字

<el-form :model"data1" :rules"rules" ref"ruleForm" label-width"210px" class"demo-ruleForm"><el-form-item label"锯路&#xff1a;" prop"sawKref"><el-input class"inptWid…

Pikachu[暴力破解:token防爆破]

暴力破解:token防爆破 校验方式&#xff1a; 请求中添加token防止爆破&#xff0c;登录时需携带服务器上一次加载时发送的token进行校验 解决&#xff1a; burp--intruder模块设置中使用Grep-Extract功能提取页面中的token&#xff0c;并将载荷类型更改为递归查询[Recursiv…

Springboot如何打包部署服务器

文章目的&#xff1a;java项目打包成jar包或war包&#xff0c; 放在服务器上去运行 一、编写打包配置 1. pom.xml 在项目中的pom.xml文件里面修改<build>...</build>的代码 >> 简单打包成Jar形式&#xff0c;参考示例&#xff1a; <build><fina…

flink 同步oracle11g数据表到pg库

1. 关闭防火墙和selinux systemctl stop firewalld systemctl disable firewalld systemctl status firewalldvi /etc/selinux/config 修改为disabled2.安装java8 yum list java-1.8* yum install java-1.8.0-openjdk* -yjava -version3.下载和部署postgresql 看需求安装pg库…