├── src
├── plot_tools
│ ├── __init__.py
│ ├── pics_tools.py
│ └── cmap_tools.py
├── data_tools
│ ├── __init__.py
│ └── mnist_dataset.py
├── kl_tools.py
└── test.py
├── .gitignore
├── output_1.png
├── output_2.png
├── README.md
└── tutorial.ipynb
/src/plot_tools/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *__pycache__/
3 |
--------------------------------------------------------------------------------
/src/data_tools/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/output_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dcmoyer/invariance-tutorial/HEAD/output_1.png
--------------------------------------------------------------------------------
/output_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dcmoyer/invariance-tutorial/HEAD/output_2.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Non-adversarial Invariant Representations Tutorial
2 |
3 | This repo contains tutorial content, example code, and working loss function
4 | calculators for
5 | [Invariant Representations without Adversarial Training](https://arxiv.org/abs/1805.09458).
6 |
7 | A transcription of the tutorial content in non-interactive HTML form is hosted
8 | on my website [in at test blogpost](dcmoyer.github.io/selfhosted/blag.html).
9 |
10 | You may also be interested in the [Echo Noise Distribution](https://arxiv.org/abs/1904.07199),
11 | a noise model for encodings _z_ that provides closed form rate terms I(z,x),
12 | with nicely computable derivatives. This is exactly the term we would like to
13 | penalize in the Inv. Rep. paper, but instead had to bound.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/data_tools/mnist_dataset.py:
--------------------------------------------------------------------------------
1 |
2 | import tensorflow as tf
3 | import numpy as np
4 | import sklearn.preprocessing as prep
5 |
6 | IMG_DIM=28
7 | NUM_LABELS=10
8 |
9 | def get_data():
10 |
11 | #TODO MOVE TO PREPROCESSING
12 | (train_x, train_y), (test_x, test_y) = tf.keras.datasets.mnist.load_data()
13 |
14 | train_x = train_x.astype(np.float32)
15 | test_x = test_x.astype(np.float32)
16 |
17 | train_x = train_x.reshape( (train_x.shape[0], IMG_DIM**2) ) / 255.0
18 | test_x = test_x.reshape( (test_x.shape[0], IMG_DIM**2) ) / 255.0
19 |
20 | def one_hot(labels):
21 | num_labels_data = labels.shape[0]
22 | one_hot_encoding = np.zeros((num_labels_data,NUM_LABELS))
23 | one_hot_encoding[np.arange(num_labels_data),labels] = 1
24 | one_hot_encoding = np.reshape(one_hot_encoding, [-1, NUM_LABELS])
25 | return one_hot_encoding
26 |
27 | train_y = one_hot(train_y)
28 | test_y = one_hot(test_y)
29 |
30 | train_y = train_y.astype(np.float32)
31 | test_y = test_y.astype(np.float32)
32 |
33 | #def standard_scale(X_train, X_test):
34 | # preprocessor = prep.StandardScaler().fit(X_train)
35 | # X_train = preprocessor.transform(X_train)
36 | # X_test = preprocessor.transform(X_test)
37 | # return X_train, X_test
38 |
39 | #train_x, test_x = standard_scale(train_x, test_x)
40 | #train_x = train_x / 255.0
41 | #test_x = test_x / 255.0
42 |
43 | return (train_x, train_y), (test_x, test_y)
44 |
45 |
--------------------------------------------------------------------------------
/src/plot_tools/pics_tools.py:
--------------------------------------------------------------------------------
1 |
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 |
5 |
6 | def plot_image_grid(x_data, img_shape, grid_shape):
7 |
8 | one_dim = False
9 | for i in grid_shape:
10 | if i == 1:
11 | one_dim = True
12 |
13 | n_pics = x_data.shape[0]
14 | n_pics_in_grid = np.prod(grid_shape)
15 |
16 | if n_pics_in_grid < n_pics:
17 | print("adding extra row to grid")
18 | grid_shape = (grid_shape[0] + 1, grid_shape[1])
19 |
20 | #sizes grid automatically (in inches)
21 | fig, ax = plt.subplots(grid_shape[0], grid_shape[1])
22 |
23 | if one_dim:
24 | for i in np.arange(max(grid_shape)):
25 | data_idx = i
26 |
27 | if data_idx >= n_pics:
28 | break
29 |
30 | #ax = plt.subplot(grid_shape[0], grid_shape[1], data_idx+1)
31 |
32 | img_data = x_data[data_idx].reshape(*img_shape)
33 |
34 | ax[i].imshow(img_data, interpolation="nearest", vmin=0, vmax=1)
35 | ax[i].get_xaxis().set_visible(False)
36 | ax[i].get_yaxis().set_visible(False)
37 |
38 | else:
39 | for i in np.arange(grid_shape[0]):
40 | for j in np.arange(grid_shape[1]):
41 |
42 | data_idx = j + grid_shape[1]*i
43 |
44 | if data_idx >= n_pics:
45 | break
46 |
47 | #ax = plt.subplot(grid_shape[0], grid_shape[1], data_idx+1)
48 |
49 | img_data = x_data[data_idx].reshape(*img_shape)
50 |
51 | ax[i,j].imshow(img_data, interpolation="nearest", vmin=0, vmax=1)
52 | ax[i,j].get_xaxis().set_visible(False)
53 | ax[i,j].get_yaxis().set_visible(False)
54 | plt.tight_layout()
55 |
56 | return fig
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/plot_tools/cmap_tools.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib
3 | import matplotlib.pyplot as plt
4 | from mpl_toolkits.axes_grid1 import AxesGrid
5 |
6 | def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap'):
7 | '''
8 | Function to offset the "center" of a colormap. Useful for
9 | data with a negative min and positive max and you want the
10 | middle of the colormap's dynamic range to be at zero.
11 |
12 | Input
13 | -----
14 | cmap : The matplotlib colormap to be altered
15 | start : Offset from lowest point in the colormap's range.
16 | Defaults to 0.0 (no lower offset). Should be between
17 | 0.0 and `midpoint`.
18 | midpoint : The new center of the colormap. Defaults to
19 | 0.5 (no shift). Should be between 0.0 and 1.0. In
20 | general, this should be 1 - vmax / (vmax + abs(vmin))
21 | For example if your data range from -15.0 to +5.0 and
22 | you want the center of the colormap at 0.0, `midpoint`
23 | should be set to 1 - 5/(5 + 15)) or 0.75
24 | stop : Offset from highest point in the colormap's range.
25 | Defaults to 1.0 (no upper offset). Should be between
26 | `midpoint` and 1.0.
27 | '''
28 | cdict = {
29 | 'red': [],
30 | 'green': [],
31 | 'blue': [],
32 | 'alpha': []
33 | }
34 |
35 | # regular index to compute the colors
36 | reg_index = np.linspace(start, stop, 257)
37 |
38 | # shifted index to match the data
39 | shift_index = np.hstack([
40 | np.linspace(0.0, midpoint, 128, endpoint=False),
41 | np.linspace(midpoint, 1.0, 129, endpoint=True)
42 | ])
43 |
44 | for ri, si in zip(reg_index, shift_index):
45 | r, g, b, a = cmap(ri)
46 |
47 | cdict['red'].append((si, r, r))
48 | cdict['green'].append((si, g, g))
49 | cdict['blue'].append((si, b, b))
50 | cdict['alpha'].append((si, a, a))
51 |
52 | newcmap = matplotlib.colors.LinearSegmentedColormap(name, cdict)
53 | plt.register_cmap(cmap=newcmap)
54 |
55 | return newcmap
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/kl_tools.py:
--------------------------------------------------------------------------------
1 |
2 | import tensorflow as tf
3 | import math
4 |
5 | #KL(N_0|N_1) = tr(\sigma_1^{-1} \sigma_0) +
6 | # (\mu_1 - \mu_0)\sigma_1^{-1}(\mu_1 - \mu_0) - k +
7 | # \log( \frac{\det \sigma_1}{\det \sigma_0} )
8 | def all_pairs_gaussian_kl(mu, sigma, add_third_term=False):
9 |
10 | sigma_sq = tf.square(sigma) + 1e-8
11 |
12 | #mu is [batchsize x dim_z]
13 | #sigma is [batchsize x dim_z]
14 |
15 | sigma_sq_inv = tf.math.reciprocal(sigma_sq)
16 | #sigma_inv is [batchsize x sizeof(latent_space)]
17 |
18 | #
19 | # first term
20 | #
21 |
22 | #dot product of all sigma_inv vectors with sigma
23 | #is the same as a matrix mult of diag
24 | first_term = tf.matmul(sigma_sq,tf.transpose(sigma_sq_inv))
25 |
26 | #
27 | # second term
28 | #
29 |
30 | #TODO: check this
31 | #REMEMBER THAT THIS IS SIGMA_1, not SIGMA_0
32 |
33 | r = tf.matmul(mu * mu,tf.transpose(sigma_sq_inv))
34 | #r is now [batchsize x batchsize] = sum(mu[:,i]**2 / Sigma[j])
35 |
36 | r2 = mu * mu * sigma_sq_inv
37 | r2 = tf.reduce_sum(r2,1)
38 | #r2 is now [batchsize, 1] = mu[j]**2 / Sigma[j]
39 |
40 | #squared distance
41 | #(mu[i] - mu[j])\sigma_inv(mu[i] - mu[j]) = r[i] - 2*mu[i]*mu[j] + r[j]
42 | #uses broadcasting
43 | second_term = 2*tf.matmul(mu, tf.transpose(mu*sigma_sq_inv))
44 | second_term = r - second_term + tf.transpose(r2)
45 |
46 | ##uncomment to check using tf_tester
47 | #return second_term
48 |
49 | #
50 | # third term
51 | #
52 |
53 | # log det A = tr log A
54 | # log \frac{ det \Sigma_1 }{ det \Sigma_0 } =
55 | # \tr\log \Sigma_1 - \tr\log \Sigma_0
56 | # for each sample, we have B comparisons to B other samples...
57 | # so this cancels out
58 |
59 | if(add_third_term):
60 | r = tf.reduce_sum(tf.math.log(sigma_sq),1)
61 | r = tf.reshape(r,[-1,1])
62 | third_term = r - tf.transpose(r)
63 | else:
64 | third_term = 0
65 |
66 | #- tf.reduce_sum(tf.log(1e-8 + tf.square(sigma)))\
67 | # the dim_z ** 3 term comes from
68 | # -the k in the original expression
69 | # -this happening k times in for each sample
70 | # -this happening for k samples
71 | #return 0.5 * ( first_term + second_term + third_term - dim_z )
72 | return 0.5 * ( first_term + second_term + third_term )
73 |
74 | #
75 | # kl_conditional_and_marg
76 | # \sum_{x'} KL[ q(z|x) \| q(z|x') ] + (B-1) H[q(z|x)]
77 | #
78 |
79 | #def kl_conditional_and_marg(args):
80 | def kl_conditional_and_marg(z_mean, z_log_sigma_sq, dim_z):
81 | z_sigma = tf.exp( 0.5 * z_log_sigma_sq )
82 | all_pairs_GKL = all_pairs_gaussian_kl(z_mean, z_sigma, True) - 0.5*dim_z
83 | return tf.reduce_mean(all_pairs_GKL)
84 |
85 |
--------------------------------------------------------------------------------
/src/test.py:
--------------------------------------------------------------------------------
1 |
2 | from data_tools import mnist_dataset
3 | import numpy as np
4 |
5 | params = {
6 | "beta" : 0.1,
7 | "lambda" : 1.0,
8 | }
9 |
10 | (train_x, train_y), (test_x, test_y) = mnist_dataset.get_data()
11 |
12 | ##
13 | ## let's build our VAE network
14 | ##
15 |
16 | import keras
17 | # sorry, for portability should be just
18 | import keras.backend as K
19 | # but both kl_tools and echo lock us into tensorflow sooo...
20 | import tensorflow as tf
21 |
22 | import kl_tools
23 |
24 | DIM_Z = 16
25 | DIM_C = mnist_dataset.NUM_LABELS
26 | INPUT_SHAPE=mnist_dataset.IMG_DIM ** 2
27 | ACTIVATION="tanh"
28 |
29 | input_x = keras.layers.Input( shape = [INPUT_SHAPE], name="x" )
30 |
31 | enc_hidden_1 = keras.layers.Dense(512, activation=ACTIVATION, name="enc_h1")(input_x)
32 | enc_hidden_2 = keras.layers.Dense(512, activation=ACTIVATION, name="enc_h2")(enc_hidden_1)
33 |
34 | #stolen straight from the docs
35 | #https://keras.io/examples/variational_autoencoder/
36 | def sampling(args):
37 | """Reparameterization trick by sampling from an isotropic unit Gaussian.
38 |
39 | # Arguments
40 | args (tensor): mean and log of variance of Q(z|X)
41 |
42 | # Returns
43 | z (tensor): sampled latent vector
44 | """
45 |
46 | z_mean, z_log_var = args
47 | batch = K.shape(z_mean)[0]
48 | dim = K.int_shape(z_mean)[1]
49 | # by default, random_normal has mean = 0 and std = 1.0
50 | epsilon = K.random_normal(shape=(batch, dim))
51 | return z_mean + K.exp(0.5 * z_log_var) * epsilon
52 |
53 |
54 | z_mean = keras.layers.Dense(DIM_Z, activation="tanh")(enc_hidden_2)
55 | z_log_sigma_sq = keras.layers.Dense(DIM_Z, activation="linear")(enc_hidden_2)
56 |
57 | z = keras.layers.Lambda(sampling, output_shape=(DIM_Z,), name='z')([z_mean, z_log_sigma_sq])
58 |
59 | ## this is the concat operation!
60 | input_c = keras.layers.Input( shape = [DIM_C], name="c")
61 | z_with_c = keras.layers.concatenate([z,input_c])
62 | z_mean_with_c = keras.layers.concatenate([z_mean,input_c])
63 |
64 | dec_h1 = keras.layers.Dense(512, activation=ACTIVATION, name="dec_h1")
65 | dec_h2 = keras.layers.Dense(512, activation=ACTIVATION, name="dec_h2")
66 | output_layer = keras.layers.Dense( INPUT_SHAPE, name="x_hat" )
67 |
68 | dec_hidden_1 = dec_h1(z_with_c)
69 | dec_hidden_2 = dec_h2(dec_hidden_1)
70 | x_hat = output_layer(dec_hidden_2)
71 |
72 | cvae = keras.models.Model(inputs=[input_x,input_c], outputs=x_hat, name="ICVAE")
73 |
74 | print(cvae.summary())
75 |
76 | ##
77 | ## make a mean model for outputs
78 | ##
79 |
80 | mean_dec_hidden_1 = dec_h1(z_mean_with_c)
81 | mean_dec_hidden_2 = dec_h2(mean_dec_hidden_1)
82 | mean_x_hat = output_layer(mean_dec_hidden_2)
83 |
84 | mean_cvae = keras.models.Model(
85 | inputs=[input_x, input_c],
86 | outputs=mean_x_hat,name="mean_VAE",
87 | )
88 |
89 |
90 | ##
91 | ## okay, now we have a network. Let's build the losses
92 | ##
93 |
94 | recon_loss = keras.losses.mse(input_x, x_hat)
95 | recon_loss *= INPUT_SHAPE #optional, in the tutorial code though
96 |
97 | kl_loss = 1 + z_log_sigma_sq - K.square(z_mean) - K.exp(z_log_sigma_sq)
98 | kl_loss = K.sum(kl_loss, axis=-1)
99 | kl_loss *= -0.5
100 |
101 | kl_qzx_qz_loss = kl_tools.kl_conditional_and_marg(z_mean, z_log_sigma_sq, DIM_Z)
102 |
103 | #optional add beta param here
104 | # and cite Higgins et al.
105 | cvae_loss = K.mean((1 + params["lambda"]) * recon_loss + params["beta"]*kl_loss + params["lambda"]*kl_qzx_qz_loss)
106 |
107 | cvae.add_loss(cvae_loss)
108 |
109 | ##
110 | ##
111 | ##
112 |
113 | learning_rate = 0.0005
114 | opt = keras.optimizers.Adam(lr=learning_rate)
115 |
116 | cvae.compile( optimizer=opt, )
117 |
118 | #training?
119 | import os
120 | if not os.path.exists("mnist_icvae.h5"):
121 | cvae.fit(
122 | { "x" : train_x, "c" : train_y }, epochs=50
123 | )
124 | cvae.save_weights("mnist_icvae.h5")
125 | else:
126 | cvae.load_weights("mnist_icvae.h5")
127 |
128 | exit(1)
129 |
130 | n_plot_samps = 10
131 | test_x_hat = mean_cvae.predict(
132 | { "x" : test_x[:n_plot_samps], "c" : test_y[:n_plot_samps] }
133 | )
134 |
135 | ##
136 | ## plot first N
137 | ##
138 |
139 | from plot_tools import pics_tools as pic
140 | import matplotlib.pyplot as plt
141 |
142 | fig = pic.plot_image_grid( \
143 | np.concatenate([test_x[:n_plot_samps],test_x_hat], axis=0),
144 | [mnist_dataset.IMG_DIM, mnist_dataset.IMG_DIM], \
145 | (2,n_plot_samps) \
146 | )
147 | plt.show()
148 |
149 | X_test_set = []
150 | Y_test_set = []
151 |
152 | for i in range(n_plot_samps):
153 | tmp_tile_array = np.tile(test_x[i],[10,1])
154 | X_test_set.append(test_x[i:(i+1),:])
155 | X_test_set.append(tmp_tile_array)
156 |
157 | Y_test_set.append(test_y[i:(i+1),:])
158 | #Y_test_set.append(np.array([[0],[1]]))
159 | Y_test_set.append(np.eye(10))
160 |
161 | X_test_set = np.concatenate(X_test_set, axis=0)
162 | Y_test_set = np.concatenate(Y_test_set, axis=0)
163 |
164 | X_test_hat = mean_cvae.predict(
165 | { "x" : X_test_set, "c" : Y_test_set }
166 | )
167 |
168 | plot_collection = []
169 | for i in range(n_plot_samps):
170 | plot_collection.append( test_x[i:(i+1),:] )
171 | plot_collection.append( X_test_hat[i*11:(i+1)*11,:] )
172 |
173 | plot_collection = np.concatenate( plot_collection, axis=0 )
174 |
175 | fig = pic.plot_image_grid( \
176 | plot_collection,
177 | [mnist_dataset.IMG_DIM, mnist_dataset.IMG_DIM], \
178 | (n_plot_samps,12) \
179 | )
180 | plt.show()
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Invariant Representations Tutorial\n",
8 | "\n",
9 | "\n",
10 | "This is part of an Invariant Representations tutorial series. There's a short intro here to the theory behind what's going on, but for more in depth coverage see [this unimplemented second part](sorry dead link right now). This tutorial requires basic variational auto-encoder knowledge, and some information theory knowledge, but otherwise should hopefully be pretty accessible.\n",
11 | "\n",
12 | "If you're not familiar with VAE, there's a great tutorial from Lilian Weng [here](https://lilianweng.github.io/lil-log/2018/08/12/from-autoencoder-to-beta-vae.html).\n",
13 | "I should find a IT for ML tutorial, but I haven't yet, so if you know of a good one please link me!\n",
14 | "\n",
15 | "PLEASE leave comments/advice/requests. I'm new at this, so this is probably sub-optimal, whatever its current state. I'm also not a regular .ipynb user, so even minor things can be PR'd in happily.\n",
16 | "\n",
17 | "This tutorial is done in Keras/TF.\n"
18 | ]
19 | },
20 | {
21 | "cell_type": "markdown",
22 | "metadata": {},
23 | "source": [
24 | "## Theory summary\n",
25 | "\n",
26 | "**Three sentence setup from VAE:** Regular auto-encoders learn functions $q(z|x)$ and $p(x|z)$, which respectively encode and decode our data $x$ to a lower-dimensional variable $z$. If the auto-encoder does well, then $p(x|z = q(z|x))$ is close to $x$. VAE builds on this, letting $q$ be a probabilistic mapping, so that $q(z|x)$ is a distribution, usually a Gaussian.\n",
27 | "\n",
28 | "**The invariant auto-encoder objective:** We want to learn $q$ and $p$ in the same setup that\n",
29 | "1. accurately describes $x$ (just like regular AEs)\n",
30 | "2. has a $z$ that is invariant to outside factor $c$ (the invariance condition)\n",
31 | "\n",
32 | "The first part can be described using maximum likelihood from e.g. [Kingma and Welling 2013](https://arxiv.org/abs/1312.6114):\n",
33 | "\n",
34 | "$$ \\max \\mathbb{E}_{q(z|x)p(x)}[ p(x|z) ] $$\n",
35 | "\n",
36 | "We can write down a Markov chain that describes this setup: $ c \\rightarrow x \\rightarrow z $. We want to enforce the constraint $z\\perp c$, i.e. $z$ independent of $c$, i.e. that $p(z|c) = p(z)$. Basically: *Make sure $z$ doesn't change if you change $c$*.\n",
37 | "\n",
38 | "Okay, so what if we can't do that? $z \\perp c$ is a really harsh constraint.\n",
39 | "\n",
40 | "One idea: relax it to minimal mutual information, $\\min I(z,c)$. If $I(z,c)$ is close to $0$, then we're good.\n",
41 | "\n",
42 | "How do we minimize $I(z,c)$? This, it turns out, is also hard (are you sensing a theme) for arbitrary encodings $z$. Doing some* math we end up at this bound:\n",
43 | "\n",
44 | "$$I(z, c) \\leq\n",
45 | "\\underbrace{- \\mathbb{E}_{x,c,z\\sim q}[ \\log p(x|z,c)]}_{\\text{Reconstruction}}\n",
46 | "+ \\underbrace{\\mathbb{E}_{x}[~KL[~q(z|x)~\\|~q(z)~]~ ]}_{\\text{Compression}}\n",
47 | "- \\underbrace{\\vphantom{\\mathbb{E}_{x,q}[]}H(x|c)}_{\\text{Const}}$$\n",
48 | "\\*\"some\" meaning section 2 of [our paper](https://arxiv.org/abs/1805.09458).\n",
49 | "\n",
50 | "This has two parts we can optimize, $p(x|z,c)$ and $q(z|x)$. These look like an encoder and decoder pair from the auto-encoders introduced at the start, except our decoder is conditional. We also have an additional KL term. Otherwise it's the same as an auto-encoder.\n",
51 | "\n",
52 | "\n",
53 | "**TL;DR:** Use a conditional decoder and an additional KL term to make your auto-encoder invariant to whatever you conditioned on.\n",
54 | "\n",
55 | "The \"TODO List\":\n",
56 | "* Make a generic encoder, $q(z|x)$.\n",
57 | "* Make a conditional decoder, $p(x|z,c)$.\n",
58 | "* Implement that KL loss (or an approximation thereof): $KL[~q(z|x)~\\|~q(z)~]$\n",
59 | "\n",
60 | "That's what we're going to do here, using neural networks for $p$ and $q$."
61 | ]
62 | },
63 | {
64 | "cell_type": "markdown",
65 | "metadata": {},
66 | "source": [
67 | "### Additional Motivations/Reference Notes\n",
68 | "\n",
69 | "In classical CV/ML, we might have designed these analytically for things like rotation, translation, scaling, etc.; these transformations themselves have analytic descriptions, and we can view the removal their effects as [quotienting out a group operation](https://arxiv.org/abs/1602.07576).\n",
70 | "\n",
71 | "For more general factors $c$, this isn't always possible. One example in [algorithmic fairness](https://arxiv.org/abs/1511.00830) requires the removal of sensitive or protected attributes (Race, Religion, Gender, Orientation) from $x$, which is a) probably not a group operation, and b) definitely has no easy analytic description. This also occurs in [instrument bias](https://arxiv.org/abs/1904.05375) for observational studies. Further, for generative models it can be helpful to \"modulo out\" certain factors, only to add them back in a [controlled manner](https://arxiv.org/abs/1706.00409).\n",
72 | "\n",
73 | "We summarized our theory objective with: \"*Make sure $z$ doesn't change if you change $c$*\". Here, \"*you*\" is often \"*a mysterious force which we cannot control*\", since if we could change $c$ and get new $(x,c)$ pairs, we could perform data augmentation. In fact, we find this regularly occuring things like rotation, translation, etc., and for some effects which aren't groups/semi-groups (occlusions, additive noise). It's simple and it \"completes the orbit\". In other words: it adds an $x$ to our dataset for each possible value of $c$. Usually on the fly."
74 | ]
75 | },
76 | {
77 | "cell_type": "markdown",
78 | "metadata": {},
79 | "source": [
80 | "## Programming Setup\n",
81 | "\n",
82 | "Okay, so we'll need Keras, Numpy, and MNIST data. I'm going to hide all that, but we're going to get $x$ as a big flat vector and $y$ as a one-hot categorical variable."
83 | ]
84 | },
85 | {
86 | "cell_type": "code",
87 | "execution_count": 1,
88 | "metadata": {
89 | "tags": [
90 | "hide_input"
91 | ]
92 | },
93 | "outputs": [
94 | {
95 | "name": "stderr",
96 | "output_type": "stream",
97 | "text": [
98 | "Using TensorFlow backend.\n"
99 | ]
100 | }
101 | ],
102 | "source": [
103 | "import keras\n",
104 | "import keras.backend as K\n",
105 | "import tensorflow as tf\n",
106 | "import numpy as np\n",
107 | "\n",
108 | "IMG_DIM = 28\n",
109 | "NUM_LABELS = 10\n",
110 | "\n",
111 | "#data comes as images and 1-dim {0,...,9} categorical variable\n",
112 | "(train_x, train_y), (test_x, test_y) = tf.keras.datasets.mnist.load_data()\n",
113 | " \n",
114 | "#cast and flatten images, renormalizing to [0,1]\n",
115 | "train_x = train_x.astype(np.float32).reshape( (train_x.shape[0], IMG_DIM**2) ) / 255.0\n",
116 | "test_x = test_x.astype(np.float32).reshape( (test_x.shape[0], IMG_DIM**2) ) / 255.0\n",
117 | "\n",
118 | "#copypaste\n",
119 | "def one_hot(labels):\n",
120 | " num_labels_data = labels.shape[0]\n",
121 | " one_hot_encoding = np.zeros((num_labels_data,NUM_LABELS))\n",
122 | " one_hot_encoding[np.arange(num_labels_data),labels] = 1\n",
123 | " one_hot_encoding = np.reshape(one_hot_encoding, [-1, NUM_LABELS])\n",
124 | " return one_hot_encoding\n",
125 | "\n",
126 | "train_y = one_hot(train_y).astype(np.float32)\n",
127 | "test_y = one_hot(test_y).astype(np.float32)"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": 2,
133 | "metadata": {},
134 | "outputs": [
135 | {
136 | "name": "stdout",
137 | "output_type": "stream",
138 | "text": [
139 | "(60000, 784)\n",
140 | "(60000, 10)\n"
141 | ]
142 | }
143 | ],
144 | "source": [
145 | "print(train_x.shape)\n",
146 | "print(train_y.shape)"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {},
152 | "source": [
153 | "## Building a Conditional VAE architecture \n",
154 | "\n",
155 | "\n",
156 | "The Conditional VAE is, as its name suggests, a variational auto-encoder with conditional output. This means it should \n",
157 | "1. Take in $x$ and map it to a latent variable $z$ using the encoder\n",
158 | "2. Map $z$ to a new $\\hat{x}$ using the decoder.\n",
159 | "3. Condition (a.k.a. control) the output $\\hat{x}$ by another input $c$, representing specific other factors.\n",
160 | "\n",
161 | "In order to do this we'll also set a few constants. We're using ```DIM_Z=5```, but feel free to come back and play around with it later. We're also using \"tanh\" activations, but you can come back and change this as well (e.g. to \"relu\").\n",
162 | "\n",
163 | "For some this is pretty basic stuff. Skip to [Loss Construction](#loss-construction) if you can do this on your own."
164 | ]
165 | },
166 | {
167 | "cell_type": "code",
168 | "execution_count": 3,
169 | "metadata": {},
170 | "outputs": [],
171 | "source": [
172 | "DIM_Z = 16\n",
173 | "DIM_C = NUM_LABELS # just as an example. Sometimes we also have another y\n",
174 | "INPUT_SHAPE = IMG_DIM ** 2\n",
175 | "ACTIVATION = \"tanh\""
176 | ]
177 | },
178 | {
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": [
182 | "Then we'll build the encoder, which is a two layer fully-connected feed-forward network with two outputs, $z_{mean}$ and $z_{sigma}$. To avoid domain problems, we'll actually output $\\log z_{sigma}$. This means we can use a linear layer instead of having to choose a non-negative activation."
183 | ]
184 | },
185 | {
186 | "cell_type": "code",
187 | "execution_count": 4,
188 | "metadata": {},
189 | "outputs": [],
190 | "source": [
191 | "#declare inputs to the encoder, which is just x\n",
192 | "input_x = keras.layers.Input( shape = [INPUT_SHAPE], name=\"x\" )\n",
193 | "\n",
194 | "#first hidden layer\n",
195 | "enc_hidden_1 = keras.layers.Dense(512, activation=ACTIVATION, name=\"enc_h1\")(input_x)\n",
196 | "#second hidden layer\n",
197 | "enc_hidden_2 = keras.layers.Dense(512, activation=ACTIVATION, name=\"enc_h2\")(enc_hidden_1)\n",
198 | "\n",
199 | "#first output, z_mean\n",
200 | "z_mean = keras.layers.Dense(DIM_Z, activation=ACTIVATION)(enc_hidden_2)\n",
201 | "#second hidden output, z_log_sigma_sq.\n",
202 | "z_log_sigma_sq = keras.layers.Dense(DIM_Z, activation=\"linear\")(enc_hidden_2)"
203 | ]
204 | },
205 | {
206 | "cell_type": "markdown",
207 | "metadata": {},
208 | "source": [
209 | "Creating the latent variable $z$ using a Gaussian layer can be a bit tricky, but luckily is covered in the [Keras documentation](https://keras.io/examples/variational_autoencoder/), which this part follows almost exactly. If you haven't seen the reparameterization trick before, this is to create a Gaussian distributed layer $z$ which can be differentiated w.r.t. its parameters. It was popularized by [Kingma and Welling 2013](https://arxiv.org/abs/1312.6114). "
210 | ]
211 | },
212 | {
213 | "cell_type": "code",
214 | "execution_count": 5,
215 | "metadata": {},
216 | "outputs": [],
217 | "source": [
218 | "#stolen straight from the docs\n",
219 | "#https://keras.io/examples/variational_autoencoder/\n",
220 | "def sampling(args):\n",
221 | " \"\"\"Reparameterization trick by sampling from an isotropic unit Gaussian.\n",
222 | "\n",
223 | " # Arguments\n",
224 | " args (tensor): mean and log of variance of Q(z|X)\n",
225 | "\n",
226 | " # Returns\n",
227 | " z (tensor): sampled latent vector\n",
228 | " \"\"\"\n",
229 | "\n",
230 | " z_mean, z_log_var = args\n",
231 | " batch = K.shape(z_mean)[0]\n",
232 | " dim = K.int_shape(z_mean)[1]\n",
233 | " # by default, random_normal has mean = 0 and std = 1.0\n",
234 | " epsilon = K.random_normal(shape=(batch, dim))\n",
235 | " return z_mean + K.exp(0.5 * z_log_var) * epsilon\n",
236 | "\n",
237 | "\n",
238 | "z_mean = keras.layers.Dense(DIM_Z, activation=\"tanh\")(enc_hidden_2)\n",
239 | "z_log_sigma_sq = keras.layers.Dense(DIM_Z, activation=\"linear\")(enc_hidden_2)\n",
240 | "\n",
241 | "z = keras.layers.Lambda(sampling, output_shape=(DIM_Z,), name='z')([z_mean, z_log_sigma_sq])"
242 | ]
243 | },
244 | {
245 | "cell_type": "markdown",
246 | "metadata": {},
247 | "source": [
248 | "Great, that's the Encoder from $x$ to $z$ done. Let's build the conditional decoder. There isn't an established standard way to condition the output, but concatenating $z$ and $c$ before inputting into the decoder is good enough for now."
249 | ]
250 | },
251 | {
252 | "cell_type": "code",
253 | "execution_count": 6,
254 | "metadata": {},
255 | "outputs": [],
256 | "source": [
257 | "#declare any additional inputs to our decoder, in this case c\n",
258 | "input_c = keras.layers.Input( shape = [DIM_C], name=\"c\")\n",
259 | "#this is the concat operation!\n",
260 | "z_with_c = keras.layers.concatenate([z,input_c])\n",
261 | "\n",
262 | "#first hidden layer\n",
263 | "dec_hidden_1 = keras.layers.Dense(512, activation=ACTIVATION, name=\"dec_h1\")(z_with_c)\n",
264 | "#second hidden layer\n",
265 | "dec_hidden_2 = keras.layers.Dense(512, activation=ACTIVATION, name=\"dec_h2\")(dec_hidden_1)\n",
266 | "\n",
267 | "#output, should be same domain as x_hat\n",
268 | "#could also use sigmoid activation\n",
269 | "x_hat = keras.layers.Dense( INPUT_SHAPE, name=\"x_hat\", activation=\"linear\" )(dec_hidden_2)"
270 | ]
271 | },
272 | {
273 | "cell_type": "markdown",
274 | "metadata": {},
275 | "source": [
276 | "Loss Construction for Invariance (...and also all that VAE stuff)\n",
277 | "----\n",
278 | "\n",
279 | "\n",
280 | "Okay, so we have both the encoder and the conditional decoder now, so the next step is building the loss function. There are three sub-components:\n",
281 | "\n",
282 | "1. Reconstruction (how far is $\\hat{x}$ from $x$), usually $\\|x - \\hat{x}\\|_2^2$.\n",
283 | "2. \"Distance\" to the Prior (from the original VAE definition), $KL[q(z|x)| p(z)]$\n",
284 | "3. \"Distance\" to the Empirical Marginal Posterior (from our paper, among others), $KL[q(z|x)| q(z)]$.\n",
285 | "\n",
286 | "The third one we'll have to approximate, so we'll deal with that second. First, the two easy ones, straight from Keras Docs, plus defining some hyper parameters:"
287 | ]
288 | },
289 | {
290 | "cell_type": "code",
291 | "execution_count": 7,
292 | "metadata": {},
293 | "outputs": [],
294 | "source": [
295 | "params = {\n",
296 | " \"beta\" : 0.1,\n",
297 | " \"lambda\" : 1.0,\n",
298 | "}\n",
299 | "\n",
300 | "recon_loss = keras.losses.mse(input_x, x_hat)\n",
301 | "recon_loss *= INPUT_SHAPE #optional, in the tutorial code though\n",
302 | "\n",
303 | "prior_loss = 1 + z_log_sigma_sq - K.square(z_mean) - K.exp(z_log_sigma_sq)\n",
304 | "prior_loss = K.sum(prior_loss, axis=-1)\n",
305 | "prior_loss *= -0.5"
306 | ]
307 | },
308 | {
309 | "cell_type": "markdown",
310 | "metadata": {},
311 | "source": [
312 | "Now for $KL[ q(z|x) | q(z) ]$. Since we're using the Gaussian $z$ layer, there's an approximation we can make using the pairwise Gaussian KL divergences. **In the original version of the paper there is an erroneous extra term at this part.** The corrected version is implemented below, but before we get into it, we should remember that we actually want to compute $KL[ q(z|x) | q(z) ]$, not this bound, and that this could also be solved using:\n",
313 | "\n",
314 | "* Direct approximation (e.g. use a neural network to approximate posterior marginal $q(z)$ term given $q(z|x)$ parameters and samples $x$).\n",
315 | "* Sampling\n",
316 | "* Use a different $z$ layer with analytic divergence to its marginal.\n",
317 | "\n",
318 | "The first and second options appear in the literature; see e.g. [Fixing a Broken ELBO (Alemi et al. 2017)](https://arxiv.org/abs/1711.00464) and [Structured Disentangled Representations Esmaeili et al. 2018](https://arxiv.org/abs/1804.02086). The third option appears in our paper [Echo Noise](https://arxiv.org/abs/1904.07199), which we'll demonstrate at the end.\n",
319 | "\n",
320 | "Knowing this, here's one way to do it *for Gaussian $z$ layers* using pairwise KL divergence:"
321 | ]
322 | },
323 | {
324 | "cell_type": "code",
325 | "execution_count": 8,
326 | "metadata": {},
327 | "outputs": [],
328 | "source": [
329 | "\n",
330 | "#KL(N_0|N_1) = tr(\\sigma_1^{-1} \\sigma_0) + \n",
331 | "# (\\mu_1 - \\mu_0)\\sigma_1^{-1}(\\mu_1 - \\mu_0) - k +\n",
332 | "# \\log( \\frac{\\det \\sigma_1}{\\det \\sigma_0} )\n",
333 | "def all_pairs_gaussian_kl(mu, sigma, add_third_term=False):\n",
334 | " sigma_sq = tf.square(sigma) + 1e-8\n",
335 | " sigma_sq_inv = tf.math.reciprocal(sigma_sq)\n",
336 | "\n",
337 | " #dot product of all sigma_inv vectors with sigma is the same as a matrix mult of diag\n",
338 | " first_term = tf.matmul(sigma_sq,tf.transpose(sigma_sq_inv))\n",
339 | " \n",
340 | " r = tf.matmul(mu * mu,tf.transpose(sigma_sq_inv))\n",
341 | " r2 = mu * mu * sigma_sq_inv \n",
342 | " r2 = tf.reduce_sum(r2,1)\n",
343 | " \n",
344 | " #squared distance\n",
345 | " #(mu[i] - mu[j])\\sigma_inv(mu[i] - mu[j]) = r[i] - 2*mu[i]*mu[j] + r[j]\n",
346 | " #uses broadcasting\n",
347 | " second_term = 2*tf.matmul(mu, tf.transpose(mu*sigma_sq_inv))\n",
348 | " second_term = r - second_term + tf.transpose(r2)\n",
349 | "\n",
350 | " # log det A = tr log A\n",
351 | " # log \\frac{ det \\Sigma_1 }{ det \\Sigma_0 } =\n",
352 | " # \\tr\\log \\Sigma_1 - \\tr\\log \\Sigma_0 \n",
353 | " # for each sample, we have B comparisons to B other samples...\n",
354 | " # so this cancels out\n",
355 | "\n",
356 | " if(add_third_term):\n",
357 | " r = tf.reduce_sum(tf.math.log(sigma_sq),1)\n",
358 | " r = tf.reshape(r,[-1,1])\n",
359 | " third_term = r - tf.transpose(r)\n",
360 | " else:\n",
361 | " third_term = 0\n",
362 | "\n",
363 | " #- tf.reduce_sum(tf.log(1e-8 + tf.square(sigma)))\\\n",
364 | " # the dim_z ** 3 term comes fro\n",
365 | " # -the k in the original expression\n",
366 | " # -this happening k times in for each sample\n",
367 | " # -this happening for k samples\n",
368 | " #return 0.5 * ( first_term + second_term + third_term - dim_z )\n",
369 | " return 0.5 * ( first_term + second_term + third_term )\n",
370 | "\n",
371 | "#\n",
372 | "# kl_conditional_and_marg\n",
373 | "# \\sum_{x'} KL[ q(z|x) \\| q(z|x') ] + (B-1) H[q(z|x)]\n",
374 | "#\n",
375 | "\n",
376 | "#def kl_conditional_and_marg(args):\n",
377 | "def kl_conditional_and_marg(z_mean, z_log_sigma_sq, dim_z):\n",
378 | " z_sigma = tf.exp( 0.5 * z_log_sigma_sq )\n",
379 | " all_pairs_GKL = all_pairs_gaussian_kl(z_mean, z_sigma, True) - 0.5*dim_z\n",
380 | " return tf.reduce_mean(all_pairs_GKL)"
381 | ]
382 | },
383 | {
384 | "cell_type": "markdown",
385 | "metadata": {},
386 | "source": [
387 | "So after all that, we can create our third loss term. We'll then add them all up, and create our model!"
388 | ]
389 | },
390 | {
391 | "cell_type": "code",
392 | "execution_count": 9,
393 | "metadata": {},
394 | "outputs": [
395 | {
396 | "name": "stderr",
397 | "output_type": "stream",
398 | "text": [
399 | "/home/dcmoyer/anaconda3/lib/python3.7/site-packages/keras/engine/training_utils.py:819: UserWarning: Output x_hat missing from loss dictionary. We assume this was done on purpose. The fit and evaluate APIs will not be expecting any data to be passed to x_hat.\n",
400 | " 'be expecting any data to be passed to {0}.'.format(name))\n"
401 | ]
402 | }
403 | ],
404 | "source": [
405 | "kl_qzx_qz_loss = kl_conditional_and_marg(z_mean, z_log_sigma_sq, DIM_Z)\n",
406 | "\n",
407 | "#Invariant Conditional Variational Autoencoder (ICVAE).\n",
408 | "#I think the name game is meh (what if someone else got there first? or defines a different one later?)\n",
409 | "#so maybe it'd be easier to say Moyer et al. 2018? too narcissistic? Conditional VAE with additional compression?\n",
410 | "#\n",
411 | "# ...the point is, this one can induce invariance. So can the others sometimes, too, in practice.\n",
412 | "\n",
413 | "icvae_loss = K.mean((1 + params[\"lambda\"]) * recon_loss + params[\"beta\"]*prior_loss + params[\"lambda\"]*kl_qzx_qz_loss)\n",
414 | "\n",
415 | "icvae = keras.models.Model(inputs=[input_x,input_c], outputs=x_hat, name=\"ICVAE\")\n",
416 | "\n",
417 | "icvae.add_loss(icvae_loss)\n",
418 | "\n",
419 | "learning_rate = 0.0005\n",
420 | "opt = keras.optimizers.Adam(lr=learning_rate)\n",
421 | "\n",
422 | "icvae.compile( optimizer=opt, )\n"
423 | ]
424 | },
425 | {
426 | "cell_type": "markdown",
427 | "metadata": {},
428 | "source": [
429 | "## Training\n",
430 | "\n",
431 | "Only run this bit once. It takes a bit (but not too long tbh). If this isn't your jupyter server, you should delete the path stuff. I just wanted to save you from repetitive gpu time/coffee breaks."
432 | ]
433 | },
434 | {
435 | "cell_type": "code",
436 | "execution_count": 10,
437 | "metadata": {},
438 | "outputs": [
439 | {
440 | "name": "stdout",
441 | "output_type": "stream",
442 | "text": [
443 | "loading from file\n"
444 | ]
445 | }
446 | ],
447 | "source": [
448 | "import os\n",
449 | "if not os.path.exists(\"mnist_icvae.h5\"):\n",
450 | " print(\"training\")\n",
451 | " icvae.fit(\n",
452 | " { \"x\" : train_x, \"c\" : train_y }, epochs=100\n",
453 | " )\n",
454 | " icvae.save_weights(\"mnist_icvae.h5\")\n",
455 | "else:\n",
456 | " print(\"loading from file\")\n",
457 | " icvae.load_weights(\"mnist_icvae.h5\")"
458 | ]
459 | },
460 | {
461 | "cell_type": "markdown",
462 | "metadata": {},
463 | "source": [
464 | "# Plots and Evaluation\n",
465 | "\n",
466 | "Okay, so now that we've trained it, let's see how well it does."
467 | ]
468 | },
469 | {
470 | "cell_type": "code",
471 | "execution_count": 13,
472 | "metadata": {},
473 | "outputs": [
474 | {
475 | "data": {
476 | "image/png": "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O3bAZDIhJycHixcvRmlpKT+WGYLC6k32+8fiukcXnHg8HvT19fGEudVqRX19Pbq6uvgEkYGBAbz33nvo6upCcXExsrKyIJVKI6rR4lEKzeQVFkDZbDZuMLO2l56eHoyOjuLMmTNoaWlBX18fn3zCquKir3ssiyTY57HckdFohFqthtPphNfrRV9fH6/kS09Ph0ajgUaj4aEzqVSKvLw83qbDQoZsH4jVuRV6+sxDY6XmgUAAbrcbR44cgVQq5efW4/Hg1KlTaGtrAwCkp6dj8eLFmDdvHq/cneq6nPbOwMIJaWlpPG4rlUrh8XgAYNx46J49e/DOO+/AarVixowZKCkp4QUSQmJl/Y13gzJrwO/3w2w248iRI9i3bx9MJhPS09OxZs0arF27lh/PwpDRLjZTwrGskgMw7uexZrfGxka8++678Pl8qKiowIoVK5Cdnc2Tpszdj/6d49EP4/f7YbFY4HA4eFVecXExdDodVzpMSSmVSu61SqVSeL1eXoLMcm7x8qAkEgnsdnvE6CVWJSesHgwGg6ivr0dnZycMBgOKioomDEXFe/wNU1B+vx8qlQpNTU344IMPEAgEMGfOHFx66aW8NULYWyicfMBaAWKt/EUiETweD2QyGTIzMyESidDV1cWnBohEIthsNpjNZvT09EAqlSI/Px96vR5utxter/ccIyrW11+onFgztlQqhcPhQEtLC/fY3W43BgYGYLVaeRWcTqfjPVDRvzfL98QKtg7T09MxY8YMVFdXIzMzEyMjI9xLstlsPArAGmKZZ8gajI1GIy+sYWHieKQlmLxZWVkoLS1FUVER2tracPz4cbS1tfEaBKlUypUTANTV1WHRokUoKCiAWq3mTd1TYdqrl22ErFRULpcjFApFlG4y61UqlaK/vx/vvvsuDh8+DACYN28eamtrkZWVNe6JiAXjKScW3x0bG0NzczMaGhrQ2toKpVKJNWvW4Nprr41QrhONigmFQjFXTsLvZBsjs44dDgf279+P1tZWSCQSXHLJJSgrK4NSqeRTOcazms6X15gObONjzYWsyz0vLw+5ubk898X64NiaYItSoVBEJF4BxDT/FC0rm73IEveBQADt7e34+OOPodFo+DF9fX149913MTw8jFmzZqG8vBzZ2dkAPrse8ep/i4ZVdrFzxBRnRkYG1q9fj+LiYgDgIafoUTPstXh4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\n",
477 | "text/plain": [
478 | ""
479 | ]
480 | },
481 | "metadata": {},
482 | "output_type": "display_data"
483 | }
484 | ],
485 | "source": [
486 | "n_plot_samps = 10\n",
487 | "test_x_hat = icvae.predict(\n",
488 | " { \"x\" : test_x[:n_plot_samps], \"c\" : test_y[:n_plot_samps] }\n",
489 | ")\n",
490 | "\n",
491 | "##\n",
492 | "## plot first N\n",
493 | "##\n",
494 | "\n",
495 | "from src.plot_tools import pics_tools as pic\n",
496 | "import matplotlib.pyplot as plt\n",
497 | "plt.style.use('grayscale')\n",
498 | "\n",
499 | "fig = pic.plot_image_grid( \\\n",
500 | " 1-np.concatenate([test_x[:n_plot_samps],test_x_hat], axis=0),\n",
501 | " [IMG_DIM, IMG_DIM], \\\n",
502 | " (2,n_plot_samps) \\\n",
503 | ")\n",
504 | "plt.show()"
505 | ]
506 | },
507 | {
508 | "cell_type": "markdown",
509 | "metadata": {},
510 | "source": [
511 | "Probably a bit fuzzy right?\n",
512 | "\n",
513 | "One thing we can do immediately though is force our network to use the mean encoding $z$. Currently it's actually sampling, so if you ran the above code block multiple times you'd get different outputs.\n",
514 | "\n",
515 | "Still, fuzzy output is a common complaint of VAE or Auto-encoders in general (in comparison with, e.g., GANs). You can somewhat ameliorate this by more epochs, a bigger latent space, more layers, convolution, etc., but in the end it might be that $\\ell_2$ isn't a great image metric (a.k.a. distortion measure). Designing one by hand seems very hard, but 5+ years of GAN research seems to show that adversaries do well (replacing $\\log p(x|z,c) \\propto \\|x - \\hat{x} \\|$). This is, in effect, training another network $r$ to produce proxy likelihoods $r(x)$ from outputs $p(x|z,c)$. There's nothing stopping us from using an adversary replacement for $\\log p(x|z,c)$.\n",
516 | "\n",
517 | "Anyway, in this next section we'll do the famous remapping tricks, taking one digit and making it map to all other 9. This is done by manipulating $c$ (at test time obviously)."
518 | ]
519 | },
520 | {
521 | "cell_type": "code",
522 | "execution_count": 14,
523 | "metadata": {},
524 | "outputs": [
525 | {
526 | "data": {
527 | "image/png": "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\n",
528 | "text/plain": [
529 | ""
530 | ]
531 | },
532 | "metadata": {},
533 | "output_type": "display_data"
534 | }
535 | ],
536 | "source": [
537 | "X_test_set = []\n",
538 | "Y_test_set = []\n",
539 | "\n",
540 | "for i in range(n_plot_samps):\n",
541 | " tmp_tile_array = np.tile(test_x[i],[10,1])\n",
542 | " X_test_set.append(test_x[i:(i+1),:])\n",
543 | " X_test_set.append(tmp_tile_array)\n",
544 | "\n",
545 | " Y_test_set.append(test_y[i:(i+1),:])\n",
546 | " #Y_test_set.append(np.array([[0],[1]]))\n",
547 | " Y_test_set.append(np.eye(10))\n",
548 | "\n",
549 | "X_test_set = np.concatenate(X_test_set, axis=0)\n",
550 | "Y_test_set = np.concatenate(Y_test_set, axis=0)\n",
551 | "\n",
552 | "X_test_hat = icvae.predict(\n",
553 | " { \"x\" : X_test_set, \"c\" : Y_test_set }\n",
554 | ")\n",
555 | "\n",
556 | "plot_collection = []\n",
557 | "for i in range(n_plot_samps):\n",
558 | " plot_collection.append( test_x[i:(i+1),:] )\n",
559 | " plot_collection.append( X_test_hat[i*11:(i+1)*11,:] )\n",
560 | "\n",
561 | "plot_collection = np.concatenate( plot_collection, axis=0 )\n",
562 | "\n",
563 | "fig = pic.plot_image_grid( \\\n",
564 | " 1-plot_collection,\n",
565 | " [IMG_DIM, IMG_DIM], \\\n",
566 | " (n_plot_samps,12) \\\n",
567 | ")\n",
568 | "plt.show()"
569 | ]
570 | },
571 | {
572 | "cell_type": "markdown",
573 | "metadata": {},
574 | "source": [
575 | "Okay, so while it doesn't work amazingly well (...some of those 9s are questionable), it **is** doing two things correctly:\n",
576 | "1. Removing the input digit class, e.g. we can't see residual 0 bits when mapping 0 to other digits.\n",
577 | "2. Transfering some elements of the style, notably how dark digits are, how tilted each digit is, and their approximate vertical alignment.\n",
578 | "\n",
579 | "With better and more expressive decoders we can improve the reconstruction performance, but the general idea here is clear. Next up: more theory connections to adversaries, and an invariant prediction method."
580 | ]
581 | }
582 | ],
583 | "metadata": {
584 | "celltoolbar": "Tags",
585 | "kernelspec": {
586 | "display_name": "Python 3",
587 | "language": "python",
588 | "name": "python3"
589 | },
590 | "language_info": {
591 | "codemirror_mode": {
592 | "name": "ipython",
593 | "version": 3
594 | },
595 | "file_extension": ".py",
596 | "mimetype": "text/x-python",
597 | "name": "python",
598 | "nbconvert_exporter": "python",
599 | "pygments_lexer": "ipython3",
600 | "version": "3.7.3"
601 | }
602 | },
603 | "nbformat": 4,
604 | "nbformat_minor": 2
605 | }
606 |
--------------------------------------------------------------------------------