在机器学习领域,经常需要根据特定的业务需求或研究目的,自定义损失函数。本文将指导如何在TensorFlow中编写自定义损失函数,特别是分类交叉熵损失函数,并将其与TensorFlow内置的分类交叉熵损失函数进行比较。
机器学习的目标是模拟人类的学习过程。与人类一样,机器也从过去的错误中学习。损失函数用于评估机器学习的质量,它显示了机器学习模型如何从给定的特征集中预测结果。构建任何机器学习模型的目的是尽可能准确地预测概率目标值。因此,损失函数可以衡量预测值与实际值之间的差距。损失函数不是固定的,会根据任务而变化。优化过程的目标是最小化损失函数。
TensorFlow是一个广泛使用的基于Python的机器学习平台,它提供了许多内置和优化的损失函数,用于开发机器学习模型。一些常用的回归任务损失函数包括均方误差(MSE)、平均绝对误差(MAE)、平均绝对百分比误差(MAPE)等。分类任务中的常见损失函数有二元交叉熵和分类交叉熵等。
当需要预测两个或更多目标类别(多类分类)时,使用分类交叉熵损失。在这里,模型被训练从多个类别中预测一个类别。如果考虑实际目标值是y,预测值是p,并且有C个类别,那么分类交叉熵(CE)损失可以定义为:
CE = -∑(y_true * log(y_pred))
在计算交叉熵损失之前,通常会对分数应用激活函数(Sigmoid/Softmax)。分类交叉熵损失是二元交叉熵损失的泛化版本。在二元交叉熵损失中,C = 2,因为只有两个类别。因此,损失变为:
BCE = -[y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred)]
在TensorFlow中,将使用自定义代码编写分类交叉熵损失函数,并将其结果与TensorFlow内置的分类交叉熵函数进行比较。模型训练后,会产生目标变量的预测值。对于损失函数,需要模型的实际值和预测值来进行比较和计算损失值。在TensorFlow中,将编写一个自定义损失函数,它将接受实际值和预测值作为输入。这个自定义损失函数将继承Keras的“损失”基类。为了获得最佳性能,需要编写函数的向量化实现。还将使用基本的TensorFlow函数,以从TensorFlow的图形特性中受益。
class Custom_CE_Loss(tf.keras.losses.Loss):
def __init__(self):
super().__init__()
def call(self, y_true, y_pred):
log_y_pred = tf.math.log(y_pred)
elements = -tf.math.multiply_no_nan(x=log_y_pred, y=y_true)
return tf.reduce_mean(tf.reduce_sum(elements,axis=1))
在这里,可以看到Custom_CE_Loss函数是从基类“Loss”继承而来的。重写了call方法,它接受真实值和预测值作为输入。还使用了TensorFlow的内置数学函数来计算对数、乘法、求和、平均值等。
接下来,将这个自定义损失函数应用于一个多类图像分类问题。将使用TensorFlow的预训练VGG16模型来对CIFAR-10图像进行分类。CIFAR-10数据集包含60000张32×32彩色图像,分为10个类别,每个类别有6000张图像。有50000张训练图像和10000张测试图像。
import tensorflow as tf
from tensorflow.keras import datasets
from keras.layers import Dense, Dropout, Flatten
from keras.models import Model
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()
# 归一化像素值
train_images, test_images = train_images / 255.0, test_images / 255.0
# 将目标类别编码为one-hot并使其变为浮点数
train_labels = tf.keras.utils.to_categorical(train_labels)
test_labels = tf.keras.utils.to_categorical(test_labels)
train_labels = tf.convert_to_tensor(train_labels, dtype=tf.float32)
test_labels = tf.convert_to_tensor(test_labels, dtype=tf.float32)
base_model = tf.keras.applications.vgg16.VGG16(input_shape = (32, 32, 3),
include_top = False,
weights = 'imagenet')
base_model.trainable=False
model = base_model.output
model = Flatten()(model)
model = Dense(4096, activation='relu')(model)
model = Dropout(rate=0.5)(model)
model = Dense(4096, activation='relu')(model)
model = Dropout(rate=0.5)(model)
model = Dense(10, activation='softmax')(model)
model = Model(inputs=base_model.inputs, outputs=model)
model.summary()
model.compile(optimizer='adam',
loss=Custom_CE_Loss(),
metrics=['accuracy'])
history = model.fit(train_images, train_labels, epochs=10,
validation_data=(test_images, test_labels))
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)