本专栏是书《深度学习入门》的阅读笔记一共八章:
第一章深度学习中的Python基础。主要讲解了深度学习将要用到的python的基础知识以及简单介绍了numpy库和matpoltlib库,本书编写深度学习神经网络代码仅使用Python和numpy库,不使用目前流行的各种深度学习框架,适合入门新手学习理论知识。
第二章感知机。主要介绍了神经网络和深度学习的基本单元感知机。感知机接收多个输入,产生一个输出,单层感知器可以实现与门,或门以及与非门,但是不能实现异或门,异或门的实现需要借助多层感知机,这也就是说,单层感知机只能表示线性空间,而非线性空间的表示需要借助多层感知机。
第三章神经网络——基于numpy的代码详解。主要讲解了神经网络的构成,神经网络中的激活函数,神经网络中层与层的矩阵乘法,3层神经网络的代码,输出层的设计和批处理。
这一章中将要引入损失函数的概念,神经网络学习的目的就是找到合适的权重和偏置使得网络的损失函数值达到最小。
从数据中学习是指,神经网络能够通过数据自动确定权重和偏置的值。传统的机器学习方法是,人先通过观察数据,找出特征量(可以从输入数据中准确提取本质数据的转换器),然后再套用机器学习的算法对特征量进行学习。而神经网络深度学习的算法不需要认为的干涉,是一种端到端(end-to-end)(端到端是指从一端到另一端,即从原始数据(输入)到目标结果(输出))的学习方式。
损失函数是表示神经网络性能的“恶劣程度”的指标,即当前的神经网络对测试数据在多大程度上不拟合,在多大程度上不一致。
均方误差函数的数学表达式为:,表示神经网络的输出,表示标签数据,k表示数据的维数。在识别手写数字的例子中,,都是10维的。
用代码实现MSE函数为:
def mean_squard_error(y,t):
return 0.5*np.sum((y-t**2))
交叉熵误差函数的数学表达式为:,对于one-hot标签的数据而言,因为只有一位数是1,其余都是0,所以实际上计算的是对应标签中数字1的索引的输出的自然对数。用代码实现交叉熵误差函数为:
def cross_entropy_error(y,t):
delta=1e-7
return -np.sum(t*np.log(y+delta))
这里在计算np.log时,加上了一个微小值delta,是为了避免出现np.log(0)等于负无穷大-inf的情况。
前面介绍的损失函数都是针对一个训练数据计算的损失函数值,但是神经网络的最终目标是使所有训练数据的损失函数值最小,假如你有100个训练数据,那么要求这100个训练数据的损失函数值达到最小。实现起来也不难,只要把所有的数据的损失函数值加起来就可以,以交叉熵误差函数为例,公式为:。N表示一共有N个训练数据,可以看到只是把所有训练数据的损失函数值相加再求平均即可。如果这样的话,有一个问题,如果N非常非常大的话,比如千万,亿数量级,那么求所有训练数据的综合将会变得非常慢,所以我们提出mini-batch的学习方式,这种学习方式就是每次训练都从所有的训练数据中随机选出一定数量的数据,作为一个mini-batch,用这个mini-batch的平均损失函数值来近似所有训练数据的平均损失函数值,下面我们用代码来实现如何从训练数据中随机选取出mini-batch的数据:
train_size=x_train.shape[0]
batch_size=100
batch_mask=np.random.choice(train_size,batch_size)#从train_size中随机选取batch_size个数,得到
#一个被选数据索引的数组
x_mask=x_train[batch_mask]
t_mask=t_train[batch_mask]
在进行神经网络学习时,不能将识别精度作为指标。因为如果以识别精度作为识别指标,则参数的导数在绝大数地方都会为0。识别精度对微小的参数变化基本上没有什么反应,和使用阶跃函数作为激活函数类似,阶跃函数的导数在绝大部分地方都是等于0的,这对神经网络的学习时非常不利的。而sigmoid函数的导数在任何地方都不为零,得益于这个斜率不会为0的性质,神经网络的学习得以正确进行。
由全部变量的偏导数集合而成的向量,称为梯度(gradient)。梯度会指向各点处的函数值降低的方向,梯度指示的方向是各点处的函数值减小最多的方向。实现代码如下:
def numerical_gradient(f,x):#f表示函数,x为numpy数组
h=1e-4#0.0001 极小值
grad=np.zeros_like(x)#生成一个与x形状相同的零向量
for idx in range(x.size):
#计算f(x+h)
tmp_val=x[idx]
x[idx]=tmp_val+h
fxh1=f(x)
#计算f(x-h)
x[idx]=tmp_val-h
fxh2=f(x)
grad[idx]=(fxh1-fxh2)/(2*h)
x[idx]=tmp_val#还原x的值
return grad
通过巧妙地使用梯度来不断减小函数的值的方法叫做梯度法。但是利用梯度法找到的最小值点不一定是函数的最小值点,它可能是函数的极小值点或者鞍点。寻找最小值的梯度法叫做梯度下降法,寻找最大值的梯度法叫做梯度上升法。我们还是用上面两个变量平方和的函数的例子来说明,用数学表达式表示梯度法为:,,代码实现为:
def gradient_decent(f,init_x,lr,step_num):#函数,x初始值,学习率,循环次数
x=init_x
for i in range(step_num):
grad=numerical_gradient(f,x)#计算梯度
x-=lr*grad#更新x
return x
步骤一:mini-batch,从训练数据中随机挑选一部分数据作为mini-batch数据进行每一次的训练迭代。我们使用梯度下降法减小损失函数的值就是基于每一组mini-batch的值进行的。
步骤二:计算梯度。为了减小损失函数的值,需要计算损失函数对于权重和偏置的梯度。
步骤三:更新参数。根据梯度下降法进行,按照梯度的方向更新参数。
步骤四:重复步骤一、二、三。
本小节将以识别手写数字为例,设计一个2层的神经网络(隐藏层为1层)来进行mnist数据集的识别。
import sys, os
import numpy as np
class TwoLayerNet:
def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.1):
#初始化,参数依次为输入神经元,隐藏层神经元,输出层神经元,初始化权重的比例
self.params = {}
params['W1']=weight_init_std*np.random.randn(input_size,hidden_size)#高斯分布
params['b1']=np.zeros(hidden_size)
params['W2']=weight_init_std*np.random.randn(hidden_size,output_size)
params['b2']=np.zeros(output_size)
def predict(self,x):#前向传播
a1=np.dot(x,W1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,W2)+b2
y=softmax(a2)
return y
def loss(self,x,t):#计算误差
y=self.predict(x)
y_loss=cross_entropy(y,t)
return y_loss
def accuracy(self,x,t):#计算准确率
y=self.predict(x)
y_num=argmax(y,axis=1)
t_num=argmax(t,axis=1)
accuracy=np.sum(y_num==t_num)/float(x.shape[0])#t是one-hot标签
return accuracy
def numerical_gradient_net(self,x,t):#计算梯度
loss_W=lambda W: self.loss(x,t)
grads={}
grad['W1']=numerical_gradient(loss_W,self.params['W1'])
grad['b1']=numerical_gradient(loss_W,self.params['b1'])
grad['W2']=numerical_gradient(loss_W,self.params['W2'])
grad['b2']=numerical_gradient(loss_W,self.params['b2'])
return grads
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
(x_train,t_train), (x_test,t_test)=load_mnist(normalize=True,one_hot_label=True)
#(训练数据,训练标签),(测试数据,测试标签)
train_loss_list=[]#用以保存loss
#超参数
iters_num=10000#迭代次数
train_size=x_train.shape[0]
batch_size=100
learning_rate=0.1
network=TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
for i in range(iters_num):
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
grads=network.numerical_gradients(x_batch,t_batch)
for key in ('W1','b1','W2','b2'):
network.params[key]-=learning_rate*grads[key]
loss=network.loss(x_batch,t_batch)
train_loss_list.append(loss)#将loss放入列表train_loss_list中
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
(x_train,t_train),(x_test,t_test)=load_mnist(normalize=True,one_hot_label=True)
iters_num=10000
learning_rate=0.1
train_size=x_train.shape[0]
batch_size=100
nums_per_epoch=max(train_size/batch_size,1)#一个epoch的训练次数
train_loss_list=[]
train_acc_list=[]
test_acc_list=[]
network=TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
for i in range(iters_num):
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
grads=network.numerical_gradients(x_batch,t_batch)
for key in ('W1','b1','W2','b2'):
network.params[key]-=learning_rate*grads[key]
loss=netwok.loss(x_batch,t_batch)
train_loss_list.append(loss)
#每个epoch计算训练和测试准确率
for i % nums_per_epoch==0:
train_acc=network.accuracy(x_train,t_train)
test_acc=network.accuracy(x_test,t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print("train acc, test acc|"+str(train_acc)+","+str(test_acc))#每一个epoch输出准确率