import numpy as np
import matplotlib.pyplot as plt
# zbior danych
= range(11)
x = [2*xi - 1 for xi in x]
y 'go', label='True data', alpha=0.5) plt.plot(x, y,
Modele uczenia maszynowego
Rozwój technologii i powszechna cyfryzacja przyczyniły się do powstania nowego zasobu, jakim są dane. Dane te są generowane i przetwarzane zarówno w sposób ustrukturyzowany, jak i nieustrukturyzowany. Strukturyzacja danych doprowadziła do rozwoju wielu modeli, które dziś ogólnie określamy jako modele uczenia maszynowego (ang. machine learning, ML). Natomiast przetwarzanie danych nieustrukturyzowanych takich jak tekst, obrazy czy wideo, przyczyniło się do rozwoju uczenia głębokiego (ang. deep learning, DL). Oba te podejścia często określane zbiorczo jako sztuczna inteligencja (ang. artificial inteligence, AI), zostały stworzone głównie do rozpoznawania wzorców. Jednak coraz częściej wykorzystywane są również do modelowania i generowania nowych danych. Klasyczny model sztucznej inteligencji możemy wyrazić jako funkcję \(f(X,\theta)\), która zależy zarówno od danych reprezentowanych przez ustrukturyzowaną macierz \(X\), jak i od parametrów \(\theta\), których wartości zostają ustalone w procesie uczenia.
W uczeniu nadzorowanym posiadamy wartości zmiennej celu dla wygenerowanych danych treningowych. Dwa podstawowe modele nadzorowanego uczenia maszynowego możemy zrealizować jako proste sieci neuronowe.
Do wygenerowania kodów użyjemy biblioteki PyTorch
Regresja liniowa
Wygenerujemy niezaszumione dane na podstawie wzoru \(y = 2 x - 1\). Na podstawie zbioru danych postaramy się oszacować nieznane parametry czyli wyraz przy \(x\) (\(\alpha_1 = 2\)) i wyraz wolny (\(\alpha_0 = -1\)).
Model regresji liniowej dla jednej zmiennej można zrealizować jako prostą jednowarstwową sieć neuronową. Cały proces można zrealizować za pomocą obiektu torch.nn.Linear
import torch
class LinearRegression(torch.nn.Module):
def __init__(self, inputSize, outputSize):
super(LinearRegression, self).__init__()
self.layers = torch.nn.Sequential(
torch.nn.Linear(inputSize, outputSize)
)
def forward(self, x):
return self.layers(x)
Aby nasze dane mogłybyć przeliczane przez bibliotekę PyTorch musimy je przetworzyć na tensory - czyli obiekty z biblioteki PyTorch.
# dostosowanie do pytorch
= np.array(x, dtype=np.float32)
x = np.array(y, dtype=np.float32)
y
= torch.from_numpy(x).view(-1,1)
X_train = torch.from_numpy(y).view(-1,1) y_train
Uwaga - ponieważ mamy jedną zmienną zawierającą 10 przypadków - potrzebujemy listy składającej się z 10 list jednoelementowych.
Mozna tez wykorzystac obiektowe programowanie.
from torch.utils.data import Dataset, DataLoader
class LinearDataset(Dataset):
def __init__(self, X_train, y_train):
self.X_train = X_train # tensor typu torch
self.y_train = y_train
def __len__(self):
return len(self.y_train)
def __getitem__(self, idx):
return self.X_train[idx], self.y_train[idx]
= LinearDataset(X_train=X_train, y_train=y_train) dataset
= DataLoader(dataset, shuffle=True, batch_size=2) dataloader
Możemy utworzyć model i wybrać optymalizator z funkcją kosztu.
# obiekt liniowej regresji w wersji sieci nn
= LinearRegression(1,1)
lr_model
= torch.nn.MSELoss()
criterion = torch.optim.SGD(lr_model.parameters(), lr=0.01) optimizer
Możemy sprawdzić, że nasz model będzie dostrajał 2 parametry.
= sum(p.numel() for p in lr_model.parameters() if p.requires_grad)
num_params print(f"liczba trenowalnych parametrów: {num_params}")
liczba trenowalnych parametrów: 2
Parametry te w początkowej inicjalizacji mają następujące wartości:
for layer in lr_model.layers:
if isinstance(layer, torch.nn.Linear):
print(f"weight: {layer.state_dict()['weight']}")
print(f"bias: {layer.state_dict()['bias']}")
weight: tensor([[-0.5483]])
bias: tensor([0.1822])
= 100
epochs # petla uczaca
for epoch in range(epochs):
# etap trenowania
lr_model.train()
= lr_model(X_train)
y_pred = criterion(y_pred, y_train)
loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch+1) % 50 == 0:
print(f'epoch: {epoch+1:03d}, loss = {loss.item():.2f}')
eval() # etap ewaluacji modelu
lr_model.
# po treningu jeszcze raz generujemy predykcje
eval()
lr_model.with torch.no_grad():
= lr_model(X_train) predicted
epoch: 050, loss = 0.38
epoch: 100, loss = 0.22
Mozna tez wykorzystac obiekt dataloader
for epoch in range(50):
for X_batch, y_batch in dataloader:
= lr_model(X_batch)
preds = criterion(preds, y_batch)
loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print(f"Epoch {epoch}, loss = {loss.item():.4f}")
Epoch 0, loss = 0.1676
Epoch 10, loss = 0.0017
Epoch 20, loss = 0.0771
Epoch 30, loss = 0.0643
Epoch 40, loss = 0.0054
Otrzymane parametry po uczeniu
print(f"po procesie uczenia waga: {lr_model.layers[0].weight} oraz bias {lr_model.layers[0].bias}")
po procesie uczenia waga: Parameter containing:
tensor([[1.9822]], requires_grad=True) oraz bias Parameter containing:
tensor([-0.8443], requires_grad=True)
Dopasowanie modelu do danych można przedstawić na wykresie
plt.clf()'go', label='True data', alpha=0.5)
plt.plot(X_train, y_train, '--', label='Predictions', alpha=0.5)
plt.plot(X_train, predicted, ='best')
plt.legend(loc plt.show()