第五章主要是介绍了TensorFlow2
的几个高阶操作,包含:
合并与分割
数据统计
张量比较
填充与复制
数据限幅
张量的高级操作
数据加载及预处理
合并与分割
合并
将多个张量在一个维度上合并成一个张量。合并有分为两种:拼接concatenate
和堆叠stack
。
-
拼接
tf.concat(x, axis)
不会产生新的维度
约束条件是:非合并的维度必须是一致的
axis
指定拼接的轴;x
条件是待合并的张量
import tensorflow
a = tf.random.normal([4,8,5])
b = tf.random.normal([6,8,5])
tf.concat([a,b], axis=0) # 结果是[10,8,5]
-
堆叠
tf.stack(x, axis)
创建新的维度,新维度的位置是任意的
可以同时堆叠多个张量
进行堆叠的张量维度必须一致
-
axis
的用法和tf.expand_dims
中相同: 表示当前维度之前插入
表示当前维度之后插入
import tensorflow
a = tf.random.normal([6,8])
b = tf.random.normal([6,8])
tf.stack([a,b], axis=0) # 结果是[2,6,8]
tf.stack([a,b], axis=-1) # 结果是[6,8,-1]
分割
合并的逆操作,将一个张量分成多个张量,通过tf.split(x,axis,num_or_size_splits)
实现
-
x
:待分割张量 -
axis
:分割的维度索引号 -
num_or_size_splits
:切割方案- 当
num_or_size_splits
为单个数值时,如10,表示切割 为 10 份 - 当
num_or_size_splits
为List
时,每个元素表示每份的长度,如[2,4,2,2]表示 切割为 4 份,每份的长度分别为 2,4,2,2
- 当
x = tf.random.normal([10,35,8])
result = tf.split(x, axis=0, num_or_size_splits=10) # 结果是[1,35,8]
result = tf.split(x,axis=0,num_or_size_splits=[4,2,2,2]) # 10=4+2+2+2,第一个维度上进行分割
result[0] # 查看第一个张量
如果希望在某个维度上全部按照长度为1进行分割,使用tf.unstack
,切割长度固定为1。
x = tf.random.normal([10,35,8])
result = tf.unstack(x,axis=0)
len(result) # 结果为10;shape为[35,8]
数据统计
向量范数Vector norm
通过函数tf.norm(x, ord)
-
x
:表示张量 -
norm
:指定范数类型
常见的3
种范数有:
-
范数:所有元素绝对值之和
- 范数:所有元素的平方和,再开根号
- 无穷范数:所有元素绝对值的最大值
import numpy as np
import tensorflow tf
x=tf.ones([2,3])
tf.norm(x,ord=1)
tf.norm(x,ord=2)
tf.norm(x,ord=np.inf) # 无穷
最大/最小、均值、和
函数 | 作用 |
---|---|
tf.reduce_max | 最大 |
tf.reduce_min | 最小 |
tf.reduce.mean | 均值 |
tf.reduce_sum | 和 |
上述的函数都可以指定axis
;如果不指定,tf.reduce_.*
函数会求解出全局元素的最大、最小、均值、和。
out = tf.random.normal([4,10])
y = tf.constant([1,2,2,0])
y = tf.one_hot(y, depth=10) # 热编码
loss = keras.losses.mse(loss)
loss = tf.reduce_mean(loss) # 平均误差
最值的索引号
通过tf.argmax(x, axis)
和tf.argmin(x, axis)
out = tf.random.normal([2,10])
out = tf.nn.softmax(out, axis=1) # 通过softmax转成概率
pred = tf.argmax(out, axis=1)
张量比较
tf.equal(a,b)
和tf.math.equal(a,b)
比较两个张量是否相等,返回的是布尔型张量
out = tf.random.normal([100,10])
out = tf.nn.softmax(out, axis=1) # 输出转换为概率
pred = tf.argmax(out, axis=1) # 选取预测值
y = tf.random.uniform([100],dtype=tf.int64,maxval=10)
out = tf.equal(pred,y) # 返回的是bool值
out = tf.cast(out, dtype=tf.float32)
correct = tf.reduce_sum(out) # 统计True的个数
填充与复制
填充tf.pad
需要补充的信号开始或者结束处填充足够的特定数值;通过tf.pad(x, axis)
实现
a = tf.constant([1,2,3,4,5])
b = tf.constant([9,8,7])
b = tf.pad(b, [[6,0]]) # 填充
tf.stack([a,b], axis=0)
在NLP中,需要加载不同长度的句子,需要设定一个阈值来固定最大的句子长度
total_words = 10000
max_review_len = 80
embedding_len = 100 # 词向量长度
(x_train, y_train), (x_test, y_test) = keras.datasets.imdb.load_data(num_words=total_words)
x_train = keras.preprocessing.sequence.pad_sequences(x_train, maxlen=max_review_len,truncating='post',padding='post')
x_test = keras.preprocessing.sequence.pad_sequences(x_test,maxlen=max_review_len,truncating='post',padding='post')
print(x_train.shape,x_test.shape)
keras.preprocessing.sequence.pad_sequences
迅速完成句子的填充与截断工作
复制tf.tile
tf.tile()
函数实现长度为1的维度复制的功能;tf.tile()
函数可以在任意维度将数据重复复制多份
x = tf.random.normal([4,32,32,3])
tf.tile(x, [2,3,3,1])
数据限幅
-
tf.maximum()
实现下限幅 -
tf.minimum()
实现上限幅 -
tf.clip_by_vlaue
实现双边限幅
x = tf.range(9)
tf.maximum(x, 2) # 下
tf.minimum(x, 7) # 上
tf.clip_by_vlaue(x, 2, 7)
高级操作
tf.gather
根据索引号收集数据tf.gather
;对于不规则的索引也非常适合。索引从0开始
x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
tf.gather(x, [0,1], axis=0)
tf.gather(x, [0,4,2,13,30], axis=1)
假设希望:抽查第 2 个班级的第 2 个同学的所有科目, 第 3 个班级的第 3 个同学的所有科目,第 4 个班级的第 4 个同学的所有科目。那么怎么实现呢?
方法1
分别取出3个,再进行堆叠stack操作
x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
x[1,1] # x[2,2] x[3,3]
tf.stack([x[1,1], x[2,2], x[3,3]])
tf.gather_nd
指定每次采样点的坐标来实现采样多个点的目的。上面问题的解决
tf.gather_nd(x, [[1,1], [2,2], [3,3]])
tf.boolean_mask
通过掩码的方式来获取数据采样;掩码的长度必须和对应维度的长度一致
x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
# mask = [True, False, True, False]
tf.boolean_mask(x, mask = [True, False, True, False], axis=0) # 第一个维度是班级
多维度掩码采样方式
希望采样第 1 个班级的第 1-2 号学生,第 2 个班级的第 2-3 号学生
x = tf.random.uniform([2,3,8],maxval=100,dtype=tf.int32)
tf.gather_nd(x,[[0,0],[0,1],[1,1],[1,2]]) # 多维坐标采集
tf.boolean_mask(x,[[True,True,False],[False,True,True]]) # 多维度掩码采样
tf.where
- 通过
tf.where(cond, a, b)
操作可以根据cond
条件的真假从a
或b
中读取数据 - 当
a=b=None
即a,b
参数不指定时,``tf.where会返回
cond张量中所有
True`的元素的索引坐标
a = tf.ones([3,3]) # 构造 a 为全 1
b = tf.zeros([3,3]) # 构造 b 为全 0
tf.constant([[True,False,False],[False,True,False],[True,True,False]])
tf.where(cond,a,b) # 根据条件从 a,b 中采样
tf.where(cond) # 获取 cond 中为 True 的元素索引
demo
获取张量中的正数及其索引
x = tf.random.normal([3,3]) # 构造 a
mask=x>0 # 比较操作,等同于 tf.equal()
# 方式1
tf.boolean_mask(x,mask) # 通过掩码的方式获取数据
# 方式2
indices=tf.where(mask) # 提取所有大于 0 的元素索引
tf.gather_nd(x,indices) # 拿到索引后,提取正数的元素值
scatter_nd
通过tf.scatter_nd(indices, updates,shape)
实现刷新张量的部分数据。只能在全0张量的白板上进行刷新,可能需要结合其他操作来实现现有张量的数据刷新功能。
- shape:白板的形状
- indices:需要刷新数据的索引
- updates:需要插入进去的新数据
indices = tf.constant([[4], [3], [1], [7]]) # 索引位置
updates = tf.constant([4.4, 3.3, 1.1, 7.7]) # 新数据
tf.scatter_nd(indices, updates, [8]) # 长度为8的白板上刷新数据
tf.meshgrid
通过tf.meshgrid
方便地生成二维网格采样点坐标,方便可视化
x = tf.linspace(-8.,8,100) # 设置 x 坐标的间隔
y = tf.linspace(-8.,8,100) # 设置 y 坐标的间隔
x,y = tf.meshgrid(x,y) # 生成网格点,并拆分后返回
x.shape,y.shape # 打印拆分后的所有点的 x,y 坐标张量 shape
[out]: (TensorShape([100, 100]), TensorShape([100, 100]))
数据加载操作
常用数据
-
Boston Housing
波士顿房价趋势数据集,用于回归模型训练与测试 - CIFAR10/100 真实图片数据集,用于图片分类任务
- MNIST/Fashion_MNIST 手写数字图片数据集,用于图片分类任务
- IMDB 情感分类任务数据集
数据处理过程
在TF中,keras.datasets
模块提供经典数据的自动下载、管理、加载等功能
- 数据集合对象
tf.data.Dataset
- 数据加载
datasets.xxx.load_data()
其中,CIFAR10、MNIST
数据集返回的是两个元组,一个保存训练的数据,一个保存的是测试的数据。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets # 导入经典数据集加载模块 # 加载 MNIST 数据集
(x, y), (x_test, y_test) = datasets.mnist.load_data() # 自动加载
- 数据转成
Dataset
对象
数据加载进入内存后,需要转换成 Dataset
对象,以利用TensorFlow
提供的各种便捷功能
train_db = tf.data.Dataset.from_tensor_slices((x, y))
- 随机散打
通过函数Dataset.shuffle(buffer_size)
来实现,buffer_size
指定缓冲池的大小,一般设置成一个较大的数字
- 批训练
一般在网络的计算过程中会同时计算多个样本,这种方式叫做批训练
- 样本数量:batch size
train_db = train_db.batch(128) # 将Dataset设置成批训练方式
- 预处理
Dataset
对象通过提供map(func)
工具函数来实现预处理
# 预处理函数实现在 preprocess 函数中,传入函数引用即可
train_db = train_db.map(preprocess)
def preprocess(x, y): # 自定义的预处理函数
# 调用此函数时会自动传入 x,y 对象,shape 为[b, 28, 28], [b] # 标准化到 0~1
x = tf.cast(x, dtype=tf.float32) / 255.
x = tf.reshape(x, [-1, 28*28])
y = tf.cast(y, dtype=tf.int32)
y = tf.one_hot(y, depth=10)
# 返回的 x,y 将替换传入的 x,y 参数,从而实现数据的预处理功能
return x,y
- 循环训练
- step:完成一个batch的数据训练
- 通过多个step来完成整个训练集的一次迭代
for epoch in range(20): # 训练 Epoch 数
for step, (x,y) in enumerate(train_db): # 迭代 Step 数
# training...