Около 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, который позволил бы людям просматривать прогнозы цен и подписываться на оповещения о прогнозах для определенных марок и моделей:
Таким образом, с помощью небольшого дополнительного кода клея люди могли подписаться и получать прогнозы для любой модели автомобиля, которую они хотели бы купить, и получать максимально точные прогнозы окончательных цен на аукционах за несколько дней до их конкурирующих участников торгов. В целом архитектура системы выглядит примерно так:
Замечания
Смысл в том, чтобы поделиться всем этим объяснением, состоит из нескольких частей:
- Автомобильные люди, я хочу, чтобы вы сами испытали BAT Predictor и отправили его людям, которые хотели бы подписаться на уведомления о нем.
- В большей степени для людей, занимающихся машинным обучением, я хотел поделиться с другом своим процессом разработки модели машинного обучения на основе пари о том, можно ли что-то предсказать, вплоть до веб-сайта, позволяющего людям получить доступ к этим предсказаниям.
- Разборки с аукционами и академическая литература не особо помогли, когда я обдумывал этот проект.
Безусловно, здесь многое упущено - за этим постом стоит много кода, и он слишком замусорен и недокументирован - если поделиться им, это скорее запутает, чем поможет. Однако в общих чертах мой процесс может помочь другим людям, которые думают над тем, как превратить игрушечную модель из scikit-learn в нечто, что они могут развернуть в полевых условиях для использования нетехническими людьми. Основные темы, которые я узнал из этого и нескольких других проектов, которыми я поделился бы с другими людьми, играющими с моделями машинного обучения с прицелом на их развертывание в сети:
- Сосредоточьте большую часть своего времени на получении хороших данных и разработке функций. Пакеты, такие как tpot, имеют большое значение в выполнении тех частей работы, которые связаны с «машинным обучением». Самый большой способ помочь себе - это не возиться с параметрами и алгоритмами, а вместо этого предлагать функции, которые информативен, насколько это возможно, и управляет способами сохранения этих функций в моделях.
- Постройте итеративно и на каждом этапе проверяйте убывающую отдачу. Когда я начал этот проект, я построил модель, на сборку которой у меня ушло около двух часов. Дальнейшие действия могли оказаться пустой тратой времени, если бы модель в любом случае оказалась неэффективной. Примите меры по построению своей инфраструктуры, соответствующие тому, насколько успешными уже были дела.
- Создавайте черновые модели, которые проверяют результаты от начала до конца, прежде чем строить окончательные модели, которые будут максимально точными. Оптимизация моделей может занять много времени, а если вы используете арендованные машины в облаке, это может стоить денег. Если вы абсолютно уверены, что собираетесь реализовать проект до конца, сначала отложите вопрос об оптимизации модели и создайте начальную модель вместо последней, которая позволит вам сосредоточиться на остальной архитектуре. В некоторых проектах я даже вставлял генератор случайных чисел для модели, ожидая замены, но желая сосредоточиться на остальной части своего стека.
- Изолируйте часть своего приложения, связанную с машинным обучением. Как и в любом другом проекте, вы не хотите, чтобы проблемы просачивались из модулей и перебирались во всю стеку. Что мне нравится делать, так это создавать чистый слой разделения между моими моделями машинного обучения и остальной частью системы, которая использует модели машинного обучения как функцию. Я не хочу думать об остатках ошибок во внешнем интерфейсе. Я не хочу думать о том, как поступать с вмененными значениями отсутствующих значений характеристик в ежедневных электронных письмах, которые я отправляю подписчикам. Разделяйте проблемы и принимайте решения о том, как сделать ваш интерфейс с моделью машинного обучения как можно более минимальным. В идеале у вас есть простая точка запроса, в которой вы отправляете наблюдение, получаете прогноз, и это все, что он делает. Выполните остальную сортировку и отработку на каком-то среднем уровне, а затем при представлении результатов только позаботьтесь о том, чтобы эти результаты отображались аккуратно.
Я не претендую на звание эксперта в области машинного обучения или эксперта в чем-либо на самом деле. Я только что выполнил несколько из этих проектов и прочитал кучу других людей, размышляющих над вещами, над которыми я размышлял при создании своих проектов, и я думал, что поделюсь своими собственными размышлениями вслух для всех, кто обдумывает свои собственный процесс. Надеюсь, это поможет вам, если вы работаете над собственными вопросами реализации, а если нет, надеюсь, вы найдете гораздо лучший способ делать то, что делаете - спасибо за чтение и удачи!