深度神经网络学到的所有变换可以简化为数值数据张量上的一些张量运算
1.逐元素运算
relu运算和加法都是逐元素的运算,即该运算独立地应用于张量中的每个元素。这些运算非常适合大规模并行实现。可通过for循环来实现。
下列代码为对逐元素relu运算的简单实现
def naive_add(x,y):
assert len(x.shape)==2#x,y为2D张量
assert x.shape==y.shape
x=x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i,j]+=y[i,j]
return x
对于加法采用相同的实现方法
def naive_relu(x):
assert len(x.shape)==2#x为一个numpy的2D张量
x=x.copy()#为避免覆盖输入张量
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i,j]=max(x[i,j],0)
return x
在实际操作中,这些操作都可直接调用Numpy库中的内置函数。
2.广播
当两个形状不同的张量相加时,如果没有歧义的话,较小的张量会被广播,以匹配较大张量的形状。广播含以下两步:
1)向较小张量添加轴(即广播轴),使其ndim与较大的张量相同
2)将较小的张量沿着新轴重复,使其形状与较大的张量相同
下面是一种简单的实现
def naive_add_matrix_and_vector(x,y):
assert len(x.shape)==2#x是一个Numpy的2D张量
assert len(y.shape)==1#y是一个Numpy向量
assert x.shape[1]==y.shape[0]
x=x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i,j]+=y[j]
return x
如果一个张量的形状为(a,b,...,n,n+1,...m),另一个张量的形状为(n,n+1,...,m),那么可以利用广播对他们做两个张量之间的逐元素运算。广播操作会自动地应用于从a到n-1的轴。
下列例子利用广播将逐元素的maximum运算应用于两个形状不同的张量
import numpy as np
x=np.random.random((64,3,32,10))#x的形状为(64,3,32,10)的随机张量
y=np.random.random((32,10))#y的形状为(32,10)的随机张量
z=np.maximum(x,y)#输出的z的形状为(64,3,32,10)与x相同
3.张量点积
点积运算,也叫张量积。与逐元素的运算不同,它将输入张量的元素合并在一起
Tensorflow中的点积使用不同的语法,但在Numpy和Keras中,都是用标准的dot运算符来实现点积。
def naive_vector_dot(x,y):
assert len(x.shape)==1
assert len(x.shape)==1
assert x.shape[0]==y.shape[0]
z=0.
for i in range(x.shape[0]):
z+=x[i]*y[i]
return z
两个向量之间的点积是一个标量,而且只有元素个数相同的向量之间才能做点积。
也可以对一个矩阵x和一个向量y做点积,返回值是一个向量,其中每个元素是y和x的每一行之间的点积。
import numpy as np
def naive_matrix_vector_dot(x,y):
assert len(x.shape)==2
assert len(y.shape)==1
assert x.shape[1]==y.shape[0]#x的第一维和y的第0维大小必须相同
z=np.zeros(x.shape[0])
for i in range(x.shape[0]):
for j in range(x.shape[1]):
z[i]+=x[i,j]*y[j]
return z
如果两个张量中有一个ndim大于1,那么dot运算就不再是对称的,即dot(x,y)不等于dot(y,x)
点积运算可以推广到任意个轴的张量。最常见的是两个矩阵之间的点积。对于两个矩阵x和y,当且仅当x.shape[1]==y.shape[0]时,才可以对他们做点积(dot(x,y))。得到的结果是一个形状我(x.shape[0],y.shape[1])的矩阵,其元素为x的行与y的列之间的点积。其简单实现如下所示
def naive_matrix_vector_dot(x,y):
assert len(x.shape)==2
assert len(y.shape)==2
assert x.shape[1]==y.shape[0]#x的第一维和y的第0维大小必须相同
z=np.zeros((x.shape[0],y.shape[1]))#这个运算返回特定形状的零矩阵
for i in range(x.shape[0]):
for j in range(y.shape[1]):
row_x=x[i:]
column_y=y[:,j]
z[i,j]=naive_vector_dot(row_x,column_y)
return z
一般来说,对于更高维的张量做点积,只要其形状匹配遵循与前面2D张量相同的原则:
(a,b,c,d).(d,)->(a,b,c)
(a,b,c,d).(d,e)->(a,b,c,e)
4.张量变形
reshape()即为对张量进行变形运算.
张量变形是指改变张良的行和列,以得到想要的形状。变形后的张量的元素总个数与初试张量相同。
经常遇到的一种特殊的张量变形是转置,对矩阵做转置就是将行和列互换,使x[i:]变为x[:,i].
5.神经网络的几何解释
由于神经网络完全由一系列张量运算组成,而这些张量运算都只是输入数据的几何变换,因此,神经网络可以解释为高维空间中非常复杂的集合变换,这些变换可以通过简单的步骤实现。
对于三维情况,想象有两张彩纸,一张红色,一张蓝色,将其中一张纸放在另一张上,将两张纸一起揉成小球,这个纸球就是输入数据,每张纸对应于分类问题中的一类。神经网络要做的就是找到可以让纸球回复凭证的变换,从而能够再次让两个类别明确可分。通过深度学习,这一过程可以用三维空间中一系列简单的变换来实现。
为复杂的、高难度的数据流形周到简洁而的表示就是机器学习的内容。
深度学习是将复杂的几何变换逐步分解为一长串基本的几何变换,深度网络的每一层都通过变换使数据解开一点点——许多层堆叠在一起,可以实现非常复杂的解开过程。