Как стать автором
Поиск
Написать публикацию
Обновить
204.02
Альфа-Банк
Лучший мобильный банк по версии Markswebb

Я управляю тестированием ИИ-моделей 4 года. Что я понял за это время?

Уровень сложностиСредний
Время на прочтение13 мин
Количество просмотров1.1K

Привет!

Меня зовут Валентин, я — руководитель направления тестирования моделей машинного обучения в Альфа-Банке. Моя команда занимается тестированием ML-моделей и модельных сервисов для наших клиентов уже более четырех лет, и более трех из них я погружен в наши процессы QA.

Я попал в Альфу через проект компании KTS, над которым работал раньше, и первое время занимался ручным тестированием. За несколько лет прошел путь от линейного тестировщика до руководителя команды из 8 человек, и в этой статье рассказываю о своем опыте и делюсь советами, которые дал бы самому себе несколько лет назад.

Эта статья будет полезна:

  • тем, кто только начинает выстраивать процессы тестирования моделей;

  • начинающим тимлидам QA-команд до 10 человек;

  • тем, кто просто хочет познакомиться с примером организации QA-процесса с нуля.

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

Свои рассуждения в рамках этой статьи я поделю на две категории:

  • Проблема — практическая ситуация, для которой можно подобрать типовое решение.

  • Экзистенциальный вопрос — те задачи, на которые нет «правильных» ответов. Только проверка в реальных практических условиях. В основном это вопросы мотивации, работы с людьми или выбора меньшего из двух зол.

Оглавление:

Уровень 0: когда ты единственный тестировщик

Любой проект начинается с малого — с задач, из которых вырастают процессы и команды. Когда я попал в команду СИМ (система управления моделями), мне поставили задачу: обеспечить функциональное и нагрузочное тестирование ML-моделей перед их деплоем в прод.

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

На тот момент у нас было два типа моделей:

  • Batch — Python-скрипты, которые запускаются по расписанию в Airflow и работают с определённой выборкой клиентских данных (фичи). Результат работы такой модели — так называемый скор, предсказание склонности клиента к определенным продуктам банка. Сформированный скор скрипты записывали в БД Hadoop.

  • Online — классические web-сервисы с взаимодействием через REST API, работают в real-time и вызываются сервисами извне. Модель разворачивается на своём URL и принимает на вход JSON определенного формата. В зависимости от данных в запросе модель делает расчёт и возвращает результат также в формате JSON.

Сначала модель проходит обучение (трейн), в ходе которого она работает с тестовыми данными. Если результат трейна удовлетворяет Data Scientist’а, то на основе mlflow-эксперимента собирается образ модели, и она допускается до вычислений на реальных данных (инференса).

В дальнейшем появились и другие типы моделей...

...такие как:

  • stream-модели, читающие поток сообщений из Kafka;

  • модели с автопереобучением, способные автоматически запускать трейн без участия человека, основываясь на метриках качества и точности расчетов этой модели;

  • каскады — наборы последовательно или параллельно запускающихся моделей, в которых результат работы одной модели передается на вход последующей (управляется оркестратором моделей).

Важный факт: на функциональное тестирование любой из этих моделей установлен SLA в 8 рабочих часов.

Итак, в самом начале я был единственным тестировщиком в команде.

Batch-модели проверял вручную, а именно — занимался проверкой лога работы дага инференса в Airflow и корректности записи скора в Hadoop. Online-модели проверял по чеклистам, поскольку на поддержку тест-кейсов просто не хватило бы ресурса (да и поддерживать их, по сути, незачем). Первое время вёл все чек-листы в Confluence, но довольно быстро перешел на Postman-коллекции — это удобнее во всех планах.

Нагрузочное тестирование проводил с помощью Locust. Работал он кривовато и не особо удобно, но более сложными инструментами пользоваться я ещё не умел, так как до этого не сталкивался с нагрузочным тестированием в принципе. В отчет по результатам тестов входила проверка утилизации ресурсов CP и RAM на дашборде в Grafana, а также проверка времени ответа модели при заданном RPS.

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

Проблема: ресурсов категорически не хватало, отлаженный процесс отсутствовал, а поток моделей рос.

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

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

Уровень 1: команда и её расширение

Согласовав решение расширять команду, я собрал профиль кандидата: нужные технические навыки, опыт, личные качества, мотивация и ценности. Этот профиль служил фильтром на начальных этапах отбора, и помогал оценить соответствие кандидатов требованиям.

Таблица 1. Профиль кандидата при найме
Таблица 1. Профиль кандидата при найме

О процессе собеседования и найме можно писать отдельную статью, так что не буду на них зацикливаться. Здесь я хочу дать две коротких рекомендации:

  1. Нет принципиальной разницы, когда проводить техническое интервью — до или после беседы о софт-скиллах. Кандидат может одинаково не подойти как по хардам, так и по софтам.

  2. Даже после прохождения HR-фильтра будьте готовы к тому, что от большинства соискателей придется отказаться — лучше вас никто не знает, какой кандидат реально подходит проекту и команде. В моем случае доля подходящих кандидатов составила около 15%.

После найма 4 тестеров (1 нагрузочный и 3 авто) у меня появилось время на развитие процессов QA.

С приходом специалиста по нагрузочному тестированию началась разработка отдельной методики нагрузочного тестирования. Мы перешли от упрощенных решений к систематизированному подходу, позволяющему контролировать качество моделей через гибкую настройку параметров нагрузки (RPS, динамика роста, длительность) и использование разнообразных метрик и отчетов. Это стало фундаментом для отказа от принципа «быстрее и проще» в пользу стратегии «сложнее, но качественнее».

И мы бы сразу провели ещё целую пачку реформ, но помимо тестирования моделей у нас появился параллельный проект (сервис для вывода моделей), который также подъедал ресурсы тестировщиков.

Возник экзистенциальный вопрос: кросс-функциональная команда или специализация по проектам?

Работа над двумя параллельными проектами потребовала стратегического выбора. Я взвесил два подхода: либо разделять команду по проектам (глубокая специализация), либо сделать её кросс-функциональной. Каждый подход имел свои преимущества.

Кросс-функциональность и её плюсы:

  • взаимозаменяемость членов команды между проектами;

  • отсутствие единого держателя знаний и компетенций (нет незаменимых);

  • более широкий стек у каждого сотрудника, более активная прокачка в разные стороны;

  • более разноплановые задачи, которые не дают заскучать;

  • более широкий взгляд на продукт в целом.

Специализация на одном проекте и её плюсы:

  • отсутствие скачков между контекстами, больше стабильности для каждого сотрудника и меньше стресса;

  • возможность более глубокого освоения определенного стека;

  • более быстрый онбординг новичков.

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

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

Уровень 2: рост и развитие

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

Я не стал самостоятельно набивать шишки — это долго и дорого — а решил пройти курс повышения квалификации. За несколько месяцев я прошел курс в школе тест-менеджеров от «Лаборатории качества», в ходе изучения которого внедрял лучшие практики, подходы и инструменты в работу своей команды.

Для начала сделал онбординг более зрелым и быстрым. Получение доступов в банкинге — процесс и без того мучительный и небыстрый, а вдобавок и документация по онбордингу у нас была не вполне адаптированной под новичка. Казалось, что легче созвониться и ответить на вопросы по доступам голосом, чем писать все подробно. Но на практике все оказалось иначе.

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

Также мы добавили в нашу работу следующие нововведения:

  • ввели матрицу скиллов для более качественного подбора сотрудников;

  • для оперативного решения проблем на начальных стадиях начали регулярно собирать обратную связь, причем не только от членов команды, но и от смежных команд;

  • определили ключевые метрики для оценки качества тестирования (скорость тестирования, количество дефектов на проде, процент минимального тестового покрытия);

  • добавили встречи 1-to-1 и практику кросс-ревью тестов;

  • выявили основную и самая критичную проблему в текущем процессе тестирования — скорость прохождения ФТ;

  • для решения сложных и повторяющихся проблем стали применять метод «5 почему». На нем хочу остановиться чуть подробнее.

Метод «5 почему» интуитивно понятен, относительно прост и быстро применим на практике. Каждую идею и проблему мы пропускаем через фильтр уточняющих вопросов, раскрывающих их суть, и в итоге получаем список истинных причин, которые детализировать глубже уже невозможно. С этими причинами мы и работаем дальше.

Например, одна из проблем заключалась в возникновении непонятных багов, дебаг которых занимал много времени и затягивал процесс вывода модели в прод. Назовем её проблемой первого уровня. Почему она возникает? Потому что нет глубокого понимания, как работает система. Это уже «подпричина второго уровня».

Дальше тем же способом выявляем «подпричины третьего уровня»:

  1. Нет документации/неочевидно, где она лежит.

  2. Непонятно, кто может проконсультировать/пошарить экспертизу.

  3. Не хватает знания стека технологий.

Это уже понятные проблемы, с которыми можно работать. А именно:

  1. Узнать, где есть документы, и поставить задачи на разработку недостающих инструкций.

  2. Уточнить у коллег их зоны ответственности и компетенции.

  3. Изучить стек технологий самостоятельно или пройти релевантный курс.

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

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

Казалось бы, проще всего железной рукой раскидать тикеты и ждать их выполнения. Жаль, конечно, но такой подход работает только в армии, и то не всегда. Нельзя ставить задачи наугад. Они должны быть осмысленными, посильными и — в идеале — интересными человеку, который их выполняет.

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

Чтобы лучше понимать свою команду, я составил профиль для каждого сотрудника на основе 1-to-1 встреч. В них собрал информацию о мотивации, потребностях в развитии, определил уровень профессиональной зрелости и, соответственно, адаптировал стиль управления: новичкам давал больше поддержки, объяснения и контроля выполнения, а опытным — свободу принятия решений. Это помогло предотвратить лишний микроменеджмент и снизить риски от возможных ошибок менее опытных коллег.

Основные выводы, которые хочу подсветить:

  1. больше делегирования — залог успеха любого руководителя;

  2. необходимое (но не достаточное) условие оптимизации работы в команде — знание слабых и сильных сторон своих сотрудников;

  3. зона ответственности сотрудника не должна опережать его скиллы, т.е. не стоит делегировать задачи тем коллегам, в подготовке которых вы еще сомневаетесь (здесь как с обгоном на трассе: не уверен на 100 % — не обгоняй).

Таблица 2. Пример определения мотивационных факторов
Таблица 2. Пример определения мотивационных факторов
Таблица 3. Пример определения стиля руководства
Таблица 3. Пример определения стиля руководства

Уровень 3: автоматизация тестов

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

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

В основу архитектуры наших автотестов лёг код, из которого автоматически генерируется документация в виде чек-листов с помощью AllureTestOps. То есть автотесты уже сами по себе являлись основой для тестовой документации. Так мы отказались от её ручной поддержки: можно просто писать автотесты, а всё остальное сделает автоматическая генерация тестов из кода. Круто!

Наш стек: Python + Allure + Requests + Pytest.

Флоу тестов: тесты вызываются в Jenkins при сборке инференса модели, достаточно отметить соответствующий чекбокс в пайплайне. Лог автотеста можно посмотреть в даге Airflow.

Дальше автоматически генерируется отчёт о результатах работы автотестов в виде Allure Report, и с помощью утилиты Allurectl он отправляется на облачный сервер Allure Testops. Там на основе отчета генерируется тестовая документация к данной модели.

По сути, автотесты проверяют REST API модели через комплекс сценариев, реализованных на pytest с интеграцией Allure для отчетности. Основная работа происходит по следующей схеме:

1. Подготовка данных и запросов.

Каждый тест начинается с формирования тестового payload (с использованием RequestData.payload), где:

  • для негативных сценариев специально удаляются обязательные/опциональные поля через Helper.delete_key();

  • данные копируются через dc().

2. Отправка и валидация.

Через кастомный ApiClient.post отправляются POST-запросы на заданный URL, после чего:

  • проверяются статус-коды ответа;

  • для успешных запросов ответ проходит валидацию через model_income_t0_validator();

  • все действия логируются через logger_fixture.

3. Параметризованное тестирование.

Особенность реализации — использование @pytest.nank.parametrize для:

  • последовательной проверки всех обязательных полей (RequestRequirements_REQUIRED_KEYS);

  • верификации опциональных полей (RequestRequirements_OPTIONAL_KEYS).

4. Тесты интегрированы в инфраструктуру через:

  • кастомные маркеры pytest (@pytest.nank.*) для классификации тестов;

  • Allure-аннотации (@allure.title, @allure.fastup) для детализированной отчетности;

  • фикстуры (например, logger_fixture) для сквозного логирования.

Такой подход обеспечивает проверку как happy path (успешных сценариев), так и edge cases (обработки ошибок), что соответствует принципам тестирования API. Все тесты независимы благодаря изолированной подготовке данных и могут выполняться как по отдельности, так и в составе test suite.

А как выбрать тесты под автоматизацию?

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

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

Неправильный выбор функциональности под автотесты может привести к следующим проблемам:

  • автоматизация не экономит, а крадет еще больше времени на поддержку тестов в актуальном состоянии;

  • тестирование становится дороже, при этом его метрики качества остаются прежними или деградируют.

К этому этапу медианное время на разработку коллекции составляло 4 ч, а ревью коллекции — 1 ч. Мы выделили следующие плюсы автоматизации на фоне Postman:

  1. Можно не поддерживать тестовую документацию за счет интеграции с Allure TestOps, это экономит время и дает более удобную и полную отчетность в Allure в сравнении с Postman.

  2. Нет ограничения на количество пользователей, в отличие от бесплатной версии Postman.

  3. Менее ограниченная логика — раньше сложные сценарии (условия, циклы) требовали написания скриптов на JS, что не подходило к нашему стеку.

  4. Гибкость — в Postman было сложно реализовать динамическую параметризацию (например, генерацию данных на лету).

  5. Если тест в Postman падал, разобраться в причине было сложнее, чем в случае с кодом.

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

Также часто для оценки выгоды от автоматизации стали использовать следующую формулу (чем выше ROI, тем выгоднее):

  1. Определяем время на прохождение ручного теста.

  2. Умножаем на количество прохождений тестов в релиз/спринт.

  3. Прибавляем буферное время (сколько закладываем на риски), получаем время, которое будет потрачено на ручное тестирование.

  4. Оцениваем время на разработку автотестов.

  5. К оценке прибавляем время на анализ результатов автотеста, умноженное на количество прохождений автотестов в спринт/релиз.

  6. Прибавляем буферное время, получаем время, которое будет потрачено на автоматическое тестирование.

  7. Сравниваем результаты, полученные в п.3 и п.6. Если п.6 меньше, то автоматизацию считаем выгодной.

Рис. 1. Пример кода автотеста для онлайн модели
Рис. 1. Пример кода автотеста для онлайн модели
Рис. 2. Allure-отчетность
Рис. 2. Allure-отчетность

После автоматизации тестов для online-моделей мы взялись за тесты для batch-моделей. 

Целей было две: 

  1. Автоматически проверять запись скоров в БД.

  2. Добавить проверку работы модели на невалидных синтетических данных, чтобы увеличить тестовое покрытие.

Архитектура решения следующая: сначала запускается пайплайн инференса batch-модели, и на этом же этапе происходит выкачка библиотеки с синтетическими тестами из Artifactory. После завершения инференса происходит проверка ключа autotests=true в конфиге с моделью. Далее проверяется соответствие кода модели стандартному шаблону и запускается код автотеста. Результаты работы выводятся в лог. Если модель не соответствует шаблону или ключ autotests=false отсутствует, то тесты не запускаются.

  1. Тесты запускаются из тестовой ветки проекта в инференс-пайплайне Jenkins.

  2. После загрузки данных read_data берется несколько строк из датафрейма pandas/pyspark и генерируем синтетические данные для проверки негативных кейсов (пустые строки, NaN, неверный тип данных и т.д.).

  3. На этапах препроцессинга и инференса используем сгенерированные синтетические данные по тестам.

  4. По завершении тестов результаты выводятся в лог пайплайна.

  5. Очищаются тестовые данные.

Рис. 3 Архитектура batch-автотеста
Рис. 3 Архитектура batch-автотеста

Виды проверок:

  1. Проверка класса на наличие необходимых методов.

  2. Проверка класса на тип входных аргументов.

  3. Проверка скоров в БД на граничные значения класса эквивалентности.

  4. Проверка скоров в БД на валидный тип данных.

  5. Проверка на невалидных типах данных (неверный тип данных в фичах, пустые строки, Nan, дубли).

Проблема + Экзистенциальный вопрос. Как и зачем?

Необходимость разработки таких тестов сразу встала под вопрос. Зачем нам тратить время на разработку, если разница во времени между ручной и автоматической проверками не так велика?

Тут мы смотрим на перспективу: один раз пишем код для шаблонной модели, и он работает всегда в любое время суток, достаточно datascientist’у заполнить конфиг для его работы. К тому же, у синтетических тестов в проверке работы модели есть преимущество: если в БД запишутся невалидные данные, то модель не упадет, а выдаст ошибку.

Планы на будущее

До конца 2025 года мы планируем следующие улучшения:

  1. Прямо сейчас мы разрабатываем автотесты для AutoML-моделей. Используя наработки для batch-моделей, мы можем покрыть тестами и самообучающиеся модели.

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

  3. Будем развивать индивидуальные планы развития (ИПР) и матрицу грейдов. Это поможет сделать развитие команды более прогнозируемым и даст каждому члену команды понимание, как ему развиваться и какие именно скиллы ему для этого нужны.

Выводы

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

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

  2. Делегирование — ключ к росту. Но только осмысленное и структурированное, с учетом навыков и мотивации членов команды.

  3. Автоматизация требует анализа эффективности (Например ROI). Если автотест «дороже» и «медленнее» ручного — стоит задуматься о его целесообразности.

  4. Автоматизация экономит время, но требует правильного подхода к реализации.

  5. Непрерывное улучшение процессов — залог развития.

  6. Сотрудничество с другими командами помогает решать сложные технические задачи.

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

Теги:
Хабы:
+12
Комментарии3

Публикации

Информация

Сайт
digital.alfabank.ru
Дата регистрации
Дата основания
1990
Численность
свыше 10 000 человек
Местоположение
Россия