Статья Автор: Деникина Н.В., Деникин А.В.

Как сохранить и снова использовать сеть XOR

Как сохранить то, чему научилась сеть

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

Здесь полезно вспомнить различие архитектуры и параметров. Архитектура — это описание слоёв и порядка вычислений: в нашем случае два входа, скрытый слой из четырёх нейронов с ReLU и два выхода. Параметры — конкретные веса и смещения, найденные при обучении. Можно сравнить архитектуру с устройством музыкального инструмента, а параметры — с его настройкой. Сохранённая настройка пригодна лишь для совместимого устройства; сама по себе она не сообщает, как соединены его части.

Метод model.state_dict() возвращает именованный набор состояния модели. В нашей сети это веса и смещения двух линейных слоёв; у других модулей в такой набор могут входить и дополнительные сохраняемые тензоры, называемые буферами. Команда torch.save записывает этот набор, а torch.load читает его обратно. Затем load_state_dict помещает значения в уже созданную модель с совместимым устройством.

В следующем примере весь путь собран в одном блоке: мы обучим сеть, запомним её выходы, сохраним параметры и загрузим их в новый экземпляр. Вместо файла на диске используем io.BytesIO — область памяти, которая ведёт себя как двоичный файл. Так можно продемонстрировать сохранение без выбора папки. Вызов buffer.seek(0) возвращает позицию чтения в начало записанных данных. При загрузке map_location="cpu" направляет тензоры на процессор, а weights_only=True ограничивает загрузку предназначенными для такого сценария объектами.



Сообщение Выходы совпали: True означает, что восстановлены не только номера классов, но и сами оценки. Функция torch.allclose сравнивает числовые результаты с допуском на вычислительную точность. Если бы мы только создали второй экземпляр той же архитектуры, но не загрузили параметры, такого совпадения ожидать не следовало бы: одинаковое устройство сети не означает одинакового состояния.

При работе с настоящим файлом вместо буфера можно передать путь: torch.save(model.state_dict(), "xor_weights.pt"). Для чтения используется тот же путь в torch.load, после чего состояние передаётся в load_state_dict. Архитектуру при этом по-прежнему создают в коде. Попытка загрузить веса сети 2 → 4 → 2 в сеть 2 → 8 → 2 приведёт к несовпадению размеров параметров. Поэтому вместе с результатом обучения нужно сохранять сведения об устройстве модели и подготовке входных данных.

Чем применение отличается от обучения

Когда мы применяем сеть, правильная метка нового объекта может быть неизвестна. Она и не нужна для прямого прохода: модель вычисляет оценки только по входу и своим параметрам. Не нужны также вычисление ошибки, обратный проход и шаг оптимизатора. Мы загружаем состояние, подготавливаем вход в прежнем формате и получаем ответ.

При этом две команды часто стоят рядом, но выполняют разные задачи. model.eval() включает режим применения для слоёв, у которых поведение зависит от режима. torch.no_grad() отключает отслеживание операций для градиентов. В нашей простой сети из линейных слоёв и ReLU переключение режима само по себе не меняет формулу ответа, однако это полезная привычка при работе с моделями. Посмотрим, что происходит с отслеживанием производных в небольшом отдельном примере.



Вывод False показывает, что результат не отслеживает градиенты. Это действие no_grad(), а не автоматическое следствие eval(). Модель внутри этого блока новая и необученная: здесь мы проверяем только режим вычислений. Для реального распознавания нужно использовать экземпляр, в который загружены обученные параметры.

Сохранение для применения и сохранение для точного продолжения обучения имеют разный объём. Для применения нашей сети достаточно архитектуры, параметров и правил подготовки входа. Для продолжения эксперимента могут понадобиться также состояние оптимизатора, номер эпохи и другие сведения о ходе обучения. Файлы состояния следует брать из доверенного источника; настройка weights_only=True не заменяет проверку происхождения файла.

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

Проверим понимание

Представьте, что обучение прошло вчера, а новый объект появился сегодня. Какие сведения нужно восстановить для ответа и какие действия обучающего цикла уже не требуются?


Что произойдёт при применении сохранённой модели?

Печать