神经网络的核心组件是层(layer),它是一种数据处理模块,可以看成是一个数据过滤器。进去一些数据,出来的数据变得更加有用(吃进去的是草,挤出来的是奶)。
大多数深度学习,都是将若干个简单的层给链接起来,实现渐进式的数据过滤,也就是数据蒸馏(过滤到一定程度就等同于蒸馏)
首先来看一个数字识别的案例
(1)读取训练集和测试集
from keras.datasets import mnist #加载keras中的mnist数据集
(train_images,train_labels),(test_images,test_labels)=mnist.load_data()
然后呢,可以用.shape函数和len函数等等,来查看一下数据集规模大小等情况
(2)构建网络
from keras import models
from keras import layers
network=model.Sequential()
network.add(layers.Dense(512,activation='relu',input_shape(28*28,)))
network.add(layers.Dense(10,activation='softmax'))
上例中,网络包含了两个Dense层。Dense层是全连接的神经层。
其中,第二层是一个10路softmax层,返回的是一个由10个概率值(总和为1)组成的数组。每个概率表示的是当前这个图像中的数字是10个数字类别中的某一个的概率。
(3)编译步骤
这一步就是要选择好损失函数,优化器和在训练和测试过程中需要监控的指标。
network.compile(optimizer='rmsprop',#优化器
loss='categorical_crossentropy',#损失函数
metrics=['accuracy']#指标
)
(4)准备图像数据
train_images=train_images.reshape((60000,28*28))
#因为在网络中input_shape我们设为28*28,因此这里要变换为网络要求的形状
train_images=train_images.astype('float32')/255
#将其取值范围控制为0-1之间
test_images=test_images.reshape((10000,28*28))
test_image=test_images.astype('float32')/255
(5)准备标签
from keras.utils import to_categorical
train_labels=to_categorical(train_labels)
test_labels=to_categorical(test_labels)
准备好这些后,我们就可以开始训练了,训练的方式是通过调用网络的fit方法:
network.fit(train_images,train_labels,epochs=5,batch_size=128)
在训练过程中会显示损失loss和精度acc。
案例完。
神经网络中的齿轮:张量运算
深度神经网络学到的所有变换也都可以简化为二进制输入上的一些二进制运算。与此类似,深度神经网络学到的所有变换也都可以简化为数值数据张量上的一些张量运算。
在上面的例子中,我们通过叠加Dense层来构建网络。
Keras层的实例如下所示:
keras.layers.Dense(512,activation='relu')
这个层的作用就是输入一个2D张量,返回另一个2D张量,可以理解为一个函数。它等价于:
output=relu(dot(W,input)+b)
仔细分解一下这行代码,它主要做了三件事dot(W,input):将输入张量和张量W做点积运算
将第一步得到的2D张量与向量b之间的加法运算
最后进行relu运算,relu(x)=max(x,0)
逐元素运算
说到relu,这里我们就来介绍一下逐元素运算。
relu运算和加法都是逐元素的运算,即该运算独立地应用于张量中的每个元素,也就是说这些运算非常适合大规模并行实现。
我们可以用代码来实现逐元素运算。下列代码是对逐元素relu运算的简单实现:
def navie_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
对于加法采用同样的实现方法:
def navie_add(x,y):
assert len(x.shape)==2
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
我们在使用numpy的时候,这些运算用的都是优化的numpy的内置函数。
广播
我们上面写的navie_add()函数只支持两个形状相同的2D张量相加。
但如果我们将两个形状不同但张量相加,会发生什么?
如果没有歧义的话,较小的张量会被广播,以匹配较大张量的形状。向较小的张量添加轴(广播轴),使其ndim与较大的张量相同。
将较小的张量沿着新轴重复,使其形状与较大的张量相同。
注意:事实上,在实际的过程中并不会创建这么多,因为非常低效。这些添加,补充,只存在算法中,而不发生在实际内存里。
下面是对广播的实现:
def navie_add(x,y):
assert len(x.shape)==2
assert len(y.shape)==1
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
张量点积
Keras中使用标准的dot运算符来实现点积。
点积是什么不做赘述。
注意:当两个张量中有一个的ndim大于1,那么dot运算就不再是对称的,即传入参数顺序不同则结果不同。
张量变形
使用reshape函数,可以将张量变形,即改变张量的行和列,以得到想要的形状。
x=np.array([[0.,1.],[2.,3.],[4.,5.]])
print(x.shape)#输出结果:(3,2)
x=x.reshape((6,1))#这时x形状已变为(6,1)
有一种特殊的张量变形是转置。对矩阵做转置是指将行和列互换。
x=np.zeros((300,20))
x=np.transpose(x)#转置
print(x.shape)#输出:(20,300)
总而言之,仿射变换,旋转,缩放等基本的几何操作都可以表示为张量运算。