毕设日志(一)

2019.3.5

现在开始准备做本科毕业设计,将每天的安排和进度记录下来,督促自己。
首先,毕设的大致方向是深度强化学习。具体内容未知。

Q1:我现在的任务是什么?
A1:(1)掌握相关的知识,如深度强化学习,图像处理等等,(2)学习相关的论文并复现。

Q2:我现在具体在做什么?
A2:了解强化学习,并用python实现一些小例子。

Q-learning算法

毕设日志(一)别人的代码:
来自 https://www.cnblogs.com/hhh5460/p/10134018.html

'''
-o---T
# T 就是宝藏的位置, o 是探索者的位置
'''
# 作者: hhh5460
# 时间:20181217

import pandas as pd
import random
import time


epsilon = 0.9   # 贪婪度 greedy
alpha = 0.4     # 学习率
gamma = 0.9     # 奖励递减值

states = range(6)           # 状态集。从0到5
actions = ['left', 'right'] # 动作集。也可添加动作'none',表示停留
rewards = [0,0,0,0,0,1]     # 奖励集。只有最后的宝藏所在位置才有奖励1,其他皆为0

q_table = pd.DataFrame(data=[[0 for _ in actions] for _ in states],
                       index=states, columns=actions)
                       

def update_env(state):
    '''更新环境,并打印'''
    global states
    
    env = list('-----T') # 环境,就是这样一个字符串(list)!!
    if state != states[-1]:
        env[state] = 'o'
    print('\r{}'.format(''.join(env)), end='')
    time.sleep(0.1)
                       
def get_next_state(state, action):
    '''对状态执行动作后,得到下一状态'''
    global states
    
    # l,r,n = -1,+1,0
    if action == 'right' and state != states[-1]: # 除非最后一个状态(位置),向右就+1
        next_state = state + 1
    elif action == 'left' and state != states[0]: # 除非最前一个状态(位置),向左就-1
        next_state = state -1
    else:
        next_state = state
    return next_state
                       
def get_valid_actions(state):
    '''取当前状态下的合法动作集合,与reward无关!'''
    global actions # ['left', 'right']
    
    valid_actions = set(actions)
    if state == states[-1]:             # 最后一个状态(位置),则
        valid_actions -= set(['right']) # 不能向右
    if state == states[0]:              # 最前一个状态(位置),则
        valid_actions -= set(['left'])  # 不能向左
    return list(valid_actions)
    
for i in range(13):
    #current_state = random.choice(states)
    current_state = 0
    
    update_env(current_state) # 环境相关
    total_steps = 0           # 环境相关
    
    while current_state != states[-1]:
        if (random.uniform(0,1) > epsilon) or ((q_table.ix[current_state] == 0).all()):  # 探索
            current_action = random.choice(get_valid_actions(current_state))
        else:
            current_action = q_table.ix[current_state].idxmax() # 利用(贪婪)

        next_state = get_next_state(current_state, current_action)
        next_state_q_values = q_table.ix[next_state, get_valid_actions(next_state)]
        q_table.ix[current_state, current_action] += alpha * (rewards[next_state] + gamma * next_state_q_values.max() - q_table.ix[current_state, current_action])
        current_state = next_state
        
        update_env(current_state) # 环境相关
        total_steps += 1          # 环境相关
        
    print('\rEpisode {}: total_steps = {}'.format(i, total_steps), end='') # 环境相关
    time.sleep(2)                                                          # 环境相关
    print('\r                                ', end='')                    # 环境相关
        
print('\nq_table:')
print(q_table)

先看懂别人的代码,了解一下强化学习。
这段代码比较简单,

例子的环境是一个一维世界,在世界的右边有宝藏,探索者只要得到宝藏尝到了甜头,以后就记住了得到宝藏的方法,这就是他用强化学习所学习到的行为。
Q-learning 是一种记录行为值 (Q value) 的方法,每种在一定状态的行为都会有一个值 Q(s, a),就是说 行为 a 在 s 状态的值是 Q(s, a)。s 在上面的探索者游戏中,就是 o 所在的地点了。而每一个地点探索者都能做出两个行为 left/right,这就是探索者的所有可行的 a 啦。

Q-learning算法的关键在于Q值的更新。

用的是时间差分方法

时间差分方法

这里看了好多遍,做下整理。
假设有两种状态 s1,s2, 两种行为a1,a2。

Q a1 a2
s1 0 1
s2 0 2

无论在s1还是在s2中,a2的奖励都要比a1要高。
当处于状态s1时,大概率选择行为a2。假设s1选择a2之后就到了s2,如何更新Q(s1,a2)的值?

Q(s2, a2) 的值比 Q(s2, a1) 的大,所以我们把大的值乘上一个衰减值 gamma (比如0.9) 并加上到达 s2 时所获取的奖励 R(eward),奖励R是自己设计的
将这个作为我现实中 Q(s1, a2) 的值,之前是根据 Q 表估计 Q(s1, a2) 的值。
所以有了现实和估计值,我们就能更新Q(s1, a2),根据估计与现实的差距,将这个差距乘以一个学习效率 alpha 累加上旧的 Q(s1, a2) 的值,变成新的值。

Q learning 的重点就是在 Q(s1, a2) 现实中,也包含了一个 Q(s2) 的最大估计值,
将对下一步的衰减的最大估计和当前所得到的奖励当成这一步的现实。

最后总结这套算法中一些参数的意义。

  1. Epsilon Greedy 是用在决策上的一种策略,比如 epsilon = 0.9 时, 就说明有 90% 的情况我会按照 Q 表的最优值选择行为,10% 的时间使用随机选行为,这样做的目的是让其有机会跳出局部最优。

  2. alpha是学习率,来决定这次的误差有多少要被学习的,alpha是一个小于1 的数。

  3. gamma 是对未来 reward 的衰减值。

结合了蒙特卡罗的采样方法和动态规划方法的bootstrapping(利用后继状态的值函数估计当前值函数)使得他可以适用于model-free的算法并且是单步更新,速度更快。值函数计算方式如下

V(s)←V(s)+α(Rt+1+γV(s′)−V(s))

其中Rt+1+γV(s′) 被称为TD目标,δt=Rt+1+γV(s′)−V(s)δt​=Rt+1​+γV(s′)−V(s) 称为TD偏差。

具体介绍:https://zhuanlan.zhihu.com/p/25913410

2019.3.6

迷宫

今天学习一下 pyqt5 的作图,然后根据 https://www.cnblogs.com/hhh5460/p/10145797.html 的代码自己用pyqt5做一个迷宫。初步构思是用三个类,迷宫图形,Q-learning算法和main主程序。

一步一步来。

借鉴了这位博主 https://www.cnblogs.com/hhh5460/p/10145797.html 的文章,我用pyqt5重做了一下,样子跟他差不多

毕设日志(一)
接下来要完成的是
1)玩家的移动,也就是红色圆圈的移动,要用到self.update()函数
2)Q-Learning算法,能不能写一个效率较高的算法?加一点自己的理解,比如终点在玩家的左下方,我可不可以让玩家在没有头绪的时候,也就是Q值都差不多的时候给他一点明确的方向?这样做有没有副作用?

2019.3.7

大致完成了,还有一点小问题。
我把迷宫改了一下

毕设日志(一)

游戏分为两个阶段:learn 和 play。这两个阶段都在另一个线程里完成,与主界面分离,每次执行一步就返回一个消息 msg,主界面每次接受信号就刷新界面,红色小球移动一格。

部分代码:
maze_graph.py

import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QBrush, QPen, QFont
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import time
from gl import *
from maze_Q_learning import Agent


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 300, W, H)
        self.setWindowTitle("maze")
        self.search_imfomation_from_maze()

        self.learning = TrainModel()
        self.learning._signal.connect(self.callbacklog)
        self.learning.start()

        self.show()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        self.drawLines(qp)
        self.drawTrap(qp)
        self.drawTreasure(qp)
        self.drawStartEnd(qp)
        self.drawPlayer(qp)
        qp.end()

    def search_imfomation_from_maze(self):
        self.traps_location = []
        self.treasure_locations = []
        for i in range(MAZE_H):
            for j in range(MAZE_W):
                if MAZE[i][j] == 1:
                    self.traps_location.append([i, j])
                elif MAZE[i][j] == 2:
                    self.treasure_locations.append([i, j])
                elif MAZE[i][j] == -1:
                    self.start = [i, j]
                elif MAZE[i][j] == 3:
                    self.end = [i, j]
        self.state = self.start[:]

    def drawLines(self, qp):
        pen = QPen(Qt.black, 1, Qt.SolidLine)
        qp.setPen(pen)
        for c in range(0, W, UNIT):
            qp.drawLine(c, 0, c, H)
        for r in range(0, H, UNIT):
            qp.drawLine(0, r, W, r)

    def drawTrap(self, qp):
        pen = QPen(Qt.white, 1, Qt.SolidLine)
        qp.setPen(pen)
        qp.setBrush(QColor(800, 0, 0))
        for trap in self.traps_location:
            qp.drawRect(PADDING + UNIT * trap[1], PADDING + UNIT * trap[0], UNIT - PADDING * 2,
                        UNIT - PADDING * 2)  ##(起点坐标,终点坐标,长,宽)

    def drawTreasure(self, qp):
        pen = QPen(Qt.white, 1, Qt.SolidLine)
        qp.setPen(pen)
        qp.setBrush(QColor(220, 250, 0))
        for treasure in self.treasure_locations:
            qp.drawRect(PADDING + UNIT * treasure[1], PADDING + UNIT * treasure[0], UNIT - PADDING * 2,
                        UNIT - PADDING * 2)  ##(起点坐标,终点坐标,长,宽)

    def drawStartEnd(self, qp):
        qp.setPen(QColor(800, 0, 0))
        qp.setFont(QFont("Decorative", 10))
        qp.drawText(PADDING + UNIT * self.start[1], PADDING + UNIT * self.start[0], UNIT - PADDING * 2,
                    UNIT - PADDING * 2, Qt.AlignCenter, '起点')
        qp.drawText(PADDING + UNIT * self.end[1], PADDING + UNIT * self.end[0], UNIT - PADDING * 2,
                    UNIT - PADDING * 2, Qt.AlignCenter, '终点')

    def drawPlayer(self, qp):
        pen = QPen(Qt.white, 2, Qt.SolidLine)
        qp.setPen(pen)
        qp.setBrush(QColor(255, 0, 0))
        qp.drawEllipse(PADDING + UNIT * self.state[1], PADDING + UNIT * self.state[0], UNIT - PADDING * 2,
                       UNIT - PADDING * 2)

    def callbacklog(self, msg):
        self.state[0] = msg // MAZE_W
        self.state[1] = msg % MAZE_W
        # print("state", self.state)
        # print("------------------------------")
        self.update()


class TrainModel(QThread):
    _signal = pyqtSignal(int)
    agent = Agent()

    def __int__(self):
        super(TrainModel, self).__init__()

    def run(self):
        print("--------Learning---------")
        for i in range(40000):
            self.agent.learn_step_by_step()
            msg = self.agent.return_msg
            self._signal.emit(msg)
            # time.sleep(0.2)

        print("--------Playing----------")
        for i in range(50):
            time.sleep(1)  # stop for watching
            if i == 0: # restart
                self.agent.learn_step_by_step(epsilon=1, start_=True)
            else:
                self.agent.learn_step_by_step(epsilon=1)
            msg = self.agent.return_msg
            self._signal.emit(msg)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

gl.py

import pandas as pd

"""
    Only need to change these constant
"""

UNIT = 40  # pixels
PADDING = 5
MAZE_H = 9  # grid height
MAZE_W = 12  # grid width
H = MAZE_H * UNIT + 1  # height
W = MAZE_W * UNIT + 1  # weight

STATES = range(MAZE_H * MAZE_W)
ACTIONS = list('udlr')
# MAZE = [[-1, 1, 1, 0, 0, 0, ],
#         [0, 1, 0, 0, 1, 0, ],
#         [0, 1, 0, 1, 0, 0, ],
#         [0, 0, 0, 1, 0, 0, ],
#         [0, 1, 0, 1, 2, 0, ],
#         [0, 0, 0, 1, 0, 3, ]]

MAZE = [[-1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
        [0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
        [0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0],
        [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0],
        [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0],
        [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
        [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0],
        [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 3], ]

REWARDS = {
    -1: 0,
    0: 0,
    1: -10,
    2: 2,
    3: 20}
#  -1:start
#  0: path
#  1:trap  -10point
#  2:treasure   2point
#  3:end    20point

Q_TABLE = pd.DataFrame(data=[[0 for _ in ACTIONS] for _ in STATES],
                       index=STATES,

学习的时候我设定的步数是40000,epsilon=0.7,学习完以后根据Q表策略进行游戏,看看在50步以内能不能完成,此时epsilon=1。

结果还可以,小球在play的时候可以以最短路径到达终点。我做这个只是方便自己理解Q-learning,包括Q值的更新和参数对它的影响,这些可以比较直观的看出来。但是还有些问题。

1)我发现奖惩的数值对游戏的影响特别大,这在设计其他的东西时也要注意。

2)还有,Q-learning是一种model-free(不用学习环境模型)的基于off-policy(学习过程中执行的策略与值估计使用的策略不一样)的temporal difference(TD)方法。而我的代码只是在线学习,并没有将策略存储到本地,学习时随机选择策略。这就导致很可能会陷入局部最优解。就像我一开始加入奖励区域时,小球会不断地进出奖励区域,从而陷入死循环。

3)这是第一行12个小格子的Q值

             u          d          l          r
0     0.000000   5.261027   0.000000  -6.254604
1     0.000000  -6.244355   4.639746  -7.812449
2     0.000000  -5.921415  -6.853677   6.729724
3     0.000000   6.470706  -4.861265   7.546971
4     0.000000  -3.667872   6.746933   7.944251
5     0.000000  -2.672183   6.776512   8.362396
6     0.000000   8.802530   7.364814  -0.158281
7     0.000000  -5.032490   2.276062  11.366744
8     0.000000  10.487153   0.417934  11.974739
9     0.000000   2.338169  11.146606  12.604988
10    0.000000  13.268409  11.828161  10.586115
11    0.000000  -0.490233  12.289693   0.000000

可以发现有陷阱的那一个方向的值基本都是负的,但是往回走的值仅仅比正确方向的值略小,说明在学习的时候走了很多弯路,浪费了很多时间。

Q1:接下来要做什么?
A1:学习DQN,改进程序。具体要做的是:复习tensorflow,神经网络,将算法改成离线学习,优化算法提高效率,最主要的是将效率数值化,怎么评估他的学习速度。

2019.3.8

先复习tensorflow基础

从官网 http://www.tensorfly.cn/tfdoc/get_started/basic_usage.html 上了解到:

TensorFlow

使用 TensorFlow, 你必须明白 TensorFlow:

使用图 (graph) 来表示计算任务.
在被称之为 会话 (Session) 的上下文 (context) 中执行图.
使用 tensor 表示数据.
通过 变量 (Variable) 维护状态.
使用 feed 和 fetch 可以为任意的操作(arbitrary operation) 赋值或者从其中获取数据.

综述

TensorFlow 是一个编程系统, 使用来表示计算任务. 图中的节点被称之为 op (operation 的缩写). 一个 op 获得 0 个或多个 Tensor, 执行计算, 产生 0 个或多个 Tensor. 每个 Tensor 是一个类型化的多维数组. 例如, 你可以将一小组图像集表示为一个四维浮点数数组, 这四个维度分别是 [batch, height, width, channels].

一个 TensorFlow 图描述了计算的过程. 为了进行计算, 图必须在 会话 里被启动. 会话 将图的 op 分发到诸如 CPU 或 GPU 之类的 设备 上, 同时提供执行 op 的方法. 这些方法执行后, 将产生的 tensor 返回. 在 Python 语言中, 返回的 tensor 是 numpy ndarray 对象; 在 C 和 C++ 语言中, 返回的 tensor 是 tensorflow::Tensor 实例.

构建图

import tensorflow as tf

# 创建一个常量 op, 产生一个 1x2 矩阵. 这个 op 被作为一个节点
# 加到默认图中.
#
# 构造器的返回值代表该常量 op 的返回值.
matrix1 = tf.constant([[3., 3.]])

# 创建另外一个常量 op, 产生一个 2x1 矩阵.
matrix2 = tf.constant([[2.],[2.]])

# 创建一个矩阵乘法 matmul op , 把 'matrix1' 和 'matrix2' 作为输入.
# 返回值 'product' 代表矩阵乘法的结果.
product = tf.matmul(matrix1, matrix2)

启动图

# 启动默认图.
sess = tf.Session()

# 调用 sess 的 'run()' 方法来执行矩阵乘法 op, 传入 'product' 作为该方法的参数. 
# 上面提到, 'product' 代表了矩阵乘法 op 的输出, 传入它是向方法表明, 我们希望取回
# 矩阵乘法 op 的输出.
#
# 整个执行过程是自动化的, 会话负责传递 op 所需的全部输入. op 通常是并发执行的.
# 
# 函数调用 'run(product)' 触发了图中三个 op (两个常量 op 和一个矩阵乘法 op) 的执行.
#
# 返回值 'result' 是一个 numpy `ndarray` 对象.
result = sess.run(product)
print(result)
# ==> [[ 12.]]

# 任务完成, 关闭会话.
sess.close()

Session 对象在使用完后需要关闭以释放资源. 除了显式调用 close 外, 也可以使用 “with” 代码块 来自动完成关闭动作.

with tf.Session() as sess:
  result = sess.run([product])
  print result

变量

Variables for more details. 变量维护图执行过程中的状态信息. 下面的例子演示了如何使用变量实现一个简单的计数器. 参见 变量 章节了解更多细节.

# 创建一个变量, 初始化为标量 0.
state = tf.Variable(0, name="counter")

# 创建一个 op, 其作用是使 state 增加 1

one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

# 启动图后, 变量必须先经过`初始化` (init) op 初始化,
# 首先必须增加一个`初始化` op 到图中.
init_op = tf.initialize_all_variables()

# 启动图, 运行 op
with tf.Session() as sess:
  # 运行 'init' op
  sess.run(init_op)
  # 打印 'state' 的初始值
  print(sess.run(state))
  # 运行 op, 更新 'state', 并打印 'state'
  for _ in range(3):
    sess.run(update)
    print(sess.run(state))

# 输出:

# 0
# 1
# 2
# 3

Fetch

为了取回操作的输出内容, 可以在使用 Session 对象的 run() 调用 执行图时, 传入一些 tensor, 这些 tensor 会帮助你取回结果. 在之前的例子里, 我们只取回了单个节点 state, 但是你也可以取回多个 tensor:

input1 = tf.constant(3.0)
input2 = tf.constant(2.0)
input3 = tf.constant(5.0)
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)

with tf.Session():
  result = sess.run([mul, intermed])
  print result

# 输出:
# [array([ 21.], dtype=float32), array([ 7.], dtype=float32)]

Feed

上述示例在计算图中引入了 tensor, 以常量或变量的形式存储. TensorFlow 还提供了 feed 机制, 该机制 可以临时替代图中的任意操作中的 tensor 可以对图中任何操作提交补丁, 直接插入一个 tensor.

feed 使用一个 tensor 值临时替换一个操作的输出结果. 你可以提供 feed 数据作为 run() 调用的参数. feed 只在调用它的方法内有效, 方法结束, feed 就会消失. 最常见的用例是将某些特殊的操作指定为 “feed” 操作, 标记的方法是使用 tf.placeholder() 为这些操作创建占位符.


input1 = tf.placeholder(tf.types.float32)
input2 = tf.placeholder(tf.types.float32)
output = tf.mul(input1, input2)

with tf.Session() as sess:
  print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))

# 输出:
# [array([ 14.], dtype=float32)]

看完基础待会再看CNN。

头疼,还是keras简单。。

学习一下 交叉熵损失函数 ,这个看到过很多次了

交叉熵

先记住公式:L=−[ylog y^+(1−y)log (1−y^)]

查了一些资料,发现还是从 信息论的角度去理解比较好
资料来源:https://blog.csdn.net/weixin_37567451/article/details/80895309

主要搞清楚两个问题:

  1. 什么是交叉熵?
  2. 为什么要用交叉熵?

交叉熵原来是用来估算平均编码长度的。给定两个概率分布p和q,通过q来表示p的交叉熵为:
毕设日志(一)
什么意思?它刻画的是通过概率分布q来表达概率分布p的困难程度,p代表正确答案,q代表的是预测值,交叉熵越小,两个概率的分布越接近。

而神经网络模型的最后通常会经过一个 Sigmoid 函数,输出一个概率值。

Sigmoid 函数图形如下所示:

毕设日志(一)
这样就把神经网络的输出也变成了一个概率分布,从而可以通过交叉熵来计算预测的概率分布和真实答案的概率分布之间的距离了。

举个例子,假设有一个3分类问题,某个样例的正确答案是(1,0,0),这个模型经过softmax回归之后的预测答案是(0.5,0.4,0.1),那么预测和正确答案之间的交叉熵为:

毕设日志(一)
如果另一个模型的预测是(0.8,0.1,0.1),那么这个预测值和真实值之间的交叉熵是:

毕设日志(一)

为什么要用交叉熵来做损失函数,不用MSE(Mean Squared Error)?

先记住结论:只有当损失函数为凸函数时,梯度下降算法才能保证达到全局最优解,MSE的损失函数是非凸函数,在分类问题中,并不是一个好的损失函数。相对MSE而言,交叉熵曲线整体呈单调性,损失越大,梯度越大。便于梯度下降反向传播,利于优化。所以一般针对分类问题采用交叉熵作为损失函数。

TensorFlow实现交叉熵

cross_entropy=-tf.reduce_mean(y_*tf.log(tf.clip_by_value(y,1e-10,1.0)))

其中y_就代表p,也就是正确结果(或者说标签),y代表q,也就是预测结果(或者说实际输出)。

这里的tf.reduce_mean()是求一个平均值,具体是这样的:

  1. tf.reduce_mean(x):就是求所有元素的平均值;

  2. tf.reduce_mean(x,0):就是求维度为0的平均值,也就是求列平均;

  3. tf.reduce_mean(x,1):就是求维度为1的平均值,也就是求行平均。

这里的tf.clip_by_value(v,a,b):表示把v限制在a~b的范围内,小于a的让它等于a,大于b的让它等于b.

因为交叉熵一般会与softmax回归一起使用,所以TensorFlow对这两个功能进行了同一封装,并提供了tf.nn.softmax_cross_entropy_with_logits函数。比如可以直接通过以下代码实现了sotfmax回归之后的交叉熵损失函数:

cross_entropy=tf.nn.sparse_sotfmax_cross_entropy_with_logits(label=y_,logits=y)

再问几个问题

  1. **函数的作用?
  2. 为什么要用sigmoid函数作为**函数?
  3. 还有什么**函数?

如果不用: 每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合

如果用: **函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。

sigmoid

优点:

  1. sigmoid函数也叫 Logistic 函数,用于隐层神经元输出,取值范围为(0,1),它可以将一个实数映射到(0,1)的区间,可以用来做二分类。
  2. 在特征相差比较复杂或是相差不是特别大时效果比较好。

缺点

  1. **函数计算量大,反向传播求误差梯度时,求导涉及除法
  2. 反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练
  3. Sigmoids函数饱和且kill掉梯度。
  4. Sigmoids函数收敛缓慢。

为何会出现梯度消失?:

反向传播算法中,要对**函数求导,sigmoid 的导数表达式为:

毕设日志(一)
sigmoid 原函数及导数图形如下:

毕设日志(一)导数从 0 开始很快就又趋近于 0 了,易造成梯度消失现象。

除此之外,还有 ReLU,softmax 等等**函数。