Commit fe2b79ce authored by Leo Colisson's avatar Leo Colisson
Browse files

Ajout graphs + partie GNN

parent fef15dbb
\documentclass[]{beamer}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[francais]{babel}
\usepackage{siunitx}
% \usepackage[caption=false]{subfig}
\usepackage{caption}
\usepackage{subcaption}
\usepackage{etoolbox}
\usepackage{marvosym} %scorpion, capricorn
\newcommand{\scorp}{\text{\Scorpio}}
\newcommand{\capri}{\text{\Capricorn}}
\usepackage{cancel} % Barer des équations
\usepackage{tikz}
\usetikzlibrary{shapes.geometric,arrows}
\usetikzlibrary{mindmap,backgrounds,positioning}
\usetikzlibrary{automata,positioning,fit,backgrounds}
\usetikzlibrary{positioning,arrows}
\usetikzlibrary{matrix,chains,positioning,decorations.pathreplacing,arrows}
\usepackage{pgfplots}
% \usepackage{subcaption}
\usetheme{Warsaw}
% \usepackage{aas_macros}
\graphicspath{{pictures/}} % on change la racine des images
\usepackage{thmtools}
% \declaretheorem[name=Definition]{definition}
% \declaretheorem[name=Theorem]{theorem}
% \declaretheorem[name=Lemma,sibling=theorem]{lemma}
% \usetheme{Ilmenau}
% \usecolortheme{seahorse}
% \usepackage{apacite}
% Thème général du diaporama - quasi obligatoire
% \usetheme{Malmoe}
% \usetheme{Copenhagen}
% Nous verrons apres ce que cela veut dire
% \usecolortheme[named=blue]{structure}
% \usepackage[citestyle=verbose]{biblatex}
% \setbeamercolor{itemize item}{fg=white!80!black}
% \setbeamercolor{itemize subitem}{fg=white!80!black}
\AtBeginSection[]
{
\begin{frame}<beamer>
\frametitle{Plan}
\tableofcontents[currentsection]
\end{frame}
}
\defbeamertemplate*{footline}{shadow theme}{%
\leavevmode%
\hbox{\begin{beamercolorbox}[wd=.5\paperwidth,ht=2.5ex,dp=1.125ex,leftskip=.3cm plus1fil,rightskip=.3cm]{author in head/foot}%
\usebeamerfont{author in head/foot}\hfill\insertshortauthor
\end{beamercolorbox}%
\begin{beamercolorbox}[wd=.5\paperwidth,ht=2.5ex,dp=1.125ex,leftskip=.3cm,rightskip=.3cm plus1fil]{title in head/foot}%
\usebeamerfont{title in head/foot}\insertshorttitle\hfill%
\insertframenumber\,/\,\inserttotalframenumber
\end{beamercolorbox}}%
\vskip0pt%
}
% \addtobeamertemplate{navigation symbols}{}{%
% \usebeamerfont{footline}%
% \usebeamercolor[fg]{footline}%
% \hspace{1em}%
% \insertframenumber/\inserttotalframenumber
% }
\title[Projet Réseau]{Présentation du Projet Réseau}
\author{Colisson Léo \and Jeanmaire Paul}
\institute{École Normale Supérieure Paris Saclay, Département Informatique \\Initiation à la recherche}
\begin{document}
% ==================================================
\begin{frame}
\titlepage
\end{frame}
% ==================================================
\begin{frame}
\tableofcontents
\end{frame}
% ==================================================
\section{Réseau de neurone}
% TODO : Paul
\section{Rétro-propagation du gradient}
% TODO : Léo
\begin{frame}[fragile]
\frametitle{Rétro-propagation du gradient}
\begin{figure}[H]
\begin{tikzpicture}[
plain/.style={
draw=none,
fill=none,
},
net/.style={
matrix of nodes,
nodes={
draw,
circle,
inner sep=10pt
},
nodes in empty cells,
column sep=2cm,
row sep=-9pt
},
>=latex
]
\matrix[net] (mat)
{
|[plain]| \parbox{1cm}{} & |[plain]| \parbox{1cm}{} & |[plain]| \parbox{1cm}{} \\
& |[plain]| \\
|[plain]| & \\
& |[plain]| \\
|[plain]| & |[plain]| \\
& & \\
|[plain]| & |[plain]| \\
& |[plain]| \\
|[plain]| & \\
& |[plain]| \\
};
\foreach \ai [count=\mi ]in {2,4,...,10}
\draw[<-] (mat-\ai-1) -- node[above] {Input \mi} +(-2cm,0);
\foreach \ai in {2,4,...,10}
{\foreach \aii in {3,6,9}
\draw[->] (mat-\ai-1) -- (mat-\aii-2);
}
\foreach \ai in {3,6,9}
\draw[->] (mat-\ai-2) -- (mat-6-3);
\draw[->] (mat-6-3) -- node[above] {Ouput} +(2cm,0);
\end{tikzpicture}
\end{figure}
\end{frame}
\begin{frame}
\frametitle{Rétro-propagation du gradient}
\begin{block}{Cas général : algorithme du gradient}
Minimiser une fonction $f(x)$ via l'algorithme du gradient :
\begin{enumerate}
\item Calcul du gradient $\nabla f(x_k)$
\item On s'arrête si $\nabla f(x_k) \leq \varepsilon$
\item On calcule $x_{k+1} = x_k - \lambda \nabla f(x_k)$ pour un $\lambda$ bien choisit
\item On recommence
\end{enumerate}
\end{block}
\end{frame}
\begin{frame}
\frametitle{Rétro-propagation du gradient}
\begin{exampleblock}{Cas des neurones}
\begin{itemize}
\item $\mathcal{N}_\omega(x) =$ fonction issue du réseau de neurone ($\omega = $ poids, $x = $ entrées)
\item $\mathcal{E}_x = $ valeur idéale de $\mathcal{N}_\omega$ dans la base d'entraînement.
\end{itemize}
But : trouver ``les meilleurs poids'' $\Rightarrow$ minimiser pour un $x$ fixé la fonction
$$f: \omega \rightarrow \mathcal{N}_\omega(x) - \mathcal{E}_x$$
Problème : on ne sait pas calculer $\nabla f$ en un seul bloc
Solution : rétro-propagation locale.
\end{exampleblock}
\end{frame}
\begin{frame}[fragile]
\frametitle{Rétro-propagation du gradient}
\begin{figure}[H]
\begin{tikzpicture}[
plain/.style={
draw=none,
fill=none,
},
net/.style={
matrix of nodes,
nodes={
draw,
circle,
inner sep=10pt
},
nodes in empty cells,
column sep=2cm,
row sep=-9pt
},
>=latex
]
\matrix[net] (mat)
{
|[plain]| \parbox{1cm}{} & |[plain]| \parbox{1cm}{} & |[plain]| \parbox{1cm}{} \\
& |[plain]| \\
|[plain]| & \\
& |[plain]| \\
|[plain]| & |[plain]| \\
& & \\
|[plain]| & |[plain]| \\
& |[plain]| \\
|[plain]| & \\
& |[plain]| \\
};
\foreach \ai [count=\mi ]in {2,4,...,10}
\draw[<-] (mat-\ai-1) -- node[above] {Input \mi} +(-2cm,0);
\foreach \ai in {2,4,...,10}
{\foreach \aii in {3,6,9}
\draw[<-] (mat-\ai-1) -- (mat-\aii-2);
}
\foreach \ai in {3,6,9}
\draw[<-] (mat-\ai-2) -- (mat-6-3);
\draw[<-] (mat-6-3) -- node[above] {Ouput} +(2cm,0);
\end{tikzpicture}
\end{figure}
\end{frame}
\section{Graph Neural Network (GNN)}
\begin{frame}
\frametitle{Graph Neural Network (GNN)}
\begin{block}{GNN}
\begin{itemize}
\item Idée : pouvoir appliquer un réseau de neurone sur un graph
\end{itemize}
\end{block}
\end{frame}
\tikzstyle{vertex}=[circle,fill=black!25,minimum size=20pt,inner sep=0pt]
\tikzstyle{selected vertex} = [vertex, fill=red!24]
\tikzstyle{edge} = [draw,thick,-]
\tikzstyle{weight} = [font=\small]
\tikzstyle{selected edge} = [draw,line width=5pt,-,red!50]
\tikzstyle{ignored edge} = [draw,line width=5pt,-,black!20]
\begin{frame}
\frametitle{Graph Neural Network (GNN)}
\begin{figure}[H]
\centering
\begin{tikzpicture}[scale=1.8, auto,swap]
% Draw a 7,11 network
% First we draw the vertices
\foreach \pos/\name/\nname in {{(0,2)/a/45}, {(2,1)/b/12}, {(4,1)/c/8},
{(0,0)/d/16}, {(3,0)/e/1}, {(2,-1)/f/7}, {(4,-1)/g/-5}}
\node[vertex] (\name) at \pos {$\nname$};
% Connect vertices with edges and draw weights
\foreach \source/ \dest /\weight in {b/a/7, c/b/8,d/a/5,d/b/9,
e/b/7, e/c/5,e/d/15,
f/d/6,f/e/8,
g/e/9,g/f/11}
\path[edge] (\source) -- node[weight] {$\weight$} (\dest);
% % Start animating the vertex and edge selection.
% \foreach \vertex / \fr in {d/1,a/2,f/3,b/4,e/5,c/6,g/7}
% \path<\fr-> node[selected vertex] at (\vertex) {$\vertex$};
% For convenience we use a background layer to highlight edges
% This way we don't have to worry about the highlighting covering
% weight labels.
% \begin{pgfonlayer}{background}
% \pause
% \foreach \source / \dest in {d/a,d/f,a/b,b/e,e/c,e/g}
% \path<+->[selected edge] (\source.center) -- (\dest.center);
% \foreach \source / \dest / \fr in {d/b/4,d/e/5,e/f/5,b/c/6,f/g/7}
% \path<\fr->[ignored edge] (\source.center) -- (\dest.center);
% \end{pgfonlayer}
\end{tikzpicture}
\end{figure}
\end{frame}
\begin{frame}
\frametitle{Graph Neural Network (GNN)}
\begin{figure}[H]
\centering
\begin{tikzpicture}[scale=1.8, auto,swap]
% Draw a 7,11 network
% First we draw the vertices
\foreach \pos/\name/\nname in {{(0,2)/a/45}, {(2,1)/b/12}, {(4,1)/c/8}, {(3,0)/e/1}, {(2,-1)/f/7}, {(4,-1)/g/-5}}
\node[vertex] (\name) at \pos {$\nname$};
\node[selected vertex] (d) at (0,0) {$f_\omega$};
% Connect vertices with edges and draw weights
\foreach \source/ \dest /\weight in {b/a/7, c/b/8,d/a/5,d/b/9,
e/b/7, e/c/5,e/d/15,
f/d/6,f/e/8,
g/e/9,g/f/11}
\path[edge] (\source) -- node[weight] {$\weight$} (\dest);
% % Start animating the vertex and edge selection.
% \foreach \vertex / \fr in {d/1,a/2,f/3,b/4,e/5,c/6,g/7}
% \path<\fr-> node[selected vertex] at (\vertex) {$\vertex$};
% For convenience we use a background layer to highlight edges
% This way we don't have to worry about the highlighting covering
% weight labels.
% \begin{pgfonlayer}{background}
% \pause
% \foreach \source / \dest in {d/a,d/f,a/b,b/e,e/c,e/g}
% \path<+->[selected edge] (\source.center) -- (\dest.center);
% \foreach \source / \dest / \fr in {d/b/4,d/e/5,e/f/5,b/c/6,f/g/7}
% \path<\fr->[ignored edge] (\source.center) -- (\dest.center);
% \end{pgfonlayer}
\end{tikzpicture}
\end{figure}
\end{frame}
\begin{frame}
\frametitle{Graph Neural Network (GNN)}
\begin{block}{Idée}
\begin{itemize}
\item $f_\omega$ est une contraction $\Rightarrow $ itération converge (Théorème de Banach).
\item On peut par exemple encoder $f_\omega$ dans un réseau de neurone récurrent.
\item Apprentissage : on chaîne les $f_\omega$, puis propagation du gradient.
\end{itemize}
\end{block}
\end{frame}
\end{document}
%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End:
......@@ -154,6 +154,7 @@
publisher="IEEE Press",
year={2009}
}
@Inbook{Ganesh2012,
author="Ganesh, Vijay
and O'Donnell, Charles W.
......
......@@ -470,75 +470,253 @@ la sortie d'un réseau neuronal, on procède de la manière suivante :
\subsection{L'apprentissage par rétro-propagation du gradient}
Cependant, définir une topologie au réseau ne suffit pas, il faut ensuite
lui faire \og{}apprendre\fg{} pour chaque neurone les bons coefficients
$w_i$, $b$. La méthode la plus efficace est celle de l'apprentissage par
rétro-propagation du gradient que nous allons étudier rapidement
maintenant.
Pour faire apprendre un réseau par rétro-propagation du gradient, on
procède en deux phases :
\begin{enumerate}
\item On commence par évaluer notre réseau sur une entrée donnée
\item On compare la sortie effective avec la sortie tabulée. Si ces
deux valeurs sont identique, on passe à une autre entrée, sinon on
calcule la différence entre ces deux valeurs, puis on inverse le
sens du réseau neuronal en \og{}rétro-propageant\fg{} ce gradient.
\end{enumerate}
Si on veut rentrer plus dans les détails de la rétro-propagation, on tombe
vite dans des considérations un peu techniques, mais pour décrire
rapidement ce phénomène, à la première étape on calcule pour chaque neurone
$i$ de la couche de sortie
\begin{equation*}
e^{sortie}_i = f'(h_i)[t_i - y_i]
\label{}
\end{equation*}
(avec $t_i$ la valeur tabulée de la sortie, $y_i$ la sortie effective, et
$h_i$ la valeur de la somme des entrées, juste avant de l'envoyer à $f$)
puis on rétro-propage $e^{(n)}_i \rightarrow e^{(n-1)}_j $ avec la formule
\begin{equation*}
e^{(n-1)}_j = f'(h^{(n-1)}_{j})\sum_i w_{ij}e^{(n)}_i
\label{}
\end{equation*}
On obtient alors un ensemble de corrections $e^{(n)}_j$ que l'on soustrait
aux valeurs alors utilisées des poids :
\begin{equation*}
w^{(n)}_{ij} = w^{(n)}_{ij} + \lambda e^{(n)}_i x^{(n-1)}_j
\label{}
\end{equation*}
(avec $\lambda \in ]0,1[$ bien choisi, suffisamment petit pour ne pas
\og{}sauter\fg{} les minima, et suffisamment grand pour que l'algorithme
converge rapidement)
Cependant, définir une topologie au réseau ne suffit pas, il faut
ensuite lui faire ``apprendre'' pour chaque neurone les bons
coefficients $w_i$, $b$. La méthode la plus efficace est celle de
l'apprentissage par rétro-propagation du gradient que nous allons
étudier rapidement maintenant.
Pour faire apprendre un réseau par rétro-propagation du gradient, on procède en deux phases :
\begin{enumerate}
\item On commence par évaluer notre réseau sur une entrée donnée
\item On compare la sortie effective avec la sortie tabulée. Si ces
deux valeurs sont identique, on passe à une autre entrée, sinon on
calcule la différence entre ces deux valeurs, puis on inverse le
sens du réseau de neurone en ``rétro-propageant'' ce gradient.
\end{enumerate}
Si on veut rentrer plus dans les détails de la rétro-propagation, on
tombe vite dans des considérations un peu techniques, mais pour
décrire rapidement ce phénomène, à la première étape on calcule pour
chaque neurone $i$ de la couche de sortie
\begin{equation*}
e^{sortie}_i = f'(h_i)[t_i - y_i]
\label{}
\end{equation*}
(avec $t_i$ la valeur tabulée de la sortie, $y_i$ la sortie effective,
et $h_i$ la valeur de la somme des entrées, juste avant de l'envoyer à
$f$) puis on rétro-propage $e^{(n)}_i \rightarrow e^{(n-1)}_j $ avec
la formule
\begin{equation*}
e^{(n-1)}_j = f'(h^{(n-1)}_{j})\sum_i w_{ij}e^{(n)}_i
\label{}
\end{equation*}
On obtient alors un ensemble de corrections $e^{(n)}_j$ que l'on
soustrait aux valeurs alors utilisées des poids :
\begin{equation*}
w^{(n)}_{ij} = w^{(n)}_{ij} + \lambda e^{(n)}_i x^{(n-1)}_j
\label{}
\end{equation*}
(avec $\lambda \in ]0,1[$ bien choisi, suffisamment petit pour ne pas
``sauter'' les minima, et suffisamment grand pour que l'algorithme
converge rapidement)
\subsection{Un modèle de neurones appliqué au graphes}
Maintenant que l'on sait faire apprendre nos réseaux neuronaux, il reste
maintenant à trouver une structure à notre réseau. Pour l'apprentissage sur
les graphes, ce n'est pas évident de prime abord.
\subsection{Traduction des formules \SAT}
Maintenant que l'on sait faire apprendre nos réseaux de neurones, il
reste maintenant à trouver une structure à notre réseau. Pour
l'apprentissage sur les graphes, ce n'est pas évident de prime abord :
comment gérer le fait qu'aucun graphe n'a la même taille ? Comme
dépendre fortement de sa structure de graphe, et pas simplement
prendre une photo grossière ? Nous allons ainsi présenter la méthode
proposée par le papier \cite{lammgraph}.
L'idée de ce papier consiste à attribuer à chaque noeud et arête du
graphe des valuations sous forme de vecteur:
\tikzstyle{vertex}=[circle,fill=blue!25,minimum size=30pt,inner sep=2pt]
\tikzstyle{selected vertex} = [vertex, fill=red!24]
\tikzstyle{edge} = [draw,thick,-]
\tikzstyle{weight} = [font=\small]
\tikzstyle{selected edge} = [draw,line width=10pt,-,red!50]
\tikzstyle{ignored edge} = [draw,line width=10pt,-,black!20]
\begin{figure}[H]
\centering
\begin{tikzpicture}[scale=1.8, auto,swap]
% Draw a 7,11 network
% First we draw the vertices
\foreach \pos/\name/\nname in {{(0,2)/a/[45,12]}, {(2,1)/b/[4,6]},
{(4,1)/c/[5,20]}, {(0,0)/d/[4,1]}, {(3,0)/e/[0.4,4]},
{(2,-1)/f/[-5,4]}, {(4,-1)/g/[-4,7]}}
\node[vertex] (\name) at \pos {$\nname$};
% Connect vertices with edges and draw weights
\foreach \source/ \dest /\weight in {b/a/[7], c/b/[8],d/a/[5],d/b/[9],
e/b/[7], e/c/[5],e/d/[15],
f/d/[6],f/e/[8],
g/e/[9],g/f/[11]}
\path[edge] (\source) -- node[weight] {$\weight$} (\dest);
\end{tikzpicture}
\end{figure}
puis, on attribue à chaque noeud une même fonction $g_\omega$ :
\begin{figure}[H]
\centering
\begin{tikzpicture}[scale=1.8, auto,swap]
% Draw a 7,11 network
% First we draw the vertices
\foreach \pos/\name/\nname in {{(0,2)/a/[45,12]}, {(2,1)/b/[4,6]},
{(4,1)/c/[5,20]}, {(3,0)/e/[0.4,4]},
{(2,-1)/f/[-5,4]}, {(4,-1)/g/[-4,7]}}
\node[vertex] (\name) at \pos {$\nname$};
\node[selected vertex] (d) at (0,0) {$g_\omega$};
% Connect vertices with edges and draw weights
\foreach \source/ \dest /\weight in {b/a/[7], c/b/[8],d/a/[5],d/b/[9],
e/b/[7], e/c/[5],e/d/[15],
f/d/[6],f/e/[8],
g/e/[9],g/f/[11]}
\path[edge] (\source) -- node[weight] {$\weight$} (\dest);
\end{tikzpicture}
\end{figure}
Cette fonction étant un réseau de neurone de paramètre $\omega$
prenant en entrée les labels des arêtes et des noeuds
voisins. Pour calculer la valeur finale des valuations étant
donné une fonction $g_\omega$, on initialise les valuations à 0
par exemple, puis on applique simultanément pour chaque noeud la
fonction $g_\omega$ en itérant jusqu'à observer une convergence
des valuations du réseau après $T$ itérations (ce qui arrive sous
certaines conditions, notamment si $g_\omega$ est
contractante).\\
Pour apprendre la fonction $g$, il faut utiliser une petite astuce. On
ne peut pas vraiment faire un algorithme de rétro-propagation du
gradient tel quel à cause de la notion de convergence, on est donc
obligé de créer un plus gros réseau de neurone. Pour ce faire, on créé
une matrice de taille $n \times T$, avec $n$ le nombre de noeuds du
réseau. Dans chaque case $(i,t)$ on mets le réseau $g_\omega$, et l'on
connecte la sorties du réseau $M[i,t]$ aux entrées de $M[j,t+1]$ dès
que $i$ et $j$ sont connectés dans le graphe. On obtient alors un gros
réseau de neurone, sur lequel on peut alors appliquer l'algorithme de
rétro-propagation du gradient.
\begin{figure}[H]
\centering
\begin{tikzpicture}[
c/.style={
draw,
circle,
fill=blue!30,
inner sep=5pt
},
node distance=15mm
]
\matrix[matrix of nodes,row sep=2em, column sep=2em]
{
\node (g1) {input};
& \node[c] (g11) {$g_\omega$}; & \node[c] (g12) {$g_\omega$};
& \node[c] (g13) {$g_\omega$}; & \node[c] (g14) {$g_\omega$};
\\
\node (g2) {input};
& \node[c] (g21) {$g_\omega$}; & \node[c] (g22) {$g_\omega$};
& \node[c] (g23) {$g_\omega$}; & \node[c] (g24) {$g_\omega$};
\\
\node (g3) {input};
& \node[c] (g31) {$g_\omega$}; & \node[c] (g32) {$g_\omega$};
& \node[c] (g33) {$g_\omega$}; & \node[c] (g34) {$g_\omega$};
\\
\node(g4) {}; & & & & \node(g44) {};
\\
};
% % Draw
\draw[->] (g1) -- (g11);
\draw[->] (g2) -- (g21);
\draw[->] (g3) -- (g31);
\draw[->] (g11) -- (g12);
\draw[->] (g12) -- (g13);
\draw[->] (g13) -- (g14);
\draw[->] (g21) -- (g22);
\draw[->] (g22) -- (g23);
\draw[->] (g23) -- (g24);
\draw[->] (g31) -- (g32);
\draw[->] (g32) -- (g33);
\draw[->] (g33) -- (g34);
\draw[->] (g11) -- (g22);
\draw[->] (g12) -- (g23);
\draw[->] (g13) -- (g24);
\draw[->] (g11) -- (g22);
\draw[->] (g12) -- (g23);
\draw[->] (g13) -- (g24);
\draw[->] (g31) -- (g22);
\draw[->] (g32) -- (g23);
\draw[->] (g33) -- (g24);
\draw[->] (g21) -- (g32);
\draw[->] (g22) -- (g33);
\draw[->] (g23) -- (g34);
\draw[->,above] (g4) to node {$T$} (g44);
\end{tikzpicture}
\caption{Nouveau réseau de neurone}
\label{fig:matrix}
\end{figure}
Les formules propositionnelles considérées dans \SAT\ sont représentées
sous forme normale conjonctive (conjonction de clauses de taille fixée).
Deux façons d'encoder ces formules dans des graphes sont présentées:
\begin{itemize}
\item les graphes de facteurs clause-variable (figure) dans lesquels
clauses et variables sont deux types de n\oe uds distincts: les graphes
ainsi formés sont bipartis, et l'étiquette des arêtes représente la
négation ou non de la variable dans la clause;
\item les graphes variable-variable, dans lesquels les n\oe uds
correspondent aux variables de la formule et les arêtes relient les
variables apparaissant au moins une fois dans la même clause.
\end{itemize}
\subsection{Résultats sur des réseaux neuronaux}
\begin{figure}[H]
\centering
\begin{tikzpicture}[
remember picture,
start chain=1 going right,
start chain=2 going right,
circ/.style={
draw,
circle,
fill=blue!30,
inner sep=5pt,
on chain=1
},
squa/.style={
draw,
inner sep=5pt,
fill=blue!20,
on chain=2
}
]
\matrix [matrix of nodes,nodes={anchor=center},row sep = 2em]
{
% Variables
\node{\tikz{
\node[circ] (x1) {$x_1$};
\node[circ] (x2) {$x_2$};
\node[circ] (x3) {$x_3$};
}};
\\
\node{\tikz{
% Clauses
\node[squa] (c1) {$\lnot x_1 \lor \lnot x_2$};
\node[squa] (c2) {$x_2 \lor \lnot x_3$};
\node[squa] (c3) {$x_1 \lor x_2$};
\node[squa] (c4) {$\lnot x_2 \lor x_3$};
\node[squa] (c5) {$\lnot x_1 \lor x_3$};
}};
\\
};
% Draw
\draw[dashed]
(x1.south) -- (c1.north)
(x1.south) -- (c5.north)
(x2.south) -- (c1.north)