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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#计算图和会话的基本使用
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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. 简单前馈神经网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*-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版

定义前向传播流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# -*-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

定义训练流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# -*-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版

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
'''