Teste de melancia usando redes neurais: desenvolvedor completo Ciclo de prototipagem para o aplicativo. no google play

O começo


Tudo começou quando encontrei um aplicativo. no mercado da Apple, que supostamente foi capaz de determinar a maturação de uma melancia. Um programa era ... estranho. Pense bem: em vez de bater com os nós dos dedos, você deveria bater na água com o seu iPhone! No entanto, eu decidi repetir essa funcionalidade em uma plataforma Andtoid.

Ferramentas de seleção


Existem poucas maneiras de resolver o problema que temos, e eu tive que aplicar alguns esforços para evitar um problema fácil. Ou seja, transformadas de Fourier, wavelets e editor de sinais. Eu queria aprender redes neurais, afinal.

Escolhi o Keras como uma biblioteca NN, uma ferramenta maravilhosa do Google, um invólucro em torno do TensorFlow e Theano. Se você é iniciante, esta é definitivamente a melhor ferramenta. Por um lado, o Keras é uma ferramenta poderosa, otimizada por velocidade, memória e hardware (e sim, funciona com GPUs). Por outro lado - esconde toda a equipe chata do usuário, permitindo focar na tarefa em si. Muito conveniente.

Keras, e redes neurais em geral, geralmente estão ligadas ao Python, a linguagem que como uma cobra gigante ... não importa. De qualquer forma, é preciso conhecer Python para trabalhar em um campo de Aprendizado Profundo. Felizmente, é uma linguagem fácil que pode ser aprendida muito rapidamente.

Além do Python, você precisará de algumas bibliotecas extras, mas dominá-las é fácil - mesmo em comparação com o próprio Python. Você precisará de uma experiência (superficial) com NumPy, PyPlot e, possivelmente, algumas outras bibliotecas.

Finalmente, vale mencionar que não precisaremos de clasters de GPU: nosso problema pode ser resolvido em uma única CPU, lenta, mas não criticamente lenta.

Plano de trabalho


Primeiro de tudo, precisamos criar uma rede neural, usando Python e Keras, no ambiente Ubuntu. Pode-se fazer isso no Windows, mas o tempo que você gastaria para configurar as coisas deve ser suficiente para aprender o Ubuntu.

O próximo passo é escrever um programa. Eu pretendo fazê-lo em Java para Android. Será um protótipo, o que significa que terá a interface do usuário, mas não NN - ainda.

Por que precisamos escrever um programa fictício? Aqui está o truque: qualquer aprendizado profundo precisa de dados para, bem, aprender. Quantas melancias devo testar (nos dois significados desta palavra) para fornecer ao NN uma quantidade suficiente de dados> Cem? Mais?

Aqui vamos usar o programa fictício: vou colocá-lo no Google Play, entregá-lo (ok, forçar todos os meus amigos a instalá-lo) e coletar dados dele ... Onde?

O próximo passo é escrever um programa do lado do servidor, um que receba dados do nosso cliente Android. Essa é uma tarefa muito simples, demorei cerca de vinte minutos para escrever, mas ainda assim, é uma etapa separada.

Finalmente, quando temos dados suficientes, podemos ensinar o NN.

Em seguida, precisamos portar o NN resultante para Java e lançar uma nova versão do nosso programa, para substituir uma "fictícia".

Lucro Não espera. O programa é gratuito. Apenas a experiência.

Criando o nn


Trabalhar com sinal de áudio, o que definitivamente é bom, significa redes neurais recorrentes ou a chamada CNN de dimensão. Como as CNNs são mais fáceis de usar e - para sinais curtos - melhor, vamos usá-las. A idéia de um NN convolucional está em "deslizar" uma "janela do sensor" sobre uma matriz de nossos dados (que é um sinal de áudio). Como resultado, em vez de analisar todos os dados simultaneamente, procuramos subpadrões locais. Cada camada seguinte do NN trabalha com padrões obtidos pelo anterior, procurando padrões de nível superior.

Para facilitar, imagine que precisamos localizar uma gaivota na foto de um oceano. Deslizamos uma pequena "janela" pela imagem, procurando um padrão branco "semelhante a uma marca de seleção". Agora, essa era uma rede neural convolucional 2D, semelhante a um sinal unidimensional, 1D CNN é uma escolha lógica.

O NN tinha a seguinte estrutura:

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

Esse NN possui duas saídas (prevê dois valores: doçura e maturação. A doçura pode ser 0 (não doce), 1 (normal) e 2 (excelente). Quanto à maturidade, pode ser 0 (muito rígida), 1 (boa ) e 2 - macio demais, como algodão com areia.

Para criar esses rótulos (saídas), precisamos de dados preparados por humanos, da maneira como eles são discutidos mais adiante, em um capítulo sobre o programa Android. O objetivo do nosso NN é prever, usando amostra de áudio, as estimativas que um ser humano faria.

Escrevendo um programa


Como mencionei, haverá duas versões de um programa: "dummy" e "final". Um manequim faz previsões aleatórias (e o usuário será avisado sobre isso). Ao fazer isso, ele grava amostras de áudio e as envia para o nosso servidor, juntamente com estimativas do usuário sobre a qualidade real da água mellon. Em outras palavras, o programa simplesmente coleta os dados.

Aqui está uma página da versão final de um programa, que é gratuita.

O que faz:

1. Quando o botão "microfone" é pressionado, uma gravação começa. O usuário tem cinco segundos para bater a água mellon três vezes, é bem parecido com bater na porta. Então você pode pressionar o botão "melancia" para obter a "previsão".

imagem

2. A gravação que fizemos é salva como um arquivo temporário. Por temporário, quero dizer que será substituído pela próxima gravação. Permite repetir as batidas se alguém falar na sala (você não acreditaria em como é difícil fazer as pessoas ficarem quietas por cinco segundos!), Ou a água escorre, ou algum outro barulho ocorre.

Ok, diga que a Water Mellon foi comprada e você a trouxe para casa. Você gravou e depois cortou, agora está pronto para estimar o sabor.

Escolha a guia "Salvar".

Nesta guia, temos duas caixas de combinação: doçura e maturação. Selecione os valores e clique em Salvar.

Importante! Você pode pressionar Salvar apenas uma vez! Isso é feito para impedir que os usuários enviem várias estimativas para a mesma mellon de água. Isso também significa que você deve selecionar os valores com cuidado e só então clicar em Salvar. Depois de salvar o resultado, um arquivo de áudio é renomeado e não será excluído na próxima vez que você gravar.

imagem

3. Finalmente, depois de fazer estimativas (comidas e consumidas) de doses de melancias, você retornou da vila onde tinha acesso ilimitado a melancias, mas não tinha Internet. Agora você está online. Abra a guia Enviar e pressione o botão Um pacote contendo informações para todas as melancias que você ainda não enviou, será enviado ao nosso servidor.

imagem

Escrevendo o programa do lado do servidor


É realmente fácil, então é melhor eu publicar o código fonte. O programa "captura" arquivos, atribui nomes exclusivos e os coloca em uma pasta que é acessível apenas ao proprietário do 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."; } ?> 

Treinando o nn


Dividimos os dados em treinamento (70%) e teste (30%). A Rede Neural converge bem, sem surpresas aqui. Uma observação para iniciantes: não se esqueça de normalizar os dados de entrada, pois você economizará muito tempo e nervos. Algo assim:

 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 para java


Existem poucas maneiras de portar o NN de Python para Java. Ultimamente, o Google tornou esse processo realmente conveniente. Portanto, se você decidir estudar livros didáticos, verifique se eles não são obsoletos. Aqui está como eu fiz isso:

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

Observe a última linha: no código Java, você precisará do nome das camadas de entrada e saída do NN. A declaração "print" exibe para nós.

Em seguida, colocamos o arquivo exportado na pasta "assets" do projeto Android Studio (o nome do arquivo é coordted.pb, não pergunte), adicionamos a biblioteca ( aqui , aqui ou melhor, aqui tensorflowinferenceinterface, e ... é isso.

É isso. Quando fiz pela primeira vez, esperava problemas, mas ... tudo funcionou.

Aqui está a chamada para o nosso NN a partir do 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; } 

Aqui "m_arrInput" - uma matriz que contém dois elementos, com nossa previsão (doçura, maturidade), no intervalo de 0 a 1.

Conclusão


Acredito que devo agradecer ao meu público pela atenção e expressar esperança de que seja interessante. Em vez disso, desejo a você mais melões de água doce e espero que você me envie novas amostras de áudio enquanto as come (melões, não amostras, hein!)

O programa é gratuito, é claro.

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


All Articles