Нейронная сеть не может выучить простое аналитическое уравнение.

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

Я сейчас делаю свои первые попытки с Pytorch. Я пытаюсь решить простое уравнение с помощью нейронной сети. Аналитически решенное уравнение должно выглядеть так:
$$
y = \frac{x_5}{x_2} – \frac{x_1-x_2}{2 x_3 x_4}\frac{x_2}{x_1}
$$
при этом я случайным образом генерирую входные данные с следующими границами: $$500 \leq x_1 \leq 1000 \ 1 \leq x_2 \leq x_1 \ 1 \leq x_5 \leq 10000 \ 1e-6\leq x_4 \leq 1e-3 \ 1000 \leq x_3 \leq 50000 $$
Сейчас нейронная сеть, похоже, не может выучить функцию. Я предполагаю, что это связано с большими разбросами входных областей функции. Я уже пробовал различные архитектуры сети и разные функции активации. Я также пытался использовать lr scheduler и варьировать скорость обучения от 1e-2 до 1e-8. Я также пытался суммировать входные переменные $x_3x_4$ в качестве отдельной переменной.
Поскольку я хотел бы добавить дополнительные уравнения в эту нейронную сеть, которые будут решаться позже, я реализовал следующую нормализованную функцию потерь:
F.l1_loss((y_est+(x_1-x_2)/(2x_3x_4)*x_2/x_1 – x_5/x_2)/(x_5/x_2),torch.FloatTensor(np.zeros(batch_size)))

Я в основном работал здесь с nn различного размера и количества слоев, но не смог добиться хороших результатов (в основном потери больше 0.5, но колеблются до 4).
Спасибо заранее.
Редактировать:
import torch.optim as optim
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import random
from torch.optim.lr_scheduler import ReduceLROnPlateau

def get_batch(batch_size=32):
batch_x = []
for i in range(batch_size):
x_1 = random.uniform(500,1000)
x_2 = random.uniform(1,x_1-1)
x_3 = random.uniform(1,10000)
x_4 = random.uniform(1e-6,0.001)
x_5 = np.random.randint(1000,50000)
batch_x.append([x_1,x_2,x_3,x_4,x_5])
return torch.FloatTensor(batch_x).cuda().contiguous()

class CustomNet(nn.Module):
def init(self,n_input,n_output,n_hidden_neurons,n_hidden_layers):
super(CustomNet, self).init()
self.sequential_layers = nn.Sequential(
*((nn.Linear(n_input,n_hidden_neurons),)+ tuple(nn.Linear(n_hidden_neurons,n_hidden_neurons) for i in range(n_hidden_layers)))
)
self.fc1 = nn.Linear(n_hidden_neurons,n_output)

def forward(self, x):
    x = torch.log(x)
    output = self.sequential_layers(x)
    output = torch.exp(output)
    output = self.fc1(output)
    return output

batch_size = 1000
n_epochs = 50000
n_input,n_output,n_hidden_neurons,n_hidden_layers = 5,1,16,1

создание модели

nnet = CustomNet(n_input,n_output,n_hidden_neurons,n_hidden_layers)
nnet = nnet.to("cuda")

lr, counter = 0.01, 0
optimizer = optim.Adam(nnet.parameters(),lr=lr)
target = torch.FloatTensor(np.zeros(batch_size)).contiguous().cuda()
lr_scheduler = ReduceLROnPlateau(optimizer, mode="min", factor=0.1, patience=1000, verbose=True)
target = torch.FloatTensor(np.zeros(batch_size)).cuda().contiguous()
for i in range(n_epochs):

# Сброс градиентов
nnet.zero_grad()

batch_x = get_batch(batch_size)
x_1,x_2,x_3,x_4,x_5 = batch_x.T
y_est = nnet(batch_x)
y_est = y_est[:,0]
output = F.l1_loss((y_est+(x_1-x_2)/(2*x_3*x_4)*x_2/x_1 - x_5/x_2)/(x_5/x_2),target)

# Обратный проход
loss = output.item()
output.backward()
optimizer.step()
lr_scheduler.step(output)

Несколько предложений:

добавьте нелинейность между скрытыми слоями и увеличьте количество скрытых слоев: в настоящее время self.sequential_layers — это просто составное линейное преобразование. Добавление ReLU между последовательными скрытыми слоями может помочь вам изучать быстрее;
не уверен, хорошо ли использовать torch.log и torch.exp. Вы можете убрать torch.log и заменить torch.exp на вашу выбранную нелинейность для соединения скрытых слоев;
используйте меньшие размеры пакетов: например, 64 или 128.

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

Почему нейронная сеть не может обучиться простой аналитической функции

В процессе работы с нейронными сетями может возникнуть ситуация, когда модель не может выучить даже простую аналитическую функцию. Рассмотрим ваш случай с использованием Pytorch, где вы пытаетесь смоделировать аналитическую зависимость:

$$
y = \frac{x_5}{x_2} – \frac{x_1-x_2}{2 x_3 x_4}\frac{x_2}{x_1}
$$

Потенциальные проблемы

  1. Масштабирование входных данных и разреженность:

    • С учетом того, что вы генерируете данные с довольно большими диапазонами (например, $500 \leq x_1 \leq 1000$ и $1 \leq x_5 \leq 10000$), ваша модель может сталкиваться с трудностями в обучении. Нейронные сети, как правило, работают лучше, когда данные нормализованы в диапазоне от 0 до 1 или от -1 до 1. Попробуйте нормализовать входные данные перед подачей в сеть.
  2. Проблема с функцией активации:

    • Использование torch.log и torch.exp в вашей модели может не подходить для ваших данных. Это может привести к тому, что модель будет слишком чувствительна к значениям входных данных, что затрудняет обучение. Вместо этого рассмотрите возможность добавления нелинейностей, таких как ReLU, между скрытыми слоями.
  3. Архитектура модели:

    • Ваша текущая архитектура нейронной сети состоит только из линейных преобразований. Для улучшения характеристики обучения рекомендуется добавить нечто вроде ReLU или другого типа нелинейной активации между слоями. Это поможет сети лучше захватывать сложные зависимости в данных.
  4. Размеры батча и количество эпох:

    • Испробуйте уменьшить размер батча. Размеры батча, равные 1000, могут привести к нестабильности градиентного спуска, что повлияет на скорость обучения. Размер 64 или 128 может дать более стабильные обновления весов.
    • Вам, возможно, стоит уменьшить количество эпох, если наблюдается сходимость потерь до определенного предела.

Изменения и предложения

Вот несколько рекомендаций для улучшения вашего кода:

  1. Добавьте функцию активации:

    self.sequential_layers = nn.Sequential(
       nn.Linear(n_input, n_hidden_neurons),
       nn.ReLU(),
       *tuple(nn.Sequential(nn.Linear(n_hidden_neurons, n_hidden_neurons), nn.ReLU()) for _ in range(n_hidden_layers)),
       nn.Linear(n_hidden_neurons, n_output)
    )
  2. Нормализуйте входные данные:
    Прежде чем передавать данные в модель, нормализуйте каждый входной элемент. Например:

    def normalize_batch(batch_x):
       return (batch_x - batch_x.mean(dim=0)) / batch_x.std(dim=0)
    
    batch_x = normalize_batch(get_batch(batch_size))
  3. Измените размер батча:

    batch_size = 64
  4. Упрощение функции потерь:
    Убедитесь, что ваша функция потерь соответствует масштабам целевых значений, можно установить target на 0:

    target = torch.zeros(batch_size).cuda()

Заключение

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

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

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