BP网络简单实现
BP算法的简单实现
"""
BPnet 简易实现
约定输入数据维度为(N, input_size)
输出数据维度为(N, output_size)
"""
import pickle
import os, sys
import numpy as np
import matplotlib.pyplot as plt
首先创建一个父类Fun, 主要定义了
forward: 前向方法,需要子类重定义;
Momentum: 一个梯度下降方法;
step: 更新系数的方法;
zero_grad: 将记录的梯度清空;
load: 加载系数;
class Fun:
def __init__(self):
self.parameters = 0.
self.grad = 0.
self.cum_direction = 0.
def forward(self, *input):
raise NotImplementedError
def Momentum(self, grad, lr=0.01, momemtum=0.9):
self.cum_direction = lr * (grad +
momemtum* self.cum_direction)
self.parameters -= self.cum_direction
def step(self, lr=0.01, momemtum=0.9):
self.Momentum(self.grad, lr, momemtum)
return 1
def zero_grad(self):
self.grad = 0.
return 1
def load(self, parameters):
self.parameters = parameters
return 1
def __call__(self, *input):
return self.forward(*input)
Linear 全连接层
全连接层需要注意的是
\]
其中\(y^r\)表示第\(r\)层的输出,用\(W\)代替\(W^r\)表示系数,而\(b^r\)表示截距, \(\sigma^r\)是激活函数,注意他是entry-wise的.
另外需要一提的是,因为numpy的特性,我们令\(W\)的列为每一个神经元所衍生的权重.
令\(y_*\)表示在\(y\)末尾添加1,而\(W_*\)表示在在最后添加一行\(b^r\).
BP算法,需要我们记录俩个东西,首先:
\mathrm{d} y^r = & diag({\sigma^r}') W^T \mathrm{d} y^{r-1} \\
& + diag({\sigma^r}') \mathrm{d} W_*^T y_*^{r-1}.
\end{array}
\]
如果\(\mathrm{d} L = \delta^T \mathrm{d} y^r\), 那么
\mathrm{d} L & = \delta^T diag({\sigma^r}') W^T \mathrm{d} y^{r-1} \\
& + \mathrm{tr}(y_*^{r-1} \delta^T diag({\sigma^r}') \mathrm{d} W_*^T).
\end{array}
\]
所以在Linear层,我们需要利用\(y_*^{r-1}\)和\(\delta^T diag({\sigma^r}')\).
class Linear(Fun):
def __init__(self, input_size, output_size):
super(Linear, self).__init__()
assert isinstance(input_size, int) and input_size > 0, \
"Invalid input_size"
assert isinstance(output_size, int) and output_size > 0, "Invalid output_size"
self.shape = (input_size+1, output_size)
self.parameters = np.random.rand(input_size+1, output_size) * 2 - 1 # (input_size+1, output_size+1)
self.parameters *= 0.01
def forward(self, x):
"""
:param x: (N, input_size)
previeous: (N, input_size+1)
:return:
"""
self.previous = np.insert(x, x.shape[1], values=1., axis=1)
return self.previous @ self.parameters
def backward(self, back_grad):
"""
:param back_grad: (N, output_size)
:return:
"""
self.grad += self.previous.T @ back_grad #w_grad (input_size+1, output_size)
pre_grad = back_grad @ self.parameters.T # (N, input_size+1)
return pre_grad[:, :-1]
ReLu
ReLu激活函数:
\left \{
\begin{array}{ll}
x, & x > 0, \\
0, & x <= 0.
\end{array}
\right.
\]
显然其导数为:
\left \{
\begin{array}{ll}
1, & x > 0, \\
0, & x\le 0.
\end{array}
\right.
\]
class ReLu(Fun):
def __init__(self):
super(ReLu, self).__init__()
self.grad = 0.
def forward(self, x):
"""
:param x: (N, output_size)
:return:
"""
self.sign_matrix = np.ones_like(x) #记录输入的是否为正
for i in range(x.shape[0]):
for j in range(x.shape[1]):
if x[i, j] <= 0:
self.sign_matrix[i, j] = 0.
return x * self.sign_matrix
def backward(self, back_ward):
"""
:param back_ward: (N, output_size)
:return: (N, output_size)
"""
return back_ward * self.sign_matrix
class Sequence(Fun):
"""
Sequence: 用于统一处理Linear层和激活函数
"""
def __init__(self, *fns):
super(Sequence, self).__init__()
self.fns = fns
def forward(self, x):
for fn in self.fns:
x = fn(x)
return x
def backward(self, back_grad):
for fn in self.fns[::-1]:
back_grad = fn.backward(back_grad)
return 1
def step(self, lr=0.01, momemtum=0.9):
for fn in self.fns[::-1]:
fn.step(lr, momemtum)
return 1
def zero_grad(self):
for fn in self.fns:
fn.zero_grad()
return 1
def get_pra(self):
d = dict()
for i, fn in enumerate(self.fns):
s = "params" + str(i)
d.update({s:fn.parameters})
return d
def load(self, parameters):
for i, fn in enumerate(self.fns):
s = "params" + str(i)
fn.load(parameters[s])
return 1
MSELoss
MSE损失函数:
\]
所以关于\(\hat{y}\)的梯度就是:
\]
MSELoss 不适合用于分类
class MSELoss(Fun):
def __init__(self, output_size):
super(MSELoss, self).__init__()
self.prime_grad = None #最初的梯度
self.output_size = output_size
def forward(self, x, y):
y = y.reshape(-1, self.output_size)
self.prime_grad = (x - y) / x.size
return np.linalg.norm(x-y) / (2 * x.size)
def backward(self):
if self.prime_grad is None:
raise ValueError("forward first...")
else:
back_grad = self.prime_grad
self.prime_grad = None
return back_grad
交叉熵损失函数
\]
其导数分俩种,一种\(k=class\):
\]
另一种是一般的\(k \not = class\)
\]
class CrossEntropyLoss(Fun):
def __init__(self, output_size):
super(CrossEntropyLoss, self).__init__()
self.prime_grad = None
self.output_size = output_size
def forward(self, x, classes):
classes = classes.reshape(1, -1)[0]
pri_grad = np.exp(x)
row_sum = np.sum(pri_grad, axis=1) + 1e-5 #+1e-5是为了放置后面除以0发生
pri_grad /= row_sum
loss = np.sum(np.log(row_sum)) - \
np.sum(x[np.arange(len(x)), classes])
pri_grad[np.arange(len(x)), classes] -= 1.
self.prime_grad = pri_grad
return loss
def backward(self):
if self.prime_grad is None:
raise ValueError("forward first...")
else:
back_grad = self.prime_grad
self.prime_grad = None
return back_grad
Net 模块,用以具体定义网络
save_pra: 用以保存网络参数
load: 用以加载网络参数
class Net:
def __init__(self, input_size, output_size):
self.shape = (input_size, output_size)
self.dense = Sequence(
Linear(input_size, 256),
ReLu(),
Linear(256, output_size)
#ReLu()
)
def forward(self, input):
#前向
x = input.reshape(-1, self.shape[0])
x = self.dense(x)
return x
def backward(self, back_grad):
#后面
self.dense.backward(back_grad)
return 1
def step(self, lr=0.01, momemtum=0.9):
#更新参数
self.dense.step(lr, momemtum)
return 1
def zero_grad(self):
#清空参数
self.dense.zero_grad()
return 1
def __call__(self, input):
return self.forward(input)
def get_pra(self):
#获得整个网络的参数
return self.dense.get_pra()
def save_pra(self, filename):
#保存参数
fh = None
try:
fh = open(filename, "wb")
pickle.dump(self.get_pra(), fh, pickle.HIGHEST_PROTOCOL)
return True
except (EnvironmentError, pickle.PicklingError) as err:
print("{0}: export error:{1}".format(
os.path.basename(sys.argv[0]),
err
))
return False
finally:
if fh is not None:
fh.close()
def load(self,filename):
#加载参数
fh = None
try:
fh = open(filename, "rb")
self.dense.load(pickle.load(fh))
return True
except (EnvironmentError, pickle.UnpicklingError) as err:
print("{0}: import error: {1}".format(
os.path.basename(sys.argv[0]),
err
))
finally:
if fh is not None:
fh.close()
利用pytorch加载数据, dataloader就实现了
import torch
import torchvision
import torchvision.transforms as transforms
root = "C:/Users/pkavs/1jupiterdata/data"
trainset = torchvision.datasets.MNIST(root=root, train=True,
download=False,
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
))
train_loader = torch.utils.data.DataLoader(trainset, batch_size=10,
shuffle=False, num_workers=0)
net = Net(784, 10)
criterion = CrossEntropyLoss(10)
lr = 0.001
running_loss_sotre = []
for epoch in range(100):
running_loss = 0.0
for i, data in enumerate(train_loader):
if i > 30:
break
img, label = data #获得图片和对应的标签
img = img.numpy().reshape(-1, 784) #将img展成 N x 784的格式
label = label.numpy().reshape(-1, 1)
out = net(img) #获得输出
pre = np.argmax(out, axis=1) #预测数字
loss = criterion(out, label) #计算损失
back_grad = criterion.backward() #计算最初的反向梯度
net.zero_grad() #清空梯度
net.backward(back_grad) #backward
net.step(lr) #更新参数
running_loss += loss #计算总损失(没有取平均)
running_loss_sotre.append(running_loss)
print(running_loss)
712.1688029691725
705.6159884056726
696.3605306115187
681.9076985339979
659.0826257639627
624.2703850543031
576.5937881650244
521.3977792378652
466.47017818483283
417.5564483014553
377.58100395697363
346.1484168296081
321.0583297303844
299.88669109678415
281.73335412780835
266.4387592276925
254.90370768549863
251.07210302796904
353.18543794564295
345.2766226396521
322.9940783753454
328.6428077023008
270.3180403869843
254.9775608799049
230.08133829802637
213.69077670024637
201.2119790409399
192.50392038991563
181.69069506741027
178.2996933457074
199.9777923246425
183.87773045669059
170.99605668520786
169.35489179121672
162.42947830951198
150.1597596908225
143.98469229350468
141.00677525306008
127.59468686946381
128.40368339434258
201.57296802164916
122.20421766353607
182.8687570603292
224.37539134301915
343.40783375914333
250.66337879988703
144.2342106544594
205.95460344415
434.22102036193616
262.12760610791236
181.4400162958133
146.26211257970715
107.27112642333483
386.98537618442816
149.87855150968906
118.72956374544908
290.49626828881196
350.8852347420042
371.930405421759
104.02686372628847
101.15345754083307
208.05191021294857
217.52326838076044
123.5967585636549
82.73846477428404
91.29321266867215
98.7565490505614
218.3168084371386
167.60294451519016
138.7077575534406
91.50995861413155
102.04165505706565
129.74743597062437
94.99038470007001
98.36682096410206
100.15976484641901
91.84933076142981
168.72629650699594
88.56789923054023
573.3308046615387
695.0027992214693
139.41352718479337
84.40384142335002
83.6842770585352
89.64514463543952
77.73045621193755
72.65538892463755
99.59331140163596
87.4561441314255
62.48886272879916
69.1248402550191
70.21564469977797
56.14423053270053
51.8464048296232
56.87218546726085
107.25968791508906
160.84428492958492
113.05206798096374
65.12042358285711
102.60376565303726
running_loss_store = np.array(running_loss_sotre)
plt.plot(np.arange(len(running_loss_store)), running_loss_store)
plt.show()
testset = torchvision.datasets.MNIST(root=root, train=False,
download=False,
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
))
test_loader = torch.utils.data.DataLoader(testset, batch_size=1,
shuffle=False, num_workers=0)
count = 0
correct = 0
for i, data in enumerate(test_loader):
img, label = data #获得图片和对应的标签
img = img.numpy().reshape(-1, 784) #将img展成 N x 784的格式
label = label.numpy()
out = net(img) #获得输出
pre = np.argmax(out, axis=1) #预测数字
correct += pre == label
correct_rate = (correct / 10000)[0]
correct_rate
0.7558
BP网络简单实现的更多相关文章
- 我对BP网络的简单的理解
最近在学习tf的神经网络算法,十多年没有学习过数学了,本来高中数学的基础,已经彻底还给数学老师了.所以我把各种函数.公式和推导当做黑盒子来用,理解他们能做到什么效果,至于他们是如何做到的,暂时不去深究 ...
- 关于BP网络的一些总结
背景 前段时间,用过一些模型如vgg,lexnet,用于做监督学习训练,顺带深入的学习了一下相关模型的结构&原理,对于它的反向传播算法记忆比较深刻, 就自己的理解来描述一下BP网络. 关于BP ...
- 基于Opencv自带BP网络的车标简易识别
代码地址如下:http://www.demodashi.com/demo/12966.html 记得把这几点描述好咯:代码实现过程 + 项目文件结构截图 + 演示效果 1.准备工作 1.1 训练集和测 ...
- 基于Levenberg-Marquardt训练算法的BP网络Python实现
经过一个多月的努力,终于完成了BP网络,参考的资料为: 1.Training feed-forward networks with the Marquardt algorithm 2.The Leve ...
- 从头推导与实现 BP 网络
从头推导与实现 BP 网络 回归模型 目标 学习 \(y = 2x\) 模型 单隐层.单节点的 BP 神经网络 策略 Mean Square Error 均方误差 \[ MSE = \frac{1}{ ...
- BP网络中的反向传播
本文的主要参考:How the backpropagation algorithm works 下面是BP网络的参数结构示意图 首先定义第l层网络第j个神经元的输出(activation) 为了表示简 ...
- Docker小白到实战之Docker网络简单了解一下
前言 现在对于Docker容器的隔离性都有所了解了,但对容器IP地址的分配.容器间的访问等还是有点小疑问,如果容器的IP由于新启动导致变动,那又怎么才能保证原有业务不会被影响,这就和网络有挂钩了,接下 ...
- unity网络----简单基础
网络 TCP:与打电话类似,通知服务到位 UDP:与发短信类似,消息发出即可 IP和端口号是网络两大重要成员 端口号(Port)分为知名端口号[0-1024,不开放)和动态端口号[1024,10000 ...
- Matlab实现BP网络识别字母
训练样本空间 每个样本使用5×5的二值矩阵表征一个字母.一共10个字母类型,分别是N,I,L,H,T,C,E,F,Z,V.每个字母9个样本.共90个. N1=[1,0,0,0,1; 1,0,0,0 ...
随机推荐
- day32 HTML
day32 HTML 什么是前端 只要是跟用户打交道的界面都可以称之为前端 # eg:电脑界面, 手机界面,平板界面, 什么是后端? eg:python, java,php,go, 不跟用户直接打交道 ...
- LVM磁盘创建与扩容
以虚拟机为例 1.在虚拟机上添加新磁盘,点击虚拟机→设置->添加,最后如下图. 2.进入系统fdisk -l,查看当前磁盘信息 [root@master shell]# fdisk -l Dis ...
- Learning Spark中文版--第六章--Spark高级编程(1)
Introduction(介绍) 本章介绍了之前章节没有涵盖的高级Spark编程特性.我们介绍两种类型的共享变量:用来聚合信息的累加器和能有效分配较大值的广播变量.基于对RDD现有的transform ...
- Mybatis逆向工程简单介绍
转自:https://blog.csdn.net/yerenyuan_pku/article/details/71909325 什么是逆向工程 MyBatis的一个主要的特点就是需要程序员自己编写sq ...
- HDFS【Namenode、SecondaryNamenode、Datanode】
目录 一. NameNode和SecondaryNameNode 1.NN和2NN 工作机制 2. NN和2NN中的fsimage.edits分析 3.checkpoint设置 4.namenode故 ...
- 重磅丨腾讯云开源业界首个 etcd 一站式治理平台 Kstone
Kstone 开源 在 CNCF 云原生基金会举办的2021年12月9日 KubeCon China大会上,腾讯云容器 TKE 团队发布了 Kstone etcd 治理平台开源项目. Kstone ...
- jenkins之代码回滚
#:通过传参数方式 #:保存后就会看到这样 #;:我们在jenkins服务器写一个脚本 root@ubuntu:~# mkdir /root/script/web1 -pv mkdir: create ...
- 第一章-Flink介绍-《Fink原理、实战与性能优化》读书笔记
Flink介绍-<Fink原理.实战与性能优化>读书笔记 1.1 Apache Flink是什么? 在当代数据量激增的时代,各种业务场景都有大量的业务数据产生,对于这些不断产生的数据应该如 ...
- 【JAVA今法修真】 第一章 今法有万象 百家欲争鸣
大家好,我是南橘,因为这段时间很忙,忙着家里的事情,忙着工作的事情,忙着考试的事情,很多时候没有那么多经历去写新的东西,同时,也是看了网上一些比较新颖的文章输出方式,自己也就在想,我是不是也可以这样写 ...
- Unity实现“笼中窥梦”的渲染效果
效果 思路 5个面用5个RenderTexture来接受5个摄像机分别获取的小场景图像: RenderTexture就当成屏幕来理解,MainCamera是把画面显示在屏幕上,屏幕就是最大的Rende ...