参考链接:
https://blog.csdn.net/yeshang_lady/article/details/124615743?ops_request_misc=&request_id=&biz_id=102&utm_term=tensorflow%20%E5%BC%A0%E9%87%8F&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-5-124615743.142v52pc_rank_34_queryrelevant25,201v3add_ask&spm=1018.2226.3001.4187
TF中张量的操作
import tensorflow as tf
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 不显示等级2以下的提示信息,去掉tf一些烦人的输出
1 张量的创建
1.1 使用tf.constant创建张量
可以直接在tf.constant中写入数组来构建tensor,此时如果指定数据类型,就会按照指定的创建.如果不指定数据类型,会自动分配数据类型.
如果都是整数,那么dtype就是int型,如果有一个元素是浮点,那么dtype就是float型.
整形当中,默认最小的类型是int32,不会使用int16,除非额外特别指定.
int16: -32768~32767
int32:-2147483648~2147483647
int64:-9223372036854775808~9223372036854775807
单精度:float32:精确到小数点后6位,4个字节
双精度:float64:精确到小数点后15位,8个字节
tf.constant([1,2,3,4,5,6])
<tf.Tensor: shape=(6,), dtype=int32, numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
创建的变量类型都是EagerTensor
a = tf.constant([1,2,3,4,5,6])
type(a)
tensorflow.python.framework.ops.EagerTensor
可以通过.numpy()来访问数组
a.numpy()
array([1, 2, 3, 4, 5, 6], dtype=int32)
tf.constant([1,2,3,4,5,6],dtype=tf.int16)
<tf.Tensor: shape=(6,), dtype=int16, numpy=array([1, 2, 3, 4, 5, 6], dtype=int16)>
tf.constant([1,2,3,4,5,6.1])
<tf.Tensor: shape=(6,), dtype=float32, numpy=array([1. , 2. , 3. , 4. , 5. , 6.1], dtype=float32)>
也可以先通过numpy创建数组a,然后通过tf.constant(a)创建tensor.
a = np.array([1,2,3,4,5,6])
tf.constant(a)
<tf.Tensor: shape=(6,), dtype=int32, numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
如果设定了shape,那么会对value进行reshape.
当value是一个常数时,会被复制填充shape
当value本身就是一个数组时,则要求元素数量必须和reshape后的数量一致,否则会报错.
tf.constant(1,shape=(2,3))
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[1, 1, 1],
[1, 1, 1]], dtype=int32)>
tf.constant([1,2,3,4,5,6],shape=(2,3))
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[1, 2, 3],
[4, 5, 6]], dtype=int32)>
另外,如果value是一个eager值,则没有影响,还可以计算梯度
v= tf.Variable([0.0])
u=tf.Variable([1.1])
with tf.GradientTape() as g:
loss = tf.constant(3*v+u) #等效于loss = 3*v + u
g.gradient(loss,v).numpy()
array([3.], dtype=float32)
但是如果value是一个符号张量,tf.constant(value)就会报错了.
所谓的符号张量可以理解为只是声明的为tf.graph,并没有实例化.只有在图流动时才进行实例化.
with tf.compat.v1.Graph().as_default():
i=tf.compat.v1.placeholder(shape=[None, None], dtype=tf.float32)
t=tf.constant(i)
其他相关操作:
(1)tf.convert_to_tensor()
也可以创建tensor,但是与tf.constant有两点不同
- 不能使用shape指定形状
- 允许使用符号张量
例如同样value i 下面的转化就不会报错.
with tf.compat.v1.Graph().as_default():
i=tf.compat.v1.placeholder(shape=[None, None], dtype=tf.float32)
t=tf.convert_to_tensor(i)
(2)tf.fill()可以用于创建标量构成的tensor,但是与tf.constant有很大不同:
- tf.fill只支持标量常数tf.constant支持任意常数
- tf.fill可以在运行时任意修改,因此在表示大型tensor时效率更高
- tf.fill并不嵌入值,所以它可以产生动态的输出尺寸
tf.fill([2,3],9.1)
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[9.1, 9.1, 9.1],
[9.1, 9.1, 9.1]], dtype=float32)>
1.2 创建特殊张量
1.2.1 创建全0,1,自定义数值张量
这种创建出来的张量内元素值都是一样的.
tf.ones创建全1张量,tf.zeros创建全0张量
默认会采用tf.float32的变量类型存储
tf.ones([2,3],dtype=tf.int32)
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[1, 1, 1],
[1, 1, 1]], dtype=int32)>
tf.zeros([2,3])
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[0., 0., 0.],
[0., 0., 0.]], dtype=float32)>
当然,也可以使用1.1中的constant,fill,convert_to_tensor创建变量
tf.constant(0,shape=[2,3])
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[0, 0, 0],
[0, 0, 0]], dtype=int32)>
tf.fill((2,3),1)
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[1, 1, 1],
[1, 1, 1]], dtype=int32)>
a = np.zeros((2,3))
tf.convert_to_tensor(a)
<tf.Tensor: shape=(2, 3), dtype=float64, numpy=
array([[0., 0., 0.],
[0., 0., 0.]])>
1.2.2 创建符合特殊分布的张量
都是通过tf.random函数进行构建
- 符合[min,max]的均匀分布的张量
tf.random.uniform([1,5],minval=0,maxval=5)
<tf.Tensor: shape=(1, 5), dtype=float32, numpy=
array([[4.3903494 , 2.6431446 , 2.7990234 , 0.94691813, 3.3530939 ]],
dtype=float32)>
- 指定均值方差的正态分布
tf.random.normal([2,2],mean=5,stddev=0.1)
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[4.9246736, 4.816348 ],
[4.970745 , 5.017907 ]], dtype=float32)>
创建一个连续的序列,和python自带的range函数基本一致
tf.range(1,10,2)
<tf.Tensor: shape=(5,), dtype=int32, numpy=array([1, 3, 5, 7, 9], dtype=int32)>
for i in range(1,10,2):
print(i)
1
3
5
7
9
1.3 可变张量
前面可以知道,tf.constant创建的张量是不可优化的.而tf.variable建立的变量是可以优化的.
a = tf.constant([1,2,3])
b = tf.Variable([1,2,3])
c = tf.fill([1,2],0)
i = np.zeros((2,3))
d = tf.convert_to_tensor(i)
print(type(a))
print(type(b))
print(type(c))
print(type(d))
<class 'tensorflow.python.framework.ops.EagerTensor'>
<class 'tensorflow.python.ops.resource_variable_ops.ResourceVariable'>
<class 'tensorflow.python.framework.ops.EagerTensor'>
<class 'tensorflow.python.framework.ops.EagerTensor'>
可见,只有 通过tf.Variable建立的变量才是可以用于模型训练的变量.并且tf.Variable变量有特有的属性:
print(b.name,b.trainable)
Variable:0 True
1.4 其他tf.xx_initializer()类方法
Tensorflow中有几个以initializer结尾的方法,这几个方法的使用有一些特殊,因为这些方法得到的并不是张量,而是一个可调用对象。下面以tf.random_uniform_initializer()为例进行说明。具体如下:
组合起来,就可以创建一些具有特殊分布的张量,例如下面就是创建了一个具有均匀分布的tensor变量
a = tf.random_uniform_initializer(minval=0,maxval=5) #产生均匀分布的函数
print(type(a))
print(callable(a))
<class 'tensorflow.python.ops.init_ops_v2.RandomUniform'>
True
tf.constant(a(shape=[2,3],dtype=tf.int32))
<tf.Tensor: shape=(2, 3), dtype=int32, numpy=
array([[0, 4, 2],
[2, 3, 3]], dtype=int32)>
2.基础操作
2.1 维度变换
- 增加新的维度:tf.expand_dims
a = tf.random.uniform([2,3],0,10,dtype=tf.int32)
print(a)
b=tf.expand_dims(a,axis=0)
print(b)
c= tf.expand_dims(a,axis=1)
print(c)
d= tf.expand_dims(a,axis=2)
print(d)
tf.Tensor(
[[7 5 2]
[0 4 3]], shape=(2, 3), dtype=int32)
tf.Tensor(
[[[7 5 2]
[0 4 3]]], shape=(1, 2, 3), dtype=int32)
tf.Tensor(
[[[7 5 2]]
[[0 4 3]]], shape=(2, 1, 3), dtype=int32)
tf.Tensor(
[[[7]
[5]
[2]]
[[0]
[4]
[3]]], shape=(2, 3, 1), dtype=int32)
- 删除维度 tf.squeeze ,会自动删除tensor size=1的维度
d = tf.squeeze(b)
print(d)
tf.Tensor(
[[7 5 2]
[0 4 3]], shape=(2, 3), dtype=int32)
上面的两种方法: tf.expand_dims和tf.squeeze都不会改变张量的存储顺序和大小,值是改变了理解方式.
- 复制张量
tf.tile()
a = tf.constant([1,2,3,4])
b = tf.tile(a,multiples=[2])
print(b)
tf.Tensor([1 2 3 4 1 2 3 4], shape=(8,), dtype=int32)
a = tf.constant([1,2,3,4],shape=[2,2])
b = tf.tile(a,multiples=[2,2])
print(b)
tf.Tensor(
[[1 2 1 2]
[3 4 3 4]
[1 2 1 2]
[3 4 3 4]], shape=(4, 4), dtype=int32)
2.2 张量的合并
- tf.concat()拼接
下面对a和b在axis=0的维度进行拼接
a = tf.random.uniform([2,3],1,10,dtype=tf.int32)
b= tf.random.uniform([1,3],1,10,dtype=tf.int32)
c = tf.concat([a,b],axis=0)
print(tf.shape(c))
print(c.shape)
tf.Tensor([3 3], shape=(2,), dtype=int32)
(3, 3)
- tf.stack()拼接时的对象的shape必须完全一样
a = tf.random.uniform([2,3],1,10,dtype=tf.int32)
b= tf.random.uniform([2,3],1,10,dtype=tf.int32)
c = tf.stack([a,b],axis=0)
print(a)
print(b)
print(c)
tf.Tensor(
[[3 8 7]
[9 8 6]], shape=(2, 3), dtype=int32)
tf.Tensor(
[[3 4 6]
[3 9 4]], shape=(2, 3), dtype=int32)
tf.Tensor(
[[[3 8 7]
[9 8 6]]
[[3 4 6]
[3 9 4]]], shape=(2, 2, 3), dtype=int32)
上述过程可以用expand_dim和concat来复现:
a = tf.expand_dims(a,axis=0)
b = tf.expand_dims(b,axis=0)
c = tf.concat([a,b],axis=0)
print(c)
tf.Tensor(
[[[3 8 7]
[9 8 6]]
[[3 4 6]
[3 9 4]]], shape=(2, 2, 3), dtype=int32)
2.3 张量分割
张量分割就是张量合并的逆过程
a = tf.squeeze(a)
print(a)
tf.Tensor(
[[3 8 7]
[9 8 6]], shape=(2, 3), dtype=int32)
通过调整axis可以指定切片的维度.
a_split_1=tf.split(a,num_or_size_splits=[1,2],axis=1)
a_split_0=tf.split(a,num_or_size_splits=[1,1],axis=0)
print(a_split_1[0])
print(a_split_1[1])
print(a_split_0[0])
print(a_split_0[1])
tf.Tensor(
[[3]
[9]], shape=(2, 1), dtype=int32)
tf.Tensor(
[[8 7]
[8 6]], shape=(2, 2), dtype=int32)
tf.Tensor([[3 8 7]], shape=(1, 3), dtype=int32)
tf.Tensor([[9 8 6]], shape=(1, 3), dtype=int32)
同一个axis也可以有不同的切片方式,通过调整num_or_size_splits=[1,1,1],就可以把a张量切片成3分
a_split_1=tf.split(a,num_or_size_splits=[1,1,1],axis=1)
print(a_split_1[0])
print(a_split_1[1])
print(a_split_1[2])
tf.Tensor(
[[3]
[9]], shape=(2, 1), dtype=int32)
tf.Tensor(
[[8]
[8]], shape=(2, 1), dtype=int32)
tf.Tensor(
[[7]
[6]], shape=(2, 1), dtype=int32)