Заповеди начинающего DS

682b6d86e6403a92b53975f2dc9d086d.png

Привет! Меня зовут Mashkka Тихонова. Я — Senior Data Scientist, а еще я активно преподаю все, что связано с ML, DS и DL — помогаю людям начать свой путь в Data Science!
За годы преподавания у меня накопилось много советов для тех, кто только-только начинает свой путь в DS. Этими советами я всегда делюсь со студентами, а теперь решила собрать их в одном посте, написанном по мотивам моей серии постов в tg .

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

Замечение: и не заваливайте горизонт без нужды на фото, пожалуйста, сделать его ровным — это всего пара секунд!

Заповедь 1. Гуглить, гуглить, гуглить и еще раз гуглить!

7191c935aed4c58ed172ae13fad1cd8c.png

Самый важный навык, который первым делом должен освоить любой DS — это гуглить!  

  • Ищешь ответ на вопрос? — Гугли, а только потом спроси!

  • Не работает код? — Гугли!

  • Выбираешь алгоритм? — Гуглить!

  • Нужен пример кода? — Гугли!

  • Не знаешь, как правильно закодить? — Гугли!

  • Нужны статьи по теме? — Гугли!

  • и т. д. и т. п.

Гугл, конечно, знает не все, но ооооочень многое. И прежде, чем бежать с вопросами к коллегам, знакомым, преподавателям — загуглите. Скорее всего вы найдете ответ.

И помните не забывайте золотые правила Гуглежа:

  1. Гуглить только в Гугле, лучше на ванильном Google.com. Никакого поиска в Яндексе, Mail или Bing, если вы ищите что-то по Data Science. Не хочу обидеть другие поисковики, я сама люблю поиск Яндекса, но для целей Data Science — это Google и только он.

  2. Гуглить только на английском: не пытайтесь что-то искать на русском языке — это боль. Даже если не знаете английский, лучше перевести ваш запрос на английский и загуглить так. 

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

Заповедь 2. Всегда фиксируйте random_state

Если у вас в коде есть какая-то случайность (случайное разбиение на train и test, случайная инициализация и т. д.), то лучше эту случайность зафиксировать. Для этого у всех методов обычно есть специальные параметры  типа ransdom_state, seed, random_seed и т.п.

Согласитесь, обидно: перезапустил ноутбук и скор упал или вообще все сломалось? Поэтому старайтесь, чтобы ваш код работал детерминировано и всегда выдавал один и тот же результат.

Всем рассказываю страшилку про своего стажера. Он долго-долго обучал нейросеть, а когда я попросила его оценить качество и прогнать модель на тесте, он с ужасом понял, что забыл зафиксировать random_state при разбиении на train и test, а код с разбиением заглушил. Странная ситуация: есть модель, есть данные, а оценить ее нельзя — непонятно, какие данные использовались при обучении и на чем её тестировать. Пришлось переобучать.

train_X, test_X, train_Y, test_Y = train_test_split(X, Y,
                                                    test_size=0.2,
                                                    random_state=42)

Итог: random_state = 42 наше все!

Заповедь 3. Читайте логи

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

Единого «правила чтения» логов нет, у каждого здесь свой «авторский» подход. Я обычно вначале смотрю, на какой строчке все сломалось, а потом прокручиваю в самый конец — в последней строчке как правило содержится ключевая информация, что не так. Если этого оказалось недостаточно, то начинаю читать логи снизу вверх, постепенно раскручивая ошибку из недр Python вверх как спираль. А еще чаще гуглю (в Google! по правилу 1) самую последнюю строчку ошибки и в большинстве случаев на Stack Overflow нахожу ответ.

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


Например:
На картинке sklearn прямо пишет о том, что в наших обучающих данных содержится всего один признак или пример надо попросту сделать train_X.reshape

1345366533e5107133c7b74b8e04534e.png

А дальше просто смотрим на размерность X_train и обнаруживаем, что да, в данных действительно всего один признак:

79fcde93bb684c6cb83f2dbb288bb8f3.png


Значит, по совету sklearn из логов надо написать так:

model.fit(train_X.reshape(-1, 1), train_Y)

Или пример чуть посложнее:

При попытке зафитить GridSearch на ирисах Фишера (задача классификации на 3 класса), оценивая качество с помощью F1:

clf = GridSearchCV(svc, parameters, scoring = 'f1')
clf.fit(iris.data, iris.target)

sklearn подскажет нам, что необходимо указать тип агрегации, среди прочего выдав в логах вот такую подсказку:

ValueError: Target is multiclass but average='binary'. Please choose another average setting, one of [None, 'micro', 'macro', 'weighted'].

нам остается просто последовать его совету и поменять scoring = 'f1' на scoring = 'f1_micro':

clf = GridSearchCV(svc, parameters, scoring = 'f1_micro')
clf.fit(iris.data, iris.target)

Заповедь 4. Едим слона по частям — дебажим по кускам

Как часто бывает, что код не работает и не понятно почему. Или еще хуже, работает неверно и в переменных оказывается записано что-то непонятное совсем с потолка.

Например, такое нередко случается, если вы пытаетесь разом совместить всю предобработку данных вместе с обучением модели и запихиваете все это в одну ячейку кода, оборачивая в ColumnTransformer, Pipeline, и GridSearchCV.

В этом случае дебаг вам в помощь! И если вы пока еще не освоили базовые логгеры и такую удобную штуку как VS Code (а я очень рекомендую вам это сделать и как можно скорее), то самое простое — это понаставить побольше принтов — print (можно прямо после каждой строчки кода), чтобы отследить что оказалось в каждой переменной на каждом шагу. Чаще всего это помогает найти ту самую строчку, где все пошло не так.

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

Что касается ColumnTransformer и Pipeline, то на этапе первично написания кода, я обычно обхожусь без них, так как они очень усложняют дебаг. А оборачиваю в них все уже в конце, после первичной отладки кода. Но тут уже вкусовой вопрос.

Замечание:   еще раз подчеркну, что специально сейчас не касаюсь сейчас всяких логгеров и прочих более продвинутых фишек откладки кода, а говорю про самый упрощенный дебаг на уровне принтов. Такой дебаг доступен всем, даже самым-самым начинающим DS, которые работают в GoogleColab. Если хотите начинать трушно, я только за!

Заповедь 5. Никогда не обучайтесь на тесте

Никогда, никогда, никогда не обучайте модель на тесте! То, что модель ни в каком виде не должна видеть тест на этапе обучения вы должны запомнить как 2×2 = 4.* Показать (или дать подглядеть) модели тест — это все равно, что прорешать со школьником его вариант ЕГЭ в классе, а потом радоваться, что он получил высокий балл. Это, конечно, здорово, но к реальным знаниям, то есть качеству модели, этот результат на таком утекшем тесте отношения не будет иметь.
*В примере 2×2 = 4 я предполагаю, что мы находимся в кольце целых чисел со стандартной операцией умножения.

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

X_train_scaled = scaler.fit_trasnform(X_train)
(Эту строчку сделать зачеркнутой!) X_test_scaled = scaler.fit_trasnform(X_test)
X_test_scaled = scaler.trasnform(X_test)

Строчка X_test_scaled = scaler.fit_trasnform(X_test) может полностью испортить ваш эксперимент, ведь обучение на тесте приведет к тому, что на обучающей и тестовых выборках данные будут отмасштабированы по-разному, а значения train и test не будут биться между собой.

Пример:

Допустим у нас есть набор данных x_train = [0, 10, 20], x_test = [0, 5, 10], к которому мы хотим применить минимаксное преобразование (это преобразование приводит данные в диапазон [0–1]). 

Отталкиваясь от значений в x_train, получаем вот такое соответствие:

0 => 0
5 => 0.25
10 => 0.5
20 => 1

В итоге получаем, что X_train_scaled = [0, 0.5, 1], X_test_scaled = [0, 0.25, 0.5].

Но стоит руке дрогнуть и случайно написать:
X_test_scaled = scaler.fit_trasnform(X_test) и, о ужас, соответствие на тесте изменится и для теста мы получим вот такую вещь:

0 => 0
5 => 0.5
10 => 1

и в итоге получим X_train_scaled = [0, 0.5, 1], X_test_scaled = [0, 0.5, 1], что полностью собьет с толку вашу модель.

Заповедь 6. Не называйте переменные также как библиотеки или функции

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

TypeError: 'ResultSet' object is not callable. 

Как это связано с функцией/пакетом? НепАнЯтнА! А загулить такое по названию метода не получается никак… Тяжело…

Помню, одна студентка парсила данные и импортировала BeautifulSoup — библиотеку для работы с html-документами по именем soup:

from bs4 import BeautifulSoup as soup

Потом в совершенно другой ячейке написала:

soup = soup(html, 'html.parser')
print(soup.prettify()[:2000])

А потом долго недоумевала, почему при первом запуске все окей, но если перезапустить ячейку, то все ломается и что не так.

А дело было в том, что после выполнения soup = soup(html, 'html.parser'), имя soup превращается в обычную переменную. Вот такие дела…

Заповедь 7. Со StackOverflow копировать код надо из ответа, а не из вопроса

89b35887c00a28329631648191c8da53.png

Заповедь эта немного шуточная и написана под вдохновением одного из подкастов Андрея Себранта, а ее название говорит само за себя, к нему и добавить-то нечего. Разве что прочитайте все ответы на вопрос и найдите тот, который действительно даёт правильный ответ!

Заповедь 8. Не забывайте про бэкап — делайте промежуточные чекпоинты

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

Или при парсинге огромного сайта после тысячной страницы вас забанили по API, а ваш код упал.

Подобных случаев, увы, в практике любого DS-ника не избежать. Но от них можно хотя бы частично обезопасить себя, если делать промежуточные чекпоинты модели, сохранять раз в несколько итераций обработанные данные и логгировать прогресс. Тогда вам не придется перезапускать все с нуля, и вы сможете частично восстановить результат.

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

Заповедь 9. Трепещите перед мощью и коварностью регулярок

429f0146c283704027a537b632d6a0cd.png

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

Ведь не зря говорят, когда некоторые люди сталкиваются с проблемой, думают «Я знаю, я решу её с помощью регулярных выражений.» Теперь у них две проблемы ©

У регулярок обожают вылезать крайние случаи, в которых они работают неправильно (спецсимволы, выражения, не удовлетворяющее шаблону в конце/начале строки и т. п.). А потом начинается бег по кругу: вы пытаетесь учесть крайний случай и регулярка начинает неправильно работать в другом месте и т. д. и т. п. Как итог, она разрастается до монструозных размеров, и вообще становится непонятно, когда она работает, а когда нет.

Надеюсь, я вам не слишком напугала. Не стоит боятся регулярок, они действительно удобные, но с ними надо всегда быть аккуратными, вот и все!

Заповедь 10 (заключительная). Встав перед выбором, проводите эксперимент

Если вы не знаете, как правильно, то Google, как вы знаете, знает все. Поэтому, в любой непонятной ситуации, по заповеди 1, мы гуглим. Но что делать, если у вас есть два варианта, оба правильные, но вы не знаете какой из них лучше взять?

Встав перед таким выбором настоящий Data Scientist проведет эксперимент: попробует оба варианта и выбирете тот, который окажется в данной ситуации лучше (например, на валидации).

Студенты часто спрашивают: «Мария, вот у меня есть какой-то там датасет (который я лично никогда и в глаза не видела) и какая-то там задача (о которой я пока имею очень смутное представление только с их слов), как мне лучше всего заполнить пропуски в данных, медианой или средним? А какой метод снижения размерности выбрать?»

На подобные вопросы я всегда отвечаю так: «Сравните и выберете то, что даст лучший результат! Только эксперимент даст вам ответ, что будет лучше в вашем конкретном случае!»

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

Итого: в любой непонятной ситуации — гугли, а в любой неоднозначной — проводи эксперимент!

Заключение:
Буду рада, если эти советы помогут вам избежать самых типичных ошибок на пути начинающего Data Scientist. А возможно, если вы уже опытный DS-ник, они вас позабавят и вы с улыбкой вспомните, как сами через это прошли. А еще обязательно прочитайте мои вредные советы Айтишнику!

Также приглашаю всех посетить открытый урок для новичков в ML на тему «Первичный анализ данных с Pandas», который пройдет я проведу уже сегодня. На встрече обсудим, зачем нужен первичный анализ данных в машинном обучении, какие существуют инструменты для первичного анализа данных в Python, как визуализировать данные и какая преобработка данных нужна в ML. Запись на урок доступна по ссылке ниже. А для тех, кто не успеет зарегистрироваться, по этой же ссылке будет доступна запись урока.

© Habrahabr.ru