Лучшие практики тестирования TensorFlow

Это рекомендуемые методы тестирования кода в репозитории TensorFlow .

Прежде чем начать

Прежде чем добавлять исходный код в проект TensorFlow, просмотрите файл CONTRIBUTING.md в репозитории проекта на GitHub. (Например, см. файл CONTRIBUTING.md для основного репозитория TensorFlow .) Все участники кода обязаны подписать Лицензионное соглашение участника (CLA).

Общие принципы

Зависит только от того, что вы используете в своих правилах BUILD.

TensorFlow — это большая библиотека, и зависимость от полного пакета при написании модульного теста для ее подмодулей является обычной практикой. Однако это отключает анализ на основе зависимостей bazel . Это означает, что системы непрерывной интеграции не могут разумно исключать несвязанные тесты для прогонов до и после отправки. Если вы зависите только от подмодулей, которые тестируете в своем BUILD файле, вы сэкономите время всем разработчикам TensorFlow и много ценной вычислительной мощности.

Однако изменение зависимости сборки для исключения полных целей TF накладывает некоторые ограничения на то, что вы можете импортировать в свой код Python. Вы больше не сможете использовать import tensorflow as tf в своих модульных тестах. Но это выгодный компромисс, поскольку он избавляет всех разработчиков от проведения тысяч ненужных тестов.

Весь код должен иметь модульные тесты

Для любого кода, который вы пишете, вам также следует написать модульные тесты. Если вы пишете новый файл foo.py , вам следует поместить его модульные тесты в foo_test.py и отправить его с тем же изменением. Стремитесь к инкрементальному покрытию тестами >90 % для всего вашего кода.

Избегайте использования собственных правил тестирования Bazel в TF.

У TF есть много тонкостей при запуске тестов. Мы постарались скрыть все эти сложности в наших макросах bazel. Чтобы избежать необходимости иметь дело с ними, используйте следующие правила вместо собственных правил тестирования. Обратите внимание, что все они определены в tensorflow/tensorflow.bzl Для тестов CC используйте tf_cc_test , tf_gpu_cc_test , tf_gpu_only_cc_test . Для тестов Python используйте tf_py_test или gpu_py_test . Если вам нужно что-то действительно близкое к собственному правилу py_test , используйте вместо этого правило, определенное в tensorflow.bzl. Вам просто нужно добавить следующую строку вверху файла BUILD: load(“tensorflow/tensorflow.bzl”, “py_test”)

Будьте в курсе, где выполняется тест

Когда вы пишете тест, наша тестовая инфраструктура может позаботиться о запуске ваших тестов на ЦП, графическом процессоре и ускорителях, если вы напишете их соответствующим образом. У нас есть автоматизированные тесты, которые работают на Linux, MacOS, Windows, системах с графическими процессорами или без них. Вам просто нужно выбрать один из перечисленных выше макросов, а затем использовать теги, чтобы ограничить место их выполнения.

  • manual тег исключит запуск вашего теста где угодно. Сюда входит выполнение тестов вручную, в которых используются такие шаблоны, как bazel test tensorflow/…

  • no_oss исключит запуск вашего теста в официальной тестовой инфраструктуре TF OSS.

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

  • Тег no_gpu можно использовать, чтобы исключить запуск вашего теста в наборах тестов графического процессора.

Убедитесь, что тесты выполняются в ожидаемых наборах тестов.

У TF довольно много наборов тестов. Иногда их установка может вызывать затруднения. Могут возникнуть различные проблемы, из-за которых ваши тесты могут быть исключены из непрерывных сборок. Таким образом, вам следует убедиться, что ваши тесты выполняются должным образом. Сделать это:

  • Подождите, пока ваши предварительные заявки на запрос на включение (PR) будут завершены.
  • Прокрутите свой PR до конца, чтобы увидеть проверки статуса.
  • Нажмите ссылку «Подробнее» справа от любого чека Кокоро.
  • Проверьте список «Цели», чтобы найти недавно добавленные цели.

Каждый класс/модуль должен иметь свой собственный файл модульного теста.

Отдельные тестовые классы помогают нам лучше изолировать сбои и ресурсы. Они приводят к тому, что тестовые файлы становятся намного короче и легче для чтения. Следовательно, все ваши файлы Python должны иметь хотя бы один соответствующий тестовый файл (для каждого foo.py должен быть foo_test.py ). Для более сложных тестов, таких как интеграционные тесты, требующие других настроек, можно добавить больше тестовых файлов.

Скорость и время работы

Шардинг следует использовать как можно реже.

Вместо шардинга рассмотрите:

  • Делаем ваши тесты меньше
  • Если вышеуказанное невозможно, разделите тесты на несколько частей.

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

Меньшие тесты лучше

Чем быстрее будут выполняться ваши тесты, тем больше вероятность, что люди будут запускать ваши тесты. Одна дополнительная секунда вашего теста может превратиться в часы дополнительного времени, потраченного разработчиками и нашей инфраструктурой на выполнение вашего теста. Постарайтесь, чтобы ваши тесты выполнялись менее 30 секунд (в неоптимальном режиме!), и делайте их небольшими. Отмечайте свои тесты как средние только в крайнем случае. Инфра не проводит никаких больших тестов ни перед отправкой, ни после отправки! Поэтому пишите большой тест только в том случае, если вы собираетесь определить, где он будет выполняться. Несколько советов, которые помогут ускорить выполнение тестов:

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

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

При использовании тестовых целей bazel небольшие тесты имеют тайм-аут в 1 минуту. Средний таймаут теста составляет 5 минут. Большие тесты просто не выполняются тестом TensorFlow ниже. Однако многие тесты не являются детерминированными по количеству времени, которое они занимают. По разным причинам ваши тесты могут время от времени занимать больше времени. И если вы пометите тест, который выполняется в среднем 50 секунд, как маленький, ваш тест не будет работать, если он будет запланирован на машине со старым процессором. Поэтому стремитесь к тому, чтобы среднее время выполнения небольших тестов составляло 30 секунд. Стремитесь к тому, чтобы среднее время работы для средних тестов составляло 2 минуты 30 секунд.

Уменьшите количество выборок и увеличьте допуски для обучения

Медленное выполнение тестов отпугивает участников. Обучение на тестах может быть очень медленным. Предпочитайте более высокие допуски, чтобы иметь возможность использовать меньше образцов в ваших тестах, чтобы ваши тесты были достаточно быстрыми (максимум 2,5 минуты).

Устранить недетерминизм и хлопья

Написание детерминированных тестов

Модульные тесты всегда должны быть детерминированными. Все тесты, выполняемые на TAP и гитаре, должны каждый раз выполняться одинаково, если на них не влияют никакие изменения кода. Чтобы убедиться в этом, ниже приведены некоторые моменты, которые следует учитывать.

Всегда учитывайте любой источник стохастичности.

Любой генератор случайных чисел или любые другие источники стохастичности могут вызвать нестабильность. Поэтому каждый из них необходимо засеять. Это не только делает тесты менее нестабильными, но и делает все тесты воспроизводимыми. Различные способы установки некоторых начальных значений, которые могут вам понадобиться в тестах TF:

# Python RNG
import random
random.seed(42)

# Numpy RNG
import numpy as np
np.random.seed(42)

# TF RNG
from tensorflow.python.framework import random_seed
random_seed.set_seed(42)

Избегайте использования sleep в многопоточных тестах.

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

Проверьте, не шелушащийся ли тест

Из-за хлопьев сборщики и разработчики теряют много часов. Их трудно обнаружить и сложно отладить. Несмотря на то, что существуют автоматизированные системы для обнаружения нестабильности, им необходимо накопить сотни тестовых запусков, прежде чем они смогут точно составить список запрещенных тестов. Даже когда они обнаруживают, они заносят ваши тесты в список запрещенных, и покрытие тестами теряется. Поэтому авторы тестов должны проверять, не являются ли их тесты нестабильными при написании тестов. Это можно легко сделать, запустив тест с флагом: --runs_per_test=1000

Используйте TensorFlowTestCase

TensorFlowTestCase принимает необходимые меры предосторожности, такие как заполнение всех генераторов случайных чисел, используемых для максимального уменьшения нестабильности. По мере того, как мы обнаруживаем и исправляем новые источники нестабильности, все они будут добавляться в TensorFlowTestCase. Поэтому вам следует использовать TensorFlowTestCase при написании тестов для tensorflow. TensorFlowTestCase определяется здесь: tensorflow/python/framework/test_util.py

Напишите герметические испытания

Герметические испытания не требуют внешних ресурсов. Они набиты всем необходимым и просто запускают любые фейковые услуги, которые могут им понадобиться. Любые сервисы, кроме ваших тестов, являются источниками недетерминизма. Даже при доступности других служб на 99 % сеть может работать нестабильно, ответ rpc может задерживаться, и вы можете получить необъяснимое сообщение об ошибке. Внешними услугами могут быть, помимо прочего, GCS, S3 или любой веб-сайт.