{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "
\n", "\n", "\n", "#
Иканам гранд рисёрч
\n", "##
Часть четвёртая: моделирование. Предобработка данных.
\n", "\n", "\n", "Проект **Иканам гранд рисёрч** реализуется [Иканам стьюдентс коммьюнити,](https://vk.com/ikanam)\n", "в частности [вот этим парнем по имени Филипп.](https://vk.com/ppilif) Если вы нашли ошибку или у вас есть предложения, замечания, деньги, слава или женщины, можно ему написать. Весь говнокод, использованный в исследовании распостраняется по лицензии [Creative Commons CC BY-NC-SA 4.0.](https://creativecommons.org/licenses/by-nc-sa/4.0/) Его можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала. При наличии технической возможности необходимо также указать активную гиперссылку на [страницу рисёрча.](https://github.com/FUlyankin/ekenam_grand_research) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Вместо введения\n", "\n", "В этой части мы наконец-то займёмся нашей заключительной целью: построением модели для прогнозирования отчисляемости людей. Как обычно, мы будем вдаваться в подробности на уровне пальцев, иногда будем призывать в помощь формулы. В этой почиташке мы разберёмся с тем, что оказалось у нас в данных и сварим из них итоговую табличку, которая будет пригодна для обучения модели. В следующей почиташке мы напрямую займёмся моделированием. \n", "\n", "\n", "# 1. Варить фичи для форкаста таргетов \n", "\n", "Из заголовка даже я ничего не понял. Поэтому, для начала, посмотрим на несколько определений. **Фича** - регрессор, признак, объясняющая переменная. Это та переменная, которую мы можем увидеть и, вопследствии, попытаться предсказать по ней значение **таргетa**, объясняемой переменной, переменной, которую нужно спрогнозировать. **Варить фичи** - создавать из грязного массива данных годные числовые переменные, которые можно запихнуть в модель. \n", "\n", "Во всех предыдущих скриптах мы старались работать с данными как можно чище. Тем не менее, в них всё ещё осталась кое-какая грязь, которая делает их непригодными для оценивания моделей. Давайте обсудим что именно в них не так, как это исправить и сделаем их предобработку. Попутно мы посмотрим на несколько красивых картинок и проверим одну гипотезу, которая довольно давно занимает мой разум. \n", "\n", "$70\\%$ работы дата-шрушера заключается в сборе данных, их предобработке и варке убойных фичей. $30\\%$ времени уходит на моделирование. Чем более крутые фичи будут сварены, тем круче будет работать итоговая модель. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2 Агрегируем данные асессоров \n", "\n", "Как вы можете вспомнить из самой первой статейки, у нас в руках оказались несколько табличек. Первая часть таблиц составлялась по приказам. После отдавалась для разметки асессорам. В итоге получился набор из 6 табличек `Total_razm`. В этом наборе оказалась вся информация о людях поступивших на эконом с 2012 по 2017 год. Отметки асессоров выглядят примерно так:\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Три разных мнения по каждому вопросу. Все эти мнения надо теперь усреднить и привести к единой цифре, которая будет отражать мудрость нашей толпы.\n", "\n", "* `kurs` - курс, до которого доучился человек. Берём медиану от трёх мнений. Переменная останется целым числом. \n", "* `leto-zima` - если человек был отчисле, то когда. Берём максимальную из предложеных оценок. \n", "* `akadem` - если хотя бы один асессор указал, что человек пытался вернуться, мы поставим единицу. \n", "\n", "Из-за того, что данных по академам собралось довольно мало, информацию о них мы нигде использоват не будем. По аналогии мы не будем нигде, кроме картинок, использовать информацию о времени года, в которое числанули человека. \n", "\n", "* `hodit_para` и `hodit_tusa` - будем усреднять мнение асессоров.\n", "\n", "В конечном счёте наша таблица преобразится и станет чуть более красивой. \n", "\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 3 Варка таргетов \n", "\n", "Отлично! Теперь займёмся конструированием той переменной, которую мы будем прогнозировать. Мы будем решать задачу классификации. Все люди делятся на два класса: закончил и не закончил. Нам бы хотелось обучить модель, которая смогла бы определять к какому из двух классов и с какой вероятностью относится человек. \n", "\n", "В нашем распоряжении есть четыре случайные величины: $Y_1, Y_2, Y_3, Y_4$. Величина $Y_1$ принимает значение $1$ в случае, если человек смог закончить первый курс. Иначе она принимает значение ноль. Остальные случайные величины ведут себя по аналогии. Наc будут интересовать четыре вероятности. \n", "\n", "1. $P( Y_1 = 1 \\mid X)$ — вероятность того, что человек закончит первый курс. \n", "2. $P( Y_2 = 1 \\mid Y_1,X)$ — вероятность того, что человек закончит второй курс, при условии, что он закончил первый курс. \n", "3. $P( Y_3 = 1 \\mid Y_2, Y_1, X)$ — вероятность того, что человек закончит третий курс, при условии, что он закончил первые два. \n", "4. $P( Y_4 = 1 \\mid Y_3, Y_2, Y_1, X)$ — вероятность того, что человек закончит четвёртый курс, при условии, что он закончил первые три. \n", "\n", "Мы не можем никак искать и прогнозировать безусловную вероятность $P(Y_2 = 1 \\mid X)$. Если человек не закончил первый курс, он не мог оказаться на втором курсе. Чтобы спрогнозировать безусловную вероятность, нам пришлось бы закидывать человека сразу на 2 курс, минуя первый. Либо сразу после того как он закончит первый курс, стирать ему память. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Вероятность того, что человек закончит эконом можно будет найти просто перемножив эти условные вероятности. \n", "\n", "$$ \n", "P( Y_1, Y_2, Y_3, Y_4 \\mid X) = P( Y_4 = 1 \\mid Y_3, Y_2, Y_1, X) \\cdot P( Y_3 = 1 \\mid Y_2, Y_1, X) \\cdot P( Y_2 = 1 \\mid Y_1,X) \\cdot P( Y_1 = 1 \\mid X).\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Руководствуясь этой замечательной формулой, сварим четыре таргета. \n", "\n", "* `target_1` — принимает значение 1, если человек закончил первый курс, иначе 0. \n", "* `target_2` — принимает значение 1, если человек закончил второй курс после того как выпустился с первого. Если человек не закончил первого курса, мы ничего конкретного про этого человека сказать не можем. Переменная будет неопределена для такой ситуации. \n", "* `targer_3` и `target_4` строятся по аналогии. \n", "\n", "Для пропусков в данных в питоне есть красивая визуализация. Лиловым обознчены хорошие переменные, жёлтым-пропуски. По оси абсцисс отложены переменные, по оси ординат наблюдения. На данный момент наши данные выглядят как-то так. \n", "\n", " \n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "У нас в данных есть несколько пропусков в страничках вконтакте. О таких людях мы не сможем сказать ничего толкового. Их нам придётся выбросить из анализа. \n", "\n", "Отдельно отметим, что у нас было несколько пропусков в баллах по ЕГЭ. Обычно для олимпиадников в приказах не публикуют баллы. Все эти пропуски в баллах ЕГЭ мы заполнили средним баллом по выборке. Если наблюдения с пропусками в одной переменной просто-напросто выбросить из дальнейшего анализа, можно потерять довольно большой пласт информации, которую в себе несли переменные без пропусков. При этом, если мы заполним колонку с баллами ЕГЭ средними значениями, модель при оценивании не увидит для олимпиадников в этой переменной ничего особенного. Она никак не будет выделять их на общем фоне по баллу ЕГЭ, зато она сможет более чётко понять как успешность студентов зависит от других переменных. Вдвойне обидно было бы выкинуть наблюдения из выборки, а потом выяснить, что ЕГЭ незначимая переменная. \n", "\n", "На практике довольно часто приходится искать баланс между заполнением пропусков и выбрасыванием переменных. Обычно пропуски заполняют либо медианой, либо средним, либо нулями. Кроме таких банальных подходов, есть подходы, основанные на методе максимального правдоподобия, которые смотрят на остальные переменные в остальной части таблицы и пытаются понять а какое значение в этой, пропущенной клетке, является самым наивероятным. \n", "\n", "На основе баллов по ЕГЭ мы также сварили ещё одну переменную `ege_diff`. Это отклонение балла человека от проходного. Также в модель была добавлена переменная `kozko`, которая отвечает за лектора по матану. Поговаривают, что в годы, когда он ведёт матан, отчисляют больше. \n", "\n", "Пропуски в `target_1` соответсвуют текущему первому курсу. Пропуски в `target_2` соответствую текущим первому и второму курсу, а также людям, которые не смогли закончить первый курс. Пропуски в `target_3` соответсвуют текущим первакам, второкам и третьему курсу, а также тем, кто не смог закончить первый, либо смог закончить первый, но не смог закончить второй курсы. Про `target_4` вы и сами догадаетесь. \n", "\n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В конечном счёте мы можем увидеть следущую картину. За $5$ последних лет первый курс не смогла закончить треть поступивших на него. Второй курс не смогли закончить $11\\%$ поступивших на него. Третий и четвёртый курс не смогли закончить совсем копейки. \n", "\n", "В итоге у нас оказывается совсем мало наблюдений для конструирования нормальной модели для оценки вероятностей закончить последние два курса. Из-за очень жёсткого дисбаланса в целевых метках, лучшей моделью будет говорить, что все закончат. Именно она будет давать наименьшую ошибку прогноза. Это не очень серьёзно. Задача прогнозирования отчислений с третьего и четвёртого курса больше напоминает задачу поиска аномалий, а не задачу классификации.\n", "\n", "Для моделирования вероятности закончить 2 курс наблюдений также маловато. Трогать его мы также не будем. Для моделирования вероятности закончить первый курс наблюдений довольно много. При этом дисбаланс между классами не особо большой и лучшей моделью явно не будет: \"говори, что все закончат\". \n", "\n", "Отдельно заметим, что летом отчислили $65$ человек, зимой $69$ человек. Напомню, что под \"отчислили\" я имею в виду, что человек либо реально был отчислен либо ушёл сам. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В переменных `hodit_para` и `hodit_tusa` также наблюдаются пропуски. Для текущего первого курса `hodit_tusa` вообще не проставлена, так как асессоры вели свою тяжёлую работу в октябре. Эти две переменные экспериментальные. Они очень здорово будут улучшать качество прогнозов. Однако мы не будем использовать их в финальной версии модели по очень веской причине, которая заключается в том, что они из будущего. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 4 Переменные из будущего \n", "\n", "Первая проблема, которая сопряжена с нашей деятельностью прогнозеров — это переменные из будущего. Дело в том, что мы изучаем явление, которое растянуто во времени. Выборку, при этом, мы собирали один раз. Отчисления это не так просто и в идеале для нас хорошо было бы вернуться в прошлое и сделать много-много срезов социальной сетки для того, чтобы накопить временной ряд. Дальше мы могли бы проанализировать как во времени меняются действия людей и построить годную модель. \n", "\n", "К сожалению, у нас нет машины времени. Мы живём здесь и сейчас, и можем собрать соц сетку только в том виде, в каком она существует сегодня. Из-за этой особенности в сборе данных возникает две проблемы. \n", "\n", "Когда я учился на 1 курсе, моя страничка была одной, сейчас моя страничка совершенно другая. Из-за того, что я поменял её, модель выцепляет не совсем те характеристики, которыми я реально обладал на первом курсе. Это хреново. \n", "\n", "Представим, что Адель училась со мной на одном курсе. Мы вместе поставили на своих страничках в графе ВУЗ, что учимся на экономе РАНХиГС. В конце первого курса Адель отчислили. Она удалила из своего профиля информацию о том, что училась на экономе. Ещё через два года я собрал информацию по контакту, чтобы попытаться спрогнозировать отчисления. В моей выборке есть дамми-переменная `vk_ekonom_ranepa_yes`, которая принимает значение равное единице, если человек в профиле указал, что учится на экономе. Конечно же эта переменная будет иметь в моей модели очень высокую важность, так как она несёт в себе информацию из будущего. Если бы мы собрали срез, когда Адель училась на первом курсе и ещё не была отчислена и у неё в профиле не было бы указано, что она учится на экономе, тогда бы эта переменная не несла бы в себе информации о будущем. Если бы она выстрелила в модели, мы бы могли сказать, что эта переменная была инструментом для ненаблюдаемой переменной \"скрытность человека\". И могли бы сказать, что на экономе выживают только те, кто умеет общаться с окружающими. В наших реалиях эта переменная перестала быть хорошим инструментом для выявления интровертов. \n", "\n", "Переменные `hodit_para` и `hodit_tusa` тоже конструировались в будущем. Мы знаем, что человек уже отчислен, с тех пор мы довольно мало с ним общались и редко видели на тусах и парах. Хочется поставить ему более низкую оценку и сместить истиное значение посещаемости вниз, непроизвольно вложив в эти переменные информацию, которая поможет модели найти правильные ответы. \n", "\n", "Ещё одним примером переменной из будущего является переменная, которая несёт в себе информацию о том насколько человек впутан в эконом. `PageRank` человека тоже является переменной из будущего. Мы собрали информацию о том насколько человек впутан в эконом уже после его отчисления. Его отчислили, зачем ему впутываться? Инструмент потерял свою силу. Если бы мы делали срез первого курса в январе каждый год, то тогда бы эта переменная несла бы в себе информацию о впутанности в эконом и мы, действительно, могли бы пихать её в модель.\n", "\n", "**Ещё раз, ещё раз.** Я собирал данные в будущем, поэтому переменные, которые я использую при моделировании, не должны нести в себе никакой информации о нём в явном виде. При строительстве модели и её интерпретации будем держать это в голове. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 5 Гипотеза Козко \n", "\n", "Если вы не знаете что такое гипотеза и как она проверяется, [вам помогут разобраться котики.](https://vk.com/doc-137614411_450958565) На самом деле, эта книжка с котиками может ответить на многие вопросы, которые у вас возникли или возникнут в будущем. Мы, напрмер, занимаемся тем, что описывается в 11 главе этой книги. Единственное, ради всего святого, не надо останавливаться в своём развитии на котиках. После них займитесь чтением нормальной литературы с большим количеством матана.\n", "\n", "Ахмед, Лёша. Добро пожаловать в этот раздел. Сейчас я собираюсь уничтожить всё, что вам действительно дорого. Пришло время развеять один очень важный миф, который поселился среди студентов иканама: \n", "\n", "> Когда матан читает Козко, с первого курса отчисляют больше людей. Когда матан читает Чирский, людей отчисляют меньше. \n", "\n", "Сформулируем гипотезу. \n", "\n", "$H_0\\colon$ в среднем людей отчисляют одинаково, $p_{k} = p_{ch}$\n", "\n", "$H_1\\colon$ ничего подобного, $p_{k} \\ne p_{ch}$\n", "\n", "В нашем распоряжении есть аж целых пять серий из испытаний Бернулли. Три серии с лектором в виде Чирского и две серии с лектором в виде Козко. Чтобы всё было честно, возьмём для проверки гипотезы 2012, 2013, 2014 и 2015 года. Проверять гипотезу мы будем по лекциям Палыча. Убидиться в этом можно, посмотрев код. Итак: \n", "\n", "\n", "| альтернатива | p-значение | вывод | \n", "|--------------------|---------------|-------|\n", "| $p_{k} \\ne p_{ch}$ | 0.320 | гипотеза о равенстве долей не отвергается |\n", "| $p_k > p_{ch}$ | 0.160 | гипотеза о равенстве долей не отвергается | \n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если добавить в выбоку 2016 год и увеличить число наблюдений, результат не изменится. Таким оразом, данные никак не противоречат тому, что людей отчисляют одинаково. \n", "\n", "Напомню, что в других преподах между двумя первыми курсами различий нет. Мораль всего этого проста: __Если ты тупой, неважно кто читает у тебя матан.__ Скорее всего, вы мне предложите дождаться результатов по отчислениям за этот год, а после перепроверить гипотезу. Друзья, я в курсе, что вы ходите принимать колки по матану. Неужели вы способны для того, чтобы доказать свою точку зрения поставить лишние двойки? " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В жизни довольно часто приходится тестировать подобные гипотезы. Обычно они тестируются перед тем как принять какое-то важное решение. Схемы принятия решений, сопряжённые с статистическими гипотезами называются **А-Б тестами.** Иногда от дизайна такого теста зависят довольно большие деньги или жизни людей. Вот несколько примеров использования таких тестов. \n", "\n", "##### Пример 1:\n", "Подобные гипотезы о долях довольно часто проверяют на практике. Например, пытаются проверить насколько хорошо будет работать новое лекарство. Тех, кто согласен принять участие в тесте, делят на две части. Одной дают плацебо, другой реальное лекарство. После смотрят какая доля людей в обеих группах поправилась и делают выводы об эффективности. \n", "\n", "##### Пример 2:\n", "Точно также работает бизнес. Например, перед тем как выкатить новый дизайн, фирма показывает его только случайному $1\\%$ пользователей, измеряет какие-то свои метрики, связанные со спецификой бизнеса (время на сайте, объемы заказа и тп) и смотрит на то выше ли в среднем метрики становятся при новом дизайне или нет. \n", "\n", "##### Пример 3: \n", "Последний пример. Предположим, что некоторый факультет научился прогнозировать успеваемость студентов. Возникла идея брать вне конкурса людей, для которых вероятность успешно закончить первый курс выше $90\\%$. Перед тем как окончательно внедрить это на практике, админимтрация факультета должна будет поставить эксперимент. В максимально однородных условиях надо будет набрать два потока. Один по экзаменам, другой по модели. Оба потока надо будет отправить учиться в течение года в одинкаовых условиях, а после надо будет проверить гипотезу о том, что тех, кто взят по модели, отчисляют в среднем меньше. Если гипотеза не будет отвергаться на нужном администрации уровне значимости, модель можно будет внедрять на постоянной основе. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 6 Профили, пропуски и категории\n", "\n", "**Дамми переменной** называют переменную, которая принимает два значения: $0$ и $1$. \n", "\n", "Перейдём к данным, которые мы собрали по профилям вконтакте. Пропуски в этих данных выглядят как-то вот так: \n", "\n", " \n", "\n", "К разному количеству пропусков и разным переменным - разные подходы.\n", "\n", " \n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Если в переменной нет пропусков просто добавим её в нашу табличку.\n", "* Данные, в которых менее сотни пропусков - это сплошные счётчики. Все их мы оставим в выборке. Если у человека нет фоток или записей на стене, мы не смогли их спарсить и указали в табличке пропуск. Если человек удалил свою страницу, то у него также нет ни фоток ни записей. Учитывая, что пропусков мало, будет логично заполнить их нулями. \n", "\n", "```\n", "['profile_albums_cnt',\n", " 'profile_audio_cnt',\n", " 'profile_followers_cnt',\n", " 'profile_friends_cnt',\n", " 'profile_mutual_friends_cnt',\n", " 'profile_notes_cnt',\n", " 'profile_photos_cnt',\n", " 'profile_subscriptions_cnt',\n", " 'profile_videos_cnt',\n", " 'profile_pages_cnt']\n", "```\n", "\n", "* От 100 до 300 пропусков находится в трёх категориальных переменны. Переменные, отвечающие за дату рождения и месяц рождения мы выбрасываем. С переменной, которая сообщает нам о том в каких отношениях состоит человек, мы разберёмся чуть позже. \n", "\n", "```\n", "['profile_bmonth', 'profile_bday', 'profile_relation_cat']\n", "```\n", "\n", "* От 300 до 400 пропусков есть в переменной, которая отвечат за число подарков и в переменной c названием вуза. Вторую первратим в две дамми. Одна дамми будет принимать значение 1, если у человека в профиле указан ранепушка. Вторая будет принимать значение 1, если в профиле ничего не указано. В какой-то степени эта переменная является переменной из будущего. Пропуски в подарках заполним нулями. Скорее всего, пропусков, вызванных удалением страницы в них столько же сколько в счётчиках выше. Остальные пропуски возникли из-за того, что человек скрыл список подарков.\n", "\n", "```\n", "['profile_university_str', 'profile_gifts_cnt']\n", "```\n", "\n", "* От 400 пропусков. Переходим к очень большому числу пропусков. \n", "\n", "```\n", "['profile_byear',\n", " 'profile_false_year_dummy',\n", " 'profile_home_town',\n", " 'profile_faculty_str',\n", " 'profile_user_photos_cnt',\n", " 'profile_change_city_school_cnt',\n", " 'profile_schools_cnt'\n", " 'profile_relation_partner',\n", " 'profile_groups_cnt',\n", " 'profile_alco_love_cat',\n", " 'profile_smoke_love_cat',\n", " 'profile_religion_str',\n", " 'profile_inspired_by_str',\n", " 'profile_life_main_cat',\n", " 'profile_people_main_cat',\n", " 'profile_political_cat',\n", " 'profile_engl_dummy',\n", " 'profile_lang_cnt',\n", " 'profile_last_bukva_class_str']\n", "```\n", "\n", "Большая часть переменных в этом куске таблицы — категориальные переменные. Такие переменные обычно являются одновременно и болью и благом. Рассмотрим одну из таких переменных: политические предпочтения. Кто-то указывает в своём профиле, что он либерал, кто-то, что он центрист, а кто-то вообще ничего не указывает. Давайте рассматривать людей, которые ничего не указали как отдельную категорию. Тогда пропусков в категориальных переменных не будет вообще. В этом состоит **великое благо категориальных переменных.**\n", "\n", "Великая боль заключается в том, что мы не можем заменить в нашей табличке значение либерал на 1, центрист на 2, а консерватор на 3. Никто не гарантирует нам, что политические взгляды упорядочиваются именно в таком формате и быть консерватором лучше, нежели либералом. Выходит, что нам необходимо дробить категориальную переменную на огромное количество дамми-переменных. Если человек указал, что он либерал, переменная принимает значение 1, если нет, то ноль. Если он указал, что консерватор, то переменная принимает значение 1, если нет то ноль и так далее. Такая процедура обработки категориальных переменных называется one hot encoding (одно горячее кодирование). \n", "\n", "В конечном счёте каждая категориальная переменная преобразуется в большое число дамми. Это очень сильно расширяет пространство признаков и является **великой болью категориальных переменных.** Мы добавим категориальные переменные в итоговую таблицу без изменений. В модели мы их пихать в итоге ну будем. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 7 Мультиколинеарность \n", "\n", "\n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Помните эту наклейку? Вы понимаете иронию? Если нет, то не очень понятно как вы сдали первый колок по линалу. Неговоря уже про эконометрику. Ну что ж, давайте разбираться. Это матрица — табличка с наблюдениями. Прям как наши. Первый столбец и последнии два столбца линейно зависимы. Из-за этого, эта матрица не обладает полным рангом. \n", "\n", "Обычно, чтобы оценить модель, матрицы перемножают и обращают. Наличие линейно-зависимых стобцов приводит к тому, что у некоторых матриц определители зануляются и обратные матрицы не находятся. Из-за этого коэффициенты в уравнениях не оцениваются. Такая проблема называется **мультиколинеарностью.** \n", "\n", "На практике обычно встречается несовершенная мультиколиниарность. При ней одни столбцы не в точности равны линейной комбинации других, а примерно равны. Этого уже достаточно, чтобы модель оступилась. Между такими столбцами высокая корреляция. Бороться с мультиколиниарностью довольно просто. Нужно выбросить одну из двух переменных. Две переменные несут в себе одинаковую инфомрацию и оценивать в модели два коэффициента избыточно. \n", "\n", "Ситуация на наклейке называется дамми-ловушкой. Это уникальная ситуация, когда человек неправильно варит фичи и сам себя загоняет в ловушку. Например, если наша категориальная переменная принимает $4$ значения: либерал, центрист, консерватор, не указал, мы можем сварить из неё четыре дамми: либерал ли, центрист ли, консерватор ли, не указал ли свои взгляды. Если мы добавим в модель константу, то есть в матрицу из наблюдений в качестве первого столбца мы вставим единички, то у нас возникнет совершенная мультиколинеарность. Одна из дамми-переменных оказалась избыточной. Мы сами загнали себя в дамми-ловушку. \n", "\n", "Второй составляющей к успешному пониманию шутки на наклейке является знание классического шестого эпизода Звёздных войн. Флот повстанцев, который прилетел на орбиту Эндора, чтобы уничтожить новую Звезду смерти, попал в ловушку. Когда это стало очевидно, [адмирал Акбар решил всем об этом сообщить.](https://www.youtube.com/watch?v=4F4qzPbcFiA) Это было сделано настолько эпично, что попало [в список мемов.](http://knowyourmeme.com/memes/its-a-trap) Я не знаю правильную ли ссылку я прикрепил на сайт с мемами, так как меня на нём забанили. Если там псковское порно или что-то вроде того, я не виноват. \n", "\n", "На Хабре меня пока ещё не забанили. На нём можно прочитать [шикарный текст](https://habrahabr.ru/company/ods/blog/322076/) про мультиколинеарность. На моменте про комплексные собственные числа я обычно впадаю в экстаз. \n", "\n", " **Ещё раз, ещё раз.** Если между признаками высокая корреляция, это может привести к мультиколиниарности и невозможности оценить некоторые модели, которые к ней чувствительны. Посмотрим на корреляции признаков, чтобы узнать, не нужно ли удалять какие-то признаки перед построением модели. \n", "\n", " \n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Вроде бы всё окей и никакие перемменные удалять не нужно. Настоящее адище с коррелированностью ожидает нас впереди. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 8 Фоточки\n", "\n", "Фотки и стены - это последовательности из контента. Каждому человеку соответствует своя последовательность. Мы не можем подставить в регрессию последовательность, потому что все остальные переменные постоянны во времени. В связи с этим, нам придётся переработать все стены и фотки в набор из описательных статистик. В качестве таких статистик мы возьмём фичи вида `что-то_cnt`,`что-то_max`,`что-то_mean`,`что-то_med`. То есть мы подсчитаем количество чего-то, среднее, медиану и максимум чего-то. Это позволит нам описать фотки и стенки более компактно. \n", "\n", "Посмотрим на наши данные по фоткам. В них довольно мало пропусков. В основном наши переменные являются счётчиками. В связи с этим просто заполним пропуски нулями.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь посмотим на то, насколько переменные коррелируют. Вполне ожидаемо, что максимумы будут иногда сильно коррелированы со счётчиками, а средние с медианами. Эти дискрептивные статистики отражают различную информацию о распределениях, однако когда объекты малочисленны, они близки друг к другу и корреляция между ними высокая. \n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В нашу табличку подъехали две проблемы. Переменная `photo_repost_median` имеет со всеми переменными нулевую корреляцию. Всё дело в том, что у всех людей на стенках медианно ноль репостов фоток. Эта переменная неинформативна и её нам придётся удалить. \n", "\n", "Вторая проблема в корреляциях. В выборе между максимумами и счётчиками будем оставлять счётчики, в выборе между средними и медианами, средние. Также между собой коррелируют средние высота и ширина фотки в пикселях. Не очень понятно зачем мы вообще скачали эти две переменные. Удалим их. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 9 Стенки \n", "\n", "Вот тут начнётся долгожданное адище с мультиколинеарностью. На стенке было очень много различных объектов. По каждому из них мы сварили целую гору описательных статистик. В итоге получилась вот такая замечательная штука. \n", "\n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Пропусков довольно мало. Заполним их нулями. Трэш-переменые, в которых много пропусков (текст со стены, эмодзи-след) выбросим. Эмодзи следы, аватары и прочие прелести мы отдельно поисследуем в трэш-части рисёрча. Сейчас же посмотрим на корреляции в счётчиках. \n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Видим нескольо плохих переменных, которые вообще не появились в выборке. Например, рекламные предложения на стенках либо стикеры на стенах. Если честно, я не уверен, что на стенах можно публиковать стикеры. Скорее всего, эта переменная осталась от старой версии API. Весь мусор на помойку.\n", "\n", "Аналогично, из матрицы корреляций для всего остального нам придётся выкинуть все медианы, средние и максимумы, соответствующие мусорным переменным. Также нам придётся удалить часть максимумов и медиан. Кроме того, нам придётся выбросить длину стены, среднее число постов за месяц, число постов с веб-странички, число постов с других устройств. Все они довольно сильно взаимосвязаны. \n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В итоговом наборе останется несколько сильно коррелированных переменных, которые удивляют. Например, корреяляция 0.9 стоит между репостами других пользователей и количеством постов с андроида. Массового андроида при этом на экономе вроде как не наблюдается. Большая часть постов делается с веб-версии. Удалим `wall_repost_user_cnt`. \n", "\n", "Количество фоток очень сильно связано с числом репостов других групп и, в принципе, числом постов. Удалим photo_cnt из выборки. Также забавно, что переменная, отвечающая за промежуток между постом и репостом, сильно коррелирует с длиной стены и числом фоток. Наверное, на остальные корреляции мы забьём. В крайнем случае, мы вернёмся на шаг назад и удалим чего-нибудь ещё." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Частично стены будут коррелировать с фотками. Например, photo_like_max и wall_like_max. Аватары выкладывают на стенку и они срывают куш по лайкам. Из-за этого придётся удалить ещё пару переменных.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "На практике, обычно, делают иначе. Все имеющиеся переменные сортируют по какому-нибудь показателю, описывающему их важность. Многие из таких показателей завязаны на информации Фишера и второй производной от функции правдоподобия, которые обычно изучают в матстате, а потом успешно забывают. После переменные сортируют по важности, находят пулы из коррелированых переменных и из каждого пула оставляют переменную с самым высоким показателем важности.\n", "\n", "Также стоит отметить, что существуют модели, которые плевать хотели на мультиколинеарность. Такие модели самостоятельно зануляют все ненужные коэффициенты. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 10 Подписки и темы \n", "\n", "Как вы помните, по пабликам мы сварили переменные с помощью LDA модели. Для каждого человеа у нас есть список из групп. В каждой группе наблюдаются какие-то темы. Мы просуммировали все доли во всех группах юзера и отнормировали всё это. В итоге у нас для каждого пользователя получился вектор, который характеризует его тематические профиль. Напомню, что темы вышли довольно независимыми друг от друга.Все тематические профили мы будем использовать при прогнозировании отчислений. \n", "\n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 11 Про доверие \n", "\n", "На самом деле, в таблице всё ещё довольно много неадекватных фичей, которые, скорее всего, не несут в себе ровно никакой информации. По крайней мере, нам так интуитивно кажется. Давайте сварим табличку, в которую мы поместим название каждой фичи, её описание, источник, тип и моё доверие к ней. В дальнейшем нам будет удобно работать с этой таблицей при выделении различных подмножеств фичей при оценивании моделей. \n", "\n", " \n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В табличке будет несколько типов переменных: дамми (dummy), категории (cat), действительное число (real), счётчик (cnt) и технические переменные (teh). Также будет несколько источников: профиль вк (profile), приказы (order), стена (wall), фотки (photo), данные от асессоров (assesors) и целевые переменные (target).\n", "\n", "Если в колонке my_opinion стоит $1$, я доверяю этой переменной. Если стоит $0$, я ничего не имею против неё, если стоит $-1$, я читаю эту переменную бесполезной или с чем-то скоррелированой. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 12 Про скалирование и градиентный спуск \n", "\n", "Последней штукой, которую делают при предобработке данных, является скалирование. Обычно, когда обучают модель, хотят минимизировать ошибку, которую она допускает. Пусть наша функция ошибки зависит от двух переменных. Мы хотим найти её минимум. Чтобы сделать это, мы могли бы взять две производные и решить систему уравнений. При больших объёмах данных такой подход может затянуться. Код начинает работать часами. \n", "\n", "Чтобы не затягивать с оцениванием моделей и получать результаты на порядок быстрее, можно минимизировать функцию численно. Обычно это делают с помощью метода градиентного спуска. Мы выбираем какую-то рандомную точку, находим в ней значение градиента функции, которое как вы помните, описывает направление наискорейшего роста функции, и начинаем двигаться в противоположном направлении. В итоге мы постепенно, за несколько шагов, скатываемся достаточно близко к точке минимума. \n", "\n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "На картинке нарисованы линии уровня нашей функции от двух переменных, если кто не понял. Красная стрелочка - это наше скатывание в минимум в ходе градиентного спуска (спасибо, Кэп). \n", "\n", "С градиентным спускам связано довольно много проблем и траблов, на которых обычно акцентируют внимание в курсах по машинному обучению, на одной из первых пар, когда повторяют методы оптимизации. Там же, попутно, рассказывают про всякие ультракрутые алгоритмы как [метод имитации отжига](https://ru.wikipedia.org/wiki/Алгоритм_имитации_отжига) и [генетические алгоритмы](https://ru.wikipedia.org/wiki/Эволюционные_алгоритмы). Мы же ограничися описанием только одной проблемой градиентного спуска. \n", "\n", "Если наши переменные измеряются в разных величинах, например $x$ лежит в диапозоне от $0$ до $1000$, а $y$ от $0$ до $1$, то линии уровня выглядят вытянуто.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Алгоритм из-за этого очень жёстко начинает колбасить. Чтобы такого не происходило, нужно привести $x$ и $y$ в один и тот же диапазон прямо как на первой картинке. Такая процедура называется **скалированием**. Она позволяет предотвратить расколбас. \n", "\n", "Во время предобработки мы не будем проводить скалирование. Табличка, которая будет лежать в папке и которую можно скачать и побаловаться, непроскалирована. Скалироание мы будем делать перед обучением модели. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "На самом деле можно было бы построить куда больше всяких разных рисунков. Например, посмотреть сколько человек указали, что они либеральны, курят и тп. Но мы не будем заниматься этой ерундой и уже наконец перейдём к самой горячей части нашего рисёрча: моделированию отчислений. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.3" } }, "nbformat": 4, "nbformat_minor": 2 }