TensorFlow学习笔记5—TensorFlow2高阶操作

1、合并与分割

接口:拼接tf.concat、tf.split、合并tf.stack、tf.unstack

(1)拼接tf.concat

不会创造新的维度,tf.concat([a,b],axis=x):a、b表示需要合并的tensor,x表示合并的维度

 

(2)合并tf.stack

使两个相同shape(所有维度相同)的tensor合并,且创造一个新的维度

 

 

(3)全拆分tf.unstack

按照指定轴,将tensor的该轴全部拆分,且维度-1

 

(4)split

在指定维度均分,num_or_size_splits属性表示tensor打散后的数量或者打散后的大小

 

2、数据统计

(1)范数tf.norm()

张量范数包含向量范数、矩阵范数。

向量范数分为一范数:绝对值的和。二范数:平方和开根号

tf.norm(a,ord=2,axis=0):ord默认为2求解二范数,axis可指定求解某维度范数

例子:

 

 

(2)tf.reduce_min/max/mean

是一个降维过程。axis可指定处理维度

 

(3)tf.argmax/argmin

求最大值与最小值的位置

 

(4)tf.equal

逐元素比较,可用于计算正确率

 

 

 

 

(5)tf.unique

返回不重复的数据,以及每个数据的下标位置

 

3、张量排序

(1)Sort(排序)/argsort(排序下标)

sort:返回对张量排序后的tensor,默认升序排列,DESCENDING表示降序

argsort:返回张量排序后元素的下标顺序

对多维度tensor排序

(2)Top_k获取最大几个值

返回前k个最大值或最大值下标

 

 

Top-k accuracy:用于判断accuracy,可同时考虑预测结果中的多个预测值可能正确的情况

 

上图中,预测结果k_b= ,表示第一个值最可能的结果为[2, 1, 0],第二个值最可能的结果为[1, 0, 2]。转置后表示为k_b= ,真实结果为target=[2, 0],升维后target=

然后可比较得预测结果k_b与真实结果         target可得出,只考虑top1正确率为50%,再将top2(次有可能正确的值)考虑进来则正确率为2/2=100%,若再将top3(次次有可能正确的值)考虑进来则正确率同样为2/2=100%

 

Top-k Accuracy计算过程

(3)实例代码:

 

import  os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import  tensorflow as tf

tf.random.set_seed(2467)

def accuracy(output, target, topk=(1,)):
    # 获取需要计算的类型数量
    maxk = max(topk)
    # 记录真实数据数量
    batch_size = target.shape[0]

    # 获取预测值前maxk个数据的排序下标,(每一行按照可能性由大到小排列)
    pred = tf.math.top_k(output, maxk).indices
    # 维度变换,由10行6列变换为6行10列(每一列按照可能性由大到小排列)
    pred = tf.transpose(pred, perm=[1, 0])
    # 真实数据升维,由1行10列复制为6行10列(不占内存的复制)
    target_ = tf.broadcast_to(target, pred.shape)
    #print("target_.shape=",target_.shape)
    # 比较得到所有预测数据正确与否[True,False,...],correct.shape=(6, 10)
    correct = tf.equal(pred, target_)
    #print('correct:',correct)

    #创建列表保存正确率
    res = []
    for k in topk:
        # 先将correct的前k行数据化为以为一维张量,并将True、False数字化为1.、0.
        correct_k = tf.cast(tf.reshape(correct[:k], [-1]), dtype=tf.float32)
        # 求和,并将1维张量化为0维标量
        correct_k = tf.reduce_sum(correct_k)
        acc = float(correct_k * (100.0 / batch_size) )
        res.append(acc)

    return res

# 正态分布10个样本,6类,10行6列
output = tf.random.normal([10, 6])
output = tf.math.softmax(output, axis=1)
# 随机生成0~5的类型,1行10列
target = tf.random.uniform([10], maxval=6, dtype=tf.int32)
print('prob:', output.numpy())
pred = tf.argmax(output, axis=1)
print('pred:', pred.numpy())
print('label:', target.numpy())

# 分别计算top1、2、3、4、5、6的正确率
acc = accuracy(output, target, topk=(1,2,3,4,5,6))
print('top-1-6 acc:', acc)

运行结果:

 

4、数据填充与复制

(1)pad

上下左右均可填充,默认填充0,常用语图片、句子的padding

例子:

图片填充:

(2)tf.tile

tile为真实复制,占用空间,broadcast为隐式复制,实际上不占用空间。

tf.tile(a, [x1, x2]):x1、x2表示新Tenor在该维是之前的n倍,1则保持不变

5、(代码)张量限幅

(1)clip_by_value

对具体元素值进行clip:

tf.maximum(a, x):对数据一边进行限幅,取a、x中最大值作为返回数,

即tf.maximum(a, x)>=a&& tf.maximum(a, x)>=x

tf.minimum(a, x):对数据一边进行限幅,取a、x中最小值作为返回数,

即tf.minimum(a, x)<=a&& tf.minimum(a, x)<=x

对数据两边进行限幅

clip_by_value(a, 2, 8):对数据两边进行限幅,取(2, 8)之间的值

(2)relu

tf.nn.relu()使小于0的值取0,大于0的值取自身

(3)clip_by_norm

vector、gradient有方向,gradient正方向表示(导数大于0)函数增加,gradient负方向表示(导数小于0)函数减少

clip_by_norm(a, k):使a的范数值变为15且保证a方向不变。

在clip时希望不要改变gradient方向,以免降低模型性能,因此需要等比例放缩(x, y),先将(x, y)(一个vector)除以它的模||(x, y)||(即(x, y)的二范数)归一化到0~1的范围,可再乘一个比例k,得到0~K范围的值,则可以在不改变gradient方向的前提下对张量限幅

上图中,保证a与aa的方向不变,范数值改变

 

(4)gradient clipping

使每个vector的方向、比例保持不变,gradient方向保持不变,norm作等比例的缩放。[w1, w2, w3]->[2, 4, 8]->[1, 2, 4]

下图,越训练值越大

下图,进行裁剪

下图,可观察到裁剪后值保持稳定

(5)代码比较gradient clipping的效果

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import  tensorflow as tf
from    tensorflow import keras
from    tensorflow.keras import datasets, layers, optimizers
print(tf.__version__)

(x, y), _ = datasets.mnist.load_data()
# 输入范围从0~1放大到0~50
x = tf.convert_to_tensor(x, dtype=tf.float32) / 50.
y = tf.convert_to_tensor(y)
y = tf.one_hot(y, depth=10)
print('x:', x.shape, 'y:', y.shape)
train_db = tf.data.Dataset.from_tensor_slices((x,y)).batch(128).repeat(30)
x,y = next(iter(train_db))
print('sample:', x.shape, y.shape)
# print(x[0], y[0])


def main():

    # 784 => 512
    w1, b1 = tf.Variable(tf.random.truncated_normal([784, 512], stddev=0.1)), tf.Variable(tf.zeros([512]))
    # 512 => 256
    w2, b2 = tf.Variable(tf.random.truncated_normal([512, 256], stddev=0.1)), tf.Variable(tf.zeros([256]))
    # 256 => 10
    w3, b3 = tf.Variable(tf.random.truncated_normal([256, 10], stddev=0.1)), tf.Variable(tf.zeros([10]))



    optimizer = optimizers.SGD(lr=0.01)


    for step, (x,y) in enumerate(train_db):

        # [b, 28, 28] => [b, 784]
        x = tf.reshape(x, (-1, 784))

        with tf.GradientTape() as tape:

            # layer1.
            h1 = x @ w1 + b1
            h1 = tf.nn.relu(h1)
            # layer2
            h2 = h1 @ w2 + b2
            h2 = tf.nn.relu(h2)
            # output
            out = h2 @ w3 + b3
            # out = tf.nn.relu(out)

            # compute loss
            # [b, 10] - [b, 10]
            loss = tf.square(y-out)
            # [b, 10] => [b]
            loss = tf.reduce_mean(loss, axis=1)
            # [b] => scalar
            loss = tf.reduce_mean(loss)



        # compute gradient
        grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
        print('==before==')
        for g in grads:
            print(tf.norm(g))

        # 进行clipping
        grads,  _ = tf.clip_by_global_norm(grads, 15)

        print('==after==')
        for g in grads:
            print(tf.norm(g))
        # update w' = w - lr*grad
        optimizer.apply_gradients(zip(grads, [w1, b1, w2, b2, w3, b3]))


        if step % 100 == 0:
            print(step, 'loss:', float(loss))


if __name__ == '__main__':
    main()

比较norm:

 无tf.clip_by_global_norm(grads, 15)                            有tf.clip_by_global_norm(grads, 15)

可观察到tf.clip_by_global_norm(grads, 15) 可以有效控制数据的norm

 

 比较loss:

 

可观察到没有使用tf.clip_by_global_norm(grads, 15)的loss变大并超过了正常值导致nan(not a number),而使用了tf.clip_by_global_norm(grads, 15)之后loss被限制在一个方便观察的范围。

6、高阶操作

(1)where选择

where(tensor):只接收一个参数时,该参数为bool类型,where函数返回参数中为true的坐标。

使用boolean_mask与gather_nd分别收集大于0的数据:

where(cond, A, B):接收三个参数时,根据cond中true和false的信息,对应位置为true则选择A中数据,对应位置为false则选择B中数据:

(2)scatter_nd更新

scatter_nd(indices, updates, shape):shape默认为一个数值全为0的Tensor数据,将updates中的数据依次按indices指定的位置更新到shape中再返回新的Tensor数据。

复杂例子:

其中indices=tf.sctter_nd([[0], [2]])则是指定了[x, y, z]中x维度的两个值0、2,因此y、z维度的值全部选中。

(3)meshgrid生成坐标系

numpy生成坐标系:x轴、y轴分别在[-2, 2]区间取5个值,一共取25个点,保存到list中,并生成numpy的array。

TensorFlow构建坐标系,使用tf.linspace()限制x、y取值范围,用tf.meshgrid()返回拆分后的坐标数据保存在x、y两个tensor中

x、y中点位信息:

用tf.stack合并两个tensor数据创建坐标轴,得到[5, 5, 2]的tensor数据,还可以Reshape得到[25, 2]的点位信息,便于理解

应用:画出函数z=sin(x)+sin(y)的等高线

代码:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
import matplotlib.pyplot as plt

def func(x):
    """
    :param x: [b, 2]
    :return:
    """
    # x: [b, 2]中[b, 0]表示x坐标,[b, 1]表示y坐标
    # 函数:z=sin(x)+sin(y)
    z = tf.math.sin(x[...,0]) + tf.math.sin(x[...,1])
    return z

# 确定x、y范围
x = tf.linspace(0., 2*3.14, 500)
y = tf.linspace(0., 2*3.14, 500)
# [50, 50],保存x、y数据
point_x, point_y = tf.meshgrid(x, y)
# [50, 50, 2]
points = tf.stack([point_x, point_y], axis=2)
# points = tf.reshape(points, [-1, 2])
print('points:', points.shape)
z = func(points)
print('z:', z.shape)

plt.figure('plot 2d func value')
plt.imshow(z, origin='lower', interpolation='none')
plt.colorbar()

plt.figure('plot 2d func contour')
# 画出函数的等高线
plt.contour(point_x, point_y, z)
plt.colorbar()
plt.show()

运行结果:

绘制等高线

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值