本文概述
- R中的深度学习:软件包简短概述
- 辛苦了, 辛苦了
- 安装keras软件包
- 加载数据
- 数据探索
- 数据预处理
- 建立模型
- 编译并拟合模型
- 可视化模型训练历史
- 预测新数据的标签
- 评估模型
- 微调你的模型
- 保存, 加载或导出模型
- Keras深度学习
今天的教程将通过keras软件包为你简要介绍R与Keras的深度学习:
- 你将首先简要概述R中的深度学习软件包, 以及
- 你将详细了解Keras, kerasR和keras软件包之间的区别, 以及当一个软件包是另一个软件包的接口时的含义;
- 然后, 你将真正开始使用RStudio的keras软件包:你将学习如何首先准备工作区并加载内置数据集, 虚拟数据和CSV数据;
- 接下来, 你将看到如何浏览和预处理从CSV文件加载的数据:将数据标准化并将其分为训练和测试集。
- 之后, 你就可以构建深度学习模型了;在这种情况下, 你将构建用于多层分类的多层感知器(MLP)。
- 你将学习如何将模型编译和拟合到数据, 如何可视化训练历史以及
- 你将根据测试数据预测目标值;
- 最后, 你将评估模型, 解释结果并微调模型以使其表现更好:你将学习如何添加层和隐藏层, 并且将了解如何调整优化参数以取得更好的结果。
- 此外, 你可能想保存(优化)模型或在其他时间重新加载模型。你将在本教程的最后一部分中看到如何完成此操作!
提示:在这里找到我们的Keras备忘单。
R中的深度学习:软件包简短概述 随着深度学习的普及, CRAN已增加了更多的R深度学习软件包。在下面, 你可以从机器学习和统计学习CRAN任务视图中看到这些软件包的概述。 “ 百分比” 列指示在RDocumentation上找到的百分比:
R包 | 百分位 | 描述 |
---|---|---|
网络 | 第96名 | 用于具有单个隐藏层的前馈神经网络以及多项式对数线性模型的软件。 |
神经网络 | 第96名 | 使用反向传播训练神经网络 |
水 | 第95名 | H2O的R脚本功能 |
神经网络 | 第88名 | Interface to the Stuttgart Neural Network Simulator (SNNS) |
张量流 | 第88名 | 与TensorFlow的接口 |
深网 | 第84名 | R中的深度学习工具包 |
达奇 | 第79名 | 深度架构和受限玻尔兹曼机器的软件包 |
恩 | 第73名 | Package to implement Recurrent Neural Networks (RRNs) |
FCNN4R | 第52名 | 与FCNN库的接口, 该库允许用户扩展ANN |
rcppDL | 第七名 | 实现具有多层(深度学习)的基本机器学习方法, 包括dA(降噪自动编码器), SdA(堆叠式降噪自动编码器), RBM(受限玻尔兹曼机器)和DBN(深信服网络) |
更深 | ??* | 打包以简化基于darch和deepnet的深度学习的训练, 微调和预测过程 |
MXNetR | ??* | 该软件包可为R提供灵活高效的GPU计算和最新的深度学习 |
在RDocumentation.org上找不到deeper和MXNetR, 因此这两个软件包的百分位数未知。
辛苦了, 辛苦了 最近, 有两个新的软件包进入R社区:由Taylor Arnold创作和创建的kerasR软件包, 以及RStudio的keras软件包。
这两个软件包都为Python深度学习软件包Keras提供了一个R接口, 你可能已经听说过, 或者甚至已经使用过它!对于那些不知道Keras软件包必须提供给Python用户的人来说, 它是” 高级神经网络API, 用Python编写, 并且能够在TensorFlow, Microsoft Cognitive Toolkit(CNTK)或Theano” 。
接口?
你会看到, 开始使用Keras是熟悉Python深度学习的最简单方法之一, 这也解释了为什么kerasR和keras软件包为R用户提供了这个出色的软件包的接口。
在这种情况下, 最好理解当R keras之类的软件包是另一个软件包Python Keras的” 接口” 时的确切含义。简而言之, 这意味着带有接口的keras R包使你可以享受R编程的好处, 同时可以访问Python Keras包的功能。
请注意, 这并不是一种不常见的做法:例如, h2o包还提供了一个接口, 但在这种情况下-顾名思义, 它是对H2O的一种开放源代码数学引擎, 你可以使用它来计算并行分布式机器学习算法。你可能知道提供接口的其他软件包是RWeka(与Weka相连的R接口), tensorflow(与TensorFlow相连的R接口), openml-r(与OpenML相连的R接口), ……你可以继续进行下去!
Keras, keras和kerasR有什么区别?
现在, 你已经了解了所有这些内容, 你可能首先会问自己以下问题:如何将原始Python包与R包进行比较?
从本质上讲, 你不会在R包和原始Python包之间发现太多差异, 主要是因为函数名称几乎完全相同;你注意到的唯一区别主要是编程语言本身(变量赋值, 库加载等), 但是要注意的最重要的事实是R包中已合并了多少原始功能。
请注意, 此注释不仅对keras库有效, 而且对tensorflow, openml-r, …和上面提到的其他接口包也有效!
其次, 你可能还想知道这两个R包之间的区别是什么。好吧, 如果你想考虑两者的不同之处, 则可能需要考虑以下几点:
- keras程序包使用管道运算符(%>
%)将功能或操作连接在一起, 尽管你在kerasR中找不到它:例如, 要使用kerasR创建模型, 你将看到需要使用$运算符。管道运算符的使用通常可以提高代码的可读性, 如果你以前使用过Tidyverse程序包, 那么你肯定已经看到了该运算符。
- 你会看到kerasR所包含的函数以与原始Keras程序包相似但不完全相同的方式命名。例如, 初始(Python)compile()函数称为keras_compile();其他功能也是如此, 例如fit()变成keras_fit()或预报(), 当你使用kerasR包时就是keras_predict。这些都是自定义包装器。
- 你可以说, RStudio的keras软件包的安装比kerasR软件包的安装容易。要安装后者, 首先需要确保配置要使用的Python版本, 如果你正在安装有多个环境或Python版本的PC上进行安装, 这可能会很棘手。但是, 我让你决定:)
让我们继续吧!
安装keras软件包 与往常一样, 开始使用任何软件包的第一步是设置你的工作区:将库安装并加载到RStudio或你正在使用的任何环境中。
不用担心, 在本教程中, 软件包将为你加载!
首先, 确保安装了keras:你可以通过在控制台中运行devtools :: install_github(” rstudio / keras” )轻松地完成安装。接下来, 你可以加载软件包并安装TensorFlow:
# Load in the keras packagelibrary(keras)# Install TensorFlowinstall_tensorflow()
完成此操作后, 你就可以开始了!很快, 对吗?
提示:有关安装过程的更多信息, 请访问软件包网站。
加载数据 现在安装过程是透明的, 并且你的工作区已经准备就绪, 你可以开始加载数据了!至此, 你在数据方面有三大选择:你可以选择使用keras软件包随附的内置数据集之一, 可以从CSV文件中加载自己的数据集, 或者你可以制作一些虚拟数据。
无论你处于哪种情况, 都将看到你可以快速开始使用该软件包。本节将快速介绍这三个选项, 并说明如何加载(或创建)入门所需的数据!
内置数据集
如果你以前有使用Python的Keras软件包的经验, 则可能已经使用mnist.load_data(), cifar10.load_data()或imdb.load_data()之类的函数访问了Keras内置数据集。
以下是一些使用keras软件包加载MNIST, CIFAR10和IMDB数据的示例:
# Read in MNIST datamnist <
- dataset_mnist()# Read in CIFAR10 datacifar10 <
- dataset_cifar10()# Read in IMDB dataimdb <
- dataset_imdb()
注意, 所有使用keras加载到内置数据集中的函数都遵循相同的模式;对于MNIST数据, 你将使用dataset_mnist()函数加载数据。
虚拟数据
另外, 你也可以快速制作一些虚拟数据以开始使用。你可以轻松地使用matrix()函数完成此操作:
请注意, 检查数据的数据结构是个好主意;重要的是已经知道你正在使用什么数据, 这对于以后需要执行的步骤很有必要。你将在本教程的后面部分中了解有关此内容的更多信息!
从文件读取数据
除了内置的数据集, 你还可以从文件中加载数据。在本教程中, 你将集中精力从CSV文件加载数据, 但是如果你想了解有关在R中导入文件的更多信息, 请考虑srcmini的R Data Import Tutorial。
让我们使用read.table包中的read.csv()函数从UCI机器学习存储库中加载数据集:
检查数据导入是否成功始终是个好主意。你通常像上面的srcmini Light块中那样使用诸如head(), str()和dim()之类的函数来快速执行此操作。
这三个函数的结果不会立即指出任何异常。通过查看str()函数的输出, 你可以看到Species列的字符串已作为因子读取。没问题, 但是一定要对下一步有所了解, 这是你将在其中探索和预处理数据的地方。
数据探索 在本教程中, 你将继续使用通过read.csv()函数导入的著名虹膜数据集。
对于那些不具备处理这些数据所需的生物学知识的人, 以下是一些背景信息:所有花朵都包含萼片和花瓣。萼片包围花瓣, 并且通常是绿色的和叶状的, 而花瓣通常是有色的叶子。对于鸢尾花, 这只是有些不同, 如下图所示:
文章图片
在上一节中, 你可能已经看到, 虹膜数据框在导入后没有任何列名。现在, 在本教程的其余部分中, 这并不是太重要:即使read.csv()函数将data.frame中的数据返回给你, 你仍需要将传递给fit()函数的数据成为矩阵或数组。
关于刚才提到的这两个数据结构, 要记住一些事情:-矩阵和数组没有列名; -矩阵是单一数据类型的二维对象; -数组是单一数据类型的多维对象;
提示:如果你想回顾一下R中的数据结构, 请观看此视频!
请注意, 另一方面, 数据帧是一种特殊的命名列表, 其中所有元素都具有相同的长度。这是一个多维对象, 可以包含多种数据类型。在上一节中检出虹膜数据框的结构时, 你已经看到这是正确的。知道这一点并考虑到你将需要处理单一数据类型的二维或多维对象, 在开始构建神经网络之前, 你应该已经准备好进行一些预处理!
目前, 列名可以方便地进行探索, 并且绝对可以帮助你理解数据, 所以让我们在names()函数的帮助下添加一些列名。接下来, 你可以立即在数据浏览中使用iris变量!例如, 绘制花瓣长度和宽度如何与plot()函数相关。
请注意, 你使用unclass()函数将物种名称(即” setosa, versicolor” 和” virginica” )转换为数字1、2和3。
现在, 仔细查看绘图功能的结果:
文章图片
该图指示了鸢尾花的不同种类的花瓣长度和花瓣宽度之间的正相关。但是, 你可能需要使用cor()函数进行测试, 这将为你提供数据集中包含的所有属性之间的整体相关性:
此外, 你可以结合使用corrplot包和cor()函数来绘制数据属性之间的相关性;在这种情况下, 你将计算虹膜数据帧的所有属性的整体相关性。你可以将计算结果存储在变量M中, 并将其传递给corrplot()函数。
另外, 不要忘记指定一个方法参数来指示如何绘制数据!
你可以在下面的srcmini Light块中进一步尝试可视化方法:
文章图片
利用R控制台进一步浏览数据。
如果你想使用ggvis软件包(图形的交互式语法)对此数据进行绘图, 请查看R For Beginners教程中srcmini的机器学习或srcmini的ggvis课程。
数据预处理 在构建模型之前, 还需要确保数据已清理, 标准化(如果适用)并分为训练集和测试集。由于数据集来自UCI机器学习存储库, 因此你可以期望它已经比较干净了, 但是无论如何, 我们还是要仔细检查数据的质量。
乍一看, 当你使用head()检查数据时, 你真的看不到任何异常, 对吧?让我们利用summary()和str()简要回顾一下检查数据导入是否成功时所学到的知识:
现在, 你已经确定数据足够干净, 可以先检查一下本教程要使用的任何数据是否需要规范化。
使用用户定义的函数(UDF)规范化数据
从上面的srcmini Light块中的summary()函数的结果可以看到, 不需要对Iris数据集进行规范化:Sepal.Length属性的值从4.3到7.9, 并且Sepal.Width包含值从2到4.4, 而Petal.Length的值从1到6.9, 而Petal.Width的值从0.1到2.5。换句话说, 虹膜数据集的所有属性的所有值都包含在0.1到7.9的范围内, 你可以认为这是可以接受的。
但是, 研究规范化对数据的影响仍然是一个好主意。你甚至可以将标准化数据传递到模型, 以查看是否有任何效果。这不在本教程的讨论范围之内, 但是你可以自己尝试一下!本教程中的代码都在这里:)
你可以使用自己的功能对虹膜数据进行归一化。在这种情况下, 它是min-max归一化函数, 可将你的数据线性转换为(x-min)/(max-min)函数。从这个角度来看, 将此公式转换为R非常简单:你可以将x或一些数据传递给该函数。然后, 你将计算第一个减法x分钟的结果, 并将结果存储在num中。接下来, 你还可以计算最大最小值, 并将结果存储在denom中。 normalize()函数的结果应返回num除以max。
要将此用户定义的函数应用于虹膜数据(不包括目标值), 不仅需要使用normalize, 还需要使用lapply()函数对数据进行规范化, 如下所示:
提示使用R控制台中的hist()函数研究规范化之前(iris)和规范化之后(iris_norm)的虹膜数据分布。
使用keras规范化你的数据
要使用keras软件包中的normalize()函数, 首先需要确保你正在使用矩阵。你可能早些时候还记得过, 矩阵的特征是矩阵数据元素具有相同的基本类型。在这种情况下, 你具有类型因子的目标值, 而其余均为数字。
这需要首先改变。
你可以使用as.numeric()函数将数据转换为数字:
可以使用数字数据框, 但是如果要使用keras包, 则需要将数据转换为数组或矩阵。你可以使用as.matrix()函数轻松地完成此操作;不要忘记在此处将暗号设置为NULL。
正如你可能已经在上一节中阅读的那样, 无需对虹膜数据进行标准化。尽管如此, 研究规范化及其效果, 以及不仅可以使用UDF还能使用keras内置的normalize()函数来完成此操作, 还是一个好主意。
将数据转换为矩阵后, 你实际上也可以使用keras包来研究可能的规范化对数据的影响:
请注意, 在这里, 你使用dimnames()将iris的暗号设置为NULL。这样可以确保数据中没有列名。
培训和测试集
既然你已经检查了数据的质量并且知道没有必要对数据进行规范化, 那么你可以继续使用原始数据并将其分为训练和测试集, 以便最终准备开始构建自己的数据。模型。这样, 你可以确保以后可以对预测模型的性能进行诚实的评估。
在将数据分为训练集和测试集之前, 最好先设置一个种子。你可以使用set.seed()轻松完成此操作:使用此确切函数, 只需将随机整数传递给它即可。种子是R的随机数生成器的数量。设置种子的主要优点是, 只要在随机数生成器中提供相同的种子, 就可以获得相同的随机数序列。
这对于代码的可重复性非常有用!
【keras(R中的深度学习)】你使用sample()函数以设置为Iris数据集的行数或150的大小进行采样。进行替换采样:你从2个元素的向量中进行选择, 并指定1或2到Iris数据集的150行。元素的分配服从0.67和0.33的概率权重。
sample()函数的replace参数设置为TRUE, 这意味着你将1或2分配给特定的行, 然后将2的向量重置为其原始状态。
换句话说, 对于数据集中的下一行, 你可以再次分配1或2。选择1或2的概率不应与其余项之间的权重成正比, 因此你可以指定概率权重。
旁注:例如, 如果你将内置数据集与特定的dataset_imdb()函数一起使用, 则可以使用$运算符轻松拆分数据:
x_train <
- imdb$train$xy_train <
- imdb$train$yx_test <
- imdb$test$xy_test <
- imdb$test$y
一站式编码
你已经成功拆分了数据, 但是仍然需要完成一个步骤才能开始构建模型。你能猜出哪一个吗?
当你想用神经网络为多类别分类问题建模时, 通常最好的做法是确保将目标属性从包含每个类别值的向量转换为矩阵, 每个类别值具有一个布尔值, 给定实例是否具有该类值。
这是对一种热编码(OHE)的宽松解释。听起来很复杂, 不是吗?
幸运的是, keras软件包具有to_categorical()函数, 它将为你完成所有这些工作;将iris.trainingtarget和iris.testtarget传递给此函数, 并将结果存储在iris.trainLabels和iris.testLabels中:
现在, 你已正式到达本教程中探索和预处理步骤的结尾。现在, 你可以继续使用keras构建你的神经网络!
建立模型 要开始构建模型, 你首先应该在keras_model_sequential()函数的帮助下初始化顺序模型。然后, 你就可以开始建模了。
但是, 在开始之前, 最好重新考虑有关此数据集的原始问题:你可以预测某种鸢尾花的种类吗?使用数字数据更容易, 并且你已经对数据进行了预处理, 并且对目标变量的值进行了热编码:一朵花是versicolor, setosa或virginica类型的, 并且会用二进制1和0值反映出来。
多层感知器是在此类问题上表现良好的一种网络。这种类型的神经网络通常是完全连接的。这意味着你正在寻求构建相对简单的全连接层堆栈以解决此问题。至于你将要使用的激活功能, 最好使用此处最常用的激活功能之一, 以便熟悉Keras和神经网络, 即relu激活功能。该整流器激活功能用于隐藏层中, 这通常是一种良好的做法。
此外, 你还会看到在输出层中使用了softmax激活功能。这样做是因为你要确保输出值在0到1的范围内, 并且可以用作预测概率:
请注意, 输出层如何创建3个输出值, 每个虹膜类别(多功能花, 弗吉尼亚或s??etosa)一个。另一方面, 第一层包含8个隐藏的笔记, 其input_shape为4。这是因为你的训练数据iris.training有4列。
你可以使用以下功能进一步检查模型:
- 你可以使用summary()函数来打印模型的摘要表示。
- get_config()将返回一个包含模型配置的列表;
- get_layer()将返回图层配置。
- 图层属性可用于检索模型图层的扁平列表;
- 要列出输入张量, 可以使用inputs属性。和
- 最后, 要检索输出张量, 可以使用输出属性。
如果要编译模型, 则优化器和损失是两个参数。
使用的一些最受欢迎的优化算法是随机梯度下降(SGD), ADAM和RMSprop。根据你选择的算法, 你需要调整某些参数, 例如学习率或动量。损失函数的选择取决于你手头的任务:例如, 对于回归问题, 通常将使用均方误差(MSE)。
如本例所示, 你将categorical_crossentropy损失函数用于确定虹膜是versicolor, virginica还是setosa类型的多类分类问题。但是, 请注意, 如果你将遇到二进制分类问题, 则应使用binary_crossentropy损失函数。
接下来, 你还可以使模型适合你的数据;在这种情况下, 你将对iris.training和iris.trainLabels中的所有样本进行200个时期或迭代的训练, 每批5个样本。
提示:如果需要, 还可以在fit()函数中指定详细参数。通过将此参数设置为1, 表示你要查看进度条日志记录。
使用上面的代码执行的操作是针对指定次数的训练数据集或暴露于训练数据集来训练模型。一个时期是整个训练集的一次通过, 然后是验证集的测试。你在上面的代码中指定的批量大小定义了将通过网络传播的样本数量。另外, 这样做还可以优化效率, 因为你要确保不会同时将太多的输入模式加载到内存中。
可视化模型训练历史 另外, 很高兴知道, 如果你将上面的srcmini Light块中的代码行分配给变量, 那么你也可以形象地看到配件。然后, 你可以将变量传递给plot()函数, 如在此特定代码块中所见!
确保更详细地研究该图。
文章图片
乍一看, 这看起来有点凌乱也就不足为奇了。你可能不完全知道自己在看什么, 对吗?
要知道的一件好事是, 损失和acc指示训练数据模型的损失和准确性, 而对于测试或验证数据, val_loss和val_acc是相同的度量标准, 损失和准确性。
但是, 即使你知道这一点, 也很难解释这两个图形。让我们尝试将其分解为一些片段, 你可能会更容易理解!你将拆分这两个图, 然后分别绘制两个图:将一个用于模型损失, 另一个用于模型精度。幸运的是, 你可以轻松地使用$运算符来访问数据并逐步进行绘制。
请查看下面的srcmini Light框, 了解如何执行此操作:
文章图片
在第一个图中, 你在训练和测试数据上绘制了模型损失。现在该做同样的事情了, 但是为了确保模型的准确性:
文章图片
这里要记住以下几点:
- 如果你的训练数据准确性不断提高, 而验证数据准确性却越来越差, 则可能是过度拟合:你的模型开始只是记住数据, 而不是从中学习。
- 如果在最后几个时期两个数据集的准确性趋势仍在上升, 则你可以清楚地看到该模型尚未过度学习训练数据集。
你如何看待结果?乍一看, 你创建的此模型是否做出正确的预测?
评估模型 尽管你已经通过查看iris.test的预测标签对模型的性能有了些微了解, 但花时间评估模型仍然很重要。使用评价()函数执行此操作:传递测试数据iris.test, 测试标签iris.testLabels并定义批次大小。将结果存储在可变分数中, 如下面的代码示例所示:
通过打印分数, 你将获得损失值和指标值(在这种情况下为” 准确性” )。
微调你的模型 微调模型可能是你要做的事情, 尤其是在开始时, 因为并非所有分类和回归问题都像在本教程第一部分中看到的那样简单。如上文所述, 你可能已经要调整两个关键的决定:要使用多少个图层以及为每个图层选择多少个” 隐藏单位” 。
从一开始, 这确实是一个漫长的旅程。
除了处理时期数或批处理大小之外, 还有其他一些方法可以调整模型以希望其性能更好:通过增加层, 增加隐藏单元的数量以及通过自己的优化来调整模型compile()函数的参数。本节将介绍这三个选项。
添加图层
如果在模型中添加另一层会发生什么?如果看起来像这样怎么办?
请注意, 你还可以可视化此新模型的损耗和准确性指标!在下面的srcmini Light块中尝试一下:
|
|
还要尝试在模型的架构中添加更多隐藏单元的效果, 并研究对评估的影响, 如下所示:
请注意, 总的来说, 这并非总是最佳的优化方法, 因为如果你没有大量数据, 那么过度拟合的情况将会而且将会变得更糟。因此, 你应该尝试将小型网络与像这样的小型数据集结合使用。
为什么不尝试可视化模型中添加隐藏节点的效果?在下面尝试一下:
|
|
除了添加层并使用隐藏单元之外, 你还可以尝试调整提供给compile()函数的优化算法的(某些)参数。到目前为止, 你始终将带有字符串adam的向量传递给优化器参数。
但这并不总是需要像这样!
另外, 请尝试尝试使用其他优化算法, 例如随机梯度下降(SGD)。例如, 尝试使用optimizer_sgd()函数调整学习率lr。你注意到效果了吗?
除了使用另一个优化器之外, 你还可以尝试使用较小的学习率来训练网络。这是最常见的微调技术之一。一种常见的做法是使初始学习率比以前训练模型时的学习率小10倍。
让我们再一次可视化培训历史记录, 以查看此小调整的效果:
|
|
- 首先, 你可以轻松地使用save_model_hdf5()和load_model_hdf5()函数将模型保存并加载到工作空间中:
save_model_hdf5(model, "my_model.h5")model <
- load_model_hdf5("my_model.h5")
- 此外, 你还可以使用save_model_weights_hdf5()和load_model_weights_hdf5()函数保存和加载模型权重:
save_model_weights_hdf5("my_model_weights.h5")model %>
% load_model_weights_hdf5("my_model_weights.h5")
- 最后, 很高兴知道你也可以将模型配置导出到JSON或YAML。在这里, 函数model_to_json()和model_to_yaml()将为你提供帮助。要将配置加载回你的工作区, 你可以只使用model_from_json()和model_from yaml()函数:
json_string <
- model_to_json(model)model <
- model_from_json(json_string)yaml_string <
- model_to_yaml(model)model <
- model_from_yaml(yaml_string)
Keras深度学习 恭喜!你已经完成了R与keras的深度学习教程。本教程只是你使用R进行深度学习的一小步。还有更多的内容!如果你还没有参加过srcmini的Python深度学习课程, 则可以考虑这样做。
同时, 如果你还没有这样做, 还请确保查看Keras文档和RStudio keras文档。你将找到有关所有函数, 参数, 更多层等的更多示例和信息。此外, 请参阅Fran?oisChollet的书” 使用Python进行深度学习” 。在学习如何使用R中的神经网络时, 所有这些资源无疑都是必不可少的!
推荐阅读
- 在SQL中使用电子表格
- 在Windows和Mac OS X上安装PostgreSQL
- SQL中的视图(虚拟表)
- 在SQL中使用ORDER BY关键字
- SQL与Tableau用法教程
- 多重继承和super()简介
- SQL中的ALTER TABLE语句简介
- 如何使用ggplot2制作直方图
- appium环境