J'utilise la fonction tf.nn.ctc_beam_search_decoder()
de Tensorflow pour décoder la sortie d'un RNN en faisant un mappage plusieurs à plusieurs (c'est-à-dire plusieurs sorties softmax pour chaque cellule du réseau).
Une version simplifiée de la sortie du réseau et du décodeur de recherche de faisceau est:
import numpy as np
import tensorflow as tf
batch_size = 4
sequence_max_len = 5
num_classes = 3
y_pred = tf.placeholder(tf.float32, shape=(batch_size, sequence_max_len, num_classes))
y_pred_transposed = tf.transpose(y_pred,
perm=[1, 0, 2]) # TF expects dimensions [max_time, batch_size, num_classes]
logits = tf.log(y_pred_transposed)
sequence_lengths = tf.to_int32(tf.fill([batch_size], sequence_max_len))
decoded, log_probabilities = tf.nn.ctc_beam_search_decoder(logits,
sequence_length=sequence_lengths,
beam_width=3,
merge_repeated=False, top_paths=1)
decoded = decoded[0]
decoded_paths = tf.sparse_tensor_to_dense(decoded) # Shape: [batch_size, max_sequence_len]
with tf.Session() as session:
tf.global_variables_initializer().run()
softmax_outputs = np.array([[[0.1, 0.1, 0.8], [0.8, 0.1, 0.1], [0.8, 0.1, 0.1], [0.8, 0.1, 0.1], [0.8, 0.1, 0.1]],
[[0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7]],
[[0.1, 0.7, 0.2], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7]],
[[0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7]]])
decoded_paths = session.run(decoded_paths, feed_dict = {y_pred: softmax_outputs})
print(decoded_paths)
La sortie dans ce cas est:
[[0]
[1]
[1]
[1]]
Ma compréhension est que le tenseur de sortie doit être de dimensions [batch_size, max_sequence_len]
, chaque ligne contenant les indices des classes pertinentes dans le chemin trouvé.
Dans ce cas, je m'attendrais à ce que la sortie soit similaire à:
[[2, 0, 0, 0, 0],
[2, 2, 2, 2, 2],
[1, 2, 2, 2, 2],
[2, 2, 2, 2, 2]]
Qu'est-ce que je ne comprends pas sur la façon dont ctc_beam_search_decoder
travaux?
Comme indiqué dans documentation tf.nn.ctc_beam_search_decoder , la forme de la sortie n'est pas [batch_size, max_sequence_len]
. Au lieu de cela, il est
[batch_size, max_decoded_length[j]]
(avec j=0
dans votre cas).
Basé sur le début de la section 2 de cet article (qui est cité dans le dépôt github ), max_decoded_length[0]
Est délimité par le haut par max_sequence_len
, mais ils ne sont pas nécessairement égaux. La citation pertinente est:
Soit S un ensemble d'exemples d'apprentissage tirés d'une distribution fixe D_ {XxZ}. L'espace d'entrée X = (R ^ m) est l'ensemble de toutes les séquences de m vecteurs à valeurs réelles dimensionnelles. L'espace cible Z = L * est l'ensemble de toutes les séquences sur l'alphabet (fini) L des étiquettes. En général, nous appelons les éléments de L * des séquences d'étiquettes ou des étiquetages. Chaque exemple en S consiste en une paire de séquences (x, z). La séquence cible z = (z1, z2, ..., zU) est au plus aussi longue que la séquence d'entrée x = (x1, x2, ..., xT), c'est-à-dire U <= T. Etant donné que les séquences d'entrée et cible n'ont généralement pas la même longueur, il n'y a a priori aucun moyen de les aligner.
En fait, max_decoded_length[0]
Dépend de la matrice spécifique softmax_outputs
. En particulier, deux de ces matrices ayant exactement les mêmes dimensions peuvent entraîner des max_decoded_length[0]
Différents.
Par exemple, si vous remplacez la ligne
softmax_outputs = np.array([[[0.1, 0.1, 0.8], [0.8, 0.1, 0.1], [0.8, 0.1, 0.1], [0.8, 0.1, 0.1], [0.8, 0.1, 0.1]],
[[0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7]],
[[0.1, 0.7, 0.2], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7]],
[[0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7], [0.1, 0.2, 0.7]]])
avec les rangées
np.random.seed(7)
r=np.random.randint(0,100,size=(4,5,3))
softmax_outputs=r/np.sum(r,2).reshape(4,5,1)
vous obtiendrez la sortie
[[1 0 1]
[1 0 1]
[1 0 0]
[1 0 0]]
(dans les exemples ci-dessus, softmax_outputs
se compose de logits et a exactement les mêmes dimensions que la matrice que vous avez fournie).
D'un autre côté, changer la graine en np.random.seed(50)
donne la sortie
[[1 0]
[1 0]
[1 0]
[0 1]]
P.S.
Concernant la dernière partie de votre question:
Dans ce cas, je m'attendrais à ce que la sortie soit similaire à:
[[2, 0, 0, 0, 0], [2, 2, 2, 2, 2], [1, 2, 2, 2, 2], [2, 2, 2, 2, 2]]
Notez que, d'après la documentation , num_classes
Représente en fait num_labels + 1
. Plus précisément:
La taille de dimension la plus à l'intérieur des entrées de Tensor,
num_classes
, Représente les classesnum_labels + 1
, Oùnum_labels
Est le nombre d'étiquettes vraies et la plus grande valeur (num_classes - 1
) Est réservé à l'étiquette vierge.Par exemple, pour un vocabulaire contenant 3 étiquettes [a, b, c],
num_classes = 4
Et l'indexation des étiquettes est {a: 0, b: 1, c: 2, vide: 3}.
Ainsi, les véritables étiquettes dans votre cas sont 0 et 1, et 2 est réservé à l'étiquette vierge. L'étiquette vierge représente la situation sans observation d'étiquette (section 3.1 ici ):
Un réseau CTC a une couche de sortie softmax (Bridle, 1990) avec une unité de plus qu'il n'y a d'étiquettes en L. Les activations du premier | L | les unités sont interprétées comme les probabilités d'observer les étiquettes correspondantes à des moments particuliers. L'activation de l'unité supplémentaire est la probabilité d'observer un `` blanc '', ou aucune étiquette. Ensemble, ces sorties définissent les probabilités de toutes les manières possibles de aligner toutes les séquences d'étiquettes possibles avec la séquence d'entrée.