一、如何创建Tensor
tf.constant(数据)
就可以创建一个tensor,tensor可以是任意的数据类型,在创建过程中,我们可以使用dtype=
的参数来定义这个tensor的变量类型,但是要注意定义的类型与我们的数据类型是否相同。
二、Tensor的常见属性
转换设备
# 从GPU转化成CPU
tensor.cpu()
# 从CPU转化成GPU
tensor.gpu()
tensor.numpy()
将tensor类型转化成nandarry类型,nadarry类型默认是在CPU上的
tf.convert_to_tensor(数据,dtpye=)
将numpy类型转化成tensor类型,但是需要注意的是,如果不指定类型的话还是会使用numpy的数据类型而不是tensor的数据类型
查看tensor的维度
tensor.ndim
:返回一个int数值,表示其维度
tf.rank(tensor)
返回一个tensor类型的数据,也表示其维度
tf.is_tensor(变量)
用于判断该变量是否为tensor类型
tensor.dtype
查看tensor的数据的类型
tf.cast(tensor,dtype=)
用于tensor类型的数据类型转化
tf.Variable类
tf.Variable(tensor)
variable类本质上也是一个tensor,但是他被赋予两个属性,一个是name,可以忽略,另一个是trainable翻译过来就是可训练的,也就是说这个变量在我们的前向传播和反向传播过程中会创建关于这个变量的w和b,也就是说,这个变量是需要一个梯度来实现参数更新
当我们创建一个Variable类后,tf会自动记录我们这个变量的相关梯度信息
如果我们的tensor是一个元素的话,我们可以使用int(tensor)等强制转化类型的方法实现从GPU转化到CPU上的一个过程
三、创建tensor
tf.zeros(形状)
:创建一个指定形状的0张量(内部所有都是0,但是满足指定的形状),形状用元组或者列表存储
tf.ones(形状)
:创建一个指定形状的数据都是1的张量
tf.ones_like(tensor) == tf.ones(tensor.shape)
tf.fill(形状,数字)
创建一个指定形状的数据全是指定数字的张量
tf.random.normal(形状,mean=1,stddev=1)
产生一个指定形状的,符合正态分布的一个tensor,mean是均值默认为0,stddev是方差默认为1
tf.random.truncated_normal(形状,mean = 1,stddev=1)
产生一个指定形状的,符合截断正态分布的一个tensor,其他参数类似
什么事截断的正态分布呢:就是说在一个标准的正态分布上,当我们的数值达到某一部分时,我们舍去那一部分,然后再次进行采样。当我们遇到梯度消失时,这样的初始化方法或许会有不错的效果
tf.random.uniform(形状,minval=,maxval=)
产生一个指定形状的,在minval~maxval之间采样的符合均匀分布的tensor
tf.random.shuffle(tensor)
对一个以为tensor进行随机打乱原有的次序
tf.one_hot(tensor)
对一个tensor一维数组进行onehot编码,数值作为索引
tf.keras.losses.mes(y,yHat)MSE(误差平方和)
求解函数
# 定义x
x = tf.random.normal([4,784])
# 创建一个输出为10的层的方法
net = layers.Dense(10)
# 创建一个隐藏层输,在参数中输入输入的维度
net.build((4,784))
# W
net.kernel
# b
net.bias
关于kenel和bias可参考:官方
四、索引切片
tensor的索引切片方式和numpy几乎是一样的。
五、选择索引
我们以一个tensor,a(维度为[4,38,5])为例子
tf.gather(a,axis=0,indices=[2,3])
表示选择我们a中的第0维度的,第2和3个数据,维度将会转化为[2,38,5]
tf.gather_nd(a,[[1,2,3],[2,5,3]])
表示选择我们a中的a[1,2,3]与a[2,5,3]组成的tensor
tf.boolean_mask(a,mask=[[True,False,False],[False,True,True]])
对于一个([2,3])维度的就取出他对应的维度就可以了,对于一个[2,3,4]维度的看前面的2,3取出对应位置的四个元素
六、维度修改
在修改维度时通常会看到tf.reshape(a,[4,-1])
这样的写法,意思是将其转化成一个二维数据,行是4,他的宽需要自动计算,最终的size仍要一致
tf.transpose(a,perm=[0,3,1,2])
对图片的维度进行转换[b,h,w,c]标称[b,c,h,w]
tf.expand_dims(a,axis=0)
增加一个维度,axis指定在哪个轴前添加
tf.squeeze(a,axis=)
维度收缩,如果不填axis的话,就对所有维度为1的的维度全部去除,如果输入axis,就只对axis维度进行去除
七、tensor复制扩张
对于一个维度为[3,4]
的tensor a
来说,扩张他的维度
tf.broadcast_to(a,[2,3,4])·
他的维度就会被扩张成[2,3,4]
a = tf.expand_dims(a,axis = 0)# 在前面添加一个维度
a2 = tf.tile(a,[2,1,1])# 在第一个维度复制两次,在第二个维度复制一次,第三个维度复制一次
区别:前者有内存优化,后者没有
八、tensor计算
- 常规数据计算:加减乘除,对应位置进行运算
- 矩阵数据计算:@,matmul,矩阵相乘
1 . 如果有一个[b,3,4]和一个[b,4,5]的矩阵,进行运算那么他会看成第一个有b个【3,4】的矩阵,第二个有b个【4,5】的矩阵,然后对应矩阵相乘,于是计算的结果为[b,3,5] - 维度上的计算:
reduce_mean/max/min/sum
,对指定的axis来计算他们对应的数值 - 科学计算:
tf.math.log(a),tf.exp(a)
这里的log是以e为低的,如果要用log2,log10,应该写成tf.math.log(a)/tf.math.log(10)
运用公式来实现,第二个exp表示的是,e的x次方
九、数据装载
对读取进来的图片先使用x = tf.convert_to_tensor(x,dtype = tf.float32)
,如果是图片还要除以255,读取y = tf.convert_to_tensor(y,dtype=tf.int32)
然后开始装载:
train = tf.data.Dataset.from_tensor_sclices((x,y)).batch(128)
打印加载结果
sample = next(iter(train))
print(sample[0].shape)
十、纪录计算过程
在进行前项传播的时候,需要把前项传播放在with tf.GradientTape() as tape:
中,才能使用自动求导过程,在写完前项传播后,我们需要在这一行包括的区域外面,写一个grads = tape.gradient(loss函数,[需要做反向传播的参数])
,grads会返回一个list,与我们输入的参数顺序相同。
还要注意,默认情况tape只会对变量求导,即tf.Variable()
类型的信息,要想对常量求导,则需要用tape.watch([需要观察求导的常量参数])
设置参数。
如果常规计算时,将variable类型改变的话,应该使用tf.assign_sub()
这类函数进行计算,这个函数类似于-=
,tf.assign_add()
类似于+=
,assgin就是赋值的意思
如果在前向传播过程出出现计算的loss为nan的话,可能是出现了梯度爆炸的问题,这就要求在随机初始化的时候设置一个参数stddev = 0.1
以控制参数的方差
import tensorflow as tf
# 创建4个张量
a = tf.constant(1.)
b = tf.constant(2.)
c = tf.constant(3.)
w = tf.constant(4.)
with tf.GradientTape() as tape:# 构建梯度环境
tape.watch([w]) # 将w加入梯度跟踪列表
# 构建计算过程
y = a * w**2 + b * w + c
# 求导
[dy_dw] = tape.gradient(y, [w])
print("dw",dy_dw)
# 用variable而非watch
w2 = tf.Variable(tf.constant(4.))
with tf.GradientTape() as tape:# 构建梯度环境
# 构建计算过程
y = a * w2**2 + b * w2 + c
# 求导
[dy_dw2] = tape.gradient(y, [w2])
print("dw2",dy_dw2)
输出:
dw tf.Tensor(10.0, shape=(), dtype=float32)
dw2 tf.Tensor(10.0, shape=(), dtype=float32)
如果注释掉:tape.watch([w]) ,则打印dw None
十一、示例:三层神经网络的代码训练mnist数据集
import tensorflow as tf
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
y = tf.convert_to_tensor(y,dtype = tf.int32)
train = tf.Dataset.from_tensor_sclices((x,y)).batch(128)
# 初始化网络参数
w1 = tf.Variable(tf.random.truncated_normal([28*28,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
epoch = 10
for i in range(epoch):
for step,(x,y) in enumerate(train):
# 转化图片存储方式
x = tf.reshape(x,[-1,28*28])
# 前项传播
with tf.GradientTape() as tape:
h1 = x@w1 + b1
h1 = tf.nn.relu(h1)
h2 = h1@w2 + b2
h2 = tf.nn.relu(h2)
out = h2@w3 + b3
# 转化成one-hot编码
y_onehot = tf.one_hot(y,depth=10)
loss = tf.square(y_onehot - out)
loss = tf.reduce_mean(loss)
# 反向传播
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])
if step%100 == 0:
print(step,"loss:",float(loss))
参考:
https://blog.youkuaiyun.com/kiminoamae/article/details/107747208?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107747208-blog-105554738.topnsimilarv1&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107747208-blog-105554738.topnsimilarv1&utm_relevant_index=1
https://blog.youkuaiyun.com/qq_39507748/article/details/105554738