Часть IV • Резюме и заключительное слово
В качестве иллюстрации применения этого на практике рассмотрим тесты для
примера анализа тональностей из главы 9. Просматривая код этого примера, вы
могли заметить файлы
data_test.js
,
embedding_test.js
,
sequence_utils_test.js
и
train_test.js
. Первые три из этих файлов охватывают тестами не относящийся
к модели код и выглядят в точности как обычные модульные тесты. Их наличие
повышает нашу уверенность в правильности формата поступающих на вход моде
ли во время обучения и выполнения вывода данных, а также допустимости наших
операций с ними.
Последний из файлов в этом списке касается самой модели машинного обучения,
а потому заслуживает немного больше нашего внимания. Фрагмент из него приведен
в листинге 12.1.
Листинг 12.1.
Модульные тесты API модели — форм ее входных и выходных сигналов,
а также ее обучаемости
Глава 12. Тестирование, оптимизация и развертывание моделей
481
Этот тест охватывает очень много всего, так что разобьем его на части. Сначала
мы создаем модель, используя вспомогательную функцию. В ходе этого теста нас
не интересует структура модели, мы обращаемся с ней как с «черным ящиком».
Контролируем формы входных и выходных сигналов:
expect(model.inputs.length).toEqual(1);
expect(model.inputs[0].shape).toEqual([null, maxLen]);
expect(model.outputs.length).toEqual(1);
expect(model.outputs[0].shape).toEqual([null, 1]);
Подобные тесты могут уловить проблемы неправильной идентификации измере
ния батчей — регрессия или классификация, форма выходного сигнала и т. д. Далее
мы компилируем и обучаем модель в течение очень маленького числа шагов. Наша
цель — просто убедиться в обучаемости модели, безошибочность, устойчивость
и сходимость нас пока что не волнуют:
const history = await model.fit(xs, ys, {epochs: 2, batchSize: 2})
expect(history.history.loss.length).toEqual(2);
expect(history.history.acc.length).toEqual(2);
Этот фрагмент кода также проверяет выдачу при обучении требуемых метрик
для анализа, ведь если бы мы обучали ее понастоящему, то хотели бы следить за
ходом обучения и степенью безошибочности полученной в его результате модели.
Наконец, пробуем простой вывод:
const predictOuts = model.predict(xs);
expect(predictOuts.shape).toEqual([2, 1]);
const values = predictOuts.arraySync();
expect(values[0][0]).toBeGreaterThanOrEqual(0);
expect(values[0][0]).toBeLessThanOrEqual(1);
expect(values[1][0]).toBeGreaterThanOrEqual(0);
expect(values[1][0]).toBeLessThanOrEqual(1);
Мы не сверяемся с какимто конкретным результатом предсказания, ведь они мо
гут меняться в зависимости от случайных начальных значений весовых коэффици
ентов или возможных будущих модификаций архитектуры модели. Мы проверяем
просто, что получили предсказание и его значение входит в ожидаемый диапазон,
в данном случае от 0 до 1.
Главный урок, который можно из этого извлечь: модель всегда должна успешно
проходить данный тест, вне зависимости от изменения внутренней архитектуры
модели, главное, чтобы не менялся входной/выходной API. Если же тест не прой
ден, значит, в модели есть проблемы. Это легкие и быстрые тесты, обеспечивающие
сильную уверенность в правильности API, подходящие для присоединения к любым
распространенным точкам подключения тестов.
12.1.2. Тестирование с помощью «золотых значений»
В предыдущем разделе мы говорили о модульном тестировании, для которого не тре
буется контроля порогового значения метрики или устойчивости/сходимости обуче
ния. Рассмотрим теперь виды тестирования полностью обученных моделей, начиная
482
Do'stlaringiz bilan baham: |