深度学习 - 16.TF x Keras Losses 常见损失函数

本文介绍了Keras中常用的损失函数,包括MAE、MSE、RMSE等,并通过实例展示了不同损失函数的特点及适用场景。

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

一.引言:

之前提到过 自定义Loss与metrics,下面盘点下 Keras中常用的损失函数,当然也是日常生产开发中常用的损失函数供今后自定义损失函数使用。损失函数虽然有很多形式,但万变不离其宗,损失函数都是将随机事件例如回归,分类等问题的目标抽象为非负的实数从而衡量该事件的损失或风险,从而矫正模型,希望模型朝着损失最小的方向进行,但是这里只是大多数情况,例如一些图像结合算法中,也会最大化损失使得图像的表达更加多元化,抽象化。

二.常见损失函数

1. MAE 平均绝对误差 (mean-absolute error)

MAE(pre,y) = \frac{1}{n}\sum_{i=1}^{n}\left | pre^{(i)}-y^{(i)} \right |

pre为预测值,y为真实值,MAE为预测值与真实值差的绝对值之和

# Mean Ablsolutely Error
def getMaeLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.mean_absolute_error(label, predict))
    return loss

模拟100个样本进行 Loss 计算,下述如无特殊声明默认使用该样本进行 Loss 计算。 

    numSamples = 100
    predict = np.random.randint(0, 2, size=(numSamples,)).astype('float32')
    label = np.random.randint(0, 2, size=(numSamples,)).astype('float32')

    maeLoss = getMaeLoss(predict, label).numpy()
    print("Mae Loss:", maeLoss)

2. MSE 均方误差 (mean-square error)

\fn_cm MSE(pre,y) = \frac{1}{n}\sum_{i=1}^{n}(pre^{(i)}-y^{(i)} )^2

pre为预测值,y为真实值,MSE为预测值与真实值差的平方之和,与MAE相同,不论 pre 比 y 高还是低于同一个数,计算得到的结果都是相同的。

# Mean Squared Error
def getMseLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.mean_squared_error(label, predict))
    return loss

3. RMSE 均方根误差 (root-mean-square error)

RMSE(pre,y) = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(pre^{(i)}-y^{(i)} )^2}

RMSE 其实和 MSE 表征大小没有区别,只不过是类似于方差和标准差的区别,如果预测目标为万元,则 MSE 的单位是万*万,而 RMSE 则是万,这里类似做一个标准化的过程。

# Root Mean Squared Error
def getRmseLoss(predict, label):
    loss = tf.sqrt(tf.reduce_mean(tf.losses.mean_squared_error(label, predict)))
    return loss

4. MAPE 平均绝对百分比误 (mean_absolute_percentage_error)

MAPE(pre,y) = \frac{1}{n}\sum_{i=1}^{n}\left \| \frac{y^{(i)}-pre^{(i)}}{y^{(i)}} \right \| \cdot 100%

MAPE 为0%表示完美模型,MAPE 大于 100 %则表示劣质模型。这里需要注意分母为0的情况。

# mean_absolute_percentage_error
def getMapeLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.mean_absolute_percentage_error(label, predict))
    return loss

5. MSLE 均方对数误差 (mean_squared_logarithmic_error)

\fn_cm MSLE(pre,y) = \frac{1}{n}\sum_{i=1}^{n}(log(y^{(i)}+1)-log(pre^{(i)}+1))^2

# Mean Squared Logarithmic Error (MSLE)
def getMsleLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.mean_squared_logarithmic_error(label, predict))
    return loss

(1) 可以看出来在均方根误差相同的情况下,预测值比真实值小这种情况的错误比较大,即对于预测值小这种情况惩罚较大。

(2) 当数据当中有少量的值和真实值差值较大的时候,使用log函数能够减少这些值对于整体误差的影响。

y = np.array([2.,3.,4.,5.,6.])
predict = y + 2
print("预测值大于真实值:",getMsleLoss(predict, y))
predict = y - 2
print("预测值小于真实值:",getMsleLoss(predict, y))
预测值大于真实值: tf.Tensor(0.13689565089565417, shape=(), dtype=float64)
预测值小于真实值: tf.Tensor(0.44519201856286134, shape=(), dtype=float64)
 

6. Cosine Similarity 余弦相似度

similarity=cos(\theta)=\frac{pre \cdot y}{\left \| pre \right \|\left \| y \right \|}=\frac{\sum_{i=1}^{n} pre_i \cdot y_i}{\sqrt{\sum_{i=1}^{n}(pre_i)^2}x\sum_{i=1}^{n}(y_i)^2}

余弦相似度一般通过夹角考虑两个向量在空间的相似度,比如常见的 User-embedding,Item-embedding 相关的相似性计算。

# Cosine Loss
def getCosLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.cosine_similarity(label, predict))
    return loss

7. Binary Crossentropy 二值交叉熵

BinaryCrossentropy= -\frac{1}{n}\sum_{i=1}^{n} y_i \cdot log^{pre_i} + (i-y_i) \cdot log^{1-pre_i}

用于二值分类,IMDB 情感分析,性别预测等等都会用到。

# Binary Loss
def getBinaryLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.binary_crossentropy(label, predict))
    return loss
    predict = utils.to_categorical(np.random.randint(0, 2, size=(numSamples,)).astype('int32'), num_classes=2)
    label = utils.to_categorical(np.random.randint(0, 2, size=(numSamples,)).astype('int32'), num_classes=2)

    binaryLoss = getBinaryLoss(predict, label).numpy()
    print("Binary Loss: ", binaryLoss)

8. Category Crossentropy 交叉熵

CategoryCrossentropy=-\sum_{i=1}^{n}y_i \cdot log^{pre_i}

用于多类别分析,最常见的手写数字识别就用了交叉熵损失函数,常与 softmax 函数结合使用。

# Category Loss
def getCategoryLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.categorical_crossentropy(label, predict))
    return loss
    predict = utils.to_categorical(np.random.randint(0, 10, size=(numSamples,)).astype('int32'), num_classes=10)
    label = utils.to_categorical(np.random.randint(0, 10, size=(numSamples,)).astype('int32'), num_classes=10)

    categoryLoss = getCategoryLoss(predict, label).numpy()
    print("Category Loss: ", categoryLoss)

9. Kullback-Leibler divergence KL散度

\fn_cm D_{KL} (P||Q)= \sum_{i=1}^{n}p(x_i)logp(x_i)-p(x_i)logq(x_i)

这里信息熵定义为:

H = -\sum_{i=1}^{n}p(x)logp(x)

K-L散度在统计学中用于衡量两个分步的相似程度,这里P,Q可以看做是真实值与预测值。

# KL Loss
def getKLLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.kullback_leibler_divergence(label, predict))
    return loss

10.Hinge Loss 合页损失 

Hinge = max(0, (1-y) \cdot pre)

Hinge Loss 又叫合页损失,因为它的函数图像和门的合页特别像,最常见的应用场景是 SVM 支持向量机。

# Hinge Loss
def getHingeLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.hinge(label, predict))
    return loss
    predict = np.where(predict < 1, -1, 1).astype('float32')
    label = np.where(label < 1, -1, 1).astype('float32')
    hingeLoss = getHingeLoss(predict, label).numpy()
    print("Hinge Loss: ", hingeLoss)

11.Possion Loss 泊松损失

Possion = pre - y * log(pre)

参考官方 API 这里为了防止 pre 出现为0的情况,所以后面改写为 y * log(pre + epsilon()) ,其中 epsilon() =  1e-07 

# Possion Loss
def getPossionLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.poisson(label, predict))
    return loss

12.Huber Loss 平滑平均绝对误差

Huber = \begin{cases} & \frac{1}{2} (y - pre)^2 \rightarrow |y-pre|\leq \delta \\ & \delta |y - pre| - \frac{1}{2}\delta ^2 \rightarrow other \end{cases}

Huber 顾名思义平滑绝对误差,其构造为分段函数,它介于 MAE 与 MSE 之间,平滑程度取决于 δ 的取值范围,当Huber损失在 [0-δ,0+δ] 区间内,等价为MSE,而在[-∞,δ]和[δ,+∞]时为MAE。所以 Huber 对数据中的异常点没有平方误差敏感。

# Huber Loss
def getHuberLoss(predict, label):
    loss = tf.reduce_mean(tf.losses.huber(label, predict))
    return loss

更多推荐算法相关深度学习:深度学习导读专栏 

<think>我们讨论的是如何在TensorFlow/Keras中使用Huber损失函数,并使其输出保留8位小数。注意,损失函数本身计算的是浮点数,我们关心的是如何显示或保存这些损失值到小数点后8位。 根据之前的讨论,我们知道: 1. TensorFlow的浮点数通常是float32,其有效数字约为6-7位,所以显示8位小数时最后一位可能不准确。 2. 我们可以通过格式化输出来控制显示的小数位数。 对于Huber损失函数,我们通常是在模型编译时指定,然后在训练过程中通过回调函数或手动记录损失值,并控制其显示精度。 具体步骤如下: 步骤1:在模型编译时使用Huber损失 ```python import tensorflow as tf model = tf.keras.models.Sequential([...]) # 构建模型 model.compile(optimizer='adam', loss=tf.keras.losses.Huber()) ``` 步骤2:在训练过程中,我们可以通过自定义回调函数来记录损失值并以指定格式输出(包括保留8位小数) 例如,我们可以使用`LambdaCallback`在每批次或每周期结束时打印损失值: ```python from tensorflow.keras.callbacks import LambdaCallback # 自定义回调,在每个epoch结束后打印损失值(保留8位小数) print_callback = LambdaCallback( on_epoch_end=lambda epoch, logs: print(f'Epoch {epoch}: loss = {logs["loss"]:.8f}') ) model.fit(X_train, y_train, epochs=10, callbacks=[print_callback]) ``` 但是,注意:`model.fit`默认会输出进度条和损失值,但默认显示的小数位数较少(通常是4位)。我们可以通过设置`verbose=0`来关闭默认输出,然后使用回调函数来自定义输出格式。 另一种方法是使用`CSVLogger`回调将训练日志保存到CSV文件,然后我们可以在保存时控制小数位数(但是CSVLogger默认使用`str(value)`写入,可能不会保留8位小数)。因此,我们可以自定义一个回调函数来写入CSV文件并控制小数位数。 自定义回调函数示例(将每个epoch的损失值以8位小数保存到CSV文件): ```python import csv import os class CustomCSVLogger(tf.keras.callbacks.Callback): def __init__(self, filename): super().__init__() self.filename = filename self.file = None self.writer = None def on_train_begin(self, logs=None): # 打开文件并写入表头 self.file = open(self.filename, 'w', newline='') self.writer = csv.writer(self.file) # 写入表头,这里假设每个epoch输出loss和val_loss(如果有验证集) headers = ['epoch'] + list(logs.keys()) self.writer.writerow(headers) def on_epoch_end(self, epoch, logs=None): logs = logs or {} # 将每个值格式化为8位小数(注意:非数值直接写入,数值则格式化) row = [epoch] + [f'{v:.8f}' if isinstance(v, float) else v for v in logs.values()] self.writer.writerow(row) def on_train_end(self, logs=None): self.file.close() # 使用回调 model.fit(X_train, y_train, epochs=10, callbacks=[CustomCSVLogger('training_log.csv')]) ``` 但是,请注意:损失值本身是浮点数,我们将其格式化为字符串保留8位小数。这样在CSV文件中就会看到8位小数的损失值。 步骤3:如果希望同时打印到控制台并保留8位小数,可以在回调中同时打印: ```python class CustomLogger(tf.keras.callbacks.Callback): def on_epoch_end(self, epoch, logs=None): logs = logs or {} # 打印每个指标,保留8位小数 print(f'Epoch {epoch}: ' + ', '.join([f'{k}: {v:.8f}' for k, v in logs.items()])) ``` 然后,在`model.fit`中使用这个回调。 注意事项: - 由于浮点数的精度限制(float32),显示8位小数时,后面的位数可能并不准确,但这是显示格式的要求。 - 如果希望更高的计算精度,可以在模型中使用float64,但会消耗更多内存和计算资源。 关于在Huber损失函数中直接设置精度?实际上,损失函数本身计算的就是浮点数,我们无法在损失函数内部设置输出的小数位数。控制显示的小数位数是在输出或记录时进行的。 因此,总结:在Huber损失函数中,我们无法直接设置其输出保留8位小数,但可以通过控制输出格式(如打印、写入文件)来达到显示8位小数的目的。 相关问题: 1. 如何在TensorFlow中使用float64精度进行训练? 2. Huber损失函数的参数delta如何调整? 3. 如何将训练过程中的损失值以高精度保存到文件? 4. 在TensorBoard中如何显示高精度的损失曲线?</think>在 TensorFlow/Keras 中使用 `tf.keras.losses.Huber` 损失函数并保留 8 位小数,需要通过以下方法控制输出格式: ### 解决方案 1. **自定义回调函数(推荐方法)** ```python import tensorflow as tf import numpy as np class PrecisionLogger(tf.keras.callbacks.Callback): def on_epoch_end(self, epoch, logs=None): logs = logs or {} # 格式化所有浮点数值保留8位小数 formatted_logs = {k: f"{v:.8f}" if isinstance(v, float) else v for k, v in logs.items()} print(f'Epoch {epoch+1}: ' + ', '.join([f'{k} = {v}' for k, v in formatted_logs.items()])) # 模型编译时使用Huber损失 model.compile(optimizer='adam', loss=tf.keras.losses.Huber(delta=1.0)) # delta为Huber损失参数 # 训练时添加回调 model.fit(X_train, y_train, epochs=10, callbacks=[PrecisionLogger()], verbose=0) ``` 2. **全局设置 NumPy 打印选项** ```python import numpy as np np.set_printoptions(precision=8, suppress=True) # 全局设置8位小数 # 使用Huber损失编译模型 model.compile(optimizer='adam', loss=tf.keras.losses.Huber()) # 训练并手动输出日志 history = model.fit(X_train, y_train, epochs=10, verbose=0) for i, loss_val in enumerate(history.history['loss']): print(f'Epoch {i+1}: loss = {loss_val:.8f}') ``` 3. **CSV 日志记录(持久化保存)** ```python import csv # 自定义CSV回调(保留8位小数) class CSVEightDecimal(tf.keras.callbacks.CSVLogger): def on_epoch_end(self, epoch, logs=None): logs = logs or {} # 格式化所有浮点数值 logs = {k: f"{v:.8f}" if isinstance(v, float) else v for k, v in logs.items()} super().on_epoch_end(epoch, logs) # 使用回调 model.fit(X_train, y_train, epochs=10, callbacks=[CSVEightDecimal('training_log.csv')]) ``` ### Huber 损失函数关键参数 ```python tf.keras.losses.Huber( delta=1.0, # 控制二次损失与线性损失的转换点 reduction=tf.keras.losses.Reduction.AUTO, # 损失归约方式 name='huber_loss' ) ``` ### 输出示例 ``` Epoch 1: loss = 1.23456789, val_loss = 1.12345678 Epoch 2: loss = 0.98765432, val_loss = 0.87654321 ... Epoch 10: loss = 0.01234567, val_loss = 0.02345678 ``` ### 注意事项 1. **精度限制** TensorFlow 默认使用 float32(约6-7位有效数字),如需更高精度: ```python tf.keras.backend.set_floatx('float64') # 使用64位浮点数 ``` 2. **Huber 损失特性** - `delta=1.0` 时等价于平滑 L1 损失 - 对异常值比 MSE 更鲁棒 - 计算公式: $$ L_{\delta}(y, \hat{y}) = \begin{cases} \frac{1}{2}(y - \hat{y})^2 & \text{for } |y - \hat{y}| \leq \delta \\ \delta |y - \hat{y}| - \frac{1}{2}\delta^2 & \text{otherwise} \end{cases} $$ 3. **TensorBoard 集成** ```python tensorboard_cb = tf.keras.callbacks.TensorBoard( update_freq='epoch', profile_batch=0 # 禁用性能分析 ) model.fit(..., callbacks=[tensorboard_cb, PrecisionLogger()]) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BIT_666

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值