Введение
Давайте научим компьютер читать рукописные цифры с помощью классификатора случайного леса в R.
Мы будем использовать набор данных рукописных цифр MNIST для обучения и тестирования нашей модели. Вы можете самостоятельно загрузить набор данных с этой страницы GitHub: Файлы MNIST в формате PNG
Подготовьте данные
Хитрость в том, чтобы сделать эту работу, чтобы закодировать изображения в формате, который может понять наш компьютер. Каждое изображение состоит из матрицы значений RGB, поэтому мы можем использовать функцию png::readPNG()
из пакета {png} для считывания этих значений в матрицу R.
image <- png::readPNG("train/0/16585.png") dim(image) #> [1] 28 28
Каждое из изображений в наборе данных MNIST имеет размер 28 на 28 пикселей. Мы можем сгладить это по одному измерению, чтобы создать набор данных с 784 функциями.
# Flatten a Matrix image_flat <- as.vector( png::readPNG("train/0/16585.png") ) length(image_flat) #> [1] 784
Если бы изображения были намного больше, мы могли бы сначала изменить их размер, используя функцию imager::resize()
из пакета {imager}, чтобы предотвратить ошибку нехватки памяти.
Прыжки прямо в
Один из самых простых способов начать работу — создать новый проект в RStudio из системы управления версиями (т. е. GitHub).
После создания нашего проекта наш каталог должен выглядеть так.
list.files() #> [1] "download-and-convert-to-png.py" #> [2] "LICENSE" #> [3] "README.md" #> [4] "test" #> [5] "test.csv" #> [6] "train" #> [7] "train.csv"
В каталоге есть два файла: train.csv и test.csv с соответствующими путями к файлам и метками для каждого изображения. Мы можем прочитать это в нашем сеансе, используя readr::read_csv()
.
# Read the labels training_images <- readr::read_csv( "train.csv", col_types = "cf" ) testing_images <- readr::read_csv( "test.csv", col_types = "cf" ) head(training_images) #> # A tibble: 6 × 2 #> filepath label #> <chr> <fct> #> 1 train/0/16585.png 0 #> 2 train/0/24537.png 0 #> 3 train/0/25629.png 0 #> 4 train/0/20751.png 0 #> 5 train/0/34730.png 0 #> 6 train/0/15926.png 0
Обратите внимание, что я указал типы столбцов CSV-файла, используя параметр col_types = “cf”
(символ и фактор), потому что пакет {parsnip} требует, чтобы метка классификации была фактором.
Создание обучающего набора данных
Следующим шагом является создание обучающего набора данных. Мы можем создать пустую матрицу размером с набор данных и просмотреть каждый из файлов изображений, чтобы прочитать их как матрицу и сгладить до вектора.
# Prepare Training Data n <- length(training_images$filepath) training_matrix <- matrix( nrow = n, ncol = 28 * 28 ) for (i in 1:n){ training_matrix[i, ] <- as.vector( png::readPNG( training_images$filepath[i] ) ) }
Чтобы данные обучения работали с функцией подбора модели, мы преобразуем их во фрейм данных и связываем столбцы с метками.
training_data <- cbind( dplyr::select(training_images, label), as.data.frame( training_matrix ) )
Подгонка модели
Теперь мы можем подогнать модель к обучающим данным. Мы также можем сохранить результат в виде файла .rds, чтобы нам не приходилось повторять процесс обучения каждый раз, когда мы хотим делать прогнозы. Моему компьютеру требуется около 6 минут, чтобы подогнать модель.
# Model Fitting rf_fit <- parsnip::fit( parsnip::rand_forest( mode = "classification" ), data = training_data, formula = label ~ . ) # Save model saveRDS(rf_fit, "mnist_model_fit.rds") # rf_fit <- readRDS("mnist_model_fit.rds")
Создание набора данных для тестирования
Теперь мы можем использовать тот же процесс, что и с набором данных для обучения, для подготовки набора данных для тестирования.
# Prepare Test Data n_test <- length(testing_images$filepath) testing_matrix <- matrix( nrow = n, ncol = 28 * 28 ) for (i in 1:n_test){ testing_matrix[i, ] <- as.vector( png::readPNG( testing_images$filepath[i] ) ) } testing_data <- na.omit( cbind( dplyr::select(testing_images, label), as.data.frame( testing_matrix ) ) )
Я добавил na.omit()
вокруг обучающих данных, потому что некоторые значения отсутствовали, и это вызывало проблемы с функцией прогнозирования.
Делать предсказания
Давайте сделаем некоторые прогнозы и посмотрим, как мы это сделали! Мы можем использовать функцию предсказания из базы R и передать ей объект пастернака и набор тестовых данных. Функция metrics()
из пакета {yardstick} упрощает расчет показателей производительности.
# Model Evaluation predictions <- predict( rf_fit, testing_data ) final_result <- dplyr::bind_cols( predictions, dplyr::select( testing_data, label ) ) yardstick::metrics( final_result, truth = "label", estimate = ".pred_class" ) #> # A tibble: 2 × 3 #> .metric .estimator .estimate #> <chr> <chr> <dbl> #> 1 accuracy multiclass 0.97 #> 2 kap multiclass 0.967
Точность 97% процентов для нашей модели! Совсем неплохо. Так сколько же это было неправильно?
wrong_idx <- which(final_result$label != final_result$.pred_class) right_idx <- which(final_result$label == final_result$.pred_class) length(wrong_idx) #> [1] 300
300 из 10 000 тестовых изображений были помечены неправильно. Давайте нарисуем несколько случайных из них, чтобы понять, почему.
random_right <- sample(right_idx, 3) random_wrong <- sample(wrong_idx, 3) # Plot the mistakes ggplot2::ggplot( data = data.frame( x = seq(1, 10, length.out = 6), y = 1, images = testing_images$filepath[c(random_right, random_wrong)] ), ggplot2::aes( x, y, image = images, label = paste(final_result$.pred_class[c(random_right, random_wrong)]) ) ) + ggimage::geom_image( size=.10 ) + ggplot2::scale_y_continuous( limits = c(0, 2) ) + ggplot2::scale_x_continuous( limits = c(0, 11) ) + ggplot2::geom_text( size = 10, nudge_y = 0.25, color = c("green", "green", "green", "red", "red", "red") ) + ggplot2::theme_void()
Мы видим, что модель была близка, потому что числа, которые она пометила неправильно, трудно различить.
Заключение
Надеюсь, вам понравилась эта статья. Спасибо за чтение, и я желаю вам всего наилучшего! До следующего.
Код из статьи: mnist_classification.R