跳至主要内容

keras:R 中的深度学习

在这篇使用 RStudio 的 keras 包进行 R 深度学习的教程中,您将学习如何构建多层感知机(MLP)。
· 15分钟 阅读

使用 AI 探索

如您所知,机器学习是计算机科学(CS)的一个分支。而深度学习则是机器学习的一个子领域,它是一组受大脑结构与功能启发的算法,通常被称为人工神经网络(ANN)。深度学习是当前机器学习领域最热门的趋势之一,在许多问题上表现出色,例如机器人学、图像识别和人工智能(AI)。

今天的教程将带您用 keras 包在 R 中进行一次深度学习的快速入门:

想进一步了解原版 Keras,或深度学习中的关键概念,如感知机与多层感知机(MLP)?不妨考虑学习 DataCamp 的Deep Learning in Python 课程,或阅读Keras 教程:Python 中的深度学习。

提示:在此查看我们的Keras 速查表。

R 中的深度学习:包的简要概览

随着深度学习的流行,CRAN 上的 R 深度学习包也愈加丰富;下方是来自机器学习与统计学习 CRAN 任务视图的这些包的概览。“Percentile” 列表示在 RDocumentation 上的百分位:

R 包百分位描述
nnet第96百分位用于单隐藏层前馈神经网络的软件,以及多项式对数线性模型。
neuralnet第96百分位使用反向传播训练神经网络
h2o第95百分位H2O 的 R 脚本功能
RSNNS第88百分位Stuttgart Neural Network Simulator(SNNS)的接口
tensorflow第88百分位TensorFlow 的接口
deepnet第84百分位R 中的深度学习工具包
darch第79百分位深度架构与受限玻尔兹曼机(RBM)的包
rnn第73百分位实现循环神经网络(RRNs)的包
FCNN4R第52百分位FCNN 库接口,支持用户可扩展的 ANN
rcppDL第7百分位多层(深度学习)基础机器学习方法的实现,包括 dA(去噪自编码器)、SdA(堆叠去噪自编码器)、RBM(受限玻尔兹曼机)与 DBN(深度置信网络)
deepr??*基于 darch 与 deepnet 的深度学习中训练、微调与预测流程的简化包
MXNetR??*为 R 带来灵活高效的 GPU 计算与最前沿深度学习的包

提示:如需比较 R 中的深度学习包,请阅读这篇博文。关于 RDocumentation 中的排名与评分,请查看这篇博文。

deepr 与 MXNetR 未在 RDocumentation.org 上找到,因此这两个包的百分位未知。

Keras、keras 与 kerasR

最近,两个新包进入了 R 社区:由 Taylor Arnold 编写与创建的kerasR 包,以及 RStudio 的 keras 包。

这两个包都为 Python 的深度学习包 Keras 提供了 R 接口。您可能已经听说过它,甚至用过它!对于不太了解 Keras 为 Python 用户提供什么的读者,它是“一个用 Python 编写的高层神经网络 API,可在 TensorFlow、Microsoft Cognitive Toolkit(CNTK)或 Theano 之上运行”。

接口?

您看,入门 Keras 是熟悉 Python 深度学习最容易的方式之一,这也解释了为什么 kerasR 与 keras 这两个包要为 R 用户提供这个优秀包的接口。

在这里,了解某个包(例如 R 的 keras)是另一个包(Python 的 Keras)的“接口”究竟意味着什么很重要。简单来说,这意味着 R 的 keras 包通过接口让您在享受 R 编程便利的同时,访问 Python Keras 包的能力。

这并不罕见:例如,h2o 包也提供了接口——顾名思义——对 H2O 的接口,H2O 是一个用于大数据的开源数学引擎,您可用其并行分布式地计算机器学习算法。您可能还知道其他提供接口的包,如 RWeka(R 到 Weka 的接口)、tensorflow(R 到 TensorFlow 的接口)、openml-r(R 到 OpenML 的接口)等等,不胜枚举!

Keras、keras 与 kerasR 有什么区别?

了解这些之后,您可能会先问:如何比较原始的 Python 包与 R 包?

本质上,R 包与原始 Python 包之间差别不大,主要因为函数名几乎都相同;您注意到的差异主要在编程语言本身(变量赋值、库加载等),而最重要的是看 R 包整合了多少原包的功能。

注意,这一点不仅适用于 keras 库,也同样适用于上文提到的 tensorflow、openml-r 等接口包!

其次,您也许会问,这两个 R 包彼此之间有何不同。若要比较,您可以参考以下几点:

  • keras 包使用管道操作符(%>%)将函数或操作串联起来,而在 kerasR 中您不会见到它:例如,用 kerasR 构建模型时,您需要使用 $ 操作符。使用管道通常能提升代码可读性,如果您之前用过 Tidyverse 包,一定见过它。

  • 您会发现 kerasR 中的函数命名与原 Keras 包类似但并非完全相同。例如,最初的(Python)compile() 函数在 kerasR 中叫 keras_compile();其他函数亦然,如 fit() 变成 keras_fit(),predict() 则变为 keras_predict。这些都是自定义封装函数。

  • 可以说,RStudio 的 keras 包安装比 kerasR 更容易;安装后者之前,您需要先配置要使用的 Python 版本,如果您的电脑上有多个环境或多个 Python 版本,这会变得棘手。不过,这就交给您自己权衡啦 :)

data science courses banner

现在您已经了解了一些背景,是时候真正开始在 R 中使用 Keras 了。正如本教程开头所述,您将首先完成工作环境的设置。然后,您会加载一些数据,并在简短的数据探索与预处理步骤后,开始构建您的 MLP!

让我们开始吧!

安装 keras 包

一如既往,使用任何包的第一步都是设置工作环境:在 RStudio 或您所用的任意环境中安装并加载库。

不必担心,在本教程中我们会为您加载该包!

首先,请确保已安装 keras:您可以在控制台中运行 devtools::install_github("rstudio/keras") 轻松完成。接着,加载该包并安装 TensorFlow:

# Load in the keras package
library(keras)

# Install TensorFlow
install_tensorflow()

完成这些后,您就可以开始了!很快,对吧?

提示:有关安装过程的更多信息,请查看包网站。

加载数据

现在安装过程很清晰,工作环境已就绪,您可以开始加载数据了!此时,对于数据来源您有三种主要选择:使用 keras 包自带的内置数据集、从例如 CSV 文件中加载您自己的数据集,或创建一些模拟数据。

无论您处在哪种情况,您都会发现可以很快上手。本节将快速介绍这三种方式,并解释如何加载(或创建)开始所需的数据!

内置数据集

如果您有使用 Python Keras 包的经验,可能已经用过 Keras 内置数据集,例如 mnist.load_data()、cifar10.load_data() 或 imdb.load_data()。

以下是用 keras 包加载 MNIST、CIFAR10 与 IMDB 数据的一些示例:

# Read in MNIST data
mnist <- dataset_mnist()

# Read in CIFAR10 data
cifar10 <- dataset_cifar10()

# Read in IMDB data
imdb <- dataset_imdb()

请注意,使用 keras 加载内置数据集的函数都遵循相同模式;对于 MNIST 数据,您将使用 dataset_mnist() 函数来加载数据。

模拟数据

或者,您也可以快速创建一些模拟数据来上手。可以使用 matrix() 函数轻松实现:

# Make your dummy data
data <- matrix(rexp(1000*784), nrow = 1000, ncol = 784)

# Make dummy target values for your dummy data
labels <- matrix(round(runif(1000*10, min = 0, max = 9)), nrow = 1000, ncol = 10)

请注意,检查数据结构是个好主意;了解您正在处理的数据对后续步骤至关重要。更多内容将在教程后面介绍!

从文件读取数据

除了内置数据集,您还可以从文件中加载数据。对于本教程,您将重点从 CSV 文件加载数据;如果您想了解更多 R 中的文件导入,请参阅 DataCamp 的R 数据导入教程。

让我们使用 read.table 包中的 read.csv() 函数,从 UCI 机器学习库加载一个数据集:

# Read in `iris` data
iris <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"), header = FALSE) 

# Return the first part of `iris`
head(....)

# Inspect the structure
str(....)

# Obtain the dimensions
dim(....)

检查数据导入是否成功总是个好主意。通常您会像上面的 DataCamp Light 代码块一样,使用 head()、str() 和 dim() 来快速完成这一步。

这三个函数的结果并未立即指出异常;从 str() 的输出可以看到,Species 列的字符串被读入为因子。这没问题,但对接下来的数据探索与预处理步骤很有帮助。

数据探索

在本教程中,您将继续使用通过 read.csv() 导入的著名 iris 数据集。

如果您缺乏处理该数据所需的生物学背景,请参考以下信息:所有花朵都包含萼片与花瓣。萼片包裹花瓣,通常为绿色、类似叶片;花瓣通常为有色的叶片。对于鸢尾花,这一点稍有不同,见下图:

deep learning Python

您可能已经在上一节看到,导入后的 iris 数据框没有列名。对本教程余下部分而言,这并不重要:即便 read.csv() 返回的是 data.frame,传给 fit() 函数的数据需要是矩阵或数组。

关于刚提到的这两种数据结构,有几点要记住:- 矩阵与数组没有列名;- 矩阵是单一数据类型的二维对象;- 数组是单一数据类型的多维对象;

提示:如果想复习 R 中的数据结构,请查看这段视频!

另一方面,数据框是特殊的命名列表,其中所有元素长度相同。它是可以包含多种数据类型的多维对象。在上一节检查 iris 数据框结构时,您已经验证了这一点。知道了这些,并考虑到您需要将数据准备为单一数据类型的二维或多维对象,在构建神经网络之前就应准备做一些预处理!

目前来说,列名有助于探索,也更便于理解数据,因此我们用 names() 函数添加列名。然后,您就可以在数据探索中直接使用 iris 变量了!例如,用 plot() 函数绘制花瓣长度与花瓣宽度的相关性。

names(iris) <- c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species")

plot(iris$Petal.Length, 
     iris$Petal.Width, 
     pch=21, bg=c("red","green3","blue")[unclass(iris$Species)], 
     xlab="Petal Length", 
     ylab="Petal Width")

注意,您使用 unclass() 函数将物种名称(“setosa、versicolor、virginica”)转换为数值 1、2、3。

现在仔细看看绘图结果:

plot

该图显示了不同鸢尾花物种的 Petal.Length 与 Petal.Width 存在正相关。不过,您可能仍希望使用 cor() 函数进行检验,它会给出数据集中所有属性之间的总体相关性:

# Overall correlation between `Petal.Length` and `Petal.Width` 
cor(................., iris$Petal.Width)

此外,您可以将 corrplot 包与 cor() 函数结合,绘制数据各属性之间的相关性;这里您计算了 iris 数据框所有属性的总体相关性。将结果存入变量 M,然后传给 corrplot() 函数。

别忘了通过 method 参数指定绘图方式!

您可以在下方 DataCamp Light 代码块中进一步尝试不同的可视化方式:

# Store the overall correlation in `M`
M <- cor(iris[,1:4])

# Plot the correlation plot with `M`
corrplot(M, method="circle")

chart

利用 R 控制台进一步探索您的数据。

如果您想用 ggvis(交互式图形语法)来为该数据绘图,请查看 DataCamp 的R 机器学习入门教程,或学习 DataCamp 的ggvis 课程。

数据预处理

在构建模型之前,还需要确保您的数据已清洗、(如适用)归一化,并拆分为训练集与测试集。由于该数据集来自 UCI 机器学习库,可预期其已较为干净,但我们仍然仔细检查一下数据质量。

初看用 head() 检查时,似乎并无异常,对吗?让我们使用 summary() 与 str() 简要回顾一下在验证数据导入成功时得到的信息:

# Pull up a summary of `iris`
summary(....)

# Inspect the structure of `iris`
str(....)

既然确认数据足够干净,您就可以开始检查本教程中所用数据是否需要归一化了。

使用用户自定义函数(UDF)归一化数据

从上方 DataCamp 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。换言之,Iris 数据集中所有属性的取值均在 0.1 到 7.9 的范围内,可认为是可接受的。

然而,研究归一化对数据的影响依然是个好主意;您甚至可以将归一化后的数据传给模型,看看是否有影响。这超出了本教程的范围,但欢迎您自行尝试!相关代码都在本教程中提供了 :)

您可以编写自己的函数来对 iris 数据进行归一化;这里使用的是最小-最大归一化函数,将您的数据线性变换为 (x-min)/(max-min)。从这个角度,将公式翻译成 R 代码很简单:编写一个函数,传入 x 或某些数据。先计算 x-min 的结果并存入 num。接着计算 max-min 并存入 denom。normalize() 函数应返回 num 与 max 的商。

要将该用户自定义函数应用到 iris 数据(不含目标值)上,您不仅需要使用 normalize,还要借助 lapply() 来归一化数据,如下所示:

# Build your own `normalize()` function
normalize <- function(x) {
  num <- x - min(x)
  denom <- max(x) - min(x)
  return (num/denom)
}

# Normalize the `iris` data
iris_norm <- as.data.frame(lapply(iris[1:4], .........))

# Return the first part of `iris` 
head(....)

提示:在 R 控制台使用 hist() 函数,分别查看归一化前(iris)和归一化后(iris_norm)Iris 数据的分布。

使用 keras 归一化数据

要使用 keras 包的 normalize() 函数,首先需要确保您在处理矩阵。正如之前所述,矩阵的特征是各元素类型相同;而您现在的目标值为因子类型,其他为数值型。

这需要先进行转换。

您可以用 as.numeric() 将数据转为数值:

iris[,5] <- ...........(iris[,5]) -1

# Turn `iris` into a matrix
iris <- as.matrix(iris)

# Set `iris` `dimnames` to `NULL`
dimnames(....) <- NULL

数值型数据框可以,但若要使用 keras 包,需将数据转为数组或矩阵。可使用 as.matrix() 轻松完成;别忘了把 dimnames 设为 NULL。

正如上一节所述,对 Iris 数据进行归一化并非必要。不过,研究归一化及其影响仍是好主意,也可以看看如何既用 UDF 也用 keras 内置的 normalize() 完成归一化。

将数据转为矩阵后,您也可以使用 keras 包来研究归一化对数据的可能影响:

# Normalize the `iris` data
iris <- normalize(....[,1:4])

# Return the summary of `iris`
summary(....)

注意,这里您使用 dimnames() 将 iris 的 dimnames 设为 NULL。这可确保数据中没有列名。

训练集与测试集

既然您已经检查过数据质量,并且知道无需归一化,就可以继续使用原始数据,将其拆分为训练集与测试集,这样就终于可以开始构建模型了。这样做可以保证您之后对模型表现做出客观评估。

在将数据拆分为训练集与测试集之前,最好先设定随机种子。您可以用 set.seed() 轻松完成:直接传入一个随机整数。种子是 R 随机数生成器的一个数。设定种子的最大好处是:当您在随机数生成器中提供相同的种子时,可以得到相同的随机数序列。

这对代码的可复现性非常有利!

您使用 sample() 函数,按 Iris 数据集的行数(150)设置样本大小进行抽样。带放回抽样:从长度为 2 的向量中选择,将 150 行中的每一行标记为 1 或 2。元素的分配受 0.67 与 0.33 的概率权重影响。

# Determine sample size
ind <- sample(2, nrow(iris), replace=TRUE, prob=c(0.67, 0.33))

# Split the `iris` data
iris.training <- iris[ind==1, 1:4]
iris.test <- iris[ind==2, 1:4]

# Split the class attribute
iris.trainingtarget <- iris[ind==1, 5]
iris.testtarget <- iris[ind==2, 5]

sample() 的 replace 参数设为 TRUE,意味着为某一行分配 1 或 2 后,再将长度为 2 的向量恢复初始状态。

换句话说,对于数据集中接下来的行,每次依然可以分配 1 或 2。选择 1 或 2 的概率不应与剩余项目的权重成比例,因此您需要指定概率权重。

旁注:如果您使用的是内置数据集,例如特定的 dataset_imdb() 函数,数据可以方便地通过 $ 操作符拆分:

x_train <- imdb$train$x
y_train <- imdb$train$y
x_test <- imdb$test$x
y_test <- imdb$test$y

独热编码(One-Hot Encoding)

您已成功拆分数据,但在开始构建模型之前还有一步。您能猜到是哪一步吗?

当您要用神经网络来建模多分类问题时,一般需要将目标属性从“每个样本对应一个类别值的向量”转换为“每个样本对应一个布尔向量,表示该样本是否属于每个类别”的矩阵。

这就是独热编码(OHE)的通俗解释。听起来有点复杂,是吧?

幸好,keras 包提供了 to_categorical() 函数为您完成这一切;将 iris.trainingtarget 与 iris.testtarget 传给它,并将结果分别存入 iris.trainLabels 与 iris.testLabels:

# One hot encode training target values
iris.trainLabels <- to_categorical(..................)

# One hot encode test target values
iris.testLabels <- to_categorical(...............)

# Print out the iris.testLabels to double check the result
print(.............)

至此,您已完成本教程的数据探索与预处理步骤。现在可以继续用 keras 构建神经网络了!

构建模型

开始构建模型前,您应先使用 keras_model_sequential() 函数初始化一个序列模型。然后,就可以开始建模了。

不过在开始之前,建议回顾一下关于该数据集的原始问题:您能否预测某一朵鸢尾花的物种?处理数值数据更容易,您已对数据进行预处理,并对目标变量做了独热编码:一朵花要么是 versicolor、setosa 或 virginica,用二进制 1 与 0 表示。

在此类问题上表现良好的网络类型是多层感知机。这种神经网络通常是全连接的。这意味着为解决此问题,您希望构建一个相对简单的全连接层堆叠。至于将使用的激活函数,为了熟悉 Keras 与神经网络,最好使用最常见的之一,即 ReLU(relu)激活函数。这个线性整流激活函数用于隐藏层,一般来说是不错的实践。

此外,您还会在输出层使用 softmax 激活函数。这样做是为了确保输出值在 0 到 1 之间,并可作为预测概率使用:

# Initialize a sequential model
model <- ......................

# Add layers to the model
model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 3, activation = 'softmax')

注意,输出层会产生 3 个输出值,分别对应每个 Iris 类别(versicolor、virginica 或 setosa)。另一方面,第一层包含 8 个隐藏单元,其 input_shape 为 4,这是因为训练数据 iris.training 有 4 列。

您可以使用以下函数进一步检查模型:

  • 使用 summary() 打印模型摘要;
  • get_config() 返回包含模型配置的列表;
  • get_layer() 返回层配置;
  • layers 属性可用于获取模型层的扁平列表;
  • 使用 inputs 属性列出输入张量;
  • 最后,使用 outputs 属性获取输出张量。
# Print a summary of a model
summary(model)

# Get model configuration
get_config(model)

# Get layer configuration
ge_layer(model, index = 1)

# List the model's layers
model$cayers

# List the input tensors
model$inputs

# List the output tensors
model$outputs

编译并拟合模型

既然已设定了模型架构,就该将模型编译并拟合到数据上。编译模型时,使用 adam 优化器和 categorical_crossentropy 损失函数。此外,通过向 metrics 参数传入 'accuracy',在训练过程中监控准确率。

# Compile the model
model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )

如果要编译模型,优化器与损失函数是必需的两个参数。

常用的优化算法包括随机梯度下降(SGD)、ADAM 与 RMSprop。根据所选算法的不同,您需要调整某些参数,如学习率或动量。损失函数的选择取决于任务类型:例如,回归问题通常使用均方误差(MSE)。

如本例所示,对于判断鸢尾花是 versicolor、virginica 还是 setosa 的多分类问题,我们使用了 categorical_crossentropy 损失函数。但请注意,若是二分类问题,应使用 binary_crossentropy 损失函数。

接下来,您也可以将模型拟合到数据上;本例中,您对 iris.training 与 iris.trainLabels 中的所有样本训练 200 个周期(epoch,或迭代),每批 5 个样本。

# Fit the model 
model %>% fit(
     iris.training, 
     iris.trainLabels, 
     epochs = 200, 
     batch_size = 5, 
     validation_split = 0.2
 )

提示:如需,可在 fit() 函数中设置 verbose 参数。将其设为 1,表示希望看到进度条日志。

上述代码会在指定的若干个 epoch(或对训练数据集的曝光次数)中训练模型。一个 epoch 是对整个训练集的一次完整遍历,随后对验证集进行测试。代码中指定的 batch size 定义了每次经由网络传播的样本数。同时,这样做还能优化效率,因为您确保不会一次将过多的输入样本加载进内存。

可视化模型训练历史

同样值得一提的是,如果将上面 DataCamp Light 代码块中的拟合过程赋值给某个变量,您还可以可视化拟合过程。然后将该变量传给 plot() 函数,就像这个代码块中展示的那样!

# Store the fitting history in `history` 
history <- model %>% fit(
     iris.training, 
     iris.trainLabels, 
     epochs = 200,
     batch_size = 5, 
     validation_split = 0.2
 )

# Plot the history
plot(history)

请务必更仔细地研究该图。

plot

乍一看,这些图有些杂乱。您可能并不完全知道自己在看什么,对吧?

有一件好事要知道:loss 与 acc 分别表示模型在训练数据上的损失与准确率,而 val_loss 与 val_acc 则是相同指标在测试或验证数据上的表现。

但即便知道这些,这两幅图也不容易解读。让我们把它们拆解为更容易理解的部分!您可以将两张图拆分为两张独立的图:一张显示模型损失,另一张显示模型准确率。幸好,您可以使用 $ 操作符访问数据并一步步绘制。

查看下方 DataCamp Light 代码框,看看该如何操作:

# Plot the model loss of the training data
plot(history$metrics$loss, main="Model Loss", xlab = "epoch", ylab="loss", col="blue", type="l")

# Plot the model loss of the test data
lines(history$metrics$val_loss, col="green")

# Add legend
legend("topright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

chart

在第一张图中,您绘制了模型在训练与测试数据上的损失。现在也以相同方式绘制模型准确率:

# Plot the accuracy of the training data 
plot(history$metrics$acc, main="Model Accuracy", xlab = "epoch", ylab="accuracy", col="blue", type="l")

# Plot the accuracy of the validation data
lines(history$metrics$val_acc, col="green")

# Add Legend
legend("bottomright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

model accuracy

此处需注意以下几点:

  • 如果训练数据的准确率持续提升,而验证数据的准确率变差,您很可能出现了过拟合:模型开始记忆数据而不是从中学习。
  • 如果两组数据的准确率在最后几个 epoch 仍在上升,则可以清楚地看到模型尚未对训练集“学过头”。

预测新数据的标签

现在模型已创建、编译并拟合到数据上,是时候实际使用模型来为测试集 iris.test 预测标签了。正如您可能预料的,可以使用 predict() 函数来完成。之后,您可以打印混淆矩阵,借助 table() 函数查看 iris.test 数据的预测标签与真实标签。

# Predict the classes for the test data
classes <- model %>% predict_classes(iris.test, batch_size = 128)

# Confusion matrix
table(iris.testtarget, classes)

您觉得结果如何?乍看之下,这个模型是否做出了正确的预测?

评估模型

尽管通过查看 iris.test 的预测标签,您已经对模型表现有了大致了解,但仍然很有必要花时间评估模型。使用 evaluate() 函数来完成:传入测试数据 iris.test、测试标签 iris.testLabels,并定义 batch size。像下面的代码示例一样,将结果存入变量 score:

# Evaluate on test data and labels
score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

# Print the score
print(score)

打印 score 后,您将得到损失值与度量值(此处为“accuracy”)。

微调模型

模型微调可能是您会经常做的事情,尤其在开始阶段,因为并非所有分类与回归问题都如本教程前半部分那样直接。正如上文所述,至少有两项关键决策您很可能会调整:您将使用多少层,以及每层选择多少个“隐藏单元”。

在开始阶段,这确实是一段探索之旅。

除了调整 epoch 数量或 batch size,还有其他方法可尝试改进模型表现:添加层、增加隐藏单元数量,以及在 compile() 函数中传入自定义优化参数。本节将逐一介绍这三种方式。

添加层

如果在模型中再添加一层,会发生什么?如果模型变成下面这样呢?

# Initialize the sequential model
model <- keras_model_sequential() 

# Add layers to model
model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 5, activation = 'relu') %>% 
    layer_dense(units = 3, activation = 'softmax')

# Compile the model
model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )

# Fit the model to the data
odel %>% fit(
     
     iris.training, iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )

# Evaluate the model
score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

# Print the score
print(score)

注意,您也可以可视化这个新模型的损失与准确率指标!在下方 DataCamp Light 代码块中尝试一下:

# Initialize a sequential model
model <- keras_model_sequential() 

# Add layers to the model
model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 5, activation = 'relu') %>% 
    layer_dense(units = 3, activation = 'softmax')

# Compile the model
model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )

# Save the training history in history
history = - model %>% fit(
  iris.training, iris.trainLabels, 
  epochs = 200, batch_size = 5,
  validation_split = 0.2
 )

# Plot the model loss
plot(history$metrics$loss, main="Model Loss", xlab = "epoch", ylab="loss", col="blue", type="l")
lines(history$metrics$val_loss, col="green")
legend("topright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

# Plot the model accuracy
plot(history$metrics$acc, main="Model Accuracy", xlab = "epoch", ylab="accuracy", col="blue", type="l")
lines(history$metrics$val_acc, col="green")
legend("bottomright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

deep learning model layers

Keras Layers model

隐藏单元

也试试在模型架构中增加更多隐藏单元,并像下面这样研究其对评估结果的影响:

# Initialize a sequential model
model <- keras_model_sequential() 

# Add layers to the model
model %>% 
    layer_dense(units = 28, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 3, activation = 'softmax')

# Compile the model
model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )

# Fit the model to the data
odel %>% fit(
     
     iris.training, iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )

# Evaluate the model
score = - model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

# Print the score
print(score)

请注意,一般来说这并非总是最好的优化方式,因为如果数据不多,过拟合可能会更严重。因此在本例这样的小数据集上,应尽量使用小网络。

为何不试试可视化在模型中添加隐藏节点的效果?在下方尝试一下:

neural network model

hidden units ann

优化参数

除了添加层与调整隐藏单元,您还可以尝试调整传递给 compile() 函数的优化算法的(部分)参数。到目前为止,您一直向 optimizer 参数传入一个字符串向量 adam。

但并不总是非这么做不可!

也试试其他优化算法,如随机梯度下降(SGD)。例如,使用 optimizer_sgd() 函数来调整学习率 lr。您是否注意到了效果?

# Initialize a sequential model
model <- keras_model_sequential() 

# Build up your model by adding layers to it
model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 3, activation = 'softmax')

# Define an optimizer
sgd <- optimizer_sgd(lr = 0.01)

# Use the optimizer to compile the model
model %>% compile(optimizer=sgd, 
                  loss='categorical_crossentropy', 
                  metrics='accuracy')

# Fit the model to the training data
odel %>% fit(
     
     iris.training, iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )

# Evaluate the model
score = - model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

# Print the loss and accuracy metrics
print(score)

除了更换优化器,您还可以尝试使用更小的学习率来训练网络。这是一种最常见的微调技巧之一;常见做法是将初始学习率设为之前用于训练模型的 1/10。

让我们再可视化一次训练历史,看看这项小调整的效果:

# Define an optimizer
sgd <- optimizer_sgd(lr = 0.01)

# Compile the model
model <- compile(optimizer=sgd, 
                 loss='categorical_crossentropy', 
                  metrics='accuracy')

# Fit the model to the training data
history = - model %>% fit(
  iris.training, iris.trainLabels, 
  epochs = 200, batch_size = 5, 
  validation_split = 0.2
 )

# Plot the model loss
plot(history$metrics$loss, main="Model Loss", xlab = "epoch", ylab="loss", col="blue", type="l")
lines(history$metrics$val_loss, col="green")
legend("topright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

# Plot the model accuracy
plot(history$metrics$acc, main="Model Accuracy", xlab = "epoch", ylab="accuracy", col="blue", type="l")
lines(history$metrics$val_acc, col="green")
legend("bottomright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

neural network model

hidden units ann

保存、加载或导出模型

在使用 keras 包的旅程中,还有最后一件事:保存或导出模型,以便您可以在其他时间重新加载它。

  • 首先,您可以方便地使用 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)

Learn Python for Data Science With DataCamp

使用 Keras 进行深度学习

恭喜您!您已经完成了这篇使用 keras 在 R 中进行深度学习的教程。这只是您在 R 中深度学习旅程中的一小步;还有更多内容等待探索!如果您还没有学习 DataCamp 的Deep Learning in Python 课程,不妨考虑一下。

与此同时,也请务必查看 Keras 文档与 RStudio 的keras 文档(如果您还未查看)。您将找到更多示例与函数、参数、更多层等信息。另外,看看 François Chollet 的著作“Deep Learning with Python”。这些资源无疑对学习如何在 R 中使用神经网络大有裨益!

主题