R语言与统计分析数据科学与R语言

64-R自动编码器模型识别异常数据

2020-03-31  本文已影响0人  wonphen

《深度学习精要(基于R语言)》学习笔记

1、数据导入与h2o初始化

> library(pacman)
> p_load(h2o, magrittr)
> 
> h2o.init(max_mem_size = "6G", nthreads = 4)
>
> # 使用智能手机活动记录仪数据
> 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]]
> 
> label <- read.table("activity_labels.txt")
> 
> h2o.train <- as.h2o(train.x, destination_frame = "h2o.train")
> h2o.test <- as.h2o(test.x, destination_frame = "h2o.test")
>
> dim(h2o.train)
## [1] 7352  561

2、训练自动编码器模型

一共561个变量,使用两个隐藏层的神经网络模型,每层100个神经元:

> m1 <- h2o.deeplearning(x = colnames(h2o.train), 
+     training_frame = h2o.train, 
+     validation_frame = h2o.test, 
+     activation = "Tanh", 
+     autoencoder = T, 
+     hidden = c(100, 100), 
+     epochs = 30, 
+     sparsity_beta = 0, 
+     input_dropout_ratio = 0, 
+     l1 = 0, 
+     l2 = 0)
> # 检查模型性能
> mse.1 <- tibble::tribble(~Model, ~Train_MSE, ~Test_MSE, 
+     "m1", m1@model$training_metrics@metrics$MSE, 
+     m1@model$validation_metrics@metrics$MSE)
> print(mse.1)
## # A tibble: 1 x 3
##   Model Train_MSE Test_MSE
##   <chr>     <dbl>    <dbl>
## 1 m1     0.000968  0.00105

模型在训练集和测试集上的MSE非常低,说明为了捕捉数据中的关键特征,模型足够复杂。训练数据和测试数据之间在模型性能中没有重大的差异。
查看模型在每种情况下的异常程度:

> p_load(ggplot2)
> err.1 <- h2o.anomaly(m1, h2o.train) %>% as.data.frame()
> 
> ggplot(err.1, aes(Reconstruction.MSE)) + 
+     geom_histogram(binwidth = 0.001, fill = "gray50") + 
+     geom_vline(xintercept = quantile(err.1[[1]], probs = 0.99), linetype = 2) + 
+     theme_bw()
模型异常程度

显而易见这里有一些情况比其余的情况更加异常,因为显示出了更高的误差率。
为了更深入的探索这些异常,我们把异常情况定义为前1%的误差率,然后提取那些情况的活动并且画出它们,从而找到那种情况异常。

> i.an <- err.1$Reconstruction.MSE >= quantile(err.1[[1]], probs = 0.99)
> 
> table(label$V2[train.y[i.an]]) %>% as.data.frame() %>% 
+     ggplot(aes(Var1, Freq)) + 
+     geom_bar(stat = "identity") + 
+     labs(x = "", y = "Freqency") + theme_bw() + 
+     theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1))
前1%异常

WALKING_DOWNSTAIRS下楼梯的异常更高。这样的工作也有助于识别模型通常会在哪里出现更多的问题。或许我们需要更多的传感器和额外的数据去更多地表示下楼梯的情况,才能够理解为什么它们会产生相对高的误差率。

3、模型微调

探索如何优化和微调自动编码器模型,去验证例如如何选取隐藏神经元的个数或者层数的问题。
使用caret包的createFolds()函数,手动实现h2o交叉验证:

> p_load(caret)
> 
> # 5折交叉验证
> folds <- createFolds(1:5000, k = 5)
> 
> # 创建超参数列表 因为笔记本性能有限,隐藏层数量控制在200以下
> hyperparams <- list(list(hidden = c(100), input_dr = c(0.2)), list(hidden = c(200), 
+     input_dr = c(0.2)), list(hidden = c(200), input_dr = c(0.2), hidden_dr = c(0.5)), 
+     list(hidden = c(200, 150), input_dr = c(0.2), hidden_dr = c(0.25, 0.25)), list(hidden = c(200, 
+         150), input_dr = c(0.2), hidden_dr = c(0.5, 0.25)))
> 
> # 运行模型,共5*5=25个模型
> fm <- lapply(hyperparams, function(params) {
+     lapply(folds, function(i) {
+         h2o.deeplearning(x = colnames(h2o.train), training_frame = h2o.train[-i, 
+             ], validation_frame = h2o.train[i, ], activation = "TanhWithDropout", 
+             autoencoder = T, hidden = params$hidden, epochs = 30, sparsity_beta = 0, 
+             input_dropout_ratio = params$input_dr, hidden_dropout_ratios = params$hidden_dr, 
+             l1 = 0, l2 = 0)
+     })
+ })

提取结果中的MSE,并合并为一个数据表格:

> fm.mse <- lapply(fm, function(m) {
+     sapply(m, h2o.mse, valid = T)
+ })
> 
> fm.mse <- data.table::data.table(Model = rep(paste0("M", 1:6), each = 5), MSE = unlist(fm.mse))
> 
> head(fm.mse)
##    Model         MSE
## 1:    M1 0.010982430
## 2:    M1 0.011488827
## 3:    M1 0.010528091
## 4:    M1 0.010218219
## 5:    M1 0.009022605
## 6:    M2 0.005756232
> ggplot(fm.mse, aes(Model, MSE)) + 
+     geom_boxplot() + 
+     stat_summary(fun.y = mean, geom = "point", col = "red") + 
+     theme_bw()
各模型MSE对比图

计算每个模型的平均MSE并从小到大排序:

> fm.mse[, .(Mean_MSE = mean(MSE)), by = Model][order(Mean_MSE)]
##    Model    Mean_MSE
## 1:    M2 0.006050593
## 2:    M3 0.006348499
## 3:    M4 0.010091912
## 4:    M1 0.010448034
## 5:    M6 0.010448034
## 6:    M5 0.011440269

从结果看,M2模型提供了最低交叉验证MSE。选好了最佳模型,我们能用所有的训练数据和实际的测试数据重新运行模型。

> fm.final <- h2o.deeplearning(x = colnames(h2o.train), 
+     training_frame = h2o.train, 
+     validation_frame = h2o.test, 
+     activation = "TanhWithDropout", 
+     autoencoder = T, 
+     hidden = hyperparams[[2]]$hidden, 
+     epochs = 30, sparsity_beta = 0, 
+     input_dropout_ratio = hyperparams[[2]]$input_dr, 
+     hidden_dropout_ratios = hyperparams[[2]]$hidden_dr, 
+     l1 = 0, 
+     l2 = 0)
>
> fm.final@model$training_metrics@metrics$MSE
> fm.final@model$validation_metrics@metrics$MSE
## [1] 0.005564679
## [1] 0.005696954

训练集和测试集的MSE相当接近,在某种程度上说,我们搜索到了一个合理的超参数集,这个模型现在是优化了的、验证过的并做好了使用的准备。
实践中,在使用不同模型或不同超参数集获得更好性能的可能性与运行和训练许多不同模型所用的时间之间,为了平衡这种取舍,通常是很困难的。有时候数据非常大,为了加速计算,使用所有数据的一个随机子集来探索最优模型是很有帮助的。
然而,我们使用的方法可以放大到更大的数据集上,只是会多花上一些时间。值得注意的是,使用更大的数据集可以从复杂模型中获益更多,因为提供了足够的数据去支持学习更复杂的结构。

上一篇 下一篇

猜你喜欢

热点阅读