TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(2)

简介: TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(2)

TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(1)https://developer.aliyun.com/article/1427020

现在,让我们看一下在 Flutter 应用中部署模型的步骤:

  1. 首先,由于我们正在获取数据并且将使用网络调用(即 HTTP 请求),因此我们需要向pubspec.yaml文件添加http依赖项,并按以下方式导入:
import 'package:http/http.dart' as http;
  1. 首先在auth.dart:内部定义的BaseAuth抽象类中添加以下函数声明
Future<double> isValidUser(String email, String password);
  1. 现在,让我们在Auth类中定义isValidUser()函数:
Future<double> isValidUser(String email, String password) async{
   final response = await http.Client()
       .get('http://34.67.160.232:8000/login?user=$email&password=$password');
     var jsonResponse = json.decode(response.body);
     var val = '${jsonResponse["result"]}';
     double result = double.parse(val);     
     return result;
   }

此函数将用户的电子邮件和密码作为参数,并将它们附加到请求 URL,以便为特定用户生成输出。 get request响应存储在变量响应中。 由于响应为 JSON 格式,因此我们使用json.decode()对其进行解码,并将解码后的响应存储在另一个变量响应中。 现在,我们使用‘${jsonResponse["result"]}'访问jsonResponse中的结果值,使用double.parse()将其转换为双精度类型整数,并将其存储在结果中。 最后,我们返回结果的值。

  1. 为了激活代码内部的恶意检测,我们从SigninSignupScreen调用了isValidUser()方法。 当具有现有帐户的用户选择从if-else块内部登录时,将调用此方法:
if (_formMode == FormMode.SIGNIN) {
    var val = await widget.auth.isValidUser(_usermail, _userpassword);
    . . . .
    } else {
      . . . .   
    }

isValidUser返回的值存储在val变量中。

  1. 如果该值小于 0.20,则表明登录活动是恶意的。 因此,我们将异常抛出并在 catch 块内抛出catch并在屏幕上显示错误消息。 这可以通过创建自定义异常类MalicousUserException来完成,该类在实例化时返回一条错误消息:
class MaliciousUserException implements Exception {
  String message() => 'Malicious login! Please try later.';
}
  1. 现在,我们将在调用isValidUser()之后添加if块,以检查是否需要抛出异常:
var val = await widget.auth.isValidUser(_usermail, _userpassword);
//Add the if block 
if(val < 0.20) {
    throw new MaliciousUserException();
}
  1. 现在,该异常已捕获在catch块内,并且不允许用户继续登录。此外,我们将_loading设置为false以表示不需要进一步的网络操作:
catch(MaliciousUserException) {
       setState(() {
         _loading = false;
           _errorMessage = 'Malicious user detected. Please try again later.';
       });

这就是一切! 我们之前基于 Firebase 认证创建的 Flutter 应用现在可以在后台运行智能模型的情况下找到恶意用户。

总结

在本章中,我们了解了如何使用 Flutter 和由 Firebase 支持的认证系统构建跨平台应用,同时结合了深度学习的优势。 然后,我们了解了如何将黑客攻击尝试归类为一般用户行为中的异常现象,并创建了一个模型来对这些异常现象进行分类以防止恶意用户登录。最后,我们使用了 Google 的 ReCaptcha 来消除对该应用的垃圾邮件使用,因此,使其在自动垃圾邮件或脚本化黑客攻击方面更具弹性。

在下一章中,我们将探索一个非常有趣的项目–使用移动应用上的深度学习生成音乐成绩单。

七、语音/多媒体处理 - 使用 AI 生成音乐

鉴于人工智能AI)的应用越来越多,将 AI 与音乐结合使用的想法已经存在了很长时间,并且受到了广泛的研究。 由于音乐是一系列音符,因此它是时间序列数据集的经典示例。 最近证明时间序列数据集在许多预测领域中非常有用–股市,天气模式,销售模式以及其他基于时间的数据集。 循环神经网络RNN)是处理时间序列数据集的最多模型之一。 对 RNN 进行的流行增强称为长短期记忆LSTM)神经元。 在本章中,我们将使用 LSTM 处理音符。

多媒体处理也不是一个新话题。 在本项目系列的早期,我们在多章中详细介绍了图像处理。 在本章中,我们将讨论并超越图像处理,并提供一个带有音频的深度学习示例。 我们将训练 Keras 模型来生成音乐样本,每次都会生成一个新样本。 然后,我们将此模型与 Flutter 应用结合使用,以通过 Android 和 iOS 设备上的音频播放器进行部署。

在本章中,我们将介绍以下主题:

  • 设计项目的架构
  • 了解多媒体处理
  • 开发基于 RNN 的音乐生成模型
  • 在 Android 和 iOS 上部署音频生成 API

让我们首先概述该项目的架构。

设计项目的架构

该项目的架构与作为应用部署的常规深度学习项目略有不同。 我们将有两组不同的音乐样本。 第一组样本将用于训练可以生成音乐的 LSTM 模型。 另一组样本将用作 LSTM 模型的随机输入,该模型将输出生成的音乐样本。 我们稍后将开发和使用的基于 LSTM 的模型将部署在 Google Cloud PlatformGCP)上。 但是,您可以将其部署在 AWS 或您选择的任何其他主机上。

下图总结了将在本项目中使用的不同组件之间的交互:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pw7KUWRD-1681785128417)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mobi-dl-tflite/img/50f17dc4-2658-4211-a7ff-9c41daafd884.png)]

移动应用要求部署在服务器上的模型生成新的音乐样本。 该模型使用随机音乐样本作为输入,以使其通过预先训练的模型来生成新的音乐样本。 然后,新的音乐样本由移动设备获取并播放给用户。

您可以将此架构与我们之前介绍的架构进行比较,在该架构中,将有一组用于训练的数据样本,然后将模型部署在云上或本地,并用于作出预测。

我们还可以更改此项目架构,以在存在为 Dart 语言编写的 midi 文件处理库的情况下在本地部署模型。 但是,在撰写本文时,还没有与我们在开发模型时使用的 Python midi 文件库的要求兼容的稳定库。

让我们从学习多媒体处理的含义以及如何使用 OpenCV 处理多媒体文件开始。

了解多媒体处理

多媒体是几乎所有形式的视觉,听觉或两者兼有的内容的总称。 术语多媒体处理本身非常模糊。 讨论该术语的更精确方法是将其分解为两个基本部分-视觉或听觉。 因此,我们将讨论多媒体处理的术语,即图像处理和音频处理。 这些术语的混合产生了视频处理,这只是多媒体的另一种形式。

在以下各节中,我们将以单独的形式讨论它们。

图像处理

图像处理或计算机视觉是迄今为止人工智能研究最多的分支之一。 在过去的几十年中,它发展迅速,并在以下几种技术的进步中发挥了重要作用:

  • 图像过滤器和编辑器
  • 面部识别
  • 数字绘画
  • 自动驾驶汽车

我们在较早的项目中讨论了图像处理的基础知识。 在这个项目中,我们将讨论一个非常流行的用于执行图像处理的库-OpenCV。 OpenCV 是开源计算机视觉的缩写。 它由 Intel 开发,并由 Willow Garage 和 Itseez(后来被 Intel 收购)推动。 毫无疑问,由于它与所有主要的机器学习框架(例如 TensorFlow,PyTorch 和 Caffe)兼容,因此它是执行图像处理的全球大多数开发人员的首要选择。 除此之外,OpenCV 还可以使用多种语言,例如 C++,Java 和 Python。

要在 Python 环境中安装 OpenCV,可以使用以下命令:

pip install opencv-contrib-python

前面的命令将同时安装主 OpenCV 模块和contrib模块。 您可以在此处找到更多模块供您选择。 有关更多安装说明,如果前面的链接不符合您的要求,则可以在此处遵循官方文档

让我们为您介绍一个非常简单的示例,说明如何使用 OpenCV 执行图像处理。 创建一个新的 Jupyter 笔记本,并从以下步骤开始:

  1. 要将 OpenCV 导入笔记本,请使用以下代码行:
import cv2
  1. 我们还要将 matplotlib 导入笔记本,因为如果您尝试使用本机 OpenCV 图像显示功能,Jupyter 笔记本将会崩溃:
from matplotlib import pyplot as plt
%matplotlib inline
  1. 让我们使用 matplotlib 为 OpenCV 的本机图像显示功能创建一个替代函数,以方便在笔记本中显示图像:
def showim(image):
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    plt.imshow(image)
    plt.show()

请注意,我们将图像的配色方案从蓝色绿色红色BGR)转换为红色绿色蓝色RGB)。 这是由于默认情况下 OpenCV 使用 BGR 配色方案。 但是,matplotlib 在显示图片时会使用 RGB 方案,并且如果不进行这种转换,我们的图像就会显得奇怪。

  1. 现在,让我们将图像读取到 Jupyter 笔记本中。 完成后,我们将能够看到加载的图像:
image = cv2.imread("Image.jpeg")
showim(image)

前面代码的输出取决于您选择加载到笔记本中的图像:

在我们的示例中,我们加载了柑橘类水果切片的图像,这是艾萨克·奎萨达(Isaac Quesada)在“Unsplash”上拍摄的惊人照片。

您可以在这里找到上一张图片。

  1. 让我们通过将之前的图像转换为灰度图像来进行简单的操作。 为此,我们就像在声明的showim()函数中那样简单地使用转换方法:
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
showim(gray_image)

这将产生以下输出:

  1. 现在让我们执行另一种常见的操作,即图像模糊。 在图像处理中通常采用模糊处理,以消除图像中信息的不必要的细节(此时)。 我们使用高斯模糊过滤器,这是在图像上创建模糊的最常见算法之一:
blurred_image = cv2.GaussianBlur(image, (7, 7), 0)
showim(blurred_image)

这将产生以下输出:

请注意,前面的图像不如原始图像清晰。 但是,它很容易达到愿意计算此图像中对象数量的目的。

  1. 为了在图像中定位对象,我们首先需要标记图像中的边缘。 为此,我们可以使用Canny()方法,该方法是 OpenCV 中可用的其他选项之一,用于查找图像的边缘:
canny = cv2.Canny(blurred_image, 10, 50)
showim(canny)

这将产生以下输出:

请注意,在上图中找到的边缘数量很高。 虽然这会显示图像的细节,但是如果我们尝试对边缘进行计数以尝试确定图像中的对象数量,这将无济于事。

  1. 让我们尝试计算上一步生成的图像中不同项目的数量:
contours, hierarchy= cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print("Number of objects found = ", len(contours))

上面的代码将产生以下输出:

Number of objects found = 18

但是,我们知道前面的图像中没有 18 个对象。 只有 9。因此,在寻找边缘时,我们将在canny方法中处理阈值。

  1. 让我们在 canny 方法中增加边缘发现的阈值。 这使得更难检测到边缘,因此仅使最明显的边缘可见:
canny = cv2.Canny(blurred_image, 50, 150)
showim(canny)

这将产生以下输出:

请注意,在柑橘类水果体内发现的边缘急剧减少,仅清晰可见其轮廓。 我们希望这会在计数时产生较少的对象。

  1. 让我们再次运行以下代码块:
contours, hierarchy= cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print("Number of objects found = ", len(contours))

这将产生以下输出:

Number of objects found = 9

这是期望值。 但是,只有在特殊情况下,该值才是准确的。

  1. 最后,让我们尝试概述检测到的对象。 为此,我们绘制了findContours()方法的上一步中确定的轮廓:
_ = cv2.drawContours(image, contours, -1, (0,255,0), 10)
showim(image)

这将产生以下输出:

请注意,我们已经在拍摄的原始图像中非常准确地识别出了九片水果。 我们可以进一步扩展此示例,以在任何图像中找到某些类型的对象。

要了解有关 OpenCV 的更多信息并找到一些可供学习的示例,请访问以下存储库

现在让我们学习如何处理音频文件。

音频处理

我们已经看到了如何处理图像以及可以从中提取信息。 在本节中,我们将介绍音频文件的处理。 音频或声音是吞没您周围环境的东西。 在许多情况下,您仅能从该区域的音频剪辑中正确预测该区域或环境,而无需实际看到任何视觉提示。 声音或语音是人与人之间交流的一种形式。 安排良好的节奏模式形式的音频称为音乐,可以使用乐器制作。

音频文件的一些流行格式如下:

  • MP3:一种非常流行的格式,广泛用于共享音乐文件。
  • AAC:是对 MP3 格式的改进,AAC 主要用于 Apple 设备。
  • WAV:由 Microsoft 和 IBM 创建,这种格式是无损压缩,即使对于小的音频文件也可能很大。
  • MIDI:乐器数字接口文件实际上不包含音频。 它们包含乐器音符,因此体积小且易于使用。

音频处理是以下技术的增长所必需的:

  • 用于基于语音的界面或助手的语音处理
  • 虚拟助手的语音生成
  • 音乐生成
  • 字幕生成
  • 推荐类似音乐

TensorFlow 团队的 Magenta 是一种非常流行的音频处理工具。

您可以通过这里访问 Magenta 主页。 该工具允许快速生成音频和音频文件的转录。

让我们简要地探讨 Magenta。

Magenta

Magenta 是 Google Brain 团队参与研究的一部分,该团队也参与了 TensorFlow。 它被开发为一种工具,可允许艺术家借助深度学习和强化学习算法来增强其音乐或艺术创作渠道。 这是 Magenta 的徽标:

让我们从以下步骤开始:

  1. 要在系统上安装 Magenta,可以使用 Python 的 pip 存储库:
pip install magenta
  1. 如果缺少任何依赖项,则可以使用以下命令安装它们:
!apt-get update -qq && apt-get install -qq libfluidsynth1 fluid-soundfont-gm build-essential libasound2-dev libjack-dev
!pip install -qU pyfluidsynth pretty_midi
  1. 要将 Magenta 导入项目中,可以使用以下命令:
import magenta

或者,按照流行的惯例,仅加载 Magenta 的音乐部分,可以使用以下命令:

import magenta.music as mm

您可以使用前面的导入在线找到很多样本。

让我们快速创作一些音乐。 我们将创建一些鼓声,然后将其保存到 MIDI 文件:

  1. 我们首先需要创建一个NoteSequence对象。 在 Magenta 中,所有音乐都以音符序列的格式存储,类似于 MIDI 存储音乐的方式:
from magenta.protobuf import music_pb2
drums = music_pb2.NoteSequence()
  1. 创建NoteSequence对象后,该对象为空,因此我们需要向其添加一些注解:
drums.notes.add(pitch=36, start_time=0, end_time=0.125, is_drum=True, instrument=10, velocity=80)
drums.notes.add(pitch=38, start_time=0, end_time=0.125, is_drum=True, instrument=10, velocity=80)
drums.notes.add(pitch=42, start_time=0, end_time=0.125, is_drum=True, instrument=10, velocity=80)
drums.notes.add(pitch=46, start_time=0, end_time=0.125, is_drum=True, instrument=10, velocity=80)
.
.
.
drums.notes.add(pitch=42, start_time=0.75, end_time=0.875, is_drum=True, instrument=10, velocity=80)
drums.notes.add(pitch=45, start_time=0.75, end_time=0.875, is_drum=True, instrument=10, velocity=80)

请注意,在前面的代码中,每个音符都有音高和力度。 再次类似于 MIDI 文件。

  1. 现在让我们为音符添加节奏,并设置音乐播放的总时间:
drums.total_time = 1.375
drums.tempos.add(qpm=60)

完成此操作后,我们现在准备导出 MIDI 文件。

  1. 我们首先需要将 MagentaNoteSequence对象转换为 MIDI 文件:
mm.sequence_proto_to_midi_file(drums, 'drums_sample_output.mid')

前面的代码首先将音符序列转换为 MIDI,然后将它们写入磁盘上的drums_sample_output.mid文件。 您现在可以使用任何合适的音乐播放器播放midi文件。

继续前进,让我们探索如何处理视频。

视频处理

视频处理是多媒体处理的另一个重要部分。 通常,我们需要弄清楚移动场景中发生的事情。 例如,如果我们要生产自动驾驶汽车,则它需要实时处理大量视频才能平稳行驶。 这种情况的另一个实例可以是将手语转换为文本以帮助与语音障碍者互动的设备。 此外,需要视频处理来创建电影和动作效果。

我们将在本节中再次探讨 OpenCV。 但是,我们将演示如何在 OpenCV 中使用实时摄像机供稿来检测面部。

创建一个新的 Python 脚本并执行以下步骤:

  1. 首先,我们需要对脚本进行必要的导入。 这将很简单,因为我们只需要 OpenCV 模块:
import cv2
  1. 现在,让我们将 Haar 级联模型加载到脚本中。 Haar 级联算法是一种用于检测任何给定图像中的对象的算法。 由于视频不过是图像流,因此我们将其分解为一系列帧并检测其中的人脸:
faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

您将不得不从以下位置获取haarcascade_frontalface_default.xml文件

Haar 级联是一类使用级联函数执行分类的分类器算法。 保罗·维奥拉(Paul Viola)和迈克尔·琼斯(Michael Jones)引入了它们,以试图建立一种对象检测算法,该算法足够快以在低端设备上运行。 级联函数池来自几个较小的分类器。

Haar 级联文件通常以可扩展标记语言XML)的格式找到,并且通常执行一项特定功能,例如面部检测,身体姿势检测, 对象检测等。 您可以在此处阅读有关 Haar 级联的更多信息

  1. 现在,我们必须实例化摄像机以进行视频捕获。 为此,我们可以使用默认的笔记本电脑摄像头:
video_capture = cv2.VideoCapture(0)
  1. 现在让我们从视频中捕获帧并显示它们:
while True:
    # Capture frames
    ret, frame = video_capture.read()
    ### We'll add code below in future steps
    ### We'll add code above in future steps
    # Display the resulting frame
    cv2.imshow('Webcam Capture', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

这样您就可以在屏幕上显示实时视频供稿。 在运行此程序之前,我们需要释放相机并正确关闭窗户。

  1. 要正确关闭实时捕获,请使用以下命令:
video_capture.release()
cv2.destroyAllWindows()

现在,让我们对脚本进行测试运行。

您应该会看到一个窗口,其中包含您的脸部实时捕捉的图像(如果您不害羞的话)。

  1. 让我们向该视频提要添加面部检测。 由于用于面部检测的 Haar 级联在使用灰度图像时效果更好,因此我们将首先将每个帧转换为灰度,然后对其进行面部检测。 我们需要将此代码添加到while循环中,如以下代码所示:
### We'll add code below in future steps
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30),
        flags=cv2.CASCADE_SCALE_IMAGE
    )
    ### We'll add code above in future steps

这样,我们就可以检测到人脸了,因此让我们在视频供稿中对其进行标记!

  1. 我们将简单地使用 OpenCV 的矩形绘制函数在屏幕上标记面孔:
minNeighbors=5,
        minSize=(30, 30),
        flags=cv2.CASCADE_SCALE_IMAGE
    )
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
    ### We'll add code above in future steps

现在让我们再次尝试运行脚本。

转到终端并使用以下命令运行脚本:

python filename.py

在这里,文件名是您保存脚本文件时的名称。

您应该获得类似于以下屏幕截图的输出:

要退出实时网络摄像头捕获,请使用键盘上的Q键(我们已在前面的代码中进行了设置)。

我们已经研究了多媒体处理的三种主要形式的概述。 现在,让我们继续前进,构建基于 LSTM 的模型以生成音频。

开发基于 RNN 的音乐生成模型

在本节中,我们将开发音乐生成模型。 我们将为此使用 RNN,并使用 LSTM 神经元模型。 RNN 与简单的人工神经网络ANN)有很大的不同-允许在层之间重复使用输入。

虽然在 ANN 中,我们希望输入到神经网络的输入值向前移动,然后产生基于错误的反馈,并将其合并到网络权重中,但 RNN 使输入多次循环返回到先前的层。

下图表示 RNN 神经元:

从上图可以看到,通过神经元激活函数后的输入分为两部分。 一部分在网络中向前移动到下一层或输出,而另一部分则反馈到网络中。 在时间序列数据集中,可以相对于给定样本在t的时间标记每个样本,我们可以扩展前面的图,如下所示:

但是,由于通过激活函数反复暴露值,RNN 趋向于梯度消失,其中 RNN 的值逐梯度小到可以忽略不计(或在梯度爆炸的情况下变大)。 为避免这种情况,引入了 LSTM 单元,该单元通过将信息存储在单元中而允许将信息保留更长的时间。 每个 LSTM 单元由三个门和一个存储单元组成。 三个门(输入,输出和遗忘门)负责确定哪些值存储在存储单元中。

因此,LSTM 单元变得独立于 RNN 其余部分的更新频率,并且每个单元格都有自己的时间来记住它所拥有的值。 就我们而言,与其他信息相比,我们忘记了一些随机信息的时间要晚得多,这更自然地模仿了自然。

您可以在以下链接中找到有关 RNN 和 LSTM 的详细且易于理解的解释

在开始为项目构建模型之前,我们需要设置项目目录,如以下代码所示:

├── app.py
├── MusicGenerate.ipynb
├── Output/
└── Samples/
    ├── 0.mid
    ├── 1.mid
    ├── 2.mid
    └── 3.mid

请注意,我们已经在Samples文件夹中下载了四个 MIDI 文件样本。 然后,我们创建了要使用的MusicGenerate.ipynb Jupyter 笔记本。 在接下来的几个步骤中,我们将仅在此 Jupyter 笔记本上工作。 app.py脚本当前为空,将来,我们将使用它来托管模型。

现在让我们开始创建基于 LSTM 的用于生成音乐的模型。

创建基于 LSTM 的模型

在本节中,我们将在 Jupyter 笔记本环境中研究MusicGenerate.ipynb笔记本:

  1. 在此笔记本中,我们将需要导入许多模块。 使用以下代码导入它们:
import mido
from mido import MidiFile, MidiTrack, Message
from tensorflow.keras.layers import LSTM, Dense, Activation, Dropout, Flatten
from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
from sklearn.preprocessing import MinMaxScaler
import numpy as np

我们使用了mido库。 如果您的系统上未安装它,则可以使用以下命令来安装它:

pip install mido

注意,在前面的代码中,我们还导入了 Keras 模块和子部件。 该项目中使用的 TensorFlow 版本为 2.0。 为了在您的系统上安装相同版本或升级当前的 TensorFlow 安装,可以使用以下命令:

pip install --upgrade pip
pip install --upgrade tensorflow

现在,我们将继续阅读示例文件。

  1. 要将 MIDI 文件读入项目笔记本,请使用以下代码:
notes = []
for msg in MidiFile('Samples/0.mid') :
    try:
        if not msg.is_meta and msg.channel in [0, 1, 2, 3] and msg.type == 'note_on':
            data = msg.bytes()
            notes.append(data[1])
    except:
        pass

这将在notes列表中加载通道0123的所有开头音符。

要了解有关注解,消息和频道的更多信息,请使用以下文档

  1. 由于音符处于大于 0–1 范围的可变范围内,因此我们将使用以下代码将其缩放以适合公共范围:
scaler = MinMaxScaler(feature_range=(0,1))
scaler.fit(np.array(notes).reshape(-1,1))
notes = list(scaler.transform(np.array(notes).reshape(-1,1)))
  1. 我们基本上拥有的是随时间变化的笔记列表。 我们需要将其转换为时间序列数据集格式。 为此,我们使用以下代码转换列表:
notes = [list(note) for note in notes]
X = []
y = []
n_prev = 20
for i in range(len(notes)-n_prev):
    X.append(notes[i:i+n_prev])
    y.append(notes[i+n_prev])

我们已将其转换为一个集合,其中每个样本都带有未来的 20 个音符,并且在数据集的末尾具有过去的 20 个音符。这可以通过以下方式进行:如果我们有 5 个样本,例如M[1]M[2]M[3]M[4]M[5],然后我们将它们安排在大小为 2 的配对中(类似于我们的 20),如下所示:

  • M[1] M[2]
  • M[2] M[3]
  • M[3] M[4],依此类推
  1. 现在,我们将使用 Keras 创建 LSTM 模型,如以下代码所示:
model = Sequential()
model.add(LSTM(256, input_shape=(n_prev, 1), return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(128, input_shape=(n_prev, 1), return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(256, input_shape=(n_prev, 1), return_sequences=False))
model.add(Dropout(0.3))
model.add(Dense(1))
model.add(Activation('linear'))
optimizer = Adam(lr=0.001)
model.compile(loss='mse', optimizer=optimizer)

随意使用此 LSTM 模型的超参数。

  1. 最后,我们将训练样本适合模型并保存模型文件:
model.fit(np.array(X), np.array(y), 32, 25, verbose=1)
model.save("model.h5")

这将在我们的项目目录中创建model.h5文件。 每当用户从应用发出生成请求时,我们都会将此文件与其他音乐样本一起使用,以随机生成新的乐曲。

现在,让我们使用 Flask 服务器部署此模型。

使用 Flask 部署模型

对于项目的这一部分,您可以使用本地系统,也可以在其他地方的app.py中部署脚本。 我们将编辑此文件以创建 Flask 服务器,该服务器生成音乐并允许下载生成的 MIDI 文件。

该文件中的某些代码与 Jupyter 笔记本类似,因为每次加载音频样本并将其与我们生成的模型一起使用时,音频样本始终需要进行类似的处理:

  1. 我们使用以下代码将所需的模块导入此脚本:
import mido
from mido import MidiFile, MidiTrack, Message
from tensorflow.keras.models import load_model
from sklearn.preprocessing import MinMaxScaler
import numpy as np
import random
import time
from flask import send_file
import os
from flask import Flask, jsonify
app = Flask(__name__)

请注意,我们进行的最后四次导入与之前在 Jupyter 笔记本中导入的内容不同。 同样,我们不需要将几个 Keras 组件导入此脚本,因为我们将从已经准备好的模型中加载。

在上一个代码块的最后一行代码中,我们实例化了一个名为app的 Flask 对象。

  1. 在此步骤中,我们将创建函数的第一部分,当在 API 上调用/generate路由时,该函数将生成新的音乐样本:
@app.route('/generate', methods=['GET'])
def generate():
    songnum = random.randint(0, 3)
    ### More code below this
  1. 一旦我们随机决定在音乐生成过程中使用哪个样本文件,我们就需要像 Jupyter 笔记本中的训练样本那样对它进行类似的转换:
def generate():
    .
    .
    .    
    notes = []
    for msg in MidiFile('Samples/%s.mid' % (songnum)):
        try:
            if not msg.is_meta and msg.channel in [0, 1, 2, 3] and msg.type == 'note_on':
                data = msg.bytes()
                notes.append(data[1])
        except:
            pass
    scaler = MinMaxScaler(feature_range=(0, 1))
    scaler.fit(np.array(notes).reshape(-1, 1))
    notes = list(scaler.transform(np.array(notes).reshape(-1, 1)))
    ### More code below this

在前面的代码块中,我们加载了示例文件,并从训练过程中使用的相同通道中提取了其注解。

  1. 现在,我们将像在训练期间一样缩放音符:
def generate():
    .
    .
    .     
    notes = [list(note) for note in notes]
    X = []
    y = []
    n_prev = 20
    for i in range(len(notes) - n_prev):
        X.append(notes[i:i + n_prev])
        y.append(notes[i + n_prev])
    ### More code below this

我们也将这些笔记列表转换为适合模型输入的形状,就像我们在训练过程中对输入所做的一样。

  1. 接下来,我们将使用以下代码来加载 Keras 模型并从该模型创建新的注解列表:
def generate():
    .
    .
    . 
    model = load_model("model.h5")
    xlen = len(X)
    start = random.randint(0, 100)
    stop = start + 200
    prediction = model.predict(np.array(X[start:stop]))
    prediction = np.squeeze(prediction)
    prediction = np.squeeze(scaler.inverse_transform(prediction.reshape(-1, 1)))
    prediction = [int(i) for i in prediction]    
    ### More code below this
  1. 现在,我们可以使用以下代码将此音符列表转换为 MIDI 序列:
def generate():
    .
    .
    . 
    mid = MidiFile()
    track = MidiTrack()
    t = 0
    for note in prediction:
        vol = random.randint(50, 70)
        note = np.asarray([147, note, vol])
        bytes = note.astype(int)
        msg = Message.from_bytes(bytes[0:3])
        t += 1
        msg.time = t
        track.append(msg)
    mid.tracks.append(track)
    ### More code below this
  1. 现在,我们准备将文件保存到磁盘。 它包含从模型随机生成的音乐:
def generate():
    .
    .
    . 
    epoch_time = int(time.time())
    outputfile = 'output_%s.mid' % (epoch_time)
    mid.save("Output/" + outputfile)
    response = {'result': outputfile}
    return jsonify(response)

因此,/generate API 以 JSON 格式返回生成的文件的名称。 然后,我们可以下载并播放此文件。

  1. 要将文件下载到客户端,我们需要使用以下代码:
@app.route('/download/<fname>', methods=['GET'])
def download(fname):
    return send_file("Output/"+fname, mimetype="audio/midi", as_attachment=True)

请注意,前面的函数在/download/filename路由上起作用,在该路由上,客户端根据上一代 API 调用的输出提供文件名。 下载的文件的 MIME 类型为audio/midi,它告诉客户端它是 MIDI 文件。

  1. 最后,我们可以添加将执行此服务器的代码:
if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8000)

完成此操作后,我们可以在终端中使用以下命令来运行服务器:

python app.py

如果代码中产生任何警告,您将从控制台获得一些调试信息。 完成此操作后,我们准备在下一节中为我们的 API 构建 Flutter 应用客户端。

在 Android 和 iOS 上部署音频生成 API

成功创建和部署模型后,现在开始构建移动应用。 该应用将用于获取和播放由先前创建的模型生成的音乐。

它将具有三个按钮:

  • 生成音乐:生成新的音频文件
  • 播放:播放新生成的文件
  • 停止:停止正在播放的音乐

另外,它的底部将显示一些文本,以显示应用的当前状态。

该应用将显示如下:

该应用的小部件树如下所示:

现在开始构建应用的 UI。

创建 UI

我们首先创建一个新的 Dart 文件play_music.dart和一个有状态的小部件PlayMusic。 如前所述,在该文件中,我们将创建三个按钮来执行基本功能。 以下步骤描述了如何创建 UI:

  1. 定义buildGenerateButton()方法以创建RaisedButton变量,该变量将用于生成新的音乐文件:
Widget buildGenerateButton() {
   return Padding(
     padding: EdgeInsets.only(left: 16, right: 16, top: 16),
     child: RaisedButton(
       child: Text("Generate Music"),
       color: Colors.blue,
       textColor: Colors.white,
     ),
   );
 }

在前面定义的函数中,我们创建一个RaisedButton,并添加Generate Music文本作为子元素。 color属性的Colors.blue值用于为按钮赋予蓝色。 另外,我们将textColor修改为Colors.white,以使按钮内的文本为白色。 使用EdgeInsets.only()给按钮提供左,右和顶部填充。 在后面的部分中,我们将在按钮上添加onPressed属性,以便每次按下按钮时都可以从托管模型中获取新的音乐文件。

  1. 定义buildPlayButton()方法以播放新生成的音频文件:
Widget buildPlayButton() {
   return Padding(
   padding: EdgeInsets.only(left: 16, right: 16, top: 16),
   child: RaisedButton(
     child: Text("Play"),
     onPressed: () {
       play();
     },
     color: Colors.blue,
     textColor: Colors.white,
     ),
   );
 }

在前面定义的函数中,我们创建一个RaisedButton,并添加"Play"文本作为子元素。 color属性的Colors.blue值用于为按钮赋予蓝色。 另外,我们将textColor修改为Colors.white,以使按钮内的文本为白色。 使用EdgeInsets.only()给按钮提供左,右和顶部填充。 在后面的部分中,我们将在按钮上添加onPressed属性,以在每次按下按钮时播放新生成的音乐文件。

  1. 定义buildStopButton()方法以停止当前正在播放的音频:
Widget buildStopButton() {
   return Padding(
     padding: EdgeInsets.only(left: 16, right: 16, top: 16),
     child: RaisedButton(
       child: Text("Stop"),
       onPressed: (){
         stop();
       },
       color: Colors.blue,
       textColor: Colors.white,
     )
   );
 }

在前面定义的函数中,我们创建一个RaisedButton,并添加"Stop"文本作为子元素。 color属性的Colors.blue值用于为按钮赋予蓝色。 另外,我们将textColor修改为Colors.white,以使按钮内的文本为白色。 使用EdgeInsets.only()给按钮提供左,右和顶部填充。 在下一节中,我们将向按钮添加onPressed属性,以在按下按钮时停止当前播放的音频。

  1. 覆盖PlayMusicState中的build()方法,以创建先前创建的按钮的Column
@override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text("Generate Play Music"),
     ),
     body: Column(
       crossAxisAlignment: CrossAxisAlignment.stretch,
       children: <Widget>[
         buildGenerateButton(),
         buildPlayButton(),
         buildStopButton(),
       ],
     )
   );
 }

在前面的代码片段中,我们返回Scaffold。 它包含一个AppBar,其中具有[Generate Play Music]作为titleScaffold的主体是Column。 列的子级是我们在上一步中创建的按钮。 通过调用相应方法将按钮添加到该列中。 此外,crossAxisAlignment属性设置为CrossAxisAlignment.stretch,以便按钮占据父容器(即列)的总宽度。

此时,该应用如下所示:

在下一节中,我们将添加一种在应用中播放音频文件的机制。

添加音频播放器

创建应用的用户界面后,我们现在将音频播放器添加到应用中以播放音频文件。 我们将使用audioplayer插件添加音频播放器,如下所示:

  1. 我们首先将依赖项添加到pubspec.yaml文件中:
audioplayers: 0.13.2

现在,通过运行flutter pub get获得包。

  1. 接下来,我们将插件导入play_music.dart
import 'package:audioplayers/audioplayers.dart';
  1. 然后,在PlayMusicState内创建AudioPlayer的实例:
AudioPlayer audioPlayer = AudioPlayer();
  1. 现在,让我们定义一个play()方法来播放远程可用的音频文件,如下所示:
play() async {
   var url = 'http://34.70.80.18:8000/download/output_1573917221.mid';
   int result = await audioPlayer.play(url);
   if (result == 1) {
     print('Success');
     }
 }

最初,我们将使用存储在url变量中的样本音频文件。 通过传递url中的值,使用audioPlayer.play()播放音频文件。 另外,如果从url变量成功访问和播放了音频文件,则结果将存储在结果变量中,其值将为1

  1. 现在,将onPressed属性添加到buildPlayButton内置的播放按钮中,以便每当按下该按钮时就播放音频文件:
Widget buildPlayButton() {
   return Padding(
   padding: EdgeInsets.only(left: 16, right: 16, top: 16),
   child: RaisedButton(
     ....
     onPressed: () {
       play();
     },
     ....
     ),
   );
 }

在前面的代码片段中,我们添加onPressed属性并调用play()方法,以便每当按下按钮时就播放音频文件。

  1. 现在,我们将定义stop()以停止正在播放的音乐:
void stop() {
   audioPlayer.stop();
 }

stop()方法内部,我们只需调用audioPlayer.stop()即可停止正在播放的音乐。

  1. 最后,我们为buildStopButton()中内置的停止按钮添加onPressed属性:
Widget buildStopButton() {
   return Padding(
     padding: EdgeInsets.only(left: 16, right: 16, top: 16),
     child: RaisedButton(
       ....
       onPressed: (){
         stop();
       },
       ....
     )
   );
 }

在前面的代码片段中,我们向onPressed中的stop()添加了一个调用,以便一旦按下停止按钮就停止音频。

现在开始使用 Flutter 应用部署模型。

TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(3)https://developer.aliyun.com/article/1427022

相关文章
|
17天前
|
机器学习/深度学习 API 语音技术
|
3月前
|
机器学习/深度学习 Dart TensorFlow
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(5)
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(5)
72 0
|
10天前
|
机器学习/深度学习 运维 监控
TensorFlow分布式训练:加速深度学习模型训练
【4月更文挑战第17天】TensorFlow分布式训练加速深度学习模型训练,通过数据并行和模型并行利用多机器资源,减少训练时间。优化策略包括配置计算资源、优化数据划分和减少通信开销。实际应用需关注调试监控、系统稳定性和容错性,以应对分布式训练挑战。
|
2月前
|
机器学习/深度学习 PyTorch TensorFlow
Python中的深度学习:TensorFlow与PyTorch的选择与使用
Python中的深度学习:TensorFlow与PyTorch的选择与使用
|
2月前
|
机器学习/深度学习 数据可视化 TensorFlow
基于tensorflow深度学习的猫狗分类识别
基于tensorflow深度学习的猫狗分类识别
63 1
|
3月前
|
机器学习/深度学习 PyTorch TensorFlow
【TensorFlow】深度学习框架概述&TensorFlow环境配置
【1月更文挑战第26天】【TensorFlow】深度学习框架概述&TensorFlow环境配置
|
3月前
|
机器学习/深度学习 人工智能 API
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:1~5
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:1~5
71 0
|
3月前
|
机器学习/深度学习 存储 人工智能
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(3)
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(3)
81 0
|
3月前
|
机器学习/深度学习 存储 编解码
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(4)
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(4)
122 0
|
3月前
|
机器学习/深度学习 存储 运维
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(1)
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:6~11(1)
56 0

热门文章

最新文章