tuhr
用google的colab,将相关代码和文件保存在google 的云端硬盘中,调用时用以下代码登录账号连接资源
from google.colab import drive
drive.mount('/content/drive')
!ls "/content/drive/My Drive/"
import os
os.chdir("/content/drive/My Drive/【源代码】深度学习入门:基于Python的理论与实现_20240716")
机器学习能够学习“特征”,但特征仍是由人设计的;而神经网络中,连特征都是由机器决定的。
1. 首先,让我们了解损失函数,均方误差,softmax函数,one-hot表示法。
1.1 均方误差表示如下
def mean_squared_error(y,t):
return 0.5*np.sum(y-t)**2)
1.2 交叉熵误差也是一种损失函数。实际上只计算对应正确结果的输出概率的自然对数。正确解标签对应的输出越大,交叉熵误差越接近0。
def cross_entropy_error(y,t):
delta=1e-7
return -np.sum(t*np.log(y+delta))
delta的作用在于,np.log(0)会导致计算困难,因此用delta防止负无限大的发生。
2. mini-batch的学习
为了节省训练时间,从训练集中选出一批mini-batch,对每个mini-batch进行学习。下面我们来编写从训练数据集中随机选择指定个数的数据的代码
首先,读入MNIST数据集
import sys,os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist
(x_train,t_train),(x_test,t_test)=load_mnist(normalize=True,one_hot_label=True)
print(x_train.shape)
print(t_train.shape)
如何从训练数据集随机抽取10笔数据?可以用np.random.choice()随机选择数字
train_size=x_train.shape[0]
batch_size=10
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
2.2 mini-batch版交叉熵误差的实现
这里,我们来实现一个可以同时处理单个数据和批量数据两种情况的函数
def cross_entropy_error(y,t):
if y.ndim==1:
t=t.reshape(1,t.size)
y=y.reshape(1,y.size)
batch_size=y.shape[0]
return -np.sum(t*np.log(y+1e-7))/batch_size
以上,通过除以batch_size对batch的个数进行正规化,计算单个数据的平均交叉熵误差。
此外,当监督数据为标签形式(非one-hot表示,而是像2,7这样的标签)时,交叉熵误差可通过如下代码实现
batch_size=y.shape[0]
return np.sum(np.log(y[np.arange(batch_size),t]+1e7))/batch_size
3. 导数
计算导数时,微小值不宜过小,10-4即可。并且,为了减小误差,可以用中心差分。
def numerical_diff(f,x):
h=1e4
return (f(x+h)-f(x-h))/(2*h)
3.1 偏导数
多个变量的函数的导数称为偏导数
3.2 梯度
由全部变量的偏导数汇总的向量就是梯度。神经网络在寻找最优参数(权重和偏置)就会通过梯度尽可能找函数最小值。
定义函数numerical_gradient,参数f为函数,x为数组,求函数在x的各个元素的微分
def numerical_gradient(f,x):
h=1e-4
grad=np.zeros_like(x)
for idx in range(x,size):
tmp_val=x[idx]
# 计算f(x+h)
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[idx]
return grad
梯度法指的是,函数的取值从当前位置沿着梯度方向前进一段距离,在新的地方重新求梯度;继续前进,如此反复,逐渐减小函数值。
在神经网络的学习中,学习率决定了一次学习中,应该学多少,以及在多大程度上更新参数。学习率需要事先确定为某个值,比如0.01或0.001.在神经网络的学习中,一般会一边改变学习率的值,一边确认学习是否正确进行了。像学习率这样的参数称为超参数,是人工设定的。
def gradient_descent(f,init_x,lr=0.01,step_num=100):
x=init_x
for i in range(step_num):
grad=numerical_gradient(f,x)
x-=lr*grad
return x
3.3 神经网络的梯度
神经网络的学习,也需要求梯度,这里的梯度是指损失函数关于权重参数的梯度。以一个简单的神经网络为例,来实现求梯度的代码,为此,我们要实现一个名为simpleNet的类
import sys,os
sys.path.append(os.pardir)
import numpy as np
from common.functions import softmax,cross_entropy_error
from common.gradient import numerical_gradient
class simpleNet:
def __init__(self):
self.W=np.random.randn(2,3)# 用高斯分布初始化W
def predict(self,x):
return np.dot(x,self.W) #点乘
def loss(self,x,t):
z=self.predict(x)
y=softmax(z)
loss=cross_entropy_error(y,t)
return loss
接下来求梯度,使用numerical_gradient(f,x)求梯度。神经网络的学习总体就是按照4个步骤:第一,选mini-batch;第二,计算梯度;第三,更新权重参数;第四,重复以上三步直到学习结束。由于随机选择mini-batch,所以又称为随机梯度下降法
3.4 实现手写数字识别的神经网络
这里以2层神经网络(隐藏层为1层得网络)为对象,使用MNIST数据集进行学习。
首先,我们将用一个名为TwoLayerNet的类实现2层神经网络
import sys,os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
class TwoLayerNet:
def __init__(self,input_size,hidden_size,output_size,\
weight_init_std=0.01):
# 初始化权重
self.params={}
self.params['W1']=weight_init_std*np.random.randn(input_size,hidden_size)
self.params['b']=np.zeros(hidden_size)
self.params['W2']=weight_init_std*\
np.random.randn(hidden_size,output_size)
self.params['b2']=np.zeros(output_size)
def predict(self,x):
W1,W2=self.params['W1self.params['W2']
b1,b2=self.params['b1'],self.params['b2']
a1=np.dot(x,W1)+b1
z1=sigimoid(a1)
a2=np.dot(z1,W2)+b2
y=softmax(a2)
rturn y
#x输入数据 t:监督数据
def loss(self,x,t):# 计算损失值
y=self.predict(x)
return cross_entropy_error(y,t)
def accuracy(self,x,t): # 计算准确度
y=self.predict(x)
y=np.argmax(y,axis=1)
t=np.argmax(t,axis=1)
accuracy=np.sum(y==t)/float(x.shape[0])
return accuracy
def numerical_gradient(self,x,t): #x是输入数据,t是监督数据
loss_W=lamdba W:self.loss(x,t)
grads={}
grads[W1']=numerical_gradient(loss_W,self.params['W1'])
grads['b1']=numerical_gradient(loss_W,self.params['b1'])
grads['W2']=numerical_gradient(loss_W,self.params['W2'])
grads['b2']=numerical_gradient(loss_W,self.params['b2])
return grads