如何从keras中的单个自定义损失函数访问所有输出 [英] How to access all outputs from a single custom loss function in keras

查看:76
本文介绍了如何从keras中的单个自定义损失函数访问所有输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试复制此出版物中提出的网络体系结构本教程作为基础使用tensorflow == 2.3.2.继续工作

要训练该网络,他们使用损失,该损失意味着同时来自网络的两个分支的输出,这使我着眼于keras中的自定义损失功能.我知道您可以定义自己的函数,只要该函数的定义如下所示即可:

  def custom_loss(y_true,y_pred): 

我还了解到,您可以给出其他类似的论点:

  def loss_function(margin = 0.3):def custom_loss(y_true,y_pred):#现在您可以使用保证金了 

然后,您仅需在编译模型时调用它们即可.在使用多个输出时,最常见的方法似乎是一种建议的此协作.

  import os将tensorflow导入为tf将keras.backend导入为K从tensorflow.keras导入数据集,层,模型,应用程序,损失从tensorflow.keras.preprocessing导入image_dataset_from_directory_URL ='https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip',origin = _URL,extract = True)PATH = os.path.join(os.path.dirname(path_to_zip),'cats_and_dogs_filtered')train_dir = os.path.join(PATH,'火车')validate_dir = os.path.join(PATH,'validation')BATCH_SIZE = 32IMG_SIZE =(160,160)IMG_SHAPE = IMG_SIZE +(3,)train_dataset = image_dataset_from_directory(train_dir,shuffle = True,batch_size = BATCH_SIZE,image_size = IMG_SIZE)validate_dataset = image_dataset_from_directory(validation_dir,shuffle = True,batch_size = BATCH_SIZE,image_size = IMG_SIZE)data_augmentation = tf.keras.Sequential([layers.experimental.preprocessing.RandomFlip('horizo​​ntal'),layers.experimental.preprocessing.RandomRotation(0.2),])preprocess_input =应用程序.resnet50.preprocess_inputbase_model =应用程序.ResNet50(input_shape = IMG_SHAPE,include_top = False,weights ='imagenet')base_model.trainable =真conv =层.Conv2D(过滤器= 128,kernel_size =(1,1))global_pooling =层数.GlobalAveragePooling2D()horizo​​ntal_pooling = layers.AveragePooling2D(pool_size =(1,5))重塑=层.重塑((-1,128))def custom_loss(y_true,y_pred):打印(y_pred.shape)#做一些涉及两个输出的东西#在此处返回一些琐碎的内容以获取正确的行为返回K.mean(y_pred)输入= tf.keras.Input(形状= IMG_SHAPE)x = data_augmentation(输入)x =预处理输入(x)x = base_model(x,training = True)first_branch = global_pooling(x)second_branch =转化次数(x)second_branch = horizo​​ntal_pooling(second_branch)second_branch =重塑(second_branch)模型= tf.keras.Model(输入,[first_branch,second_branch])base_learning_rate = 0.0001model.compile(optimizer = tf.keras.optimizers.Adam(lr = base_learning_rate),损失= custom_loss,指标= ['准确性'])model.summary()initial_epochs = 10历史= model.fit(train_dataset,epochs = initial_epochs,validate_data = validation_dataset) 

这样做的时候,我认为赋予损失函数的y_pred将是一个包含两个输出的列表.但是,在运行它时,我在stdout中得到的是这样的:

  Epoch 1/10(无,2048)(5、128) 

据此我了解到,损失函数是在每个输出中一个一个地调用的,而不是在所有输出中都调用一次的,这意味着我无法定义在两个输出中都使用两个输出的损失同时.有什么办法可以做到这一点?

如果不清楚,或者您需要更多详细信息,请告诉我.

解决方案

好的,这是一种简单的方法.我们可以通过使用 loss_weights 参数来实现.我们可以权衡完全相同的多个输出,以便获得合并的损失结果.因此,对于两个输出,我们可以做到

  loss_weights = 1 *输出1 + 1 *输出2 

在您的情况下,您的网络有两个输出,名称分别是 reshape global_average_pooling2d .您现在可以执行以下操作

 #计算一个输出的损耗,即整形def reshape_loss(y_true,y_pred):#对这两个进行一些数学运算返回K.mean(y_pred)#计算另一个输出的损失,即global_average_pooling2ddef gap_loss(y_true,y_pred):#对这两个进行一些数学运算返回K.mean(y_pred) 

现在编译时,您需要这样做

  model.compile(Optimizer = tf.keras.optimizers.Adam(lr = base_learning_rate),损失= {'reshape':reshape_loss,'global_average_pooling2d':差距},loss_weights = {'重塑':1.,'global_average_pooling2d':1.}) 

现在,损失 1. * reshape + 1. * global_average_pooling2d 的结果.

I'm trying to reproduce the architecture of the network proposed in this publication in tensorFlow. Being a total beginner to this, I've been using this tutorial as a base to work on, using tensorflow==2.3.2.

To train this network, they use a loss which implies outputs from two branches of the network at the same time, which made me look towards custom losses function in keras. I've got that you can define your own, as long as the definition of the function looks like the following:

def custom_loss(y_true, y_pred):

I also understood that you could give other arguments like so:

def loss_function(margin=0.3):
    def custom_loss(y_true, y_pred):
        # And now you can use margin

You then just have to call these while compiling your model. When it comes to using multiple outputs, the most common approach seem to be the one proposed here, where you would give several losses functions, one being called for each of your output. However, I could not find a solution to give several outputs to a loss function, which is what I need here.

To further explain it, here is a minimal working example showing what I've tried, which you can try for yourself in this collab.

import os
import tensorflow as tf
import keras.backend as K
from tensorflow.keras import datasets, layers, models, applications, losses
from tensorflow.keras.preprocessing import image_dataset_from_directory

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

BATCH_SIZE = 32
IMG_SIZE = (160, 160)
IMG_SHAPE = IMG_SIZE + (3,)

train_dataset = image_dataset_from_directory(train_dir,
                                             shuffle=True,
                                             batch_size=BATCH_SIZE,
                                             image_size=IMG_SIZE)

validation_dataset = image_dataset_from_directory(validation_dir,
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,
                                                  image_size=IMG_SIZE)

data_augmentation = tf.keras.Sequential([
  layers.experimental.preprocessing.RandomFlip('horizontal'),
  layers.experimental.preprocessing.RandomRotation(0.2),
])
preprocess_input = applications.resnet50.preprocess_input
base_model = applications.ResNet50(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')
base_model.trainable = True
conv = layers.Conv2D(filters=128, kernel_size=(1,1))
global_pooling = layers.GlobalAveragePooling2D()
horizontal_pooling = layers.AveragePooling2D(pool_size=(1, 5))
reshape = layers.Reshape((-1, 128))

def custom_loss(y_true, y_pred):
    print(y_pred.shape)
    # Do some stuffs involving both outputs
    # Returning something trivial here for correct behavior
    return K.mean(y_pred)

inputs = tf.keras.Input(shape=IMG_SHAPE)
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=True)

first_branch = global_pooling(x)

second_branch = conv(x)
second_branch = horizontal_pooling(second_branch)
second_branch = reshape(second_branch)

model = tf.keras.Model(inputs, [first_branch, second_branch])
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
              loss=custom_loss,
              metrics=['accuracy'])
model.summary()

initial_epochs = 10
history = model.fit(train_dataset,
                    epochs=initial_epochs,
                    validation_data=validation_dataset)

while doing so, I thought that the y_pred given to loss function would be a list, containing both outputs. However, while running it, what I've got in stdout was this:

Epoch 1/10
(None, 2048)
(None, 5, 128)

What I understand from this is that the loss function is called with every output, one by one, instead of being called once with all the outputs, which means I can't define a loss that would use both the outputs at the same time. Is there any way to achieve this?

Please let me know if I'm unclear, or if you need further details.

解决方案

Ok, here is an easy way to achieve this. We can achieve this by using the loss_weights parameter. We can weigh multiple outputs exactly the same so that we can get the combined loss results. So, for two output we can do

loss_weights = 1*output1 + 1*output2

In your case, your network has two outputs, by the name they are reshape, and global_average_pooling2d. You can do now as follows

# calculation of loss for one output, i.e. reshape
def reshape_loss(y_true, y_pred):
    # do some math with these two 
    return K.mean(y_pred)

# calculation of loss for another output, i.e. global_average_pooling2d
def gap_loss(y_true, y_pred):
    # do some math with these two 
    return K.mean(y_pred)

And while compiling now you need to do as this

model.compile(
    optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate), 
    loss = {
         'reshape':reshape_loss, 
         'global_average_pooling2d':gap_loss
      },
    loss_weights = {
        'reshape':1., 
        'global_average_pooling2d':1.
     }
    )

Now, the loss is the result of 1.*reshape + 1.*global_average_pooling2d.

这篇关于如何从keras中的单个自定义损失函数访问所有输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆