tensorflow 入门:创建tensor,索引切片,数学运行,前向传播

MNIST

hello,world!

tensorflow 入门:创建tensor,索引切片,数学运行,前向传播_第1张图片

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


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

(x, y), (x_val, y_val) = 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)          # one-hot编码,深度为10
print(x.shape, y.shape)
train_dataset = tf.data.Dataset.from_tensor_slices((x, y))   # 转换为dataset类型
train_dataset = train_dataset.batch(16)   # 控制batch

 


model = keras.Sequential([
    layers.Dense(512, activation='relu'),  # 三层全连接
    layers.Dense(256, activation='relu'),
    layers.Dense(10)])

optimizer = optimizers.SGD(learning_rate=0.001)


def train_epoch(epoch):
    for step, (x, y) in enumerate(train_dataset):
        with tf.GradientTape() as tape:   
            # [b, 28, 28] => [b, 784]
            x = tf.reshape(x, (-1, 28*28))   #自动求导
            # Step1. compute output
            # [b, 784] => [b, 10]
            out = model(x)
            # Step2. compute loss
            loss = tf.reduce_sum(tf.square(out - y)) / x.shape[0]  # 平方差求和

        # Step3. optimize and update w1, w2, w3, b1, b2, b3
        grads = tape.gradient(loss, model.trainable_variables)  # 计算导数
        # w' = w - lr * grad
        optimizer.apply_gradients(zip(grads, model.trainable_variables))  # 梯度更新

        if step % 100 == 0:
            print(epoch, step, 'loss:', loss.numpy())


for epoch in range(30):
    train_epoch(epoch)

tensor的一些操作

tf.constant(1,dtype=tf.double)   # 创建一个tensor 虽然取名为常量,但也是可以改变的
<tf.Tensor: shape=(), dtype=float64, numpy=1.0>  # float = flaot32
tf.constant('hello')    
<tf.Tensor: shape=(), dtype=string, numpy=b'hello'>   # 字符型,bool型
tf.constant([True,False])
<tf.Tensor: shape=(2,), dtype=bool, numpy=array([ True, False])>

with tf.device('cpu'):
    a = tf.constant([1])
with tf.device('GPU'):
    b = tf.range(4)
a.device,b.device
('/job:localhost/replica:0/task:0/device:CPU:0',    # 判断设备
 '/job:localhost/replica:0/task:0/device:GPU:0')
aa = a.gpu()
aa.device
'/job:localhost/replica:0/task:0/device:GPU:0'  # 设备的转换

b.numpy() 
array([0, 1, 2, 3], dtype=int32)   # 转换为 array

b.shape
TensorShape([4])                # 形状

b.ndim    # 维度
1

tf.rank(b)
<tf.Tensor: shape=(), dtype=int32, numpy=1>    # 跟 ndim差不多吧。。

tf.rank(tf.ones([3,4,5]))
<tf.Tensor: shape=(), dtype=int32, numpy=3>

tf.is_tensor(b) 
True

b.dtype
tf.int32

a = np.arange(5)
a.dtype
dtype('int64')

aa = tf.convert_to_tensor(a,dtype = tf.float32)   # array ->tensor
aa
<tf.Tensor: shape=(5,), dtype=float32, numpy=array([0., 1., 2., 3., 4.], dtype=float32)>

tf.cast(aa,dtype=tf.float32)    # 数据类型转换
<tf.Tensor: shape=(5,), dtype=float32, numpy=array([0., 1., 2., 3., 4.], dtype=float32)>

b = tf.constant([0,1])      # 整形和bool型的转换
tf.cast(b,dtype = tf.bool)
<tf.Tensor: shape=(2,), dtype=bool, numpy=array([False,  True])>

bb = tf.cast(b,dtype=tf.bool)
tf.cast(bb,tf.int32)
<tf.Tensor: shape=(2,), dtype=bool, numpy=array([False,  True])>

a = tf.range(5)
b = tf.Variable(a)      # 可优化的类型(需要自动求导的)
b.dtype,b.name
(tf.int32, 'Variable:0')

b = tf.Variable(a,name='input')    # name没啥用
b.name,b.trainable     # 可训练,需要跟踪这个变量
('input:0', True)

tf.is_tensor(b)     # 仍是一个tensor
True

b.numpy()
array([0, 1, 2, 3, 4], dtype=int32)


a = tf.ones([])
float(a)         # 针对标量,可以直接强制转换吧。。


tensor的创建

tf.constant([[1,2],[3,4]])
<tf.Tensor: shape=(2, 2), dtype=int32, numpy=
array([[1, 2],
       [3, 4]], dtype=int32)>

tf.convert_to_tensor(np.ones([2,3]))     # numpy 转 tensor
<tf.Tensor: shape=(2, 3), dtype=float64, numpy=
array([[1., 1., 1.],
       [1., 1., 1.]])>

tf.convert_to_tensor([[1,2],[3,4]])
<tf.Tensor: shape=(2, 2), dtype=int32, numpy=
array([[1, 2],
       [3, 4]], dtype=int32)>


tf.zeros([2,3,3])   # shape
<tf.Tensor: shape=(2, 3, 3), dtype=float32, numpy=
array([[[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]],

       [[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]]], dtype=float32)>

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

tf.zeros(a.shape)    # 同上

tf.ones([2,3,3])   
tf.ones_like(a)


tf.fill([2,3],5)    # 指定填充值
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[5, 5, 5],
       [5, 5, 5]], dtype=int32)>



#随机初始化
tf.random.normal([2,2],mean=1,stddev=1)        # 正泰分布,均值,方差
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[ 2.16922   , -0.55846536],
       [ 2.9959366 ,  2.3603005 ]], dtype=float32)>
       
# 截断分布,就是截断的产生正态分布的随机数,即随机数与均值的差值若大于两倍的标准差,则重新生成。
# 比如用sigmoid函数,这样随机初始化,就可以减缓梯度消失,将梯度小的部分截去了
tf.random.truncated_normal([2,2],mean=0,stddev=1)   
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[ 0.4707204 ,  0.6333614 ],
       [-0.9041825 , -0.66911685]], dtype=float32)>

tf.random.uniform([2,2],minval=0,maxval=10)    # 均匀分布,指定范围


idx = tf.range(10)
print(idx)
idx = tf.random.shuffle(idx)     # 打乱索引
print(idx)
tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)
tf.Tensor([2 4 6 3 9 0 7 5 8 1], shape=(10,), dtype=int32)

a = tf.random.uniform([10],maxval=10,dtype =tf.int32)
print(a)
a = tf.gather(a,idx)       # 根据打乱的索引来打乱吧。。
a  
tf.Tensor([5 6 1 9 6 6 1 5 6 0], shape=(10,), dtype=int32)
<tf.Tensor: shape=(10,), dtype=int32, numpy=array([1, 6, 1, 9, 0, 5, 5, 6, 6, 6], dtype=int32)>

索引和切片

a = tf.ones([1,5,5,3])
a[0][0]     # shape 5.3
a[0][0][0][2]   # shape 1

a[0,0,0,2]   # 同上= tf.range(10)<tf.Tensor: shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int32)>[-1:]     # 反向,最后是 -1 
<tf.Tensor: shape=(1,), dtype=int32, numpy=array([9], dtype=int32)>[:-2]      # 除了最后几个。。切片
<tf.Tensor: shape=(8,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7], dtype=int32)>


a = tf.random.uniform([4,28,28,3]) 
a[0,:,:,:].shape    # 同 a[0]

a[:,0,:,:].shape    # 
TensorShape([4, 28, 3])

a[0:2,:,:,:].shape
TensorShape([2, 28, 28, 3])

a[:,0:28:2,0:28:4,:].shape    # start:end:step ,同 ::2
TensorShape([4, 14, 7, 3])

a = tf.range(4)
a[::-1]  # 逆序
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([3, 2, 1, 0], dtype=int32)>

a[2::-2]
<tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 0], dtype=int32)>

a=tf.random.normal([2,4,28,28,3])   # 可以理解为 2 个任务,4张28*28的三通道图片
a[0].shape == a[0,:,:,:,:].shape   # Ture

a[0,...].shape   # 同上,... 就可以代替一堆中间的维度 :
TensorShape([4, 28, 28, 3])

a[0,...,2].shape
TensorShape([4, 28, 28])

高级的

a = tf.random.normal([4,35,8])  # 假如是4 个班级,每班35个人,8门课
tf.gather(a,axis=0,indices=[2,3]).shape  # axis=0表示我们取班级这个维度,indices表示取的班级号
TensorShape([2, 35, 8])

tf.gather(a,axis=1,indices = [2,3,7,1,16]).shape  # 顺序不固定。以人数的维度选取
TensorShape([4, 5, 8])

# 对于这种需要嵌套的,就需要一层一层来。gather只能操作一个维度
aa = tf.gather(a,axis=0,indices=[1,2]) # 两个班级两个学生的八门成绩
aaa = tf.gather(aa,axis=2,indices=[9,10])

# gather_nd 操作多维,只需要看最内层就行。。。
tf.gather_nd(a,[0,1]).shape    # 0号班级,1号学生的八门成绩 
TensorShape([8])   # 相当于  a[0,1]  

tf.gather_nd(a,[[0,1,2]])    # 值相同,但维度不同,,,就相当于[a[0,1,1]]
<tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.5100031], dtype=float32)>
tf.gather_nd(a,[0,1,2])
<tf.Tensor: shape=(), dtype=float32, numpy=1.5100031>

tf.gather_nd(a,[[[0,0,0],[1,1,1],[2,2,2]]])
<tf.Tensor: shape=(1, 3), dtype=float32, numpy=array([[ 0.6371678 , -0.72258306,  0.7952548 ]], dtype=float32)>
# 表示 0班0号学生0课的成绩和 1班1号学生1课的成绩 和,,,,,

[[a[0,0,0],a[1,1,1],a[2,2,2]]]   # 值是相同的,就是类型不一样。。。
[[<tf.Tensor: shape=(), dtype=float32, numpy=0.6371678>,
  <tf.Tensor: shape=(), dtype=float32, numpy=-0.72258306>,
  <tf.Tensor: shape=(), dtype=float32, numpy=0.7952548>]]

# 额,,蒙版。。。就是bool索引吧
tf.boolean_mask(a,mask=[True,True,False,False]).shape # 维度要匹配啊,,
TensorShape([2, 35, 8])

b = tf.ones([2,3,4])
tf.boolean_mask(b,mask = [[True,True,False],[False,True,True]])  # 多维

维度变换

[b,h,w] - > [b,2,h/2 *w] 将一个图片分为了上下两部分

a = tf.random.normal([4,28,28,3])
a.shape,a.ndim
(TensorShape([4, 28, 28, 3]), 4)

tf.reshape(a,[4,28*28,3]).shape   #抹掉了行列的概念,变成了一张图片
TensorShape([4, 784, 3])

tf.reshape(a,[4,-1,3]).shape   # -1 很好使啊
TensorShape([4, 784, 3])

tf.reshape(a,[4,-1]).shape   # 表示一堆数据点
TensorShape([4, 2352])

tf.reshape(tf.reshape(a,[4,-1]),[4,14,56,3]).shape  # 只要size正确


tf.transpose(a).shape
TensorShape([3, 28, 28, 4])    # 转置

tf.transpose(a,perm=[0,3,1,2]).shape   # 指定维度的排列
TensorShape([4, 3, 28, 28])
# 在pytorch 中图片的维度是 [b,3,h,w] 就可以这样转换


a.shape
TensorShape([4, 28, 28, 3])

tf.expand_dims(a,axis=0).shape    # 增加维度
TensorShape([1, 4, 28, 28, 3])
tf.expand_dims(a,axis=3).shape   # axis = -1 从后加
TensorShape([4, 28, 28, 1, 3])

b = tf.ones([1,2,1,1,3])
b.shape

tf.squeeze(b).shape
TensorShape([2, 3])

tf.squeeze(b,axis=0).shape   # 降维,就是把1 去了8
TensorShape([2, 1, 1, 3])

tf.squeeze(b,axis=2).shape
TensorShape([1, 2, 1, 3])


# Broadcasting   广播机制
# 从最低维对齐开始扩张,比如 [4,32,8] + [5] (维度为1)
# 将 [5] 扩张维 [4,32,8] 意义就是给每个学生的没课加5分,相当于一个偏置
# 可以节省内存。写起来也很简洁。
x = tf.random.normal([4,32,32,3])
(x+tf.random.normal([3])).shape
TensorShape([4, 32, 32, 3])

(x+tf.random.normal([32,32,1])).shape  # 广播是自动进行的
TensorShape([4, 32, 32, 3])

(x+tf.random.normal([4,1,1,1])).shape
TensorShape([4, 32, 32, 3])

(x+tf.random.normal([1,4,1,1])).shape
# 报错,无法扩张,从低维开始,若维度不匹配,填充1,然后扩张。
# 这里第二维为4,需要为1 才可以扩张。

a = tf.ones([3,4])
a = tf.broadcast_to(a,[2,3,4])   # 手动进行
a.shape
TensorShape([2, 3, 4])

b = tf.ones([3,4])
b =tf.expand_dims(b,axis=0)   # 广播的过程,先扩维,在填充
print(b.shape)
b = tf.tile(b,[2,1,1])    # 要是1 才可以
b.shape

(1, 3, 4)
TensorShape([2, 3, 4])

数学运算

b = tf.fill([2,2],2.)
a = tf.ones([2,2])
a+b,a-b,a*b,a/b  # 逐元素加减乘除

b//2,b%2  # 整除,余数

tf.math.log(a),tf.exp(a)  # log ,exp,这里的log 以 e为底,实现log_10等就用个计算

tf.math.log(8,) / tf.math.log(2.)  # log_e(8) / log_e(2) = log_2(8)

tf.pow(b,3) # 开方 or b**3

tf.sqrt(b)    # 开跟

a@b,tf.matmul(a,b)    # 矩阵乘法,可以进行

a=tf.ones([4,2,3])
b=tf.fill([4,3,5],2.)    # 就是4 对矩阵[2,3]和[3,5] 的相乘,后两维参与运算
a@b     # shape=(4, 2, 5)

c = tf.broadcast_to(b,[4,3,5])    # 如果维度不匹配,还可以进行广播后在矩阵相乘
a@c 

x = tf.ones([4,2])
W = tf.ones([2,1])
b = tf.constant(0.1)    # 自动广播
x @ W + b

前向传播

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

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

(x, y), _ = datasets.mnist.load_data()
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.   # x: [0~255] => [0~1.]
y = tf.convert_to_tensor(y, dtype=tf.int32)

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


train_db = tf.data.Dataset.from_tensor_slices((x,y)).batch(128)
train_iter = iter(train_db)   # 迭代器,数据加标签
sample = next(train_iter)     # 一个一个取出
print('batch:', sample[0].shape, sample[1].shape) # 数据,标签 batch = 128


# [b, 784] => [b, 256] => [b, 128] => [b, 10]  过程
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

for epoch in range(10):
    for step, (x, y) in enumerate(train_db):
        # 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] => [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] => [b, 10]
            y_onehot = tf.one_hot(y, depth=10)

            # [b, 10]
            loss = tf.square(y_onehot - out)   # 均方差,计算损失
            # mean: scalar
            loss = tf.reduce_mean(loss)   # 进行一个放缩

        # compute gradients
        grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
        # print(grads)
        # w1 = w1 - lr * w1_grad
        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])


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

函数总结

函数 作用 备注
os.environ[‘TF_CPP_MIN_LOG_LEVEL’]=‘2’ 控制输出信息
tf.constant(data,dtype=) 创建tensor 列表,常数,字符,bool都可
t.numpy() 转换为array
with tf.device(‘gpu’): 指定变量存储的设备
a.device/a.shape/a.ndim/b.dtype/b.trainable 属性
a = b.gpu() 转移设备
tf.rank(b)/tf.is_tensor(b)
a = tf.convert_to_tensor(a,dtype) array - > tensor
tf.cast(a,dtype) 数据类型转换
tf.range()
tf.Variable(a) 可优化类型,需要跟进自动求导的变量
tf.zeros/ones/ones_like/fill(shape,var) 创建tensor
tf.random.normal(shape,mean,stddev) 正态分布
tf.random.truncated_normal(shape,mean,stddev) 截断分布,减缓梯度消失
tf.random.uniform(shape,minval,maxval) 均匀分布
idx = tf.random.shuffle(idx) 打乱索引(tf.gather(a,idx)打乱数据)
a[start:\end:step] a[0,…,2] 索引和切片
tf.gather(data,axis,indices) 指定维度,索引选取,嵌套选取,需要多个gather配合
tf.gather_nd(data,[idx]) 操作多维 相当于 [a[idx]]只需要看最内层
tf.boolean_mask(a,mask=[bool]) 蒙版,可多维,需要匹配
tf.reshape(a,shape) 维度变化,-1的妙用
tf.transpose(a,perm) 转置,perm指定维度的排列
tf.expand_dims(a,axis) 在axis出加一维
tf.squeeze(a,axis) 去掉一维的,axis指定维度
a = tf.broadcast_to(a,shape) 手动广播
±*/ // % 逐元素
tf.math.log(a),tf.exp(a) log_10 ,e log_e(8) / log_e(2) = log_2(8)
tf.pow(b,3)/tf.sqrt(b) 次方
a@b, tf.matmul(a,b) 矩阵乘法
tf.reduce_min(x)/reduce_max(x) 最大最小值
tf.data.Dataset.from_tensor_slices((x,y)).batch(16) 构建dataset 对象
train_iter = iter(train_db) / sample = next(train_iter) 迭代器
for step, (x, y) in enumerate(train_db): batch迭代
with tf.GradientTape() as tape 自动求导,跟踪Variable 变量
y_onehot = tf.one_hot(y, depth=10) 独热
tf.reduce_mean(loss) 均值
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3]) 求导
w1.assign_sub(lr * grads[0]) 原地更新参数,类型不变

你可能感兴趣的:(深度学习)