这个文档作为日常我使用tensorflow的一些小笔记。
学习资源
使用tensorboard进行网络结构可视化
sess = tf.Session()
writer = tf.summary.FileWriter("logs/", sess.graph)
writer.close()
在logs的上一个目录打开powershell,运行:
tensorboard --logdir=logs --host=0.0.0.0
这样就可以在浏览器中打开0.0.0.0进行网络结构的查看了。–host也可以不加,我的chrome浏览器用不了默认的地址,所以这样解决了。
如果shell端显示了地址但是浏览器打不开,就换一个地址,经测试这个比较容易打开:
tensorboard --logdir=logs --host=127.0.0.1
tensor flow中的name_scope的理解与使用:
程序的运行过程中,可能训练集与测试集会稍微改一下(不重要的)参数,那么又希望测试集能复用训练集的模型参数,就可以使用reuse方法,这与name_scope紧密相关。
看这段代码,取自GANs网络的一部分。
with tf.variable_scope('discriminator'):
# 对抗网络将真实数据与生成数据进行鉴别,这里需要复用其参数
D_00 = tf.layers.dense(input, 256, tf.nn.elu,name = 'latent')
D_01 = tf.layers.dense(D_00, 128, tf.nn.elu,name = 'latent_1')
D_02 = tf.layers.dense(D_01, 64, tf.nn.elu,name = 'latent_2')
prob_real = tf.layers.dense(D_02, 1, tf.nn.sigmoid, name = 'out')
# reuse the layers for generator
D_l1 = tf.layers.dense(gen,256,tf.nn.elu,name='latent',reuse=True) #gen为生成的数据,D_l1与D_00名字相同,并在同一个name_scope下reuse,那么D_l1与D_l0完全相同
D_l2 = tf.layers.dense(D_l1,128,tf.nn.elu,name='latent_1',reuse=True)
D_l3 = tf.layers.dense(D_l2,64,tf.nn.elu,name='latent_2',reuse=True)
prob_false = tf.layers.dense(D_l3,1,tf.nn.sigmoid,name= 'out', reuse = True)
#注,可以通过 var.name()打印查看变量的名字
LSTM的使用
看例子,单层lstm网络
rnn_cell = tf.contrib.rnn.BasicLSTMCell(num_units=batch_size)
#
outputs, final_state = tf.nn.dynamic_rnn(
cell = rnn_cell, # 选择传入的cell
inputs = image, # 传入的数据,维度为[time_step, input_size]
initial_state = None, #初始化状态
dtype = tf.float32, #数据类型
time_major = False,
)
output = tf.layers.dense(inputs=outputs[:, -1, :], units=n_classes, activation=tf.nn.sigmoid) # 取最后一个step的数据
output输出的纬度为[batch_size, time_step, num_units]。
也就是说它为最后一层(如果为多层神经网络)一个step的输出结果。我们想拿它输出的最后一个step的数据,就通过output[:,-1,:]的方式。
而state是每一次最后那个step的输出,那么其维度为[batch_size, num_units]。
MultiRNNCell
看下面这个例子
# 写法一
# cell = tf.nn.rnn_cell.BasicLSTMCell(hidden_size, forget_bias=0.0, state_is_tuple=True)
# cell2 = tf.nn.rnn_cell.BasicLSTMCell(hidden_size2, forget_bias=0.0, state_is_tuple=True)
# mlstm_rnn = tf.contrib.rnn.MultiRNNCell([cell,cell2], state_is_tuple=True)
#写法二
# 为了简便,导入rnn.
from tensorflow.contrib import rnn
mber_units = [128,64,128] #例如我要堆叠三层的lstm网络
rnn_cell = [rnn.BasicLSTMCell(num_units=n) for n in number_units]
mlstm_rnn = rnn.MultiRNNCell(rnn_cell)
#
outputs, final_state = tf.nn.dynamic_rnn(
cell = mlstm_rnn, # 选择传入的cell
inputs = image, # 传入的数据
initial_state = None, #初始化状态
dtype = tf.float32, #数据类型
time_major = False,
)
out_put = tf.layers.dense(inputs=outputs[:, -1, :], units=n_classes, activation=tf.nn.sigmoid) #
# 查看输出的final_state,这里final_state是一个元祖,其与网络的层数有关.
print(final_state[0].h.shape) #第一层的state的h state
print(final_state[1].h.shape) #第一层的state的c state
print(final_state[1].h.shape)
关于参数和输出数据维度的理解
- 在图中,每一个小方框为一个前馈网络层,num_units代表了这个层中隐藏的神经元的个数。那么,h(t)的维度等于num_units, x t x_t xt为 输入向量,若其为28维,则每一个前馈网络层的输入为 h ( t − 1 ) h(t-1) h(t−1)与 x t x_t xt进行拼接,也就是156维。
- 其实是只有一个前馈网络层(即图中的小方框只有一个)的,不同的time_step共享同一套参数,并通过数据的不断刷新而进行网络参数的更新。1、2
- 许多文章中,都是把num_units设置为与batch_size相同的值,实际上这二者并没有直接的联系。就像常规的神经网络层的的维度可以自由设置。
- state的输出数据,与网络的层数有关。看上面的代码的最后三行。
回到tensorflow,一些基础语法部分
后面打算利用tenorflow实现非负性矩阵填充算法(NMF),这里先准备一些基础的东西。
在使用梯度下降法**更新参数时,可以使用tf.assign()**方法。
看例子
import tensorflow as tf
x = tf.Variable(1)
x_new = tf.assign(x,2) # x.assign(x+1)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess.run(x) #x 值为1
sess.run(y) # x值更新为2,同时将更新后的值赋予y
print(x.eval())
** tf的强大的求导功能**
以函数关系
y
=
f
(
x
1
,
x
2
)
y =f(x_1,x_2)
y=f(x1,x2)为例,我们想要求
y
y
y关于
x
1
,
x
2
x_1,x_2
x1,x2的偏导数,那么直接使用函数:
∂
x
1
,
∂
x
2
=
t
f
.
g
r
a
d
i
e
n
t
s
(
x
s
=
[
x
1
,
x
2
]
,
y
s
=
y
)
\partial x_1,\partial x_2 = tf.gradients(xs = [x_1,x_2], ys = y)
∂x1,∂x2=tf.gradients(xs=[x1,x2],ys=y),代码形式为
dx1, dx2 = tf.gradients(xs=[x1,x2], ys=y)
非常的nice。
结合上面两步,我们就可以进行参数更新了:
x1 = x1.assign( x1 - a * dx1)
tensorflow保存模型与重新加载
保存
#模型中会有一些占位符作为输入
X = tf.placeholder(tf.float32, shape=[None, Col]) # 网络输入
Y = tf.placeholder(tf.float32,[None, Col]) # 网络输出
#模型的一些中间值或者输出值
cost = tf.reduce_mean(tf.square(Y - output)) #output为模型的一个返回值
#保存
saver = tf.compat.v1.train.Saver()
tf.add_to_collection('s_cost',cost)
sess = tf.Session()
#当模型完成了训练之后
saver.save(sess,"save/my_model") #会将模型保存到一个save文件夹下,名字为my_model,实际上会生成四个这前缀的文件
文件二,模型的读取
with tf.compat.v1.Session() as sess:
saver = tf.compat.v1.train.import_meta_graph("save/model.meta")
saver.restore(sess, tf.train.latest_checkpoint('save/'))
mb_data = test[:mb_size,:] # minibatch data
graph = tf.get_default_graph()
# tensor_name_list = [tensor.name for tensor in graph.as_graph_def().node]# 得到当前图中所有变量的名称
X = graph.get_tensor_by_name('Placeholder:0') # 网络输入
Y = graph.get_tensor_by_name('Placeholder_1:0') # 网络输入
# real = graph.get_operation_by_name('real')
s_cost = graph.get_collection('s_cost')[0]
cost= sess.run([s_cost],feed_dict={X:inputs, Y:mb_mat})
下面总结知识点
- 保存的时候,非占位符变量需要通过添加,如 tf.add_to_collection(‘s_cost’,cost),其中单引号的内容是它保存后的名字,在读取的时候根据这个名字进行检索
- 占位符保存的时候没有名字,就通过如X = graph.get_tensor_by_name(‘Placeholder:0’) # 网络输入的方式来获得。这个也是挺坑的,那么,可以根据 tensor_name_list = [tensor.name for tensor in graph.as_graph_def().node] 得到当前图中所有变量的名称,将这个name list打印出来就可以查看一下。
- 在读取的时候,两步,先通过 tf.compat.v1.train.import_meta_graph(‘模型名字.meta’),然后通过saver.restore(sess, tf.train.latest_checkpoint(‘save/’))读取。
- 获取非占位符,通过 graph.get_collection(‘s_cost’)[0],其中’s_cost’为对应变量保存时的名字。末尾必须加[0]
- 加载完需要的输入输出变量之后,就可以通过sess.run来跑模型了。
整体而言还是挺坑的,很多博客上的都不太行,自己搞了两个小时才通。palceholder应该可以命名的,后面再说了。想去使用pytorch了==
将tensorflow的warning取消输出
from warnings import simplefilter
simplefilter(action='ignore', category=FutureWarning)