听说GAN很高大上,其实就这么简单!

简介:

本文使用的tensorflow版本:1.4

tensorflow安装:pip install tensorflow

1、先来目睹一下效果吧

这篇文章讲解了如何使用GAN来生成我们的手写数字,我们首先来看看生成的效果吧:
10轮:

331c3cc4ba2a21d5f1f2ed4be9266d0828cca841

50轮:

f6f2bf1a18c37a5e92a0891d14327a6940a78d76

100轮:

3b5ae7395e94f1a095cacf9b66c51dd6c10b56d8

200轮:

5b46fcfdb574e3164fd1bf30bed596bfd7e5d6e5

可以看到,在10轮的时候,我们的Generator生成的图片非常模糊,几乎是无法用肉眼来分别数字的,到了第50轮的时候,已经初见雏形了,有一些数字比如1、4、5这些都可以很清楚的分辨出来,不过还并不是十分完美。到了100轮的时候,像数字8和9这些也基本能准确的生成了,而到200轮的时候,除去个别的以外,基本上都能正确的手写出来了,由于时间的原因,没有继续训练下去,如果大家感兴趣,可以训练更多轮,看看更好的效果。

2、思路解析

设定参数

本文设定的参数是,图片的大小是28*28,这是mnist图片的标准大小,后面是一些保存模型的设定。我们总共的训练轮数是500轮。在我们的Generator和Discriminator中,我们设定的是一个简单的有两层隐藏层的全链接神经网络。对于Generator来说,输入的的大小是[batch_size,z_size],第一个隐藏层有150个神经元,第二个隐藏层有300个神经元,输出的大小就是图片的size28*28。而对于Discriminator来说,我们的输入的大小是[batch_size * 2,img_size],因为我们要掺杂真实的img和Generator生成的img。第一个隐藏层有300个神经元,第二个隐藏层有150个神经元,输出层只有1个数,表示该图片为真实图片的概率。关于神经网络的结构我们会在后面详细讲解。

 

img_height = 28 img_width = 28 img_size = img_height * img_width
to_train = Trueto_restore = Falseoutput_path = "output"# 总迭代次数500max_epoch = 500h1_size = 150h2_size = 300z_size = 100batch_size = 256

创建Generator

刚才也讲到了,对于Generator来说,输入的的大小是[batch_size,z_size],第一个隐藏层有150个神经元,第二个隐藏层有300个神经元,输出的大小就是图片的size28*28。总的来说,经过Generator,由[batch_size,z_size] 变为 [batch_size,img_size]

 

# generate (model 1) def build_generator(z_prior):
w1 = tf.Variable(tf.truncated_normal([z_size, h1_size], stddev=0.1), name="g_w1", dtype=tf.float32)
b1 = tf.Variable(tf.zeros([h1_size]), name="g_b1", dtype=tf.float32)
h1 = tf.nn.relu(tf.matmul(z_prior, w1) + b1)
w2 = tf.Variable(tf.truncated_normal([h1_size, h2_size], stddev=0.1), name="g_w2", dtype=tf.float32)
b2 = tf.Variable(tf.zeros([h2_size]), name="g_b2", dtype=tf.float32)
h2 = tf.nn.relu(tf.matmul(h1, w2) + b2)
w3 = tf.Variable(tf.truncated_normal([h2_size, img_size], stddev=0.1), name="g_w3", dtype=tf.float32)
b3 = tf.Variable(tf.zeros([img_size]), name="g_b3", dtype=tf.float32)
h3 = tf.matmul(h2, w3) + b3
x_generate = tf.nn.tanh(h3)
g_params = [w1, b1, w2, b2, w3, b3] return x_generate, g_params

创建Discrminator

而对于Discriminator来说,我们的输入的大小是[batch_size * 2,img_size],因为我们要掺杂真实的img和Generator生成的img。第一个隐藏层有300个神经元,第二个隐藏层有150个神经元,输出层只有1个数,表示该图片为真实图片的概率。要注意,我们的输入和输出是严格对应的,所以对于输出成的输出h3来说,前batch_size个代表着对真实图片的判别概率,而后batch_size代表着对Generator生成的图片的判别概率。这里是用了一个tf.slice()函数,之前没有接触过,故在这里做一下记录:
1,函数原型 tf.slice(inputs,begin,size,name='')
2,用途:从inputs中抽取部分内容
inputs:可以是list,array,tensor
begin:n维列表,begin[i] 表示从inputs中第i维抽取数据时,相对0的起始偏移量,也就是从第i维的begin[i]开始抽取数据
size:n维列表,size[i]表示要抽取的第i维元素的数目
所以可以看到,最终y_data保存的是真实图片的判别概率,这些值要越接近于1越好,而y_generated保存的是Generator生成的图片的判别概率,这些值要越接近于0越好。

 
def build_discriminator(x_data, x_generated, keep_prob): # tf.concat
x_in = tf.concat([x_data, x_generated], 0)
w1 = tf.Variable(tf.truncated_normal([img_size, h2_size], stddev=0.1), name="d_w1", dtype=tf.float32)
b1 = tf.Variable(tf.zeros([h2_size]), name="d_b1", dtype=tf.float32)
h1 = tf.nn.dropout(tf.nn.relu(tf.matmul(x_in, w1) + b1), keep_prob)
w2 = tf.Variable(tf.truncated_normal([h2_size, h1_size], stddev=0.1), name="d_w2", dtype=tf.float32)
b2 = tf.Variable(tf.zeros([h1_size]), name="d_b2", dtype=tf.float32)
h2 = tf.nn.dropout(tf.nn.relu(tf.matmul(h1, w2) + b2), keep_prob)
w3 = tf.Variable(tf.truncated_normal([h1_size, 1], stddev=0.1), name="d_w3", dtype=tf.float32)
b3 = tf.Variable(tf.zeros([1]), name="d_b3", dtype=tf.float32)
h3 = tf.matmul(h2, w3) + b3
y_data = tf.nn.sigmoid(tf.slice(h3, [0, 0], [batch_size, -1], name=None))
y_generated = tf.nn.sigmoid(tf.slice(h3, [batch_size, 0], [-1, -1], name=None))
d_params = [w1, b1, w2, b2, w3, b3] return y_data, y_generated, d_params

保存图片

关于保存图片的代码,我们这里就不讲了,这也不是重点,大家有兴趣的话可以研究下:

 

def show_result(batch_res, fname, grid_size=(8, 8), grid_pad= 5 ):
batch_res = 0.5 * batch_res.reshape((batch_res.shape[0], img_height, img_width)) + 0.5
img_h, img_w = batch_res.shape[1], batch_res.shape[2]
grid_h = img_h * grid_size[0] + grid_pad * (grid_size[0] - 1)
grid_w = img_w * grid_size[1] + grid_pad * (grid_size[1] - 1)
img_grid = np.zeros((grid_h, grid_w), dtype=np.uint8) for i, res in enumerate(batch_res): if i >= grid_size[0] * grid_size[1]: break
img = (res) * 255
img = img.astype(np.uint8)
row = (i // grid_size[0]) * (img_h + grid_pad)
col = (i % grid_size[1]) * (img_w + grid_pad)
img_grid[row:row + img_h, col:col + img_w] = img
imsave(fname, img_grid)

设定训练目标

对于Discriminator来说,他希望能够使二分类的结果越准确越好,即越能准确判别真实图片和Generator生成的图片越好,所以我们这里使用类似于逻辑回归中的损失函数,而对于Generator来说,它希望的是Discriminator无法分辨它生成的图片,所以它希望Discriminator能将它生成的图片越多的分类为真实图片,所以我们设定的训练目标如下:

 

# 损失函数的设置
d_loss = - (tf.log(y_data) + tf.log(1 - y_generated))
g_loss = - tf.log(y_generated)
optimizer = tf.train.AdamOptimizer(0.0001)
# 两个模型的优化函数
d_trainer = optimizer.minimize(d_loss, var_list=d_params)
g_trainer = optimizer.minimize(g_loss, var_list=g_params)

训练

训练其实很简单,每次得到batch_size大小的样本,先训练一次Discriminator,然后再训练我们的Generator

 

z_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
steps = 60000 / batch_size for i in range(sess.run(global_step), max_epoch): for j in np.arange(steps): # for j in range(steps): print("epoch:%s, iter:%s" % (i, j)) # 每一步迭代,我们都会加载256个训练样本,然后执行一次train_step
x_value, _ = mnist.train.next_batch(batch_size)
x_value = 2 * x_value.astype(np.float32) - 1
z_value = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32) # 执行生成
sess.run(d_trainer,
feed_dict={x_data: x_value, z_prior: z_value, keep_prob: np.sum(0.7).astype(np.float32)}) # 执行判别 if j % 1 == 0:
sess.run(g_trainer,
feed_dict={x_data: x_value, z_prior: z_value, keep_prob: np.sum(0.7).astype(np.float32)})

3、完整代码

本文涉及到的完整代码如下:

 

import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import numpy as np from skimage.io import imsave import os import shutil
img_height = 28img_width = 28img_size = img_height * img_width
to_train = Trueto_restore = Falseoutput_path = "output"# 总迭代次数500max_epoch = 500h1_size = 150h2_size = 300z_size = 100batch_size = 256# generate (model 1)def build_generator(z_prior):
w1 = tf.Variable(tf.truncated_normal([z_size, h1_size], stddev=0.1), name="g_w1", dtype=tf.float32)
b1 = tf.Variable(tf.zeros([h1_size]), name="g_b1", dtype=tf.float32)
h1 = tf.nn.relu(tf.matmul(z_prior, w1) + b1)
w2 = tf.Variable(tf.truncated_normal([h1_size, h2_size], stddev=0.1), name="g_w2", dtype=tf.float32)
b2 = tf.Variable(tf.zeros([h2_size]), name="g_b2", dtype=tf.float32)
h2 = tf.nn.relu(tf.matmul(h1, w2) + b2)
w3 = tf.Variable(tf.truncated_normal([h2_size, img_size], stddev=0.1), name="g_w3", dtype=tf.float32)
b3 = tf.Variable(tf.zeros([img_size]), name="g_b3", dtype=tf.float32)
h3 = tf.matmul(h2, w3) + b3
x_generate = tf.nn.tanh(h3)
g_params = [w1, b1, w2, b2, w3, b3] return x_generate, g_params# discriminator (model 2)def build_discriminator(x_data, x_generated, keep_prob): # tf.concat
x_in = tf.concat([x_data, x_generated], 0)
w1 = tf.Variable(tf.truncated_normal([img_size, h2_size], stddev=0.1), name="d_w1", dtype=tf.float32)
b1 = tf.Variable(tf.zeros([h2_size]), name="d_b1", dtype=tf.float32)
h1 = tf.nn.dropout(tf.nn.relu(tf.matmul(x_in, w1) + b1), keep_prob)
w2 = tf.Variable(tf.truncated_normal([h2_size, h1_size], stddev=0.1), name="d_w2", dtype=tf.float32)
b2 = tf.Variable(tf.zeros([h1_size]), name="d_b2", dtype=tf.float32)
h2 = tf.nn.dropout(tf.nn.relu(tf.matmul(h1, w2) + b2), keep_prob)
w3 = tf.Variable(tf.truncated_normal([h1_size, 1], stddev=0.1), name="d_w3", dtype=tf.float32)
b3 = tf.Variable(tf.zeros([1]), name="d_b3", dtype=tf.float32)
h3 = tf.matmul(h2, w3) + b3
y_data = tf.nn.sigmoid(tf.slice(h3, [0, 0], [batch_size, -1], name=None))
y_generated = tf.nn.sigmoid(tf.slice(h3, [batch_size, 0], [-1, -1], name=None))
d_params = [w1, b1, w2, b2, w3, b3] return y_data, y_generated, d_params#def show_result(batch_res, fname, grid_size=(8, 8), grid_pad=5):
batch_res = 0.5 * batch_res.reshape((batch_res.shape[0], img_height, img_width)) + 0.5
img_h, img_w = batch_res.shape[1], batch_res.shape[2]
grid_h = img_h * grid_size[0] + grid_pad * (grid_size[0] - 1)
grid_w = img_w * grid_size[1] + grid_pad * (grid_size[1] - 1)
img_grid = np.zeros((grid_h, grid_w), dtype=np.uint8) for i, res in enumerate(batch_res): if i >= grid_size[0] * grid_size[1]: break
img = (res) * 255
img = img.astype(np.uint8)
row = (i // grid_size[0]) * (img_h + grid_pad)
col = (i % grid_size[1]) * (img_w + grid_pad)
img_grid[row:row + img_h, col:col + img_w] = img
imsave(fname, img_grid)def train(): # load data(mnist手写数据集)
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
x_data = tf.placeholder(tf.float32, [batch_size, img_size], name="x_data")
z_prior = tf.placeholder(tf.float32, [batch_size, z_size], name="z_prior")
keep_prob = tf.placeholder(tf.float32, name="keep_prob")
global_step = tf.Variable(0, name="global_step", trainable=False) # 创建生成模型
x_generated, g_params = build_generator(z_prior) # 创建判别模型
y_data, y_generated, d_params = build_discriminator(x_data, x_generated, keep_prob) # 损失函数的设置
d_loss = - (tf.log(y_data) + tf.log(1 - y_generated))
g_loss = - tf.log(y_generated)
optimizer = tf.train.AdamOptimizer(0.0001) # 两个模型的优化函数
d_trainer = optimizer.minimize(d_loss, var_list=d_params)
g_trainer = optimizer.minimize(g_loss, var_list=g_params)
saver = tf.train.Saver() # 启动默认图
init = tf.global_variables_initializer()
sess = tf.Session() # 初始化
sess.run(init) if to_restore:
chkpt_fname = tf.train.latest_checkpoint(output_path)
saver.restore(sess, chkpt_fname) else: if os.path.exists(output_path):
shutil.rmtree(output_path)
z_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
os.mkdir(output_path)

steps = 60000 / batch_size for i in range(sess.run(global_step), max_epoch): for j in np.arange(steps): # for j in range(steps):
print("epoch:%s, iter:%s" % (i, j)) # 每一步迭代,我们都会加载256个训练样本,然后执行一次train_step
x_value, _ = mnist.train.next_batch(batch_size)
x_value = 2 * x_value.astype(np.float32) - 1
z_value = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32) # 执行生成
sess.run(d_trainer,
feed_dict={x_data: x_value, z_prior: z_value, keep_prob: np.sum(0.7).astype(np.float32)}) # 执行判别 if j % 1 == 0:
sess.run(g_trainer,
feed_dict={x_data: x_value, z_prior: z_value, keep_prob: np.sum(0.7).astype(np.float32)})
x_gen_val = sess.run(x_generated, feed_dict={z_prior: z_sample_val})
show_result(x_gen_val, "output/sample{0}.jpg".format(i))
z_random_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
x_gen_val = sess.run(x_generated, feed_dict={z_prior: z_random_sample_val})
show_result(x_gen_val, "output/random_sample{0}.jpg".format(i))
sess.run(tf.assign(global_step, i + 1))
saver.save(sess, os.path.join(output_path, "model"), global_step=global_step)if __name__ == '__main__':
train()


原文发布时间为:2018-08-23
本文作者:文文
本文来自云栖社区合作伙伴“ Python爱好者社区”,了解相关信息可以关注“ Python爱好者社区”。
相关文章
|
存储 SQL 缓存
StarRocks常见面试问题(一)
StarRocks常见面试问题(一)
|
JSON 前端开发 数据格式
12306火车票查询--Python可以这么玩!!!
12306火车票查询--Python可以这么玩!!!
|
23天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9371 30
|
9天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
8天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
639 1
|
21天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2480 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
4天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
414 0
|
3天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
161 120