J'aimerais pouvoir convertir un modèle Tensorflow en modèle Caffe.
J'ai cherché sur google mais je n'ai pu trouver que des convertisseurs de caffe en tensorflow mais pas l'inverse.
Quelqu'un at-il une idée sur la façon de le faire?
Merci, Evi
J'ai eu le même problème et j'ai trouvé une solution. Le code peut être trouvé ici ( https://github.com/lFatality/tensorflow2caffe ) et j'ai également documenté le code dans certaines vidéos Youtube.
Partie 1 couvre la création de l'architecture de VGG-19 dans Caffe et tflearn (API de niveau supérieur pour TensorFlow, avec quelques modifications au code natif TensorFlow devrait également fonctionner).
Dans Partie 2 , l'exportation des poids et des biais du modèle TensorFlow dans un fichier numpy est décrite. Dans tflearn, vous pouvez obtenir les poids d'un calque comme ceci:
#get parameters of a certain layer
conv2d_vars = tflearn.variables.get_layer_variables_by_name(layer_name)
#get weights out of the parameters
weights = model.get_weights(conv2d_vars[0])
#get biases out of the parameters
biases = model.get_weights(conv2d_vars[1])
Pour une couche convolutionnelle, le nom_couche est Conv_2D
. Les couches entièrement connectées sont appelées FullyConnected
. Si vous utilisez plusieurs couches d'un certain type, un entier croissant avec un trait de soulignement précédent est utilisé (par exemple, la deuxième couche conv est appelée Conv_2D_1
). J'ai trouvé ces noms dans le graphique du TensorBoard. Si vous nommez les couches dans votre définition d'architecture, ces noms de couches peuvent alors prendre les noms que vous avez définis.
Dans TensorFlow natif, l'exportation nécessitera un code différent mais le format des paramètres doit être le même, de sorte que les étapes suivantes doivent toujours être applicables.
Partie couvre la conversion réelle. Ce qui est essentiel, c'est la conversion des poids lorsque vous créez le modèle de café (les biais peuvent être reportés sans changement). TensorFlow et Caffe utilisent des formats différents lors de l'enregistrement d'un filtre. Alors que TensorFlow utilise [height, width, depth, number of filters]
( documents TensorFlow, en bas ), Caffe utilise [number of filters, depth, height, width]
( documents Caffe, chapitre 'Stockage et communication d'objets blob' =). Pour convertir entre les formats, vous pouvez utiliser la fonction transpose
(par exemple: weights_of_first_conv_layer.transpose((3,2,0,1))
. La séquence 3,2,0,1 peut être obtenue en énumérant le format TensorFlow (Origin) puis passer au format Caffe (format cible) tout en conservant les nombres à leur variable spécifique.).
Si vous voulez connecter une sortie tenseur à une couche entièrement connectée, les choses deviennent un peu délicates. Si vous utilisez VGG-19 avec une taille d'entrée de 112x112, cela ressemble à ceci.
fc1_weights = data_file[16][0].reshape((4,4,512,4096))
fc1_weights = fc1_w.transpose((3,2,0,1))
fc1_weights = fc1_w.reshape((4096,8192))
Ce que vous obtenez de TensorFlow si vous exportez les paramètres à la connexion entre le tenseur et la couche entièrement connectée est un tableau avec la forme [entries in the tensor, units in the fc-layer]
(Ici: [8192, 4096]
). Vous devez découvrir quelle est la forme de votre tenseur de sortie, puis remodeler le tableau afin qu'il s'adapte au format TensorFlow (voir ci-dessus, number of filters
Étant le number of units in the fc-layer
). Après cela, vous utilisez la transposition-conversion que vous avez utilisée précédemment, puis remodelez à nouveau le tableau, mais dans l'autre sens. Alors que TensorFlow enregistre les poids de couche fc sous [number of inputs, number of outputs]
, Caffe fait l'inverse.
Si vous connectez deux couches fc l'une à l'autre, vous n'avez pas à effectuer le processus complexe décrit précédemment mais vous devrez prendre en compte les différents formats de couche fc en les transposant à nouveau (fc_layer_weights.transpose((1,0))
)
Vous pouvez ensuite définir les paramètres du réseau en utilisant
net.params['layer_name_in_prototxt'][0].data[...] = weights
net.params['layer_name_in_prototxt'][1].data[...] = biases
Ce fut un bref aperçu. Si vous voulez tout le code, c'est dans mon dépôt github. J'espère que ça aide. :)
À votre santé,
Fatalité
Comme suggéré dans le commentaire de @Patwie, vous devez le faire manuellement en copiant les poids couche par couche. Par exemple, pour copier les premiers poids de la couche conv d'un point de contrôle tensorflow vers un modèle de caffem, vous devez faire quelque chose comme ceci:
sess = tf.Session()
new_saver = tf.train.import_meta_graph("/path/to/checkpoint.meta")
what = new_saver.restore(sess, "/path/to/checkpoint")
all_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
conv1 = all_vars[0]
bias1 = all_vars[1]
conv_w1, bias_1 = sess.run([conv1,bias1])
net = caffe.Net('path/to/conv.prototxt', caffe.TEST)
net.params['conv_1'][0].data[...] = conv_w1
net.params['conv_1'][1].data[...] = bias_1
...
net.save('modelfromtf.caffemodel')
Note1: Ce code a PAS a été testé. Je ne sais pas si cela fonctionnera, mais je pense que oui. En outre, cela concerne uniquement une couche conv. En pratique, vous devez d'abord analyser votre point de contrôle tensorflow pour vérifier quels poids de couche sont à quel index (print all_vars ), puis copier individuellement les poids de chaque couche.
Remarque 2: Une certaine automatisation peut être effectuée en itérant sur les couches conv init car elles suivent généralement un modèle défini (conv1-> bn1-> relu1-> conv2-> bn2-> relu2 ...)
Remarque 3: Tensorflow peut encore diviser les pondérations de chaque couche en indices séparés. Par exemple: les poids et les biais sont séparés pour une couche conv comme indiqué au dessus. De plus, gamma , signifie et la variance est séparée pour la couche de normalisation par lots.