【发布时间】:2020-05-28 08:13:44
【问题描述】:
我有一个类似于 MNIST 的数据集(200.000 行 784 像素 + 1 个分类输出(785 列)),我想使用 R 中的 Keras 库(在 RStudio 中)训练 MLP 和 CNN。我目前正在使用一台具有 32 GB RAM 和英特尔 i7-8700 @3.2 GHz 的计算机,使用 Keras 的 fit() 函数训练这个神经网络时我没有任何问题(训练时间约 4 分钟)。然而,当我在我的笔记本电脑上执行相同的脚本(8 GB RAM,intel i5-6300 @2.3 GHz)时,它甚至不能在至少 10 分钟内完成一个 epoch。
我在大学担任实验室教授,我担心我的学生由于缺乏计算能力而无法在他们的笔记本电脑上运行带有该数据库的脚本。我的想法是使用带有fit_generator() 函数的生成器来拟合相同的模型,并在每次调用生成器函数时加载部分数据集而不是整个数据集(为了使用比加载整个数据集和结果更少的内存在更快的训练中)。但是,这会产生一些意想不到的结果。 fit() 函数在训练(120.000 行)中达到的准确率约为 98.8%,在测试(80.000 行)中达到 ~98.4%,但使用 fit_generator() 函数在相同训练中达到 ~1.05%,在测试中达到 ~1.01%同样的测试。我发现了相关问题here、here、here 和here,似乎问题在于fit_generator() 没有对训练数据进行洗牌,这导致网络总是用相同的批量(例如,计算反向传播时使用相同的梯度)并且不能很好地表示整个数据集,从而导致准确性差。我已经使用fit() 训练了模型,但是将shuffle 参数设置为FALSE,并且准确度下降到0.1%,因此它证实了对训练数据进行改组对于训练模型至关重要。
我的问题是:
- 在使用规格较低的计算机时使用生成器来避免问题/减少培训时间是个好主意还是有更好的解决方案?
- 我通过将
steps_per_epoch参数设置为等于ceil(nrow(train_dataset)/batch_size)来使用整个数据集训练模型,因此在使用fit()和fit_generator()时应该使用相同的数据,除了“洗牌”部分对吗? - 如果使用加载部分数据集的生成器是使用低规格计算机训练模型的良好解决方案,我如何使用生成器以有效的方式对训练数据进行混洗?
我见过的所有生成器都获取整个数据集,并在每次调用中生成一批样本,或者不打乱数据。我用下面的代码创建了一个生成器。它以datafile 带有数据(训练数据或测试数据)的文件作为参数,batch_size 在每次调用中生成的批处理大小,mlp 以处理数据以训练 MLP 或 CNN , val 以便开始为验证数据生成不同索引的批次,shuffle 表示我们是否要对数据进行混洗。我对数据进行洗牌的想法是创建一个随机索引,并为索引中的每个数字只读取文件的一行(使用read.table() 中的skip 和nrow 参数)。由于多次调用read.table(),这是非常低效的:
data_generator <- function(datafile, batch_size = 128, mlp = TRUE, val = TRUE, shuffle = TRUE) {
nrow_file <- R.utils::countLines(datafile) - 1
if (!val) {
skip <- 0
} else {
skip <- nrow_file / 2
}
function() {
# Calculate the rows to read in this epoch
rows_to_read <- batch_size
if (skip + batch_size > nrow_file) {
rows_to_read <- nrow_file - skip
}
if (shuffle) {
index <- sample (c(1:nrow_file), size=batch_size, replace =F)
} else {
index <- (skip + 1):(skip + rows_to_read)
}
# Load only the rows that we want to use in training
trData <- as.list(numeric(batch_size))
for(i in index) {
ii <- i - 1
trData[[which(i == index)]] <- read.table(datafile, sep = ";", header = TRUE,
skip = ii, nrows = 1)
}
trData <- do.call("rbind",trData)
# Upload the rows to train
skip <<- skip + batch_size
if (skip >= nrow_file) {
skip <<- 0
}
# Build inputs and output
y_train <- trData[,1]
x_train <- trData[,-1]
if (mlp) {
# Return data as is for mlp
list(data.matrix(x_train), data.matrix(y_train))
} else {
# Return data reshaped for CNN
list(array_reshape(data.matrix(x_train), c(nrow(x_train), 28, 28, 1)),
data.matrix(y_train))
}
}
}
我用来训练 MLP 模型(类似于 CNN)的代码是:
没有生成器
MLP_model <- keras_model_sequential()
MLP_model %>%
layer_dense(units = 500, activation = 'relu', input_shape = c(784),
kernel_regularizer = regularizer_l2(l = 0.0001),
bias_regularizer = regularizer_l2(l = 0.0001)) %>%
layer_dropout(rate = 0.4, seed = 150) %>%
layer_batch_normalization() %>%
layer_dense(units = 300, activation = 'relu',
kernel_regularizer = regularizer_l2(l = 0.001),
bias_regularizer = regularizer_l2(l = 0.001)) %>%
layer_dropout(rate = 0.3, seed = 150) %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = 'softmax',
kernel_regularizer = regularizer_l2(l = 0.001),
bias_regularizer = regularizer_l2(l = 0.001))
MLP_model %>% compile(
loss = loss_categorical_crossentropy,
optimizer = optimizer_adam(),
metrics = c('accuracy')
)
history <- MLP_model %>% fit(
x_train_mlp, y_train,
epochs = 20, batch_size = 124,
validation_split = 0.2,
shuffle = TRUE
)
带生成器:
MLP_model <- keras_model_sequential()
MLP_model %>%
layer_dense(units = 500, activation = 'relu', input_shape = c(784),
kernel_regularizer = regularizer_l2(l = 0.0001),
bias_regularizer = regularizer_l2(l = 0.0001)) %>%
layer_dropout(rate = 0.4, seed = 150) %>%
layer_batch_normalization() %>%
layer_dense(units = 300, activation = 'relu',
kernel_regularizer = regularizer_l2(l = 0.001),
bias_regularizer = regularizer_l2(l = 0.001)) %>%
layer_dropout(rate = 0.3, seed = 150) %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = 'softmax',
kernel_regularizer = regularizer_l2(l = 0.001),
bias_regularizer = regularizer_l2(l = 0.001))
MLP_model %>% compile(
loss = loss_categorical_crossentropy,
optimizer = optimizer_adam(),
metrics = c('accuracy')
)
history <- MLP_model %>% fit_generator(
data_generator(traindatafile,
batch_size = 128, mlp = TRUE, val = FALSE),
steps_per_epoch = round((R.utils::countLines(traindatafile)-1) / (128)),
epochs = 10)
提前致谢!
【问题讨论】:
-
如果您不介意额外的包依赖关系,您可以使用:r-bloggers.com/read-random-rows-from-a-huge-csv-file 或
DMwR2::sampleCSV(rdrr.io/cran/DMwR2/man/sampleCSV.html) 从大文件中采样行。 -
其他选择:
LaF::sample_lines(cran.r-project.org/web/packages/LaF/index.html);在 linux(或 macos)data.table::fread 上使用 shuf (gshuf):fread("shuf -n 5000 bigfile.csv");RevoScaleR::rxImport,或vroom::vroom_lines
标签: r performance keras rstudio generator