Prueba de melón de agua utilizando redes neuronales: desarrollo completo. Ciclo de creación de prototipos a la aplicación. en google play

El principio


Todo comenzó cuando encontré una aplicación. en el mercado de Apple, eso supuestamente pudo determinar la madurez de un melón de agua. Un programa fue ... extraño. Piénselo: en lugar de golpear con los nudillos, ¡se suponía que golpearías el agua con tu iPhone! Sin embargo, he decidido repetir esa funcionalidad en una plataforma Andtoid.

Herramientas de selección


Hay pocas formas de resolver el problema que tenemos, y tuve que aplicar ciertos esfuerzos para evitar uno fácil. Es decir, transformadas de Fourier, wavelets y editor de señales. Quería aprender redes neuronales después de todo.

Elegí Keras como una biblioteca NN, una herramienta maravillosa de Google, un contenedor alrededor de TensorFlow y Theano. Si eres un principiante, esta es definitivamente la mejor herramienta. Por un lado, Keras es una herramienta poderosa, optimizada por velocidad, memoria y hardware (y sí, funciona con GPU). Por otro lado, oculta todo el personal aburrido del usuario, lo que permite centrarse en la tarea misma. Muy conveniente

Keras, y las redes neuronales en general, generalmente están vinculadas a Python, el lenguaje que al igual que una serpiente gigante ... no importa. De todos modos, uno necesita conocer Python para trabajar en un campo de Deep Learning. Afortunadamente, es un lenguaje fácil que se puede aprender muy rápido.

Además de Python, necesitará algunas bibliotecas adicionales, pero dominarlas es fácil, incluso en comparación con Python. Necesitará una experiencia (superficial) con NumPy, PyPlot y posiblemente un par de otras bibliotecas.

Finalmente, vale la pena mencionar que no vamos a necesitar clasificadores de GPU: nuestro problema se puede resolver en una sola CPU, lento, pero no críticamente lento.

Plan de trabajo


En primer lugar, necesitamos crear una red neuronal, usando Python y Keras, en el entorno Ubuntu. Uno puede hacerlo en Windows, pero el tiempo que pasaría configurando las cosas debería ser suficiente para aprender Ubuntu.

El siguiente paso es escribir un programa. Planeo hacerlo en Java para Android. Será un prototipo, lo que significa que tendrá la interfaz de usuario, pero no NN, todavía.

¿Por qué tenemos que escribir un programa ficticio? Aquí está el truco: cualquier aprendizaje profundo necesita datos para, bueno, aprender. ¿Cuántos melones de agua debo probar (en ambos sentidos de esta palabra) para proporcionar al NN una cantidad suficiente de datos> cien? Más?

Aquí vamos a utilizar el programa ficticio: lo colocaré en Google Play, lo regalaré (está bien, obligaré a todos mis amigos a instalarlo) y recogeré datos de él ... ¿Dónde?

El siguiente paso es escribir un programa del lado del servidor, uno que reciba datos de nuestro cliente Android. Esta es una tarea muy simple, me tomó unos veinte minutos escribir, pero aún así, es un paso separado.

Finalmente, cuando tenemos suficientes datos, podemos enseñar el NN.

Luego necesitamos portar el NN resultante a Java y lanzar una nueva versión de nuestro programa, para reemplazar una "falsa".

Ganancia No, espera El programa es gratis. Solo la experiencia.

Creando el nn


Trabajar con señal de audio, que definitivamente golpea el agua, significa redes neuronales recurrentes o la llamada CNN dimensional. Como las CNN son más fáciles de usar y, para señales cortas, mejor, las vamos a usar. La idea de un NN convolucional es "deslizar" una "ventana de sensor" sobre una matriz de nuestros datos (que es una señal de audio). Como resultado, en lugar de analizar todos los datos simultáneamente, buscamos subpatrones locales. Cada capa siguiente del NN funciona con patrones obtenidos por la anterior, buscando patrones de nivel superior.

Para hacerlo más fácil, imagine que tenemos que ubicar una gaviota en la foto de un océano. Deslizamos una pequeña "ventana" a través de la imagen, buscando un patrón blanco "similar a una marca de verificación". Ahora, esa era una red neuronal convolucional en 2D, igual que para una señal unidimensional, 1D CNN es una opción lógica.

El NN tenía la siguiente estructura:

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']) 

Este NN tiene dos salidas (predice dos valores: dulzura y madurez. La dulzura puede ser 0 (no dulce), 1 (normal) y 2 (excelente). En cuanto a la madurez, puede ser 0 (demasiado rígido), 1 (bueno ) y 2: demasiado suave, como el algodón con arena.

Para crear esas etiquetas (salidas), necesitamos datos preparados por humanos, la forma en que se hace se analiza más adelante, en un capítulo sobre el programa Android. El objetivo de nuestro NN es predecir, utilizando muestras de audio, las estimaciones que haría un humano.

Escribir un programa


Como mencioné, habrá dos versiones de un programa: "ficticio" y "final". Un simulado hace predicciones aleatorias (y el usuario recibirá una advertencia al respecto). Mientras lo hace, graba muestras de audio y las envía a nuestro servidor junto con estimaciones de los usuarios de una calidad real del agua mellon. En otras palabras, el programa simplemente recopila los datos.

Aquí hay una página de la versión final de un programa, es gratis.

Lo que hace

1. Cuando se presiona el botón "micrófono", comienza una grabación. El usuario tiene cinco segundos para golpear el agua tres veces, es bastante similar a tocar la puerta. Luego puede presionar el botón "sandía" para obtener la "predicción".

imagen

2. La grabación que hicimos se guarda como un archivo temporal. Por temporal quiero decir que se sobrescribirá en la próxima grabación. Permite repetir golpes si alguien habla en la habitación (¡no creerías lo difícil que es callar a la gente durante cinco segundos!), O corre agua, o hay algún otro ruido.

De acuerdo, digamos que se compró el agua mellon y la trajo a casa. Hiciste una grabación y luego la cortaste. Ahora estás listo para estimar su sabor.

Elija la pestaña "Guardar".

En esta pestaña tenemos dos cuadros combinados: dulzura y madurez. Seleccione los valores y haga clic en Guardar.

Importante! ¡Solo puede presionar Guardar una vez! Se hace para evitar que los usuarios envíen múltiples estimaciones para el mismo agua mellon. También significa que debe seleccionar valores con cuidado, y solo entonces hacer clic en Guardar. Después de guardar el resultado, se cambia el nombre de un archivo de audio y no se eliminará la próxima vez que grabe.

imagen

3. Finalmente, después de haber hecho una estimación (meand comer) dosificación de melones de agua, ha regresado de la aldea donde tenía un acceso ilimitado a los melones de agua, pero no a Internet. Ahora estas en línea. Abra la pestaña Enviar y presione el botón. Se enviará a nuestro servidor un paquete que contenga información para todas las sandías que aún no haya enviado.

imagen

Escribir el programa del lado del servidor


Es realmente fácil, así que mejor publico el código fuente. El programa "captura" archivos, asigna nombres únicos y los coloca en una carpeta accesible solo para el propietario del sitio.

 <?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."; } ?> 

Entrenando el nn


Dividimos los datos en entrenamiento (70%) y pruebas (30%). Neural Net converge bien, no hay sorpresas aquí. Una nota para principiantes: no se olvide de normalizar los datos de entrada, le ahorrará mucho tiempo y nervios. Algo como esto:

 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]) 

Portando a Java


Hay pocas formas de portar el NN de Python a Java. Últimamente, Google hizo este proceso realmente conveniente, así que si decides estudiar libros de texto, asegúrate de que no estén obsoletos. Así es como lo hice:

 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") 

Tenga en cuenta la última línea: en el código Java, necesitará el nombre de las capas de entrada y salida del NN. La declaración "imprimir" nos los muestra.

A continuación, colocamos el archivo exportado en la carpeta "activos" del proyecto de Android Studio (el nombre del archivo es coordinado.pb, no pregunte), agregamos la biblioteca ( aquí , aquí o mejor, aquí interfaz de inferencia de tensorflow, y ... eso es todo.

Eso es todo Cuando lo hice por primera vez, esperaba problemas, pero ... todo funcionó.

Aquí está la llamada a nuestro NN desde el código 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; } 

Aquí "m_arrInput": una matriz que contiene dos elementos, con nuestra predicción (dulzura, madurez), en un rango de 0 a 1.

Conclusión


Creo que se supone que debo agradecer a mi audiencia por la atención y expresar la esperanza de que fue interesante. En cambio, les deseo más melones de agua dulce, y espero que me envíen nuevas muestras de audio, mientras las comen (¡melones, no muestras, eh!)

El programa es gratis, por supuesto.

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


All Articles