Область глубокого обучения и компьютерного зрения развивается семимильными шагами. Алгоритмы распознавания изображений Google и Facebook уже пару лет побеждают людей. Вдобавок к этому теперь они могут идентифицировать кого-то, даже если лицо частично закрыто.

Область распознавания изображений вызвала интерес у ученых и исследователей еще в 1960-х годах. Однако проблема заключалась в том, что системы той эпохи, даже самые лучшие из суперкомпьютеров, не были достаточно мощными.

Исследователи рано поняли, что для хорошей машины распознавания изображений им необходимо построить более крупные и глубокие нейронные сети. Но чем больше и глубже нейронная сеть, тем больше ресурсов ей требуется как для хранения, так и для вычислений.

Однако с 2000-х все изменилось, поскольку наши процессоры стали быстрее, а память дешевле и больше. Все стало выглядеть лучше, когда такие компании, как Google/Alphabet, начали разрабатывать свои собственные модели, которые могут превзойти людей в задаче распознавания изображений.

В 2015 году Google выпустила Tensorflow, библиотеку с открытым исходным кодом для программирования потоков данных, которая может без проблем работать как на ЦП, так и на GPS. Выпуск библиотеки Tensorflow сделал этот чрезвычайно мощный инструмент доступным для всех, и с тех пор область распознавания изображений находится на подъеме.

Были выпущены и другие библиотеки, такие как MXnet, Keras, PyTorch, которые теперь предоставили всем множество возможностей для обучения и тестирования своих моделей глубокого обучения.

Мое путешествие:

В июле прошлого года я получил перелом колена, не мог ходить в офис и не мог работать на офисном ноутбуке. Мне стало чертовски скучно, и я решил кое-чему научиться у Kaggle, на котором сидел больше года, серьезно.

Я смотрел конкурсы знаний, и один из них привлек мое внимание. Это был конкурс на распознавание рукописных цифр. Это было совершенно за пределами моего опыта (или его отсутствия). Поэтому я решил, что за 3–4 недели, которые у меня есть, с ограниченной подвижностью, я опубликую достойный результат в таблице лидеров Kaggle.

Я скачал наборы данных и начал это опасное путешествие по знакомству с распознаванием изображений.

Прежде чем мы начнем, вот характеристики моего ПК

Это не мой компьютер (если кому интересно)

Core i5 6600k, разогнанный до 4,4 ГГц, 16 ГБ оперативной памяти ddr4, два gtx 980tis (теперь я обновился до 1080tis, чтобы сократить время обучения), RStudio с RBase с любой последней доступной версией. Я также опубликую свой код для шагов, которые я выполнил, так что, если вы хотите, вы можете продолжить и запустить их.

Итак начнем -

Загрузка наборов данных:

После того, как вы зарегистрировались и приняли условия конкурса, вы можете загрузить наборы данных. Наборы данных представлены в формате csv. Каждая строка имеет «Ярлык» и информацию о 784 пикселях (оттенки серого). Следовательно, каждое изображение имеет размер 28 * 28 пикселей. Наша цель — предсказать «метку», используя информацию о пикселях. Чтобы загрузить наборы данных -

train = read.csv("E:/Documents/R/KaggleData/Digit Recognizer/train.csv",stringsAsFactors = F) 
test = read.csv("E:/Documents/R/KaggleData/Digit Recognizer/test.csv",stringsAsFactors = F)

График данных:

Мы можем построить изображение из информации о пикселях, увидеть несколько из них и сравнить с их метками.

par(mfrow=c(4,3),pty='s',mar=c(1,1,1,1),xaxt='n',yaxt='n') 
all_img = array(dim=c(10,28*28)) 
for(di in 0:9) 
{ 
all_img[di+1,] = apply(train[train[,1]==di,-1],2,sum) 
all_img[di+1,] = all_img[di+1,]/max(all_img[di+1,])*255
z = array(all_img[di+1,],dim=c(28,28))
z = z[,28:1] ##right side up
image(1:28,1:28,z,main=di) 
}

Это вывод, который вы увидите -

Первая попытка — случайный лес:

Нам нужно с чего-то начать, чтобы получить базовый уровень, чтобы мы могли отслеживать наш прогресс. Я решил пойти со старым верным Random Forest. Моя модель Random Forest дала мне где-то около 96,7% точности и где-то 1000 в таблице лидеров.

library(randomForest)
set.seed(100)
model = randomForest(as.factor(label)~.,data = train,ntree=2000) pred = predict(model,newdata = train) 
importance(model) 
varImpPlot(model) 
table(pred,train$label) 
test$label = predict(model,newdata = test) 
submission_data = data.frame(ImageId=1:nrow(test),Label=test$label)

Код для построения тестовых изображений, который можно использовать для проверки подписей к изображению для различных моделей —

par(mfrow=c(4,3),pty='s',mar=c(1,1,1,1),xaxt='n',yaxt='n') 
all_img = array(dim=c(21,28*28))
for(dim in 1:10)
{
all_img[dim,] = apply(test[dim,-785],2,sum)
number<-test[dim,785]
z = array(all_img[dim,],dim=c(28,28))
z = z[,28:1] ##right side up 
image(1:28,1:28,z,main=number)
}

Это просто базовый код, который выполнит работу. Я оставлю это вам, ребята, чтобы побаловаться в области настройки гиперпараметров, и поверьте мне, что это отличный инструмент и актив, который даст вам представление о работе модели.

Получив предсказанные метки для тестового набора данных, вы можете построить несколько из них для сравнения с предсказанной меткой. Это даст вам представление о том, дает ли ваша модель правильные прогнозы или нет.

Здесь мы видим, что по крайней мере для первых 10 тестовых изображений наша модель дает правильный прогноз.

Вторая попытка — повышение градиента:

Второй выбранной мной моделью было Gradient Boosting, которое в большинстве случаев работает аналогично RF. Здесь снова я просто публикую код для запуска GBM, вы должны настроить и использовать параметры Hyper. Я получил немного более низкую точность по сравнению с RF, около 96,1%.

library(gbm) 
set.seed(100) 
model1 = gbm(as.factor(label)~.,data=train,n.trees = 2000, shrinkage = 0.11, distribution = "multinomial", 
interaction.depth = 7, bag.fraction = 0.9, cv.folds = 10, n.minobsinnode = 50) 
pred1 = predict(model1,newdata=train,type = "response")
pred1 = apply(pred1, 1, which.max) 
table(pred1,train$label) 
test$label = predict(model1,newdata = test) 
submission_data = data.frame(ImageId=1:nrow(test),Label=test$label)

Здесь мы видим, что по крайней мере для тестовых изображений с 11 по 20 наша модель дает правильный прогноз.

Третья попытка — нейронная сеть:

Затем я попробовал нейронную сеть, поскольку они являются моделями, на которых основаны модели глубокого обучения. Мне удалось получить точность, аналогичную RF.

library(neuralnet)
m = model.matrix(~.,data =train)
n = names(train) 
f = as.formula(paste("label ~", paste(n[!n %in% "label"], collapse = " + "))) 
model2 = neuralnet(f,data =m,hidden = 2, threshold =0.1, stepmax = 1000000,rep = 2, algorithm = "rprop+", err.fct = "sse", linear.output = FALSE) plot(model2) 
res = neuralnet::compute(model2,as.data.frame(train[,2:785])) 
pred2 = round(res$net.result) 
table(pred2,unlist(train))

Дополнительный совет. Каждая из приведенных выше моделей обеспечивает одинаковую точность тестовых данных. Что вы можете сделать, так это взять несколько из них, а затем сложить их. Сложенные модели дадут вам немного лучший прогноз по сравнению с базовыми моделями. Вы можете попробовать ряд базовых моделей, таких как Adaboost, Treebag, XGboost и т. д., помимо перечисленных выше.

Последняя попытка — сверточная нейронная сеть:

Попробовав руки на обычных моделях, мы готовы к работе с большими мальчиками. Пришло время для некоторой свертки. Дайте мне знать в комментариях, если вам нужна подробная статья о CNN, о том, как это работает и где все это можно использовать. Это был код, который дал мне точность более 99,9% и поставил меня в топ-20 таблицы лидеров.

library(DiagrammeR) 
library(mxnet) 
train.x = train[,-1] 
train.y = train[,1] 
train.x = t(train.x/255) 
test = t(test/255)

Теперь у нас есть данные в нужном нам формате, давайте приступим к созданию наших слоев

#CNN - RELU 
data = mx.symbol.Variable('data') 
# first conv 
conv1 = mx.symbol.Convolution(data=data, kernel=c(5,5), num_filter=20) 
tanh1 = mx.symbol.Activation(data=conv1, act_type="relu") 
pool1 = mx.symbol.Pooling(data=tanh1, pool_type="max", kernel = c(2,2), stride=c(2,2))
# second conv 
conv2 = mx.symbol.Convolution(data=pool1, kernel=c(5,5), num_filter=50) 
tanh2 = mx.symbol.Activation(data=conv2, act_type="relu") 
pool2 = mx.symbol.Pooling(data=tanh2, pool_type="max", kernel = c(2,2), stride=c(2,2))
# first fullc 
flatten = mx.symbol.Flatten(data=pool2) 
fc1 = mx.symbol.FullyConnected(data=flatten, num_hidden=500) 
tanh3 = mx.symbol.Activation(data=fc1, act_type="relu")
# second fullc 
fc2 = mx.symbol.FullyConnected(data=tanh3, num_hidden=10)
# loss 
lenet = mx.symbol.SoftmaxOutput(data=fc2) 
train.array = train.x 
dim(train.array) = c(28, 28, 1, ncol(train.x))
test.array = test dim(test.array) = c(28, 28, 1, ncol(test))
devices = list(mx.gpu(0)) 
mx.set.seed(0) 
tic = proc.time()

Теперь у нас есть слои в местах, давайте обучим нашу модель -

model = mx.model.FeedForward.create(lenet, X=train.array, y=train.y,kvstore = "device", ctx=devices, num.round=20, array.batch.size=100, learning.rate=0.05, wd=0.00001, momentum=0.9, eval.metric=mx.metric.accuracy, epoch.end.callback=mx.callback.log.train.metric(100)) 
preds = predict(model,train.array) 
pred.label = max.col(t(preds)) - 1 
table(pred.label) 
table(train.y,pred.label) 
preds1 = predict(model,test.array) 
pred1.label = max.col(t(preds1)) - 1 
table(pred1.label) 
table(test.y,pred1.label)

Куда пойти отсюда:

Теперь мы уже получили точность 99,9% на тестовых данных. Тем не менее, если вы видите таблицу лидеров Kaggle, вы увидите материалы со 100% точностью. Теперь этого можно добиться двумя способами:

  1. Чтобы сделать некоторые преобразования данных поезда, которые могут немного поворачивать изображения влево и вправо. Это поможет получить больше данных для обучения.
  2. Чтобы получить больше данных с веб-сайта MNIST, а затем обучиться на этих данных.

Вы можете попробовать оба этих подхода и посмотреть, где вы окажетесь в таблице лидеров для испытания распознавания рукописных цифр на Kaggle.

Надеюсь, вы все узнали что-то новое из этой статьи. Дайте нам знать в комментариях, помогло ли это вам начать работу, а также если у вас есть какие-либо вопросы. Всегда рад помочь.

Профиль автора

Правин Кумар Сингх В настоящее время я работаю менеджером по анализу рисков в Standard Chartered Bank. Имею 5-летний опыт работы в области анализа кредитных рисков. Мне нравится заниматься машинным обучением и расширенной аналитикой. Помимо работы я люблю путешествовать, играть в игры на своем компьютере и возиться с Arduino.

Первоначально опубликовано на https://www.skilledroots.com 7 июля 2019 г.