亞寵展、全球寵物產業風向標——亞洲寵物展覽會深度解析
1056
2022-05-30
3.4 GAN的工程實踐
之前幾節我們了解了生成對抗網絡(GAN)的設計原理。但是,實際它是如何實現的呢?在這一節中我們會詳細介紹GAN的實踐方法以及代碼的編寫[4]。
從之前的數學推導中我們知道,我們要做的是優化下面的式子。
(3-36)
計算公式中的期望值可以等價于計算真實數據分布與生成數據分布的積分,在實踐中我們使用采樣的方法來逼近期望值。
首先我們從前置的隨機分布pg(z)中取出m個隨機數{z(1), z(2), …, z(m)},其次我們再從真實數據分布pdata(x)中取出m個真實樣本{x(1), x(2), …, x(m)}我們使用平均數代替上式中的期望,公式改寫如下。
(3-37)
在GAN的原始論文中給出了完整的偽代碼,其中θd為判別器D的參數,θg為生成器G的參數。
該偽代碼每次迭代過程中的前半部分為訓練判別器的過程,后半部分為訓練生成器。對于判別器,我們會訓練k次來更新參數θd,在論文的實驗中研究者把k設為1,使得實驗成本最小。生成器在每次迭代中僅更新一次,如果更新多次可能無法使得生成數據分布與真實數據分布的JS散度距離下降。
下面我們嘗試使用TensorFlow來實現上一節中GAN訓練過程的可視化。
偽代碼2 基礎GAN的偽代碼實現(其中對于判別器會迭代k次,
k為超參數,大多數情況下可以使k = 1)
for訓練的迭代次數do
for重復k次do
從生成器前置隨機分布pg(z)取出m個小批次樣本z(1), …, z(m);
從真實數據分布pdata(x)取出m個小批次樣本x(1), …, x(m);
使用隨機梯度下降更新判別器參數;
end for
從生成器前置隨機分布pg(z)取出m個小批次樣本z(1), …, z(m);
使用隨機梯度下降更新生成器參數;
end for
首先需要設置真實數據樣本的分布,這里設置均值為3、方差為0.5的高斯分布。
class DataDistribution(object):
def __init__(self):
self.mu = 3
self.sigma = 0.5
def sample(self, N):
samples = np.random.normal(self.mu, self.sigma, N)
samples.sort()
return samples
接著設定生成器的初始化分布,這里設定的是平均分布。
class GeneratorDistribution(object):
def __init__(self, range):
self.range = range
def sample(self, N):
return np.linspace(-self.range, self.range, N) + \
np.random.random(N) * 0.01
使用下面的代碼設置一個最簡單的線性運算函數,用于后面的生成器與判別器。
def linear(input, output_dim, scope=None, stddev=1.0):
norm = tf.random_normal_initializer(stddev=stddev)
const = tf.constant_initializer(0.0)
with tf.variable_scope(scope or 'linear'):
w = tf.get_variable('w', [input.get_shape()[1], output_dim], initializer=norm)
b = tf.get_variable('b', [output_dim], initializer=const)
return tf.matmul(input, w) + b
基于該線性運算函數,我們可以完成簡單的生成器和判別器代碼。
def generator(input, h_dim):
h0 = tf.nn.softplus(linear(input, h_dim, 'g0'))
h1 = linear(h0, 1, 'g1')
return h1
def discriminator(input, h_dim):
h0 = tf.tanh(linear(input, h_dim * 2, 'd0'))
h1 = tf.tanh(linear(h0, h_dim * 2, 'd1'))
h2 = tf.tanh(linear(h1, h_dim * 2, 'd2'))
h3 = tf.sigmoid(linear(h2, 1, 'd3'))
return h3
設置優化器,這里使用的是學習率衰減的梯度下降方法。
def optimizer(loss, var_list, initial_learning_rate):
decay = 0.95
num_decay_steps = 150
batch = tf.Variable(0)
learning_rate = tf.train.exponential_decay(
initial_learning_rate,
batch,
num_decay_steps,
decay,
staircase=True
)
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(
loss,
global_step=batch,
var_list=var_list
)
return optimizer
下面來搭建GAN模型類的代碼,除了初始化參數之外,其中核心的兩個函數分別是模型的創建和模型的訓練。
class GAN(object):
def __init__(self, data, gen, num_steps, batch_size, log_every):
self.data = data
self.gen = gen
self.num_steps = num_steps
self.batch_size = batch_size
self.log_every = log_every
self.mlp_hidden_size = 4
self.learning_rate = 0.03
self._create_model()
def _create_model(self):
......
def train(self):
......
創建模型。這里需要創建預訓練判別器D_pre、生成器Generator和判別器Discriminator,按照之前的公式定義生成器和判別器的損失函數loss_g與loss_d以及它們的優化器opt_g與opt_d,其中D1與D2分別代表真實數據與生成數據的判別。
def _create_model(self):
with tf.variable_scope('D_pre'):
self.pre_input = tf.placeholder(tf.float32, shape=(self.batch_size, 1))
self.pre_labels = tf.placeholder(tf.float32, shape=(self.batch_size, 1))
D_pre = discriminator(self.pre_input, self.mlp_hidden_size)
self.pre_loss = tf.reduce_mean(tf.square(D_pre - self.pre_labels))
self.pre_opt = optimizer(self.pre_loss, None, self.learning_rate)
with tf.variable_scope('Generator'):
self.z = tf.placeholder(tf.float32, shape=(self.batch_size, 1))
self.G = generator(self.z, self.mlp_hidden_size)
with tf.variable_scope('Discriminator') as scope:
self.x = tf.placeholder(tf.float32, shape=(self.batch_size, 1))
self.D1 = discriminator(self.x, self.mlp_hidden_size)
scope.reuse_variables()
self.D2 = discriminator(self.G, self.mlp_hidden_size)
self.loss_d = tf.reduce_mean(-tf.log(self.D1) - tf.log(1 - self.D2))
self.loss_g = tf.reduce_mean(-tf.log(self.D2))
self.d_pre_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
scope='D_pre')
self.d_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=
'Discriminator')
self.g_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=
'Generator')
self.opt_d = optimizer(self.loss_d, self.d_params, self.learning_rate)
self.opt_g = optimizer(self.loss_g, self.g_params, self.learning_rate)
訓練模型的代碼如下所示,首先需要預先訓練判別器D_pre,然后將訓練后的參數共享給判別器Discriminator。接著就可以正式訓練生成器Generator與判別器Discriminator了。
def train(self):
with tf.Session() as session:
tf.global_variables_initializer().run()
# pretraining discriminator
num_pretrain_steps = 1000
for step in range(num_pretrain_steps):
d = (np.random.random(self.batch_size) - 0.5) * 10.0
labels = norm.pdf(d, loc=self.data.mu, scale=self.data.sigma)
pretrain_loss, _ = session.run([self.pre_loss, self.pre_opt], {
self.pre_input: np.reshape(d, (self.batch_size, 1)),
self.pre_labels: np.reshape(labels, (self.batch_size, 1))
})
self.weightsD = session.run(self.d_pre_params)
for i, v in enumerate(self.d_params):
session.run(v.assign(self.weightsD[i]))
for step in range(self.num_steps):
# update discriminator
x = self.data.sample(self.batch_size)
z = self.gen.sample(self.batch_size)
loss_d, _ = session.run([self.loss_d, self.opt_d], {
self.x: np.reshape(x, (self.batch_size, 1)),
self.z: np.reshape(z, (self.batch_size, 1))
})
# update generator
z = self.gen.sample(self.batch_size)
loss_g, _ = session.run([self.loss_g, self.opt_g], {
self.z: np.reshape(z, (self.batch_size, 1))
})
if step % self.log_every == 0:
print('{}:{}\t{}'.format(step, loss_d, loss_g))
if step % 100 == 0 or step==0 or step == self.num_steps -1 :
self._plot_distributions(session)
可視化代碼如下,使用對數據進行采樣的方式來展示生成數據與真實數據的分布。
def _samples(self, session, num_points=10000, num_bins=100):
xs = np.linspace(-self.gen.range, self.gen.range, num_points)
bins = np.linspace(-self.gen.range, self.gen.range, num_bins)
# data distribution
d = self.data.sample(num_points)
pd, _ = np.histogram(d, bins=bins, density=True)
# generated samples
zs = np.linspace(-self.gen.range, self.gen.range, num_points)
g = np.zeros((num_points, 1))
for i in range(num_points // self.batch_size):
g[self.batch_size * i:self.batch_size * (i + 1)] = session.run(self.G, {
self.z: np.reshape(
zs[self.batch_size * i:self.batch_size * (i + 1)],
(self.batch_size, 1)
)
})
pg, _ = np.histogram(g, bins=bins, density=True)
return pd, pg
def _plot_distributions(self, session):
pd, pg = self._samples(session)
p_x = np.linspace(-self.gen.range, self.gen.range, len(pd))
f, ax = plt.subplots(1)
ax.set_ylim(0, 1)
plt.plot(p_x, pd, label='Real Data')
plt.plot(p_x, pg, label='Generated Data')
plt.title('GAN Visualization')
plt.xlabel('Value')
plt.ylabel('Probability Density')
plt.legend()
plt.show()
最后設置主函數用于運行項目,分別設置迭代次數、批次數量以及希望展示可視化的間隔,這里的設置分別為1200、12和10。
def main(args):
model = GAN(
DataDistribution(),
GeneratorDistribution(range=8),
1200, #num_steps
12, #batch_size
10, #log_every
)
model.train()
運行過程中我們會分別看到圖3-11到圖3-13的幾個階段,GAN的生成器從平均分布開始會逐漸逼近最終的高斯分布,實現生成數據與真實數據分布的重合。
圖3-11 GAN可視化初始階段狀態
圖3-12 GAN可視化訓練中狀態
圖3-13 GAN可視化最終狀態
語言生成
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。