Tại sao cần placeholder tensorflow

So far we have used Variables to manage our data, but there is a more basic structure, the placeholder. A placeholder is simply a variable that we will assign data to at a later date. It allows us to create our operations and build our computation graph, without needing the data. In TensorFlowterminology, we then feed data into the graph through these placeholders.

import tensorflow as tf

x = tf.placeholder("float", None)
y = x * 2

with tf.Session() as session:
    result = session.run(y, feed_dict={x: [1, 2, 3]})
    print(result)

This example works a little differently from our previous ones, let’s break it down.

First, we import tensorflow as normal. Then we create a placeholdercalled x, i.e. a place in memory where we will store value later on.

Then, we create a Tensor called, which is the operation of multiplying x by 2. Note that we haven’t defined any initial values for x yet.

We now have an operation (y) defined, and can now run it in a session. We create a session object, and then run just the y variable. Note that this means, that if we defined a much larger graph of operations, we can run just a small segment of the graph. This subgraph evaluation is actually a bit selling point of TensorFlow, and one that isn’t present in many other libraries that do similar things.

Running y requires knowledge about the values of x. We define these inside the feed_dict argument to run. We state here that the values of x are [1, 2, 3]. We run y, giving us the result of [2, 4, 6].

Placeholders do not need to be statically sized. Let’s update our program to allow x to take on any length. Change the definition of x to be:

x = tf.placeholder("float", None)

Now, when we define the values of x in the feed_dict we can have any number of values. The code should still work, and give the same answer, but now it will also work with any number of values in feed_dict.

Placeholders can also have multiple dimensions, allowing for storing arrays. In the following example, we create a 3 by 2 matrix, and store some numbers in it. We then use the same operation as before to do element-wise doubling of the numbers.

import tensorflow as tf

x = tf.placeholder("float", [None, 3])
y = x * 2

with tf.Session() as session:
    x_data = [[1, 2, 3],
              [4, 5, 6],]
    result = session.run(y, feed_dict={x: x_data})
    print(result)

The first dimension of the placeholder is None, meaning we can have any number of rows. The second dimension is fixed at 3, meaning each row needs to have three columns of data.

We can extend this to take an arbitrary number of None dimensions. In this example, we load up the image from our last lesson, then create a placeholder that stores a slice of that image. The slice is a 2D segment of the image, but each “pixel” has three components (red, green, blue). Therefore, we need None for the first two dimensions, but need 3 (or None would work) for the last dimension. We then use TensorFlow’s slicemethod to take a subsegment out of the image to operate on.

import tensorflow as tf
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import os

# First, load the image again
dir_path = os.path.dirname(os.path.realpath(__file__))
filename = dir_path + "/MarshOrchid.jpg"
raw_image_data = mpimg.imread(filename)

image = tf.placeholder("uint8", [None, None, 3])
slice = tf.slice(image, [1000, 0, 0], [3000, -1, -1])

with tf.Session() as session:
    result = session.run(slice, feed_dict={image: raw_image_data})
    print(result.shape)

plt.imshow(result)
plt.show()

1) Take a look at the other functions for arrays in TensorFlow at the official documentation.

2) Break the image apart into four “corners”, then stitch it back together again.

3) Convert the image into grayscale. One way to do this would be to take just a single colour channel and show that. Another way would be to take the average of the three channels as the gray colour.

Generative Discriminative Networks - GANs

GANs là gì?

GANs là một thuật toán học không giám sát (Unsupersived Learning) được Ian Goodfellow giới thiệu vào năm 2014 tại hội nghị NIPS, trong đó bao gồm hai thành phần chính là GeneratorDiscriminator:

  • Generator (ký hiệu G G) nhận nhiệm vụ học ra cách áp xạ từ một không gian tìm ẩn ZZ (a latent space) vào một không gian với phân phối từ dữ liệu cho trước.

  • Discriminator (ký hiệu DD) nhận nhiệm vụ phân biệt dữ liệu được tạo ra từ GG và dữ liệu cho trước.

Một cách toán học: Giả sử ta có z∈Zz \in Zz∼pZ(z)z \sim p_Z(z), dữ liệu cho trước xxx∼pdata(x)x \sim p_{data}(x) (xx gọi là real data). Ta có GG sẽ ánh xạ zz không gian dữ liệu cho trước x^=G(z)\hat{x}=G(z) (x^\hat{x} gọi là fake data). D(x)D(x) là xác suất mà xx là real data hay fake data. Mục tiêu của GANs là làm sao cho GG cố gắng tạo ra được x^\hat{x} sao cho DD không còn thể phân biệt được là fake data. Tối ưu GG DD giống như trò chơi minimaxminimax với hàm mục tiêu V (D,G)V(D, G), trong đó GG cố gắng làm tăng xác suất mà x^\hat{x} được tạo ra là real data và DD thì cố gắng làm điều ngược lại.

min⁡Gmax⁡DV(D,G)=Ex∼pdata(x)[logD(x)]+Ez∼pZ(z)[ log(1−D(G(z)))] \min_{G} \max_{D} V(D, G) = \mathbb{E}_{x \sim p_{data}(x)}[log D(x)] + \mathbb{E}_{z \sim p_Z(z)}[log(1 - D(G(z)))]

Tối ưu GANs

Qúa tình tối ưu GANs cũng khá đơn giản:

  1. Lấy ngẫu nhiên mm mẫuz∈Zz \in Zmm mẫu xx từ dữ liệu cho trước.
  2. Tối ưu DD dựa trên zzxx.
  3. Lấy ngẫu nhiên mm mẫu z∈Zz \in Z (có thể dùng lại zz ở bước 1).
  4. Tối ưu GG dựa trên zz.
  5. Quay lại bước 11.

Minh hoạ bằng Python với Tensorflow

Sau đây mình sẽ minh hoạ GAN với tập dữ liệu MNIST. Toàn bộ mã nguồn có thể tìm được tại đây

Trước tiên mình cần cài đặt thư viện Tensorflow cho Python

[sudo] pip install tensorflow #Hoặc tensorflow-gpu đối với các bạn sử dụng Tensorflow với GPU

Trước tiên ta cần khai bái các thư viện cần thiết:

import tensorflow as tf
import tensorflow.contrib.slim as slim #slim cho phép khai báo nhanh các lớp thông dụng
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data #Tensorfow cung cấp sẵn API để lấy dữ liệu từ tập MNIST

Ta cần định nghĩa GGDD , ở đây mình sử dụng một mạng truyền thẳng (feed-forward) đơn giản với một lớp ẩn:

def generator(inputs):
	with tf.variable_scope("generator"):
		net = slim.fully_connected(inputs, 256, scope = "fc1")
		net = slim.fully_connected(net, 784, scope = "fake_images", activation_fn = tf.nn.sigmoid)
	return net

def discriminator(inputs):
	with tf.variable_scope("discriminator"):
		net = slim.fully_connected(inputs, 256, scope = "fc1")
		net = slim.fully_connected(net, 1, scope = "predictions", activation_fn = tf.nn.sigmoid)
	return net

Ta đinh nghĩa các hyperparameters cần thiết:

mnist_loader = input_data.read_data_sets('MNIST_data')
batch_size = 32
z_dim = 100 #Số chiều của Z
learning_rate = 0.0002
num_iters = 100000

Sau đó ta khởi tạo mạng:

random_z = tf.placeholder(shape = [batch_size, z_dim], dtype = tf.float32, name = "random_vector") #vector z
real_images = tf.placeholder(shape = [batch_size, 784], dtype = tf.float32, name = "real_images") #real data
fake_images = generator(random_z) #fake data

predictions = discriminator(tf.concat([real_images, fake_images], axis = 0)) #fake và real data được đưa qua Discriminator
real_preds = tf.slice(predictions, [0, 0], [batch_size, -1])
fake_preds = tf.slice(predictions, [batch_size, 0], [batch_size, -1])

Sau đó ta định nghĩa hàm mất cho GeneratorDiscriminator. Ở đây mình sử dụng Adam Optimizer để tối ưu GG DD.

gen_loss = -tf.reduce_mean(tf.log(fake_preds))
dis_loss = -tf.reduce_mean(tf.log(real_preds) + tf.log(1. - fake_preds))

gen_vars = slim.get_variables(scope = "generator")
dis_vars = slim.get_variables(scope = "discriminator")

optimizer = tf.train.AdamOptimizer(learning_rate)
gen_train_op = optimizer.minimize(gen_loss, var_list = gen_vars)
dis_train_op = optimizer.minimize(dis_loss, var_list = dis_vars)

Sau đó ta tiến hành tối ưu GGDD

sess = tf.Session()
sess.run(tf.global_variables_initializer())
for iter in xrange(1, num_iters + 1):
	## Vector ngẫu nhiên z được lấy từ phân phối đều trên [-1, 1]
    feed_dict = {
            random_z: np.random.uniform(-1., 1., size=[batch_size, z_dim]),
            real_images: mnist_loader.train.next_batch(batch_size=batch_size)[0]
            }
    _, _, _gen_loss, _dis_loss = sess.run(
		    [gen_train_op, dis_train_op, gen_loss, dis_loss],
			feed_dict = feed_dict
			)
			
    if (iter % 50) == 0:
        print("Iteration [{:06d}/{:06d}]".format(iter, num_iters))
        print("\t>> Generator Loss: {}".format(_gen_loss))
        print("\t>> Discriminator Loss: {}".format(_dis_loss))

Ứng dụng của GANs và Những điều lưu ý

Trong những năm gần đây, GAN đã có những ứng dụng mạnh mẽ trong nhiều bài toán như Image Super Resolution, Image Translation, Domain Adaptaion. Tuy nhiên để tối ưu GANs là điều không phải dễ, điều này đòi hỏi về phần cứng cũng như sự phân tích bài toán:

  • GAN đòi hỏi chi phí phần cứng cao với các bài toán xử lý ảnh có kích thước lớn.

  • Việc điều chỉnh Learning Rate cũng không phải là điểu dễ dàng, phải cân bằng làm sao cho DiscriminatorGenerator cân bằng lẫn nhau, nếu không dễ dẫn đến một bên lấn áp phần còn lại, dẫn đến Generator không cho ra kết quả tốt.

Bài viết này chỉ cung cấp một khái niệm và ví dụ cơ bản nhất về GANs, nếu có gì sai sót mong các bạn có thể đóng góp kiến.

Tác giả

Trương Thành Đạt
Email:
Đại học Khoa học Tự nhiên, Đại học Quốc Gia, TPHCM
Github: https://github.com/truongthanhdat
Github Page: http://truongthanhdat.github.io/

Tham khảo

  1. Generative Adversarial Networks Wiki
  2. Generative Adversarial Nets. Ian Goodfellow. NIPS 2014
  3. Mã nguồn tham khảo