Вопрос или проблема
Существует модифицированная модель efficientnet TF, которую я пытаюсь смоделировать в pytorch. Я внес изменения в архитектуру модели в pytorch, выгрузил веса модели TF и загрузил их обратно в новую модель pytorch. Выгрузка весов в TF выполняется с помощью следующего кода:
model = tf.saved_model.load(model_path)
ws = []
for i in range(len(model.variables)):
ws.append((i, model.variables[i].name, model.variables[i].numpy()))
with open("manually_dumped_contentnet_weights.pkl", "wb") as ofile:
pickle.dump(ws, ofile)
Форма весов в pytorch, похоже, совпадает с архитектурой и импортированными весами (после транспонирования, необходимого между conv2d и depth-wise conv2d). Я могу запустить модель без каких-либо ошибок. Но вывод сильно отличается от вывода модели TF.
Я заметил, что в коде TF модель не загружается напрямую, а в сессии tf:
with Session(graph=Graph(), config=ConfigProto(allow_soft_placement=True, log_device_placement=False)) as sess:
saved_model.loader.load(sess, [saved_model.tag_constants.SERVING], model_path)
patch_feature, patch_label = sess.run(output_nodes, feed_dict={input_node: patch})
Теперь я задаюсь вопросом, была ли моя первоначальная попытка выгрузить веса модели выполнена неправильно. Или я что-то упускаю.
Транспозиции, которые я выполнил при загрузке данных, составляют (3,2,0,1) для conv2d и (2,3,0,1) для depth-wise conv2d:
def reload_conv2d(layer, weights):
### weights - это кортеж, где каждый элемент состоит из кортежа: (1) номер индекса, (2) имя слоя, из которого были выгружены веса в TF, и (3) веса
count = 0
if (
"/conv2d/kernel" not in weights[0][1]
and "/conv2d_1/kernel" not in weights[0][1]
and "depthwise_conv2d/depthwise_kernel" not in weights[0][1]
and "final_conv2d/final_conv2d" not in weights[0][1]
):
raise ValueError(
f"необходимо, чтобы conv2d/kernel был на первом индексе, но получено {weights[0][1]}"
)
transpose_shape = (2,3,0,1) if "depthwise" in weights[0][1] else (3, 2, 0, 1)
transposed_weights = torch.from_numpy(weights[0][2].transpose(transpose_shape[0], transpose_shape[1], transpose_shape[2], transpose_shape[3]))
layer.weight.data = transposed_weights
count += 1
if layer.bias is not None or layer.bias:
if (
"/conv2d/bias" not in weights[1][1]
and "/conv2d_1/bias" not in weights[1][1]
):
raise ValueError(
f"необходимо, чтобы conv2d/bias был на втором индексе, но получено {weights[1][1]}"
)
layer.bias.data = (
torch.from_numpy(weights[1][2])
if type(weights[1][2]) == np.ndarray
else torch.from_numpy(weights[1][2])
)
count += 1
return layer, count
Я не понимаю, почему при одном и том же входе модели pytorch и TF дают совершенно разные результаты. Это может быть связано с выгрузкой весов, загрузкой весов… или, возможно, изменением архитектуры модели? Тот факт, что входные веса TF (после изменения модели и транспонирования) были загружены нормально и я могу запустить модель без проблем, не помогает в отладке этого.
Ответ или решение
Проблема, с которой вы столкнулись при переносе весов модели EfficientNet из TensorFlow в PyTorch, может быть вызвана несколькими факторами. Давайте подробно разберем возможные источники ошибок и предложим решения.
1. Проверка архитектуры модели
Убедитесь, что архитектура вашей модели в PyTorch точно соответствует архитектуре модели в TensorFlow. Все изменения, которые были сделаны, могут потенциально вызвать расхождения в выходных данных. Одна из важных вещей, на которую следует обратить внимание, – это порядок операций и их параметры. Проверьте все слои и убедитесь, что все они настроены в соответствии с оригинальной моделью.
2. Корректность загрузки весов
Ваш метод загрузки весов выглядит хорошо с точки зрения преобразования формата весов, однако возможны ошибки, связанные с неправильно указанными индексами слоев или неучтенными начальными значениями. Попробуйте выполнить следующие действия:
-
Проверьте, что вы загружаете веса для всех слоев, включая все слои BatchNorm (если такие имеются), которые могут влиять на выходные данные. Обратите внимание, что некоторые слои могут иметь дополнительные параметры, которые также нужно загрузить.
-
Убедитесь, что вы правильно обрабатываете все варианты весов, особенно если у вас несколько экземпляров
Conv2d
. Попробуйте вывести на печать размеры весов после загрузки, чтобы убедиться, что они совпадают с ожидаемыми размерами в вашей модели PyTorch.
3. Инициализация параметров
Если после загрузки весов результаты с пykTorch значительно отличаются от TensorFlow, возможно, что некоторые параметры не были правильно инициализированы. Например, если вы используете активаторы или инициализацию весов, убедитесь, что они идентичны в обеих версиях.
4. Проверка входных данных
Хотя это может показаться очевидным, убедитесь, что входные данные, которые вы подаете в обе модели, идентичны. Возможно, формат данных, их нормализация или предобработка отличаются в двух фреймворках. Например, проверьте следующее:
- Стандартизация/нормализация: одно и то же ли среднее и стандартное отклонение используются в обеих моделях?
- Размер входного тензора: совпадают ли размеры входных данных?
5. Наблюдение за промежуточными выводами
Для дальнейшей диагностики попробуйте вставить print
функции или использовать отладчик, чтобы проследить значения промежуточных тензоров и выходов на каждом шаге. Это может помочь в выявлении, на каком именно этапе происходит расхождение.
Пример исправленного кода
Попробуйте следующий исправленный подход к загрузке весов, который учитывает вышеперечисленные аспекты:
import torch
import numpy as np
def reload_conv2d(layer, weights):
def load_layer_params(layer, weight_data, weight_shape, bias_data=None):
transposed_weights = torch.from_numpy(weight_data.transpose(*weight_shape)).float()
layer.weight.data = transposed_weights
if bias_data is not None:
layer.bias.data = torch.from_numpy(bias_data).float()
# Проверка и загрузка весов для conv2d и depthwise_conv2d
for i in range(len(weights)):
weight_name = weights[i][1]
weight_data = weights[i][2]
if 'depthwise' in weight_name and 'kernel' in weight_name:
load_layer_params(layer, weight_data, (2, 3, 0, 1))
elif 'kernel' in weight_name:
load_layer_params(layer, weight_data, (3, 2, 0, 1))
return layer
Заключение
Перенос моделей между различными фреймворками может быть сложной задачей, особенно когда дело касается глубоких нейросетей с многоуровневыми архитектурами. Обратите особое внимание на соответствие архитектуры моделей, порядок обработки весов и входных данных. Удачи в отладке, и если будут дополнительные вопросы или потребуется помощь с конкретными частями кода, не стесняйтесь обращаться!