Как я могу изменить этот код для запуска сверточной нейронной сети, чтобы получить 2-мерный вывод?

Вопрос или проблема

У меня есть модель движения, для которой я могу моделировать данные. Она имеет два параметра, а выходными данными являются широта и долгота в N равномерно распределенных временных точках. Моя цель – использовать сверточную нейронную сеть, чтобы обучить связи между параметрами P и смоделированными данными. Этот ответ прекрасно объясняет случай, когда данные обрабатываются в виде решетки, где каждая строка – это широта, столбцы – долготы, а значения – это количество вхождений в этой точке широты/долготы.

Но теперь я больше не хочу игнорировать время. Я хочу настроить это так, чтобы у меня был один длинный вектор, где одна позиция к следующей – это временной приращение, и в каждой позиции есть два значения: широта и долгота. Здесь ли нужно использовать “каналы”? В предыдущем случае количество каналов было 1 – теперь, как я думаю, это должно быть 2.

Код ниже (в значительной степени заимствован из упомянутого выше ответа) – моя попытка изменить вышеупомянутый ответ для случая, когда я теперь смотрю только на широту. К сожалению, я получаю ошибку ниже. Итак, у меня два вопроса:

  1. Почему я получаю ошибку?
  2. Как я могу адаптировать это, чтобы учесть случай, когда у меня есть и широта, и долгота?

Как вы увидите, мои входные размеры обучения: [399,1,501,1] для # образцов (смоделированных наборов данных), каналов, строк (положение во времени x), ничего. Я бы хотел, чтобы это последнее измерение было 2, чтобы учесть как широту, так и долготу.

Заранее спасибо!

>>> datalist_train.shape
    torch.Size([599, 1, 501, 1])
>>> datalist_test.shape
    torch.Size([399, 1, 501, 1])
>>> params_train.shape
    torch.Size([599, 2])
>>> params_test.shape
    torch.Size([399, 2])
>>> datalist_train.dtype
torch.float64
>>> datalist_test.dtype
torch.float64
>>> params_train.dtype
torch.float64
>>> params_test.dtype
torch.float64

n_parameters = 2
def create_model(n_output_parameters, hidden_size=16):
model = nn.Sequential(
    #Нормализовать входной пакет
    nn.BatchNorm2d(num_features=1),

    #Обучить скрытый размер ядер 3x3, которые обнаруживают особенности
    nn.Conv2d(in_channels=1, out_channels=hidden_size, kernel_size=3, padding='same'),
    nn.ReLU(),
    nn.BatchNorm2d(hidden_size),

    #Уменьшить пространственные размеры вдвое, сохранив максимальные значения по квадранту
    nn.MaxPool2d(2),

    #Среднее значение пространственных размеров до одного скаляра, и убрать избыточные размеры
    nn.AdaptiveAvgPool2d(1),
    nn.Flatten(start_dim=1),

    #Отобразить входную форму (B, hidden_size) на (B, n_output_parameters)
    nn.Linear(in_features=hidden_size, out_features=hidden_size),
    nn.ReLU(),
    nn.Linear(in_features=hidden_size, out_features=n_output_parameters),
)
return model

cnn_model = create_model(n_parameters)
#Сообщить размер CNN
print(
  'CNN имеет',
  sum(p.numel() for p in cnn_model.parameters() if p.requires_grad),
  'обучаемых параметров'
)
#
# Подготовить данные для модели
# 1. конвертировать в тензоры
# 2. добавить измерение каналов
# 3. обернуть в DataLoader, чтобы получить пакетные образцы
#

#CNN ожидает входной формат (пакет/образцы, каналы, высота, ширина)
# У нас сейчас (B, H, W), так что добавьте единственный канал, чтобы
# получить (B, C=1, H, W)
datalist_train, datalist_test=[tensor.unsqueeze(dim=1) for tensor in (datalist_train, datalist_test)]

#Тренировочные и валидационные наборы данных, возвращающие пару (Ai, Pi) для каждого индекса.
# т.е. связывает Ai и Pi каждого образца.
train_dataset = TensorDataset(datalist_train, params_train)
val_dataset = TensorDataset(datalist_test, params_test)

train_i,params_i=train_dataset[0]#возвращает Ai и Pi для образца 0
print('Образец 0 (A0, P0) из train_dataset:', train_i.shape, ',', params_i.shape)

#Создание пакетов
batch_size = 32
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

#
# Обучение и оценка cnn_model
#

#Для воспроизводимости
np.random.seed(0)
torch.manual_seed(0)

cnn_model = create_model(n_parameters)
optimizer = torch.optim.Adam(cnn_model.parameters())
loss_function = nn.MSELoss()

#
# Цикл обучения
#
n_epochs = 12

#Используется для записи потерь и метрик
from collections import defaultdict
metrics_dict = defaultdict(list)

for epoch in range(n_epochs):

cnn_model.train()
for minibatch in train_loader:
    A_minibatch, P_minibatch = minibatch

    P_predicted = cnn_model(A_minibatch)
    loss = loss_function(P_predicted, P_minibatch)

    #шаг оптимизатора
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
#/конец эпохи

#
# Оценка cnn_model за эпоху
#
cnn_model.eval()
with torch.no_grad():
    P_predicted_train = cnn_model(datalist_train)
    P_predicted_val = cnn_model(datalist_test)

train_loss = loss_function(P_predicted_train, params_train).item()
val_loss = loss_function(P_predicted_val, params_test).item()
val_mae = nn.L1Loss()(P_predicted_val, params_test).item()

metrics_dict['epoch'].append(epoch + 1)
metrics_dict['train_loss'].append(train_loss)
metrics_dict['val_loss'].append(val_loss)
metrics_dict['val_mae'].append(val_mae)

#
# Сообщение о результатах
#
print(
    f'[эпоха {epoch + 1:2d}/{n_epochs}]',
    f'[учебные потери: {train_loss:5.3f} | валидационные потери: {val_loss:5.3f}]',
    f'[валидационная MAE: {val_mae:5.3f}]'
)

Traceback (most recent call last):
  File "<string>", line 7, in <module>
  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\modules\module.py", line 1736, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\modules\module.py", line 1747, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\modules\container.py", line 250, in forward
    input = module(input)
            ^^^^^^^^^^^^^
  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\modules\module.py", line 1736, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\modules\module.py", line 1747, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\modules\batchnorm.py", line 193, in forward
    return F.batch_norm(
           ^^^^^^^^^^^^^
  File "C:\Users\jarab\DOCUME~1\VIRTUA~1\R-RETI~1\Lib\site-packages\torch\nn\functional.py", line 2812, in batch_norm
    return torch.batch_norm(
           ^^^^^^^^^^^^^^^^^
RuntimeError: mixed dtype (CPU): all inputs must share same datatype.

Ответ или решение

Теория

Когда мы работаем с многомерными выходными данными в нейронных сетях, важно корректно обрабатывать соответствующие размеры и типы данных. Для задачи построения сверточной нейронной сети (CNN), которая предсказывает два выходных параметра (широта и долгота), необходимо настроить данные таким образом, чтобы они имели правильные размерности и каналы входных данных. Основное использование каналов в CNN заключается в усложнении представления данных, например, добавлении цветовых каналов (RGB) в изображениях. В вашем контексте каналами могут выступать широта и долгота.

Пример

Рассмотрим ваш код, где текущий размер входных данных [399, 1, 501, 1], первый "1" – это количество каналов. Поскольку теперь вы хотите учитывать и широту, и долготу, количество каналов (вторая размерность) должно быть равно 2. Ошибку, которую вы получаете, может вызывать некорректная работа с типами данных.

Применение

  1. Изменение количества каналов:

    Чтобы изменить количество каналов, вам нужно преобразовать данные так, чтобы размерность каналов стала равной 2. Предполагается, что каждом временному моменту соответствуют две величины: широта и долгота. Каждую такую пару можно рассматривать как канал. Это может быть реализовано путем изменения размерности входных данных.

    # Теперь каналы будут равны 2
    datalist_train, datalist_test = [tensor.view(tensor.size(0), 2, -1, 1) for tensor in (datalist_train, datalist_test)]
  2. Корректировка нормализации данных:

    Ошибка вашего кода также указывает на проблему с типами данных. Все входные данные должны иметь одинаковый тип, например, torch.float32. Проверьте, что все входные тензоры имеют одинаковый тип:

    datalist_train = datalist_train.float()
    datalist_test = datalist_test.float()
    params_train = params_train.float()
    params_test = params_test.float()
  3. Исправление создания модели:

    Убедитесь, что модель настроена на правильное количество входных каналов и выходных параметров. Измените первую сверточную уровень, чтобы обрабатывать двуканальные входные данные:

    def create_model(n_output_parameters, hidden_size=16):
       model = nn.Sequential(
           nn.BatchNorm2d(num_features=2),  # Изменение количества каналов
           nn.Conv2d(in_channels=2, out_channels=hidden_size, kernel_size=3, padding='same'),
           nn.ReLU(),
           nn.BatchNorm2d(hidden_size),
           nn.MaxPool2d(2),
           nn.AdaptiveAvgPool2d(1),
           nn.Flatten(start_dim=1),
           nn.Linear(in_features=hidden_size, out_features=hidden_size),
           nn.ReLU(),
           nn.Linear(in_features=hidden_size, out_features=n_output_parameters),
       )
       return model

Эти изменения должны помочь корректно обрабатывать двухмерные данные (широта и долгота) с различными временными интервалами в вашей CNN без ошибок. Важно всегда проверять соответствие размерностей и типов данных при работе с нейронными сетями, чтобы избежать ошибок при их выполнении.

Оцените материал
Добавить комментарий

Капча загружается...