Вот код и некоторые пункты того, чем мы занимаемся:

  • Keras глубокое обучение со сверточными сетями;
  • Миллионы образов, сотни ГБ и более дискового пространства;
  • Решение Pandas DataFrame для организации имен файлов (вместо подкаталога по метке классификации);
  • использование исходных изображений и путей к файлам, без изменения размера и перемещения копий;
  • (но если вы копируете файлы…) эффективный метод организации каталогов;
  • использование одинаковых изображений и путей для классификации (биномиальной, полиномиальной) и регрессии;
  • многопоточное, поточно-ориентированное использование ЦП для создания очереди мини-пакетных матриц проектирования, подаваемых на графический процессор, выполняющий фактическое обучение;
  • несколько пользователей могут использовать одни и те же файлы изображений на сетевом файловом сервере;
  • использование существующей базы данных id для отслеживания и выбора файлов (включая случайную выборку по мере необходимости);
  • объединенная модель, обученная с использованием того же id, чтобы организовать другие функции в отдельной матрице проекта.

Многое из того, что мы здесь представляем с точки зрения кода и идей, уже существует в той или иной форме в основной части двух выпусков Keras, # 1627 и # 1638. Проблема регрессии обсуждается в сообщении StackOverflow. Наш вклад состоит в том, чтобы обобщить и прояснить эту информацию и развить ее в виде учебника, который, как мы надеемся, поможет другим. Наш код был протестирован с Keras 2.0 с бэкэндом Tensorflow.

2 Мотивация

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

Этот метод работает особенно хорошо, когда мини-пакетная обработка (обучение модели) выполняется графическим процессором (ами), в то время как многоядерный ЦП создает и поддерживает очередь правильно преобразованных матриц мини-пакетного проектирования. Это предварительное кэширование - это многопоточное решение, уже встроенное в Keras. По нашему опыту, этот метод позволяет графическому процессору (-ам) работать практически с максимальной производительностью, не дожидаясь чтения файла изображения, изменения размера, преобразования и т. Д., Тем самым сводя к минимуму время, затрачиваемое (и, следовательно, стоимость) использования машины с графическим процессором. .

Практикам известно, что этот тип фоновой эффективности не так хорош, как более известная старшая сестра знаменитостей, «машинное обучение», но он абсолютно необходим, чтобы что-то сделать в разумные временные и финансовые рамки.

+-----------+      +-----+      +--------------+      +-----+
| existing  | ---> | CPU | ---> | mini-batch 1 | ---> | GPU |
| file      |      +-----+      | mini-batch 2 |      +-----+
| server    |                   | mini-batch 3 |
| structure |                   | ...          |
+-----------+                   | mini-batch n |         ^
                                +--------------+         |
                                                         |
      ^               ^              ^          +---------------+
      |               |              |          | The sexy bit, |
      |               |              |          | "machine      |
 +------------------------------------------+   |  learning".   |
 | Necessary, behind the scenes grunt work. |   +---------------+
 +------------------------------------------+

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

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

Чтобы использовать существующие предварительно обученные модели, такие как VGA-16 и InceptionV3, изображения следует изменить до 224x224 и 299x299 соответственно. Вместо создания новых каталогов изображений с измененным размером это можно и нужно делать на лету, как это делается с помощью встроенного метода Keras .flow_from_directory(directory) класса ImageDataGenerator, где ключевым моментом является комбинация очереди ЦП и обработки графическим процессором. Мы включим эту способность в дальнейшее.

Помните, что эта двойственность CPU / GPU уже существует в генераторах Keras - мы просто переупорядочиваем некоторые части, чтобы можно было использовать списки путей к файлам вместо каталогов файлов.

3 Текущая стандартная практика для каталогов изображений

Стандартная практика для биномиальной модели («кошки» против «собак») состоит в создании двух подкаталогов, data/train/cats и data/train/dogs (затем еще два для проверки и еще два для тестирования). Точно так же для полиномиальной модели несколько классов выводятся из самой структуры каталогов (например, data/train/black_hair, data/train/grey_hair, data/train/white_hair.

Эти предположения о структуре каталогов встроены в стандартный метод Keras .flow_from_directory(directory) класса ImageDataGenerator, где документация описывает directory как:

«… Путь к целевому каталогу. Он должен содержать по одному подкаталогу для каждого класса. Любые изображения PNG, JPG или BMP внутри каждого дерева каталогов подкаталогов будут включены в генератор ».

Предположения о структуре каталогов подходят для небольших наборов данных, но для нашего случая использования миллионов изображений и сотен ГБ хранилища это неэффективно, потому что, а именно, мы не хотим копировать все эти файлы изображений (возможно, более одного раза) или сделать паутину символических ссылок в ошибочной попытке «упростить» процесс копирования.

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

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

4 Отход от парадигмы cats и dogs: использование базы данных ids

Чтобы сделать это более конкретным, мы отворачиваемся от кошек и собак и в пользу нашей собственной индустрии рекламных технологий и связанных с ней креативов. Креативы - это фактические изображения для рекламы, показываемой на веб-странице. Креативы бывают разных размеров, два из самых распространенных (width x height в пикселях) - это 300 x 250 и 728 x 90. Креатив хранится в базах данных нашей компании с уникальным идентификатором (creative_id) и отображается в нескольких внутренних (а иногда и внешних) таблицах, так что один creative_id связан с десятками полей метаданных (например, рекламодатель, страна, кампания, категория бренда и т. д.) и миллиарды полей транзакций на уровне журнала (веб-сайт, на котором размещено объявление, отметка времени и цена аукциона и т. д.).

Одна из наших основных целей - avoid несколько копий сотен ГБ через нашу сеть. То есть, если эти файлы уже существуют где-то в нашей системе, мы бы предпочли, чтобы наш конвейер модели считывал непосредственно из этих файлов. существующих местоположений, а не создавать произвольный, недолговечный набор из dogs и cats каталогов. Мы используем простой потокобезопасный многопоточный генератор, основанный на обсуждении в Заявке Keras № 1638, с использованием стандартных инструментов Keras и Python, которые можно использовать так же, как метод Keras .flow_from_directory(directory).

5 Идентификатор базы данных связан с другими данными функции

Изображение обычно - это просто одна из многих функций, связанных с объектом более высокого уровня в системе. Например, в AppNexus и в индустрии рекламных технологий изображение, отображаемое на веб-странице в виде рекламы, является лишь одной из особенностей концепции более высокого уровня креатив. Креатив загружается в нашу систему рекламодателем для конкретной рекламной кампании определенного бренда. Итак, мы видим, что при показе объявления с ним связан кортеж из (image, advertiser, brand). Каждый из них имеет связанный с ним идентификатор, который представляет строку в одной из наших таблиц базы данных. Например, два объявления могут быть представлены в нашей системе как:

| creative_id | advertiser_id | brand_id |
|-------------+---------------+----------|
|    49557622 |         87654 |     5678 |
|     3303651 |         98765 |     1234 |

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

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

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

cd /path/to/our/images/
tree imgs
 imgs
 ├── 622
 │   └── 57
 │       └── 49557622.png
 ├── 651
 │   └── 03
 │       ├── 3303651.png
 │       ├── 35903651.png
 │       ├── 44603651.png
 │       └── 95403651.png
 └── 756
     ├── 55
     │   ├── 31255756.png
     │   └── 95555756.png
     ├── 61
     │   ├── 1161756.png
     │   ├── 461756.png
     │   └── 5561756.png
     ├── 64
     │   └── 58164756.png
     └── 67
         ├── 3367756.png
         ├── 3767756.png
         └── 5467756.png

Эта структура каталогов используется для балансировки количества подкаталогов и количества файлов в каждом подкаталоге. Обратите внимание, что соглашение об именах подкаталогов (ниже верхнего уровня imgs каталога) использует последние три цифры creative_id нашей системы, а уровень ниже этого - следующие две цифры более высокого порядка. Таким образом, у нас есть 1000 подкаталогов ниже imgs и 100 подкаталогов в каждом из них. Если в каждом из них по 1000 файлов, то у нас есть эффективная система для хранения 100 миллионов файлов.

Почему это так? Даже при большом масштабе всегда есть необходимость в навигации по каталогам и использовании команд оболочки ls, cd, du, feh и т. Д. Изучение структуры каталогов с помощью TAB функций завершения большинства оболочек намного проще, имея всего 1000 файлов или каталогов, чем со 100 миллионами, брошенными в один большой банк. Итак, здесь мы включили опыт пользователя в нашу структуру организации файлов, где пользователь - это единственное нас, практикующих машинное обучение.

Дополнительным преимуществом этой организации является то, что каждый каталог нижнего уровня теоретически должен содержать относительно случайный и сбалансированный (классовый, биномиальный и полиномиальный) образец изображений, что удобно для некоторого конвейерного тестирования. Эта сбалансированная случайность обусловлена ​​тем, что изображения, коррелированные на уровне объекта id (creative_id в нашем рабочем примере), будут помещены в разные каталоги. То есть рассмотрим рекламодателя, который загружает несколько объявлений (креативов) в нашу систему за один сеанс, чтобы они регистрировались в наших базах данных с последовательными значениями creative_id (например, 234567, 234568, 234569,…). Вполне вероятно, что эти объявления визуально очень похожи (одно и то же рекламное изображение, но с текстом на немецком, испанском или английском языках, в зависимости от страны, в которой будет показано объявление). Наша структура каталогов гарантирует, что они будут в разных каталогах.

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

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

df

       object_id   bi  multi
index                       
0         461756  dog  white
1        1161756  cat  black
2        3303651  dog  white
3        3367756  dog   grey
4        3767756  dog   grey
5        5467756  cat  black
6        5561756  dog  white
7       31255756  cat   grey
8       35903651  cat  black
9       44603651  dog  black
10      49557622  cat  black
11      58164756  dog   grey
12      95403651  cat  white
13      95555756  dog   grey

При обычном подходе «каждая классификационная метка получает свой собственный каталог», каждый из 14 файлов изображений в приведенной выше таблице необходимо будет скопировать дважды: один раз в набор биномиальных каталогов и один раз в набор полиномиальных каталогов. Ниже мы покажем, как избежать использования обеих копий и использовать только исходное расположение каждого файла изображения.

6 Функция для создания пути к файлу из идентификатора базы данных

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

Это даже можно использовать для обработки Pandas DataFrame выше, что позволяет удобно хранить идентификатор базы данных, путь к файлу и двоичные / полиномиальные метки:

Теперь мы ясно видим идею: каждый объект представлен одним путем к изображению, но может принимать разные роли (метки) в разных моделях. Мы показали только примеры биномиальных и полиномиальных значений, но идея легко расширяется, так что вышеупомянутый DataFrame может включать дополнительные столбцы для дополнительных меток классификации и значений регрессии.

7 Обучите простую модель Keras на собаках и кошках Kaggle

Итак, мы все находимся на одной странице, давайте убедимся, что у нас одинаковые настройки и данные, и обучим модель. Мы будем следовать примеру в блоге Keras Франсуа Шоле от июня 2016 года, который был обновлен до API Keras 2.0 14 марта 2017 года. Наша цель будет заключаться в использовании нашей вышеупомянутой file_path_from_db_id() функции и структуры каталогов в качестве временной замены в соответствующем месте этой модели.

Мы выбрали несколько иную настройку изображений кошек и собак из блога Keras, но все же используем те же данные из конкурса Kaggle. Вот несколько команд оболочки, которые можно скопировать / вставить в ваш терминал для извлечения подмножества данных:

Затем мы запускаем скрипт, связанный с сообщением в блоге Keras, classifier_from_little_data_script_1.py. Поскольку наша текущая цель - создать более эффективный рабочий конвейер, а не производить лучшие параметры модели, изменение количества эпох с 50 до 5 позволяет нам быстро запустить и протестировать нашу базовую линию. Мы показываем здесь результаты для машины без графического процессора, чтобы тестирование и отладку конвейера можно было провести дешево, прежде чем потребуется более дорогое время графического процессора.

Да, конечно, это ужасные результаты модели, как и ожидалось, из-за уменьшения количества обучающих выборок (2000), проверочных выборок (888) и эпох (5). Но наша цель здесь - это первый необходимый шаг в решении любой проблемы машинного обучения: сначала запустить конвейер. Только тогда вы сможете дольше работать на компьютере с графическим процессором с большим объемом данных, чтобы лучше обучить модель.

8 Небольшой обход, чтобы переименовать файлы Kaggle для целей этой демонстрации

Скрипт, который мы используем ниже, читает те же изображения собак и кошек Kaggle, но мы хотим продемонстрировать использование предложенной выше структуры каталогов. Итак, в нашем скрипте есть функция get_demo_data(), которая создает новую структуру каталогов из подкаталогов cats и dogs и возвращает DataFrame с новыми путями к файлам. Гимнастика панд, которая делает это, находится в функции new_tricks_from_old_dogs(), которая превращает это:

в Билла Мюррея:

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

9 Создайте генератор с использованием DataFrame

Наконец, учитывая простые DataFrames, приведенные выше, содержащие столбец фактических имен файлов в вашей системе (imgpath) вместо отдельных категорийных каталогов, мы можем построить наш генератор, используя прекрасный механизм Keras и некоторую безопасность потоковой передачи. Мы сделаем следующие основные замены.

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

Теперь мы вносим изменения, чтобы продемонстрировать нашу основную цель: с учетом наших вышеупомянутых DataFrames df_train и df_valid создать генератор, который Keras может использовать для предварительного кеширования данных изображения для каждого мини-пакета, используя имена путей к файлам. Мы просто передаем эти DataFrames в нашу новую функцию-генератор. Полный код находится в нашей версии скрипта, classifier_from_little_data_script_dfgen.py, который импортирует из akmtdfgen.py. Соответствующий код:

Запуск с этими изменениями (на машине без графического процессора):

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

10 Использование полного набора данных из 25000 изображений с новым генератором на компьютере с графическим процессором

Давайте запустим тот же код примерно в 10 раз больше данных. Во-первых, разделите подкаталоги меток старого стиля только так, чтобы наша функция get_demo_data() могла их найти и создать имитацию структуры каталогов и DataFrame; как правило, вы не копируете файлы изображений, а просто заполняете DataFrame фактическими путями к файлам (извинения за избиение мертвой лошади на этом этапе):

Теперь обучите модель с новым генератором на всех данных, но только на 5 эпох (поскольку наша цель - протестировать рабочий конвейер, а не классифицировать собак и кошек):

Как насчет этого: точность проверки 83% всего за 5 эпох, что примерно так же, как при использовании данных в 10 раз меньше, но с увеличением данных, как сказал наш герой Франсуа Шоле. Конечно, эти 83% являются ранним колебанием и работают в течение 50 эпох, поскольку в сообщении в блоге достигается сходимость точности проверки около 85% (с точностью обучения около 90%).

После того, как все вышеперечисленное заработает, добавить дополнительные обучающие данные так же просто, как добавить новые строки в DataFrame, которые содержат объект ids изображений и пути к фактическим файлам. Концептуально это можно представить как подключение внешнего диска с новыми обучающими образами, где новый диск установлен в /mnt/more/training/data. Вам не нужно будет копировать эти файлы в существующий каталог data, и в этом заключается изящество этой техники.

11 Использование генератора для обучения объединенной модели

Объединенная модель объединяет выходные данные двух (или более) последовательных моделей. Обычно одна ветвь (скажем, левая) будет сверткой, обученной на данных изображения, в то время как правая ветвь будет некоторыми другими данными, не относящимися к изображению, такими как выходные данные модели LSTM (см. Пример видео-вопроса) или просто традиционная расчетная матрица числовых характеристик. Ключевым моментом, имеющим отношение к нашему обсуждению, является то, что матрица дизайна для правой ветви будет иметь столько строк, сколько изображений в данных обучения, проверки и тестирования, так что выходные данные левой модели могут быть объединены по столбцам с правая матрица дизайна.

Идея использования генератора для обучения и проверки объединенной модели была сформулирована и исследована в выпусках Keras 3386 и 3466, но только для матриц проектирования, которые могут уместиться в памяти. Учитывая наш вариант использования миллионов изображений, если матрица дизайна состоит из тысяч функций, то маловероятно, что она поместится в основную память, и поэтому мы расширяем наш генератор выше для решения этого случая.

Давайте уточнить на примере: в простой последовательной модели, обсуждаемой вначале в этом посте, генератор возвращает кортеж из входной матрицы X и целевого вектора Y для каждого мини-пакета. Вместе эти X и Y представляют собой случайную выборку основного DataFrame, длина которой представляет все выборки за одну эпоху. Каждая строка X содержит значения пикселей изображения, считанного из пути к файлу, найденного в DataFrame, а каждая ячейка Y содержит целевую метку для этого изображения. В объединенной модели мы хотим, чтобы с каждым изображением была связана дополнительная строка с числовыми характеристиками. Таким образом, для каждого мини-пакета генератор будет возвращать те же X и Y, что и раньше, а также вторую матрицу X2 с тем же количеством строк (количество столбцов представляет количество функций этой второй матрицы). Все мини-матрицы X2 для эпохи, когда они сложены друг над другом, представляют собой всего лишь общую матрицу функций для правой модели, которую мы назовем bc (чтобы соблюдать bcolz).

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

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

Если вторичная матрица bc может уместиться в памяти, то ее можно передать непосредственно генератору и idx перетасованного кадра данных с начала эпохи можно использовать для правильной перестановки bc, например:

Затем фрагменты df и bc для мини-пакета находятся с использованием индексов [i:j], где i и j увеличиваются на размер мини-пакета во время каждой итерации цикла мини-пакета:

11.1 Массив функций слишком велик для памяти

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

Допустим, столбец индекса перемешанного мини-пакета DataFrame называется idx и содержит значения [13, 5, 1, 7]. Используя приведенный выше пример, наша мини-партия будет выглядеть так:

Эти четыре файла изображений считываются, и их пиксели возвращаются в виде строк X. Индекс получается и используется для индексации массива bcolz на диске с помощью:

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

Мы реализовали объединенную модель со случайными X2 данными для (вздыхает, к сожалению) собак и кошек в скрипте classifier_from_little_data_script_dfgen_merged.py. Это не улучшит точность вашей модели (на самом деле - это просто случайные данные о характеристиках, которые мы создали), но продемонстрирует, как обучить объединенную модель с миллионами изображений и с данными о характеристиках, которые не помещаются в память. Обязательно запустите (и поймите) код тестирования генератора, запустив сначала другой файл, `python akmtdfgen.py `.

Кроме того, см. Код для устранения некоторых потенциальных проблем, связанных с многопоточностью, если у вас есть более старая версия bcolz и / или лежащая в ее основе библиотека сжатия blosc. Если вы используете conda (или нет), убедитесь, что у вас установлена ​​последняя версия: pip install --upgrade bcolz. Строка документации akmtdfgen.py содержит результат bcolz.print_versions(), использованный для этого сообщения.

Вот и все! Теперь у вас есть инструменты для обучения ваших моделей на том объеме данных, который вы можете сохранить на диске. Вы будете ограничены по времени (и бюджету), но больше не будете ограничены машинной памятью. Посмотрите (и запустите!) Сопроводительный код, чтобы понять, как все это работает. Керас - движущаяся цель добра, и мы должны оставаться на цыпочках.

12 Предстоящие публикации об объединенных моделях bcolz и использовании Spark вместо графического процессора

В следующих статьях Дэниел Остин и Ашутош Санзгири, мои коллеги по Data Science и наши постоянные эксперты по глубокому обучению, будут дальше обсуждать наше комплексное использование других технологий в AppNexus, подробно рассказывая об использовании объединенных моделей для классификации изображений в масштабе и об использовании Spark. MLlib »и CPU-кластер вместо машины с GPU.

13 Благодарности

Во-первых, я хочу поблагодарить Дэниела Остина и Эллисон Круг из AppNexus за то, что они сделали этот пост: он управлял машинным обучением, она управляла написанием. Также благодарим Ашутоша Санзгири, Лей Ху, Андре Бикфорда и Майка Райта за отличные и полезные отзывы во время написания. Помимо замечательных людей из Keras (Франсуа!) И бесконечного количества информации в StackOverflow, я также хочу поблагодарить наших фантастических коллег из AppNexus, которые помогли понять, создать и использовать наши приложения для глубокого обучения: мой друг Портландцы Дэниел, Ашутош и Сэм Селджан; и нью-йоркская банда Лея, Муссы Тайфи, Каннана Шанкарана и Алекса Тэнди. Это было и остается веселым и продуктивным настоящим сотрудничеством с замечательной командой.