Python机器学习笔记:不得不了解的机器学习知识点(2)

      以前一篇笔记: Python机器学习笔记:不得不了解的机器学习知识点(1)html

1,什么样的资料集不适合用深度学习?

  • 数据集过小,数据样本不足时,深度学习相对其它机器学习算法,没有明显优点。
  • 数据集没有局部相关特性,目前深度学习表现比较好的领域主要是图像/语音/天然语言处理等领域,这些领域的一个共性是局部相关性。图像中像素组成物体,语音信号中音位组合成单词,文本数据中单词组合成句子,这些特征元素的组合一旦被打乱,表示的含义同时也被改变。对于没有这样的局部相关性的数据集,不适于使用深度学习算法进行处理。举个例子:预测一我的的健康情况,相关的参数会有年龄、职业、收入、家庭情况等各类元素,将这些元素打乱,并不会影响相关的结果。

2,softmax函数的数学推导及Python实现

  softmax用于多分类过程当中最后一层,将多个神经元的输出,映射到(0, 1)区间内,能够当作几率来理解,从而来进行多分类!前端

  softmax函数以下:算法

  更形象的以下图表示:后端

  softmax 直白来讲就是讲原来输出是 3, 1, -3 经过 softmax 函数一做用,就映射成为(0, 1)的值,而这些值的累和为1,那么咱们就能够将其理解成几率,在最后选取输出节点的时候,咱们能够选取几率最大的节点,做为咱们的预测目标!网络

  Python代码实现:app

# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
import math


# softmax函数,或称归一化指数函数
def softmax(x, axis=1):
    # 为了不求 exp(x) 出现溢出的状况,通常须要减去最大值
    # 计算每行的最大值
    row_max = x.max(axis=axis)
    # 每行元素都须要减去对应的最大值,不然求exp(x)会溢出,致使INF状况
    row_max = row_max.reshpae(-1, 1)
    x = x - row_max

    x_exp = np.exp(x)
    # 若是是列向量,则axis=0
    x_sum = np.sum(x_exp, axis=1, keepdims=True)
    s = x_exp / x_sum
    return s

# 简单一些
def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

# 使用 tf的softmax函数
with tf.Session() as sess:
    tf_s2 = tf.nn.softmax(x, axis=axis)
    s2 = sess.run(tf_s2)

  下面咱们分析一下,减去最大值和不减去最大值是否有必要吗?首先看代码:dom

import numpy as np

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

def softmax1(x):
    """Compute softmax values for each sets of scores in x."""
    return np.exp(x) / np.sum(np.exp(x), axis=0)

scores = [3.0, 1.0, 0.2]
print(softmax(scores))
print(softmax1(scores))
'''
结果输出以下:
[0.8360188  0.11314284 0.05083836]
[0.8360188  0.11314284 0.05083836]
'''

  其实两个结果输出是同样的,即便第一个实现了每列和最大值的差别,而后除以总和,可是问题来了,实如今代码和时间复杂度方面是否类似?哪个更有效率?机器学习

  固然,他们都是正确的,可是从数值稳定性的角度来看,第一个是正确的,由于咱们避免了求 exp(x) 出现溢出的状况,这里减去了最大值。咱们推导一下:分布式

# 转化公式: a ^(b – c)=(a ^ b)/(a ^ c)

e ^ (x - max(x)) / sum(e^(x - max(x))

= e ^ x / (e ^ max(x) * sum(e ^ x / e ^ max(x)))

= e ^ x / sum(e ^ x)

  

3,欧氏距离和曼哈顿距离

  欧氏距离(也称为欧几里得度量),是应用勾股定理计算两个点之间的直线距离,也就是指m维空间两个点之间的真实距离,或者向量的天然长度(即该点到原点的距离)。ide

  下面是欧式距离的公式(分别是二维空间,三维空间,n维空间):

   曼哈顿距离表示的是两个点在标准坐标系上绝对轴距之和,是种使用在几何度量空间的几何学用语。

  例如在平面上,坐标(x1, y1)的i点与坐标(x2, y2)的j点的曼哈顿距离为:
 
d(i,j)=|X1-X2|+|Y1-Y2|
 
  如图所示,很直接明了的理解欧氏距离和曼哈顿距离:

  图中红线表明曼哈顿距离,绿色表明欧氏距离,也就是直线距离,而蓝色和黄色表明等价的曼哈顿距离

  欧氏距离和曼哈顿距离的Python实现:

import numpy as np

def manhattan_distance(vec1, vec2):
    """
    This method implements the manhattan distance metric
    :param p_vec: vector one
    :param q_vec: vector two
    :return: the manhattan distance between vector one and two
    """
    return np.sum(np.fabs(vec1 - vec2))

def edclidean_distance(vec1, vec2):
    """
    This method implements the edclidean distance metric
    :param vec1: vector one
    :param vec2: vector two
    :return: the edclidean distance between vector one and two
    """
    # 方法一
    distance = np.sqrt(np.sum(np.square(vec1 - vec2)))

    # method 2
    dist = np.linalg.norm(vec1 - vec2)
    return distance

  

4,什么是数据埋点

  数据埋点咱们能够分为两类,其一是页面统计,其二是行为统计。

  页面统计能够帮咱们知晓某个页面被多少人访问了多少次,行为统计是指用户在界面上的操做行为,应用最多的是按钮的点击次数。

5,请简要说说一个完整的机器学习项目流程

5.1:抽象成数学问题

  明确问题是进行机器学习的第一步。机器学习的训练过程一般都是一件很是耗时的事情,胡乱尝试时间成本是很是高的。
  这里的抽象成数学问题,指的咱们明确咱们能够得到什么样的数据,目标是一个分类仍是回归或者是聚类的问题,若是都不是的话,若是划归为其中的某类问题。

5.2:获取数据

  数据决定了机器学习结果的上限,而算法只是尽量逼近这个上限。数据要有表明性,不然必然会过拟合。并且对于分类问题,数据偏斜不能过于严重,不一样类别的数据数量不要有数个数量级的差距。

  并且还要对数据的量级有一个评估,多少个样本,多少个特征,能够估算出其对内存的消耗程度,判断训练过程当中内存是否可以放得下。若是放不下就得考虑改进算法或者使用一些降维的技巧了。若是数据量实在太大,那就要考虑分布式了。

5.3 特征预处理与特征选择

  良好的数据要可以提取出良好的特征才能真正发挥效力。

  特征预处理、数据清洗是很关键的步骤,每每可以使得算法的效果和性能获得显著提升。归一化、离散化、因子化、缺失值处理、去除共线性等,数据挖掘过程当中不少时间就花在它们上面。这些工做简单可复制,收益稳定可预期,是机器学习的基础必备步骤。
  筛选出显著特征、摒弃非显著特征,须要机器学习工程师反复理解业务。这对不少结果有决定性的影响。特征选择好了,很是简单的算法也能得出良好、稳定的结果。这须要运用特征有效性分析的相关技术,如相关系数、卡方检验、平均互信息、条件熵、后验几率、逻辑回归权重等方法。

5.4:训练模型与调优

  直到这一步才用到咱们上面说的算法进行训练。如今不少算法都可以封装成黑盒供人使用。可是真正考验水平的是调整这些算法的(超)参数,使得结果变得更加优良。这须要咱们对算法的原理有深刻的理解。理解越深刻,就越能发现问题的症结,提出良好的调优方案。

5.5:模型诊断

  如何肯定模型调优的方向与思路呢?这就须要对模型进行诊断的技术。
  过拟合、欠拟合判断是模型诊断中相当重要的一步。常见的方法如交叉验证,绘制学习曲线等。过拟合的基本调优思路是增长数据量,下降模型复杂度。欠拟合的基本调优思路是提升特征数量和质量,增长模型复杂度。
  偏差分析 也是机器学习相当重要的步骤。经过观察偏差样本,全面分析偏差产生偏差的缘由:是参数的问题仍是算法选择的问题,是特征的问题仍是数据自己的问题……
  诊断后的模型须要进行调优,调优后的新模型须要从新进行诊断,这是一个反复迭代不断逼近的过程,须要不断地尝试, 进而达到最优状态。

5.6:模型融合

  通常来讲,模型融合后都能使得效果有必定提高。并且效果很好。
  工程上,主要提高算法准确度的方法是分别在模型的前端(特征清洗和预处理,不一样的采样模式)与后端(模型融合)上下功夫。由于他们比较标准可复制,效果比较稳定。而直接调参的工做不会不少,毕竟大量数据训练起来太慢了,并且效果难以保证。

5.7:上线运行

  这一部份内容主要跟工程实现的相关性比较大。工程上是结果导向,模型在线上运行的效果直接决定模型的成败。 不单纯包括其准确程度、偏差等状况,还包括其运行的速度(时间复杂度)、资源消耗程度(空间复杂度)、稳定性是否可接受。
  这些工做流程主要是工程实践上总结出的一些经验。并非每一个项目都包含完整的一个流程。这里的部分只是一个指导性的说明,只有你们本身多实践,多积累项目经验,才会有本身更深入的认识。

6,全链接神经网络网络结构

  (此题参考:https://blog.csdn.net/cuiyuan605/article/details/84307323)

  神经网络算法,是使用计算机模拟生物神经系统,来模拟人类思惟方式的算法。它的基本单位就是人工神经元。经过相互链接造成一张神经网络。对于神经网络有些了解的盆友可能都知道,神经网络其实就是一个输入 X(向量) 到输出 Y(向量)的映射函数:f(x) = Y,函数的系数就是咱们所要训练的网络参数 W,只要函数系数肯定下来,对于任何输入xi,咱们就能获得一个与之对应的输出 yi,至于 yi 是否符合咱们的预期,这就是输入如何提升模型性能方面的问题。

 

   生物神经网络中,每一个神经元与其余神经元链接,当它“激活”时,会传递化学物质到相连的神经元,改变其余神经元的电位,当电位达到必定“阈值”,那么这个神经元也会被激活。

  单我的工神经元的计算公式以下:

  其中:

   为输入参数向量,表示其余神经元输入的信号。

  为每一个输入参数的权重值,表示对应神经元信号的权重。

  theta 为阈值或者误差值,是指该激活神经元的难易程度。

  y 为神经元的输出值,表示该神经元是否被激活。

  Act() 为激活函数,理想的激活函数以下图(a)中的跃阶函数,“1” 为神经元兴奋,“0”为神经元抑制,但因为跃阶函数具备不是连续可导等很差的性质,所以通常采用下面(b) 图的 Sigmoid 函数做为激活函数:

  下面定义一个全链接神经网络:

  全链接神经网络,就是指每一层的每一个神经元都和下一层的每一个神经元项链接。

  Layer:0 为输入层

  Layer:L 为输出层

  其余L-1 个Layer 为隐层

  输入 x  :,咱们称一个输入值 x 为一个样本

  输出 y  :,变量的上标(L)表示该变量出于神经网络的那一层。

   表示第 L 层编号为 i 的神经元

   表示第 L 层的神经元数量

 

 7,全链接神经网络的前向传播

  前向传播比较简单,就是向量点乘,也就是加权求和,而后通过一个激活函数。也就是网络如何根据输入 X 获得输出 Y的。

  记 为第 l-1 层第 k个神经元到第 l 层第 j 个神经元的权重, 为第 l 层 第 j 个神经元的偏置,为第 l 层第 j 个神经元的激活值(激活函数的输出)。不难看出 的值取决于上一层神经元的激活:

  将上面重写为矩阵形式:

  为了方便表示,记  为每一层权重输入,矩阵形式则变为

  利用矩阵形式能够一层层计算网络的激活值,最终能根据输入X 获得相应的输出 

8,随机梯度降低法

(此题参考:https://blog.csdn.net/qq_38150441/article/details/80533891 和 https://blog.csdn.net/qq_39037383/article/details/89156894)

  梯度降低算法的思想就是根据人类在渐进学习中,不断从错误中纠正本身的认知的过程当中感触到的。

8.1 梯度降低

  简单来讲,梯度降低就是从山顶找一条最短的路走到山底最低的地方。可是由于选择方向的缘由,咱们找到的最低点可能不是真正的最低点。如图所示,黑色标注的路线所指的方向并非真正的地方。(由于梯度降低是一种思想,没有严格的定义,因此用一个比喻来解释什么是梯度降低)

  既然是选择一个方向下山,那么这个方向该如何选?每次该怎么走?

  先说选的方向,在算法中是以随机方式给出的,这也是形成有时候走不到真正最低点的缘由。若是选定了方向,之后每走一步,都选择的时最陡的方向,直到最低点。总结起来就是:随机选择一个方向,而后每次都选择最陡的方向,直到这个方向上能达到的最低点。

  在机器学习算法中,有时候须要对原始的模型构建损失函数,而后经过优化算法对损失函数进行优化,以便寻找到最优的参数,使得损失函数的值最小。而求解机器学习参数的优化算法中,使用最多的就是基于梯度降低的优化算法(Gradient Descent GD)。

  梯度降低的优缺点

  • 优势:效率。在梯度降低法的求解过程当中,只需求解损失函数的一阶导数,计算的代价比较小,能够在不少大规模数据集上应用。
  • 缺点:求解的时局部最优值,即因为方向选择的问题,获得的结果不必定是全局最优步长选择,太小使得函数收敛速度慢,过大又容易找不到最优解。

8.2 随机梯度降低

  随机梯度降低(SGD)是一种简单但很是有效地方法,多用于支持向量机,逻辑回归等凸损失函数下的线性分类器的学习。而且SGD已经成功应用于文本分类和天然语言处理中常常遇到的大规模和稀疏机器学习问题。SGD 既能够用于分类计算,也能够用于回归计算。

  随机梯度降低法不是对每一个样本集进行求梯度更新参数,而是对一个或者多个样本进行求梯度,更新参数,采集多个样本为样本集再进行以下操做:

1.初始化参数为任意值(能够取到面上任意一点)

2.对样本集里每一个样本进行遍历以下操做
      1.求解梯度值

      2.更新参数
   
3.若达到指定迭代次数或者收敛条件,则训练结束

  随机梯度降低法不一样于批量梯度降低,随机梯度降低是每次迭代使用一个样原本对参数进行更新。使得训练速度加快。

  对于一个样本的目标函数为:

  对目标函数求偏导:

  参数更新:

   随机梯度降低的优缺点:

  • 优势:因为不是在所有训练数据上的损失函数,而是在每轮迭代中,随机优化某一条训练数据上损失函数,这样每一轮参数的更新速度大大加快。
  • 缺点:准确度降低,因为即便在目标函数为强凸函数的状况下,SGD仍旧没法作到线性收敛。可能会收敛到局部最优,而单个样本并不能表明全体样本的趋势,并且不易于并行实现。

9,LR的原理和Loss的推导

  首先,LR是一个分类模型,讨论二分类状况下,在这个基础上咱们假设样本服从伯努利分布(0~1)分布。作了假设分布后下一步就是求分布参数,这个过程通常采用极大似然估计MLE(Maximum Likelihood Estimation),具体的方法就是求该假设分布在训练样本上的联合几率(样本带入连乘),而后求其关于 theta 的最大值,为了方便计算因此通常取 -log,单调性保持不变,全部就有了 logLoss: L(Y, P(Y|X)) = - logP(Y|X)。

 10,机器学习中,为什么要常常对数据作归一化

  (参考文献:https://blog.csdn.net/abc_138/article/details/82798674)

  通常作机器学习应用的时候大部分时间是花费在特征处理上,其中很关键的一步就是对特征数据进行归一化。

  首先要明白归一化的目的是什么,其目的是为了不数值较大的特征A变化掩盖了数值较小的特征B变化,最终但愿让特征AB都能对结果有影响。

  那么为何要作归一化呢?

  维基百科给出的解释:1,归一化后加快了梯度降低求最优解的速度。2,归一化有可能提升精度。

解释:归一化为何能提升梯度降低法求解最优解的速度?

  以下图所示(来自:斯坦福机器学习视频)

 

   蓝色的圈圈图表明的是两个特征的等高线。其中左图两个特征 X1和 X2的区间差异很是大,X1区间为[0, 2000] ,x2区间是 [1, 5],像这种有的数据那么大,有的数据那么小,两类之间的幅度相差这么大,其所造成的等高线很是尖。当使用梯度降低法寻求最优解时,颇有可能走“之字型”路线(垂直等高线走),从而致使须要迭代不少次才能收敛。而右图对两个原始特征进行了归一化,其对应的等高线显得很圆,在梯度降低进行求解时能较快的收敛,所以若是机器学习模型使用梯度降低法求最优解时,归一化每每很是有必要,不然很难收敛,甚至不能收敛。

解释:归一化有可能提升精度

  一些分类器须要计算样本之间的距离(如欧式距离),例如KNN。若是一个特征值域范围很是大,那么距离计算就主要取决于这个特征,从而与实际状况相悖(好比这时实际状况是值域范围小的特征更重要)。

归一化的类型

1,线性归一化

  这种归一化方法比较适用于在数值比较集中的状况。这种方法有个缺陷,若是max和min 不稳定,很容易使得归一化结果不稳定,使得后续使用效果也不稳定。实际使用中能够用经验常量值来替代 max和 min。

2,标准差标准化

  通过处理的数据符合标准正态分布,即均值为0,标准差为1。

3,非线性归一化

  常常用在数据分化比较大的场景,有些数值很大,有些很小。经过一些数学函数,将原始值进行映射。该方法包括 log、指数,正切等。须要根据数据分布的状况,决定非线性函数的曲线,好比log(V, 2)仍是log(V, 10)等。

11,batch

  深度学习中频繁出现batch这个词语,因此咱们有必要了解一下。

  深度学习中 的优化算法,说白了就是梯度降低。每次的参数更新有两种方式。

  第一种,遍历所有数据集算一次损失函数,而后算函数对各个参数的梯度,更新梯度。这张方式每更新一次参数都要把数据集里的全部样本都看一遍,计算量开销大,计算速度慢,不支持在线学习,这称为 Batch gradient descent,批梯度降低。

  另外一种,每看一个数据就算一下损失函数,而后求梯度更新参数,这个称为随机梯度降低, stochastic gradient  descent。这个方法速度比较快,可是收敛性能不太好,可能在最优势附近晃来晃去, hit 不到最优势。两次参数的更新也有可能互相抵消掉,形成目标函数震荡的比较剧烈。

  为了克服两种方法的缺点,如今通常采用的时一种折中手段,mini-batch gradient decent,小批的梯度降低,这种方法把数据分为若干个批,按批来更新参数。这样一个批中的一组数据共同决定了本次梯度的方向,降低起来就不容易跑偏,减小了随机性。另一方面由于批次的样本数与整个数据集相比少了不少,计算量也不是很大。

  基本上如今的梯度降低都是基于 mini-batch的,因此Keras的模块中常常会出现 batch_size,就是指这个。

12,关于机器学习拟合问题

12.1 什么是机器学习过拟合?

  所谓过拟合,就是指模型在训练集上的效果很好,在测试集上的预测效果不好。

12.2 如何避免过拟合问题?

  1,重采样Bootstrap 

  2,L1,L2 正则化

  3,决策树的剪枝操做

  4,交叉验证

12.3 什么是机器学习的欠拟合?

  所谓欠拟合就是模型复杂度低或者数据集过小,对模型数据的拟合程度不高,所以模型在训练集上的效果就很差。

12.3 如何避免欠拟合问题?

  1,增长样本数量

  2,增长样本特征的数量

  3,能够进行特征维度扩展

12.4  算法的偏差通常是由那几个方面引发的?

  1,因模型没法表达基本数据的复杂度而形成的误差(bias)——欠拟合

  2,因模型过分拟合训练集数据而形成的方差(variance)——过拟合

13,为何朴素贝叶斯如此“朴素”?

  贝叶斯算法简单高效,在处理分类问题上,是首先要考虑的方法之一。

  贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。公式以下:

   该公式最大的优势就是能够忽略AB 的联合几率直接求其条件几率分布。

  而朴素贝叶斯为何如此朴素,由于他假定全部的特征在数据集中的做用是一样重要和独立的。正如咱们所知,这个假设在现实世界中是很不真实的,所以说朴素贝叶斯真的很“朴素”。

  朴素贝叶斯分类是一种很是简单的分类算法,其思想是朴素的。即:对于给出的待分类项,求解在此项出现的条件下各个类别出现的几率,那个最大,就认为此待分类项属于那个类别。

  理论上,朴素贝叶斯模型与其余分类方法相比具备最小的偏差率。可是实际上并不是老是如此,这是由于朴素贝叶斯模型给定输出类别的状况下,假设属性之间相互独立,这个假设在实际应用中每每是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果很差。而在属性相关性较小的时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法经过考虑部分关联性适度改进。

 

14,反向传播算法(BP算法)的推导及其Python实现

  下面学习如何调整一个神经网络的参数,也就是偏差反向传播算法(BP算法)。以获得一个可以根据输入,预测正确输出的模型。

14.1,首先咱们要了解优化的目标

  根据人工神经元的定义,有如下三个公式:

  其中,Act() 是激活函数,以前学习过。

  根据上面两个公式,能够得出各个神经元之间的通用公式,以下:

  其中上式是人工神经网络正向传播的核心公式。

  那么,咱们根据什么来调整神经网络的参数,以获得一个可以正确预测结果的模型呢?请看下面的公式:

  上式用来计算咱们指望的输出和实际输出的“差异”,其中cost() 叫作损失函数。咱们的指望是损失函数值达到最小。

  可是只根据一次输出的损失值,对参数进行调整,没法使模型适应全部输入样本。咱们须要的是,调整参数,使得全部输入样本,获得输出的总损失值最小,而不是只让妻子一个样本的损失值最小,致使其余样本损失值增大。所以有下面公式:

  上式表示一个 batch 的全部样本输出的总损失值的平均值。其中,bn 表示一个 batch中样本的数量。

  为何不用全部的样本计算损失值,而将全部样本分红一个个的 batch呢?由于全部的训练样本数量太大了,可能有数以百万计,将全部的样本损失值都一块儿进行运算,计算量过于庞大,大大下降了模型计算的速度。

  而计算总的损失值 C,其中是一个以全部的链接权重 W 和 全部的阈值 theta 未为变量的多元函数。咱们想要的模型就是求得 C 最小时,全部 W 和 theta 的值。直接计算显然是不可能的,由于对于一个大的深度神经网络,全部的参数变量,可能数以万计。

  在这里咱们使用梯度降低算法来逐步逼近 C的最小值,也便是先随机获得一组参数变量的值,而后计算参数变量当前的梯度,向梯度的反方向,也就是C变小最快的方向,逐步调整参数值,最终获得 C 的最小值,或者近似最小值。

  而将全部样本,随机分红一个个固定长度的 batch,以获得近似的梯度方向,叫作随机梯度降低算法。

14.2 开始求梯度

   那么根据梯度的定义,接下来的任务,就是求取各个参数变量相对于 C 的偏导数。咱们将使用偏差反向传播算法来求取各个参数变量的偏导数。

  求取偏导数的方法和神经网络正向传播(根据样本计算输出值)的方式相似,也是逐层求解,只是方向正好相反,从最后一层开始,逐层向前。

  首先,咱们先求神经网络最后一层,也便是输出层的相关参数的偏导数。为了下降推导的复杂性,咱们只计算相对一个样本的损失值函数 Cbi 的偏导数,由于相对于总损失值函数 C 的偏导数值,也不过是把某个参数的全部相对于 Cbi 偏导数值加起来而已。

  根据上面公式,以及 复合函数求导法则,能够获得输出层(L层)某个神经元的权值参数 W 的偏导数,计算公式以下:

  根据前面三个公式求导以下:

  将这三个公式代入上面公式,能够获得:

  咱们令:

  则:

  将上式代入损失函数求导的公式中能够获得:

  这样咱们就获得了输出层 L 相关的权重参数 W 的偏导数计算公式!

  接下来,同理能够求得输出层 L 相关的阈值 theta 的偏导数计算公式为:

  而根据第二个公式能够获得:

  将上式代入到上上式能够获得:

  这就是 输出层 L 相关的阈值 theta 的偏导数计算公式!

14.3 根据 L 层,求前一层参数的偏导函数

  从下面公式,可知,一个权重参数 W 只影响一个 L-1 层的神经元:

   所以能够获得有下面公式:

 

  将上式代入到上上式能够获得:

  根据假设:

  咱们能够获得:

  将上式代入到上上式,能够获得:

  同理,咱们能够获得:

  根据14.3 第一个公式能够获得:

  将上式代入到上上式,能够获得:

  这样咱们就获得了 L-1 层神经元相关参数的计算公式。

  下面咱们还须要推导一下 之间的关系,根据下面公式:

  咱们能够获得:

  同理可得:

  将上式代入到上上式,能够得:

  咱们知道,一个权重参数 W 只影响一个 L-1 层的神经元,但这个 L-1 层神经元影响了全部 L层的神经元。所以,根据多元复合函数求导法则。有:

  根据咱们以前的假设,能够获得:

  将上式代入到上上式,能够获得:

  咱们能够知道:

  将上式代入到上上式,能够获得:

  最后将上式代入以前的公式,能够获得:

  这样咱们就获得了反向传播,逐层推导的通用公式:

  这里, W 和 Z 都是整箱传播过程当中已经算好的常数,而 能够从 L层开始逐层向前推导,直到第1层,第0层是输入层,不须要调整参数,而第L层的参数能够参考下面公式:

 

   下面是全链接神经网络的Python实现代码:

#coding=utf-8
import numpy as np
import matplotlib.pylab as plt
import random
 
class NeuralNetwork(object):
    def __init__(self, sizes, act, act_derivative, cost_derivative):
        #sizes表示神经网络各层的神经元个数,第一层为输入层,最后一层为输出层
        #act为神经元的激活函数
        #act_derivative为激活函数的导数
        #cost_derivative为损失函数的导数
        self.num_layers = len(sizes)
        self.sizes = sizes
        self.biases = [np.random.randn(nueron_num, 1) for nueron_num in sizes[1:]]
        self.weights = [np.random.randn(next_layer_nueron_num, nueron_num)
            for nueron_num, next_layer_nueron_num in zip(sizes[:-1], sizes[1:])]
        self.act=act
        self.act_derivative=act_derivative
        self.cost_derivative=cost_derivative
 
    #前向反馈(正向传播)
    def feedforward(self, a):
        #逐层计算神经元的激活值,公式(4)
        for b, w in zip(self.biases, self.weights):
            a = self.act(np.dot(w, a)+b)
        return a
 
    #随机梯度降低算法
    def SGD(self, training_data, epochs, batch_size, learning_rate):
        #将训练样本training_data随机分为若干个长度为batch_size的batch
        #使用各个batch的数据不断调整参数,学习率为learning_rate
        #迭代epochs次
        n = len(training_data)
        for j in range(epochs):
            random.shuffle(training_data)
            batches = [training_data[k:k+batch_size] for k in range(0, n, batch_size)]
            for batch in batches:
                self.update_batch(batch, learning_rate)
            print("Epoch {0} complete".format(j))
 
    def update_batch(self, batch, learning_rate):
        #根据一个batch中的训练样本,调整各个参数值
        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 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-(learning_rate/len(batch))*nw for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b-(learning_rate/len(batch))*nb for b, nb in zip(self.biases, nabla_b)]
 
    #反向传播
    def backprop(self, x, y):
        #保存b和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]
        #保存每一层神经元的z值
        zs = []
        for b, w in zip(self.biases, self.weights):
            z = np.dot(w, activation)+b
            zs.append(z)
            activation = self.act(z)
            activations.append(activation)
        #反向传播获得各个参数的偏导数值
        #公式(13)
        d = self.cost_derivative(activations[-1], y) * self.act_derivative(zs[-1])
        #公式(17)
        nabla_b[-1] = d
        #公式(14)
        nabla_w[-1] = np.dot(d, activations[-2].transpose())
        #反向逐层计算
        for l in range(2, self.num_layers):
            z = zs[-l]
            sp = self.act_derivative(z)
            #公式(36),反向逐层求参数偏导
            d = np.dot(self.weights[-l+1].transpose(), d) * sp
            #公式(38)
            nabla_b[-l] = d
            #公式(37)
            nabla_w[-l] = np.dot(d, activations[-l-1].transpose())
        return (nabla_b, nabla_w)
 
#距离函数的偏导数
def distance_derivative(output_activations, y):
    #损失函数的偏导数
    return 2*(output_activations-y)
 
# sigmoid函数
def sigmoid(z):
    return 1.0/(1.0+np.exp(-z))
 
# sigmoid函数的导数
def sigmoid_derivative(z):
    return sigmoid(z)*(1-sigmoid(z))
 
if __name__ == "__main__":
    #建立一个5层的全链接神经网络,每层的神经元个数为1,8,5,3,1
    #其中第一层为输入层,最后一层为输出层
    network=NeuralNetwork([1,8,5,3,1],sigmoid,sigmoid_derivative,distance_derivative)
 
    #训练集样本
    x = np.array([np.linspace(-7, 7, 200)]).T
    #训练集结果,因为使用了sigmoid做为激活函数,需保证其结果落在(0,1)区间内
    y = (np.cos(x)+1)/2
 
    #使用随机梯度降低算法(SGD)对模型进行训练
    #迭代5000次;每次随机抽取40个样本做为一个batch;学习率设为0.1
    training_data=[(np.array([x_value]),np.array([y_value])) for x_value,y_value in zip(x,y)]
    network.SGD(training_data,5000,40,0.1)
 
    #测试集样本
    x_test = np.array([np.linspace(-9, 9, 120)])
    #测试集结果
    y_predict = network.feedforward(x_test)
 
    #图示对比训练集和测试集数据
    plt.plot(x,y,'r',x_test.T,y_predict.T,'*')
    plt.show()
相关文章
相关标签/搜索