精通 TensorFlow 1.x:11~15(3)https://developer.aliyun.com/article/1426826
V 函数和 Q 函数之间的关系:
V*(s)
是状态s
下的最优值函数,其给出最大奖励,并且Q*(s,a)
是状态s
下的最佳 Q 函数,其通过选择动作a
给出最大期望奖励。 因此,V*(s)
是所有可能动作中所有最优 Q 函数Q*(s,a)
的最大值:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4PkNXMjX-1681566540590)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/bce4bf13-c9cc-416f-a901-56121b7f631c.png)]
强化学习技巧
可以根据模型的可用性对强化学习技术进行如下分类:
- 模型可用:如果模型可用,则智能体可以通过迭代策略或值函数来离线计划,以找到提供最大奖励的最优策略。
- 值迭代学习:在值迭代学习方法中,智能体通过将
V(s)
初始化为随机值开始,然后重复更新V(s)
直到找到最大奖励。 - 策略迭代学习 : 在策略迭代学习方法中,智能体通过初始化随机策略
p
开始,然后重复更新策略,直到找到最大奖励。
- 模型不可用:如果模型不可用,则智能体只能通过观察其动作的结果来学习。因此,从观察,行动和奖励的历史来看,智能体会尝试估计模型或尝试直接推导出最优策略:
- 基于模型的学习:在基于模型的学习中,智能体首先从历史中估计模型,然后使用策略或基于值的方法来找到最优策略。
- 无模型学习:在无模型学习中,智能体不会估计模型,而是直接从历史中估计最优策略。 Q-Learning 是无模型学习的一个例子。
作为示例,值迭代学习的算法如下:
initialize V(s) to random values for all states Repeat for s in states for a in actions compute Q[s,a] V(s) = max(Q[s]) # maximum of Q for all actions for that state Until optimal value of V(s) is found for all states
策略迭代学习的算法如下:
initialize a policy P_new to random sequence of actions for all states Repeat P = P_new for s in states compute V(s) with P[s] P_new[s] = policy of optimal V(s) Until P == P_new
强化学习的朴素神经网络策略
我们按照以下策略进行:
- 让我们实现一个朴素的基于神经网络的策略。为定义一个新策略使用基于神经网络的预测来返回动作:
def policy_naive_nn(nn,obs): return np.argmax(nn.predict(np.array([obs])))
- 将
nn
定义为一个简单的单层 MLP 网络,它将具有四个维度的观测值作为输入,并产生两个动作的概率:
from keras.models import Sequential from keras.layers import Dense model = Sequential() model.add(Dense(8,input_dim=4, activation='relu')) model.add(Dense(2, activation='softmax')) model.compile(loss='categorical_crossentropy',optimizer='adam') model.summary()
这就是模型的样子:
Layer (type) Output Shape Param # ================================================================= dense_16 (Dense) (None, 8) 40 _________________________________________________________________ dense_17 (Dense) (None, 2) 18 ================================================================= Total params: 58 Trainable params: 58 Non-trainable params: 0
- 这个模型需要训练。运行 100 集的模拟并仅收集分数大于 100 的那些剧集的训练数据。如果分数小于 100,那么这些状态和动作不值得记录,因为它们不是好戏的例子:
# create training data env = gym.make('CartPole-v0') n_obs = 4 n_actions = 2 theta = np.random.rand(4) * 2 - 1 n_episodes = 100 r_max = 0 t_max = 0 x_train, y_train = experiment(env, policy_random, n_episodes, theta,r_max,t_max, return_hist_reward=100 ) y_train = np.eye(n_actions)[y_train] print(x_train.shape,y_train.shape)
我们能够收集 5732 个样本进行训练:
(5732, 4) (5732, 2)
- 接下来,训练模型:
model.fit(x_train, y_train, epochs=50, batch_size=10)
- 训练的模型可用于玩游戏。但是,在我们合并更新训练数据的循环之前,模型不会从游戏的进一步游戏中学习:
n_episodes = 200 r_max = 0 t_max = 0 _ = experiment(env, policy_naive_nn, n_episodes, theta=model, r_max=r_max, t_max=t_max, return_hist_reward=0 ) _ = experiment(env, policy_random, n_episodes, theta,r_max,t_max, return_hist_reward=0 )
我们可以看到,这种朴素的策略几乎以同样的方式执行,虽然比随机策略好一点:
Policy:policy_naive_nn, Min reward:37.0, Max reward:200.0, Average reward:71.05 Policy:policy_random, Min reward:36.0, Max reward:200.0, Average reward:68.755
我们可以通过网络调整和超参数调整,或通过学习更多游戏玩法来进一步改进结果。 但是,有更好的算法,例如 Q-Learning。
在本章的其余部分,我们将重点关注 Q-Learning 算法,因为大多数现实生活中的问题涉及无模型学习。
实现 Q-Learning
Q-Learning 是一种无模型的方法,可以找到可以最大化智能体奖励的最优策略。在最初的游戏过程中,智能体会为每对(状态,动作)学习 Q 值,也称为探索策略,如前面部分所述。一旦学习了 Q 值,那么最优策略将是在每个状态中选择具有最大 Q 值的动作,也称为利用策略。学习算法可以以局部最优解决方案结束,因此我们通过设置exploration_rate
参数来继续使用探索策略。
Q-Learning 算法如下:
initialize Q(shape=[#s,#a]) to random values or zeroes Repeat (for each episode) observe current state s Repeat select an action a (apply explore or exploit strategy) observe state s_next as a result of action a update the Q-Table using bellman's equation set current state s = s_next until the episode ends or a max reward / max steps condition is reached Until a number of episodes or a condition is reached (such as max consecutive wins)
上述算法中的Q(s, )
表示我们在前面部分中描述的 Q 函数。此函数的值用于选择操作而不是奖励,因此此函数表示奖励或折扣奖励。使用未来状态中 Q 函数的值更新 Q 函数的值。众所周知的贝尔曼方程捕获了这一更新:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wcQ4ninC-1681566540591)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/64daa9fe-499c-4067-81fc-9e8d555740e2.png)]
这基本上意味着在时间步骤t
,在状态s
中,对于动作a
,最大未来奖励(Q)等于来自当前状态的奖励加上来自下一状态的最大未来奖励。
Q(s, a)
可以实现为 Q 表或称为 Q 网络的神经网络。在这两种情况下,Q 表或 Q 网络的任务是基于给定输入的 Q 值提供最佳可能的动作。随着 Q 表变大,基于 Q 表的方法通常变得棘手,因此使神经网络成为通过 Q 网络逼近 Q 函数的最佳候选者。让我们看看这两种方法的实际应用。
您可以按照本书代码包中的 Jupyter 笔记本ch-13b_Reinforcement_Learning_DQN
中的代码进行操作。
Q-Learning 的初始化和离散化
极地车环境返回的观测涉及环境状况。极点车的状态由我们需要离散的连续值表示。
如果我们将这些值离散化为小的状态空间,那么智能体会得到更快的训练,但需要注意的是会有收敛到最优策略的风险。
我们使用以下辅助函数来离散极推车环境的状态空间:
# discretize the value to a state space def discretize(val,bounds,n_states): discrete_val = 0 if val <= bounds[0]: discrete_val = 0 elif val >= bounds[1]: discrete_val = n_states-1 else: discrete_val = int(round( (n_states-1) * ((val-bounds[0])/ (bounds[1]-bounds[0])) )) return discrete_val def discretize_state(vals,s_bounds,n_s): discrete_vals = [] for i in range(len(n_s)): discrete_vals.append(discretize(vals[i],s_bounds[i],n_s[i])) return np.array(discrete_vals,dtype=np.int)
我们将每个观察尺寸的空间离散为 10 个单元。您可能想尝试不同的离散空间。在离散化之后,我们找到观察的上限和下限,并将速度和角速度的界限改变在 -1 和 +1 之间,而不是-Inf
和+Inf
。代码如下:
env = gym.make('CartPole-v0') n_a = env.action_space.n # number of discrete states for each observation dimension n_s = np.array([10,10,10,10]) # position, velocity, angle, angular velocity s_bounds = np.array(list(zip(env.observation_space.low, env.observation_space.high))) # the velocity and angular velocity bounds are # too high so we bound between -1, +1 s_bounds[1] = (-1.0,1.0) s_bounds[3] = (-1.0,1.0)
使用 Q-Table 的 Q-Learning
您可以在ch-13b.ipynb
中按照本节的代码进行操作。 由于我们的离散空间的尺寸为[10,10,10,10]
,因此我们的 Q 表的尺寸为[10,10,10,10,2]
:
# create a Q-Table of shape (10,10,10,10, 2) representing S X A -> R q_table = np.zeros(shape = np.append(n_s,n_a))
我们根据exploration_rate
定义了一个利用或探索的 Q-Table 策略:
def policy_q_table(state, env): # Exploration strategy - Select a random action if np.random.random() < explore_rate: action = env.action_space.sample() # Exploitation strategy - Select the action with the highest q else: action = np.argmax(q_table[tuple(state)]) return action
定义运行单个剧集的episode()
函数,如下所示:
- 首先初始化变量和第一个状态:
obs = env.reset() state_prev = discretize_state(obs,s_bounds,n_s) episode_reward = 0 done = False t = 0
- 选择操作并观察下一个状态:
action = policy(state_prev, env) obs, reward, done, info = env.step(action) state_new = discretize_state(obs,s_bounds,n_s)
- 更新 Q 表:
best_q = np.amax(q_table[tuple(state_new)]) bellman_q = reward + discount_rate * best_q indices = tuple(np.append(state_prev,action)) q_table[indices] += learning_rate*( bellman_q - q_table[indices])
- 将下一个状态设置为上一个状态,并将奖励添加到剧集的奖励中:
state_prev = state_new episode_reward += reward
experiment()
函数调用剧集函数并累积报告奖励。您可能希望修改该函数以检查连续获胜以及特定于您的游戏或游戏的其他逻辑:
# collect observations and rewards for each episode def experiment(env, policy, n_episodes,r_max=0, t_max=0): rewards=np.empty(shape=[n_episodes]) for i in range(n_episodes): val = episode(env, policy, r_max, t_max) rewards[i]=val print('Policy:{}, Min reward:{}, Max reward:{}, Average reward:{}' .format(policy.__name__, np.min(rewards), np.max(rewards), np.mean(rewards)))
现在,我们要做的就是定义参数,例如learning_rate
,discount_rate
和explore_rate
,并运行experiment()
函数,如下所示:
learning_rate = 0.8 discount_rate = 0.9 explore_rate = 0.2 n_episodes = 1000 experiment(env, policy_q_table, n_episodes)
对于 1000 集,基于我们的简单实现,基于 Q-Table 的策略的最大奖励为 180:
Policy:policy_q_table, Min reward:8.0, Max reward:180.0, Average reward:17.592
我们对算法的实现很容易解释。但是,您可以对代码进行修改以将探索率设置为最初,然后随着时间步长的过去而衰减。同样,您还可以实现学习和折扣率的衰减逻辑。让我们看看,由于我们的 Q 函数学得更快,我们是否可以用更少的剧集获得更高的奖励。
使用 Q-Network 或深度 Q 网络(DQN)的 Q-Learning
在 DQN 中,我们将 Q-Table 替换为神经网络(Q-Network),当我们使用探索状态及其 Q 值连续训练时,它将学会用最佳动作进行响应。因此,为了训练网络,我们需要一个存储游戏内存的地方:
- 使用大小为 1000 的双端队列实现游戏内存:
memory = deque(maxlen=1000)
- 接下来,构建一个简单的隐藏层神经网络模型,
q_nn
:
from keras.models import Sequential from keras.layers import Dense model = Sequential() model.add(Dense(8,input_dim=4, activation='relu')) model.add(Dense(2, activation='linear')) model.compile(loss='mse',optimizer='adam') model.summary() q_nn = model
Q-Network 看起来像这样:
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 8) 40 _________________________________________________________________ dense_2 (Dense) (None, 2) 18 ================================================================= Total params: 58 Trainable params: 58 Non-trainable params: 0 _________________________________________________________________
执行游戏的一集的episode()
函数包含基于 Q-Network 的算法的以下更改:
- 生成下一个状态后,将状态,操作和奖励添加到游戏内存中:
action = policy(state_prev, env) obs, reward, done, info = env.step(action) state_next = discretize_state(obs,s_bounds,n_s) # add the state_prev, action, reward, state_new, done to memory memory.append([state_prev,action,reward,state_next,done])
- 使用 bellman 函数生成并更新
q_values
以获得最大的未来奖励:
states = np.array([x[0] for x in memory]) states_next = np.array([np.zeros(4) if x[4] else x[3] for x in memory]) q_values = q_nn.predict(states) q_values_next = q_nn.predict(states_next) for i in range(len(memory)): state_prev,action,reward,state_next,done = memory[i] if done: q_values[i,action] = reward else: best_q = np.amax(q_values_next[i]) bellman_q = reward + discount_rate * best_q q_values[i,action] = bellman_q
- 训练
q_nn
的状态和我们从记忆中收到的q_values
:
q_nn.fit(states,q_values,epochs=1,batch_size=50,verbose=0)
将游戏玩法保存在内存中并使用它来训练模型的过程在深度强化学习文献中也称为记忆重放。让我们按照以下方式运行基于 DQN 的游戏:
learning_rate = 0.8 discount_rate = 0.9 explore_rate = 0.2 n_episodes = 100 experiment(env, policy_q_nn, n_episodes)
我们获得 150 的最大奖励,您可以通过超参数调整,网络调整以及使用折扣率和探索率的速率衰减来改进:
Policy:policy_q_nn, Min reward:8.0, Max reward:150.0, Average reward:41.27
我们在每一步计算和训练模型;您可能希望在剧集之后探索将其更改为训练。此外,您可以更改代码以丢弃内存重放,并为返回较小奖励的剧集再训练模型。但是,请谨慎实现此选项,因为它可能会减慢您的学习速度,因为初始游戏会更频繁地产生较小的奖励。
总结
在本章中,我们学习了如何在 Keras 中实现强化学习算法。为了保持示例的简单,我们使用了 Keras;您也可以使用 TensorFlow 实现相同的网络和模型。我们只使用了单层 MLP,因为我们的示例游戏非常简单,但对于复杂的示例,您最终可能会使用复杂的 CNN,RNN 或序列到序列模型。
我们还了解了 OpenAI Gym,这是一个框架,提供了一个模拟许多流行游戏的环境,以实现和实践强化学习算法。我们谈到了深层强化学习概念,我们鼓励您探索专门写有关强化学习的书籍,以深入学习理论和概念。
强化学习是一种先进的技术,你会发现它常用于解决复杂的问题。在下一章中,我们将学习另一系列先进的深度学习技术:生成对抗网络。
十四、生成对抗网络
生成模型被训练以生成与他们训练的数据类似的更多数据,并且训练对抗模型以通过提供对抗性示例来区分真实数据和假数据。
生成对抗网络(GAN)结合了两种模型的特征。 GAN 有两个组成部分:
- 生成模型,用于学习如何生成类似数据的
- 判别模型,用于学习如何区分真实数据和生成数据(来自生成模型)
GAN 已成功应用于各种复杂问题,例如:
- 从低分辨率图像生成照片般逼真的高分辨率图像
- 在文本中合成图像
- 风格迁移
- 补全不完整的图像和视频
在本章中,我们将学习以下主题,以学习如何在 TensorFlow 和 Keras 中实现 GAN:
- 生成对抗网络
- TensorFlow 中的简单 GAN
- Keras 中的简单 GAN
- TensorFlow 和 Keras 中的深度卷积 GAN
生成对抗网络 101
如下图所示,生成对抗网络(通常称为 GAN)有两个同步工作模型,用于学习和训练复杂数据,如图像,视频或音频文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFlcI7Yc-1681566540591)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/51604132-6071-4e6b-80d6-4515efac6085.png)]
直观地,生成器模型从随机噪声开始生成数据,但是慢慢地学习如何生成更真实的数据。生成器输出和实际数据被馈送到判别器,该判别器学习如何区分假数据和真实数据。
因此,生成器和判别器都发挥对抗性游戏,其中生成器试图通过生成尽可能真实的数据来欺骗判别器,并且判别器试图不通过从真实数据中识别伪数据而被欺骗,因此判别器试图最小化分类损失。两个模型都以锁步方式进行训练。
在数学上,生成模型G(z)
学习概率分布p(z)
,使得判别器D(G(z), x)
无法在概率分布p(z)
和p(x)
之间进行识别。 GAN 的目标函数可以通过下面描述值函数V
的等式来描述,(来自此链接):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gCtmk8H4-1681566540591)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/db26da3f-bcb8-4519-99e4-ef18c9edfa57.png)]
可以在此链接中找到 IAN Goodfellow 在 NIPS 2016 上关于 GAN 的开创性教程。
这个描述代表了一个简单的 GAN(在文献中也称为香草 GAN),由 Goodfellow 在此链接提供的开创性论文中首次介绍。从那时起,在基于 GAN 推导不同架构并将其应用于不同应用领域方面进行了大量研究。
例如,在条件 GAN 中,为生成器和判别器网络提供标签,使得条件 GAN 的目标函数可以通过以下描述值函数V
的等式来描述:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1K2B0R0Z-1681566540591)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/fb6f5df6-c343-405f-bd0c-28f5a7b5051c.png)]
应用中使用的其他几种衍生产品及其原始论文,如文本到图像,图像合成,图像标记,样式转移和图像转移等,如下表所示:
GAN 衍生物 | 原始文件 | 示例应用 |
StackGAN | https://arxiv.org/abs/1710.10916 | 文字到图像 |
StackGAN++ | https://arxiv.org/abs/1612.03242 | 逼真的图像合成 |
DCGAN | https://arxiv.org/abs/1511.06434 | 图像合成 |
HR-DCGAN | https://arxiv.org/abs/1711.06491 | 高分辨率图像合成 |
条件 GAN | https://arxiv.org/abs/1411.1784 | 图像标记 |
InfoGAN | https://arxiv.org/abs/1606.03657 | 风格识别 |
Wasserstein GAN | https://arxiv.org/abs/1701.07875https://arxiv.org/abs/1704.00028 | 图像生成 |
耦合 GAN | https://arxiv.org/abs/1606.07536 | 图像转换,域适应 |
BEGAN | https://arxiv.org/abs/1703.10717 | 图像生成 |
DiscoGAN | https://arxiv.org/abs/1703.05192 | 风格迁移 |
CycleGAN | https://arxiv.org/abs/1703.10593 | 风格迁移 |
让我们练习使用 MNIST 数据集创建一个简单的 GAN。在本练习中,我们将使用以下函数将 MNIST 数据集标准化为介于[-1, +1]
之间:
def norm(x): return (x-0.5)/0.5
我们还定义了 256 维的随机噪声,用于测试生成器模型:
n_z = 256 z_test = np.random.uniform(-1.0,1.0,size=[8,n_z])
显示将在本章所有示例中使用的生成图像的函数:
def display_images(images): for i in range(images.shape[0]): plt.subplot(1, 8, i + 1) plt.imshow(images[i]) plt.axis('off') plt.tight_layout() plt.show()
建立和训练 GAN 的最佳实践
对于我们为此演示选择的数据集,判别器在对真实和假图像进行分类方面变得非常擅长,因此没有为生成器提供梯度方面的大量反馈。因此,我们必须通过以下最佳实践使判别器变弱:
- 判别器的学习率保持远高于生成器的学习率。
- 判别器的优化器是
GradientDescent
,生成器的优化器是Adam
。 - 判别器具有丢弃正则化,而生成器则没有。
- 与生成器相比,判别器具有更少的层和更少的神经元。
- 生成器的输出是
tanh
,而判别器的输出是 sigmoid。 - 在 Keras 模型中,对于实际数据的标签,我们使用 0.9 而不是 1.0 的值,对于伪数据的标签,我们使用 0.1 而不是 0.0,以便在标签中引入一点噪声
欢迎您探索并尝试其他最佳实践。
TensorFlow 中的简单的 GAN
您可以按照 Jupyter 笔记本中的代码ch-14a_SimpleGAN
。
为了使用 TensorFlow 构建 GAN,我们使用以下步骤构建三个网络,两个判别器模型和一个生成器模型:
- 首先添加用于定义网络的超参数:
# graph hyperparameters g_learning_rate = 0.00001 d_learning_rate = 0.01 n_x = 784 # number of pixels in the MNIST image # number of hidden layers for generator and discriminator g_n_layers = 3 d_n_layers = 1 # neurons in each hidden layer g_n_neurons = [256, 512, 1024] d_n_neurons = [256] # define parameter ditionary d_params = {} g_params = {} activation = tf.nn.leaky_relu w_initializer = tf.glorot_uniform_initializer b_initializer = tf.zeros_initializer
- 接下来,定义生成器网络:
z_p = tf.placeholder(dtype=tf.float32, name='z_p', shape=[None, n_z]) layer = z_p # add generator network weights, biases and layers with tf.variable_scope('g'): for i in range(0, g_n_layers): w_name = 'w_{0:04d}'.format(i) g_params[w_name] = tf.get_variable( name=w_name, shape=[n_z if i == 0 else g_n_neurons[i - 1], g_n_neurons[i]], initializer=w_initializer()) b_name = 'b_{0:04d}'.format(i) g_params[b_name] = tf.get_variable( name=b_name, shape=[g_n_neurons[i]], initializer=b_initializer()) layer = activation( tf.matmul(layer, g_params[w_name]) + g_params[b_name]) # output (logit) layer i = g_n_layers w_name = 'w_{0:04d}'.format(i) g_params[w_name] = tf.get_variable( name=w_name, shape=[g_n_neurons[i - 1], n_x], initializer=w_initializer()) b_name = 'b_{0:04d}'.format(i) g_params[b_name] = tf.get_variable( name=b_name, shape=[n_x], initializer=b_initializer()) g_logit = tf.matmul(layer, g_params[w_name]) + g_params[b_name] g_model = tf.nn.tanh(g_logit)
- 接下来,定义我们将构建的两个判别器网络的权重和偏差:
with tf.variable_scope('d'): for i in range(0, d_n_layers): w_name = 'w_{0:04d}'.format(i) d_params[w_name] = tf.get_variable( name=w_name, shape=[n_x if i == 0 else d_n_neurons[i - 1], d_n_neurons[i]], initializer=w_initializer()) b_name = 'b_{0:04d}'.format(i) d_params[b_name] = tf.get_variable( name=b_name, shape=[d_n_neurons[i]], initializer=b_initializer()) #output (logit) layer i = d_n_layers w_name = 'w_{0:04d}'.format(i) d_params[w_name] = tf.get_variable( name=w_name, shape=[d_n_neurons[i - 1], 1], initializer=w_initializer()) b_name = 'b_{0:04d}'.format(i) d_params[b_name] = tf.get_variable( name=b_name, shape=[1], initializer=b_initializer())
- 现在使用这些参数,构建将真实图像作为输入并输出分类的判别器:
# define discriminator_real # input real images x_p = tf.placeholder(dtype=tf.float32, name='x_p', shape=[None, n_x]) layer = x_p with tf.variable_scope('d'): for i in range(0, d_n_layers): w_name = 'w_{0:04d}'.format(i) b_name = 'b_{0:04d}'.format(i) layer = activation( tf.matmul(layer, d_params[w_name]) + d_params[b_name]) layer = tf.nn.dropout(layer,0.7) #output (logit) layer i = d_n_layers w_name = 'w_{0:04d}'.format(i) b_name = 'b_{0:04d}'.format(i) d_logit_real = tf.matmul(layer, d_params[w_name]) + d_params[b_name] d_model_real = tf.nn.sigmoid(d_logit_real)
- 接下来,使用相同的参数构建另一个判别器网络,但提供生成器的输出作为输入:
# define discriminator_fake # input generated fake images z = g_model layer = z with tf.variable_scope('d'): for i in range(0, d_n_layers): w_name = 'w_{0:04d}'.format(i) b_name = 'b_{0:04d}'.format(i) layer = activation( tf.matmul(layer, d_params[w_name]) + d_params[b_name]) layer = tf.nn.dropout(layer,0.7) #output (logit) layer i = d_n_layers w_name = 'w_{0:04d}'.format(i) b_name = 'b_{0:04d}'.format(i) d_logit_fake = tf.matmul(layer, d_params[w_name]) + d_params[b_name] d_model_fake = tf.nn.sigmoid(d_logit_fake)
- 现在我们已经建立了三个网络,它们之间的连接是使用损失,优化器和训练函数完成的。在训练生成器时,我们只训练生成器的参数,在训练判别器时,我们只训练判别器的参数。我们使用
var_list
参数将此指定给优化器的minimize()
函数。以下是为两种网络定义损失,优化器和训练函数的完整代码:
g_loss = -tf.reduce_mean(tf.log(d_model_fake)) d_loss = -tf.reduce_mean(tf.log(d_model_real) + tf.log(1 - d_model_fake)) g_optimizer = tf.train.AdamOptimizer(g_learning_rate) d_optimizer = tf.train.GradientDescentOptimizer(d_learning_rate) g_train_op = g_optimizer.minimize(g_loss, var_list=list(g_params.values())) d_train_op = d_optimizer.minimize(d_loss, var_list=list(d_params.values()))
- 现在我们已经定义了模型,我们必须训练模型。训练按照以下算法完成:
For each epoch: For each batch: get real images x_batch generate noise z_batch train discriminator using z_batch and x_batch generate noise z_batch train generator using z_batch
笔记本电脑的完整训练代码如下:
n_epochs = 400 batch_size = 100 n_batches = int(mnist.train.num_examples / batch_size) n_epochs_print = 50 with tf.Session() as tfs: tfs.run(tf.global_variables_initializer()) for epoch in range(n_epochs): epoch_d_loss = 0.0 epoch_g_loss = 0.0 for batch in range(n_batches): x_batch, _ = mnist.train.next_batch(batch_size) x_batch = norm(x_batch) z_batch = np.random.uniform(-1.0,1.0,size=[batch_size,n_z]) feed_dict = {x_p: x_batch,z_p: z_batch} _,batch_d_loss = tfs.run([d_train_op,d_loss], feed_dict=feed_dict) z_batch = np.random.uniform(-1.0,1.0,size=[batch_size,n_z]) feed_dict={z_p: z_batch} _,batch_g_loss = tfs.run([g_train_op,g_loss], feed_dict=feed_dict) epoch_d_loss += batch_d_loss epoch_g_loss += batch_g_loss if epoch%n_epochs_print == 0: average_d_loss = epoch_d_loss / n_batches average_g_loss = epoch_g_loss / n_batches print('epoch: {0:04d} d_loss = {1:0.6f} g_loss = {2:0.6f}' .format(epoch,average_d_loss,average_g_loss)) # predict images using generator model trained x_pred = tfs.run(g_model,feed_dict={z_p:z_test}) display_images(x_pred.reshape(-1,pixel_size,pixel_size))
我们每 50 个周期印刷生成的图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yPE3BLWZ-1681566540592)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/mastering-tf-1x-zh/img/bd3dca6c-f38a-4de9-b6a2-6dd6c1722938.png)]
正如我们所看到的那样,生成器在周期 0 中只产生噪声,但是在周期 350 中,它经过训练可以产生更好的手写数字形状。您可以尝试使用周期,正则化,网络架构和其他超参数进行试验,看看是否可以产生更快更好的结果。
Keras 中的简单的 GAN
您可以按照 Jupyter 笔记本中的代码ch-14a_SimpleGAN
。
现在让我们在 Keras 实现相同的模型:
- 超参数定义与上一节保持一致:
# graph hyperparameters g_learning_rate = 0.00001 d_learning_rate = 0.01 n_x = 784 # number of pixels in the MNIST image # number of hidden layers for generator and discriminator g_n_layers = 3 d_n_layers = 1 # neurons in each hidden layer g_n_neurons = [256, 512, 1024] d_n_neurons = [256]
- 接下来,定义生成器网络:
# define generator g_model = Sequential() g_model.add(Dense(units=g_n_neurons[0], input_shape=(n_z,), name='g_0')) g_model.add(LeakyReLU()) for i in range(1,g_n_layers): g_model.add(Dense(units=g_n_neurons[i], name='g_{}'.format(i) )) g_model.add(LeakyReLU()) g_model.add(Dense(units=n_x, activation='tanh',name='g_out')) print('Generator:') g_model.summary() g_model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=g_learning_rate) )
这就是生成器模型的样子:
Generator: _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= g_0 (Dense) (None, 256) 65792 _________________________________________________________________ leaky_re_lu_1 (LeakyReLU) (None, 256) 0 _________________________________________________________________ g_1 (Dense) (None, 512) 131584 _________________________________________________________________ leaky_re_lu_2 (LeakyReLU) (None, 512) 0 _________________________________________________________________ g_2 (Dense) (None, 1024) 525312 _________________________________________________________________ leaky_re_lu_3 (LeakyReLU) (None, 1024) 0 _________________________________________________________________ g_out (Dense) (None, 784) 803600 ================================================================= Total params: 1,526,288 Trainable params: 1,526,288 Non-trainable params: 0 _________________________________________________________________
- 在 Keras 示例中,我们没有定义两个判别器网络,就像我们在 TensorFlow 示例中定义的那样。相反,我们定义一个判别器网络,然后将生成器和判别器网络缝合到 GAN 网络中。然后,GAN 网络仅用于训练生成器参数,判别器网络用于训练判别器参数:
# define discriminator d_model = Sequential() d_model.add(Dense(units=d_n_neurons[0], input_shape=(n_x,), name='d_0' )) d_model.add(LeakyReLU()) d_model.add(Dropout(0.3)) for i in range(1,d_n_layers): d_model.add(Dense(units=d_n_neurons[i], name='d_{}'.format(i) )) d_model.add(LeakyReLU()) d_model.add(Dropout(0.3)) d_model.add(Dense(units=1, activation='sigmoid',name='d_out')) print('Discriminator:') d_model.summary() d_model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.SGD(lr=d_learning_rate) )
这是判别器模型的外观:
Discriminator: _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= d_0 (Dense) (None, 256) 200960 _________________________________________________________________ leaky_re_lu_4 (LeakyReLU) (None, 256) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 256) 0 _________________________________________________________________ d_out (Dense) (None, 1) 257 ================================================================= Total params: 201,217 Trainable params: 201,217 Non-trainable params: 0 _________________________________________________________________
- 接下来,定义 GAN 网络,并将判别器模型的可训练属性转换为
false
,因为 GAN 仅用于训练生成器:
# define GAN network d_model.trainable=False z_in = Input(shape=(n_z,),name='z_in') x_in = g_model(z_in) gan_out = d_model(x_in) gan_model = Model(inputs=z_in,outputs=gan_out,name='gan') print('GAN:') gan_model.summary()
gan_model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=g_learning_rate) )
这就是 GAN 模型的样子:
GAN: _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= z_in (InputLayer) (None, 256) 0 _________________________________________________________________ sequential_1 (Sequential) (None, 784) 1526288 _________________________________________________________________ sequential_2 (Sequential) (None, 1) 201217 ================================================================= Total params: 1,727,505 Trainable params: 1,526,288 Non-trainable params: 201,217 _________________________________________________________________
- 太好了,现在我们已经定义了三个模型,我们必须训练模型。训练按照以下算法进行:
For each epoch: For each batch: get real images x_batch generate noise z_batch generate images g_batch using generator model combine g_batch and x_batch into x_in and create labels y_out set discriminator model as trainable train discriminator using x_in and y_out generate noise z_batch set x_in = z_batch and labels y_out = 1 set discriminator model as non-trainable train gan model using x_in and y_out, (effectively training generator model)
为了设置标签,我们分别对真实和假图像应用标签 0.9 和 0.1。通常,建议您使用标签平滑,通过为假数据选择 0.0 到 0.3 的随机值,为实际数据选择 0.8 到 1.0。
以下是笔记本电脑训练的完整代码:
n_epochs = 400 batch_size = 100 n_batches = int(mnist.train.num_examples / batch_size) n_epochs_print = 50 for epoch in range(n_epochs+1): epoch_d_loss = 0.0 epoch_g_loss = 0.0 for batch in range(n_batches): x_batch, _ = mnist.train.next_batch(batch_size) x_batch = norm(x_batch) z_batch = np.random.uniform(-1.0,1.0,size=[batch_size,n_z]) g_batch = g_model.predict(z_batch) x_in = np.concatenate([x_batch,g_batch]) y_out = np.ones(batch_size*2) y_out[:batch_size]=0.9 y_out[batch_size:]=0.1 d_model.trainable=True batch_d_loss = d_model.train_on_batch(x_in,y_out) z_batch = np.random.uniform(-1.0,1.0,size=[batch_size,n_z]) x_in=z_batch y_out = np.ones(batch_size) d_model.trainable=False batch_g_loss = gan_model.train_on_batch(x_in,y_out) epoch_d_loss += batch_d_loss epoch_g_loss += batch_g_loss if epoch%n_epochs_print == 0: average_d_loss = epoch_d_loss / n_batches average_g_loss = epoch_g_loss / n_batches print('epoch: {0:04d} d_loss = {1:0.6f} g_loss = {2:0.6f}' .format(epoch,average_d_loss,average_g_loss)) # predict images using generator model trained x_pred = g_model.predict(z_test) display_images(x_pred.reshape(-1,pixel_size,pixel_size))
精通 TensorFlow 1.x:11~15(5)https://developer.aliyun.com/article/1426828