深入解析神经网络从原理到实现


Posted in Python onJuly 26, 2019

1.简单介绍

在机器学习和认知科学领域,人工神经网络(artificial neural network,缩写ANN),简称神经网络(neural network,缩写NN)或类神经网络,是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统。现代神经网络是一种非线性统计性数据建模工具。典型的神经网络具有以下三个部分:

结构 (Architecture) 结构指定了网络中的变量和它们的拓扑关系。例如,神经网络中的变量可以是神经元连接的权重(weights)和神经元的激励值(activities of the neurons)。

激励函数(Activity Rule) 大部分神经网络模型具有一个短时间尺度的动力学规则,来定义神经元如何根据其他神经元的活动来改变自己的激励值。一般激励函数依赖于网络中的权重(即该网络的参数)。

学习规则(Learning Rule)学习规则指定了网络中的权重如何随着时间推进而调整。这一般被看做是一种长时间尺度的动力学规则。一般情况下,学习规则依赖于神经元的激励值。它也可能依赖于监督者提供的目标值和当前权重的值。

2.初识神经网络

如上文所说,神经网络主要包括三个部分:结构、激励函数、学习规则。图1是一个三层的神经网络,输入层有d个节点,隐层有q个节点,输出层有l个节点。除了输入层,每一层的节点都包含一个非线性变换。

深入解析神经网络从原理到实现 

图1

那么为什么要进行非线性变换呢?

(1)如果只进行线性变换,那么即使是多层的神经网络,依然只有一层的效果。类似于0.6*(0.2x1+0.3x2)=0.12x1+0.18x2。
(2)进行非线性变化,可以使得神经网络可以拟合任意一个函数,图2是一个四层网络的图。

深入解析神经网络从原理到实现 

图2

下面使用数学公式描述每一个神经元工作的方式

(1)输出x
(2)计算z=w*x
(3)输出new_x = f(z),这里的f是一个函数,可以是sigmoid、tanh、relu等,f就是上文所说到的激励函数。

3.反向传播(bp)算法

有了上面的网络结构和激励函数之后,这个网络是如何学习参数(学习规则)的呢?

首先我们先定义下本文使用的激活函数、目标函数

(1)激活函数(sigmoid):深入解析神经网络从原理到实现

def sigmoid(z):
  return 1.0/(1.0+np.exp(-z))

sigmoid函数有一个十分重要的性质:深入解析神经网络从原理到实现,即计算导数十分方便。

def sigmoid_prime(z):
  return sigmoid(z)*(1-sigmoid(z))

下面给出一个简单的证明:深入解析神经网络从原理到实现

(2)目标函数(差的平方和)深入解析神经网络从原理到实现,公式中的1/2是为了计算导数方便。

然后,这个网络是如何运作的

(1)数据从输入层到输出层,经过各种非线性变换的过程即前向传播。

def feedforward(self, a):
  for b, w in zip(self.biases, self.weights):
    a = sigmoid(np.dot(w, a)+b)
  return a

其中,初始的权重(w)和偏置(b)是随机赋值的

biases = [np.random.randn(y, 1) for y in sizes[1:]]
weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]

(2)参数更新,即反向传播

在写代码之前,先进行推导,即利用梯度下降更新参数,以上面的网络结构(图1)为例

(1)输出层与隐层之间的参数更新

深入解析神经网络从原理到实现

(2)隐层与输入层之间的参数更新

深入解析神经网络从原理到实现

有两点需要强调下:

(2)中的结果比(1)中的结果多了一个求和公式,这是因为计算隐层与输入层之间的参数时,输出层与隐层的每一个节点都有影响。

(2)中参数更新的结果可以复用(1)中的参数更新结果,从某种程度上,与反向传播这个算法名称不谋而合,不得不惊叹。

def backprop(self, x, y):
  """返回一个元组(nabla_b, nabla_w)代表目标函数的梯度."""
  nabla_b = [np.zeros(b.shape) for b in self.biases]
  nabla_w = [np.zeros(w.shape) for w in self.weights]
  # feedforward
  activation = x
  activations = [x] # list to store all the activations, layer by layer
  zs = [] # list to store all the z vectors, layer by layer
  for b, w in zip(self.biases, self.weights):
    z = np.dot(w, activation)+b
    zs.append(z)
    activation = sigmoid(z)
    activations.append(activation)
  # backward pass
  delta = self.cost_derivative(activations[-1], y) * \
    sigmoid_prime(zs[-1])
  nabla_b[-1] = delta
  nabla_w[-1] = np.dot(delta, activations[-2].transpose())
  """l = 1 表示最后一层神经元,l = 2 是倒数第二层神经元, 依此类推."""
  for l in xrange(2, self.num_layers):
    z = zs[-l]
    sp = sigmoid_prime(z)
    delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
    nabla_b[-l] = delta
    nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
  return (nabla_b, nabla_w)

4.完整代码实现

# -*- coding: utf-8 -*-

import random
import numpy as np

class Network(object):

  def __init__(self, sizes):
  """参数sizes表示每一层神经元的个数,如[2,3,1],表示第一层有2个神经元,第二层有3个神经元,第三层有1个神经元."""
    self.num_layers = len(sizes)
    self.sizes = sizes
    self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
    self.weights = [np.random.randn(y, x)
            for x, y in zip(sizes[:-1], sizes[1:])]

  def feedforward(self, a):
    """前向传播"""
    for b, w in zip(self.biases, self.weights):
      a = sigmoid(np.dot(w, a)+b)
    return a

  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 xrange(epochs):
      random.shuffle(training_data)
      mini_batches = [
        training_data[k:k+mini_batch_size]
        for k in xrange(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 update_mini_batch(self, mini_batch, eta):
    """使用后向传播算法进行参数更新.mini_batch是一个元组(x, y)的列表、eta是学习速率"""
    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):
    """返回一个元组(nabla_b, nabla_w)代表目标函数的梯度."""
    nabla_b = [np.zeros(b.shape) for b in self.biases]
    nabla_w = [np.zeros(w.shape) for w in self.weights]
    # 前向传播
    activation = x
    activations = [x] # list to store all the activations, layer by layer
    zs = [] # list to store all the z vectors, layer by layer
    for b, w in zip(self.biases, self.weights):
      z = np.dot(w, activation)+b
      zs.append(z)
      activation = sigmoid(z)
      activations.append(activation)
    # backward pass
    delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])
    nabla_b[-1] = delta
    nabla_w[-1] = np.dot(delta, activations[-2].transpose())
    """l = 1 表示最后一层神经元,l = 2 是倒数第二层神经元, 依此类推."""
    for l in xrange(2, self.num_layers):
      z = zs[-l]
      sp = sigmoid_prime(z)
      delta = np.dot(self.weights[-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 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 sigmoid(z):
  return 1.0/(1.0+np.exp(-z))

def sigmoid_prime(z):
  """sigmoid函数的导数"""
  return sigmoid(z)*(1-sigmoid(z))

5.简单应用

# -*- coding: utf-8 -*-

from network import *

def vectorized_result(j,nclass):
  """离散数据进行one-hot"""
  e = np.zeros((nclass, 1))
  e[j] = 1.0
  return e

def get_format_data(X,y,isTest):
  ndim = X.shape[1]
  nclass = len(np.unique(y))
  inputs = [np.reshape(x, (ndim, 1)) for x in X]
  if not isTest:
    results = [vectorized_result(y,nclass) for y in y]
  else:
    results = y
  data = zip(inputs, results)
  return data

#随机生成数据
from sklearn.datasets import *
np.random.seed(0)
X, y = make_moons(200, noise=0.20)
ndim = X.shape[1]
nclass = len(np.unique(y))

#划分训练、测试集
from sklearn.cross_validation import train_test_split
train_x,test_x,train_y,test_y = train_test_split(X,y,test_size=0.2,random_state=0)

training_data = get_format_data(train_x,train_y,False)
test_data = get_format_data(test_x,test_y,True)

net = Network(sizes=[ndim,10,nclass])
net.SGD(training_data=training_data,epochs=5,mini_batch_size=10,eta=0.1,test_data=test_data)

参考文献
(1)周志华《机器学习》
(2)https://github.com/mnielsen/neural-networks-and-deep-learning
(3)https://zhuanlan.zhihu.com/p/21525237

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
利用Python抓取行政区划码的方法
Nov 28 Python
Python中跳台阶、变态跳台阶与矩形覆盖问题的解决方法
May 19 Python
python ftp 按目录结构上传下载的实现代码
Sep 12 Python
python 重命名轴索引的方法
Nov 10 Python
浅谈numpy生成数组的零值问题
Nov 12 Python
python调用虹软2.0第三版的具体使用
Feb 22 Python
python实现QQ批量登录功能
Jun 19 Python
tensorflow实现训练变量checkpoint的保存与读取
Feb 10 Python
Python自动化测试笔试面试题精选
Mar 12 Python
Python Celery异步任务队列使用方法解析
Aug 10 Python
selenium切换标签页解决get超时问题的完整代码
Aug 30 Python
python-地图可视化组件folium的操作
Dec 14 Python
python单例模式的多种实现方法
Jul 26 #Python
django的ORM操作 增加和查询
Jul 26 #Python
Django在pycharm下修改默认启动端口的方法
Jul 26 #Python
Python解析命令行读取参数之argparse模块
Jul 26 #Python
Django Rest framework三种分页方式详解
Jul 26 #Python
浅析Windows 嵌入python解释器的过程
Jul 26 #Python
python flask几分钟实现web服务的例子
Jul 26 #Python
You might like
攻克CakePHP系列三 表单数据增删改
2008/10/22 PHP
将PHP从5.3.28升级到5.3.29时Nginx出现502错误
2015/05/09 PHP
PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法
2016/02/15 PHP
PHP数组访问常用方法解析
2020/09/05 PHP
如何用javascript判断录入的日期是否合法
2007/01/08 Javascript
javascript 函数调用规则
2009/08/26 Javascript
深入认识javascript中的eval函数
2009/11/02 Javascript
js setattribute批量设置css样式
2009/11/26 Javascript
window.onbeforeunload方法在IE下无法正常工作的解决办法
2010/01/23 Javascript
iphone safari不支持position fixed的解决方法
2012/05/04 Javascript
原生js操作checkbox用document.getElementById实现
2013/10/12 Javascript
JavaScript中setInterval的用法总结
2013/11/20 Javascript
JavaScript搜索字符串并将搜索结果返回到字符串的方法
2015/04/06 Javascript
js使用DOM操作实现简单留言板的方法
2015/04/10 Javascript
js图片轮播手动切换效果
2015/11/10 Javascript
微信小程序 图片加载(本地,网路)实例详解
2017/03/10 Javascript
vue2.0 常用的 UI 库实例讲解
2017/12/12 Javascript
vue+element-ui实现表格编辑的三种实现方式
2018/10/31 Javascript
js实现简单进度条效果
2020/03/25 Javascript
动态创建类实例代码
2009/10/07 Python
Python查询阿里巴巴关键字排名的方法
2015/07/08 Python
Python中字符串的处理技巧分享
2016/09/17 Python
使用python和Django完成博客数据库的迁移方法
2018/01/05 Python
python skimage 连通性区域检测方法
2018/06/21 Python
python与caffe改变通道顺序的方法
2018/08/04 Python
python 使用matplotlib 实现从文件中读取x,y坐标的可视化方法
2019/07/04 Python
Python 迭代,for...in遍历,迭代原理与应用示例
2019/10/12 Python
python爬虫scrapy基于CrawlSpider类的全站数据爬取示例解析
2021/02/20 Python
斯凯奇新西兰官网:SKECHERS新西兰
2018/02/22 全球购物
日本热销NO.1胶原蛋白冻:Aishitoto爱希特多
2019/06/20 全球购物
行政助理的岗位职责
2014/02/18 职场文书
销售员未完成销售业绩的检讨书
2014/10/12 职场文书
详解CocosCreator消息分发机制
2021/04/16 Javascript
golang日志包logger的用法详解
2021/05/05 Golang
Golang 并发下的问题定位及解决方案
2022/03/16 Golang
使用 MybatisPlus 连接 SqlServer 数据库解决 OFFSET 分页问题
2022/04/22 SQL Server