L'apprentissage en profondeur prend du temps non seulement pour apprendre lui-même, mais aussi pour exécuter un modèle d'apprentissage formé. Cependant, je souhaite effectuer une reconnaissance d'objets en temps réel avec SSD (Single Shot MultiBox Detector) etc.! Ou, lorsque vous voulez jouer à un jeu de combat d'action en temps réel entre l'IA et des personnes qui ont été renforcées et apprises par DQN (DeepQ Network), etc., la propriété en temps réel de l'exécution du modèle devient très importante.
Achetez un bon PC! En parlant de cela, je n'ai pas beaucoup d'argent, et peut-être que je veux l'exécuter de manière portable sur un ordinateur portable. Par conséquent, cette fois, nous examinerons comment exécuter Keras (Tensor Flow) à grande vitesse.
Faisons le. Cette fois, je vais essayer les exemples de débutants et d'experts du MNIST comme exemple aussi facile à comprendre que possible et facile à essayer. Le code créé cette fois-ci est placé dans github.
C'est une histoire sans corps ni couvercle, mais si vous pouvez charger un bon grappin, charger AWS ou ne pas avoir de restrictions particulières sur l'environnement d'exécution, poignardez TitanX et achetez un PC rempli de mémoire dès maintenant. Divers sites ont comparé les vitesses de processeur et de processeur graphique de TensorFlow. Par exemple, cet article (Comparaison des vitesses d'exécution de Tensorflow sur CPU / GPU / AWS Dans 5aa4a746b31b9be4838d)), il y a une différence de plusieurs dizaines de fois entre CPU et GPU.
Même si vous faites de votre mieux dans cet article pour accélérer, ce sera environ 2 à 5 fois plus rapide que l'original, donc si vous pouvez prendre cette mesure dès le début, c'est certainement mieux. J'ai des ennuis parce que je ne peux pas prendre de telles mesures! Je le fais déjà, mais je veux le rendre plus rapide! Voyons la suite.
Commençons par vérifier avec l'exemple MNIST de la version débutante. Si vous l'implémentez sans réfléchir dans Keras,
#La modélisation
model = Sequential()
model.add(InputLayer(input_shape=input_shape, name='input'))
model.add(Dense(nb_classes))
model.add(Activation('softmax', name='softmax'))
optimizer = SGD(lr=0.5)
model.compile(loss='categorical_crossentropy',
optimizer=optimizer,
metrics=['accuracy'])
...
#Formation modèle
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
verbose=1, validation_data=(X_test, Y_test))
...
#Évaluation du modèle
score = model.evaluate(X_test, Y_test, verbose=0)
...
#Exécution du modèle
model.predict(np.array([x])
Je pense que cela ressemblera à ce qui précède. Notez que la méthode d'exécution du modèle à la fois avec evaluer etc. est différente de la situation où de nouvelles données arrivent séquentiellement comme dans l'exécution en temps réel, donc cette fois
# X_le test est de 10000 données à 784 dimensions à 1 canal
start = time.perf_counter()
n_loop = 5
for n in range(n_loop):
predictions = [model.predict(np.array([x])) for x in X_test]
print('elapsed time for {} prediction {} [msec]'.format(len(X_test), (time.perf_counter()-start) * 1000 / n_loop))
La vitesse d'exécution du modèle est mesurée en faisant tourner la prédiction 10000 fois et en prenant le temps écoulé moyen pendant 5 semaines (pour mesurer la précision à la milliseconde, pas time.time ()
mais `time. .pref_counter () ʻest utilisé).
Au fait, le résultat ci-dessus est
elapsed time for 10000 prediction 3768.8394089927897 [msec]
était.
K.function
from keras import backend as K
pred = K.function([model.input], [model.output])
for n in range(n_loop):
predictions = [pred([np.array([x])]) for x in X_test]
Keras peut appeler le backend comme "from keras import backend as K", qui est également mentionné dans la documentation officielle (https://keras.io/ja/backend/), mais K. Vous pouvez créer une instance de la fonction Keras en utilisant function
. En exécutant le modèle à partir d'ici, vous pouvez accélérer légèrement l'exécution par rapport à l'utilisation de Keras telle quelle. Dans ce cas
elapsed time for 10000 prediction 3210.0291186012328 [msec]
C'est devenu comme.
En premier lieu, même si le même modèle est construit entre Keras et TensorFlow, il y aura une différence considérable dans la vitesse d'exécution et la vitesse d'apprentissage.
#La modélisation
x = tf.placeholder(tf.float32, [None, imageDim], name="input")
W = tf.Variable(tf.zeros([imageDim, outputDim]), dtype=tf.float32, name="Weight")
b = tf.Variable(tf.zeros([outputDim]), dtype=tf.float32, name="bias")
y = tf.nn.softmax(tf.matmul(x, W)+b, name="softmax")
#Réglage de la fonction objectif
cross_entropy = tf.reduce_mean(
-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1])
)
#Paramètres de l'optimiseur
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
#Formation modèle
sess.run(tf.global_variables_initializer())
for i in range(1000):
batch_xs, batch_ys = tfmnist.train.next_batch(100)
sess.run(train_step,feed_dict={x: batch_xs, y_: batch_ys})
#Évaluation du modèle
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
result = sess.run(
accuracy,
feed_dict={x: tfmnist.test.images, y_:tfmnist.test.labels}
)
#Exécution du modèle
sess.run(y, feed_dict={x: np.array([test_x])})
C'est un avantage de TensorFlow que vous pouvez effectuer des réglages détaillés par rapport à Keras, mais même ainsi, la partie de l'écriture du modèle est inévitablement compliquée. Cependant, le résultat de l'exécution du modèle créé par TensorFlow
elapsed time for 10000 prediction 2662.211540598946 [msec]
On constate que la vitesse est considérablement améliorée par rapport à l'implémentation Keras.
Ce n'est pas que les utilisateurs de Keras doivent pleurer et passer à TensorFlow. Vous pouvez améliorer la vitesse d'exécution en créant uniquement le modèle avec Keras et en exécutant le reste (entraînement, prédiction, etc.) à partir de TensorFlow.
import keras.backend.tensorflow_backend as KTF
import tensorflow as tf
old_session = KTF.get_session()
sess = tf.Session()
KTF.set_session(sess)
#La modélisation
model = Sequential()
model.add(InputLayer(input_shape=input_shape, name='input'))
model.add(Dense(nb_classes))
model.add(Activation('softmax', name='softmax'))
x = tf.placeholder(tf.float32, [None, imageDim], name="input")
y = model(x)
y_ = tf.placeholder(tf.float32, [None, nb_classes])
#La fonction objectif, la création de l'optimiseur et l'exécution de l'évaluation de la formation sont les mêmes que ci-dessus, elles sont donc omises.
KTF.set_session(old_session)
Vous obtenez la sortie y
en créant l'espace réservé d'entréex
et en l'attribuant au modèle. Après cela, définissez la fonction objectif et l'optimiseur en fonction de la méthode d'implémentation TensorFlow, puis activez la formation.
Avec cette méthode, le résultat de l'exécution sera
elapsed time for 10000 prediction 2685.7926497992594 [msec]
Même si la partie modèle est une implémentation Keras, la vitesse d'exécution est assez proche de l'implémentation TensorFlow.
Le montant qui peut être facilement augmenté en appuyant sur Python est au plus le niveau ci-dessus (cela peut être plus rapide si vous utilisez PyPy, etc.), et si vous voulez le rendre plus rapide, vous devez exécuter le modèle à partir de C ++. TensorFlow publie TensorFlow Serving, qui est une API permettant d'utiliser des modèles entraînés dans des applications. Avec cette API, vous pouvez exécuter le modèle à grande vitesse en chargeant le modèle TensorFlow côté C ++.
Si vous êtes un utilisateur Linux, vous pouvez l'exécuter sans problème si vous suivez le tutoriel, mais il est toujours difficile de l'exécuter sous OSX (j'ai également échoué à construire l'environnement, donc je n'ai pas pu écrire les détails cette fois ...), github Il existe également de nombreux problèmes pour OSX. Donc cette fois, je vais frapper directement TensorFlow c ++ sans utiliser Serving. Même si Serving devient disponible, il sera utile si vous souhaitez accéder au modèle Keras à partir de C ++.
Puisque vous pouvez directement utiliser le répertoire TensorFlow, mettez un lien vers le dossier tensorflow situé sous pyenv etc. à partir d'un endroit facile à utiliser. Si vous ne voulez pas polluer le répertoire installé par pip, clonez la version correspondante depuis github.
Exécutez . / Configure
depuis le répertoire racine du tensorflow à utiliser. Il vous sera demandé de spécifier le compilateur à utiliser et de définir les options par défaut, mais fondamentalement, il n'y a aucun problème avec la spécification par défaut ou oui. Cependant, si vous n'avez pas de GPU, répondez N à la question d'activer OpenCL ou CUDA.
Pour compiler TensorFlow, Bazel, qui est une source ouverte de l'outil de construction utilisé à l'origine par Google en interne, est requis. L'installation se poursuivra en se référant à ici.
Avec OSX, vous pouvez l'installer en une seule fois avec brew install bazel & brew upgrade bazel
.
Exportez les données du modèle sous une forme lisible à partir de C ++.
sess = tf.Session()
#Pour Keras
import keras.backend.tensorflow_backend as KTF
KTF.set_session(sess)
...
saver = tf.train.Saver()
saver.save(sess, "models/" + "model.ckpt")
tf.train.write_graph(sess.graph.as_graph_def(), "models/", "graph.pb")
Les poids peuvent être fixés pour les modèles qui n'ont pas besoin d'être formés. Documentation officielle
What this does is load the GraphDef, pull in the values for all the variables from the latest checkpoint file, and then replace each Variable op with a Const that has the numerical data for the weights stored in its attributes It then strips away all the extraneous nodes that aren't used for forward inference, and saves out the resulting GraphDef into an output file.
Il semble que la taille des données du modèle puisse être réduite en définissant la variable de paramètre sur Const
et en supprimant les nœuds qui ne sont pas nécessaires pour l'exécution (puisque la variable de paramètre est définie sur Const, la vitesse d'accès sera-t-elle légèrement améliorée? ).
Utilisez freeze_graph.py
pour geler. Allez dans le répertoire racine de tensorflow et
bazel build tensorflow/python/tools:freeze_graph && \
bazel-bin/tensorflow/python/tools/freeze_graph \
--input_graph=/path/to/graph.pb \
--input_checkpoint=/path/to/model.ckpt \
--output_graph=/path/to/output/frozen_graph.pb --output_node_names=softmax
En frappant, un graphe fixé sur le chemin spécifié par ʻoutput_graph sera généré (cela prendra beaucoup de temps car diverses compilations seront exécutées à la première fois). Si vous ne spécifiez pas ʻoutput_node_names
, vous vous fâcherez, mais c'est dans TensorFlow.
y = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2)+b_fc2, name="softmax")
Vous pouvez le définir en lui donnant un nom comme. Cependant, à Keras
model.add(Activation('softmax', name='softmax'))
Même si vous spécifiez comme, une erreur sera imprimée car elle a un alias en interne. dans ce cas
[print(n.name) for n in sess.graph.as_graph_def().node]
Veuillez imprimer le nom du nœud directement comme indiqué dans, et vérifier le nom défini en interne (c'était Softmax dans votre environnement).
Nous écrirons le code cpp qui lit le modèle et l'exécute. Pour plus de détails, voir github, et je présenterai les parties principales en les extrayant. Tout d'abord, créez un répertoire pour cette fois sous tensorflow_ROOT / tensorflow
(loadgraph cette fois), et créez-y un fichier cc ( tensorflow_ROOT / tensorlow / loadgraph / mnist_tf.cc
).
GraphDef graph_def;
status = ReadBinaryProto(Env::Default(), graph_file_name, &graph_def);
if (!status.ok()) {
cout << status.ToString() << "\n";
return 1;
}
cout << "loaded graph" << "\n";
// Add the graph to the session
status = session->Create(graph_def);
if (!status.ok()) {
cout << status.ToString() << "\n";
return 1;
}
Tout d'abord, lisez les données du graphique et démarrez la session.
Tensor x(DT_FLOAT, TensorShape({nTests, imageDim}));
MNIST mnist = MNIST("./MNIST_data/");
auto dst = x.flat<float>().data();
for (int i = 0; i < nTests; i++) {
auto img = mnist.testData.at(i).pixelData;
std::copy_n(img.begin(), imageDim, dst);
dst += imageDim;
}
const char* input_name = "input";
vector<pair<string, Tensor>> inputs = {
{input_name, x}
};
Ensuite, créez un tenseur d'entrée x
et remplissez-le avec les données de test MNIST. Puisque 10000 vecteurs flottants de 768 dimensions sont stockés dans mnist.testData, ils sont enregistrés dans «x» un par un. Ensuite, créez une paire de tenseur avec le nom créé côté python. Ce nom est
# TensorFlow
x = tf.placeholder(tf.float32, [None, imageDim], name="input")
# Keras
InputLayer(input_shape=input_shape, name='input')
Il faut prendre la correspondance avec le nom donné côté python donné comme. Du côté de la sortie, créez un vecteur Tensor de la même manière, enregistrez le nom de sortie (softmax dans ce cas), le tenseur de sortie et le vecteur d'entrée créé plus tôt dans la session et exécutez-le.
vector<Tensor> outputs;
// Run the session, evaluating our "softmax" operation from the graph
status = session->Run(inputs, {output_name}, {}, &outputs);
if (!status.ok()) {
cout << status.ToString() << "\n";
return 1;
}else{
cout << "Success run graph !! " << "\n";
}
Si le modèle s'exécute correctement, les sorties doivent contenir les valeurs de sortie.
int nHits = 0;
for (vector<Tensor>::iterator it = outputs.begin() ; it != outputs.end(); ++it) { //Je tourne la boucle, mais cette fois il n'y a qu'une seule sortie, donc item= outputs.front()Synonyme de
auto items = it->shaped<float, 2>({nTests, 10}); //Résultat de classification de 10 numéros 10 dimensions x 10000 données de test
for(int i = 0 ; i < nTests ; i++){
int arg_max = 0;
float val_max = items(i, 0);
for (int j = 0; j < 10; j++) {
if (items(i, j) > val_max) {
arg_max = j;
val_max = items(i, j);
}
} //Calculer l'indice de la valeur maximale du vecteur à 10 dimensions
if (arg_max == mnist.testData.at(i).label) {
nHits++;
}
}
}
float accuracy = (float)nHits/nTests;
La précision est calculée en comparant les données de l'enseignant avec le résultat de l'exécution comme indiqué dans.
Enfin, créez un fichier BUILD (un fichier qui décrit les dépendances, etc., comme un fichier make) dans la même hiérarchie que le fichier cpp.
cc_binary(
name = "mnistpredict_tf",
srcs = ["mnist_tf.cc", "MNIST.h"],
deps = [
"//tensorflow/core:tensorflow",
],
)
cc_binary(
name = "mnistpredict_keras",
srcs = ["mnist_keras.cc", "MNIST.h"],
deps = [
"//tensorflow/core:tensorflow",
],
)
Construit le.
bazel build -c opt --copt=-mavx --copt=-mavx2 --copt=-msse4.2 --copt=-msse4.1 --copt=-msse3 --copt=-mfma :mnistpredict_tf
bazel build -c opt --copt=-mavx --copt=-mavx2 --copt=-msse4.2 --copt=-msse4.1 --copt=-msse3 --copt=-mfma :mnistpredict_keras
J'ai plusieurs options, mais ce n'est pas indispensable.
The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
...
J'étais en colère contre diverses choses, alors je le porte cette fois.
Si la compilation réussit, un fichier exécutable nommé par BUILD sera créé dans tensorflow_ROOT / bazel-bin / tensorflow / loadgraph
.
cd tensorflow_ROOT/bazel-bin/tensorflow/loadgraph
./mnistpredict_tf
Accédez à ce niveau, apportez le dossier MNIST_TEST et les données graphiques fixes, et exécutez-le (le contenu du dossier MNIST_TEST doit être développé).
Maintenant, résumons tous les résultats des modèles introduits jusqu'à présent. Il s'agit du temps (msec) requis lorsque le processus de prédiction consistant à «renvoyer le résultat de la détermination dont le nombre est renvoyé lorsqu'un élément de données de pixel de 768 dimensions (28x28) est entré» est répété 1000 fois (5 fois chacun). Prend la moyenne).
msec | Keras | Keras(K.function) | Keras(tf) | TensorFlow |
---|---|---|---|---|
Python | 3787 | 3242 | 2711 | 2588 |
C++ | 578 | - | 577 | 576 |
En premier lieu, les performances de traitement en boucle sont extrêmement différentes entre Python et C ++, donc cela ne peut pas être aidé, mais dans une simple comparaison, C ++ est toujours la victoire écrasante. Le résultat est que le TensorFlow simple est le plus rapide parmi les implémentations Python, suivi par le modèle de type tf d'exécution Keras. Le Keras ordinaire est considérablement plus lent que ceux-ci, mais vous pouvez voir une légère amélioration de la vitesse en exécutant K.function.
Je comparerai également la vitesse dans l'édition experte qui utilise la couche Convolution. Le modèle utilise les éléments suivants.
C'est presque la même que l'édition pour débutants, donc je vais l'omettre grossièrement, mais il y a un point à prendre en compte.
Il n'y a pas de problème lors de l'utilisation de Keras seul, mais il convient d'être prudent lors de l'utilisation de K.function ou de l'utilisation de Keras et TensorFlow avec la gestion de learning_phase. Il est nécessaire de spécifier l'indicateur learning_phase lorsque le modèle utilisé est différent entre l'apprentissage et le test / exécution, par exemple lorsqu'il y a une couche Dropout. L'indicateur learning_phase spécifie 1 pendant l'entraînement et 0 pendant l'exécution.
Vous devez spécifier K.learning_phase () pour l'entrée, et entrer 0 à l'exécution.
# K.Lors de l'utilisation de la fonction
pred = K.function([model.input, K.learning_phase()], [model.output])
[pred([np.array([x]), 0]) for x in X_test]
#Lors de l'utilisation du modèle Keras de TensorFlow
[sess.run(y, feed_dict={x: np.array([test_x]), K.learning_phase(): 0}) for test_x in X_test]
Un tenseur de type Bool est créé, 0 est attribué et il est enregistré en entrée avec le nom keras_learning_phase
.
Tensor lp(DT_BOOL, TensorShape({}));
lp.flat<bool>().setZero();
...
vector<pair<string, Tensor>> inputs = {
{input_name, x}, {"keras_learning_phase", lp}
};
Les résultats seront comparés de la même manière que pour les débutants.
msec | Keras | Keras(K.function) | Keras(tf) | TensorFlow |
---|---|---|---|---|
Python | 9693 | 9087 | 8571 | 8124 |
C++ | 5528 | - | 5530 | 5512 |
Pour être honnête, je pensais qu'il y aurait plus de différence de performances entre Python et C ++, mais il n'y avait pas beaucoup de différence. En comparant le côté Python, l'ordre est presque le même que celui de l'édition débutant.
Comme il n'est pas directement lié à ce thème, je l'ai ajouté en bonus, mais la vitesse de calcul augmentera en fonction de la façon de sélectionner BLAS (Basic Linear Algebra Subprograms), qui définit les spécifications des opérations de base liées aux matrices et aux vecteurs.
Pour savoir comment utiliser OpenBLAS, veuillez vous référer à J'ai déjà écrit un article. Pour Intel MKL,
Linux: Comment installer mkl numpy OSX: Construction de mkl & numpy sur mac
Il nous a donné un article de commentaire très facile à comprendre, alors je pense que vous devriez y jeter un coup d'œil. À propos, si vous insérez python depuis anaconda, numpy et scipy de la compilation MKL seront inclus par défaut (cette méthode est extrêmement simple). Mais,[ Selon l'article de Building mkl & numpy on mac, il vaut mieux le mettre depuis MKL que via anaconda. Il semble que cela coûte cher, donc je ne peux pas dire lequel est le meilleur.
Pour savoir à quel point les spécifications de calcul augmenteront, consultez les articles mentionnés ci-dessus et
Comparaison des vitesses SVD de décomposition de singularité Python
Il y a un article qui est comparé comme celui-ci, donc je pense que vous devriez y jeter un coup d'œil, mais il semble être 20% à plusieurs fois plus rapide (30% de plus dans l'environnement actuel).
Cela fait un peu long, mais j'ai essayé de voir à quel point la vitesse d'exécution change avec Keras, TensorFlow et leur exécution en c ++. En conséquence, TensorFlow est le plus rapide si vous le faites uniquement du côté Python, et s'il est gênant de créer un modèle, etc., le type hybride qui utilise Keras semble être bon. Si vous utilisez C ++, la vitesse ne changera pas tellement, peu importe celle que vous prenez, elle sera donc plus rapide que l'implémentation Python.
Dans l'édition experte, Python et C ++ étaient près de 1,8 fois plus rapides, mais il n'y avait aucune différence que ce à quoi je m'attendais. À l'origine, la partie exécution du modèle de TensorFlow devait exécuter une compilation de C ++ derrière, donc la différence qui est apparue cette fois n'était pas la différence de vitesse d'exécution du modèle, mais la différence de performances de traitement de boucle, etc. à d'autres endroits. Je pense que c'est. Donc, sauf quand je veux vraiment la vitesse de traitement de C ++ dans des parties autres que l'exécution du modèle, telles que la combinaison avec le traitement d'image, j'ai senti qu'il n'y avait pas de grande différence pour transformer le modèle du côté Python. La prochaine fois, je vais l'essayer avec un SSD.
Recommended Posts