深度学习中损失函数(loss function)介绍

深度学习中损失函数(loss function)介绍

​ 在深度学习的宏伟城堡中,损失函数扮演着国王的角色,它决定了模型训练的方向和目标。损失函数,也被称为代价函数,是衡量模型预测与实际结果之间差异的函数。在深度学习的训练过程中,我们的目标就是最小化这个损失函数,就像是在一场游戏中,我们的目标是获得尽可能低的失误和丢分。

​ 损失函数的选择对于模型的训练至关重要。不同的问题可能需要不同的损失函数。比如在图像识别中,我们可能需要一个能够处理大量类别的损失函数,这时候交叉熵损失就是一个很好的选择。而在预测房价等连续值的问题中,均方误差损失可能更为合适。

​ 损失函数不仅是评价模型性能的标尺,也是指导模型学习的方向。在训练过程中,我们通过计算损失函数的梯度,并使用梯度下降算法来更新模型的权重,以此来减少损失函数的值。

1. 损失函数的作用

1-1. 损失函数作用

​ 想象一下,你在玩一个投飞镖的游戏,而飞镖靶就是损失函数。你的每一次投掷,都对应着模型的一次预测。飞镖落在靶上的位置,就是预测结果与实际结果之间的误差。损失函数的作用就是告诉我们,我们的飞镖投得有多准,或者说,我们的模型预测得有多准确。

​ 在深度学习中,损失函数计算模型输出(预测值)和真实标签(实际值)之间的差异。这个差异,或者说"误差",量化了模型的性能。训练深度学习模型的过程,本质上就是通过调整模型的参数来最小化损失函数的过程。损失函数通常被视为一种 优化目标 ,它在训练过程中不断变化,我们希望通过优化算法(如梯度下降)来不断 减小损失函数,使得模型更精确地拟合数据。

1-2. 为什么要让损失函数持续下降

损失函数的下降反映了模型的性能提高,因此我们希望通过调整模型参数使损失函数尽可能持续下降。具体来说:

  • 反映模型拟合度:损失函数值越小,说明模型在训练数据上的拟合程度越高,预测误差越小。
  • 优化目标 :在机器学习和深度学习中,我们的最终目标是训练出一个 尽可能准确 的模型,使得它对未见过的数据(测试数据或未来数据)具有较好的预测能力。为了达到这个目标,必须尽量 最小化损失函数,降低模型的误差。
  • 梯度下降优化:优化过程的核心就是通过梯度下降法(或其变种)不断更新模型参数,以减少损失函数。每次更新时,都会使用损失函数来计算梯度,并沿着梯度的方向调整参数。
  • 避免过拟合或欠拟合:通过不断调整模型参数(如网络层、权重等),让损失函数下降,可以防止模型在训练数据上产生过拟合或欠拟合。过拟合是指模型在训练数据上表现很好,但在新数据(测试数据)上表现差;欠拟合则是指模型的表达能力不足,无法有效地拟合训练数据。

模型的拟合度(Model Fit)指的是模型在训练数据上的表现程度,衡量模型能够多好地捕捉训练数据中的规律和模式。拟合度越高,表示模型能够更精确地预测训练数据中的目标变量。具体来说:

  1. 高拟合度 :模型能够准确地预测训练数据中的结果,误差较小。这通常意味着模型的复杂度较高,可以很好地表达数据中的模式和关系。然而,过高的拟合度可能导致过拟合,即模型过度依赖训练数据中的噪声,不能很好地推广到新数据。
  2. 低拟合度 :模型不能很好地拟合训练数据,误差较大。这通常表明模型过于简单,无法捕捉数据的复杂规律,可能存在欠拟合现象。欠拟合的模型通常表现出较高的训练误差和较低的泛化能力。

为了避免过拟合和欠拟合,常通过调整模型的复杂度、损失函数、训练数据的量和质量、正则化方法等,来优化模型的拟合度,使其既能在训练集上取得较好的结果,又能在测试集或实际应用中具有较好的预测能力。

梯度下降是通过不断调整参数来最小化损失函数,最终使模型学习到最优的参数,从而提高预测准确性。假设你正在训练一个神经网络,并且已经得到了初始的参数(例如权重)。你计算出当前参数下的损失函数值(比如MSE损失)。接下来,你计算每个参数的梯度(即每个权重对损失函数的影响)。然后,你根据这些梯度调整权重,使得损失函数逐渐减小,模型预测更加准确。这个过程重复进行,直到损失函数的值降到足够小为止。

1-3. 损失函数下降的目标

  • 模型优化:每次调整参数时,我们希望通过梯度下降法(或其他优化算法)最小化损失函数。这是因为只有在训练过程中,模型的误差不断下降,才能有效地提高模型的预测性能。
  • 收敛与稳定性:损失函数在训练过程中逐渐减少,通常是模型朝着最佳解(即最小化误差)收敛的标志。若损失函数在多个训练轮次中持续下降,表明模型已经开始稳定地找到最优的参数。
  • 防止欠拟合和过拟合 :当损失函数 持续下降 到一个较低的值时,通常说明模型在训练集上拟合得较好。如果损失函数长时间不下降,可能表明模型无法有效拟合数据,产生了 欠拟合 。如果损失函数持续下降,并且在训练集和测试集上都表现良好,模型可能会很好地泛化,避免 过拟合

1-4. 损失函数下降的具体作用

  • 减少误差:损失函数的值反映了模型的预测值与真实值之间的误差。通过不断降低损失函数,我们希望减少模型的误差,使其预测更加精确。
  • 训练收敛:训练中的优化目标是让损失函数趋于最小。随着训练轮次的增加,损失值应当逐渐减少,模型的参数也会越来越接近最优解。损失函数达到某个极小值时,表示训练收敛,模型已经学到了数据中的规律。
  • 提高泛化能力 :一个低损失函数值通常意味着模型不仅在训练集上表现好,同时也在未见过的数据上(例如验证集或测试集)具有较好的预测能力。这有助于提升模型的 泛化能力

1-5. 如何让损失函数持续下降

让损失函数持续下降通常依赖于以下几个因素:

  • 合理的学习率(Learning Rate):学习率过大可能导致损失函数震荡或发散,学习率过小可能导致训练过程非常缓慢。通过调整学习率,可以使得梯度下降更加平稳,使得损失函数持续下降。
  • 优化器选择:不同的优化器(如SGD、Adam、RMSprop等)会影响训练过程中的参数更新方式,进而影响损失函数的下降速度。Adam优化器通常能加速训练并使得损失函数下降得更快。
  • 模型架构:模型的复杂度(层数、神经元数量等)也影响损失函数的下降速度。如果模型太简单,可能无法捕捉数据的复杂性,导致损失函数下降缓慢或者停滞。如果模型过于复杂,则可能导致过拟合,使损失函数在测试数据上不下降。
  • 数据预处理:合适的数据处理方式(如标准化、归一化、去噪等)可以加速训练过程,让损失函数更快速地下降。
  • 训练轮次(Epochs):适当增加训练轮次可以帮助模型有更多的机会去优化参数,从而让损失函数继续下降。

2. 常见的损失函数

  1. 均方误差损失(MSE)

    想象你在玩一个扔球进篮子的游戏,而篮子的大小就是均方误差损失。你每次扔球,球与篮子中心的距离平方就是你的得分。游戏的目标是尽可能让得分低,也就是让球尽可能接近篮子中心。在深度学习中,均方误差损失函数计算预测值与实际值之间差的平方的平均值,常用于回归问题。

  2. 交叉熵损失(Cross-Entropy Loss)

    想象你在玩一个猜谜游戏,每次猜测后,系统会告诉你猜测结果与正确答案之间的差异。交叉熵损失就是这样一个函数,它衡量的是模型输出的概率分布与真实标签的概率分布之间的差异。在分类问题中,当我们有多个类别时,交叉熵损失是首选的损失函数。

  3. Hinge损失(Hinge Loss)

    想象你在玩一个挥剑砍靶的游戏,目标是让剑刃尽可能地砍进靶心。Hinge损失函数用于支持向量机(SVM)等最大间隔分类器,它鼓励模型产生更大的间隔,以提高分类的鲁棒性。

  4. 绝对值损失(Absolute Loss)

    想象你在玩一个射箭游戏,目标是射中靶心,而靶心的半径就是绝对值损失。你的箭与靶心的距离越小,得分越低。绝对值损失计算预测值与实际值之间差的绝对值的平均值,它对异常值的敏感度低于均方误差损失。

3. 损失函数代码使用示例

通过以下一个示例,我们不断的优化参数,调整训练方式,来逐步使得损失函数下降的方法

【注意】:每次的参数调整只是一个调整探索过程,并不是调整后的参数一定为最优解,通过示例为了体会这个调参的过程,如何逐渐的去使得损失函数下降

demo_reg.py 功能介绍:

1.数据获取与预处理:代码首先从UCI机器学习库(archive.ics.uci.edu)获取波士顿房价数据集,对数据进行清洗(去除多余空格),并将清洗后的数据保存到本地文件housing.data中。

2.数据加载与分割:使用numpy加载本地文件,并提取特征变量x和目标变量y,然后将数据分为训练集和测试集。

3.模型定义:定义一个简单的线性神经网络模型Net,包含一个线性层用于预测。

4.模型训练:使用均方误差损失函数(MSELoss)和随机梯度下降优化器(SGD)训练模型,共迭代10000次,并在每次迭代后打印损失值和部分预测值与实际值。

注意事项:

1.确保网络连接正常,以便成功从UCI机器学习库获取数据。

2.确保数据文件housing.data可以正确保存到本地,并且路径正确。

3.确保模型训练时的数据形状一致,以避免广播问题导致的警告。

完整代码如下:demo_reg.py

python 复制代码
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Project  :demo_create
# @File     :demo_reg.py
# @Time     :2024-12-12 18:15
# @Author   :wangting_666


"""
 pip install torch -i https://mirrors.aliyun.com/pypi/simple/
 pip install requests -i https://mirrors.aliyun.com/pypi/simple/
 pip install numpy -i https://mirrors.aliyun.com/pypi/simple/
"""

# 导入所需的库
import torch
import requests
import numpy as np
import re

# 指定数据文件的URL
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data'
response = requests.get(url)
# 发送HTTP GET请求
if response.status_code == 200:
    # 请求成功,获取网页内容
    url_data = response.text
    # 按行分割数据
    lines = url_data.split('\n')

    processed_lines = []
    for line in lines:
        # 去除每行的首尾空白字符
        stripped_line = line.strip()
        if stripped_line:
            # 将多个连续空格替换为单个空格,并去除首尾空白字符
            processed_line = re.sub(r'\s{2,}', ' ', line).strip()
            processed_lines.append(processed_line)

    # 将处理后的数据行合并为一个字符串,每行之间用换行符分隔
    url_data = '\n'.join(processed_lines)

    # 指定本地文件名
    filename = 'housing.data'
    # 将处理后的数据写入本地文件
    with open(filename, 'w') as f:
        f.write(url_data)
    print('Data saved to {}'.format(filename))
else:
    # 请求失败,打印错误信息
    print('Failed to retrieve data')

# 使用numpy加载本地文件
data = np.loadtxt('housing.data')
# 506行,14列数据
print("data.shape: {}".format(data.shape))

# 提取目标变量y和特征变量x
# data 数组中提取最后一列数据,存储在变量 y 中。
y = data[:, -1]
# data 数组中提取除了最后一列之外的所有列,存储在变量 x 中。
x = data[:, 0:-1]

# 将数据分为训练集和测试集
x_train = x[0:496, ...]
y_train = y[0:496, ...]
x_test = x[496:, ...]
y_test = y[496:, ...]

# 打印训练集和测试集的形状
print("x_train.shape: {}".format(x_train.shape))
print("y_train.shape: {}".format(y_train.shape))
print("x_test.shape: {}".format(x_test.shape))
print("y_test.shape: {}".format(y_test.shape))


# 定义神经网络模型
class Net(torch.nn.Module):
    def __init__(self, n_features, n_output):
        super(Net, self).__init__()
        # 定义一个线性层,用于预测
        self.predict = torch.nn.Linear(n_features, n_output)

    def forward(self, x):
        # 前向传播
        out = self.predict(x)
        return out


# 实例化模型
net = Net(13, 1)
# 定义损失函数
loss_func = torch.nn.MSELoss()

# 定义优化器
optimizer = torch.optim.SGD(net.parameters(), lr=0.0001)

# 训练模型
for i in range(10000):
    # 将训练数据转换为PyTorch张量
    x_data = torch.tensor(x_train, dtype=torch.float32)
    y_data = torch.tensor(y_train, dtype=torch.float32)
    # 前向传播,计算预测值
    pred = net.forward(x_data)
    # 计算损失
    loss = loss_func(pred, y_data) * 0.001

    # 清空梯度
    optimizer.zero_grad()
    # 反向传播,计算梯度
    loss.backward()
    # 更新模型参数
    optimizer.step()

    # 每轮迭代后打印损失和部分预测值、实际值
    print("item:{},loss:{}".format(i, loss.item()))
    print(pred[0:10])
    print(y_data[0:10])

最后item输出损失函数结果(11.82%):loss:0.11819077283143997

item:9999,loss:0.11819077283143997
tensor([[22.0104],
        [20.1129],
        [22.3382],
        [22.9463],
        [21.9336],
        [21.3733],
        [21.1441],
        [17.4917],
        [14.9874],
        [18.2216]], grad_fn=<SliceBackward0>)
tensor([24.0000, 21.6000, 34.7000, 33.4000, 36.2000, 28.7000, 22.9000, 27.1000,
        16.5000, 18.9000])

3-1. 调整学习率 (Learning Rate)

通过调整学习率来优化,目前使用的学习率lr=0.001,这可能会导致模型训练得较慢。可以尝试增大学习率来加速训练,或者使用更适合的值。可以尝试以下几种调整:

  • 尝试 0.0010.01
  • 过高的学习率可能会导致发散,可以逐步减小。
python 复制代码
# optimizer = torch.optim.SGD(net.parameters(), lr=0.0001)
optimizer = torch.optim.SGD(net.parameters(), lr=0.001)

最后item输出损失函数结果(9.47%):item:9999,loss:0.09471820294857025

item:9999,loss:0.09471820294857025
tensor([[22.1652],
        [21.6274],
        [19.9424],
        [19.1086],
        [19.8691],
        [20.0128],
        [22.4789],
        [24.8061],
        [26.4139],
        [23.9335]], grad_fn=<SliceBackward0>)
tensor([24.0000, 21.6000, 34.7000, 33.4000, 36.2000, 28.7000, 22.9000, 27.1000,
        16.5000, 18.9000])

3-2. 调整损失函数权重

当前在计算损失时将结果乘以了一个常数 0.001,这会影响损失的计算。可以尝试不同的乘法因子,看看是否能改善模型表现。比如,将 0.001 改为 0.0001

python 复制代码
# 计算损失
# loss = loss_func(pred, y_data) * 0.001
loss = loss_func(pred, y_data) * 0.0001

最后item输出损失函数结果(1.07%):loss:0.0107608987018466

item:9999,loss:0.0107608987018466
tensor([[24.8059],
        [21.2679],
        [21.0506],
        [20.6933],
        [20.7666],
        [21.0484],
        [21.9299],
        [22.2718],
        [19.4872],
        [21.5408]], grad_fn=<SliceBackward0>)
tensor([24.0000, 21.6000, 34.7000, 33.4000, 36.2000, 28.7000, 22.9000, 27.1000,
        16.5000, 18.9000])

3-3. 增加训练轮数 (Epochs)

目前代码中训练轮数为 10000 次。如果你觉得模型在 10000 次迭代后仍未达到最优结果,可以适当增加训练轮数。例如,增加到 2000030000,并观察损失函数是否持续下降。

python 复制代码
# for i in range(10000):
for i in range(30000):

最后item输出损失函数结果(0.93%):loss:0.00932362861931324

item:29999,loss:0.00932362861931324
tensor([[22.3280],
        [22.3310],
        [22.1053],
        [20.9865],
        [21.1635],
        [21.2615],
        [21.4697],
        [22.1821],
        [20.5868],
        [21.7904]], grad_fn=<SliceBackward0>)
tensor([24.0000, 21.6000, 34.7000, 33.4000, 36.2000, 28.7000, 22.9000, 27.1000,
        16.5000, 18.9000])

3-4. 使用更高级的优化器(优化器调整)

当前使用的是 SGD(随机梯度下降) ,尽管它是最基础的优化器,但它可能在某些问题上收敛较慢,尤其是当损失函数比较复杂时。你可以尝试使用 Adam 优化器,它通常比 SGD 更有效,能加速训练并避免过拟合。

SGD:标准的梯度下降方法,适用于较简单的任务,收敛速度较慢且容易陷入局部最小值。

Adam:结合了动量和自适应学习率调整,通常能在复杂问题中更快收敛,避免过拟合。

学习率过大会导致模型训练不稳定,过小则会导致收敛缓慢。调整学习率可以帮助模型快速收敛

python 复制代码
# optimizer = torch.optim.SGD(net.parameters(), lr=0.0001)
optimizer = torch.optim.Adam(net.parameters(), lr=0.001)

最后item输出损失函数结果(0.86%):loss:0.008578178472816944

item:29999,loss:0.008578178472816944
tensor([[22.4775],
        [22.4890],
        [22.7133],
        [22.8217],
        [22.9005],
        [22.6268],
        [22.3341],
        [22.4805],
        [22.4585],
        [22.4555]], grad_fn=<SliceBackward0>)
tensor([24.0000, 21.6000, 34.7000, 33.4000, 36.2000, 28.7000, 22.9000, 27.1000,
        16.5000, 18.9000])

3-5. 使用更复杂的损失函数

尽管 MSELoss 是回归任务中的标准损失函数,但有时候可以根据任务的特性尝试其他损失函数。例如,Huber Loss 对于一些数据中的异常值更具鲁棒性,可以尝试替换掉 MSELoss

python 复制代码
# loss_func = torch.nn.MSELoss()
loss_func = torch.nn.HuberLoss(delta=1.0)

最后item输出损失函数结果(0.06%):loss:0.0006132011767476797

item:29999,loss:0.0006132011767476797
tensor([[21.1054],
        [21.1156],
        [21.3443],
        [21.4555],
        [21.5352],
        [21.2573],
        [20.9581],
        [21.1065],
        [21.0816],
        [21.0821]], grad_fn=<SliceBackward0>)
tensor([24.0000, 21.6000, 34.7000, 33.4000, 36.2000, 28.7000, 22.9000, 27.1000,
        16.5000, 18.9000])

通过以上的示例,可以看到损失值降低的办法非常多,是一个持续优化的过程。

相关推荐
铭瑾熙15 分钟前
深度学习之全景分割
人工智能·深度学习
醉后才知酒浓35 分钟前
OpenCV--特征匹配
人工智能·opencv·计算机视觉
小李飞刀李寻欢40 分钟前
opencv实现给图像加上logo图像
人工智能·opencv·计算机视觉
新加坡内哥谈技术1 小时前
iOS 18.2 今天正式推送更新,带来了备受瞩目的 ChatGPT 集成以及更多 Apple Intelligence 工具
大数据·人工智能·语言模型·chatgpt
wux_labs1 小时前
开发OPEA微服务
人工智能·微服务·云原生·架构·大模型
O_o3811 小时前
RGB图片的处理和复原
深度学习
GWY_uu2 小时前
Open CV图像处理基础函数详解
图像处理·人工智能·计算机视觉
微软技术栈2 小时前
Azure OpenAI 生成式人工智能白皮书
人工智能
西猫雷婶2 小时前
python学opencv|读取图像(十一)彩色图像转灰度图的两种办法
开发语言·人工智能·python·opencv·计算机视觉
神一样的老师2 小时前
【鸿睿创智开发板试用】在OpenHarmony 4.1环境下移植OpenCV示例
人工智能·opencv·计算机视觉