TensorFlow深度学习-第四章

本章中的内容包含:

  • 数值类型
  • 数值精度
  • 数值运算
  • 张量
    • 张量创建
    • 待优化张量
  • 索引与切片
  • 维度变换
    • 改变视图
    • 插入维度
    • 删除维度
    • 复制数据
    • 广播机制

TensorFlow是一个面向深度学习算法的科学计算库,内部数据保存在张量Tensor对象中,所有的运算操作都是基于张量进行的

image

数据类型

数值类型

数值类型的张量是TF的主要数据载体,包含:

  1. 标量Scalar,单个的实数,维度是0,形状shape[]
  2. 向量Vectorn个实数的有序集合,通过中括号包裹,例如[1,2,4,5,3],维数是1,长度不定,shape为n
  3. 矩阵Matrix,m行n列实数的有序集合,shape为[m,n]
  4. 张量是所有维度数(dim>2)的数组的统称,每个维度也称之为轴Axis。通常将标量、向量、矩阵也统称为张量;张量的维度和形状自行判断
标量

创建标量的关键字是constant,必须通过TF规定的方式去创建张量

import tensorflow as tf

a = 2  # python形式
b = tf.constant(2.0)  # 这才是TF形式
c = tf.constant([1,2.0,3.7])

tf.is_true(b)  # True

返回值中几个具体信息:

  • id:内部索引对象的编号
  • shape:张量的形状
  • dtype:张量的数值精度
向量

向量的定义必须通过List类型转递给tf.constant函数

a = tf.constant([1.0])  # 即使是一个元素也是如此
b = tf.constant([1.0, 2.4, 4.5]) 
矩阵
a = tf.constant([[1,2],
                 [3,4]])  # 2维

b = tf.constant([[[1,2], [3,4]],
                 [[5,6], [7,8]]])  # 3维

字符串类型

字符串类型Strings类型的数据

a = tf.constant("hello tensorflow")

tf.strings模块中提供了常见的工具函数:

  • join
  • length
  • split

布尔类型

TF中支持布尔类型的张量

a = tf.constant([True, False])

# tf中布尔类型和Python的中布尔类型是不等同的
b =  tf.constant(True)
b == True     # 结果是False

数值精度

精度设置和获取

TF支持不同类型的精度,Bit位数越长,精度越高,同时占用的内存空间越大。

  • tf.int16/32/64
  • tf.float16/32/64;tf.float64就是tf.double

需要注意的点:

  1. 高精度转低精度可能会报错
  2. 对于浮点数,高精度的张量可以表示更精准的数据
  3. 实际中,一般使用tf.int32tf.float32
import numpy as np
import tensorflow as tf

# 创建张量的时候指定精度
tf.constant(12345678, dtype=tf.int32)
tf.constant(np.pi, dtype=tf.float64)

通过张量的dtype属性可以获取张量的精度

类型转换

通过tf.cast函数进行转换,需要注意的地方:

  • 保证转换操作的合法性,比如高精度转低精度,可能发生溢出现象
  • 布尔型和整形之间可以转换
  • False默认是0,True表示1;其他非0数字默认是1
a = tf.constant([True, False])
tf.cast(a, tf.bool)  # 1,0

待优化张量

有些张量是需要计算梯度,因此产生了需要计算待优化的张量,专门用来支持梯度信息的记录,使用的函数是tf.Variable

tf.Variable类型在普通的张量类型基础上添加了nametrainable等属性来支持计算的构建。

梯度的计算会消耗大量的资源,且会自动更新相关参数。

  • 不需要优化的张量,比如神经网络的输入Xtf.Variable不进行封装

  • 需要优化的张量,比如神经网络的权重和偏置等,通过tf.Variable进行把包裹

    import tensorflow as tf
    
    a = tf.constant([1,2,3])
    b = tf.Variable(a)
    b.name, b.trainable  
    
    # 直接创建
    c = tf.Variable([1,2,3])
    
    • name属性表示计算图中的名称;
    • trainable表示张量是否需要优化,默认是True,表示优化

创建张量

从Numpy、List对象创建

numpy中的array数组和Python中的list都可以直接用来创建张量,通过tf.convert_to_tensor

import tensorflow as tf
import numpy as np

tf.convert_to_tensor([1,2,3])
tf.convert_to_tensor(np.array([[1,2,3],[4,5,6]])

numpy中默认使用的是64-bit精度,转到TF中使用的是tf.float64

创建全0、全1张量

几个函数记住即可,like只是创建形状相同的张量:

  • tf.ones()/tf.ones_like()
  • tf.zeros()/tf.zeros_like()
tf.ones([2,3])

a = tf.zeros([2,4])
b = tf.ones_like(a)  # 形状相同

自定义数值张量

在创建张量的时候,可以指定初始值:tf.fill(shape, vlaue)

tf.fill([2,3], -1)  # 形状为2*3,值全部是-1

创建已知分布的张量

正态分布和均匀分布是最常见的。

  • 正态分布:卷积神经网络中卷积核张量W,tf.random.normal(shape, mean=0.0, stddev=1.0)
  • 均匀分布:对抗网络中的隐藏层z一般采样自均匀分布,tf.random.uniform(shape, minval=0,maxval=None,dtype=float32)

注意:如果均匀分布中采样的是整数类型,必须指定maxval和数据类型

创建序列

创建序列类型的张量是通过函数tf.range(),标准的格式为:

tf.range(start,end,delta=1)  # 含头不含尾,delta为步长

张量的应用

标量

标量的应用主要是误差值的表示、各种测量指标的表示,入精确度、精度、召回率等

image
out = tf.random.uniform(4,10)  # 随机模拟网络输出
y = tf.constant([2,3,4,5]) # 随机构造样本真实输出标签
y = tf.one_hot(y, depth=10)  # 转成热编码
loss = tf.keras.losses.mse(y, out)  # 计算MSE
loss = tf.reduce_mean(loss)  # 平均MSE
print(mse)

向量

在全连接层和卷积神经网络中,偏置b就是向量b=[b_!,b_2]^T

image

通过高层结口Dense()方式创建地网络层,张量W和b存储在类的内部,由类自动创建。

  • 通过全连接层的bias成员查看偏置b
  • 类的偏置bias初始值全部是0
fc = layers.Dense(3)  # 创建一层Wx+b,输出节点为3
fc.build(input_shape=(2,4))
fc.bias  # 查看偏置

矩阵

矩阵也是非常常见的张量类型,比如全连接层的批量输入X=[b,d_{in}],其中b表示的是输入样本的个数,即batch sized_{in}表示的是输入特征的长度。

w = tf.constant([3,4])  # 定义两个张量
b = tf.constant([3])  
o = x@w + b  # 执行X@W+b
  • X@W+b叫做线性层,也称之为全连接层,通过Dense类直接实现。

  • 通过全连接层的kernel属性查看权重矩阵W

fc = layers.Dense(3)  # 定义全连接层的输出节点为3
fc.build(input_shape=(2,4))  # 定义全连接层的输入节点为4
fc.kernel # 查看权重矩阵

3维张量

三维的张量一个典型应用是表示序列信号,它的格式是𝑋 = [𝑏, 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛, 𝑓𝑒𝑎𝑡𝑢𝑟𝑒 𝑙𝑒𝑛]

  • 𝑏表示序列信号的数量
  • 𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛表示时间维度上的采样点数
  • 𝑓𝑒𝑎𝑡𝑢𝑟𝑒 𝑙𝑒𝑛表示每个点的特征长度

4维张量

4维张量在卷积神经网络中应用的非常广泛,它用于保存特征图Feature maps数据, 格式一般定义为[b,h,w,c]

  • b表示输入的数量
  • h/w表示特征图的高宽
  • c表示特征图的通道数量

对于含有 RGB 3 个通道的彩色图片,每张图片包含了 h 行 w 列像素点,每个点需要 3 个数 值表示 RGB 通道的颜色强度,因此一张图片可以表示为[h, w, 3]

# 创建32x32的彩色图片输入,个数为4
x = tf.random.normal([4,32,32,3]) # 创建卷积神经网络
layer = layers.Conv2D(16,kernel_size=3) out = layer(x) # 前向计算
out.shape # 输出大小

# 卷积核张量也是4维张量,通过kernel属性来查看
layer.kernel.shape

索引和切片

索引

  1. 从0开始
  2. 两种方式
    1. [i][j][k]...
    2. [i,j,k,…]
x = tf.random.normal([4, 32, 32, 3])
x[0]
x[0][1][2]

切片

通过𝑠𝑡𝑎𝑟𝑡: 𝑒𝑛𝑑: 𝑠𝑡𝑒𝑝切片方式提取数据

  • 含头不含尾
  • step步长,可以为负数

关于冒号和三个点的使用:都是表示某个维度上的所有数据

x = tf.random.normal([4, 32, 32, 3])
x[1:3]
x[0,::]
x[0, 0:28, 2:28:2, :]
x[::-2]
x[0:2,...,1:]

维度变换

线性层的批量形式
Y=X@W+b
假设:

  • X 包含了 2 个样本,每个样本的特征长度为 4,X 的 shape 为[2,4]
  • 线性层的输出为3个节点,其shape为[4,3]
  • 偏置b的shape为[3]

那么不同shape的张量之间如何进行相加?此时,维度变换可以解决


image

改变视图reshape

张量存储
  1. 张量的存储体现张量在内存上保存为一块连续的存储区域
  2. 张量的存储需要人为跟踪
  3. shape中相对靠左的维度称之为大维度;相对靠右的维度称之为小维度
张量视图

语法格式为tf.reshape(x, new_shape)

  • 改变张量的视图始终不改变张量的存储顺序
  • 视图变换需要满足新视图的元素总量与内存区域大小相等即可
  • 为了能够正确恢复出数据,必须保证张量的存储顺序与新视图的维度顺序一致
  • 在实现reshape操作的时候,需要记住张量的存储顺序
  • 参数-1表示长度的自定推导
x = tf.random.normal([4, 32, 32, 3])
tf.reshape(x, [2,-1])
tf.reshape(x,[2,4,12])
tf.reshape(x,[2,-1,3])

增删维度

增加维度
  • 增加一个长度为1的维度相当于是给原数据的维度增加一个新维度,可以理解成改变视图的一种特殊方式
  • 数据的存储方式不变,通过函数tf.expand_dims(x,axis)来实现
  • axis为正,表示在当前维度之前插入一个新维度;axis为负数,在当前维度之后插入一个新维度
x = tf.random.uniform([28,28],maxval=10,dtype=tf.int32)
x = tf.expand_dims(x,axis=2)
删除维度
  • 增加维度的逆操作,只能删除长度为1的维度
  • 不改变张量的存储方式
  • 通过tf.squeeze(x, axis)来实现
  • axis表示删除维度的索引号;如果不指定,默认删除全部长度为1的维度
x = tf.random.uniform([1,28,28,1],maxval=10,dtype=tf.int32)
x = tf.squeeze(x, axis=2)
tf.squeeze(x)

维度交换

  • 改变张量的存储,后续的所有操作都是基于新的存储顺序
  • 改变张量的视图
  • 通过tf.transpose(x, perm)来实现;其中perm表示新维度的顺序list
x = tf.random.normal([2,32,32,3])
tf.transpose(x,perm=[0,3,1,2])

数据复制

通过函数tf.tile(x, multiples)来实现,关于参数multiples

  1. 1表示不复制
  2. 2表示长度为2倍,即复制1份
  3. 3表示长度为3倍,即复制2份;类推

复制操作会创建一个新的张量来保存复制后的张量,涉及到大量的IO操作,运算代价大

b = tf.constant([1,2])
b = tf.expand_dims(b, axis=0)  # 插入新维度
b = tf.tile(b, multiples=[2,1])  # axis=0上复制1份

x = tf.range(4)
x = tf.reshape(x, [2,2])
x = tf.tile(x, multiples=[1,2])  # 列上复制
x = tf.tile(x,multiples=[2,1])  # 行上复制

广播机制Broadcasting

通过函数tf.broadcast_to(x, new_shape)实现

特点
  • 自动扩展,一种轻量级张量复制;在逻辑上扩展张量数据的形状
  • 对于大部 分场景,Broadcasting 机制都能通过优化手段避免实际复制数据而完成逻辑运算
  • 通过优化手段避免实际复制数据而完成逻辑运算,较少计算开销
  • 广播机制不会立即复制数据,逻辑上改变张量的形状
x = tf.random.normal([2,4])
w = tf.random.normal([4,3])
b = tf.random.normal([3])
y = x@w+b   # 等价于y = x@w + tf.broadcast_to(b,[2,3])  实现自动广播
核心思想

广播机制的核心思想是普适性,同一份数据能够适合于不同的位置

  • 长度为1,默认数据适合当前维度的其他位置
  • 长度不是1,增加维度后才会才适合
image

有些运算可以在处理不同 shape 的张量时,会隐式地调用广播机制

image

数学运算

方法 作用
// 整除
% 余除
tf.power(x,a),x**a 乘方
tf.square(x) 平方
tf.sqrt(x) 平方根
tf.power(a,x),a**x 指数运算
tf.exp(x) 自然指数
tf.math.log(x) 自然对数
log_ax=\frac{log_ex}{log_ea} 其他底数的对数

矩阵相乘

两种方式实现:

  1. @
  2. tf.matmul(a,b)函数

实战-前向传播

采用的手写数字图片集数据:

  • 输入节点数是784,第一层节点数是256,第二次层是128,第三层是10
  • 3层神经网络的实现

o𝑢𝑡 = 𝑟𝑒𝑙𝑢\{𝑟𝑒𝑙𝑢\{𝑟𝑒𝑙𝑢[𝑋@𝑊 + 𝑏 ]@𝑊 + 𝑏 \}@𝑊 + 𝑏 \}

#  计算每个线性函数的张量参数
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros([128]))
w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))
b3 = tf.Variable(tf.zeros([10]))

# 将输入数据进行维度变化
# [b, 28, 28] => [b, 28*28]
x = tf.reshape(x, [-1, 28*28])
 
# 非线性函数的计算
h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 256])
h1 = tf.nn.relu(h1)  # 得到输出函数

# 完成剩下两个非线性函数
# [b, 256] => [b, 128]
h2 = h1@w2 + b2
h2 = tf.nn.relu(h2)
# [b, 128] => [b, 10]
out = h2@w3 + b3

# 计算均方差MSE
# mse = mean(sum(y-out)^2)
# [b, 10]
loss = tf.square(y_onehot - out)
# mean: scalar
loss = tf.reduce_mean(loss)

# 计算梯度
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3]
                      
# w1 = w1 - lr * w1_grad
w1.assign_sub(lr * grads[0])
b1.assign_sub(lr * grads[1])
w2.assign_sub(lr * grads[2])
b2.assign_sub(lr * grads[3])
w3.assign_sub(lr * grads[4])
b3.assign_sub(lr * grads[5])
  • tape.gradient()求出网络参数的梯度信息
  • assign_sub():实现参数的自我更新
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容