相关前驱文章:【机器学习】(六)线性模型:线性回归
“学习”指从训练数据中自动获取最优权重参数的过程,即调整权重和偏置以便拟合训练数据的过程。
机器学习的主要任务是在学习是寻找最优参数。神经网络也必须寻找最优参数(权重和偏置)。
最优参数指损失函数取最小值时的参数。
步骤:
1.从训练数据中随机选取一部分数据(mini-batch)
2.为了减小mini_batch的损失函数的值,需要求出各个权重参数的梯度。
3.将权重参数沿着梯度方向进行微小更新
4.重复1-3
损失函数
损失函数是衡量神经网络性能的“恶劣程度”的指标。即有神经网络对监督数据在多大程度上不拟合。
返回值越小越好,证明越拟合。
识别精度(34%)是不连续的、离散的值。
损失函数(0.93432…)会发生连续的变化。作为指标更有优势。
针对单个数据的损失函数
均方误差
import numpy as np
# 均方误差
def mean_squared_error(y, t): # 参数为NumPy数组
return 0.5 * np.sum((y-t)**2) # 输出和各个元素之差的平方
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
print(mean_squared_error(np.array(y), np.array(t))) # 0.09750000000000003
交叉熵误差
# 交叉熵误差
def cross_entropy_error(y, t):
delta = 1e-7 # 设置一个微小值,防止对数的真数为0
return -np.sum(t * np.log(y + delta))
mini-batch学习
神经网络的学习可以从训练数据中选出一批数据(mini-batch,小批量),然后对每个mini_batch进行学习。类似,抽样检测。
随机抽取
# 从指定数字中随机选取想要个数。返回一个包含被选数据的索引的数组
batch_mask = np.random.choice(60000, 10)
x_batch = x_train[batch_mask]
t_train = t_train[batch_mask]
mini_batch版交叉熵误差的实现
可以同时处理单个数据和批量数据(数据作为batch集中输入)。
监督数据t是one-hot表示
# one-hot形式的mini-batch交叉熵。t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
def cross_entropy_error(y, t):
if y.ndim == 1: # 输入维度为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
监督数据是标签形式
# 标签形式的mini-batch交叉熵。t = 2
def cross_entropy_error(y, t):
if y.ndim == 1: # 输入维度为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[np.arange(batch_size), t] + 1e-7)) / batch_size
若batcg_size=5
处理标签np.arange(batch_size)
会生成一个NumPy数组[0, 1, 2, 3, 4]。y[np.arange(batch_size), t]
抽出每个数据的正确解标签对应的神经网路输出[y[0,2], y[1,7], y[2,0], y[3,9], y[4,4]]
数值微分/导数
数据微分是利用微小的差分求导数的过程。
舍入误差指省略小数的精细部分的数值。(1e-50 = 0.0)
中心差分:以x为中心,求解它左右两边的差分。f(x+h)-f(x-h)
向前差分:f(x+h)-f(x)。因为h不可能无限接近于0,会产生误差。
导数:
# 导数
def numerical_diff(f, x): # 函数f,和传递给f的参数x
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
偏导数:将目标变量之外的变量固定到某些特定的值再定义新的函数,然后对新定义的函数应用求导。
def f(x): # f(x)=x0^2+x1^2
return np.sum(x**2) # 计算NumPy数组中各个元素的平方,再求和
def f1(x0): # 对x0在x0=3,x1=4处求导
return x0*x0 + 4.0**2.0
def f1(x1): # 对x1在x0=3,x1=4处求导
return 3.0**2.0 + x1*x1
梯度
梯度:由全部变量的偏导数汇总而成的向量
梯度指示的方向是各点处的函数值减小最多的方向。
函数极小值、最小值、鞍点(一个方向是极大值另一个方向是极小值)的地方,梯度为0。
import numpy as np
# 梯度
def numerical_gradient(f, x): # 函数f,NumPy数组x
h = 1e-4 # 0.0001
grad = np.zeros_like(x) # 生成和x形状相同的数组,元素全是0
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 # 还原
return grad
梯度法(梯度下降法)
梯度表示的是各点处函数减小的中最多的方向。
通过不断沿梯度方向前进,逐渐减小函数值的过程就是梯度法。
梯度法可以求得函数的极小值,甚至是最小值。
# 梯度下降法。参数:函数,初始值,学习率(多大程度更新参数),梯度法重复次数
def gradient_dedcent(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
学习率不能过大(发散)或者过小(更新不够)。
学习率是一种超参数(需要人工设定,尝试多个值)。
神经网络的梯度
神经网络的梯度指损失函数关于权重参数的梯度。
import numpy as np
from h import softmax # 激活函数
from loss_functions import cross_entropy_error_one # one-hot形式的mini-batch交叉熵
# 梯度
def numerical_gradient(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val # 还原值
it.iternext()
return grad
class simpleNet:
def __init__(self): # 用高斯函数进行初始化。权重为W的神经网络
self.W = np.random.randn(2,3) # 形状为2*3的权重参数
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_one(y, t) # 求损失值
return loss
x = np.array([0.6, 0.9]) # 输入
t = np.array([0, 0, 1]) # 正确解标签
net = simpleNet()
print(net.W) # 初始化权重参数
p = net.predict(x)
print(np.argmax(p)) # 最大值索引
f = lambda w: net.loss(x, t) # 简单函数 参数:返回值
'''
def f(w): # 伪参数
return net.loss(x, t)
'''
dW = numerical_gradient(f, net.W) # 得到的神经网络梯度
print(dW)
求出神经网络梯度后,就可以根据梯度法,更新权重参数。