Как использовать разработку через тестирование в рабочем процессе Data Science

Еще одна вещь, которую специалисты по данным и инженеры по машинному обучению должны поучиться у разработчиков программного обеспечения.

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

Что такое разработка через тестирование?

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

Самый простой способ понять TDD - это система «Red, Green, Refactor», основанная на рабочей модели, предложенной Кентом Беком в 2003 году:

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

Вы также можете думать об этих шагах как о поиске ответов на разные вопросы:

  • Красный: Как я могу проверить, поддерживает ли мой код определенные функции?
  • Зеленый: Как написать код, который прошел проверку?
  • Рефакторинг: Что мне нужно изменить в базе кода, чтобы улучшить ее без ущерба для функциональности?

У этого подхода есть множество преимуществ по сравнению с другими методами:

  • Написание тестов заставляет задуматься о том, какие сценарии пользователи могут создавать позже. Хороший тест показывает, что программа должна предоставлять с учетом конкретных входных данных или поведения пользователя в целом.
  • Вам нужно написать больше кода, но каждая его часть проверяется дизайном. Таким образом, общее качество повысится.
  • Мышление в этой парадигме способствует разработке четко определенных модулей вместо чрезмерно сложных (и трудных в обслуживании) кодовых баз.

Когда (не) использовать TDD в Data Science

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

TDD, вероятно, не стоит усилий в следующих случаях:

  • Вы исследуете источник данных, особенно если вы делаете это, чтобы получить представление о потенциальных возможностях и недостатках этого источника.
  • Вы создаете простое и понятное доказательство концепции. Ваша цель - оценить, перспективны ли дальнейшие усилия.
  • Вы работаете с полным и управляемым источником данных.
  • Вы (и будете) единственным человеком, который работает над проектом. Это предположение сильнее, чем может показаться на первый взгляд, но справедливо для специального анализа.

Напротив, TDD отлично в следующих случаях:

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

Пример TDD: подготовка твита к задачам НЛП

В этом примере я использовал pytest вместо unittest из стандартной библиотеки Python. Если вы ищете введение в последний, см. Ссылку внизу этого сообщения.

Чтобы провести вас через процесс TDD, я выбрал простой, но все же реалистичный пример: подготовка списка твитов для дальнейшего анализа. В частности, я хочу основывать анализ на чистых и уникальных твитах. Для этого требуется код для четырех подзадач:

  1. Очистите твиты от упоминаний других аккаунтов.
  2. Отфильтровывайте ретвиты.
  3. Удалите специальные символы из твитов.
  4. Отфильтруйте пустые строки.

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

Поскольку я с самого начала знал об этих четырех задачах, я начал с создания тестовых примеров для всех из них. То есть я придумал образцовые твиты, которые иллюстрируют все эти проблемы. Чтобы они были легко доступны, я создал @pytest.fixture функцию. Подумайте об этих функциях как о гибких заполнителях для входных значений ваших тестовых примеров. Вот связанный фрагмент кода:

Весь мой код является частью большего tweet_project модуля, который включает tweet_cleaning файл со всеми функциями, относящимися к этому примеру. Давайте начнем с красного:

Этот тест не проходит, так как все, что сейчас содержится в функцииclean_mentions, - это pass. Следовательно, он возвращает None вместо пустой строки. Теперь пришло время для Зеленого, то есть написания кода, который прошел тест. В моем примере я использовал регулярное выражение для удаления символа «@» и всего, что было после него, вплоть до следующего пробела:

Теперь тест пройден. Есть ли что-нибудь для рефакторинга прямо сейчас? Ничего, что напрямую влияет на функциональность.

Я использовал тот же метод для остальных трех шагов. Вот тесты для них:

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

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

Предыдущие тестовые примеры недостаточно охватывают потенциальные сценарии. Вот почему я применил для этого новый pytest.ficture. Вы также можете видеть, что мой тест охватывает две основные характеристики желаемого результата. Во-первых, он должен вернуть только один твит из нового набора тестов. Во-вторых, мне нужно убедиться, что результатом является list, а не набор или строка (или что-то совершенно другое), чтобы функции в дальнейшем могли полагаться на это.

Этап красный успешен, потому что тест не пройден. На этапе зеленого я использовал существующие (и, следовательно, протестированные) функции и объединил их так, чтобы тест прошел:

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

Заключение

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

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

  • Каждый шаг разработки тестируется сам по себе, и легко понять, что содержит тест.
  • Поскольку каждый шаг основан на предыдущих тестах, гораздо труднее сломать что-то незамеченным. Такой подход значительно снижает потребность в отладке.
  • Существует четкий способ добавления дополнительных функций к существующей базе кода: расширить существующий тест или добавить новый.
  • Работа в среде TDD поощряет ясное мышление и делает очень маловероятным попадание в тупик или полную путаницу.

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

Сообщите мне в комментариях или в Твиттере, помог ли вам этот пост или вы хотите что-то добавить. Я также рад подключиться к LinkedIn. Спасибо за чтение!

Дополнительный материал:

Чтобы познакомиться с TDD с unittest в Python, я рекомендую эту запись в блоге Дмитрия Расторгуева: