home

Generative Adversarial Network

Creator: Petar Veličković (original)

Generative adversarial network (GAN) architecture. A GAN has two parts. The generator GG maps latent noise z→∼pz\vec z \sim p_z to fake samples. The discriminator DD acts as a classifier that learns to distinguish these fake samples from real data. GG incurs a penalty when DD detects implausible results. This signal is backpropagated through DD into the generator weights such that GG learns to produce more realistic samples over time, eventually fooling the discriminator if training succeeds.


Generative Adversarial Network

Download

PNG PDF SVG

Code

generative-adversarial-network.typ (64 lines)
#import "@preview/cetz:0.5.3": canvas, draw
#import draw: circle, content, hobby, line, on-layer

#set page(width: auto, height: auto, margin: 8pt, fill: none)
#set text(size: 12pt)

#canvas({
  let arrow-style = (stroke: 0.8pt, mark: (end: "stealth", scale: 0.4, fill: black))
  let annotated-arrow(start, end, name, formula, caption: none, caption-padding: 0.1) = {
    line(start, end, ..arrow-style, name: name)
    content(name + ".mid", formula, anchor: "south", padding: 0.1)
    if caption != none {
      content(name + ".mid", text(size: 14pt, caption), anchor: "north", padding: caption-padding)
    }
  }

  let (y-real, y-fake) = (2, 0)

  for (pos, name, label, radius) in (
    ((0, y-fake), "zin", $arrow(z)_"in"$, 0.53),
    ((3, y-fake), "fake", $arrow(x)_"fake"$, 0.53),
    ((3, y-real), "real", $arrow(x)_"real"$, 0.53),
    ((6, y-real / 2), "D", $arrow(x)$, 0.4),
  ) {
    circle(pos, name: name, stroke: none, fill: rgb("#66B2B2"), radius: radius) // teal!60
    content(name, label)
  }
  content((rel: (3, 0), to: "D"), text(baseline: -1pt)[real?], name: "out", padding: 2pt)

  annotated-arrow((-3.2, y-fake), "zin", "zin-line", $p_z (arrow(z))$, caption: [latent noise])
  annotated-arrow("zin", "fake", "fake-line", $G(arrow(z))$, caption: [generator])
  annotated-arrow(
    (rel: (-5, 0), to: "real"),
    "real",
    "real-line",
    $p_"data" (arrow(x))$,
    caption: [real data],
  )

  // fake and real samples feed into a switch (orange) that picks D's input
  for (idx, y) in ((1, y-fake), (2, y-real)) {
    circle((4.5, y), radius: 0.06, fill: black, name: "dot" + str(idx))
  }
  on-layer(1, circle(
    (4.25, 2 * y-real / 3),
    radius: 0.12,
    fill: orange,
    stroke: none,
    name: "dot3",
  ))
  for (start, end) in (("fake", "dot1"), ("real", "dot2"), ("dot3", "D")) {
    line(start, end, ..arrow-style)
  }
  hobby("dot1", (4.2, (y-real - y-fake) / 2), "dot2", stroke: (dash: "dashed"), omega: 2)

  annotated-arrow(
    "D",
    "out",
    "disc-line",
    $D(arrow(x))$,
    caption: [discriminator],
    caption-padding: 0.45,
  )
})