Bagaimana AI belajar menghasilkan gambar kucing



Bagaimana terjemahan AI dapat belajar menghasilkan gambar kucing .

Penelitian Generative Adversarial Nets (GAN) yang diterbitkan pada tahun 2014 adalah terobosan di bidang model generatif. Peneliti utama Yann Lekun menyebut jaring permusuhan "ide terbaik dalam pembelajaran mesin selama dua puluh tahun terakhir." Hari ini, berkat arsitektur ini, kita dapat membuat AI yang menghasilkan gambar realistis kucing. Keren!


DCGAN selama pelatihan

Semua kode yang berfungsi ada di repositori Github . Ini akan berguna bagi Anda jika Anda memiliki pengalaman dalam pemrograman Python, pembelajaran mendalam, bekerja dengan Tensorflow dan jaringan saraf convolutional.

Dan jika Anda baru belajar mendalam, saya sarankan Anda membiasakan diri dengan serangkaian artikel yang sangat baik Machine Learning is Fun!

Apa itu DCGAN?


Deep Convolutional Generative Adverserial Networks (DCGAN) adalah arsitektur pembelajaran mendalam yang menghasilkan data yang mirip dengan data dari set pelatihan.

Model ini menggantikan dengan lapisan konvolusional lapisan yang sepenuhnya terhubung dari jaringan permusuhan generatif. Untuk memahami cara kerja DCGAN, kami menggunakan metafora konfrontasi antara seorang kritikus ahli seni dan pemalsu.

Pemalsu ("generator") sedang mencoba membuat gambar Van Gogh palsu dan mengirimkannya sebagai gambar asli.



Seorang kritikus seni ("diskriminator") sedang mencoba untuk menghukum pemalsuan, menggunakan pengetahuannya tentang kanvas nyata Van Gogh.



Seiring waktu, kritikus seni semakin mendefinisikan kepalsuan, dan pemalsuan membuat semuanya lebih sempurna.


Seperti yang Anda lihat, DCGAN terdiri dari dua jaringan saraf pembelajaran dalam yang terpisah yang saling bersaing.

  • Generator sedang mencoba untuk membuat data yang dapat dipercaya. Dia tidak tahu apa data sebenarnya, tetapi dia belajar dari respons jaringan saraf musuh, mengubah hasil karyanya dengan setiap iterasi.
  • Diskriminator mencoba untuk menentukan data palsu (membandingkan dengan yang asli), menghindari sejauh mungkin yang salah dalam kaitannya dengan data nyata. Hasil dari model ini adalah umpan balik untuk generator.


Skema DCGAN.

  • Generator mengambil vektor noise acak dan menghasilkan gambar.
  • Gambar diberikan kepada diskriminator, ia membandingkannya dengan sampel pelatihan.
  • Diskriminator mengembalikan angka - 0 (palsu) atau 1 (gambar asli).

Mari kita buat DCGAN!


Sekarang kita siap untuk membuat AI kita sendiri.

Pada bagian ini, kita akan fokus pada komponen utama model kita. Jika Anda ingin melihat seluruh kode, buka di sini .

Masukkan data


Buat bertopik untuk input: inputs_real untuk pembeda dan inputs_z untuk generator. Harap dicatat bahwa kami akan memiliki dua tingkat pembelajaran, secara terpisah untuk generator dan pembeda.

DCGAN sangat sensitif terhadap hiperparameter, sehingga sangat penting untuk menyempurnakannya.
def model_inputs(real_dim, z_dim):

 """ Create the model inputs :param real_dim: tuple containing width, height and channels :param z_dim: The dimension of Z :return: Tuple of (tensor of real input images, tensor of z data, learning rate G, learning rate D) """ # inputs_real for Discriminator inputs_real = tf.placeholder(tf.float32, (None, *real_dim), name='inputs_real') # inputs_z for Generator inputs_z = tf.placeholder(tf.float32, (None, z_dim), name="input_z") # Two different learning rate : one for the generator, one for the discriminator learning_rate_G = tf.placeholder(tf.float32, name="learning_rate_G") learning_rate_D = tf.placeholder(tf.float32, name="learning_rate_D") return inputs_real, inputs_z, learning_rate_G, learning_rate_D 

Diskriminator dan generator


Kami menggunakan tf.variable_scope karena dua alasan.

Pertama, untuk memastikan semua nama variabel dimulai dengan generator / diskriminator. Nantinya ini akan membantu kita dalam melatih dua jaringan saraf.
Kedua, kami akan menggunakan kembali jaringan ini dengan data input yang berbeda:

  • Kami akan melatih generator, dan kemudian mengambil sampel gambar yang dihasilkannya.
  • Dalam diskriminator, kami akan membagikan variabel untuk gambar input palsu dan nyata.



Mari kita buat diskriminator. Ingat bahwa sebagai input, dibutuhkan gambar nyata atau palsu dan mengembalikan 0 atau 1 sebagai respons.

Beberapa catatan:

  • Kita perlu menggandakan ukuran filter di setiap lapisan konvolusional.
  • Menggunakan downsampling tidak disarankan. Sebaliknya, hanya lapisan convolutional yang dilucuti yang berlaku.
  • Di setiap lapisan, kami menggunakan normalisasi batch (dengan pengecualian lapisan input), karena ini mengurangi pergeseran kovarians. Baca lebih lanjut di artikel luar biasa ini .
  • Kami akan menggunakan Leaky ReLU sebagai fungsi aktivasi, ini akan membantu untuk menghindari efek dari gradien "menghilang".

 def discriminator(x, is_reuse=False, alpha = 0.2): ''' Build the discriminator network. Arguments --------- x : Input tensor for the discriminator n_units: Number of units in hidden layer reuse : Reuse the variables with tf.variable_scope alpha : leak parameter for leaky ReLU Returns ------- out, logits: ''' with tf.variable_scope("discriminator", reuse = is_reuse): # Input layer 128*128*3 --> 64x64x64 # Conv --> BatchNorm --> LeakyReLU conv1 = tf.layers.conv2d(inputs = x, filters = 64, kernel_size = [5,5], strides = [2,2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name='conv1') batch_norm1 = tf.layers.batch_normalization(conv1, training = True, epsilon = 1e-5, name = 'batch_norm1') conv1_out = tf.nn.leaky_relu(batch_norm1, alpha=alpha, name="conv1_out") # 64x64x64--> 32x32x128 # Conv --> BatchNorm --> LeakyReLU conv2 = tf.layers.conv2d(inputs = conv1_out, filters = 128, kernel_size = [5, 5], strides = [2, 2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name='conv2') batch_norm2 = tf.layers.batch_normalization(conv2, training = True, epsilon = 1e-5, name = 'batch_norm2') conv2_out = tf.nn.leaky_relu(batch_norm2, alpha=alpha, name="conv2_out") # 32x32x128 --> 16x16x256 # Conv --> BatchNorm --> LeakyReLU conv3 = tf.layers.conv2d(inputs = conv2_out, filters = 256, kernel_size = [5, 5], strides = [2, 2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name='conv3') batch_norm3 = tf.layers.batch_normalization(conv3, training = True, epsilon = 1e-5, name = 'batch_norm3') conv3_out = tf.nn.leaky_relu(batch_norm3, alpha=alpha, name="conv3_out") # 16x16x256 --> 16x16x512 # Conv --> BatchNorm --> LeakyReLU conv4 = tf.layers.conv2d(inputs = conv3_out, filters = 512, kernel_size = [5, 5], strides = [1, 1], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name='conv4') batch_norm4 = tf.layers.batch_normalization(conv4, training = True, epsilon = 1e-5, name = 'batch_norm4') conv4_out = tf.nn.leaky_relu(batch_norm4, alpha=alpha, name="conv4_out") # 16x16x512 --> 8x8x1024 # Conv --> BatchNorm --> LeakyReLU conv5 = tf.layers.conv2d(inputs = conv4_out, filters = 1024, kernel_size = [5, 5], strides = [2, 2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name='conv5') batch_norm5 = tf.layers.batch_normalization(conv5, training = True, epsilon = 1e-5, name = 'batch_norm5') conv5_out = tf.nn.leaky_relu(batch_norm5, alpha=alpha, name="conv5_out") # Flatten it flatten = tf.reshape(conv5_out, (-1, 8*8*1024)) # Logits logits = tf.layers.dense(inputs = flatten, units = 1, activation = None) out = tf.sigmoid(logits) return out, logits 



Kami telah membuat generator. Ingatlah bahwa dibutuhkan vektor noise (z) sebagai input dan, berkat lapisan konvolusi yang ditransformasikan, menciptakan gambar palsu.

Pada setiap lapisan, kami membagi dua ukuran filter, dan juga menggandakan ukuran gambar.

Generator bekerja paling baik saat menggunakan tanh sebagai fungsi aktivasi output.

 def generator(z, output_channel_dim, is_train=True): ''' Build the generator network. Arguments --------- z : Input tensor for the generator output_channel_dim : Shape of the generator output n_units : Number of units in hidden layer reuse : Reuse the variables with tf.variable_scope alpha : leak parameter for leaky ReLU Returns ------- out: ''' with tf.variable_scope("generator", reuse= not is_train): # First FC layer --> 8x8x1024 fc1 = tf.layers.dense(z, 8*8*1024) # Reshape it fc1 = tf.reshape(fc1, (-1, 8, 8, 1024)) # Leaky ReLU fc1 = tf.nn.leaky_relu(fc1, alpha=alpha) # Transposed conv 1 --> BatchNorm --> LeakyReLU # 8x8x1024 --> 16x16x512 trans_conv1 = tf.layers.conv2d_transpose(inputs = fc1, filters = 512, kernel_size = [5,5], strides = [2,2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name="trans_conv1") batch_trans_conv1 = tf.layers.batch_normalization(inputs = trans_conv1, training=is_train, epsilon=1e-5, name="batch_trans_conv1") trans_conv1_out = tf.nn.leaky_relu(batch_trans_conv1, alpha=alpha, name="trans_conv1_out") # Transposed conv 2 --> BatchNorm --> LeakyReLU # 16x16x512 --> 32x32x256 trans_conv2 = tf.layers.conv2d_transpose(inputs = trans_conv1_out, filters = 256, kernel_size = [5,5], strides = [2,2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name="trans_conv2") batch_trans_conv2 = tf.layers.batch_normalization(inputs = trans_conv2, training=is_train, epsilon=1e-5, name="batch_trans_conv2") trans_conv2_out = tf.nn.leaky_relu(batch_trans_conv2, alpha=alpha, name="trans_conv2_out") # Transposed conv 3 --> BatchNorm --> LeakyReLU # 32x32x256 --> 64x64x128 trans_conv3 = tf.layers.conv2d_transpose(inputs = trans_conv2_out, filters = 128, kernel_size = [5,5], strides = [2,2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name="trans_conv3") batch_trans_conv3 = tf.layers.batch_normalization(inputs = trans_conv3, training=is_train, epsilon=1e-5, name="batch_trans_conv3") trans_conv3_out = tf.nn.leaky_relu(batch_trans_conv3, alpha=alpha, name="trans_conv3_out") # Transposed conv 4 --> BatchNorm --> LeakyReLU # 64x64x128 --> 128x128x64 trans_conv4 = tf.layers.conv2d_transpose(inputs = trans_conv3_out, filters = 64, kernel_size = [5,5], strides = [2,2], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name="trans_conv4") batch_trans_conv4 = tf.layers.batch_normalization(inputs = trans_conv4, training=is_train, epsilon=1e-5, name="batch_trans_conv4") trans_conv4_out = tf.nn.leaky_relu(batch_trans_conv4, alpha=alpha, name="trans_conv4_out") # Transposed conv 5 --> tanh # 128x128x64 --> 128x128x3 logits = tf.layers.conv2d_transpose(inputs = trans_conv4_out, filters = 3, kernel_size = [5,5], strides = [1,1], padding = "SAME", kernel_initializer=tf.truncated_normal_initializer(stddev=0.02), name="logits") out = tf.tanh(logits, name="out") return out 

Kerugian dalam diskriminator dan generator


Karena kita melatih generator dan diskriminator, kita perlu menghitung kerugian untuk kedua jaringan saraf. Diskriminator harus memberi 1 ketika "menganggap" gambar itu nyata, dan 0 jika gambar itu palsu. Sesuai dengan ini dan Anda perlu mengkonfigurasi kerugian. Kerugian diskriminator dihitung sebagai jumlah kerugian untuk citra asli dan palsu:

d_loss = d_loss_real + d_loss_fake

di mana d_loss_real adalah kerugian ketika diskriminator menganggap gambar itu salah, tetapi sebenarnya itu nyata. Itu dihitung sebagai berikut:

  • Kami menggunakan d_logits_real , semua label sama dengan 1 (karena semua data adalah nyata).
  • labels = tf.ones_like(tensor) * (1 - smooth) . Mari kita gunakan label smoothing: turunkan nilai label dari 1,0 ke 0,9 untuk membantu pembeda menggeneralisasi lebih baik.

d_loss_fake adalah kerugian ketika diskriminator menganggap gambar itu nyata, tetapi sebenarnya itu palsu.

  • Kami menggunakan d_logits_fake , semua label adalah 0.

Untuk kehilangan generator, d_logits_fake dari diskriminator digunakan. Kali ini, semua label adalah 1, karena generator ingin mengelabui pembeda.

 def model_loss(input_real, input_z, output_channel_dim, alpha): """ Get the loss for the discriminator and generator :param input_real: Images from the real dataset :param input_z: Z input :param out_channel_dim: The number of channels in the output image :return: A tuple of (discriminator loss, generator loss) """ # Generator network here g_model = generator(input_z, output_channel_dim) # g_model is the generator output # Discriminator network here d_model_real, d_logits_real = discriminator(input_real, alpha=alpha) d_model_fake, d_logits_fake = discriminator(g_model,is_reuse=True, alpha=alpha) # Calculate losses d_loss_real = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_real, labels=tf.ones_like(d_model_real))) d_loss_fake = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake, labels=tf.zeros_like(d_model_fake))) d_loss = d_loss_real + d_loss_fake g_loss = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake, labels=tf.ones_like(d_model_fake))) return d_loss, g_loss 

Pengoptimal


Setelah menghitung kerugian, generator dan pembeda harus diperbarui secara individual. Untuk melakukan ini, gunakan tf.trainable_variables() membuat daftar semua variabel yang didefinisikan dalam grafik kami.

 def model_optimizers(d_loss, g_loss, lr_D, lr_G, beta1): """ Get optimization operations :param d_loss: Discriminator loss Tensor :param g_loss: Generator loss Tensor :param learning_rate: Learning Rate Placeholder :param beta1: The exponential decay rate for the 1st moment in the optimizer :return: A tuple of (discriminator training operation, generator training operation) """ # Get the trainable_variables, split into G and D parts t_vars = tf.trainable_variables() g_vars = [var for var in t_vars if var.name.startswith("generator")] d_vars = [var for var in t_vars if var.name.startswith("discriminator")] update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) # Generator update gen_updates = [op for op in update_ops if op.name.startswith('generator')] # Optimizers with tf.control_dependencies(gen_updates): d_train_opt = tf.train.AdamOptimizer(learning_rate=lr_D, beta1=beta1).minimize(d_loss, var_list=d_vars) g_train_opt = tf.train.AdamOptimizer(learning_rate=lr_G, beta1=beta1).minimize(g_loss, var_list=g_vars) return d_train_opt, g_train_opt 

Pelatihan


Sekarang kami menerapkan fungsi pelatihan. Idenya cukup sederhana:

  • Kami menyimpan model kami setiap lima periode (zaman).
  • Kami menyimpan gambar dalam folder dengan gambar setiap 10 batch terlatih.
  • Setiap 15 periode kami menampilkan g_loss , d_loss dan gambar yang dihasilkan. Ini karena notebook Jupyter mungkin macet ketika menampilkan terlalu banyak gambar.
  • Atau kita dapat langsung menghasilkan gambar nyata dengan memuat model yang disimpan (ini akan menghemat 20 jam pelatihan).

 def train(epoch_count, batch_size, z_dim, learning_rate_D, learning_rate_G, beta1, get_batches, data_shape, data_image_mode, alpha): """ Train the GAN :param epoch_count: Number of epochs :param batch_size: Batch Size :param z_dim: Z dimension :param learning_rate: Learning Rate :param beta1: The exponential decay rate for the 1st moment in the optimizer :param get_batches: Function to get batches :param data_shape: Shape of the data :param data_image_mode: The image mode to use for images ("RGB" or "L") """ # Create our input placeholders input_images, input_z, lr_G, lr_D = model_inputs(data_shape[1:], z_dim) # Losses d_loss, g_loss = model_loss(input_images, input_z, data_shape[3], alpha) # Optimizers d_opt, g_opt = model_optimizers(d_loss, g_loss, lr_D, lr_G, beta1) i = 0 version = "firstTrain" with tf.Session() as sess: sess.run(tf.global_variables_initializer()) # Saver saver = tf.train.Saver() num_epoch = 0 if from_checkpoint == True: saver.restore(sess, "./models/model.ckpt") show_generator_output(sess, 4, input_z, data_shape[3], data_image_mode, image_path, True, False) else: for epoch_i in range(epoch_count): num_epoch += 1 if num_epoch % 5 == 0: # Save model every 5 epochs #if not os.path.exists("models/" + version): # os.makedirs("models/" + version) save_path = saver.save(sess, "./models/model.ckpt") print("Model saved") for batch_images in get_batches(batch_size): # Random noise batch_z = np.random.uniform(-1, 1, size=(batch_size, z_dim)) i += 1 # Run optimizers _ = sess.run(d_opt, feed_dict={input_images: batch_images, input_z: batch_z, lr_D: learning_rate_D}) _ = sess.run(g_opt, feed_dict={input_images: batch_images, input_z: batch_z, lr_G: learning_rate_G}) if i % 10 == 0: train_loss_d = d_loss.eval({input_z: batch_z, input_images: batch_images}) train_loss_g = g_loss.eval({input_z: batch_z}) # Save it image_name = str(i) + ".jpg" image_path = "./images/" + image_name show_generator_output(sess, 4, input_z, data_shape[3], data_image_mode, image_path, True, False) # Print every 5 epochs (for stability overwize the jupyter notebook will bug) if i % 1500 == 0: image_name = str(i) + ".jpg" image_path = "./images/" + image_name print("Epoch {}/{}...".format(epoch_i+1, epochs), "Discriminator Loss: {:.4f}...".format(train_loss_d), "Generator Loss: {:.4f}".format(train_loss_g)) show_generator_output(sess, 4, input_z, data_shape[3], data_image_mode, image_path, False, True) return losses, samples 

Bagaimana cara menjalankannya


Semua ini dapat berjalan langsung di komputer Anda jika Anda siap menunggu 10 tahun, jadi lebih baik menggunakan layanan GPU berbasis cloud seperti AWS atau FloydHub. Secara pribadi, saya melatih DCGAN ini selama 20 jam di Microsoft Azure dan Deep Virtual Machine mereka . Saya tidak memiliki hubungan bisnis dengan Azure, saya hanya menyukai layanan pelanggan mereka.

Jika Anda mengalami kesulitan menjalankan di mesin virtual, lihat artikel yang bagus ini .

Jika Anda meningkatkan model, jangan ragu untuk melakukan permintaan penarikan.

Source: https://habr.com/ru/post/id416129/


All Articles