一年前写了python学习系列,持续大约一个月。现在回想起来,当时确实是打下了一定的python基础,现在写代码都是在用python,而很少用c++了。
最近要做的研究方向与深度学习很相关,所以在学这方面的内容。之前学了一段时间tensorflow,但很散乱,断断续续的。前两天突然想到以前写的python学习系列,可以用写文章的方式来整理,于是就有了此系列文章。希望在暑假期间可以更完吧。
还是老规矩,因为是整理自己思路用的,所以有的部分简略,如有不理解的内容请留言。
准备环境
Anaconda 3
tensorflow 1.90
pycharm 2018.1.4
基本概念
张量
张量(tensor)是tensorflow(以下简称tf)中的一个最基本的数据结构。张量可以想象为一个n维的数组
1
1
。n称为张量的阶数。
下面是创建一个一阶张量的代码。
arr = tf.random_uniform([4])
代码的本意是产生一个长为4的、填充值为均匀分布随机数的一阶张量,但在输入执行后,会发现输出类似如下结果:
<tf.Tensor 'random_uniform_1:0' shape=(4,) dtype=float32>
而且不能通过通常意义上的变量获取方法来修改或者访问,是怎么回事?
会话
在tf中的基本单元都需要在“会话”中完成运行,而不是直接在python的环境下。
会话的创建和使用如下:
sess = tf.session()
sess.run(arr)
然后就会发现输出了张量arr的值。
占位符与变量
张量和一般的变量容易混淆,但实际上是完全不同的概念。可以这么认为,tf中的变量和一般程序的变量是一个含义,而张量是其变量应有的值。
就如在c++中声明了一个int变量a,然后赋值2,那么a就是变量,2就是张量(0阶张量),它充当的是一个字面量,是一个右值。所以变量是有值的。
而对于占位符来说,一般变量又相当于它的右值。通过占位符可以规定输入变量的格式、类型。但占位符本身是没有值的,只是起一个接口的作用。如下示例:
var1 = tf.Variable(arr)
sess.run(tf.global_variables_initializer())
var2 = np.random.rand(4,4)
x = tf.placeholder(tf.float32, shape=(4,4))
sess.run(x, feed_dict={x: var2})
x是一个占位符,var2是一个符合其形状的数组变量,因此在运行时将其“填充”到占位符上,从而运行出结果。
var1是一个变量,通过输入张量arr作为初始值,再运行初始化算子进行初始化。注意:这里的var1是不能通过feed_dict输入给x的,为什么?因为占位符所接受的变量是“外界变量”,不是在其会话体系内的;而tf内部的变量是定义在会话体系内的,不会被占位符识别。
流与计算图
流(flow)是对张量的一个形象描述,即张量在图中流动。这个图就是计算图 2 2 。
计算图的创建和使用如下:
g = tf.Graph()
with g.as_default():
v=tf.get_variable('v',initializer=tf.zeros_initializer()(shape = [1]))
with tf.Session(graph=g) as sess:
tf.global_variables_initializer().run()
with tf.variable_scope('', reuse=True):
print(sess.run(tf.get_variable('v')))
这段代码通过Graph()函数创建计算图g,然后在该计算图中定义了一个一维变量v。然后再在会话中使用该计算图,调用其变量。
注意到其中的variable_scope 和 get_variable是与我们之前提到的变量相关的方法,在变量繁多时,需要对变量进行管理。
变量的管理
get_variable()是一种创建变量的方法,与Variable()不同的是,它具有对变量名的管理能力。如下代码 3 3 :
w_1 = tf.Variable(3, name="w_1")
w_2 = tf.Variable(1, name="w_1")
print(w_1.name)
print(w_2.name)
w_1 = tf.get_variable(name="w_1",initializer=1)
w_2 = tf.get_variable(name="w_1",initializer=2)# 报错
使用get_variable()方法创建的变量,会在其所在变量空间中注册其变量名,使得这个变量名在该区域唯一。而Varible()是每一次调用都会创建一个新的变量,并且不保留其记录,不利于变量管理。
get_variable的参数如下:
参数 | 说明 | 举例 |
---|---|---|
name | 唯一标识符 | ‘var1’ |
shape | 变量的形状 | [2,3] |
initializer | 初始化方法 | tf.random_normal_initializer |
variable_scope就是之前提到的用来注册变量名的变量空间,它有些类似命名空间,但与 name_scope 又有所不同 4 4 ,在后面也会提到。它的最大的用处是实现变量共享。
使用方法如下:
with tf.Session() as sess:
tf.global_variables_initializer().run()
with tf.variable_scope('scp1', reuse=True):
print(sess.run(tf.get_variable('v1')))
with tf.variable_scope('scp1', reuse=True):
print(sess.run(tf.get_variable('v1')))
其中有一个参数reuse,用以标记变量是否共享,标为True后,相同变量域下同名变量可以共享数据。
可视化
事实上,在刚刚计算图的部分中,并没有涉及到操作数之间的运算。下面是一个有运算过程的例子。
g = tf.Graph()
with g.as_default():
v1 = tf.get_variable('v', shape=[2, 3], initializer=tf.random_normal_initializer)
with tf.Session(graph=g) as sess:
writer = tf.summary.FileWriter(r"tsbd",sess.graph)
mr = tf.random_uniform([3, 2])
tf.global_variables_initializer().run()
with tf.variable_scope('', reuse=True):
v = tf.get_variable('v')
op_mat = tf.matmul(v, mr)
print(sess.run(op_mat))
writer.close()
使用tensorboard可视化这个过程
5
5
:
实践
根据已学的内容来实现一个单层神经网络,不涉及训练过程,只是描述出其结构。
import tensorflow as tf
import numpy as np
hidden_nodes = 5
input_size = 3
g = tf.Graph()
with g.as_default():
with tf.variable_scope('input_layer'):
input_x = tf.placeholder(tf.float32, shape=[None, input_size])
with tf.variable_scope('hidden_layer'):
Wh = tf.get_variable('W', shape=[input_size, hidden_nodes])
bh = tf.get_variable('b', shape=[hidden_nodes])
hout = tf.nn.relu(tf.add(tf.matmul(input_x, Wh), bh))
with tf.variable_scope('output_layer'):
Wo = tf.get_variable('W', shape=[hidden_nodes, 1])
bo = tf.get_variable('b', shape=[1])
out = tf.nn.sigmoid(tf.add(tf.matmul(hout, Wo), bo))
with tf.Session() as sess:
writer = tf.summary.FileWriter(r"C:\tsbd", sess.graph)
tf.global_variables_initializer().run()
print(sess.run(out, feed_dict={input_x: np.random.random((3, 3))}))
writer.close()
总结
本次学习了tensorflow的一些基本概念和数据结构。张量是tf中的基本数据结构,占位符是定义了结构而未填充数值的接口,变量有两种定义方式,其中一种方式可以进行变量共享,在变量域下能够对变量进行管理并且有利于可视化,要使用tf提供的运算作用于张量。
最后,利用这些基础知识实现了一个单层神经网络。