神经网络(从手撕到工具包)
在学习神经网络之前,我认为弄懂逻辑回归是很有必要的(对于逻辑回归比较陌生的可以看看该作者分类那篇文章(●'◡'●)),因为逻辑回归就相当于神经网络的一个“神经元”,而整个神经网络就是由这些“神经元”构成的。我们知道每个"神经元"的作用是完成一个二分类的任务,那么将这些“神经元”链接起来自然而然是为了完成一个多分类的任务,这也就是神经网络的作用了。为了方便后面分享内容的理解,我们会先从一个神经元模型讲起,再到二层前馈神经网络,再和大家分享误差反向传播内容,最后与大家分享keras实现神经网络模型。希望能对大家有所帮助,如有错误,还望大家不吝指出。
目录
一、神经元模型
二、二层前馈神经网络
三、误差反向传播
四、使用Keras实现神经网络模型
五、文章代码合集
一、神经元模型
在神经元模型中有输入值x1,x2......xd(可以是正或负的实数值),也有权重w1,w2.......wd(这里也可以叫它们为神经元的突触传递强度),也有偏置值b,之后我们就会得到输入总和(我们也管输入总和为膜电位),最后我们通过激活函数sigmoid函数得到整个神经元的输出值a,a是0和1之间的连续值。正如我上述一样,一个逻辑回归模型就相当于一个神经元。并且与逻辑回归模型一样,神经元模型也是用平均交叉熵误差作为损失函数:
接下来我们将神经元拼接组合就得到了我们的神经网络。
二、二层前馈神经网络
如上图所示的是一个二层前馈神经网络。有人把输入层也包含在内,认为它是三层的。考虑到权重参数只有W和V这两层,把它当作二层的做法似乎更为妥当。
这个神经网络接受二维的输入,然后用三个神经元进行输出。我们要对网络进行训练,使得每个输出神经元的输出值能够代表数据属于每个类别的概率。b、a是输入总和,是输入总和应用sigmoid函数后,得到中间层神经元的输出,但有一点需要注意,今后我们用的不一定是sigmoid函数,又可能使用别的函数,我们称这类函数为激活函数。这个中间层的输出决定了输出层的神经元的活动。输出层中的是输入总和运用softmax函数后得到的输出。
使用了Softmax函数易知,输出的和等于1,因此就可以把这些值解释为概率了。这样我们就完成了网络功能的定义。上面的是永远为1的虚拟神经元,这样做是为了让求和表达式中包含偏置项。
接下来,我们用代码来实现上面的神经网络。
我们先生成一些需要用到的数据。
import numpy as np
import matplotlib.pyplot as pltnp.random.seed(seed=1) #固定随机数
N = 200 #数据个数
K = 3 #分类个数
T = np.zeros((N,3),dtype=np.uint8)#dtype=np.uint8有利于加快运算速度
X = np.zeros((N,2))#生成二维数据X_range0 = [-3,3]
X_range1 = [-3,3]
#有利于后面的画图Mu = np.array([[-.5,-.5],[.5,1.0],[1,-.5]]) #分类的中心
Sig = np.array([[.7,.7],[.8,.3],[.3,.8]]) #分类的离散值
Pi = np.array([0.4,0.8,1]) #各分布所占的比例for n in range(N):wk = np.random.rand()for k in range(K):if wk < Pi[k]:T[n, k] = 1breakfor k in range(2):X[n,k] = np.random.randn() * Sig[T[n,:] == 1,k] + Mu[T[n,:] == 1,k]
将数据分为训练数据X_train,T_train和测试数据X_test,T_test。为了验证是否发生过拟合。
TrainingRatio = 0.5
X_n_training = int(N * TrainingRatio)
X_train = X[:X_n_training,:]
X_test = X[X_n_training:,:]
T_train = T[:X_n_training,:]
T_test = T[X_n_training:,:]
然后我们将训练集和测试集可视化
def Show_data(x,t):wk,n = t.shapefor i in range(n):plt.plot(x[t[:,i] == 1,0],x[t[:,i] == 1,1],marker='o',linestyle='none',alpha=0.8)plt.grid(True)plt.subplot(1,2,1)
Show_data(X_train,T_train)
plt.xlim(X_range0)
plt.ylim(X_range1)
plt.title('Training Data')plt.subplot(1,2,2)
Show_data(X_test,T_test)
plt.xlim(X_range0)
plt.ylim(X_range1)
plt.title('Test Data')
plt.show()
我们把二层前馈神经网络的函数命名为FNN。FNN接收传给网络的输入x,输出y。输入x是D维向量,输出y是K维向量,目前我们探讨的是D=2,K=3的情况。我们希望网络的函数FNN能够一次处理N(这里的N=100)个数据,因此将x作为数据量N*D维的矩阵,将y作为数据量N*K维的矩阵。向量y[n,0]、y[n,1]、y[n,2]表示x[n,:]属于类别0、1、2的概率。这里要注意的是,必须保证概率的和为1,好在我们有softmax函数,做到这点还是没有难度。另外,为了使中间层的数量和输出的维度能够自由修改,我们把二者分别命名为M、K,并将其作为网络的参数(通过输入数据x就能知道N和D的值,所以N和D不在参数里)。
影响网络行为的重要参数——中间层的权重W和输出层的权重V也要传给网络。W是M*(D+1)矩阵(由于还有偏置输入的权重,所以为D+1),V是K*(M+1)矩阵(这里也需要考虑中间层的偏置神经元,所以为M+1).。
W和V的信息通过汇总了W和V信息的向量wv传递。比如,中间层的神经元数据M=2,输出维度K=3,那么向网络传递的权重为:
W=[[0,1,2],[3,4,5]] M*(D+1)=2*3
V=[[6,7,8],[9,10,11],[12,13,14]] K*(M+1)=3*3
这时的wv为:
wv = np.array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14])
wv的长度是M*(D+1)+K*(M+1)。将这些参数汇为一个,后面进行最优化的程序就会更容易。
FNN的输出是与N个数据想应的输出y(N*K)矩阵,中间层的输出z,输出层和中间层的输入总和为a,b。
def Sigmoid(x):y = 1 / (1 + np.exp(-x))return ydef FNN(wv,M,K,x):N,D = x.shape #输入维度w = wv[:M * (D + 1)] #传递到中间层神经元时用到的权重w = w.reshape(M,(D + 1)) v = wv[M * (D + 1):] #传递到输出层神经元时用到的权重v = v.reshape((K,M+1))b = np.zeros((N,M + 1)) #中间层神经元的输入总和z = np.zeros((N,M+1)) #中间层神经元的输出a = np.zeros((N,K)) #输出层神经元的输入总和y = np.zeros((N,K)) #输出层神经元的输出for n in range(N):#中间层的计算for m in range(M):b[n,m] = np.dot(w[m,:],np.r_[x[n,:],1]) #np.r_[x[n,:],1]将1的虚拟输入作为x的第三个元素加在后面,np.r_[A,B]是横向链接矩阵的命令z[n,m] = Sigmoid(b[n,m])#输出层的计算z[n,M] = 1 #虚拟神经元,求偏置wkz = 0for k in range(K):a[n,k] = np.dot(v[k,:],z[n,:])wkz = wkz + np.exp(a[n,k]) #softmax函数for k in range(K):y[n,k] = np.exp(a[n,k]) / wkzreturn y,a,z,b
最后测试程序的效果,设M=2,K=3,WV的长度为2*3+3*3=15的权重向量。WV的元素全部为1,输入只使用X_traind两个数据,显示的输出从上到下依次是y,a,z,b的值。
WV=np.ones(15)
M=2
K=3
FNN(WV,M,K,X_train[:2,:]
这就是我们神经网络的前向传播,得出来的结果肯定不是我们想要的,参数肯定不是理想的,而要得到理想的参数,就要用到神经网络里面非常重要的误差反向传播。
三、误差反向传播
误差反向传播使用网络输出中的误差(输出与监督信号的差)信息,从输出层权重V到中间层权重W,即与输入反方向地更新权重,这也是这个方法名称的由来。
首先,我们知道每一个输出层的输出后的误差函数是平均交叉熵,那么我们根据平均交叉熵使用梯度法,自然而然就更新了权重。
平均交叉熵:
N是样本数量,K是样本的类别个数。
那要用到梯度法,那我们肯定就离不开平均交叉熵的导数了。
我们先来求离输出层最近的参数V的导数。
我们知道,所以有:
由于过程复杂,我们直接给出结果(对过程有兴趣的可以私信我)。
所以由梯度法就有:
同理离输出层次远的参数W更新如下:
其中
接下来就是代码实现部分了。
首先我们肯定得将平均交叉熵误差函数写出来da
def CE_FNN(wv,M,K,x,t):N,D = x.shapey,a,z,b = FNN(wv,M,K,x)ce = -np.dot(t.reshape(-1),np.log(y.reshape(-1))) / Nreturn ce
接着我们将导数写出来
def dCE_FNN(wv,M,K,x,t):N,D = x.shape#把wv恢复为w和vw = wv[:M * (D + 1)]w = w.reshape(M,(D + 1))v = wv[M * (D + 1):]v = v.reshape((K,M + 1))#输入x,得到yy,a,z,b = FNN(wv,M,K,x)dwv = np.zeros_like(wv)dw = np.zeros((M,D + 1))dv = np.zeros((K,M + 1))delta1 = np.zeros(M) #第一层的误差delta2 = np.zeros(K) #第二层的误差for n in range(N):#求输出层的误差for k in range(K):delta2[k] = (y[n,k] - t[n,k])#求中间层的误差for j in range(M):delta1[j] = z[n,j] * (1 - z[n,j]) * np.dot(v[:,j],delta2)#求v的梯度dvfor k in range(K):dv[k,:] = dv[k,:] + delta2[k] * z[n,:] / N#求w的梯度dwfor j in range(M):dw[j,:] = dw[j,:] + delta1[j] * np.r_[x[n,:],1] / N#汇总dw和dv的信息,形成dwvdwv = np.c_[dw.reshape((1,M * (D + 1))),dv.reshape((1,K * (M + 1)))]dwv = dwv.reshape(-1)return dwv
下一步,我们用梯度法更新参数
def Fit_FNN(wv_init,M,K,x_train,t_train,x_test,t_test,n,alpha):wv = wv_init.copy()err_train = np.zeros(n)err_test = np.zeros(n)wv_hist = np.zeros((n,len(wv_init)))for i in range(n):wv = wv - alpha * dCE_FNN(wv,M,K,x_train,t_train)err_train[i] = CE_FNN(wv,M,K,x_train,t_train)err_test[i] = CE_FNN(wv,M,K,x_test,t_test)wv_hist[i,:] = wvreturn wv,wv_hist,err_train,err_test
接下来,让我们通过作图来看看效果。
M = 2
K = 3
np.random.seed(1)
WV_init = np.random.normal(0,0.01,M*3+K*(M+1))
N_step = 1000
alpha = 0.5
WV,WV_hist,Err_train,Err_test = Fit_FNN(WV_init,M,K,X_train,T_train,X_test,T_test,N_step,alpha)#显示权重随时间的变化
plt.subplot(1,2,1)
plt.plot(WV_hist[:,:M*3],'r')
plt.plot(WV_hist[:,M*3:],'b')
#显示决策边界
plt.subplot(1,2,2)
Show_data(X_test,T_test)
xb = np.linspace(-2,2,50)
x11 = -(WV[0]*xb + WV[2])/ WV[1]
x12 = -(WV[3]*xb + WV[5])/ WV[4]
x13 = -(WV[6]*xb + WV[8])/ WV[7]
plt.plot(xb,x11,'g')
plt.plot(xb,x12,'r')
plt.plot(xb,x13,'b')
plt.show()
神经网络(从手撕到工具包)相关推荐
- 手撕图机器学习,图神经网络
手撕图机器学习,图神经网络 写在前面 & 配套链接(访者必读) 图的基本表示 图的基本参数 图的类别 节点连接数(Node degree) 图的矩阵表示(邻接矩阵) 连接列表和邻接列表 其他图 ...
- 手撕Resnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
Alexnet网络详解代码:手撕Alexnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑.跑的代码有问题的可以在评论区指出,看到了会回复.训练代 ...
- 手撕神经网络(2)—— 将基本组件搭建成躯干
前言 之前写了篇文章<手撕神经网络(1)--神经网络的基本组件>介绍了手撕神经网络的各个层.但是疫情和学习原因,导致其后续一直没有机会写,这两天难得空闲,终于有机会继续分享. 概要 在文章 ...
- 手撕Alexnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
Alexnet网络详解代码:手撕Alexnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑.跑的代码有问题的可以在评论区指出,看到了会回复.训练代 ...
- 从零构建神经网络-不使用框架(纯纯手撕)
一.从零构建神经网络-不使用框架(纯手撕) 神经网络从0开始 动手从零开始实现一个神经网络,不使用框架,一步一步推理应该可以加深一下对神经网络的理解. 网络结构为三层全连接网络,节点个数依次为784. ...
- 深度学习之手撕深度神经网络DNN代码(基于numpy)
声明 1)本文仅供学术交流,非商用.所以每一部分具体的参考资料并没有详细对应.如果某部分不小心侵犯了大家的利益,还望海涵,并联系博主删除. 2)博主才疏学浅,文中如有不当之处,请各位指出,共同进步,谢 ...
- 手撕VGG卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑。跑的代码有问题的可以在评论区指出,看到了会回复。训练代码和预测代码均有。
Alexnet网络详解代码:手撕Alexnet卷积神经网络-pytorch-详细注释版(可以直接替换自己数据集)-直接放置自己的数据集就能直接跑.跑的代码有问题的可以在评论区指出,看到了会回复.训练代 ...
- Interview:算法岗位面试—11.06早上上海某智能驾驶科技公司(创业)笔试+面试之手撕代码、项目考察、比赛考察、图像算法的考察等
Interview:算法岗位面试-11.06早上上海某智能驾驶科技公司(创业)笔试+面试之手撕代码.项目考察.比赛考察.图像算法的考察等 导读:该公司是在同济某次大型招聘会上投的,当时和HR聊了半个多 ...
- 手撕 CNN 经典网络之 VGGNet(理论篇)
2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司一起研发了新的卷积神经网络,并命名为VGGNet.VGGNet是比AlexNet更深的 ...
最新文章
- 编译-链接-运行-环境配置各种error汇总
- 软件破解系列之OD中断方法
- 驱动操作硬件的根本操作
- 好物推荐 | 轻薄神器,妈妈再也不用担心我的颈椎了
- [CQOI 2006]线段树之简单题
- 详解Oracle DELETE和TRUNCATE 的区别
- vue中引入外部文件js、css、img的方法
- [Java] 蓝桥杯ADV-182 算法提高 前10名
- 计算机主机结构讲解,电脑内部结构图和讲解
- WIN10虚拟机安装教程
- KY RD9700_USB网卡驱动
- Liunx使用apt安装本地deb软件包
- php后缀加swp,当编辑文件出现swp的时候如何处理
- 一分钟让你知道Hadoop是什么
- 机器学习-新闻分类案例
- 爱思助手 for Mac(苹果手机助手)中文版
- 白平衡(Color Constancy,无监督AWB):CVPR2019论文解析
- linux iscsi软件,Redhat Linux 配置 iSCSI 连接存储
- 没有处理程序要使用以下任何注释:javax.persistence.PersistenceContext
- 基于PHP的租赁商城系统(包括数据库和后台)