神经网络基础

神经网络基础

深度学习框架

深度学习框架:Tensorflow, Keras, PyTorch, Paddle(百度的,飞桨aistudio可以使用)

Tensorflow 2.0, 2019年发布,相比于1.0更加简洁

Pytorch,Facebook开发,Python + Torch

科学计算库,代替 Numpy,利用 GPUs

动态图设计,可以高效地进行神经网络的构造,学术界用的多

对比维度 TensorFlow Pytorch
开发风格 早期静态图(先编译再执行),2.0后支持动态图(Eager模式)(执行的时候再编译、更快) 原生动态图(即时执行),调试方便
生态系统 工业部署工具完善(TF Serving,Lite等) 学术研究主流(Huggingface等)
API设计 API复杂但功能全,Keras高层封装 Pythoic风格,代码简洁直观
硬件支持 GPU/CPU/移动端优化成熟 GPU/TPU支持良好,移动端支持较弱
使用场景 声场环境、大规模部署 快速试验、学术研究
社区趋势 工业界应用广泛 学术界占比高

神经网络网络图:从输入层传递到隐藏层传递到输出层(预测结果)

深度学习本质上就是神经网络,只是层数较多,强调深度(Googlenet网络为22层,resnet可以达到50/101/152层)

相同点:神经网络不是一个新的概念,一个端到端的黑盒,可解释性较差、自适应完成特征提取

生物神经元:

单个神经细胞要么兴奋要么抑制,接收到的信号加权总和如果超过一个阈值就被激活(兴奋),并向下传递消息;否则就保持抑制。

这个"简单的开关机制"是如何来实现的?

通过激活函数来实现的,它构成了网络学习的根本。

一个神经元就是一个决策小单元。它接收很多上游传来的信号,同时它需要做一个"决定":我收到的这些信息足够重要吗?我需要把这个信息继续往下传递吗?

激活函数就是这个决策的"开关" 。它将所有输入信号的加权总和作为输入,然后输出一个值。

最简单的激活函数(阶跃函数)就像一个纯粹的开关:输入总和超过阈值,就输出1(兴奋=>传递);否则输出0(抑制=>不传递)。

人工神经元

整个神经网络由无数个神经元组成。当这些成千上万的简单决策汇集在一起时=> 整个网络就能做出极其复杂的决策

x是信号,也是输入层的input,到隐藏层经过权重的连接关系,向前传递到输出层。这种权重的关系我们称之为神经网络中的参数。连接边的数量--权重--需要拟合

我们要干的事情也就是模拟前向传播

前向传播的目的就是运用当前的神经网络参数得到结果

可以从这些问题入手:

为什么激活函数是非线性的?曲线是怎样的?=> 赋予神经网络拟合复杂模式的能力,如果使用线性激活函数,多层网络会退化为单层线性模型,无法学习非线性关系。非线性激活函数(如RELU、Sigmoid)通过引入非线性变化,是网络能够堆叠多层并逼近任意复杂函数 => 是深度学习强大表达能力的关键。

如何使用numpy模拟前向传播?(从左到右一层层向前传)

神经网络的权重: 中的(1)代表第1层的权重;下标i代表 下一层的第i个神经元;下标j代表 上一层的第j个神经元。每一层只和前一层相关,都是一个特征提取的结果。b是偏移项,要额外加减多少。

模拟神经网络的前向传播:

Step1,初始化网络 :设置3层神经网络的W和b

Step2,定义激活函数

第一层,第二层的激活函数使用sigmoid ;输出层的激活函数使用 恒等函数,即 g(x)=x

Step3,前向传播

前向传播比较简单,就是向量点乘(即加权求和),然后经过一个激活函数。最终输出预测结构Y'。

激活函数(拟合非线性)

神经网络中的神经元接受上一层神经元的输出值作为输入值,输入层神经元节点会将输入属性值直接传递给下一层 ,在多层神经网络中,上层节点的输出和下层节点的输入之间具有一个函数关系,这个函数称为激活函数

为什么需要激活函数:

如果不用激活函数,就相当于激励函数f(x) = x,此时每一层节点的输入都是上层输出的线性函

数,那么无论神经网络有多少层,输出都是输入的线性组合 => 与没有隐藏层效果相当

引入非线性函数作为激励函数,这样神经网络表达能力会更加强大 => 不再是输入的线性组合,而是几乎可以逼近任意函数

sigmoid激活函数

sigmoid函数:输入为连续实值,输出结果为0和1之间,负无穷的输出结果为0,正无穷的输出结果为1。

在深度神经网络中梯度反向传递时导致梯度爆炸和梯度消失,其中梯度爆炸发生的概率很小,而梯度消失发生的概率较大。

Sigmoid 的 output 不是0均值,这样会导致后一层的神经元将得到上一层输出的非0均值的信号

当 x>0, 对w求局部梯度都为正 => 反向传播中,w的更新,要么都往正方向更新,要么都往负方向更新,使得收敛缓慢。

常用于在二分类问题中,将输出结果映射到[0,1]。经常与二分类交叉熵损失一起使用

Sigmoid函数对中央区的信号增益较大,对两侧区的信号增益小,在信号的特征空间映射上,有很好的效果,从神经科学上来看,中央区属于神经元的兴奋态,两侧区属于神经元的抑制态,所以神经网络学习上,可以将重点特征推向中央区,将非重点特征推向两侧区。

梯度消失和梯度爆炸(本质都是链式法则的乘法效应)

反向传播干嘛?

反向传播就是从输出层往输入层倒着算,每个参数对最终预测误差的影响力(这个影响力就是梯度)。拿到梯度后,模型会按梯度大小和方向微调参数 ------ 梯度正常,参数更新就合理,模型就能慢慢学;梯度出问题,参数就更不了、乱更新,模型就学废了。

梯度下降需要每一层都有明确的误差才能更新参数,所以接下来的重点是如何将输出层的误差反向传播给隐藏层。

梯度消失:梯度越传越小最后几乎为0

从输出层往输入层传梯度时,梯度值一层比一层小,传到前面的隐藏层 / 输入层时,梯度几乎是 0。👉 结果:靠近输入层的参数几乎不更新,相当于模型只学了后面几层的浅层知识,深层网络白搭了。

结合 sigmoid 激活函数来看
  1. sigmoid 激活函数的导数有个 "硬上限":它的导数最大值只有0.25,所有点的导数都在 0~0.25 之间。
  2. 反向传播时,梯度要逐层乘上激活函数的导数 + 上一层的权重 w(链式法则)。比如初始化的权重 w 是 0~1 之间的随机数,那每传一层,梯度就会乘一个 "小于 0.25 的数 × 小于 1 的数",结果还是远小于 0.25。
  3. 深层网络(比如几十层)的话,梯度就会被连续乘几十个小于 1 的数,最后直接趋近于 0。

模型 "学不到深层特征",比如想让网络学图片的底层边缘→中层纹理→高层物体,结果底层边缘的参数更不了,模型只能瞎猜。

如果初始化神经网络的权值为 [0,1] 之间的随机值,由反向传播算法可知,梯度从后向前传播时,每传递一层梯度值都会减小为原来的0.25倍。如果神经网络层数多,那么梯度在多层传播后将变得很小(接近于0),即梯度消失 。当网络权值初始化为 (1,+∞) 区间内的值,则会出现梯度爆炸

假设有三个隐藏层,每层的神经元个数都是1,对应的非线性激活函数为

我们要更新b1,就需要求出损失函数对于b1的导数,如果初始化的神经网络权重|w|小于1,

当层数增多时,小于1的值不断相乘,最后就导致梯度消失的情况出现。同理,当权重|w|过大时,导致 ,最后大于1的值不断相乘,就会产生梯度爆炸。

当梯度小于1时,预测值与真实值之间的误差每传播一层会衰减一次,如果在深层模型中使用sigmoid作为激活函数,这种现象尤为明显,将导致模型收敛停滞不前。

梯度消失发生时,接近于输出层的隐藏层由于其梯度相对正常,所以权值更新时也就相对正常,但是当越靠近输入层时,由于梯度消失,会导致靠近输入层的隐藏层权值更新缓慢或者停滞 => 这样就导致在训练时,只等价于后面几层的浅层网络的学习。

梯度爆炸:梯度越传越大,最后变成超大的数

从输出层往输入层传梯度时,梯度值一层比一层大,传到前面的层时,梯度变成特别大的数(几百、几千甚至无穷)。👉 结果:参数被大幅乱更新,模型的预测误差会突然飙升,直接发散,训练彻底不收敛。

和梯度消失是 "反过来的",核心还是反向传播的链式乘法

  1. 如果初始化的权重 w 不是 0~1,而是大于 1 的数(比如 2、3),再结合激活函数的导数(比如 sigmoid 的 0.25),就会出现 "导数 × 权重> 1" 的情况(比如 0.25×5=1.25)。
  2. 深层网络中,梯度会连续乘几十个大于 1 的数,最后直接变成超大的数,也就是梯度爆炸。

模型参数变得极不稳定,预测结果忽大忽小,损失函数曲线直接 "飞上天",训练到一半就彻底崩了。

tanh激活函数:

y=tanh(x)是奇函数,即图像过原点并且穿越第一、第三象限的严格单调递增曲线。

定义域:R 值域:(-1, 1) 也称为双曲正切函数,函数曲线与Sigmoid函数相似, 可以通过缩放平移相互变换。


相比于sigmoid函数,tanh的均值是0,sigmoid函数均值不为0就意味着自带了一个bias,在计算时是额外的负担,这会使得收敛变得更慢

在原点附近更接近y=x,在几何变换运算中具有优势,比如在激活值较低时(也可以通过变换把结果映射到[-1,1]之间,tanh在 [-1, 1]的范围内和y=x 非常接近),可以利用indentity function的某些性质,直接进行矩阵运算

tanh相比于Sigmoid函数更容易训练,具有优越性。用于归一化回归问题,其输出在[-1,1]范围内。通常与L2损失一起使用。

Tanh函数是零中心化的,而sigmoid函数的输出恒大于0。非零中心化的输出会使得其后一层的神经元的输入发生偏置偏移(bias shift),并进一步使得梯度下降的收敛速度变慢。

激活函数relu


单侧抑制,它将所有负数输入都变成0,而正数则保持不变。

• relu的梯度:对于被激活的神经元(输入为正数),其梯度恒定为1。

在网络学习(反向传播)时,这意味着误差信号可以几乎无衰减地向后传递,不会像Sigmoid函数那样因逐层相乘而变得越来越小(梯度消失)。=> 信号既然能有效传达,权重就能被持续更新,所以模型能学得又快又稳。

总结:

激活函数的特点是非线性,而数据的分布绝大多数是非线性的 => 可以强化网络的学习能力

• 不同的激活函数特点不同,应用也不同

sigmoid和tanh函数输出值在(0,1)和(-1,1)之间 => 适合处理概率值、会产生梯度消失 => 不适合深层网络训练

relu的有效导数是常数1,解决了深层网络中出现的梯度消失问题 => 更适合深层网络训练。

怎么去训练神经网络呢?

要让损失函数越小越好,刚开始误差很大,像一个二次函数,沿着切线走越来越接近min,后面变缓之后沿切线走依旧保持斜率的变化靠近min,所以梯度常常按照导数的方式。学习率就像是步长,走多了可能就到另一边去了,跨远了;走少了可能很久都接近不了底。可以离得远的时候步子大一点,离得近的时候步子小一点(动态学习率)。

SFT监督学习(人做标注)

整个流程:

1、定义网络结构(指定输入层、输出层、输出层的大小)

2、初始化模型参数

3、循环操作(多少次)

  • 执行前向传播
  • 计算损失函数
  • 执行后向传播
  • 权值更新

损失函数

用来衡量模型预测值与真实值不一致的程度,是一个非负实数函数

如果我们想要衡量神经网络对于两个类别的分类能力,可以使用二分类交叉熵损失函数 binary crossentropy。

有常见的两种衡量公式:

利用损失函数去做反向传播。

权重矩阵在反向传播的过程中同样扮演着传递的作用。前向传播:传递输入信号;反向传播:传递输出的误差

输出层误差在转置权重矩阵的帮助下,传递到了隐藏层,用来更新与隐藏层相连的权重矩阵。

优化方法

SGD,随机性下降(看切线指导方向)算法收敛速度快,但容易收敛到局部最优。SGD的缺点是更新方向依赖于当前batch计算出的梯度,因而很不稳定

Momentum,借用了物理中的动量概念,更新的时候在一定程度上保留之前更新的方向,同时利用当前batch的梯度微调最终的更新方向,即模拟了运动惯性。

Adagrad,自适应梯度算法,能够在训练中自动的对learning rate进行调整,对于出现频率较低参数采用较大的α更新,对于出现频率较高的参数采用较小的α更新

RMSprop,均方根传播,Adagrad会累加之前所有的梯度平方,而RMSprop仅仅是计算对应的平均值,可以缓解Adagrad算法学习率下降较快的问题

Adam, 结合了 AdaGrad 和 RMSProp 算法最优的性能,它还是能提供解决稀疏梯度和噪声问题的优化方法,在深度学习中使用较多

编一个神经网络

python 复制代码
# 使用numpy实现一个神经网络
import numpy as np
import matplotlib.pyplot as plt

# 设置中文字体,解决中文显示乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']  # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# n为样本大小,d_in为输入维度,h为隐藏层维度,d_out为输出维度
n, d_in, h, d_out = 64, 1000, 100, 10

# 随机生成输入数据x和目标输出y
x = np.random.randn(n, d_in)      # 输入数据,形状为(64, 1000)
y = np.random.randn(n, d_out)     # 目标输出,形状为(64, 10)

# 随机初始化权重参数
# 输入层到隐藏层的权重(1000,100)
w1 = np.random.randn(d_in, h)
# 隐藏层到输出层的权重(100,10)
w2 = np.random.randn(h, d_out)
# 设置学习率
learning_rate = 1e-6

# 用于记录每次迭代的loss值
loss_history = []

# 训练500次
for t in range(500):
    # 前向传播
    temp = x.dot(w1)                  # 输入层到隐藏层的线性变换
    temp_relu = np.maximum(temp, 0)   # ReLU激活函数,隐藏层输出
    y_pred = temp_relu.dot(w2)        # 隐藏层到输出层的线性变换,得到预测值

    # 计算损失函数(均方误差和)
    loss = np.square(y_pred - y).sum()
    loss_history.append(loss)  # 记录loss值
    print(t, loss)

    # 反向传播,计算梯度
    grad_y_pred = 2.0 * (y_pred - y)              # 损失对预测输出的梯度
    #print('grad_y_pred=', grad_y_pred.shape) #(64, 10)
    grad_w2 = temp_relu.T.dot(grad_y_pred)        # 损失对w2的梯度
    grad_temp_relu = grad_y_pred.dot(w2.T)        # 损失对隐藏层输出的梯度
    grad_temp = grad_temp_relu.copy()             # 复制一份用于ReLU处理
    grad_temp[temp<0] = 0                         # ReLU小于0的部分梯度置零
    grad_w1 = x.T.dot(grad_temp)                  # 损失对w1的梯度

    # 更新权重参数
    w1 = w1 - learning_rate * grad_w1
    w2 = w2 - learning_rate * grad_w2

# 绘制Loss曲线
plt.figure(figsize=(10, 6))
plt.plot(loss_history, 'b-', linewidth=2)
plt.title('训练过程中的Loss变化曲线', fontsize=14)
plt.xlabel('迭代次数', fontsize=12)
plt.ylabel('Loss值', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()

# 输出最终训练得到的权重参数
print(w1, w2)
# print(w1) 
# print(w2) 

这是一个两层神经网络:输入层 (1000维) --隐藏层 (100维) + ReLU激活 -- 输出层 (10维)。

训练流程:

  1. 前向传播:算预测值 y_pred
  2. 算 Loss:看看预测得有多差
  3. 反向传播 :算每个权重的梯度(就是 "该往哪个方向改、改多少")
  4. 更新权重:沿着梯度方向微调 w1、w2
  5. 循环 500 次 → Loss 越来越小

一、前向传播(算预测值)

python 复制代码
temp = x.dot(w1)                  # 输入 × 权重w1
temp_relu = np.maximum(temp, 0)   # ReLU激活(负数变0)
y_pred = temp_relu.dot(w2)        # 隐藏层 × 权重w2 → 输出
  • 输入数据 x 经过 w1 加权 → 得到隐藏层值
  • 用 ReLU 把负数变成 0(增加非线性)
  • 再经过 w2 加权 → 得到最终预测 y_pred
前向参数 解读 形状(对应代码) 核心作用
temp 隐藏层「激活前值」 (64,100)(64 个样本,每个样本 100 个隐藏层神经元) ReLU 激活的 "半成品",反向传播要判断它的正负来截断梯度
temp_relu 隐藏层「激活后值」 (64,100) 和 temp 一样 前向传给 w2 算预测值,反向作为算 w2 梯度的 "原材料"
y_pred 网络「预测值」 (64,10)(64 个样本,每个样本 10 个输出) 和真实值 y 比,算误差的核心

二、计算loss(判断预测准不准)

python 复制代码
loss = np.square(y_pred - y).sum()

均方误差和(预测值-真实值)² 全部加起来 Loss 越大 → 错得越离谱

三、反向传播(手动算梯度)

梯度 = 这个权重对最终 Loss 有多大影响

梯度告诉你:权重往 + 方向调,Loss 会变大还是变小

1、对输出求导

python 复制代码
grad_y_pred = 2.0 * (y_pred - y)

Loss = (y_pred - y)² 对 y_pred 求导 → 2(y_pred - y) 这是误差信号,从输出层开始往回传。

2、算w2的梯度

python 复制代码
grad_w2 = temp_relu.T.dot(grad_y_pred)

w2 是隐藏层→输出层的权重。梯度 = 隐藏层输出 × 输出误差

grad_w2针对w2的梯度,直接用来更新w2

3、把误差传回隐藏层

python 复制代码
grad_temp_relu = grad_y_pred.dot(w2.T)

误差要继续往前传,必须乘 w2 的转置 。把输出层的误差(grad_y_pred)传回隐藏层,得到「隐藏层每个神经元的误差」(理解为:输出层的误差,是隐藏层哪些神经元导致的)

4、处理relu激活

python 复制代码
grad_temp = grad_temp_relu.copy()
grad_temp[temp < 0] = 0

ReLU 函数:

  • 正数 → 梯度 = 1
  • 负数 → 梯度 = 0

所以小于 0 的位置,误差直接清零!这也是relu能缓解梯度消失的原因。

对 grad_temp_relu 做ReLU 反向处理后的最终隐藏层误差(因为 ReLU 的规则是:激活前 temp<0 的神经元,激活后是 0,对输出没贡献,所以这些神经元的误差要清零)

5、算w1的梯度

python 复制代码
grad_w1 = x.T.dot(grad_temp)

w1 是输入→隐藏层 的权重。梯度 = 输入数据 × 传回的误差。w1 中每个权重该怎么改的具体指令,和 grad_w2 逻辑完全一样,只是针对 w1

四、优化(用梯度更新权重)

python 复制代码
w1 = w1 - learning_rate * grad_w1
w2 = w2 - learning_rate * grad_w2
  • 梯度是正 → 权重 减小一点,Loss 会下降
  • 梯度是负 → 权重 增大一点,Loss 会下降

新权重 = 旧权重 - 学习率 × 梯度

总结:

前馈神经网络的训练过程可以分为以下三步

• 前向计算每一层的状态和激活值,直到最后一层

• 反向计算每一层的参数的偏导数(方向)

• 更新参数

TensorFlow

计算图(Graph)

定义:TensorFlow的计算图是一个由节点(操作)和边(张量)组成的数据流图,描述计算的依赖关系。

优点:

• 高效执行:图优化(如算子融合)可提升性能。

• 跨平台支持:可导出到移动端、嵌入式设备等。

计算图可加速计算,图模式下运算在C++层执行,避免Python循环的慢速交互。

能并行化优化:自动调度无依赖的节点并行计算(如GPU/多核CPU)。

什么是张量?

张量(Tensor) 是 TensorFlow 中的核心数据结构,可以理解为多维数组。

• 标量(0维):单个数值(如 3.0)。

• 向量(1维):一维数组(如 [1, 2, 3])。

• 矩阵(2维):二维表格(如 [[1, 2], [3, 4]])。

• 高维张量:如 RGB 图片(3维:[高度, 宽度, 通道])。

张量存储数据,并在计算图中流动,支持自动求导和 GPU 加速。

图与会话的现代应用(TensorFlow 2.x)

1.x多是静态,写死计算不灵活。

在 TensorFlow 2.x 中,传统的 计算图(Graph)+ 会话(Session) 模式被大幅简化,但仍保留核心优化能力,主要通过 tf.function 和 即时执行(Eager Execution) 结合使用。

1)tf.function:自动构建计算图
作用:将普通 Python 函数转换为 TensorFlow 计算图,提升执行效率(如减少 Python 调用开销)。

优势:

• 加速训练/推理:适合循环、矩阵运算等密集计算。

• 跨平台部署:可导出为 SavedModel 供 TensorFlow Serving 使用。
@tf.function # 装饰器,自动编译为计算图
def add_and_multiply(x, y):
return x + y, x * y
result = add_and_multiply(2, 3) # 首次调用时构建图,后续高效执行
2)即时执行(Eager Execution)

Tensorflow2.0默认模式,一种交互式编程模式,让代码像普通 Python 程序一样逐行运行(类似NumPy),
无需先构建计算图,便于调试。

与图的协作:

• 调试时:用即时执行快速验证逻辑。

• 部署时:用 @tf.function 转换为图模式提升性能。

TF 1.x:需显式构建图和会话,适合高性能场景。

TF 2.x:默认即时执行,tf.function实现图模式加速。
a = tf.constant(2)
b = tf.constant(3)
print(a + b) # 直接输出 5,无需 Session

分布式训练

数据并行:将训练数据拆分到多个设备(如GPU/CPU),每个设备计算梯度后同步更新模型(主流方案)。

实现工具:tf.distribute.MirroredStrategy(单机多卡)、MultiWorkerMirroredStrategy(多机多卡)。

参数服务器(Parameter Server):部分设备存储模型参数,其余设备计算梯度(适合超大模型)。

纯粹课堂笔记,有任何侵权,联系我,马上删。

相关推荐
cxr8282 小时前
龙虾长程任务测试 —— 撰写零人公司自动化运营实践研究报告
运维·人工智能·自动化·openclaw
key_3_feng2 小时前
PolarDB for AI RAG系统建设方案
人工智能·polardb
mit6.8242 小时前
生成式推荐GR4AD
人工智能
网络工程小王2 小时前
【提示词工程和思维链的讲解】学习笔记
人工智能·笔记·学习
我的Doraemon2 小时前
大模型是怎么被训练出来的?
人工智能·深度学习·机器学习
SomeB1oody2 小时前
【Python深度学习】1.1. 多层感知器MLP(人工神经网络)介绍
开发语言·人工智能·python·深度学习·机器学习
枕石 入梦2 小时前
【源码解析】OpenClaw 多渠道 AI 助手网关的架构设计与核心原理
人工智能·openclaw·小龙虾
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月6日
大数据·人工智能·python·信息可视化·语言模型·自然语言处理·ai编程
逻极2 小时前
Windows平台Ollama AMD GPU编译全攻略:基于ROCm 6.2的实战指南(附构建脚本)
人工智能·windows·gpu·amd·ollama