深度学习_04_3_TensorFlow2基础_Broadcasting&数学运算&前向传播

04_3_TensorFlow2基础_Broadcasting&数学运算&前向传播

Broadcasting

本质上是一个张量维度扩张的手段

对某个维度上面重复n多次,但是没有真正的复制数据,和tf.tile不同。tile会重复n多次并且会真实的在数据上体现出来(复制数据)。

是一种优化的手段,呈现出数据已经被扩张了。

在X @ W+b上,[10]变成[b,10]的步骤就是broadingcast的步骤

在这里插入图片描述

Key idea

  • 小维度先对齐,没有的插入维度
  • 扩张成为相同的大小

在这里插入图片描述

例子:

[1,3]->[4,3]没有真正复制成数据,只是有利于计算

如果是[1,4]和[1,3]进行相加,是不能broadencast的。

在这里插入图片描述

How to understand?

例子:

[8] 对所有班级所有学生(默认高维度是通用的)的物理和数学加分(偏置)

[35,1] 给每个班级的前x个学生的每个科目加x0分

在这里插入图片描述

Why broadcasting?

  1. for real demanding

    expand in (axis=0)

    tf.tile 对1的维度进行复制,复制b次。变成[b,10]

    一般来讲,大维度是更加高层的概念;小维度的是底层的概念,这样的话如果没有写高维度的配置,高维度是默认的,所有维度都适用的。

    在这里,默认班级和学生的概念更高一些,让科目的概念更低一些,这样的话如果只给了一个低维度的概念,要给所有人加五分,默认每个班级和学生都是适合于这个配置的。

    [5.0]是data,dimension为1,shape为1的Tensor,扩张成[4,32,8]

  2. memory consumption

    如果显式的通过tf.tile这种复制方式,就会扩张内存消耗,而通过broadcast是运行的时候自动优化的手段,这样就节省了大量的内存占用空间。

    真正复制要消耗1024*4字节,利用broadcast只消耗8*4字节。

在这里插入图片描述

Broadcastable?

  • Match from Last dim!
    • if current dim=1,expand to same
    • if either has no dim, insert one dim and expand to same
    • otherwise, NOT broadcastable

例子:[4] 和 [1,3] 是不能broadcast的。

从右边对齐,如果没有维度,则插入1个维度,它的shape是1;如果有维度的话,比较他们维度的数量是不是相等的,如果不等的话就是不能broadcast。

例子:[4,32,14,14] 和 [1,32,1,1] 可以

在这里插入图片描述

[4,32,14,14] 和 [14,14] 默认先从右边(小维度)对齐,可以

在这里插入图片描述

[4,32,14,14] 和 [2,32,14,14] 2不能扩张成4,不能

在这里插入图片描述

It’s efficient and intuitive!(好处)

节省内存空间;编程的时候更简洁

在这里插入图片描述

Broadcasting(编程,自动broadcast)

只要对应操作支持broadcast,程序会自动判断如果shape不一致的时候,broadcast为相同的shape

在这里插入图片描述

tf.broadcast_to(编程,显式的)

在这里插入图片描述

Broadcast VS Tile

Tile占用的内存空间更大

Broadcast的shape已经扩张成[2,3,4],但是它的实际上的内存区域没有完成扩张的复制,所以更加高效

数学运算

Outline

  • 加、减、乘、除
  • 多少次方、平方
  • 平方根
  • 整除、取余
  • l o g e log_e loge e n e^n en
  • 张量,矩阵运算
  • 矩阵形式的转换

Operation type

  • element-wise

    对应元素的加减乘除

  • matrix-wise

    矩阵运算的多个并行计算的过程

  • dim-wise

    求某个维度的均值、最大值、最小值、求和

±*/%//

两个相同维度,可以加减乘除

还可以整除、余除

在这里插入图片描述

tf.math.log & tf.exp

这个log函数是以e为底的

在这里插入图片描述

要实现log2,log10

因为有公式 l o g a b / l o g a c = l o g c b log_a b / log_a c = log_c b logab/logac=logcb,因此要实现c是2即可

在这里插入图片描述

pow,sqrt

在这里插入图片描述

@或tf.matmul(a,b)

在这里插入图片描述

多维度的矩阵乘法

With broadcasting

在这里插入图片描述

Recap(复习)

其实矩阵的相乘规则是满足标量的相乘规则。矩阵相乘的设计就是使 y = ( y 0 , y 1 , y 2 ) y=(y^0,y^1,y^2) y=(y0,y1,y2)满足 y = w ∗ x + b y=w*x+b y=wx+b

在这里插入图片描述

具体的实例: Y = X @ W + b Y = X@W+b Y=X@W+b

在这里插入图片描述

加上非线性因子relu后,把负数的去掉,所以下图会全部保留下来

在这里插入图片描述

前向传播(张量)

基于我们学过的知识:创建Tensor、索引与切片、Broadcasting、数学运算。完成一个简单的前向传播。

Recap

之前讲过分类问题,通过一个简单的非线性层( r e l u [ X @ W + b 1 ] relu[X@W+b_1] relu[X@W+b1] ),通过串联3个非线性层,来增加它的复杂度,这样我们得到输出。然后构建一个loss函数,out和label的误差。通过最小化误差函数,来得到更新的过程,做之后[ W 1 ′ , b 1 ′ , W 2 ′ , b 2 ′ , W 3 , b 3 ′ W_1',b_1', W_2',b_2',W_3,b_3' W1,b1,W2,b2,W3,b3]的参数。

本次是用代码实现这样的一个过程。

import os

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

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets

# 加载数据集,自动从网上下载
# x:[60k,28,28] y:[60k]
(x, y), _ = datasets.mnist.load_data()
# 转化成Tensor
# 原来x的范围为[0,255],y的范围为[0,9],习惯把数据转换为0~1的数值
# x: [0,255] => [0,1.]
# y: [0,9]
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.
y = tf.convert_to_tensor(y, dtype=tf.int32)

print(x.shape, y.shape, x.dtype, y.dtype)
# 看x和y的最小值和最大值
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))

# 设置batch,希望一次能取128张照片
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
# train_iter可以对train_db做next方法
train_iter = iter(train_db)
# 一直取,直到得到一个最终的元素。这里是一个sample例子
# x:(128, 28, 28) y:(128,)
sample = next(train_iter)
print('batch: ', sample[0].shape, sample[1].shape)

# 创建权值
# 层是降维的过程,一开始是[b,784] => 降维[b,256] => 降维[b,128] => 降维[b,10]
# w的shape要满足运算规则,因此为[dim_in,dim_out],初始化为截断的正态分布
# b的shape为[dim_out],初始化为0
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]))

lr = 1e-3  # 10^-3

for epoch in range(10):  # 对整个数据集迭代10次,iterate db for 10,表示对数据集迭代多少次
    # 对一次数据集的所有图片做一个循环
    # for (x, y) in train_db:
    for step, (x, y) in enumerate(train_db):
    # for every batch,表示对当前的数据集,你迭代的数据集,迭代哪个进度。比如说batch为128,一共有60k图片,一共取多少次,step表示当前进度的表示条
        # x:[128,28,28]
        # y:[128]

        # 维度变换 [b,28,28] => [b,28*28]
        x = tf.reshape(x, [-1, 28 * 28])

        # 使得梯度信息被记录下来
        with tf.GradientTape() as tape:  # 默认只会跟踪tf.Variable类型
            # 在这里我们希望x:[b,28*28]
            # h1 = x@w1 + b1
            # [b,784]@[784,256] + [256] => [b,256]+[256] 加号会自动做broadcast/自己做broadcast
            # b是batch,在这里b为128,也就是x.shape[0]
            # => [b,256] + [b,256] => [b,256]
            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

            # compute loss
            # 输出out:[b,10]
            # 真实结果y:[b],因此要将y进行one-hot encoding
            # =>[b,10]
            y_onehot = tf.one_hot(y, depth=10)

            # mse(均方误差) = mean(sum(y-out)^2) y-out的平方和,之后平均
            # y-out 还是shape为[b,10]
            loss = tf.square(y_onehot - out)
            # 均值mean:得到scalar
            # 相当于 loss / b / 10。/b是求出每一个batch上的均值;/10是求每个instance上每一个点的均值
            loss = tf.reduce_mean(loss)
            # 需要自动求导,需要把前向计算的过程,也就是需要求梯度的过程,把它包在GradientTape

        # compute gradients
        grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
        # print(grads)
        # 本来optimizer可以一次w1 = w1 - lr * w1_grad,在这里进行手写
        # 在这里相减后,w1又从tf.Variable变回了tf.Tensor,这里使用assign_sub进行原地更新,保持数据类型不变
        w1.assign_sub(lr * grads[0])
        # w1 = w1 - 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])

        # print(isinstance(b3,tf.Variable))
        # print(isinstance(b3,tf.Tensor))
        if step % 100 == 0:
            print(epoch, step, 'loss:', float(loss))

    # 100 loss: nan 梯度爆炸,解决方案:给6个参数一个范围值,stddev=0.1
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值