tf.keras快速入门——自定义损失函数(一)

本文介绍了如何在Keras中实现自定义损失函数,并通过鸢尾花分类案例演示了简单的均方误差损失函数及如何使用TensorFlow内置的交叉熵损失函数。此外,还探讨了通过继承`tf.keras.losses.Loss`类来定义更复杂的损失函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

官网的自定义损失函数:here
在官网中,我们可以看见给出了两种方法来使用Keras提供自定义损失。

1. 简单的均方误差

def custom_mean_squared_error(y_true, y_pred):
    return tf.math.reduce_mean(tf.square(y_true - y_pred))
    
model.compile(optimizer=keras.optimizers.Adam(), loss=custom_mean_squared_error)

这种情况下,y_truey_pred的值会自动传入该损失函数中,且仅有实际输出、预测输出的两个默认参数,所以这种方式仅仅适合于简单的损失函数。如果您需要一个使用除y_truey_pred之外的其他参数的损失函数,则可以将tf.keras.losses.Loss类子类化。

1.1 简单案例

这里还是以鸢尾花分类为例,我们简单的使用序列模型来定义所需要的模型,然后在compile中指定我们自定义的损失函数。由于我们在序列模型中使用的损失函数是sparse_categorical_crossentropy,这里先简单记录下这两个的区别:

1.1.1 sparse_categorical_crossentropy & categorical_crossentropy
  • 如果yone-hot encoding格式(即独热编码的向量格式),使用sparse_categorical_crossentropy
  • 如果y是整数,非one-hot encoding格式,使用categorical_crossentropy

简单抄下,在鸢尾花分类中的代码,即:

from sklearn.datasets import load_iris
x_data = load_iris().data  # 特征,【花萼长度,花萼宽度,花瓣长度,花瓣宽度】
y_data = load_iris().target # 分类

import tensorflow as tf
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(4, input_shape=(4,), activation='relu'))
model.add(tf.keras.layers.Dense(3, input_shape=(4,), activation='softmax'))
model.compile(
    optimizer="adam", 
    loss="sparse_categorical_crossentropy",  # 三分类的结果,已经需要使用独热码来表示。故而不能使用categorical_crossentropy
    metrics=['accuracy']
)
model.fit(x_data, y_data, epochs=100)

由于鸢尾花是三种类别,也就是三分类的结果。而不是非01的结果,故而这里需要使用sparse_categorical_crossentropy。但其本质都是交叉熵。我们都知道交叉熵的公式可以表示为:
H ( p , q ) = − ∑ i y ( x i ) l o g ( y ^ ( x i ) ) H(p, q) = -\sum_i y(x_i) log(\hat y(x_i)) H(p,q)=iy(xi)log(y^(xi))
y ^ ( x i ) \hat y(x_i) y^(xi)表示 x i x_i xi的预测分布,而 y ( x i ) y(x_i) y(xi)表示在训练数据中的类别的概率分布。
但是,由于如果需要使用交叉熵,我需要获取到最终的类别的下标值,而对于y_truey_pred这两个均是tf.Tensor类型的对象,却没办法获取到其对应的值,故而这里宣告失败了。转而,还是使用平方损失函数来解决:

from sklearn.datasets import load_iris
x_data = load_iris().data  # 特征,【花萼长度,花萼宽度,花瓣长度,花瓣宽度】
y_data = load_iris().target # 分类
# 转化为独热编码
y_data_one_hot = tf.one_hot(y_data, depth=3) # 3分类

import tensorflow as tf
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(4, input_shape=(4,), activation='relu'))
model.add(tf.keras.layers.Dense(3, input_shape=(4,), activation='softmax'))


def custom_mean_squared_error(y_true, y_pred):
    return tf.math.reduce_mean(tf.square(y_true - y_pred))


model.compile(
    optimizer="adam", 
    loss=custom_mean_squared_error,
    metrics=['acc']
)
history = model.fit(x_data, y_data_one_hot, epochs=300)


for key in history.history.keys():
    plt.plot(history.epoch, history.history[key])

在这里插入图片描述
当然,还又另一种方式,也就是直接调用tensorflow提供的交叉熵损失函数,即:

# 转化为独热编码
y_data_one_hot = tf.one_hot(y_data, depth=3) # 3分类

import tensorflow as tf
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(4, input_shape=(4,), activation='relu'))
model.add(tf.keras.layers.Dense(3, input_shape=(4,), activation='softmax'))


def custom_mean_squared_error(y_true, y_pred):
    return tf.losses.categorical_crossentropy(y_true, y_pred)


model.compile(
    optimizer="adam", 
    loss=custom_mean_squared_error,
    metrics=['acc']
)
history = model.fit(x_data, y_data_one_hot, epochs=300)

or

from sklearn.datasets import load_iris
x_data = load_iris().data  # 特征,【花萼长度,花萼宽度,花瓣长度,花瓣宽度】
y_data = load_iris().target # 分类
# 转化为独热编码
#y_data_one_hot = tf.one_hot(y_data, depth=3) # 3分类

import tensorflow as tf
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(4, input_shape=(4,), activation='relu'))
model.add(tf.keras.layers.Dense(3, input_shape=(4,), activation='softmax'))


def custom_mean_squared_error(y_true, y_pred):
    return tf.losses.sparse_categorical_crossentropy(y_true, y_pred)


model.compile(
    optimizer="adam", 
    loss=custom_mean_squared_error,
    metrics=['acc']
)
history = model.fit(x_data, y_data, epochs=300)


for key in history.history.keys():
    plt.plot(history.epoch, history.history[key])

2. 实例化Loss类

类似的,该类需要继承自tf.keras.losses.Loss,然后需要复写下面的两个方法:

  • __init__(self):接受要在调用损失函数期间传递的参数;
  • call(self, y_true, y_pred):使用目标 y_true 和模型预测 y_pred 来计算模型的损失;

如,官网案例:mse, 存在一个会抑制预测值远离 0.5

class CustomMSE(keras.losses.Loss):
    def __init__(self, regularization_factor=0.1, name="custom_mse"):
        super().__init__(name=name)
        self.regularization_factor = regularization_factor

    def call(self, y_true, y_pred):
        mse = tf.math.reduce_mean(tf.square(y_true - y_pred))
        reg = tf.math.reduce_mean(tf.square(0.5 - y_pred))
        return mse + reg * self.regularization_factor
        
model.compile(optimizer=keras.optimizers.Adam(), loss=CustomMSE())

由于比较简单,就不再套鸢尾花分类案例了。但从上面的官网的案例中也可以看见一个问题就是:
这里的简单的实例化子类来定义损失函数,其实和上面的直接定义一个function来作为其损失函数类似。因为传入call中的参数还是只有y_truey_pred两个值,然后需要在上面做文章,也是比较困难的。
在下篇中,将讨论一个更加灵活的自定义损失函数的方式,其实在前面的博客中,也提及过,即:复杂度&学习率&损失函数,如:

import tensorflow as tf
x = tf.random.normal([20, 2], mean=2, stddev=1, dtype=tf.float32)
y = [item1 + 2 * item2 for item1, item2 in x]
w = tf.Variable(tf.random.normal([2, 1], mean=0, stddev=1))

epoch = 5000
lr = 0.002
for epoch in range(epoch):
    with tf.GradientTape() as tape:
        y_hat = tf.matmul(x, w)
        loss = tf.reduce_mean(tf.where(tf.greater(y_hat, y), 3*(y_hat - y), y-y_hat))
    w_grad = tape.gradient(loss, w)
    w.assign_sub(lr * w_grad)

print(w.numpy().T) # [[0.73728406 0.83368826]]
### TensorFlow 初学者命令教程 对于希望快速上手 TensorFlow 的用户来说,掌握些基础命令至关重要。下面是些基本操作和概念介绍。 #### 导入 TensorFlow 库 为了使用 TensorFlow 提供的功能,在 Python 脚本或交互环境中首先要导入该库: ```python import tensorflow as tf ``` #### 创建常量 可以利用 `tf.constant` 函数来定义不可变的数据结构——张量(tensor),这类似于其他编程语言中的变量声明方式: ```python hello_world = tf.constant('Hello, TensorFlow!') print(hello_world.numpy()) # 输出字符串内容 ``` #### 构建简单模型 构建神经网络通常涉及层(layer)的概念。通过堆叠多个层形成更复杂的架构。这里展示了个简单的线性回归模型实例化过程: ```python class MyModel(tf.keras.Model): def __init__(self): super(MyModel, self).__init__() self.dense_layer = tf.keras.layers.Dense(units=1) def call(self, inputs): return self.dense_layer(inputs) model = MyModel() loss_object = tf.keras.losses.MeanSquaredError() # 定义损失函数 optimizer = tf.keras.optimizers.Adam() # 配置优化器算法 ``` #### 执行训练循环 编写自定义训练循环允许更加灵活地控制整个流程。此部分展示了如何迭代数据集并更新参数以最小化误差: ```python for epoch in range(num_epochs): # 设定轮次数量 for features, labels in dataset: # 获取批次样本及其标签 with tf.GradientTape() as tape: # 记录前向传播过程中计算梯度所需的信息 predictions = model(features) # 前向传递得到预测值 loss = loss_object(labels, predictions)# 根据真实值与预测值得到当前batch的平均损失 gradients = tape.gradient(loss, model.trainable_variables) # 自动求导获得权重变化方向 optimizer.apply_gradients(zip(gradients, model.trainable_variables)) # 更新权值 print(f'Epoch {epoch}, Loss: ', loss.numpy()) ``` 上述代码片段提供了关于 TensorFlow 使用入门级别的指导[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦否

文章对你有用?不妨打赏一毛两毛

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值