机器学习的核心在于从数据中提取规律和特征,并用于分类或预测。对于识别手写数字,如果人工设计一个识别算法逻辑是十分困难的。一种方法是任务在数据中提取更重要的特征量,然后利用机器学习算法如SVM或KNN。而神经网络的方法则是完全由机器自主提取特征,中间没有任何人工干预
1 训练数据和测试数据
一般来说,机器学习的数据分为训练数据(或监督数据)和测试数据。首先使用训练数据寻找最优参数,然后使用测试数据评估模型泛化能力。
泛化能力指的是模型处理未被观察到数据的能力,是机器学习的目标。
如果模型只能处理特定数据集,泛化能力差,这种状态被称为过拟合(over fitting)
2 损失函数
神经网络利用损失函数作为指标表现现在的状态,再利用这一指标寻找更优权重参数。损失函数表现了网络和数据集不拟合程度,损失函数越低,网络更优
均方差法(mean squared error):
yk: 神经网络输出
tk:监督数据
(k代表数据维数)
import numpy as np
def mean_squared_error(y, t):
return 0.5 * np.sum((y - t) ** 2)
t = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
y = np.array([0.1, 0.05, 0.6, 0, 0.05, 0.1, 0, 0.1, 0, 0])
print(mean_squared_error(y, t)) # 0.0975
在本例中,数组t代表监督数据,里面2位置标为1,其他位置标为0代表结果为2.这一表示法叫one-hot表示。利用均方差得到的总损失为0.0975
交叉熵误差(cross entropy error):
由于tk的one-hot表示里只有正确解值为1,其他解值为0.交叉熵的值为-log(y),其中y为正确解的输出概率
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
t = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
y = np.array([0.1, 0.05, 0.6, 0, 0.05, 0.1, 0, 0.1, 0, 0])
print(cross_entropy_error(y, t)) # 0.511
这里我们加上一个很小的值delta,这使得在t为0时不会出现log(0)导致无限大
mini-batch学习
机器学习的过程即为对训练数据计算损失函数,并且找到使损失函数值最小的参数,以交叉熵为例:
这里我们把损失函数式子扩大到n个数据。最后除以n得到“平均损失函数”
一般来说,我们无法将全部数据作为训练样本,而是取全部数据里的一批样本(称为mini-batch)
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)
这一步从mnist库里导出数据集。x_train形状为(60000, 784),因为训练数据集个数60000,输入数据784维(28 X 28),t_train形状为(60000, 10),训练数据集个数60000,监督数据one-hot标签量为10
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]
这一步利用np.random.choice方法在数据集里随机抽取10个数据,并赋值给x_batch和t_batch
实现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
这里我们创建的cross_entropy_error可以处理单个数据和批数据。对于单个数据先将其变换为一个大小为1的数组,在代入交叉熵公式计算。将所以数据交叉熵之和除以batch_size相当于得到了数据集的平均交叉熵
使用损失函数的意义:
损失函数的作用是使用一个连续函数来代表模型的识别精度。神经网络的学习依靠对损失函数求导(准确来说是偏导)找到函数极小值处的参数
导数:
导数在数学上定义为函数切线斜率(lim(h -> 0) f(x + h) - f(x) / h )。但是在程序里实现求导,我们不可能使用一个无限趋于0的h值。h值的选取不得过小,否则会受到round-off误差的影响较大。
为了弥补这一误差,我们可以计算函数在(x + h) 和(x - h)间的差分,称为中心差分。计算(x + h)和x的差分叫前向差分
注:使用微小差分求导被称为数值微分(numerical differentiation),基于数学式求导称为解析求导(analytic differentiation)
def numerical_diff(f, x):
h = 1e-4
return ((f(x + h) - f(x - h)) / (2 * h))
经测试,数值差分结果和真实导数0.2差距可以忽略不计
偏导数:
对带有多个自变量的函数求导时,需要指定关于其某一个变量求导,而将其他变量当做常数,这样得到的导数被称为偏导
如对于函数f(x1, x2) = x1 ^ 2 + x2 ^ 2, ∂/∂x1 = 2x1 ∂/∂x2 = 2x2
由关于f(x)所有变量偏导汇总起来的向量被称为梯度,实现代码如下:
def numerical_gradient(f, x):
h = 1e-4
grad = np.zeros_like(x)
for i in range(x.size):
temp = x[i]
x[i] = temp + h
fxh1 = f(x)
x[i] = temp - h
fxh2 = f(x)
grad[i] = (fxh1 - fxh2) / (2 * h)
x[i] = temp
return grad
注:np.zeros_like(x)会生成一个形状和x相同,所有值为0的数组
该函数遍历所有自变量x逐个求偏导,最后汇总起来的数组即为梯度