151 votes

Comprendre les histogrammes de TensorBoard (poids)

Il est vraiment simple de voir et de comprendre les valeurs scalaires dans TensorBoard. En revanche, il n'est pas évident de comprendre les graphiques d'histogrammes.

Par exemple, ce sont les histogrammes des poids de mon réseau.

enter image description here

(Après avoir corrigé un bug grâce à sunside) enter image description here Quelle est la meilleure façon de les interpréter ? Les poids de la couche 1 semblent essentiellement plats, qu'est-ce que cela signifie ?

J'ai ajouté le code de construction du réseau ici.

X = tf.placeholder(tf.float32, [None, input_size], name="input_x")
x_image = tf.reshape(X, [-1, 6, 10, 1])
tf.summary.image('input', x_image, 4)

# First layer of weights
with tf.name_scope("layer1"):
    W1 = tf.get_variable("W1", shape=[input_size, hidden_layer_neurons],
                         initializer=tf.contrib.layers.xavier_initializer())
    layer1 = tf.matmul(X, W1)
    layer1_act = tf.nn.tanh(layer1)
    tf.summary.histogram("weights", W1)
    tf.summary.histogram("layer", layer1)
    tf.summary.histogram("activations", layer1_act)

# Second layer of weights
with tf.name_scope("layer2"):
    W2 = tf.get_variable("W2", shape=[hidden_layer_neurons, hidden_layer_neurons],
                         initializer=tf.contrib.layers.xavier_initializer())
    layer2 = tf.matmul(layer1_act, W2)
    layer2_act = tf.nn.tanh(layer2)
    tf.summary.histogram("weights", W2)
    tf.summary.histogram("layer", layer2)
    tf.summary.histogram("activations", layer2_act)

# Third layer of weights
with tf.name_scope("layer3"):
    W3 = tf.get_variable("W3", shape=[hidden_layer_neurons, hidden_layer_neurons],
                         initializer=tf.contrib.layers.xavier_initializer())
    layer3 = tf.matmul(layer2_act, W3)
    layer3_act = tf.nn.tanh(layer3)

    tf.summary.histogram("weights", W3)
    tf.summary.histogram("layer", layer3)
    tf.summary.histogram("activations", layer3_act)

# Fourth layer of weights
with tf.name_scope("layer4"):
    W4 = tf.get_variable("W4", shape=[hidden_layer_neurons, output_size],
                         initializer=tf.contrib.layers.xavier_initializer())
    Qpred = tf.nn.softmax(tf.matmul(layer3_act, W4)) # Bug fixed: Qpred = tf.nn.softmax(tf.matmul(layer3, W4))
    tf.summary.histogram("weights", W4)
    tf.summary.histogram("Qpred", Qpred)

# We need to define the parts of the network needed for learning a policy
Y = tf.placeholder(tf.float32, [None, output_size], name="input_y")
advantages = tf.placeholder(tf.float32, name="reward_signal")

# Loss function
# Sum (Ai*logp(yi|xi))
log_lik = -Y * tf.log(Qpred)
loss = tf.reduce_mean(tf.reduce_sum(log_lik * advantages, axis=1))
tf.summary.scalar("Q", tf.reduce_mean(Qpred))
tf.summary.scalar("Y", tf.reduce_mean(Y))
tf.summary.scalar("log_likelihood", tf.reduce_mean(log_lik))
tf.summary.scalar("loss", loss)

# Learning
train = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

5 votes

Je viens de remarquer que tu n'utilises pas du tout les activations sur la dernière couche. Tu voulais probablement dire tf.nn.softmax(tf.matmul(layer3_act, W4)) .

0 votes

@sunside Merci. Il s'avère que l'histogramme est également très utile pour le débogage. J'ai mis à jour les photos.

1 votes

@SungKim J'utilise votre implémentation comme référence, mais comment ajoutez-vous le biais ? Comme ceci ? B1 = tf.get_variable("B1", shape=[hidden_layer_neurons],initializer=tf.random_normal_in‌​itializer()) y layer1_bias = tf.add(layer1, B1) y tf.summary.histogram("bias", layer1_bias)

155voto

Markus Points 933

Il semble que le réseau n'ait rien appris dans les couches un à trois. La dernière couche change, ce qui signifie qu'il peut y avoir un problème avec les gradients (si vous les modifiez manuellement), que vous limitez l'apprentissage à la dernière couche en optimisant uniquement ses poids ou que la dernière couche "mange" vraiment toutes les erreurs. Il se peut également que seuls les biais soient appris. Le réseau semble apprendre quelque chose, mais il n'utilise peut-être pas tout son potentiel. Plus de contexte serait nécessaire ici, mais jouer avec le taux d'apprentissage (par exemple en utilisant un plus petit) pourrait valoir la peine d'essayer.

En général, les histogrammes affichent le nombre d'occurrences d'une valeur par rapport à d'autres valeurs. En d'autres termes, si les valeurs possibles se situent dans un intervalle de 0..9 et vous voyez un pic de montant 10 sur la valeur 0 cela signifie que 10 entrées prennent la valeur 0 ; en revanche, si l'histogramme montre un plateau de 1 pour toutes les valeurs de 0..9 cela signifie que pour 10 entrées, chaque valeur possible 0..9 se produit exactement une fois. Vous pouvez également utiliser les histogrammes pour visualiser les distributions de probabilité lorsque vous normalisez toutes les valeurs de l'histogramme par leur somme totale ; si vous faites cela, vous obtiendrez intuitivement la probabilité avec laquelle une certaine valeur (sur l'axe des x) apparaîtra (par rapport aux autres entrées).

Maintenant pour layer1/weights le plateau signifie que :

  • la plupart des poids sont dans la fourchette de -0,15 à 0,15
  • il y a (presque) autant de chances qu'un poids ait l'une de ces valeurs, c'est-à-dire qu'elles sont (presque) uniformément distribuées.

En d'autres termes, presque le même nombre de poids ont les valeurs suivantes -0.15 , 0.0 , 0.15 et tout ce qui se trouve entre les deux. Certains poids ont des valeurs légèrement inférieures ou supérieures. En résumé, on dirait simplement que les poids ont été initialisés à l'aide d'une distribution uniforme avec une moyenne de zéro et une plage de valeurs. -0.15..0.15 ... à prendre ou à laisser. Si vous utilisez effectivement une initialisation uniforme, c'est généralement le cas lorsque le réseau n'a pas encore été formé.

En comparaison, layer1/activations forme une courbe en forme de cloche (gaussienne) : Les valeurs sont centrées autour d'une valeur spécifique, dans ce cas-ci 0 mais ils peuvent aussi être plus grands ou plus petits que cela (tout aussi probablement, puisque c'est symétrique). La plupart des valeurs semblent proches de la moyenne de 0 mais les valeurs vont de -0.8 a 0.8 . Je suppose que le layer1/activations est prise comme la distribution sur toutes les sorties de couche dans un lot. Vous pouvez voir que les valeurs changent au fil du temps.

L'histogramme de la couche 4 ne me dit rien de précis. D'après la forme, il montre simplement que certaines valeurs de poids autour de -0.1 , 0.05 y 0.25 ont tendance à se produire avec une probabilité plus élevée ; une raison pourrait c'est que différentes parties de chaque neurone captent en fait les mêmes informations et sont fondamentalement redondantes. Cela peut signifier que vous pourriez utiliser un réseau plus petit ou que votre réseau a le potentiel d'apprendre plus de caractéristiques distinctives afin d'éviter l'adaptation excessive. Il ne s'agit toutefois que d'hypothèses.

De plus, comme déjà indiqué dans les commentaires ci-dessous, n'ajoutez pas d'unités de biais. En les omettant, vous contraignez votre réseau à une solution qui peut être invalide.

0 votes

Réponse étonnante ! Merci beaucoup. J'ai ajouté le code pour la référence. Pour la simplicité, je n'ai pas ajouté de biais dans mon réseau.

0 votes

Pourriez-vous expliquer un peu plus pour le layer4 ? Je vous en suis très reconnaissant.

6 votes

Ne pas avoir de parti pris du tout peut être une très mauvaise idée - c'est vraiment comme si on essayait de tracer une ligne à travers un nuage de points (de très haute dimension), mais qu'on était obligé de passer par la valeur 0 ; cela pourrait fonctionner, et vous donnerait un peu de mais il y a de fortes chances que ce soit une mauvaise solution ou tout simplement une mauvaise solution.

1voto

Zain Points 179

Ici, j'expliquerais indirectement l'intrigue en donnant un exemple minimal. Le code suivant produit un histogramme simple dans tensorboard.

from datetime import datetime
import tensorflow as tf
filename = datetime.now().strftime("%Y%m%d-%H%M%S")
fw = tf.summary.create_file_writer(f'logs/fit/{filename}')
with fw.as_default():
    for i in range(10):
        t = tf.random.uniform((2, 2), 1000)
        tf.summary.histogram(
            "train/hist",
            t,
            step=i
        )
        print(t)

Nous voyons que la génération d'une matrice 2x2 avec une plage maximale de 1000 produira des valeurs de 0 à 1000. Pour voir à quoi pourrait ressembler ce tenseur, je mets ici le log de quelques-uns d'entre eux.

enter image description here

 tf.Tensor(
[[398.65747  939.9828  ]
 [942.4269    59.790222]], shape=(2, 2), dtype=float32)
tf.Tensor(
[[869.5309  980.9699 ]
 [149.97845 454.524  ]], shape=(2, 2), dtype=float32)
tf.Tensor(
[[967.5063   100.77594 ]
 [ 47.620544 482.77008 ]], shape=(2, 2), dtype=float32)

Nous nous sommes connectés à tensorboard 10 fois. A droite du graphique, une ligne de temps est générée pour indiquer les pas de temps. La profondeur de l'histogramme indique quelles sont les nouvelles valeurs. Les valeurs plus claires/avant sont plus récentes et les valeurs plus sombres/loin sont plus anciennes.

Les valeurs sont rassemblées en groupes qui sont indiqués par ces structures triangulaires. L'axe des x indique la plage de valeurs dans laquelle se trouve le groupe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X