PointNet : L'Architecture qui a tout changé

Comment traiter des nuages de points bruts sans voxélisation ni projection 2D ? La réponse de Stanford qui a révolutionné la 3D.
Expert
90 min
PyTorch, Mathématiques
Un objet unique extrait d'un scan, réduit à ses points : la forme se lit, sans maillage ni texture
Ce que PointNet reçoit en entrée : une liste de points, sans ordre et sans structure. Tout l'enjeu du réseau tient dans cette absence.
"Avant 2017, le Deep Learning 3D était un enfer de grilles de voxels gourmandes en mémoire. PointNet a prouvé qu'on pouvait apprendre directement sur des coordonnées XYZ désordonnées."

1. Le Problème de l'Ordre

Un nuage de points est un ensemble de N points. Si vous permutez l'ordre des points, l'objet reste le même (une chaise est une chaise, que le point 1 soit le pied ou le dossier). Mais un réseau de neurones classique (MLP, CNN) est sensible à l'ordre d'entrée.

PointNet résout cela en utilisant une fonction symétrique (Max Pooling) qui rend le réseau invariant aux permutations.

Chaîne en quatre étapes : nuage de N points sur trois coordonnées, perceptron appliqué point par point, agrégation par maximum, puis vecteur unique décrivant tout le nuage N x 3 le nuage brut MLP partagé le même réseau sur chaque point max par canal 1 x 1024 la signature Le maximum ne dépend pas de l'ordre des points : c'est ce qui rend le réseau insensible aux permutations.
L'architecture de PointNet, en quatre temps. Le même perceptron traite chaque point isolément, puis un maximum par canal résume l'ensemble. Cette opération est la clé : elle donne le même résultat quel que soit l'ordre dans lequel les points arrivent, alors qu'un nuage n'a pas d'ordre.

2. T-Net : L'Alignement Spatial

Comme pour l'ordre, le réseau doit être invariant à la rotation. PointNet apprend une matrice de transformation 3x3 (via un mini-réseau appelé T-Net) pour aligner canoniquement l'objet avant de le traiter.

tnet_module.py
class TNet(nn.Module):
    def __init__(self, k=3):
        super().__init__()
        self.conv1 = nn.Conv1d(k, 64, 1)
        self.fc1 = nn.Linear(1024, 512)
        # ... couches cachées ...
        self.fc3 = nn.Linear(256, k*k)
        # k doit survivre a __init__ : forward en a besoin.
        self.k = k
    
    def forward(self, x):
        # Apprend une matrice de transformation k x k
        matrix = self.fc3(x).view(-1, self.k, self.k)
        # Ajout de l'identité pour la stabilité, sur le meme appareil que x
        identity = torch.eye(self.k, device=x.device)
        return matrix + identity

3. Implémentation du Backbone

Le cœur de PointNet est une série de MLPs partagés (Conv1d avec kernel size 1 en PyTorch) qui montent la dimension de chaque point de 3 (xyz) à 64, puis 1024.

pointnet_cls.py
class PointNetCls(nn.Module):
    def __init__(self, k=10, feature_transform=False):
        super().__init__()
        self.feat = PointNetFeat(global_feat=True)
        self.fc1 = nn.Linear(1024, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, k) # k classes
        self.dropout = nn.Dropout(p=0.3)

    def forward(self, x):
        x, trans, trans_feat = self.feat(x)
        x = F.relu(self.fc1(x))
        x = F.log_softmax(self.fc3(x), dim=1)
        return x, trans_feat

4. Classification vs Segmentation

Tâche Output Architecture
Classification 1 classe par nuage Max Pooling global -> FC Layers
Segmentation 1 classe par point Concaténer Global Feat (1024) à chaque Point Feat (64)

Pour la segmentation, l'idée de génie est de réinjecter l'information globale (le contexte de la forme entière) au niveau local de chaque point. Ainsi, le point sait "je suis un point situé en bas" ET "je fais partie d'une chaise", donc "je suis probablement un pied".

Sur ce sujet : Les modules d’apprentissage profond 3D.

Entraînez votre premier réseau 3D

Notre TP "Deep Learning 3D" vous guide pas à pas pour entraîner PointNet sur le dataset ModelNet40 et segmenter des parties d'avions.

Recevoir le guide gratuit Passer le test (10 min)

Le programme et les tarifs →