【使用 TensorFlow 2】03/3 创建自定义损失函数

一、说明

TensorFlow 2发布已经接近5年时间,不仅继承了Keras快速上手和易于使用的特性,同时还扩展了原有Keras所不支持的分布式训练的特性。3大设计原则:简化概念,海纳百川,构建生态.这是本系列的第三部分,我们将创建代价函数并在 TensorFlow 2 中使用它们。

图 1:实际应用中的梯度下降算法

二、关于代价函数

神经网络学习将训练数据中的一组输入映射到一组输出。它通过使用某种形式的优化算法来实现这一点,例如梯度下降、随机梯度下降、AdaGrad、AdaDelta 或一些最近的算法,例如 Adam、Nadam 或 RMSProp。梯度下降中的"梯度"指的是误差梯度。每次迭代后,网络都会将其预测输出与实际输出进行比较,然后计算"误差"。通常,对于神经网络,我们寻求最小化错误。因此,用于最小化误差的目标函数通常称为成本函数或损失函数,并且由"损失函数"计算的值简称为"损失"。各种问题中使用的典型损失函数 --

A。均方误差

b. 均方对数误差

C。二元交叉熵

d. 分类交叉熵

e. 稀疏分类交叉熵

在Tensorflow中,这些损失函数已经包含在内,我们可以如下所示调用它们。

1 损失函数作为字符串

model.compile(损失='binary_crossentropy',优化器='adam',指标='准确性'

或者,

  1. 损失函数作为对象

从tensorflow.keras.losses导入mean_squared_error

model.compile(损失=mean_squared_error,优化器='sgd')

将损失函数作为对象调用的优点是我们可以在损失函数旁边传递参数,例如阈值。

从tensorflow.keras.losses导入mean_squared_error

model.compile(损失=均方误差(参数=值),优化器='sgd')

三、使用函数创建自定义损失:

为了使用函数创建损失,我们需要首先命名损失函数,它将接受两个参数,y_true(真实标签/输出)和y_pred(预测标签/输出)。

def loss_function(y_true, y_pred):

***一些计算***

回波损耗

四、创建均方根误差损失 (RMSE):

损失函数名称 --- my_rmse

目的是返回目标 (y_true) 和预测 (y_pred) 之间的均方根误差。

RMSE 公式:

  • 误差:真实标签和预测标签之间的差异。
  • sqr_error:误差的平方。
  • mean_sqr_error:误差平方的平均值
  • sqrt_mean_sqr_error:误差平方均值的平方根(均方根误差)。
ax 复制代码
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import backend as K
#defining the loss function
def my_rmse(y_true, y_pred):
    #difference between true label and predicted label
    error = y_true-y_pred    
    #square of the error
    sqr_error = K.square(error)
    #mean of the square of the error
    mean_sqr_error = K.mean(sqr_error)
    #square root of the mean of the square of the error
    sqrt_mean_sqr_error = K.sqrt(mean_sqr_error)
    #return the error
    return sqrt_mean_sqr_error
#applying the loss function
model.compile (optimizer = 'sgd', loss = my_rmse)

五、创建 Huber 损失

图 2:Huber 损失(绿色)和平方误差损失(蓝色)作为 y --- f(x) 的函数

Huber损失的公式:

这里,

δ是阈值,

a是误差(我们将计算 a ,标签和预测之间的差异)

所以,当**|a| ≤δ,损失= 1/2*(a)²**

当**|a|>δ 时,损失 = δ(|a| --- (1/2)*δ)**

代码:

ax 复制代码
# creating the Conv-Batch Norm block

def conv_bn(x, filters, kernel_size, strides=1):
    
    x = Conv2D(filters=filters, 
               kernel_size = kernel_size, 
               strides=strides, 
               padding = 'same', 
               use_bias = False)(x)
    x = BatchNormalization()(x)
return x

解释:

首先我们定义一个函数 - my huber loss,它接受 y_true 和 y_pred

接下来我们设置阈值 = 1

接下来我们计算误差 a = y_true-y_pred

接下来我们检查误差的绝对值是否小于或等于阈值。is_small_error返回一个布尔值(True 或 False)。

我们知道,当**|a| ≤δ,loss = 1/2*(a)²,** 因此我们将small_error_loss 计算为误差的平方除以2

否则,当**|a| >δ,** 则损失等于 δ(|a| --- (1/2)*δ)。我们在big_error_loss中计算这一点。

最后,在return语句中,我们首先检查is_small_error是true还是false,如果是true,函数返回small_error_loss,否则返回big_error_loss。这是使用 tf.where 完成的。

然后我们可以使用下面的代码编译模型,

ax 复制代码
model.compile(optimizer='sgd', loss=my_huber_loss)

在前面的代码中,我们始终使用阈值1。

但是,如果我们想要调整超参数(阈值)并在编译期间添加新的阈值,该怎么办?然后我们必须使用函数包装,即将损失函数包装在另一个外部函数周围。我们需要一个包装函数,因为默认情况下任何损失函数只能接受 y_true 和 y_pred 值,并且我们不能向原始损失函数添加任何其他参数。

5.1 使用包装函数的 Huber 损失

包装函数代码如下所示:

ax 复制代码
import tensorflow as tf
#wrapper function which accepts the threshold parameter
def my_huber_loss_with_threshold(threshold):
   def my_huber_loss(y_true, y_pred):   
  
       error = y_true - y_pred     
       is_small_error = tf.abs(error) <= threshold     
       small_error_loss = tf.square(error) / 2     
       big_error_loss = threshold * (tf.abs(error) - (0.5 * threshold))
       return tf.where(is_small_error, small_error_loss, big_error_loss)
    return my_huber_loss

在这种情况下,阈值不是硬编码的。相反,我们可以在模型编译期间通过阈值。

ax 复制代码
model.compile(optimizer='sgd', loss=my_huber_loss_with_threshold(threshold=1.5))

5.2 使用类的 Huber 损失 (OOP)

ax 复制代码
import tensorflow as tf
from tensorflow.keras.losses import Loss

class MyHuberLoss(Loss): #inherit parent class
  
    #class attribute
    threshold = 1
  
    #initialize instance attributes
    def __init__(self, threshold):
        super().__init__()
        self.threshold = threshold

    #compute loss
    def call(self, y_true, y_pred):
        error = y_true - y_pred
        is_small_error = tf.abs(error) <= self.threshold
        small_error_loss = tf.square(error) / 2
        big_error_loss = self.threshold * (tf.abs(error) - (0.5 * self.threshold))
        return tf.where(is_small_error, small_error_loss, big_error_loss)

MyHuberLoss 是类名。在类名之后,我们从tensorflow.keras.losses继承父类**'Loss'**。所以MyHuberLoss继承为Loss。这允许我们使用 MyHuberLoss 作为损失函数。

**init**从类中初始化对象。

从类实例化对象时执行的调用函数

init 函数获取阈值,call 函数获取我们之前出售的 y_true 和 y_pred 参数。因此,我们将阈值声明为类变量,这允许我们给它一个初始值。

init 函数中,我们将阈值设置为 self.threshold。

在调用函数中,所有阈值类变量将由 self.threshold 引用。

以下是我们如何在 model.compile 中使用这个损失函数。

ax 复制代码
model.compile(optimizer='sgd', loss=MyHuberLoss(threshold=1.9))

六、创建对比损失(用于暹罗网络):

连体网络比较两个图像是否相似。对比损失是暹罗网络中使用的损失函数。

在上面的公式中,

Y_true 是图像相似度细节的张量。如果图像相似,它们就是 1,如果不相似,它们就是 0。

D 是图像对之间的欧几里德距离的张量。

边距是一个常量,我们可以用它来强制它们之间的最小距离,以便将它们视为相似或不同。

如果Y_true =1,则方程的第一部分变为 D²,第二部分变为零。因此,当 Y_true 接近 1 时,D² 项具有更大的权重。

如果Y_true = 0,则方程的第一部分变为零,第二部分产生一些结果。这为最大项赋予了更大的权重,而为 D 平方项赋予了更少的权重,因此最大项在损失的计算中占主导地位。

使用包装函数的对比损失

ax 复制代码
def contrastive_loss_with_margin(margin):
    def contrastive_loss(y_true, y_pred):
        
        square_pred = K.square(y_pred)
        margin_square = K.square(K.maximum(margin - y_pred, 0))
        return K.mean(y_true * square_pred + (1 - y_true) * margin_square)
    return contrastive_loss

七、结论

Tensorflow 中不可用的任何损失函数都可以使用函数、包装函数或以类似的方式使用类来创建。阿琼·萨卡

相关推荐
奔跑中的小象3 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南3 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang3 小时前
LangGraph 状态机实战
人工智能
刹那芳华19923 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作3 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门4 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜4 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟4 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能
天国梦4 小时前
自习室智能化升级避坑指南:天学网AI智习室方案实测与选型建议
大数据·人工智能
阿里云云原生5 小时前
可用性从 99.9% 跃升至 99.995%:畅捷通如何用 AI 重塑运维底座?
运维·网络·人工智能