ajout algos + images

This commit is contained in:
Laureηt 2023-08-21 16:19:19 +02:00
parent 52913ae4b9
commit cca27ba374
Signed by: Laurent
SSH key fingerprint: SHA256:kZEpW8cMJ54PDeCvOhzreNr4FSh6R13CMGH/POoO8DI
10 changed files with 135 additions and 15 deletions

BIN
assets/gp_efficiency_1_pca.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/gp_massflow_1_pca.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/gp_train_30_pca.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/gp_train_32_pod.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/graphunet-architecture.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/kpfcnn-results.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/pca_cumsum_11000.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/pca_cumsum_1200.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/pvd-results.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -18,6 +18,7 @@
\usepackage{graphicx} \usepackage{graphicx}
\usepackage{microtype} \usepackage{microtype}
\usepackage{amsmath} \usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsfonts} \usepackage{amsfonts}
\usepackage{bbold} \usepackage{bbold}
\usepackage[numbers]{natbib} \usepackage[numbers]{natbib}
@ -27,6 +28,10 @@
\usepackage{caption} \usepackage{caption}
\usepackage{placeins} \usepackage{placeins}
\usepackage{algorithmicx}
\usepackage{algorithm}
\usepackage{algpseudocode}
% We use \hypersetup to pass options to hyperref % We use \hypersetup to pass options to hyperref
\hypersetup{ \hypersetup{
colorlinks = true, colorlinks = true,
@ -149,6 +154,8 @@
\begin{document} \begin{document}
\NoAutoSpacing
\frontmatter \frontmatter
\vbox{ \vbox{
@ -517,6 +524,30 @@ On peut finalement simplifier cette expression via une reparamétrisation:
\end{equation} \end{equation}
Si l'on réeffectue une dérivation de l'\gls{elbo} avec ces nouvelles expressions, on en conclut qu'il suffit de trouver une approximation $\epsilon_\theta (\boldsymbol{x}_t, t) \approx \epsilon_0$. En pratique on utilise un réseau de neurones que l'on entraine à minimiser $|\| \epsilon_0 - \epsilon_\theta (\boldsymbol{x}_t, t) \|_2^2$. Une fois cette approximation trouvée, on peut facilement remonter à $p_\theta (\boldsymbol{x}_{t-1} | \boldsymbol{x}_t)$. Si l'on réeffectue une dérivation de l'\gls{elbo} avec ces nouvelles expressions, on en conclut qu'il suffit de trouver une approximation $\epsilon_\theta (\boldsymbol{x}_t, t) \approx \epsilon_0$. En pratique on utilise un réseau de neurones que l'on entraine à minimiser $|\| \epsilon_0 - \epsilon_\theta (\boldsymbol{x}_t, t) \|_2^2$. Une fois cette approximation trouvée, on peut facilement remonter à $p_\theta (\boldsymbol{x}_{t-1} | \boldsymbol{x}_t)$.
\begin{algorithm}
\caption{DDPM training}
\begin{algorithmic}[1]
\Repeat
\State $x_0 \sim \boldsymbol{x_0}$
\State $t \sim \text{Uniform}( \lbrack\!\lbrack 1, T \rbrack\!\rbrack )$
\State $\epsilon \sim \mathcal{N}(0, \mathbf{I})$
\State take gradient step on $\displaystyle \nabla_\theta \| \epsilon - \epsilon_\theta ( \sqrt{\overline{\alpha}_t} x_0 + \sqrt{1 - \overline{\alpha}_t} \epsilon, t ) \|_2^2$
\Until{converged}
\end{algorithmic}
\end{algorithm}
\begin{algorithm}
\caption{DDPM sampling}
\begin{algorithmic}[1]
\State $x_T \sim \mathcal{N}(0, \mathbf{I})$
\For{$t=T, ..., 1$}
\State $z \sim \mathcal{N}(0, \mathbf{I})\ \text{if}\ t > 1, \text{else}\ z = 0$
\State $\displaystyle x_{t-1} = \frac{1}{\sqrt{\overline{\alpha}_t}} \left( x_t - \frac{\sqrt{1 - \alpha_t}}{\sqrt{\overline{\alpha}_t}} \epsilon_\theta (x_t, t) \right) + \sigma_t z$
\EndFor
\State \Return $x_0$
\end{algorithmic}
\end{algorithm}
Après avoir achevé l'entraînement adéquat de notre modèle, on peut désormais appliquer $p_\theta (\boldsymbol{x}_{t-1} | \boldsymbol{x}_t)$ itérativement pour passer d'un échantillon $x_T$ à sa prédiction $\hat{x}_0$. Dans les faits, le réseau commence à halluciner des reconstructions qui présentent une forte ressemblance avec nos données d'entraînement, il génère donc de nouvelles données. Après avoir achevé l'entraînement adéquat de notre modèle, on peut désormais appliquer $p_\theta (\boldsymbol{x}_{t-1} | \boldsymbol{x}_t)$ itérativement pour passer d'un échantillon $x_T$ à sa prédiction $\hat{x}_0$. Dans les faits, le réseau commence à halluciner des reconstructions qui présentent une forte ressemblance avec nos données d'entraînement, il génère donc de nouvelles données.
Il est possible de démontrer théoriquement~\cite{luo_understanding_2022} l'équivalence entre les \glspl{vdm} et les méthodes de score matching~\cite{song_generative_2020} lorsque $T$ tend vers l'infini. Les méthodes de score matching, constituent une famille de techniques permettant l'estimation de la densité de probabilité associée à un ensemble de données. Elles se basent exclusivement sur le calcul du gradient de cette densité de probabilité, éliminant ainsi la nécessité du calcul laborieux d'une constante de normalisation. Une fois le gradient estimé (e.g. via un réseau de neurones), la densité de probabilité peut être retrouvée au moyen de méthodes d'échantillonnage telles que la méthode de recuit de Langevin~\cite{song_generative_2020}. Il est possible de démontrer théoriquement~\cite{luo_understanding_2022} l'équivalence entre les \glspl{vdm} et les méthodes de score matching~\cite{song_generative_2020} lorsque $T$ tend vers l'infini. Les méthodes de score matching, constituent une famille de techniques permettant l'estimation de la densité de probabilité associée à un ensemble de données. Elles se basent exclusivement sur le calcul du gradient de cette densité de probabilité, éliminant ainsi la nécessité du calcul laborieux d'une constante de normalisation. Une fois le gradient estimé (e.g. via un réseau de neurones), la densité de probabilité peut être retrouvée au moyen de méthodes d'échantillonnage telles que la méthode de recuit de Langevin~\cite{song_generative_2020}.
@ -590,6 +621,20 @@ En subsituant l'équation \ref{eq:166} dans l'équation ci-dessus, on obtient:
Cette approche présente divers avantages. Premièrement, elle s'appuie sur un unique réseau de neurones, contrairement à la méthode guidée par classificateur qui en utilise deux. Deuxièmement, l'entraînement ne devient pas significativement plus complexe ; nous procédons de la même manière qu'auparavant, en ajoutant simplement les embeddings de classe et en parfois excluant aléatoirement ces embeddings (selon une loi uniforme) pour apprendre en conditionnement non contraint. Troisièmement, les données telles que le texte se prêtent difficilement à une classification en classes, et cette approche permet l'utilisation de vecteurs scalaires pour le conditionnement. Cette approche présente divers avantages. Premièrement, elle s'appuie sur un unique réseau de neurones, contrairement à la méthode guidée par classificateur qui en utilise deux. Deuxièmement, l'entraînement ne devient pas significativement plus complexe ; nous procédons de la même manière qu'auparavant, en ajoutant simplement les embeddings de classe et en parfois excluant aléatoirement ces embeddings (selon une loi uniforme) pour apprendre en conditionnement non contraint. Troisièmement, les données telles que le texte se prêtent difficilement à une classification en classes, et cette approche permet l'utilisation de vecteurs scalaires pour le conditionnement.
\begin{algorithm}
\caption{Joint training a diffusion model with classifier-free guidance}
\begin{algorithmic}[1]
\Require $p_\text{uncond}$: probability of unconditional training
\Repeat
\State $(x_0, c) \sim (\boldsymbol{x_0}, \boldsymbol{c})$
\State $c \leftarrow \varnothing$ with probability $p_\text{uncond}$
\State $t \sim \text{Uniform}( \lbrack\!\lbrack 1, T \rbrack\!\rbrack )$
\State $\epsilon \sim \mathcal{N}(0, \mathbf{I})$
\State take gradient step on $\nabla_\theta \| \epsilon - \epsilon_\theta ( \sqrt{\overline{\alpha}_t} x_0 + \sqrt{1 - \overline{\alpha}_t} \epsilon, t, c ) \|_2^2$
\Until{converged}
\end{algorithmic}
\end{algorithm}
Dans notre cas d'application, nous pouvons conditionner sur les scalaires représentant les performances de nos maillages. Dans notre cas d'application, nous pouvons conditionner sur les scalaires représentant les performances de nos maillages.
% https://liorsinai.github.io/coding/2023/01/04/denoising-diffusion-3-guidance.html#guided-diffusion % https://liorsinai.github.io/coding/2023/01/04/denoising-diffusion-3-guidance.html#guided-diffusion
@ -734,7 +779,14 @@ Une première solution simple consistait en l'utilisation de une ou plusieurs co
Pour donner un ordre de grandeur, si l'on utilisai un espace latent de taille $8$, rien que pour prédire les positions 3D des points dans notre maillage (sans prendre en compte la connectivité), l'utilisation d'une seule couche dense impliquait déjà $8 \times 3.10^4 \times 3 = 7,2.10^6$ paramètres. Prédire la connectivité était tout simplement impossible, car il aurait fallu une couche dense avec plus de $8 \times 9.10^8 = 7,2.10^9$ paramètres, ce qui dépassait largement les capacités de calcul de nos ressources GPU. Pour donner un ordre de grandeur, si l'on utilisai un espace latent de taille $8$, rien que pour prédire les positions 3D des points dans notre maillage (sans prendre en compte la connectivité), l'utilisation d'une seule couche dense impliquait déjà $8 \times 3.10^4 \times 3 = 7,2.10^6$ paramètres. Prédire la connectivité était tout simplement impossible, car il aurait fallu une couche dense avec plus de $8 \times 9.10^8 = 7,2.10^9$ paramètres, ce qui dépassait largement les capacités de calcul de nos ressources GPU.
Une seconde solution consitait à utiliser une architecture plus intelligente, telle que Graph U-Net. Cette approche permettait d'éviter l'utilisation de couches denses dans le décodeur grâce à des connexions résiduelles (skip connections). Cependant, ce faisant l'information ne passait pas entièrement par l'espace latent entre le décodeur et l'encodeur. Par conséquent, il était impossible de créer un modèle génératif complet avec cette architecture, puisqu'une partie de l'information pour générer des échantillons était compris dans les skip connections. Une seconde solution consitait à utiliser une architecture plus intelligente, telle que Graph U-Net~\cite{gao_graph_2019}. Cette approche permettait d'éviter l'utilisation de couches denses dans le décodeur grâce à des connexions résiduelles (skip connections). Cependant, ce faisant l'information ne passait pas entièrement par l'espace latent entre le décodeur et l'encodeur. Par conséquent, il était impossible de créer un modèle génératif complet avec cette architecture, puisqu'une partie de l'information pour générer des échantillons était compris dans les skip connections.
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\textwidth]{graphunet-architecture.png}
\caption{Architecture de Graph U-Net}
\label{fig:graphunet_archi}
\end{figure}
Face aux difficultés rencontrées avec les réseaux basés sur les VAE et les limitations de l'architecture Graph U-Net, nous avons pris la décision de mettre de côté ces approches. Et plus largement puisque la connectivité de nos graphes est "locale" (les noeuds des nos maillages sont connectés à leurs voisins proches dans l'espace), nous avons décidé de nous orienter vers des approches basées uniquement sur les positions des noeuds. En effet, la connectivité d'un nuage de points peut facilement être retrouvé via diverses techniques~\cite{peng_shape_2021,sulzer_deep_2022,andrade-loarca_poissonnet_2023}. Face aux difficultés rencontrées avec les réseaux basés sur les VAE et les limitations de l'architecture Graph U-Net, nous avons pris la décision de mettre de côté ces approches. Et plus largement puisque la connectivité de nos graphes est "locale" (les noeuds des nos maillages sont connectés à leurs voisins proches dans l'espace), nous avons décidé de nous orienter vers des approches basées uniquement sur les positions des noeuds. En effet, la connectivité d'un nuage de points peut facilement être retrouvé via diverses techniques~\cite{peng_shape_2021,sulzer_deep_2022,andrade-loarca_poissonnet_2023}.
@ -788,11 +840,17 @@ Une autre architecture largement reconnue dans le domaine du traitement de nuage
\FloatBarrier \FloatBarrier
\subsection{Test de KP-FCNN} \subsection{Test de KP-FCNN}
Dans notre situation, nous avons la possibilité d'opter pour le réseau KP-FCNN, initialement conçu pour la segmentation, mais pouvant être ajusté pour la prédiction de bruit pour une utilisation dans \gls{ddpm}. À cette fin, nous faisons usage de la bibliothèque easy\_kpconv, laquelle met en œuvre les \glspl{kpconv} et nous autorise à maintenir un code clair et réutilisable. Lorsque nous l'entraînons sur le jeu de données rotor37\_1200\_mmd2048, nous obtenons des résultats plutôt probants. Dans notre situation, nous avons la possibilité d'opter pour le réseau KP-FCNN, initialement conçu pour la segmentation, mais pouvant être ajusté pour la prédiction de bruit pour une utilisation dans \gls{ddpm}. À cette fin, nous faisons usage de la bibliothèque easy\_kpconv, implémentant les \glspl{kpconv} et nous permettant de créer un code clair et réutilisable.
% TODO: insérer ici résultats \begin{figure}[h!]
\centering
\includegraphics[width=0.8\textwidth]{kpfcnn-results.png}
\caption{Résultats de KP-FCNN sur rotor37\_1200.}
\label{fig:kpfcnn_results}
\end{figure}
% décrire les résultats, c'est bien, mais encore un peu de bruit et pas trop scalable à cause des MLPs dans le decodeur Comme on l'observe dans la figure~\ref{fig:kpfcnn_results}, cette architecture permet de générer des nuages de points en forme d'aube. Cependant, on remarque que les générations sont d'assez mauvaise qualité puisque les nuages de points semblent comporter une part de bruit résiduel importante. Certaines générations ne ressemblent de même pas du tout à des aubes, mais plutôt à des nuages de points aléatoires.
Autre point négatif, le decodeur de KP-FCNN étant composé de \glspl{mlp}, il n'est pas scalable. En effet le réseau actuel comporte environ 4 millions de paramètres, dont une large majorité se situent dans le décodeur.
\FloatBarrier \FloatBarrier
\subsection{Présentation des \glspl{pvconv}} \subsection{Présentation des \glspl{pvconv}}
@ -812,16 +870,25 @@ Une version légèrement améliorée de gls{pvconv}, appelée gls{spvconv}, a é
\FloatBarrier \FloatBarrier
\subsection{Test de PVD} \subsection{Test de PVD}
Le premier papier ayant utilisé une architecture du type PVCNN++ pour la générations de nuages de point est PVD. Ce réseau utilise \gls{ddpm} et travaille directement sur le nuage de points. Si l'on récupère l'implémentation des auteurs et que l'on la modifie pour utiliser rotor37\_37\_mmd2048 on obtient de très bon résultat. Cependant, une bonne partie de la codebase étant basé sur celle de PVCNN++ et de PointFlow, celle-ci est tout aussi difficile à modifier.
\begin{figure}[h!] \begin{figure}[h!]
\centering \centering
\includegraphics[width=0.8\textwidth]{pvd-architecture.png} \includegraphics[width=0.8\textwidth]{pvd-architecture.png}
\caption{Architecture de PVD~\cite{zhou_3d_2021}} \caption{Architecture de PVD.}
\label{fig:pvd_archi} \label{fig:pvd_archi}
\end{figure} \end{figure}
% TODO: afficher résultats et décrire Le premier papier ayant utilisé une architecture du type PVCNN++ pour la générations de nuages de point est PVD~\cite{zhou_3d_2021}. Ce réseau utilise \gls{ddpm} et travaille directement sur le nuage de points. Si l'on récupère l'implémentation des auteurs et que l'on la modifie pour utiliser rotor37\_37\_mmd2048 on obtient de très bon résultat. Cependant, une bonne partie de la codebase étant basé sur celle de PVCNN++ et de PointFlow, celle-ci est tout aussi difficile à modifier.
\begin{figure}[h!]
\centering
\includegraphics[width=0.8\textwidth]{pvd-results.png}
\caption{Résultats de PVD sur rotor37\_1200.}
\label{fig:pvd_results}
\end{figure}
Comme on l'observe sur la figure~\ref{fig:pvd_results}, les générations que produisent PVD sont de très bonne qualité. Contrairement à KP-FCNN, les générations ne présentent pas de bruits résiduels. Cependant PVD est un réseau assez lourd, (27,6 millions de paramètres) et nécéssite un entrainement plutot long d'environ 10 heures pour produire de bon résultats.
% TODO: vérif le 10h et le nb de params.
\FloatBarrier \FloatBarrier
\glsunset{ldm} \glsunset{ldm}
@ -849,14 +916,22 @@ Les résultats précédents montrent que la résolution de notre problème est p
pour essayer d'améliorer les résultats que nous avions obtenus via KP-FCNN, nous avons opté pour une approche par \gls{ldm}. Pour cela il nous fallait choisir un modèle capable de transformer/réduire nos données d'entrée. Nous avons sélectionné dans un premier temps une \gls{pca}. En effet, après analyse, les données de rotor37\_1200 se porte très bien à une compression par \gls{pca}. De plus, il s'agit d'une méthode non paramétrique, ce qui est un avantage car pas besoin d'opti. pour essayer d'améliorer les résultats que nous avions obtenus via KP-FCNN, nous avons opté pour une approche par \gls{ldm}. Pour cela il nous fallait choisir un modèle capable de transformer/réduire nos données d'entrée. Nous avons sélectionné dans un premier temps une \gls{pca}. En effet, après analyse, les données de rotor37\_1200 se porte très bien à une compression par \gls{pca}. De plus, il s'agit d'une méthode non paramétrique, ce qui est un avantage car pas besoin d'opti.
% TODO: insérer figures cumsum mode pca \begin{figure}[h!]
\centering
\includegraphics[width=0.5\textwidth]{pca_cumsum_1200.png}\includegraphics[width=0.5\textwidth]{pca_cumsum_11000.png}
\caption{Somme cumulative des modes de la PCA. Gauche: rotor37\_1200. Droite: rotor37\_11000.}
\label{fig:pca_cumsum}
\end{figure}
Via cette figure nous en déduisons qu'environ 30 modes permettent de décrire 99\% de l'information contenue dans les données. Nous avons donc choisi de réduire nos données à 30 dimensions via une \gls{pca}. Dans le cas de rotor37\_1200 on passe donc de 30000 points contenant leur positions et leur normales à un vecteur de taille 30. Ce vecteur etant très petit et aussi structuré, nous pouvons nous permettre d'utiliser un simple \gls{mlp} pour prédire le bruit dans le \gls{ddpm}. Le réseau résultat dépasse à peine les 10 000 paramètres, ce qui est très peu comparé aux autres modèles présentés précedemment. Ainsi le réseau est très simple à manipuler et à entrainer. Pour le décodeur nous pouvons donc simplement effectuer l'opération inverse de la PCA pour transformer un vecteure de taille 30 en un nuage de points de taille 30000 (positions + normales). Via cette figure nous en déduisons qu'environ 30 modes permettent de décrire 90\% de l'information contenue dans rotor37\_1200. On remarque cependant que cette compression est moins efficace pour rotor37\_11000, ce qui est normal car les données se ressemblent moins.
%Pour palier à ce problème nous avons essayé de remplacer la PCA par une POD, mais comme nous le verrons par la suite, même si cela permettait une meilleur compression, le calcul de la POD était bien plus couteux et se prétait moins bien aux autres techniques nous allons utiliser.
% travaile sur 1200 dans un premier temps
Nous avons donc choisi de réduire nos données à 30 dimensions via une \gls{pca}. Dans le cas de rotor37\_1200 on passe donc de 30000 points contenant leur positions et leur normales à un vecteur de taille 30. Ce vecteur etant très petit et aussi structuré, nous pouvons nous permettre d'utiliser un simple \gls{mlp} pour prédire le bruit dans le \gls{ddpm}. Le réseau résultat dépasse à peine les 10 000 paramètres, ce qui est très peu comparé aux autres modèles présentés précedemment. Ainsi le réseau est très simple à manipuler et à entrainer. Pour le décodeur nous pouvons donc simplement effectuer l'opération inverse de la PCA pour transformer un vecteure de taille 30 en un nuage de points de taille 30000 (positions + normales).
Après quelques entrainements, on obtient des résultats très satisfasant, comme le montre la figure suivante: Après quelques entrainements, on obtient des résultats très satisfasant, comme le montre la figure suivante:
% TODO: insérer des résultats
% décrire les résultat. % décrire les résultat.
\FloatBarrier \FloatBarrier
@ -887,11 +962,29 @@ Une solution est d'entrainer un modèle de regression pour trouver une relation
Ainsi si l'on entrainer un \gls{gp} sur des couples (nuage de points, critère de performance), on peut ensuite utiliser ce \gls{gp} pour vérifier si nos nuages de points générés sont corrects. En effet, si l'on génère un nuage de points, et que l'on obtient un critère de performance très différent de celui attendu, alors on peut en déduire que notre génération est incorrecte. Ainsi si l'on entrainer un \gls{gp} sur des couples (nuage de points, critère de performance), on peut ensuite utiliser ce \gls{gp} pour vérifier si nos nuages de points générés sont corrects. En effet, si l'on génère un nuage de points, et que l'on obtient un critère de performance très différent de celui attendu, alors on peut en déduire que notre génération est incorrecte.
% insérer figure gp entrainé \begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{gp_train_30_pca.png}
\caption{Entrainement d'un \gls{gp} sur 30 modes PCA de rotor37\_1200}
\end{figure}
% insérer figure gp générations massflow=-1 % \begin{figure}[h!]
% \centering
% \includegraphics[width=1\textwidth]{gp_train_32_pod.png}
% \caption{Entrainement d'un \gls{gp} sur 32 modes POD de rotor37\_1200}
% \end{figure}
% insérer figure gp générations efficiency=1 \begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{gp_massflow_1_pca.png}
\caption{Vérification du conditionnement (out\_massflow=1) par \gls{gp}}
\end{figure}
\begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{gp_efficiency_1_pca.png}
\caption{Vérification du conditionnement (isentropy\_efficiency=1) par \gls{gp}}
\end{figure}
Comme on l'observe sur les figures, on observe très clairement un changement de la densité de probabilité des modes \glspl{pca} lorsque l'on conditionne nos générations. on en déduit que notre modèle est capable de générer correctement des données conditionnées. On observer de même sa capacité à générer certaines données out of distribution, du moment que celle-ci ne sont pas trop éloignées de la distribution d'entrainement. Par example il est totalement impossible de demander la génération d'aubes ayant un in\_massflow et out\_massflow différents, car cela n'existe pas dans le jeu de données d'entrainement, et car cela est aussi impossible physiquement dans un système fermé. Comme on l'observe sur les figures, on observe très clairement un changement de la densité de probabilité des modes \glspl{pca} lorsque l'on conditionne nos générations. on en déduit que notre modèle est capable de générer correctement des données conditionnées. On observer de même sa capacité à générer certaines données out of distribution, du moment que celle-ci ne sont pas trop éloignées de la distribution d'entrainement. Par example il est totalement impossible de demander la génération d'aubes ayant un in\_massflow et out\_massflow différents, car cela n'existe pas dans le jeu de données d'entrainement, et car cela est aussi impossible physiquement dans un système fermé.