Tensorflow基础 - 03

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

# 索引
x = tf.random.normal([4, 32, 32, 3])  # 创建 4D 张量
# 取第 1 张图片的数据
print(x[0])
# 取第 1 张图片的第 2 行
print(x[0][1])
# 取第 1 张图片,第 2 行,第 3 列的数据
print(x[0][1][2])
# 取第 3 张图片,第 2 行,第 1 列的像素,B 通道(第 2 个通道)颜色强度值
print(x[2][1][0][1])
# 当张量的维度数较高时,使用[𝑗][𝑘]...[𝑙]的方式书写不方便,可以采用[𝑗,𝑘,…,𝑙]的方式索引,它们是等价的

# 切片:start:end:step
# 读取第 2,3 张图片
print(x[1:3])
# 读取第 1 张图片的所有行
print(x[0, ::])
# 为了更加简洁,::可以简写为单个冒号:
print(x[:, 0:28:2, 0:28:2, :])
# # start:end:step      从 start 开始读取到 end(不包含 end),步长为 step
# # start:end           从 start 开始读取到 end(不包含 end),步长为 1
# # start:              从 start 开始读取完后续所有元素,步长为 1
# # start::step         从 start 开始读取完后续所有元素,步长为 step
# # :end:step           从 0 开始读取到 end(不包含 end),步长为 step
# # :end                从 0 开始读取到 end(不包含 end),步长为 1
# # ::step              步长为 step 采样
# # ::                  读取所有元素
# # :                   读取所有元素
# # step 可以为负数,考虑最特殊的一种例子,当step = −1时,start:end:−1表示从 start 开始,逆序读取至 end 结束(不包含 end),索引号𝑓𝑜𝑒 ≤ 𝑡𝑢𝑏𝑠𝑢。
x = tf.range(9)  # 创建 0~9 向量
print(x[8:0:-1])  # 从 8 取到 0,逆序,不包含 0
x[::-1]  # 逆序全部元素
x[::-2]  # 逆序间隔采样
x = tf.random.normal([4, 32, 32, 3])
x[0, ::-2, ::-2]  # 行、列逆序间隔采样
x[:, :, :, 1]  # 取 G 通道数据
# # a,⋯,b     a 维度对齐到最左边,b 维度对齐到最右边,中间的维度全部读取,其他维度按 a/b 的方式读取
# # a,⋯       a 维度对齐到最左边,a 维度后的所有维度全部读取,a 维度按 a 方式读取。这种情况等同于 a 索引/切片方式
# # ⋯,b       b 维度对齐到最右边,b 之前的所有维度全部读取,b 维度按 b 方式读取
# # ⋯         读取张量所有数据
# x[0:2, ..., 1:]  # 高宽维度全部采集,读取第 1~2 张图片的 G/B 通道数据
# x[2:, ...]  # 高、宽、通道维度全部采集,等价于 x[2:] 读取最后 2 张图片
# x[..., :2]  # 所有样本,所有高、宽的前 2 个通道,读取 R/G 通道数据


# 维度变换
# 在神经网络运算过程中,维度变换是最核心的张量操作,通过维度变换可以将数据任意地切换形式,满足不同场合的运算需求。
# 基本维度变换函数 改变视图 reshape、插入新维度 expand_dims,删除维度 squeeze、交换维度 transpose、复制数据 tile 等
# 1.改变视图
x = tf.range(96)  # 生成向量
x = tf.reshape(x, [2, 4, 4, 3])  # 改变 x 的视图,获得 4D 张量,存储并未改变
print(x)
print('张量维度:', x.ndim, '形状列表:', x.shape)  # 获取张量的维度数和形状列表
y = tf.reshape(x, [2, -1])  # 可以将张量的视图任意地合法改变 参数−1表示当前轴上长度需要根据张量总元素不变的法则自动推导
print(y)
z = tf.reshape(x, [2, 4, 12])
print(z)

# 增,删维度

# 增加维度 tf.expand_dims 的 axis 为正时,表示在当前维度之前插入一个新维度;为负时,表示当前维度之后插入一个新的维度。
x = tf.random.uniform([2, 2], maxval=10, dtype=tf.int32)
print(x)
x1 = tf.expand_dims(x, axis=2)  # axis=2 表示宽维度后面的一个维度
print(x1)
x2 = tf.expand_dims(x, axis=0)  # 高维度之前插入新维度
print(x2)

# 删除维度
# 是增加维度的逆操作,与增加维度一样,删除维度只能删除长度为 1 的维度,也不会改变张量的存储。
# 可以通过 tf.squeeze(x, axis)函数,axis 参数为待删除的维度的索引号
x = tf.squeeze(x, axis=0)  # 删除图片数量维度

# 交换维度
x = tf.random.normal([2, 32, 32, 3])
y = tf.transpose(x, perm=[0, 3, 1, 2])  # 交换维度 图片数量、行、列、通道数 -> 图片数量、通道数、行、列
z = tf.transpose(x, perm=[0, 2, 1, 3])  # 交换维度 图片数量、行、列、通道数 -> 图片数量、列、行、通道数

# 复制数据
# 可以通过 tf.tile(x, multiples)函数完成数据在指定维度上的复制操作,
# multiples 分别指定了每个维度上面的复制倍数,
# 对应位置为 1 表明不复制,为 2 表明新长度为原来长度的2 倍,即数据复制一份,以此类推。
b = tf.constant([1, 2])  # 创建向量 b
print(b)
b = tf.expand_dims(b, axis=0)  # 插入新维度,变成矩阵
print(b)
b = tf.tile(b, multiples=[2, 1])  # 样本维度上复制一份
print(b)

x = tf.range(4)
print(x,'\n')
x = tf.reshape(x, [2, 2])  # 创建 2 行 2 列矩阵
print(x,'\n')
x = tf.tile(x, multiples=[1, 3])  # 列维度复制两份
print(x,'\n')
x = tf.tile(x, multiples=[2, 1])  # 行维度复制一份
print(x,'\n')

# Broadcasting Broadcasting
# 称为广播机制(或自动扩展机制),它是一种轻量级的张量复制手段,在逻辑上扩展张量数据的形状,但是只会在需要时才会执行实际存储复制操作
# 通过 tf.broadcast_to(x, new_shape)函数可以显式地执行自动扩展功能,将现有 shape 扩张为 new_shape
a = tf.random.normal([32, 1], dtype=tf.float16)  # 创建矩阵
print(a)
a = tf.broadcast_to(a, [2, 32, 32, 3])  # 扩展为 4D 张量
print(a)
# 在𝑑维度上,张量已经有 2 个特征数据,新 shape 对应维度的长度为𝑑(𝑑 ≠ 2,如𝑑=3),那么
# 当前维度上的这 2 个特征无法普适到其它位置,故不满足普适性原则,无法应用Broadcasting 机制,将会触发错误
# b = tf.random.normal([32, 2])
# b = tf.broadcast_to(b, [2, 32, 32, 4])  # 不符合 Broadcasting 条件
# 在进行张量运算时,有些运算在处理不同 shape 的张量时,会隐式地自动调用Broadcasting 机制,如+,-,*,/等运算等,
# 将参与运算的张量 Broadcasting 成一个公共shape,再进行相应的计算。
a = tf.random.normal([2, 32, 32, 1])
b = tf.random.normal([32, 32])
print(a + b, a - b, a * b, a / b)  # 测试加减乘除运算的 Broadcasting 机制

# 数学运算

# 加、减、乘、除是最基本的数学运算,分别通过 tf.add, tf.subtract, tf.multiply, tf.divide函数实现
# TensorFlow 已经重载了+、 −、 ∗ 、/运算符,一般推荐直接使用运算符来完成加、减、乘、除运算
a = tf.range(5)
b = tf.constant(2)
print(a // b)  # 整除运算
print(a % b)  # 余除运算

# 乘方运算 通过 tf.pow(x, a)可以方便地完成𝑦 =  𝑎 的乘方运算,也可以通过运算符**实现 ∗∗𝑏运算
x = tf.range(4)
print(tf.pow(x, 3))  # 乘方运算
print(x ** 2)  # 乘方运算符
# 设置指数为 1/𝑎 形式,即可实现√𝑎根号运算
x = tf.constant([1., 4., 9.])
print(x ** (0.5))  # 平方根
# 对于常见的平方和平方根运算,可以使用 tf.square(x)和 tf.sqrt(x)实现。
x = tf.range(5)
print(x)
x = tf.cast(x, dtype=tf.float32)  # 转换为浮点数
print(x)
x = tf.square(x)  # 平方
print(x)
x = tf.sqrt(x)  # 平方根
print(x)

# 指数和对数运算
# 通过 tf.pow(a, x)或者**运算符也可以方便地实现指数运算a^x
x = tf.constant([1., 2., 3.])
print(2 ** x)  # 指数运算
# 对于自然指数e 𝑥 ,可以通过 tf.exp(x)实现
print(tf.exp(1.))  # 自然指数运算
# 在 TensorFlow 中,自然对数log e 可以通过 tf.math.log(x)实现
x = tf.exp(3.)
print(tf.math.log(x))  # 对数运算
# 如果希望计算其它底数的对数,可以根据对数的换底公式:log(a)x=log(e)x/log(e)a
# 间接地通过 tf.math.log(x)实现
x = tf.constant([1., 2.])
x = 10 ** x
print(tf.math.log(x) / tf.math.log(10.))  # 换底公式

# 矩阵相乘运算
a = tf.random.normal([4, 3, 28, 32])
b = tf.random.normal([4, 3, 32, 2])
print(a @ b)
# 矩阵相乘函数同样支持自动 Broadcasting 机制,
a = tf.random.normal([4,28,32])
b = tf.random.normal([32,16])
tf.matmul(a,b) # 先自动扩展,再矩阵相乘


# 前向传播实战
# out = 𝑅𝑓𝐿𝑈{𝑅𝑓𝐿𝑈{𝑅𝑓𝐿𝑈[X@W1+𝒃1]@W2+𝒃2}@W3+𝒃3}
# 采用的数据集是 MNIST 手写数字图片集
# 输入节点数为 784,第一层的输出节点数是256,第二层的输出节点数是 128,第三层的输出节点是 10
# 也就是当前样本属于 10 类别的概率。
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
(x, y), (x_val, y_val) = datasets.mnist.load_data()  # 加载 MNIST数据集 返回两个元组,一个是训练集,一个是测试集
x = 2 * tf.convert_to_tensor(x, dtype=tf.float32) / 255. - 1  # 转换为浮点张量,并缩放到-1~1
y = tf.convert_to_tensor(y, dtype=tf.int32)  # 转换为整形张量
y_onehot = tf.one_hot(y, depth=10)  # one-hot 编码 depth指的是总类别数
lr = 0.1
with tf.GradientTape() as tape:
    # Step1:创建每个非线性层的𝑿和𝒃张量参数
    # 每层的张量都需要被优化,故使用 Variable 类型,并使用截断的正太分布初始化权值张量,偏置向量初始化为 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]))
    # Step2:改变视图,[b, 28, 28] => [b, 28*28]
    x = tf.reshape(x, [-1, 28 * 28])
    # Step3:向前计算
    # 第一层计算,[b, 784]@[784, 256] + [256] => [b, 256] + [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
    # Step4:将真实的标注张量𝒛转变为 One-hot 编码,并计算与 out 的均方差
    # 计算网络输出与标签之间的均方差,mse = mean(sum(y-out)^2)
    # [b, 10]
    loss = tf.square(y_onehot - out)
    # 误差标量,mean: scalar
    loss = tf.reduce_mean(loss)
    # 自动梯度,需要求梯度的张量有[w1, b1, w2, b2, w3, b3]
    grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
    # 梯度更新,assign_sub 将当前值减去参数值,原地更新
    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])

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值