神经网络
在机器学习中,神经网络一般指的是“神经网络学习”,是机器学习与神经网络两个学科的交叉部分。所谓神经网络,目前用得最广泛的一个定义是“神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所做出的交互反应”。
1 概述
1.1 神经元模型
神经网络中最基本的单元是神经元模型(neuron)。在生物神经网络的原始机制中,每个神经元通常都有多个树突(dendrite),一个轴突(axon)和一个细胞体(cell body),树突短而多分支,轴突长而只有一个;在功能上,树突用于传入其它神经元传递的神经冲动,而轴突用于将神经冲动传出到其它神经元,当树突或细胞体传入的神经冲动使得神经元兴奋时,该神经元就会通过轴突向其它神经元传递兴奋。
简单看下最初的M-P神经元。M-P神经元:神经元收到n个其他神经元传递过来的信号Xi,通过权重Wi连接,超过阈值θ就用f函数激活,这里的f,是激活函数。如下图所示:
与线性分类十分相似,神经元模型最理想的激活函数也是阶跃函数,即将神经元输入值与阈值的差值映射为输出值1或0,若差值大于零输出1,对应兴奋;若差值小于零则输出0,对应抑制。但阶跃函数不连续,不光滑,故在M-P神经元模型中,也采用Sigmoid函数来近似, Sigmoid函数将较大范围内变化的输入值挤压到 (0,1) 输出值范围内,所以也称为挤压函数(squashing function)。
将多个神经元按一定的层次结构连接起来,就得到了神经网络。它是一种包含多个参数的模型,比方说10个神经元两两连接,则有100个参数需要学习(每个神经元有9个连接权以及1个阈值),若将每个神经元都看作一个函数,则整个神经网络就是由这些函数相互嵌套而成。
1.2 感知机与多层网络
感知机(Perceptron)是由两层神经元组成的一个简单模型,但只有输出层是M-P神经元,即只有输出层神经元进行激活函数处理,也称为功能神经元(functional neuron);输入层只是接受外界信号(样本属性)并传递给输出层(输入层的神经元个数等于样本的属性数目),而没有激活函数。这样一来,感知机与之前线性模型中的对数几率回归的思想基本是一样的,都是通过对属性加权与另一个常数求和,再使用sigmoid函数将这个输出值压缩到0-1之间,从而解决分类问题。不同的是感知机的输出层应该可以有多个神经元,从而可以实现多分类问题,同时两个模型所用的参数估计方法十分不同。
给定训练集,则感知机的n+1个参数(n个权重+1个阈值)都可以通过学习得到。阈值Θ可以看作一个输入值固定为-1的哑结点的权重ωn+1,即假设有一个固定输入xn+1=-1的输入层神经元,其对应的权重为ωn+1,这样就把权重和阈值统一为权重的学习了。简单感知机的结构如下图所示:
感知机权重的学习规则如下:对于训练样本(x,y),当该样本进入感知机学习后,会产生一个输出值,若该输出值与样本的真实标记不一致,则感知机会对权重进行调整,若激活函数为阶跃函数,则调整的方法为(基于梯度下降法):
其中 η∈(0,1)称为学习率,可以看出感知机是通过逐个样本输入来更新权重,首先设定好初始权重(一般为随机),逐个地输入样本数据,若输出值与真实标记相同则继续输入下一个样本,若不一致则更新权重,然后再重新逐个检验,直到每个样本数据的输出值都与真实标记相同。容易看出:感知机模型总是能将训练数据的每一个样本都预测正确,和决策树模型总是能将所有训练数据都分开一样,感知机模型很容易产生过拟合问题。
由于感知机模型只有一层功能神经元,因此其功能十分有限,只能处理线性可分的问题,对于这类问题,感知机的学习过程一定会收敛(converge),因此总是可以求出适当的权值。但是对于像书上提到的异或问题,只通过一层功能神经元往往不能解决,因此要解决非线性可分问题,需要考虑使用多层功能神经元,即神经网络。多层神经网络的拓扑结构如下图所示:
在神经网络中,输入层与输出层之间的层称为隐含层或隐层(hidden layer),隐层和输出层的神经元都是具有激活函数的功能神经元。只需包含一个隐层便可以称为多层神经网络,常用的神经网络称为“多层前馈神经网络”(multi-layer feedforward neural network),该结构满足以下几个特点:
- 每层神经元与下一层神经元之间完全互连
- 神经元之间不存在同层连接
- 神经元之间不存在跨层连接
1.3 BP神经网络
由上面可以得知:神经网络的学习主要蕴含在权重和阈值中,多层网络使用上面简单感知机的权重调整规则显然不够用了,BP神经网络算法即误差逆传播算法(error BackPropagation)正是为学习多层前馈神经网络而设计,BP神经网络算法是迄今为止最成功的的神经网络学习算法。
一般而言,只需包含一个足够多神经元的隐层,就能以任意精度逼近任意复杂度的连续函数[Hornik et al.,1989],故下面以训练单隐层的前馈神经网络为例,介绍BP神经网络的算法思想。
简单说一下这里的符号:
这里只看有三层的神经网络,第一层是输入层,Xi是各个属性值,中间是一层隐层结点bh,隐层结点和输入层结点每个之间有连接权重Vih,隐层每个结点有一个阈值γh,隐层和输出层之间也有连接权重Whj,输出层每个结点的阈值是θj。
BP算法的更新规则是基于每个样本的预测值与真实类标的均方误差来进行权值调节,即BP算法每次更新只针对于单个样例。需要注意的是:BP算法的最终目标是要最小化整个训练集D上的累积误差。
如果基于累积误差最小化的更新规则,则得到了累积误差逆传播算法(accumulated error backpropagation),即每次读取全部的数据集一遍,进行一轮学习,从而基于当前的累积误差进行权值调整,因此参数更新的频率相比标准BP算法低了很多,但在很多任务中,尤其是在数据量很大的时候,往往标准BP算法会获得较好的结果。另外对于如何设置隐层神经元个数的问题,至今仍然没有好的解决方案,常使用“试错法”进行调整。
前面提到,BP神经网络强大的学习能力常常容易造成过拟合问题,有以下两种策略来缓解BP网络的过拟合问题:
早停:将数据分为训练集与测试集,训练集用于学习,测试集用于评估性能,若在训练过程中,训练集的累积误差降低,而测试集的累积误差升高,则停止训练。
引入正则化(regularization):基本思想是在累积误差函数中增加一个用于描述网络复杂度的部分,例如所有权值与阈值的平方和,其中λ∈(0,1)用于对累积经验误差与网络复杂度这两项进行折中,常通过交叉验证法来估计。
2 R中的实现
2.1 相关软件包
R语言中已经有许多用于神经网络的软件包。本小结将介绍4种神经网络R软件包,分别是nnet
软件包、AMORE
软件包、neuralnet
软件包以及RSNNS
软件包。
2.1.1 nnet包
nnet提供了最常见的前馈反向传播神经网络算法。
nnet软件包是用来建立单隐藏层的前馈人工神经网络模型,同时也能用来建立多项对数线性模型。
R提供了nnet软件包丰富的网上学习资源,包括软件包的使用说明文档、函数源代码、操作实例文档等,具体可参见 http://cran.r-project.org/web/packages/nnet/index.html ,其中含有相关链接。
library(nnet) # 加载nnet软件包
## Warning: package 'nnet' was built under R version 3.4.4
2.1.2 AMORE包
AMORE包比 nnet包更进一步提供了更为丰富的控制参数,并可以增加多个隐藏层。
AMORE软件包是用来建立多层前馈神经网络模型。
R提供了AMORE软件包丰富的网上学习资源,包括软件包的使用说明文档、函数源代码、操作实例文档等,具体可参见 https://cran.r-project.org/web/packages/AMORE/index.html ,其中含有相关链接。
library(AMORE) # 加载AMORE软件包
2.1.3 neuralnet包
neuralnet包的改进在于提供了弹性反向传播算法和更多的激活函数形式。
neuralnet软件包是用来建立多层前馈神经网络模型。
R提供了neuralnet软件包丰富的网上学习资源,包括软件包的使用说明文档、函数源代码、操作实例文档等,具体可参见 https://cran.r-project.org/web/packages/neuralnet/index.html ,其中含有相关链接。
library(neuralnet) # 加载neuralnet软件包
## Warning: package 'neuralnet' was built under R version 3.4.4
2.1.4 RSNNS包
前面三个包主要是基于BP神经网络,而RSNNS包则提供了更多的神经网络模型。
RSNNS软件包是用来建立多层前馈神经网络模型。
R提供了RSNNS软件包丰富的网上学习资源,包括软件包的使用说明文档、函数源代码、操作实例文档等,具体可参见 https://cran.r-project.org/web/packages/RSNNS/index.html ,其中含有相关链接。
library(RSNNS) # 加载RSNNS软件包
## Warning: package 'RSNNS' was built under R version 3.4.4
## Loading required package: Rcpp
##
## Attaching package: 'RSNNS'
## The following object is masked from 'package:AMORE':
##
## train
2.2 核心函数介绍
2.2.1 nnet包
nnet提供了最常见的前馈反向传播神经网络算法。
nnet只有一层隐藏层,是基于BP神经网络的一种算法。
nnet包中主要有4个函数,分别为class.ind()、multinom()、nnet()和nnetHess()。其中函数multinom()用来建立多项对数模型。
2.2.1.1 class.ind()
class.ind()函数主要用来进行数据预处理,具体地说,这个函数是用来对建模数据中的结果变量进行处理的(即对模型中的y进行处理)。函数的基本格式如下:
class.ind(cl)
从函数的基本格式可以看出,它的使用比较简单,函数中只有一个参数。这个参数可以是因子向量,也可是类别向量。
我们在此举一个例子,方便大家理解。
vector1 <- c("a", "b", "a", "c") # 生成字符向量vector1
vector2 <- c(1, 2, 1, 3) # 生成数量向量vector2
class.ind(vector1) # 对字符向量vector1进行预处理
## a b c
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 1 0 0
## [4,] 0 0 1
class.ind(vector2) # 对数量向量vector2进行预处理
## 1 2 3
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 1 0 0
## [4,] 0 0 1
我们通过字符向量和数量向量分别看该函数的处理结果。从输出结果中我们可以看到,该函数主要是将向量变成一个矩阵,每行代表一个样本,将样本类别用0和1表示。
2.2.1.2 nnet()
nnet()函数是构造神经网络模型的核心函数,主要用来建立单隐藏层的前馈人工神经网络模型,同时可以建立无隐藏层的前馈人工神经网络模型。
nnet()函数的使用格式有两种,下面分别进行介绍。
第一种使用格式:
nnet(formula, data, weights, ...,
subset, na.action, contrasts = NULL)
其中:
·formula:函数模型的形式。例如,labelx1+x2+x3,label是类别标签,label. 表示使用data中除了label外的所有属性进行建模。
·data:模型中包含的有变量的一组可选格式的数据。
·weights:各类样本在模型中所占的权重,默认为1,即按照各个样本原始比例进行建模
·sebset:主要用于抽取样本数据中的部分样本作为训练集,数据格式为向量。向量中的每个数代表所需要抽取样本的行数。
第二种使用格式:
nnet(x, y, weights, size, Wts, mask,
linout = FALSE, entropy = FALSE, softmax = FALSE,
censored = FALSE, skip = FALSE, rang = 0.7, decay = 0,
maxit = 100, Hess = FALSE, trace = TRUE, MaxNWts = 1000,
abstol = 1.0e-4, reltol = 1.0e-8, ...)
其中:
·x:自变量,可以是矩阵也可以是格式化数据集
·y:类别变量,此处y必须是一个矩阵,这个矩阵由class.ind()函数生成 ·weights:各类样本在模型中所占的权重,默认为1,即按照各个样本原始比例进行建模
·size:隐藏层中的节点个数,同城为输入层节点个数的1.2倍至1.5倍,即自变量个数的1.2倍至1.5倍。当取size=0时,说明建立的模型为无隐藏层的人工神经网络模型。 ·rang:初始随机权重的范围[-rang, rang] ·decay:建模过程中,模型权重值的衰减精度,即当模型的权重值每次衰减小于该参数值时,模型将不再进行迭代。该参数的默认值为0。这个值是递减的(可以防止过拟合)
·linout:线性输出单元开关;
·skip:是否允许跳过隐层;
·maxit:最大迭代次数,这个参数是为了防止模型进入死循环,或是减少没有必要的迭代
·Hess:是否输出Hessian值
下面我们进行输出结果的介绍:
wts中包含了模型迭代过程中的最优权重值,即最优系数。
residual中包含了训练集的残差。
convergence表示在模型建立的迭代过程中,迭代次数是否达到最大迭代次数。如果结果为1,说明迭代次数达到了最大迭代次数,应该对模型进行进一步分析,因为这说明迭代过程中没有涉及其他决定模型精度的条件,会导致我们建立的模型精度不高;如果结果为0,则说明没有达到最大迭代次数。
2.2.1.3 nnetHess()
这个函数用来估计人工神经网络模型中的二次导数矩阵。这个函数的具体使用格式如下:
nnetHess(net, x, y, weights)
在该函数中,我们看到4个参数。net参数中我们需要带入nnet()函数构造的人工神经网络模型,x和y表示的是自变量和类别变量,weights的使用方式和nnet()中的weights使用方式一样。
2.2.2 AMORE包
AMORE包则更进一步提供了更为丰富的控制参数,并可以增加多个隐藏层。
2.2.3 neuralnet包
neuralnet包的改进在于提供了弹性反向传播算法和更多的激活函数形式。
2.2.4 RSNNS包
前面三个包主要是基于BP神经网络,而RSNNS包则提供了更多的神经网络模型。
2.3 数据集
本章我们使用datasets软件包中的iris数据集进行演示,我们首先对其进行简单的了解。
data(iris) # 获取数据集iris
summary(iris) # 获取iris数据集的概况信息
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
## 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
## Median :5.800 Median :3.000 Median :4.350 Median :1.300
## Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
## 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
## Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
在获取完以上信息之后,我们来看iris数据集的基本信息。它一共包含150个样本以及4个样本特征,其中结果标签中总共具有三种类别,并且三种类别均有50个样本,所占比重相同。在输出的结果中还显示了样本的最小值、四分之一分位点、中位数、均值、四分之三分位点以及最大值。
在输出结果中,setosa、versicolor和virginica是鸢尾花所属的三种类别。本数据采集了这三种花的四项基本特征,分别为:花萼长度、花萼宽度、花瓣长度和花瓣宽度。
本章节主要介绍如何根据这四个特征来建立人工神经网络模型,实现对三种花进行分类。
3 应用案例
下面我们使用R语言来构建人工神经网络模型分析iris数据集,查看模型的预测能力。
3.1 数据初探
在对数据有了一定了解之后,我们首先要做的是确定需要建立的模型的基本形式。
可以看到,数据集中的Species是支持向量机模型中的结果标签,对应的部分分别是Swpal.Length、Sepal.Width、Petal.Length以及Petal.Width。所以我们建立模型的简单公式可以大概分为Species ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width。在接下来的建模过程中,我们将围绕这个公式进行分析。
3.2 数据处理
在建立人工神经网络模型之前,我们需要对数据进行预处理。
作为建立人工神经网络模型的处理方式主要进行数据的归一化。数据归一化是一种常用的神经网络预测前要做的处理方法,需要将所有数据都转化为[0,1]之间的数,其目的是取消各维度数据间数量级的差别,避免因为输入输出数据的数量级差别较大导致网络预测误差较大。
数据归一化的方法有多种,主要使用的是最大最小法和平均数方差法。这里我们采用最大最小法进行数据归一化。
最大最小法的函数形式如下: \[ x_k = (x_k – x_{min}) / (x_{max} – x_{min})\] 式中\(x_{min}\)指的是数据序列中最小的数, \(x_{max}\)表示数据序列中最大的数。
平均数方差法的函数形式如下: \[ x_k = (x_k – x_{mean}) / x_{var} \] 式中,\(x_{mean}\)表示数据序列的均值,\(x_{var}\)表示数据的方差。
对于第一种0-1归一化方法,这里将通过自写程序对原始数据进行预处理,这里也可以使用其他的函数。此处给出相应程序供大家参考。
scale0 <- function(x){
ncol <- dim(x)[2]-1 # 提取预处理样本集中特征变量个数
nrow <- dim(x)[1] # 提取预处理样本集中样本总量
new <- matrix(0, nrow, ncol) # 构造保存新样本集的零矩阵
for(i in 1:ncol) { # 建立循环进行数据提取
max <- max(x[,i]) # 提取每个变量的最大值
min <- min(x[,i]) # 提取每个变量的最小值
for(j in 1:nrow) {
new[j,i] <- (x[j,i]-min)/(max - min) # 计算归一化之后的新数据集
}
}
new # 输出得到的新数据集
}
3.3 建立模型
通过前面的介绍我们了解到,nnet()函数在建立支持单隐藏层前馈神经网络模型的时候有两种建立方式,下面我们具体介绍两种建立过程。
使用第一种函数使用格式,需要首先确定模型数据,再确定模型的相应变量和自变量。
set.seed(1) # 确定抽样随机种子
samp <- sample(1:150, 100) # 从总样本集中抽取100个样本作为训练集
iris[samp, 1:4] <- scale0(iris[samp, ]) # 对样本进行预处理(使用自编函数)
r <- 1/max(abs(iris[samp, 1:4])) # 确定参数rang的变化值
set.seed(101) # 确定构建模型随机种子
nnetModel1 <- nnet(Species ~ ., data = iris, subset = samp,
size = 2, rang = r, decay = 5e-4, maxit = 200) # 建立神经网络模型
## # weights: 19
## initial value 112.651586
## iter 10 value 47.377992
## iter 20 value 17.139600
## iter 30 value 7.931815
## iter 40 value 7.010459
## iter 50 value 6.959446
## iter 60 value 6.948984
## iter 70 value 6.932991
## iter 80 value 6.920669
## iter 90 value 6.910135
## iter 100 value 6.793606
## iter 110 value 6.418930
## iter 120 value 6.062719
## iter 130 value 5.774312
## iter 140 value 5.704716
## iter 150 value 5.602482
## iter 160 value 5.534234
## iter 170 value 5.506035
## iter 180 value 5.500827
## iter 190 value 5.498079
## iter 200 value 5.496488
## final value 5.496488
## stopped after 200 iterations
根据函数的第二种使用格式,我们在首先应该将响应变量和自变量提取出来。自变量用矩阵表示,响应变量用class.ind()函数相应的预处理。确定好数据之后,应该根据数据分析所使用的各项参数的具体值。使用这种模式不需要特别强调所建立模型的形式,函数会自动将所有输入到x矩阵中的数据作为构建模型的自变量。构建模型具体过程如下:
x <- subset(iris, select = -Species) # 提取iris中除去类别的数据作为自变量
y <- iris[, 5] # 提取iris中的类别变量作为响应变量
y <- class.ind(y) # 对响应变量进行预处理,将其变为指标矩阵
set.seed(101) # 确定模型构建随机种子
nnetModel2 <- nnet(x, y, size = 2, rang = r, decay = 5e-4, maxit = 200) # 构建神经网络模型
## # weights: 19
## initial value 158.913750
## iter 10 value 85.260181
## iter 20 value 54.944394
## iter 30 value 50.584788
## iter 40 value 50.392842
## iter 50 value 50.310375
## iter 60 value 50.231725
## iter 70 value 50.194801
## iter 80 value 50.088354
## iter 90 value 49.849151
## iter 100 value 49.749197
## iter 110 value 49.722189
## iter 120 value 49.717006
## iter 130 value 49.714296
## iter 140 value 49.709887
## iter 150 value 49.700255
## iter 160 value 49.578326
## iter 170 value 49.218922
## iter 180 value 48.152775
## iter 190 value 47.416702
## iter 200 value 47.217706
## final value 47.217706
## stopped after 200 iterations
上述过程中,两种模型的相关参数都是一样的,权重衰减速度decay均为5e-4,最大迭代次数maxit均为200,隐藏层节点数均为2个,最终我们得到了一个4-2-3的神经网络,即输入层是4个节点,隐藏层是2个节点,输出层是3个节点。
3.4 结果分析
summary(nnetModel1) # 查看nnetModel1模型的结果
## a 4-2-3 network with 19 weights
## options were - softmax modelling decay=5e-04
## b->h1 i1->h1 i2->h1 i3->h1 i4->h1
## 7.43 1.25 2.48 -7.20 -6.50
## b->h2 i1->h2 i2->h2 i3->h2 i4->h2
## 2.58 -2.85 2.54 -5.57 -5.42
## b->o1 h1->o1 h2->o1
## -5.03 1.67 10.34
## b->o2 h1->o2 h2->o2
## -2.64 9.83 -9.45
## b->o3 h1->o3 h2->o3
## 7.67 -11.49 -0.90
通过summary()函数我们得到关于模型的相关信息。第一行中代表的是模型的总体信息,即这是一个4个输入层,2个隐藏层,3个输出层的人工神经网络模型,它的权重共有19个。
第二行显示的是模型中的参数设定,在构建模型的过程中,我们只设置了权重衰减最小值,使用这里显示出了模型衰减最小值为5e-4.
结果的第三部分显示出模型的具体判断过程,其中i1,i2,i3和i4分别表示输入层的4个节点;h1和h2分别表示隐藏层的2个节点;o1,o2和o3分别表示输出层的3个节点。b可以认为是一个常数项。判断过程下方的数值表示的是每一个节点向下一个节点的输入值的权重值。
3.5 预测判别
通常我们建立模型之后,主要目的是使用模型进行相应的预测和判别。在利用nnet()函数建立的模型进行预测时,我们将用到R语言中自带的函数predict()来对模型进行预测。
在使用predict()函数时,应该首先确定预测模型的类别。由于我们在建立模型的时候有两种方式,用predict();函数预测时会有两种不同的预测结果,这里需要我们分清楚将要进行的是哪一类模型。具体操作如下:
针对第一种建模方式建立的模型:
x <- iris[, 1:4] # 确认要进行预测的样本特征矩阵
pred <- predict(nnetModel1, x, type = "class") # 根据模型model对x数据进行预测
set.seed(110)
pred[sample(1:150, 6)] # 随机挑选6个预测结果进行展示
## [1] "virginica" "versicolor" "virginica" "virginica" "virginica"
## [6] "versicolor"
进行数据预测时,我们主要注意的问题是必须保证用于预测的自变量向量的个数同模型建立时使用的自变量向量个数一致,否则程序会出现问题。使用predict()函数进行预测时,我们无需特意调整预测结果类型。根据上述结果展示,我们可以发现predict()函数在预测时自动识别预测结果的类型,并自动生成了相应的类别名称。相对地,利用第一种建模方式建立的模型在预测时较为方便。
针对第二种建模方式建立的模型:
xx <- iris[, 1:4] # 确认需要进行预测的样本特征矩阵
pred <- predict(nnetModel2, xx) # 根据模型对xx数据进行预测
dim(pred) # 查看预测结果的维度
## [1] 150 3
pred[sample(1:150, 6), ] # 随机挑选6个预测结果进行展示
## setosa versicolor virginica
## [1,] 0.18060235 0.06603900 0.8854194738
## [2,] 0.19757773 0.05341646 0.9164268540
## [3,] 0.07747761 0.27960169 0.4227512673
## [4,] 0.07482277 0.88304010 0.0003121728
## [5,] 0.14580385 0.09717943 0.8178997037
## [6,] 0.91623896 0.02863937 0.0131332682
通过predict()函数对第二种模型进行预测,得到一个矩阵,而第一种模型得到的预测是模型中类别的名字。
在随机挑选的4个预测结果中,我们可以看到每个样本对应3种类别分别有3个数字,而这3个数字是3个输出结果的输出值。这三个数求和约等于1,可以理解为样本是某一类别的概率,对于样本类别的判别则是概率最大的一类。
因此,我们需要将上述预测结果进行进一步处理,以便直观看出预测类别。
name <- c("setosa","versicolor","virginica") # 确定3个类别的名称
prednew <- max.col(pred) # 确定每行中最大值所在的列
prednewname <- name[prednew] # 根据预测结果将其变为相应的类别名称
set.seed(200)
prednewname[sample(1:150, 6)] # 随机挑选6个预测结果展示
## [1] "versicolor" "versicolor" "versicolor" "virginica" "versicolor"
## [6] "virginica"
进行预测之后,我们需要检查模型预测的精度,需要用到table()函数将预测结果和真实值进行对比,过程如下:
true <- max.col(y) # 确定真实值中每行中最大值所在的列
table(true, prednewname) # 展示模型预测精度
## prednewname
## true setosa versicolor virginica
## 1 32 1 17
## 2 0 33 17
## 3 0 2 48
通过table()函数得到的模型预测精度结果,我们可以看到模型将32个setosa类型的样本预测正确,但是1个样本被预测为versicolor,17个样本被预测为virginica;33个versicolor类型的样本预测正确,但是17个被误预测为virginica;48个virginica类型的样本预测正确,但是2个被预测为versicolor。
3.6 模型差异分析
在利用nnet()函数进行模型建立的过程中,Wts参数通常被默认为原始值,但是在nnet()函数中,Wts参数的值在模型建立过程中是系统随机生成的,也就是说我们每一次建立模型所使用的迭代初始值都是不相同的。因此我们在实际建模中会遇到下面的现象:使用同样的数据,采取同样的节点数,设定同样的参数,得到的模型却是天壤之别。
我们将从3个方面对模型差异进行分析。
3.6.1 模型是否因为迭代次数达到最大值而停止
如果模型的不同是因为建立模型时迭代次数达到最大值而停止迭代所导致的,那么可以通过直接改变迭代的最大次数来使得模型变得更加精确。
nnetModel1$convergence # 查看第一个模型的迭代过程中是否达到迭代次数的最大值
## [1] 1
nnetModel2$convergence # 查看第二个模型的迭代过程中是否达到迭代次数的最大值
## [1] 1
通过结果我们可以看出,两个模型的迭代结果均为1,说明在建模过程中,迭代的停止是因为模型的迭代次数达到了最大迭代次数。
3.6.2 模型迭代的最终值
模型迭代的最终值即为模型拟合标准同模型权重衰减值的和。在模型的输出结果中,主要包含在模型的value中,该值越小说明模型拟合效果越好。我们对模型的迭代最终值的观察过程及结果如下:
nnetModel1$value # 查看第一个模型的迭代最终值
## [1] 5.496488
nnetModel2$value # 查看第二个模型的迭代最终值
## [1] 47.21771
从输出结果中我们看到,两个模型的迭代最终值有非常大的不同,模型2的值大于模型1的值,说明模型1的拟合效果明显好于模型2.
对于由初始值不同而导致模型不同的情况,我们可以使用该结果值进行判断,可以多运行几次nnet()函数,取到value值最小的模型作为最理想的模型。
3.6.3 观察两个模型的预测结果
人工神经网络模型的预测结果是该模型最核心的作用,对于两个模型的差异,需要对模型的预测能力做出分析。
如果两个模型在预测能力上没有任何差异,说明我们讨论两个模型的不同失去了意义。模型预测能力观察的过程及结果如下:
name <- c("setosa", "versicolor", "virginica") # 为三个类别确定名称
pred1 <- name[max.col(predict(nnetModel1, x))] # 利用第一种模型的预测方法对模型1进行预测
pred2 <- name[max.col(predict(nnetModel2, x))] # 利用第二种模型的预测方法对模型2进行预测
table(iris$Species, pred1) # 模型1预测精度展示
## pred1
## setosa versicolor virginica
## setosa 32 18 0
## versicolor 0 35 15
## virginica 0 1 49
table(iris$Species, pred2) # 模型2预测精度展示
## pred2
## setosa versicolor virginica
## setosa 32 1 17
## versicolor 0 33 17
## virginica 0 2 48
4 本章汇总
内容 | 类别 | 用途 |
---|---|---|
subset() | 函数 | 从某一个数据框中选择出符合某条件的数据或是相关的列 |
iris | 数据集 | datasets软件包提供的数据集 |
nnet | 软件包 | 用于人工神经网络模型的建立 |
AMORE | 软件包 | 用于人工神经网络模型的建立 |
neuralnet | 软件包 | 用于人工神经网络模型的建立 |
RSNNS | 软件包 | 用于人工神经网络模型的建立 |
class.ind() | 函数 | 用于人工神经网络模型数据的预处理 |
nnet() | 函数 | 用于人工神经网络模型的建立 |
plot() | 函数 | 绘制相关图像 |
5 参考文献
[1] 周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.
[2] 黄文, 王正林. 数据挖掘 : R语言实战[M]. 电子工业出版社, 2014.
[3] 努力进行光合作用. 周志华《Machine Learning》学习笔记[DB/OL]. https://blog.csdn.net/u011826404 .2016-12-20/2018-06-10