1. 程式人生 > >第二十二節,TensorFlow中RNN實現一些其它知識補充

第二十二節,TensorFlow中RNN實現一些其它知識補充

pre 針對 arr state nim 很多 常常 位置 代價函數

一 初始化RNN

上一節中介紹了 通過cell類構建RNN的函數,其中有一個參數initial_state,即cell初始狀態參數,TensorFlow中封裝了對其初始化的方法。

1.初始化為0

對於正向或反向,第一個cell傳入時沒有之前的序列輸出值,所以需要對其進行初始化。一般來講,不用刻意取指定,系統會默認初始化為0,當然也可以手動指定其初始化為0.

initial_state = lstm_cell.zero_state(batch_size, dtype=tf.float32)

2.初始化為指定值

在確保創建組成RNN的cell時,設置了輸出為元組類型(即初始化state_is_tuple=True)的前提下,刻意使用LSTMStateTuple函數。但是有時想給lstm_cell的initial_state賦予我們想要的值,而不簡單的用0來初始化。

LSTMStateTuple(c ,h)

可以把 LSTMStateTuple() 看做一個op.

from tensorflow.contrib.rnn.python.ops.core_rnn_cell_impl import LSTMStateTuple

...
c_state = ...
h_state = ...
# c_state , h_state 都為Tensor
initial_state = LSTMStateTuple(c_state, h_state)

當然,GRU就沒有這麽麻煩了,因為GRU沒有兩個state

二 優化RNN

RNN的優化技巧有很多,對於前面講述的神經網絡技巧大部分在RNN上都適用,但是也有例外,下面就來介紹下RNN自己特有的兩個優化方法的處理。

1.dropout功能

在RNN中,如果想使用dropout功能,不能使用以前的dropout。

def tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None):  # pylint: disable=invalid-name

因為RNN有自己的dropout,並且實現方式與RNN不一樣。

def tf.nn.rnn_cell.DropoutWrapper(self, cell, input_keep_prob=1.0,
output_keep_prob=1.0,state_keep_prob=1.0,
variational_recurrent=False,input_size=None,
dtype=None, seed=None):

使用例子:

lstm_cell = tf.nn.rnn_cell.DropoutWrapper(lstm_cell,output_keep_prob = 0.5)

從t-1時刻的狀態傳遞到t時刻進行計算,這中間不進行memory的dropout,僅在同一個t時刻中,多層cell之間傳遞信息時進行dropout。所以RNN的dropout方法會有兩個設置參數input_keep_prop(傳入cell的保留率)和output_keep_prob(輸出cell的保留率)。

  • 如果希望是input傳入cell時丟棄掉一部分input信息,就設置input_keep_prob,那麽傳入到cell的就是部分input。
  • 如果希望cell的output只有一部分作為下一層cell的input,就定義為output_keep_prob。

例子:

lstm_cell=tf.nn.rnn_cell.BasicLSTMCell(size,forget_bias=0.0,state_is_tuple=True)
lstm_cell=tf.nn.rnn_cell.DropoutWrapper(lstm_cell,output_keep_prob=0.5)

在上面代碼中,一個RNN層後面跟一個DropoutWrapper,是一種常見的用法。

RNN中dropout詳情請點擊這裏:RNN變體之dropout

2.LN基於層的歸一化

這部分內容是對應於批歸一化(BN)的。由於RNN的特殊結構,它的輸入不同於前面所講的全連接、卷積網絡。

  • 在BN中,每一層的輸入只考慮當前批次樣本(或批次樣本的轉換值)即可。
  • 但是在RNN中,每一層的輸入除了當前批次樣本的轉換值,還得考慮樣本中上一個序列樣本的輸出值,所以對於RNN的歸一化,BN算法不再適用,最小批次覆蓋不了全部的輸入數據,而是需要對於輸入BN的某一層來做歸一化,即layer-Normalization.

由於RNN的網絡都被LSTM,GRU這樣的結構給封裝起來,所以想實現LN並不像BN那樣直接在外層加一個BN層就可以,需要需要改寫LSTM或GRU的cell,對其內部的輸入進行歸一化處理。

TensorFlow中目前還不支持這樣的cell,所以需要開發者自己來改寫cell的代碼,具體的方法可以在下面例子中講到。

三 在GRUCell中實現LN

下面程序我們對每個樣本進行歸一化處理,即通過計算每個樣本的均值和方差,然後歸一化處理,註意這裏並不是批歸一化,批歸一化是針對batch_size個樣本計算均值方差,然後歸一化處理。代碼如下:

# -*- coding: utf-8 -*-
"""
Created on Thu May 17 14:35:38 2018

@author: zy
"""

‘‘‘
在GRUCell中實現LN
‘‘‘

import tensorflow as tf
import numpy as np
from tensorflow.contrib.rnn.python.ops.core_rnn_cell import  _linear
from tensorflow.contrib.rnn.python.ops.core_rnn_cell import array_ops
from tensorflow.contrib.rnn.python.ops.core_rnn_cell import RNNCell


tf.reset_default_graph()



def ln(tensor,scope=None,epsilon=1e-5):
    ‘‘‘
    沿著第二個軸層歸一化二維張量tensor(按行求平均,方差)
    這裏其實是對每個樣本進行歸一化處理  計算每個樣本的方差和均值然後歸一化處理  並不是批歸一化
    args:
        tensor:輸入張量,二維  大小為batch_size x num_units
        scope:命名空間
        epsilon:為了防止除數為0
    ‘‘‘
    assert(len(tensor.get_shape()) == 2)
    #沿著axis=1軸(即每個樣本),計算輸入張量的均值和方差  大小均為batch_size x 1
    m,v = tf.nn.moments(tensor,axes = 1,keep_dims=True)    
    if not isinstance(scope,str):
        scope = ‘‘
        
    #獲取共享變量  不存在則創建
    with tf.variable_scope(scope+layer_norm):
        #獲取縮放比例
        scale = tf.get_variable(name=scale,shape=[tensor.get_shape()[1]],initializer=tf.constant_initializer(1))
        #獲取偏移量
        shift = tf.get_variable(name=shift,shape=[tensor.get_shape()[1]],initializer=tf.constant_initializer(0))
        
    #歸一化處理 xi_bar = (xi - μ)/sqrt(ε + σ^2)
    ln_initial = (tensor - m)/tf.sqrt(v+epsilon)
    
    #yi = γ*xi_bar + β
    return ln_initial*scale + shift


class LNGRUCell(RNNCell):
    ‘‘‘
    創建GRU單元的類  使用了層歸一化
    ‘‘‘
    def  __init__(self,num_units,input_size=None,activation=tf.nn.tanh):
        ‘‘‘
        args:
            num_units:隱藏層節點個數
        ‘‘‘
        if input_size is not None:
            print(%s:The input_size parameter is deprecates.%self)
        self.__num_units = num_units
        self.__activation = activation
        
    @property
    def state_size(self):
        return self.__num_units
    
    @property
    def output_size(self):
        return self.__num_units
    
    def __call__(self,inputs,state):
        ‘‘‘
        args:
            inputs:這個時序的輸入xt  batch_size x n_inputs
            state:上一個時序的輸出ht_1  num_units x num_units 
        ‘‘‘
        with tf.variable_scope(Gates):
            ‘‘‘
            args: a 2D Tensor or a list of 2D, batch x n, Tensors.
            output_size: int, second dimension of W[i].
            bias: boolean, whether to add a bias term or not.
            bias_initializer: starting value to initialize the bias
            (default is all zeros).
            Returns:
                A 2D Tensor with shape [batch x output_size] equal to
                sum_i(args[i] * W[i]), where W[i]s are newly created matrices.
            ‘‘‘
            #計算加權值[Wz.[xt,ht_1]+bias,Wr.[xt,ht_1]+bias] 在計算的時候輸入x在前權重w在後 
            #即[batch_size x (n_inputs+num_units)] x [(n_inputs+num_units) x output_size]
            #print(‘inputs:‘,inputs.shape)   #inputs: (?, 28)
            #print(‘state:‘,state.shape)     #state: (?, 128)
            # batch_size x (2*__num_units)
            value = _linear([inputs,state],output_size=2*self.__num_units,bias=True,bias_initializer=tf.constant_initializer(1.0))          
            #分割成兩份 每份大小batch_size x __num_units
            r,u = array_ops.split(value=value,num_or_size_splits=2,axis=1)
            #層歸一化
            r = ln(r,scope=r/)
            #層歸一化
            u = ln(u,scope=u/)
            #計算rt,zt 大小均為大小batch_size x __num_units
            r,u = tf.nn.sigmoid(r),tf.nn.sigmoid(u)
            
        with tf.variable_scope(Candidate):
            #計算加權值W.[xt,rt*ht_1]   大小batch_size x __num_units
            Cand = _linear([inputs,r*state],self.__num_units,True)
            #層歸一化
            c_pre = ln(Cand,scope=new_h/)
            #計算ht_hat 大小batch_size x __num_units
            c = self.__activation(c_pre)
        #zt*ht_1 + (1-zt)*ht_hat   大小batch_size x __num_units
        new_h = u * state + (1 - u) * c
        return new_h,new_h

def single_layer_static_gru(input_x,n_steps,n_hidden):
    ‘‘‘
    返回靜態單層GRU單元的輸出,以及cell狀態
    
    args:
        input_x:輸入張量 形狀為[batch_size,n_steps,n_input]
        n_steps:時序總數
        n_hidden:gru單元輸出的節點個數 即隱藏層節點數
    ‘‘‘
    
    #把輸入input_x按列拆分,並返回一個有n_steps個張量組成的list 如batch_sizex28x28的輸入拆成[(batch_size,28),((batch_size,28))....] 
    #如果是調用的是靜態rnn函數,需要這一步處理   即相當於把序列作為第一維度 
    input_x1 = tf.unstack(input_x,num=n_steps,axis=1)

    #可以看做隱藏層
    gru_cell = LNGRUCell(num_units=n_hidden)
    #靜態rnn函數傳入的是一個張量list  每一個元素都是一個(batch_size,n_input)大小的張量 
    hiddens,states = tf.contrib.rnn.static_rnn(cell=gru_cell,inputs=input_x1,dtype=tf.float32)
        
    return hiddens,states


def  mnist_rnn_classfication():
    ‘‘‘
    1. 導入數據集
    ‘‘‘
    tf.reset_default_graph()
    from tensorflow.examples.tutorials.mnist import input_data
    
    #mnist是一個輕量級的類,它以numpy數組的形式存儲著訓練,校驗,測試數據集  one_hot表示輸出二值化後的10維
    mnist = input_data.read_data_sets(MNIST-data,one_hot=True)
    
    print(type(mnist)) #<class ‘tensorflow.contrib.learn.python.learn.datasets.base.Datasets‘>
    
    print(Training data shape:,mnist.train.images.shape)           #Training data shape: (55000, 784)
    print(Test data shape:,mnist.test.images.shape)                #Test data shape: (10000, 784)
    print(Validation data shape:,mnist.validation.images.shape)    #Validation data shape: (5000, 784)
    print(Training label shape:,mnist.train.labels.shape)          #Training label shape: (55000, 10)
    
    ‘‘‘
    2 定義參數,以及網絡結構
    ‘‘‘
    n_input = 28             #LSTM單元輸入節點的個數
    n_steps = 28             #序列長度
    n_hidden = 128           #LSTM單元輸出節點個數(即隱藏層個數)
    n_classes = 10           #類別
    batch_size = 128         #小批量大小
    training_step = 5000     #叠代次數
    display_step  = 200      #顯示步數
    learning_rate = 1e-4     #學習率  
    
    
    #定義占位符
    #batch_size:表示一次的批次樣本數量batch_size  n_steps:表示時間序列總數  n_input:表示一個時序具體的數據長度  即一共28個時序,一個時序送入28個數據進入LSTM網絡
    input_x = tf.placeholder(dtype=tf.float32,shape=[None,n_steps,n_input])
    input_y = tf.placeholder(dtype=tf.float32,shape=[None,n_classes])


    #可以看做隱藏層
    hiddens,states = single_layer_static_gru(input_x,n_steps,n_hidden)
                
    print(hidden:,hiddens[-1].shape)      #(128,128)
    
    #取LSTM最後一個時序的輸出,然後經過全連接網絡得到輸出值
    output = tf.contrib.layers.fully_connected(inputs=hiddens[-1],num_outputs=n_classes,activation_fn = tf.nn.softmax)
    
    ‘‘‘
    3 設置對數似然損失函數
    ‘‘‘
    #代價函數 J =-(Σy.logaL)/n    .表示逐元素乘
    cost = tf.reduce_mean(-tf.reduce_sum(input_y*tf.log(output),axis=1))
    
    ‘‘‘
    4 求解
    ‘‘‘
    train = tf.train.AdamOptimizer(learning_rate).minimize(cost)
    
    #預測結果評估
    #tf.argmax(output,1)  按行統計最大值得索引
    correct = tf.equal(tf.argmax(output,1),tf.argmax(input_y,1))       #返回一個數組 表示統計預測正確或者錯誤 
    accuracy = tf.reduce_mean(tf.cast(correct,tf.float32))             #求準確率
    
    
    #創建list 保存每一叠代的結果
    test_accuracy_list = []
    test_cost_list=[]
    
    
    with tf.Session() as sess:
        #使用會話執行圖
        sess.run(tf.global_variables_initializer())   #初始化變量    
        
        #開始叠代 使用Adam優化的隨機梯度下降法
        for i in range(training_step): 
            x_batch,y_batch = mnist.train.next_batch(batch_size = batch_size)   
            #Reshape data to get 28 seq of 28 elements
            x_batch = x_batch.reshape([-1,n_steps,n_input])
            
            #開始訓練
            train.run(feed_dict={input_x:x_batch,input_y:y_batch})   
            if (i+1) % display_step == 0:
                 #輸出訓練集準確率        
                training_accuracy,training_cost = sess.run([accuracy,cost],feed_dict={input_x:x_batch,input_y:y_batch})   
                print(Step {0}:Training set accuracy {1},cost {2}..format(i+1,training_accuracy,training_cost))
        
        
        #全部訓練完成做測試  分成200次,一次測試50個樣本
        #輸出測試機準確率   如果一次性全部做測試,內容不夠用會出現OOM錯誤。所以測試時選取比較小的mini_batch來測試
        for i in range(200):        
            x_batch,y_batch = mnist.test.next_batch(batch_size = 50)      
            #Reshape data to get 28 seq of 28 elements
            x_batch = x_batch.reshape([-1,n_steps,n_input])
            test_accuracy,test_cost = sess.run([accuracy,cost],feed_dict={input_x:x_batch,input_y:y_batch})
            test_accuracy_list.append(test_accuracy)
            test_cost_list.append(test_cost) 
            if (i+1)% 20 == 0:
                 print(Step {0}:Test set accuracy {1},cost {2}..format(i+1,test_accuracy,test_cost)) 
        print(Test accuracy:,np.mean(test_accuracy_list))


if __name__ == __main__:
    mnist_rnn_classfication()

技術分享圖片

我們可以最後的測試集準確率達到了97.62%,比上一節96.46%提升了不少。本例只是使用了一個GRUCell,在多個cell中LN的效果會更明顯些。

三 CTC網絡的loss

CTC(Connectionist Temporan Classification)是語音辨識中的一個關鍵技術,通過增減一個額外的Symbol代表NULL來解決疊字問題。

RNN的優勢在於處理連續的數據,在基於連續的時間序列分類任務中,常常會使用CTC的方法。

該方法主要體現在loss值處理上,通過對序列對不上的label添加blank(空label)的方式,將預測的輸出值與給定的label值在時間序列上對齊,通過交叉熵算法求出具體損失值。

比如在語音識別的例子中,對於一句語音有它的序列值與對應的文本,可以使用CTC的損失函數求出模型輸出和label之間的loss,再通過優化器的叠代訓練讓損失值變小的方式將模型訓練出來。

1.ctc_loss函數介紹

TensorFlow中提供了一個ctc_loss函數,其作用就是按照序列來處理輸出標簽和標準標簽之間的損失。因為也是成型的函數封裝,對於初學者內部實現不用花太多時間關註,只要會用即可。

def tf.nn.ctc_loss(labels, inputs, sequence_length,
             preprocess_collapse_repeated=False,
             ctc_merge_repeated=True,
             ignore_longer_outputs_than_inputs=False, time_major=True):
  • labels: An `int32` `SparseTensor`.`labels.indices[i, :] == [b, t]` means `labels.values[i]` stores the id for (batch b, time t).`labels.values[i]` must take on values in `[0, num_labels)`.See `core/ops/ctc_ops.cc` for more details.一個32位的系數矩陣張量(SparseTensor)

  • inputs: 3-D `float` `Tensor`.If time_major == False, this will be a `Tensor` shaped:`[batch_size , max_time , num_classes]`. If time_major == True (default), this will be a `Tensor` shaped: `[max_time, batch_size , num_classes]`.The logits。(常用變量logits表示),經過RNN後輸出的標簽預測值,三維的浮點型張量,當time_major為False時形狀為[batch_size,max_time,num_classes],否則為[max_time, batch_size , num_classes](默認值)。

  • sequence_length: 1-D `int32` vector, size `[batch_size]`.The sequence lengths.序列長度。

  • preprocess_collapse_repeated: Boolean. Default: False.If True, repeated labels are collapsed prior to the CTC calculation.是否需要預處理,將重復的label合並成一個label,默認是False.

  • ctc_merge_repeated: Boolean. Default: True.在計算時是否將每個non_blank(非空)重復的label當成單獨label來解釋,默認是True。

  • ignore_longer_outputs_than_inputs: Boolean. Default: False.If True, sequences with longer outputs than inputs will be ignored.

  • time_major: The shape format of the `inputs` Tensors.If True, these `Tensors` must be shaped `[max_time, batch_size, num_classes]`.If False, these `Tensors` must be shaped `[batch_size, max_time, num_classes]`.Using `time_major = True` (default) is a bit more efficient because it avoids transposes at the beginning of the ctc_loss calculation. However, most TensorFlow data is batch-major, so by this function also accepts inputs in batch-major form.決定inputs的數據格式。

對於preprocess_collapse_repeated與ctc_merge_repeated參數,都是對於ctc_loss中重復標簽處理的控制,各種情況組合如下表所示:

參數情況 說明

preprocess_collapse_repeated=True

ctc_merge_repeated=True

忽略全部重復標簽,只計算不重復的標簽

preprocess_collapse_repeated=False

ctc_merge_repeated=True

標準的CTC模式,也是默認模式,不做預處理,只運算時重復標簽不再當成獨立的標簽來計算

preprocess_collapse_repeated=True

ctc_merge_repeated=False

忽略全部重復標簽,只計算不重復的標簽,因為預處理時已經把重復的標簽去掉了

preprocess_collapse_repeated=False

ctc_merge_repeated=False

所有重復標簽都會參加計算

對於ctc_loss的返回值,仍然屬於loss的計算模式,當取批次樣本進行訓練時,同樣也需要對最終的ctc_loss求均值。

return A 1-D `float` `Tensor`, size `[batch]`, containing the negative log probabilities.

註意:對於重復標簽方面的ctc_loss計算,一般情況下默認即可。另外這裏有個隱含的規則,Inputs中的classes是指需要輸出多少類,在使用ctc_loss時,需要將clsses+1,即再多生成一個類,用於存放blank。因為輸入的序列與label並不是一一對應的,所以需要通過添加blank類,當對應不上時,最後的softmax就會將其生成到blank。具體做法就是在最後的輸出層多構建一個節點即可。

這個規則是ctc_loss內置的,否則當標準標簽label中的類索引等於Inputs中size-1時會報錯。

2.SparseTensor類型

前面提到了SparseTensor類型,這裏主要介紹一下。

首先介紹下稀疏矩陣,它是相對於密集矩陣而言的。

密集矩陣就是我們常見的矩陣。當密度矩陣中大部分的數都為0時,就可以使用一種更好的存儲方式(只是將矩陣中不為0的索引和值記錄下來)存儲。這種方式就可以大大節省內存控件,它就是"稀疏矩陣"。

稀疏矩陣在TensorFlow中的結構類型如下:

def tf.SparseTensor(indices, values, dense_shape):
  • Indices:就是前面所說的不為0的位置信息。它是一個二維的int64 張量或list、數組,shape為[N,ndims],N表示位置個數,ndims表示維度,指定了sparse tensor中的索引,例如indices=[[1,3],[2,4]],表示dense tense中對應索引為[1,3],[2,4]位置的元素的值不為0。
  • values:一維的張量或者list、數組,形狀為[N],存儲密集矩陣中不為0位置所對應的值,它要與indices裏的順序對應。例如,indices=[[1,3],[2,4]],values=[18,3.6],表示[1,3]的位置是18,[2,4]的位置是3.6.
  • dense_shape:一維的int64 張量或者list、數組,表示原來密度矩陣的形狀[ndims]。

3.生成SparseTensor

了解了SpareTensor類型之後,就可以按照參數來拼接出一個SpareTensor了。在實際應用中,常會用到需要將密集矩陣dense轉換成稀疏矩陣SparseTensor。代碼如下:

def dense_to_sparse(dense,dtype=np.int32):
    ‘‘‘
    把密集矩陣轉換為稀疏矩陣
    
    args:
        dense:密集矩陣
        dtype:稀疏矩陣值得類型
    ‘‘‘
    indices = []
    values = []
    
    #遍歷每一行
    for n,seq in enumerate(dense):        
        #在已存在的列表中添加新的列表內容  添加密集矩陣每個索引位置
        indices.extend(zip([n]*len(seq),range(len(seq))))
        #添加密集矩陣每個元素的值
        values.extend(seq)
        #print(‘n {0} ,seq {1} ,indices {2},values {3}‘.format(n,seq,indices,values))
        
    indices = np.asarray(indices,dtype = np.int64)
    values = np.asarray(values,dtype = dtype)    
    shape = np.asarray([len(dense),indices.max(0)[1]+1],dtype = np.int64)            
    return tf.SparseTensor(indices = indices,values = values,dense_shape = shape)

4.SparseTensor轉dense

TensorFlow中提供了一個這樣的函數:

def tf.sparse_tensor_to_dense(sp_input,
                           default_value=0,
                           validate_indices=True,
                           name=None):
  • sp_input:一個SparseTensor。

  • default_value:沒有指定索引的對應的默認值,默認是0。

  • validate_indices:布爾值,如果是True,該函數會檢查sp_input的indices的lexicographic order是否有重復。
  • name:返回tensor的名字前綴,可選。

5.levenshtein距離

前面介紹了ctc_loss是用來訓練時間序列分類模型的。評估模型時,一般常使用計算得到的levenshtein距離值作為模型的評分(正確率或錯誤率)。

levenshtein距離又叫編輯距離(Edit Distance),是指兩個字符串之間,由一個轉成另一個所需的最小編輯操作次數。許可的編輯操作包括:將一個字符串替換成另一個字符、插入一個字符、刪除一個字符。一般來說,編輯距離越小,兩個字符串的相似度越大。

這種方法應用非常廣泛,在全序列對比,局部序列對比中都會用到,例如語音識別,拼音糾錯,DNA對比等。

在TensorFlow中,levenshtein距離的處理被封裝成對兩個稀疏矩陣進行操作,定義如下:

def tf.edit_distance(hypothesis, truth, normalize=True, name="edit_distance"):
  • hypothesis:SparseTensor類型,輸入預測的序列結果。
  • truth:SparseTensor類型,輸入真實的序列結果。
  • normalize:標準化,默認True,求出來的levenshtein距離除以真實序列的長度。
  • name:op的名字,可選、

返回一個R-1維的密度矩陣,包含每個序列的levenshtein距離。R是輸入序列hypothesis的秩。

四 CTCdecoder

CTC結構中海油一個重要的環節就是CTCdecoder。

1.CTCdecoder介紹

雖然在輸入ctc_loss中的logits(inputs)是我們的預測結果,但卻是帶有空標簽的,而且是一個與時間序列強對應的輸出。在實際情況下,我們需要轉換好的類似於原始標準標簽的疏忽從。這時可以使用CTCdecoder,經過它對預測結果加工後,就可以與標準標簽進行損失值得運算了。

2.CTCdecoder函數

在TensorFLow中,提供了兩個函數。

def tf.nn.ctc_greedy_decoder(inputs, sequence_length, merge_repeated=True):
  • inputs:三維張量,模型的輸出預測值logits,形狀為[max_time , batch_size , num_classes]。
  • sequence_length:序列的長度。形狀為[batch_size]。
  • merge_repeated:布爾值,默認是True。

返回值:tuple(decoded,neg_sum_logits):

decoded: A single-element list. `decoded[0]` is an `SparseTensor` containing the decoded outputs s.t.:
`decoded.indices`: Indices matrix `(total_decoded_outputs x 2)`. The rows store: `[batch, time]`.
`decoded.values`: Values vector, size `(total_decoded_outputs)`. The vector stores the decoded classes.
`decoded.shape`: Shape vector, size `(2)`. The shape values are: `[batch_size, max_decoded_length]`
neg_sum_logits: A `float` matrix `(batch_size x 1)` containing, for the sequence found, the negative of the sum of the greatest logit at each timeframe.

def tf.nn.ctc_beam_search_decoder(inputs, sequence_length, beam_width=100,
                            top_paths=1, merge_repeated=True):

另外一種尋路策略,參數和上面那個函數大致一樣。

註意:在實際情況下,解碼完後的decoded是一個list,不能直接用,通常取decoded[0],然後轉換成密集矩陣,得到的是一個批次的結果,然後再一條一條地取到沒一個樣本的結果。

第二十二節,TensorFlow中RNN實現一些其它知識補充