- 5.2.2.1 Метод прямого прохода Multi-Head Self-Attention
- 5.2.2.2 Методы обратного прохода Multi-Head Self-Attention
- 5.2.2.3 Методы работы с файлами
5.Методы работы с файлами
Мы уже далеко продвинулись в работе над реализацией алгоритма Multi-Head Self-Attention. В предыдущих разделах мы уже реализовали стандартными средствами MQL5 операции прямого и обратного прохода нашего класса CNeuronMHAttention. Теперь для возможности полноценного его использования в наших моделях необходимо дополнить его методами работы с файлами. Как бы вам ни казалось, корректная работа этих методов для промышленного использования не менее важна корректной работы методов прямого и обратного проходов.
Да, мы можем создать модель и протестировать ее работу и без сохранения результатов обучения. Но для проведения повторного теста нам придется заново обучать нашу модель. А в процессе промышленной эксплуатации нам бы не хотелось повторять процесс обучения каждый раз. Напротив, довольно часто тратятся большие усилия на разработку и обучения модели на больших наборах данных, что позволяет построить по-настоящему рабочую модель. При этом ожидается, что в процессе промышленной эксплуатации достаточно будет запустить модель, и она будет полностью готова к функционированию на реальных данных. Поэтому, подходя к работе над методами работы с файлами, мы должны продумать их функционал таким образом, чтобы на выходе мы полностью восстановили состояние модели с минимальными затратами. Что ж, мы уже не раз выполняли данную работу и давайте воспользуемся уже отработанным алгоритмом.
Вначале посмотрим на структуру нашего класса многоголового внимания CNeuronMHAttention.
class CNeuronMHAttention : public CNeuronAttention
|
На первый взгляд нет ничего сложного. В теле класса объявляется лишь один сверточный слой m_cW0 и одна переменная m_iHeads, указывающая на количество используемых голов внимания. Основное количество объектов наследуется от родительского класса CNeuronAttention. Мы уже создали аналогичный метод при работе над родительским классом, а сейчас можем им воспользоваться. Я советую еще раз заглянуть в метод родительского класса CNeuronAttention::Save и убедиться, что в нем есть сохранение всех необходимых нам данных. Только после этого можно приступать к работе над методом сохранения данных текущего класса. На этот раз здесь все действительно очень просто.
В параметрах метод CNeuronMHAttention::Save получает хендл файла для записи данных. В теле метода мы сразу передаем полученный хендл в аналогичный метод родительского класса, в котором уже реализованы все контроли. Помимо контролей в методе родительского класса реализовано сохранение унаследованных объектов и их данных. Таким образом, проверяя результат работы метода родительского класса, мы сразу получаем консолидированный результат прохождения блока контролей и сохранения унаследованных объектов. Нам остается лишь сохранить количество используемых голов внимания и данные сверточного слоя m_cW0.
bool CNeuronMHAttention::Save(const int file_handle)
|
Метод загрузки данных CNeuronMHAttention::Load строится по правилу загрузки данных из файла в строгом соответствии с последовательностью их записи. Поэтому в теле метода полученный в параметрах хендл файла мы сразу передаем в аналогичный метод родительского класса и проверяем результат его работы.
bool CNeuronMHAttention::Load(const int file_handle)
|
После выполнения операций метода родительского класса мы считываем из файла количество используемых голов внимание и данные внутреннего сверточного слоя m_cW0. С загрузкой константы все очень просто: мы просто читаем значение из файла и сохраняем в нашу переменную m_iHeads. Но перед обращением к методу загрузки мы должны проверить тип загружаемого объекта. Только при совпадении типов объектов мы вызываем метод загрузки данных и проверяем результат выполнения операций.
m_iHeads = FileReadInteger(file_handle); |
Ожидается, что после успешного выполнения операций родительского класса мы получим полностью восстановленные унаследованные объекты. Но давайте вспомним, что объекты мы унаследовали, а инициализировали их в соответствующем методе этого класса и с параметрами отличными от родительского класса. Ведь в этом классе практически для всех объектов мы делали поправку на количество используемых голов внимания. При этом в методе загрузки данных родительского класса мы не только загружаем данные объектов из файла, но и инициализируем несохраненные объекты. Это такие объекты, данные которых используются только в рамках одной итерации прямого и обратного проходов.
Поэтому возвращаемся к методу родительского класса и еще раз критически оцениваем все операции. И тут внимания нужно обратить на следующие строки кода.
bool CNeuronAttention::Load(const int file_handle)
|
В них инициализируется буфер матрицы коэффициентов зависимости m_cScores. Как можно заметить, инициализация проходит нулевыми значениями в размере достаточном только для одной головы внимания. А это не соответствует требованиям нашего алгоритма Multi-Head Self-Attention. Логично будет добавить в метод загрузки нашего класса повторную инициализацию буфера с приданием ему нужного размера.
//--- инициализируем Scores
|
После успешного завершения выполнения всех операций мы выходим из метода с положительным результатом.
На этом мы завершаем работу стандартными средствами MQL5 над классом CNeuronMHAttention, в котором мы реализовали алгоритм Multi-Head Self-Attention. В следующем разделе мы дополним его функционал возможностью совершения многопоточных операций с помощью технологии OpenCL.