手撕深度学习中的损失函数(上)

news2025/1/18 17:02:07

面试中经常会问到损失函数的相关问题,本文推导了深度学习中常用损失函数的计算公式和反向传播公式,并使用numpy实现。


定义损失函数基类:

class Loss:
    
    def loss(self, predictions, targets):
        raise NotImplementedError
    
    def grad(self, predictions, targets):
        raise NotImplementedError

定义损失函数中会用到的数学函数

def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))


def softmax(x, dim=-1):
    x_max = np.max(x, axis=dim, keepdims=True)
    # 减去最大值,防止上溢
    x_exp = np.exp(x - x_max)
    return x_exp / np.sum(x_exp, axis=dim, keepdims=True)


def log_softmax(x, dim=-1):
    x_max = np.max(x, axis=dim, keepdims=True)
    x_exp = np.exp(x - x_max)
    exp_sum = np.sum(x_exp, axis=dim, keepdims=True)
    return x - x_max - np.log(exp_sum)


def one_hot(labels, n_classes):
    return np.eye(n_classes)[labels.reshape(-1)]

MAE

平均绝对误差(Mean Absolute Error):模型预测值 y y y和真实值 y ^ \hat y y^之间距离的平均值
l o s s = 1 n ∑ i ∣ y i − y ^ i ∣ loss = \frac{1}{n}\sum_i{|y_i-\hat y_i|} loss=n1iyiy^i

g r a d = 1 n s i g n ( y − y ^ ) grad = \frac{1}{n}sign(y-\hat y) grad=n1sign(yy^)

class MAE(Loss):

    def loss(self, predictions, targets):
        return np.sum(np.abs(predictions - targets)) / targets.shape[0]

    def grad(self, predictions, targets):
        return np.sign(predictions - targets) / targets.shape[0]

MSE

均方差损失(Mean Squared Error Loss):模型预测值 y y y和真实值 y ^ \hat y y^之间差值平方的平均值
l o s s = 1 2 n ∑ i ∣ ∣ y i − y ^ i ∣ ∣ 2 loss = \frac{1}{2n}\sum_i{||y_i-\hat y_i||^2} loss=2n1i∣∣yiy^i2

g r a d = 1 n ( y − y ^ ) grad = \frac{1}{n}(y - \hat y) grad=n1(yy^)

class MSE(Loss):

    def loss(self, predictions, targets):
        return 0.5 * np.sum((predictions - targets) ** 2) / targets.shape[0]

    def grad(self, predictions, targets):
        return (predictions - targets) / targets.shape[0]

Huber Loss

结合了MAE和MSE的优点,也被称为 Smooth Mean Absolute Error

在误差较小时使用MSE,误差较大时使用MAE
l o s s = 1 n { 0.5 ∗ ∑ i ∣ ∣ y i − y ^ i ∣ ∣ 2 , ∣ y i − y ^ ∣ < δ ∑ i δ ∣ y i − y ^ i ∣ − 0.5 ∗ δ 2 , o t h e r w i s e loss=\frac{1}{n}\left\{ \begin{aligned} & 0.5* \sum_i{||y_i-\hat y_i||^2}, \quad \quad |y_i - \hat y| < \delta \\ & \sum_i{\delta |y_i-\hat y_i| - 0.5 * \delta^2}, \quad \quad otherwise \\ \end{aligned} \right. loss=n1 0.5i∣∣yiy^i2,yiy^<δiδyiy^i0.5δ2,otherwise

g r a d = 1 n { y − y ^ , ∣ y − y ^ ∣ < δ δ s i g n ( y − y ^ ) , o t h e r w i s e grad=\frac{1}{n}\left\{ \begin{aligned} & y-\hat y,\quad \quad |y - \hat y| < \delta \\ & \delta sign(y-\hat y),\quad \quad otherwise \\ \end{aligned} \right. grad=n1{yy^,yy^<δδsign(yy^),otherwise


MAE, MSE, Huber Loss函数曲线
loss1
class HuberLoss(Loss):

    def __init__(self, delta=1.0):
        self.delta = delta

    def loss(self, predictions, targets):
        dist = np.abs(predictions - targets)
        l2_mask = dist < self.delta
        l1_mask = ~l2_mask
        l2_loss = 0.5 * (predictions - targets) ** 2
        l1_loss = self.delta * dist - 0.5 * self.delta ** 2
        total_loss = np.sum(l2_loss * l2_mask + l1_loss * l1_mask) / targets.shape[0]
        return total_loss

    def grad(self, predictions, targets):
        error = predictions - targets
        l2_mask = np.abs(error) < self.delta
        l1_mask = ~l2_mask
        l2_grad = error
        l1_grad = self.delta * np.sign(error)
        total_grad = l2_grad * l2_mask + l1_grad * l1_mask
        return total_grad / targets.shape[0]

Cross Entropy Loss

熵定义为:信息的数学期望。
H = − ∑ i p ( x i ) l o g ( p ( x i ) ) H = -\sum_i{p(x_i)log(p(x_i))} H=ip(xi)log(p(xi))
KL散度

KL散度(Kullback-Leibler Divergence)也叫做相对熵,用于度量两个概率分布之间的差异程度,p相对q的KL散度为
D q ( p ) = H q ( p ) − H ( p ) = ∑ x p l o g ( p ) − p l o g ( q ) D_q(p) = H_q(p) - H(p) = \sum_x{plog(p)-plog(q)} Dq(p)=Hq(p)H(p)=xplog(p)plog(q)
交叉熵

预测概率分布q与真实概率分布p的差异
H q ( p ) = − ∑ x p l o g ( q ) H_q(p) = -\sum_{x} p log(q) Hq(p)=xplog(q)
详细推导:
l o s s = − 1 N ∑ i = 1 N ∑ k = 1 K y ^ i k ⋅ l o g _ s o f t m a x ( y i k ) = − ∑ i = 1 N l o g _ s o f t m a x ( y i c ) loss = -\frac{1}{N} \sum_{i=1}^N\sum_{k=1}^{K}\hat y_i^k \cdot log\_softmax(y_i^k) = -\sum_{i=1}^Nlog\_softmax(y_i^c) loss=N1i=1Nk=1Ky^iklog_softmax(yik)=i=1Nlog_softmax(yic)
y c y^c yc是标签, y ^ \hat y y^是标签的独热编码, y y y是预测概率向量,向量长度等于 y y y的类别 K K K

化简:
C E = − l o g _ s o f t m a x ( y c ) = − l o g ( e x p ( y c ) ∑ k = 1 K e x p ( y k ) ) = − y c + l o g ( ∑ k = 1 K e x p ( y k ) ) CE = -log\_softmax(y^c) = -log(\frac {exp(y^c)}{\sum_{k=1}^{K}exp(y^k)})=-y^c + log(\sum_{k=1}^{K}exp(y^k)) CE=log_softmax(yc)=log(k=1Kexp(yk)exp(yc))=yc+log(k=1Kexp(yk))

求导:
g r a d = 1 N ∑ i = 1 N ∂ C E i ∂ y i = 1 N ∑ i = 1 N s o f t m a x ( y i ) − y ^ i grad = \frac{1}{N}\sum_{i=1}^N\frac{\partial CE_i}{\partial y_i} = \frac{1}{N}\sum_{i=1}^N softmax(y_i) - \hat y_i grad=N1i=1NyiCEi=N1i=1Nsoftmax(yi)y^i

class CrossEntropy(Loss):

    def loss(self, predictions, targets):
        # targets是one-hot向量
        ce = -log_softmax(predictions, dim=1) * targets
        return np.sum(ce) / targets.shape[0]

    def grad(self, predictions, targets):
        logits = softmax(predictions, dim=1)
        return (logits - targets) / targets.shape[0]

上面的代码中不同类别的权重是相等的,如果不同类别的样本数量差异过大,可以调整不同类别的权重。

顺便实现一下KL散度

注意,交叉熵的标签是one-hot向量,KL散度的标签是概率分布向量

下面是更通用的推导, y ^ \hat y y^是概率向量, ∑ k = 1 K y ^ k = 1 \sum_{k=1}^{K}\hat y^k=1 k=1Ky^k=1
C E = − ∑ k = 1 K y ^ k l o g _ s o f t m a x ( y c ) = − ∑ k = 1 K y ^ k l o g ( e x p ( y k ) ∑ k = 1 K e x p ( y k ) ) = − ∑ k = 1 K [ y i k − l o g ( ∑ k = 1 K e x p ( y k ) ) ] ⋅ y ^ i k = l o g ( ∑ k = 1 K e x p ( y k ) ) − ∑ k = 1 K y i k ⋅ y ^ i k CE = -\sum_{k=1}^{K}\hat y^klog\_softmax(y^c) = -\sum_{k=1}^{K}\hat y^klog(\frac {exp(y^k)}{\sum_{k=1}^{K}exp(y^k)})=-\sum_{k=1}^{K}[y_i^k - log(\sum_{k=1}^{K}exp(y^k))]\cdot \hat y_i^k = log(\sum_{k=1}^{K}exp(y^k))-\sum_{k=1}^{K}y_i^k \cdot \hat y_i^k CE=k=1Ky^klog_softmax(yc)=k=1Ky^klog(k=1Kexp(yk)exp(yk))=k=1K[yiklog(k=1Kexp(yk))]y^ik=log(k=1Kexp(yk))k=1Kyiky^ik

g r a d = 1 N ∑ i = 1 N ∂ C E i ∂ y i = 1 N ∑ i = 1 N s o f t m a x ( y i ) − y ^ i grad = \frac{1}{N}\sum_{i=1}^N\frac{\partial CE_i}{\partial y_i} = \frac{1}{N}\sum_{i=1}^N softmax(y_i) - \hat y_i grad=N1i=1NyiCEi=N1i=1Nsoftmax(yi)y^i

class KLDivLoss(Loss):

    def loss(self, predictions, targets):
        # targets是概率向量
        ce = (log_softmax(targets) - np.log(predictions)) * targets
        return np.sum(ce) / targets.shape[0]

    def grad(self, predictions, targets):
        logits = softmax(predictions, dim=1)
        return (logits - targets) / targets.shape[0]

扩展:

为什么在分类任务中,用交叉熵而不是MSE?

Logistic Regression为例,分别用交叉熵和MSE推导反向传播过程
f w , b ( x ) = σ ( ∑ i w i x i + b ) f_{w,b}(x) = \sigma(\sum_i{w_ix_i+b}) fw,b(x)=σ(iwixi+b)
其中, σ \sigma σSigmoid激活函数,f是预测值,0 < f(x) < 1

1.交叉熵
L = − [ y l n ( f ( x ) ) + ( 1 − y ) l n ( 1 − f ( x ) ) ] y ∈ { 0 , 1 } L = -[yln(f(x)) + (1-y)ln(1-f(x))] \quad y \in \{0,1\} L=[yln(f(x))+(1y)ln(1f(x))]y{0,1}
反向传播:
∂ L ( w , b ) ∂ w = − [ y ∂ l n ( f ( x ) ) ∂ w + ( 1 − y ) ∂ ( 1 − l n ( f ( x ) ) ) ∂ w ] \frac{\partial L(w,b)}{\partial w} = - [y \frac{\partial ln(f(x))}{\partial w} + (1-y) \frac{\partial (1-ln(f(x)))}{\partial w}] wL(w,b)=[ywln(f(x))+(1y)w(1ln(f(x)))]
z = w x + b z=wx+b z=wx+b
∂ l n ( f ( x ) ) ∂ z = ∂ l n ( σ ( z ) ) ∂ z = 1 − σ ( z ) ∂ ( 1 − l n ( f ( x ) ) ) ∂ w = − σ ( z ) \frac{\partial ln(f(x))}{\partial z} = \frac{\partial ln(\sigma(z))}{\partial z} = 1 - \sigma(z) \qquad \frac{\partial (1-ln(f(x)))}{\partial w} = - \sigma(z) zln(f(x))=zln(σ(z))=1σ(z)w(1ln(f(x)))=σ(z)

∂ L ( w , b ) ∂ z ∂ z ∂ w = − [ y ( 1 − σ ( z ) ) + ( 1 − y ) ( − σ ( z ) ) ] ∂ z ∂ w = − ( y − σ ( z ) ) x \frac{\partial L(w,b)}{\partial z} \frac{\partial z}{\partial w} = -[y(1-\sigma (z)) + (1-y)(-\sigma (z))]\frac{\partial z}{\partial w} = - (y - \sigma(z))x zL(w,b)wz=[y(1σ(z))+(1y)(σ(z))]wz=(yσ(z))x

梯度下降:
w t = w t − 1 − η ∑ i ( − ( y i − σ ( w x + b ) ) x i ) w_t = w_{t-1} - \eta \sum_i(- (y_i - \sigma(wx+b))x_i) wt=wt1ηi((yiσ(wx+b))xi)

  • 当标签 y y y和预测值 f ( x ) f(x) f(x)差距越大,梯度越大,下降的越快

2.MSE
L = 1 2 ( f ( x ) − y ) 2 y ∈ { 0 , 1 } L = \frac{1}{2}(f(x) - y)^2 \quad y \in \{0,1\} L=21(f(x)y)2y{0,1}
反向传播:
∂ L ( w , b ) ∂ w = ( f ( x ) − y ) ∂ f ( x ) ∂ w \frac{\partial L(w,b)}{\partial w} = (f(x) - y) \frac{\partial f(x)}{\partial w} wL(w,b)=(f(x)y)wf(x)
同令 z = w x + b z=wx+b z=wx+b
∂ f ( x ) ∂ z ∂ z ∂ w = σ ( z ) ( 1 − σ ( z ) ) x \frac{\partial f(x)}{\partial z} \frac{\partial z}{\partial w} = \sigma(z)(1-\sigma(z))x zf(x)wz=σ(z)(1σ(z))x

∂ L ( w , b ) ∂ w = ( f ( x ) − y ) f ( x ) ( 1 − f ( x ) ) x \frac{\partial L(w,b)}{\partial w} = (f(x) - y) f(x)(1-f(x))x wL(w,b)=(f(x)y)f(x)(1f(x))x

  • y = 0 y=0 y=0 f ( x ) f(x) f(x)趋近1时,因为有 1 − f ( x ) 1-f(x) 1f(x)这一项,梯度会消失,然而预测是完全错误的
  • y = 1 y=1 y=1 f ( x ) f(x) f(x)趋近0时,因为有 f ( x ) f(x) f(x)这一项,梯度也会消失,预测完全错误

总结:

​交叉熵损失函数关于输入权重的梯度表达式与预测值与真实值的误差成正比且不含激活函数的梯度,而均方误差损失函数关于输入权重的梯度表达式中则含有,由于常用的sigmoid/tanh等激活函数存在梯度饱和区,使得MSE对权重的梯度会很小,参数w调整的慢,训练也慢,而交叉熵损失函数则不会出现此问题,其参数w会根据误差调整,训练更快,效果更好。


参考资料:

borgwang/tinynn: A lightweight deep learning library

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/422755.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL Server数据库-----基础知识

数据库基础数据类型 整数类型 int 范围 2的正负31次方 小数类型 decimal 范围 正负10的38次方&#xff0c;不包含两端的 固定长度的非Unicode字符 char 可变长度的非Unicode字符 varchar 如varchar(20)是最多可以有20个字符&#xff0c;-12-3 可变长度的Unicode字符 nvar…

xmind免费安装使用教程

一、简介 xmind 是一款功能强大的思维导图和脑图制作工具&#xff0c;它可以帮助用户将复杂的信息和想法以图形化的方式进行组织和展示&#xff0c;使得思维更加清晰和有条理。xmind 是一款跨平台的思维导图软件&#xff0c;支持 Windows、MacOS 和 Linux 系统。它提供了丰富的…

pytorch进阶学习(八):使用训练好的神经网络模型进行图片预测

课程资源&#xff1a; 【小学生都会的Pytorch】九、运用你的模型做预测&#xff08;1&#xff09;_哔哩哔哩_bilibili 笔记&#xff1a; pytorch进阶学习&#xff08;四&#xff09;&#xff1a;使用不同分类模型进行数据训练&#xff08;alexnet、resnet、vgg等&#xff09…

免费远程桌面连接工具合集

随着科技的进步和通信技术的发展&#xff0c;远程办公变得越来越普遍。这种办公模式有助于提高工作效率&#xff0c;对于员工来说很友好的是&#xff0c;上班变得更加灵活了。 今天就给大家推荐几款远程桌面连接工具&#xff0c;不仅可以电脑连接&#xff0c;手机也可以直接连…

读写分离导致读不到刚插入的数据

背景 前两天在做一个功能的时候&#xff0c;需要先插表&#xff0c;如果数据重复则从数据库中查询出这条数据&#xff0c;这段代码在测试环境并没有什么问题&#xff0c;但是到生产之后就会偶现的报一些错&#xff0c;就是读不到已插入的数据&#xff0c;导致后续业务出现问题…

超详细Django+vue+vscode前后端分离搭建

文章目录一、Django后端搭建1.1 创建项目和app1.2 注册app1.3 运行项目1.4 配置mysql数据库1.5 创建数据库类1.6 使用Django后台进行数据管理2、Django rest framework配置2.1 序列化2.2 添加视图2.3 添加路由2.4 在项目根目录下的urls中加入如下代码2.5 api测试2.6 筛选和搜索…

BGP协议解析(白话版)

之前一直没搞明白BGP有啥用&#xff0c;加了跟没加没啥区别&#xff0c;专门查资料写了这篇《BGP协议解析》。 下面使用eNSP模拟器演示&#xff01; IBGP与EBGP的区别 BGP分为两种&#xff1a;IBGP与EBGP。 两个路由器的BGP号相同&#xff0c;建立邻居关系叫IBGP&#xff0…

树莓派连接串口时无法开机

树莓派连接串口时无法开机我的情况我的思考我的解决过程重点参考我的情况 因为项目需要&#xff0c;因此需要使用树莓派控制电机&#xff0c;而电机是一上电就会给树莓派发送数据&#xff0c;而这时树莓派还正处于开机时&#xff0c;结果就是开机失败。当将串口断开时就又可以…

PHP快速入门05-时间日期与时区,附30个常用案例

文章目录前言一、时间日期与时区1.1 时间与日期1.2 时区二、 30个日期时间函数的用法示例2.1 获取当前的时间戳2.2 将时间戳格式化为日期时间2.3 获取当前的日期2.4 获取当前的时间2.5 获取当前年份2.6 获取当前月份2.7 获取当前日期的第几天2.8 计算两个日期之间的天数差2.9 计…

央媒报道的长与短

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体 胡老师。 在最近的媒体服务中&#xff0c;遇到一个问题&#xff0c;与大家讨论下&#xff0c;很多媒体特别是央媒&#xff0c;在活动报道中不会完全按照新闻稿通稿的内容去报道&#xff0c;有的会根…

MQ选型,kafka、RocketMQ、RabbitMQ、ActiveMQ

MQ&#xff08;Message Queue&#xff09;&#xff0c;是基础数据结构中“先进先出”的一种数据结构。指把要传输的数据&#xff08;消息&#xff09;放在队列中&#xff0c;用队列机制来实现消息传递——生产者产生消息并把消息放入队列&#xff0c;然后由消费者去处理。消费者…

java SimpleDateFormat和Calendar日期类

目录一、SimpleDateFormat使用二、Calendar使用一、SimpleDateFormat使用 使用Date直接输出日期时&#xff0c;是使用系统默认的格式输出&#xff0c;所以需要使用SimpleDateFormat来格式化日期。 那么SimpleDateFormat类怎么使用呢&#xff0c;我们需要先了解此类的格式化符号…

Codeforces Round 866 (Div. 2) 题解

目录 A. Yuras New Name&#xff08;构造&#xff09; 思路&#xff1a; 代码&#xff1a; B. JoJos Incredible Adventures&#xff08;构造&#xff09; 思路&#xff1a; 代码&#xff1a; C. Constructive Problem&#xff08;思维&#xff09; 思路&#xff1a; 代…

一、计算机的发展历史

一、计算机的发展历史 第一台现代计算机 ENIAC&#xff1a;世界上第一台现代通用电子数字计算机&#xff0c;诞生于1946年2月14日的美国宾夕法尼亚大学。研制电子计算机的想法产生于第二次世界大战进行期间。当时激战正酣&#xff0c;各国的武器装备还很差&#xff0c;占主要地…

Java垃圾收集原理

程序计数器、虚拟机栈、本地方法栈这三个区域随线程而灭&#xff0c;栈中栈帧的内存大小也是在确定的。这几个区域的内存分配和回收都具有确定性&#xff0c;因此不需要过多考虑如何回收。 Java堆和方法区这两个区域有着很显著的不确定性 一个接口的实现类需要的内存可能不一…

软考第七章 下一代互联网

下一代互联网 1.IPv6 IPv4的缺陷&#xff1a; 网络地址短缺路由速度慢&#xff0c;IPv4头部多达13个字段&#xff0c;路由器处理的信息量很大缺乏安全功能不支持新的业务模式 关于PIv6的研究成果都包含在1998年12月发表的RFC 2460文档中 1.1 IPv6分组格式 版本&#xff1a…

量子退火Python实战(3):投资组合优化(Portfolio) MathorCup2023特供PyQUBO教程

文章目录前言一、什么是投资组合优化&#xff1f;二、投资组合优化建模1. 目标函数&#xff1a;回报2.约束函数&#xff1a;风险3.最终优化目标函数三、基于PyQUBO实现1. 获取数据2. 数据处理3. 目标函数PyQUBO实现4. OpenJij实施优化总结前言 提示&#xff1a;包含pyQUBO用法…

硬件语言Verilog HDL牛客刷题day11 A里部分 和 Z兴部分

1.VL72 全加器 1.题目&#xff1a; ① 请用题目提供的半加器实现全加器电路① 半加器的参考代码如下&#xff0c;可在答案中添加并例化此代码。 2. 解题思路 &#xff08;可以看代码&#xff09; 2.1 先看 半加器 s 是加位 &#xff0c; C 是进位。 2.2 再看全加器 …

2023年新手如何选择云服务器配置来部署自己的网站?

现在做网站的人越来越少了&#xff0c;没有以前那种百万网站站长的势头。但是&#xff0c;不论个人站长还是企业&#xff0c;只要网上开展业务其实都会需要自己网站或小程序、APP等平台。如今&#xff0c;很少有人使用虚拟主机&#xff0c;但是独立服务器成本高&#xff0c;一般…

【2023】Kubernetes-网络原理

目录kubernetes网络模型kubernetes网络实现容器到容器之间通信Pod之间的通信Pod到Service之间的通信集群内部与外部组件之间的通信开源容器网络方案FlannelCalicokubernetes网络模型 Kubernetes网络模型设计的一个基础原则是&#xff1a;每个Pod都拥有一个独立的IP地址&#x…