每个训练实例的损失值获取 - Keras

11

我希望能够在模型训练时获取每个实例的损失值。

history = model.fit(..)

例如以上的代码返回了每个epoch的损失值而不是mini batch或instance的值。

如何最好地解决这个问题?有什么建议吗?

4个回答

15

在官方 Keras 文档页面的末尾,您会找到正是您所寻找的内容:https://keras.io/callbacks/#callback

以下是创建自定义回调的代码:

class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = []

    def on_batch_end(self, batch, logs={}):
        self.losses.append(logs.get('loss'))

model = Sequential()
model.add(Dense(10, input_dim=784, kernel_initializer='uniform'))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

history = LossHistory()
model.fit(x_train, y_train, batch_size=128, epochs=20, verbose=0, callbacks=[history])

print(history.losses)
# outputs
'''
[0.66047596406559383, 0.3547245744908703, ..., 0.25953155204159617, 0.25901699725311789]
'''

3

如果您想获取每个批次的损失值,您可能需要在生成器内调用model.train_on_batch。不知道您的数据集是什么样子,很难提供完整的示例,但您需要将数据集分成批次,并逐个馈送。

def make_batches(...):
    ...

batches = make_batches(...)
batch_losses = [model.train_on_batch(x, y) for x, y in batches]

单个实例的情况会稍微复杂一些。当然,你可以训练1大小的批次,但这很可能会破坏你的优化器(通过最大化梯度方差),并且会显著降低性能。此外,由于损失函数是在Python的域外评估的,因此没有直接的方法可以在不与C/C++和CUDA源代码打交道的情况下劫持计算。即使这样做,后端本身也会按批次评估损失(受益于高度向量化的矩阵操作),因此强制它对每个实例进行损失评估将严重降低性能。简而言之,黑客攻击后端只会(可能)帮助你减少GPU内存传输(与从Python接口训练1大小的批次相比)。如果你真的想得到每个实例的分数,我建议你在批次上进行训练,并在实例上进行评估(这样你就可以避免高方差问题并减少昂贵的梯度计算,因为梯度只在训练期间估计):
def make_batches(batchsize, x, y):
    ...


batchsize = n
batches = make_batches(n, ...)
batch_instances = [make_batches(1, x, y) for x, y in batches]
losses = [
    (model.train_on_batch(x, y), [model.test_on_batch(*inst) for inst in instances]) 
    for batch, instances in zip(batches, batch_instances)
]

我想在每个实例上训练模型后得到损失值。所以,如果我没错的话,你是建议我每次只喂入一个实例作为批处理?我在想是否有更有效的方法来获取损失信息。 - e.hunnigton

1

一种解决方案是计算训练期望和来自训练输入的预测之间的损失函数。在损失=均方误差且输出为三维(即图像宽度x高度x通道)的情况下:

model.fit(train_in,train_out,...)

pred = model.predict(train_in)
loss = np.add.reduce(np.square(test_out-pred),axis=(1,2,3)) # this computes the total squared error for each sample
loss = loss / ( pred.shape[1]*pred.shape[2]*pred.shape[3]) # this computes the mean over the sample entry 

np.savetxt("loss.txt",loss) # This line saves the data to file

0

这里这里汇集资源后,我得到了以下代码。也许它会对你有所帮助。这个想法是你可以覆盖keras的Callbacks类,然后使用on_batch_end方法来检查keras自动提供给该方法的logs中的损失值。

这里有一个具有特定功能的NN的工作代码。也许你可以从这里开始 -

import numpy as np
import pandas as pd
import seaborn as sns
import os
import matplotlib.pyplot as plt
import time
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import Callback

# fix random seed for reproducibility
seed = 155
np.random.seed(seed)

# load pima indians dataset

# download directly from website
dataset = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/pima-indians-diabetes/pima-indians-diabetes.data", 
                      header=None).values
X_train, X_test, Y_train, Y_test = train_test_split(dataset[:,0:8], dataset[:,8], test_size=0.25, random_state=87)
class NBatchLogger(Callback):
    def __init__(self,display=100):
        '''
        display: Number of batches to wait before outputting loss
        '''
        self.seen = 0
        self.display = display

    def on_batch_end(self,batch,logs={}):
        self.seen += logs.get('size', 0)
        if self.seen % self.display == 0:
            print('\n{0}/{1} - Batch Loss: {2}'.format(self.seen,self.params['samples'],
                                                logs.get('loss')))


out_batch = NBatchLogger(display=1000)
np.random.seed(seed)
my_first_nn = Sequential() # create model
my_first_nn.add(Dense(5, input_dim=8, activation='relu')) # hidden layer
my_first_nn.add(Dense(1, activation='sigmoid')) # output layer
my_first_nn.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

my_first_nn_fitted = my_first_nn.fit(X_train, Y_train, epochs=1000, verbose=0, batch_size=128,
                                     callbacks=[out_batch], initial_epoch=0)

如果您需要类似这样的东西,请告诉我。


回调接口似乎没有获取每个实例损失的操作。on_batch_end仅在每个小批量结束时返回损失。我想要做的是更深入地了解并在每个训练实例之后获取损失。有什么建议吗? - e.hunnigton
谢谢 @SRC。这个可行!我只需要将批量大小设置为1。 - e.hunnigton
@e.hurrington,你不应该这样做。batchsize会严重影响训练性能(即使您不考虑训练时间),例如您的模型可能无法在大小为1的批次上收敛。 - Eli Korvigo
@SRC 谢谢。我不介意训练时间,但我需要它收敛。我会看看其他的选择。如果你有任何建议,请分享。再次感谢。 - e.hunnigton
@e.hurrington,我在我的编辑中给了你一个替代方案。 - Eli Korvigo
欢迎。我很高兴它对你有帮助。然而,我同意@EliKorvigo的观点,使用批量大小=1不仅会严重降低性能,还可能阻止模型收敛。请记住,我们正在讨论非常高维度的优化问题。因此,如果您想每个小批次都有损失(将批量大小设置为64或128已经很小了),那么请使用这种方法,否则,我相信EliKorvigo的方法是您唯一的选择。无论如何,我相信这两个答案都应该给您一个良好的方向。感谢EliKorvigo,我也从您的答案中学到了东西。点赞。 - SRC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接