深度学习基础:从损失函数到简易神经网络代码实操

87 阅读11分钟

深度学习基础:从损失函数到简易神经网络代码实操

损失函数是机器学习/深度学习中衡量模型预测与真实值差异的核心指标,它指导模型通过优化算法调整参数,是训练过程的“指南针”。

1.损失函数

一、损失函数的核心作用

  • 量化模型预测值 ^y与真实值 y 的不一致程度
  • 指导模型参数更新(通过梯度下降等算法最小化损失)
  • 评估模型性能,损失越小通常代表模型泛化能力越强。

在这里插入图片描述

二、常见损失函数分类

损失函数主要分为回归类(处理连续值预测)和分类类(处理离散类别预测)两类。

1. 回归类损失函数

(1)均方误差(MSE)

  • 适用场景:回归任务(如房价预测、温度预测)。
  • 公式: MSE=1ni=1n(yiy^i)2\text{MSE} = \frac{1}{n}\sum_{i=1}^n (y_i - \hat{y}_i)^2 其中: y_i 是第 i 个样本的真实值, ^y_i 是预测值, n 是样本数量。
  • 优点:计算简单,对小误差敏感;
  • 缺点:易受异常值影响(大误差会被平方放大),单位是原数据单位的平方(不直观)。

(2)均方根误差(RMSE)

  • 适用场景:回归任务(是MSE的改进,让单位更直观)。
  • 公式: RMSE=1ni=1n(yiy^i)2\text{RMSE} = \sqrt{\frac{1}{n}\sum_{i=1}^n (y_i - \hat{y}_i)^2}
  • 优点:单位与原数据一致,更易解释;
  • 缺点:同样易受异常值影响。
2. 分类类损失函数

交叉熵损失

  • 适用场景:分类任务(如图像分类、文本分类,尤其适用于多分类)。
  • 核心逻辑:通过衡量“模型预测的概率分布”与“真实标签的概率分布”之间的差异,引导模型输出更接近真实标签的概率。
  • 公式(假设有 N 个样本、 K 个类别): L=1Ni=1Nj=1Kq(i,j)log(p(i,j))L = -\frac{1}{N}\sum_{i=1}^N \sum_{j=1}^K q(i,j) \cdot \log(p(i,j)) 其中:
  • 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=[z1,z2,...,zK]\boldsymbol{z} = [z_1, z_2, ..., z_K] 某一元素 z_i 的Softmax值为:

Softmax(zi)=ezii=1Nezj\text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{i=1}^{N} e^{z_j}}

在这里插入图片描述

三、应用场景

  • 多分类任务的输出层(如分类“猫/狗/鸟”等3类时,输出3个概率值);
  • 配合交叉熵损失函数,实现多分类任务的损失计算。

5.神经网络

神经网络是模拟人脑神经元连接模式的机器学习模型,核心是通过多层“神经元”的计算,实现对复杂数据的拟合、分类或预测。

一、基本结构(以简单网络为例)

一个基础神经网络通常包含3类层:

  1. 输入层:接收原始数据(如图片的像素、文本的特征向量),神经元数量=数据的特征维度;
  2. 隐藏层:对输入数据做“特征提取与变换”,是网络的核心计算层(可包含1~N层,层数/神经元数决定网络复杂度);
  3. 输出层:输出模型结果(如分类任务的类别概率、回归任务的预测值),神经元数量=任务目标维度(如二分类用1个神经元,5分类用5个神经元)。 在这里插入图片描述

二、核心逻辑

  1. 前向传播:数据从输入层→隐藏层→输出层,每层神经元通过“加权求和+激活函数”处理数据;
  2. 激活函数:给网络引入“非线性”(如Sigmoid、ReLU、Softmax),让网络能拟合复杂关系;
  3. 反向传播:通过“损失函数”计算预测误差,再反向更新各层的权重,不断优化模型。

三、应用场景

  • 计算机视觉:图像分类、目标检测;
  • 自然语言处理:文本分类、机器翻译;
  • 其他:推荐系统、语音识别等。

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),网络层数过深。

解决方法:

  1. 改用ReLU类激活函数(大输入范围导数稳定)
  2. 优化权重初始化策略
  3. 使用批归一化
  4. 引入残差连接(跳连)

二、梯度爆炸

  • 定义:反向传播时梯度值急剧增大(甚至发散),导致权重更新幅度过大、模型无法收敛。
  • 表现:权重参数剧变、数值不稳定、训练过程波动大。

解决方法:

  1. 批归一化;
  2. 梯度截断(设置阈值限制梯度大小);
  3. 优化权重初始化;
  4. 降低学习率。

三、核心优化手段

深度学习优化是通过调整参数/超参数提升模型性能的迭代过程,关键方向包括:

  1. 损失函数:用均方误差、交叉熵等衡量预测与真实值的差异;
  2. 梯度下降:通过计算损失函数的梯度更新参数,变体包括批量/随机/小批量梯度下降;
  3. 学习率调整:平衡收敛速度与稳定性;
  4. 正则化:缓解过拟合(如L1/L2、Dropout);
  5. 批归一化:加速训练、缓解梯度问题;
  6. 自适应学习率:如AdaGrad、RMSProp等,动态调整学习率;
  7. 超参数调优:通过网格搜索、随机搜索等找到最优超参数组合。

四、调参技巧

针对超参数(学习率、正则化系数、网络结构等)的调整方法:

  1. 学习率:用指数衰减、余弦退火等策略逐步降低;
  2. 批量大小:平衡训练速度与收敛效果(大批次快但易陷局部最优,小批次反之);
  3. 正则化:结合L1/L2、Dropout降低过拟合;
  4. 网络结构:调整层数、神经元数或尝试不同层类型(卷积层/循环层等);
  5. 权重初始化:尝试随机初始化、预训练参数等;
  6. 早停策略:验证集误差上升时停止训练,避免过拟合;
  7. 参数搜索:用网格/随机搜索等工具(如scikit-learn)寻找最优组合。

深度学习基础部分结束啦,将来会给大家带来pytorch开发基础喵