R语言 建立一个简单的神经网络
神经网络 这个术语指的是有机或人工性质的神经元系统。在人工智能参考中,神经网络是一组算法,其目的是像人脑一样识别一种模式。它们通过一种机器感知、标记或对原始输入进行聚类来解释感官数据。识别是数字的,存储在向量中,所有现实世界的数据,无论是图像、声音、文本或时间序列,都必须转化为数字。一个神经网络可以被描绘成一个系统,它由一些高度互联的节点组成,被称为 “神经元”,它们被组织成层,利用对外部输入的动态状态反应来处理信息。在了解神经网络的工作和架构之前,让我们试着了解一下人工神经元到底是什么。
人工神经元
感知器: 感知器是20世纪50年代和60年代由科学家Frank Rosenbalt开发的一种人工神经元,灵感来自Warren McCulloch和Walter Pitts的早期工作。那么,感知器是如何工作的?一个感知器接受几个二进制输出x1 , x2 , ….,并产生一个单一的二进制输出。
它可以有更多或更少的输入。为了计算/计算输出,权重起着重要的作用。权重w1 , w2 , ….,是实数,表示各自输入对输出的重要性。神经元的输出(o或1)完全取决于一个阈值,并根据函数计算出来。
这里t0 是阈值。它是一个实数,是神经元的一个参数。这就是基本的数学模型。感知器是一个通过权衡证据做出决定的设备。通过改变权重和阈值,我们可以得到不同的决策模型。
西格玛神经元: 西格玛神经元非常接近于感知器,但经过修改,其权重和偏置的微小变化只导致其输出的微小变化。这将使西格玛神经元网络能够更有效地学习。就像感知器一样,sigmoid神经元有输入,x1 , x2 , ….但这些输入不只是0或1,也可以是0和1之间的任何数值。因此,例如,0.567…是一个sigmoid神经元的有效输入。一个sigmoid神经元也有每个输入的权重,w1 , w2 , …,和一个总的偏置,b. 但输出不是0或1。相反,它是σ(w.x + b),其中σ被称为sigmoid函数。
一个具有输入x1 , x2 , …, 权重w1 , w2 , …, 和偏置b的sigmoid神经元的输出是。
神经网络的结构
一个神经网络由三层组成。
- 输入层: 根据现有数据获取输入的层。
- 隐层: 使用反向传播来优化输入变量的权重,以提高模型的预测能力的层。
- 输出层: 根据输入层和隐藏层的数据进行预测的输出。
输入数据通过输入层被引入神经网络,输入数据中的每个成分都有一个神经元,并被传达给网络中的隐藏层(一个或多个)。它被称为 “隐藏”,只是因为它们不构成输入或输出层。在隐藏层中,所有的处理实际上是通过一个以权重和偏差为特征的连接系统发生的(如前所述)。一旦收到输入,神经元就会计算一个加权和,并加上偏置,根据结果和激活函数(最常见的是sigmoid),它决定是否应该被 “解雇 “或 “激活”。然后,该神经元在一个称为 “前向传递 “的过程中将信息传递给下游的其他连接的神经元。在这个过程结束时,最后一个隐藏层与输出层相连,输出层有一个神经元用于每个可能的期望输出。
在R编程中实现神经网络
使用R语言来实现神经网络是非常容易的,因为它里面有很好的库。在用R语言实现神经网络之前,让我们先了解一下数据的结构。
了解数据的结构
这里让我们使用二进制数据集。目的是通过gre、gpa和排名等变量来预测一个考生是否会被大学录取。为了让用户更好地理解,R脚本是并排提供的,并有注释。数据是.csv格式的。我们将用 getwd() 函数获得工作目录,并将数据集binary.csv放在其中,以便进一步进行。请在这里下载csv文件。
# preparing the dataset
getwd()
data <- read.csv("binary.csv" )
str(data)
输出
'data.frame': 400 obs. of 4 variables:
admit: int 0 1 1 1 0 1 1 0 1 0 ... gre : int 380 660 800 640 520 760 560 400 540 700 ...
gpa : num 3.61 3.67 4 3.19 2.93 3 2.98 3.08 3.39 3.92 ... rank : int 3 3 1 4 4 2 1 2 3 2 ...
观察数据集的结构,我们可以发现它有4个变量,其中admission告诉我们考生是否会被录取(如果被录取为1,如果不被录取为0)gre,gpa和rank分别给出考生的gre分数,他/她在以前大学的gpa和以前大学的排名。 我们用录取作为因变量,用gre、gpa和排名作为自变量。现在以逐步的方式来理解整个过程
步骤1:数据的缩放
为了给数据集建立一个神经网络,我们必须确保对数据进行适当的缩放,这是非常重要的。数据的缩放是至关重要的,因为否则,一个变量可能仅仅因为其规模而对预测变量产生很大的影响。使用未缩放的数据可能会导致毫无意义的结果。缩放数据的常用技术是最小-最大归一化、Z-分数归一化、中位数和MAD,以及tan-h估计器。最小-最大归一化将数据转化为一个共同的范围,从而消除所有变量的缩放效应。在这里,我们使用最小-最大归一化来进行数据的缩放。
# Draw a histogram for gre data
hist(data$gre)
输出:
从上面的gre直方图中,我们可以看到gre从200到800不等。 我们调用以下函数来规范我们的数据。
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
# Min-Max Normalization
datagre <- (datagre - min(datagre)) / (max(datagre) - min(datagre))
hist(datagre)
输出:
从上面的表述中,我们可以看到,gre数据的比例范围是0到1。我们对gpa和rank也是如此。
# Min-Max Normalization
datagpa <- (datagpa - min(datagpa)) / (max(datagpa) - min(datagpa))
hist(datagpa)
datarank <- (datarank - min(datarank)) / (max(datarank) - min(datarank))
hist(datarank)
输出:
从以上两个直方图可以看出,gpa和rank也是在0到1的范围内进行缩放的。 缩放后的数据用于拟合神经网络。
第2步:对数据进行取样
现在将数据分为训练集和测试集。训练集用于寻找因变量和自变量之间的关系,而测试集则是分析模型的性能。我们使用60%的数据集作为训练集。训练集和测试集的数据分配是通过随机抽样完成的。我们使用 sample() 函数在R上进行随机抽样。使用 set.seed() ,每次生成相同的随机样本并保持一致性。在拟合神经网络时使用索引变量来创建训练和测试数据集。R脚本的内容如下。
set.seed(222)
inp <- sample(2, nrow(data), replace = TRUE, prob = c(0.7, 0.3))
training_data <- data[inp==1, ]
test_data <- data[inp==2, ]
第3步:拟合神经网络
现在在我们的数据上拟合一个神经网络。 neuralnet() 函数帮助我们为我们的数据建立一个神经网络。我们在这里使用的 neuralnet() 函数的语法如下。
语法:
neuralnet(formula, data, hidden = 1, stepmax = 1e+05, rep = 1, lifesign = “none”, algorithm = “rprop+”, err.fct = “sse”, linear.output = TRUE)
参数
参数 | 说明 |
---|---|
formula | 对要拟合的模型的符号描述。 |
data | 一个包含公式中指定的变量的数据框。 |
hidden | 一个整数向量,指定每层中隐藏神经元(顶点)的数量。 |
err.fct | 一个用于计算误差的可微调函数。另外,也可以使用代表平方误差之和和交叉熵的字符串’sse’和’ce’。 |
linear.output | 逻辑的。如果act.fct不应该应用于输出神经元,则将线性输出设置为TRUE,否则为FALSE。 |
lifesign | 一个字符串,指定函数在计算神经网络的过程中会打印多少东西。’无’、’最小’或’全部’。 |
rep | 神经网络训练的重复次数。 |
algorithm | 一个字符串,包含计算神经网络的算法类型。以下类型是可能的:’backprop’, ‘rprop+’, ‘rprop-‘, ‘sag’, 或 ‘slr’。backprop “指的是反向传播,”rprop+”和 “rprop-“指的是带有或不带有权重回溯的弹性反向传播,而 “sag “和 “slr “则是指使用修正的全局收敛算法(grprop)。 |
stepmax | 神经网络训练的最大步骤。达到这个最大值就会停止神经网络的训练过程。 |
library(neuralnet)
set.seed(333)
n <- neuralnet(admit~gre + gpa + rank,
data = training_data,
hidden = 5,
err.fct = "ce",
linear.output = FALSE,
lifesign = 'full',
rep = 2,
algorithm = "rprop+",
stepmax = 100000)
hidden: 5 thresh: 0.01 rep: 1/2 steps: 1000 min thresh: 0.092244246452834
2000 min thresh: 0.092244246452834
3000 min thresh: 0.092244246452834
4000 min thresh: 0.092244246452834
5000 min thresh: 0.092244246452834
6000 min thresh: 0.092244246452834
7000 min thresh: 0.092244246452834
8000 min thresh: 0.0657773918077728
9000 min thresh: 0.0492128119805471
10000 min thresh: 0.0350341801886022
11000 min thresh: 0.0257113452845989
12000 min thresh: 0.0175961794629306
13000 min thresh: 0.0108791716102531
13253 error: 139.80883 time: 7.51 secs
hidden: 5 thresh: 0.01 rep: 2/2 steps: 1000 min thresh: 0.147257381292693
2000 min thresh: 0.147257381292693
3000 min thresh: 0.091389043508166
4000 min thresh: 0.0648814957085886
5000 min thresh: 0.0472858320232246
6000 min thresh: 0.0359632940146351
7000 min thresh: 0.0328699898176084
8000 min thresh: 0.0305035254157369
9000 min thresh: 0.0305035254157369
10000 min thresh: 0.0241743801258625
11000 min thresh: 0.0182557959333173
12000 min thresh: 0.0136844933371039
13000 min thresh: 0.0120885410813301
14000 min thresh: 0.0109156031403791
14601 error: 147.41304 time: 8.25 secs
从上面的输出我们可以得出结论,两个重复都收敛了。但我们将在第一次重复中使用输出驱动,因为它给出的误差(139.80883)比第二次重复得出的误差(147.41304)小。现在,让我们绘制我们的神经网络,并将计算出的神经网络可视化。
# plot our neural network
plot(n, rep = 1)
输出
该模型在其隐藏层有5个神经元。黑线表示有权重的连接。权重是用反向传播算法计算的。蓝线显示的是偏置项(回归方程中的常数)。现在生成神经网络模型的误差,以及输入、隐藏层和输出之间的权重。
# error
n$result.matrix
输出
[, 1] [, 2]
error 1.398088e+02 1.474130e+02
reached.threshold 9.143429e-03 9.970574e-03
steps 1.325300e+04 1.460100e+04
Intercept.to.1layhid1 -6.713132e+01 -1.136151e+02
gre.to.1layhid1 -2.448706e+01 1.469138e+02
gpa.to.1layhid1 8.326628e+01 1.290251e+02
rank.to.1layhid1 2.974782e+01 -5.733805e+01
Intercept.to.1layhid2 -2.582341e+01 2.508958e-01
gre.to.1layhid2 -5.800955e+01 1.302115e+00
gpa.to.1layhid2 3.206933e+01 -4.856419e+00
rank.to.1layhid2 6.723053e+01 1.540390e+01
Intercept.to.1layhid3 3.174853e+01 -3.495968e+01
gre.to.1layhid3 1.050214e+01 1.325498e+02
gpa.to.1layhid3 -6.478704e+01 -4.536649e+01
rank.to.1layhid3 -7.706895e+01 -1.844943e+02
Intercept.to.1layhid4 1.625662e+01 2.188646e+01
gre.to.1layhid4 -3.552645e+01 1.956271e+01
gpa.to.1layhid4 -1.151684e+01 2.052294e+01
rank.to.1layhid4 -2.263859e+01 1.347474e+01
Intercept.to.1layhid5 2.448949e+00 -3.978068e+01
gre.to.1layhid5 -2.924269e+00 -1.569897e+02
gpa.to.1layhid5 -7.773543e+00 1.500767e+02
rank.to.1layhid5 -1.107282e+03 4.045248e+02
Intercept.to.admit -5.480278e-01 -3.622384e+00
1layhid1.to.admit 1.580944e+00 1.717584e+00
1layhid2.to.admit -1.943969e+00 -6.195182e+00
1layhid3.to.admit -5.137650e+01 6.731498e+00
1layhid4.to.admit -1.112174e+03 -4.245278e+00
1layhid5.to.admit 7.259237e+02 1.156083e+01
第4步:预测
让我们使用神经网络模型来预测评级。我们必须记住,预测的评级将被缩放,它必须被转换,以便与真实的评级进行比较。同时,将预测的等级与真实的等级进行比较。
# Prediction
output <- compute(n, rep = 1, training_data[, -1])
head(output$net.result)
输出
[, 1]
2 0.34405929
3 0.41148373
4 0.07642387
7 0.98152454
8 0.26230256
9 0.07660906
head(training_data[1, ])
输出
admit gre gpa rank
2 1 0.7586207 0.8103448 0.6666667
第5步:混淆矩阵和错误分类误差
然后,我们使用 compute() 方法对我们的结果进行四舍五入,并创建一个混淆矩阵来比较真/假阳性和阴性的数量。我们将用训练数据形成一个混淆矩阵
# confusion Matrix Misclassification error -Training data
output <- compute(n, rep = 1, training_data[, -1])
p1 <- outputnet.result
pred1 <- ifelse(p1 > 0.5, 1, 0)
tab1 <- table(pred1, training_data$admit)
tab1
输出
pred1 0 1
0 177 58
1 12 34
该模型产生了177个真阴性(0),34个真阳性(1),而有12个假阴性和58个假阳性。现在,让我们计算一下错误分类误差(针对训练数据),{1-分类误差}。
1 - sum(diag(tab1)) / sum(tab1)
输出
[1] 0.2491103
错误分类误差为24.9%。我们可以通过增加隐蔽层中的递减节点和偏置来进一步提高我们模型的准确性和效率。
机器学习算法的优势在于其每次预测输出时的学习和改进能力。在神经网络的背景下,这意味着定义神经元之间连接的权重和偏差变得更加精确。这就是为什么权重和偏差的选择,使网络的输出接近所有训练输入的真实值。同样地,我们可以在R中制作更有效的神经网络模型来预测和驱动决策。