图像标注 python实现-LSTM篇

上一篇文章介绍了RNN的工作原理和在图像标注上的应用,这篇介绍RNN的变体LSTM。

要知道为什么会出现LSTM,首先来看RNN存在什么问题。RNN由于激活函数和本身结构的问题,存在梯度消失的现象,导致

(1)网络结构不能太深,不然深层网络的梯度可以基本忽略,没有起到什么作用,白白增加训练时间。

(2)只能形成短期记忆,不能形成长期记忆。 因为梯度逐层减少,只有比较临近的层梯度才会相差不多,所以对临近的信息记忆比较多,对较远的信息记忆差。

接下来看看LSTM怎么解决这个问题:

所有 RNN 都具有一种重复神经网络模块的链式的形式。在标准的 RNN 中,这个重复的模块只有一个非常简单的结构,例如一个 tanh 层。

正是由于tanh导数恒小于1,出现的梯度越来越小的问题。

LSTM通过更加精细的在每步时间节点的计算克服梯度问题,RNN把一个状态信息用一次计算更新,LSTM用四次计算来更新,即“四个门”。

原本我是把h叫做状态的,但是在这个新的结构里,h叫状态已经不太合适了,有一个新的变量C才是真正意义上的状态,好像大部分资料都把C叫做细胞状态,至于h,我现在也不知道怎么称呼它好了,它确实也在传播状态信息,但是却是可有可无的,因为h的值完全可以由C得到,不显式传递也不会有任何问题。

在RNN中,$x_t\in \mathbb{R}^{D} ,h_t\in \mathbb{R}^H, W_x\in\mathbb{R}^{H\times D}, W_h\in\mathbb{R}^{H\times H},b\in\mathbb{R}^{H}$。

在LSTM中,$x_t\in \mathbb{R}^{D}, h_t\in \mathbb{R}^H, W_x\in\mathbb{R}^{4H\times D}, W_h\in\mathbb{R}^{4H\times H},b\in\mathbb{R}^{4H}$。

第一步还是一样,$a\in\mathbb{R}^{4H}$ , $a=W_xx_t + W_hh_{t-1}+b$,RNN得到a直接就可以直接激活当作下一个状态了,而LSTM中得到了四个输出。

$$
\begin{align*}
i = \sigma(a_i) \hspace{2pc}
f = \sigma(a_f) \hspace{2pc}
o = \sigma(a_o) \hspace{2pc}
g = \tanh(a_g)
\end{align*}
$$

i,f,o,g分别叫做输入门,遗忘门,输出门,阻塞门,$i,f,o,g\in\mathbb{R}^H$。

$$
c_{t} = f\odot c_{t-1} + i\odot g \hspace{4pc}
h_t = o\odot\tanh(c_t)
$$

下面来理解一下这些公式:

“遗忘“可以理解为“之前的内容记住多少“,其精髓在于只能输出(0,1)小数的sigmoid函数和粉色圆圈的乘法,LSTM网络经过学习决定让网络记住以前百分之多少的内容。

下一步是确定什么样的新信息被存放在细胞状态中。这里包含两个部分。第一,sigmoid 层称 “输入门层” 决定什么值我们将要更新。然后,一个 tanh 层称“阻塞门层”创建一个新的候选值向量加入到状态中。下一步,我们会讲这两个信息来产生对状态的更新。

最终,我们需要确定输出什么值。这个输出将会基于我们得到的的新的细胞状态和“输出门”的信息。

有了遗忘门和输入门之后,在对h求导,这时候的导数已经不是恒小于1了,所以克服了梯度消失的问题。(关于RNN和LSTM的梯度可以在代码中很清晰的看到对比,当然也可以手动直接求梯度,也可以发现LSTM中h的梯度不是恒小于1)。

正向计算和反向求梯度

def lstm_step_forward(x, prev_h, prev_c, Wx, Wh, b):
    """
    Forward pass for a single timestep of an LSTM.

    The input data has dimension D, the hidden state has dimension H, and we use
    a minibatch size of N.

    Inputs:
    - x: Input data, of shape (N, D)
    - prev_h: Previous hidden state, of shape (N, H)
    - prev_c: previous cell state, of shape (N, H)
    - Wx: Input-to-hidden weights, of shape (D, 4H)
    - Wh: Hidden-to-hidden weights, of shape (H, 4H)
    - b: Biases, of shape (4H,)

    Returns a tuple of:
    - next_h: Next hidden state, of shape (N, H)
    - next_c: Next cell state, of shape (N, H)
    - cache: Tuple of values needed for backward pass.
    """
    next_h, next_c, cache = None, None, None
    #############################################################################
    # TODO: Implement the forward pass for a single timestep of an LSTM.        #
    # You may want to use the numerically stable sigmoid implementation above.  #
    #############################################################################
    H=Wh.shape[0]
    a = np.dot(x, Wx) + np.dot(prev_h, Wh) + b    # (1)
    i = sigmoid(a[:, 0:H])                # (2-5)
    f = sigmoid(a[:, H:2*H])
    o = sigmoid(a[:, 2*H:3*H])
    g = np.tanh(a[:, 3*H:4*H])
    next_c = f * prev_c + i * g           # (6)
    next_h = o * np.tanh(next_c)          # (7)

    cache = (i, f, o, g, x, Wx, Wh, prev_c, prev_h,next_c)

    return next_h, next_c, cache

def lstm_step_backward(dnext_h, dnext_c, cache):
    """
    Backward pass for a single timestep of an LSTM.

    Inputs:
    - dnext_h: Gradients of next hidden state, of shape (N, H)
    - dnext_c: Gradients of next cell state, of shape (N, H)
    - cache: Values from the forward pass

    Returns a tuple of:
    - dx: Gradient of input data, of shape (N, D)
    - dprev_h: Gradient of previous hidden state, of shape (N, H)
    - dprev_c: Gradient of previous cell state, of shape (N, H)
    - dWx: Gradient of input-to-hidden weights, of shape (D, 4H)
    - dWh: Gradient of hidden-to-hidden weights, of shape (H, 4H)
    - db: Gradient of biases, of shape (4H,)
    """
    dx, dh, dc, dWx, dWh, db = None, None, None, None, None, None
    #############################################################################
    # TODO: Implement the backward pass for a single timestep of an LSTM.       #
    #                                                                           #
    # HINT: For sigmoid and tanh you can compute local derivatives in terms of  #
    # the output value from the nonlinearity.                                   #
    #############################################################################

    i, f, o, g, x, Wx, Wh, prev_c, prev_h,next_c =cache

    do=dnext_h*np.tanh(next_c)
    dnext_c+=o*(1-np.tanh(next_c)**2)*dnext_h
    di,df,dg,dprev_c=dnext_c*(g,prev_c,i,f)
    da=np.hstack([i*(1-i)*di,f*(1-f)*df,o*(1-o)*do,(1-g*g)*dg])

    dx=np.dot(da,Wx.T)
    dWx=np.dot(x.T,da)
    dprev_h=np.dot(da,Wh.T)
    dWh=np.dot(prev_h.T,da)
    db=np.sum(da,axis=0)

    return dx, dprev_h, dprev_c, dWx, dWh, db

def lstm_forward(x, h0, Wx, Wh, b):
    """
    Forward pass for an LSTM over an entire sequence of data. We assume an input
    sequence composed of T vectors, each of dimension D. The LSTM uses a hidden
    size of H, and we work over a minibatch containing N sequences. After running
    the LSTM forward, we return the hidden states for all timesteps.

    Note that the initial cell state is passed as input, but the initial cell
    state is set to zero. Also note that the cell state is not returned; it is
    an internal variable to the LSTM and is not accessed from outside.

    Inputs:
    - x: Input data of shape (N, T, D)
    - h0: Initial hidden state of shape (N, H)
    - Wx: Weights for input-to-hidden connections, of shape (D, 4H)
    - Wh: Weights for hidden-to-hidden connections, of shape (H, 4H)
    - b: Biases of shape (4H,)

    Returns a tuple of:
    - h: Hidden states for all timesteps of all sequences, of shape (N, T, H)
    - cache: Values needed for the backward pass.
    """
    h, cache = None, None
    #############################################################################
    # TODO: Implement the forward pass for an LSTM over an entire timeseries.   #
    # You should use the lstm_step_forward function that you just defined.      #
    #############################################################################
    N,T,D=x.shape
    H=h0.shape[1]
    h=np.zeros((N,T,H))
    cache={}
    prev_h=h0
    prev_c=np.zeros((N,H))

    for t in range(T):
        xt=x[:,t,:]
        next_h,next_c,cache[t]=lstm_step_forward(xt,prev_h,prev_c,Wx,Wh,b)
        prev_h=next_h
        prev_c=next_c
        h[:,t,:]=prev_h

    return h, cache

def lstm_backward(dh, cache):
    """
    Backward pass for an LSTM over an entire sequence of data.]

    Inputs:
    - dh: Upstream gradients of hidden states, of shape (N, T, H)
    - cache: Values from the forward pass

    Returns a tuple of:
    - dx: Gradient of input data of shape (N, T, D)
    - dh0: Gradient of initial hidden state of shape (N, H)
    - dWx: Gradient of input-to-hidden weight matrix of shape (D, 4H)
    - dWh: Gradient of hidden-to-hidden weight matrix of shape (H, 4H)
    - db: Gradient of biases, of shape (4H,)
    """
    dx, dh0, dWx, dWh, db = None, None, None, None, None
    #############################################################################
    # TODO: Implement the backward pass for an LSTM over an entire timeseries.  #
    # You should use the lstm_step_backward function that you just defined.     #
    #############################################################################
    N, T, H = dh.shape
    D = cache[0][4].shape[1]

    dprev_h = np.zeros((N, H))
    dprev_c = np.zeros((N, H))
    dx = np.zeros((N, T, D))
    dh0 = np.zeros((N, H))
    dWx= np.zeros((D, 4*H))
    dWh = np.zeros((H, 4*H))
    db = np.zeros((4*H,))

    for t in range(T):
        t = T-1-t
        step_cache = cache[t]
        dnext_h = dh[:,t,:] + dprev_h
        dnext_c = dprev_c
        dx[:,t,:], dprev_h, dprev_c, dWxt, dWht, dbt = lstm_step_backward(dnext_h, dnext_c, step_cache)
        dWx, dWh, db = dWx+dWxt, dWh+dWht, db+dbt

    dh0 = dprev_h 

    return dx, dh0, dWx, dWh, db

剩下的代码和昨天几乎没有区别,还有昨天代码中留下了一个问题,现在应该也能轻松解答了吧hhh。

参考:

https://blog.csdn.net/zhaojc1995/article/details/80572098

http://cs231n.github.io

原文地址:https://www.cnblogs.com/super-JJboom/p/9572169.html

时间: 2024-08-29 07:36:31

图像标注 python实现-LSTM篇的相关文章

问道python之基础篇【一】 认识python

问道python之基础篇[一] 认识python 1.python的简介 1.1.什么是python? Python是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年发明,第一个公开发行版发行于1991年. Python语法简洁清晰,特色之一是强制用空白符作为语句缩进. Python具有丰富和强大的库.它常被昵称为胶水语言,能够用其他语言制作的各种模块(尤其是C++)很轻松地联结在一起. 1.2.python的特点 简单,易学,免费.开源,高层语言,移植性,

开源图像标注工具labelme的安装使用及汉化

一 LabelMe简介 labelme是麻省理工(MIT)的计算机科学和人工智能实验室(CSAIL)研发的图像标注工具,人们可以使用该工具创建定制化标注任务或执行图像标注,项目源代码已经开源. 项目开源地址:https://github.com/CSAILVision/LabelMeAnnotationTool labelMe项目地址:http://labelme.csail.mit.edu/Release3.0/ MIT的labelme源代码可以安装到服务器上使用,是一个在线的Javascri

问道python之基础篇【二】python入门

问道python之基础篇[二] Python入门 1.python的工作过程 python解释器把源代码转换为字节码的中间形式,然后再把它翻译成机器使用的机器语言并运行. 2.pyc文件 2.1.什么是pyc文件 执行python代码时,如果导入了其他的.py文件,那么在执行过程中会自动生成一个与其同名的.pyc文件,该文件就是python解释器编译之后产生的字节码. ps:代码经过编译可以产生字节码:字节码通过反编译也可以得到代码. Pyc文件一般由3个部分组成: 最开始4个字节是一个Maig

Python学习基础篇第一篇——快速入门(适合初学者)

一.Python学习基础篇第一篇--(快速入门) 建议从Python2.7开始学习,Python2.7可以支持扩展大量的第三方类库,是目前比较成熟的版本 编写代码的软件推荐将python自带的IDLE和PyCharm集成IDE结合起来使用 1.1 Python命令行 Python命令行将以 >>> 开始,比如 >>>print 'Hello World!' 对于验证简单的命令可以在python自带的IDLE中完成  1.2 在Python自带的IDLE写一段小程序 在所

怒学Python——第四篇——函数与模块

Python的函数:其实和C++非常类似,不过由于是弱类型的语言(用起来感觉是......),把那些类型声明都去掉了,传的是值不是引用(至少2.7是),有一点点小区别是前面必须加def来定义(好像宏定义的样子......),下面给出示例 def sum(a, b): #sum为函数名,注意冒号...... c = a + b #这一部分是函数的语句块 return c #到这里函数就结束了,和C++一样 print sum(1, 2) #根据函数名调用函数,输出1+2的值 这里也有lambda函

怒学Python——第三篇——结构控制

众所周知,程序语句运行的结构无非是顺序结构.分支结构和循环结构,Python也是如此,顺序结构过于简单不提,简单记录一下分支和循环与C++的异同点,顺便提一下,对于已经会C++的人来说,学Python应该注意的是语句块通过缩进来控制. 分支结构:都使用if.else,多出了一个elif代替C++的else if,本质相同,就不给出例子 循环结构:和C++对比,没有了do..until语句,有while和for,里面同样使用break来结束循环,使用continue来进入下一个循环,但for出现了

第六篇:Python函数进阶篇

在了解完了 Python函数基础篇之后,本篇的存在其实是为了整合知识,由于该篇的知识是否杂乱,故大家可以通过点开点连接直接进入其详细介绍,该篇主要大致的介绍一下几个知识点:  一.Python的迭代器和生成器 二.Python的内置函数 三.Python的open函数之文件处理 四.Python的递归函数 五.Python的高阶函数 六.Python的装饰器 一.Python的迭代器和生成器: 本篇我们介绍可迭代对象和迭代器对象--包括两者的定义及区别.为什么要有迭代器对象,其的用法是怎么样的

深度学习图像标注工具VGG Image Annotator (VIA)使用教程

VGG Image Annotator (VIA)是一款开源的图像标注工具,由Visual Geometry Group开发. 可以在线和离线使用,可标注矩形.圆.椭圆.多边形.点和线.标注完成后,可以导出为csv和json文件格式. 地址:http://www.robots.ox.ac.uk/~vgg/software/via/. 中文使用教程: 原文:https://blog.csdn.net/heiheiya/article/details/81530952 原文地址:https://ww

Python+Selenium练习篇之1-摘取网页上全部邮箱

前面已经介绍了Python+Selenium基础篇,通过前面几篇文章的介绍和练习,Selenium+Python的webUI自动化测试算是入门了.接下来,我计划写第二个系列:练习篇,通过一些练习,了解和掌握一些Selenium常用的接口或者方法. 练习场景:在某一个网页上有些字段是我们感兴趣的,我们希望摘取出来,进行其他操作.但是这些字段可能在一个网页的不同地方.例如,我们需要在关于百度页面-联系我们,摘取全部的邮箱. 思路拆分: 1. 首先,需要得到当前页面的source内容,就像,打开一个页