Как использовать разработку через тестирование в рабочем процессе Data Science
Еще одна вещь, которую специалисты по данным и инженеры по машинному обучению должны поучиться у разработчиков программного обеспечения.
Каждый разработчик программного обеспечения знает о разработке через тестирование (или сокращенно TDD), но недостаточно людей в области науки о данных и машинного обучения. Это удивительно, поскольку TDD может значительно повысить скорость и качество проектов в области науки о данных. В этом посте я расскажу вам об основных идеях, лежащих в основе TDD, и приведу пример кода, который иллюстрирует как достоинства TDD для науки о данных, так и то, как на самом деле реализовать его в проекте на основе Python.
Что такое разработка через тестирование?
TDD - это эволюционный подход к разработке программного обеспечения. То есть он полагается на постепенные улучшения, что хорошо сочетается с гибкими процессами.
Самый простой способ понять TDD - это система «Red, Green, Refactor», основанная на рабочей модели, предложенной Кентом Беком в 2003 году:
- Красный: напишите новый тест и убедитесь, что он не прошел. Если он проходит, кодовая база уже охватывает необходимый функционал и не требует дополнительной работы.
- Зеленый: напишите код, который прошел проверку. Самое главное, что все предыдущие тесты тоже должны пройти! То есть новый код дополняет существующую функциональность.
- Рефакторинг: при необходимости измените код. Например, убедитесь, что структура базы кода находится на правильном уровне абстракции. На этом этапе не добавляйте и не изменяйте никаких функций.
Вы также можете думать об этих шагах как о поиске ответов на разные вопросы:
- Красный: Как я могу проверить, поддерживает ли мой код определенные функции?
- Зеленый: Как написать код, который прошел проверку?
- Рефакторинг: Что мне нужно изменить в базе кода, чтобы улучшить ее без ущерба для функциональности?
У этого подхода есть множество преимуществ по сравнению с другими методами:
- Написание тестов заставляет задуматься о том, какие сценарии пользователи могут создавать позже. Хороший тест показывает, что программа должна предоставлять с учетом конкретных входных данных или поведения пользователя в целом.
- Вам нужно написать больше кода, но каждая его часть проверяется дизайном. Таким образом, общее качество повысится.
- Мышление в этой парадигме способствует разработке четко определенных модулей вместо чрезмерно сложных (и трудных в обслуживании) кодовых баз.
Когда (не) использовать TDD в Data Science
Надеюсь, я убедил вас, что TDD - отличная идея для разработки программного обеспечения. При таком подходе, когда мы можем применить эти принципы в науке о данных для получения наиболее существенного эффекта?
TDD, вероятно, не стоит усилий в следующих случаях:
- Вы исследуете источник данных, особенно если вы делаете это, чтобы получить представление о потенциальных возможностях и недостатках этого источника.
- Вы создаете простое и понятное доказательство концепции. Ваша цель - оценить, перспективны ли дальнейшие усилия.
- Вы работаете с полным и управляемым источником данных.
- Вы (и будете) единственным человеком, который работает над проектом. Это предположение сильнее, чем может показаться на первый взгляд, но справедливо для специального анализа.
Напротив, TDD отлично в следующих случаях:
- Конвейер аналитики
- Сложное подтверждение концепции, то есть различные способы решения подзадачи, чистые данные и т. Д.
- Работа с подмножеством данных, поэтому вы должны убедиться, что вы фиксируете проблемы, когда возникают новые проблемы, не разрушая рабочий код.
- Вы работаете в команде, но хотите убедиться, что никто не нарушит работающий код.
Пример TDD: подготовка твита к задачам НЛП
В этом примере я использовал pytest
вместо unittest
из стандартной библиотеки Python. Если вы ищете введение в последний, см. Ссылку внизу этого сообщения.
Чтобы провести вас через процесс TDD, я выбрал простой, но все же реалистичный пример: подготовка списка твитов для дальнейшего анализа. В частности, я хочу основывать анализ на чистых и уникальных твитах. Для этого требуется код для четырех подзадач:
- Очистите твиты от упоминаний других аккаунтов.
- Отфильтровывайте ретвиты.
- Удалите специальные символы из твитов.
- Отфильтруйте пустые строки.
Я сознательно заказал их так, чтобы впоследствии не было идеально. Причина в том, что ряд автономных тестов может покрыть каждую из этих задач. Комбинация и порядок этих задач - отдельный шаг позже.
Поскольку я с самого начала знал об этих четырех задачах, я начал с создания тестовых примеров для всех из них. То есть я придумал образцовые твиты, которые иллюстрируют все эти проблемы. Чтобы они были легко доступны, я создал @pytest.fixture
функцию. Подумайте об этих функциях как о гибких заполнителях для входных значений ваших тестовых примеров. Вот связанный фрагмент кода:
Весь мой код является частью большего tweet_project
модуля, который включает tweet_cleaning
файл со всеми функциями, относящимися к этому примеру. Давайте начнем с красного:
Этот тест не проходит, так как все, что сейчас содержится в функцииclean_mentions
, - это pass
. Следовательно, он возвращает None
вместо пустой строки. Теперь пришло время для Зеленого, то есть написания кода, который прошел тест. В моем примере я использовал регулярное выражение для удаления символа «@» и всего, что было после него, вплоть до следующего пробела:
Теперь тест пройден. Есть ли что-нибудь для рефакторинга прямо сейчас? Ничего, что напрямую влияет на функциональность.
Я использовал тот же метод для остальных трех шагов. Вот тесты для них:
Вы можете видеть, что detect_
функции возвращают логическое значение, которое в дальнейшем можно использовать в качестве фильтра. Соответствующая функция, которую я написал для прохождения этих тестов, выглядит так:
Сейчас четыре функции полностью независимы. У каждого из них есть специальный тест, который гарантирует, что они работают должным образом. Чтобы завершить этот пример, давайте создадим тест, который проверяет, дает ли весь конвейер желаемый результат. То есть я хочу добавить функциональность, которая принимает набор твитов, очищает то, что нужно очистить, отфильтровывает то, что бесполезно, и возвращает набор твитов, готовых для дальнейшего анализа.
Предыдущие тестовые примеры недостаточно охватывают потенциальные сценарии. Вот почему я применил для этого новый pytest.ficture
. Вы также можете видеть, что мой тест охватывает две основные характеристики желаемого результата. Во-первых, он должен вернуть только один твит из нового набора тестов. Во-вторых, мне нужно убедиться, что результатом является list
, а не набор или строка (или что-то совершенно другое), чтобы функции в дальнейшем могли полагаться на это.
Этап красный успешен, потому что тест не пройден. На этапе зеленого я использовал существующие (и, следовательно, протестированные) функции и объединил их так, чтобы тест прошел:
Этот фрагмент кода работает, но необходимость рефакторинга очевидна. Это отчасти сбивает с толку, а отчасти уродливо. Я рекомендую вам попрактиковаться. Однако, что бы вы ни делали с этого момента, все тесты должны оставаться зелеными. Вы не можете добавлять дополнительные функции до начала следующего цикла.
Заключение
Я хочу подчеркнуть, что TDD может быть настолько хороша, насколько хороши тесты, написанные программистом или специалистом по данным. Поэтому очень важно подумать о том, какие сценарии могут произойти. Например, в приведенном выше примере не было тестового примера с двумя упоминаниями. Есть также некоторые предположения, например, что ретвиты в данных всегда начинаются с RT.
Однако это не ограничения TDD, а результат работы людей над сложными проблемами. Преимущества TDD превалируют:
- Каждый шаг разработки тестируется сам по себе, и легко понять, что содержит тест.
- Поскольку каждый шаг основан на предыдущих тестах, гораздо труднее сломать что-то незамеченным. Такой подход значительно снижает потребность в отладке.
- Существует четкий способ добавления дополнительных функций к существующей базе кода: расширить существующий тест или добавить новый.
- Работа в среде TDD поощряет ясное мышление и делает очень маловероятным попадание в тупик или полную путаницу.
Я понимаю, что наука о данных - это не то же самое, что разработка программного обеспечения. Тем не менее, время от времени мыслить как разработчик - очень полезная вещь.
Сообщите мне в комментариях или в Твиттере, помог ли вам этот пост или вы хотите что-то добавить. Я также рад подключиться к LinkedIn. Спасибо за чтение!
Дополнительный материал:
Чтобы познакомиться с TDD с unittest
в Python, я рекомендую эту запись в блоге Дмитрия Расторгуева: