Ga naar hoofdinhoud

PyTorch-tutorial: vanaf nul een simpel neuraal netwerk bouwen

Leer de basis van PyTorch kennen en krijg een uitgebreid beeld van hoe neurale netwerken werken. Ga vandaag nog aan de slag met PyTorch.
Bijgewerkt 2 okt 2026  · 11 min lezen

Verken met AI

ChatGPTClaudePerplexity

PyTorch-tutorial Neuraal Netwerk

In deze PyTorch-tutorial behandelen we de kernfuncties achter neurale netwerken en bouwen we er zelf een vanaf nul. Het hoofddoel van dit artikel is om de basis van PyTorch, een geoptimaliseerde deep-learning-tensorbibliotheek, te demonstreren en je tegelijk een gedetailleerde achtergrond te geven over hoe neurale netwerken werken. 

Let op: Bekijk deze DataCamp-workspace om de code uit dit artikel te volgen.

Wat zijn neurale netwerken? 

Neurale netwerken worden ook wel artificiële neurale netwerken (ANNs) genoemd. De architectuur vormt de basis van deep learning, een subset van machine learning die zich bezighoudt met algoritmen die geïnspireerd zijn op de structuur en werking van het menselijk brein.  Simpel gezegd vormen neurale netwerken de basis van architecturen die nabootsen hoe biologische neuronen signalen aan elkaar doorgeven. 

Daarom kom je vaak bronnen tegen die de eerste vijf minuten besteden aan het in kaart brengen van de neurale structuur van het menselijk brein om je visueel te helpen begrijpen hoe een neuraal netwerk werkt. Maar als je geen vijf minuten over hebt, is het makkelijker om een neuraal netwerk te definiëren als een functie die inputs naar gewenste outputs afbeeldt.  

De generieke architectuur van een neuraal netwerk bestaat uit het volgende:

  • Invoallaag: Data wordt via de invoerlaag in het netwerk gevoerd. Het aantal neuronen in de invoerlaag is gelijk aan het aantal features in de data. Technisch gezien wordt de invoerlaag niet beschouwd als een van de lagen in het netwerk, omdat er op dit punt geen berekening plaatsvindt. 
  • Verborgen laag: De lagen tussen de invoer- en uitvoerlaag heten verborgen lagen. Een netwerk kan een willekeurig aantal verborgen lagen hebben: hoe meer verborgen lagen, hoe complexer het netwerk. 
  • Uitvoerlaag: De uitvoerlaag wordt gebruikt om een voorspelling te doen. 
  • Neuronen: Elke laag heeft een verzameling neuronen die met neuronen in andere lagen interageren. 
  • Activatiefunctie: Voert niet-lineaire transformaties uit om het model te helpen complexe patronen uit de data te leren. 

Drielaags neuraal netwerk

Let op: het neurale netwerk in de afbeelding hierboven wordt beschouwd als een drielaags neuraal netwerk en niet als een vierlaags - dit komt doordat we de invoerlaag niet als laag meetellen. Het aantal lagen in een netwerk is dus het aantal verborgen lagen plus de uitvoerlaag.

Hoe werken neurale netwerken? 

Laten we het algoritme opdelen in kleinere componenten om beter te begrijpen hoe neurale netwerken werken.

Initieel wegen toekennen

Het initialiseren van gewichten is de eerste component in de architectuur van een neuraal netwerk. De beginwaarden die we instellen bepalen het startpunt voor het optimalisatieproces van het model. 

Hoe we onze gewichten instellen is belangrijk, vooral bij het bouwen van diepe netwerken. Diepe netwerken zijn namelijk gevoeliger voor het probleem van exploderende of verdwijnende gradiënten.  Deze problemen vallen buiten de scope van dit artikel, maar beide beschrijven een situatie waarin het algoritme niet leert. 

Hoewel gewichtsinitialisatie het probleem van verdwijnende of exploderende gradiënten niet volledig oplost, draagt het wel bij aan de preventie ervan. 

Hier zijn een paar veelvoorkomende manieren om gewichten te initialiseren: 

Nul-initialisatie

Nul-initialisatie betekent dat gewichten als nul worden ingesteld. Dit is geen goede oplossing, omdat ons neurale netwerk de symmetrie niet doorbreekt - het zal niet leren. 

Wanneer een constante waarde wordt gebruikt om de gewichten van een neuraal netwerk te initialiseren, kun je verwachten dat het slecht presteert, omdat alle lagen hetzelfde zullen leren. Als alle outputs van de verborgen eenheden dezelfde invloed op de kosten hebben, dan zijn de gradiënten identiek. 

Willekeurige initialisatie

Willekeurige initialisatie doorbreekt de symmetrie, wat betekent dat het beter is dan nul-initialisatie, maar sommige factoren kunnen de algehele kwaliteit van het model bepalen. 

Als de gewichten bijvoorbeeld willekeurig met grote waarden worden geïnitialiseerd, kun je verwachten dat elke matrixvermenigvuldiging een aanzienlijk grotere waarde oplevert. Wanneer in zulke scenario’s een sigmoid-activatiefunctie wordt toegepast, is het resultaat een waarde dicht bij één, wat de leersnelheid vertraagt.

Een ander scenario waarin willekeurige initialisatie problemen kan veroorzaken is wanneer de gewichten willekeurig met kleine waarden worden geïnitialiseerd. In dat geval zal elke matrixvermenigvuldiging aanzienlijk kleinere waarden opleveren, en het toepassen van een sigmoidfunctie zal een waarde dichter bij nul opleveren, wat de leersnelheid ook vertraagt. 

Xavier/Glorot-initialisatie 

Een Xavier- of Glorot-initialisatie - het gaat onder beide namen - is een heuristische aanpak om gewichten te initialiseren. Je ziet deze aanpak vaak wanneer een tanh- of sigmoid-activatiefunctie wordt toegepast op het gewogen gemiddelde. De aanpak werd voor het eerst voorgesteld in 2010 in het onderzoek Understanding the difficulty of training deep feedforward neural networks door Xavier Glorot en Yoshua Bengio. Deze techniek is erop gericht de variantie door het netwerk gelijk te houden om te voorkomen dat gradiënten exploderen of verdwijnen. 

He/Kaiming-initialisatie 

De He- of Kaiming-initialisatie is een andere heuristische aanpak. Het verschil met de He- en Xavier-heuristiek is dat He-initialisatie een andere schaalfactor voor de gewichten gebruikt die rekening houdt met de niet-lineariteit van activatiefuncties. 

Dus wanneer de ReLU-activatiefunctie in de lagen wordt gebruikt, is He-initialisatie de aanbevolen aanpak. Je kunt meer over deze aanpak leren in Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification van He et al.

Topcursussen over neurale netwerken

Introductie tot Deep Learning in Python

BasicSkill Level
4 uur
264.8K learners
Leer de basis van neurale netwerken en hoe je deep learning-modellen kunt bouwen met Keras 2.0 in Python.
See DetailsRight Arrow
Start Course

Forwardpropagatie

Neurale netwerken werken door een gewogen gemiddelde plus een bias-term te nemen en daar een activatiefunctie op toe te passen om een niet-lineaire transformatie toe te voegen. In de formulering van het gewogen gemiddelde bepaalt elk gewicht het belang van elke feature (dus hoeveel die bijdraagt aan het voorspellen van de output).

Gewogen som in een neuraal netwerk.png

De bovenstaande formule is het gewogen gemiddelde plus een bias-term, waarbij 

  • z de gewogen som van de input van een neuron is
  • Wn de gewichten aanduidt
  • Xn de onafhankelijke variabelen aanduidt, en
  • b de bias-term is.

Als de formule bekend klinkt, komt dat omdat het lineaire regressie is. Zonder niet-lineariteit in de neuronen zouden we lineaire regressie hebben, een simpel model. De niet-lineaire transformatie stelt ons neuraal netwerk in staat complexe patronen te leren. 

Activatiefuncties

We hebben in de sectie over gewichtsinitialisatie al naar enkele activatiefuncties verwezen, maar nu weet je hoe belangrijk ze zijn in een architectuur voor neurale netwerken.

Laten we dieper ingaan op een paar veelvoorkomende activatiefuncties die je waarschijnlijk tegenkomt in onderzoekspapers en de code van anderen.  

Sigmoid

De sigmoidfunctie wordt gekenmerkt door een S-vormige curve die begrensd is tussen de waarden nul en één. Het is een differentieerbare functie, wat betekent dat de helling van de curve op elk willekeurig punt kan worden bepaald, en monotoon, wat betekent dat hij niet volledig stijgend of dalend is. Je gebruikt de sigmoid-activatiefunctie doorgaans voor binaire classificatieproblemen.

Zo kun je je eigen sigmoidfunctie visualiseren met Python: 

# Sigmoid function in Python
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 50)
z = 1/(1 + np.exp(-x))

plt.subplots(figsize=(8, 5))
plt.plot(x, z)
plt.grid()
plt.show()

Lijngrafiek van sigmoidfunctie

Tanh

De hyperbolische tangens (tanh) heeft dezelfde S-vormige curve als de sigmoidfunctie, behalve dat de waarden begrensd zijn tussen -1 en 1. Kleine inputs worden dus dichter bij -1 afgebeeld en grotere inputs dichter bij 1.

Tanh-functie-expressie

Hier is een voorbeeld van een tanh-functie gevisualiseerd met Python: 

# tanh function in Python
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 50)
z = np.tanh(x)

plt.subplots(figsize=(8, 5))
plt.plot(x, z)
plt.grid()
plt.show()

Lijngrafiek van tanh-functie

Softmax

De softmaxfunctie wordt meestal gebruikt als activatiefunctie in de uitvoerlaag. Het is een generalisatie van de sigmoidfunctie naar meerdere dimensies. Daarom wordt deze gebruikt in neurale netwerken om klasse-lidmaatschap te voorspellen voor meer dan twee labels. 

Rectified Linear Unit (ReLU)

Het gebruiken van de sigmoid- of tanh-functie voor diepe neurale netwerken is riskant, omdat ze vatbaarder zijn voor het probleem van verdwijnende gradiënten. De Rectified Linear Unit (ReLU) werd geïntroduceerd als oplossing voor dit probleem en is vaak de standaardactivatiefunctie voor diverse neurale netwerken. 

Hier is een visueel voorbeeld van de ReLU-functie in Python: 

# ReLU in Python
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 50)
z = [max(0, i) for i in x]

plt.subplots(figsize=(8, 5))
plt.plot(x, z)
plt.grid()
plt.show()

Lijngrafiek van ReLU-functie

ReLU is begrensd tussen nul en oneindig: let op dat voor inputwaarden kleiner dan of gelijk aan nul de functie nul teruggeeft, en voor waarden boven nul geeft de functie de ingevoerde waarde terug (dus als je twee invoert, wordt twee teruggegeven). Per saldo gedraagt de ReLU-functie zich erg vergelijkbaar met een lineaire functie, wat optimaliseren en implementeren een stuk eenvoudiger maakt. 

Het proces van invoer naar uitvoerlaag heet de forward pass of forwardpropagatie. In deze fase worden de outputs die door het model zijn gegenereerd gebruikt om een kostenfunctie te berekenen, zodat we na elke iteratie weten hoe het neuraal netwerk presteert. Deze informatie wordt vervolgens terug door het model gestuurd om de gewichten te corrigeren, zodat het model betere voorspellingen kan doen in een proces dat backpropagatie heet. 

Backpropagatie

Aan het einde van de eerste forward pass maakt het netwerk voorspellingen met de geïnitialiseerde, nog niet afgestemde gewichten. Het is dus zeer waarschijnlijk dat de voorspellingen niet accuraat zijn. Met het verlies dat tijdens forwardpropagatie is berekend, sturen we informatie terug door het netwerk om de gewichten te verfijnen in een proces dat backpropagatie heet. 

Uiteindelijk gebruiken we de optimalisatiefunctie om ons te helpen de gewichten te vinden die de fout kunnen verkleinen, waardoor het model betrouwbaarder wordt en beter kan generaliseren naar nieuwe voorbeelden. De wiskunde achter hoe dit werkt valt buiten de scope van dit artikel, maar de geïnteresseerde lezer kan meer over backpropagatie leren in onze cursus Introduction to Deep Learning in Python.

PyTorch-tutorial: stap voor stap een neuraal netwerk vanaf nul bouwen

In dit artikelgedeelte bouwen we een simpel artificieel neuraal netwerk met de PyTorch-bibliotheek. Bekijk deze DataCamp-workspace om de code te volgen

PyTorch is een van de populairste bibliotheken voor deep learning. Het biedt een veel directere debugervaring dan TensorFlow. Het heeft nog diverse andere voordelen zoals gedistribueerde training, een robuust ecosysteem, cloudondersteuning, en de mogelijkheid om production-ready code te schrijven, enzovoort. Je kunt meer leren over PyTorch in het skill track Introduction to Deep Learning with PyTorch. 

Laten we beginnen met de tutorial. 

Datadefinitie & -voorbereiding 

De dataset die we in onze tutorial gebruiken is make_circles uit scikit-learn - zie de documentatie. Het is een speeldataset met een grote cirkel met een kleinere cirkel in een tweedimensionaal vlak en twee features. Voor onze demonstratie gebruikten we 10.000 samples en voegden we een standaardafwijking van 0,05 aan Gaussiaans ruis toe aan de data. 

Voordat we ons neuraal netwerk bouwen, is het goed gebruik om onze data op te splitsen in trainings- en testsets, zodat we de prestaties van het model op ongeziene data kunnen evalueren.

import matplotlib.pyplot as plt
from sklearn.datasets import make_circles
from sklearn.model_selection import train_test_split

# Create a dataset with 10,000 samples.
X, y = make_circles(n_samples = 10000,
                    noise= 0.05,
                    random_state=26)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.33, random_state=26)

# Visualize the data.
fig, (train_ax, test_ax) = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(10, 5))
train_ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=plt.cm.Spectral)
train_ax.set_title("Training Data")
train_ax.set_xlabel("Feature #0")
train_ax.set_ylabel("Feature #1")

test_ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test)
test_ax.set_xlabel("Feature #0")
test_ax.set_title("Testing data")
plt.show()

Trainings- en testdatasets

De volgende stap is het converteren van de trainings- en testdata van NumPy-arrays naar PyTorch-tensors. Hiervoor gaan we een custom dataset maken voor onze trainings- en testbestanden. We maken ook gebruik van PyTorch’ Dataloader-module zodat we onze data in batches kunnen trainen. Hier is de code: 

import warnings
warnings.filterwarnings("ignore")

!pip install torch -q

import torch
import numpy as np
from torch.utils.data import Dataset, DataLoader

# Convert data to torch tensors
class Data(Dataset):
    def __init__(self, X, y):
        self.X = torch.from_numpy(X.astype(np.float32))
        self.y = torch.from_numpy(y.astype(np.float32))
        self.len = self.X.shape[0]
       
    def __getitem__(self, index):
        return self.X[index], self.y[index]
   
    def __len__(self):
        return self.len
   
batch_size = 64

# Instantiate training and test data
train_data = Data(X_train, y_train)
train_dataloader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)

test_data = Data(X_test, y_test)
test_dataloader = DataLoader(dataset=test_data, batch_size=batch_size, shuffle=True)

# Check it's working
for batch, (X, y) in enumerate(train_dataloader):
    print(f"Batch: {batch+1}")
    print(f"X shape: {X.shape}")
    print(f"y shape: {y.shape}")
    break


"""
Batch: 1
X shape: torch.Size([64, 2])
y shape: torch.Size([64])
"""

Laten we nu verder gaan met het implementeren en trainen van ons neuraal netwerk. 

Implementatie van het neurale netwerk & modeltraining

We implementeren een eenvoudig tweelaags neuraal netwerk dat de ReLU-activatiefunctie (torch.nn.functional.relu) gebruikt. Hiervoor maken we een klasse NeuralNetwork die erft van nn.Module, de basisklasse voor alle neurale-netwerkmodules in PyTorch. 

Hier is de code: 

import torch
from torch import nn
from torch import optim

input_dim = 2
hidden_dim = 10
output_dim = 1

class NeuralNetwork(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super(NeuralNetwork, self).__init__()
        self.layer_1 = nn.Linear(input_dim, hidden_dim)
        nn.init.kaiming_uniform_(self.layer_1.weight, nonlinearity="relu")
        self.layer_2 = nn.Linear(hidden_dim, output_dim)
       
    def forward(self, x):
        x = torch.nn.functional.relu(self.layer_1(x))
        x = torch.nn.functional.sigmoid(self.layer_2(x))

        return x
       
model = NeuralNetwork(input_dim, hidden_dim, output_dim)
print(model)


"""
NeuralNetwork(
  (layer_1): Linear(in_features=2, out_features=10, bias=True)
  (layer_2): Linear(in_features=10, out_features=1, bias=True)
)
"""

En dat is alles. 

Om het model te trainen moeten we een verliesfunctie definiëren om de gradiënten te berekenen en een optimizer om de parameters bij te werken. Voor onze demonstratie gebruiken we binaire cross-entropy en stochastic gradient descent met een leersnelheid van 0,1. 

learning_rate = 0.1

loss_fn = nn.BCELoss()

optimizer = torch.optim.SGD(model.parameters(), lr=lea

Laten we ons model trainen

num_epochs = 100
loss_values = []


for epoch in range(num_epochs):
    for X, y in train_dataloader:
        # zero the parameter gradients
        optimizer.zero_grad()
       
        # forward + backward + optimize
        pred = model(X)
        loss = loss_fn(pred, y.unsqueeze(-1))
        loss_values.append(loss.item())
        loss.backward()
        optimizer.step()

print("Training Complete")

"""
Training Complete
"""

Omdat we de verlieswaarden hebben bijgehouden, kunnen we het verlies van het model in de tijd visualiseren. 

step = range(len(loss_values))

fig, ax = plt.subplots(figsize=(8,5))
plt.plot(step, np.array(loss_values))
plt.title("Step-wise Loss")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.show()

Model trainen

De visualisatie hierboven toont het verlies van ons model over 100 epochs. In het begin start het verlies op 0,7 en neemt het geleidelijk af - dit laat zien dat ons model zijn voorspellingen in de loop van de tijd heeft verbeterd. Het model lijkt echter rond epoch 60 te stagneren, wat aan verschillende redenen kan liggen, zoals dat het model zich in de buurt van een lokaal of globaal minimum van de verliesfunctie bevindt. 

Hoe dan ook, het model is getraind en klaar om voorspellingen te doen op nieuwe voorbeelden - in de volgende sectie bekijken we hoe je dat doet.

Voorspellingen & modelevaluatie

Voorspellingen maken met ons PyTorch-neuraal netwerk is vrij eenvoudig. 

import itertools  # Import this at the top of your script

# Initialize required variables
y_pred = []
y_test = []
correct = 0
total = 0

"""
We're not training so we don't need to calculate the gradients for our outputs
"""
with torch.no_grad():
    for X, y in test_dataloader:
        outputs = model(X)  # Get model outputs
        predicted = np.where(outputs.numpy() < 0.5, 0, 1)  # Convert to NumPy and apply threshold
        predicted = list(itertools.chain(*predicted))  # Flatten predictions
        y_pred.append(predicted)  # Append predictions
        y_test.append(y.numpy())  # Append true labels as NumPy
        total += y.size(0)  # Increment total count
        correct += (predicted == y.numpy()).sum().item()  # Count correct predictions

print(f'Accuracy of the network on the 3300 test instances: {100 * correct // total}%')

"""
Accuracy of the network on the 3300 test instances: 97%
"""

Let op: Elke run van de code geeft een andere output, dus je krijgt mogelijk niet exact dezelfde resultaten. 

De bovenstaande code loopt door de testbatches, die zijn opgeslagen in de variabele test_dataloader, zonder de gradiënten te berekenen. We voorspellen vervolgens de voorbeelden in de batch en slaan de resultaten op in een variabele outputs. Daarna zetten we alle waarden kleiner dan 0,5 op 0 en die gelijk aan of groter dan 0,5 op 1. Deze waarden worden vervolgens toegevoegd aan een lijst met onze voorspellingen.

Daarna tellen we de daadwerkelijke voorspellingen van de voorbeelden in de batch op bij een variabele total. Vervolgens berekenen we het aantal correcte voorspellingen door het aantal voorspellingen dat gelijk is aan de werkelijke klassen te bepalen en dit op te tellen. Het totale aantal correcte voorspellingen per batch wordt opgehoogd en opgeslagen in onze variabele correct.

Om de nauwkeurigheid van het algehele model te berekenen, vermenigvuldigen we het aantal correcte voorspellingen met 100 (om een percentage te krijgen) en delen we dit dan door het aantal voorbeelden in onze testset. Ons model haalde 97% nauwkeurigheid. We duiken dieper met de confusion matrix en scikit-learn’s classification_report om een beter beeld te krijgen van hoe ons model presteerde.

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix

import seaborn as sns

y_pred = list(itertools.chain(*y_pred))
y_test = list(itertools.chain(*y_test))

print(classification_report(y_test, y_pred))

"""
              precision    recall  f1-score   support

        0.0       0.98      0.97      0.98      1635
        1.0       0.98      0.98      0.98      1665

    accuracy                           0.98      3300
  macro avg       0.98      0.98      0.98      3300
weighted avg       0.98      0.98      0.98      3300

"""

cf_matrix = confusion_matrix(y_test, y_pred)

plt.subplots(figsize=(8, 5))

sns.heatmap(cf_matrix, annot=True, cbar=False, fmt="g")

plt.show()

Voorspellingen neurale netwerken

Ons model presteert behoorlijk goed. Ik raad je aan de code te verkennen en wat wijzigingen te maken, zodat wat we in dit artikel hebben behandeld beter blijft hangen.  

In deze PyTorch-tutorial hebben we de fundamentele basis van neurale netwerken behandeld en PyTorch, een Python-bibliotheek voor deep learning, gebruikt om ons netwerk te implementeren. We gebruikten de circles-dataset van scikit-learn om een tweelaags neuraal netwerk te trainen voor classificatie. Daarna maakten we voorspellingen op de data en evalueerden we onze resultaten met de nauwkeurigheidsmaat. 

Onderwerpen
Python
Data Science

Cursussen voor Python

Cursus

Introductie tot Data Science in Python

4 uur
504.2K
Duik in datawetenschap met Python en leer hoe je je data goed kunt analyseren en visualiseren. Je hoeft niet te kunnen programmeren of iets dergelijks.
Bekijk detailsRight Arrow
Start Cursus
Meer bekijkenRight Arrow
Gerelateerd

blog

AI vanaf nul leren in 2026: een complete gids van de experts

Ontdek alles wat je moet weten om in 2026 AI te leren, van tips om te beginnen tot handige resources en inzichten van industrie-experts.
Adel Nehme's photo

Adel Nehme

15 min

Meer BekijkenMeer Bekijken