Используйте естественный язык для проверки поведения ваших моделей машинного обучения.
Мотивация
Представьте, что вы создаете модель машинного обучения для прогнозирования настроений клиентов на основе отзывов. После его развертывания вы понимаете, что модель неправильно помечает некоторые положительные отзывы как отрицательные, когда они перефразированы с использованием отрицательных слов.
Это всего лишь один пример того, как чрезвычайно точная модель машинного обучения может дать сбой без надлежащего тестирования. Таким образом, тестирование вашей модели на точность и надежность имеет решающее значение перед развертыванием.
Но как вы тестируете свою модель машинного обучения? Одним из простых подходов является использование модульного теста:
from textblob import TextBlob def test_sentiment_the_same_after_paraphrasing(): sent = "The hotel room was great! It was spacious, clean and had a nice view of the city." sent_paraphrased = "The hotel room wasn't bad. It wasn't cramped, dirty, and had a decent view of the city." sentiment_original = TextBlob(sent).sentiment.polarity sentiment_paraphrased = TextBlob(sent_paraphrased).sentiment.polarity both_positive = (sentiment_original > 0) and (sentiment_paraphrased > 0) both_negative = (sentiment_original < 0) and (sentiment_paraphrased < 0) assert both_positive or both_negative
Этот подход работает, но может быть сложным для понимания нетехническими или бизнес-участниками. Было бы неплохо, если бы вы могли включить в свои тесты цели и задачи проекта, выраженные на естественном языке?
Вот когда поведение пригодится.
Не стесняйтесь играть и разветвлять исходный код этой статьи здесь:
Что такое вести себя?
behave — это фреймворк Python для разработки, основанной на поведении (BDD). BDD — это методология разработки программного обеспечения, которая:
- Уделяет особое внимание сотрудничеству между заинтересованными сторонами (например, бизнес-аналитиками, разработчиками и тестировщиками)
- Позволяет пользователям определять требования и спецификации для программного приложения.
Поскольку поведение обеспечивает общий язык и формат для выражения требований и спецификаций, оно идеально подходит для определения и проверки поведения моделей машинного обучения.
Чтобы установить поведение, введите:
pip install behave
Давайте используем поведение для выполнения различных тестов на моделях машинного обучения.
Проверка инвариантности
Тестирование инвариантности проверяет, дает ли модель ML согласованные результаты в различных условиях.
Пример проверки инвариантности включает проверку того, инвариантна ли модель к перефразированию. Если модель является вариантом перефразирования, она может ошибочно классифицировать положительный отзыв как отрицательный, если отзыв перефразирован с использованием отрицательных слов.
Файл характеристик
Чтобы использовать поведение для проверки инвариантности, создайте каталог с именем features
. В этом каталоге создайте файл с именем invariant_test_sentiment.feature
.
└── features/ └─── invariant_test_sentiment.feature
В файле invariant_test_sentiment.feature
мы укажем требования к проекту:
Части этого файла «Дано», «Когда» и «Тогда» представляют фактические шаги, которые будут выполняться поведением во время теста.
Реализация шага Python
Чтобы реализовать шаги, используемые в сценариях с Python, начните с создания каталога features/steps
и файла с именем invariant_test_sentiment.py
внутри него:
└── features/ ├──── invariant_test_sentiment.feature └──── steps/ └──── invariant_test_sentiment.py
Файл invariant_test_sentiment.py
содержит следующий код, который проверяет, согласуется ли тональность, создаваемая моделью TextBlob, между исходным текстом и его перефразированной версией.
from behave import given, then, when from textblob import TextBlob @given("a text") def step_given_positive_sentiment(context): context.sent = "The hotel room was great! It was spacious, clean and had a nice view of the city." @when("the text is paraphrased") def step_when_paraphrased(context): context.sent_paraphrased = "The hotel room wasn't bad. It wasn't cramped, dirty, and had a decent view of the city." @then("both text should have the same sentiment") def step_then_sentiment_analysis(context): # Get sentiment of each sentence sentiment_original = TextBlob(context.sent).sentiment.polarity sentiment_paraphrased = TextBlob(context.sent_paraphrased).sentiment.polarity # Print sentiment print(f"Sentiment of the original text: {sentiment_original:.2f}") print(f"Sentiment of the paraphrased sentence: {sentiment_paraphrased:.2f}") # Assert that both sentences have the same sentiment both_positive = (sentiment_original > 0) and (sentiment_paraphrased > 0) both_negative = (sentiment_original < 0) and (sentiment_paraphrased < 0) assert both_positive or both_negative
Объяснение кода выше:
- Шаги идентифицируются с помощью декораторов, соответствующих предикату функции:
given
,when
иthen
. - Декоратор принимает строку, содержащую остальную часть фразы на соответствующем шаге сценария.
- Переменная
context
позволяет обмениваться значениями между шагами.
Запустить тест
Чтобы запустить тест invariant_test_sentiment.feature
, введите следующую команду:
behave features/invariant_test_sentiment.feature
Выход:
Feature: Sentiment Analysis # features/invariant_test_sentiment.feature:1 As a data scientist I want to ensure that my model is invariant to paraphrasing So that my model can produce consistent results in real-world scenarios. Scenario: Paraphrased text Given a text When the text is paraphrased Then both text should have the same sentiment Traceback (most recent call last): assert both_positive or both_negative AssertionError Captured stdout: Sentiment of the original text: 0.66 Sentiment of the paraphrased sentence: -0.38 Failing scenarios: features/invariant_test_sentiment.feature:6 Paraphrased text 0 features passed, 1 failed, 0 skipped 0 scenarios passed, 1 failed, 0 skipped 2 steps passed, 1 failed, 0 skipped, 0 undefined
Выходные данные показывают, что первые два шага выполнены, а последний шаг не выполнен, что указывает на то, что на модель влияет перефразирование.
Направленное тестирование
Направленное тестирование — это статистический метод, используемый для оценки того, имеет ли влияние независимая переменная на зависимую переменную определенное направление, положительное или отрицательное.
Примером направленного тестирования является проверка того, оказывает ли присутствие определенного слова положительное или отрицательное влияние на оценку тональности данного текста.
Чтобы использовать поведение для направленного тестирования, мы создадим два файла directional_test_sentiment.feature
и directional_test_sentiment.py
.
└── features/ ├──── directional_test_sentiment.feature └──── steps/ └──── directional_test_sentiment.py
Файл функций
Код в directional_test_sentiment.feature
определяет требования проекта следующим образом:
Обратите внимание, что «И» добавлено к прозе. Поскольку предыдущий шаг начинается с «Дано», поведение переименует «И» в «Дано».
Реализация шага Python
Код indirectional_test_sentiment.py
реализует тестовый сценарий, который проверяет, положительно ли влияет присутствие слова «круто» на оценку тональности, сгенерированную моделью TextBlob.
from behave import given, then, when from textblob import TextBlob @given("a sentence") def step_given_positive_word(context): context.sent = "I love this product" @given("the same sentence with the addition of the word '{word}'") def step_given_a_positive_word(context, word): context.new_sent = f"I love this {word} product" @when("I input the new sentence into the model") def step_when_use_model(context): context.sentiment_score = TextBlob(context.sent).sentiment.polarity context.adjusted_score = TextBlob(context.new_sent).sentiment.polarity @then("the sentiment score should increase") def step_then_positive(context): assert context.adjusted_score > context.sentiment_score
Второй шаг использует синтаксис параметра {word}
. При запуске файла .feature
значение, указанное для {word}
в сценарии, автоматически передается соответствующей пошаговой функции.
Это означает, что если в сценарии указано, что одно и то же предложение должно включать слово «потрясающий», поведение автоматически заменит {word}
на «потрясающий».
Это преобразование полезно, когда вы хотите использовать разные значения для параметра
{word}
, не изменяя ни файл.feature
, ни файл.py
.
Запустить тест
behave features/directional_test_sentiment.feature
Выход:
Feature: Sentiment Analysis with Specific Word As a data scientist I want to ensure that the presence of a specific word has a positive or negative effect on the sentiment score of a text Scenario: Sentiment analysis with specific word Given a sentence And the same sentence with the addition of the word 'awesome' When I input the new sentence into the model Then the sentiment score should increase 1 feature passed, 0 failed, 0 skipped 1 scenario passed, 0 failed, 0 skipped 4 steps passed, 0 failed, 0 skipped, 0 undefined
Поскольку все шаги пройдены, мы можем сделать вывод, что оценка тональности увеличивается из-за присутствия нового слова.
Минимальное функциональное тестирование
Тестирование минимальной функциональности — это тип тестирования, который проверяет, соответствует ли система или продукт минимальным требованиям и функционален ли он для предполагаемого использования.
Одним из примеров тестирования минимальной функциональности является проверка того, может ли модель обрабатывать различные типы входных данных, например числовые, категориальные или текстовые данные.
Чтобы использовать тестирование минимальной функциональности для проверки ввода, создайте два файла minimum_func_test_input.feature
и minimum_func_test_input.py
.
└── features/ ├──── minimum_func_test_input.feature └──── steps/ └──── minimum_func_test_input.py
Файл функций
Код в minimum_func_test_input.feature
определяет требования к проекту следующим образом:
Реализация шага Python
Код в minimum_func_test_input.py
реализует требования, проверяя, соответствует ли вывод, созданный predict
для определенного типа ввода, ожиданиям.
from behave import given, then, when import numpy as np from sklearn.linear_model import LinearRegression from typing import Union def predict(input_data: Union[int, float, str, list]): """Create a model to predict input data""" # Reshape the input data if isinstance(input_data, (int, float, list)): input_array = np.array(input_data).reshape(-1, 1) else: raise ValueError("Input type not supported") # Create a linear regression model model = LinearRegression() # Train the model on a sample dataset X = np.array([[1], [2], [3], [4], [5]]) y = np.array([2, 4, 6, 8, 10]) model.fit(X, y) # Predict the output using the input array return model.predict(input_array) @given("I have an integer input of {input_value}") def step_given_integer_input(context, input_value): context.input_value = int(input_value) @given("I have a float input of {input_value}") def step_given_float_input(context, input_value): context.input_value = float(input_value) @given("I have a list input of {input_value}") def step_given_list_input(context, input_value): context.input_value = eval(input_value) @when("I run the model") def step_when_run_model(context): context.output = predict(context.input_value) @then("the output should be an array of one number") def step_then_check_output(context): assert isinstance(context.output, np.ndarray) assert all(isinstance(x, (int, float)) for x in context.output) assert len(context.output) == 1 @then("the output should be an array of three numbers") def step_then_check_output(context): assert isinstance(context.output, np.ndarray) assert all(isinstance(x, (int, float)) for x in context.output) assert len(context.output) == 3
Запустить тест
behave features/minimum_func_test_input.feature
Выход:
Feature: Test my_ml_model Scenario: Test integer input Given I have an integer input of 42 When I run the model Then the output should be an array of one number Scenario: Test float input Given I have a float input of 3.14 When I run the model Then the output should be an array of one number Scenario: Test list input Given I have a list input of [1, 2, 3] When I run the model Then the output should be an array of three numbers 1 feature passed, 0 failed, 0 skipped 3 scenarios passed, 0 failed, 0 skipped 9 steps passed, 0 failed, 0 skipped, 0 undefined
Поскольку все шаги пройдены, мы можем сделать вывод, что результаты модели соответствуют нашим ожиданиям.
Недостатки поведения и почему вы все равно должны его использовать
В этом разделе будут описаны некоторые недостатки использования поведения по сравнению с pytest, а также объяснено, почему этот инструмент все же стоит рассмотреть.
Кривая обучения
Использование Behavior-Driven Development (BDD) в поведении может привести к более крутой кривой обучения, чем более традиционный подход к тестированию, используемый pytest.
Контраргумент: акцент на совместной работе в BDD может привести к лучшему согласованию между бизнес-требованиями и разработкой программного обеспечения, что приведет к более эффективному процессу разработки в целом.
Более низкая производительность
Тесты поведения могут быть медленнее, чем тесты pytest, потому что поведение должно анализировать файлы функций и сопоставлять их с определениями шагов перед запуском тестов.
Контраргумент: фокус поведения на четко определенных шагах может привести к тому, что тесты будет легче понять и модифицировать, что сократит общие усилия, необходимые для обслуживания тестов.
Меньше гибкости
поведение более жестко по своему синтаксису, в то время как pytest обеспечивает большую гибкость в определении тестов и фикстур.
Контраргумент:жесткая структура поведения может помочь обеспечить согласованность и удобочитаемость тестов, упрощая их понимание и поддержку с течением времени.
Краткое содержание
Хотя у поведения есть некоторые недостатки по сравнению с pytest, его акцент на совместной работе, четко определенные шаги и структурированный подход все же могут сделать его ценным инструментом для команд разработчиков.
Заключение
Поздравляем! Вы только что узнали, как использовать поведение для тестирования моделей машинного обучения. Я надеюсь, что эти знания помогут вам в создании более понятных тестов.
Я люблю писать о концепциях науки о данных и экспериментировать с различными инструментами обработки данных. Вы можете связаться со мной в LinkedIn и Twitter.
Отметьте этот репозиторий, если хотите проверить код статей, которые я написал. Подпишитесь на меня на Medium, чтобы получать уведомления о моих последних статьях по науке о данных: