TensorFlow快进到入坟

记录对这个框架的基本理解,一些重要的API和一些工程实践。

三个核心:

  1. 所有的数据都是张量
  2. 所有计算都是计算图
  3. 执行运算和管理资源都要使用会话

一些基本运行原理:

Tensorflow的所有计算都在计算图(Graph)中运行,通过集合(Collection)管理资源(变量、张量、队列资源),其中TensorFlow维护了一个tf.GraphKeys.VARIABLES的集合,该集合包含了所有的变量。

例如a = tf.constan([1.0,2.0],name="a")中,tf.constant是一个计算,计算的结果是一个Tensor,每一个Tensor都有三个属性:

  1. 名字(name),作为张量的唯一标识符。
  2. 维度(shape),描述张量的维度信息,例如shape=(3,3,1)表示该张量有三个维度(可以理解为多维数组),各个维度长度分别为3,3,1,采用None则表示该维度不定长。
  3. 类型(type),数据类型,同其他编程语言。

logits: 一个事件发生与该事件不发生的比值的对数

note

numpy.squeeze(a, axis=None)

squeeze()函数的功能是:从矩阵shape中,去掉维度为1的。例如一个矩阵是的shape是(51),使用过这个函数后,结果为(5,)。

参数: 
a是输入的矩阵 
axis : 选择shape中的一维条目的子集。如果在shape大于1的情况下设置axis,则会引发错误。

tf.gfile.Glob(filename)
Defined in tensorflow/python/lib/io/file_io.py.

Returns a list of files that match the given pattern(s).
   


    
图:一些Tensor和Operation的集合
    
实际上,Tensorflow而是首先将 python 代码所描绘的图转换(即“序列化”)成 Protocol Buffer,再通过 C/C++/CUDA 运行 Protocol Buffer 所定义的图。

基础又重要的内容

1. Part 1

#计算图和会话的基本使用
g1 = tf.graph()
with g1.as_default():
    pass
	#your operation

g2 = tf.graph()
with g1.as_default():
    pass
	#your operation

with tf.Session(graph=g1) as sess:
    tf.initializer_all_variables().run()
    #your operation

    
#定义一个常向量
a = tf.constant([1.0,2.0],name="a")

#定义矩阵乘法
x1 = tf.matmul(x,w1)

#定义一个一维变向量,初始化为0, 计算图中张量的初始化需要使用`initializer`
v = tf.get_variable(name="v",initializer=tf.zeros_initializer(shape=[1]))

#定义一个随机矩阵变量,标准差为2,注意get_variable不会处理命名冲突,Varaible则会,因此在需要共享变量名的场景下应采用get_variable
a = tf.Variable(tf.random_normal([2,3],stddev=2))

#assign用于给变量赋值
tf.assign(w1,w2)

#定义了一个placeholder数据入口
x = tf.placeholder(type=tf.float32, shape=(3,2), name="x-input")

#定义了反向传播算法和梯度下降优化器
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimizer(cross_entropy)

2. Part 2

#tf.reduce_mean 函数用于计算张量tensor某个维度上的的平均值,tf.clip_by_value可以吧数据限制在一个范围之内

#例中利用_y张量计算了平均交叉熵。
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y,1e-10,1.0)))

#定义了一个使用softmax回归的交叉熵损失函数
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(y,y_)

#定义了一个均方差的损失函数
mse = tf.reduce_mean(tf.square(_y-y))

#tf.reduce_sum压缩求和,tf.select当参数一条件为真时,会使用参数二的值,否则会使用参数三的值,tf.greater会逐个比较张量中每个元素的大小
#以下代码实现了一个简单的自定义损失函数
loss = tf.reduce_sum(tf.select(tf.greater(v1,v2),(v1-v2)*a,(v2-v1)*b)

#利用tf.contrib.layers.12_regularizer()对原均方差损失函数进行了正则化,lambda表示正则化权重,w为正则化损失参数
loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib.layers.12_regularizer(lambda)(w)
                     
#以下代码中利用add_yo_colection将正则化损失项加入到losses集合中
 tf.add_to_collection('losses',tf.contrib.layers.12_regularizer(lambda)(w))
                     
#以下代码定义了一个滑动平均类ema,并定义了滑动平均操作maintain_averages_op,其中decay_rate衰减率,越接近1模型越稳定,step是控制衰减率的变量。
ema = tf.train.ExponentialMovingAverage(decay_rate,step)
maintain_averages_op = ema.apply([v1])

案例

1. 简单前馈神经网络

# -*-coding:utf-8 -*-
import tensorflow as tf
from numpy.random import RandomState

batch_size = 8
w1= tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2= tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
x = tf.placeholder(tf.float32, shape=(None, 2), name="x-input")
y_= tf.placeholder(tf.float32, shape=(None, 1), name='y-input')

a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))) 
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)

rdm = RandomState(1)
X = rdm.rand(128,2)
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))) 
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
Y = [[int(x1+x2 < 1)] for (x1, x2) in X]

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)    
    
    STEPS = 5000
    for i in range(STEPS):
        start = (i*batch_size) % 128
        end = (i*batch_size) % 128 + batch_size
        sess.run(train_step, feed_dict={x: X[start:end], y_: Y[start:end]})
        if i % 1000 == 0:
            total_cross_entropy = sess.run(cross_entropy, feed_dict={x: X, y_: Y})
            print("After %d training step(s), cross entropy on all data is %g" % (i, total_cross_entropy))
    
    # 输出训练后的参数取值。
    print "\n"
    print "w1:", sess.run(w1)
    print "w2:", sess.run(w2)

2. MNIST手写数据集 - CNN版

定义前向传播流程

# -*-coding:utf-8-*-
import tensorflow as tf

INPUT_NODE = 784    #输入的节点数 28*28
OUTPUT_NODE = 10    #输出的节点数 10

IMAGE_SIZE = 28     #图像大小
NUM_CHANNELS = 1    #图像深度
NUM_LABELS = 10     #数字种类

CONV1_DEEP = 32     #第一层卷积层深度
CONV1_SIZE = 5      #第一层卷积层尺寸

CONV2_DEEP = 64     #第二层卷积层深度
CONV2_SIZE = 5      #第二层卷积层尺寸

FC_SIZE = 512       #全连接层的节点个数

#定义CNN前向传播的过程
def inference(input_tensor, train, regularizer):
	#卷积层1
    with tf.variable_scope('layer1-conv1'):
        conv1_weights = tf.get_variable(
            "weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer=tf.constant_initializer(0.0))
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))

    #池化层1
    with tf.name_scope("layer2-pool1"):
        pool1 = tf.nn.max_pool(relu1, ksize = [1,2,2,1],strides=[1,2,2,1],padding="SAME")

    #卷积层2
    with tf.variable_scope("layer3-conv2"):
        conv2_weights = tf.get_variable(
            "weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],
            initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable("bias", [CONV2_DEEP], initializer=tf.constant_initializer(0.0))
        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))

    #池化层2
    with tf.name_scope("layer4-pool2"):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
        pool_shape = pool2.get_shape().as_list()
        nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
        reshaped = tf.reshape(pool2, [pool_shape[0], nodes])

    #全连接层1
    with tf.variable_scope('layer5-fc1'):
        fc1_weights = tf.get_variable("weight", [nodes, FC_SIZE],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))
        fc1_biases = tf.get_variable("bias", [FC_SIZE], initializer=tf.constant_initializer(0.1))

        fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
        if train: fc1 = tf.nn.dropout(fc1, 0.5)
        
    #全连接层2
    with tf.variable_scope('layer6-fc2'):
        fc2_weights = tf.get_variable("weight", [FC_SIZE, NUM_LABELS],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))
        fc2_biases = tf.get_variable("bias", [NUM_LABELS], initializer=tf.constant_initializer(0.1))
        logit = tf.matmul(fc1, fc2_weights) + fc2_biases

    return logit

定义训练流程

# -*-coding:utf-8-*-
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import LeNet5_infernece
import os
import numpy as np

BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99		#学习率衰减率
REGULARIZATION_RATE = 0.0001	#正则化
TRAINING_STEPS = 1000   		#训练轮数
MOVING_AVERAGE_DECAY = 0.99		#滑动平均模型的指数衰减率

def train(mnist):
    # 定义输出为4维矩阵的placeholder
    x = tf.placeholder(tf.float32, [
            BATCH_SIZE,
            LeNet5_infernece.IMAGE_SIZE,
            LeNet5_infernece.IMAGE_SIZE,
            LeNet5_infernece.NUM_CHANNELS],
        name='x-input')
    y_ = tf.placeholder(tf.float32, [None, LeNet5_infernece.OUTPUT_NODE], name='y-input')
    
    #这里的L2 regularizer是正则器,用于全连接层
    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    #这里的y是训练后的答案
    y = LeNet5_infernece.inference(x,False,regularizer)
    #定义滑动平均模型的迭代
    global_step = tf.Variable(0, trainable=False)

    # 定义滑动平均类
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    # 定义滑动平均操作
    variables_averages_op = variable_averages.apply(tf.trainable_variables())
    # 定义交叉熵函数
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
    # 均值交叉熵函数
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    # 定义损失函数
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
    # 定义滑动平均下降的学习率
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE, LEARNING_RATE_DECAY,
        staircase=True)

    # 定义训练迭代器
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
    # 定义训练操作
    with tf.control_dependencies([train_step, variables_averages_op]):
        train_op = tf.no_op(name='train')
        

    # 计算准确率
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    keep_prob = tf.placeholder(tf.float32)

    # 初始化TensorFlow持久化类。
    saver = tf.train.Saver()

    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(TRAINING_STEPS):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)

            reshaped_xs = np.reshape(xs, (
                BATCH_SIZE,
                LeNet5_infernece.IMAGE_SIZE,
                LeNet5_infernece.IMAGE_SIZE,
                LeNet5_infernece.NUM_CHANNELS))
            
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: reshaped_xs, y_: ys})

            if i % 10 == 0:
                train_accuracy = accuracy.eval(feed_dict={x: reshaped_xs, y_: ys, keep_prob: 1.0})
                #print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
                print("The %d training accuracy is: %g" % (i,train_accuracy))


    def test():
        pass


def main(argv=None):
    mnist = input_data.read_data_sets("/Dataset/MNIST_data", one_hot=True)
    train(mnist)

if __name__ == '__main__':
    main()

3. MNIST手写数据集 - GAN版

这里挂的是别人的代码,自己的代码一直出问题没解决。

生成器和判断器同样采用简单的三层全连接网络。

import tensorflow as tf #导入tensorflow
from tensorflow.examples.tutorials.mnist import input_data #导入手写数字数据集
import numpy as np #导入numpy
import matplotlib.pyplot as plt #plt是绘图工具,在训练过程中用于输出可视化结果
import matplotlib.gridspec as gridspec #gridspec是图片排列工具,在训练过程中用于输出可视化结果
import os #导入os
 

 
def xavier_init(size): #初始化参数时使用的xavier_init函数
    in_dim = size[0] 
    xavier_stddev = 1. / tf.sqrt(in_dim / 2.) #初始化标准差
    return tf.random_normal(shape=size, stddev=xavier_stddev) #返回初始化的结果
 
X = tf.placeholder(tf.float32, shape=[None, 784]) #X表示真的样本(即真实的手写数字)
 
D_W1 = tf.Variable(xavier_init([784, 128])) #表示使用xavier方式初始化的判别器的D_W1参数,是一个784行128列的矩阵
D_b1 = tf.Variable(tf.zeros(shape=[128])) #表示全零方式初始化的判别器的D_1参数,是一个长度为128的向量
 
D_W2 = tf.Variable(xavier_init([128, 1])) #表示使用xavier方式初始化的判别器的D_W2参数,是一个128行1列的矩阵
D_b2 = tf.Variable(tf.zeros(shape=[1])) ##表示全零方式初始化的判别器的D_1参数,是一个长度为1的向量
 
theta_D = [D_W1, D_W2, D_b1, D_b2] #theta_D表示判别器的可训练参数集合
 
 
Z = tf.placeholder(tf.float32, shape=[None, 100]) #Z表示生成器的输入(在这里是噪声),是一个N列100行的矩阵
 
G_W1 = tf.Variable(xavier_init([100, 128])) #表示使用xavier方式初始化的生成器的G_W1参数,是一个100行128列的矩阵
G_b1 = tf.Variable(tf.zeros(shape=[128])) #表示全零方式初始化的生成器的G_b1参数,是一个长度为128的向量
 
G_W2 = tf.Variable(xavier_init([128, 784])) #表示使用xavier方式初始化的生成器的G_W2参数,是一个128行784列的矩阵
G_b2 = tf.Variable(tf.zeros(shape=[784])) #表示全零方式初始化的生成器的G_b2参数,是一个长度为784的向量
 
theta_G = [G_W1, G_W2, G_b1, G_b2] #theta_G表示生成器的可训练参数集合
 
def save(saver, sess, logdir, step): #保存模型的save函数
   model_name = 'model' #模型名前缀
   checkpoint_path = os.path.join(logdir, model_name) #保存路径
   saver.save(sess, checkpoint_path, global_step=step) #保存模型
   print('The checkpoint has been created.')

def sample_Z(m, n): #生成维度为[m, n]的随机噪声作为生成器G的输入
    return np.random.uniform(-1., 1., size=[m, n])
 
 
def generator(z): #生成器,z的维度为[N, 100]
    G_h1 = tf.nn.relu(tf.matmul(z, G_W1) + G_b1) #输入的随机噪声乘以G_W1矩阵加上偏置G_b1,G_h1维度为[N, 128]
    G_log_prob = tf.matmul(G_h1, G_W2) + G_b2 #G_h1乘以G_W2矩阵加上偏置G_b2,G_log_prob维度为[N, 784]
    G_prob = tf.nn.sigmoid(G_log_prob) #G_log_prob经过一个sigmoid函数,G_prob维度为[N, 784]
 
    return G_prob #返回G_prob
 
 
def discriminator(x): #判别器,x的维度为[N, 784]
    D_h1 = tf.nn.relu(tf.matmul(x, D_W1) + D_b1) #输入乘以D_W1矩阵加上偏置D_b1,D_h1维度为[N, 128]
    D_logit = tf.matmul(D_h1, D_W2) + D_b2 #D_h1乘以D_W2矩阵加上偏置D_b2,D_logit维度为[N, 1]
    D_prob = tf.nn.sigmoid(D_logit) #D_logit经过一个sigmoid函数,D_prob维度为[N, 1]
 
    return D_prob, D_logit #返回D_prob, D_logit
 
 
def plot(samples): #保存图片时使用的plot函数
    fig = plt.figure(figsize=(4, 4)) #初始化一个4行4列包含16张子图像的图片
    gs = gridspec.GridSpec(4, 4) #调整子图的位置
    gs.update(wspace=0.05, hspace=0.05) #置子图间的间距
 
    for i, sample in enumerate(samples): #依次将16张子图填充进需要保存的图像
        ax = plt.subplot(gs[i])
        plt.axis('off')
        ax.set_xticklabels([])
        ax.set_yticklabels([])
        ax.set_aspect('equal')
        plt.imshow(sample.reshape(28, 28), cmap='Greys_r')
 
    return fig
 
 
G_sample = generator(Z) #取得生成器的生成结果
D_real, D_logit_real = discriminator(X) #取得判别器判别的真实手写数字的结果
D_fake, D_logit_fake = discriminator(G_sample) #取得判别器判别的生成的手写数字的结果
 
D_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_real, labels=tf.ones_like(D_logit_real))) #对判别器对真实样本的判别结果计算误差(将结果与1比较)
D_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_fake, labels=tf.zeros_like(D_logit_fake))) #对判别器对虚假样本(即生成器生成的手写数字)的判别结果计算误差(将结果与0比较)
D_loss = D_loss_real + D_loss_fake #判别器的误差
G_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_fake, labels=tf.ones_like(D_logit_fake))) #生成器的误差(将判别器返回的对虚假样本的判别结果与1比较)
 
dreal_loss_sum = tf.summary.scalar("dreal_loss", D_loss_real) #记录判别器判别真实样本的误差
dfake_loss_sum = tf.summary.scalar("dfake_loss", D_loss_fake) #记录判别器判别虚假样本的误差
d_loss_sum = tf.summary.scalar("d_loss", D_loss) #记录判别器的误差
g_loss_sum = tf.summary.scalar("g_loss", G_loss) #记录生成器的误差
 
summary_writer = tf.summary.FileWriter('snapshots/', graph=tf.get_default_graph()) #日志记录器
 
D_solver = tf.train.AdamOptimizer().minimize(D_loss, var_list=theta_D) #判别器的训练器
G_solver = tf.train.AdamOptimizer().minimize(G_loss, var_list=theta_G) #生成器的训练器
 
mb_size = 128 #训练的batch_size
Z_dim = 100 #生成器输入的随机噪声的列的维度
 
mnist = input_data.read_data_sets('../../MNIST_data', one_hot=True) #mnist是手写数字数据集
 
sess = tf.Session() #会话层
sess.run(tf.global_variables_initializer()) #初始化所有可训练参数
 
if not os.path.exists('out/'): #初始化训练过程中的可视化结果的输出文件夹
    os.makedirs('out/')
 
if not os.path.exists('snapshots/'): #初始化训练过程中的模型保存文件夹
    os.makedirs('snapshots/')
 
saver = tf.train.Saver(var_list=tf.global_variables(), max_to_keep=50) #模型的保存器
 
i = 0 #训练过程中保存的可视化结果的索引
 
for it in range(100000): #训练100万次
    if it % 1000 == 0: #每训练1000次就保存一下结果
        samples = sess.run(G_sample, feed_dict={Z: sample_Z(16, Z_dim)})
 
        fig = plot(samples) #通过plot函数生成可视化结果
        plt.savefig('out/{}.png'.format(str(i).zfill(3)), bbox_inches='tight') #保存可视化结果
        i += 1
        plt.close(fig)
 
    X_mb, _ = mnist.train.next_batch(mb_size) #得到训练一个batch所需的真实手写数字(作为判别器的输入)
 
    #下面是得到训练一次的结果,通过sess来run出来
    _, D_loss_curr, dreal_loss_sum_value, dfake_loss_sum_value, d_loss_sum_value = sess.run([D_solver, D_loss, dreal_loss_sum, dfake_loss_sum, d_loss_sum], feed_dict={X: X_mb, Z: sample_Z(mb_size, Z_dim)})
    _, G_loss_curr, g_loss_sum_value = sess.run([G_solver, G_loss, g_loss_sum], feed_dict={Z: sample_Z(mb_size, Z_dim)})
 
    if it%100 ==0: #每过100次记录一下日志,可以通过tensorboard查看
        summary_writer.add_summary(dreal_loss_sum_value, it)
        summary_writer.add_summary(dfake_loss_sum_value, it)
        summary_writer.add_summary(d_loss_sum_value, it)
        summary_writer.add_summary(g_loss_sum_value, it)
 
    if it % 1000 == 0: #每训练1000次输出一下结果
        save(saver, sess, 'snapshots/', it)
        print('Iter: {}'.format(it))
        print('D loss: {:.4}'. format(D_loss_curr))
        print('G_loss: {:.4}'.format(G_loss_curr))
        print()
        

'''
--------------------- 
作者:jiongnima 
来源:CSDN 
原文:https://blog.csdn.net/jiongnima/article/details/80033169 
'''