【TensorFlow-windows】学习笔记七——生成对抗网络

前言

既然学习了变分自编码(VAE),那也必须来一波生成对抗网络(GAN)。

国际惯例,参考网址:

论文: Generative Adversarial Nets

PPT:Generative Adversarial Networks (GANs)

Generative Adversarial Nets in TensorFlow

GAN原理学习笔记
GAN — Ways to improve GAN performance

理论

粗略点的讲法就说:一个生成器GGG,一个判别器DDD,前者用来将噪声输入转换成图片,后者判别当前输入图片是真实的还是生成的。

为了更详细地了解GAN,还是对论文进行简要的组织、理解吧。有兴趣直接看原始论文,这里进行部分关键内容的摘抄。

任意的GGGDDD函数空间都存在特定解,GGG要能表示训练集的分布,而DDD一定是等于12\frac{1}{2}21,也就是说判别器无法分辨当前输入是真的还是假的,这样就达到了鱼目混珠的效果。在GAN中,使用多层感知器构建GGGDDD,整个模型可以使用反向传播算法学习。

论文里面有一句很好的话解释了GAN的动机:目前深度学习在判别模型的设计中取得了重大成功,但是在生成模型却鲜有成效,主要原因在于在极大似然估计和相关策略中有很多难以解决的概率计算难题(想想前一篇博客的变分自编码的理论,阔怕),而且丢失了生成背景下的分段线性单元的优势,因此作者就提出了新的生成模型估计方法,避开这些难题,也就是传说中的GAN。它的训练完全不需要什么鬼似然估计,只需要使用目前炒鸡成功的反传和dropout算法。

为了让生成器学到数据分布pgp_gpg,需要定义一个先验的噪声输入pz(z)p_z(z)pz(z),然后使用G(z;θg)G(z;\theta_g)G(z;θg)将其映射到数据空间,这里的GGG是具有参数θg\theta_gθg的多层感知器。然后定义另一个多层感知器D(x;θd)D(x;\theta_d)D(x;θd),输出一个标量。D(x)D(x)D(x)代表的是xxx来自于真实样本而非生成的样本pgp_gpg的概率,我们训练DDD去最大化将正确标签同时赋予训练集和GGG生成的样本的概率,也就是DDD把真的和假的图片都当成真的了。同时要去训练GGG去最小化log⁡(1−D(G(z)))\log (1-D(G(z)))log(1D(G(z))),是为了让生成的图片被赋予正样本标签的概率大点,损失函数就是:
min⁡Gmax⁡DV(D,G)=Ex∼pdata(x)[log⁡D(x)]+Ez∼pz(z)[log⁡(1−D(G(z)))]\min_G \max_D V(D,G)=E_{x\sim p_{data}(x)}[\log D(x)]+E_{z\sim p_z(z)}[\log(1-D(G(z)))] GminDmaxV(D,G)=Expdata(x)[logD(x)]+Ezpz(z)[log(1D(G(z)))]
在优化DDD的时候,在训练的内循环中是无法完成的,计算上不允许,并且在有限数据集上会导致过拟合,因此可以以k:1k:1k:1 的训练次数比例分别优化DDDGGG。这能够让DDD保持在最优解附近,只要GGG变化比较缓慢。

而且在实际中,上式可能无法提供足够的梯度让GGG很好地学习,在训练早期,当GGG很差的时候,DDD能够以很高的概率将其归为负样本,因为生成的数据与训练数据差距很大,这样log⁡(1−D(G(z)))\log(1-D(G(z)))log(1D(G(z)))就饱和了,与其说最小化log⁡(1−D(G(z)))\log(1-D(G(z)))log(1D(G(z)))不如去最大化log⁡(D(G(z)))\log(D(G(z)))log(D(G(z))),这个目标函数对GGGDDD的收敛目标不变,但是能早期学习具有更强的梯度。

训练算法:

外层一个大循环就不说了,对所有的批数据迭代,内层有一个小循环,控制上面说的判别器DDD与生成器GGG的训练比例为k:1k:1k:1的:

  • 以下步骤执行kkk次:

    • 从噪声先验pg(z)p_g(z)pg(z)中采样mmm个噪声样本{z(1),⋯ ,z(m)}\{z^{(1)},\cdots,z^{(m)}\}{z(1),,z(m)}

    • 从原始样本分布pdata(x)p_{data}(x)pdata(x)中选取mmm个样本x(1)⋯x(m){x^{(1)}\cdots x^{(m)}}x(1)x(m),说这么复杂,原始样本的分布不就是原始样本么,直接从原始样本里面选一批数据就行了

    • 更新判别器
      ∇θd1m∑i=1m[log⁡D(x(i))+log⁡(1−D(G(z(i))))]\nabla_{\theta_d}\frac{1}{m}\sum_{i=1}^m \left[\log D\left(x^{(i)}\right)+\log \left(1-D\left(G\left(z^{(i)}\right)\right)\right)\right] θdm1i=1m[logD(x(i))+log(1D(G(z(i))))]

  • 从噪声先验pg(z)p_g(z)pg(z)中采样mmm个噪声样本{z(1),⋯ ,z(m)}\{z^{(1)},\cdots,z^{(m)}\}{z(1),,z(m)}

  • 更新生成器
    ∇θg1m∑i=1mlog⁡(1−D(G(z(i))))\nabla \theta_g \frac{1}{m}\sum_{i=1}^m \log\left(1-D\left(G \left( z^{(i)}\right) \right)\right) θgm1i=1mlog(1D(G(z(i))))

后面作者又证明了两个内容:

  • pg=pdatap_g=p_{data}pg=pdata的全局最优
  • 训练算法的收敛性

身为一个合格的程序猿,还是很有必要看看数学推导的o(╯□╰)o虽然不一定能懂

先看一个简单的式子:y→alog⁡(y)+blog⁡(1−y)y\to a\log(y)+b\log(1-y)yalog(y)+blog(1y),这个式子在[0,1][0,1][0,1]范围取得最大值的点是在aa+b\frac{a}{a+b}a+ba,证明很简单,直接两边求导,令y′=0y'=0y=0就能算出来。

再看看我们的优化目标,当给定生成器GGG的时候,也就是GGG固定的时候:
V(G,D)=∫xpdata(x)log⁡D(x)dx+∫zpz(z)log⁡(1−D(g(z)))dz=∫xpdata(x)log⁡(D(x))+pg(x)log⁡(1−D(x))dxV(G,D)=\int_x p_{data}(x)\log D(x)dx+\int _zp_z(z)\log(1-D(g(z)))dz\\ =\int _xp_{data}(x)\log(D(x))+p_g(x)\log(1-D(x))dx V(G,D)=xpdata(x)logD(x)dx+zpz(z)log(1D(g(z)))dz=xpdata(x)log(D(x))+pg(x)log(1D(x))dx
长得挺像,那么DDD的最优解就是:
DG∗(x)=pdata(x)pdata(x)+pg(x)D_G^*(x)=\frac{p_{data}(x)}{p_{data}(x)+p_g(x)} DG(x)=pdata(x)+pg(x)pdata(x)
DDD的训练目标可以看成最大化对数似然去估算条件概率P(Y=y∣x)P(Y=y|x)P(Y=yx),这里YYY表示xxx来自于原始数据pdatap_{data}pdata(此时y=1y=1y=1)还是生成数据pgp_gpg(此时y=0y=0y=0),所以损失函数又可以写成:
C(G)=max⁡DV(G,D)=Ex∼pdata[log⁡DG∗(x)]+Ez∼pz[log⁡(1−DG∗(G(z)))]=Ex∼pdata[log⁡DG∗(x)]+Ez∼pg[log⁡(1−DG∗(x))]=Ex∼pdata[log⁡pdata(x)pdata(x)+pg(x)]+Ex∼pg[log⁡pg(x)pdata(x)+pg(x)]\begin{aligned} C(G)&=\max_D V(G,D)\\ &=E_{x\sim p_{data}}\left[\log D_G^*(x)\right]+E_{z\sim p_z}\left[\log(1-D^*_G(G(z)))\right]\\ &=E_{x\sim p_{data}}\left[\log D_G^*(x)\right]+E_{z\sim p_g}\left[\log(1-D^*_G(x))\right]\\ &=E_{x\sim p_{data}}\left[\log\frac{p_{data}(x)}{p_{data}(x)+p_g(x)}\right]+E_{x\sim p_g}\left[\log\frac{p_g(x)}{p_{data}(x)+p_g(x)}\right] \end{aligned} C(G)=DmaxV(G,D)=Expdata[logDG(x)]+Ezpz[log(1DG(G(z)))]=Expdata[logDG(x)]+Ezpg[log(1DG(x))]=Expdata[logpdata(x)+pg(x)pdata(x)]+Expg[logpdata(x)+pg(x)pg(x)]
理论1 当且仅当pg=pdatap_g=p_{data}pg=pdata的时候,训练目标C(G)C(G)C(G)达到全局最小,此时,C(G)C(G)C(G)收敛到值−log⁡4-\log 4log4

证明:当pg=pdatap_g=p_{data}pg=pdata的时候,DG∗(x)=12D^*_G(x)=\frac{1}{2}DG(x)=21,因此C(G)=log⁡12+log⁡12=−log⁡4C(G)=\log \frac{1}{2}+\log\frac{1}{2}=-\log 4C(G)=log21+log21=log4,感觉正常应该是:
Ex∼pdata[−log⁡2]+Ex∼pg[−log⁡2]E_{x\sim p_{data}}[-\log 2]+E_{x\sim p_g}[-\log 2] Expdata[log2]+Expg[log2]
但是作者貌似让Ex∼pdata=Ex∼pg=1E_{x\sim p_{data}}=E_{x\sim p_g}=1Expdata=Expg=1了,我估计是因为收敛到最终解的时候,理想状态是判别器无法分辨哪个真哪个假,所以都当成正样本了。这样还能将C(G)C(G)C(G)变形:
C(G)=−log⁡4+KL(pdata∥pdata+pg2)+KL(pg∥pdata+pg2)C(G)=-\log 4+KL\left(p_{data}\parallel\frac{p_{data}+p_g}{2}\right)+KL\left(p_g\parallel \frac{p_{data}+p_g}{2}\right) C(G)=log4+KL(pdata2pdata+pg)+KL(pg2pdata+pg)
其实最终理想状态下后面两个KL距离是等于0的,代表衡量的两个分布一样。

这里作者提到了一个表达式称为Jensen-Shannon divergence,衡量模型分布和数据生成过程:
C(G)=−log⁡4+2⋅JSD(pdata∥pg)C(G)=-\log 4+2\cdot JSD(p_{data}\parallel p_g) C(G)=log4+2JSD(pdatapg)
这个JSDJSDJSD始终是非负的,当且仅当pdata=pgp_{data}=p_gpdata=pg的时候取000,意思就是生成模型能够完美生成数据分布。

接下来看看算法收敛性

理论2 如果GGGDDD有足够的容量,并且在训练算法的每一步,给定GGG时判别器都能达到它的最优,那么pgp_gpg的更新便可以通过优化
Ex∼pdata[log⁡DG∗(x)]+Ex∼pg[log⁡(1−DG∗(x))]E_{x\sim p_{data}}\left[\log D_G^*(x)\right]+E_{x\sim p_g}\left[\log (1-D_G^*(x))\right] Expdata[logDG(x)]+Expg[log(1DG(x))]
然后pgp_gpg就收敛到了pdatap_{data}pdata

证明就不看了,因为我不是特别懂作者那一段文字,我们只需要知道收敛结果就是pg=pdatap_g=p_{data}pg=pdata就行了。

代码实现-模型训练与保存

老流程:读数据、初始化相关参数、定义数据接受接口、初始化权重和偏置、构建基本模块(生成器和判别器)、构建模型、定义损失和优化器、训练

读取数据

这个就不说了,全博客通用:

IMG_HEIGHT=28
IMG_WIDTH=28
CHANNELS=3
#读取数据集
def read_images(dataset_path,batch_size):imagepaths,labels=list(),list()data=open(dataset_path,'r').read().splitlines()for d in data:imagepaths.append(d.split(' ')[0])labels.append(int(d.split(' ')[1]))imagepaths=tf.convert_to_tensor(imagepaths,dtype=tf.string)labels=tf.convert_to_tensor(labels,dtype=tf.int32)image,label=tf.train.slice_input_producer([imagepaths,labels],shuffle=True)image=tf.read_file(image)image=tf.image.decode_jpeg(image,channels=CHANNELS)image=tf.image.rgb_to_grayscale(image)    image=tf.reshape(image,[IMG_HEIGHT*IMG_WIDTH])image=tf.cast(image,tf.float32)image = image / 255.0image=tf.convert_to_tensor(image)inputX,inputY=tf.train.batch([image,label],batch_size=batch_size,capacity=batch_size*8,num_threads=4)return inputX,inputY

定义相关参数

主要是学习率,训练次数,输入单元、隐单元、输出单元的神经元个数

#定义相关参数
learning_rate=0.0002
num_steps=1000
batch_size=128
disp_step=1000
num_class=10
gen_hid_num=256
dis_hid_num=256
noise_dim=100
num_input=IMG_HEIGHT*IMG_WIDTH

定义数据接收接口

需要注意GAN主要有两类接口,生成器接收的是噪声输入,判别器接收的是真实图片或者生成的图片

#建立生成器、判别器的接收接口
gen_input=tf.placeholder(tf.float32,shape=[None,noise_dim],name='gen_input')
dis_input=tf.placeholder(tf.float32,shape=[None,num_input],name='dis_input')

初始化权重

#定义权重
def glorot_init(shape):return tf.random_normal(shape=shape,stddev=1/tf.sqrt(shape[0]/2.0))weights={'gen_hidden1':tf.Variable(glorot_init([noise_dim,gen_hid_num])),'gen_out':tf.Variable(glorot_init([gen_hid_num,num_input])),'dis_hidden1':tf.Variable(glorot_init([num_input,dis_hid_num])),'dis_out':tf.Variable(glorot_init([dis_hid_num,1]))
}
biases={'gen_hidden1':tf.Variable(tf.zeros([gen_hid_num])),'gen_out':tf.Variable(tf.zeros([num_input])),'dis_hidden1':tf.Variable(tf.zeros([dis_hid_num])),'dis_out':tf.Variable(tf.zeros([1]))
}

基本模块:生成器和判别器

#定义基本模块
def generator(x):hidden_layer=tf.add(tf.matmul(x,weights['gen_hidden1']),biases['gen_hidden1'])hidden_layer=tf.nn.relu(hidden_layer)out_layer=tf.add(tf.matmul(hidden_layer,weights['gen_out']),biases['gen_out'])out_layer=tf.nn.sigmoid(out_layer)return out_layerdef discriminator(x):hidden_layer=tf.add(tf.matmul(x,weights['dis_hidden1']),biases['dis_hidden1'])hidden_layer=tf.nn.relu(hidden_layer)out_layer=tf.add(tf.matmul(hidden_layer,weights['dis_out']),biases['dis_out'])out_layer=tf.nn.sigmoid(out_layer)return out_layer

构建模型

注意我们的测试函数是生成器,最终需要它来生成图片,所以需要加入函数中

#生成器
gen_sample=generator(gen_input)
tf.add_to_collection('generation',gen_sample)
#判别器
dis_real=discriminator(dis_input)
dis_fake=discriminator(gen_sample)

定义损失和优化器

损失函数包含生成器和判别器,但是针对生成器,我们上面说过,最小化log⁡(1−D(G(z)))\log (1-D(G(z)))log(1D(G(z)))不如最大化log⁡(D(G(z)))\log(D(G(z)))log(D(G(z))),所以生成器的损失可以定义为负对数,这样就把最大化又变成最小化了:

gen_loss = -tf.reduce_mean(tf.log(disc_fake))

当然你也可以使用最小化的方法,此博文即用最小化log⁡(1−D(G(z)))\log(1-D(G(z)))log(1D(G(z)))的方法:

G_loss = tf.reduce_mean(tf.log(1-prob_artist1))

判别器还是老样子最大化log⁡(D(x))−log⁡(D(G(z)))\log (D(x))-\log(D(G(z)))log(D(x))log(D(G(z))),加个负号也是最小化了:

disc_loss = -tf.reduce_mean(tf.log(disc_real) + tf.log(1. - disc_fake))

【注】其实有的时候也可以直接用交叉熵来定义损失,让判别器对真实图片的标签接近1,对假图片的判别标签接近0

d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = D, labels = tf.ones_like(D)))
d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = _D, labels = tf.zeros_like(_D)))
d_loss = d_loss_real + d_loss_fake

而对于生成器,希望判别器对假图片的判别标签接近1:

g_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = _D, labels = tf.ones_like(_D)))

话不多说,接下来定义优化器:

optimizer_gen=tf.train.AdamOptimizer(learning_rate=learning_rate)
optimizer_dis=tf.train.AdamOptimizer(learning_rate=learning_rate)

但是因为采用的类似于固定梯度下降法,即在更新生成器时,判别器参数不动,同理更新判别器时生成器参数不动,所以需要先指定分开训练的时候分别对谁求梯度:

#因为采用了固定梯度下降,所以必须知道每个优化器需要优化什么
gen_var=[weights['gen_hidden1'],weights['gen_out'],biases['gen_hidden1'],biases['gen_out']]
dis_var=[weights['dis_hidden1'],weights['dis_out'],biases['dis_hidden1'],biases['dis_out']]

这样就可以针对性求解了:

#优化
train_gen=optimizer_gen.minimize(gen_loss,var_list=gen_var)
train_dis=optimizer_dis.minimize(dis_loss,var_list=dis_var)

训练模型与保存

#初始化
init=tf.global_variables_initializer()
saver=tf.train.Saver()
input_image,input_label=read_images('./mnist/train_labels.txt',batch_size)
with tf.Session() as sess:sess.run(init)coord=tf.train.Coordinator()tf.train.start_queue_runners(sess=sess,coord=coord)for step in range(1,num_steps):time_start = time.time()batch_x,batch_y=sess.run([input_image,tf.one_hot(input_label,num_class,1,0)])z=np.random.uniform(-1.0,1.0,size=(batch_size,noise_dim))sess.run([train_gen,train_dis],feed_dict={dis_input:batch_x,gen_input:z})if step%1000==0 or step==1:                        g_loss,d_loss=sess.run([gen_loss,dis_loss],feed_dict={gen_input:z,dis_input:batch_x})time_end=time.time()print('step:%i----Generator loss:%f-----Discriminator Loss:%f' %(step,g_loss,d_loss))coord.request_stop()coord.join()print('optimization finished')saver.save(sess,'./GAN_mnist_model/GAN_mnist')

我没有训练多少次,有兴趣的可以多训练,最终让判别器的损失接近0.50.50.5就说明接近最优解了,我的训练结果:

step:1----Generator loss:0.393680-----Discriminator Loss:1.626469
step:1000----Generator loss:3.580971-----Discriminator Loss:0.078812
step:2000----Generator loss:4.907338-----Discriminator Loss:0.037951
step:3000----Generator loss:5.269949-----Discriminator Loss:0.015779
step:4000----Generator loss:3.202836-----Discriminator Loss:0.119377
step:5000----Generator loss:3.977841-----Discriminator Loss:0.140365
step:6000----Generator loss:3.546029-----Discriminator Loss:0.111060
step:7000----Generator loss:3.723459-----Discriminator Loss:0.099416
step:8000----Generator loss:4.479396-----Discriminator Loss:0.130558
step:9000----Generator loss:4.041896-----Discriminator Loss:0.132201
step:10000----Generator loss:3.873767-----Discriminator Loss:0.241299
step:11000----Generator loss:4.237263-----Discriminator Loss:0.162134
step:12000----Generator loss:3.463274-----Discriminator Loss:0.223905
step:13000----Generator loss:3.941289-----Discriminator Loss:0.261881
step:14000----Generator loss:3.292896-----Discriminator Loss:0.356275
optimization finished

【更新日志】2018-8-27
还是依据论文流程,把判别器的训练放在前面

d_loss,g_loss=sess.run([dis_loss,gen_loss],feed_dict={dis_input:batch_x,gen_input:z})

代码实现-模型调用

还是老套路:

  • 载入模型

    sess=tf.Session()
    new_saver=tf.train.import_meta_graph('./GAN_mnist_model/GAN_mnist.meta')
    new_saver.restore(sess,'./GAN_mnist_model/GAN_mnist')
    
  • 载入运算图

    graph=tf.get_default_graph()
    print(graph.get_all_collection_keys())
    #['generation', 'queue_runners', 'summaries', 'train_op', 'trainable_variables', 'variables']
    
  • 获取预测函数和数据接收接口

    gen=graph.get_collection('generation')
    gen_input=graph.get_tensor_by_name('gen_input:0')
    
  • 随便丢个噪声给生成器

    noise_input=np.random.uniform(-1.0,1.0,size=[1,100])
    g=sess.run(gen,feed_dict={gen_input:noise_input})
    gen_img=g[0]*255.0
    gen_img=gen_img.reshape(28,28)
    plt.imshow(gen_img)
    plt.show()
    

    这里写图片描述

后记

效果貌似不是特别好呢,可能训练次数不是特别够,也可能传统的GAN结构对手写数字的生成能力不够,需要加深层数或者使用更好的GAN变种算法,后续打算再找几个GAN算法研究研究。这里先贴一下这篇博客关于GAN的损失函数的对比
这里写图片描述

训练代码:链接:https://pan.baidu.com/s/12_DNKILTtletYbDDhHDi6Q 密码:vyu7

测试代码:链接:https://pan.baidu.com/s/1rvAKjBnazzKRiL7nPiufVQ 密码:nreb
【更新日志】2018-8-27
从论文来看,我们一般需要先训练判别器,再训练生成器,但是TensorFlow-Examples中给的例子是

_, _, gl, dl = sess.run([train_gen, train_disc, gen_loss, disc_loss],feed_dict=feed_dict)

建议还是改一下:

_, _, gl, dl = sess.run([train_disc,train_gen,  disc_loss, gen_loss],feed_dict=feed_dict)

但是收敛度遇到问题了,目前正在解决。
【更新日志2018-8-29日】
找到未收敛或者收敛程度不好的原因了,要使用AdamOptimizer优化器,不要使用AdagradOptimizer优化器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Openpose——windows编译(炒鸡简单)

前言 最近准备看看rtpose的代码,发现已经由openpose这个项目维护着了,由于经常在windows下调试代码,所以尝试了一下如何在windows下编译openpose源码,整体来说非常简单的。 国际惯例,参考博客: [OpenPos…

强化学习——Qlearning

前言 在控制决策领域里面强化学习还是占很重比例的,最近出了几篇角色控制的论文需要研究,其中部分涉及到强化学习,都有开源,有兴趣可以点开看看: A Deep Learning Framework For Character Motion Synthesis and Edit…

【TensorFlow-windows】keras接口学习——线性回归与简单的分类

前言 之前有写过几篇TensorFlow相关文章,但是用的比较底层的写法,比如tf.nn和tf.layers,也写了部分基本模型如自编码和对抗网络等,感觉写起来不太舒服,最近看官方文档发现它的教程基本都使用的keras API,这…

【TensorFlow-windows】keras接口——卷积手写数字识别,模型保存和调用

前言 上一节学习了以TensorFlow为底端的keras接口最简单的使用,这里就继续学习怎么写卷积分类模型和各种保存方法(仅保存权重、权重和网络结构同时保存) 国际惯例,参考博客: 官方教程 【注】其实不用看博客,直接翻到文末看我的c…

【TensorFlow-windows】keras接口——BatchNorm和ResNet

前言 之前学习利用Keras简单地堆叠卷积网络去构建分类模型的方法,但是对于很深的网络结构很难保证梯度在各层能够正常传播,经常发生梯度消失、梯度爆炸或者其它奇奇怪怪的问题。为了解决这类问题,大佬们想了各种办法,比如最原始的…

【TensorFlow-windows】keras接口——卷积核可视化

前言 在机器之心上看到了关于卷积核可视化相关理论,但是作者的源代码是基于fastai写的,而fastai的底层是pytorch,本来准备自己用Keras复现一遍的,但是尴尬地发现Keras还没玩熟练,随后发现了一个keras-vis包可以用于做…

【TensorFlow-windows】投影变换

前言 没什么重要的,就是想测试一下tensorflow的投影变换函数tf.contrib.image.transform中每个参数的含义 国际惯例,参考文档 官方文档 描述 调用方法与默认参数: tf.contrib.image.transform(images,transforms,interpolationNEAREST,…

【TensorFlow-windows】扩展层之STN

前言 读TensorFlow相关代码看到了STN的应用,搜索以后发现可替代池化,增强网络对图像变换(旋转、缩放、偏移等)的抗干扰能力,简单说就是提高卷积神经网络的空间不变性。 国际惯例,参考博客: 理解Spatial Transformer…

【TensorFlow-windows】MobileNet理论概览与实现

前言 轻量级神经网络中,比较重要的有MobileNet和ShuffleNet,其实还有其它的,比如SqueezeNet、Xception等。 本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。 国际惯例,参考博客: 纵…

【TensorFlow-windows】keras接口——ImageDataGenerator裁剪

前言 Keras中有一个图像数据处理器ImageDataGenerator,能够很方便地进行数据增强,并且从文件中批量加载图片,避免数据集过大时,一下子加载进内存会崩掉。但是从官方文档发现,并没有一个比较重要的图像增强方式&#x…

【TensorFlow-windows】TensorBoard可视化

前言 紧接上一篇博客,学习tensorboard可视化训练过程。 国际惯例,参考博客: MNIST机器学习入门 Tensorboard 详解(上篇) Tensorboard 可视化好帮手 2 tf-dev-summit-tensorboard-tutorial tensorflow官方mnist_…

深度学习特征归一化方法——BN、LN、IN、GN

前言 最近看到Group Normalization的论文,主要提到了四个特征归一化方法:Batch Norm、Layer Norm、Instance Norm、Group Norm。此外,论文还提到了Local Response Normalization(LRN)、Weight Normalization(WN)、Batch Renormalization(BR)…

【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据

前言 之前使用tensorflow和keras的时候,都各自有一套数据读取方法,但是遇到一个问题就是,在训练的时候,GPU的利用率忽高忽低,极大可能是由于训练过程中读取每个batch数据造成的,所以又看了tensorflow官方的…

骨骼动画——论文与代码精读《Phase-Functioned Neural Networks for Character Control》

前言 最近一直玩CV,对之前学的动捕知识都忘得差不多了,最近要好好总结一下一直以来学习的内容,不能学了忘。对2017年的SIGGRAPH论文《Phase-Functioned Neural Networks for Character Control》进行一波深入剖析吧,结合源码。 额…

颜色协调模型Color Harmoniztion

前言 最近做换脸,在肤色调整的那一块,看到一个有意思的文章,复现一波玩玩。不过最后一步掉链子了,有兴趣的可以一起讨论把链子补上。 主要是github上大佬的那个复现代码和原文有点差异,而且代码复杂度过高&#xff0…

Openpose推断阶段原理

前言 之前出过一个关于openpose配置的博客,不过那个代码虽然写的很好,而且是官方的,但是分析起来很困难,然后再opencv相关博客中找到了比较清晰的实现,这里分析一波openpose的推断过程。 国际惯例,参考博…

换脸系列——眼鼻口替换

前言 想着整理一下换脸相关的技术方法,免得以后忘记了,最近脑袋越来越不好使了。应该会包含三个系列: 仅换眼口鼻;换整个面部;3D换脸 先看看2D换脸吧,网上已经有现成的教程了,这里拿过来整理一…

换脸系列——整脸替换

前言 前面介绍了仅替换五官的方法,这里介绍整张脸的方法。 国际惯例,参考博客: [图形算法]Delaunay三角剖分算法 维诺图(Voronoi Diagram)分析与实现 Delaunay Triangulation and Voronoi Diagram using OpenCV (…

3D人脸重建——PRNet网络输出的理解

前言 之前有款换脸软件不是叫ZAO么,分析了一下,它的实现原理绝对是3D人脸重建,而非deepfake方法,找了一篇3D重建的论文和源码看看。这里对源码中的部分函数做了自己的理解和改写。 国际惯例,参考博客: 什…

tensorflow官方posenet模型解析

前言 tensorflow官方有个姿态估计项目,这个输入和openpose还有点不一样,这里写个单人情况下的模型输出解析方案。 国际惯例,参考博客: 博客: 使用 TensorFlow.js 在浏览器端上实现实时人体姿势检测 tensorflow中posnet的IOS代…