R语言数据挖掘实践——神经网络代码实战

R语言数据挖掘实践——神经网络代码实战

下面我们开始运用R语言分析来源于UCI数据库中的关于白酒品质研究的数据集进行算法演示,该数据集是关于白酒中的各项变量对白酒品质的影响情况。

这里将利用该数据集建立出适合的单隐藏层前馈人工神经网络模型,并对所建立的模型进行相应的分析,查看建立模型的预测能力如何。

数据探索

我们先从UCI数据库中下载关于白酒品质的的"winequality-white.csv"数据集,下载地址为:http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/。这里包含了12个变量,其中特征变量11个,结果变来呢为quality变量。该数据库中将白酒品质总共分为1到10这10个等级,本文数据中包含了3至9这7个等级,为了方便分析,我们将白酒品质分为3个等级,其中品质3、4、5为“bad”品质,品质6为“mid”品质,品质7、8、9为“good”品质。分析代码如下:

> wine

> summary(wine)

fixed.acidity volatile.acidity citric.acid residual.sugar chlorides free.sulfur.dioxide total.sulfur.dioxide

Min. : 3.800 Min. :0.0800 Min. :0.0000 Min. : 0.600 Min. :0.00900 Min. : 2.00 Min. : 9.0

1st Qu.: 6.300 1st Qu.:0.2100 1st Qu.:0.2700 1st Qu.: 1.700 1st Qu.:0.03600 1st Qu.: 23.00 1st Qu.:108.0

Median : 6.800 Median :0.2600 Median :0.3200 Median : 5.200 Median :0.04300 Median : 34.00 Median :134.0

Mean : 6.855 Mean :0.2782 Mean :0.3342 Mean : 6.391 Mean :0.04577 Mean : 35.31 Mean :138.4

3rd Qu.: 7.300 3rd Qu.:0.3200 3rd Qu.:0.3900 3rd Qu.: 9.900 3rd Qu.:0.05000 3rd Qu.: 46.00 3rd Qu.:167.0

Max. :14.200 Max. :1.1000 Max. :1.6600 Max. :65.800 Max. :0.34600 Max. :289.00 Max. :440.0

density pH sulphates alcohol quality

Min. :0.9871 Min. :2.720 Min. :0.2200 Min. : 8.00 Min. :3.000

1st Qu.:0.9917 1st Qu.:3.090 1st Qu.:0.4100 1st Qu.: 9.50 1st Qu.:5.000

Median :0.9937 Median :3.180 Median :0.4700 Median :10.40 Median :6.000

Mean :0.9940 Mean :3.188 Mean :0.4898 Mean :10.51 Mean :5.878

3rd Qu.:0.9961 3rd Qu.:3.280 3rd Qu.:0.5500 3rd Qu.:11.40 3rd Qu.:6.000

Max. :1.0390 Max. :3.820 Max. :1.0800 Max. :14.20 Max. :9.000

> #将白酒品质分为3个等级

> #设置中间变量对处理后的向量进行临时存储

> cha

> for(i in 1:4898) {

+ if (wine[i,12]>6) cha[i]

+ else if(wine[i,12]>5) cha[i]

+ else cha[i]

+ }

> #将字符型变量转化为含有因子的变量并赋值给数据集wine

> wine[,12]=factor(cha)

> summary(wine$quality)

bad good mid

1640 1060 2198

我们将利用数据集建立出适合的单隐藏层前馈人工神经网络模型。在模型中我们将根据样本白酒的非挥发性酸、挥发性酸、柠檬酸、剩余糖分、氯化物、游离二氧化硫、总二氧化硫、密度、酸性、硫酸盐、酒精度这11个属性来对白酒的品质进行判别。

数据处理

在建立人工神经网络模型之前,我们首先应对数据进行预处理。

作为建立人工神经网络模型的处理方式主要进行数据的归一化。数据归一化方法是神经网络预测前对数据常做的一种处理方法,即将所有数据都转化为[0,1]之间的束,其目的是取消各维度数据间数量级的差别,避免因为输入输出数据数量级别较大而造成网络预测误差较大。

数据归一化的方法主要有以下两种:

1、最大最小法。函数形式如下:

Xk = (Xk - Xmin)/(Xmax - Xmin)

其中Xmin为数据序列中的最小数,Xmax为序列中的最大数。

2、平均数方差法。函数形式如下:

Xk = (Xk - Xmean) / Xvar

其中,Xmean为数据序列的均值,Xvar为数据的方差。

下面采用最大最小法,对于这种0-1归一化方法,我们将通过自写程序对原始数据进行预处理,程序文件命名为“scale01.R”,相应代码如下:

#确定程序名称为scale01

scale01

#提取预处理样本集中特征变量个数

ncol

#提取预处理样本集中样本总量

nrow

#建立用于保存新样本集的矩阵

new

for(i in 1:ncol){

#提取每个变量的最大值

max

#提取每个变量的最小值

min

for(j in 1:nrow){

#计算归一化后的新数据集

new[j,i]

}

}

new

}

建立模型

nnet()函数在建立支持单隐藏层前馈神经网络模型的时候有两种建立方式,一种是根据既定公式建立模型,而另一种是根据所给的数据建立模型。接下来我们将具体讲述基于上述数据函数的两种建模过程。

根据函数的第一种使用格式,在针对上述数据建模时,应该先确定我们所建立模型所使用的数据,然后再确定所建立模型的响应变量和自变量,具体建模操作如下:

> library(nnet)

> set.seed(71)

> #从总样本集中抽取3000个样本作为训练集

> samp

> #对样本进行预处理

> source("scale01.R")

> wine[samp,1:11]

Error in colMeans(x, na.rm = TRUE) : 'x' must be numeric

> #确定参数rang的变化范围

> r

> set.seed(101)

> #建立神经网络模型

> model1

在使用第一种格式建立模型时,如果使用数据中的全部自变量作为模型的自变量时,我们可以简要的使用"quality~."代替全部的自变量。

根据函数的第二种使用格式,我们针对上述数据建立模型时,首先应该将响应变量和自变量分别提取出来。自变量通常用一个矩阵表示,而对于响应变量则应该进行相应的预处理。

首先要利用class.ind()函数将响应变量处理为类指标矩阵。在确定好数据后还应根据数据分析所使用的各项参数的具体值。对于建立神经网络模型的具体过程如下:

> #提取wine数据集中除quality列以外的数据作为自变量

> x

> #提取wine数据集中的quality列数据作为响应变量

> y

> #对响应变量进行预处理

> y

> set.seed(101)

> #建立神经网络模型

> model2

在使用第二种格式建立模型时,不需要特别强调所建立模型的形式,函数会自动将所有输入到x矩阵中的数据作为建立模型所需要的自变量。在上述过程中,两种模型的相关参数都是一样的,两种模型的权重衰减速度最小值为5e-4;最大迭代次数都为200次;隐藏层的节点数都为4个;最终我们建立出来的模型是一个11-4-3的神经网络模型,即输入层是11个节点,隐藏层是4个节点,输出层是3个节点。

结果分析

我们使用summary()函数查看结果。

> summary(model1)

a 11-4-3 network with 63 weights

options were - softmax modelling decay=5e-04

b->h1 i1->h1 i2->h1 i3->h1 i4->h1 i5->h1 i6->h1 i7->h1 i8->h1 i9->h1 i10->h1 i11->h1

-52.03 -0.03 0.75 0.01 -0.02 0.39 0.00 0.00 53.67 -0.18 -0.24 -0.05

b->h2 i1->h2 i2->h2 i3->h2 i4->h2 i5->h2 i6->h2 i7->h2 i8->h2 i9->h2 i10->h2 i11->h2

2.42 -0.60 -4.12 2.12 -0.24 17.76 -0.06 0.02 7.41 -4.60 1.53 0.28

b->h3 i1->h3 i2->h3 i3->h3 i4->h3 i5->h3 i6->h3 i7->h3 i8->h3 i9->h3 i10->h3 i11->h3

-8.64 -11.41 0.41 -4.39 -17.36 0.07 -19.32 4.77 -8.63 0.38 13.47 -10.25

b->h4 i1->h4 i2->h4 i3->h4 i4->h4 i5->h4 i6->h4 i7->h4 i8->h4 i9->h4 i10->h4 i11->h4

22.81 16.27 -9.61 -66.33 -9.52 7.77 -5.45 0.51 22.76 9.51 -15.32 -6.63

b->o1 h1->o1 h2->o1 h3->o1 h4->o1

-13.58 27.08 6.70 -0.23 1.08

b->o2 h1->o2 h2->o2 h3->o2 h4->o2

12.61 -26.27 -16.02 0.75 -0.76

b->o3 h1->o3 h2->o3 h3->o3 h4->o3

0.97 -0.81 9.32 -0.53 -0.33

通过summary()函数我们可以得到关于模型的相关信息。在输出结果的第一行我们可以看到模型的总体类型,该模型总共有三层,输入层有11个节点,隐藏层有4个节点,输出层有3个节点,该模型的权重总共有63个。

在输出结果的第二行显示的是模型中的相关参数的设定,在该模型的建立过程中,我们只设定了相应的模型权重衰减最小值,所以这里显示出了模型衰减最小值为5e-4。

接下来的第三部分是模型的具体判断过程,其中的i系列代表的是输入层的11个节点,h系列代表的是隐藏层的4个节点,而o系列代表的是输出层的3个节点。对于b,我们可以将它理解为模型中的常数项。第三部分中的数字代表的是没一个节点向下一个节点的输入值的权重值。

预测判别

通常我们利用样本数据建立模型之后,主要的目的都是利用模型来进行相应的预测和判别。在利用nnet()函数建立的模型进行预测时,我们将使用predict()函数对模型进行预测。

在使用predict()函数时,我们应该首先确认将要用于预测模型的类别。由于我们在建立模型时有两种监理方式,而利用predict()函数进行预测的时候,对于两种模型会存在两种不同的预测结果,所以我们必须分清楚将要进行预测的模型是哪一类模型。具体操作如下:

针对第一种建模方式所建立的模型:

> #确认需要进行预测的样本特征矩阵

> x

> #根据模型model1对x数据进行预测

> pred

> set.seed(110)

> #随机挑选8个预测结果进行展示

> pred[sample(1:4898,8)]

[1] "bad" "mid" "good" "mid" "mid" "mid" "mid" "mid"

在进行数据预测时,我们主要注意的问题就是必须保证用于预测的自变量向量的个数同模型建立时使用的自变量向量个数一致,否则将无法预测结果。在使用predict()函数进行预测时,我们不用刻意去调整预测结果类型。通过上述预测结果的展示,我们可以看到predict()函数在预测时会自动识别预测结果的类型,并自动生成了相应的类别名称。相对来说,利用第一种建模方式建立的模型在预测时较为方便。

针对第二种建模方式所建立的模型:

> #确认需要进行预测的样本特征矩阵

> xt

> #根据模型model2对xt数据进行预测

> pred

> #查看预测结果维度

> dim(pred)

[1] 4898 3

> #随机挑选4个预测结果进行展示

> pred[sample(1:4898,4),]

bad good mid

[1,] 0.7883393 0.010327155 0.2200430

[2,] 0.4667784 0.063874484 0.4121678

[3,] 0.1170516 0.415681237 0.5202564

[4,] 0.8431664 0.007020471 0.3159719

通过predict()函数对第二种模型进行预测,我们可以的看到预测结果是一个矩阵,而不像第一种模型那样直接预测出模型中类别的名字。

在随机挑选的4个预测结果中,我们可以看到每个样本对应3种类别分别有3个数字,而这3个数字正是3个输出结果的输出值。这3个数的求和大约是等于1的,所以我们又可以将它简要地看作概率,即样本为其中某一类别的概率,对于样本类别的判别则为概率最大的那一类。

因此对于上述预测结果我们需要将其进行进一步处理,处理之后才能直观地看出样本的预测类别。对于预测结果pred的处理,具体过程如下:

> #为3个类别确定名称

> name

> #确定每行中最大值所在的列

> prednew

> #根据预测结果将其变为相对应的类别名称

> prednewn

> set.seed(201)

> #随机挑选8个预测结果进行展示

> prednewn[sample(1:4898,8)]

[1] "mid" "bad" "mid" "bad" "mid" "mid" "mid" "bad"

通常在进行预测之后,我们还需要检查模型预测的精度,这便需要用到table()函数对预测结果和真实结果做出对比展示。过程如下:

> #确定真实值的每行中最大值所在的列

> true

> #模型预测精度展示

> table(true,prednewn)

prednewn

true bad good mid

1 1088 8 544

2 80 175 805

3 535 93 1570

通过观察table()函数对模型预测精度的展示结果,我们可以看到在模型预测时,模型将所有属于属于bad品质的白酒中的1088个样本预测正确,但将另外8个样本预测为good品质,并且将544个样本预测为mid品质;模型将所有属于good品质的白酒中的175个样本预测正确,但将另外80个样本预测为bad品质,并且将805个样本预测为mid品质;模型将所有属于mid品质的白酒中的1570个样本预测正确,但将另外93个样本预测为good品质,并且将535个样本预测为bad品质。

模型差异分析

在利用nnet()函数建立模型的过程中,其中参数Wts的值我们通常默认为原始值。但是在nnet()函数中,参数Wts的值在建立模型的过程中用于迭代的权重初始值,该参数的默认值为系统随机生成,换句话说,我们每次建立模型所使用的迭代初始值都是不相同的。因此我们再实际建模过程中会遇到这样的现象:我们用同样的数据,采取同样的节点数,设定同样的参数,但是最后会得到两个不同的模型,甚至是两个差异非常大的模型。

为了具体介绍该问题,我们先使用iris数据集进行举例,首先我们利用下列语句建立模型model1以及模型model2,具体代码如下:

>library(nnet)

>x

>y

>y

>set.seed(101)

>#建立模型model1

>model1

>#建立模型model2

>model2

从建立模型的语句观察,我们发现两个模型应该是一样的模型,但是通过对其进行具体分析,我们将发现两个模型存在很大的差异,接下来我们从三个方面对模型差异进行分析。

1、模型是否因为迭代次数达到最大值而停止

如果模型的不同是因为建立模型时迭代次数达到最大值而停止迭代所导致的,那么我们可以直接改变迭代的最大次数来使模型变得更加精确。具体查看方式如下:

>查看model1的迭代过程中是否达到迭代次数最大值

> model1$convergence

[1] 0

>查看model2的迭代过程中是否达到迭代次数最大值

> model2$convergence

[1] 0

从输出结果中我们可以看到,两个模型的迭代结果值都为0,这说明了再建立模型的过程中,迭代的停止并非是因为模型的迭代次数达到了最大迭代数。所以说明模型的最大迭代次数并不是影响两个模型不同的主要原因。

2、模型迭代的最终值

模型迭代的最终值即为模型拟合标准同模型权重衰减值的和。在模型的输出结果中,主要包含在模型的value中,该值越小说明模型拟合效果越好。我们对模型迭代的最终值的观察过程及结果如下:

>#查看模型model1的迭代最终值

> model1$value

[1] 3.198636

>#查看模型model2的迭代最终值

> model2$value

[1] 2.598032

从输出结果中我们可以看到,两个模型的迭代最终值差异并不是很大。

因此对于因为初始迭代值不同而导致的模型不同的情况,我们可以使用该结果值来进行判断,我们应该多运行几次nnet()函数,而选择所有模型中该结果值最小的一个模型作为最理想的模型。

3、观察两个模型的预测结果

人工神经网络模型的预测效果是该模型最终最核心的作用,所以对于两模型差异的情况,我们必须对模型的预测能力做出分析。

如果两个模型在预测能力上显示不出任何差异,那么我们讨论两个模型不同也就失去了意义,因为我们所追求的就是模型的预测能力,所以在模型的差异问题上,我们最关心的也是两个模型的预测能力的差异。观察过程及结果如下:

> #为三个类别确定名称

> name

> #对模型model1进行预测

> pred1

> #对模型model2进行预测

> pred2

> table(iris$Species,pred1)

pred1

setosa versicolor virginica

setosa 50 0 0

versicolor 0 49 1

virginica 0 0 50

> table(iris$Species,pred2)

pred2

setosa versicolor virginica

setosa 50 0 0

versicolor 0 49 1

virginica 0 0 50

优化建模

在以上对nnet()函数的特别问题分析之后,我们了解到用相同数据相同参数建立的模型有可能不是最优模型。那么,应该怎么做才能得到最优模型呢?

针对这个问题,如果在时间和条件允许的情况下,我们可以多运行几次模型,并从中挑选出针对于测试集样本误判率最小的模型。

首先,要确定出隐藏层最优节点的数目。之前已经介绍了对于人工神经网络模型中隐藏层的相关确定条件,但是在实际模型构建过程中,仍需要尽可能地测试每一节点数目下模型的误判率,以确定最优的模型误判率。实现代码如下:

> wine

> set.seed(71)

> wine

> nrow.wine

> source("scale01.R")

> #设置中间变量对处理后的向量进行临时存储

> cha

> for(i in 1:4898) {

+ if (wine[i,12]>6) cha[i]

+ else if(wine[i,12]>5) cha[i]

+ else cha[i]

+ }

Error in if (wine[i, 12] > 6) cha[i] :

missing value where TRUE/FALSE needed

> #将字符型变量转化为含有因子的变量并赋值给数据集wine

> wine[,12]=factor(cha)

> set.seed(444)

> #从总样本集中抽取70%的样本作为训练集

> samp

> #对训练集样本进行预处理

> wine[samp,1:11]

> wine[-samp,1:11]

> #确定参数range的变化范围

> r

> n

> err1

> err2

> for(i in 1:17){

+ set.seed(111)

+ model

+ err1[i]

+ err2[i]

+ }

> plot(1:17,err1,'l',col=1,lty=1,ylab = "模型误判率",xlab="隐藏层节点个数",ylim=c(min(min(err1),min(err2)),max(max(err1),max(err2))))

> lines(1:17,err2,col=1,lty=3)

> points(1:17,err1,col=1,pch="+")

> points(1:17,err2,col=1,pch="o")

> legend(1,0.53,"测试集误判率",bty = "n",cex=1.5)

> legend(1,0.35,"训练集误判率",bty="n",cex=1.5)

R语言数据挖掘实践——神经网络代码实战

经过上述程序运行之后,将得到关于样本集在不同的隐藏层节点数下所对应的模型误判率。从图中我们可以清楚地看到,训练集样本错误跟随隐藏层节点数的增加而下降,但是与此同时,测试集样本错误却未随着隐藏层节点的增加而下降,这种现象便是由于模型中隐藏层节点数增加而引起的模型过度拟合导致的。

从图中可以看到,模型针对测试集误判率大概在模型隐藏层节点数为3的时候取到最小值,所以我们将隐藏层节点数数确定为3。

从前文中我们分析到,当神经网络模型训练周期过长的时候,建立的人工神经网络模型将会记录下训练集中几乎全部信息,这将会产生过度拟合的问题。即该模型针对于训练集的时候将会体现出非常优异的预测能力,但是由于该模型记录下了训练集中的全部信息,则该模型也将训练集中的许多特有的信息记录下来,所以当模型用于其他样本集的时候,模型的预测能力将会大大下降,即模型的泛化能力非常弱。

在确定最优隐藏层节点数的时候,接下来确定出最优的迭代次数,实现代码如下:

>err11

>err12

>for(i in 1:500){

+set.seed(111)

+model

+err11[i]

+err12[i]

>}

> plot(1:length(err11),err11,'l',col=1,ylab = "模型误判率",xlab="训练周期",ylim=c(min(min(err11),min(err12)),max(max(err11),max(err12))))

> lines(1:length(err11),err12,col=1,lty=3)

> legend(250,0.47,"测试集误判率",bty = "n",cex=1.2)

> legend(250,0.425,"训练集误判率",bty="n",cex=1.2)

R语言数据挖掘实践——神经网络代码实战

从上图可以看到,模型针对于训练集和测试集的误判率均同时随训练周期的增大而降低,之前也讨论到当模型训练周期过长时,模型应该会出现过度拟合的问题,即在训练周期达到一定程度时,测试集误差将会反向变化,训练集误差将会随着模型训练周期的增大而增大。

对于这个问题,是用R语言进行模型构建时会经常遇到,但这并非说明理论出现了错误。对该问题进行进一步分析可以得知出现该问题存在着两个原因。

首先,在nnet程序包中,函数在构建模型时将会设定一个条件值以避免函数进入死循环。即在默认情况下,当函数计算值变化为零时模型将会停止运转,所以很多时候模型将不会运行到过高的训练周期。

其次,由于训练集样本同测试集样本的相似度过高,所以训练集中的特征同样为测试集中的特征,所以即使在过度拟合的情况下,所构建的模型同样能很好地适用于与训练集相似度很高的数据集。

尽管会出现上图中的问题,但是该图像仍然具有一定的参考价值。从图中可以发现,训练集误差随着训练周期的增大而不断减小;但是对于测试集,当训练周期达到一定程度后,模型的误差率将会趋于平稳,模型的误判率将不再下降。所图中的情况,我们综合分析决定将模型的训练周期确定为300。

因此,最终得出的模型为隐藏层节点数为3,训练周期为300,对于最新抽取的样本集中,在随机数生成器初始值为111情况下的人工神经网络模型。

>set.seed(111)

>model

> #根据需要进行预测的样本特征矩阵

> x

> #根据模型model对x数据进行预测

> pred

> table(wine[-samp,12],pred)

pred

bad good mid

bad 237 5 51

good 20 59 109

mid 177 35 207

相关阅读:《 》


分享到:


相關文章: