如果你在使用R语言,想要用R语言建立一个深度神经网络,那将是非常困难的。因为好多的深度神经网络的架构,都是在python上,而关于R语言的非常少。如nnet包,RSNNS包等,受之于激活函数的影响,以及只能使用一个cpu进行单线程计算,并且这些包也没有提供建立卷积神经网络的功能。
几年前,我被 R 的 tensorflow 包短暂地吸引住了,但我无法建立一个可靠的工作流程,因为它的 Python 后端不断地挡路。 (许多令人恼火的小时都在徒劳地尝试配置我的 GPU)。
Torch 是一个使用 C++ 后端 libtorch,作为运算的底层,而不是 Python 。
然而,torch 仍然处于起步阶段,尽管它能够完成大多数成熟的深度学习框架可以做的事情,但它没有提供那种让初学者可以直观地掌握的高级 API。 这篇文章的目的是帮助读者熟悉torch包。
现在我们以mnist手写数字数据集,使用R语言的torch包,建立一个卷积神经网络,作为torch的教学。
安装torch包
install.packages("torch")
library(torch)
# install_torch() #一般情况下该行代码是不需要运行的,在library之后,会下载安装torch需要的底层架构,如果没有成功安装,就需要你手动运行这行代码。如果失败:请找把梯子。
#以及需要配置其它的系统环境,根据提示进行配置
如果没有mnist数据集,可以通过下面的网址进行下载,格式是csv文件。数据集下载:
https://pjreddie.com/media/files/mnist_train.csv
https://pjreddie.com/media/files/mnist_test.csv
1.mnist数据的读取以及后续处理工作
torch::nn_conv2d所需要的数据形式是(n,channel,height,width)
所以我们需要将minist同样处理成该数据形式。
#训练集
dfminist2<-read.csv("mnist_train.csv",header=F)
dfminist2[,1]<-dfminist2[,1]+1
xminist2<-as.matrix(dfminist2[,2:785])
xminist2<-xminist2/255
xarray2<-array(0,dim=c(60000,1,28,28))
for(i in 1:60000){
xarray2[i,1,,]<-matrix(xminist2[i,],nrow=28,byrow=T)}
#测试集
dfminist<-read.csv("mnist_test.csv",header=F)
dfminist[,1]<-dfminist[,1]+1
xminist<-as.matrix(dfminist[,2:785])
xminist<-xminist/255
xarray<-array(0,dim=c(10000,1,28,28))
for(i in 1:10000){
xarray[i,1,,]<-matrix(xminist[i,],nrow=28,byrow=T)}
2.torch dataset数据索引:
Dataset 有点像传统的 data.frame,但它有一些特殊功能,可以更轻松地进行深度学习。 与其将其视为静态电子表格,不如将其视为一个函数,它将以一口大小的块或批次将数据输入我们的网络。
我们使用 dataset 函数创建一个 Dataset 对象。 它本质上是我们可以从数据集访问的属性和方法列表。
minist_dataset<-dataset(
initialize=function(xarray,label){
self$x<-torch_tensor(xarray,dtype=torch_float())
self$y<-torch_tensor(label,dtype=torch_long())},
.getitem=function(index){
list(x=self$x[index,,,],y=self$y[index])},
.length=function(){length(self$y)})
3.生成训练集和测试集的dataset数据索引
ministdsta<-minist_dataset(xarray2,label=dfminist2[,1])
ministdste<-minist_dataset(xarray,label=dfminist[,1])
4.dataloader,数据加载器
我们有我们的训练和验证数据集。 我们需要对数据做的最后一件事是创建数据加载器对象。 数据加载器通过网络提供批量数据。 我们对训练集进行洗牌,以便在每个时期(学习阶段的迭代)重新洗牌数据。
ministdlta<-dataloader(ministdsta,batch_size=32,shuffle=T)
ministdlte<-dataloader(ministdste,batch_size=32,shuffle=T)
5.创建神经网络结构
net <- nn_module(
initialize = function() {
self$conv1 <- nn_conv2d(1,32, kernel_size=3)#卷积层
self$conv2 <- nn_conv2d(32,64,kernel_size=3)
self$conv3 <- nn_conv2d(64,128, kernel_size=3)
self$conv4 <- nn_conv2d(128,256,kernel_size=3)
self$fc1 <- nn_linear(4*4*256, 128)#线性层
self$fc2 <- nn_linear(128,10)
self$dropout1<-nn_dropout(0.25)#随机丢失,用来防止过拟合
self$dropout2<-nn_dropout(0.25)
self$dropout3<-nn_dropout(0.25)
},
forward = function(x) {
x %>%
self$conv1() %>%
nnf_relu() %>%
self$conv2() %>%
nnf_relu() %>%
nnf_avg_pool2d(2) %>%
self$dropout1()%>%
self$conv3() %>%
nnf_relu() %>%
self$conv4() %>%
nnf_relu() %>%
nnf_avg_pool2d(2) %>%
self$dropout2()%>%
torch_flatten(start_dim = 2) %>%
self$fc1() %>%
nnf_selu() %>%
self$dropout3()%>%
self$fc2()
})
model<-net()
该网络进行了连续2步卷积,1次池化;接着又是连续2步卷积,1次池化,最后将图片特征提取成256*4*4的结果,输入全连接层,进行分类。
6.模型训练
torch神经网络的训练主要有以下4步:
1. 将梯度设置为零。
2. 定义和计算成本和优化器
3. 在网络上传播错误。
4. 应用梯度优化。
optimizer <- optim_adam(model$parameters)#优化器
n_epochs <-10#迭代步数
model$train()# 设置成训练模型
for(epoch in 1:n_epochs) {
train_losses <- c()
coro::loop(for(b in ministdlta) {
optimizer$zero_grad()
output <- model(b[[1]])
loss <- nnf_cross_entropy(output, b[[2]])
loss$backward()
optimizer$step()
train_losses <- c(train_losses, loss$item())
})
cat(sprintf("Epoch %d: train loss: %3f\n",
epoch, mean(train_losses)))
}
我只训练了17步:
7.模型评价:
训练集的表现:
# Evaluate
model$eval()#设置预测模型
pre<-c()
true<-c()
coro::loop(for(b in ministdlta) {#测试集的情况
output <- model(b[[1]])
pred <- torch_max(output, dim = 2)[[2]]
pre<-c(pre,as.numeric(pred))
true<-c(true,as.numeric(b[[2]]))
})
ma<-table(true,pre)
ma
sum(diag(ma))/sum(ma)
测试集表现:
# Evaluate
model$eval()#设置预测模型
pre<-c()
true<-c()
coro::loop(for(b in ministdlte) {#训练集的情况
output <- model(b[[1]])
pred <- torch_max(output, dim = 2)[[2]]
pre<-c(pre,as.numeric(pred))
true<-c(true,as.numeric(b[[2]]))
})
ma<-table(true,pre)
ma
sum(diag(ma))/sum(ma)
8.结论,可以看出,通过使用卷积神经网络,可以将mnist数据集的预测集,分类正确率提高到99.43%,而传统的方法,却很难达到这样的水平。