Tensorflow入门教程(三十二)自编码

#
#作者:韦访
#博客:https://blog.csdn.net/rookie_wei
#微信:1007895847
#添加微信的备注一下是CSDN的
#欢迎大家一起学习
#

------韦访 20181204

1、概述

继续学习,深度学习主要有两种训练模式,一种是监督学习,这种模式既需要有样本数据,每个样本还得有对应的标签。另一种是非监督学习,这种模式只要有样本数据就行了,不需要标签。我们以前所有的学习都是监督学习。监督学习要对每个样本进行标注,工作量非常大,所以,现在开始,我们来学习非监督学习。

2、自编码网络

自编码网络(Auto-Encoder AE)是输入等于输出的网络,最简单的模型只有三层:输入层、隐藏层、输出层。如下图所示,

如上图所示,输入层的样本还充当了输出层的标签作用,也就是,输出尽可能等于输入。隐藏层维数必须小于输入输出层的维数,这样就相当于隐藏层对输入进行了压缩,再在输出层解压,从而保留了数据的主要特征。

下面通过一个例子说明。

3、加载MNIST数据

我们要做的例子是这样的,建一个自编码模型,提取MNIST数据集的特征,然后,通过这些特征,重建MNIST数据集。

现在,先将加载MNIST数据集,还不知道什么是MNIST数据集的小伙伴们,出门左转,先去看看第一讲的内容:https://blog.csdn.net/rookie_wei/article/details/80142876

很简单,代码如下,

from tensorflow.examples.tutorials.mnist import input_data
#加载MNIST数据集
mnist = input_data.read_data_sets("mnist_data", one_hot=True)

4、定义模型

模型的话,MNIST数据集每张图片的大小是28×28=784,我们要构建4层网络,维度先从784降到256再降到128,然后经过128,再升到256.最后输出784,如下图示,

import tensorflow as tf
import os

#定义学习率
learning_rate = 0.01
#输入维数
n_input = 784
#隐藏层维数
n_hidden_l1 = 256
n_hidden_l2 = 128

#创建变量
#权重
Weights = {
    'encoder_w1' : tf.Variable(tf.random_normal([n_input, n_hidden_l1])),
    'encoder_w2' : tf.Variable(tf.random_normal([n_hidden_l1, n_hidden_l2])),
    'decoder_w1' : tf.Variable(tf.random_normal([n_hidden_l2, n_hidden_l1])),
    'decoder_w2' : tf.Variable(tf.random_normal([n_hidden_l1, n_input])),
}

#偏置
biases = {
    'encoder_b1' : tf.Variable(tf.zeros([n_hidden_l1])),
    'encoder_b2' : tf.Variable(tf.zeros([n_hidden_l2])),
    'decoder_b1' : tf.Variable(tf.zeros([n_hidden_l1])),
    'decoder_b2' : tf.Variable(tf.zeros([n_input])),
}

#编码
def encoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, Weights['encoder_w1']), biases['encoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, Weights['encoder_w2']), biases['encoder_b2']))
    return layer_2

#解码
def decoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, Weights['decoder_w1']), biases['decoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, Weights['decoder_w2']), biases['decoder_b2']))
    return layer_2
#创建占位符
#输入
x = tf.placeholder(tf.float32, [None, n_input])

#输出
y = x

#得到输出
encoder_out = encoder(x)
pred = decoder(encoder_out)

# 定义损失函数
# cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(pred)))
cross_entropy = tf.reduce_mean(tf.pow(y - pred, 2))
# 优化器
# optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)
optimizer = tf.train.RMSPropOptimizer(learning_rate).minimize(cross_entropy)

 5、开始训练

#训练周期
training_epochs = 100
#batch大小
batch_size = 256
#多少个batch显示一下损失
display_step = 500

#保持模型的路径
saver_dir = './saver4'
saver_file = os.path.join(saver_dir, 'mnist.ckpt')

if os.path.exists(saver_dir) == False:
    os.mkdir(saver_dir)

saver = tf.train.Saver();

#建会话
with tf.Session() as sess:
    #如果有模型存在,则导入,否则,初始化变量
    if os.path.exists(os.path.join(saver_dir, 'mnist.ckpt.index')):
        saver.restore(sess, saver_file)
        print('load....')
    else:
        sess.run(tf.global_variables_initializer())
        print('init....')
    # #迭代一次所有数据需要多少步
    one_epochs_step = int(mnist.train.num_examples / batch_size)
    #开始训练
    for epochs in range(training_epochs):
        for i in range(one_epochs_step):
            #获取下一batch的数据
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            opt, cross = sess.run([optimizer, cross_entropy], feed_dict={x: batch_xs})
            #打印下
            if  i %  display_step == 0:
                print('epochs:' + str(epochs) + '   cross:', cross)


    # 检测训练结果,tf.argmax取出数组中最大值的下标,tf.equal再对比下标是否一样即可知道预测是否正确
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(pred, 1))
    # correct_prediction得到的结果是True或者False的数组
    # 我们再经过tf.cast将其转为数字的形式,即将[True, True, Flase, Flase]转成[1, 1, 0, 0]
    # 最后用tf.reduce_mean计算数组中所有元素的平均值,即预测的准确率
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    # 开始预测运算,并将准确率输出
    # print(sess.run(accuracy, feed_dict={x: mnist.test.images, pred: mnist.test.images}))
    print(1 - accuracy.eval(feed_dict={x: mnist.test.images, y: mnist.test.images}))
    #最后,将会话保存下来
    saver.save(sess, saver_file)

运行结果,

注:上面的结果我不止跑了100 epochs 。如果训练次数少,而发现损失还在降的话,可以增加训练次数以达到更好的效果。

 6、可视化显示结果

上面只能看到一堆数字,没那么直观,那么,我们现在以图片的形式显示结果来看看,

show_num = 10
result = sess.run(pred,feed_dict={x : mnist.test.images[:show_num]})
_, xy = plt.subplots(2, 10, figsize=(10, 2))
for i in range(show_num):
    xy[0][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))
    xy[1][i].imshow(np.reshape(result[i], (28, 28)))

plt.show()

运行结果,

注:上图没用我自己的电脑跑,因为没有GPU,所以训练的次数也少了点,可以看到生成的图片有些噪音点,不过也能大致还原原图,用我自己电脑跑生成的噪音点比这个少一点的,但也有噪音点。

不知道你有没有注意到我们定义模型时,我有注释了一组损失函数和优化器,这组损失函数和优化器是我们第一讲时用到了,那么,它们能在这里使用吗?我们来运行一下看看,将上面代码中的损失函数和优化器改成,

# 定义损失函数
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(pred)))
# 优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)

运行结果,

这个就不是我们想要的结果了,也就是说,并不是任何地方想用什么优化器就用什么优化器的。再来看看我们常用的优化器如下表所示,

再将优化器改为,

# 优化器
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)

试试,运行结果,

一样不行,所以,后面我想用一讲的时间来研究一下这几个优化器的差异。

7、将图片压缩成2维数据

先继续我们这一讲的内容,我们用4层网络将MNIST从784维压缩到2维,再显示到直角坐标系中看看,网络结构如下,

代码跟上面的类似,稍微修改就可以了,

#encoding:utf-8
from tensorflow.examples.tutorials.mnist import input_data
#加载MNIST数据集
mnist = input_data.read_data_sets("mnist_data", one_hot=True)

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
import os

#定义学习率
learning_rate = 0.01
#输入维数
n_input = 784
#隐藏层维数
n_hidden_l1 = 256
n_hidden_l2 = 64
n_hidden_l3 = 16
n_hidden_l4 = 2
#创建变量
#权重
Weights = {
    'encoder_w1' : tf.Variable(tf.random_normal([n_input, n_hidden_l1])),
    'encoder_w2' : tf.Variable(tf.random_normal([n_hidden_l1, n_hidden_l2])),
    'encoder_w3' : tf.Variable(tf.random_normal([n_hidden_l2, n_hidden_l3])),
    'encoder_w4' : tf.Variable(tf.random_normal([n_hidden_l3, n_hidden_l4])),

    'decoder_w1' : tf.Variable(tf.random_normal([n_hidden_l4, n_hidden_l3])),
    'decoder_w2' : tf.Variable(tf.random_normal([n_hidden_l3, n_hidden_l2])),
    'decoder_w3' : tf.Variable(tf.random_normal([n_hidden_l2, n_hidden_l1])),
    'decoder_w4' : tf.Variable(tf.random_normal([n_hidden_l1, n_input])),
}

#偏置
biases = {
    'encoder_b1' : tf.Variable(tf.zeros([n_hidden_l1])),
    'encoder_b2' : tf.Variable(tf.zeros([n_hidden_l2])),
    'encoder_b3' : tf.Variable(tf.zeros([n_hidden_l3])),
    'encoder_b4' : tf.Variable(tf.zeros([n_hidden_l4])),

    'decoder_b1' : tf.Variable(tf.zeros([n_hidden_l3])),
    'decoder_b2' : tf.Variable(tf.zeros([n_hidden_l2])),
    'decoder_b3' : tf.Variable(tf.zeros([n_hidden_l1])),
    'decoder_b4' : tf.Variable(tf.zeros([n_input])),
}

#编码
def encoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, Weights['encoder_w1']), biases['encoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, Weights['encoder_w2']), biases['encoder_b2']))
    layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, Weights['encoder_w3']), biases['encoder_b3']))
    layer_4 = tf.nn.sigmoid(tf.add(tf.matmul(layer_3, Weights['encoder_w4']), biases['encoder_b4']))
    return layer_4

#解码
def decoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, Weights['decoder_w1']), biases['decoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, Weights['decoder_w2']), biases['decoder_b2']))
    layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, Weights['decoder_w3']), biases['decoder_b3']))
    layer_4 = tf.nn.sigmoid(tf.add(tf.matmul(layer_3, Weights['decoder_w4']), biases['decoder_b4']))

    return layer_4

#创建占位符
#输入
x = tf.placeholder(tf.float32, [None, n_input])

#输出
y = x

#得到输出
encoder_out = encoder(x)
pred = decoder(encoder_out)

# 定义损失函数
cross_entropy = tf.reduce_mean(tf.pow(y - pred, 2))
# 优化器
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)

#训练周期
training_epochs = 500
#batch大小
batch_size = 256
#多少个batch显示一下损失
display_step = 500

#保持模型的路径
saver_dir = './saver_demo2'
saver_file = os.path.join(saver_dir, 'mnist.ckpt')

if os.path.exists(saver_dir) == False:
    os.mkdir(saver_dir)

saver = tf.train.Saver();

#建会话
with tf.Session() as sess:
    #如果有模型存在,则导入,否则,初始化变量
    if os.path.exists(os.path.join(saver_dir, 'mnist.ckpt.index')):
        saver.restore(sess, saver_file)
        print('load....')
    else:
        sess.run(tf.global_variables_initializer())
        print('init....')
    # #迭代一次所有数据需要多少步
    one_epochs_step = int(mnist.train.num_examples / batch_size)
    #开始训练
    for epochs in range(training_epochs):
        for i in range(one_epochs_step):
            #获取下一batch的数据
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            opt, cross = sess.run([optimizer, cross_entropy], feed_dict={x: batch_xs})
            #打印下
            if  i %  display_step == 0:
                print('epochs:' + str(epochs) + '   cross:', cross)


    # 检测训练结果,tf.argmax取出数组中最大值的下标,tf.equal再对比下标是否一样即可知道预测是否正确
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(pred, 1))
    # correct_prediction得到的结果是True或者False的数组
    # 我们再经过tf.cast将其转为数字的形式,即将[True, True, Flase, Flase]转成[1, 1, 0, 0]
    # 最后用tf.reduce_mean计算数组中所有元素的平均值,即预测的准确率
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    # 开始预测运算,并将准确率输出
    # print(sess.run(accuracy, feed_dict={x: mnist.test.images, pred: mnist.test.images}))
    print(1 - accuracy.eval(feed_dict={x: mnist.test.images, y: mnist.test.images}))
    #最后,将会话保存下来
    saver.save(sess, saver_file)

    show_num = 10
    result = sess.run(pred,feed_dict={x : mnist.test.images[:show_num]})
    _, xy = plt.subplots(2, 10, figsize=(10, 2))
    for i in range(show_num):
        xy[0][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))
        xy[1][i].imshow(np.reshape(result[i], (28, 28)))

    plt.show()

运行结果,

从784维压缩到2维,还能还原成这样,确实有点牛x,将二维数据显示在坐标系上看看,代码如下,

#将onehot转成一般数字
aa = [np.argmax(i) for i in mnist.test.labels]

#获取转成2维后的数据
encoder_result = sess.run(encoder_out, feed_dict={x: mnist.test.images})

#显示
plt.scatter(encoder_result[:, 0], encoder_result[:, 1], c=aa)
plt.colorbar()
plt.show()

运行结果,

我们看到颜色相近的点都聚在一起了,可以看到,经过自编码降维之后,数据更易于分类了。

8、线性解码器

上面的例子中,我们使用sigmoid作为激活函数,将输出缩放到[0,1]之间了,当我们对最终提取的特征节点采用该激励函数时,就相当于对输入限制或缩放,使其位于[0,1]范围中。

由多个带有S型激活函数的隐含层及一个线性输出层构成的自编码器,称为线性解码器。

对于上面的例子,我们试着使用线性解码器看看,将编码函数改成下面代码,


#编码
def encoder(x):
    layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, Weights['encoder_w1']), biases['encoder_b1']))
    layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, Weights['encoder_w2']), biases['encoder_b2']))
    layer_3 = tf.nn.sigmoid(tf.add(tf.matmul(layer_2, Weights['encoder_w3']), biases['encoder_b3']))
    # layer_4 = tf.nn.sigmoid(tf.add(tf.matmul(layer_3, Weights['encoder_w4']), biases['encoder_b4']))
    layer_4 = tf.add(tf.matmul(layer_3, Weights['encoder_w4']), biases['encoder_b4'])
    return layer_4

运行结果,

效果比上面的好。

总结:

关于自编码,还有很多知识点,比如卷积网络的自编码,去噪自编码,栈式自编码,变分自编码,条件变分自编码等等。其中,条件变分自编码和栈式自编码应用蛮广泛的,我就先止于此吧,以后用到再补了。

 

如果您感觉本篇博客对您有帮助,请打开支付宝,领个红包支持一下,祝您扫到99元,谢谢~~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值