Python——numpy实现简单BP神经网络识别手写数字
程序员文章站
2022-05-21 23:53:37
...
直接上代码:
1.核心代码:神经网络以及BP算法
import numpy as np
from tqdm import trange # 替换range()可实现动态进度条,可忽略
def sigmoid(x): # **函数采用Sigmoid
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x): # Sigmoid的导数
return sigmoid(x) * (1 - sigmoid(x))
class NeuralNetwork: # 神经网络
def __init__(self, layers): # layers为神经元个数列表
self.activation = sigmoid # **函数
self.activation_deriv = sigmoid_derivative # **函数导数
self.weights = [] # 权重列表
self.bias = [] # 偏置列表
for i in range(1, len(layers)): # 正态分布初始化
self.weights.append(np.random.randn(layers[i-1], layers[i]))
self.bias.append(np.random.randn(layers[i]))
def fit(self, x, y, learning_rate=0.2, epochs=3): # 反向传播算法
x = np.atleast_2d(x)
n = len(y) # 样本数
p = max(n, epochs) # 样本过少时根据epochs减半学习率
y = np.array(y)
for k in trange(epochs * n): # 带进度条的训练过程
if (k+1) % p == 0:
learning_rate *= 0.5 # 每训练完一代样本减半学习率
a = [x[k % n]] # 保存各层**值的列表
# 正向传播开始
for lay in range(len(self.weights)):
a.append(self.activation(np.dot(a[lay], self.weights[lay]) + self.bias[lay]))
# 反向传播开始
label = np.zeros(a[-1].shape)
label[y[k % n]] = 1 # 根据类号生成标签
error = label - a[-1] # 误差值
deltas = [error * self.activation_deriv(a[-1])] # 保存各层误差值的列表
layer_num = len(a) - 2 # 导数第二层开始
for j in range(layer_num, 0, -1):
deltas.append(deltas[-1].dot(self.weights[j].T) * self.activation_deriv(a[j])) # 误差的反向传播
deltas.reverse()
for i in range(len(self.weights)): # 正向更新权值
layer = np.atleast_2d(a[i])
delta = np.atleast_2d(deltas[i])
self.weights[i] += learning_rate * layer.T.dot(delta)
self.bias[i] += learning_rate * deltas[i]
def predict(self, x): # 预测
a = np.array(x, dtype=np.float)
for lay in range(0, len(self.weights)): # 正向传播
a = self.activation(np.dot(a, self.weights[lay]) + self.bias[lay])
a = list(100 * a/sum(a)) # 改为百分比显示
i = a.index(max(a)) # 预测值
per = [] # 各类的置信程度
for num in a:
per.append(str(round(num, 2))+'%')
return i, per
2.训练
from NeuralNetwork import NeuralNetwork
import numpy as np
import pickle
import csv
def train():
file_name = 'data/train.csv' # 数据集为42000张带标签的28x28手写数字图像
y = []
x = []
y_t = []
x_t = []
with open(file_name, 'r') as f:
reader = csv.reader(f)
header_row = next(reader)
print(header_row)
for row in reader:
if np.random.random() < 0.8: # 大约80%的数据用于训练
y.append(int(row[0]))
x.append(list(map(int, row[1:])))
else:
y_t.append(int(row[0]))
x_t.append(list(map(int, row[1:])))
len_train = len(y)
len_test = len(y_t)
print('训练集大小%d,测试集大小%d' % (len_train, len_test))
x = np.array(x)
y = np.array(y)
nn = NeuralNetwork([784, 784, 10]) # 神经网络各层神经元个数
nn.fit(x, y)
file = open('NN.txt', 'wb')
pickle.dump(nn, file)
count = 0
for i in range(len_test):
p, _ = nn.predict(x_t[i])
if p == y_t[i]:
count += 1
print('模型识别正确率:', count/len_test)
def mini_test(): # 小型测试,验证神经网络能正常运行
x = [[0, 0], [0, 1], [1, 0], [1, 1]]
y = [0, 1, 2, 3]
nn = NeuralNetwork([2, 4, 16, 4])
nn.fit(x, y, epochs=10000)
for i in x:
print(nn.predict(i))
# mini_test()
train()
运行结果:
-
-
-
-
可以发现提高神经网络规模几乎无法提高正确率,但如果加入正则化的话应该能进一步提高正确率。
3.测试
import csv
import pickle
import numpy as np
from matplotlib import pyplot as plt
def diplay_test(): # 读取测试集,预测,画图
file_name = 'data/test.csv'
file = open('NN.txt', 'rb')
nn = pickle.load(file)
with open(file_name, 'r') as f:
reader = csv.reader(f)
header_row = next(reader)
print(header_row)
i = 0
for row in reader:
i += 1
img = np.array(row, dtype=np.uint8)
img = img.reshape(28, 28)
plt.imshow(img, cmap='gray')
pre, lst = nn.predict(row)
plt.title(str(pre), fontsize=24)
plt.axis('off')
plt.savefig('img/img' + str(i) + '.png')
diplay_test()
运行结果:
上一篇: python手写神经网络实现识别手写数字
下一篇: redis之集群搭建 windows环境
推荐阅读