NumPy-indexering & slicing: hoe je tensors moeiteloos kunt knippen

1. Waarom is indexering/slicing zo belangrijk?



Bij deep learning kom je vaak met tensors te maken.

  • Alleen de eerste paar samples uit een batch halen
  • Alleen een specifiek kanaal (R/G/B) uit een afbeelding selecteren
  • Alleen een deel van de tijdstappen uit sequentiedata knippen
  • Alleen een bepaalde klasse uit labels extraheren

Al deze handelingen komen uiteindelijk neer op "indexering (indexing) & slicing".

En de indexering/slicing-syntaxis van PyTorch’s Tensor is bijna identiek aan die van NumPy, dus als je NumPy goed onder de knie hebt, wordt het schrijven van deep‑learning‑code veel eenvoudiger.


2. Basisindexering: 1‑dimensies

2.1 Indexering van een 1‑D array

import numpy as np

x = np.array([10, 20, 30, 40, 50])

print(x[0])  # 10
print(x[1])  # 20
print(x[4])  # 50
  • Indexen beginnen bij 0
  • x[i] geeft het i‑de element

2.2 Negatieve indexen

Als je vanaf het einde wilt tellen, gebruik je negatieve indexen.

print(x[-1])  # 50 (laatste)
print(x[-2])  # 40

PyTorch werkt hier ook op dezelfde manier.


3. Basis slicing: start:stop:step



Slicing wordt gebruikt als x[start:stop:step].

x = np.array([10, 20, 30, 40, 50])

print(x[1:4])    # [20 30 40], 1 ≤ i < 4
print(x[:3])     # [10 20 30], vanaf 0 tot 3 (exclusief)
print(x[2:])     # [30 40 50], vanaf 2 tot het einde
print(x[:])      # kopie van het geheel

Met een step kun je een interval instellen.

print(x[0:5:2])  # [10 30 50], 0 tot 4 met stap 2
print(x[::2])    # [10 30 50], hetzelfde

In deep learning is dit handig om bijvoorbeeld elke tweede tijdstap te overslaan of om een bepaalde interval van samples te kiezen.


4. Indexering van 2‑D en hoger: rijen en kolommen

Een 2‑D array is in feite een matrix/batch, dus het begint al de deep‑learning‑sfeer te krijgen.

import numpy as np

X = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])  # vorm: (3, 3)

4.1 Enkelvoudige indexering van rijen/kolommen

print(X[0])      # eerste rij: [1 2 3]
print(X[1])      # tweede rij: [4 5 6]

print(X[0, 0])   # rij 1, kolom 1: 1
print(X[1, 2])   # rij 2, kolom 3: 6
  • X[i] → i‑de rij (1‑D array)
  • X[i, j] → waarde op rij i, kolom j (scalar)

4.2 Rij‑slicing

print(X[0:2])    # rijen 0 en 1
# [[1 2 3]
#  [4 5 6]]

print(X[1:])     # vanaf rij 1 tot het einde
# [[4 5 6]
#  [7 8 9]]

Dit is een patroon dat je vaak ziet bij het knippen van een deel van een batch in PyTorch.

4.3 Kolom‑slicing

print(X[:, 0])   # alle rijen, kolom 0 → [1 4 7]
print(X[:, 1])   # alle rijen, kolom 1 → [2 5 8]
  • : betekent "hele dimensie"
  • X[:, 0] betekent "alle rijen, kolom 0"

In deep learning:

  • Bij een array van vorm (batch_size, feature_dim) kun je een specifieke feature selecteren met X[:, k].

5. 3‑D en hoger: batch × kanaal × hoogte × breedte

Laten we een voorbeeld nemen met beelddata.

# (batch, hoogte, breedte) aannemen
images = np.random.randn(32, 28, 28)  # 32 beelden, 28x28

5.1 Een enkel beeld selecteren

img0 = images[0]        # eerste beeld, vorm: (28, 28)
img_last = images[-1]   # laatste beeld

5.2 Een deel van de batch gebruiken

first_8 = images[:8]    # eerste 8, vorm: (8, 28, 28)

5.3 Een deel van het beeld knippen (crop)

# midden 20x20 gebied
crop = images[:, 4:24, 4:24]  # vorm: (32, 20, 20)

In PyTorch ziet het er zo uit:

# images_torch: (32, 1, 28, 28) een tensor
center_crop = images_torch[:, :, 4:24, 4:24]

Het concept van indexering/slicing blijft vrijwel hetzelfde.


6. Slicing is meestal een "view"

Een belangrijk punt:

Het resultaat van slicing is meestal een view van de originele array. Dat betekent dat er geen nieuwe data gekopieerd wordt, maar dat je een venster op de originele data hebt.

x = np.array([10, 20, 30, 40, 50])
y = x[1:4]   # view

print(y)     # [20 30 40]
y[0] = 999

print(y)     # [999  30  40]
print(x)     # [ 10 999  30  40  50]  ← originele array verandert!

Deze eigenschap heeft voordelen:

  • Bespaart geheugen en is sneller
  • Maar kan leiden tot onverwachte wijzigingen van de originele data

Wil je een volledig aparte array, gebruik dan copy().

x = np.array([10, 20, 30, 40, 50])
y = x[1:4].copy()

y[0] = 999
print(x)  # [10 20 30 40 50], origineel ongewijzigd

PyTorch heeft een vergelijkbaar concept, dus het is handig om het verschil "view vs copy" te kennen voor debugging.


7. Booleaanse indexering: filteren op voorwaarden

Booleaanse indexering wordt gebruikt om alleen de elementen te selecteren die een bepaalde voorwaarde voldoen.

import numpy as np

x = np.array([1, -2, 3, 0, -5, 6])

mask = x > 0
print(mask)      # [ True False  True False False  True]

pos = x[mask]
print(pos)       # [1 3 6]
  • x > 0 → array van True/False
  • x[mask] → alleen de True‑posities

Voorbeeld van combinaties:

X = np.array([[1, 2, 3],
              [4, 5, 6],
              [-1, -2, -3]])

pos = X[X > 0]
print(pos)  # [1 2 3 4 5 6]

In deep learning wordt dit vaak gebruikt om:

  • Alleen samples met een bepaalde label te extraheren
  • Bij verliesberekening een masker toe te passen en alleen bepaalde waarden te middelen

PyTorch werkt hier ook op dezelfde manier:

import torch

x = torch.tensor([1, -2, 3, 0, -5, 6])
mask = x > 0
pos = x[mask]

8. Indexering met een integer array / lijst (Fancy Indexing)

Met een array of lijst van integer‑indexen kun je meerdere posities in één keer selecteren.

x = np.array([10, 20, 30, 40, 50])

idx = [0, 2, 4]
print(x[idx])  # [10 30 50]

Ook in 2‑D werkt het.

X = np.array([[1, 2],
              [3, 4],
              [5, 6]])  # vorm: (3, 2)

rows = [0, 2]
print(X[rows])  
# [[1 2]
#  [5 6]]

In deep learning kun je bijvoorbeeld:

  • Een willekeurig gemengde indexarray gebruiken om een batch te trekken
  • Alleen bepaalde labels/predictions verzamelen voor statistieken

PyTorch ondersteunt dezelfde stijl.


9. Veelgebruikte indexeringspatronen samengevat

Hier zijn enkele patronen die vaak voorkomen in deep learning:

import numpy as np

# (batch, feature)
X = np.random.randn(32, 10)

# 1) Alleen de eerste 8 samples
X_head = X[:8]              # (8, 10)

# 2) Alleen een specifieke feature (bijv. kolom 3)
f3 = X[:, 3]                # (32,)

# 3) Alleen even‑index samples
X_even = X[::2]             # (16, 10)

# 4) Alleen samples met label 1
labels = np.random.randint(0, 3, size=(32,))
mask = labels == 1
X_cls1 = X[mask]            # samples met label 1

# 5) Random shuffle en splitsen in train/val
indices = np.random.permutation(len(X))
train_idx = indices[:24]
val_idx = indices[24:]

X_train = X[train_idx]
X_val = X[val_idx]

Deze patronen werken vrijwel identiek op PyTorch‑tensors. Het beheersen van NumPy‑indexering is dus gelijk aan het kunnen "knippen, mengen en selecteren" van tensor‑data.


10. Afsluiting

Samengevat:

  • Indexering: x[i], x[i, j], negatieve indexen
  • Slicing: start:stop:step, :, multi‑dimensies (X[:, 0], X[:8], X[:, 4:8])
  • Slicing levert meestal een view; gebruik copy() als je een aparte array wilt
  • Booleaanse indexering: filteren met voorwaarden (x[x > 0], X[labels == 1])
  • Integer‑array indexering: selecteren met een lijst van indices (x[[0,2,4]])

Met deze kennis kun je PyTorch‑tensors moeiteloos manipuleren: batch‑slicing, kanaalselectie, masker‑toepassing, en sample‑mixing.

image