TensorFlow2.4 开发 基础篇① 张量和变量(Tensor)
文章会不断更新,喜欢的小伙伴可以帮博主点个赞哟(〃‘▽’〃)
从这一篇开始,我们就真正的开始学习 TensorFlow 2.4 了,第一篇的话我们讲的是 TensorFlow 的基础,TensorFlow 1 版本和 2 版本的语法有很大的不同,但总的来说,基础还是一样的,学过 TensorFlow 1 版本以及numpy基础使用的基础基本可以不用看,不会用在查就可以了。
TensorFlow 2 的一大特点就是直接对代码进行运算并返回结果,这一点和Python很像,不像是 TensorFlow 1 需要把所有代码写完之后,创建 Session 区域运行。
Tensor 都可以与 Numpy 数据相互转换。
例如:
import tensorflow as tf
a = tf.constant([[1, 2],
[3, 4]])
print(tf.add(a, 10)) # 矩阵各位置 + 10
输出结果:
tf.Tensor(
[[11 12]
[13 14]], shape=(2, 2), dtype=int32)
如果不想直接显示结果,像 TensorFlow 1 那样,将 tf.executing_eagerly() 设置为 False 即可(不建议)
变量简单,我们就先讲一下变量,真正的基础最多的还是张量的相关操作,毕竟 TensorFlow 直译就是“流动的张量”对吧。
我们先介绍下变量的定义:变量是计算机语言中能储存计算结果或能表示值的抽象概念。(说白了就是小时候学的解方程组那个 x,y,会变的量->变量)
但是有一点,TensorFlow 有一个、两个甚至更多个存储变量的地方(取决于运算环境),比如我们正常的电脑有一个 CPU 和一个 GPU 那么就有这两个存储变量的位置,tf.debugging.set_log_device_placement(True)
这句代码就可以看变量位于哪一个设备上,这一点对分布式训练有关联,大家目前只需要知道这一点就行了。
其实定义变量和定义张量很像,毕竟都是 TensorFlow 的一种数据类型。但是变量无法重构形状,大家可以往后看 2.2.3张量形态学操作。
import tensorflow as tf
MyVariable = tf.Variable([[1, 2], [3, 4]])
boolVariable = tf.Variable([False, False, False, True])
complexVariable = tf.Variable([5 + 4j, 6 + 1j])
# 像Python一样TensorFlow的变量一样可以储存各种类型的数据
print("变量大小: ",MyVariable.shape)
print("变量类型: ",MyVariable.dtype)
print("转换为Numpy格式: \n", MyVariable.numpy())
变量大小: (2, 2)
变量类型: <dtype: 'int32'>
转换为Numpy格式:
[[1 2]
[3 4]]
最后说一下变量的更改,我们可以通过特定的语句再重新分配变量,说是重新分配是因为它是覆盖了现有变量的内存而不是释放原来的内存,分配新的变量。
MyVariable = tf.Variable([1, 2])
print(MyVariable.numpy())
MyVariable.assign([2, 1]) # assign 语句修改变量
print(MyVariable.numpy())
[1 2]
[2 1]
这就讲完了?对,没错,变量基础操作就这么少的干货,大家继续看下面的张量。
我们先说一说张量(Tensor)是什么,张量是具有统一类型(称为 dtype)的多维数组,从某个角度上来讲张量与 Numpy 中的数组有一定的相似性,并且他们之间可以相互转换。但张量毕竟不是数组,所有的张量都是不可变的,它并不能像 Numpy 数组一样进行更新或改变,我们只能创建新的张量。
那么矩形张量又是什么呢,矩形张量,便是张量最普通的形式,也是我们平时所说的张量,还有两种特殊张量(不规则张量和稀疏张量)。创建一些基本张量就像创建数组一样。
无维度张量(也称为标量或“零秩”张量):
import tensorflow as tf
import numpy as np
MyTensor = tf.constant(4) # 张量(无轴(零维),也称为标量或“零秩”张量)
print(MyTensor)
tf.Tensor(4, shape=(), dtype=int32)
一维张量(也称为“向量”或“一秩”张量):
MyTensor = tf.constant([2, 3, 4])
print(MyTensor)
tf.Tensor([2 3 4], shape=(3,), dtype=int32)
二维张量(也称为“矩阵”或“二秩”张量):
MyTensor = tf.constant([[1, 2],
[3, 4],
[5, 6]], dtype=tf.float32) # 在后面加dtype可以指定数据类型
# 在后期训练数据的时候,将非必要位数的数据压缩为位数低的数据可提高训练效率
print(MyTensor)
tf.Tensor(
[[1. 2.]
[3. 4.]
[5. 6.]], shape=(3, 2), dtype=float32)
以此类推,我们可以创建更多维度的张量,维度我们也称之为轴(axis),是在处理大数据中 axis 这个词的直译。
张量也是像数组一样可以直接做计算的。
a = tf.constant([[1, 1],
[1, 1]])
b = tf.constant([[2, 3],
[4, 5]])
print(a + b) # print(tf.add(a, b)) # 加法
print(a - b) # print(tf.subtract(a, b)) # 减法
print(a * b) # print(tf.multiply(a, b)) # 对应位相乘(逐元素乘法)
print(a @ b) # print(tf.matmul(a, b)) # 矩阵相乘
print(a / b) # print(tf.divide(a, b)) # 除法
tf.Tensor(
[[3 4]
[5 6]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[-1 -2]
[-3 -4]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[2 3]
[4 5]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[6 8]
[6 8]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[0.5 0.33333333]
[0.25 0.2 ]], shape=(2, 2), dtype=float64)
这里大家要注意的是矩阵的乘法有两种,一种是对应位相乘也叫逐元素乘法,另一种是正了八经的矩阵乘法,要搞清楚!!! 而对于计算来讲,TensorFlow 2 有即时计算的特性,我们可以调用 tf 的计算方法,也可以像 Python 一样直接对他们做运算处理。不光是简单的加减乘除,对于 numpy 数组的处理方式都可以完美的在 Tensor 上进行,例如 argmax(最大值索引)、softmax(归一化处理)、square(平方)、reduces_sum(累加)等,这些在我们使用的时候专门去查即可,对于我来讲,先学了一堆其实也没记住多少,都是随用随查 API (真香)。
我们来讲一下张量的形状,我们在大数据分析或者做无论是机器学习还是深度学习的时候都习惯性的“.shape”一下,目的就是为了直观的看一下数据集的整体情况。
MyTensor = tf.ones([5, 5]) # 创建一个元素全部为 1,大小为 5 X 5 的张量
print("每个元素的数据类型:\t\t", MyTensor.dtype)
print("张量维度:\t\t\t", MyTensor.ndim)
print("张量大小:\t\t\t", MyTensor.shape)
print("在第一个维度上的元素数量:\t", MyTensor.shape[0])
print("最后一个维度上的元素数量:\t", MyTensor.shape[-1])
print("张量的总数据量:\t\t\t", tf.size(MyTensor).numpy())
每个元素的数据类型: <dtype: 'float32'>
张量维度: 2
张量大小: (5, 5)
在第一个维度上的元素数量: 5
最后一个维度上的元素数量: 5
张量的总数据量: 25
“\t” 是制表符,让它好看点 φ(>ω<*)
上面这几个方法是具体的看你的数据集是什么样子的,既然我们要跑数据,连数据都不知道是什么样子还何谈去清洗或者训练它呢。
再往下说是张量的索引方式,所谓索引,就是想在图书馆里找书一样,我们去图书馆可能会有某个特别的需求,在图书馆的索引机上去查找,索引机也会给你一个号码,这个号码就是告诉你在哪个楼哪层哪个区域的第几个书架上,这样就可以准确的找到所需要的书籍了,那么将我们 Tensor 中的一个个元素去看做这个“书籍”,同样的它也有对应的索引方式。
Tensor 的索引方式是遵循 Python 列表的索引方式的,也就是说 Tensor ≈ List,但这并不能说 Tensor 等于或者约等于 List,它们仅仅是索引相同罢了,这是 TensorFlow 方便大家使用而做出的决定(也遵循 Numpy 的基本索引规则)。
我们先不提一些特殊的索引方式,最简单的 Python 索引有几个规则:
所谓单值索引,就是直接用单个数字做索引,上一份代码大家就明白了。
MyTensor = tf.constant([1, 2, 3, 4, 5, 6, 7, 8])
print("第一个数字:\t", MyTensor[0].numpy()) # 第一个数字索引为 0
print("第二个数字:\t", MyTensor[1].numpy())
print("最后一个数字:\t", MyTensor[-1].numpy()) # 负索引代表倒序
第一个数字: 1
第二个数字: 2
最后一个数字: 8
而切片索引大家就能玩的更花了,但有一点 切片的最后一个索引是不在其内的(array[:3]代表的是从第一个数到第三个数(没有3什么事)),我们先看一个一维的 Tensor
MyTensor = tf.constant([1, 2, 3, 4, 5, 6, 7, 8])
print("所有的数:\t", MyTensor[:].numpy())
print("从第一个数到第三个数:\t", MyTensor[:3].numpy()) # 最后一个索引不算入其内
print("从第五个数到最后一个数:\t", MyTensor[4:].numpy())
print("从第三个数到第六个数:\t", MyTensor[2:6].numpy())
print("步长为二截取数据:\t", MyTensor[::2].numpy())
print("倒序数组:\t", MyTensor[::-1].numpy())
所有的数: [1 2 3 4 5 6 7 8]
从第一个数到第三个数: [1 2 3]
从第五个数到最后一个数: [5 6 7 8]
从第三个数到第六个数: [3 4 5 6]
步长为二截取数据: [1 3 5 7]
倒序数组: [8 7 6 5 4 3 2 1]
我们再来看一看多维的 Tensor
MyTensor = tf.constant([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print("Tensor:\n", MyTensor[:, :].numpy(), end="\n\n")
print("第一行:\t", MyTensor[0, :].numpy())
print("第一列:\t", MyTensor[:, 0].numpy())
print("最后一行:", MyTensor[-1, :].numpy())
print("第一行第一个数字:\t", MyTensor[0, 0].numpy())
print("除了第一行其他的数据:\n", MyTensor[1:, :].numpy())
Tensor:
[[1 2 3]
[4 5 6]
[7 8 9]]
第一行: [1 2 3]
第一列: [1 4 7]
最后一行: [7 8 9]
第一行第一个数字: 1
除了第一行其他的数据:
[[4 5 6]
[7 8 9]]
说白了多维度的 Tensor 切片也就是在索引上多加了一个维度,即使我们维度再高也可以进行切片,找出我们想要的“书籍”。
那么除了上面我们所说的单值索引和切片索引我们还有一些特殊的索引方式,这些索引方式也能大大地提高我们的编程效率。
其实讲到这里,大家可能会想到 Numpy 中的 argmax 和 argmin 等函数,没错 TensorFlow 一样有这样的函数。
import numpy as np
MyTensor = tf.constant([[1, 2, 3, 4],
[4, 9, 6, 4],
[7, 8, 5, 4]])
print("按列取最大值索引:", tf.argmax(MyTensor).numpy()) # argmax有两个参数,第二个不填默认为 0,按列
print("按列取最大值索引:", tf.argmax(MyTensor, 0).numpy())
print("按列取最大值索引:", tf.argmax(MyTensor, axis=0).numpy())
print("按行取最大值索引:", tf.argmax(MyTensor, axis=1).numpy())
print("Numpy同款函数(整数组最大值索引):", np.argmax(MyTensor))
# Numpy在第二个参数不填的情况下,是将多维数组取消维度返回索引
按列取最大值索引: [2 1 1 0]
按列取最大值索引: [2 1 1 0]
按列取最大值索引: [2 1 1 0]
按行取最大值索引: [3 1 1]
Numpy同款函数(整数组最大值索引): 5
同样的特殊索引我们不一下讲完,第一是多我不可能全部都列出来,其次特殊索引一般用的会比较少,但是在某些场合有奇效,当遇到时我们再查,学习效率会高很多。
明天继续写 QwQ,太多了