TensorFlow2初探——认识tensor

一、什么是Tensor

tensor翻译成中文就是张量,本意上是指任何维度大于2的变量,在tensorflow中,不论什么维度,都可以叫做tensor
在一般的理解中,我们把维度为0的数据称为元素,维度为1的数据称为向量,维度为2的数据称为矩阵,维度为2以上的数据称为张量

二、如何创建Tensor

tf.constant(数据)就可以创建一个tensor,tensor可以是任意的数据类型,在创建过程中,我们可以使用dtype=的参数来定义这个tensor的变量类型,但是要注意定义的类型与我们的数据类型是否相同

三、Tensor的常见属性

在GPU上创建tensor,我们这里的tensor就是创建在GPU上了,如果要创建在cpu上就将gpu改成cpu即可

with tf.device("gpu"):
	a = tf.constant([1])

但一般来说,对于一台电脑来说,通常只有一张显卡,就是说他会默认在gpu:0(第一张显卡)上创建Tensor
tensor.device:这个属性用于查看我们当前的tensor是放在哪个设备上的(GPU或者CPU)

如何修改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(误差平方和)求解函数

创建一个wx+b

# 定义x
x = tf.random.normal([4,784])
# 创建一个输出为10的层的方法
net = layers.Dense(10)
# 创建一个隐藏层输,在参数中输入输入的维度
net.build((4,784))
# W
net.kernel
# b
net.bias

五、索引切片

a[start:end:step]
# 从start,到end的数据中([start,end))间隔step个数字进行切片
a[start:end:-1]
# 逆序采样,从end到start采样

a[0,…,0],如果a是一个维度为[1,10,10,10,5]的一个张量,他表示的是a取第一个维度的第0个元素,第二第三第四维度的数据都取,到第五个位读取第0个元素

六、选择索引

我们以一个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来说,扩张他的维度

  1. tf.broadcast_to(a,[2,3,4])他的维度就会被扩张成【2,3,4】
a = tf.expand_dims(a,axis = 0)# 在前面添加一个维度
a2 = tf.tile(a,[2,1,1])# 在第一个维度复制两次,在第二个维度复制一次,第三个维度复制一次
  1. 区别:前者有内存优化,后者没有

九、tensor计算

  1. 常规数据计算:加减乘除,对应位置进行运算
  2. 矩阵数据计算:@,matmul,矩阵相乘
    1 . 如果有一个[b,3,4]和一个[b,4,5]的矩阵,进行运算那么他会看成第一个有b个【3,4】的矩阵,第二个有b个【4,5】的矩阵,然后对应矩阵相乘,于是计算的结果为[b,3,5]
  3. 维度上的计算:reduce_mean/max/min/sum,对指定的axis来计算他们对应的数值
  4. 科学计算: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()类型的信息

如果常规计算时,将variable类型改变的话,应该使用tf.assign_sub()这类函数进行计算,这个函数类似于-=,tf.assign_add()类似于+=,assgin就是赋值的意思

如果在前向传播过程出出现计算的loss为nan的话,可能是出现了梯度爆炸的问题,这就要求在随机初始化的时候设置一个参数stddev = 0.1以控制参数的方差

十二、下面是实现一个简单三层神经网络的代码

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))

猜你喜欢

转载自blog.csdn.net/kiminoamae/article/details/107747208