当前位置: 首页 > news >正文

Tensorflow基础——数据类型、计算图

Tensorflow,tensor(张量)是基本的数据结构,可以理解成多维数组;flow(流)指计算图中数据的流动方式。
它的核心与NumPy非常相似,但支持GPU。
它支持分布式计算(跨多个设备和服务器)。·
它包含一种即时(JIT)编译器,可使其针对速度和内存使用情况来优化计算。它的工作方式是从Python函数中提取计算图,然后进行优化(通过修剪未使用的节点),最后有效地运行它(通过自动并行运行相互独立的操作)。
计算图可以导出为可移植格式,因此可以在一个环境中(例如在Linux上使用Python)训练TensorFlow模型,然后在另一个环境中(例如在Android设备上使用Java)运行TensorFlow模型。
它实现了反向模式的自动微分(autodiff) 并提供了一些优秀的优化器,例如RMSProp和Nadam,因此可以轻松地最小化各种损失函数。

TensorFlow在这些核心功能的基础上提供了更多功能:最重要的当然是Keras, 但它还具有数据加载和预处理操作(tf.data、tf.io等)​,以及图像处理操作(tf.image)和信号处理操作(tf.signal)

一、基本数据类型

基础数据类型是对应python、numpy中的原生类型,一般使用tf.constant创建。

TensorFlow 类型Python类型描述NumPy类型
tf.float32float32位单精度浮点数最常用,在精度和性能之间取得了良好平衡。np.float32
tf.float64float64位双精度浮点数。计算精度高,但占用内存更多,计算更慢。np.float64
tf.int32int32位有符号整数。常用于表示索引、尺寸等。np.int32
tf.int64int64位有符号整数。np.int64
tf.stringstr可变长度的字节数组。用于存储文本或序列化数据。np.object_
tf.boolbool布尔型。用于逻辑运算和掩码(mask)。np.bool_
tf.complex64complex64位复数,由两个32位浮点数(实部和虚部)组成。np.complex64
tf.uint8int8位无符号整数。常用于表示图像数据(像素值范围0-255)。np.uint8
tf.int16int16位有符号整数。有时用于音频等数据。np.int16

类型转换:可以使用 tf.cast() 函数进行数据类型转换。

1、字符串(tf.string)

虽然是基本类型,但是由它组成的张量在处理文本时非常特殊。可以存储任意长度的字节序列,使得tensorflow能够原生的处理字符串数据。

tf.constant("hello world")
tf.constant(b"hello world") # 二进制编码后的字符串
tf.constant([ord(c) for c in "诞生于1996,梦想做说唱领袖"]) # 将字符转成Unicode编码
b=tf.strings.unicode_encode(u, "UTF-8")                    # 将整数列表转化成二进制文本
tf.strings.length(b, unit="UTF8_CHAR")
tf.strings.unicode_decode(b, "UTF-8")                      # 将二进制编码转成整数编码

2、变量(tf.Variable)

使用tf.constant创建的tensor值是不变的,无法修改。这意味着不能使用常规张量在神经网络中表示权重,因为需要反向传播进行调整。因此,需要使用tf.Varibale创建变量

tf.Variable的行为与tf.Tensor的行为非常相似:可以使用它执行相同的操作,它在NumPy中也可以很好地发挥作用,并且对类型也很挑剔。但是,它也可以使用assign()方法(或assign_add()与assign_sub(),给变量增加或减少给定值)进行修改。

还可以通过单元(或切片)的assign()方法(直接指定将不起作用)或者使用scatter_update()或scatter_nd_update()方法来修改单个单元(或切片),但直接赋值将不起作用

v = tf.Variable([[1.,2.,3.], [4.,5.,6.]])
# 变量赋值
v.assign(2*v)        # 每个元素都乘2
v[0,1].assign(42)    # 将[0,1]位置的数值改为42
v.scatter_nd_update(indices=[[0,0], [1,2]], updates=[100., 200.])    # 将[0,0], [1,2]位置的分别改为100和200

二、特殊数据类型

1、张量的创建和基础运算

在 TensorFlow 中,张量是一个多维数组,具有统一的数据类型(dtype) 和固定的维度(shape)。你可以将它看作是任何数据的标准表示形式。在tensorflow中所有的输入和输出数据都是张量。

张量使用tf.constant()进行创建,其有三个关键属性:shape数据形状(用元组表示),dtype数据类型(tf.float32、tf.int32、tf.string等),name名字和图形引用。

维度数学名称Python 例子TensorFlow 张量形状(shape)
0标量 (Scalar)s = 5() (空元组)
1向量 (Vector)v = [1, 2, 3](3,)
2矩阵 (Matrix)m = [[1, 2], [3, 4]](2, 2)
33阶张量t = [[[1], [2]], [[3], [4]]](2, 2, 1)
nn阶张量...(d1, d2, ..., dn)
# tf.squeeze, tf.reduce_mean, tf.reduce_sum, tf.reduce_max, tf.math.log
# 构造一个简单的张量
x = tf.constant([[1.0, 2.0, 3.0],[4.0, 5.0, 6.0]])print("原始张量 x:")
print(x.numpy())# 1. tf.squeeze:去掉维度为1的维度
x_expand = tf.expand_dims(x, axis=0)   # shape (1, 2, 3)
print("\n扩展后张量形状:", x_expand.shape)
print("squeeze之后:", tf.squeeze(x_expand).shape)  # (2,3)# 2. tf.reduce_mean:求均值
mean_all = tf.reduce_mean(x)                 # 所有元素的平均值
mean_axis0 = tf.reduce_mean(x, axis=0)       # 按列求均值
print("\nreduce_mean 所有元素:", mean_all.numpy())
print("reduce_mean 按列:", mean_axis0.numpy())
#
# 3. tf.reduce_sum:求和
sum_all = tf.reduce_sum(x)
sum_axis1 = tf.reduce_sum(x, axis=1)         # 按行求和
print("\nreduce_sum 所有元素:", sum_all.numpy())
print("reduce_sum 按行:", sum_axis1.numpy())
#
# 4. tf.reduce_max:最大值
max_all = tf.reduce_max(x)
max_axis0 = tf.reduce_max(x, axis=0)
print("\nreduce_max 所有元素:", max_all.numpy())
print("reduce_max 按列:", max_axis0.numpy())
#
# 5. tf.math.log:自然对数
log_x = tf.math.log(x)
print("\nlog(x):")
print(log_x.numpy())

许多函数和类都有别名。例如,tf.add()和tf.math.add()是相同的函数。这允许TensorFlow为常见的操作提供简洁的名称 。

但是,也有不同的,例如:tf.transpose()函数的功能与NumPy的T属性完全不相同。在TensorFlow中,使用自己的转置数据副本创建一个新的张量,而在NumPy中,t.T只是相同数据的转置视图。类似地,tf.reduce_sum()之所以这样命名,是因为其GPU内核(即GPU实现)使用的reduce算法不能保证元素添加的顺序:32位浮点数的精度有限,因此每次调用此操作时,结果可能会稍有不同。tf.reduce_mean()也是如此(当然,tf.reduce_max()是确定性的)。

2、张量和Numpy之间的转换

张量可以与numpy配合使用:用numpy数组创建张量,反之亦然。两个数据类型可以通过tf.constant()和张量.numpy()进行转换。

特性NumPy 数组 (np.ndarray)TensorFlow 张量 (tf.Tensor)
核心目的通用数值计算机器学习模型构建和训练
硬件加速仅在 CPU 上运行可透明地在 CPU, GPU, TPU 上运行
计算图

即时执行(Eager Execution)

命令式编程,代码立即执行并返回结果。

两种模式

1. 即时执行(默认): 和 NumPy 一样立即执行。
2. 图执行: 先构建一个计算图,然后再统一执行。图模式效率更高,且是部署的基础。

不可变性数组内容可以被修改(可变)。张量内容不可以被修改(不可变)。在机器学习中,模型的权重是 tf.Variable,它是可变的特殊张量。
梯度计算不支持自动求导(Autograd)。

原生支持自动求导(通过 GradientTape),

这是训练神经网络(反向传播)的核心功能。

原生API拥有极其丰富和成熟的数学函数库。

API 更专注于机器学习所需的操作(如卷积、池化、损失函数等)。

3、tensorflow的类型转换

类型转换会严重影响性能,并且自动完成的类型转换很容易被忽视。为了避免这种情况,TensorFlow 不会自动执行任何类型转换:如果对不兼容类型的张量执行此操作,会引发异常。例如,不能把浮点数张量和整数张量相加,甚至不能将32位浮点数和64位浮点数相加。

import tensorflow as tf
tf.constant(2.) + tf.constant(40)    # 报错,一个是float32,一个是int类型
tf.constant(2.) + tf.constant(40., dtype=tf.float64) # 报错,一个是float64,一个是float32

使用tf.cast()方法进行数据类型转换则可以将两个tensor进行相加:

t2 = tf.constant(40, dtype=tf.float64)
tf.constant(2.0) + tf.cast(t2, tf.float32) # 确实需要转换类型时,使用tf.cast()

4、张量数组(tf.TensorArray)

张量数组可以理解为张量列表,其默认固定长度,可以动态增长(通常预先指定大小,避免不必要的内存重新分配),包含的张量必须有相同的形状和数据类型。

通常使用有:

  • 使用tf.TensorArray()构造函数
  • 使用write(index,value)方法在指定的索引位置写入一个张量(这里所有写入的dtype都必须一致,shape也要兼容)
  • 使用read(index)方法从指定的索引位置读取一个张量
  • 使用stack()方法将TensorArray中所有元素堆叠起来,形成一个更高维度的常见张量
  • 使用unstack()方法将一个常规的张量,沿着0维度将每一部分分解并存入TensorArray中
import tensorflow as tfta = tf.TensorArray(dtype=tf.float32,   # 要存储的数据类型为:浮点数size=0,             # 初始大小, 0表示动态大小,但指定大小更高效dynamic_size=True,  # 允许在超出初始大小的时候自动增长clear_after_read=False    # 读取后是否清除值
)# clear_after_read参数的展示
array = tf.TensorArray(dtype=tf.float32, size=3)
array = array.write(0, tf.constant([1., 2.]))
array = array.write(1, tf.constant([3., 10.]))
array = array.write(2, tf.constant([5., 7.])) 
array.stack()            # 现在是一个(3,2)的张量数组
tensor1 = array.read(1)  #  tf.constant([3., 10.]) , 输出位置清0,也就是读出之后3,10的位置就清零了
tensor1                  # 这是一个张量:[3.,10.]
array.stack()            # 现在这里第二行的值就被清零了# 展示unstack()的用法
input_tensor = tf.constant([[1, 2], [3, 4], [5, 6]], dtype=tf.float32)    # 使用 unstack 将其分解到 TensorArray 中
ta_from_unstack = tf.TensorArray(dtype=tf.float32, size=3)                # 这会创建 3 个元素,每个元素的形状是 (2,)
ta_from_unstack = ta_from_unstack.unstack(input_tensor)
elem = ta_from_unstack.read(0)        # 读取第一个元素
print(elem)                           # 输出: tf.Tensor([1. 2.], shape=(2,), dtype=float32)
  • 与tf.while_loop结合使用
import tensorflow as tf
def dynamic_loop(n):# 1. 创建一个 TensorArray 来收集结果,预先指定大小 nta = tf.TensorArray(dtype=tf.int32, size=n, dynamic_size=False)# 2. 定义循环条件和 body 函数i = 0def cond(i, ta):                   # 循环条件:i < nreturn i < ndef body(i, ta):                   # 循环体:将 i*2 写入数组,然后 i+1ta = ta.write(i, i * 2)        # 在位置 i 写入值 i * 2return (i + 1, ta)# 3. 执行循环_, final_ta = tf.while_loop(cond=cond,        # 循环条件body=body,        # 循环体loop_vars=(i, ta),# 循环变量,body执行后更新变量集合parallel_iterations=1 # 对于有写入操作的循环,通常设为 1)# 4. 将结果堆叠成一个一维张量return final_ta.stack()# 调用函数
result = dynamic_loop(5)
print(result) # 输出: tf.Tensor([0 2 4 6 8], shape=(5,), dtype=int32)

三、tensorflow函数和计算图

1、计算图

计算图是用于计算任务的有向图,主要由两部分组成:节点(代表操作,包括数学运算、数据输出和输入)和边(代表张量,输入和输出的关系)。它定义了计算步骤和依赖关系,但并不执行实际计算。

计算图的工作模式:惰性求值,它将图与会话进行了分离。一般分为两步:先构建图,再执行图。

构建图:在这个极端,只是定义计算图的机构,代码不会执行任何实际的计算,只是在图中创建节点。

import tensorflow as tf# 1. 构建图
# 定义两个常量节点(操作)
a = tf.constant(5, name='input_a')
b = tf.constant(3, name='input_b')# 定义一个乘法操作节点
c = tf.multiply(a, b, name='mul_c')# 定义一个加法操作节点
d = tf.add(a, b, name='add_d')# 定义最终输出节点,这里的e并不是计算结果而是一个tensor,name为add_e。
e = tf.add(c, d, name='add_e')

执行图:这个阶段要实际计算图,并得到结果。它必须在session会话中或调用tf.function执行。session负责将图的操作分配到CPU或GPU等硬件上执行,并提供执行方法。可以联想到文件的操作,有些类似,都需要close操作。

# 2. 在会话中执行图
# 创建一个会话,运行计算图来计算节点 ‘e’ 的值
sess = tf.Session()
output = sess.run(e)
print("Output of the computation:", output) # 输出: 23
sess.close()    # 关闭会话,释放资源
# 使用with块更方便
with tf.Session() as sess:output = sess.run(e)print("Output:", output)

eager和graph

  • eager execution:这是命令式逐行执行的编程方式。操作会在被调用的时候立即计算并返回具体值,像numpy一样。
  • graph:就是图执行。先创建图,需要调用的时候在执行对应操作。

既然编程函数就能成功运行程序,为什么要将函数变成计算图,主要有以下4个原因:

  1. 计算梯度(自动微分):
    1. 深度学习训练的核心是反向传播,反向传播需要前向运算的“依赖关系”,而计算图正好就是一张依赖图,有输入有输出。
    2. tensorflow可以在图上“从输出往输入走”,自动套用链式法则。
    3. 系统可以很容易的分析那些操作时独立的,从而将它们并行的调度到不同的计算机设备(CPU\GPU)上执行,极大的提高效率
  2. 硬件优化。图是静态的,因此 tf 能够在图级别做很多全局优化,而eager模式下操作时一步步立即执行的,没有机会做整体优化:
    • 常数折叠:预先计算那些由常量组成的节点(比如 tf.constant(2) + tf.constant(3) 会在图编译时直接被替换为 tf.constant(5))。
    • 修剪无用的节点:自动去除途中与最终输出无关的计算分支
    • 算子融合(简化表达式):将多个小操作合并成一个更大更高效的操作(例如,将矩阵乘法和加法融合成一个单一的操作)
    • 内存复用:释放重用张量内存,避免爆显存
  3. 部署和可移植性
    • 计算图是一个独立的、与语言和平台无关的实体(一个json/protobuf格式)
    • 训练好模型后,可以将图导出:tfserving(服务器上推理)、tf lite(移动端、嵌入式)、tf.js(浏览器)
    • 没有图只能在python中运行,无法跨平台部署
  4. 调试和可视化:图能够在tensorboard中显示:图的结构、数据流、梯度规模、耗时瓶颈

    准备好优化的图后,TF函数会以适当的顺序有效地执行图中的操作。因此,TF函数通常比原始的Python函数运行得更快,尤其是在执行复杂计算的情况下。大多数时候,想利用tensorflow加速Python函数时,不需了解更多,只需将其转换为TF函数即可。

    2、tf.function

    tf.function是一个装饰器,将python函数编译成可调用的计算图。使用它修饰函数后,TensorFlow 在第一次调用时跟踪(Tracing)每一步运算,最终把这些操作记录成一张 计算图(Graph)。之后,在调用function对象的时候执行图中的结构。

    默认情况下,TF函数会为每个不同的输入形状和数据类型集生成一个新图,并将其缓存以供后续调用。例如,如果调用tf_cube(tf.constant(10)),将为形状为[ ]的int32张量生成图。如果调用tf_cube(tf.constant(20)),则会重用相同的图。但是,如果随后调用tf_cube(tf.constant([10,20])),则会为形状为[2]的int32张量生成一个新图。这就是TF函数处理多态(即变化的参数类型和形状)的方式。

    但是,这仅适用于张量参数:如果将Python数值传递给TF函数,则将为每个不同的值生成一个新图,例如,调用tf_cube(10)和tf_cube(20)将生成两个图。如果用不同的Python数值多次调用TF函数,则会生成许多图,这会降低程序运行速度并消耗大量RAM(必须删除TF函数才能释放它)

    @tf.function  # 更常用的写法,直接换成装饰器
    def tf_cube(x):print(f"x = {x}")return x ** 3
    tf_cube.python_function(2) # 使用原始的python函数
    tf_cube(tf.constant(2.0)) # 运行多次但只打印一次 ,这里会跟踪1次
    # 如果传入的是python数据,则每次都会跟踪生成新的图
    tf_cube(2)    # 跟踪,生成新的计算图
    tf_cube(3)    # 跟踪

    @tf.function 的一个高级且非常重要的用法:使用input_signature参数指定形状和类型来精确控制函数何时被追踪(Retracing)。,只要输入的(形状、类型)与第一次追踪时相同,TensorFlow 就不会再次追踪,而是直接复用之前构建好的计算图,只执行这个高效的图。

    # 1. 使用 input_signature 参数指定函数输入的“形状”和类型
    @tf.function(input_signature=[tf.TensorSpec([None, 28, 28], tf.float32)])
    def shrink(images):# 2. 打印语句,用于调试和观察追踪行为print("Tracing", images)# 3. 函数的实际操作:对图像进行下采样(缩小)return images[:, ::2, ::2]
    # 假设我们有一些数据
    batch_of_imgs = tf.random.normal([32, 28, 28]) # 32张28x28的图片
    another_batch = tf.random.normal([64, 28, 28]) # 64张28x28的图片
    wrong_shape_imgs = tf.random.normal([32, 32, 32]) # 错误的形状# 第一次调用:触发追踪
    # 输出: Tracing Tensor("images:0", shape=(None, 28, 28), dtype=float32)
    result1 = shrink(batch_of_imgs)
    print(result1.shape) # 输出: (32, 14, 14)# 第二次调用:批次大小不同,但符合签名 (None, 28, 28) -> 不会重新追踪!
    # 不会有 "Tracing" 输出
    result2 = shrink(another_batch)
    print(result2.shape) # 输出: (64, 14, 14)# 第三次调用:形状不符合签名 (28,28 vs 32,32) -> 会报错!
    # 抛出 ValueError: Python inputs incompatible with input_signature
    try:result3 = shrink(wrong_shape_imgs)
    except ValueError as e:print(e)

    在自定义指标并在keras模型中使用的时候keras会自动将函数转化称为tf函数,无需使用tf.function(),如果想让keras使用加速线性代数,只需要在调用compile()方法的时候设置jit_compile=True。

    在大多数情况下,将执行 TensorFlow 操作的 Python 函数转换为 TF 函数很简单:用 tf.function 装饰它或让 Keras 处理。但是,有一些规则需要遵守:

    • 避免调用外部库
      •   如果调用任何外部库,包括 NumPy 基础标准库,此调用将在跟踪过程中执行,它不会成为图的一部分。 实际上,TensorFlow 图只能包含 TensorFlow 结构(张量、运算、变量、数据集等)。
      • 因此,请使用 `tf.reduce_sum()` 代替 `np.sum()`,使用 `tf.sort()` 代替内置的 `sorted()` 函数。除非确实希望这些代码只在跟踪过程中运行。)
    • 随机数生成必须使用 TF 提供的方法
      •   如果你写了一个返回 `np.random.rand()` 的 TF 函数 `f(x)`,那么随机数只会在跟踪函数时生成一次。
      •  tf.constant(2.) 和 tf.constant(np.random.rand()) 返回相同的随机数。但tf.constant([2., 3.])将返回不同的随机值。要生成真正的随机数,请使用 `tf.random.uniform([])`。这样每次操作都会生成新的随机数,因为它属于图的一部分。
    • 避免副作用:如果非 TensorFlow 代码具有副作用(例如写日志、更新 Python 计数器),不要期望每次调用 TF 函数都会发生这些副作用,因为它们只会在**跟踪**函数时执行。
    • 慎用 `py_function`
      •   可以在 `tf.py_function()` 操作中包装任何 Python 代码,但这样会降低性能,因为 TensorFlow 无法对其中代码做任何图优化。
      •  这会降低可移植性,因为该代码只能在安装了 Python 的平台上运行。
    • Python 函数要符合规则
      • 可以调用其他 Python 函数,它们也必须遵循相同的规则,因为 TensorFlow 会在计算图中捕获它们的操作。
      •  注意:这些函数本身需要用 `@tf.function` 修饰。
    • 变量必须只创建一次
      • 如果在 TF 函数内部创建了变量(例如数据集或张量列表),必须确保只创建一次,否则会引发错误。
      • 常见做法:在 `__init__` 或 `build()` 方法中创建变量,更新时用 `assign()`,而不是重新赋值。
    • 源码可用性
      •   Python 源码必须可用才能用于 TensorFlow。
      •   如果函数定义在交互式 Python shell 中,或者没有源码(比如 `.pyc` 编译文件),生成图可能失败。
    • 避免使用 Python 原生循环遍历 Dataset
      •   TensorFlow 图会捕获张量级别的循环(`tf.range()`),而不会捕获 Python 原生 `for` 循环。
      •   如果用 Python 的 `for` 循环迭代 Dataset,则无法在图模式下被捕获,也无法在分布式环境下正常运行。(不在图里的计算,无法重现到其他机器)
    • 出于性能原因,应尽可能使用向量化实现,而不是使用循环。

    http://www.dtcms.com/a/395133.html

    相关文章:

  1. 在Anaconda中安装TensorFlow1.14.0与TensorFlow2.0.0
  2. 面试题:分布式锁要点总结(Redisson)
  3. C++第四篇:函数增强
  4. C#上位机软件:1.7 熟悉VS并开启你的第一个C#程序
  5. Nextcloud App增加模块内嵌网页
  6. 04-django配置日志-loguru
  7. docker离线部署gpt-oss-20b流程,从下载到安装再到可以使用
  8. 关系数据库MySQL的常用基础命令详解实战
  9. 面向动态环境的MEC突破:MLGO微算法科技推出自适应权重深度确定性策略梯度(AWDDPG)算法,革新多用户任务迁移技术
  10. Ansys Zemax | 确保自由曲面设计的可制造性
  11. 智造新势力:看“文化+科技”如何重塑制造新范式
  12. 【算法训练营Day25】动态规划part1
  13. 打破网络壁垒:使用内网穿透轻松实现远程桌面访问
  14. 2025 PyCharm IDE 社区版与专业版合并后,新手该如何安装?(附 Toolbox 图形化安装教程)
  15. 07-css元素定位布局
  16. 波动率曲面及SVI模型的Python数值拟合
  17. 基于Python新闻平台的文本数据挖掘系统
  18. 2017/12 JLPT听力原文 问题四
  19. 【Tawk】Tawk.to聊天小部件移动端位置调整完整指南
  20. jieba 库
  21. 开启 3D 之旅 - 你的第一个 WebGL 三角形
  22. 基于AWS Lambda的机器学习动态定价系统 CI/CD管道部署方案介绍
  23. 3D 文件格式解释
  24. 对Hive表进行归档,减少小文件的影响
  25. R 中,geo 数据集 分析探针转基因的时候,一个探针对应的多个基因的情况
  26. 机器学习-逻辑回归-考试预测通过-1
  27. 计算机中用8位如何计算最大值和最小值-128~127
  28. PyTorch 神经网络工具箱完全指南
  29. docker一键安装部署若依Ruoyi-Vue(保姆级)
  30. 通义DeepResearch论文六连发全面解读