声明
机缘巧合下,如雷贯耳了TensorFlow框架。略览后欣喜,遂决心深入研究。文章是本人在自研人工智能的道路上,通过阅读、视频、博客等形式记录的所见所闻。介于小白鼠属性满格,难免漏洞,请诸位大神不吝赐教,也请其他小白不要纠结。若涉及侵权,烦请告知,必删;若阅完有点滴心得,定窃喜不枉辛劳。
计算机自发明以来,一直扮演人类祖先的角色:以自我为中心(计算密集型,涉及CPU计算、GPU计算等,比如TensorFlow),疯狂攫取外界资源(IO密集型——涉及HTTP请求、磁盘操作等,比如Django、Scrapy)。直至现今,疯狂到想让电脑直接变成人脑。哎,技术无罪,有罪的是掌握技术的人。为什么要写这段呢,因为我也不知道。
TensorFlow简单三步骤:使用 tensor 表示数据;使用图 (graph) 来表示计算任务;在会话(session)中运行图。TensorFlow字面意思是由Tensor(张量)和Flow(流)组成。实际上还包括:
1、Operation,专门运算的操作节点;
2、Graph图,整个程序的结构,包括输入,输入的流向,输出等;
3、Session会话,专门用于运行程序所建立的图。
TensorFlow分为前端系统和后端系统,前端系统(Tensor、Operation、Graph)负责定义程序的图的结构,后端系统(Session)负责运算图的结构。TensorFlow程序通常被组织成一个构建阶段和一个执行阶段。在构建阶段,op的执行步骤被描述成一个图。在执行阶段,使用会话执行执行图中的op。
Tensor
张量,重要概念。类型是对numpy的ndarray类型的封装,类似于数组;阶,类似于数组的维。张量有如下属性:operation、name、shape(0维表示为(),1维表示为(2),2维表示为(?,2))、graph。在TensorFlow中,有很多操作张量的函数,有生成张量、创建随机张量、张量类型与形状变换和张量的切片与运算。
tf.zeros(shape, dtype=tf.float32, name=None)
创建所有元素设置为零的张量。此操作返回一个dtype具有形状shape和所有元素设置为零的类型的张量。
tf.zeros_like(tensor, dtype=None, name=None)
给tensor定单张量(),此操作返回tensor与所有元素设置为零相同的类型和形状的张量。
tf.ones(shape, dtype=tf.float32, name=None)
创建一个所有元素设置为1的张量。此操作返回一个类型的张量,dtype形状shape和所有元素设置为1。
tf.ones_like(tensor, dtype=None, name=None)
给tensor定单张量(),此操作返回tensor与所有元素设置为1 相同的类型和形状的张量。
tf.fill(dims, value, name=None)
创建一个填充了标量值的张量。此操作创建一个张量的形状dims并填充它value。
tf.constant(value, dtype=None, shape=None, name='Const')
创建一个常数张量。
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
从截断的正态分布中输出随机值,和 tf.random_normal() 一样,但是所有数字都不超过两个标准差
tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
从正态分布中输出随机值,由随机正态分布的数字组成的矩阵
tf.random_uniform(shape, minval=0.0, maxval=1.0, dtype=tf.float32, seed=None, name=None)
从均匀分布输出随机值。生成的值遵循该范围内的均匀分布 [minval, maxval)。下限minval包含在范围内,而maxval排除上限。
tf.random_shuffle(value, seed=None, name=None)
沿其第一维度随机打乱
tf.set_random_seed(seed)
设置图级随机种子
tf.string_to_number(string_tensor, out_type=None, name=None)
将输入Tensor中的每个字符串转换为指定的数字类型。注意,int32溢出导致错误,而浮点溢出导致舍入值
tf.shape(input, name=None)
返回张量的形状
tf.squeeze(input, squeeze_dims=None, name=None)
这个函数的作用是将input中维度是1的那一维去掉。但是如果你不想把维度是1的全部去掉,那么你可以使用squeeze_dims参数,来指定需要去掉的位置
tf.expand_dims(input, dim, name=None)
该函数作用与squeeze相反,添加一个指定维度
张量频繁用到的操作是形状的改变,即维度的变化(一维变二维,或者两列变三列)。涉及静态改变形状和动态改变形状。
静态形状的改变指:不改变维度,只改变维度中的行列数;一旦张量形状固定,不能再次进行修改。
动态形状的改变指改变维度。动态形状和静态形状的区别:有没有生成一个新的张量。两种形状的改变都不能改变张量的总大小。
如果你定义了一个没有标明具体维度的占位符,即用None表示维度,那么当你将值输入到占位符时,这些无维度就是一个具体的值,并且任何一个依赖这个占位符的变量,都将使用这个值。
注意:
1、转换静态形状的时候,1-D到1-D,2-D到2-D,不能跨阶数改变形状;
2、对于已经固定或者设置静态形状的张量/变量,不能再次设置静态形状;
3、tf.reshape()动态创建新张量时,元素个数不能不匹配;
4、运行时候,动态获取张量的形状值,只能通过tf.shape(tensor)[]。
Operation
只要使用TensorFlow框架的API定义的函数都是Operation,Tensor也是Operation,指代的是数据。
Graph
图由整个程序构造,可用系统默认的图,也可自定义。一个Session只能运行一个图。
Session
相当于中间件,对上解析并运行定义的图结构,对下分配CPU或者GPU资源进行计算,掌握资源(变量、队列、线程等)。如果不与图绑定,那会话使用默认的图。
Session可能返回错误:RuntimeError、TypeError、ValueError。
Feed机制
将某些特殊的操作指定为"feed"操作,标记的方法是使用 tf.placeholder() 为这些操作创建占位符,并且在Session.run方法中增加一个feed_dict参数。突然感觉灭霸还是个心细的紫叔叔,让矮人族打造无限手套的时候特意留了六个位置,因为他知道,早晚要用到这六个位置,跟TensorFlow里的占位符简直异曲同工。如果没有正确提供feed参数,placeholder() 操作将会产生错误。
tf.placeholder(tf.float32, [None, 3])# 行数不固定,列数固定
run(fetches, feed_dict = None, graph = None):训练模型时实时提供数据进行训练,用placeholder占位符和feed_dict结合使用
举个栗子
import tensorflow as tf
import os
# 忽略不必要的警告信息
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# 创建一张图包含Operation和Tensor,两个图之间互不干扰
g = tf.Graph()
with g.as_default():
c = tf.constant(11.0)
print(g)
print(c.graph)
# 实现一个加法运算,定义两个Tensor和一个Operation
a = tf.constant(5.0)
b = tf.constant(6.0)
sum = tf.add(a, b)
# 不是Operation不能运行,但是有重载机制,默认会给运算符重载成op类型(var + a可执行)
# var = 2
# 默认的图,相当于给程序分配内存
graph = tf.get_default_graph()
print(graph)
# 一个会话只能运行一个图,可在会话中指定图运行
# 上下文管理器with默认会释放资源
with tf.Session(graph=graph) as sess:
print(sum) # Tensor("Add:0", shape=(), dtype=float32),分别为op名字,张量形状,张量数据类型
print(a.graph)
print(sum.graph)
print(sess.graph)
print(sess.run(sum)) # run()执行图
# 静态形状和动态形状
# 对于静态形状而言,一旦张量形状固定,不能再次进行修改
# 动态形状可新增一个,进行修改,但是元素数量要相同
# 如果需要维度修改,必须用动态形状修改
plt = tf.placeholder(tf.float32, [None, 2])
print(plt)
plt.set_shape([3, 2])
print(plt)
# plt.set_shape([4, 2]) # 不能修改
plt_reshape = tf.reshape(plt, [1, 6])
print(plt_reshape)
with tf.Session() as sess:
pass
# 变量op
# 1、变量op可持久化保存,普通张量op不能
# 2、当定义一个变量op时,一定要在会话中取运行初始化
# 3、name参数在tensorboard中使用显示,防止冲突
a = tf.constant([1, 2, 3, 4, 5], name="a")
var = tf.Variable(tf.random_normal([1, 2], mean=0, stddev=1.0))
print(a, var)
# 必须做一步显示的初始化op
init_op = tf.global_variables_initializer()
with tf.Session() as s:
s.run(init_op)
# 把程序的图结构写入事件,把指定的图写入事件中
filewriter = tf.summary.FileWriter("./summary", graph=s.graph)
print(s.run([a, var]))