Создайте генератор, который перемешивает данные тренировок для Keras в R / Train модели Keras с компьютером lowspe c - PullRequest
0 голосов
/ 12 февраля 2020

У меня есть набор данных, аналогичный MNIST (200 000 строк 784 пикселя + 1 категориальный вывод (785 столбцов)), и я хочу обучить MLP и CNN, используя библиотеку Keras в R (в RStudio). В настоящее время я использую компьютер с 32 ГБ оперативной памяти с процессором Intel i7-8700 @ 3,2 ГГц, и у меня нет проблем при обучении этой нейронной сети с использованием функции fit() от Keras (время обучения ~ 4 минуты). Тем не менее, когда я выполняю тот же сценарий на своем ноутбуке (8 ГБ ОЗУ с Intel i5-6300 @ 2,3 ГГц), он не может сделать эпоху менее чем за 10 минут.

Я работаю профессором лаборатории в университете, и я беспокоюсь, что мои студенты не смогли запустить сценарий с этой базой данных на своих ноутбуках из-за недостатка вычислительных мощностей. Моя идея состояла в том, чтобы установить те же модели, используя генератор с функцией fit_generator(), и загружать часть набора данных вместо всего набора данных при каждом вызове функции генератора (чтобы использовать меньше памяти, чем при загрузке всего набора данных и в результате в более быстрой тренировке). Однако это приводит к неожиданным результатам. Точность, достигнутая функцией fit(), составляет ~ 98,8% на тренировке (120 000 строк) и ~ 98,4% на тесте (80 000 строк), но использование функции fit_generator() составляет ~ 1,05% на той же тренировке и ~ 1,01% на тот же тест. Я нашел связанные проблемы здесь , здесь , здесь и здесь , и кажется, что проблема в том, что fit_generator() не перетасовывает обучающие данные, и это приводит к тому, что сеть всегда обучается с одинаковыми пакетами (например, с одним и тем же градиентом при вычислении обратного распространения) и плохо представляет весь набор данных, что приводит к низкой точности. Я обучил модель, используя fit(), но установив для аргумента shuffle значение FALSE, и точность упала до 0,1%, что подтверждает, что перетасовка данных обучения имеет решающее значение для обучения модели.

Мои вопросы:

  • Является ли хорошей идеей использовать генератор, чтобы избежать проблем / сократить время обучения при использовании компьютера с более низкими характеристиками, или есть лучшее решение?
  • Я тренирую модели, используя весь набор данных, устанавливая аргумент steps_per_epoch равным ceil(nrow(train_dataset)/batch_size), поэтому он должен использовать те же данные при использовании fit() и fit_generator(), за исключением "перетасовки" part right?
  • В случае, если использование генератора, загружающего часть набора данных, является хорошим решением для обучения моделей с компьютерами с низкой скоростью c, как я могу эффективно перетасовать тренировочные данные, используя генератор?

Все генераторы, которые я видел, берут весь набор данных и производят серию выборок в каждом вызове или не перемешивают данные. Я создал генератор с кодом ниже. В качестве аргументов он принимает datafile файл с данными (данные обучения или тестовые данные), batch_size размер пакета, который будет производиться при каждом вызове, mlp для обработки данных для обучения MLP или CNN. , val для того, чтобы начать производить пакеты с другим индексом для данных проверки, и shuffle, чтобы указать, хотим ли мы переставлять данные или нет. Моя идея перетасовать данные состояла в том, чтобы создать случайный индекс и прочитать только одну строку файла для каждого числа в индексе (используя аргументы skip и nrow в read.table()). Это крайне неэффективно из-за нескольких вызовов 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)

Заранее спасибо!

1 Ответ

0 голосов
/ 13 февраля 2020

отвечая на мой собственный вопрос и спасибо @ user12728748 за комментарии, я изменяю генератор для чтения случайных выборок из файла:

data_generator <- function(datafile, batch_size = 128, 
                           mlp, val, 
                           shuffle = TRUE, validation_split = 0) {
  nrow_file <- py_eval(paste("sum(1 for line in open('", datafile, "'))", sep = '')) - 1
  skip <- 0 

  if (val) {
    nrow_file <- validation_split * nrow_file
  } else {
    nrow_file <- (1 - validation_split) * nrow_file
  }


  if (nrow_file > 0) {
    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)
        }

        # Create rows to skip
        if (val) {
          # in validation, skip training rows and validation rows that are not found in index
          rows_to_skip <- c(1:ifelse(validation_split > 0,((1 - validation_split) * nrow_file / validation_split),1),
                            setdiff(1:nrow_file, index) + (1 - validation_split) * nrow_file / validation_split)
        } else {
          # in training, skip validation rows and training rows that are not found in index
          rows_to_skip <- c(ifelse(validation_split > 0,
                                   nrow_file + 1, 0):ifelse(validation_split > 0,
                                                            nrow_file/(1 - validation_split), 0),
                            setdiff(1:nrow_file, index))
          if (rows_to_skip[1] == 0) rows_to_skip <- rows_to_skip[-1]
        }


        trData <- import("pandas")$read_csv(datafile, 
                                            skiprows = rows_to_skip, 
                                            sep = ";")

        # Upload the rows to train
        skip <<- skip + batch_size
        if (skip >= nrow_file) {
          skip <<- 0
        }
        # Build inputs and output
        y_train <- to_categorical(trData[,1], num_classes = 10)
        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))
        }
      }
  } else {
    NULL
  }
}

Я добавил аргумент validation_split, чтобы установить процент от данные обучения, которые мы хотим использовать в качестве проверки.

Однако использование генератора для обучения модели, по-видимому, не улучшает время обучения при использовании компьютера c с низкой скоростью, но использует ОЗУ почти на 4 ГБ (особенно при обучении небольшими партиями (~ 128 образцы)), и вы можете использовать компьютер для выполнения других задач при выполнении кода без сбоя программы.

Здесь я оставляю вам код для обучения и оценки модели MLP с помощью генератора:

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')
)

validation_split <- 0.2
history <- MLP_model %>% fit_generator(
  data_generator(traindatafile,
                 batch_size_train, mlp = TRUE, 
                 val = FALSE, validation_split = validation_split), 
  steps_per_epoch = round((1 - validation_split) * (py_eval(paste("sum(1 for line in open('", traindatafile, "'))", sep = '')) - 1) / (batch_size_train)),
  validation_data = data_generator(traindatafile,
                                   batch_size_train, mlp = TRUE, 
                                   val = TRUE, validation_split = validation_split),
  validation_steps = round((validation_split) * (py_eval(paste("sum(1 for line in open('", traindatafile, "'))", sep = '')) - 1) / (batch_size_train)),
  callbacks = c(early_stopping),
  epochs = 10)

MLP_metrics_train <- MLP_model %>% 
  evaluate_generator(data_generator(traindatafile,
                                    batch_size_eval, 
                                    mlp = TRUE, 
                                    val = FALSE,
                                    shuffle = FALSE),
                     steps = ceiling((py_eval(paste("sum(1 for line in open('", traindatafile, "'))", sep = '')) - 1) / (batch_size_eval)))
MLP_metrics_test <- MLP_model %>% 
  evaluate_generator(data_generator(testdatafile,
                                    batch_size_eval, 
                                    mlp = TRUE, 
                                    val = FALSE,
                                    shuffle = FALSE),
                     steps = ceiling((py_eval(paste("sum(1 for line in open('", testdatafile, "'))", sep = '')) - 1) / (batch_size_eval)))


y_pred_mlp <- MLP_model %>% 
  predict_generator(data_generator(testdatafile,
                                   batch_size_eval, 
                                   mlp = TRUE, 
                                   val = FALSE,
                                   shuffle = FALSE),
                    steps = ceiling((py_eval(paste("sum(1 for line in open('", testdatafile, "'))", sep = '')) - 1) / (batch_size_eval)))

Надеюсь, это кому-нибудь поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...