J'ai remarqué qu'un événement fréquent lors de l'entraînement est l'introduction de NAN
.
Souvent, il semble être introduit par les poids dans les couches de produit intérieur/entièrement connecté ou de convolution exploser.
Est-ce que cela se produit parce que le calcul du gradient explose? Ou est-ce à cause de l'initialisation du poids (si oui, pourquoi l'initialisation du poids a-t-elle cet effet)? Ou est-ce que cela est probablement dû à la nature des données d'entrée?
La question primordiale est simplement la suivante: Quelle est la raison la plus fréquente selon laquelle les NAN surviennent pendant l’entraînement? Et deuxièmement, quelles sont certaines méthodes pour lutter contre cela (et pourquoi fonctionnent-elles)?
Bonne question.
Je suis tombé sur ce phénomène plusieurs fois. Voici mes observations:
Raison: les grands gradients jettent le processus d'apprentissage hors-piste.
Ce à quoi vous pouvez vous attendre: En regardant le journal d'exécution, vous devriez regarder les valeurs de perte par itération. Vous remarquerez que la perte commence à croître significativement d'itération en itération, la perte sera éventuellement trop importante pour être représentée par une variable à virgule flottante et elle deviendra nan
.
Que pouvez-vous faire: Diminuer le base_lr
(dans le solveur.prototxt) par un ordre de grandeur (au moins). Si vous avez plusieurs couches de perte, vous devez consulter le journal pour voir quelle couche est responsable de l’aggravation du gradient et diminuer le loss_weight
(dans train_val.prototxt) pour cette couche spécifique, au lieu de la règle générale base_lr
.
Raison: caffe ne parvient pas à calculer un taux d'apprentissage valide et obtient 'inf'
ou 'nan'
au lieu de cela, ce taux invalide multiplie toutes les mises à jour et donc invalide tous les paramètres.
Ce à quoi vous devez vous attendre: En regardant le journal d'exécution, vous devriez voir que le taux d'apprentissage lui-même devient 'nan'
, par exemple:
... sgd_solver.cpp:106] Iteration 0, lr = -nan
Que pouvez-vous faire: corrigez tous les paramètres affectant le taux d'apprentissage dans votre 'solver.prototxt'
fichier.
Par exemple, si vous utilisez lr_policy: "poly"
et vous oubliez de définir max_iter
paramètre, vous allez vous retrouver avec lr = nan
...
Pour plus d’informations sur le taux d’apprentissage dans caffe, voir ce fil .
Raison: Parfois, les calculs de perte dans les couches de perte entraînent l'apparition de nan
s. Par exemple, Alimentation InfogainLoss
couche avec des valeurs non normalisées , en utilisant un calque de perte personnalisé avec des bugs, etc.
Ce à quoi vous devez vous attendre: En regardant le journal d'exécution, vous ne remarquerez probablement rien d'inhabituel: la perte diminue progressivement, et tout à coup un nan
apparaît.
Que pouvez-vous faire: Voyez si vous pouvez reproduire l'erreur, ajoutez une impression à la couche de perte et corrigez l'erreur.
Par exemple: Une fois, j'ai utilisé une perte qui a normalisé la pénalité par la fréquence d'apparition d'étiquettes dans un lot. Il se trouve que si l'une des étiquettes de formation n'apparaissait pas du tout dans le lot, la perte calculée produisait nan
s. Dans ce cas, travailler avec un nombre de lots suffisant (par rapport au nombre d'étiquettes dans le jeu) était suffisant pour éviter cette erreur.
Raison: vous avez une entrée avec nan
dedans!
Ce à quoi vous devez vous attendre: une fois que le processus d'apprentissage "touche" cette entrée défectueuse - la sortie devient nan
. En regardant le journal d'exécution, vous ne remarquerez probablement rien d'inhabituel: la perte diminue progressivement, et tout à coup un nan
apparaît.
Que pouvez-vous faire: reconstruisez vos jeux de données en entrée (lmdb/leveldn/hdf5 ...) pour vous assurer de ne pas avoir de mauvais fichiers image dans votre ensemble formation/validation. Pour le débogage, vous pouvez construire un réseau simple qui lit la couche d’entrée, a une perte fictive au dessus de celle-ci et traverse toutes les entrées: si l’une d’entre elles est défectueuse, ce réseau factice devrait également produire nan
.
"Pooling"
couchePour une raison quelconque, en choisissant stride
> kernel_size
pour le regroupement peut entraîner des résultats avec nan
s. Par exemple:
layer {
name: "faulty_pooling"
type: "Pooling"
bottom: "x"
top: "y"
pooling_param {
pool: AVE
stride: 5
kernel: 3
}
}
résultats avec nan
s dans y
.
"BatchNorm"
Il a été signalé que sous certains paramètres "BatchNorm"
La couche peut générer nan
s en raison d'instabilités numériques.
Ce problème a été soulevé dans bvlc/caffe et PR # 5136 tente de le réparer.
Récemment, j'ai pris conscience de debug_info
flag: paramètre debug_info: true
dans 'solver.prototxt'
fera en sorte que caffe print inscrive plus d’informations de débogage (y compris les gradients et les valeurs d’activation) au cours de la formation: Cette information peut aide à repérer les explosions de gradient et d’autres problèmes dans le processus de formation .
Cette réponse ne concerne pas une cause de nan
s, mais propose plutôt un moyen d’aider à le déboguer. Vous pouvez avoir cette couche python:
class checkFiniteLayer(caffe.Layer):
def setup(self, bottom, top):
self.prefix = self.param_str
def reshape(self, bottom, top):
pass
def forward(self, bottom, top):
for i in xrange(len(bottom)):
isbad = np.sum(1-np.isfinite(bottom[i].data[...]))
if isbad>0:
raise Exception("checkFiniteLayer: %s forward pass bottom %d has %.2f%% non-finite elements" %
(self.prefix,i,100*float(isbad)/bottom[i].count))
def backward(self, top, propagate_down, bottom):
for i in xrange(len(top)):
if not propagate_down[i]:
continue
isf = np.sum(1-np.isfinite(top[i].diff[...]))
if isf>0:
raise Exception("checkFiniteLayer: %s backward pass top %d has %.2f%% non-finite elements" %
(self.prefix,i,100*float(isf)/top[i].count))
Ajout de cette couche dans votre train_val.prototxt
à certains moments, vous pensez que cela peut causer des problèmes:
layer {
type: "Python"
name: "check_loss"
bottom: "fc2"
top: "fc2" # "in-place" layer
python_param {
module: "/path/to/python/file/check_finite_layer.py" # must be in $PYTHONPATH
layer: "checkFiniteLayer"
param_str: "prefix-check_loss" # string for printouts
}
}
Dans mon cas, ne pas définir le biais dans les couches de convolution/déconvolution en était la cause.
Solution: ajoutez ce qui suit aux paramètres de la couche de convolution.
biais_filler {type: "constante" valeur: 0}