Компьютерное зрение на основе обучения (1): контролируемое обучение и нейронные сети

TensorFlow алгоритм Нейронные сети компьютерное зрение

В последние годы волна искусственного интеллекта захлестнула весь технологический круг. Ведущие мировые технологические компании, такие как Google, Facebook, Microsoft и Baidu, обратили внимание на искусственный интеллект как на свое стратегическое направление в будущем. С постоянным появлением таких приложений, как распознавание лиц, вспомогательное вождение и AlphaGo, зрение, основанное на обучении, все больше и больше меняет нашу жизнь. Эта серия статей постепенно представит принципы алгоритмов зрения, лежащие в основе этих, казалось бы, волшебных систем.

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

Развитие компьютерного зрения

Что такое компьютерное зрение? Сначала давайте посмотрим на определение из Википедии:

Computer vision is an interdisciplinary field that deals with how computers can be made for gaining high-level understanding from digital images or videos.

Проще говоря, компьютерное зрение — это способность машин автоматически понимать изображения или видео.

Истоки компьютерного зрения можно проследить до 1966 года, когда Марвин Мински, известный эксперт Массачусетского технологического института в области искусственного интеллекта, дал своим студентам бакалавриата летнее задание: «Подключите камеру к компьютеру и заставьте компьютер описать то, что он видел». ". Хотя люди могут легко понимать изображения, оказывается, что заставить компьютеры понимать изображения сложнее, чем мы думали вначале.

Ранние исследования компьютерного зрения из-за вычислительных ресурсов и данных были сосредоточены на геометрии и рассуждениях. В 1990-е годы, в связи с непрерывным развитием компьютерного оборудования и постепенной популяризацией цифровых камер, компьютерное зрение вступило в период бурного развития. Основным прорывом в этот период стало появление различных искусственно созданных функций, таких как локальные функции, такие как SIFT и HOG. Эти функции устойчивы к масштабированию, вращению и т. д. по отношению к исходным пикселям, поэтому они широко используются, создавая визуальные приложения, такие как сшивание изображений, поиск изображений и трехмерная реконструкция. Еще одним большим прорывом стала популярность методов, основанных на статистике и машинном обучении. С постоянной популяризацией цифровых фотографий также появились крупномасштабные наборы данных. на основе Vision), который постепенно стал основным, потому что он может автоматически изучать параметры модели на основе большого количества данных.

С постоянным улучшением вычислительной мощности и созданием массивных данных в Интернете традиционные технологии машинного зрения, основанные на искусственных функциях и простых алгоритмах машинного обучения, таких как SVM/boosting, столкнулись с узким местом. Поэтому и промышленность, и научные круги изучают, как избежать утомительного проектирования искусственных признаков, одновременно повышая эффективность подбора модели, чтобы в дальнейшем использовать массивные данные. Глубокое обучение хорошо удовлетворяет эту потребность, поэтому оно широко используется в области видения. После 2010 года компьютерное зрение постепенно вошло в эру глубокого обучения. Знаковым событием является конкурс ImageNet 2012. В этом соревновании алгоритм, основанный на глубоком обучении, значительно превзошел хорошо разработанный традиционный алгоритм, потряс все академическое сообщество, а затем стимулировал применение глубокого обучения в других областях. Конкурс также рассматривается как знаковое событие в возрождении глубокого обучения во всей области искусственного интеллекта.

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

нейронная сеть

Нейронная сеть (NN), которая представляет собой просто сеть нейронов, является самой ранней и самой простой моделью глубокого обучения. Многие другие более сложные алгоритмы, такие как сверточные нейронные сети, и многие концепции глубокого обучения с подкреплением основаны на нейронных сетях. Поэтому в этой статье мы сначала представим принцип работы нейронной сети. Чтобы понять нейронные сети, нам нужно сначала понять, что такое нейроны.

Нейроны и персептроны

Нейрон — это наименьшая единица нейронной сети. Каждый нейрон отображает несколько входов в один выход. Как показано, выход нейрона представляет собой взвешенную сумму входов плюс смещение, которое затем передается через функцию активации. В частности, это может быть выражено как:

y=φ(n∑i(wi∗xi)+b)=φ(w⋅x+b)

Функция активации φ имеет различные формы. Если используется ступенчатая функция, нейрон эквивалентен линейному классификатору:

y={1if w⋅x+b>00else

Этот классификатор исторически был известен как персептрон.

Многослойная нейронная сеть

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

Благодаря наличию нелинейных функций активации многослойные нейронные сети могут соответствовать нелинейным функциям. По историческим причинам многослойные нейронные сети также известны как многослойные персептроны (MLP).

Нейронные сети могут соответствовать нелинейным функциям. Но чтобы соответствовать различным нелинейным функциям, нужно ли нам разрабатывать различные нелинейные функции активации и сетевые структуры? Ответ - не надо.universal approximation theoremБыло показано, что нейронные сети с прямой связью представляют собой общую основу аппроксимации. Проще говоря, для часто используемых функций активации, таких как сигмовидная и relu, даже нейронная сеть только с одним скрытым слоем, если нейронов достаточно, она может бесконечно аппроксимировать любую непрерывную функцию. На практике неглубоким нейронным сетям может потребоваться слишком много нейронов для аппроксимации сложных нелинейных функций, что усложняет обучение и влияет на эффективность обобщения. Поэтому мы часто используем более глубокие модели, чтобы уменьшить количество необходимых нейронов и улучшить способность сети к обобщению.

Основные концепции машинного обучения

Глубокие нейронные сети — это класс алгоритмов глубокого обучения, который является частным случаем машинного обучения. Поэтому в этом разделе мы вводим основные понятия, связанные с обучением моделей и их реализацией в Tensorflow в рамках общей структуры машинного обучения. Связанные понятия применяются к алгоритмам машинного обучения, включая NN.

Часто задаваемые вопросы о машинном обучении

Общие проблемы машинного обучения можно разделить на 4 категории проблем:
- Контролируемое обучение
- Неконтролируемое обучение
- Полуконтролируемое обучение
- Обучение с подкреплением

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

контролируемое обучение

В обучении с учителем, учитывая N обучающих выборок {(x1,y1),...,(xN,yN)}, наша цель состоит в том, чтобы получить функцию от входа к выходу: f:X→Y. На практике обычно функцию f напрямую не оптимизируют, а подбирают набор параметризованных функций fθ в соответствии с конкретной ситуацией задачи и преобразуют функцию оптимизации fθ в параметр оптимизации θ.

Общие проблемы классификации и проблемы регрессии являются частным случаем обучения с учителем. Линейные классификаторы, глубокие нейронные сети и другие модели — все это параметризованные функции, предназначенные для решения этих задач. Для простоты возьмем в качестве примера линейный классификатор y=fθ(x)=wTx+b, а оптимизируемым параметром θ будет (w,b).

функция потерь

Чтобы измерить, насколько хороша функция, нам нужен объективный стандарт. В обучении с учителем этот критерий обычно представляет собой функцию потерь L:Y×Y→R. Для обучающей выборки (xi,yi) результат предсказания модели равен y, а соответствующие потери равны L(yi,y). Чем меньше потери, тем точнее предсказанная функция. На практике необходимо выбирать различные функции потерь в соответствии с характеристиками проблемы.

В задаче с двумя классами обычно используемая логистическая регрессия использует сигмоиду + ​​перекрестный вход в качестве потерь. Для общих потерь тензорный поток предоставляет соответствующие функции.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=y)__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Для задач множественной классификации, как показано на рисунке выше, мы можем расширить линейный классификатор бинарной классификации до N линейных уравнений: yi=∑jWi, jxj+bi. Затем он нормализуется с помощью softmax(x)i=exp(xi)∑jexp(xj), и нормализованный результат используется как вероятность каждого класса. Поэтому мультиклассификация обычно использует softmax + перекрестную энтропию в качестве функции потерь.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__loss = tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits)__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Можно доказать, что для задачи с двумя классами использование сигмовидного перекрестного входа и мягкого максимального перекрестного входа в качестве потерь теоретически полностью эквивалентно. Кроме того, на практике обычно принято напрямую вычислять кросс-энтропию softmax вместо того, чтобы сначала вычислять softmax, а затем вычислять cross-entory, что в основном рассматривается с точки зрения численной устойчивости.

Минимизация потерь и регуляризация

После определения функции потерь задача обучения с учителем может быть преобразована в задачу минимизации экспериментальных потерь 1n∑ni=1L(yi,f(xi)) . На практике, чтобы обеспечить подгонку модели, сложность функции f иногда бывает относительно высокой. Как показано в самой правой ситуации на самом рисунке, если количество обучающих выборок невелико или метка неверна, если экспериментальные потери минимизируются напрямую без ограничения f, модель склонна к переоснащению.

Поэтому, когда количество выборок невелико или качество маркировки невысокое, необходимо добавить дополнительный регуляризатор, чтобы обеспечить обобщающую способность модели. На практике разные обычные условия могут быть выбраны в соответствии с различными потребностями. В нейронных сетях чаще встречается регулярный член нормы l2: R(w)=12∑ni=1w2i

В тензорном потоке обычно есть два способа добавить обычные термины. Один из них — реализовать соответствующие потери регуляризации в соответствии с потребностями, а затем добавить их к другим потерям для оптимизации. Этот метод может реализовать более сложные регуляризаторы.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__weight_decay = tf.multiply(tf.nn.l2_loss(weights), wd, name='weight_loss')__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

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

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__tf.contrib.layers.apply_regularization(tf.contrib.layers.l2_regularizer(wd), weights)
tf.losses.get_regularization_losses()__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Градиентный спуск и обратное распространение

После определения функции потерь и регулярного члена окончательные регуляризованные потери равны: loss(θ)=1n∑ni=1L(yi,f(xi))+αR(w)

С помощью функции потерь соответствующие параметры могут быть оптимизированы и решены стандартными алгоритмами градиентного спуска. Например, w в линейном классификаторе может быть итеративно обновлен по следующей формуле:

Установив соответствующую скорость обучения, параметры будут постепенно сходиться к локальному/глобальному оптимальному решению.

Обратное распространение можно рассматривать как обобщение градиентного спуска в нейронных сетях, которое также минимизирует функцию потерь, вычисляет градиент параметров относительно потерь, а затем итеративно обновляет параметры. Конкретный вывод здесь опущен из соображений экономии места. В тензорном потоке вам нужно только указать функцию потерь и размер шага (скорость обучения), и оптимизатор может автоматически помочь нам завершить процесс градиентного спуска/обратного распространения:

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

OCR в действии

Наконец, в этой статье показано, как реализовать классификатор Softmax и классификатор MLP с Tensorflow на примере OCR. Экспериментальный набор данных использует хорошо известный набор данных распознавания цифр MNIST. Набор данных содержит 60 000 обучающих изображений и 10 000 тестовых изображений. Каждое изображение в наборе данных представляет число от 0 до 9, а размер изображения составляет 28×28.

Классификатор Софтмакс

Сначала мы реализуем простой классификатор Softmax. Поскольку процесс запуска построенной сети в Tensorflow сложен, для повышения эффективности разработки и возможности повторного использования кода мы разделяем код на три основных модуля, а именно модуль Dataset, модуль Net и модуль Solver.

Структура модели

Для каждого класса Net нам нужно реализовать три функции:
- inference
Мы определяем основную структуру сети в функции вывода. В тензорном потоке переменные представлены tf.Variable. Поскольку классификатор Softmax является выпуклой функцией, любая инициализация может гарантировать глобальное оптимальное решение, поэтому мы можем простоWиbИнициализирован в 0. Классификатор Softmax можно просто умножить на матрицуy = tf.matmul(data, W) + bза которым следует одинtf.nn.softmaxреализация функции.

  • loss
    Согласно предыдущему введению, для обеспечения численной стабильности мы напрямую используем прямой расчетtf.nn.softmax_cross_entropy_with_logitsПуть.

  • metric
    После обучения модели нам необходимо проверить производительность модели на проверочном или тестовом наборе. Когда тестовый набор относительно велик, мы не можем получить результаты модели на всем тестовом наборе за один раз.Нам нужно разделить тестовый набор на небольшие партии, протестировать каждую партию, а затем агрегировать результаты каждой партии. С этой целью tensorflow предоставляет модуль tf.metrics, который может автоматически выполнять оценку каждого пакета и суммировать все оценки. В этом примере мы решаем задачу классификации, поэтому мы можем использоватьtf.metrics.accuracyРассчитать точность классификации.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__class Softmax(Net):

def __init__(self, **kwargs):
self.output_dim = kwargs.get('output_dim', 1)
return

def inference(self, data):

feature_dim = data.get_shape()[1].value

with tf.name_scope('weights'):
W = tf.Variable(tf.zeros([feature_dim, self.output_dim]))
with tf.name_scope('biases'):
b = tf.Variable(tf.zeros([self.output_dim]), name='bias')
with tf.name_scope('y'):
y = tf.matmul(data, W) + b
with tf.name_scope('probs'):
probs = tf.nn.softmax(y)

return {'logits': y, 'probs': probs}

def loss(self, layers, labels):
logits = layers['logits']

with tf.variable_scope('loss'):
loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits))
return loss

def metric(self, layers, labels):
probs = layers['probs']
with tf.variable_scope('metric'):
metric, update_op = tf.metrics.accuracy(
labels=tf.argmax(labels, 1), predictions=tf.argmax(probs, 1))
return {'update': update_op, 'accuracy': metric}__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Dataset

In versions of TensorFlow before 1.2, we recommended using multi-threaded,
queue-based input pipelines for performance. Beginning with TensorFlow 1.2, however, we recommend using the tf.contrib.data module instead.

Начиная с Tensorflow1.2, Tensorflow предоставляет новый API на основе tf.contrib.data. По сравнению с исходным API на основе QuequRunner и Coordinator структура кода намного проще. Поэтому мы приняли новый API в классе Dataset для реализации чтения данных.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__class MNIST(Dataset):

def __init__(self, **kwargs):
self.data_dir = kwargs.get('data_dir', None)
self.split = kwargs.get('split', 'train')

self.count = kwargs.get('count', None)
self.buffer_size = kwargs.get('buffer_size', 10000)
self.batch_size = kwargs.get('batch_size', 50)

if self.split not in ['train', 'validation', 'test']:
raise ValueError('unsupported dataset mode!')

# download mnist data
images, labels = load_dataset(self.data_dir, self.split)

# build dataset
dataset = tf.contrib.data.Dataset.from_tensor_slices((images, labels))
if self.buffer_size is not None:
dataset = dataset.shuffle(buffer_size=self.buffer_size)
dataset = dataset.repeat(self.count)
dataset = dataset.batch(self.batch_size)

with tf.name_scope('input'):
self._iterator = dataset.make_one_shot_iterator()
self._batch = self._iterator.get_next()

def batch(self):
return self._batch

def shape(self):
return self._iterator.output_shapes__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Сначала мы читаем набор данных mnist в формате numpy.array.images, labels. затем пройтиtf.contrib.data.Dataset.from_tensor_slicesПреобразуйте его в формат tf.contrib.data.Dataset.
После этого мы можем установить количество обходов набора данных (Нет означает бесконечность), размер пакета и необходимость перемешивания набора данных. Наконец, возьмем самый простойmake_one_shot_iterator()иget_next(), получить пакет основных данных сети. По умолчанию каждый пакет содержит 50 изображений и соответствующие им метки.

Solver

Наконец, мы вводим класс Sover. Класс Solver в основном содержит пять функций:
- build_optimizer
Поскольку сеть относительно проста, здесь мы выбираем самый простой алгоритм стохастического градиентного спуска.tf.train.GradientDescentOptimizerи использует фиксированную скорость обучения.
- build_train_net, build_test_net
Эти две функции похожи тем, что они связывают данные в наборе данных с сетевой структурой в Сети. В конце мы звонимtf.summary.scalarДобавьте потерю к итогу. tensorflow предоставляет мощный модуль визуализации tensorboard, который может легко визуализировать переменные в сводке.
- train_net
В начале работы train_net мы завершили инициализацию модулей Graph, Saver, summary и других. затем пройтиsummary_writer.add_graph(tf.get_default_graph()), выведите структуру сети в сводку.
после инициализацииtf.Session(), и пройтиsession.runВыполните соответствующую операцию. В тензорном потоке используется символьный режим программирования, процесс создания графа только завершает композицию, а не выполняет реальных операций с данными. Когда соответствующая операция выполняется в сеансе, базовые данные фактически обрабатываются.
- test_net
Подобно train_net, test_net в основном завершает инициализацию различных модулей, а затем считывает последнюю модель, записанную в файле контрольной точки в файле каталога модели, и тестирует ее в тестовом наборе.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__class BasicSolver(Solver):

def __init__(self, dataset, net, **kwargs):
self.learning_rate = float(kwargs.get('learning_rate', 0.5))
self.max_steps = int(kwargs.get('max_steps', 2000))

self.summary_iter = int(kwargs.get('summary_iter', 100))
self.summary_dir = kwargs.get('summary_dir', 'summary')
self.snapshot_iter = int(kwargs.get('snapshot_iter', 100000))
self.snapshot_dir = kwargs.get('snapshot_dir', 'cache')

self.dataset = dataset
self.net = net

def build_optimizer(self):
with tf.variable_scope('optimizer'):
train_op = tf.train.GradientDescentOptimizer(self.learning_rate).minimize(
self.loss)
return train_op

def build_train_net(self):
data, labels = self.dataset.batch()

self.layers = self.net.inference(data)
self.loss = self.net.loss(self.layers, labels)
self.train_op = self.build_optimizer()

for loss_layer in tf.get_collection('losses') + [self.loss]:
tf.summary.scalar(loss_layer.op.name, loss_layer)

def build_test_net(self):
data, labels = self.dataset.batch()

self.layers = self.net.inference(data)
self.metrics = self.net.metric(self.layers, labels)
self.update_op = self.metrics.pop('update')

for key, value in self.metrics.iteritems():
tf.summary.scalar(key, value)

def train(self):
self.build_train_net()
saver = tf.train.Saver(tf.trainable_variables())
init_op = tf.global_variables_initializer()
summary_op = tf.summary.merge_all()
summary_writer = tf.summary.FileWriter(
os.path.join(self.summary_dir, 'train'))
summary_writer.add_graph(tf.get_default_graph())

with tf.Session() as sess:
sess.run(init_op)

for step in xrange(1, self.max_steps + 1):
start_time = time.time()
sess.run(self.train_op)
duration = time.time() - start_time

if step % self.summary_iter == 0:
summary, loss = sess.run([summary_op, self.loss])

summary_writer.add_summary(summary, step)

examples_per_sec = self.dataset.batch_size / duration
format_str = ('step %6d: loss = %.4f (%.1f examples/sec)')
print(format_str % (step, loss, examples_per_sec))

sys.stdout.flush()

if (step % self.snapshot_iter == 0) or (step == self.max_steps):
saver.save(sess, self.snapshot_dir + '/model.ckpt', global_step=step)

def test(self):
self.build_test_net()
saver = tf.train.Saver()
init_op = [
tf.global_variables_initializer(),
tf.local_variables_initializer()
]
summary_op = tf.summary.merge_all()
summary_writer = tf.summary.FileWriter(
os.path.join(self.summary_dir, 'test'))
summary_writer.add_graph(tf.get_default_graph())

with tf.Session() as sess:
sess.run(init_op)

checkpoint = tf.train.latest_checkpoint(self.snapshot_dir)
if not os.path.isfile(checkpoint + '.index'):
print("[error]: can't find checkpoint file: {}".format(checkpoint))
sys.exit(0)
else:
print("load checkpoint file: {}".format(checkpoint))
num_iter = int(checkpoint.split('-')[-1])

saver.restore(sess, checkpoint)

while True:
try:
sess.run(self.update_op)
except tf.errors.OutOfRangeError:
results = sess.run([summary_op] + self.metrics.values())
summary = results[0]
metrics = results[1:]
for key, metric in zip(self.metrics.keys(), metrics):
print("{}: {}".format(key, metric))
summary_writer.add_summary(summary, num_iter)
break__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

На следующем рисунке представлена ​​визуализированная структура сети в tensorboad и статистика потерь. Как видите, tensorboad обеспечивает хорошую визуальную поддержку нашего анализа.

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

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__step 100: loss = 0.3134 (116833.0 examples/sec)
step 200: loss = 0.4800 (113359.6 examples/sec)
step 300: loss = 0.3528 (114410.9 examples/sec)
step 400: loss = 0.2597 (105278.7 examples/sec)
step 500: loss = 0.3301 (106834.0 examples/sec)
step 600: loss = 0.4013 (115992.9 examples/sec)
step 700: loss = 0.3428 (112871.5 examples/sec)
step 800: loss = 0.3181 (113913.7 examples/sec)
step 900: loss = 0.1850 (123507.2 examples/sec)
step 1000: loss = 0.0863 (125653.2 examples/sec)
step 1100: loss = 0.2726 (105703.2 examples/sec)
step 1200: loss = 0.4849 (115736.9 examples/sec)
step 1300: loss = 0.2986 (100582.8 examples/sec)
step 1400: loss = 0.2994 (103973.8 examples/sec)
step 1500: loss = 0.2626 (102500.1 examples/sec)
step 1600: loss = 0.0996 (107712.0 examples/sec)
step 1700: loss = 0.2523 (114912.4 examples/sec)
step 1800: loss = 0.3264 (105703.2 examples/sec)
step 1900: loss = 0.2911 (114975.4 examples/sec)
step 2000: loss = 0.2648 (132312.4 examples/sec)

accuracy: 0.919499993324__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Как видите, простая линейная модель может достигать точности 92%. Мы предполагаем, что задача распознавания цифр не должна быть линейно разделимой, поэтому использование более сложного нелинейного классификатора должно дать лучшие результаты.

Классификатор МЛП

Благодаря нашей модульной конструкции каждый модуль в основном отделен напрямую. Так что очень легко заменить классификатор Softmax на MLP, нам просто нужно заново реализовать слой Net.

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__class MLP(Net):

def __init__(self, **kwargs):
self.output_dim = kwargs.get('output_dim', 1)
return

def inference(self, data):
with tf.variable_scope('hidden1'):
hidden1 = linear_relu(data, 128)

with tf.variable_scope('hidden2'):
hidden2 = linear_relu(hidden1, 32)

with tf.variable_scope('softmax_linear'):
y = linear(hidden2, self.output_dim)

probs = tf.nn.softmax(y)
return {'logits': y, 'probs': probs}

def loss(self, layers, labels):
logits = layers['logits']

with tf.variable_scope('loss'):
loss = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits))
return loss

def metric(self, layers, labels):
probs = layers['probs']
with tf.variable_scope('metric'):
metric, update_op = tf.metrics.accuracy(
labels=tf.argmax(labels, 1), predictions=tf.argmax(probs, 1))
return {'update': update_op, 'accuracy': metric}


def linear_relu(x, size, wd=0):
return tf.nn.relu(linear(x, size, wd), name=tf.get_default_graph().get_name_scope())

def linear(x, size, wd=0):

weights = tf.get_variable(
name='weights',
shape=[x.get_shape()[1], size],
initializer=tf.contrib.layers.xavier_initializer())
biases = tf.get_variable(
'biases', shape=[size], initializer=tf.constant_initializer(0.0))
out = tf.matmul(x, weights) + biases

if wd != 0:
weight_decay = tf.multiply(tf.nn.l2_loss(weights), wd, name='weight_loss')
tf.add_to_collection('losses', weight_decay)

return out
__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Чтобы продемонстрировать эффект MLP, мы строим нейронную сеть с двумя скрытыми слоями. Структура кода примерно такая же, как у классификации Softmax, поэтому я не буду ее слишком подробно объяснять. Поскольку линейный слой появляется в сети много раз, мы абстрагируем его в повторно используемую функцию. Кроме того, чтобы улучшить визуализацию графика в tensorboad, мы будем связывать переменные и операции черезwith tf.variable_scope('hidden1'):Размещается под той же переменной variable_scope. Таким образом, все связанные переменные и операции будут сведены в расширяемый узел в tensorboad, что обеспечит лучшую визуализацию.

Окончательные результаты сети и текущие результаты следующие:

__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__step 100: loss = 0.4675 (49113.6 examples/sec)
step 200: loss = 0.2348 (53200.2 examples/sec)
step 300: loss = 0.1858 (51922.6 examples/sec)
step 400: loss = 0.1935 (49554.6 examples/sec)
step 500: loss = 0.2634 (51552.4 examples/sec)
step 600: loss = 0.1800 (51871.2 examples/sec)
step 700: loss = 0.0524 (51225.0 examples/sec)
step 800: loss = 0.1634 (50606.9 examples/sec)
step 900: loss = 0.1549 (56239.0 examples/sec)
step 1000: loss = 0.1026 (54755.9 examples/sec)
step 1100: loss = 0.0928 (51871.2 examples/sec)
step 1200: loss = 0.0293 (50864.7 examples/sec)
step 1300: loss = 0.1918 (54528.1 examples/sec)
step 1400: loss = 0.1001 (48725.7 examples/sec)
step 1500: loss = 0.1263 (50003.6 examples/sec)
step 1600: loss = 0.0956 (54176.0 examples/sec)
step 1700: loss = 0.1012 (52025.6 examples/sec)
step 1800: loss = 0.3386 (53471.5 examples/sec)
step 1900: loss = 0.1626 (54641.8 examples/sec)
step 2000: loss = 0.0215 (54528.1 examples/sec)

accuracy: 0.970499992371__Wed Nov 08 2017 10:01:18 GMT+0800 (CST)____Wed Nov 08 2017 10:01:18 GMT+0800 (CST)__

Видно, что при использовании простой нейронной сети с 2 скрытыми слоями точность классификации повысилась до 97%, что намного лучше, чем у простого линейного классификатора. Демонстрируется значительное влияние выбора модели на конечную производительность.

Полная загрузка кода:GitHub.com/dong--J Ян/…

об авторе

Донг Цзянь, 360 Senior Data Scientist, бывший исследователь Amazon. В настоящее время он в основном занимается научными и технологическими инновациями в области глубокого обучения, обучения с подкреплением, компьютерного зрения и т. д., и имеет богатый опыт работы с большими данными и компьютерным зрением. Он много раз приводил команду к участию во всемирно известных соревнованиях по искусственному интеллекту, таких как Pascal VOC и ImageNet, и выигрывал чемпионаты.

Во время работы в докторантуре он опубликовал множество научных статей на ведущих международных научных конференциях и в журналах. С момента прихода в компанию 360 в конце 2015 года Донг Цзянь участвовал и руководил несколькими проектами в области компьютерного зрения и больших данных в качестве основного технического персонала.

благодарныйЧен Сипланирование этой статьи.