Keras自定义Loss函数

时间:2024-10-20 08:22:52

###前言
Keras本身提供了很多常用的loss函数(即目标函数),但这些损失函数都是比较基本的、通用的。有时候我们需要根据自己所做的任务来自定义损失函数,虽然Keras是一个很高级的封装,自定义loss还是比较简单的。这里记录一下自定义loss的方法,一为助记、二为助人。
###官方定义的损失函数
####第一种方式:自定义一个函数
自定义loss函数之前,我们可以看看Keras官方是如何定义loss的,进入keras/keras/文件,我们可以看到很多Keras自带loss的实现代码。比如最简单的均方误差损失函数:

def mean_squared_error(y_true, y_pred):
    return ((y_pred - y_true), axis=-1)

其中y_true为网络给出的预测值,y_true即是标签,两者均为tensor。在loss中直接操作这两个变量即可实现自己想要的loss。例如,我们将其改为四次方的平均值来作为新的loss:

def mean_squared_error2(y_true, y_pred):
	return (((y_pred-y_true)),axis=-1)

在model编译阶段将loss指定为我们自定义的函数:

(optimizer='rmsprop',loss=mean_squared_error2)

####实践
keras/examples/mnist_cnn.py为例,按照第一种方式修改loss。将原本的交叉熵损失函数进行改进,新的损失函数为:
l o s s = − ( 1 − ε ) l o g ( e z 1 / Z ) − ε ∑ i = 1 3 l o g ( e z i / Z ) , Z = e z 1 + e z 2 + e z 3 loss=−(1−ε)log(e^{z1}/Z)−ε∑i=\frac{1}{3}log(e^{zi}/Z),Z=e^{z1}+e^{z2}+e^{z3} loss=(1ε)log(ez1/Z)εi=31log(ezi/Z),Z=ez1+ez2+ez3

新的损失函数定义详见这篇blog

对应的keras代码为:

#custom loss
def mycrossentropy(y_true, y_pred, e=0.1):
    return (1-e)*K.categorical_crossentropy(y_pred,y_true) + e*K.categorical_crossentropy(y_pred, K.ones_like(y_pred)/num_classes)

(loss=mycrossentropy,
              optimizer=(),
              metrics=['accuracy'])

两种loss对应的准确率曲线如下:(5 epochs)
这里写图片描述
其中虚线表示损失,实线表示acc,发现新的损失函数并没有原有的交叉熵损失函数效果好(其实都很好,只是差一点),可能是因为mnist数据太过简单了,体现不出新的损失函数的优点。这些说明我们要根据自己的实际任务来设计loss,不同的loss函数有不同的适用范围。
####第二种方式:自定义一个层次
在Keras自带的examples中又发现了另外一种定义loss函数的例子,该例子将新的损失函数定义为一个层次来使用。在keras/examples/variational_autoencoder.py文件中:

# Custom loss layer
class CustomVariationalLayer(Layer):
    def __init__(self, **kwargs):
        self.is_placeholder = True
        super(CustomVariationalLayer, self).__init__(**kwargs)

    def vae_loss(self, x, x_decoded_mean):
        xent_loss = original_dim * metrics.binary_crossentropy(x, x_decoded_mean)#Square Loss
        kl_loss = - 0.5 * (1 + z_log_var - (z_mean) - (z_log_var), axis=-1)# KL-Divergence Loss
        return (xent_loss + kl_loss)

    def call(self, inputs):
        x = inputs[0]
        x_decoded_mean = inputs[1]
        loss = self.vae_loss(x, x_decoded_mean)
        self.add_loss(loss, inputs=inputs)
        # We won't actually use the output.
        return x

新的loss包含均方差和KL散度,该官方代码的解析可以跳转到另一篇bolg:Keras官方示例代码解释(1):variational autoencoder
该自定义层次的使用如下:

y = CustomVariationalLayer()([x, x_decoded_mean])
vae = Model(x, y)
(optimizer='rmsprop', loss=None)

第一句代码的x为输入数据,x_decoded_mean为网络的输出数据(除开loss层的最后一层)。在compile函数中指定’loss’为None,表示loss已经作为一个层次在网络中了,不需要在这里指定loss。

有关自定义层可以参考官方中文文档中的编写自己的层
####实践
variational_autoencoder.py的基础上,对比loss1=均方差loss2=均方差+KL散度(即自定义的loss层)。实验结果如下:(10 epochs)
这里写图片描述
可以看出,使用loss2得到的重建结果要好于loss1的情况。
###后记
自定义loss函数或者说自定义层是将CNN用于特定任务的第一步,与君共勉。

参考资料:
keras 自定义 loss损失函数, sample在loss上的加权 和 metric
Keras中自定义复杂的loss函数
TensorFlow四种Cross Entropy算法实现和应用
如何利用Keras的扩展性