Fork me on GitHub

TF-char5-TF2高级操作

char5-TF高阶操作

第五章主要是介绍了TensorFlow2的几个高阶操作,包含:

  • 合并与分割
  • 数据统计
  • 张量比较
  • 填充与复制
  • 数据限幅
  • 张量的高级操作
  • 数据加载及预处理

合并与分割

合并

将多个张量在一个维度上合并成一个张量。合并有分为两种:拼接concatenate和堆叠stack

  1. 拼接tf.concat(x, axis)
  • 不会产生新的维度
  • 约束条件是:非合并的维度必须是一致的
  • axis指定拼接的轴;x条件是待合并的张量
1
2
3
4
5
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]
  1. 堆叠tf.stack(x, axis)
  • 创建新的维度,新维度的位置是任意的
  • 可以同时堆叠多个张量
  • 进行堆叠的张量维度必须一致
  • axis的用法和tf.expand_dims中相同:
    • $axis \geq 0$ 表示当前维度之前插入
    • $axis < 0$表示当前维度之后插入

1
2
3
4
5
6
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
1
2
3
4
5
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。

1
2
3
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种范数有:

  • $L_1$范数:所有元素绝对值之和
    $$
    ||x||1=\sum{i=1}|x_i|
    $$

  • $L_2$范数:所有元素的平方和,再开根号

$$
||x||_2=\sqrt{\sum_i|x_i|^2}
$$

  • 无穷范数:所有元素绝对值的最大值

$$
||x||_\infin = max_i(|x_i|)
$$

1
2
3
4
5
6
7
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_.*函数会求解出全局元素的最大、最小、均值、和。

1
2
3
4
5
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)

1
2
3
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)比较两个张量是否相等,返回的是布尔型张量

1
2
3
4
5
6
7
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)实现

1
2
3
4
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中,需要加载不同长度的句子,需要设定一个阈值来固定最大的句子长度

1
2
3
4
5
6
7
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() 函数可以在任意维度将数据重复复制多份

1
2
x = tf.random.normal([4,32,32,3])
tf.tile(x, [2,3,3,1])

数据限幅

  • tf.maximum()实现下限幅
  • tf.minimum()实现上限幅
  • tf.clip_by_vlaue实现双边限幅
1
2
3
4
x = tf.range(9)
tf.maximum(x, 2) # 下
tf.minimum(x, 7) # 上
tf.clip_by_vlaue(x, 2, 7)

高级操作

tf.gather

根据索引号收集数据tf.gather;对于不规则的索引也非常适合。索引从0开始

1
2
3
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操作

1
2
3
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

指定每次采样点的坐标来实现采样多个点的目的。上面问题的解决

1
tf.gather_nd(x, [[1,1], [2,2], [3,3]])

tf.boolean_mask

通过掩码的方式来获取数据采样;掩码的长度必须和对应维度的长度一致

1
2
3
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 号学生

1
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`的元素的索引坐标
1
2
3
4
5
6
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

获取张量中的正数及其索引

1
2
3
4
5
6
7
8
9
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:需要插入进去的新数据

1
2
3
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方便地生成二维网格采样点坐标,方便可视化

1
2
3
4
5
6
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模块提供经典数据的自动下载、管理、加载等功能

  1. 数据集合对象

tf.data.Dataset

  1. 数据加载

datasets.xxx.load_data()

其中,CIFAR10、MNIST数据集返回的是两个元组,一个保存训练的数据,一个保存的是测试的数据。

1
2
3
4
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets # 导入经典数据集加载模块 # 加载 MNIST 数据集
(x, y), (x_test, y_test) = datasets.mnist.load_data() # 自动加载
  1. 数据转成Dataset对象

数据加载进入内存后,需要转换成 Dataset 对象,以利用TensorFlow 提供的各种便捷功能

1
train_db = tf.data.Dataset.from_tensor_slices((x, y))
  1. 随机散打

通过函数Dataset.shuffle(buffer_size)来实现,buffer_size指定缓冲池的大小,一般设置成一个较大的数字

  1. 批训练

一般在网络的计算过程中会同时计算多个样本,这种方式叫做批训练

  • 样本数量:batch size
1
train_db = train_db.batch(128)  # 将Dataset设置成批训练方式
  1. 预处理

Dataset对象通过提供map(func)工具函数来实现预处理

1
2
3
4
5
6
7
8
9
10
11
# 预处理函数实现在 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
  1. 循环训练
  • step:完成一个batch的数据训练
  • 通过多个step来完成整个训练集的一次迭代
1
2
3
for epoch in range(20): # 训练 Epoch 数
for step, (x,y) in enumerate(train_db): # 迭代 Step 数
# training...

本文标题:TF-char5-TF2高级操作

发布时间:2019年12月01日 - 23:12

原始链接:http://www.renpeter.cn/2019/12/01/TF-char5-Tensorflow%E9%AB%98%E7%BA%A7%E6%93%8D%E4%BD%9C.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea