Programmation pour l’IA
Florent Capelli
Université d’Artois, CRIL
Permet de calculer automatiquement des gradients, symboliquement :
x = torch.empty(100).uniform_(-20,20)
y = 3*x+2+torch.normal(mean=0., std=3., size=(100,))
a = torch.tensor([0.], requires_grad=True)
b = torch.tensor([0.], requires_grad=True)
for e in range(1000):
loss = torch.mean((a*x+b-y)**2)
loss.backward()
with torch.no_grad():
a -= (1e-3)*a.grad
b -= (1e-3)*b.grad
a.grad_zero_()
b.grad_zero_()a et b comme des tenseurs avec
information de gradientloss, en le calculant, maintient une trace du
calcul.loss.backward() propage le gradient dans l’arbre de
calcul.import torch
from torch import nn
m = nn.Sequential(nn.Linear(2,3), nn.ReLU(),
nn.Linear(3,1), nn.Sigmoid())
print(m.parameters())[Parameter containing:
tensor([[ 0.4524, -0.0160],
[ 0.5088, 0.5218],
[ 0.3939, 0.5888]], requires_grad=True), Parameter containing:
tensor([-0.1352, -0.0697, 0.5479], requires_grad=True), Parameter containing:
tensor([[ 0.4776, -0.1121, 0.0311]], requires_grad=True), Parameter containing:
tensor([-0.0203], requires_grad=True)]
from torch import optimtorch.optim is a package implementing various optimization algorithms.
Most commonly used methods are already supported, and the interface is general enough, so that more sophisticated ones can also be easily integrated in the future.
optim.SGDoptim.Adam|
|
Implements Adadelta algorithm. |
|
|
Implements Adafactor algorithm. |
|
|
Implements Adagrad algorithm. |
|
|
Implements Adam algorithm. |
|
|
Implements AdamW algorithm, where weight decay does not accumulate in the momentum nor variance. |
|
|
SparseAdam implements a masked version of the Adam algorithm suitable for sparse gradients. |
|
|
Implements Adamax algorithm (a variant of Adam based on infinity norm). |
|
|
Implements Averaged Stochastic Gradient Descent. |
|
|
Implements L-BFGS algorithm. |
|
|
Implements Muon algorithm. |
|
|
Implements NAdam algorithm. |
|
|
Implements RAdam algorithm. |
|
|
Implements RMSprop algorithm. |
|
|
Implements the resilient backpropagation algorithm. |
|
|
Implements stochastic gradient descent (optionally with momentum). |
optim.SGD()import torch
from torch import optim
from torch import nn
m = nn.Sequential(nn.Linear(2,3), nn.ReLU(),
nn.Linear(3,1), nn.Sigmoid())
o = optim.SGD(m.parameters(), lr=0.01)
for input, target in dataset:
o.zero_grad()
output = m(input)
loss = loss_fn(output, target)
loss.backward()
o.step()o.step() met à jour les paramètres en utilisant les
spécificités de l’optimizer choisi.optim.SGD()import torch
from torch import optim
from torch import nn
m = nn.Sequential(nn.Linear(2,3), nn.ReLU(),
nn.Linear(3,1), nn.Sigmoid())
o = optim.SGD(m.parameters(), lr=0.01)
# /!\ la fonction minibatch n'existe pas !
# /!\ C'est juste pour l'exemple
for b in minibatch(dataset, 10):
o.zero_grad()
for input, target in b:
output = m(input)
loss = loss_fn(output, target)
loss.backward()
o.step()loss.backward() accumule les gradients pour chaque
exemple dans b.o.step()) qu’après
avoir regardé tous les exemples du minibatch.import torch
from torch import optim
from torch import nn
m = nn.Sequential(nn.Linear(2,3), nn.ReLU(),
nn.Linear(3,1), nn.Sigmoid())
o = optim.Adam(m.parameters(), lr=0.01)
for input, target in dataset:
o.zero_grad()
output = m(input)
loss = loss_fn(output, target)
loss.backward()
o.step()o.Pour que le calcul de gradient fonctionne sur la fonction
loss_fn, il faut qu’elle soit compatible avec la dérivation
automatique de Pytorch.
nnnn.MSELoss, nn.CrossEntropyLoss
etc.Utilisation de la classe abstraite DataSet. Trois
méthodes à définir :
__init__(self, ...) : constructeur ;__len__(self) : retourne le nombre d’exemples dans le
jeu de données.__getitem__(self, idx) : retourne X,y où
X est l’exemple d’index idx et y
est sa classe.iris.csvimport pandas as pd
from torch.utils.data import Dataset
class DatasetIris(Dataset):
def __init__(self):
df = pd.read_csv("iris.csv")
self.data = torch.from_numpy(df.to_numpy()[:, :4].astype(float))
self.target = torch.from_numpy(
df["variety"].replace({"Virginica": 0, "Versicolor": 1, "Setosa": 2})
.to_numpy(dtype=int))
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx], self.target[idx]
d = DatasetIris()
print(d[0:4])
print(len(d))DataLoaderUne fois qu’on a un DataSet, on peut y accéder avec
DataLoader :
from torch.utils.data import DataLoader
training_data = DatasetIris()
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
for X,y in train_dataloader:
print(X.shape)
print(y.shape)
break>>> torch.Size([64, 4])
>>> torch.Size([64])
Dataset/DataLoaderDataLoader(train, collate_fn = ..., sampler = ...).to(device)Envoie la structure de données (et les calculs sous-jacents) dans le GPU :
iris.csvnn.Sequential( nn.Linear(4,8), nn.ReLU(), nn.Linear(8,3), nn.Softmax(dim=0)
)
\[Softmax([x_1,x_2,x_3]) = [{e^{x_1} \over S}, {e^{x_2} \over S}, {e^{x_3} \over S}]\] où \(S = e^{x_1}+e^{x_2}+e^{x_3}\).
Intuition: on représente la probabilité d’être dans la classe \(i \in \{0,1,2\}\) comme \(e^{x_i} \over S\).
Au final :
\[\begin{align} Pr(classe(X) = 0) = 1 \\ Pr(classe(X) = 1) = 0 \\ Pr(classe(X) = 2) = 0 \end{align} \]
On représente donc les sorties de la classe \(0\) par \([1,0,0]\), les sorties de la classe \(1\) par \([0,1,0]\) et les sorties de la classe \(2\) par \([0,0,1]\).
On appelle cela : one-hot encoding.
loss_fn = nn.CrossEntropyLoss()
(log-vraissemblance ou entropie croisée).Adam