手写数字识别神经网络的实现(1)

描述

对MNIST数据集使用2层神经网络(1层隐藏层)实现。

1、2层神经网络的类

将2层神经网络实现为一个TwoLayerNet的类:

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
    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        grads = {}
        batch_num = x.shape[0]
        # forward
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)
        da1 = np.dot(dy, W2.T)
        dz1 = sigmoid_grad(a1) * da1
        grads['W1'] = np.dot(x.T, dz1)
        grads['b1'] = np.sum(dz1, axis=0
        return grads

定义了初始化函数__init__,其中params变量W1,2分别是第1,2层的权重,b1,2分别是1,2层的偏置。初始化函数中包含输入层、隐藏层和输出层的神经元数,W1是随机生成的大小为输入层神经元数量(m)*隐藏层神经元数量的矩阵(n),b1是隐藏层神经元数量(n)的全0一维数组,W2是随机生成的大小为隐藏层神经元数量(n)*输出层神经元数量(k)的矩阵,b2是输出层神经元数量(k)的全0一维数组。

数据集

定义了predict函数,表明网络结构,输入x权重+偏置进行激活得到隐藏层z1,z1权重+偏置进行激活得到输出y。

定义损失函数loss,使用的是交叉熵误差。

定义精确度函数accuracy,计算输出与标签一致(即正确识别)的概率。

定义了数值微分求权重梯度的函数numerical_gradient,返回梯度值,之前介绍过。

定义了另一种求权重梯度的方法,后面介绍。

上述代码中涉及的两个变量params和grads是字典型实例变量,前者保存了神经网络中全部的参数,后者保存了各个参数的梯度。

2、Mini-batch的实现

定义了2层神经网络的类相当于对神经网络进行封装形成一个模块,需要的时候设计参数即可直接调用。现在对MNIST数据集进行学习,使用这个封装好的2层神经网络。

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
iters_num = 10000  # 适当设定循环的次数
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1


# 获取mini-batch
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]
# 计算梯度
    #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)

读入数据后,调用了2层神经网络的类,设置好函数完成调用,其中输入神经元为784个(图片像素为28*28),隐藏层50个神经元,输出层10个神经元(对应0-9的分类)。

设置超参数,包括学习的次数iters_num,训练数据的数量train_size,一次随机选取的训练数据的个数batch_size,学习率learning_rate。

获取mini-batch,计算mini-batch中的梯度,有两种计算方法数值微分和后面会介绍的高效方法,任意一种都行。通过grad函数更新参数W1,W2,b1,b2,使参数向梯度方向即使损失函数减小的方向移动,保存每一次更新的损失函数的值,后续绘图可以观察损失函数的变化。损失函数在不断减小,说明神经网络确实在学习。

数据集

3、基于测试数据的评价

损失函数的值是某一个mini-batch的损失函数的值,不能说明在其他数据集上也能有同等程度的表现,神经网络的学习必须确认是否能识别训练数据之外的其他数据,即确认是否会发生过拟合。

下面的代码会对训练和测试数据的每一个epoch记录识别精度。一个epoch表示学习中所有数据均被使用过一次的更新次数,训练数据有60000个,mini-batch是100个,那么重复随机梯度下降法600次,所有训练数据就都被看过了,因此600次是一个epoch。

train_acc_list = []
test_acc_list = []
iter_per_epoch = max(train_size / batch_size, 1)


if i % iter_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))
# 绘制图形
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

每经历一个epoch就会对所有训练和测试数据计算精度,然后绘制训练和测试精确度的变化,训练和测试的精度在慢慢提高且基本重叠,说明没有过拟合。

数据集

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分