tensorflow基础

1 基本数据类型

数值型,字符串性,布尔类型

1.1 数值型

标量:一般的 实数,shape为[],维度为0

向量: n 个实数的有序集合 ,shape为[n],维度为1

矩阵: n 行 m 列实数的有序集合,shape为[n,m],维度为2

张量:维度>2的数组

一般的:标量,向量,矩阵也统称为张量

1.1.1 标量的创建

a = 1.1
aa = tf.constant(1.2)#(标量)张量的创建方式
type(a),type(aa),tf.is_tensor(aa)
print(aa)
#Out[1]:
(float, tensorflow.python.framework.ops.EagerTensor, True)
x = tf.constant([1,2.,3.3])
print(x)
#out 
<tf.Tensor: id=165, shape=(3,), dtype=float32, numpy=array([1. , 2. , 3.3],
dtype=float32)>
x.numpy()
#out
[1., 2., 3.3]

其中 id 是 TensorFlow 中内部索引对象的编号, shape 表示张量的形状, dtype 表示张量的数
值精度, 张量 numpy()方法可以返回 Numpy.array 类型的数据,方便导出数据到系统的其他
模块:

1.1.2向量的创建方式

只能通过List类型传给tf.constant()

a = tf.constant([1, 2])
print(a)
print(a.shape) #error a.shape()
print(a.dtype)

out:

tf.Tensor([1 2], shape=(2,), dtype=int32)
(2,)
<dtype: 'int32'>

1.1.2矩阵的创建

矩阵张量的创建如向量,也需要通过列表

a = tf.constant([[1, 2], [2.1,3.4])
print(a)
print(a.shape) #error a.shape()
print(a.dtype)
print(a.numpy())

out

tf.Tensor([[1.  2. ][2.1 3.4]], shape=(2, 2), dtype=float32)
(2, 2)
<dtype: 'float32'>
[[1.  2. ][2.1 3.4]]

1.1.3高纬张量的创建

一般就是两个函数API,通过将Python 的List和Numpy的array转化为tensor:即tf.constant()和 tf.convert_to_tensor() 其中之一

a = tf.constant([[[1,2],[3,4]],[[5,6],[7,8]]])

1.2 字符串类型

TensorFlow 还支持字符串(String)类型的数据,例如在表示图片数据时,可以先记录图片的路径,再通过预处理函数根据路径读取图片张量。 通过传入字符串对象即可创建字符串类型的张量

a = tf.constant("hello,deep learning!")
print(a)

更多字符串的操作查看 tf.strings 模块

1.3 布尔类型

a = tf.constant(True)  

需要注意的是, TensorFlow 的布尔类型和 Python 语言的布尔类型并不对等,不能通用

a == True
#out
False

2 数值精度

2.1常用精度类型

常用的精度类型有 tf.int16, tf.int32, tf.int64, tf.float16, tf.float32, tf.float64,其中 tf.float64 即为 tf.double

对于大部分深度学习算法,一般使用 tf.int32, tf.float32 可满足运算精度要求,部分对
精度要求较高的算法,如强化学习,可以选择使用 tf.int64, tf.float64 精度保存张量

测试
import numpy as np
np.pi
tf.constant(np.pi, dtype=tf.float32)
#Out
<tf.Tensor: id=29, shape=(), dtype=float32, numpy=3.1415927>
#如果采用 tf.float32 精度保存𝜋,则能获得更高的精度:
tf.constant(np.pi, dtype=tf.float64)
#Out
<tf.Tensor: id=31, shape=(), dtype=float64, numpy=3.141592653589793>

2.2精度转换

通过 tf.cast 函数进行转换:

a = tf.constant(1.2,dtype=tf.float16)
print('befor':a)
a = tf.cast(a,dtype=tf.float32)
print('after':a)

out

befor: tf.Tensor(1.2, shape=(), dtype=float16)
after: tf.Tensor(1.2001953, shape=(), dtype=float32)

2.3 类型转换

通过 tf.cast 函数进行转换:

低精度往高精度,高精度往低精度注意溢出

a = tf.constant(np.pi, dtype=tf.float16)
tf.cast(a, tf.double)

bool类型和int型

a = tf.constant([True, False])
tf.cast(a, tf.int32)
Out[18]:
<tf.Tensor: id=48, shape=(2,), dtype=int32, numpy=array([1, 0])>

3 待优化张量

为了区分需要计算梯度信息的张量(w,b)和不需要计算梯度信息的张量,Tensorflow设计了一种专门的数据类型支持梯度信息的记录:tf.Variable。 tf.Variable 类型在普通的张量类型基础上添加了 name, trainable 等属性来支持计算图的构建 。name属性数据命名计算图的变量,这套命名体系有TensorFlow内部维护,trainable是bool类型:默认是True。

g = tf.Variable([[1,2],[3,4]],name='val')
print(g)
print(g.name, g.trainable)

out

<tf.Variable 'Variable:0' shape=(2, 2) dtype=int32, numpy=array([[1, 2],[3, 4]])>
Variable:0 True

4张量的创建

将张量创建为全 0 或者全 1 数据是非常常见的张量初始化手段。考虑线性变换𝒚 = 𝑊𝒙 + 𝒃, 将权值矩阵 W 初始化为全 1 矩阵,偏置 b 初始化为全 0 向量,此时线性变化层输出𝒚 = 𝒙,是一种比较好的层初始化状态。 通过 tf.zeros()和 tf.ones()即可创建任意形状全 0 或全 1 的张量。例如,创建为 0 和为 1 的标量张量 。

4.1 tf.zeros, tf.ones

h = tf.zero([])
h = tf.zero([1])
h = tf.zero([1,2])
h = tf.zero([[3,2], [4,5]])
h = tf.one([1])

4.2 tf.zeros_like, tf.ones_like

可以方便地新建与某个张量 shape 一致,内容全 0 或全 1的张量。例如,创建与张量 a 形状一样的全 0 张量

a = tf.ones([2,3])
tf.zeros_like(a)

out

[[0. 0. 0.]
 [0. 0. 0.]], shape=(2, 3), dtype=float32)

4.3 tf.fill(shape, value)

可以创建全为自定义形状为shape,数值为value 的张量。

k = tf.fill([2,3],1)
print(k)

out

tf.Tensor([[1 1 1] [1 1 1]], shape=(2, 3), dtype=int32)

4.4 创建已知分布的张量

创建已知分布的张量 ,正态分布(Normal Distribution,或 Gaussian Distribution)和均匀分布(Uniform
Distribution)是最常见的分布之一,创建采样自这 2 种分布的张量非常有用,比如在卷积神经网络中,卷积核张量 W 初始化为正态分布有利于网络的训练;在对抗生成网络中,隐藏变量 z 一般采样自均匀分布

4.4.1 tf.random.normal(shape, mean=0.0 ,stddev=1.0)

tf.random.normal([2,2], mean=1,stddev=2)

out

<tf.Tensor: id=150, shape=(2, 2), dtype=float32, numpy=
array([[-2.2687864, -0.7248812],
[ 1.2752185, 2.8625617]], dtype=float32)>

4.4.2 tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)

maxval默认为1,dtyoe默认为tf.float32

tf.random.uniform([2,2],maxval=100,dtype=tf.int32)

如果需要均匀采样整形类型的数据,必须指定采样区间的最大值 maxval 参数,同时制定数据类型为 tf.int*型:

4.5 创建序列

通过 tf.range(start, limit, delta=1)可以创建[𝑠𝑡𝑎𝑟𝑡, 𝑙𝑖𝑚𝑖𝑡),步长为 delta 的序列,不包含 limit
本身,start默认为0,步长默认为1

tf.range(10)
tf.range(2,10,2)

out

tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)
tf.Tensor([2 4 6 8], shape=(4,), dtype=int32)

5 切片和索引

索引两种方式

5.1切片

5.1.1 [i] [j] [k]

5.1.2[i,j,k]

x = tf.random.uniform([4,32,32,3])#四张三通道的彩色图片
print(x[0])#打印第一张图片的数据
print(x[0][1])#打印第一张数据第2行
print(x[0][1][2])#打印第一张数据第2行第三列
print(x[0][1][2][2])#打印第一张数据第三通道的第2行第三列
#由于[][][]格式有点复杂,简化为[]
print(x[0, 1, 2, 2])#打印第一张数据第三通道的第2行第三列

5.2 索引

通过𝑠𝑡𝑎𝑟𝑡: 𝑒𝑛𝑑: 𝑠𝑡𝑒𝑝切片方式可以方便地提取一段数据,其中 start 为开始读取位置的
索引, end 为结束读取位置的索引(不包含 end 位), step 为读取步长 ,其中从第一个元素读取时 start 可以省略,即 start=0 是可以省略,取到最后一个元素时 end 可以省略,步长为 1 时 step 可以省略,简写方式总结如表格

6 维度变换

在神经网络运算过程中,维度变换是最核心的张量操作 ,通过维度变换可以将数据任
意地切换形式,满足不同场合的运算需求

基本的维度变换包含了改变视图 reshape,插入新维度 expand_dims,删除维度
squeeze,交换维度 transpose,复制数据 tile 等

6.1 reshape

通过 tf.reshape(old_shape, new_shape),可以将张量的视图任意的合法改变

通过 Reshape 改变视图时,必须始终记住张量的存储顺序,新视图的维度顺序不能与存储顺序相悖。

即 张量按着初始视图[𝑏, ℎ,w , 𝑐]写入的内存布局,我们改变初始视图[𝑏, ℎ, , 𝑐]的理解
方式,它可以有多种合法理解方式:
[ b, ℎ ∗ w , 𝑐] 张量理解为 b 张图片, h∗w 个像素点, c 个通道
[b ℎ, w ∗ 𝑐 ]张量理解为 b 张图片, h 行,每行的特征长度为 w∗
c
[b,h ∗w∗ c]] 张量理解为 b 张图片,每张图片的特征长度为 h∗w∗c
但不能是[b,c,h,w],[b,w,h*c]之类的

x = tf.range(8)
x = tf.reshape(x,[2,2,2])
print(x)
#在 TensorFlow 中,可以通过张量的 ndim 和 shape 成员属性获得张量的维度数和形状:
print(x.ndim,x.shape)

out

tf.Tensor([[[0 1][2 3]][[4 5][6 7]]], shape=(2, 2, 2), dtype=int32)
3 (2, 2, 2)

如果new_shape中存在-1,则机器会自动根据数据量和其他维度推导。

x = tf.reshape(x,[2,-1])
print(x)

out

tf.Tensor([[0 1 2 3][4 5 6 7]], shape=(2, 4), dtype=int32)

6.2 增加维度tf.expand_dims

tf.expand_dims(x, axis)函数,axis为正,则在axis之前插入一个新的维度 ,否则为后。

z = tf.random.uniform([2,2],maxval=4,dtype=tf.int32)
print(z)
#out
tf.Tensor([[2 1][3 3]], shape=(2, 2), dtype=int32)
z = tf.expand_dims(z,axis=2)
print(z)
#out
tf.Tensor([[[2][1]][[3][3]]], shape=(2, 2, 1), dtype=int32)
z = tf.expand_dims(z,axis=0)
print(z)
#out
tf.Tensor([[[[2][1]][[3][3]]]], shape=(1, 2, 2, 1), dtype=int32)

6.3 删除维度tf.squeeze

tf.squeeze(x, axis)函数 , axis 参数为待删除的维度的索引号,不指定维度参数 axis,即 tf.squeeze(x), 那么他会默认删除所有长度为 1 的维度 。

6.4交换维度tf.transpose

改变视图、 增删维度都不会影响张量的存储。在实现算法逻辑时, 在保持维度顺序不
变的条件下, 仅仅改变张量的理解方式是不够的,有时需要直接调整的存储顺序,即交换
维度(Transpose)。通过交换维度,改变了张量的存储顺序,同时也改变了张量的视图。

我们以[𝑏, ℎ, , 𝑐]转换到[𝑏, 𝑐, ℎ, ]为例

tf.transpose(x, perm)函数 ,其中perm为新shape的索引顺序List

t = tf.random.uniform([2,3,1,2],maxval=4,dtype=tf.int32)
print(t)
#out
shape=(2, 3, 1, 2)
t = tf.transpose(t,perm=[1,0,2,3])#通过维度的索引将2,3维度交换
print(t)
#out
shape=(3,2,1,2)

6.5 数据复制tf.tile

**tf.tile(b, multiples=[2,1]) ** mutiples的List说明了在每个维度上复制的倍数,即可在 axis=0 维度复制 1 次,在 axis=1 维度不复制。

p = tf.range(4)
p = tf.reshape(p,[2,2])
print(p)
q = tf.tile(p,multiples=[1,2])
r = tf.tile(p, multiples=[2,1])
print(q)
print(r)

out

tf.Tensor(
[[0 1]
 [2 3]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[0 1 0 1]
 [2 3 2 3]], shape=(2, 4), dtype=int32)
tf.Tensor(
[[0 1]
 [2 3]
 [0 1]
 [2 3]], shape=(4, 2), dtype=int32)

6.6 自动扩展 Broadcasting

tf.broadcast_to(x, new_shape)

对于所有长度为 1 的维度, tf.broadcast_to 的效果和 tf.tile 一样,都能在此维度上逻辑复
制数据若干份,区别在于 tf.tile 会创建一个新的张量, 执行复制 IO 操作,并保存复制后的
张量数据, Broadcasting 并不会立即复制数据, 它会逻辑上改变张量的形状,使得视图上变
成了复制后的形状。操作对用户透明,但是 Broadcasting 机制节省了大量计算资源,建议在
运算过程中尽可能地利用 Broadcasting 提高计算效率

7 数学运算

+    
-   
*
//          整除
%           取余
tf.pow(a, x) 或者**运算符可以方便实现指数运算𝑎^x
tf.square(x)
tf.sqrt(x)
tf.exp(x)      自然指数𝑒^x
tf.math.log(x) 自然对数ln𝑥

8 矩阵相乘

经网络中间包含了大量的矩阵相乘运算,\通过@运算符可以方便的实现矩阵相乘,还可以通过 tf.matmul(a, b)

u = tf.random.uniform([2,4],maxval=4,dtype=tf.int32)
v = tf.random.uniform([4,3],maxval=4,dtype=tf.int32)
o = u@v
print(o)

out

tf.Tensor([[ 6 12  4][ 8 16 11]], shape=(2, 3), dtype=int32)

根据矩阵相乘的定义, a 和 b 能够矩阵相乘的条件是, a 的倒数第一个维度长度(列)和b 的倒数第二个维度长度(行)必须相等。比如张量 a shape:[4,3,28,32]可以与张量 b shape:[4,3,32,2]进行矩阵相乘: 得到的结果为shape:

[4,3,28,2]

9 前向传播实战

import os
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, datasets
from matplotlib import pyplot as plt

(x, y), (x_test, y_test) = datasets.mnist.load_data()
x = tf.convert_to_tensor(x, dtype=tf.float32)/255
y = tf.convert_to_tensor(y, dtype=tf.int32)
y = tf.one_hot(y,depth=10)

train_dataset = tf.data.Dataset.from_tensor_slices((x, y))
train_dataset = train_dataset.batch(512)


w1 = tf.Variable(tf.random.truncated_normal([784,256],stddev=.1))
b1 = tf.Variable(tf.random.truncated_normal([256],stddev=0.1))
w2 = tf.Variable(tf.random.truncated_normal([256, 128],stddev=0.1))
b2 = tf.Variable(tf.random.truncated_normal([128],stddev=0.1))
w3 = tf.Variable(tf.random.truncated_normal([128, 10],stddev=.1))
b3 = tf.Variable(tf.random.truncated_normal([10],stddev=0.1))

def train_epoch(epoch):
    lr = 0.01
    loss = 0
    for step, (x, y) in enumerate(train_dataset):

        with tf.GradientTape() as tape:

            x = tf.reshape(x,(-1, 28 * 28))
            h1 = x@w1+b1  #tf.broadcast_to(b1, [x.shape[0],256])
            h1 = tf.nn.relu(h1)
            h2 = h1@w2+b2
            h2 = tf.nn.relu(h2)
            h3 = h2@w3+b3
            loss = tf.square((y - h3))
            loss = tf.reduce_mean(loss)/x.shape[0]
            grads = tape.gradient(loss,[w1,b1,w2,b2,w3,b3])
            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])
    return loss#返回最后一次的loss

def train():
    losses = []
    steps = []
    for i in range(10):
        steps.append(i)
        loss = train_epoch(i)
        losses.append(loss)
    return steps, losses

if __name__ == '__main__':
    step, loss = train()
    plt.figure()
    plt.plot(step, loss)
    plt.show()

out

posted @ 2020-03-04 21:37  Arrkwin  阅读(330)  评论(0)    收藏  举报