- PVSM.RU - https://www.pvsm.ru -

Deep learning и Caffe на новогодних праздниках

Мотивация

В данной статье вы познакомитесь c применением deep learning на практике. Будет использован фреймворк Caffe [1] на датасете SVHN [2].

Deep Learning. Этот buzz word уже давно звенит в ушах, но попробовать его на практике никак не удавалось. Подвернулся удобный случай это исправить! На новогодние праздники был назначен контест на kaggle [3] по распознаванию номеров домов в рамках курса по анализу изображений.

Была дана часть известной выборки SVHN [2], состоящей из 73257 изображений в обучающей и 26032 в тестовой (неразмеченной) выборках. Всего 10 классов для каждой цифры. Изображение имеет размер 32x32 в цветовом пространстве RGB. Как показывает бенчмарк [4], методы на основе deep learning показывают точность выше чем у человека — 1.92% против 2% ошибки!

У меня был опыт работы с алгоритмами машинного обучения на основе SVM и Naive Bayes. Применять уже известные методы скучно, поэтому решил использовать что-нибудь из deep learning, а именно сверточную нейронную сеть [5].

Выбор Caffe

Для работы с глубокими нейросетями существует много разных библиотек и фреймворков. Мои критерии были такими:

  1. туториалы,
  2. легкость освоения,
  3. легкость разворачивания,
  4. активное сообщество.

По ним отлично подошел Caffe:

  1. Хорошие туториалы есть на их сайте [1]. Отдельно рекомендую лекции из Caffe Summer Bootcamp [6]. Для быстрого старта можно почитать про основания нейронных сетей [7] и потом про Caffe [8].
  2. Для начала работы с Caffe даже не требуется язык программирования. Конфигурируется Caffe с помощью конфигурационных файлов, а запускается из командной строки.
  3. Для разворачивания есть chef-кукбук [9] и docker-образы [10].
  4. На гитхабе [11] ведется активная разработка, а в гугл-группе [12] можно задать вопрос по использованию фреймворка.

К тому же Caffe очень быстрый, т.к. использует GPU (хотя можно обойтись и CPU).

Установка

Изначально я поставил Caffe на свой ноутбук с помощью docker [10] и запускал его в режиме CPU. Обучение нейросети проходило очень медленно, но сравнивать было не с чем и казалось, что это нормально.

Затем наткнулся на 25$ купон амазона [13] и решил попробовать на AWS g2.2xlarge [14] с NVIDIA GPU и поддержкой CUDA. Там развернул Caffe с помощью Chef [9]. В итоге получилось в 41 раз быстрее — на CPU 100 итераций проходило за 290 сек, на GPU c CUDA за 7 cек!

Архитектура нейронной сети

Если в алгоритмах машинного обучения необходимо было формировать хороший вектор признаков, чтобы получить приемлемое качество, то в сверточных нейросетях этого делать не нужно. Главное — придумать хорошую архитектуру сети.

Введем следующие обозначения:

  • input — входной слой, обычно это пиксели изображения,
  • conv — слой свертки [1 [15]],
  • pool — слой подвыборки [2 [16]],
  • fully-conn — полносвязный слой [3 [17]],
  • output — выходной слой, выдает предполагаемый класс изображения.

Для задачи классификации изображений основной является следующая архитектура НС:

    input -> conv -> pool -> conv -> pool -> fully-conn -> fully-conn -> output

Количество (conv -> pool) слоев может быть разным, но обычно не меньше 2х. Количество fully-conn не меньше 1го.

В рамках данного контеста было перепробовано несколько архитектур. Наибольшую точность я получил со следующей:

    input -> conv -> pool -> conv -> pool -> conv -> pool -> fully-conn -> fully-conn -> output

Имплементация архитектуры на Caffe

Caffe конфигурируется с помощью Protobuf файлов. Имплементация архитектуры для контеста находится здесь [18]. Рассмотрим ключевые моменты конфигурации каждого слоя.

Входной слой (input)

Конфигурация входного слоя

name: "WinnyNet-F"
layers {
  name: "svhn-rgb"
  type: IMAGE_DATA
  top: "data"
  top: "label"
  image_data_param {
    source: "/home/deploy/opt/SVHN/train-rgb-b.txt"
    batch_size: 128
    shuffle: true
  }
  transform_param {
    mean_file: "/home/deploy/opt/SVHN/svhn/winny_net5/mean.binaryproto"
  }
  include: { phase: TRAIN }
}
layers {
  name: "svhn-rgb"
  type: IMAGE_DATA
  top: "data"
  top: "label"
  image_data_param {
    source: "/home/deploy/opt/SVHN/test-rgb-b.txt"
    batch_size: 120
  }
  transform_param {
    mean_file: "/home/deploy/opt/SVHN/svhn/winny_net5/mean.binaryproto"
  }
  include: { phase: TEST }
}
...

Первые 2 слоя (для обучающей и тестовой фазы) имеют type: IMAGE_DATA, т.е. сеть на вход принимает изображения. Изображения перечисляются в текстовом файле [19], где 1 колонка — путь к изображению, 2 колонка — класс. Путь к текстовому файлу указывается в атрибуте image_data_param.

Кроме изображений, можно подавать на вход данные из HDF5 [20], LevelDB и lmbd. Последние 2 варианта особенно актуальны, если критична скорость работы. Таким образом Caffe может работать с любыми данными, а не только изображениями. Проще всего работать с IMAGE_DATA, поэтому он и был выбран для контеста.

Также входные слои могут включать атрибут transform_param. В нем указываются трансформации, которым надо подвергнуть входные данные. Обычно, перед подачей изображений на нейросеть, их нормализуют или проводят более хитрые операции, например Local Contrast Normalization [21]. В данном случае был указан mean_file — вычитание «среднего» изображения [22] из входного.

В Caffe используется batch gradient descent [23]. Входной слой содержит параметр batch_size. За одну итерацию на вход нейросети поступает batch_size элементов выборки.

Слои свертки и подвыборки (conv, pool)

Конфигурация слоев свертки и подвыборки

    ...
    layers {
      bottom: "data"
      top: "conv1/5x5_s1"
      name: "conv1/5x5_s1"
      type: CONVOLUTION
      blobs_lr: 1
      blobs_lr: 2
      convolution_param {
        num_output: 64
        kernel_size: 5
        stride: 1
        pad: 2
        weight_filler {
          type: "xavier"
          std: 0.0001
        }
      }
    }
    layers {
      bottom: "conv1/5x5_s1"
      top: "conv1/5x5_s1"
      name: "conv1/relu_5x5"
      type: RELU
    }
    layers {
      bottom: "conv1/5x5_s1"
      top: "pool1/3x3_s2"
      name: "pool1/3x3_s2"
      type: POOLING
      pooling_param {
        pool: MAX
        kernel_size: 3
        stride: 2
      }
    }

    layers {
      bottom: "pool1/3x3_s2"
      top: "conv2/5x5_s1"
      name: "conv2/5x5_s1"
      type: CONVOLUTION
      blobs_lr: 1
      blobs_lr: 2
      convolution_param {
        num_output: 64
        kernel_size: 5
        stride: 1
        pad: 2
        weight_filler {
          type: "xavier"
          std: 0.01
        }
      }
    }
    layers {
      bottom: "conv2/5x5_s1"
      top: "conv2/5x5_s1"
      name: "conv2/relu_5x5"
      type: RELU
    }
    layers {
      bottom: "conv2/5x5_s1"
      top: "pool2/3x3_s2"
      name: "pool2/3x3_s2"
      type: POOLING
      pooling_param {
        pool: MAX
        kernel_size: 3
        stride: 2
      }
    }
    layers {
      bottom: "pool2/3x3_s2"
      top: "conv3/5x5_s1"
      name: "conv3/5x5_s1"
      type: CONVOLUTION
      blobs_lr: 1
      blobs_lr: 2
      convolution_param {
        num_output: 128
        kernel_size: 5
        stride: 1
        pad: 2
        weight_filler {
          type: "xavier"
          std: 0.01
        }
      }
    }
    layers {
      bottom: "conv3/5x5_s1"
      top: "conv3/5x5_s1"
      name: "conv3/relu_5x5"
      type: RELU
    }
    layers {
      bottom: "conv3/5x5_s1"
      top: "pool3/3x3_s2"
      name: "pool3/3x3_s2"
      type: POOLING
      pooling_param {
        pool: MAX
        kernel_size: 3
        stride: 2
      }
    }
    ...

3м является слой свертки с type: CONVOLUTION. Далее идет указание функции активации c type: RELU. 4м слоем является слой подвыборки с type: POOL. Далее 2 раза идет повторение conv, pool слоев, но с другими параметрами.

Подбор параметров для этих слоев является эмпирическим.

Полносвязные и выходные слои (fully-conn, output)

Конфигурация полносвязных и выходных слоев

    ...
    layers {
      bottom: "pool3/3x3_s2"
      top: "ip1/3072"
      name: "ip1/3072"
      type: INNER_PRODUCT
      blobs_lr: 1
      blobs_lr: 2
      inner_product_param {
        num_output: 3072
        weight_filler {
          type: "gaussian"
          std: 0.001
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layers {
      bottom: "ip1/3072"
      top: "ip1/3072"
      name: "ip1/relu_5x5"
      type: RELU
    }
    layers {
      bottom: "ip1/3072"
      top: "ip2/2048"
      name: "ip2/2048"
      type: INNER_PRODUCT
      blobs_lr: 1
      blobs_lr: 2
      inner_product_param {
        num_output: 2048
        weight_filler {
          type: "xavier"
          std: 0.001
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layers {
      bottom: "ip2/2048"
      top: "ip2/2048"
      name: "ip2/relu_5x5"
      type: RELU
    }
    layers {
      bottom: "ip2/2048"
      top: "ip3/10"
      name: "ip3/10"
      type: INNER_PRODUCT
      blobs_lr: 1
      blobs_lr: 2
      inner_product_param {
        num_output: 10
        weight_filler {
          type: "xavier"
          std: 0.1
        }
      }
    }
    layers {
      name: "accuracy"
      type: ACCURACY
      bottom: "ip3/10"
      bottom: "label"
      top: "accuracy"
      include: { phase: TEST }
    }
    layers {
      name: "loss"
      type: SOFTMAX_LOSS
      bottom: "ip3/10"
      bottom: "label"
      top: "loss"
    }

Полносвязный слой имеет type: INNER_PRODUCT. Выходной слой соединяется со слоем функцией потерь (type: SOFTMAX_LOSS) и слоем точности (type: ACCURACY). Слой точности срабатывает только в тестовой фазе и показывает процент верно классифицированных изображений в валидационной выборке.

Важным является указание атрибута weight_filler. Если он будет большим, то функция потерь (loss) может на начальных итерациях возвращать NaN. В таком случае надо уменьшить параметр std у атрибута weight_filler.

Параметры обучения

Конфигурация параметров обучения

    net: "/home/deploy/opt/SVHN/svhn/winny-f/winny_f_svhn.prototxt"
    test_iter: 1
    test_interval: 700
    base_lr: 0.01
    momentum: 0.9
    weight_decay: 0.004
    lr_policy: "inv"
    gamma: 0.0001
    power: 0.75
    solver_type: NESTEROV
    display: 100
    max_iter: 77000
    snapshot: 700
    snapshot_prefix: "/mnt/home/deploy/opt/SVHN/svhn/snapshots/winny_net/winny-F"
    solver_mode: GPU

Для получения хорошо обученной нейронной сети нужно задать параметры обучения. В Caffe параметры обучения устанавливаются через конфигурационный protobuf файл. Конфигурационный файл для данного контеста находится здесь [24]. Параметров много [25], рассмотрим некоторые из них подробнее:

  • net — путь к конфигурации архитектуры НС,
  • test_interval — количество итераций между которыми проводится тестирование НС (phase: test),
  • snapshot — количество итераций между которыми сохраняется состояние обучения НС.
    В Caffe можно приостанавливать и возобновлять обучение.

Обучение и тестирование

Чтобы запустить обучение НС, нужно выполнить команду caffe train с указанием конфигурационного файла [24], где заданы параметры обучения:

> caffe train --solver=/home/deploy/winny-f/winny_f_svhn_solver.prototxt

Краткий лог обучения

    .......................
    I0109 18:12:17.035543 12864 solver.cpp:160] Solving WinnyNet-F
    I0109 18:12:17.035578 12864 solver.cpp:247] Iteration 0, Testing net (#0)
    I0109 18:12:17.077910 12864 solver.cpp:298]     Test net output #0: accuracy = 0.0666667
    I0109 18:12:17.077997 12864 solver.cpp:298]     Test net output #1: loss = 2.3027 (* 1 = 2.3027 loss)
    I0109 18:12:17.107712 12864 solver.cpp:191] Iteration 0, loss = 2.30359
    I0109 18:12:17.107795 12864 solver.cpp:206]     Train net output #0: loss = 2.30359 (* 1 = 2.30359 loss)
    I0109 18:12:17.107817 12864 solver.cpp:516] Iteration 0, lr = 0.01
    .......................
    I0109 18:13:17.960325 12864 solver.cpp:247] Iteration 700, Testing net (#0)
    I0109 18:13:18.045385 12864 solver.cpp:298]     Test net output #0: accuracy = 0.841667
    I0109 18:13:18.045462 12864 solver.cpp:298]     Test net output #1: loss = 0.675567 (* 1 = 0.675567 loss)
    I0109 18:13:18.072872 12864 solver.cpp:191] Iteration 700, loss = 0.383181
    I0109 18:13:18.072949 12864 solver.cpp:206]     Train net output #0: loss = 0.383181 (* 1 = 0.383181 loss)
    .......................
    I0109 20:08:50.567730 26450 solver.cpp:247] Iteration 77000, Testing net (#0)
    I0109 20:08:50.610496 26450 solver.cpp:298]     Test net output #0: accuracy = 0.916667
    I0109 20:08:50.610571 26450 solver.cpp:298]     Test net output #1: loss = 0.734139 (* 1 = 0.734139 loss)
    I0109 20:08:50.640389 26450 solver.cpp:191] Iteration 77000, loss = 0.0050708
    I0109 20:08:50.640470 26450 solver.cpp:206]     Train net output #0: loss = 0.0050708 (* 1 = 0.0050708 loss)
    I0109 20:08:50.640494 26450 solver.cpp:516] Iteration 77000, lr = 0.00197406
    .......................
    I0109 20:52:32.236827 30453 solver.cpp:247] Iteration 103600, Testing net (#0)
    I0109 20:52:32.263108 30453 solver.cpp:298]     Test net output #0: accuracy = 0.883333
    I0109 20:52:32.263183 30453 solver.cpp:298]     Test net output #1: loss = 0.901031 (* 1 = 0.901031 loss)
    I0109 20:52:32.290550 30453 solver.cpp:191] Iteration 103600, loss = 0.00463345
    I0109 20:52:32.290627 30453 solver.cpp:206]     Train net output #0: loss = 0.00463345 (* 1 = 0.00463345 loss)
    I0109 20:52:32.290644 30453 solver.cpp:516] Iteration 103600, lr = 0.00161609

Одна эпоха — это (73257-120)/128 ~= 571 итерация. Чуть больше чем за 1 эпоху, на 700 итерации, точность сети на валидационной выборке 84%. На 134 эпохе точность уже 91%. На 181 эпохе — 88%. Возможно, если обучать сеть больше эпох, например 1000, точность стабилизируется и будет выше. В данном контесте обучение было остановлено на 181 эпохе.

В Caffe можно возобновлять обучение сети из snapshot добавляя параметр --snapshot:

> caffe train --solver=/home/deploy/winny-f/winny_f_svhn_solver.prototxt
              --snapshot=winny_net/winny-F_snapshot_77000.solverstate

Тестирование на неразмеченных изображениях

Для тестирования НС, необходимо создать deploy конфигурацию архитектуры сети [26]. В ней, в отличие от предыдущей конфигурации, отсутствует слой точности и упрощен входной слой.

Тестовая выборка, состоящая из 26032 изображений, идет без разметки. Поэтому, чтобы оценить точность на тестовой выборке контеста, нужно написать немного кода [27]. Caffe имеет интерфейсы для Питона и Матлаба [28].

Для тестирования сетей из разных эпох в Caffe есть снапшоты. Сеть 134 эпохи показала точность (Private Score в kaggle) 88.7%, а сеть 181 эпохи — 87.6%.

Идеи по повышению точности

Судя по магистерской диссертации [29], точность реализованной архитектуры может достигать 96%.

Как можно попробовать повысить полученную точность 88.7%?

  • Обучать сеть больше эпох. Например, в туториале по deep learning в facial keypoints detection [30] сеть обучали 1000 эпох.
  • Стандартизовать данные, чтобы математическое ожидание было равно 0 и дисперсия 1. Для этого потребуется использовать HDF5 или LevelDb/lmdb для хранения данных.
  • Поработать с параметрами обучения. Например, уменьшать learning_rate каждые 100 эпох.
  • Также можно попробовать использовать dropout слои, но для этого потребуется обучать сеть еще больше эпох, чем 1000 [31].
  • Датасет SVHN содержит дополнительные 600 000 размеченных изображений. В исследованиях их используют, но в рамках контеста их использование было бы нечестным. В этом случае можно сгенерировать новые данные на основе имеющихся.

Заключение

Реализованная сверточная нейронная сеть показала точность 88.9%. Это не лучший результат, но для первого блина неплохо. Есть потенциал для увеличения точности до 96%.

Благодаря фреймворку Caffe погружение в deep learning не вызывает больших трудностей. Достаточно создать пару конфигурационных файлов и одной командой запустить процесс обучения. Конечно, также нужны базовые познания в теории искусственных нейронных сетей. Эту и другую информацию для быстрого старта я постарался дать в этой статье.

Автор: lytr

Источник [32]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/obrabotka-izobrazhenij/80924

Ссылки в тексте:

[1] Caffe: http://caffe.berkeleyvision.org/

[2] SVHN: http://ufldl.stanford.edu/housenumbers/

[3] контест на kaggle: https://inclass.kaggle.com/c/svhn-mipt2

[4] бенчмарк: http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html#5356484e

[5] сверточную нейронную сеть: https://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D1%91%D1%80%D1%82%D0%BE%D1%87%D0%BD%D0%B0%D1%8F_%D0%BD%D0%B5%D0%B9%D1%80%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D0%B5%D1%82%D1%8C

[6] Caffe Summer Bootcamp: http://courses.cs.tau.ac.il/Caffe_workshop/Bootcamp/

[7] основания нейронных сетей: https://sites.google.com/site/deeplearningcvpr2014/

[8] про Caffe: https://docs.google.com/presentation/d/1UeKXVgRvvxg9OUdh_UiC5G71UMscNPlvArsWER41PsU/edit#slide=id.p

[9] chef-кукбук: https://github.com/robomakery/caffe-cookbook

[10] docker-образы: https://registry.hub.docker.com/u/tleyden5iwx/caffe-cpu-master/

[11] гитхабе: https://github.com/BVLC/caffe/

[12] гугл-группе: https://groups.google.com/forum/#!forum/caffe-users

[13] 25$ купон амазона: http://habrahabr.ru/post/243757/

[14] g2.2xlarge: http://aws.amazon.com/ru/ec2/instance-types/

[15] 1: http://ufldl.stanford.edu/tutorial/supervised/FeatureExtractionUsingConvolution/

[16] 2: http://ufldl.stanford.edu/tutorial/supervised/Pooling/

[17] 3: http://ufldl.stanford.edu/tutorial/supervised/MultiLayerNeuralNetworks/

[18] здесь: https://gist.github.com/sld/6ecd597d455b62ef5d36#file-train-test

[19] текстовом файле: https://gist.github.com/sld/5cb766398471c63903f3#file-train-rgb-b-txt

[20] HDF5: http://ru.wikipedia.org/wiki/Hierarchical_Data_Format

[21] Local Contrast Normalization: http://www.frontiersincomputervision.com/slides/FCV_Learn_LeCun.pdf

[22] «среднего» изображения: https://gist.github.com/sld/5cb766398471c63903f3#file-mean_image-py

[23] batch gradient descent: http://en.wikipedia.org/wiki/Stochastic_gradient_descent#Iterative_method

[24] здесь: https://gist.github.com/sld/6ecd597d455b62ef5d36#file-solver

[25] много: http://caffe.berkeleyvision.org/tutorial/solver.html

[26] deploy конфигурацию архитектуры сети: https://gist.github.com/sld/6ecd597d455b62ef5d36#file-deploy

[27] немного кода: https://gist.github.com/sld/5cb766398471c63903f3#file-net-py

[28] интерфейсы для Питона и Матлаба: http://caffe.berkeleyvision.org/tutorial/interfaces.html

[29] магистерской диссертации: http://www.cs.toronto.edu/%7Enitish/msc_thesis.pdf

[30] deep learning в facial keypoints detection: http://danielnouri.org/notes/2014/12/17/using-convolutional-neural-nets-to-detect-facial-keypoints-tutorial/#second-model-convolutions

[31] потребуется обучать сеть еще больше эпох, чем 1000: http://danielnouri.org/notes/2014/12/17/using-convolutional-neural-nets-to-detect-facial-keypoints-tutorial/#dropout

[32] Источник: http://habrahabr.ru/post/249089/