# coding: utf-8
from collections import OrderedDict
try:
import urllib.request
except ImportError:
raise ImportError('You should use Python 3.x')
import numpy as np
import mnist
def softmax(x):
if x.ndim == 2:
x = x.T
x = x-np.max(x, axis=0)
y = np.exp(x) / np.sum(np.exp(x), axis=0)
return y.T
x = x-np.max(x)
return np.exp(x) / np.sum(np.exp(x))
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
# 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
if t.size == y.size:
t = t.argmax(axis=1)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
out = x * y
return out
def backward(self, dout):
dx = dout * self.y # 翻转x和y
dy = dout * self.x
return dx, dy
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
# 获取x数组中小于0的元素的索引
self.mask = (x <= 0)
out = x.copy() # out变量表示要正向传播给下一层的数据,即上图中的y
# ##请补充代码将x数组中小于0的元素赋值为0
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
# ##请补充代码完成Relu层的反向传播
dx = dout
return dx
class Linear:
def __init__(self, W, b):
self.W = W # 权重参数
self.b = b # 偏置参数
self.x = None # 用于保存输入数据
# 定义成员变量用于保存权重和偏置参数的梯度
self.dW = None
self.db = None
# 全连接层的前向传播
def forward(self, x):
# 保存输入数据到成员变量用于backward中的计算
self.x = x
# ##请补充代码求全连接层的前向传播的输出保存到变量out中
out = np.dot(self.x,self.W) + self.b
return out
# 全连接层的反向传播
def backward(self, dout):
# ##请同学补充代码完成求取dx,dw,db,dw,db保存到成员变量self.dW,self.db中
self.dW = np.dot(self.x.T, dout)
dx = np.dot(dout, self.W.T)
db = np.sum(dout, axis=0)
return dx
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None # softmax的输出
self.t = None # 监督数据
# SoftmaxWithLoss层的前向传播函数
def forward(self, x, t):
self.t = t
self.y = softmax(x) # ## 请补充代码获取预测值
self.loss = cross_entropy_error(self.y, self.t) # ## 请补充代码获取模型损失
return self.loss
# SoftmaxWithLoss层的反向传播函数
def backward(self, dout=1):
# ##请补充代码完成求取SoftmaxWithLoss层的反向传播的输出
# 注意:反向传播时将要传播的值除以批的大小,传递给前面层的是单个数据的误差
batch_size = self.t.shape[0]
dx = (self.y - self.t) / batch_size
return dx
class TwoLayerNet:
# 模型初始化
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
# 初始化权重
self.params = {'W1': weight_init_std * np.random.randn(input_size, hidden_size), 'b1': np.zeros(hidden_size),
'W2': weight_init_std * np.random.randn(hidden_size, output_size), 'b2': np.zeros(output_size)}
# 获取第一层权重和偏置
# 获取第二层权重和偏置
# 生成层
# 将神经网络的层保存为有序字典OrderedDict
self.layers = OrderedDict()
# 添加第一个全连接层到有序字典中
self.layers['Linear1'] = Linear(self.params['W1'], self.params['b1'])
# ##请补充代码添加激活函数层和第二个全连接层到有序字典中
self.layers['Linear2'] = Linear(self.params['W2'], self.params['b2'])
# 将SoftmaxWithLoss类实例化为self.lastLayer
self.lastLayer = SoftmaxWithLoss()
# 通过前向传播获取预测值
def predict(self, x):
# 遍历有序字典
for layer in self.layers.values():
x = layer.forward(x) # ## 请补充代码完成神经网络层的前向传播
return x
# x:输入数据, t:监督数据
def loss(self, x, t):
# 获取预测值
y = self.predict(x)
# 返回损失
return self.lastLayer.forward(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 gradient(self, x, t):
# forward
self.loss(x, t)
# backward
dout = 1
# ##请补充代码求最后SoftmaxWithLoss层的反向传播输出
# 从后往前遍历有序字典
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
# ##请补充代码获取正在被遍历的层的反向传播输出
dout = layer.backward(dout)
# 设定
grads = {}
# 获取第一层网络参数的梯度
grads['W1'], grads['b1'] = self.layers['Linear1'].dW, self.layers['Linear1'].db
# ##请补充代码获取第二层网络参数的梯度
grads['W2'], grads['b2'] = self.layers['Linear2'].dW, self.layers['Linear2'].db
return grads
# 主函数
if __name__ == '__main__':
mnist.init_mnist()
(x_train, t_train), (x_test, t_test) = mnist.load_mnist(flatten=True, normalize=True, one_hot_label=True)
# print(t_train[0])
print(t_test[0])
# ##请补充实例化TwoLayerNet类创建network对象的代码
network = TwoLayerNet(input_size=784, hidden_size=100, output_size=10) # 在这里调整参数
# 定义训练循环迭代次数
iters_num = 1000
# 获取训练数据规模
train_size = x_train.shape[0]
# 定义训练批次大小
batch_size = 100
# 定义学习率
learning_rate = 0.1
# 创建记录模型训练损失值的列表
train_loss_list = []
# 创建记录模型在训练数据集上预测精度的列表
train_acc_list = []
# 创建记录模型在测试数据集上预测精度的列表
test_acc_list = []
# 计算一个epoch所需的训练迭代次数(一个epoch定义为所有训练数据都遍历过一次所需的迭代次数)
iter_per_epoch = max(train_size / batch_size, 1)
# print(train_size)
# ##请补充创建训练循环的代码
for i in range(int(iters_num)):
for j in range(int(iter_per_epoch)):
# 在每次训练迭代内部选择一个批次的数据
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
# ##请补充计算梯度的代码
grad = network.gradient(x_batch, t_batch)
# ##请补充更新模型参数的代码
for key in ('W1', 'b1', 'W2', 'b2'): # 梯度更新
network.params[key] -= learning_rate * grad[key]
# ##请补充向train_loss_list列表添加本轮迭代的模型损失值的代码
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
# ##请补充向train_acc_list列表添加当前模型对于训练集预测精度的代码
train_acc = network.accuracy(x_train, t_train)
train_acc_list.append(train_acc)
# ##请补充向test_acc_list列表添加当前模型对于测试集预测精度的代码
test_acc = network.accuracy(x_test, t_test)
test_acc_list.append(test_acc)
# 输出一个epoch完成后模型分别在训练集和测试集上的预测精度以及损失值
print("iteration:{} ,train acc:{}, test acc:{} ,loss:{}|".format(i, train_acc, test_acc, loss))
python实现两层神经网络识别手写数字体(2)
最新推荐文章于 2023-02-08 20:33:30 发布