1.前言
如图是一个神经网络的简化结构,隐藏层每一个节点都是一个神经元,比如下图的a1,a2,a3。机器学习中的神经网络是模拟生物神经网络结构,每个神经元与其他神经元相连,当神经元的电位超过了一个‘阈值’,那么它就会被激活,即‘兴奋’起来。
机器学习的神经网络是怎么模拟大脑神经元‘兴奋’这个概念的?结合a1这个神经元做简要的分析:
首先对于a1的定义,我们给出如下的公式..
剖析神经元兴奋的定义“当神经元的电位超过了一个‘阈值’,那么它就会被激活”,对应与上面公式,怎么使得上诉公式达到神经元的效果?
当达到某一阀值时候
,即兴奋状态,反之当未达到阀值时
,即未兴奋状态。
所以理想情况下这个函数应该如下图的红色,因为起特性就是大于零被激活,小于零未激活,所以称之为激活函数。 :
但是红色函数存在不连续,不光滑等不太好的性质...所以在实际情况下,我们一般会使用sigmod函数来作为激活函数(即图中蓝线):
其实我们可以发现,对于神经网络来说,每一个神经元我们都可以理解成一个逻辑回归(LR)模型。这样去理解它或许对你之后的理解有所帮助。对于逻辑回归算法模型的推到及编程可以查看我之前的一篇文章:点击此处跳转
2.前向传播的神经网络运算
由图,输入层的输入矩阵()可以表示为: 隐藏层的输入矩阵(
)可以表示为:
输入层的每个输入对应每个隐藏层
的权重为
,所以权重的矩阵(
)可以表示为:
由神经元的兴奋启发可以将上式表示为:
由上式子可以整理得;
(b为偏置单元,对应图1的,x0,a0,在本节介绍中省略)
将矩阵代入上式:
同理,我们对于接下来的隐藏层和输出层也用同样的方法,那么我们最终的输出层就是一个在0,1之间分布的值,当其大于0时,即输入的x属于1这个类别。当其小于0时,输入的x属于0这个类别。
3.梯度下降与代价函数
你可能会因为不清楚第二节的前向传播算法中的权重矩阵()是怎么来的,从而导致你对整个算法的过程有些不知所云。这一章节会为你解决这个疑惑。
需要明白的是,在设计一个神经网络算法模型的时候,有些参数是提前人为定义的。
#参数1:
隐藏层的层数
#参数2:
每层隐藏层的神经元个数
#参数3:
输出层的神经元个数
#参数4
定义每次训练的样本数
#参数5
定义训练的次数
#参数6
定义学习率
当上诉参数设定后,将在神经网络中起到怎样的作用呢?结合下图来说明一下:
1.在定义了隐藏层的层数,每层神经元的个数,输出层神经元个数后我们能确定上图中w,b,v,b_,u,b__的矩阵大小。在神经网络第一轮训练时候,上诉矩阵的参数即w11,w12...等的值是人为定义的一系列满足正态分布的随机值。实质上之后你会发现,无论起先w,b的矩阵值如何,通过代价函数的反复迭代都能让其收敛到最低点。
2.定义每次训练的样本数即确定了输入层输入x的个数。
3.人为定义不同的训练的次数,学习率,通过观察准确率的变化,来选择最适合神经网络的参数。
在前言部分为大家介绍到,一个神经网络的每个神经元可以看做一个逻辑回归算法模型。
逻辑回归的代价函数如下:
关于逻辑回归代价函数的推到,我简要说明一下:
1.在二分类问题中,对于每个观察样本:
这个公式很好理解,拆分开来可以这样表示:
2.对于n个出现的样本,样本间相互独立,则n个出现的概率为每一个出现的概率的乘积。
3.为了满足凸函数求最优解的思想,我们对L(w)取对数,并化简:
4.由sigmod函数可以推到出如下结果:
5.有次推导逻辑回归的代价函数:
对于神经网络来说,我们可以由上一章节的前向传播的算法得到最后的输出,之后我们算出这个输出与真实值的误差,然后用同样的方式反向传播回去迭代修正之前w,b,v,b_,u,b__矩阵的值。
而对于每个隐藏层来说,其每个神经元都可以作为一个逻辑回归单元,这样神经网络误差算法的代价函数就可以做出如下表示:
(k表示神经元的序号)
同理我们可以用逻辑回归算法模型的方式来对做梯度下降算法,即找出满足:
时,的w和b的值。对的求解,可以使用链式法则:
对于神经元的求和:
(这个公式可由前向传播算法矩阵运算中得出)
所以
假设:
那么:
所以由梯度下降算法可以求得:
(
为人为设定的学习率。)
4.BP反向传播算法
通过第四节,我们对于神经网络更新参数w,b有了一定的认识,而在上节最后,原来的更新w,b的问题转为了求解
用一张动态图表示前向(FP)和后向(BP)传播的全过程:
对于输出层来说:
(用均方误差来定义样本的y值和通过算法模型求解出来的y值的距离差距)
其中
对于隐藏层来说:
用替换调
,用
替换掉
:
这个式子说明了反向传播算法的本质,我们先求得输入层,然后通过
不断后向推到
....
通过一轮一轮反复迭代修正w,b。使得最终的w,b是最能完成分类的参数。
5.编程实现
如果需要更为详细的编程实现过程,点下面链接:
import numpy as np
import random
import os, struct
from array import array as pyarray
from numpy import append, array, int8, uint8, zeros
class NeuralNet(object):
# 初始化神经网络,sizes是神经网络的层数和每层神经元个数
def __init__(self, sizes):
self.sizes_ = sizes
self.num_layers_ = len(sizes) # 层数
self.w_ = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])] # w_、b_初始化为正态分布随机数
self.b_ = [np.random.randn(y, 1) for y in sizes[1:]]
# Sigmoid函数,S型曲线,
def sigmoid(self, z):
return 1.0/(1.0+np.exp(-z))
# Sigmoid函数的导函数
def sigmoid_prime(self, z):
return self.sigmoid(z)*(1-self.sigmoid(z))
def feedforward(self, x):
for b, w in zip(self.b_, self.w_):
x = self.sigmoid(np.dot(w, x)+b)
return x
def backprop(self, x, y):
nabla_b = [np.zeros(b.shape) for b in self.b_]
nabla_w = [np.zeros(w.shape) for w in self.w_]
activation = x
activations = [x]
zs = []
for b, w in zip(self.b_, self.w_):
z = np.dot(w, activation)+b
zs.append(z)
activation = self.sigmoid(z)
activations.append(activation)
delta = self.cost_derivative(activations[-1], y) * \
self.sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
for l in range(2, self.num_layers_):
z = zs[-l]
sp = self.sigmoid_prime(z)
delta = np.dot(self.w_[-l+1].transpose(), delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return (nabla_b, nabla_w)
def update_mini_batch(self, mini_batch, eta):
nabla_b = [np.zeros(b.shape) for b in self.b_]
nabla_w = [np.zeros(w.shape) for w in self.w_]
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.w_ = [w-(eta/len(mini_batch))*nw for w, nw in zip(self.w_, nabla_w)]
self.b_ = [b-(eta/len(mini_batch))*nb for b, nb in zip(self.b_, nabla_b)]
# training_data是训练数据(x, y);epochs是训练次数;mini_batch_size是每次训练样本数;eta是learning rate
def SGD(self, training_data, epochs, mini_batch_size, eta, test_data=None):
if test_data:
n_test = len(test_data)
n = len(training_data)
for j in range(epochs):
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)
if test_data:
print("Epoch {0}: {1} / {2}".format(j, self.evaluate(test_data), n_test))
else:
print("Epoch {0} complete".format(j))
def evaluate(self, test_data):
test_results = [(np.argmax(self.feedforward(x)), y) for (x, y) in test_data]
return sum(int(x == y) for (x, y) in test_results)
def cost_derivative(self, output_activations, y):
return (output_activations-y)
# 预测
def predict(self, data):
value = self.feedforward(data)
return value.tolist().index(max(value))
# 保存训练模型
def save(self):
pass # 把_w和_b保存到文件(pickle)
def load(self):
pass
打赏一下作者: