Test du melon d'eau à l'aide de réseaux de neurones: Full Dev. Passez du prototypage à l'application. sur google play

Le commencement


Tout a commencé lorsque j'ai trouvé une application. sur le marché d'Apple, qui aurait été en mesure de déterminer la maturité d'un mellon d'eau. Un programme était ... étrange. Pensez-y: au lieu de frapper avec vos jointures, vous étiez censé frapper le pastèque avec votre iPhone! Néanmoins, j'ai décidé de répéter cette fonctionnalité sur une plateforme Andtoid.

Outils de sélection


Il y a peu de façons de résoudre le problème que nous avons, et j'ai dû déployer certains efforts pour en éviter un facile. C'est-à-dire, transformées de Fourier, ondelettes et éditeur de signaux. Après tout, je voulais apprendre les réseaux de neurones.

J'ai choisi Keras comme bibliothèque NN, un merveilleux outil de Google, un wrapper autour de TensorFlow et Theano. Si vous êtes débutant, c'est certainement le meilleur outil. D'un côté, Keras est un outil puissant, optimisé par la vitesse, la mémoire et le matériel (et oui, il fonctionne avec les GPU). De l'autre - il cache tout le personnel ennuyeux à l'utilisateur, ce qui permet de se concentrer sur la tâche elle-même. Très pratique.

Keras, et les réseaux de neurones en général, sont généralement liés à Python, le langage qui, comme un serpent géant ... tant pis. Quoi qu'il en soit, il faut connaître Python pour travailler dans un domaine d'apprentissage profond. Heureusement, c'est une langue facile à apprendre très rapidement.

En plus de Python, vous aurez besoin de quelques bibliothèques supplémentaires, mais leur maîtrise est facile - même par rapport à Python lui-même. Vous aurez besoin d'une expérience (superficielle) avec NumPy, PyPlot et éventuellement quelques autres bibliothèques.

Enfin, il convient de mentionner que nous n'allons pas avoir besoin de clusters GPU: notre problème peut être résolu sur un seul processeur, lent, mais pas critique.

Plan de travail


Tout d'abord, nous devons créer un réseau de neurones, en utilisant Python et Keras, dans l'environnement Ubuntu. On peut le faire dans Windows, mais le temps que vous passeriez à configurer les choses devrait être suffisant pour apprendre Ubuntu à la place.

L'étape suivante consiste à écrire un programme. J'ai l'intention de le faire sur Java pour Android. Ce sera un prototype, ce qui signifie qu'il aura l'interface utilisateur, mais pas NN - pour le moment.

Pourquoi devons-nous écrire un programme factice? Voici l'astuce: tout apprentissage en profondeur a besoin de données pour, enfin, apprendre. Combien de mellons d'eau dois-je tester (dans les deux sens de ce mot) pour fournir au NN une quantité suffisante de données> cent? Plus?

Ici, nous allons utiliser le programme factice: je vais le placer sur Google Play, le donner (ok, forcer tous mes amis à l'installer) et en collecter les données ... Où?

L'étape suivante consiste à écrire un programme côté serveur, l'un recevant des données de notre client Android. C'est une tâche très simple, il m'a fallu une vingtaine de minutes pour écrire, mais c'est quand même une étape distincte.

Enfin, lorsque nous avons suffisamment de données, nous pouvons enseigner le NN.

Ensuite, nous devons porter le NN résultant vers Java et publier une nouvelle version de notre programme, pour remplacer une version "factice".

Bénéfice Non, attends. Le programme est gratuit. Juste l'expérience.

Création du nn


Travailler avec un signal audio, ce qui frappe définitivement le mellon d'eau, signifie soit des réseaux de neurones récurrents, soit ce que l'on appelle un CNN dimensionnel. Les CNN étant plus faciles à utiliser et - pour les signaux courts - meilleurs, nous allons les utiliser. L'idée d'un NN convolutif consiste à «faire glisser» une «fenêtre de capteur» sur un tableau de nos données (qui est un signal audio). En conséquence, au lieu d'analyser toutes les données simultanément, nous recherchons des sous-modèles locaux. Chaque couche suivante du NN fonctionne avec des motifs obtenus par le précédent, à la recherche de motifs de niveau supérieur.

Pour vous faciliter la tâche, imaginez qu'il nous faut localiser une mouette sur la photo d'un océan. Nous faisons glisser une petite "fenêtre" sur l'image, à la recherche d'un motif blanc "en forme de coche". Maintenant, c'était un réseau neuronal convolutif 2D, comme pour un signal unidimensionnel, 1D CNN est un choix logique.

Le NN avait la structure suivante:

model = Sequential() model.add(Conv1D(filters=32, kernel_size=512, strides=3, padding='valid', use_bias=False, input_shape=(nSampleSize, 1), name='c1d', activation='relu')) model.add(Activation('relu', input_shape=(nSampleSize, 1))) model.add(MaxPooling1D(pool_size=(2))) model.add(Conv1D(32, (3))) model.add(Activation('relu')) model.add(MaxPooling1D(pool_size=(2))) model.add(Conv1D(64, (3))) model.add(Activation('relu')) model.add(MaxPooling1D(pool_size=(2))) model.add(Flatten()) model.add(Dense(64)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(nNumOfOutputs)) #1)) model.add(Activation('sigmoid')) model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy']) 

Ce NN a deux sorties (il prédit deux valeurs: douceur et maturité. La douceur peut être 0 (pas sucrée), 1 (normale) et 2 (excellente). Quant à la maturité, elle peut être 0 (trop rigide), 1 (bonne ) et 2 - trop doux, comme du coton avec du sable.

Pour créer ces étiquettes (sorties), nous avons besoin de données préparées par des humains, la façon dont cela est fait est discutée plus tard, dans un chapitre sur le programme Android. L'objectif de notre NN est de prédire, à l'aide d'un échantillon audio, les estimations qu'un humain ferait.

Écrire un programme


Comme je l'ai mentionné, il y aura deux versions d'un programme: "factice" et "final". Un mannequin fait des prédictions aléatoires (et l'utilisateur va en être averti). Ce faisant, il enregistre des échantillons audio et les envoie à notre serveur avec les estimations des utilisateurs d'une qualité réelle du mellon d'eau. En d'autres termes, le programme collecte simplement les données.

Voici une page de la version finale d'un programme, elle est gratuite.

Ce qu'il fait:

1. Lorsque le bouton "microphone" est enfoncé, un enregistrement commence. L'utilisateur dispose de cinq secondes pour frapper le mellon à trois reprises, c'est assez similaire à frapper à la porte. Ensuite, vous pouvez appuyer sur le bouton "pastèque" pour obtenir la "prédiction".

image

2. L'enregistrement que nous avons fait est sauvegardé dans un fichier temporaire. Par temporaire, je veux dire qu'il sera écrasé par le prochain enregistrement. Cela permet de répéter des coups si quelqu'un parle dans la pièce (vous ne croiriez pas à quel point il est difficile de faire taire les gens pendant cinq secondes!), Ou l'eau coule, ou un autre bruit a lieu.

Ok, disons que le mellon d'eau est acheté et que vous l'avez ramené à la maison. Vous avez fait un enregistrement, puis vous le coupez. Vous êtes maintenant prêt à estimer son goût.

Choisissez l'onglet «Enregistrer».

Sur cet onglet, nous avons deux zones de liste déroulante: douceur et maturité. Sélectionnez les valeurs et cliquez sur Enregistrer.

Important! Vous ne pouvez appuyer qu'une fois sur Enregistrer! Ceci est fait pour empêcher les utilisateurs d'envoyer plusieurs estimations pour le même mellon d'eau. Cela signifie également que vous devez sélectionner les valeurs avec soin, puis seulement cliquer sur Enregistrer. Après avoir enregistré le résultat, un fichier audio est renommé et il ne sera pas supprimé la prochaine fois que vous effectuez un enregistrement.

image

3. Enfin, après avoir fait des estimations (consommés) de dos de pastèques, vous êtes rentré du village où vous disposiez d'un accès illimité aux pastèques, mais pas d'Internet. Vous êtes maintenant en ligne. Ouvrez l'onglet Soumettre et appuyez sur le bouton. Un paquet contenant des informations pour tous les pastèques que vous n'avez pas encore soumis, sera envoyé à notre serveur.

image

Écriture du programme côté serveur


C'est vraiment facile, donc je ferais mieux de publier le code source. Le programme "capture" les fichiers, attribue des noms uniques et les place dans un dossier accessible uniquement au propriétaire du site.

 <?php if (is_uploaded_file($_FILES['file']['tmp_name'])) { $uploads_dir = './melonaire/'; $tmp_name = $_FILES['file']['tmp_name']; $pic_name = $_FILES['file']['name']; $filename = md5(date('Ymd H:i:s:u')); move_uploaded_file($tmp_name, $uploads_dir.$filename); } else { echo "File not uploaded successfully."; } ?> 

Former le nn


Nous divisons les données en formation (70%) et tests (30%). Neural Net converge bien, pas de surprise ici. Une note pour les débutants: n'oubliez pas de normaliser les données d'entrée, cela vous fera gagner beaucoup de temps et de nerfs. Quelque chose comme ça:

 for file_name in os.listdir(path): nSweetness, nRipeness, arr_loaded = loadData(file_name) arr_data.append(arr_loaded / max(abs(arr_loaded))) # 2 stands for num. of inputs of a combo box - 1 arr_labels.append([nSweetness / 2.0, nRipeness / 2.0]) 

Portage vers Java


Il existe plusieurs façons de porter le NN de Python vers Java. Dernièrement, Google a rendu ce processus très pratique, donc si vous décidez d'étudier des manuels, assurez-vous qu'ils ne sont pas obsolètes. Voici comment je l'ai fait:

 from keras.models import Model from keras.models import load_model from keras.layers import * import os import sys import tensorflow as tf # ------------------- def print_graph_nodes(filename): g = tf.GraphDef() g.ParseFromString(open(filename, 'rb').read()) print() print(filename) print("=======================INPUT=========================") print([n for n in g.node if n.name.find('input') != -1]) print("=======================OUTPUT========================") print([n for n in g.node if n.name.find('output') != -1]) print("===================KERAS_LEARNING=====================") print([n for n in g.node if n.name.find('keras_learning_phase') != -1]) print("======================================================") print() # ------------------- def get_script_path(): return os.path.dirname(os.path.realpath(sys.argv[0])) # ------------------- def keras_to_tensorflow(keras_model, output_dir, model_name,out_prefix="output_", log_tensorboard=True): if os.path.exists(output_dir) == False: os.mkdir(output_dir) out_nodes = [] for i in range(len(keras_model.outputs)): out_nodes.append(out_prefix + str(i + 1)) tf.identity(keras_model.output[i], out_prefix + str(i + 1)) sess = K.get_session() from tensorflow.python.framework import graph_util, graph_io init_graph = sess.graph.as_graph_def() main_graph = graph_util.convert_variables_to_constants(sess, init_graph, out_nodes) graph_io.write_graph(main_graph, output_dir, name=model_name, as_text=False) if log_tensorboard: from tensorflow.python.tools import import_pb_to_tensorboard import_pb_to_tensorboard.import_to_tensorboard( os.path.join(output_dir, model_name), output_dir) model = load_model(get_script_path() + "/models/model.h5") #keras_to_tensorflow(model, output_dir=get_script_path() + "/models/model.h5", # model_name=get_script_path() + "/models/converted.pb") print_graph_nodes(get_script_path() + "/models/converted.pb") 

Notez la dernière ligne: en code Java, vous aurez besoin du nom des couches d'entrée et de sortie du NN. L'instruction "print" les affiche pour nous.

Ensuite, nous plaçons le fichier exporté dans le dossier "assets" du projet Android Studio (le nom du fichier est coordted.pb, ne demandez pas), ajoutez la bibliothèque ( ici , ici ou mieux, ici tensorflowinferenceinterface, et ... c'est tout.

Voilà. Quand je l'ai fait pour la première fois, je m'attendais à des problèmes, mais ... tout a fonctionné.

Voici l'appel à notre NN à partir du code Java:

  protected Void doInBackground(Void... params) { try { //Pass input into the tensorflow tf.feed(INPUT_NAME, m_arrInput, 1, // batch ? m_arrInput.length, 1); // channels ? //compute predictions tf.run(new String[]{OUTPUT_NAME}); //copy the output into the PREDICTIONS array tf.fetch(OUTPUT_NAME, m_arrPrediction); } catch (Exception e) { e.getMessage(); } return null; } 

Ici "m_arrInput" - un tableau contenant deux éléments, avec notre prédiction (douceur, maturité), dans une plage de 0 à 1.

Conclusion


Je crois que je suis censé remercier mon auditoire pour son attention et exprimer l'espoir que c'était intéressant. Au lieu de cela, je vous souhaite plus de mellons d'eau douce, et j'espère que vous m'enverrez de nouveaux échantillons audio, au fur et à mesure que vous les mangerez (mellons, pas d'échantillons, hein!)

Le programme est bien sûr gratuit.

Source: https://habr.com/ru/post/fr481974/


All Articles