web-dev-qa-db-fra.com

Déployer un réseau de segmentation sémantique (U-Net) avec TensorRT (pas de prise en charge du suréchantillonnage)

J'essaie de déployer un U-Net formé avec TensorRT. Le modèle a été formé en utilisant Keras (avec Tensorflow comme backend). Le code est très similaire à celui-ci: https://github.com/zhixuhao/unet/blob/master/model.py

Lorsque j'ai converti le modèle au format UFF, en utilisant du code comme celui-ci:

import uff
import os
uff_fname = os.path.join("./models/", "model_" + idx + ".uff")
uff_model = uff.from_tensorflow_frozen_model(
    frozen_file = os.path.join('./models', trt_fname), output_nodes = output_names, 
    output_filename = uff_fname
)

J'obtiendrai l'avertissement suivant:

Warning: No conversion function registered for layer: ResizeNearestNeighbor yet.
Converting up_sampling2d_32_12/ResizeNearestNeighbor as custom op: ResizeNearestNeighbor
Warning: No conversion function registered for layer: DataFormatVecPermute yet.
Converting up_sampling2d_32_12/Shape-0-0-VecPermuteNCHWToNHWC-LayoutOptimizer as custom op: DataFormatVecPermute

J'ai essayé d'éviter cela en remplaçant la couche de suréchantillonnage par un suréchantillonnage (interpolation bilinéaire) et en transposant la convolution. Mais le convertisseur me lancerait des erreurs similaires. J'ai vérifié https://docs.nvidia.com/deeplearning/sdk/tensorrt-support-matrix/index.html et il semble que toutes ces opérations ne soient pas encore prises en charge.

Je me demande s'il existe une solution à ce problème? Existe-t-il un autre format/cadre que TensorRT aime et prend en charge le suréchantillonnage? Ou est-il possible de le remplacer par d'autres opérations prises en charge?

J'ai également vu quelque part que l'on peut ajouter des opérations personnalisées pour remplacer celles qui ne sont pas prises en charge pour TensorRT. Bien que je ne suis pas sûr de savoir comment serait le flux de travail. Il serait également très utile que quelqu'un indique un exemple de calques personnalisés.

Merci d'avance!

18
Yayuchen

Les avertissements sont dus au fait que ces opérations sont pas encore prises en charge par TensorRT, comme vous l'avez déjà mentionné. Malheureusement, il n'y a aucun moyen facile de résoudre ce problème. Vous devez soit modifier le graphique (même après la formation ) pour utiliser uniquement une opération prise en charge par combinaison; ou écrivez ces opérations vous-même sous la forme couche personnalisée .

Cependant, il existe une meilleure façon d'exécuter l'inférence sur d'autres appareils en C++. Vous pouvez utiliser TensorFlow mélangé avec TensorRT ensemble . TensorRT analysera le graphique pour les opérations qu'il prend en charge et les convertira en nœuds TensorRT, et le reste du graphique sera géré par TensorFlow comme d'habitude. Plus d'informations ici . Cette solution est beaucoup plus rapide que de réécrire les opérations vous-même. La seule partie compliquée est de construire TensorFlow à partir de sources sur votre appareil cible et générer la bibliothèque dynamiquetensorflow_cc. Récemment, il existe de nombreux guides et supports pour les ports TensorFlow vers diverses architectures, par exemple BRAS .

2
Xenon

Hé, j'ai fait quelque chose de similaire, je dirais que la meilleure façon de résoudre le problème est d'exporter votre modèle vers .onnx avec un bon comme celui-ci , si vous cochez matrice de support pour onnx , le suréchantillonnage est supporté: enter image description here

Ensuite, vous pouvez utiliser https://github.com/onnx/onnx-tensorrt pour convertir le modèle onnx en tensorrt, je l'ai pour convertir un réseau que j'ai formé au pytorch et qui avait un suréchantillonnage . Le dépôt pour onnx-tensorrt est un peu plus actif, et si vous cochez l'onglet pr, vous pouvez vérifier que d'autres personnes écrivent des couches et des fork personnalisés à partir de là.

1
bpinaya

Mise à jour le 28/09/2019

Nvidia a publié TensorRT 6.0.1 il y a environ deux semaines et a ajouté une nouvelle API appelée "IResizeLayer". Cette couche prend en charge l'interpolation "la plus proche" et peut donc être utilisée pour implémenter un suréchantillonnage. Plus besoin d'utiliser des couches/plugins personnalisés!

Réponse originale:

merci pour toutes les réponses et suggestions affichées ici!

Au final, nous avons implémenté le réseau dans l'API TensorRT C++ directement et chargé les poids à partir du fichier de modèle .h5. Nous n'avons pas encore le temps de profiler et de peaufiner la solution, mais l'inférence semble fonctionner selon les images de test que nous avons alimentées.

Voici le workflow que nous avons adopté:

Étape 1: codez la couche de suréchantillonnage.

Dans notre modèle U-Net, toute la couche de suréchantillonnage a un facteur d'échelle de (2, 2) et elles utilisent toutes l'interpolation ResizeNearestNeighbors. Essentiellement, la valeur de pixel à (x, y) dans le tenseur d'origine ira à quatre pixels: (2x, 2y), (2x + 1, 2y), (2x, 2y + 1) et (2x + 1, 2y + 1 ) dans le nouveau tenseur. Cela peut être facilement codé dans une fonction de noyau CUDA.

Une fois que nous avons obtenu le noyau de suréchantillonnage, nous devons l'envelopper avec l'API TensorRT, en particulier la classe IPluginV2Ext . La référence développeur contient des descriptions des fonctions à implémenter. Je dirais que enqueue () est la fonction la plus importante car le noyau CUDA y est exécuté.

Il existe également des exemples dans le dossier Samples TensorRT. Pour ma version, ces ressources sont utiles:

Étape 2: coder le reste du réseau à l'aide de l'API TensorRT

Le reste du réseau devrait être assez simple. Il suffit de trouver la fonction "addxxxLayer" différente de définitions de réseau TensorRT .

Une chose à garder à l'esprit: selon la version de TRT que vous utilisez, la façon d'ajouter du rembourrage peut être différente. Je pense que la dernière version (5.1.5) permet aux développeurs d'ajouter des paramètres dans addConvolution() afin que le bon mode de remplissage puisse être sélectionné.

Mon modèle a été formé en utilisant Keras, le mode de remplissage par défaut est que la droite et le bas obtiennent plus de remplissage si le nombre total de remplissage n'est pas pair. Vérifiez ceci lien de débordement de pile pour plus de détails. Il y a un mode dans 5.1.5 qui représente ce schéma de remplissage.

Si vous utilisez une ancienne version (5.1.2.2), vous devrez ajouter le remplissage comme n calque séparé avant le calque de convolution, qui a deux paramètres: pré-remplissage et post-remplissage.

De plus, tout est NCHW dans TensorRT

Échantillon utile:

  • TensorRT-5.1.2.2/samples/sampleMNISTAP

Étape 3: chargez les poids

TensorRT veut des poids au format [out_c, in_c, filter_h, filter_w], qui est mentionné dans un documentation archivée . Keras a des poids au format [filter_h, filter_w, c_in, c_out].

Nous avons obtenu un fichier de poids purs en appelant model.save_weights('weight.h5') en Python. Ensuite, nous pouvons lire les poids dans un tableau Numpy en utilisant h5py, effectué la transposition et enregistré les poids transposés dans un nouveau fichier. Nous avons également déterminé le nom du groupe et du jeu de données à l'aide de h5py. Ces informations ont été utilisées lors du chargement de poids dans le code C++ à l'aide de HDF5 C++ API .

Nous avons comparé la sortie couche par couche entre le code C++ et Python. Pour notre U-Net, toutes les cartes d'activation sont les mêmes jusqu'à peut-être le troisième bloc (après 2 regroupements). Après cela, il y a une petite différence entre les valeurs des pixels. L'erreur absolue en pourcentage est 10 ^ -8, donc nous ne pensons pas que ce soit si mauvais. Nous sommes toujours en train de peaufiner l'implémentation C++.

Encore une fois, merci pour toutes les suggestions et réponses que nous avons reçues dans ce post. J'espère que notre solution pourra également vous être utile!

1
Yayuchen