Около 10 дней назад я увидел сообщение об очень симпатичной машине марки и модели, о существовании которой я раньше не подозревал:

Я знал о МГБ и о том, что у них довольно ужасная репутация, но эта штука выглядела такой привлекательной. Я начал их читать и просмотрел несколько видеороликов, и в конце концов обнаружил, что просматриваю еще одно на веб-сайте, на который я случайно наткнулся раньше, BringATrailer.com. Отсутствие контроля над импульсами в конечном итоге привело меня к тому, что я сделал несколько ставок ниже цены той, которую я видел на Craigslist, но я понял несколько вещей о веб-сайте BringATrailer, который начал захватывать большую часть моего внимания:

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

Учитывая мой опыт работы с моделью прогнозирования цен TAP Deals, я полагал, что модель машинного обучения, обученная на tpot, скорее всего, сможет принять в качестве входных данных все основные характеристики объявления о транспортном средстве (марка, модель, год, время аукциона, исторический подсчет аукционов от продавца и некоторые другие, например) и возвращение в качестве результата прогноза окончательной цены аукциона. Конечно, здесь упускается из виду этап сбора данных, но достаточно сказать, что из-за довольно шаблонного характера BringATrailer.com довольно легко пройти через все текущие и исторические аукционы и извлечь интересующие элементы.

Построение модели

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

Хороший, но не отличный результат, тем не менее, хороший результат! В какой-то степени это разумный результат - если окончательные аукционные цены можно было полностью спрогнозировать только на основе информации, доступной в начале аукциона ... тогда зачем в какой-то степени проводить аукционы? С другой стороны, если бы результаты здесь показали очень низкий R² (например, в диапазоне

В моей следующей модели были учтены три семейства функций - «основные» характеристики транспортного средства (т. Е. Основные управляющие переменные транспортного средства, использованные в исходной модели), функции «ставки», связанные с историей заявок до текущего момента времени t, а также функции «комментирования» истории комментариев до текущего момента времени t. Хотя я рекомендую вам разрабатывать свои собственные функции, я могу сказать, что мои функции, связанные со ставками, обычно включают время между ставками, скачки цен между ставками, количество уникальных участников торгов и распределение ставок по участникам торгов и т. Д. Мои функции комментариев включали декомпозицию текстов комментариев в векторном пространстве, а также временные особенности и уникальность функций комментатора. Я собрал эти функции для всех прошлых аукционов, где я установил время аукциона t на 24 часа (например, учитываю только основные переменные транспортного средства, доступные в начале аукциона, а также функции «ставка» и «комментарии», созданные в первые 24 часа.

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

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

В крайнем случае, например, модель, обученная на данных, собранных за 2 секунды до закрытия аукциона, вероятно, будет очень точной - поскольку окончательная цена теперь, скорее всего, будет последней ставкой, что, конечно, является особенностью модель! Обычно мы не хотим включать в модель переменную, которую мы пытаемся предсказать, но в этом я менее убежден. Если мы наблюдаем переменную, которую пытаемся предсказать в достаточной мере до окончания аукциона, я думаю, что это честная игра - на самом деле мы не пытаемся предсказать окончательную цену, мы пытаемся предсказать значение наивысшей ставки t = 168, или 168 часов после начала аукциона (конец 7 дней). Если в большинстве случаев самая высокая ставка t = 167 = t = 168, это нормально - мы все равно сможем сообщить окончательную оценку гипотетическому пользователю за час до закрытия аукциона.

Создание семейства моделей

Убежденный, что результаты были многообещающими, я решил создать не одну модель, а 14 моделей с 12-часовыми интервалами, начиная со второго аукциона, проводимого онлайн. В идеале я должен обучать каждую модель на данных до определенного t часа. Учитывая, что с гибкими по времени моделями всегда очень сложно работать, я остановился, чтобы реализовать несколько фрагментов кода, чтобы помочь сохранить ограничения на моих моделях (например, не передавать случайно данные из t = 96 в модель, которая пытается прогнозировать на основе t = 48):

  • Доступ ко всем наблюдениям осуществлялся двумя способами Listing.test_cases или Listing.train_cases - train случаи были случаями, когда хеш MD5 URL-адреса листинга BringATrailer.com заканчивался цифрой, и test случаями были случаи, когда хеш оканчивался на a-f. Таким образом, с точки зрения обучения моделей никогда не будет перекрестного заражения.
  • Был реализован стандартный генератор функций, который явно возвращает функции для Listing в момент t. Вся генерация функций располагалась централизованно, и запросы явно пропускают данные о времени, превышающие заданное t в этот момент.

Имея это место, я арендовал более крупный облачный ящик и создал 14 обучающих экземпляров tpot, передавая t для каждого экземпляра. Через день у меня был такой график:

Наконец, я написал интерполятор, который выдает оценочную окончательную цену аукциона в определенный момент t аукциона. Здесь x (t) представляет особенности аукциона в t, а fi (x (t)) представляет некоторую обученную модель f, который был обучен конкретно особенностям наблюдений в момент времени t = i. Параметр d - это некоторая скорость распада - чем дальше модель обучается от конкретной модели во времени, тем меньше мы должны зависеть от оценки этой конкретной модели (здесь, если вы сделаете d отрицательный, он выполнит этот трюк - в моем коде я фактически нормализую это, а затем делаю 1- (it) до некоторого положительного г) .

Затем, используя этот интерполятор, вы можете перемещаться по диапазону t, чтобы сгенерировать прогноз для каждого t, который объединяет оценки и оценки с избыточным весом, наиболее близкие к этому конкретному t:

Наконец, для каждой из 14 моделей у нас есть диаграммы рассеяния ошибок, полученные ранее. В * очень легкомысленном * смысле этот график сообщает нам много информации о том, сколько ошибок есть в каждой модели - мы можем использовать эту ошибку для имитации ошибки конкретного прогноза в любой момент - вместо того, чтобы прогнозировать цену, мы прогнозируем цену плюс или минус средний процент ошибок, который мы наблюдаем для других прогнозов относительно этой конкретной цены (например, мы обычно на 15% меньше для прогнозов на 20 000 ± 10 000 долларов по модели i, поэтому мы скажем, что оценка может быть слишком высокой или слишком низкой примерно в той же пропорции). Это не особенно строго, но дает быструю шкалу ошибок в оценках, которые примерно соответствуют тому району, который нам нужен, без особых усилий. В результате, примерно через 5 дней работы с этим проектом, примерно за три дня до окончания аукциона у меня был следующий график:

Вооружившись этим, я знал, когда бросить курить - я не собирался поднимать цену до 12 тысяч долларов за машину, и я знал, что она там и закончится:

Я сказал 12 тысяч долларов, но в итоге получилось 13,5 тысяч долларов. Как бы то ни было, резерв не был выполнен, поэтому продажа закончилась неудачей, но предсказание было неплохим для того, кто только узнал о марке / модели за несколько дней до этого!

Производственные модели

Наконец, я добавил к модели несколько приятных штрихов. Я ненавижу запускать производственные процессы на Python и предпочитаю писать свои «клеящие» приложения на Ruby - в результате вся работа по прогнозированию выполняется на Python путем загрузки моих joblib'ed моделей. Они получают рабочие запросы через очередь Redis и отвечают своими прогнозами для данных наблюдений в очереди вывода. Код Ruby занимается управлением базами данных и согласованием записей, а также сбором новых данных с BringATrailer.com. Наконец, я решил добавить интерфейс в Node, который позволил бы людям просматривать прогнозы цен и подписываться на оповещения о прогнозах для определенных марок и моделей:

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

Замечания

Смысл в том, чтобы поделиться всем этим объяснением, состоит из нескольких частей:

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

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

  1. Сосредоточьте большую часть своего времени на получении хороших данных и разработке функций. Пакеты, такие как tpot, имеют большое значение в выполнении тех частей работы, которые связаны с «машинным обучением». Самый большой способ помочь себе - это не возиться с параметрами и алгоритмами, а вместо этого предлагать функции, которые информативен, насколько это возможно, и управляет способами сохранения этих функций в моделях.
  2. Постройте итеративно и на каждом этапе проверяйте убывающую отдачу. Когда я начал этот проект, я построил модель, на сборку которой у меня ушло около двух часов. Дальнейшие действия могли оказаться пустой тратой времени, если бы модель в любом случае оказалась неэффективной. Примите меры по построению своей инфраструктуры, соответствующие тому, насколько успешными уже были дела.
  3. Создавайте черновые модели, которые проверяют результаты от начала до конца, прежде чем строить окончательные модели, которые будут максимально точными. Оптимизация моделей может занять много времени, а если вы используете арендованные машины в облаке, это может стоить денег. Если вы абсолютно уверены, что собираетесь реализовать проект до конца, сначала отложите вопрос об оптимизации модели и создайте начальную модель вместо последней, которая позволит вам сосредоточиться на остальной архитектуре. В некоторых проектах я даже вставлял генератор случайных чисел для модели, ожидая замены, но желая сосредоточиться на остальной части своего стека.
  4. Изолируйте часть своего приложения, связанную с машинным обучением. Как и в любом другом проекте, вы не хотите, чтобы проблемы просачивались из модулей и перебирались во всю стеку. Что мне нравится делать, так это создавать чистый слой разделения между моими моделями машинного обучения и остальной частью системы, которая использует модели машинного обучения как функцию. Я не хочу думать об остатках ошибок во внешнем интерфейсе. Я не хочу думать о том, как поступать с вмененными значениями отсутствующих значений характеристик в ежедневных электронных письмах, которые я отправляю подписчикам. Разделяйте проблемы и принимайте решения о том, как сделать ваш интерфейс с моделью машинного обучения как можно более минимальным. В идеале у вас есть простая точка запроса, в которой вы отправляете наблюдение, получаете прогноз, и это все, что он делает. Выполните остальную сортировку и отработку на каком-то среднем уровне, а затем при представлении результатов только позаботьтесь о том, чтобы эти результаты отображались аккуратно.

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