有没有大佬帮我看看这个MLP的代码为什么起不到训练的效果?npy文件使用的是Minist官方的文件。

import numpy as np


# 将输出压缩成激活值得函数
def sigmoid(z):
return 1 / (1 + np.exp(-z))


# sigmoid 的导函数
def sigmoid_derivative(z):
return sigmoid(z) * (1 - sigmoid(z))


class Network(object):
def __init__(self, sizes):
self.num_layers = len(sizes) # 网络层数(包括并不存在的输入层)
self.sizes = sizes[1:] # 各层网络节点数(不包含不存在的输入层)
# 第一层为输入层,没有权重和偏置
# self.biases[i][j] 为第i+2层、第j+1个节点的偏置
self.biases = [np.random.rand(y,1) for y in sizes[1:]]
# self.weights[i][j] 为第i+2层、第j+1个节点的权重向量,该向量维度为前一层的输出向量维度
self.weights = [np.random.rand(x,y) for x,y in zip(sizes[1:],sizes[:-1])]

def SGD(self, training_data, epochs, mini_batch_size, eta):
"""
随机梯度下降法进行训练
:param training_data: 一个(x,y)元组的列表,x表示输入,y表示期望输出
:param epochs: 训练周期
:param mini_batch_size: 小批量训练数据大小
:param eta: 学习速率
"""
n = len(training_data) # 测试数据样本量
for j in range(epochs):
# 打乱训练数据
np.random.shuffle(training_data)
# 将训练数据分割成多个指定大小的小批量数据
mini_batches = [training_data[k:k + mini_batch_size] for k in range(0, n, mini_batch_size)]
for mini_batch in mini_batches:
# 小批量数据学习
self.update_mini_batch(mini_batch, eta)
# 打印进展
len_test = len(training_data)
num = self.evaluate(training_data)
print("周期 {} 完成!正确率 {}/{} {:.2f}%".format(j+1,num,len_test,num/len_test*100))

def update_mini_batch(self, mini_batch, eta):
"""
根据小批量数据计算梯度并更新权重和偏置
:param mini_batch: 小批量数据,包含若干个 (x,y) 组合,x为单个输入,y为期望输出
:param eta: 学习速率,每次更新会乘这个系数,值越大,则更改的越大
"""
# 构造权重和偏置的梯度容器,填充为 0
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
# 遍历样本中所有数据
for x,y in mini_batch:
# 计算得到权重和偏置的梯度
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
# 将所有样本中的梯度求和
nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
# 利用梯度更新权重和偏置
self.weights = [w - (eta / len(mini_batch)) * nw for w, nw in zip(self.weights, nabla_w)]
self.biases = [b - (eta / len(mini_batch)) * nb for b, nb in zip(self.biases, nabla_b)]

def backprop(self, x, y):
"""
反向传播计算梯度
:param x: 单个输入
:param y: 期望输出
:return: 梯度
"""
# 构造一个存储权重和偏置梯度的容器,填充为 0
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
# 保存各层的净输入向量
zs = []
# 保存各层激活向量
activations = [x]

# 这里进行前向传播的过程,将各层的净输入向量保存在zs中,激活向量(作为下一层的输入)保存在activations中
for b, w in zip(self.biases, self.weights):
z = np.dot(w, x) + b
activation = sigmoid(z)
x = activation
zs.append(z)
activations.append(activation)

# 下面是反向传播核心代码
"""
神经网络的代价函数为二次代价,即取期望输出与实际输出之差的平方的一半
"""
# 首先计算输出层的梯度
activations[-1][y] -= 1
nabla_b[-1] = activations[-1] * sigmoid_derivative(zs[-1])
nabla_w[-1] = np.dot(nabla_b[-1], activations[-2].reshape(1, len(activations[-2])))
# 下面从倒数第二层开始反向逐层计算剩余层梯度
for l in range(2, self.num_layers):
# 用-l+1层的误差计算-l层的误差
nabla_b[-l] = np.dot(self.weights[-l+1].transpose(), nabla_b[-l+1]) * sigmoid_derivative(zs[-l])
nabla_w[-l] = np.dot(nabla_b[-l], activations[-l-1].reshape(1, len(activations[-l-1])))
return nabla_b, nabla_w

def evaluate(self, test_data):
"""
用于评估当前神经网络,返回测试数据中判断正确的样本数
:param test_data: 用于评估的数据集
:return: 数据集中判断正确的样本数量
"""
test_result = [(np.argmax(self.feedforward(x)), y) for x, y in test_data]
return sum(int(x == y) for x, y in test_result)

def feedforward(self, a):
"""
前向传播计算结果
:param a: 用于预测的向量
:return: 输出向量
"""
for b, w in zip(self.biases, self.weights): # 遍历所有层
a = sigmoid(np.dot(w, a) + b) # 计算当前层的激活数值向量,隐式的包含一个循环:遍历当前层的节点
return a


if __name__ == "__main__":
# 加载训练数据
training_images = np.load("train-images.npy", allow_pickle=True)
training_labels = np.load("train-labels.npy", allow_pickle=True)
training_data = [(np.reshape(x,(28*28,1)),y) for x,y in zip(training_images, training_labels)]
# 加载测试数据
test_images = np.load("t10k-images.npy", allow_pickle=True)
test_labels = np.load("t10k-labels.npy", allow_pickle=True)
test_data = [(np.reshape(x,(28*28,1)),y) for x,y in zip(test_images,test_labels)]
# 创建神经网络
net = Network([28*28, 16, 16, 10]) # 输入层28*28 输出层10 固定,其他层可以任意
# 进行随机梯度下降训练
net.SGD(training_data, 100, 10, 1) # 参数依次为:训练数据集、训练周期、小批量数据大小、学习速率
# 加载测试数据对网络效果进行验证
len_test = len(test_data) # 获取测试数据样本量
num = net.evaluate(test_data) # 获取正确样本量
# 打印结果
print("最终正确率:{}/{} {:.2f}%".format(num, len_test, num / len_test * 100))

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值