
Conv1d de PyTorch applique un filtre appris le long d’une séquence. Pour un lot, organisez l’entrée sous la forme (N, C, L) : exemples, canaux, puis longueur de séquence. Si vos données placent les pas de temps avant les caractéristiques, permutez ces deux derniers axes avant de transmettre le tenseur à la couche.
Ce guide présente cinq petits exemples dont les poids sont fixés pour rendre les calculs vérifiables. Nous avons exécuté 26 contrôles sur CPU avec Python 3.12.14 et PyTorch 2.15.0+cpu : dimensions, valeurs, configurations invalides et rétropropagation. Chaque bloc Python fonctionne indépendamment avec PyTorch installé, sans jeu de données ni modèle à télécharger.
La documentation de Conv1d décrit les dimensions attendues et les paramètres. Ici, un exemple contient un canal de cinq valeurs. Nous choisissons le filtre [1, 0, -1], désactivons son biais et conservons un pas de 1 sans padding.
import torch
x = torch.tensor([[[1., 2., 3., 4., 5.]]])
layer = torch.nn.Conv1d(1, 1, kernel_size=3, bias=False)
with torch.no_grad():
layer.weight.copy_(torch.tensor([[[1., 0., -1.]]]))
y = layer(x)
print('Input:', tuple(x.shape))
print('Output:', tuple(y.shape))
print('Values:', y.detach().tolist())
Sortie observée :
Input: (1, 1, 5)
Output: (1, 1, 3)
Values: [[[-2.0, -2.0, -2.0]]]
Le premier calcul donne 1 × 1 + 2 × 0 + 3 × (-1) = -2. Un déplacement d’une position produit 2 × 1 + 3 × 0 + 4 × (-1) = -2, puis encore -2. Trois fenêtres complètes tiennent dans ce signal de cinq valeurs.
Les poids sont utilisés dans l’ordre affiché. Il s’agit d’une corrélation croisée, appelée convolution dans l’API de PyTorch. Les poids fixes rendent le résultat prévisible ; une boucle d’entraînement apprendrait normalement ces poids. Ce résultat démontre l’opération, sans constituer un détecteur entraîné utile.
Supposons que chaque pas de temps comporte trois mesures de capteurs. Deux exemples de cinq pas peuvent arriver sous la forme (2, 5, 3). Dans cet exemple, les trois mesures doivent occuper l’axe des canaux. Elles ne représentent pas trois pas de temps distincts.
Utilisez transpose pour permuter les axes de séquence et de caractéristiques. Les valeurs distinctes ci-dessous permettent de vérifier que chaque caractéristique conserve son identité :
import torch
# Two sequences, five time steps, three features per step.
raw = torch.arange(30, dtype=torch.float32).reshape(2, 5, 3)
x = raw.transpose(1, 2)
layer = torch.nn.Conv1d(3, 4, kernel_size=3, bias=False)
with torch.no_grad():
layer.weight.fill_(1.)
y = layer(x)
print('Input:', tuple(x.shape))
print('First feature:', x[0, 0].tolist())
print('Output:', tuple(y.shape))
print('First output channel:', y[0, 0].detach().tolist())
Sortie observée :
Input: (2, 3, 5)
First feature: [0.0, 3.0, 6.0, 9.0, 12.0]
Output: (2, 4, 3)
First output channel: [36.0, 63.0, 90.0]
La première caractéristique reste [0, 3, 6, 9, 12]. La première valeur de sortie additionne les trois premières positions des trois canaux : (0 + 3 + 6) + (1 + 4 + 7) + (2 + 5 + 8) = 36. Les quatre canaux de sortie sont identiques parce que nous avons volontairement attribué les mêmes poids à leurs filtres.
Nos contrôles ont confirmé que raw.reshape(2, 3, 5) produit des valeurs différentes de cette transposition. Obtenir les bonnes dimensions avec reshape ne garantit pas que leur sens est préservé. Transmettre directement le tenseur (2, 5, 3) a également provoqué une erreur : la couche attend trois canaux, mais cette disposition en place cinq sur l’axe des canaux.
Examinez un lot avant de construire la suite du modèle. Notre guide PyTorch DataLoader, en anglais explique la constitution des lots. Pour des signaux à un canal regroupés sous la forme (N, L), ajoutez un axe de canal avec unsqueeze(1). Ne supposez pas que tout tenseur à deux dimensions représente un lot : notre contrôle sans axe de lot a aussi confirmé que Conv1d accepte (C, L) pour un seul exemple.
Pour un padding entier identique aux deux extrémités, calculez :
L_out = floor((L_in + 2p - d(k - 1) - 1) / s) + 1
k désigne la taille du noyau, s le pas, p le padding par côté et d la dilatation. Nous avons vérifié cette formule avec six combinaisons. Pour une longueur de 8, un noyau de taille 3, un pas de 2, aucun padding et une dilatation de 1, elle donne floor(5 / 2) + 1 = 3.
L’exemple suivant utilise un filtre de trois coefficients égaux à 1. Les résultats montrent les valeurs réunies dans chaque fenêtre :
import torch
x = torch.arange(1, 9, dtype=torch.float32).reshape(1, 1, 8)
cases = [
('valid', dict(kernel_size=3)),
('stride 2', dict(kernel_size=3, stride=2)),
('pad 1', dict(kernel_size=3, padding=1)),
('dilation 2', dict(kernel_size=3, dilation=2)),
('same', dict(kernel_size=3, padding='same')),
]
for label, options in cases:
layer = torch.nn.Conv1d(1, 1, bias=False, **options)
with torch.no_grad():
layer.weight.fill_(1.)
y = layer(x)
print(label, tuple(y.shape), y.detach().flatten().tolist())
Sortie observée :
valid (1, 1, 6) [6.0, 9.0, 12.0, 15.0, 18.0, 21.0]
stride 2 (1, 1, 3) [6.0, 12.0, 18.0]
pad 1 (1, 1, 8) [3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0, 15.0]
dilation 2 (1, 1, 4) [9.0, 12.0, 15.0, 18.0]
same (1, 1, 8) [3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0, 15.0]
Avec un pas de 2, les fenêtres commencent aux positions 0, 2 et 4. Avec une dilatation de 2, la première fenêtre lit les valeurs 1, 3 et 5, soit une somme de 9. Les trois poids couvrent désormais cinq positions, mais le filtre conserve trois coefficients appris par canal d’entrée connecté.
Un zéro à chaque extrémité conserve ici une longueur de huit. La première somme vaut 3 pour la fenêtre [0, 1, 2], et la dernière vaut 15 pour [7, 8, 0]. Le cas same donne les mêmes valeurs. Notre test distinct a confirmé que la construction d’une couche avec padding='same' et un pas de 2 provoque une erreur dans la version testée.
Vérifiez aussi la largeur effective du noyau : d(k - 1) + 1. Notre test avec une entrée de longueur huit sans padding et un noyau de taille neuf a échoué. Augmenter le nombre de canaux de sortie ne permet pas de faire tenir cette fenêtre.
Dans l’exemple suivant, les valeurs du second canal valent dix fois celles du premier. Avec groups=1, chacun des deux filtres de sortie additionne les fenêtres des deux canaux d’entrée. Avec groups=2, chaque sortie ne lit qu’un canal.
import torch
x = torch.tensor([[[1., 2., 3., 4.], [10., 20., 30., 40.]]])
for groups in (1, 2):
layer = torch.nn.Conv1d(2, 2, kernel_size=2, groups=groups, bias=False)
with torch.no_grad():
layer.weight.fill_(1.)
print('Groups:', groups, 'Weight:', tuple(layer.weight.shape))
print('Values:', layer(x).detach().tolist())
Sortie observée :
Groups: 1 Weight: (2, 2, 2)
Values: [[[33.0, 55.0, 77.0], [33.0, 55.0, 77.0]]]
Groups: 2 Weight: (2, 1, 2)
Values: [[[3.0, 5.0, 7.0], [30.0, 50.0, 70.0]]]
La première valeur combinée vaut 1 + 2 + 10 + 20 = 33. En séparant les groupes, on obtient 3 et 30. Les dimensions des poids changent également : la couche groupée possède quatre coefficients au lieu de huit, mais les deux sorties conservent la forme (1, 2, 3).
L’API exige que les nombres de canaux d’entrée et de sortie soient divisibles par le nombre de groupes. Nous avons testé séparément des valeurs invalides pour ces deux nombres ; toutes deux ont été rejetées. Nous avons aussi vérifié un multiplicateur depthwise de deux : Conv1d(2, 4, 2, groups=2) a produit deux filtres indépendants par canal d’entrée, sans mélanger les deux canaux. Un nombre de coefficients réduit ne prouve pas un gain de vitesse sur votre matériel ; ce guide ne comporte aucun benchmark.
Conserver la longueur de sortie ne garantit pas la causalité. Dans un modèle de prévision, une caractéristique à la position t peut devoir exclure toutes les positions ultérieures. La fenêtre symétrique de trois valeurs de notre exemple same inclut l’échantillon suivant.
Pour la couche ci-dessous, dont le pas vaut 1, nous ajoutons dilation × (kernel_size - 1) zéros à gauche et aucun à droite. La couche elle-même n’ajoute pas de padding. F.pad accepte la paire gauche/droite pour le dernier axe.
import torch
import torch.nn.functional as F
x = torch.tensor([[[1., 2., 3., 4., 5.]]])
layer = torch.nn.Conv1d(1, 1, kernel_size=3, dilation=2, bias=False)
with torch.no_grad():
layer.weight.fill_(1.)
left = layer.dilation[0] * (layer.kernel_size[0] - 1)
y = layer(F.pad(x, (left, 0)))
changed = x.clone()
changed[..., 3:] = 100.
y_changed = layer(F.pad(changed, (left, 0)))
print('Values:', y.detach().flatten().tolist())
print('Earlier outputs unchanged:', torch.equal(y[..., :3], y_changed[..., :3]))
Sortie observée :
Values: [1.0, 2.0, 4.0, 6.0, 9.0]
Earlier outputs unchanged: True
Avec une dilatation de 2, la valeur à la position 4 vaut 1 + 3 + 5 = 9. Modifier les entrées aux positions 3 et 4 n’a pas modifié les sorties des positions 0 à 2. Un test séparé avec le padding symétrique same a modifié la sortie antérieure à la position 2 lorsque nous avons changé l’entrée à la position 3.
Ce contrôle établit le comportement de cette seule couche. Un système de prévision complet peut encore laisser passer des informations futures par la normalisation, la construction des caractéristiques, l’alignement des cibles ou la séparation entre entraînement et test. Examinez ces choix séparément. Si vous prédisez la valeur suivante, définissez explicitement le décalage de la cible ; la convolution ne décale pas automatiquement les étiquettes.
En cas d’échec, examinez le sens des axes autant que leurs dimensions :
in_channels pour accepter une entrée dont les axes ont été inversés par erreur.Nous avons aussi contrôlé une rétropropagation avec un signal de longueur cinq et un filtre de trois coefficients égaux à 1. Additionner les trois sorties a donné les gradients d’entrée [1, 2, 3, 2, 1] : la valeur centrale participe à trois fenêtres. Ce test vérifie le passage des gradients dans cet exemple, sans établir de convergence sur une tâche réelle.
Une fois ces mécanismes compris, choisissez le reste du modèle selon le problème. Notre guide des architectures de réseaux de neurones, en anglais traite de ce choix plus large. Les exemples présents se limitent aux séquences unidimensionnelles et aux calculs sur CPU. Ils ne testent ni CUDA, ni la précision d’un modèle, ni le débit. L’environnement local a émis un avertissement d’initialisation lié à l’absence de NumPy ; tous les contrôles utilisant uniquement des tenseurs ont abouti sans cette bibliothèque.
Choisissez une charge de travail d’IA, de calcul ou de stockage à tester sur Hivenet, ou échangez avec notre équipe sur sa mise en production.