https://pytorch.org/tutorials/recipes/recipes/defining_a_neural_network.html
Zacznijmy od przykładu sieci neuronowej w Pytorch składającej się z 1 warstwy wejściowej, 1 warstwy ukrytej ukrytej i warstwy wyjściowej. Warstwa ukryta będzie mieć funkcję sigmoidalną aktywacji natomiast wyjściowa Relu.
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
# Definicja sieci neuronowej
class ExampleNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(ExampleNN, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.sigmoid = nn.Sigmoid()
self.fc2 = nn.Linear(hidden_size, output_size)
self.relu = nn.ReLU()
def forward(self, x):
out = self.fc1(x)
out = self.sigmoid(out)
out = self.fc2(out)
out = self.relu(out)
return out
# Parametry sieci
input_size = 10
hidden_size = 20
output_size = 1
# Przykładowe dane
X = torch.randn(100, input_size)
y = torch.randint(0, 2, (100, output_size)).float()
# Inicjalizacja modelu
model = ExampleNN(input_size, hidden_size, output_size)
# Definicja funkcji straty i optymalizatora
criterion = nn.BCEWithLogitsLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
losses = []
# Trening modelu
for epoch in range(1000):
# Forward pass
outputs = model(X)
loss = criterion(outputs, y)
# Backward pass i aktualizacja wag
optimizer.zero_grad()
loss.backward()
optimizer.step()
# Dodanie aktualnego spadku błędu do listy
losses.append(loss.item())
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')
# Wykres błędu sieci neuronowej w danej epoce
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.show()
W tym przykładzie:
nn.Module i reprezentuje naszą sieć neuronową składającą się z warstwy wejściowej, jednej warstwy ukrytej i warstwy wyjściowej. Warstwa ukryta ma rozmiar hidden_size i używa funkcji aktywacji sigmoidalnej (nn.Sigmoid()). Na końcu mamy warstwę wyjściową o rozmiarze output_size, która używa funkcji aktywacji ReLU (nn.ReLU()).__init__()) definiujemy warstwy sieci oraz funkcje przejścia.
nn.Linear(in_features, out_features, bias=True, device=None, dtype=None) wykonuje mnożenie wejścia przez wagi, sumowanie oraz dodaje bias w poszczeólnych neuronoach wyjściowych. Jako in_features podajemy rozmiar warstwy wejściowej (ile danych wejściowych przyjmie sieć), jako out_features definiujemy ilość neuronów (ile wyjść będzie miała warstwa ukryta).nn.Sigmoid().nn.Linear(in_features, out_features, bias=True, device=None, dtype=None) gdzie jako in_features podajemy ilość wyjść (neuronów) z fc1 a jako out_features ile neuronów ma być w warstwie wyjściowej.nn.ReLU()forward() symuluje przejścia pomiędzy warstwami. Kolejno, wektor wejściowy trafia do fc1, wynik fc1 trafia do funkcji sigmoid, z funkcji sigmoid przekazujemy dane do fc2 i finalnie wynik fc2 przekazujemy do funkcji aktywacji relu co daje nam finalny output sieci neuronowej.W problemie regresji celem jest przewidywanie wartości ciągłych zmiennych, na przykład prognozowanie ceny domu na podstawie jego cech, przewidywanie temperatury na podstawie danych meteorologicznych, czy prognozowanie sprzedaży produktu na podstawie danych historycznych.
Popularne miary służące do oceny jakości modelu regresyjnego:
Jest to jedna z najczęściej stosowanych miar do oceny wydajności modeli regresji. MSE oblicza średnią kwadratów różnicy między wartościami przewidywanymi przez model a rzeczywistymi wartościami etykiet. Im niższa wartość MSE, tym lepiej model dopasowuje się do danych.
\[MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2\]MAE oblicza średnią wartość bezwzględnej różnicy między wartościami przewidywanymi przez model a rzeczywistymi wartościami etykiet. Jest mniej wrażliwy na wartości odstające niż MSE, ponieważ nie podnosi błędów do kwadratu.
\[MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|\]RMSE jest podobny do MSE, ale zamiast obliczać średnią kwadratów różnic, oblicza pierwiastek kwadratowy z MSE. Oznacza to, że RMSE mierzy średnią różnicę między wartościami przewidywanymi przez model a rzeczywistymi wartościami etykiet, ale zwraca wynik w tych samych jednostkach co oryginalne dane, co ułatwia interpretację. Im niższa wartość RMSE, tym lepiej model dopasowuje się do danych.
\[RMSE = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2}\]$R^2$ Score, znany również jako współczynnik determinacji, mierzy, jak dobrze model regresji dopasowuje się do danych. Przyjmuje wartość między 0 a 1, gdzie 1 oznacza idealne dopasowanie modelu do danych, a 0 oznacza brak dopasowania. R^2 Score jest szczególnie przydatny do porównywania różnych modeli regresji.
\[R^2 = 1 - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{n} (y_i - \bar{y})^2}\]Gdzie:
W bibliotece scikit-learn znajdują się gotowe implementacje powyższych funkcji.
https://scikit-learn.org/stable/
Poniżej przykładowy kod który trenuje sieć neuronową aż osiągnie zadany błąd MSE:
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 1. Przygotowanie danych
x = np.arange(0, 4*np.pi, 0.2) # lub np.arange(0, 4*np.pi, 0.1) dla większej gęstości punktów
y = np.sin(x)
x_tensor = torch.FloatTensor(x.reshape(-1, 1))
y_tensor = torch.FloatTensor(y.reshape(-1, 1))
# Podział na zbiór treningowy i testowy
split_idx = int(0.8 * len(x))
x_train, x_test = x_tensor[:split_idx], x_tensor[split_idx:]
y_train, y_test = y_tensor[:split_idx], y_tensor[split_idx:]
# 2. Zdefiniowanie modelu sieci neuronowej
class NeuralNet(nn.Module):
def __init__(self, input_size, hidden_size):
super(NeuralNet, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.fc2 = nn.Linear(hidden_size, 1)
self.activation = nn.Sigmoid() # Funkcja aktywacji w warstwie ukrytej
def forward(self, x):
x = self.activation(self.fc1(x))
x = self.fc2(x)
return x
# 3. Trenowanie modelu
def train_model(hidden_size, learning_rate, num_epochs, mse_threshold):
model = NeuralNet(1, hidden_size)
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)
for epoch in range(num_epochs):
model.train()
optimizer.zero_grad()
outputs = model(x_train)
loss = criterion(outputs, y_train)
loss.backward()
optimizer.step()
if (epoch+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item()}')
# Warunek przerwania treningu, jeśli błąd spadnie poniżej progu
if loss.item() <= mse_threshold:
print(f'MSE Threshold reached at epoch {epoch+1}. Stopping training.')
break
return model
# Parametry
hidden_size = 10
learning_rate = 0.01
num_epochs = 1000
mse_threshold = 0.01 # Próg błędu MSE
# Trenowanie modelu
model = train_model(hidden_size, learning_rate, num_epochs, mse_threshold)
# Ocena modelu
model.eval()
with torch.no_grad():
outputs = model(x_test)
mse = nn.MSELoss()(outputs, y_test)
print(f'Mean Squared Error (Test): {mse.item()}')
# Wykres
plt.plot(x_test.numpy(), y_test.numpy(), label='True')
plt.plot(x_test.numpy(), outputs.numpy(), label='Predicted')
plt.legend()
plt.show()
Opis zadania: Celem tego zadania jest projektowanie sieci neuronowej do interpolacji funkcji sinusoidalnej \(y = \sin(x)\), przy użyciu punktów (x, y) w postaci danych uczących. Przeprowadzimy badania mające na celu zrozumienie wpływu różnych czynników na jakość interpolacji, w tym liczby punktów w zbiorze uczącym, liczby neuronów w warstwie wejściowej oraz wartości funkcji celu (kryterium zatrzymania trenowania). Do oceny jakości interpolacji użyjemy błędu średniokwadratowego.
Badane czynniki:
Metoda oceny jakości interpolacji: Do oceny jakości interpolacji użyjemy błędu średniokwadratowego (MSE).
Kroki do wykonania:
Wyniki: