目录
5.1 批量归一化(Batch Normalization):
1 神经元模型与激活函数
在机器学习中,神经网络一般指的是“神经网络学习”,是机器学习与神经网络两个学科的交叉部分。所谓神经网络,目前用得最广泛的一个定义是“神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所做出的交互反应”。
神经网络中最基本的单元是神经元模型(neuron)。在生物神经网络的原始机制中,每个神经元通常都有多个树突(dendrite),一个轴突(axon)和一个细胞体(cell body),树突短而多分支,轴突长而只有一个;在功能上,树突用于传入其它神经元传递的神经冲动,而轴突用于将神经冲动传出到其它神经元,当树突或细胞体传入的神经冲动使得神经元兴奋时,该神经元就会通过轴突向其它神经元传递兴奋。
在深度学习领域,前馈神经网络是最基础也最重要的模型之一。它的基本计算单元是神经元,这种结构的设计灵感来自于生物神经元的工作机制。

每个人工神经元都能接收多个输入信号,这些信号经过加权求和后,再通过一个非线性激活函数产生输出。这个过程可以用数学表达式表示为 f(∑wᵢxᵢ - ),其中wᵢ是权重参数,xᵢ是输入信号,
是偏置项,f是激活函数。
神经元是神经网络的基础计算单元,其设计灵感来自生物神经元的工作机制。每个人工神经元包含以下关键组件:
- 输入向量(x₁, x₂, ..., xₙ):表示从其他神经元或外部输入接收到的信号
- 权重向量(w₁, w₂, ..., wₙ):每个输入信号对应的权重
- 偏置项(b):调整神经元激活阈值
- 加权求和函数:z = w₁x₁ + w₂x₂ + ... + wₙxₙ + b
- 激活函数:f(z) = output
激活函数在神经网络中扮演着至关重要的角色,它们为网络引入了非线性变换能力。最常用的ReLU函数形式简单,仅保留正值而将负值置零,这种特性不仅加快了训练速度,还有效缓解了深层网络中的梯度消失问题。Sigmoid函数则将输入压缩到(0,1)区间,主要用于二分类问题的输出层。而Tanh函数与Sigmoid相似,但其输出范围是(-1,1),且具有零中心化的特性,这使得数据在传播过程中更稳定。激活函数的选择至关重要,因为它们引入了非线性变换,使网络能够学习复杂的模式。让我们详细分析几个常用的激活函数:
- ReLU (Rectified Linear Unit):
- f(x) = max(0,x)
- 优点:
- 计算简单,训练快速
- 缓解梯度消失问题
- 产生稀疏激活
- 缺点:
- 死亡ReLU问题(神经元永久停止激活)
- 输出不是零中心的
- Sigmoid:
- f(x) = 1/(1+e^(-x))
- 优点:
- 输出范围在[0,1]之间,适合二分类
- 平滑可导
- 缺点:
- 存在梯度饱和问题
- 输出不是零中心的
- 计算复杂度较高
- Tanh:
- f(x) = (e^x - e^(-x))/(e^x + e^(-x))
- 优点:
- 输出范围在[-1,1]之间
- 是零中心的
- 缺点:
- 同样存在梯度饱和问题
- 计算复杂度高
前馈神经网络的基本单元是神经元,也称为感知器。每个神经元接收多个输入信号,并产生一个输出信号。从数学角度看,神经元执行以下操作:
import numpy as np
class Neuron:
def __init__(self, weights, bias):
self.weights = weights # 权重向量
self.bias = bias # 偏置项
def forward(self, inputs):
# 线性组合
z = np.dot(self.weights, inputs) + self.bias
# 通过激活函数
return self.activation(z)
def activation(self, z):
# ReLU激活函数
return max(0, z)
激活函数是神经网络中引入非线性的关键。常用的激活函数包括:
- ReLU: f(x) = max(0,x)
- Sigmoid: f(x) = 1/(1+e^(-x))
- Tanh: f(x) = (e^x - e^(-x))/(e^x + e^(-x))
2 网络架构与前向传播
在网络结构上,前馈神经网络采用分层设计,包括输入层、若干隐藏层和输出层。每层神经元只与下一层神经元相连,形成有向无环的信息传播路径。这种结构使得信息能够从输入层逐层传递到输出层,每一层都对输入特征进行转换和抽象,从而逐步提取更高层次的特征表示。输入数据在网络中的传播过程被称为前向传播,每一层的计算都可以表示为矩阵运算的形式,这大大提高了计算效率。前馈神经网络的结构具有以下特点:
- 层次结构:
- 输入层:接收原始数据
- 隐藏层:进行特征转换和抽象
- 输出层:产生最终预测
- 全连接特性:
- 每层的每个神经元都与下一层的所有神经元相连
- 连接强度由权重参数决定
- 信息只向前传播,没有循环或跨层连接
前馈神经网络由多层神经元组成,信息从输入层经过隐藏层最后到达输出层,没有循环连接。每层神经元只与下一层神经元相连。
class FeedForwardNet:
def __init__(self, layer_sizes):
self.layers = []
for i in range(len(layer_sizes)-1):
# 初始化每层的权重和偏置
weights = np.random.randn(layer_sizes[i+1], layer_sizes[i])
bias = np.random.randn(layer_sizes[i+1], 1)
self.layers.append((weights, bias))
def forward(self, x):
activations = [x]
for weights, bias in self.layers:
# 前向传播过程
z = np.dot(weights, activations[-1]) + bias
activation = np.maximum(0, z) # ReLU
activations.append(activation)
return activations
网络的训练过程本质上是一个优化问题,目标是最小化预测值与真实值之间的差异,这个差异通过损失函数来度量。对于回归问题,常用均方误差(MSE)作为损失函数;而对于分类问题,交叉熵损失则更为合适,因为它能更好地处理概率分布。为了最小化损失函数,我们使用梯度下降算法逐步调整网络参数。实践中最常用的是小批量梯度下降,它在计算效率和优化效果之间取得了很好的平衡。
3. 损失函数与梯度下降
神经网络通过最小化损失函数来学习。常用的损失函数包括:
- 均方误差(MSE): 用于回归问题
- 交叉熵损失: 用于分类问题
优化过程使用梯度下降算法,通过计算损失函数对各参数的梯度来更新参数:
def mse_loss(predictions, targets):
return np.mean((predictions - targets) ** 2)
def gradient_descent(network, learning_rate, x_batch, y_batch):
# 前向传播
activations = network.forward(x_batch)
# 计算损失
loss = mse_loss(activations[-1], y_batch)
# 反向传播计算梯度
gradients = network.backward(activations, y_batch)
# 更新参数
for layer, gradients in zip(network.layers, gradients):
weights, bias = layer
weight_grads, bias_grads = gradients
weights -= learning_rate * weight_grads
bias -= learning_rate * bias_grads
4 反向传播算法
反向传播算法是训练过程中计算梯度的核心方法。当网络完成一次前向传播后,我们可以计算出预测值与真实值之间的误差。反向传播算法巧妙地运用链式法则,将这个误差从输出层逐层反向传递,从而计算出每个参数对最终损失的贡献。这个过程首先计算输出层的误差,然后逐层反向传播到前面的层,同时计算每层参数的梯度。这个算法的效率远超直接计算梯度的方法,使得深层神经网络的训练成为可能。反向传播是计算神经网络梯度的高效算法,它基于链式法则。算法分为以下步骤:
- 前向传播计算每层的激活值
- 计算输出层的误差
- 误差反向传播到各层
- 计算每层参数的梯度
def backward(self, activations, targets):
layer_gradients = []
# 输出层误差
error = activations[-1] - targets
for i in reversed(range(len(self.layers))):
weights, bias = self.layers[i]
# 计算权重梯度
weight_grads = np.dot(error, activations[i].T)
# 计算偏置梯度
bias_grads = np.sum(error, axis=1, keepdims=True)
# 计算下一层的误差
if i > 0: # 非第一层
error = np.dot(weights.T, error)
error *= (activations[i] > 0) # ReLU导数
layer_gradients.append((weight_grads, bias_grads))
return layer_gradients[::-1]
5 优化技巧与正则化
为了提升网络的性能和泛化能力,研究者们发展出了许多优化技巧。批量归一化通过标准化每层的输入分布,加速了训练收敛速度,同时允许使用更大的学习率。Dropout技术在训练时随机"丢弃"一部分神经元,这种方法可以看作是训练多个子网络并进行集成,有效防止了过拟合。合适的权重初始化也很重要,He初始化和Xavier初始化都考虑到了网络的结构特点,使得信号在传播过程中保持适当的尺度。
在实际应用中,我们还需要注意一些实现细节。比如使用梯度裁剪来防止梯度爆炸,采用早停策略避免过拟合,以及选择合适的优化器如Adam来自适应地调整学习率。这些技巧的组合使用能够显著提升网络的训练效果。同时,为了评估模型的泛化能力,我们通常将数据集划分为训练集、验证集和测试集,并在验证集上监控模型性能来调整超参数。
为了提高网络性能,有多种优化技巧:
5.1 批量归一化(Batch Normalization):
- 原理:标准化每层的输入分布
- 计算步骤:
- 计算批量均值:μᵦ = (1/m)∑xᵢ
- 计算批量方差:σ²ᵦ = (1/m)∑(xᵢ - μᵦ)²
- 标准化:x̂ᵢ = (xᵢ - μᵦ)/√(σ²ᵦ + ε)
- 缩放和平移:yᵢ = γx̂ᵢ + β
- 优点:
- 加速训练收敛
- 允许使用更大的学习率
- 减少对初始化的依赖
- 具有轻微的正则化效果
def batch_norm(x, gamma, beta):
mean = np.mean(x, axis=0)
var = np.var(x, axis=0)
x_norm = (x - mean) / np.sqrt(var + 1e-8)
return gamma * x_norm + beta
5.2 Dropout正则化:
- 原理:训练时随机"丢弃"一部分神经元
- 实现步骤:
- 生成随机掩码:mask = np.random.binomial(1, p, size)
- 屏蔽部分神经元:h = mask * h
- 缩放剩余值:h = h/p
- 作用:
- 防止过拟合
- 实现模型集成
- 提高泛化能力
def dropout(x, keep_prob):
mask = np.random.binomial(1, keep_prob, size=x.shape)
return (x * mask) / keep_prob
5.3 学习率调度:
def learning_rate_decay(initial_lr, epoch):
return initial_lr / (1 + 0.01 * epoch)
6 实现考虑
在实际应用中,还需要考虑:
- 权重初始化: 使用He初始化或Xavier初始化
- 小批量处理: 平衡计算效率和梯度估计质量
- 超参数调优: 学习率、批量大小、层数等
- 模型评估: 训练集、验证集、测试集的划分
- 早停: 防止过拟合
总的来说,前馈神经网络是深度学习的基础模型,理解它的原理和实现对于深入学习更复杂的模型架构至关重要。虽然现代深度学习框架(如PyTorch、TensorFlow)已经为我们处理了大部分实现细节。
内容不全等,请各位理解支持!!