4.6 索引与切片
4.6.1 索引
和numpy的索引一样
x = tf.random.normal([4,32,32,3])
#取第一章图片的,第二行,第三类的rgb向量
x[0,1,2]
#输出为
<tf.Tensor: id=9, shape=(3,), dtype=float32, numpy=array([-0.3177959 , -2.0918367 , -0.67390585], dtype=float32)>
4.6.2 切片
通过切片方式可以方便地提取一段数据,其中为开始读取位置的索引,为结束读取位置的索引(不包含位),为步长。
x = tf.random.normal([4,32,32,3])
#读取第2,3张图片
x[1:3]
使用来替代时,表示全部都取,
x[:,0:28:2,0:28:2,:]
4.7 维度变换
基本的维度变换包含了改变视图,插入新维度,删除维度,交换维度,复制数据等。
4.7.1 Reshape
x = tf.range(96)
x = tf.reshape(x,[2,4,4,3])
x
#输出结果为
<tf.Tensor: id=19, shape=(2, 4, 4, 3), dtype=int32, numpy=
array([[[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]],
[[12, 13, 14],
[15, 16, 17],
[18, 19, 20],
[21, 22, 23]],
[[24, 25, 26],
[27, 28, 29],
[30, 31, 32],
[33, 34, 35]],
[[36, 37, 38],
[39, 40, 41],
[42, 43, 44],
[45, 46, 47]]],
[[[48, 49, 50],
[51, 52, 53],
[54, 55, 56],
[57, 58, 59]],
[[60, 61, 62],
[63, 64, 65],
[66, 67, 68],
[69, 70, 71]],
[[72, 73, 74],
[75, 76, 77],
[78, 79, 80],
[81, 82, 83]],
[[84, 85, 86],
[87, 88, 89],
[90, 91, 92],
[93, 94, 95]]]])>
4.7.2 增删维度
增加一个长度为1的维度相当于给原有的数据增加一个新维度的概念,维度长度为1,故数据并不需要改变,仅仅是改变数据的理解方式。
x = tf.random.uniform([28,28],maxval=10,dtype=tf.int32)
tf.expand_dims(x, axis = 2)
感觉这个
tf.reshape(x,[28,28,1])
结果一样。
是增加维度的逆操作,与增加维度一样,删除维度只能删除长度为1的维度,也不会改变张量的存储。
x = tf.random.normal([1,28,28,1])
tf.squeeze(x,axis=0)
4.7.3 交换维度
交换维度操作是非常常见的,比如在 TensorFlow中,图片张量默认存储格式是通道后行格式:,但是部分库的图片格式是通道先行:。
通过 tf.transpose(x,perm) 完成维度交换操作,其中 perm 表示新维度的顺序 List。
x = tf.random.normal([2,32,32,3])
tf.transpose(x,[0,3,1,2])
4.7.4 数据复制
当通过增加维度操作插入新维度后,可能希望在新的维度上面复制若干份数据,满足后续算法的格式要求。
考虑的例子,偏置插入新维度后,需要在新维度上复制 batch size份数据后,才能完成张量相加。
考虑,,为了完成加法,的形状也应该是。
但是的定义为:
所以需要把变成一个的矩阵,如下所示:
通过 tf.tile(b,multiples)函数完成数据在指定维度上的复制操作。(对应位置为1表明不复制,为2表明新长度为原来的长度的2倍)
b = tf.constant([1,2,3])
b = tf.expand_dims(b,axis=0)
b
#输出结果为
<tf.Tensor: id=52, shape=(1, 3), dtype=int32, numpy=array([[1, 2, 3]])>
b = tf.tile(b,multiples=[2,1])
b
#输出结果为
<tf.Tensor: id=54, shape=(2, 3), dtype=int32, numpy=
array([[1, 2, 3],
[1, 2, 3]])>
tf.tile会创建一个新的张量来保存复制后的张量,由于复制操作涉及到大量数据的读写运算,计算代价太高,而且神经网络中不同 shape 之间的运算操作十分频繁,所以常常用 Broadcasting(广播)操作。
4.8 Broadcasting
Broadcasting 也叫广播机制(自动扩展也行更合适),它是一种轻量级张量复制的手段,在逻辑上扩展张量数据的形状,但是只要在需要时才会执行实际存储复制操作。
对于大部分场景,Broadcasting 机制都能通过优化手段避免实际复制数据而完成逻辑运算,从而相对于 tf.tile 函数,减少了大量计算代价。
x = tf.random.normal([2,4])
w = tf.random.normal([4,3])
b = tf.random.normal([3])
y = x @ w + b
y
#输出结果为
<tf.Tensor: id=74, shape=(2, 3), dtype=float32, numpy=
array([[-1.6606035 , 0.35164022, 0.66946125],
[ 0.6017679 , 1.4871141 , -2.0808659 ]], dtype=float32)>
这里面自动调用了 Broadcasting 函数 tf.broadcast_to(x, new_shape)。
4.9 数学运算
4.9.1 加减乘除
加减乘除是最基本的数学运算,分别通过 tf.add,tf.subtract,tf.multiply,tf.divide 函数实现;或者直接用运算符
整除和余数也是常见的运算之一,分别通过//和%运算符实现,如下:
a = tf.range(5)
b = tf.constant(2)
a // b
#输出结果为
<tf.Tensor: id=80, shape=(5,), dtype=int32, numpy=array([0, 0, 1, 1, 2])>
a%b
#输出结果为
<tf.Tensor: id=81, shape=(5,), dtype=int32, numpy=array([0, 1, 0, 1, 0])>
4.9.2 乘方
通过 tf.power(x,a)实现乘方运算,也可以通过运算符 ** 实现 运算,
x=tf.range(4)
tf.pow(x,3)
#输出结果为
<tf.Tensor: id=87, shape=(4,), dtype=int32, numpy=array([ 0, 1, 8, 27])>
x ** 3
#输出结果为
<tf.Tensor: id=89, shape=(4,), dtype=int32, numpy=array([ 0, 1, 8, 27])>
通过设置指数为形式即可实现根号运算:
x = tf.constant([1.,4.,9.])
x ** (0.5)
#输出结果为
<tf.Tensor: id=95, shape=(3,), dtype=float32, numpy=array([1., 2., 3.], dtype=float32)>
特别地,对于平方和平方根运算,可以用 tf.square(x) 和 tf.sqrt(x) 实现。
4.9.3 指数,对数
- 通过 tf.pow(a,x) 或者 ** 运算符实现运算
- 通过 tf.exp(x) 实现自然对数。
- 通过 tf.math.log(x) 实现自然对数
- 通过对数换底公式 实现计算其他底数的对数
4.9.4 矩阵相乘
通过 @ 运算符和 tf.matmul(a,b)实现矩阵运算。
4.10 前向传播实战
w1 = tf.Variable(initial_value=tf.random.truncated_normal([784,256],stddev=0.1))
b1 = tf.Variable(initial_value=tf.zeros([256]))
w2 = tf.Variable(initial_value=tf.random.truncated_normal([256,128],stddev=0.1))
b2 = tf.Variable(initial_value=tf.zeros(128))
w3 = tf.Variable(initial_value=tf.random.truncated_normal([128,10],stddev=0.1))
b3 = tf.Variable(tf.zeros([10]))
(x_train,y_train),(x_val,y_val) = datasets.mnist.load_data()
x_train = tf.reshape(x_train,[-1,28*28])
x_train = tf.dtypes.cast(x_train,tf.float32)
y_train = tf.one_hot(y_train,depth=10)
lr = 0.1
Loss = []
for i in range(100):
with tf.GradientTape() as tape:
h1 = tf.matmul(x_train,w1) + b1
h1 = tf.nn.relu(h1)
h2 = tf.matmul(h1,w2) + b2
h2 = tf.nn.relu(h2)
h3 = tf.matmul(h2,w3) + b3
loss = (y_train - h3)** 2
loss = tf.reduce_mean(loss)
grads = tape.gradient(loss,[w1,b1,w2,b2,w3,b3])
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])
Loss.append(loss)
print(f"step:{i},loss:{loss}")
参考资料:https://github.com/dragen1860/Deep-Learning-with-TensorFlow-book