【AI with ML】第 13 章 :在 Android 应用程序中使用 TensorFlow Lite

news2025/2/6 12:55:11

 🔎大家好,我是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 构建了许多库。

创建您的第一个 TensorFlow Lite Android 应用程序

如果您还没有 Android Studio,请立即安装。设置、更新和准备好一切可能需要一些时间。在接下来的几页中,我将引导您创建一个新应用程序、设计其用户界面、添加 TensorFlow Lite 依赖项,然后对其进行编码以进行推理。这将是一个非常简单的应用程序——您在其中输入一个值,它会执行推理并计算 Y = 2X – 1,其中 X 是您输入的值。对于如此简单的功能来说,这实在是太过分了,但是像这样的应用程序的脚手架几乎与复杂得多的应用程序的脚手架相同。

第 2 步。编辑布局文件

在屏幕的左侧,您会看到项目资源管理器。确保在顶部选择了 Android 并找到res文件夹。其中有一个布局文件夹,您会在其中找到activity_main.xml(参见图 13-3)。

图 13-3。查找您的活动设计文件

双击打开它,您将看到 Android Studio 布局编辑器。这使您可以访问活动用户界面的可视化表示,以及显示定义的 XML 编辑器。你可能只看到其中之一,但如果你想同时看到两者(我推荐!),你可以使用图 13-4右上角突出显示的三个按钮. 这些为您(从左到右)单独提供了 XML 编辑器,同时提供了 XML 编辑器和可视化设计器的分屏,以及单独的可视化设计器。还要注意正下方的属性选项卡。它允许您编辑任何单个用户界面元素的属性。当您构建更多 Android 应用程序时,您可能会发现使用可视化布局工具将项目从控件面板拖放到设计图面和属性窗口以设置诸如布局宽度之类的内容会更加容易。

图 13-4。在 Android Studio 中使用布局编辑器

正如您在图 13-4中所见,您将拥有一个非常基本的 Android 活动,其中包含一个TextView显示“Hello World”的控件。将活动的所有代码替换为:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        xmlns:android="http://schemas.android.com/apk/res/android" 
        android:layout_height="match_parent" 
        android:layout_width="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/lblEnter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Enter X:  "
            android:textSize="18sp"></TextView>

        <EditText
            android:id="@+id/txtValue"
            android:layout_width="180dp"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:text="1"></EditText>

        <Button
            android:id="@+id/convertButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Convert">

        </Button>
    </LinearLayout>
</LinearLayout>

在此代码中需要注意的重要事项是android:id字段,尤其是对于EditTextButton。可以更改这些,但如果您这样做,稍后编写代码时将需要使用相同的值。我分别调用了它们txtValueconvertButton,所以请注意代码中的这些值!

步骤 3. 添加 TensorFlow Lite 依赖项

张量流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 文档中获取此依赖项的最新版本号。

图 13-5。选择您的 build.gradle 文件

第二次编辑要求您在该android{}部分中创建一个新设置,如下所示:

android{
...
    aaptOptions {
        noCompress "tflite"
    }
...
}

此步骤可防止编译器压缩您的.tflite文件。Android Studio 编译器编译资产以使其更小,从而减少从 Google Play 商店下载的时间。但是,如果.tflite文件被压缩,TensorFlow Lite 解释器将无法识别它。为确保它不会被压缩,您需要为.tflite文件设置aaptOptions为。如果您使用了不同的扩展名(有些人只使用.lite),请确保您在此处拥有该扩展名。noCompress

您现在可以尝试构建您的项目。将下载并链接 TensorFlow Lite 库。

第 4 步。添加您的 TensorFlow Lite 模型

在第 12 章你创建了一个非常简单的模型,从训练它的一组 X 和 Y 值推断出 Y = 2X – 1,将其转换为 TensorFlow Lite,并将其保存为.tflite文件。此步骤需要该文件。

首先要做的是在您的项目中创建一个资产文件夹。为此,导航到项目资源管理器中的app/src/main文件夹,右键单击文件夹并选择新建目录。称之为资产。将训练模型后下载的.tflite文件拖到该目录中。如果你之前没有创建这个文件,你可以在本书的GitHub 存储库中找到它。

完成后,项目浏览器应该如图 13-6 所示。如果资产文件夹还没有特殊资产图标,请不要担心;这最终将由 Android Studio 更新,通常在下一次构建之后。

图 13-6。将您的模型添加为资产

现在所有管道都已完成,是时候开始编码了!

第 5 步。编写活动代码以使用 TensorFlow Lite 进行推理

尽管事实上,您使用的是 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<Button>(R.id.convertButton)
convertButton.setOnClickListener{
    doInference()
}
txtValue = findViewById<EditText>(R.id.txtValue)

您还需要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 个字节,您可以设置一个ByteBuffer4 个字节的 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中看到结果。

图 13-7。在模拟器中运行解释器

超越“Hello World”——处理图像

正如您在前几页中看到的那样,构建 Android 应用程序涉及很多脚手架,TensorFlow Lite 解释器需要代码和配置才能正确初始化。既然你已经解决了这个问题,如果你想创建其他使用 TensorFlow Lite 的 Android 应用程序,你将经历几乎相同的过程。这您会遇到的唯一主要区别是以模型理解的方式格式化输入数据,并以相同的方式解析输出数据。因此,例如,在第 12 章中,您构建了一个 Dogs vs. Cats 模型,该模型允许您输入猫或狗的图像,并得出推论。该模型期望将 224 × 224 像素的图像作为输入,在三个颜色通道中进行归一化——这需要弄清楚究竟如何从 Android 图像控件获取图像并对其进行格式化,以便神经网络能够理解它!

例如,让我们从图 13-8中的图像开始,这是一张简单的狗图像,恰好是 395 × 500 像素。

图 13-8。一只狗的形象来解释

您需要做的第一件事是将其大小调整为 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 整数数组,然后将像素复制进去。您可以使用getPixelsAPI 执行此操作:

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 调试器中看到它的样子。

图 13-9。解析输出值

当您使用 Android 创建移动应用程序时,这是您必须考虑的最复杂的部分(当然除了创建模型之外)。Python 表示值的方式,尤其是使用 Numpy,可能与 Android 的方式非常不同。您必须创建转换器以根据神经网络期望数据输入的方式重新格式化您的数据,并且您必须了解神经网络使用的输出模式,以便您可以解析结果。

TensorFlow Lite 示例应用程序

TensorFlow 团队提供了许多开源示例应用程序,您可以剖析这些应用程序以了解它们如何在本章中建立的基础上工作。它们包括(但不限于)以下内容:

图片分类 :从设备的摄像头读取输入并对多达一千种不同的项目进行分类。

物体检测 :从设备的摄像头读取输入,并为检测到的对象提供边界框。

姿势估计 :看看相机中的人物并推断他们的姿势。

语音识别 :识别常见的口头命令。

手势识别 :训练手势模型并在相机中识别它们。

智能回复 :获取输入消息并生成对它们的回复。

图像分割 :类似于目标检测,但预测图像中每个像素属于哪个类别。

风格迁移 :将新的艺术风格应用于任何图像。

数字分类器 :识别手写数字。

文本分类 :使用在 IMDb 数据集上训练的模型,识别文本中的情绪。

问题解答 :使用来自 Transformers (BERT) 的双向编码器表示,自动回答用户查询!

您可以在 GitHub 上的Awesome TFLite 存储库中找到另一个精选的应用程序列表。

概括

在本章中,您体验了在 Android 上使用 TensorFlow Lite 的滋味。向您介绍了 Android 应用程序的结构以及如何将 TensorFlow Lite 融入其中。您学习了如何将模型实现为 Android 资产,以及如何在解释器中加载和使用它。最重要的是,您看到了将基于 Android 的数据(例如图像或数字)转换为模拟模型中使用的张量的输入数组的需要,以及如何解析输出数据,意识到它也是有效的内存映射张量在ByteBuffers。您通过几个示例详细介绍了如何执行此操作,希望这些示例使您能够处理其他场景。在下一章中,您将再次执行此操作,但这次是在 iOS 上使用 Swift。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/102904.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue的虚拟DOM和diff算法

虚拟DOM和diff算法密不可分&#xff0c; 虚拟dom&#xff0c;它本身就是一个 JavaScript 对象&#xff0c;为解决DOM操作非常耗时&#xff0c;把DOM转换为虚拟DOM&#xff0c;DOM操作转换为js计算&#xff0c;js执行速度较快。 diff算法在vue中被优化为O(n)的时间复杂度&#x…

手机损坏的数据导出方法

主板损坏的症状 手机主板损坏的症状表现一般为系统启动失败、屏幕无显示、启动黑屏死机、无法开机、没有声音、无法打电话、没网络等等。 资料的重要性 手机坏了,怎么把手机里面重要的资料导出来?今天小编就来跟大家说说手机导资料这个事。 手机坏了,具体要看手机是哪里…

java计算机毕业设计ssm基于JavaWeb的公务员招考信息发布平台

项目介绍 随着互联网技术的发发展,计算机技术广泛应用在人们的生活中,逐渐成为日常工作、生活不可或缺的工具,各种管理系统层出不穷。公务员是各国负责统筹管理经济社会秩序和国家公共资源,维护国家法律规定,贯彻执行相关义务的公职人员。每年报考公务员的人数逐渐增加,特为此…

寒假训练营 第一节 时空复杂度分析

一.时空复杂度的分析 1.时间复杂度 时间复杂度&#xff1a;程序运行会消耗多少时间。 在此&#xff0c;为了方便讨论&#xff0c;我们假定每一个语句消耗的时间一样&#xff0c;视为一个时间单元。 #include <iostream> using namespace std; int main() {int n 100;…

国企的设备管理工作如何做好?

国企的设备管理工作如何做好&#xff1f; 在市场经济建设持续深化、经营体制不断转变的过程中&#xff0c;一些企业设备管理工作发生了很大的变化&#xff0c;出现整体水平下降&#xff0c;设备技术状况恶化等现象&#xff0c;这在一定程度上制约了企业技术创新&#xff0c;制…

Transformer实现以及Pytorch源码解读(二)-embedding源码分析

前言 本部分博客需要先阅读博客&#xff1a;《Transformer实现以及Pytorch源码解读&#xff08;一&#xff09;-数据输入篇》 作为知识储备。 Embedding使用方式 如下面的代码中所示&#xff0c;embedding一般是先实例化nn.Embedding(vocab_size, embedding_dim)。实例化的过…

业务安全情报,预知发现黑灰产的企业攻击

业务遭遇欺诈风险&#xff0c;发起攻击的黑灰产主要是为了谋取利益。对于黑灰产利益目的甄别需要多方面情报&#xff0c;再辅助技术和专家经验&#xff0c;然后进行综合判断&#xff0c;进而帮助企业及时响应、精准布控。 安全情报帮助企业提前发现攻击 12月13日&#xff0c;“…

谁代表先进生产力?

互联网企业代表先进生产力方向 做软件项目的时候碰到三类企业 1、 传统企业&#xff0c;以卖货或卖服务为主 2、 互联网类&#xff0c;做个服务工具或平台 3、 分配模式类&#xff0c;以某分配模式为“宝贝” 毫无疑问&#xff1a; 互联网企业代表先进的生产力方向 互联网类…

即时通讯音视频开发之音频基础及编码原理

即时通讯应用中的实时音视频技术&#xff0c;几乎是IM开发中的最后一道高墙。原因在于&#xff1a;实时音视频技术 音视频处理技术 网络传输技术 的横向技术应用集合体&#xff0c;而公共互联网不是为了实时通信设计的。 比特率&#xff1a; 表示经过编码&#xff08;压缩&am…

C#读取Excel文件内容(WPS)

本地安装的WPS版本为 一、下载accessdatabaseengine_X64后安装 网址&#xff1a;https://www.microsoft.com/en-us/download/details.aspx?id54920 二、项目中引用OleDb包 三、代码部分 //excelFilePath为文件路径&#xff08;例如D:\Test.xslx&#xff09; > //strin…

Android---RecyclerView回收复用机制

一、RecyclerView回收复用 回收什么&#xff1f;复用什么&#xff1f; 回收&#xff1a;回收即缓存。当屏幕上的一个itemView滑出屏幕(即不可见了)&#xff0c;RecyclerView就利用回收机制&#xff0c;将该itemView放入内存。当其它itemView出现时&#xff0c;不用每次都去new…

JavaScript-Sass

Sass的基础使用 1.简介 1.1简介 Sass是世界上最成熟&#xff0c;最稳定&#xff0c;最强大的CSS扩展语言Sass是css预编译工具可以更加优雅的书写csssass写出来的东西浏览器不认识需要进行转换VSCode推荐使用Easy Sass插件Sass中可以使用加减乘除&#xff0c;条件分支以及循环…

【Three.js入门】处理动画、尺寸自适应、双击进入/退出全屏(Clock跟踪时间,Gsap动画库,自适应画面,进入/退出全屏)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

Python -- 网络编程

目录 1.网络通信的概念 2.IP地址 3.网络通信方式 3.1 直接通信 3.2 使用集线通信 3.3 通用交换机通信 3.4 使用路由器连接多个网络 3.5 复杂的通信过程 4.端口 4.1 端口号 4.2 知名端口号 4.3 动态端口号 4.4 端口号作用 5.socker概念 5.1 不同电脑上的进程之间…

【二叉树经典习题讲解】

If you find a path with no obstacles, probably doesnt lead anywhere. 目录 1 前中后序遍历一颗二叉树 2 总的结点个数 3 求叶子节点个数 4 求树的高度 5 第k层结点个数 6 二叉树的层序遍历 7 判断一棵树是否为完全二叉树 1 二叉树的前序遍历 2 单值二叉树 3 翻转二…

2022卡塔尔世界杯的两个球员:一个吸螺,一个没吸

你好&#xff0c;我是YourBatman&#xff1a;一个俗人&#xff0c;贪财好色。 2022年12月18日&#xff0c;卢塞尔球场&#xff0c;太太太精彩了&#xff0c;这场世界杯决赛&#xff01;卡塔尔世界杯&#xff0c;已经离我们远去&#xff0c;阿根廷最终满载而归。 那一个个珍贵…

大脑的默认模式网络DMN

虽然默认模式网络DMN现在是rs-fMRI领域中的研究热点&#xff0c;但最初观察到默认模式网络的工具是PET&#xff0c;并且是从任务态过渡到静息态的 PET中大脑功能活动基线的定义&#xff1a; 基线是理解复杂系统的基础根据脑氧提取分数&#xff08;OEF值&#xff09;可以确定正…

前端CSS实现跳动的文字

效果图 首选来一个简单的布局 这里就不用多说&#xff0c;都是简单排版 <h1>一个爬坑的Coder</h1>html {height: 100%; }body {display: flex;justify-content: center;align-items: center;height: 100%; } h1 {font-size: 48px; }每个文字独立出来 每个文字都…

最全GIS开发编程语言汇总及分类

推荐查看>>>科研所需模型软件教程&#xff1a;水文水资源、大气科学、农林生态、地信遥感、统计分析、编程语言等... 最近总有很多人关心GIS开发语言的问题&#xff0c;这个确实很重要&#xff0c;毕竟学习一门编程语言需要花费不少时间和精力&#xff0c;找不到合适…

【数据库】并发控制理论

并发控制&#xff08;concurrency control&#xff09; 恢复&#xff08;recovery) 理论支持&#xff1a;基于事务的ACID Atomicity: All actions in the txn happend, or none happen. “All or nothing” Consistency: IF each txn is consistent and the DB starts consis…