大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
个人主页-Sonhhxg_柒的博客_CSDN博客
欢迎各位→点赞 + 收藏⭐️ + 留言
系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟
文章目录
什么是安卓工作室?
创建您的第一个 TensorFlow Lite Android 应用程序
步骤 1. 创建一个新的 Android 项目
第 2 步。编辑布局文件
步骤 3. 添加 TensorFlow Lite 依赖项
第 4 步。添加您的 TensorFlow Lite 模型
第 5 步。编写活动代码以使用 TensorFlow Lite 进行推理
超越“Hello World”——处理图像
TensorFlow Lite 示例应用程序
概括
第12章介绍TensorFlow Lite 是一套工具,可帮助您将模型转换为可由移动或嵌入式系统使用的格式。在接下来的几章中,您将了解如何在各种运行时环境中使用这些模型。在这里,您将了解如何创建使用 TensorFlow Lite 模型的 Android 应用程序。我们将从快速探索用于创建 Android 应用程序的主要工具开始:Android Studio。
安卓工作室是一个集成开发环境 (IDE),用于为各种设备(从手机和平板电脑到电视、汽车、手表等)开发 Android 应用程序。在本章中,我们将专注于将其用于手机应用程序。它可以免费下载,并且有适用于所有主要操作系统的版本。
Android Studio 为您提供的好处之一是 Android 模拟器,因此您无需拥有物理设备即可试用应用程序。您将在本章中广泛使用它!传统上,Android 应用程序是使用 Java 编程语言构建的,但最近 Google 引入了从 Kotlin 到 Android Studio,您将在本章中使用该语言。
KOTLIN?
Kotlin是一种现代的开源语言,它与 Java 一起是构建 Android 应用程序的主要编程语言。它旨在简洁,减少您需要编写的样板代码量。它还旨在对程序员友好,帮助您避免许多常见类型的错误(例如空指针异常,使用内置的可空类型)。它也不是进化的死胡同,可以与先前存在的库互操作。这一点尤为重要,因为 Android 有 Java 语言的发展历史,因此为 Java 构建了许多库。
如果您还没有 Android Studio,请立即安装。设置、更新和准备好一切可能需要一些时间。在接下来的几页中,我将引导您创建一个新应用程序、设计其用户界面、添加 TensorFlow Lite 依赖项,然后对其进行编码以进行推理。这将是一个非常简单的应用程序——您在其中输入一个值,它会执行推理并计算 Y = 2X – 1,其中 X 是您输入的值。对于如此简单的功能来说,这实在是太过分了,但是像这样的应用程序的脚手架几乎与复杂得多的应用程序的脚手架相同。
一次启动并运行 Android Studio 后,您可以使用文件 → 新建 → 新建项目创建一个新应用,这将打开“新建项目”对话框(图 13-1)。
选择Empty Activity,如图13-1所示。这是最简单的 Android 应用程序,预先存在的代码很少。按 Next,您将进入 Configure Your Project 对话框(图 13-2)。
在此对话框中,如图所示将名称设置为FirstTFLite,并确保语言为 Kotlin。最低 SDK 级别可能会默认为 API 23,如果您愿意,可以保留它。
完成后,按完成。Android Studio 现在将为您的应用创建所有代码。Android 应用程序需要大量文件。您创建的单个活动有一个定义其外观的布局文件(XML 格式),以及一个用于关联源的.kt (Kotlin) 文件。还有几个配置文件定义了应如何构建应用程序、应使用哪些依赖项以及它的资源、资产等。一开始可能会让人不知所措,即使对于像这样一个非常简单的应用程序也是如此。
上在屏幕的左侧,您会看到项目资源管理器。确保在顶部选择了 Android 并找到res文件夹。其中有一个布局文件夹,您会在其中找到activity_main.xml(参见图 13-3)。
双击打开它,您将看到 Android Studio 布局编辑器。这使您可以访问活动用户界面的可视化表示,以及显示定义的 XML 编辑器。你可能只看到其中之一,但如果你想同时看到两者(我推荐!),你可以使用图 13-4右上角突出显示的三个按钮. 这些为您(从左到右)单独提供了 XML 编辑器,同时提供了 XML 编辑器和可视化设计器的分屏,以及单独的可视化设计器。还要注意正下方的属性选项卡。它允许您编辑任何单个用户界面元素的属性。当您构建更多 Android 应用程序时,您可能会发现使用可视化布局工具将项目从控件面板拖放到设计图面和属性窗口以设置诸如布局宽度之类的内容会更加容易。
正如您在图 13-4中所见,您将拥有一个非常基本的 Android 活动,其中包含一个TextView
显示“Hello World”的控件。将活动的所有代码替换为:
在此代码中需要注意的重要事项是android:id
字段,尤其是对于EditText
和Button
。可以更改这些,但如果您这样做,稍后编写代码时将需要使用相同的值。我分别调用了它们txtValue
和convertButton
,所以请注意代码中的这些值!
张量流Lite 本身并不是 Android API 的一部分,因此当您在 Android 应用程序中使用它时,您需要让环境知道您将导入外部库。在 Android Studio 中,这是使用 Gradle 构建工具实现的。该工具允许您通过使用名为build.gradle的 JSON 文件对其进行描述来配置您的环境。起初这可能有点令人困惑,尤其是对于新的 Android 开发人员而言,因为 Android Studio 实际上为您提供了两个 Gradle 文件。通常这些被描述为“项目级” build.gradle和“应用程序级” build.gradle。第一个位于项目文件夹中,后者位于app文件夹中(因此得名),如图 13-5 所示。
您将要编辑应用程序级文件,在图 13-5中突出显示。这具有您的应用程序的依赖项详细信息。打开它,并进行两次编辑。第一个是添加一个implementation
到依赖项部分。这是为了包括 TensorFlow Lite 库:
implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
笔记
您可以在TensorFlow Lite 文档中获取此依赖项的最新版本号。
第二次编辑要求您在该android{}
部分中创建一个新设置,如下所示:
android{
...
aaptOptions {
noCompress "tflite"
}
...
}
此步骤可防止编译器压缩您的.tflite文件。Android Studio 编译器编译资产以使其更小,从而减少从 Google Play 商店下载的时间。但是,如果.tflite文件被压缩,TensorFlow Lite 解释器将无法识别它。为确保它不会被压缩,您需要为.tflite文件设置aaptOptions
为。如果您使用了不同的扩展名(有些人只使用.lite),请确保您在此处拥有该扩展名。noCompress
您现在可以尝试构建您的项目。将下载并链接 TensorFlow Lite 库。
在第 12 章你创建了一个非常简单的模型,从训练它的一组 X 和 Y 值推断出 Y = 2X – 1,将其转换为 TensorFlow Lite,并将其保存为.tflite文件。此步骤需要该文件。
首先要做的是在您的项目中创建一个资产文件夹。为此,导航到项目资源管理器中的app/src/main文件夹,右键单击主文件夹并选择新建目录。称之为资产。将训练模型后下载的.tflite文件拖到该目录中。如果你之前没有创建这个文件,你可以在本书的GitHub 存储库中找到它。
完成后,项目浏览器应该如图 13-6 所示。如果资产文件夹还没有特殊资产图标,请不要担心;这最终将由 Android Studio 更新,通常在下一次构建之后。
现在所有管道都已完成,是时候开始编码了!
尽管事实上,您使用的是 Kotlin,您的源文件位于您可以在图 13-6中看到的java目录中。打开它,您会看到一个包含您的包名称的文件夹。在其中,您应该会看到MainActivity.kt文件。双击此文件以在代码编辑器中将其打开。
首先,您需要一个辅助函数来从assets目录加载 TensorFlow Lite 模型:
private fun loadModelFile(assetManager: AssetManager,
modelPath: String): ByteBuffer {
val fileDescriptor = assetManager.openFd(modelPath)
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel
val startOffset = fileDescriptor.startOffset
val declaredLength = fileDescriptor.declaredLength
return fileChannel.map(FileChannel.MapMode.READ_ONLY,
startOffset, declaredLength)
}
因为.tflite文件实际上是一个压缩的二进制 blob 权重和偏差,解释器将使用它来构建内部神经网络模型,所以它是一个 ByteBuffer
在 Android 方面。此代码将加载文件modelPath
并将其作为ByteBuffer
.
然后,在您的活动中,在类级别(即,在类声明下方,不在任何类函数内),您可以添加模型和解释器的声明:
private lateinit var tflite : Interpreter
private lateinit var tflitemodel : ByteBuffer
因此,在这种情况下,执行所有工作的解释器对象将被调用tflite
,您将作为 a 加载到解释器中的模型ByteBuffer
被称为tflitemodel
。
接下来,在onCreate
创建活动时调用的方法中,添加一些代码来实例化解释器并加载model.tflite
到其中:
try{
tflitemodel = loadModelFile(this.assets, "model.tflite")
tflite = Interpreter(tflitemodel)
} catch(ex: Exception){
ex.printStackTrace()
}
此外,当您在 中时onCreate
,为您将与之交互的两个控件添加代码——您将在EditText
其中键入值的位置,以及Button
您将按下以获取推断的控件:
var convertButton: Button = findViewById
您还需要EditText
在类级别与tflite
和一起声明tflitemodel
,因为它将在下一个函数中引用。您可以通过以下方式做到这一点:
private lateinit var txtValue : EditText
最后,是时候进行推理了。您可以使用一个名为的新函数来执行此操作doInference
:
private fun doInference(){
}
在此函数中,您可以从输入中收集数据,将其传递给 TensorFlow Lite 进行推理,然后显示返回值。
您将在其中输入数字的EditText
控件将为您提供一个字符串,您需要将其转换为浮点数:
var userVal: Float = txtValue.text.toString().toFloat()
正如您在第 12 章中回忆的那样,在将数据输入模型时,您需要将其格式化为 Numpy 数组。作为 Python 构造,Numpy 在 Android 中不可用,但您可以FloatArray
在此上下文中使用。即使你只输入一个值,它仍然需要在一个数组中,大致近似于一个张量:
var inputVal: FloatArray = floatArrayOf(userVal)
该模型将向您返回一个需要解释的字节流。如您所知,您从模型中获取了一个浮点值,假设一个浮点数是4 个字节,您可以设置一个ByteBuffer
4 个字节的 a 来接收输出。可以通过多种方式对字节进行排序,但您只需要默认的本机顺序:
var outputVal: ByteBuffer = ByteBuffer.allocateDirect(4)
outputVal.order(ByteOrder.nativeOrder())
要执行推理,您可以调用run
解释器上的方法,将输入和输出值传递给它。然后它将读取输入值并写入输出值:
tflite.run(inputVal, outputVal)
输出被写入ByteBuffer
,其指针现在位于缓冲区的末尾。要读取它,您必须将它重置为缓冲区的开头:
outputVal.rewind()
现在您可以读取ByteBuffer
浮点数的内容:
var f:Float = outputVal.getFloat()
如果你想向用户显示这个,你可以使用AlertDialog
:
val builder = AlertDialog.Builder(this)
with(builder)
{
setTitle("TFLite Interpreter")
setMessage("Your Value is:$f")
setNeutralButton("OK", DialogInterface.OnClickListener {
dialog, id -> dialog.cancel()
})
show()
}
现在运行该应用程序并亲自尝试一下!您可以在图 13-7中看到结果。
正如您在前几页中看到的那样,构建 Android 应用程序涉及很多脚手架,TensorFlow Lite 解释器需要代码和配置才能正确初始化。既然你已经解决了这个问题,如果你想创建其他使用 TensorFlow Lite 的 Android 应用程序,你将经历几乎相同的过程。这您会遇到的唯一主要区别是以模型理解的方式格式化输入数据,并以相同的方式解析输出数据。因此,例如,在第 12 章中,您构建了一个 Dogs vs. Cats 模型,该模型允许您输入猫或狗的图像,并得出推论。该模型期望将 224 × 224 像素的图像作为输入,在三个颜色通道中进行归一化——这需要弄清楚究竟如何从 Android 图像控件获取图像并对其进行格式化,以便神经网络能够理解它!
例如,让我们从图 13-8中的图像开始,这是一张简单的狗图像,恰好是 395 × 500 像素。
这您需要做的第一件事是将其大小调整为 224 × 224 像素,这是训练模型的图像尺寸。这可以在 Android 中使用Bitmap
库来完成。例如,您可以创建一个新的 224 × 224 位图:
val scaledBitmap = Bitmap.createScaledBitmap(bitmap, 224, 224, false)
(在这种情况下bitmap
,包含应用程序作为资源加载的原始图像。完整的应用程序可以在本书的GitHub 存储库中找到。)
现在如果尺寸合适,您必须协调图像在 Android 中的结构方式与模型期望的结构方式。如果您还记得,在本书前面的训练模型时,您将图像作为归一化张量输入。例如,这样的图像将是 (224, 224, 3):224 × 224 是图像大小,3 是颜色深度。这些值也都归一化到 0 到 1 之间。
因此,总而言之,您需要 224 × 224 × 3 个介于 0 和 1 之间的浮点值来表示图像。要将其存储在 a 中ByteArray
,其中 4 个字节构成一个浮点数,您可以使用以下代码:
val byteBuffer = ByteBuffer.allocateDirect(4 * 224 * 224 * 3)
byteBuffer.order(ByteOrder.nativeOrder())
另一方面,我们的 Android 图像将每个像素存储为 RGB 值中的 32 位整数。对于特定像素,这可能看起来像 0x0010FF10。前两个值是透明度,可以忽略,其余为RGB;即,红色为 0x10,绿色为 0xFF,蓝色为 0x10。到目前为止,您所做的简单归一化只是将 R、G、B 通道值除以 255,这将为您提供红色 0.06275、绿色 1 和蓝色 0.06275。
因此,要进行此转换,我们首先将位图转换为 224 × 224 整数数组,然后将像素复制进去。您可以使用getPixels
API 执行此操作:
val intValues = IntArray(224 * 224)
scaledbitmap.getPixels(intValues, 0, 224, 0, 0, 224, 224)
现在你需要遍历这个数组,一个一个地读取像素并将它们转换成规范化的浮点数。您将使用位移位来获取特定频道。例如,考虑前面的值 0x0010FF10。如果将其向右移动 16 位,您将得到 0x0010(FF10 被“丢失”)。如果您然后将其“与”0xFF,您将得到 0x10,只保留底部的两个数字。类似地,如果您向右移动了 8 位,您将得到 0x0010FF,并且对其执行“与”运算将得到 0xFF。这是一种允许您快速轻松地去除构成像素的相关位的技术。您可以shr
为此使用整数运算,input.shr(16)
阅读“将输入向右移动 16 个像素”:
var pixel = 0
for (i in 0 until INPUT_SIZE) {
for (j in 0 until INPUT_SIZE) {
val input = intValues[pixel++]
byteBuffer.putFloat(((input.shr(16) and 0xFF) / 255))
byteBuffer.putFloat(((input.shr(8) and 0xFF) / 255))
byteBuffer.putFloat(((input and 0xFF)) / 255))
}
}
作为之前,当涉及到输出时,您需要定义一个数组来保存结果。它不一定是ByteArray
; 事实上,FloatArray
如果您知道结果将是浮点数,那么您可以定义类似 a 的内容,因为它们通常是浮点数。在这种情况下,对于 Dogs vs. Cats 模型,您有两个标签,模型架构在输出层中定义了两个神经元,包含猫和狗类的各自属性。因此,要读回结果,您可以定义一个结构来包含这样的输出张量:
val result = Array(1) { FloatArray(2) }
请注意,它是一个包含两个项目的数组的单个数组。回想一下,当您使用 Python 时,您可能会看到类似的值[[1.0 0.0]]
— 这里是一样的。Array(1)
是定义包含数组[]
,而FloatArray(2)
是[1.0 0.0]
。当然,这可能有点令人困惑,但我希望您在编写更多 TensorFlow 应用程序时会习惯这一点!
作为之前,您解释使用interpreter.run
:
interpreter.run(byteBuffer, result)
现在您的结果将是一个数组,其中包含一个包含两个值的数组。您可以在图 13-8中的 Android 调试器中看到它的样子。
当您使用 Android 创建移动应用程序时,这是您必须考虑的最复杂的部分(当然除了创建模型之外)。Python 表示值的方式,尤其是使用 Numpy,可能与 Android 的方式非常不同。您必须创建转换器以根据神经网络期望数据输入的方式重新格式化您的数据,并且您必须了解神经网络使用的输出模式,以便您可以解析结果。
代码生成
在在撰写本文时,一个用于从元数据生成代码的工具在实验模式下可用。要使用它,您需要在执行转换时将元数据添加到 TensorFlow Lite 模型。我不会在这里详细介绍它的发展,但是您可以查阅文档以了解如何为您的模型创建元数据,然后使用此工具生成代码,帮助您避免处理
ByteBuffer
像那些低级别的 s你在本章中一直在使用。您可能还想看看TensorFlow Lite Model Maker 库。
这TensorFlow 团队提供了许多开源示例应用程序,您可以剖析这些应用程序以了解它们如何在本章中建立的基础上工作。它们包括(但不限于)以下内容:
图片分类 :从设备的摄像头读取输入并对多达一千种不同的项目进行分类。
物体检测 :从设备的摄像头读取输入,并为检测到的对象提供边界框。
姿势估计 :看看相机中的人物并推断他们的姿势。
语音识别 :识别常见的口头命令。
手势识别 :训练手势模型并在相机中识别它们。
智能回复 :获取输入消息并生成对它们的回复。
图像分割 :类似于目标检测,但预测图像中每个像素属于哪个类别。
风格迁移 :将新的艺术风格应用于任何图像。
数字分类器 :识别手写数字。
文本分类 :使用在 IMDb 数据集上训练的模型,识别文本中的情绪。
问题解答 :使用来自 Transformers (BERT) 的双向编码器表示,自动回答用户查询!
您可以在 GitHub 上的Awesome TFLite 存储库中找到另一个精选的应用程序列表。
TENSORFLOW LITE ANDROID 支持库
这TensorFlow 团队还为 TensorFlow Lite 创建了一个支持库,其目标是提供一组高级类来支持 Android 上的常见场景。在撰写本文时,它提供了处理某些计算机视觉场景的能力,方法是提供预制类和函数来处理使用图像作为张量的复杂性,以及解析输出的概率数组。
在本章中,您体验了在 Android 上使用 TensorFlow Lite 的滋味。向您介绍了 Android 应用程序的结构以及如何将 TensorFlow Lite 融入其中。您学习了如何将模型实现为 Android 资产,以及如何在解释器中加载和使用它。最重要的是,您看到了将基于 Android 的数据(例如图像或数字)转换为模拟模型中使用的张量的输入数组的需要,以及如何解析输出数据,意识到它也是有效的内存映射张量在ByteBuffer
s。您通过几个示例详细介绍了如何执行此操作,希望这些示例使您能够处理其他场景。在下一章中,您将再次执行此操作,但这次是在 iOS 上使用 Swift。