《深度学习精要(基于R语言)》学习笔记
1、概览
本文涉及以下主题:
• 深度前馈神经网络入门
• 常见的激活函数:整流器、双曲正切和maxout
• 选取超参数
• 深度神经网络模型训练并预测新数据
2、深度前馈神经网络的入门
深度前馈神经网络来自输入的信息流要穿过直到输出为止的每一个相继的层,中间没有任何反馈和递归循环(既包括前向也包括后向连结的模型被称为循环神经网络(recurrent neural networks))。前馈神经网络对于预测和分类是很有用的。
在深度前馈神经网络中,从输入层到隐藏层的变换和从隐藏层到输出的变换是同时学习的。本质上,模型学习的是函数形式以及权重。在实践中,虽然模型不可能学习真正的生成模型(generative model),但是它能(非常接近的)近似真正的模型。隐藏神经元越多,近似就越接近。
深度前馈神经网络必须要确定的一个关键部分是成本或损失函数。两个最常用的成本函数分别是交叉熵(cross-entropy)和二次的函数均方误差(MSE)。
3、常用的激活函数——整流器、双曲正切和maxout
激活函数决定了输入层和隐藏层之间的映射。对于怎样激活神经元,它定义了函数形式。我们曾经使用过双曲正切f(x)=tanh(x)作为激活函数。双曲正切在某些情况下表现很好,但它有一种潜在的局限,就是在很小或者很大的取值上会饱和。
> p_load(ggplot2, magrittr)
>
> x <- seq(-5, 5, 0.1)
>
> data.frame(x = x, tanh.x = tanh(x)) %>%
+ ggplot(aes(x, tanh.x)) +
+ geom_line(color = "red", size = 1) + theme_bw() +
+ labs(title = "双曲正切", y = "", x = "") +
+ theme(legend.title = element_blank())
双曲正切函数
> y <- ifelse(x > 0, x, 0)
> data.frame(x = x, y = y) %>% ggplot(aes(x, y)) +
+ geom_line(color = "red", size = 1) +
+ theme_bw() + labs(title = "整流器", y = "", x = "")
整流器
4、选取超参数
参数通常是指诸如权重或者偏置/截距等,还有许多其他的参数必须被设置在偏移上而且没有在模型的训练过程中被优化或被学到。这些参数有时被称为超参数。事实上,甚至模型的选择(例如深度前馈神经网络、随机森林或者支持向量机)也能被视为一种超参数。
超参数的设置会影响到模型的准确度和训练速度,我们必须做出自己的决策。理解每个超参数是什么样有助于启发我们的决定。例如,如果我们从一个模型开始,它的性能比可接受的超参数更差,应该改变成在模型中允许更大的容量和灵活性,例如增加更多的隐藏神经元、隐藏神经元额外的层、更多的训练轮数等。如果在训练数据和测试数据上的模型性能有较大差异,或许表明模型过拟合了数据,这种情况下我们可以调整超参数,降低容量或者加上更多的正则化。
5、深度神经网络训练
> library(pacman)
> p_load(h2o)
> options(width = 70, digits = 2)
>
> cl <- h2o.init(max_mem_size = "6G", nthreads = 4)
## Connection successful!
##
## R is connected to the H2O cluster:
## H2O cluster uptime: 1 hours 39 minutes
## H2O cluster timezone: Asia/Shanghai
## H2O data parsing timezone: UTC
## H2O cluster version: 3.28.0.4
## H2O cluster version age: 1 month and 3 days
## H2O cluster name: H2O_started_from_R_Admin_lgj814
## H2O cluster total nodes: 1
## H2O cluster total memory: 5.13 GB
## H2O cluster total cores: 4
## H2O cluster allowed cores: 4
## H2O cluster healthy: TRUE
## H2O Connection ip: localhost
## H2O Connection port: 54321
## H2O Connection proxy: NA
## H2O Internal Security: FALSE
## H2O API Extensions: Amazon S3, Algos, AutoML, Core V3, TargetEncoder, Core V4
## R Version: R version 3.6.3 (2020-02-29)
> # 使用智能手机活动记录仪数据
> train.x <- read.table("./train/X_train.txt")
> test.x <- read.table("./test/X_test.txt")
> train.y <- read.table("./train/y_train.txt")[[1]]
> test.y <- read.table("./test/y_test.txt")[[1]]
>
> use.train <- cbind(train.x, outcome = factor(train.y))
> use.test <- cbind(test.x, outcome = factor(test.y))
>
> use.labels <- read.table("activity_labels.txt")
>
> h2o.train <- as.h2o(use.train, destination_frame = "h2o.train")
>
> h2o.test <- as.h2o(use.test, destination_frame = "h2o.test")
构建浅层神经网络模型:
> mt1 <- h2o.deeplearning(
+ x = colnames(train.x),
+ y = "outcome",
+ training_frame = h2o.train,
+ # 线性整流器
+ activation = "RectifierWithDropout",
+ hidden = 50,
+ # 10次训练迭代
+ epochs = 10,
+ # 交叉熵成本损失函数
+ loss = "CrossEntropy",
+ # 输入变量0.2丢弃
+ input_dropout_ratio = 0.2,
+ # 隐藏神经元0.5丢弃
+ hidden_dropout_ratios = 0.5,
+ # 导出权重和偏差
+ export_weights_and_biases = T
+ )
>
> mt1
## Model Details:
## ==============
##
## H2OMultinomialModel: deeplearning
## Model ID: DeepLearning_model_R_1585310900485_2
## Status of Neuron Layers: predicting outcome, 6-class classification, multinomial distribution, CrossEntropy loss, 28,406 weights/biases, 396.5 KB, 73,520 training samples, mini-batch size 1
## layer units type dropout l1 l2 mean_rate
## 1 1 561 Input 20.00 % NA NA NA
## 2 2 50 RectifierDropout 50.00 % 0.000000 0.000000 0.003258
## 3 3 6 Softmax NA 0.000000 0.000000 0.011217
## rate_rms momentum mean_weight weight_rms mean_bias bias_rms
## 1 NA NA NA NA NA NA
## 2 0.008596 0.000000 -0.000506 0.062389 0.356355 0.149577
## 3 0.070302 0.000000 -0.004762 0.766483 -0.023791 0.259486
##
##
## H2OMultinomialMetrics: deeplearning
## ** Reported on training data. **
## ** Metrics reported on full training frame **
##
## Training Set Metrics:
## =====================
##
## Extract training frame with `h2o.getFrame("h2o.train")`
## MSE: (Extract with `h2o.mse`) 0.047
## RMSE: (Extract with `h2o.rmse`) 0.22
## Logloss: (Extract with `h2o.logloss`) 0.17
## Mean Per-Class Error: 0.058
## Confusion Matrix: Extract with `h2o.confusionMatrix(<model>,train = TRUE)`)
## =========================================================================
## Confusion Matrix: Row labels: Actual class; Column labels: Predicted class
## 1 2 3 4 5 6 Error Rate
## 1 1160 52 14 0 0 0 0.0538 = 66 / 1,226
## 2 1 1072 0 0 0 0 0.0009 = 1 / 1,073
## 3 0 46 940 0 0 0 0.0467 = 46 / 986
## 4 0 1 0 1271 6 8 0.0117 = 15 / 1,286
## 5 0 0 0 319 1055 0 0.2322 = 319 / 1,374
## 6 0 0 0 0 0 1407 0.0000 = 0 / 1,407
## Totals 1161 1171 954 1590 1061 1415 0.0608 = 447 / 7,352
##
## Hit Ratio Table: Extract with `h2o.hit_ratio_table(<model>,train = TRUE)`
## =======================================================================
## Top-6 Hit Ratios:
## k hit_ratio
## 1 1 0.939200
## 2 2 0.999048
## 3 3 1.000000
## 4 4 1.000000
## 5 5 1.000000
## 6 6 1.000000
> dim(train.x)
## [1] 7352 561
模型信息为:
Status of Neuron Layers: predicting outcome, 6-class classification, multinomial distribution, CrossEntropy loss, 28,406 weights/biases, 396.5 KB, 73,520 training samples, mini-batch size 1
6-class classification:结果有6个离散的水平
multinomial distribution:多项式分布模型
CrossEntropy loss:交叉熵成本损失函数
28,406 weights/biases:28406个权重/偏差(biase)。偏差就像截距或者常数的偏移。因为这是一个前馈神经网络,只在相邻的层之间有权重。输入变量没有偏差,但隐藏神经元和输出有。28406个权重的构成中有来自于输入变量和第一层隐藏神经元之间的个权重,来自于隐藏神经元和输出之间的个权重(6是因为结果中有六个不同的水平)以及来自于隐藏神经元的50个偏差和来自输出的6个偏差。
Training Set Metrics报告了训练集上的性能度量,包括均方误差(越低越好)、 (越高越好)以及对数损失(越低越好)。
最后,代码打印出来一个混淆矩阵,显示了真实输出对比预测输出的情况。行中显示的是观测的输出,列中显示的是预测的输出。对角线标示了正确的分类并根据结果水平显示了误差率。
使用h2o.deepfeatures()函数提取并查看模型的特征:
> fea <- as.data.frame(h2o.deepfeatures(
+ # 模型
+ mt1,
+ # 数据
+ h2o.train,
+ # 要提取的层数
+ 1
+ ))
>
> fea[1:5,1:5]
## DF.L1.C1 DF.L1.C2 DF.L1.C3 DF.L1.C4 DF.L1.C5
## 1 0 2.0 1.3 0.52 0.0094
## 2 0 2.2 2.0 0.69 0.5562
## 3 0 1.6 1.7 0.77 0.0000
## 4 0 1.3 1.7 0.25 0.0000
## 5 0 1.4 1.7 0.74 0.4888
注意特征结果中的零,它们在结果中是因为我们使用了线性整流器,低于零的值被删为零。
提取每一层的权重:
> p_load(dplyr)
> w1 <- as.matrix(h2o.weights(mt1, 1))
>
> # 为权重画个热力图
> w1 %>% t() %>% as.data.frame() %>%
+ mutate(row = 1:nrow(.)) %>%
+ reshape2::melt(id.vars = "row") %>%
+ ggplot(aes(variable, row, fill = value)) +
+ geom_tile() +
+ scale_fill_gradientn(colours = c("black", "white", "blue")) +
+ theme_bw() + theme(axis.text = element_blank()) +
+ labs(title = "Heatmap of Weights for Layer 1",
+ x = "Hidden Neuron",
+ y = "Input Variable")
权重热力图
6、手动预测新数据
使用h2o.predict()函数可以很轻松的通过模型完成预测,但手动逐步处理代码预测可以帮助我们完全搞清楚什么样的部分进入了模型以及它们是如何使用的。
- 我们已经对第一层提取了权重。但是,为了构建第一个隐藏层的神经元,我们还需要输入数据和偏差。因为需要在一整列当中加入一些常数项来构建深度特征(即使是偏差也要存储在一个向量中,每个隐藏神经元有一个偏差),我们复制偏差并把它转化到一个维数与输入数据相匹配的矩阵中。
> dim(use.train)
## [1] 7352 562
> names(use.train)[c(1:5, 557:562)]
## [1] "V1" "V2" "V3" "V4" "V5" "V557"
## [7] "V558" "V559" "V560" "V561" "outcome"
> # 输入的训练数据框
> d <- as.matrix(use.train[, -562])
>
> # 提取第一层的偏差
> b1 <- h2o.biases(mt1, 1)
> dim(b1)
## [1] 50 1
> class(b1)
## [1] "H2OFrame"
# 转换为矩阵
> b1 <- as.matrix(b1)
> head(b1)
## C1
## [1,] 0.291
## [2,] 0.414
## [3,] 0.232
## [4,] 0.064
## [5,] 0.150
## [6,] 0.512
> # 将每一层的偏差按行合并为矩阵
> b12 <- do.call(rbind, rep(list(t(b1)), nrow(d)))
> dim(b12)
## [1] 7352 50
> b12[1:5, 1:5]
## [,1] [,2] [,3] [,4] [,5]
## C1 0.29 0.41 0.23 0.064 0.15
## C1 0.29 0.41 0.23 0.064 0.15
## C1 0.29 0.41 0.23 0.064 0.15
## C1 0.29 0.41 0.23 0.064 0.15
## C1 0.29 0.41 0.23 0.064 0.15
- 将输入的数据标准化
> d.scaled <- apply(d, 2, scale)
> d.scaled[1:5, 1:5]
## V1 V2 V3 V4 V5
## [1,] 0.201 -0.064 -0.420 -0.87 -0.94
## [2,] 0.056 0.031 -0.254 -0.88 -0.92
## [3,] 0.074 -0.043 -0.076 -0.87 -0.91
## [4,] 0.067 -0.208 -0.250 -0.87 -0.94
## [5,] 0.030 0.028 -0.110 -0.88 -0.93
- 将之前提取的权重右乘标准化后的数据,加上偏差矩阵
> d.weighted <- d.scaled %*% t(w1) + b12
> d.weighted[1:5, 1:5]
## [,1] [,2] [,3] [,4] [,5]
## C1 -1.1 3.9 2.5 1.03 0.019
## C1 -1.8 4.3 4.0 1.39 1.112
## C1 -2.4 3.3 3.3 1.53 -0.225
## C1 -1.8 2.6 3.4 0.51 -0.072
## C1 -2.2 2.9 3.5 1.47 0.978
- 因为在隐藏层上引入了丢弃(丢弃比例为0.5),所以需要修正
> d.weighted <- d.weighted * (1 - 0.5)
> d.weighted[1:5, 1:5]
## [,1] [,2] [,3] [,4] [,5]
## C1 -0.57 2.0 1.3 0.52 0.0094
## C1 -0.90 2.2 2.0 0.69 0.5562
## C1 -1.19 1.6 1.7 0.77 -0.1124
## C1 -0.91 1.3 1.7 0.25 -0.0360
## C1 -1.12 1.4 1.7 0.74 0.4888
- 使用的是线性整流器,对每一列取零或高于零的值
pmax()函数返回输入值的最大值
> d.weighted.rectifier <- apply(d.weighted, 2, pmax, 0)
> d.weighted.rectifier[1:5, 1:5]
## [,1] [,2] [,3] [,4] [,5]
## C1 0 2.0 1.3 0.52 0.0094
## C1 0 2.2 2.0 0.69 0.5562
## C1 0 1.6 1.7 0.77 0.0000
## C1 0 1.3 1.7 0.25 0.0000
## C1 0 1.4 1.7 0.74 0.4888
- 将处理结果与H2O提取的特征进行比较,检查是否正确
> all.equal(
+ as.numeric(fea[,1]),
+ d.weighted.rectifier[,1],
+ check.attributes = F,
+ use.names = F,
+ # 对由浮点运算所引起的轻微数值差异给予一些容忍度
+ tolerance = 1e-04)
## [1] TRUE
返回为TRUE,说明结果完全一致。用相似的方式,能对下一层即输出层提取权重和偏差。如果我们有许多神经元的隐藏层,处理会非常相似,只要对每一层重复这些步骤来生成特征,而且总是建立在上一层的结果之上。
网友评论