I want to train a neural network that removes scratches from pictures. I chose a GAN architecture with a generator (G) and a discriminator (D) and two sets of images scratchy and non-scratchy, with similar motives. G in my setting uses mainly convolutional and deconvolutional layers with ReLU
. As input it uses the scratchy images. The D discriminates between the output of G and the non-scratchy images.
For example the generator would perform the following transformations:
(128, 128, 3) > (128, 128, 128) > (128, 128, 3)
Where the tuples contain (width, height, channels). Input and output need to have the same format.
But in order to get output that have same global structure as the inputs (i.e. streets, houses etc) it seem I have to use filter-sizes and strides of 1 and basically pass the complete pictures through the network.
However, the features I am looking at are rather small and local. It should be sufficient to use filters of up to 20 pixels in size for the convolutions. And then then apply the network to all parts of the input picture.
What would be a good generator architecture for such a task? Would you agree with the structure of the generator or would you expect different designs to perform better on local changes?
Here is the code for the generator that I use implemented in tensorflow.
def generator(x, batch_size, reuse=False):
with tf.variable_scope('generator') as scope:
if (reuse):
tf.get_variable_scope().reuse_variables()
s = 1
f = 1
assert ((WIDTH + f - 1) / s) % 1 == 0
keep_prob = 0.5
n_ch1 = 32
w = init_weights('g_wc1', [f, f, CHANNELS, n_ch1])
b = init_bias('g_bc1', [n_ch1])
h = conv2d(x, w, s, b)
h = bn(h, 'g_bn1')
h = tf.nn.relu(h)
h = tf.nn.dropout(h, keep_prob)
h1 = h
n_ch2 = 128
w = init_weights('g_wc2', [f, f, n_ch1, n_ch2])
b = init_bias('g_bc2', [n_ch2])
h = conv2d(h, w, s, b)
h = bn(h, "g_bn2")
h = tf.nn.relu(h)
h = tf.nn.dropout(h, keep_prob)
h2 = h
n_ch3 = 256
w = init_weights('g_wc3', [f, f, n_ch2, n_ch3])
b = init_bias('g_bc3', [n_ch3])
h = conv2d(h, w, s, b)
h = bn(h, "g_bn3")
h = tf.nn.relu(h)
h = tf.nn.dropout(h, keep_prob)
output_shape = [batch_size, HEIGHT//s//s, WIDTH//s//s, n_ch2]
w = init_weights('g_wdc3', [f, f, n_ch2, n_ch3])
b = init_bias('g_bdc3', [n_ch2])
h = deconv2d(h, w, s, b, output_shape)
h = bn(h, "g_bnd3")
h = tf.nn.relu(h)
h = h + h2
output_shape = [batch_size, HEIGHT//s, WIDTH//s, n_ch1]
w = init_weights('g_wdc2', [f, f, n_ch1, n_ch2])
b = init_bias('g_bdc2', [n_ch1])
h = deconv2d(h, w, s, b, output_shape)
h = bn(h, "g_bnd2")
h = tf.nn.relu(h)
h = h + h1
output_shape = [batch_size, HEIGHT, WIDTH, CHANNELS]
w = init_weights('g_wdc1', [f, f, CHANNELS, n_ch1])
b = init_bias('g_bdc1', [CHANNELS])
h = deconv2d(h, w, s, b, output_shape)
return tf.nn.sigmoid(h+x)
When you use a stride s>1
you will get an hourglass, where the layers get smaller but deeper. The depth is independently controlled by the n_ch
variables.
BTW, I am running this on a Google colab notebook. Its amazing to have such an engine for free and be able to experiment with deep learning! Fantastic!