使用神经网络测试西瓜:完整版。 从原型到应用程序的循环。 在Google Play

开始


当我找到一个应用程序时,一切就开始了。 在苹果市场上,据说可以确定西瓜的成熟度。 一个程序很奇怪。 试想一下:您应该不用用指关节敲门,而是应该用iPhone来敲西瓜! 尽管如此,我还是决定在Andtoid平台上重复该功能。

选择工具


解决我们存在的问题的方法很少,我不得不付出一定的努力来避免一个简单的问题。 即傅立叶变换,小波和信号编辑器。 我毕竟想学习神经网络。

我选择Keras作为NN库,这是Google的出色工具,是TensorFlow和Theano的包装。 如果您是初学者,那绝对是最好的工具。 一方面,Keras是功能强大的工具,通过速度,内存和硬件进行了优化(是的,它可与GPU一起使用)。 另一方面-它向用户隐藏了所有无聊的人员,从而使您可以专注于任务本身。 很方便

Keras和一般的神经网络通常都与Python捆绑在一起,Python就像一条巨蛇……没关系。 无论如何,为了在深度学习领域工作,人们需要了解Python。 幸运的是,它是一种易于学习的语言。

除了Python外,您还需要一些额外的库,但是掌握它们很容易-即使与Python本身相比也是如此。 您需要具有NumPy,PyPlot以及其他几个库的(浅)经验。

最后值得一提的是,我们不需要GPU集群:我们的问题可以在单个CPU上解决,速度很慢,但不是很慢。

工作计划


首先,我们需要在Ubuntu环境中使用Python和Keras创建一个神经网络。 可以在Windows中做到这一点,但是花时间配置东西应该足以学习Ubuntu。

下一步是编写程序。 我计划在Java for Android上执行此操作。 这将是一个原型,这意味着它将具有UI,但没有NN-尚未。

为什么我们必须编写一个虚拟程序? 这就是诀窍:任何深度学习都需要数据来学习。 为了向NN提供足够数量的数据>百,我应该测试多少个西瓜(在这个词的两个含义中)? 还有吗

在这里,我们将使用虚拟程序:我将其放置在Google Play上,将其赠送(好吧,强迫我所有的朋友安装它)并从中收集数据……在哪里?

下一步是编写一个服务器端程序,该程序将从我们的Android客户端接收数据。 这是一个非常简单的任务,我花了大约二十分钟来写,但是仍然是一个单独的步骤。

最后,当我们有足够的数据时,我们可以教NN。

然后,我们需要将生成的NN移植到Java并发布程序的新版本,以替换“虚拟”程序。

获利 不,等等 该程序是免费的。 只是经验。

创建nn


处理音频信号(肯定敲响了西瓜)意味着使用递归神经网络或所谓的一个二维CNN。 由于CNN易于使用,并且-对于短信号-更好,我们将使用它们。 卷积神经网络的想法是在数据数组(是音频信号)上“滑动”“传感器窗口”。 结果,我们不是在同时分析所有数据,而是在寻找局部子模式。 NN的每个后续层都与前一层所获得的模式一起使用,以寻找更高级别的模式。

为了简化起见,假设我们必须在海洋照片中找到一只海鸥。 我们在图片上滑动一个小的“窗口”,寻找白色的“类似复选标记”的图案。 现在,这是一个2D卷积神经网络,需要一维信号,因此1D CNN是一个合理的选择。

NN具有以下结构:

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

该NN具有两个输出(它预测两个值:甜度和成熟度。甜度可以为0(不甜),1(正常)和2(优异)。至于成熟度,可以为0(太僵硬),1(好) )和2-太软,像带沙子的棉花。

要创建这些标签(输出),我们需要人类准备的数据,稍后将在有关Android程序的一章中讨论其完成方式。 我们的神经网络的目标是使用音频样本预测人类会做出的估计。

编写程序


正如我提到的,程序将有两个版本:“虚拟”和“最终”。 一个虚拟的人会进行随机预测(并且会警告用户)。 这样,它会记录音频样本并将其与用户对西瓜真实质量的估计一起发送到我们的服务器。 换句话说,该程序只是收集数据。

这是程序最终版本的页面 ,它是免费的。

它的作用:

1.当按下“麦克风”按钮时,开始录音。 用户有五秒钟的时间敲打西瓜三遍,这与敲门非常相似。 然后您可以按“西瓜”按钮以获取“预测”。

图片

2.我们制作的录音将保存为临时文件。 临时的,我的意思是下一次录音将覆盖它。 如果有人在房间里讲话,它可以重复敲门(您不会相信让人们安静五秒钟是多么困难!),水流淌或有其他噪音。

好的,说买了西瓜,您已经把它带回家了。 您已经制作了录音,然后将其剪切,现在可以估计其味道了。

选择“保存”标签。

在此选项卡上,我们有两个组合框:甜度和成熟度。 选择值,然后单击“保存”。

重要! 您只能按一次保存! 这样做是为了防止用户发送同一西瓜的多个估计值。 这也意味着您必须仔细选择值,然后才能单击“保存”。 保存结果后,音频文件将重命名,并且在下次录制时不会删除。

图片

3.最后,在对西瓜的食用量进行了估算(食用)之后,您从没有限制地使用西瓜但没有互联网的村庄返回。 现在您在线。 打开“提交”选项卡,然后按按钮。 包含您尚未提交的所有西瓜信息的软件包将被发送到我们的服务器。

图片

编写服务器端程序


这确实很容易,所以我最好只发布源代码。 程序“捕获”文件,分配唯一的名称,并将其放置到仅网站所有者可访问的文件夹中。

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

训练nn


我们将数据分为训练(70%)和测试(30%)。 神经网络融合良好,在这里不足为奇。 初学者注意事项:不要忘记对输入数据进行规范化,它将为您节省大量时间和精力。 像这样:

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

移植到Java


有几种方法可以将NN从Python移植到Java。 最近,Google使此过程非常方便,因此,如果您决定学习教科书,请确保它们不再过时。 这是我的做法:

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

请注意最后一行:在Java代码中,您将需要NN的输入和输出层的名称。 “ print”语句为我们显示它们。

接下来,我们将导出的文件放置到Android Studio项目的“ assets”文件夹中(文件名是coordted.pb,不要问),添加库( 在这里这里或更好的地方, 在这里 tensorflowinferenceinterface,就这样。)。

就是这样 当我第一次这样做时,我曾预料会有问题,但是……一切都正常了。

这是从Java代码到我们的NN的调用:

  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; } 

这里的“ m_arrInput”-包含两个元素的数组,我们的预测(甜度,成熟度)在0到1范围内。

结论


我相信我应该感谢听众的关注,并希望它很有趣。 取而代之的是,我希望您有更多甜甜的西瓜,我希望您在吃它们时能给我发送新的音频样本(甜瓜,不是样本,是吧!)

该程序是免费的,当然。

Source: https://habr.com/ru/post/zh-CN481974/


All Articles