提交 8f3cd22a 编写于 作者: W wizardforcel

2020-12-07 19:25:18

上级 0dafb24c
......@@ -373,7 +373,7 @@ Watson 的软件架构使用:
* IBM 的 DeepQA 软件
* Apache UIMA(非结构化信息管理架构)
* 多种语言,包括 Java,C ++和 Prolog
* 多种语言,包括 Java,C++ 和 Prolog
* SUSE Linux Enterprise Server
* 适用于分布式计算的 Apache Hadoop
......
......@@ -1132,7 +1132,7 @@ GP 在近似解决方案可以接受或最佳解决方案的情况下效果很
**小但有价值的改进**
已发现 GP 在技术努力往往集中在经济重要性较高的领域中效果很好。 在这些领域中,以前的研究人员可能花费了大量时间和精力,并且“最新技术”趋于先进。 在这种情况下,很难改善当前的解决方案。 但是,在这些相同的领域中,小的改进可能非常有价值。 在这种情况下,GP 有时会做出很小但有价值的贡献。 例如石油勘探,材料管理和财务应用。
已发现 GP 在技术努力往往集中在经济重要性较高的领域中效果很好。 在这些领域中,以前的研究人员可能花费了大量时间和精力,并且“最新技术”趋于先进。 在这种情况下,很难改善当前的解决方案。 但是,在这些相同的领域中,小的改进可能非常有价值。 在这种情况下,GP 有时会做出很小但有价值的贡献。 例如石油探索,材料管理和财务应用。
现在,让我们看一下 GA 和 GP 的一些特定于行业的应用程序:
......
......@@ -348,7 +348,7 @@ Amazon Translate 使用机器学习和连续学习模型来改善其翻译的性
可以通过三种不同的方式访问该服务,就像可以访问许多 AWS 服务一样:
* 在 AWS 控制台中,翻译少量文本片段并对该服务进行采样。
* 使用 AWS API(支持的语言为 C ++,Go,Java,JavaScript,.NET,Node.js,PHP,Python 和 Ruby)。
* 使用 AWS API(支持的语言为 C++ ,Go,Java,JavaScript,.NET,Node.js,PHP,Python 和 Ruby)。
* 可以通过 AWS CLI 访问 Amazon Translate。
**用于 Amazon Translate**
......
......@@ -33,13 +33,13 @@ RL 是指学习操作方法并将情况映射到某些动作以最大化回报
我们需要一个可以通过与未知地形交互来从自身经验中学习的代理。 这是 RL 真正闪耀的地方。
让我们考虑一下代理与新环境进行交互以进行学习时的探索阶段。 它可以探索多少? 在这一点上,该代理不知道环境有多大,并且在许多情况下,它将无法探索所有可能性。 那么,代理应该做什么? 它是应该从其有限的经验中吸取教训,还是等到进一步探索后再采取行动? 这是 RL 的主要挑战之一。 为了获得更高的报酬,代理必须支持经过尝试和测试的行为。 但是为了发现此类动作,它应继续尝试以前未选择的较新动作。 多年来,研究人员已经广泛研究了在勘探与开发之间的这种[b] 折衷方案,但它仍然是活跃的[b] 视差镜
让我们考虑一下代理与新环境进行交互以进行学习时的探索阶段。 它可以探索多少? 在这一点上,该代理不知道环境有多大,并且在许多情况下,它将无法探索所有可能性。 那么,代理应该做什么? 它是应该从其有限的经验中吸取教训,还是等到进一步探索后再采取行动? 这是 RL 的主要挑战之一。 为了获得更高的报酬,代理必须支持经过尝试和测试的行为。 但是为了发现此类动作,它应继续尝试以前未选择的较新动作。 多年来,研究人员已经广泛研究了在探索与开发之间的这种折衷方案,但它仍然是活跃的话题
# 强化学习的实际示例
让我们看看 RL 在现实世界中出现的位置。 这将帮助我们了解它的工作原理以及使用此可以构建哪些可能的应用程序,除非:
**游戏性**:让我们考虑一下棋类游戏,例如 Go 或 Chess。 为了确定最佳动作,玩家需要考虑各种因素。 可能性的数量如此之大,以至于无法进行暴力搜索。 如果我们要使用传统技术制造一台可以玩这种游戏的机器,则需要指定许多规则来涵盖所有这些可能性。 RL 完全绕过了这个问题。 我们不需要手动指定任何逻辑规则。 学习代理仅通过示例学习并与自己玩游戏。
**游戏性**:让我们考虑一下棋类游戏,例如围棋或象棋。 为了确定最佳动作,玩家需要考虑各种因素。 可能性的数量如此之大,以至于无法进行暴力搜索。 如果我们要使用传统技术制造一台可以玩这种游戏的机器,则需要指定许多规则来涵盖所有这些可能性。 RL 完全绕过了这个问题。 我们不需要手动指定任何逻辑规则。 学习代理仅通过示例学习并与自己玩游戏。
有关此主题的更详尽讨论,请参阅第 2 章,“人工智能基本用例”中的“游戏”部分。
......@@ -61,7 +61,7 @@ RL 是指学习操作方法并将情况映射到某些动作以最大化回报
![](img/B15441_22_01.png)
图 1:增强材料 l 的构成
图 1:RL 的构成
通常,RL 代理执行以下步骤:
......@@ -71,7 +71,7 @@ RL 是指学习操作方法并将情况映射到某些动作以最大化回报
4. 环境对此动作做出反应。 代理从环境中获得强化,也称为奖励。
5. 代理计算并记录有关该奖励的信息。 重要的是要注意,将为此状态/动作对收到此奖励,以便在给定特定状态的情况下,将来可将其用于采取更多的奖励行动。
RL 系统可以同时执行多个事情–通过执行试错搜索来学习,了解所处环境的模型,然后使用该模型来计划下一步。 在下一节中,我们将动手操作,并开始使用流行的 fr amework 使用 Python 编写的强化学习系统。
RL 系统可以同时执行多个事情–通过执行试错搜索来学习,了解所处环境的模型,然后使用该模型来计划下一步。 在下一节中,我们将动手操作,并开始使用流行的框架使用 Python 编写的强化学习系统。
# 创建环境
......@@ -297,13 +297,13 @@ if __name__=='__main__':
$ python3 balancer.py --input-env cartpole
```
如果运行代码,您将看到小柱平衡自身:
如果运行代码,您将看到杆子平衡自身:
![](img/B15441_22_10.png)
图 10:Cartpole 示例输出 4
如果让它运行几秒钟,您将看到小刀杆仍然站立,如以下屏幕截图所示:
如果让它运行几秒钟,您将看到杆子仍然站立,如以下屏幕截图所示:
![](img/B15441_22_11.png)
......@@ -313,7 +313,7 @@ $ python3 balancer.py --input-env cartpole
![](img/B15441_22_12.png)
图 12:情节输出
图 12:绘图输出
不同的情节需要完成不同的步骤。 如果您滚动查看打印出来的信息,您将能够看到。 希望,当您运行本示例时,您至少在大多数情况下都会看到棘突。 凭借在本章中获得的知识,我认为我们还没有准备好在 Go 的游戏中击败 AlphaZero。 但是我们了解了如何构建此类系统的基础知识。
......
......@@ -8,7 +8,7 @@
* 大数据的三个 V
* 适用于人工智能和机器学习的大数据
* 使用大数据的机器学习管道
* 阿帕奇 Hadoop
* Apache Hadoop
* Apache Spark
* Apache Impala
* NoSQL 数据库
......@@ -108,7 +108,7 @@ Google 的工作方式与图书馆卡目录类似,不同之处在于 Google
## 全球数据中心
与可口可乐配方一样,Google 喜欢将其*秘密调味料* 成分保密,因此 Google 不会发布有关其拥有多少个数据中心以及它们位于何处的详细信息。 可以肯定的是,他们有很多,而且遍布世界各地。
与可口可乐配方一样,Google 喜欢将其*秘密调味料*成分保密,因此 Google 不会发布有关其拥有多少个数据中心以及它们位于何处的详细信息。 可以肯定的是,他们有很多,而且遍布世界各地。
无论如何,当您向 Google 提交查询时,智能网络路由器都会将您的搜索查询扩展到距离您最近的且可用于执行搜索的数据中心。
......@@ -233,11 +233,11 @@ Hadoop 核心高度依赖 MapReduce。 MapReduce 是一种编程设计模式,
MapReduce 仅在多节点群集体系结构模型的上下文中才有意义。 MapReduce 背后的想法是,许多问题可以分为两个步骤。 映射和归约。
MapReduce 实现由一个 map 组件和一个 reduce 组件组成,reduce 组件执行映射,过滤和/或排序(例如,将不同家用物品的图像映射到队列中,每种类型的物品一个队列),reduce 组件完成摘要 操作(继续示例,计算每个队列中的图像数量,为我们提供家用物品的频率)。
MapReduce 实现由一个映射组件和一个归约组件组成,映射组件执行映射,过滤和/或排序(例如,将不同家用物品的图像映射到队列中,每种类型的物品一个队列),归约组件完成摘要操作(继续示例,计算每个队列中的图像数量,为我们提供家用物品的频率)。
### Apache Hive
有许多组件组成了 Hadoop 生态系统,并且有涵盖该主题的完整书籍。 例如,我们推荐 Manish Kumar 和 Chanchal Singh 的 *Mastering Hadoop 3* 。 其中许多组件不在本书讨论范围之内,但是在结束本主题之前,我们应该介绍的 Hadoop 的一个重要组件是 Apache Hive。
有许多组件组成了 Hadoop 生态系统,并且有涵盖该主题的完整书籍。 例如,我们推荐 Manish Kumar 和 Chanchal Singh 的《Mastering Hadoop 3》。 其中许多组件不在本书讨论范围之内,但是在结束本主题之前,我们应该介绍的 Hadoop 的一个重要组件是 Apache Hive。
Apache Hive 是构建在 Apache Hadoop 之上的数据仓库软件组件,支持数据查询和分析。 Apache Hive 支持类似 SQL 的界面,以获取 Hadoop 支持的各种数据库和文件系统中存储的数据。 如果没有 Hive,则需要实现复杂的 Java 代码以提供必要的 MapReduce 逻辑。 Apache Hive 提供了一种抽象,以支持对底层 Java 的类似 SQL 的查询(HiveQL),而无需实现复杂的低级 Java 代码。
......@@ -266,13 +266,13 @@ RDD 中的数据集被划分为逻辑分区,这些逻辑分区冗余地存储
1. 通过并行化驱动程序中的现有集合
2. 通过引用来自外部磁盘存储的数据集,例如共享文件系统,S3,HBase,HDFS 或任何其他支持 Hadoop 兼容输入格式的外部数据源
Spark 利用 RDD 的概念来实现更快,更有效的 MapReduce 操作。 使用第二版 Spark,现在还支持更简单的数据结构,从而简化了数据集的处理。 这些是 DataFrames
Spark 利用 RDD 的概念来实现更快,更有效的 MapReduce 操作。 使用第二版 Spark,现在还支持更简单的数据结构,从而简化了数据集的处理。 这些是数据帧
### 数据帧
Spark 中的新抽象是 DataFrames。 最初通过引入 Spark 2.0 作为 RDD 的替代接口来支持 DataFrames 。 这两个接口有些相似。 DataFrame 将数据组织到命名列中。 从概念上讲,它等效于关系数据库中的表或 Python 的 pandas 包或 R 中的 DataFrames。这使 DataFrames 比 RDD 易于使用。 RDD 不支持类似的列级标题引用集。
Spark 中的新抽象是数据帧。 最初通过引入 Spark 2.0 作为 RDD 的替代接口来支持数据帧。 这两个接口有些相似。数据帧将数据组织到命名列中。 从概念上讲,它等效于关系数据库中的表或 Python 的 pandas 包或 R 中的数据帧。这使数据帧比 RDD 易于使用。 RDD 不支持类似的列级标题引用集。
DataFrame 可以从多种数据源生成,包括:
数据帧可以从多种数据源生成,包括:
* Hive 中的结构化数据文件(例如,Parquet,ORC,JSON)
* 蜂巢表
......@@ -285,13 +285,13 @@ DataFrame API 在 Scala,Java,Python 和 R 中可用。
SparkSQL 是,它可以激发出 Hive 对 Hadoop 的作用。 它允许 Spark 框架的用户查询数据框架,就像在传统的关系数据库中查询 SQL 表一样。
SparkSQL 添加了一个抽象级别,以允许查询存储在 RDD,DataFrame 和外部源中的数据,从而为所有这些数据源创建统一的接口。 提供这种统一的接口使开发人员可以轻松地创建复杂的 SQL 查询,以与异类的各种源(例如 HDFS 文件,S3,传统数据库等)相对。
SparkSQL 添加了一个抽象级别,以允许查询存储在 RDD,数据帧和外部源中的数据,从而为所有这些数据源创建统一的接口。 提供这种统一的接口使开发人员可以轻松地创建复杂的 SQL 查询,以与异类的各种源(例如 HDFS 文件,S3,传统数据库等)相对。
更具体地说,SparkSQL 使用户能够:
* 对导入的数据和现有的 RDD 运行 SQL 查询
* 从 Apache Parquet 文件,ORC 文件和 Hive 表导入数据
* 将 RDD 和 DataFrame 输出到 Hive 表或 Apache Parquet 文件
* 将 RDD 和数据帧输出到 Hive 表或 Apache Parquet 文件
SparkSQL 具有列式存储,基于成本的优化器和代码生成以加快查询速度。 它可以使用 Spark 引擎支持数千个节点。
......@@ -374,11 +374,11 @@ Avinash Lakshman(亚马逊 DynamoDB 的作者之一)和 Prashant Malik 最
MongoDB 是面向文档的水平可伸缩数据库。 它使用 JSON 数据格式存储数据。 它通常用于存储网站数据,并且在内容管理和缓存应用程序中也很流行。 它支持复制和高可用性配置,以最大程度地减少数据丢失。
根据查询的类型,它可能是一个高性能的系统。 它是用 C ++编写的。 它完全支持索引,具有丰富的查询语言,可以进行配置以提供跨数据中心的高可用性。
根据查询的类型,它可能是一个高性能的系统。 它是用 C++ 编写的。 它完全支持索引,具有丰富的查询语言,可以进行配置以提供跨数据中心的高可用性。
## Redis
Redis 是的另一个开源 NoSQL 数据库。 这是一个键值存储。 它支持键中的哈希,集,字符串,排序集和列表。 因此,Redis 也被称为数据结构服务器。 Redis 支持运行原子操作,例如增加散列中存在的值,设置交集计算,字符串附加,差和联合。 Redis 利用内存中的数据集来实现高性能。 Redis 支持大多数最受欢迎的编程语言,例如 Python,Java,Scala,C ++等。
Redis 是的另一个开源 NoSQL 数据库。 这是一个键值存储。 它支持键中的哈希,集,字符串,排序集和列表。 因此,Redis 也被称为数据结构服务器。 Redis 支持运行原子操作,例如增加散列中存在的值,设置交集计算,字符串附加,差和联合。 Redis 利用内存中的数据集来实现高性能。 Redis 支持大多数最受欢迎的编程语言,例如 Python,Java,Scala,C++ 等。
## Neo4j
......@@ -392,10 +392,10 @@ Neo4j 用 Java 实现,并通过交易 HTTP 端点或二进制“螺栓”协
# 总结
在本章中,我们首先围绕大数据奠定了核心和基本概念的基础。 然后,我们了解了与大数据有关的许多不同技术。 当涉及到大数据技术 Hadoop 时,我们了解了其中的所有“老”。 我们还了解了当今市场上当前最流行的大数据工具,即 Spark。
在本章中,我们首先围绕大数据奠定了核心和基本概念的基础。 然后,我们了解了与大数据有关的许多不同技术。 当涉及到大数据技术 Hadoop 时,我们了解了其中的所有“老祖宗”。 我们还了解了当今市场上当前最流行的大数据工具,即 Spark。
最后,我们了解了大数据实现中常用的另一种技术,即 NoSQL 数据库。 NoSQL 数据库引擎支持财富 500 强公司中许多最大的工作量,并在当今最常见的网站中服务数百万个页面。
对于当今机器学习中存在的所有令人惊奇和令人兴奋的应用程序,我们坚信,我们只是在探索一切可能的事物。 我们衷心希望您能更好地掌握机器学习中涉及的概念,但更重要的是,我们希望您的好奇心得到激发,这本书激发您毕生对这种美丽事物的兴趣。 话题
对于当今机器学习中存在的所有令人惊奇和令人兴奋的应用程序,我们坚信,我们只是在探索一切可能的事物。 我们衷心希望您能更好地掌握机器学习中涉及的概念,但更重要的是,我们希望您的好奇心得到激发,这本书激发您毕生对这种美丽话题的兴趣
我们很高兴看到您使用本章,本书中所包含的知识做什么。 希望您发现这本书引人入胜,有趣并且有用。 我们希望您阅读和阅读时一样开心。 我们希望您在所有努力中继续取得成功。
\ No newline at end of file
......@@ -509,7 +509,7 @@ Histogram of 2000 points sampled from the final Poisson distribution
在继续进行更多技术讨论之前,我认为解释 Python 作为本书编程语言的选择会有所帮助。 在过去的十年中,数据科学和机器学习领域的研究呈指数增长,拥有数千篇有价值的论文和数十种完善的工具。 特别是由于 Python 的高效性,美观性和紧凑性,许多研究人员和程序员都选择使用 Python 创建一个完整的科学生态系统,该生态系统已免费发布。
如今,诸如 scikit-learn,SciPy,NumPy,Matplotlib,pandas 之类的软件包代表了数百种可用于生产环境的系统的骨干,并且其使用量还在不断增长。 此外,复杂的深度学习应用程序(例如 Theano,TensorFlow 和 PyTorch)允许每个 Python 用户创建和训练复杂模型而没有任何速度限制。 实际上,必须注意 Python 不再是脚本语言。 它支持许多特定任务(例如,Web 框架和图形),并且可以与用 C 或 C ++编写的本机代码进行接口。
如今,诸如 scikit-learn,SciPy,NumPy,Matplotlib,pandas 之类的软件包代表了数百种可用于生产环境的系统的骨干,并且其使用量还在不断增长。 此外,复杂的深度学习应用程序(例如 Theano,TensorFlow 和 PyTorch)允许每个 Python 用户创建和训练复杂模型而没有任何速度限制。 实际上,必须注意 Python 不再是脚本语言。 它支持许多特定任务(例如,Web 框架和图形),并且可以与用 C 或 C++ 编写的本机代码进行接口。
由于这些原因,Python 几乎是所有数据科学项目中的最佳选择,并且由于其功能,所有具有不同背景的程序员都可以轻松地学会在短时间内有效地使用它。 也可以使用其他免费解决方案(例如 R,Java 或 Scala),但是,在 R 的情况下,可以完全涵盖统计和数学函数,但缺少构建完整应用程序所必需的支持框架。 相反,Java 和 Scala 具有完整的可用于生产环境的库生态系统,但是特别是 Java 不像 Python 那样紧凑且易于使用。 而且,对本机代码的支持要复杂得多,并且大多数库都完全依赖 JVM(从而导致性能损失)。
......
......@@ -191,7 +191,7 @@ Xcode 用于开发 iOS 应用,您需要 Mac 电脑和免费的 Apple ID 才能
尽管您可以使用 Xcode 模拟器测试运行书中的许多应用程序,但是书中的某些应用程序需要使用实际的 iOS 设备上的相机拍摄照片,然后才能使用经过 TensorFlow 训练的深度学习模型对其进行处理。 此外,通常最好在实际设备上测试模型的准确性能和内存使用情况:在模拟器中运行良好的模型可能会崩溃或在实际设备中运行太慢。 因此,强烈建议或要求您(如果并非总是)至少在您的实际 iOS 设备上测试并运行本书中的 iOS 应用程序一次。
本书假定您熟悉 iOS 编程,但是如果您不熟悉 iOS 开发,则可以从许多出色的在线教程中学习,例如 [Ray Wenderlich 的 iOS 教程](https://www.raywenderlich.com)。 我们不会介绍复杂的 iOS 编程; 我们将主要向您展示如何在我们的 iOS 应用中使用 TensorFlow C ++ API 来运行 TensorFlow 训练有素的模型来执行各种智能任务。 Apple 的两种官方 iOS 编程语言 Objective-C 和 Swift 代码都将用于与我们的移动 AI 应用程序中的 C ++代码进行交互。
本书假定您熟悉 iOS 编程,但是如果您不熟悉 iOS 开发,则可以从许多出色的在线教程中学习,例如 [Ray Wenderlich 的 iOS 教程](https://www.raywenderlich.com)。 我们不会介绍复杂的 iOS 编程; 我们将主要向您展示如何在我们的 iOS 应用中使用 TensorFlow C++ API 来运行 TensorFlow 训练有素的模型来执行各种智能任务。 Apple 的两种官方 iOS 编程语言 Objective-C 和 Swift 代码都将用于与我们的移动 AI 应用程序中的 C++ 代码进行交互。
......@@ -211,7 +211,7 @@ Android Studio 是开发 Android 应用程序的最佳工具,并且 TensorFlow
![](img/3b90efdb-6655-4e35-b74a-a44e69e17487.png)Fig 1.4 Android SDK Manager to install SDK tools and NDK
最后,由于 TensorFlow Android 应用程序使用 C ++中的本机 TensorFlow 库来加载和运行 TensorFlow 模型,因此您需要安装 Android **本机开发套件****NDK**),您可以执行以下任一操作 从上一个屏幕快照中显示的 Android SDK Manager 中获取,或者直接从[这里](https://developer.android.com/ndk/downloads/index.html)下载 NDK。 NDK 版本 r16b 和 r15c 均已通过测试可运行本书中的 Android 应用。 如果直接下载 NDK,则在打开项目并选择 Android Studio 的 File | 文件后,您可能还需要设置 Android NDK 位置。 项目结构,如以下屏幕截图所示:
最后,由于 TensorFlow Android 应用程序使用 C++ 中的本机 TensorFlow 库来加载和运行 TensorFlow 模型,因此您需要安装 Android **本机开发套件****NDK**),您可以执行以下任一操作 从上一个屏幕快照中显示的 Android SDK Manager 中获取,或者直接从[这里](https://developer.android.com/ndk/downloads/index.html)下载 NDK。 NDK 版本 r16b 和 r15c 均已通过测试可运行本书中的 Android 应用。 如果直接下载 NDK,则在打开项目并选择 Android Studio 的 File | 文件后,您可能还需要设置 Android NDK 位置。 项目结构,如以下屏幕截图所示:
![](img/1f6a2d74-0be0-4f36-9796-2cf66eb1694f.png)Fig 1.5 Setting project-level Android NDK location
......
......@@ -346,7 +346,7 @@ bazel-bin/tensorflow/tools/benchmark/benchmark_model
![](img/d2e25f9d-c88d-4fe5-918d-ba7e64420682.png)Figure 2.5 Adding the retrained model file and the label file to app
3. 单击 Xco​​de 中的`RunModelViewController.mm`文件,该文件使用 TensorFlow C ++ API 处理输入图像,通过 Inception v1 模型运行它,并获得图像分类结果,并更改行:
3. 单击 Xco​​de 中的`RunModelViewController.mm`文件,该文件使用 TensorFlow C++ API 处理输入图像,通过 Inception v1 模型运行它,并获得图像分类结果,并更改行:
```py
NSString* network_path = FilePathForResourceName(@"tensorflow_inception_graph", @"pb");
......@@ -452,7 +452,7 @@ private static final String LABEL_FILE = "file:///android_asset/dog_retrained_la
在 TensorFlow 的早期版本中,将 TensorFlow 添加到您自己的应用程序非常繁琐,需要使用 TensorFlow 的手动构建过程和其他手动设置。 在 TensorFlow 1.4 中,该过程非常简单,但在 TensorFlow 网站上并未详细记录详细步骤。 缺少的另一件事是缺少有关如何在基于 Swift 的 iOS 应用中使用 TensorFlow 的文档; 示例 TensorFlow iOS 应用程序都在 Objective-C 中,它们调用了 TensorFlow 的 C ++ API。 让我们看看我们如何做得更好。
在 TensorFlow 的早期版本中,将 TensorFlow 添加到您自己的应用程序非常繁琐,需要使用 TensorFlow 的手动构建过程和其他手动设置。 在 TensorFlow 1.4 中,该过程非常简单,但在 TensorFlow 网站上并未详细记录详细步骤。 缺少的另一件事是缺少有关如何在基于 Swift 的 iOS 应用中使用 TensorFlow 的文档; 示例 TensorFlow iOS 应用程序都在 Objective-C 中,它们调用了 TensorFlow 的 C++ API。 让我们看看我们如何做得更好。
......@@ -478,7 +478,7 @@ pod 'TensorFlow-experimental'
![](img/b89b3444-a6a9-45b4-bb02-ddf8388a6992.png)Figure 2.6 Adding utility files, model files, label file and image files
6.`ViewController.m`重命名为`ViewController.mm`,因为我们将在该文件中混合使用 C ++代码和 Objective-C 代码来调用 TensorFlow C ++ API 并处理图像输入和推断结果。 然后,在`@interface ViewController`之前,添加以下`#include`和函数原型:
6.`ViewController.m`重命名为`ViewController.mm`,因为我们将在该文件中混合使用 C++ 代码和 Objective-C 代码来调用 TensorFlow C++ API 并处理图像输入和推断结果。 然后,在`@interface ViewController`之前,添加以下`#include`和函数原型:
```py
#include <fstream>
......@@ -651,7 +651,7 @@ pod 'TensorFlow-experimental'
3. 运行命令`pod install`下载并安装 TensorFlow pod;
4. 在 Xcode 中打开`HelloTensorFlow_Swift.xcworkspace`文件,然后将两个文件(`ios_image_load.mm``ios_image_load.h`)拖放到 TensorFlow iOS 示例目录`tensorflow/examples/ios/simple``HelloTensorFlow_Swift`项目文件夹中。 当将两个文件添加到项目中时,您将看到一个消息框,如以下屏幕截图所示,询问您是否要配置 Objective-C 桥接头,Swift 代码调用 C ++头需要此头。 Objective-C 代码。 因此,单击创建桥接标题按钮:
4. 在 Xcode 中打开`HelloTensorFlow_Swift.xcworkspace`文件,然后将两个文件(`ios_image_load.mm``ios_image_load.h`)拖放到 TensorFlow iOS 示例目录`tensorflow/examples/ios/simple``HelloTensorFlow_Swift`项目文件夹中。 当将两个文件添加到项目中时,您将看到一个消息框,如以下屏幕截图所示,询问您是否要配置 Objective-C 桥接头,Swift 代码调用 C++ 头需要此头。 Objective-C 代码。 因此,单击创建桥接标题按钮:
![](img/afa22723-f59f-4544-8a61-e73ee7c43e85.png)Figure 2.9 Creating Bridging Header when adding C++ file
......
......@@ -87,7 +87,7 @@ sudo pip install matplotlib
protoc object_detection/protos/*.proto --python_out=.
```
这将编译`object_detection/protos`目录中的所有 Protobuf,以使 TensorFlow 对象检测 API 满意。 Protobuf 或 Protocol Buffer 是一种自动序列化和检索结构化数据的方法,它比 XML 轻巧且效率更高。 您所需要做的就是编写一个描述数据结构的.proto 文件,然后使用 protoc(proto 编译器)生成自动解析和编码 protobuf 数据的代码。 注意`--python_out`参数指定了所生成代码的语言。 在本章的下一部分中,当我们讨论如何在 iOS 中使用模型时,我们将使用带有`--cpp_out`的协议编译器,因此生成的代码是 C ++。 有关协议缓冲区的完整文档,请参见[这里](https://developers.google.com/protocol-buffers)
这将编译`object_detection/protos`目录中的所有 Protobuf,以使 TensorFlow 对象检测 API 满意。 Protobuf 或 Protocol Buffer 是一种自动序列化和检索结构化数据的方法,它比 XML 轻巧且效率更高。 您所需要做的就是编写一个描述数据结构的.proto 文件,然后使用 protoc(proto 编译器)生成自动解析和编码 protobuf 数据的代码。 注意`--python_out`参数指定了所生成代码的语言。 在本章的下一部分中,当我们讨论如何在 iOS 中使用模型时,我们将使用带有`--cpp_out`的协议编译器,因此生成的代码是 C++ 。 有关协议缓冲区的完整文档,请参见[这里](https://developers.google.com/protocol-buffers)
4. 仍在模型/研究中,运行`export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim`,然后运行`python object_detection/builders/model_builder_test.py`以验证一切正常。
5. 启动`jupyter notebook`命令并在浏览器中打开`http://localhost:8888`。 首先单击`object_detection`,然后选择`object_detection_tutorial.ipynb`笔记本并逐个单元运行演示。
......@@ -477,7 +477,7 @@ If you run the app, after completing the following steps, and encounter an error
-force_load $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/lib/libtensorflow-core.a $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/protobuf_ios/lib/libprotobuf.a $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/protobuf_ios/lib/libprotobuf-lite.a $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/downloads/nsync/builds/lipo.ios.c++11/nsync.a
```
需要第一个–force_load,因为它确保 TensorFlow 所需的 C ++构造函数将被链接,否则,您仍可以构建和运行该应用程序,但会遇到有关未注册会话的错误。
需要第一个–force_load,因为它确保 TensorFlow 所需的 C++ 构造函数将被链接,否则,您仍可以构建和运行该应用程序,但会遇到有关未注册会话的错误。
最后一个库用于 nsync,这是一个 C 库,[可导出互斥量和其他同步方法](https://github.com/google/nsync)。 在新的 TensorFlow 版本中引入。
......@@ -566,7 +566,7 @@ LoadGraph 尝试加载三个用户选择的模型文件之一,并返回加载
Found 1 possible inputs: (name=image_tensor, type=uint8(4), shape=[?,?,?,3])
```
这就是为什么我们需要使用`uint8`创建一个图像张量,而不是`float`类型来加载到我们的模型,否则在运行模型时会出现错误。 另请注意,当我们使用 TensorFlow C ++ API 的`Session``Run`方法将`image_data`转换为`Tensor`类型的`image_data`时,我们不使用`input_mean``input_std`就像我们在使用图像分类模型时所做的(有关详细比较,请参见第 2 章,“通过迁移学习对图像进行分类”的 HelloTensorFlow 应用程序的`RunInferenceOnImage`实现)。 我们知道有四个名为`detection_boxes``detection_scores``detection_classes``num_detections`的输出,因此`RunInferenceOnImage`具有以下代码来为模型输入图像输入并获得四个输出:
这就是为什么我们需要使用`uint8`创建一个图像张量,而不是`float`类型来加载到我们的模型,否则在运行模型时会出现错误。 另请注意,当我们使用 TensorFlow C++ API 的`Session``Run`方法将`image_data`转换为`Tensor`类型的`image_data`时,我们不使用`input_mean``input_std`就像我们在使用图像分类模型时所做的(有关详细比较,请参见第 2 章,“通过迁移学习对图像进行分类”的 HelloTensorFlow 应用程序的`RunInferenceOnImage`实现)。 我们知道有四个名为`detection_boxes``detection_scores``detection_classes``num_detections`的输出,因此`RunInferenceOnImage`具有以下代码来为模型输入图像输入并获得四个输出:
```py
tensorflow::Tensor image_tensor(tensorflow::DT_UINT8, tensorflow::TensorShape({1, image_height, image_width, wanted_channels}));
......
......@@ -520,7 +520,7 @@ dispatch_async(dispatch_get_global_queue(0, 0), ^{
});
```
6.`audioRecognition`方法内部,我们首先定义一个 C ++ `string`数组,其中包含要识别的 10 个命令以及两个特殊值`"_silence_"``"_unknown_"`
6.`audioRecognition`方法内部,我们首先定义一个 C++ `string`数组,其中包含要识别的 10 个命令以及两个特殊值`"_silence_"``"_unknown_"`
```py
std::string commands[] = {"_silence_", "_unknown_", "yes", "no", "up", "down", "left", "right", "on", "off", "stop", "go"};
......@@ -548,7 +548,7 @@ std::string input_name2 = "decoded_sample_data:1";
std::string output_name = "labels_softmax";
```
7. 对于`"decoded_sample_data:0"`,我们需要将采样率值作为标量发送(否则在调用 TensorFlow Session 的`run`方法时会出错),并且在 TensorFlow C ++ API 中定义了张量,如下所示:
7. 对于`"decoded_sample_data:0"`,我们需要将采样率值作为标量发送(否则在调用 TensorFlow Session 的`run`方法时会出错),并且在 TensorFlow C++ API 中定义了张量,如下所示:
```py
tensorflow::Tensor samplerate_tensor(tensorflow::DT_INT32, tensorflow::TensorShape());
......@@ -644,7 +644,7 @@ The process on how to fix the "Not found: Op type not registered" error is a big
我们在第 2 章中使用 TensorFlow 窗格创建了一个基于 Swift 的 iOS 应用。 现在让我们创建一个新的 Swift 应用程序,该应用程序使用我们在上一节中手动构建的 TensorFlow iOS 库,并在我们的 Swift 应用程序中使用语音命令模型:
1. 通过 Xcode 创建一个新的 Single View iOS 项目,并按照与上一节中的步骤 1 和 2 相同的方式设置该项目,除了将语言设置为 Swift。
2. 选择 Xcode 文件 | 新增 | 文件 ...,然后选择 Objective-C 文件。 输入名称`RunInference`。 您将看到一个消息框,询问您“您是否要配置一个 Objective-C 桥接头?” 单击创建桥接标题。 将文件`RunInference.m`重命名为`RunInfence.mm`,因为我们将混合使用 C,C ++和 Objective-C 代码来进行后期录音音频处理和识别。 我们仍在 Swift 应用程序中使用 Objective-C,因为要从 Swift 调用 TensorFlow C ++代码,我们需要一个 Objective-C 类作为 C ++代码的包装。
2. 选择 Xcode 文件 | 新增 | 文件 ...,然后选择 Objective-C 文件。 输入名称`RunInference`。 您将看到一个消息框,询问您“您是否要配置一个 Objective-C 桥接头?” 单击创建桥接标题。 将文件`RunInference.m`重命名为`RunInfence.mm`,因为我们将混合使用 C,C++ 和 Objective-C 代码来进行后期录音音频处理和识别。 我们仍在 Swift 应用程序中使用 Objective-C,因为要从 Swift 调用 TensorFlow C++ 代码,我们需要一个 Objective-C 类作为 C++ 代码的包装。
3. 创建一个名为`RunInference.h`的头文件,并添加以下代码:
```py
......@@ -772,7 +772,7 @@ func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully f
}
```
如果您确实想将尽可能多的代码移植到 Swift,[则可以用 Swift 替换 C 中的音频文件转换代码](https://developer.apple.com/documentation/audiotoolbox/extended_audio_file_services)。 还有一些非官方的开源项目提供了官方 TensorFlow C ++ API 的 Swift 包装器。 但是为了简单起见和达到适当的平衡,我们将保持 TensorFlow 模型的推论,在本示例中,还将保持音频文件的读取和转换,以及在 C ++和 Objective-C 中与控制 UI 和 录音,并启动呼叫以进行音频处理和识别。
如果您确实想将尽可能多的代码移植到 Swift,[则可以用 Swift 替换 C 中的音频文件转换代码](https://developer.apple.com/documentation/audiotoolbox/extended_audio_file_services)。 还有一些非官方的开源项目提供了官方 TensorFlow C++ API 的 Swift 包装器。 但是为了简单起见和达到适当的平衡,我们将保持 TensorFlow 模型的推论,在本示例中,还将保持音频文件的读取和转换,以及在 C++ 和 Objective-C 中与控制 UI 和 录音,并启动呼叫以进行音频处理和识别。
这就是构建使用语音命令识别模型的 Swift iOS 应用所需的全部内容。 现在,您可以在 iOS 模拟器或实际设备上运行它,并看到与 Objective-C 版本完全相同的结果。
......
......@@ -389,7 +389,7 @@ bazel-bin/tensorflow/python/tools/optimize_for_inference \
但是在 iOS 或 Android 应用上加载输出模型文件 `image2text_frozen_optimized.pb`会导致相同的`Input 0 of node ExpandDims_6 was passed float from input_feed:0 incompatible with expected int64` 错误。 看起来,尽管我们试图至少在某种程度上实现福尔摩斯在本章中可以做的事情,但有人希望我们首先成为福尔摩斯。
如果您在其他型号(例如我们在前几章中看到的型号)上尝试过`strip_unused``optimize_for_inference`工具,则它们可以正常工作。 事实证明,尽管官方 TensorFlow 1.4 和 1.5 发行版中包含了两个基于 Python 的工具,但在优化一些更复杂的模型时却存在一些错误。 更新和正确的工具是基于 C ++`transform_graph`工具,现在是 [TensorFlow Mobile 网站](https://www.tensorflow.org/mobile)推荐的官方工具。 运行以下命令以消除在移动设备上部署时与 int64 错误不兼容的 float:
如果您在其他型号(例如我们在前几章中看到的型号)上尝试过`strip_unused``optimize_for_inference`工具,则它们可以正常工作。 事实证明,尽管官方 TensorFlow 1.4 和 1.5 发行版中包含了两个基于 Python 的工具,但在优化一些更复杂的模型时却存在一些错误。 更新和正确的工具是基于 C++ `transform_graph`工具,现在是 [TensorFlow Mobile 网站](https://www.tensorflow.org/mobile)推荐的官方工具。 运行以下命令以消除在移动设备上部署时与 int64 错误不兼容的 float:
```py
bazel build tensorflow/tools/graph_transforms:transform_graph
......@@ -459,7 +459,7 @@ target 'Image2Text'
![](img/62be062e-39dc-4f0c-8a59-7e0e675bafaa.png)Figure 6.5: Setting up the Image2Text iOS app, also showing how LoadMemoryMappedModel is implemented
3. 打开`ViewController.mm`并添加一堆 Objective-C 和 C ++ 常量,如下所示:
3. 打开`ViewController.mm`并添加一堆 Objective-C 和 C++ 常量,如下所示:
```py
static NSString* MODEL_FILE = @"image2text_frozen_transformed";
......@@ -527,7 +527,7 @@ dispatch_async(dispatch_get_global_queue(0, 0), ^{
如果选择了非映射模型,则使用`generateCaption(false)`
5.`viewDidLoad`方法的末尾,添加代码以加载`word_counts.txt`并将这些单词逐行保存在 Objective-C 和 C ++中:
5.`viewDidLoad`方法的末尾,添加代码以加载`word_counts.txt`并将这些单词逐行保存在 Objective-C 和 C++ 中:
```py
NSString* voc_file_path = FilePathForResourceName(VOCAB_FILE, VOCAB_FILE_TYPE);
......
......@@ -31,7 +31,7 @@ AlphaZero 算法包含三个主要组件:
* 一个深度卷积神经网络,它以棋盘位置(或状态)为输入,并从该位置输出一个值作为预测的博弈结果,该策略是输入棋盘状态下每个可能动作的移动概率列表。
* 一种通用的强化学习算法,该算法通过自玩从头开始学习,除了游戏规则外,没有特定的领域知识。 通过自增强学习学习深度神经网络的参数,以使预测值与实际自游戏结果之间的损失最小,并使预测策略与搜索概率之间的相似性最大化,这来自以下算法。
* 一种通用(与域无关)的**蒙特卡洛树搜索****MCTS**)算法,该算法从头至尾模拟自玩游戏,并通过 考虑到从深度神经网络返回的预测值和策略概率值,以及访问节点的频率—有时,选择访问次数较少的节点称为强化学习中的探索(与采取较高预测值的举动相反) 价值和政策,这称为剥削)。 勘探与开发之间的良好平衡可以带来更好的结果。
* 一种通用(与域无关)的**蒙特卡洛树搜索****MCTS**)算法,该算法从头至尾模拟自玩游戏,并通过 考虑到从深度神经网络返回的预测值和策略概率值,以及访问节点的频率—有时,选择访问次数较少的节点称为强化学习中的探索(与采取较高预测值的举动相反) 价值和政策,这称为剥削)。 探索与开发之间的良好平衡可以带来更好的结果。
强化学习的历史可以追溯到 1960 年代,当时该术语在工程文献中首次使用。 但是突破发生在 2013 年,当时 DeepMind 将强化学习与深度学习相结合,并开发了深度强化学习应用程序,该应用程序学会了从头开始玩 Atari 游戏,以原始像素为输入的,并随后击败了人类。 与监督学习不同,监督学习需要标记数据进行训练,就像我们在前几章中建立或使用的许多模型中所看到的那样,强化学习使用反复试验的方法来获得更好的效果:代理与环境交互并接收 对每个州采取的任何行动都会获得奖励(正面或负面)。 在 AlphaZero 下棋 象棋的示例中,只有在游戏结束后才能获得奖励,获胜的结果为+1,失败的为-1,平局为 0。强化学习 AlphaZero 中的算法对我们前面提到的损失使用梯度下降来更新 深层神经网络的 参数, 就像一个通用函数近似,可以学习和 编码游戏技巧。
......
......@@ -34,7 +34,7 @@ You can also pick a prebuilt TensorFlow Lite model, such as the MobileNet models
2. 构建 TensorFlow Lite 转换器工具。 如果您从[这里](https://github.com/tensorflow/tensorflow/releases)下载 TensorFlow 1.5 或 1.6 版本,则可以从 TensorFlow 源根目录在终端上运行`bazel build tensorflow/contrib/lite/toco:toco`。 如果您使用更高版本或获取最新的 TensorFlow 仓库,您应该可以使用此`build`命令来执行此操作,但如果没有,请查看该新版本的文档。
3. 使用 TensorFlow Lite 转换器工具将 TensorFlow 模型转换为 TensorFlow Lite 模型。 在下一节中,您将看到一个详细的示例。
4. 在 iOS 或 Android 上部署 TensorFlow Lite 模型-对于 iOS,使用 C ++ API 加载和运行模型; 对于 Android,请使用 Java API(围绕 C ++ API 的包装器)加载和运行模型。 与我们之前在 TensorFlow Mobile 项目中使用的`Session`类不同,C ++和 Java API 均使用 TensorFlow-lite 特定的`Interpreter`类来推断模型。 在接下来的两个部分中,我们将向您展示 iOS C ++代码和 Android Java 代码以使用`Interpreter`
4. 在 iOS 或 Android 上部署 TensorFlow Lite 模型-对于 iOS,使用 C++ API 加载和运行模型; 对于 Android,请使用 Java API(围绕 C++ API 的包装器)加载和运行模型。 与我们之前在 TensorFlow Mobile 项目中使用的`Session`类不同,C++ 和 Java API 均使用 TensorFlow-lite 特定的`Interpreter`类来推断模型。 在接下来的两个部分中,我们将向您展示 iOS C++ 代码和 Android Java 代码以使用`Interpreter`
If you run a TensorFlow Lite model on Android, and if the Android device is Android 8.1 (API level 27) or above and supports hardware acceleration with a dedicated neural network hardware, a GPU, or some other digital signal processors, then the `Interpreter` will use the Android Neural Networks API ([https://developer.android.com/ndk/guides/neuralnetworks/index.html](https://developer.android.com/ndk/guides/neuralnetworks/index.html)) to speed up the model running. For example, Google's Pixel 2 phone has a custom chip optimized for image processing, which can be turned on with Android 8.1, and support hardware acceleration.
......@@ -114,7 +114,7 @@ target 'HelloTFLite'
pod 'TensorFlowLite'
```
运行`pod install`。 然后在 Xcode 中打开 `HelloTFLite.xcworkspace`,将`ViewController.m`重命名为`ViewController.mm`,并添加必要的 C ++头文件和 TensorFlow Lite 头文件。 您的 Xcode 项目应类似于以下屏幕截图:
运行`pod install`。 然后在 Xcode 中打开 `HelloTFLite.xcworkspace`,将`ViewController.m`重命名为`ViewController.mm`,并添加必要的 C++ 头文件和 TensorFlow Lite 头文件。 您的 Xcode 项目应类似于以下屏幕截图:
![](img/b4b1750b-cc97-42f8-a032-226b595d7e46.png)Figure 11.1 A new Xcode iOS project using the TensorFlow Lite podWe're only showing you how to use the TensorFlow Lite pod in your iOS apps. There's another way to add TensorFlow Lite to iOS, similar to building the custom TensorFlow Mobile iOS library that we've done many times in the previous chapters. For more information on how to build your own custom TensorFlow Lite iOS library, see the documentation at [https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/g3doc/ios.md](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/g3doc/ios.md).
......
......@@ -36,7 +36,7 @@
* [Arducam 5 Megapixels 1080p 传感器 OV5647 微型相机](https://www.amazon.com/gp/product/B012V1HEP4)约合 14 美元,以支持图像分类。
* [16 GB MicroSD 和适配器](https://www.amazon.com/gp/product/B00TDBLTWK),价格约为 10 美元,用于存储 Raspbian(Raspberry Pi 的官方操作系统)的安装文件,并用作 安装后的硬盘驱动器。
* 一个 USB 磁盘,例如 [SanDisk 32GB USB Drive](https://www.amazon.com/gp/product/B008AF380Q),售价 9 美元,将用作交换分区(有关详细信息,请参阅下一节) 因此我们可以手动构建 TensorFlow 库,这是构建和运行 TensorFlow C ++代码所必需的。
* 一个 USB 磁盘,例如 [SanDisk 32GB USB Drive](https://www.amazon.com/gp/product/B008AF380Q),售价 9 美元,将用作交换分区(有关详细信息,请参阅下一节) 因此我们可以手动构建 TensorFlow 库,这是构建和运行 TensorFlow C++ 代码所必需的。
* 售价 110 美元的 [GoPiGo 机器人基础套件](https://www.amazon.com/gp/product/B00NYB3J0A)[官方网站](https://www.dexterindustries.com/shop),将 Raspberry Pi 板变成可以移动的机器人。
您还需要 HDMI 电缆将 Raspberry Pi 板连接到计算机显示器,USB 键盘和 USB 鼠标。 总共要花 200 美元,包括 110 美元的 GoPiGo,来构建一个可以移动,看,听,说的 Raspberry Pi 机器人。 尽管与功能强大的 Raspberry Pi 计算机相比,GoPiGo 套件似乎有点昂贵,但是如果没有它,那么一动不动的 Raspberry Pi 可能会失去很多吸引力。
......@@ -342,7 +342,7 @@ tensorflow/contrib/pi_examples/label_image/gen/bin/label_image
tensorflow/contrib/pi_examples/camera/gen/bin/camera
```
看看 C ++源代码`tensorflow/contrib/pi_examples/label_image/label_image.cc``tensorflow/contrib/pi_examples/camera/camera.cc`,您会看到它们使用与前几章中的 iOS 应用类似的 C ++代码来加载模型图文件,准备输入张量,运行 模型,并获得输出张量。
看看 C++ 源代码`tensorflow/contrib/pi_examples/label_image/label_image.cc``tensorflow/contrib/pi_examples/camera/camera.cc`,您会看到它们使用与前几章中的 iOS 应用类似的 C++ 代码来加载模型图文件,准备输入张量,运行 模型,并获得输出张量。
默认情况下,摄像机示例还使用`label_image/data`文件夹中解压缩的预构建 Inception 模型。 但是对于您自己的特定图像分类任务,提供通过迁移学习重新训练的模型。您可以像第 2 章,“通过迁移学习对图像进行分类”一样,在运行两个示例应用程序时使用`--graph`参数。
......@@ -415,7 +415,7 @@ run(softmax_tensor, {
在这里,将`softmax_tensor`定义为 TensorFlow 图的`get_tensor_by_name(self.output_name_)`,将`output_name_``input_samples_name_``input_rate_name_`分别定义为`labels_softmax``decoded_sample_data:0``decoded_sample_data:1`,与 我们在第 5 章,“了解简单语音命令”中的 iOS 和 Android 应用程序中使用过。
在之前的章节中,我们主要使用 Python 训练和测试 TensorFlow 模型,然后再使用本机 TensorFlow C ++库的 Java 接口代码在使用 C ++或 Android 的 iOS 中运行模型。 在 Raspberry Pi 上,您可以选择直接使用 TensorFlow Python API 或 C ++ API 在 Pi 上运行 TensorFlow 模型,尽管通常仍会在功能更强大的模型上进行训练 电脑。 有关完整的 TensorFlow Python API 文档,请参见[这里](https://www.tensorflow.org/api_docs/python)
在之前的章节中,我们主要使用 Python 训练和测试 TensorFlow 模型,然后再使用本机 TensorFlow C++ 库的 Java 接口代码在使用 C++ 或 Android 的 iOS 中运行模型。 在 Raspberry Pi 上,您可以选择直接使用 TensorFlow Python API 或 C++ API 在 Pi 上运行 TensorFlow 模型,尽管通常仍会在功能更强大的模型上进行训练 电脑。 有关完整的 TensorFlow Python API 文档,请参见[这里](https://www.tensorflow.org/api_docs/python)
要使用 GoPiGo Python API 使机器人根据您的语音命令移动,请首先在`listen.py`中添加以下两行:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册