beeline 100% match
Я не настоящий бигдатщик, я просто xgboost на github'e нашел.

Погоня за 500кр от Билайна заставила окунуться в мир машинного обучения, к коему я интерес проявлял и раньше, но доверия не оказывал и, соответственно, не окунался. Беглый поиск показал, что в этом плане рулит сейчас xgboost от китайских товарищей из вашингтонского университета. Как я понял, это что-то вроде Apple в области машинного обучения: нажал одну кнопку — получил быстро и красиво что хотел.

При ближайшем рассмотрении выявились настройки, подкручивая которые, можно ускорять или замедлять обучение, уточнять или огрублять предсказания, даваемые этой программой. Входные данные программе удобнее всего подаются в libsvm формате. Наверняка есть куча библиотек, с помощью которых можно что угодно (от csv до avi) перевести в libsvm, но в данном случае я обошелся своим «велосипедом» на JavaScript.

Итак, уже 30 сентября с xgboostной помощью было получено 76.58% попаданий по предварительной оценке. Матерые бигдатщики тем временем попадали на 77%+! Прочитав о пользе ансамблей (это объединение нескольких предсказаний по принципу большинства голосов) стал искать другие методы, чтобы получить нескоррелированные с уже имеющимися предсказания. Так как если составлять ансамбль из примерно одинаковых по своему происхождению предсказаний, то их точность усредняется, а если из совсем разных — то точность растет.

Попробовал метод опорных векторов (SVM), метод ближайших соседей с взвешиванием по удаленности соседа и «случайно-лесной». Эти методы уже потребовали кое-какой подготовки данных: где-то нормализовать, где-то взвесить значимость факторов, пришлось немного разбираться, так как знаю питон на уровне «читаю/пишу со словарем», с питоновской sklearn (Sci-Kit Learn), благо там документация и примеры понятны и коту. Результаты намного хуже: от 57 до 73% попаданий, сказалось неумение готовить данные, к которому эти методы критичны. Хотя вроде делал как пишут в книжках, категории в фиктивные переменные, числа в диапазон [0;1], незначимые выкидывал.

Чтобы разнообразить модели начал избавляться от значимых и незначимых факторов. Переменная x8 содержит более 90% из имеющейся во всех переменных информации о возрастной группе (только по ней одной можно получить 72%+ попаданий). Xgboost без этой информации что-то стал часто промахиваться — в пределах 55% попаданий, а вот по ближайшим соседям, на удивление, осталось 73%. Создав еще несколько десятков вариантов с различными обрезаниями доступной для обучения информации, собрал их в ансамбль и… 77.05%. Это печально…

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

Непонятно, было ли это предусмотрено устроителями конкурса, но 27 октября перебором за несколько часов и примерно 23 тыс. попыток получил 15 тыс. первых строк тестовой выборки с правильными ответами (ни капчи, ни ограничений по частоте отправки вариантов решений не было, так что все прошло быстро и дешево даже в 1-2 потока). Но особой пользы, кроме потехи ЧСВ, эти дополнительные 15к не принесли: по моим оценкам лидеров обойти на зачетных 70% тестовых данных не удалось, так как новой информации, определяющей возрастную группу, использованные мною методы не смогли обнаружить.

Практические итоги урока ML от Билайна


  • xgboost оптимален при поиске зависимостей в данных, позволяет получить 95% от наилучшего возможного результата ценой 5% усилий, годится при потребительском подходе к задаче лентяем с нулевым уровнем.
  • Для продвинутого уровня пойдет python-sklearn. Если постоянно с ним работать, то тоже дело нетрудоемкое, классы в библиотеке удобные, реализуют практически все фантазии.
  • Объединяя нескоррелированные результаты даже низкого качества можно улучшить точность до уровня выше лучшего из имеющихся исходных вариантов (полезность ансамблей).

А как участвовали в конкурсе от Билайна и что из него вынесли вы?

Комментарии (36)


  1. Wadik
    29.10.2015 11:45

    Пробовал Google Prediction, результат порядка 47% был.


  1. bak
    29.10.2015 11:59

    0.75 с GradientBoostingClassifier-ом из scikit-learn и feature-selection-ом (надо было ансамбли попробовать).


  1. u1d
    29.10.2015 12:39

    Вот опубликовали финальные результаты, удивительно, что стало с Владимиром Лариным, который все лидировал с 77.4% по первым тестовым 15тыс строк, а на остальных у него оказалось менее 76.20%. У всех был overfit.


  1. mmvds
    29.10.2015 13:42

    Тоже не бигдатщик :)
    Пробовал DeductorAcademic от российских разработчиков (использовал еще студентом), на деревьях показал максимум 74,4%, на нейронных сетях максимум <50% (1-2 скрытых слоя), пробовал разбивать выборку на части по полноте данных, существенных улучшений не принесло. Квантификация столбцов со строками помогла поднять результат на деревьях с 73,7 до 74,4% Про xgboost прочитал уже только 27-ого октября :(


  1. rushter
    29.10.2015 14:22

    Я был первым — куда-то пропал. Результаты не финальные.


    1. u1d
      29.10.2015 14:32

      Похоже, что из финального рейтинга выкидывают подозрительных или нежелающих сотрудничать (неприславших резюме). Еще у Ильи Езепова пропал бот или однофамилец(?) из «финального рейтинга». Мои 23к+ попыток явно были подозрительны :)


    1. u1d
      29.10.2015 14:45
      +1

      Чтобы больше 77.3% получить на первых 15000 тестовых секретный какой-то метод использовали или можете поделиться?)


      1. rushter
        29.10.2015 15:45

        Ничего особенного, stacking разных моделей.


  1. kraidiky
    29.10.2015 14:31

    Использовал самодельную нейронную сеть. Не смог перепрыгнуть выше 75% на тестовой подвыборке. Когда опубликуют всю тестовую выборку узнаю на сколько мог бы запрыгнуть при идеальном заполнении пропущеных значений, но пока результат всё равно мне нравится. Как я понимаю, все кто пробовал сетки получал в разы худший результат.


    1. rocknrollnerd
      29.10.2015 18:05

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


    1. u1d
      29.10.2015 22:51

      Использовал самодельную нейронную сеть. Не смог перепрыгнуть выше 75%

      А что за сеть дала такой хороший результат? Много ли слоев, структура связей, какая активационная функция? Если не секрет, конечно :)


      1. kraidiky
        29.10.2015 23:10
        +1

        Глубина в среднем 4 слоя. Функция активации гипертангенс. Свзи в первом приближении хаотические. 73 нейрона, 1560 синапсов. Продукт жестокого и изощрённого дропапа и дропдауна. Допобрпдотка входных данных: всякое нормирование, заполнение пропущеных, представление категорий как ворох входов 0-1, подробнее не опишу, предобработку мне жена делала. :)

        Вообще по трезвому и выспавшемуся размышления называть хорошим результат лишь на 3% лучше чем можно получить из голой переменной x8 можно лишь с определённой натяжкой.


      1. kraidiky
        31.10.2015 12:37

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


        1. u1d
          31.10.2015 16:59

          pastebin.com/AqqXwqH5 но как я понял, Вы можете использовать для тренировки 40к, а проверить свои гипотезы на оставшихся 10к, разница с тем, чтобы тренировать на 50к а проверять на еще 15 будет небольшая.


          1. kraidiky
            01.11.2015 00:30

            Не так всё просто. Я пытался натренировать отдельный классификатор на редкие категории, типа 0, а её в трейновой выборке всего 350 значений. Не особо раскроссвалидируешься. Кроме того я пытался довольно экзотическим алгоритм борьбы с оверфитом заниматься. Я учил модель, потом смотрел на остатках степень оверфита и если перебор — менял метапараметры и учил дальше. Методологически такая выборка уже считается скомпрометированной, и то как просела итоговая таблица относительно предварительной — тому доказательство.

            Но судя по тому, что я узнал из вашего файла, максимум доступный мне если бы все пропущенные значения были заполнены напрямую божественным прозрением, доступный мне максимум составил бы всего 75,68. Всё равно три четверти процента не допрыгиваю до крутых дядек.

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


  1. Eugene713
    29.10.2015 16:43
    +4

    Использовал ансамбли random forest, с небольшой предобработкой пробелов в данных. Результат был 76.38, куда делся из финальной — непонятно


  1. rocknrollnerd
    29.10.2015 18:10

    Я не поднялся сколь-либо далеко до топа, в процессе получил что-то вроде 76.6 на случайном лесе из scikit-learn. Какое-то время игрался с отбрасыванием шумящих фич, что давало забавный эффект — маленький лес лучше работал при выкидывании половины неважных фич, но уже большой (~2000 деревьев) показывал результат наоборот — видимо, все шло в ход.

    Лучи легкого неодобрения авторам реализации мультипроцессного леса в scikit-learn, который для каждого процесса копирует дополнительно выборку в память. x8 процессов — x8 памяти, пожалуйста.

    Очень много времени убил на попытки решить это кастомной нейросетью из Theano+Lasagne, результаты на удивление грустные (см. чуть выше). Зато в качестве бонуса — много опыта на тему влияния разных метапарамтеров. Learning rate decay и много-много дропаута сильно помогли.

    А вообще было весело.


  1. ssh1
    29.10.2015 18:41

    Был ли прирост качества если добавить эти «угаданные» 15000 объектов к обучающей выборке?


    1. u1d
      29.10.2015 19:03

      Качнул эти дополнительные 15к в последний день, так что погонял по ним по-быстрому только xgboostом, разницы при кроссвалидации не заметил. Может, 5-10 сотых добавляли, что для меня в меркантильном плане роли не играло :) А если проверять создаваемую модель на этих же 15к, то, очевидно, там и 99% попаданий быстро достигались.


  1. Mnemonik
    29.10.2015 18:48
    +3

    Вот это нормально ваще, я писал сам на перле дробилку под все это, все как положено, сам алгоритм, плюс thread queue и thread pool с 10 потоками (на 12 ядерном сервере) которая все это жевала. Выбил максимум что-то около 58%, уже не помню точно. И забил, потому что не совсем понятно какие данные более ценны чем другие. Непонятны веса этих данных.
    Например может там в одной колонке город, в другой улица, и совпадающая улица без совпадающего города не значит близкоживущих людей, а вот совпадающий город вполне. Вобщем забил.
    А теперь чувствую себя прямо лохом — все оказывается пихали это в чьи-то чужие дробилки и двигали скроллер…


    1. mmvds
      29.10.2015 19:02

      Зачем вам живущие рядом, если требовалось узнать возрастную группу? :)


      1. u1d
        29.10.2015 19:18
        +2

        А может там детдом или, наоборот, дом престарелых, или общага заводская :) на 50 тыс., конечно, вряд ли это позволит что-то выделить.


        1. mmvds
          29.10.2015 20:10

          при значимости 73,743% для x8, максимальная значимость из квантифицировнных строк была для x17 с 0,128% значимости, т.е. не так уж и много можно было выдоить из строковых данных


      1. Mnemonik
        29.10.2015 20:26

        Это был просто сферический пример. Без известных весов признаков, что важнее чего, это все тыканье пальцем в небо было, я к этому.
        Да те же самые строковые данные и численные. Численные понятно чем ближе число тем более одни данные похожи на другие. А строковые? если они чуть-чуть разные это близкие данные или все равно разные? И что важнее, если например совпадает 10 из 10 строк, но числа расходятся процентов на 10, или когда совпадает 9 из 10 строковых, зато числа расходятся всего на 1?..


        1. mmvds
          29.10.2015 20:50

          Я полагал что строки скорее всего хэши от строк (названий тарифов, услуг), соответственно связи между xxxxxx1 и xxxxxx2 никакой нет.
          Хотя могу и ошибаться и это перечень значений классификатора, т.е. грубо говоря axxxxx и axxxxx показывают принадлежность к одному классу.


          1. Mnemonik
            29.10.2015 21:57

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


          1. Mnemonik
            29.10.2015 22:01

            В общем я попробовал несколько раз, подумал намного больше раз, но ни один из вариантов не показался мне «да, это правильный подход». всегда есть какие-то «неизвестные».
            Наверное big-data это не мое. =D


        1. kraidiky
          29.10.2015 21:00
          +1

          Кстати о численных и категориальных, мало кто заметил, но переменная x8 которая тут оказалась самой главной вообще-то категориальная, у неё всего меньше сотни возможных значений.


          1. mmvds
            29.10.2015 21:22

            Вы имеете ввиду дискретная численная переменная, а не непрерывная, численная? Пробовал оба варианта, результат был одинаков


          1. Mnemonik
            29.10.2015 21:58

            я делал несколько разных срезов — сколько каких значений встречается, какой разброс у значений… там есть несколько интересных колонок, есть какие-то с очень маленьким разбросом, типа все в пределах 0.2, есть какие-то с совсем небольшим количеством значений.


            1. kraidiky
              29.10.2015 23:17

              Есть три колонки в которых много 0 и 1, некоторое количество 1/2, в полтора раза реже встречаются 1/2 и 2/3, в два раза реже 1/4 и 3/4 и так далее. До сих пор интересно что кодировали эти столбцы. Есть колонки в которых часто встречаются значения 0,6,12,18,x6, в три раза реже 2,8,14,x6+2 и сильно реже все остальные. Пытался для подвыборок по разным типам таких значений делать отдельные классификаторы, но так ничего из них полезного и не вытянул.


    1. u1d
      29.10.2015 22:24
      +2

      Вот это нормально ваще, я писал сам на перле дробилку

      Да, это все нормально. Вы занимались тем, что делаете хорошо, пускай в данной задаче и не пришли к каким-то выдающимся результатам. А реализовали бы другой алгоритм (кстати, какой был у Вас реализован на 58% попаданий?) — получили бы более приятные 75%+. Зато теперь узнали, пускай и не на своем опыте, что есть много удобных, эффективных, простых в использовании инструментов, реализующих ML. Они и «фичи» взвесят, и кроссвалидацию сделают, и годный прогноз дадут.

      И, уверяю Вас, далеко не все использовали готовые решения и библиотеки. Я и сам пару лет назад решал подобные задачи без представления о ML (Экселем и Си). Тогда этого даже хватало на призовые места :)


  1. stychos
    29.10.2015 21:35
    -1

    А я на пхп наговнокодил на 30 % =)


  1. mintsev
    29.10.2015 22:05

    Использовал классическую линейную регрессию из коммерческого miner продукта — получил 52% на тестовой выборке — но и времени потратил совсем мало


  1. SKolotienko
    29.10.2015 22:39
    +1

    Сначала изучал как обрабатывать пропущенные данные.
    Затем сделал one hot encoding для категорических признаков (в дальнейшем сделал вывод что они полностью бесполезные).
    Затем попробовал классификаторы в порядке знакомства с ними: линейный SVM, ближайший сосед, random forest. Только последний смог дать более-менее адекватную точность.
    Далее решил попробовать xgboost и сразу же удалось достичь точности порядка 76%, но этого к тому моменту уже было недостаточно, чтобы удержаться в Top25. Довольно много времени ушло на знакомство с этим классификатором и на его настройку.
    Потом попробовал собирать ансамбли из этих классификаторов, но за один вечер так и не удалось сделать ничего лучше, чем чистый xgboost.
    В конце концов как-то потратив целый день и улучшив точность на жалкие 0.2% (так и не попав в Top25), понял, что оставшийся процент точности (расстояние до лидера) не стоит таких усилий — перестал уже что-либо делать.


    1. rocknrollnerd
      29.10.2015 22:51

      О, я тоже сначала долго думал над строковыми столбцами, решая, какие из них переводить в one-hot и считать категориальными, а какие нет. Ориентировался по разбросу значений, само собой — столбец, где было ~1500 разных значений, допустим, точно можно выбросить, а тот, где их всего три, стоит и оставить. Испытал легкое разочарование, когда оказалось, что их все можно выкинуть и точность только вырастет)