数值微分就是利用数值方法近似求解函数的导数的方法。根据大一学习的【高等数学】的知识可知,函数的导数近似等于函数的差值除以自变量的差值,当自变量的差值趋近于无穷小时。
用Python表示如下:
def numerical_diff(f, x):
h=1e-4 # 0.0001
return (f(x+h)-f(x-h))/(2*h)
为了保证这个微小的差分不被Python解释器直接省略,所以这里采用中心差分的形式(f(x+h)-f(x-h))/(2*h)
。
用数值微分举个例子:
def function_1(x):
return 0.01*x**2 + 0.1*x
#计算function_1(5)的数值微分
print(numerical_diff(function_1, 5))
结果:
0.1999999999990898
可以看出计算结果和真实结果0.2的误差已经非常小了,可以近似认为它们两者相等。
偏导数
有多个变量的函数的导数称为偏导数。求一个变量的偏导数,就是先将其他变量当做常数,再对函数求导。
梯度
由全部变量的偏导数汇总而成的向量称为梯度。
用Python实现如下:
def numerical_gradient(f, x):
h=1e-4 # 0.0001
grad=np.zeros_like(x) # 生成和x形状相同的数组
for i in range(x.size):
t=x[i]
# f(x+h)的计算
x[i]=t+h
f_h1=f(x)
# f(x-h)的计算
x[i]=t-h
f_h2=f(x)
grad[i]=(f_h1-f_h2)/(2*h)
x[i]=t # 还原值
return grad
函数numerical_gradient(f, x)
中,参数f为多元函数,参数x为numpy数组。
梯度是一个向量,这个向量指示的方向是各点处的函数值减小最多的方向。
梯度法
机器学习的主要任务是在学习时寻找最优参数,这个最优参数是指是损失函数取最小值的参数。而通过使用梯度来寻找损失函数最小值(或者说尽可能小的值)的方法称为梯度法。
寻找最小值的梯度法称为梯度下降法,一般而言,在神经网络中,梯度法主要是指梯度下降法。
用Python实现梯度下降法:
def gradient_descent(f, init_x, lr=0.1, step_num=100):
x=init_x
for i in range(step_num):
grad=numerical_gradient(f, x)
x -= lr*grad
return x
参数f是要进行优化的函数,init_x是初始值,lr是学习率,step_num是梯度法重复的次数。
像学习率这样的参数称为超参数,这是和权重、偏置都不同的参数,权重和偏置可以通过训练学习自动获得,而学习率这样的超参数只能人工设定。由于是人工设定,所以必须多尝试几个值,以便达到较好的效果。
两层神经网络的类
先实现一个名为TwolayerNet
的神经网络,再利用这个网络的实例进行随机梯度下降法的学习。
import os, sys
sys.path.append(os.pardir)
from common.funciton import *
from common.gradient import numerical_gradient
import numpy as np
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["b1"]=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['w1'], self.params['w2']
b1, b2=self.params['b1'], self.params['b2']
a1=np.dot(x, w1) + b1
z1=sigmoid(a1)
a2=np.dot(z1, w2) + b2
y=softmax(a2)
return 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
# x:输入数据, t:监督数据
def numerical_gradient(self, x, t):
loss_w = lambda 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
神经网络的学习
神经网络的学习的实现使用的是mini-batch学习。即是从训练数据中随机选择一部分数据(称为mini-batch),再以这些mini-batch为对象,使用随机梯度下降法更新参数的过程。
接下来以TwolayerNet
类为对象,使用MNIST数据集进行学习。
import os, sys
sys.path.append(os.pardir)
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=[]
# 超参数
iters_num=1000 # 梯度法更新次数
train_size=x_train.shape[0]
batch_size=100 # 每个mini-batch的大小
learning_rate=0.1 # 学习率
network=TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
for i in range(iters_num):
# 获取mini-batch
batch_mask=np.random.choice(train_size, batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
# 计算梯度
grad=network.numerical_gradient(x_batch, t_batch)
# grad=network.gradient(x_batch, t_batch) # 高速版
# 更新参数
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
# 记录学习过程
loss=network.loss(x_batch, t_batch)
train_loss_list.append(loss)
学习的过程会花费较长的时间,电脑配置太低了就很难受。
每天学习一点点,每天进步一点点。
网友评论