3.2.2 使用函数式API创建的模型
序贯API允许逐层创建模型以解决大多数问题,但局限在于它不允许创建共享图层或具有多个输入或输出的模型。Keras中的函数式API是创建模型的另一种方式,它可提供更大的灵活性,包括创建更复杂的模型。
使用函数式API创建的模型允许定义多个输入或输出模型以及共享图层的模型。下面我们先看看Keras函数式API的三个独特方面。
- 定义输入:在函数模型中,我们必须创建一个输入层,用来指定输入数据的形状。输入层采用参数shape指定输入数据的维度。
- 连接图层:模型中的图层成对连接,这是通过在定义新图层时指定输入的来源完成的。使用管道符号%>%,从当前输入层创建新的图层。
- 创建模型:Keras提供了一个Model类,可以使用它从创建的图层创建模型,它只要求指定输入层和输出层。
本节将介绍具有不同大小内核的多个卷积层是如何解译同一图像的输入的。该模型采用尺寸为32×32×3像素的彩色CIFAR图像。有两个共享此输入的CNN特征提取子模型,其中一个内核大小为4,另一个内核大小为8。这些特征提取子模型的输出被平展为向量,然后串联成一个长向量,并在最终输出层进行二进制分类之前被传递到全连接层进行解译。
以下为模型拓扑结构。
- 一个输入层。
- 两个特征提取层。
- 一个解译层。
- 一个稠密输出层。
首先,我们需要使用Keras API定义适当的层,这里的关键API是利用layer_concatenate()函数创建合并层。以下是完整的模型拓扑代码。
> # 创建网络拓扑 > library(keras) > # input layer > visible <- layer_input(shape = c(32,32,3)) > # first feature extractor > flat1 <- visible %>% + layer_conv_2d(32,kernel_size = 4,activation = 'relu') %>% + layer_max_pooling_2d(pool_size = c(2,2)) %>% + layer_flatten() > # second feature extractor > flat2 <- visible %>% + layer_conv_2d(16,kernel_size = 8,activation = 'relu') %>% + layer_max_pooling_2d(pool_size = c(2,2)) %>% + layer_flatten() > # merge feature extractors > merge <- layer_concatenate(list(flat1,flat2)) > # interpretation layer > hidden1 <- merge %>% + layer_dense(512,activation = 'relu') > # prediction output > output <- hidden1 %>% + layer_dense(10,activation = 'sigmoid') > model <- keras_model(inputs = visible,outputs = output)
关于卷积神经网络的内容将在后面详细介绍,执行以下代码,创建的网络拓扑结构如图3-8所示。
图3-8 函数式API的网络拓扑结构
> deepviz::plot_model(model) # 网络拓扑可视化
CIFAR-10数据集共有60 000张彩色照片,这些照片的分辨率为32×32,分为10类,每类有6000张图。其中,50 000张用于训练,另外10 000张用于测试。我们先加载数据,并查看数据结构。
> num_classes <- 10 > batch_size <- 32 > epochs <- 10 > c(c(x_train, y_train), c(x_test, y_test)) %<-% dataset_cifar10() > cat("X_train shape: " ,dim(x_train)) X_train shape: 50000 32 32 3 > cat("y_train shape: ",dim(y_train)) y_train shape: 50000 1 > cat("X_test shape: " ,dim(x_test)) X_test shape: 10000 32 32 3 > cat("y_test shape: ",dim(y_test)) y_test shape: 10000 1
x_train是50 000张32×32×3的彩色图像,x_test是10 000张32×32×3的彩色图像。通过以下代码绘制x_train数据集的前9张图像,如图3-9所示。
> par(mfrow=c(3,3)) > par(mar=c(0,0,1.5,0),xaxs='i',yaxs='i') > for(i in 1:9){ + plot(as.raster(x_train[i,,,],max=255)) + } > par(mfrow=c(1,1))
运行以下程序代码,对数据进行预处理。
图3-9 绘制训练集的前9张图像
> x_train <- x_train / 255 > x_test <- x_test / 255 > y_train <- to_categorical(y_train,num_classes) > y_test <- to_categorical(y_test,num_classes)
接下来,训练模型,并将训练过程保存在history变量中。训练完成后,利用plot()函数绘制每个训练周期的计算误差及准确率,如图3-10所示。
> opt <- optimizer_rmsprop(lr=0.0001, decay=1e-6) > # 使用RMSProp训练模型 > model %>% compile(loss='categorical_crossentropy', + optimizer=opt, + metrics=c('accuracy')) > history <- model %>% fit(x_train, y_train, + batch_size=batch_size, + epochs=epochs, + validation_data=list(x_test, y_test), + shuffle=TRUE, + verbose = 2) > plot(history)
图3-10 训练模型每个训练周期的计算误差及准确率
训练集和验证集的计算误差均随着训练周期次数增加而减少,准确率随着训练周期次数增加而提升。从图3-10的结果来看,增加训练周期次数应该还可以提高模型准确率。
最后,让我们利用测试集评估训练好的模型的性能。
> scores <- model %>% evaluate(x_test, y_test, verbose=0) > cat('Test loss:',scores[[1]]) Test loss: 0.9686212 > cat('Test accuracy:',scores[[2]]) Test accuracy: 0.6715
模型在测试集上的准确率为67%。在后面的卷积神经网络章节将会详细讲解如何提升模型准确率。