cálculo de perda em diferentes tamanhos de lote em keras

Eu sei que, em teoria, a perda de uma rede em um lote é apenas a sum de todas as perdas individuais. Isso é refletido no código Keras para calcular a perda total. Relevantemente:

for i in range(len(self.outputs)): if i in skip_target_indices: continue y_true = self.targets[i] y_pred = self.outputs[i] weighted_loss = weighted_losses[i] sample_weight = sample_weights[i] mask = masks[i] loss_weight = loss_weights_list[i] with K.name_scope(self.output_names[i] + '_loss'): output_loss = weighted_loss(y_true, y_pred, sample_weight, mask) if len(self.outputs) > 1: self.metrics_tensors.append(output_loss) self.metrics_names.append(self.output_names[i] + '_loss') if total_loss is None: total_loss = loss_weight * output_loss else: total_loss += loss_weight * output_loss 

No entanto, notei que quando eu treino uma rede com um batch_size=32 e um batch_size=64 , o valor de perda para cada época ainda é mais ou menos o mesmo com apenas uma diferença de ~0.05% . No entanto, a precisão para ambas as redes permaneceu a mesma. Então, basicamente, o tamanho do lote não teve muito efeito na rede.

A minha pergunta é quando dobro o tamanho do lote, supondo que a perda está realmente sendo sumda, não deveria a perda ser de fato o dobro do valor anterior, ou pelo menos maior? A desculpa de que a rede provavelmente aprendeu melhor com o tamanho maior do lote é negada pelo fato de a precisão ter permanecido exatamente igual.

O fato de que a perda permanece mais ou menos a mesma, independentemente do tamanho do lote, me faz pensar que está sendo calculada a média.

O código que você publicou refere-se a modelos com várias saídas, em que cada saída pode ter suas próprias perdas e pesos. Assim, os valores de perda de diferentes camadas de saída são sumdos. No entanto, as perdas individuais são calculadas sobre o lote como você pode ver no arquivo loss.py. Por exemplo, este é o código relacionado à perda de entropia cruzada binária:

 def binary_crossentropy(y_true, y_pred): return K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1) 

Atualização: Logo após adicionar a segunda parte desta resposta (isto é, funções de perdas), como o OP, fiquei perplexo com o axis=-1 na definição de function de perda e pensei comigo mesmo que ele deveria ser axis=0 para indicar a média do lote ?! Então percebi que todos os K.mean() usados ​​na definição de perdas estão lá para o caso de uma camada de saída consistindo de múltiplas unidades. Então, onde está a perda da média do lote? Eu inspecionei o código para encontrar a resposta: para obter o valor de perda para uma function de perda específica, uma function é chamada pegando os labels verdadeiros e previstos, bem como os pesos da amostra e a máscara como suas inputs:

 weighted_loss = weighted_losses[i] # ... output_loss = weighted_loss(y_true, y_pred, sample_weight, mask) 

o que é essa function weighted_losses[i] ? Como você pode descobrir, é um elemento da lista de funções de perda (aumentadas) :

 weighted_losses = [ weighted_masked_objective(fn) for fn in loss_functions] 

fn é na verdade uma das funções de perda definidas no arquivo loss.py ou pode ser uma function de perda customizada definida pelo usuário. E agora, qual é essa function weighted_masked_objective ? Foi definido no arquivo training_utils.py :

 def weighted_masked_objective(fn): """Adds support for masking and sample-weighting to an objective function. It transforms an objective function `fn(y_true, y_pred)` into a sample-weighted, cost-masked objective function `fn(y_true, y_pred, weights, mask)`. # Arguments fn: The objective function to wrap, with signature `fn(y_true, y_pred)`. # Returns A function with signature `fn(y_true, y_pred, weights, mask)`. """ if fn is None: return None def weighted(y_true, y_pred, weights, mask=None): """Wrapper function. # Arguments y_true: `y_true` argument of `fn`. y_pred: `y_pred` argument of `fn`. weights: Weights tensor. mask: Mask tensor. # Returns Scalar tensor. """ # score_array has ndim >= 2 score_array = fn(y_true, y_pred) if mask is not None: # Cast the mask to floatX to avoid float64 upcasting in Theano mask = K.cast(mask, K.floatx()) # mask should have the same shape as score_array score_array *= mask # the loss per batch should be proportional # to the number of unmasked samples. score_array /= K.mean(mask) # apply sample weighting if weights is not None: # reduce score_array to same ndim as weight array ndim = K.ndim(score_array) weight_ndim = K.ndim(weights) score_array = K.mean(score_array, axis=list(range(weight_ndim, ndim))) score_array *= weights score_array /= K.mean(K.cast(K.not_equal(weights, 0), K.floatx())) return K.mean(score_array) return weighted 

Como você pode ver, primeiro a perda por amostra é computada na linha score_array = fn(y_true, y_pred) e então no final a média das perdas é retornada, ou seja, return K.mean(score_array) . Então, isso confirma que as perdas relatadas são a média de perdas por amostra em cada lote.

Observe que K.mean() , no caso de usar o Tensorflow como backend, chama a function tf.reduce_mean() . Agora, quando K.mean() é chamado sem um argumento de axis (o valor padrão do argumento do axis seria None ), como é chamado na function weighted_masked_objective , a chamada correspondente para tf.reduce_mean() calcula a média sobre todos os eixos e retorna um único valor . É por isso que não importa a forma da camada de saída e a function de perda usada, apenas um único valor de perda é usado e relatado por Keras (e deve ser assim, porque os algoritmos de otimização precisam minimizar um valor escalar, não um vetor ou tensor) .

Intereting Posts