文章目录
tensorflow 基础
数据类型
数值
在 TensorFlow 中间,为了表达方便,一般把标量、向量、矩阵也统称为张量,不作区分,需要根据张量的维度数(Dimension)和形状自行判断.
与标量不同,向量的定义须通过 List 类型传给 tf.constant()。创建一个元素的向量
标量
t2 = tf.constant(1, dtype=tf.int32)
向量
t3 = tf.constant([1,2,3,4], dtype=tf.float32)
矩阵
t4 = tf.constant([[1, 2],[3, 4]], dtype=tf.float64)
张量
t5 = tf.constant([[[1, 2], [3, 4]],[[5, 6], [7, 8]]])
t6 = tf.constant([[[[1, 2], [3, 4]],[[5, 6], [7, 8]]],[[[1, 2], [3, 4]],[[5, 6], [7, 8]]]])
字符串
ts1 = tf.constant('Hello World', dtype=tf.string)
ts2 = tf.strings.lower(ts1)
布尔
tb1 = tf.constant(True)
tb2 = tf.constant([True, False])
tb3 = tf.constant(True, dtype=tf.int32)
数值精度
常用的精度类型有tf.int16, tf.int32, tf.int64, tf.float16, tf.float32, tf.float64,其中 tf.float64 即为 tf.double。
对于大部分深度学习算法,一般使用 tf.int32, tf.float32 可满足运算精度要求,部分对精度要求较高的算法,如强化学习,可以选择使用 tf.int64, tf.float64 精度保存张量。
pi1 = tf.constant(np.pi, dtype=tf.float32)
pi2 = tf.constant(np.pi, dtype=tf.float64)
if pi1.dtype != tf.float64 :
pi3 = tf.cast(pi1, tf.tf.float64)
results:
tf.Tensor(3.1415927, shape=(), dtype=float32)
tf.Tensor(3.141592653589793, shape=(), dtype=float64)
tf.Tensor(3.1415927410125732, shape=(), dtype=float64)
可以看到转换后数值不一致的,也不是零
待优化张量
通过tf.Variable()函数可以将普通张量转换为待优化张量
张量的 name 和 trainable 属性是 Variable 特有的属性,name 属性用于命名计算图中的
变量,这套命名体系是 TensorFlow 内部维护的,一般不需要用户关注 name 属性;trainable
表征当前张量是否需要被优化,创建 Variable 对象是默认启用优化标志,可以设置
trainable=False 来设置张量不需要优化
var1 = tf.Variable([1, 2, 3, 4],dtype=tf.float32, name='Test_var1')
var2 = tf.Variable([1, 2, 3, 4],dtype=tf.float32, name='Test_var2', trainable=False)
print(var1)
print(var2.trainable)
print(var1.numpy())
创建张量
从Numpy List中创建Tensor
Numpy Array 和 python list 是非常重要的数据载体,很多时候都是将原始数据读取出后通过Array 和list,即为数据预处理部分, 然后转化为tensor类型
通过 tf.convert_to_tensor 可以创建新 Tensor,并将保存在 Python List 对象或者 Numpy
Array 对象中的数据导入到新 Tensor 中:
a = [1., 1.]
ctt1 = tf.convert_to_tensor(a)
b = np.array([1.,1.])
ctt2 = tf.convert_to_tensor(b)
print(a)
print(ctt1)
print(b.max())
print(ctt2)
Numpy 中浮点数数组默认使用 64-Bit 精度保存数据,转换到 Tensor 类型时精度为 tf.float64,可以在需要的时候转换为 tf.float32 类型
tf.constant()和tf.convert_to_tensor()都能够自动的把 Numpy 数组或者 Python List 数据类型转化为 Tensor 类型,这两个 API 命名来自 TensorFlow 1.x 的命名习惯,在 TensorFlow 2 中函数的名字并不是很贴切,使用其一即
全0,1 Tensor
print(tf.zeros([]))
print(tf.ones([]))
print(tf.zeros([2,2,3]))
zl1 = tf.constant([[2.,3.],[4, 5.]])
print(tf.zeros_like(zl1,name='zero_like1'))
print(tf.ones_like(zl1,name='one_like1', dtype=tf.int32))
tf.*_like 是一个便捷函数,可以通过 tf.zeros(a.shape)等方式实现
创建自定义张量
通过 tf.fill(shape, value)可以创建全为自定义数值 value 的张量
print(tf.fill([2,4], 3))
创建已知分布的张量
正态分布 Normal Distribution
高斯分布 Gaussian Distribution
创建采样自这 2 种分布的张量非常有用,比如在卷积神经网络中,卷积核张量W 初始化为正态分布有利于网络的训练;在对抗生成网络中,隐藏变量 z 一般采样自均匀分布
通过 tf.random.normal(shape, mean=0.0, stddev=1.0)可以创建形状为 shape,均值为mean,标准差为 stddev 的正态分布𝒩(𝑚𝑒𝑎𝑛, 𝑠𝑡𝑑𝑑𝑒𝑣2)
通过 tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)可以创建采样自[𝑚𝑖𝑛𝑣𝑎𝑙, 𝑚𝑎𝑥𝑣𝑎𝑙]区间的均匀分布的张量
print(tf.random.normal([3, 3], mean=0, stddev=1.0))
print(tf.random.normal([3, 3], mean=1, stddev=1.0))
print(tf.random.uniform([3,3],0, 1, tf.float64, seed=1, name='uniform_test'))
print(tf.random.uniform([3,3],0, 1, tf.float64, seed=1, name='uniform_test'))
如果需要均匀采样整形类型的数据,必须指定采样区间的最大值 maxval 参数,同时制定据类型为 tf.int*型
创建序列
print(tf.range(10))
print(tf.range(10, delta=2))
print(tf.range(5, 10))
results:
tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)
tf.Tensor([0 2 4 6 8], shape=(5,), dtype=int32)
tf.Tensor([5 6 7 8 9], shape=(5,), dtype=int32)
张量的典型应用
标量
简单的数字,一般用于各种测试值指标
准确度 Acc Accuracy
精度 Precision
训练曲线 批次 batch
训练曲线 步数 step
代价 损失 loss
…
向量 矩阵
向量表示偏置b, 矩阵表示W
layer1 = tf.keras.layers.Dense(3, kernel_initializer=tf.random_uniform_initializer, bias_initializer=tf.random_normal_initializer)
layer2 = tf.keras.layers.Dense(3, kernel_initializer=tf.random_uniform_initializer(0,1), bias_initializer=tf.random_normal_initializer(1,1))
layer1.build([3,3])
layer2.build([3,3])
print(layer1.kernel)
print(layer1.bias)
print(layer2.kernel)
print(layer2.bias)
3维张量
可用于NLP预料数据中
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.imdb.load_data(num_words=10000)
print(x_train[0])
x_train = tf.keras.preprocessing.sequence.pad_sequences(x_train, maxlen=80)
print(x_train.shape)
print(x_train[0])
embedding = tf.keras.layers.Embedding(10000, 100)
out = embedding(x_train)
print(out.shape)
4维张量
最常用就是图像卷积
x = tf.random.normal([4, 32, 32, 3])
layer = tf.keras.layers.Conv2D(16, kernel_size=3, padding='same')
out = layer(x)
print(out.shape)
切片与索引
索引
x = tf.random.normal([4,32,32,3])
print(x[0])
print(x[0][1])
print(x[0][1][2])
print(x[0,1,2])
当张量的维度数较高时,使用[𝑖][𝑗]. . .[𝑘]的方式书写不方便,可以采用[𝑖,𝑗, … , 𝑘]的方 式索引,它们是等价的
切片
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nkPjkfkR-1583029951598)(user-image/1581307001784.png)]
特别地,step 可以为负数,考虑最特殊的一种例子,step = −1时,start: end: −1表示 从 start 开始,逆序读取至 end 结束(不包含 end),索引号𝑒𝑛𝑑 ≤ 𝑠𝑡𝑎𝑟
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-utbYucUr-1583029951599)(user-image/1581307300530.png)]
x = tf.random.normal([4,32,32,3])
print(x[0])
print(x[0][1])
print(x[0][1][2])
print(x[0, 1, 2])
print(x[0, 1, 2, 1:2])
print(x[0][1][2][1:2])
print(x[0][0:2][::])
print(x[2:,...])
print(x[...,1])
维度变换
我们先来认识一下张量的存储和视图(View)的概念。张量 的视图就是我们理解张量的方式,比如 shape 为[2,4,4,3]的张量 A,我们从逻辑上可以理解 为 2 张图片,每张图片 4 行 4 列,每个位置有 RGB 3 个通道的数据;张量的存储体现在张量在内存上保存为一段连续的内存区域,对于同样的存储,我们可以有不同的理解方式,比如上述 A,我们可以在不改变张量的存储下,将张量 A 理解为 2 个样本,每个样本的特征为长度 48 的向量,这就是存储与视图的关系
语法上来说,视图的变换只需要满足下面公式:
b
∗
h
∗
w
∗
c
b*h*w*c
b∗h∗w∗c
正是由于视图的设计约束很少,完全由用户定义,使得在改变视图时容易出现逻辑隐患
x1 = tf.range(50)
x2 = tf.reshape(x1, [2,-1])
x3 = tf.reshape(x1, [2, 5, 5, 1])
x4 = tf.reshape(x3, [1, 5, 5, 2])
print(x1.shape)
print(x2.shape)
print(x3.shape)
print(x3)
print(x4)
x5 = tf.random.uniform([28, 28], 0, 10, dtype=tf.int32)
x6 = tf.expand_dims(x5, axis=0)
x6 = tf.expand_dims(x6, axis=3)
x7 = tf.expand_dims(x5, axis=2)
x8 = tf.squeeze(x7, axis=2)
x9 = tf.squeeze(x6)
print(f'x5:{x5.shape} x6{x6.shape} x7{x7.shape} x8{x8.shape} x9{x9.shape}')
x10 = tf.transpose(x6, perm=[0, 2, 1, 3])
print(x6[:,0,...])
print(x10[:,:,0,:])
维度复制
tf.tile会创建一个新的张量来保存复制后的张量,复制操作涉及到大量数据的读写 IO 运算,计算代价相对较高
x1 = tf.range(4)
x2 = tf.reshape(x1, [2,2])
x3 = tf.tile(x2, multiples=[1, 2])
print(f'x1{x1}, x2{x2}, x3{x3}')
Broadcasting
Broadcasting 机制的核心思想是普适性,即同一份数据能普遍适合于其他位置。在验证普适性之前,需要将张量 shape 靠右对齐,然后进行普适性判断:对于长度为 1 的维度, 默认这个数据普遍适合于当前维度的其他位置;对于不存在的维度,则在增加新维度后默 认当前数据也是普适性于新维度的,从而可以扩展为更多维度数、其他长度的张量形状。
如+,-,*,/等运算等自动调用了broadcasting机制
数学运算
加减乘除
乘方
指数、对数
使用tf.sqrt时,需要指定传入数据类型
a = tf.range(5, dtype=tf.float32)
a1 = tf.reshape(a, [5,1])
b = tf.constant(2.)
print(tf.add(a, b))
print(tf.subtract(a, b))
print(tf.multiply(a, b))
print(tf.divide(a, b))
print(tf.multiply(a1, a))
print(tf.multiply(a1, a1))
print(a // b)
print(a % b)
print(tf.pow(a, 2))
print(a**3)
print(tf.square(a))
print(tf.sqrt([a]))
print(tf.exp(1.))
print(tf.math.log(tf.exp(1.)))
矩阵相乘
相乘也支持Broadcasting机制
构建模型时,需要将数据类型保持一致,全float32够用了,部分情况float64
一个例子:
下面这个例子有问题,y使用onehot编码,但是out的数字和onehot编码不一致,分类问题别用MSE衡量
上述有误,不是没有效果,是使用的数据太大了,变化太小,不稳定,理论是对的,实际数据太大导致>
# coding : utf-8
from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
w1 = tf.Variable(tf.random.truncated_normal([784, 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]))
x_train = tf.reshape(x_train, [-1, 28*28])
x_train = tf.cast(x_train, tf.float32)
y_onehot = tf.one_hot(y_train, depth=10, dtype=tf.float32)
for epoch in tf.range(10):
with tf.GradientTape() as tape:
h1 = x_train @ w1 + tf.broadcast_to(b1, [x_train.shape[0], 256])
h1 = tf.nn.relu(h1)
h2 = h1 @ w2 + b2
h2 = tf.nn.relu(h2)
out = h2 @ w3 + b3
loss = tf.square(y_onehot - out)
loss = tf.reduce_mean(loss)
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
lr = 0.01
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])
print('end')