8_7_变分AE
AE(Auto-Encoders):自动的编码
x一排从左边输入进来之后,经过隐层的压缩和解压缩之后(一般来说是2-4层),之后会还原成输入的样子,通常用作一个网络的初始化训练,本来的w是没有的,现在采用AE的方式,先有一个值,过后不管是什么网络,在当前的w的基础上,只需要微调就可以,因为变化不是特别大。
这样就可以节省很多时间来训练其他的网络。
但是现在AE只能通过输入的内容来决定输出,但是变分AE,操作的不是某一个数据,而是输入的数据的规律,有了这些规律,就可逆向的生成数据。(根据前几批的规律总结规律,逆向的造出虚假的数据,让这些数据和真实的数据都是很类的)可以用作图像处理,如逆向生成一个人在某一个地方的图片,这些逆向生成的图片是无法分辨出来的,因为所有的分布都是一样的,基于这种思路,对抗网络生成的数据,比变分AE要更强大。变分AE有细微的差别,对抗网络生成的几乎看不出来,以假乱真,如AI变脸。
引入包:
生成正态分布的规律:
from scipy.stats import norm
因为要在原始的数据的基础上,总结规律,把它变成一个具有正态分布的规矩,基于这个规律来伪造一批数据。
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets('MNIST_data/')
拿分号的格式来读取mnist数据集
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from scipy.stats import norm
from tensorflow.examples.tutorials.mnist import input_data
开始写这个网络:
输入进来的节点数量是784
隐层:
第一次压缩到256个节点
第二次压缩到2个节点
节点越少,提纯度越高(因为把特征都提取出来了)
第一个节点求正态分布的均值(mean),第二个节点求偏差程度(var)再在次基础上逆向恢复256个
变分AE就是在往常的AE基础上,中间增加了一个求均值和方差的规律,所以这层2个节点。
n_input=784#输入进来的节点数量是784
n_hidden_1=256#第一次压缩到256个节点
n_hidden_2=2 #mean lg_var 第二次压缩到2个节点
输入数据
用占位符表示N行784列
提取出来规律后在它的基础上,从右往左,逆向的生成数据,所以在这两个节点上伪造数据,这两个节点表示的就是均值和方差,要以这个的规律去伪造x。
x=tf.placeholder(tf.float32, [None, n_input]) #(N, 784)
zinput=tf.placeholder(tf.float32, [None, n_hidden_2]) #(N, 2)
由n_input层到达隐层,用输入w
N行784列784行256列,这是第一层的
tauncated_normal是截断函数
在正态分布的基础上,还加了截断功能,表示变量是随机生成的,一旦变量的方差大于阈值(stddev)0.001,所有的数据就重做,随机数只有小于阈值才会保留,所以可能会重复多次。
b默认初值是0。
'w1':tf.Variable(tf.truncated_normal([n_input, n_hidden_1], stddev=0.001)),
'b1':tf.Variable(tf.zeros([n_hidden_1])),
这表示由n_input到隐层
由n_input到n_hidden_2有两套访问参数,第一套是到均值(mean)的,仍然采用截断的方式。
'mean_w1':tf.Variable(tf.truncated_normal([n_hidden_1, n_hidden_2], stddev=0.001)),
'mean_b1':tf.Variable(tf.zeros([n_hidden_2])),
第二套是到方差(lg_var),参数和上边一样
'log_sigma_w1':tf.Variable(tf.truncated_normal([n_hidden_1,n_hidden_2], stddev=0.001)),
'log_sigma_b1':tf.Variable(tf.zeros([n_hidden_2])),
然后从这两个节点恢复到256,再由356恢复到784,所以参数和上边刚好逆着来
'w2':tf.Variable(tf.truncated_normal([n_hidden_2, n_hidden_1], stddev=0.001)),
'b2':tf.Variable(tf.zeros([n_hidden_1])),
'w3':tf.Variable(tf.truncated_normal([n_hidden_1, n_input], stddev=0.001)),
'b3':tf.Variable(tf.zeros([n_input]))
这就是五层的网络参数的结构
weights={
'w1':tf.Variable(tf.truncated_normal([n_input, n_hidden_1], stddev=0.001)),
'b1':tf.Variable(tf.zeros([n_hidden_1])),
'mean_w1':tf.Variable(tf.truncated_normal([n_hidden_1, n_hidden_2], stddev=0.001)),
'mean_b1':tf.Variable(tf.zeros([n_hidden_2])),
'log_sigma_w1':tf.Variable(tf.truncated_normal([n_hidden_1,n_hidden_2], stddev=0.001)),
'log_sigma_b1':tf.Variable(tf.zeros([n_hidden_2])),
'w2':tf.Variable(tf.truncated_normal([n_hidden_2, n_hidden_1], stddev=0.001)),
'b2':tf.Variable(tf.zeros([n_hidden_1])),
'w3':tf.Variable(tf.truncated_normal([n_hidden_1, n_input], stddev=0.001)),
'b3':tf.Variable(tf.zeros([n_input]))
}
从n_input层到n_hidden_1层:
wx+b
先将线性函数做出来
用非线性函数将线性函数激活一下,所以加一个relu函数
x是N行784,w是784行256列,所以应该是N行256列
h1=tf.nn.relu(tf.add(tf.matmul(x, weights['w1']), weights['b1'])) #(N, n_hidden_1)
由第一层到第二层是一个分叉的结构,其中一条到了mean节点,另一条到了var节点
从n_hidden_1到mean,没有激活,要保持线性的关系,所以乘完以后是N行2列
z_mean=tf.add(tf.matmul(h1, weights[‘mean_w1’]), weights[‘mean_b1’]) #(N, n_hidden_2)
另一个
z_log_sigma_sq=tf.add(tf.matmul(h1, weights['log_sigma_w1']), weights['log_sigma_b1']) #(N, n_hidden_2)
这样就完成了h1到mean隐层到mean和到var节点的两条映射:
h1=tf.nn.relu(tf.add(tf.matmul(x, weights['w1']), weights['b1'])) #(N, n_hidden_1)
z_mean=tf.add(tf.matmul(h1, weights['mean_w1']), weights['mean_b1']) #(N, n_hidden_2)
z_log_sigma_sq=tf.add(tf.matmul(h1, weights['log_sigma_w1']), weights['log_sigma_b1']) #(N, n_hidden_2)
模拟正态数据:
生成一个符合高斯正态分布的随机分布,分布的尺寸是h1的第0维(也就是N),N行2列
stack是一个拼接函数,可以把两部内容拼一下
拼成的形状满足的是均值为0,方差为1,并且是float的数据
#生成正态分布数据,(None, n_hidden_2)
eps=tf.random_normal(tf.stack([tf.shape(h1)[0], n_hidden_2]), 0, 1, dtype=tf.float32)
再mean层和var层的基础上,归类,生成一个类别
首先在方差点,先求e 的指数,再开根号,让得到的数乘以随机数(eps),因为z_log_sigma_sq是N行2列,指数之后开根号还是N行2列,再加上z_mean,所以到最后z点还是N行2列
z=tf.add(z_mean, tf.multiply(tf.sqrt(tf.exp(z_log_sigma_sq)), eps)) #(N, n_hidden_2)
在z点的基础上恢复,恢复到256
再由256恢复到784
怎么进来的怎么出去
h2=tf.nn.relu(tf.matmul(z, weights['w2'])+weights['b2']) #(N, n_hidden_1)
把出去的数叫reconstrucation(重构),表示的是模型的实际输出,不是人为的伪造,再恢复成N行784列
reconstruction=tf.matmul(h2, weights['w3'])+weights['b3'] #(N, n_input)
#生成正态分布数据,(None, n_hidden_2)
eps=tf.random_normal(tf.stack([tf.shape(h1)[0], n_hidden_2]), 0, 1, dtype=tf.float32)
z=tf.add(z_mean, tf.multiply(tf.sqrt(tf.exp(z_log_sigma_sq)), eps)) #(N, n_hidden_2)
h2=tf.nn.relu(tf.matmul(z, weights['w2'])+weights['b2']) #(N, n_hidden_1)
reconstruction=tf.matmul(h2, weights['w3'])+weights['b3'] #(N, n_input)
拿伪造的数据生成一个伪造的输出
z_input是一个虚拟出来的N行2列的,模拟的是z,和z有共同的分布
h2out(倒数第二层):虚拟的数据,wx+b,relue
用虚拟的N行2列去还原倒数第二层
h2out=tf.nn.relu(tf.matmul(zinput, weights['w2'])+weights['b2']) #(N, n_hidden_1)
再由倒数第二层去还原倒数第一层
由虚拟数据得到的一个图像
reconstructionout=tf.matmul(h2out, weights['w3'])+weights['b3'] #(N, n_input)
为了让模型更好的以假乱真,要逐渐缩小模型的误差
首先
降低真实数据的误差
真实数据和虚拟数据各占二分之一
拿真实数据和输入的x的差求平方, 乘n分之1,再乘0.5
#误差
reconstruction_loss=0.5*tf.reduce_mean(tf.pow(tf.subtract(reconstruction, x), 2.0))
kl算法
1+方差-均值的根号下-方差的指数,再加1
latent_loss=-0.5*tf.reduce_mean(1+z_log_sigma_sq-tf.square(z_mean)-tf.exp(z_log_sigma_sq), 1) #KL
模型总的误差为这两个误差的和
cost=tf.reduce_mean(reconstruction_loss+latent_loss)
最终的优化器:在这个学习率下最小
optimizer=tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost)
想要训练50趟,每训练1次,批处理128个数据,每训练3次展示1次
trainint_epochs=50
batch_size=128
display_step=3
模型的均值误差是0
avg_cost=0.0
每一趟的批次:训练数据的总量55000/128
遍历这些批次,分别取值,取128个值,
如果训练次数到了3的整数倍,就打印
结束这个训练
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(trainint_epochs):
avg_cost=0.0
total_batch=int(mnist.train.num_examples/batch_size)
for i in range(total_batch):
batch_xs, batch_ys=mnist.train.next_batch(batch_size)
_, c=sess.run([optimizer, cost], feed_dict={x:batch_xs})
avg_cost+=c/total_batch
if epoch % display_step == 0:
print('epoch:', epoch+1, 'cost:', avg_cost)
print('finished')
测试一下模型的误差:
用10000张测试图像求误差,最后求均值
print('cost:', cost.eval({x:mnist.test.images})/total_batch)
看可视化结果:
要画10张图
模型等于
想要的是真实的输出,前十张用来画图
画2行10列,所以整图宽度是10,高度是2,
第一行第i列画的是原图
第二行第i列画的是模型的实际输出pred
对比一下
show_num=10
pred=sess.run(reconstruction, feed_dict={x:mnist.test.images[:show_num]})
f, a=plt.subplots(2, 10, figsize=(10, 2))
for i in range(show_num):
a[0][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))
a[1][i].imshow(np.reshape(pred[i], (28, 28)))
plt.draw()
饼图的压缩:
实际输出,两列
拿第一列和第二列看x轴,y轴,看饼图分布
pred=sess.run(z, feed_dict={x:mnist.test.images})
plt.figure(figsize=(6, 6)) #虽然只有一个图,但是占了6*6的面积
plt.scatter(pred[:, 0], pred[:, 1], c=mnist.test.labels)
plt.colorbar()
plt.show()
伪造样本:
伪造出来的样本具有很高的识别度
伪造一个15行15列,
每个位置上是一个数字,伪造的数字样本,大小是2828,也就是说和真实mnist有相同的尺寸,
2815,2815,表示一个二维的数组,figure用全0画出来
想画一个digit_sizen行,digit_sizen列的一个分布
这些分布,从0.05-0.95之间
因为小于0.05和大于0.95两边的数,在正态分布中是逐渐趋于极值的,是不可信的,取最可信的范围0.05-0.95,然后平均分成15份,x轴是15份,y轴也是15份,
linspace表示平均分
由随机区域0.05-0.95,逆向的生成 x轴坐标和y轴坐标
grid_x=norm.ppf()
grid_y=norm.ppf()
以前的做法是有坐标之后再去画正态分布的轴,但是现在没有坐标,仅凭一个分布的数据去逆向生成x轴和y轴的坐标
先竖着固定,再去遍历行
for i, yi in enumerate(grid_x): #行数
for j, xi in enumerate(grid_y): #列数
取当前的行值和列值,把他包装成一个列表
一取就是一对列表,xi,yi是一对,用列表生成一个数组np.array,得到一个坐标z_sample,225*225的一个坐标
z_sample=np.array([[xi, yi]])
这些坐标是根据虚拟数据逆向生成的坐标
拿坐标给zinput逆向数据
然后得到伪造的数据x_decoded
x_decoded=sess.run(reconstructionout,feed_dict={zinput:z_sample}) #(784, )
784个数据,取第0维度(也就是第1行),相当于取1行,
再将784列转置,重构成28*28
digit=x_decoded[0].reshape(digit_size, digit_size) #(28, 28)
然后再把每一批数据28*28给figure,因为figure是全0的
i到i+1,每循环一次i,取28个数据,行和列每次都取28个数据,取出来一幅图,
figure[i*digit_size:(i+1)*digit_size, j*digit_size:(j+1)*digit_size]=digit
然后把图画出来,最后就会生成伪造的数据
plt.figure(figsize=(10, 10))
plt.imshow(figure, cmap='Greys_r')
plt.show()
#伪造样本
n=15 #样本数量15*15
digit_size=28 #样本尺寸28*28
figure=np.zeros((digit_size*n, digit_size*n))
grid_x=norm.ppf(np.linspace(0.05, 0.95, n)) #逆向返回高斯分布在0.05~0.95之间的X坐标值
grid_y=norm.ppf(np.linspace(0.05, 0.95, n))
for i, yi in enumerate(grid_x): #行数
for j, xi in enumerate(grid_y): #列数
z_sample=np.array([[xi, yi]])
x_decoded=sess.run(reconstructionout,feed_dict={zinput:z_sample}) #(784, )
digit=x_decoded[0].reshape(digit_size, digit_size) #(28, 28)
figure[i*digit_size:(i+1)*digit_size, j*digit_size:(j+1)*digit_size]=digit
plt.figure(figsize=(10, 10))
plt.imshow(figure, cmap='Greys_r')
plt.show()
整体代码:
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 10 21:14:06 2022
@author: 玉想琼思
"""
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from scipy.stats import norm
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets('MNIST_data/')
n_input=784
n_hidden_1=256
n_hidden_2=2 #mean lg_var
x=tf.placeholder(tf.float32, [None, n_input]) #(N, 784)
zinput=tf.placeholder(tf.float32, [None, n_hidden_2]) #(N, 2)
weights={
'w1':tf.Variable(tf.truncated_normal([n_input, n_hidden_1], stddev=0.001)),
'b1':tf.Variable(tf.zeros([n_hidden_1])),
'mean_w1':tf.Variable(tf.truncated_normal([n_hidden_1, n_hidden_2], stddev=0.001)),
'mean_b1':tf.Variable(tf.zeros([n_hidden_2])),
'log_sigma_w1':tf.Variable(tf.truncated_normal([n_hidden_1,n_hidden_2], stddev=0.001)),
'log_sigma_b1':tf.Variable(tf.zeros([n_hidden_2])),
'w2':tf.Variable(tf.truncated_normal([n_hidden_2, n_hidden_1], stddev=0.001)),
'b2':tf.Variable(tf.zeros([n_hidden_1])),
'w3':tf.Variable(tf.truncated_normal([n_hidden_1, n_input], stddev=0.001)),
'b3':tf.Variable(tf.zeros([n_input]))
}
h1=tf.nn.relu(tf.add(tf.matmul(x, weights['w1']), weights['b1'])) #(N, n_hidden_1)
z_mean=tf.add(tf.matmul(h1, weights['mean_w1']), weights['mean_b1']) #(N, n_hidden_2)
z_log_sigma_sq=tf.add(tf.matmul(h1, weights['log_sigma_w1']), weights['log_sigma_b1']) #(N, n_hidden_2)
#生成正态分布数据,(None, n_hidden_2)
eps=tf.random_normal(tf.stack([tf.shape(h1)[0], n_hidden_2]), 0, 1, dtype=tf.float32)
z=tf.add(z_mean, tf.multiply(tf.sqrt(tf.exp(z_log_sigma_sq)), eps)) #(N, n_hidden_2)
h2=tf.nn.relu(tf.matmul(z, weights['w2'])+weights['b2']) #(N, n_hidden_1)
reconstruction=tf.matmul(h2, weights['w3'])+weights['b3'] #(N, n_input)
h2out=tf.nn.relu(tf.matmul(zinput, weights['w2'])+weights['b2']) #(N, n_hidden_1)
reconstructionout=tf.matmul(h2out, weights['w3'])+weights['b3'] #(N, n_input)
#误差
reconstruction_loss=0.5*tf.reduce_mean(tf.pow(tf.subtract(reconstruction, x), 2.0))
latent_loss=-0.5*tf.reduce_mean(1+z_log_sigma_sq-tf.square(z_mean)-tf.exp(z_log_sigma_sq), 1) #KL
cost=tf.reduce_mean(reconstruction_loss+latent_loss)
optimizer=tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost)
trainint_epochs=50
batch_size=128
display_step=3
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(trainint_epochs):
avg_cost=0.0
total_batch=int(mnist.train.num_examples/batch_size)
for i in range(total_batch):
batch_xs, batch_ys=mnist.train.next_batch(batch_size)
_, c=sess.run([optimizer, cost], feed_dict={x:batch_xs})
avg_cost+=c/total_batch
if epoch % display_step == 0:
print('epoch:', epoch+1, 'cost:', avg_cost)
print('finished')
print('cost:', cost.eval({x:mnist.test.images})/total_batch)
# 可视化结果
show_num=10
pred=sess.run(reconstruction, feed_dict={x:mnist.test.images[:show_num]})
f, a=plt.subplots(2, 10, figsize=(10, 2))
for i in range(show_num):
a[0][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))
a[1][i].imshow(np.reshape(pred[i], (28, 28)))
plt.draw()
pred=sess.run(z, feed_dict={x:mnist.test.images})
plt.figure(figsize=(6, 6)) #虽然只有一个图,但是占了6*6的面积
plt.scatter(pred[:, 0], pred[:, 1], c=mnist.test.labels)
plt.colorbar()
plt.show()
#伪造样本
n=15 #样本数量15*15
digit_size=28 #样本尺寸28*28
figure=np.zeros((digit_size*n, digit_size*n))
grid_x=norm.ppf(np.linspace(0.05, 0.95, n)) #逆向返回高斯分布在0.05~0.95之间的X坐标值
grid_y=norm.ppf(np.linspace(0.05, 0.95, n))
for i, yi in enumerate(grid_x): #行数
for j, xi in enumerate(grid_y): #列数
z_sample=np.array([[xi, yi]])
x_decoded=sess.run(reconstructionout,feed_dict={zinput:z_sample}) #(784, )
digit=x_decoded[0].reshape(digit_size, digit_size) #(28, 28)
figure[i*digit_size:(i+1)*digit_size, j*digit_size:(j+1)*digit_size]=digit
plt.figure(figsize=(10, 10))
plt.imshow(figure, cmap='Greys_r')
plt.show()