Python深度学习基于Tensorflow(2)Tensorflow基础

文章目录

首先是Tensorflow的安装,由于可能会出现版本冲突,最好在conda环境安装,同时,目前windows版本好像只支持2.10,更高的版本目前只支持linux系统。

conda 安装, 配置以及使用_conda安装配置-CSDN博客
23.10.02更新 windows系统下的Tensorflow安装(图多详细)_windows 安装tensorflow-CSDN博客
23.10.02更新 Windows下CUDA和CUDNN的安装和配置(图多详细)_cuda百度云-CSDN博客

GPU涉及到显卡,这里可以在命令行输入nvidia-smi获取GPU信息

安装顺序为:查看显卡信息 -> 安装CUDA,CUDNN -> 安装Tensorflow GPU版

list 在 numpy 中叫 ndarray,在 tensorflow 中叫 tensor,其性能上的主要区别是:ndarray只能在CPU上计算,而tensorflow不仅可以在CPU上计算,也可以在GPU/TPU上计算。

tensor 和 numpy 一样,有三个属性:TensorObject.ndim、 TensorObject.shape、 TensorObject.dtype。分别表示数据维度,数据形状,数据类型。只需要在tensor后面加一个numpy(),如:TensorObject.numpy()。就可以很轻松的将tensor转化为numpy。

python 复制代码
import tensorflow as tf

基本操作

数据转换和数据生成

首先是将已有数据转化为tensor,其中tensor有两种,一种是常数,一种是变量,其中常数是不可以对数值进行更改的,而变量的值是可以更改的。

python 复制代码
lst = [1.123, 2.333, 4.12212] 

# 获得常数
tensor = tf.constant(lst)
# 获得变量
tensor_variable = tf.Variable(lst)
## 更改变量
tensor_variable.assign([1.3,1.3,1.3])

# tensor转换为numpy
array = tensor.numpy()

# numpy转化为tensor
tensor = tf.constant(array)
tensor_variable = tf.Variable(array)
tensor = tf.convert_to_tensor(array)

# 拓展:创建不规则张量
tensor = tf.ragged.constant([
    [0, 1, 2, 3],
    [4, 5],
    [6, 7, 8],
    [9]])

数据生成主要是random,range,linspace;这三个函数对应于numpy中的random,arange,linspace

python 复制代码
## range 生成0-100之间步长为3的tensor
tf.range(0,100,3)

## linspace 在0-100之间等距离生成3个数的tensor
tf.linspace(0,100,3)

## random示例:normal 生成形状为[10,20]的正态分布矩阵
tf.random.normal(shape=[10,20])
操作形状

操作形状与numpy不一样,numpy可以直接调用,tensor不能,需要tf.reshape()

python 复制代码
tf_const = tf.random.normal(shape=[10,20])

## 展开为1维向量
tf_const = tf.reshape(tf_const, -1)

tensorflow 采取 行优先 内存访问顺序,一般来说,tf.reshape 唯一合理的用途是合并或者拆分相邻轴

进行轴变换需要用到tf.transpose()

python 复制代码
tf_const = tf.random.normal(shape=[10,20,30])

## 使原来的0,1,2 变成 1,2,0
tf.transpose(tf_const, [1,2,0])
数据提取和保存

张量切片与NumPy切片一样,也是基于索引。切片或者索引是Python语言中针对字符串、元祖或者列表进行读写的魔法方法,在第1章介绍NumPy的时候也提到过,针对NumPy数组,我们也可以进行索引或者切片操作。同样的,我们也可以对TensorFlow里面的张量进行索引或者切片操作,并且遵循Python语言或者说NumPy数组的索引规则。

  • 索引从下标0开始。
  • 负索引按照倒叙进行索引,比如 -1表示倒数第一个元素。
  • 切片的规则是start:stop:step。
  • 通过制定多个索引,可以对多维度张量进行索引或者切片。
变量

tensorflow 与 numpy 最不同的一点就是 tensorflow 有变量。

变量是深度学习在训练模型时用来存储和更新参数的,在创建的时候必须要初始化才能使用,即一定要赋值。变量和常量除了定义方式以外以及相关操作一致。

Numpy和Tensorflow的比较
操作类别 NumPy TensorFlow 2+
数据类型 np.ndarray tf.Tensor
np.float32 tf.float32
np.float64 tf.double
np.int64 tf.int64
从已有数据构建 np.array(3.2, 4.3, dtype=np.float16) a=tf.constant(3.2, 4.3, dtype=tf.float16)#常量 v=tf.Variable(3.2, 4.3, type=tf.float16)#变量
x.copy() tf.identity(x);tf.tile(a,(n,m))# 元组里的每个数值对应该轴复制次数
np.concatenate tf.concat((a,b),axis)# 待拼接的轴对应的维度数值可以不等,但其他维度形状需一致 tf.stack((a,b),axis)# 带堆叠张量的所有维度数值必须相等
线性代数 np.dot #内积 np.multiply(*)#逐元素相乘或哈达玛积 tf.matmul(x, y, name=None) 或(@)#内积tf.multiply(x, y, name=None),或(*)#逐元素相乘或哈达玛积
属性 x.ndim x.ndim
x.shape x.shape
x.size tf.size(x)
改变形状 x.reshape tf.reshape(x,(n,(-1)))#-1表示自动计算其他维度
np.transpose(x, 新的轴顺序 ) tf.transpose(x, 新的轴顺序 )
x.flatten() tf.reshape(x,-1);tf.keras.layers.Flatten()
维度增减 np.expand_dims(arr, axis) tf.expend_dims(a,axis)
np.squeeze(arr, axis) tf.squeeze(a,axis),#如果不声明axis,那么将压缩所有数值为1的维度。
类型转换 np.floor(x) x=tf.cast(x,dtype=XX) x=x.numpy()=>np.array
比较 np.less tf.less(x,threshold)
np.less_equal tf.less_equal(x, threshold)
np.greater_equal tf.greater_equal(x, threshold)
随机种子 np.random.seed tf.random.set_seed(n)

计算图

计算图类似于一个计算过程,具体可以看机器学习入门(10)--- 浅显易懂的计算图、链式法则讲解_请画出该函数的计算图,请用方形节点表示-CSDN博客

Tensorflow目前有三种图:很拉跨的静态图,效率低的动态图以及方便的自动图。

TensorFlow有3种计算图:TensorFlow1.0时代的静态计算图,TensorFlow 2.0时代的动态计算图和Autograph。静态计算图,需要先使用TensorFlow的各种算子创建计算图,再开启一个会话(Session)执行计算图。 而在TensorFlow 2.0时代,默认采用的是动态计算图,即每使用一个算子后,该算子会被动态加入隐含的默认计算图中立即执行并获取返回结果,而无须执行Session。 使用动态计算图(即Eager Excution立即执行)的好处是方便调试程序,执行TensorFlow代码犹如执行Python代码一样,而且可以使用Python,非常便捷。不过使用动态计算图的坏处是运行效率相对会低一些,因为在执行动态图期间会有许多次Python进程和TensorFlow的C++进程之间的通信。而静态计算图不通过Python这个中间环节,基本在TensorFlow内核上使用C++代码执行,效率更高。 为了兼顾速度与性能,在TensorFlow 2.0中可以使用@tf.function装饰器将普通Python函数转换成对应的TensorFlow计算图构建代码。与执行静态图方式类似,使用@tf.function构建静态图的方式叫作Autograph(自动图)

静态图

其中静态图是1.0版本的产物,很拉,大概是这样子

python 复制代码
import tensorflow as tf

#定义计算图
grap = tf.compat.v1.Graph()
with grap.as_default():
    #placeholder为占位符,执行会话时候指定填充对象
    x = tf.compat.v1.placeholder(tf.float32,shape=[],name='x')  
    y = tf.compat.v1.placeholder(tf.float32,shape=[],name='y')  
    b = tf.compat.v1.Variable(15.0,dtype=tf.float32) 
    z=tf.multiply(x,y,name='c')+b
    #初始化参数
    init_op = tf.compat.v1.global_variables_initializer()

#执行计算图
with tf.compat.v1.Session(graph = grap) as sess:
    sess.run(init_op)
    print(sess.run(fetches = z,feed_dict = {x:20,y:36}))

可以看到,先要创建Graph,然后要把计算写入Graph中,接着再进行计算。

动态图

以上代码如果采用动态计算图的方式实现,需要做如下处理。

1)把占位符改为其他张量,如tf.constant或tf.Variable。

2)无须显式创建计算图。

3)无须变量的初始化。

4)无须执行Session,把sess.run中的feed_dict改为传入函数的参数,fetches改为执行函数即可。

采用TensorFlow 2.0动态图执行的方式,代码如下:

python 复制代码
import tensorflow as tf

#定义常量或变量
x=tf.constant(20,dtype=tf.float32)
y=tf.constant(36,dtype=tf.float32)

#定义函数
def mul(x,y):  
    #定义常量或变量
    b=tf.Variable(15 ,dtype=tf.float32) 
    z=tf.multiply(x,y,name='c')+b
    return z

#执行函数
print(mul(x,y).numpy())    

与静态计算图相比,可以看到动态计算图虽然调试编码效率高但是执行效率偏低

自动图

TensorFlow 2.0 之后的自动图(AutoGraph)可以将动态计算图转换成静态计算图,兼顾开发效率和执行效率。通过给函数添加@tf.function装饰器就可以实现AutoGraph功能,但是在编写函数时需要遵循一定的编码规范,否则可能达不到预期的效果,这些编码规范主要包括如下几点。

  • 避免在函数内部定义变量(tf.Variable)。
  • 函数体内应尽可能使用TensorFlow中的函数而不是Python语言自有函数。比如使用tf.print而不是print,使用tf.range而不是range,使用tf.constant(True)而不是True。
  • 函数体内不可修改该函数外部的Python列表或字典等数据结构变量。

用@tf.fuction装饰函数,把动态计算图转换为自动图如下:

python 复制代码
import tensorflow as tf

#定义常量或变量
x=tf.constant(20,dtype=tf.float32)
y=tf.constant(36,dtype=tf.float32)
b=tf.Variable(15 ,dtype=tf.float32)
#定义函数
@tf.function
def mul(x,y):  
    # 定义常量或变量
    # b=tf.Variable(15 ,dtype=tf.float32) # 不可以在自动图里定义变量
    z=tf.multiply(x,y,name='c')+b
    return z

#执行函数
print(mul(x,y).numpy())     

这样看起来不爽,可以用类包装一下就好看了

python 复制代码
import tensorflow as tf

#定义一个类
class Test_Mul:
    def __init__(self):
        super(Test_Mul, self).__init__()
        self.b=tf.Variable(15 ,dtype=tf.float32) 

    @tf.function
    def mul(self,x,y):      
        z=tf.multiply(x,y,name='c')+self.b
        return z

#执行函数
x=tf.constant(20,dtype=tf.float32)
y=tf.constant(36,dtype=tf.float32)
Test=Test_Mul()
print(Test.mul(x,y).numpy())     

自动微分

构建计算图后,肯定是需要计算微分求导数的,tensorflow深度学习架构帮助我们自动地完成了求梯度运算。 Tensorflow一般使用梯度磁带tf.GradientTape来记录正向运算过程,然后使用反播磁带自动得到梯度值。

按照上述流程进行一次微分

python 复制代码
import tensorflow as tf
import numpy as np 

# f(x) = a*x**2 + b*x + c的导数
#缺省情况,张量tf.constant为常量,只有变量tf.Variable作为参数更新
x = tf.Variable(0.0,name = "x",dtype = tf.float32)
a = tf.constant(1.0)
b = tf.constant(5.0)
c = tf.constant(2.0)

with tf.GradientTape() as tape:
    tape.watch([a,b,c]) # 这里abc是张量,如果不需要观察张量的导数,可以删掉
    y = a*tf.pow(x,2) + b*x + c
    
dy_dx,dy_da,_,dy_dc = tape.gradient(y,[x,a,b,c])
print(dy_da)
print(dy_dc)

二次微分可以使用嵌套的方式:

python 复制代码
with tf.GradientTape() as tape2:
    with tf.GradientTape() as tape1:   
        y = a*tf.pow(x,2) + b*x + c
    dy_dx = tape1.gradient(y,x)   
dy2_dx2 = tape2.gradient(dy_dx,x)

print(dy2_dx2)

这里要注意的是:梯度磁带会自动监视 tf.Variable,但不会监视 tf.Tensor。如果无意中将变量(tf.Variable)变为常量(tf.Tensor)(如tf.Variable 与一个tf.Tensor相加,其和就变成常量了),梯度磁带将不再监控tf.Tensor。 为避免这种情况,可使用 Variable.assign 给tf.Variable赋值

python 复制代码
x = tf.Variable(2.0)

for epoch in range(2):
  with tf.GradientTape() as tape:
    y = x+1

  dy_x=tape.gradient(y, x)
  #print(type(x).__name__, ":", tape.gradient(y, x))
  print(dy_x)
  #变量变为常量tf.Tensor
  x = x + 1   # This should be `x.assign_add(1)`

最后要记得删除tape!!!

python 复制代码
del tape

使用Tensorflow 实现回归

上一节numpy一样,设置一样的函数如下:

y = 3 x 2 + 2 x + 1 y=3x^2+2x+1 y=3x2+2x+1

图像如下:

假设知道最高项为3,设函数为: y = a x 2 + b x + c y=ax^2+bx+c y=ax2+bx+c

python 复制代码
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

## 准备数据
np.random.seed(42)  
  
x = np.linspace(-10, 10, 50)  
y = 3 * np.power(x, 2) + 2 * x + 1

a = np.random.random(size=(1, 1))  
b = np.random.random(size=(1, 1))  
c = np.random.random(size=(1, 1))  

## 定义模型
class LinearRegression:
    def __init__(self):
        self.a = tf.Variable(a)
        self.b = tf.Variable(b)
        self.c = tf.Variable(c)

    def __call__(self, x):
        return self.a * tf.square(x) + self.b * x + self.c

model = LinearRegression()

## 定义损失
@tf.function
def compute_loss(y, y_pred):
    return tf.reduce_mean(tf.square(y-y_pred))

## 定义训练过程
def train_one_epoch(x, y, lr=1e-4):
    with tf.GradientTape() as tape:
        y_pred = model(x)
        loss = compute_loss(y, y_pred)
    a, b, c = model.a, model.b, model.c
    da, db, dc = tape.gradient(loss, [a, b, c])
    a.assign(a - lr*da)
    b.assign(b - lr*db)
    c.assign(c - lr*dc)
    return loss.numpy()


## 计算loss
loss_list = []
for i in range(30):
    loss = train_one_epoch(x, y)
    loss_list.append(loss)


## 画出loss图
plt.plot(loss_list)

得到的损失变化如下:

这里学习率过高会导致不收敛,出现loss反而变大的情况,同时学习率过低可以会导致loss下降得很慢,因此这里可以使用官方定义的优化器来进行梯度更新,这会减少assign代码。

python 复制代码
## 定义训练过程
def train_one_epoch(x, y, lr=1e-4):
    with tf.GradientTape() as tape:
        y_pred = model(x)
        loss = compute_loss(y, y_pred)
    a, b, c = model.a, model.b, model.c
    da, db, dc = tape.gradient(loss, [a, b, c])
    a.assign(a - lr*da)
    b.assign(b - lr*db)
    c.assign(c - lr*dc)
    return loss.numpy()

# 替换成

## 定义优化器
opt = tf.keras.optimizers.Adam(learning_rate=1e-1)

## 定义训练过程
def train_one_epoch(x, y):
    with tf.GradientTape() as tape:
        y_pred = model(x)
        loss = compute_loss(y, y_pred)
    a, b, c = model.a, model.b, model.c
    da, db, dc = tape.gradient(loss, [a, b, c])
    opt.apply_gradients(grads_and_vars=zip([da,db,dc], [a,b,c]))
    return loss.numpy()

如果要使用minimize这种更为简单的形式,这里我们需要把loss计算整合一下:

python 复制代码
@tf.function
def get_loss():
    y_pred = model(x)
    loss = compute_loss(y, y_pred)
    return loss


## 定义训练过程
def train_one_epoch(x, y):
    a, b, c = model.a, model.b, model.c
    opt.minimize(get_loss, [a, b, c])
    return get_loss()
相关推荐
结局无敌1 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
在所不辞兄1 小时前
为什么神经网络对对抗扰动和分布偏移往往比较脆弱,而人类感知相对更鲁棒?
人工智能·深度学习·神经网络·机器学习
滴啦嘟啦哒2 小时前
【具身笔记】【π0.5 微调与仿真】五、在MuJoCo中实现可视化仿真
笔记·python·vla·具身
全栈练习生2 小时前
AI安全与红队测试
python·ai
费曼学习法2 小时前
把「发送成功」当「送达」?我排查 IM 长消息被静默吞掉的完整过程
python·ai编程·自动化运维
鸽鸽程序猿2 小时前
【项目】【在线判题系统】b端题目增删改
开发语言·python
NetX行者2 小时前
Python 工程化入门:从脚本到规范的 Python 项目结构
网络·python
szxinmai主板定制专家2 小时前
储能 EMS、微网控制器选型:RK3588、RK3576、RK3568+FPGA 怎么选?多网、多 CAN、EMC 设计权衡
人工智能·深度学习·机器学习·fpga开发·工业网关·rk3576·电力网关
MasonYyp2 小时前
asyncio并发协程
python
小小张说故事3 小时前
Python 的整数为什么永远不会溢出?拆开大整数对象,看 30 位一节的存储与运算代价
python