文章目录
- 前言
- 一、神经网络
- 基本概念
- 基本组成
- 工作流程
- 训练过程
- 类型
- 应用举例
- 不同层次特征的学习
- 为什么从基础特征到复杂特征逐渐推进
- 什么是感受野
- 更简单的解释
- 具体示例
- 总结
- 二、TensorFlow实现简单神经网络
- 安装及环境配置
- 数据预处理
- 标准化
- Dense层
- Convolutional Layer
- 训练
- DEBUG
- 三、激活函数
- RELU
- Softmax
- Softmax的优化
- 多分类问题
- 四、高级优化方法
- Adam
前言
博客仅记录个人学习进度和一些查缺补漏。
学习内容:BV1Bq421A74G
一、神经网络
神经网络(Artificial Neural Networks,ANNs)是受人脑结构启发而构建的计算模型,用于模拟大脑神经元网络处理信息的方式。它们是机器学习和深度学习领域的核心技术之一,能够解决多种复杂的模式识别和预测问题。
基本概念
基本组成
神经网络由以下基本组件构成:
- 神经元(Neurons):网络的基本单元,类似于人脑中的神经元。每个神经元接收输入,进行加权求和,并通过激活函数处理这个求和值来产生输出。
- 层(Layers):神经元被组织成层。一个基本的神经网络包括输入层、一个或多个隐藏层以及输出层。
- 连接权重(Weights):连接神经元的边具有权重,这些权重在训练过程中学习得到。
- 偏置(Biases):每个神经元还可以有一个偏置项,它用来调整神经元的输出。
- 激活函数(Activation Functions):用于引入非线性,使得神经网络能够学习和执行更复杂的任务。
工作流程
- 前向传播(Forward Propagation):输入数据在网络中从输入层经过隐藏层传递到输出层,每一层的输出成为下一层的输入。
- 损失函数(Loss Function):计算模型预测值和真实值之间的差异。
- 反向传播(Backpropagation):利用损失函数计算的梯度,通过网络反向传播这些梯度,以更新网络中的权重和偏置。
- 优化算法(Optimization Algorithms):如梯度下降(Gradient Descent)及其变体,用于在每次迭代中更新权重,以最小化损失函数。
训练过程
训练神经网络通常涉及以下步骤:
- 初始化:随机初始化网络权重和偏置。
- 迭代训练:通过多次迭代训练数据集,每次迭代都会进行前向传播和反向传播。
- 评估和调整:在验证集上评估模型性能,并根据需要调整模型结构或训练过程。
- 测试:使用测试集评估训练好的模型的性能。
类型
- 前馈神经网络(Feedforward Neural Networks,FNN):最常见的类型,信息只向前流动,没有反馈连接。
- 卷积神经网络(Convolutional Neural Networks,CNN):特别适合处理图像数据,具有卷积层来捕捉局部特征。
- 循环神经网络(Recurrent Neural Networks,RNN):适合处理序列数据,如时间序列分析和自然语言处理。
- 长短期记忆网络(Long Short-Term Memory,LSTM):一种特殊的RNN,能够学习长期依赖关系。
应用举例
g->激活函数
基于TensorFlow的简单dense层
不同层次特征的学习
第一层:低级特征
边缘和纹理:第一层的卷积核(filters)通常检测图像的基本边缘、线条和简单的纹理。这些特征包括水平、垂直和对角线方向的边缘。
局部感受野:第一层的卷积核通常应用于图像的小局部区域(称为感受野),这使得它们只能提取局部的一些基础特征。
中间层:中级特征
模式和形状:中间层的神经元结合了前几层检测到的低级特征,形成更复杂的模式和形状,如角元素、曲线、简单几何图形。
增大的感受野:随着层数增加,每层的神经元感受的图像区域会变大,即“感受野”会变大。这样它们可以综合前面层次的信息,识别更加复杂的模式。
高级层:高级特征
物体组件和语义特征:在更深的层中,神经网络开始组合之前学习到的复杂模式,识别出具体物体的部分或更抽象的语义特征。例如,一些神经元可能对人脸的某些组件(如眼睛、鼻子)特别敏感。
全图感知:最深的层一般具有最大的感受野,能够感知到整个输入图像的信息,因而能识别出完整的物体或者整个场景。
为什么从基础特征到复杂特征逐渐推进
在多层神经网络中,尤其是卷积神经网络(CNN),特征提取往往是从基础特征到复杂特征逐渐推进。这是因为:
-
局部到全局:早期层的卷积滤波器(filters)处理的是图像的小块区域,只能检测出简单的边缘和纹理。随着层数的增加,高层的滤波器能够结合更多早期层的输出,逐渐构建出更复杂的特征,如形状、物体部件,最终形成完整的物体识别。
-
逐层组合:每一层的输出是前一层的输入。早期层提取简单的特征,这些特征被传递到后续层,后续层将在此基础上进一步组合和加工这些特征,形成更高级的描述。
-
有效特征表征:通过这样的层次化结构,神经网络能有效地表示和处理图像中的信息,从而更好地完成识别任务。从简单到复杂的特征提取过程,使得模型能够理解和处理多种多样的视觉模式,增强了其泛化能力。
什么是感受野
感受野(Receptive Field)是一个神经元在输入图像上“看到”或响应的区域。更简单地说,它指的是输出层的一个特定神经元对应于输入图像的哪些位置。
更简单的解释
- 直观理解:
- 小感受野:在神经网络的早期层,每个神经元只对图像的一小块区域负责,就像你用放大镜仅仅看到图像中的非常小的一部分,比如一个小方块的边缘。
- 大感受野:在后续层,每个神经元能够"看到"更大范围的图像,就像你逐渐放远视角,能看到图像中更大的部分,比如整个形状或物体的一部分。
具体示例
假设你有一个三层卷积神经网络,每一层都有一个3x3的卷积核:
- 第一层:每个神经元看到3x3的图像区域。此时的感受野是3x3。
- 第二层:接受第一层3x3区域的输出作为输入,相当于每个第二层的神经元看到了5x5的图像区域(因为第一层看3x3区域的每一个会扩大感受野)。
- 第三层:这个神经元的感受野会更大,达到7x7。
通过层叠更多层次,每个神经元能够逐渐"感知"到输入图像中更大的区域,最终高层的神经元可以拥有整个图像的全局视角,这使得它们能够识别整体的复杂特征和物体。
总结
从基础特征到复杂特征的逐层推进源自网络各层对信息逐渐复杂和综合的处理方式。感受野是帮助理解这一过程的一个关键概念,它直观描述了每个神经元在输入图像中负责的具体区域。从小感受野到大感受野,使得神经网络能够逐步从细节到整体进行信息处理和特征提取。
二、TensorFlow实现简单神经网络
安装及环境配置
安装TensorFlow的前置:
- NVIDIA GPU 驱动程序已安装。
- CUDA Toolkit 和 cuDNN 已安装(版本需要与 TensorFlow 版本兼容)。
- 也可以一键安装:
pip install tensorflow[and-cuda]
在~/.bashrc文件末尾添加以下内容:(以cuda-12.1为例)
export PATH=/usr/local/cuda-12.1/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
然后运行source ~/.bashrc来使环境变量生效。
nvcc --version 查看是否安装成功
运行时会出现很多warning,能识别GPU,代码能运行就行。
数据预处理
标准化
使用 tf.keras.layers.Normalization 进行标准化处理
标准化(Standardization)是一种数据预处理技术,它将数据的特征值转换为具有零均值(0)和单位方差(1)的分布。标准化的公式如下:
其中:
- x 是原始数据点。
- μ 是数据点的均值。
- σ 是数据点的标准差。
就像之前做的正态分布的标准化。
print(f"Temperature Max, Min pre normalization: {np.max(X[:,0]):0.2f}, {np.min(X[:,0]):0.2f}")
print(f"Duration Max, Min pre normalization: {np.max(X[:,1]):0.2f}, {np.min(X[:,1]):0.2f}")
norm_l = tf.keras.layers.Normalization(axis=-1)
norm_l.adapt(X) # learns mean, variance
Xn = norm_l(X)
print(f"Temperature Max, Min post normalization: {np.max(Xn[:,0]):0.2f}, {np.min(Xn[:,0]):0.2f}")
print(f"Duration Max, Min post normalization: {np.max(Xn[:,1]):0.2f}, {np.min(Xn[:,1]):0.2f}")
在 TensorFlow 中,tf.keras.layers.Normalization 是一个层(Layer),它用于对输入数据进行标准化处理,使得每个样本的每个特征的均值为0,标准差为1。这种标准化通常在数据预处理阶段使用,有助于提高模型的训练效率和性能。
这里的 norm_l 是一个 Normalization 层的实例,它被创建并设置了 axis 参数。axis=-1 表示标准化将以最后一个轴进行,对这个轴位上的所有数据进行处理,这通常是特征轴。这意味着层将对每个样本的特征进行独立的标准化,而不是在整个批次或整个特征集上进行。
在机器学习和数据处理中,数据通常以多维数组(例如,二维数组或矩阵)的形式存在。在这些数组中,每个维度(或轴)代表了数据的不同方面。在 TensorFlow 和 Keras 中,axis 参数用于指定在哪个维度上进行操作。
在本例中:当你在使用 tf.keras.layers.Normalization 层时,axis=-1 的设置意味着标准化操作将在输入数据的最后一个维度上进行。在大多数情况下,对于输入数据:
- axis=0 指的是样本轴(batch size 或样本数量)。
- axis=1 指的是特征轴(在二维数组中,这通常是列,代表每个样本的特征)。
- axis=-1 等同于 axis=1,因为它指的是最后一个维度,也就是特征轴。
因此,当你设置 axis=-1 时,你告诉标准化层对每个样本的特征进行独立的标准化,而不是在整个数据集的所有特征上进行标准化。这意味着每个样本的特征将被调整为具有0的均值和1的标准差,而不考虑其他样本的特征。这样做的目的是使得模型训练不会受到单个样本特征值范围的影响,从而有助于模型更快地收敛,并可能提高模型的性能。
Dense层
Dense的官方文档:https://tensorflow.google.cn/api_docs/python/tf/keras/layers/Dense
tf.random.set_seed(1234) # applied to achieve consistent results
model = Sequential(
[
tf.keras.Input(shape=(2,)),
Dense(3, activation='sigmoid', name = 'layer1'),
Dense(1, activation='sigmoid', name = 'layer2')
]
)
在TensorFlow中,tf.random.set_seed(seed) 函数用于设置随机数生成器的种子,以确保每次运行代码时都能获得一致的结果。这对于实验的可重复性非常重要,因为不同的随机种子可能会导致模型初始化权重的随机性,从而影响模型训练的结果。
定义了一个Sequential模型,它是一个线性堆叠的层序列。模型包含一个Input层和两个Dense层:
- f.keras.Input 是一个用于定义输入层的类。它用于指定模型输入数据的形状。当你构建一个模型时,Input 层是模型的第一层,它告诉模型期望的输入数据的维度。tf.keras.Input(shape=(2,)) 表示模型的输入数据应该有 2 个特征。这里的 shape 参数是一个 tuple(元组),它定义了输入数据的形状。在这种情况下,形状是 (2,),这意味着每个输入样本都应该是一个包含两个数值的向量。
- 第一个Dense层有3个神经元,使用sigmoid激活函数,命名为layer1。
- 第二个Dense层有1个神经元,也使用sigmoid激活函数,命名为layer2。
每个Dense层都是全连接层,其中神经元的数量对应于该层的输出维度。sigmoid激活函数是一个将输入映射到(0, 1)区间的激活函数,它在二分类问题中经常被用作输出层的激活函数。
注意:W(原输入特征数量,该层w参数的数量)
更直观的:
Convolutional Layer
卷积神经网络
训练
model.compile(
loss = tf.keras.losses.BinaryCrossentropy(),
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01),
)
model.fit(
Xt,Yt,
epochs=10,
)
- model.compile 方法用于配置模型的学习过程。它接收损失函数、优化器和其他可选参数(如评估指标)。
- loss = tf.keras.losses.BinaryCrossentropy():指定了模型的损失函数为二元交叉熵(Binary Crossentropy),这适用于二分类问题。
- optimizer = tf.keras.optimizers.Adam(learning_rate=0.01):指定了模型的优化器为 Adam 优化器,并将学习率设置为 0.01。Adam 是一种自适应学习率优化算法,它结合了 RMSprop 和 Momentum 两种优化算法的优点
- model.fit 方法用于训练模型。
- Xt 和 Yt 分别代表训练数据的特征和标签。Xt 应该是一个形状为 (samples, features) 的数组,而 Yt 应该是一个形状为 (samples,) 的数组或二维数组(如果使用 one-hot 编码)。
- epochs=10:指定了训练过程中整个数据集将被遍历的次数。每个 epoch 都会对所有训练数据进行一次前向和后向传播。
其中,fit已经帮我们完成了反向传播
DEBUG
三、激活函数
除了常常用于分类的SIGMOD函数,还有其他的激活函数可供使用
需要根据你的标签类型需要去01或是正负或是非负,事实证明在隐藏层最常用的就是relu。
Q 为什么需要激活函数:
A 引入非线性:
无论是多层感知机还是卷积神经网络,若没有激活函数,它们都只会做线性变换,层与层之间的叠加仍然是线性的。这时,无论多少层的神经网络,其输出都可以用一个线性变换来表示,无法捕捉复杂的数据模式和特征。引入非线性激活函数后,神经网络可以学习和表示更复杂的非线性关系,从而增强模型的表达能力。
激发多样性:
不同的激活函数使各个神经元对输入信号的响应不同。这样,在网络的不同部分可以捕捉和表示数据的不同特征和模式,从而促进特征学习和分类。
梯度传播:
常见的激活函数,例如ReLU、sigmoid和tanh,具有可微性质,允许通过反向传播算法计算和传递梯度。反向传播是神经网络训练的重要方法,只有激活函数使得梯度存在且易于计算,网络才能通过调整权重逐步逼近目标输出。
数值稳定性:
适当的激活函数可以帮助缓解梯度消失或爆炸问题。例如,ReLU函数的引入极大地改善了深层神经网络的训练效果,因为它在正区间不饱和,能够有效传递梯度。
引入非线性激活函数使神经网络能够更好地学习复杂特征,主要原因有以下几个方面:
复杂函数逼近:
线性函数的组合仍然是线性函数,无法捕捉输入和输出之间的复杂非线性关系。而由于现实世界中的大多数问题(如图像、语音识别等)往往具有高度非线性和复杂性,单纯的线性变换无法有效地建模这些问题。激活函数如ReLU、sigmoid等引入了非线性,使得网络有能力逼近任意复杂的非线性函数。这基于著名的通用逼近定理,该定理指出,具有非线性激活函数的单隐层前馈神经网络在适当的条件下可以逼近任意连续函数。
特征选取能力:
通过非线性激活函数,每一层的神经元在处理输入数据时可以进行更加多样和丰富的特征变换。例如,ReLU激活函数可以强化正值输入、抑制负值输入,从而有助于提取关键特征,而sigmoid函数则可以将输入映射到0到1的范围,从而有助于概率分布的建模。这些非线性变换使得神经网络能够在多层次上提取和组合特征,从而捕捉更高阶的特征关系。
增加模型复杂度:
激活函数通过引入非线性,使得每一层网络输出的特征变换更加复杂和灵活。多层神经网络中,每一层都对前一层的输出进行加权求和并通过激活函数处理。这些处理步骤叠加起来,使得整体网络具备更强的表达能力,能够学习和表征更加复杂和细致的特征。
决策边界:
没有非线性激活函数,神经网络只能形成线性分类器,学习到的决策边界是线性的,无法分类线性不可分的数据。引入非线性激活函数后,神经网络可以学习到复杂的非线性决策边界,从而能够对复杂的、高维数据进行更有效的分类。
RELU
relu保证了激活后的函数值非负
ReLU(Rectified Linear Unit,修正线性单元)是深度学习中常用的一种激活函数。它的数学表达式非常简单:
这意味着当输入 ( x ) 为正数时,ReLU 函数输出 ( x );当输入 ( x ) 为负数时,ReLU 函数输出 0。这个函数的图形是一个斜率为 1 的直线,从 ( x = 0 ) 开始。
ReLU 激活函数的优点包括:
-
计算简单:ReLU 函数的计算非常快速,因为它只涉及一个阈值操作。
-
减少梯度消失问题:在正区间内,ReLU 的梯度是常数 1,这有助于缓解梯度消失问题,特别是在深层网络中。
-
稀疏激活:由于负值被置为 0,ReLU 函数会产生稀疏的输出,这意味着在任何时候只有一部分神经元被激活,这有助于提高模型的稀疏性和计算效率。
-
减少模型复杂度:在某些情况下,ReLU 可以减少模型的复杂度和过拟合的风险。
然而,ReLU 也有一些缺点,例如:
-
死亡ReLU问题:如果输入 ( x ) 持续为负,ReLU 函数的梯度将为 0,这可能导致在训练过程中某些神经元不再更新(即“死亡”)。
-
不饱和:ReLU 函数在正区间内不饱和,这意味着它不会像 sigmoid 或 tanh 函数那样在一定范围内达到稳定值。
尽管如此,ReLU 及其变体(如 Leaky ReLU、Parametric ReLU(PReLU)、Exponential Linear Unit(ELU)等)在许多深度学习应用中仍然是首选的激活函数之一。在 TensorFlow 中,你可以很容易地在神经网络层中使用 ReLU 激活函数:
Softmax
Softmax 函数是一种在多类分类问题中常用的激活函数,特别是在神经网络的输出层。它的作用是将一个向量或一组实数转换为概率分布,使得每个元素的值都在0到1之间,并且所有元素的和为1。这使得Softmax函数非常适合用作分类任务的输出,因为它可以解释为属于某个类别的概率。
Softmax 函数的数学表达式定义如下:
设z 为一个实数向量,σ(z) 为 Softmax 函数,则对于向量z 中的每个元素z_i ,Softmax 函数定义为:
其中,分子是 z_i 的指数,分母是所有 z 中元素指数的总和。这个公式确保了输出值在0到1之间,并且所有输出值的和为1。
Softmax 函数的特点:
-
概率解释:Softmax 函数的输出可以被解释为概率,这对于分类问题非常有用。
-
处理多个类别:Softmax 可以处理任意数量的类别,使得每个类别都有一个相应的概率输出。
-
数值稳定性:在实际计算中,直接计算指数可能会因为数值过大而导致溢出。为了避免这个问题,通常会从每个 ( z_i ) 中减去 ( z ) 中的最大值,即
-
梯度特性:Softmax 函数的梯度相对于其他激活函数(如 Sigmoid)更平滑,这有助于梯度下降算法的稳定性。
Softmax的优化
之前是做了一步中间变化将z转换成了a然后再进行损失函数的计算,这就会导致在z->a这一步的softmax激活函数后,过大或者过小的数会损失精度,所以直接将这一层用线性函数激活然后在最终计算损失函数的时候,确认进行逻辑回归(true)。这样在底层TensorFlow就回自动安排计算顺序使得精度增加。
多分类问题
在机器学习和深度学习中,“Multiple Class”(多类)和"Multiple Label"(多标签)是两种不同的分类任务,它们在目标和处理方式上有所区别:
-
Multiple Class(多类分类):
- 多类分类任务是指每个样本仅属于一个类别的情况。
- 目标是预测样本属于预定义类别中的哪一个。
- 例如,一个图像识别任务,目标是识别图像中的物体是猫、狗还是鸟(假设每个图像中只有一个物体)。
- 在神经网络的输出层,通常使用 Softmax 激活函数来处理多类分类问题,因为 Softmax 可以输出每个类别的概率,并且这些概率的总和为1。
- 多类分类问题通常使用交叉熵损失函数(Cross-Entropy Loss)来训练模型。
-
Multiple Label(多标签分类):
- 多标签分类任务是指每个样本可以同时属于多个类别的情况。
- 目标是预测样本同时属于哪些类别。
- 例如,一个图像识别任务,目标是识别图像中同时存在的多个物体,如猫、狗和汽车(一个图像中可能同时包含多个物体)。
- 在神经网络的输出层,通常使用 Sigmoid 激活函数来处理多标签分类问题,因为 Sigmoid 可以为每个类别输出一个概率,表示样本属于该类别的概率。
- 多标签分类问题可以使用二元交叉熵损失函数(Binary Cross-Entropy Loss)来训练模型,每个类别独立计算损失。
四、高级优化方法
以前的单纯梯度下降,如果学习率太低就回导致进度缓慢,如果学习率太高就会导致震荡
Adam
Adam(Adaptive Moment Estimation)是一种广泛使用的深度学习优化算法,由Diederik P. Kingma和Jimmy Ba在2014年提出。它结合了动量法(Momentum)和RMSProp的思想,通过计算梯度的一阶矩估计和二阶矩估计来调整每个参数的学习率,从而实现更高效的网络训练。Adam算法的关键组成部分之一是使用指数加权移动平均值来估算梯度的动量和第二力矩,即它使用状态变量来存储这些估计值,并对其进行偏差校正,以确保在训练初期时梯度估计不会偏向于0。
Adam算法的优点包括:
- 自适应学习率:Adam通过计算一阶和二阶矩估计来为每个参数自适应地调整学习率。
- 偏差校正:初始阶段梯度估计可能偏低,通过偏差校正可以加速初期的学习速率。
- 适应性强:Adam在很多不同的模型和数据集上都表现出了良好的性能。
然而,Adam算法也存在一些缺点,如训练过程中可能会出现震荡现象,影响收敛速度,以及算法对于初始参数的选择较为敏感。
在实际应用中,Adam算法的参数配置包括学习率(alpha)、一阶矩估计的指数衰减率(beta1)、二阶矩估计的指数衰减率(beta2)和非常小的数(epsilon),以防止在实现中除以零。通常,这些超参数的默认值在很多情况下都表现良好,但某些问题可能需要仔细的超参数调整。
Adam算法在深度学习领域内是十分流行的算法,因为它能很快地实现优良的结果。在原论文中,作者经验性地证明了Adam算法的收敛性符合理论性的分析,并在多个数据集上应用优化算法,证明了其高效性。
在PyTorch中,使用Adam优化器非常简单,首先需要导入torch.optim模块,然后选择Adam优化器。例如:
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单的线性模型
model = nn.Linear(10, 1)
# 定义损失函数
criterion = nn.MSELoss()
# 定义Adam优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)
总的来说,Adam算法是一种高效的优化算法,在深度学习领域具有广泛的应用前景。随着计算资源和算法技术的不断发展,Adam算法在未来有望在更多领域取得突破性成果。