深度学习基础:从损失函数到简易神经网络代码实操
损失函数是机器学习/深度学习中衡量模型预测与真实值差异的核心指标,它指导模型通过优化算法调整参数,是训练过程的“指南针”。
1.损失函数
一、损失函数的核心作用
- 量化模型预测值 ^y与真实值 y 的不一致程度
- 指导模型参数更新(通过梯度下降等算法最小化损失)
- 评估模型性能,损失越小通常代表模型泛化能力越强。
二、常见损失函数分类
损失函数主要分为回归类(处理连续值预测)和分类类(处理离散类别预测)两类。
1. 回归类损失函数
(1)均方误差(MSE)
- 适用场景:回归任务(如房价预测、温度预测)。
- 公式: 其中: y_i 是第 i 个样本的真实值, ^y_i 是预测值, n 是样本数量。
- 优点:计算简单,对小误差敏感;
- 缺点:易受异常值影响(大误差会被平方放大),单位是原数据单位的平方(不直观)。
(2)均方根误差(RMSE)
- 适用场景:回归任务(是MSE的改进,让单位更直观)。
- 公式:
- 优点:单位与原数据一致,更易解释;
- 缺点:同样易受异常值影响。
2. 分类类损失函数
交叉熵损失
- 适用场景:分类任务(如图像分类、文本分类,尤其适用于多分类)。
- 核心逻辑:通过衡量“模型预测的概率分布”与“真实标签的概率分布”之间的差异,引导模型输出更接近真实标签的概率。
- 公式(假设有 N 个样本、 K 个类别): 其中:
- q(i,j) :第 i 个样本真实类别为第 j 类的概率(通常是独热编码,即真实类别为1,其他为0);
- p(i,j) :模型预测第 i 个样本属于第 j 类的概率。
- 优点:梯度计算简单,训练初期梯度大、收敛快;适配类别不平衡场景;
- 缺点:仅适用于分类任务,依赖模型输出概率分布。
三、损失函数的训练趋势
训练过程中,损失通常会快速下降→逐渐稳定→不再变化(如图2-13所示):
- 初期:模型参数随机初始化,损失较大,优化算法快速调整参数,损失骤降;
- 中期:损失下降速度放缓,逐渐接近最优值;
- 后期:损失趋于稳定(达到“收敛”),继续训练无法优化模型(甚至可能过拟合)。
四、损失函数的选择建议
| 任务类型 | 推荐损失函数 |
|---|---|
| 回归任务 | MSE/RMSE |
| 分类任务 | 交叉熵损失 |
2.模型评估
模型评估的核心是诊断网络状态(偏差/方差),以指导模型优化。
一、偏差与方差
- 偏差:衡量模型的训练集误差(即模型能达到的最优误差),代表模型对数据中隐含规律的拟合能力。
- 方差:衡量模型在新数据上的表现差异,代表模型的泛化能力。
模型常见状态分类:
状态 表现特征 原因&解决方案 高偏差+低方差 训练集误差高,泛化能力差 模型复杂度不足 → 增加网络规模/复杂度 高偏差+高方差 训练集误差高,泛化能力差 数据量不足/特征选择不当 → 扩充数据/优化特征 低偏差+高方差 训练集误差低,泛化能力差 过拟合 → 正则化/简化模型 低偏差+低方差 训练集误差低,泛化能力好 理想状态 → 保持模型配置
二、 过拟合与欠拟合
1、过拟合
- 表现:训练集性能好,但新数据表现差。
- 原因:模型过于复杂、训练数据量不足、特征冗余等。
解决方案:
- 正则化(L1/L2、随机失活);
- 简化模型结构;
- 增加训练数据/特征选择;
- 集成学习(如随机森林)。
2、欠拟合
- 表现:训练集和新数据的性能均较差。
- 原因:模型复杂度不足、特征选择不充分、训练迭代不足等。
解决方案:
- 增加模型复杂度(如增加参数/网络层数);
- 优化特征工程(补充关键特征);
- 调整超参数(如增大学习率、增加迭代次数)。
3. 正则化
正则化是解决高方差(过拟合)的核心方案,核心思想是限制网络的有效规模,降低拟合能力。
常用正则化方法:
一、L1、L2正则化
- 原理:在损失函数中添加参数的惩罚项,限制参数规模。
- L1正则化:添加参数的绝对值之和,促使参数稀疏化(实现特征选择)。
- L2正则化:添加参数的平方和,促使参数分散化,也称为“权重衰减”。
二、随机失活(Dropout)
- 训练时随机选择部分神经元置零,不参与本次优化迭代。
- 作用:
- 降低神经元间的耦合依赖,迫使每个神经元独立提取特征;
- 相当于训练多个“子网络”,测试时集成这些子网络的结果,提升泛化能力。
三、批归一化
- 在每个训练批次(batch)中,对网络每一层的输入做归一化处理,保持输入分布稳定。
- 优势:加速训练、增强网络对架构/超参数的鲁棒性、缓解梯度消失问题。
四、早停技术(Early Stopping)
- 基于验证集表现停止训练:当验证集误差不再下降(或开始上升)时,提前终止训练,避免模型过拟合。
五、数据扩充(Data Augmentation)
- 从现有数据生成“伪数据”(如图像的翻转、旋转、色彩变换等),扩大训练样本量,减少过拟合风险。
4.SoftMax函数
Softmax是多分类任务专用的激活函数,用于将模型输出转换为“类别概率分布”。
一、核心作用
将长度为 K 的任意实向量,压缩为:
- 每个元素值∈ (0, 1)
- 所有元素之和=1的实向量(即“概率向量”),从而表示样本属于每个类别的概率。
二、计算逻辑
对于输入向量 某一元素 z_i 的Softmax值为:
三、应用场景
- 多分类任务的输出层(如分类“猫/狗/鸟”等3类时,输出3个概率值);
- 配合交叉熵损失函数,实现多分类任务的损失计算。
5.神经网络
神经网络是模拟人脑神经元连接模式的机器学习模型,核心是通过多层“神经元”的计算,实现对复杂数据的拟合、分类或预测。
一、基本结构(以简单网络为例)
一个基础神经网络通常包含3类层:
- 输入层:接收原始数据(如图片的像素、文本的特征向量),神经元数量=数据的特征维度;
- 隐藏层:对输入数据做“特征提取与变换”,是网络的核心计算层(可包含1~N层,层数/神经元数决定网络复杂度);
- 输出层:输出模型结果(如分类任务的类别概率、回归任务的预测值),神经元数量=任务目标维度(如二分类用1个神经元,5分类用5个神经元)。
二、核心逻辑
- 前向传播:数据从输入层→隐藏层→输出层,每层神经元通过“加权求和+激活函数”处理数据;
- 激活函数:给网络引入“非线性”(如Sigmoid、ReLU、Softmax),让网络能拟合复杂关系;
- 反向传播:通过“损失函数”计算预测误差,再反向更新各层的权重,不断优化模型。
三、应用场景
- 计算机视觉:图像分类、目标检测;
- 自然语言处理:文本分类、机器翻译;
- 其他:推荐系统、语音识别等。
6.实操,动手做个简易神经网络(附源码)
接下来,做一个输入层为10,隐藏层为15,输出层为5的简易神经网络:
import numpy as np
class neuralnetwork:
def __init__(self,input_size,hidden_size,output_size):
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
#初始化
self.w1 = np.random.randn(self.input_size,self.hidden_size)#输入层到隐藏层权重
self.w2 = np.random.randn(self.hidden_size,self.output_size)#隐藏层到输出层权重
self.b1 = np.zeros((1,self.hidden_size))#输入层到隐藏层偏置
self.b2 = np.zeros((1,self.output_size))#隐藏层到输出层偏置
def sigmoid(self,x):
#sigmoid激活函数
return 1/(1 + np.exp(-x))
def forward(self,x):
#向前传播
#输入层(x) → 隐藏层(z1→a1) → 输出层(z2→a2)
self.z1 = np.dot(x,self.w1) + self.b1#线性变换
self.a1 = np.tanh(self.z1)#双曲正切非线性变换
#sigmoid 的输出是 (0,1),均值是 0.5,会导致后续层的输入 “偏向正方向”,梯度容易变小(梯度消失
#tanh 的输出是 (-1,1),均值接近 0,能缓解这个问题,梯度传播更顺畅
self.z2 = np.dot(self.a1,self.w2) + self.b2
self.a2 = self.sigmoid(self.z2)
return self.a2
def backward(self,x,y,learning_rate):
m = x.shape[0]# 获取样本数量(batch_size)
dz2 = self.a2 - y#误差
dw2 = (1/m) * np.dot(self.a1.T,dz2) #.T为矩阵转置,(1/m):对所有样本的梯度取平均,
#w2 是a1 到 z2的权重,所以 w2 的梯度 = 输入(a1).T × 输出误差(dz2)的平均,计算相关性
#误差越大、输入越强,w2 需要调整的幅度越大。
db2 = (1/m) * np.sum(dz2,axis=0,keepdims=True)
#对所有样本的误差 dz2 按「列」求和(axis=0 表示沿着样本维度求和
#计算隐藏层误差和梯度
dz1 = np.dot(dz2,self.w2.T) * (1 - np.power(self.a1,2))
dw1 = (1/m) * np.dot(x.T,dz1)
db1 = (1/m) * np.sum(dz1,axis=0,keepdims=True)
#更新权重和偏置
self.w2 -= learning_rate * dw2
self.b2 -= learning_rate * db2
self.w1 -= learning_rate * dw1
self.b1 -= learning_rate * db1
def calculate_loss(self,y_true,y_pred):
#预测结果(y_pred)和真实标签(y_true)
m = y_true.shape[0]#获取样本的数量
loss = -1/m * np.sum(y_true * np.log(y_pred) + (1-y_true) * np.log(1-y_pred))
#计算损失
return loss
def train(self,x,y,epochs,learning_rate):
for epoch in range(epochs):
#向前传播
output = self.forward(x)
#反向传播
self.backward(x,y,learning_rate)
#计算损失
loss = self.calculate_loss(y,output)
#每1000次迭代打印一次损失
if epoch % 1000 == 0:
print(f"迭代次数:{epoch}:损失:{loss}")
#创建神经网络对象
input_size = 10
hidden_size = 15
output_size = 5
nn = neuralnetwork(input_size,hidden_size,output_size)
#准备训练数据
X = np.random.randn(100,input_size)
Y = np.random.randint(0,2,size=(100,output_size))
#训练神经网络
epochs = 10000
learning_rate = 0.1
nn.train(X,Y,epochs,learning_rate)
#训练好的模型进行预测
input_data = np.random.randn(1,input_size)
output = nn.forward(input_data)
print("结果:",output)
我都做了比较明确的注释 是不是很有趣喵~
7.模型优化
一、梯度消失
- 定义:深度网络反向传播时,梯度从输出层向输入层传播过程中逐渐缩小至0,导致早期层参数更新缓慢,影响训练效果。
- 原因:使用Sigmoid/tanh等激活函数(输入过大/过小时导数趋近于0),网络层数过深。
解决方法:
- 改用ReLU类激活函数(大输入范围导数稳定)
- 优化权重初始化策略
- 使用批归一化
- 引入残差连接(跳连)
二、梯度爆炸
- 定义:反向传播时梯度值急剧增大(甚至发散),导致权重更新幅度过大、模型无法收敛。
- 表现:权重参数剧变、数值不稳定、训练过程波动大。
解决方法:
- 批归一化;
- 梯度截断(设置阈值限制梯度大小);
- 优化权重初始化;
- 降低学习率。
三、核心优化手段
深度学习优化是通过调整参数/超参数提升模型性能的迭代过程,关键方向包括:
- 损失函数:用均方误差、交叉熵等衡量预测与真实值的差异;
- 梯度下降:通过计算损失函数的梯度更新参数,变体包括批量/随机/小批量梯度下降;
- 学习率调整:平衡收敛速度与稳定性;
- 正则化:缓解过拟合(如L1/L2、Dropout);
- 批归一化:加速训练、缓解梯度问题;
- 自适应学习率:如AdaGrad、RMSProp等,动态调整学习率;
- 超参数调优:通过网格搜索、随机搜索等找到最优超参数组合。
四、调参技巧
针对超参数(学习率、正则化系数、网络结构等)的调整方法:
- 学习率:用指数衰减、余弦退火等策略逐步降低;
- 批量大小:平衡训练速度与收敛效果(大批次快但易陷局部最优,小批次反之);
- 正则化:结合L1/L2、Dropout降低过拟合;
- 网络结构:调整层数、神经元数或尝试不同层类型(卷积层/循环层等);
- 权重初始化:尝试随机初始化、预训练参数等;
- 早停策略:验证集误差上升时停止训练,避免过拟合;
- 参数搜索:用网格/随机搜索等工具(如scikit-learn)寻找最优组合。