[深層学習シリーズ(6)]:RNNシリーズ(5):RNNモデルの動的ルーティング

動的ルーティングはアテンションメカニズムに似ており、その主な目的は、対応するパラメータcをシーケンスデータに割り当てることです。これは、アテンションメカニズムにいくらか似ています。注意メカニズムと比較して、動的ルーティングアルゴリズムの精度が向上していることが実際に証明されています。注意メカニズムで重みを計算するために使用される類似性アルゴリズムとは異なり、このペーパーでは動的ルーティングアルゴリズムを使用して重みを割り当てます。動的ルーティングアルゴリズムはカプセルネットワークで使用されます。このアルゴリズムは主にここでの参照に使用され、RNNで使用されます。実際には、CNNまたはRNNの一部のアルゴリズムは相互に学習でき、場合によっては奇跡的な効果をもたらすことが証明されています。具体的な実用的な詳細については、この記事をご覧ください。


目次

1つの動的ルーティングアルゴリズム

第二に、RNNでの動的ルーティングアルゴリズムの実践

3.動的ルーティングに基づくRNNモデルの実践-ロイターニュースの分類

3.1、データの読み込み

3.2.IndyLSTMユニットを使用してRNNモデルを構築する


 

1つの動的ルーティングアルゴリズム

ディープラーニングのパイオニアであり、バックプロパゲーションなどの古典的なニューラルネットワークアルゴリズムの発明者であるジェフリーヒントンは、カプセルネットワークを提案しました。カプセルネットワークは、カプセルに基づく新しいタイプのニューラルネットワークです。カプセルネットワークは、動的ルーティングでトレーニングされています。カプセル間のアルゴリズム。Hinton etal。による初期のレポートでカプセルとカプセル間のルーティングを説明する概略図を次の図に示します。

次のカプセルネットワークでのルーティングアルゴリズムの簡単な紹介です。カプセルネットワークでは、動的ルーティングによってカプセルが低レベルのカプセルと親カプセルにグループ化され、親カプセルの出力が計算されます。それを計算する方法は?動的ルーティングでは、変換行列を使用して入力カプセルのベクトルを変換して投票を形成し、同様の投票グループを使用します。これらの投票は、最終的に親カプセルの出力ベクトルになります。具体的な計算は次のとおりです。

  • 類似性の重み(結合係数)を計算するc_ {ij}

                                                   c_ {ij} = \ frac {exp(b_ {ij})} {\ sum_ {K} exp(b_ {ij})}

その中にb_ {ij}は、下層カプセルと上層カプセルの類似性があり、各反復の前にデフォルトで0に初期化されます。ここでは、Softmaxを使用して類似性を正規化し、最後に類似性の重みを取得します。Softmaxを使用すると、すべての重みcijが負ではなく、それらの合計が1に等しくなることが保証されます。本質的に、softmaxは、上記の係数cijの確率的性質を強制します。概念的には、類似度の重みを計算することでc_ {ij}、カプセルがカプセルをアクティブ化する可能性を測定します。

  • カプセルの出力を計算します(活性化ベクトル)v_ {j}

まず、低レベルカプセルネットワークの出力(予測ベクトル)は次のとおりですu_ {i}。ここでは、変換行列に低レベルカプセルの出力を乗算して、次元を変換し、新しい出力(予測)を取得する必要があります。ベクトル)\ widehat {u} _ {j | i}

                                                   \ widehat {u} _ {j | i} = W_ {ji} u_ {i}

次に、類似度の重み(結合係数)に従ってc_ {ij}加重和を計算し、最後にカプセルの出力を取得しますs_ {j}

                                                  s_ {j} = \ sum _ {i} {c_ {ji} \ widehat {u} _ {j | i}}

さらに、スカッシュ非線形関数を渡す必要があります。これにより、カプセルの出力の方向が維持され、長さが1未満に制限されます。この関数は、小さいベクトルをゼロに圧縮し、大きいベクトルを単位ベクトルに圧縮できます。

                                                  v_ {j} = \ frac {\ left \ |  s_ {j} \ right \ | ^ {2}} {1+ \ left \ |  s_ {j} \ right \ | ^ {2}} \ frac {s_ {j}} {\ left \ |  s_ {j} \ right \ | ^ {2}}

  • 類似度の重みを更新するb_ {ij}

直感的には、予測ベクトルはカプセルからの予測(投票)であり、カプセルの出力に影響を与えます。活性化ベクトルと予測ベクトルの類似性が高い場合、2つのカプセルは高度に相関していると結論付けることができます。この類似性は、予測ベクトルと活性化ベクトルの内積によって測定されます。b_ {ij}更新の計算は次のとおりです。

                                                  b_ {ji} \ leftarrow b_ {ji} + \ widehat {u} _ {j | i} \ cdot v_ {j}

したがって、類似度スコアは、ニューロンのような可能性だけを考慮するのではなく、可能性と特性属性の両方を考慮します。動的ルーティングの最終的な擬似コードは次のとおりです。

参照接続:

CapsNet入門シリーズ3:カプセル間の動的ルーティングアルゴリズム

カプセル間の動的ルーティングの理解(ヒントンのカプセルネットワークに基づく)

ヒントンの論文「カプセル間の動的ルーティング」をどのように扱うか?

NLP /カプセルネットをゆっくり学ぶ

第二に、RNNでの動的ルーティングアルゴリズムの実践

次のように、カプセルネットワークの動的ルーティングアルゴリズムをRNNモデルに適用するには、特定の変更が必要です。

(1)完全にリンクされたネットワークを使用して、RNNの出力結果を予測ベクトルに変換します。shared_routing_uhat()関数を\ widehat {u} _ {j | i}参照してください。

(2)入力シーケンスの長さをマスクし、動的ルーティングアルゴリズムを使用して、動的な長さのシーケンスデータ入力をサポートします。masked_routing_iter()関数を参照してください。

(3)RNN出力の結果に対して情報集約を実行し、動的ルーティング計算の結果に対してドロップアウト処理を実行して、より強力な正規化機能を実現します。routing_maskクラスを参照してください

具体的な実装コードは次のとおりです。

def mkMask(input_tensor,maxLen):
    '''
    计算变长RNN模型的掩码,根据序列长度生成对应的掩码
    :param input_tensor:输入标签文本序列的长度list,(batch_size)
    :param maxLen:输入标签文本的最大长度
    :return:
    '''
    shape_of_input=tf.shape(input_tensor)
    shape_of_output=tf.concat(axis=0,values=[shape_of_input,[maxLen]])

    oneDtensor=tf.reshape(input_tensor,shape=(-1,))
    flat_mask=tf.sequence_mask(oneDtensor,maxlen=maxLen)

    return tf.reshape(flat_mask,shape_of_output)

def shared_routing_uhat(caps,out_caps_num,out_caps_dim):
    '''
    定义函数,将输入转化成uhat
    :param caps: 输入向量,[batch_size,max_len,cap_dims]
    :param out_caps_num:输出胶囊的个数
    :param out_cap_dim:输出胶囊的维度
    :return:
    '''
    batch_size,max_len=caps.shape[0],caps.shape[1]

    caps_uchat=tf.keras.layers.Dense(out_caps_num*out_caps_dim,activation='tanh')(caps)
    caps_uchat=tf.reshape(caps_uchat,[batch_size,max_len,out_caps_num,out_caps_dim])

    return caps_uchat

def _squash(in_caps,axes):
    '''
    定义_squash激活函数
    :param in_caps:
    :param axes:
    :return:
    '''
    _EPSILON=1e-9
    vec_squared_norm=tf.reduce_sum(tf.square(in_caps),axis=axes,keepdims=True)
    scalar_factor=vec_squared_norm/(1+vec_squared_norm)/tf.sqrt(vec_squared_norm+_EPSILON)
    vec_squared=scalar_factor*in_caps
    return vec_squared

def masked_routing_iter(caps_uhat,seqLen,iter_num):
    '''
    动态路由计算
    :param caps_uhat:输入向量,(batch_size,max_len,out_caps_num,out_caps_dim)
    :param seqLen:
    :param iter_num:
    :return:
    '''
    assert iter_num>0

    #获取批次和长度
    batch_size,max_len=tf.shape(caps_uhat)[0],tf.shape(caps_uhat)[1]
    #获取胶囊的个数
    out_caps_num=int(tf.shape(caps_uhat)[2])
    seqLen=tf.where(tf.equal(seqLen,0),tf.ones_like(seqLen),seqLen)
    mask=mkMask(seqLen,max_len) #(batch_size,max_len)
    float_mask=tf.cast(tf.expand_dims(mask,axis=-1),dtype=tf.float32)#(batch_size,max_len,1)

    #初始化相似度权重b
    B=tf.zeros([batch_size,max_len,out_caps_num],dtype=tf.float32)

    #迭代更新相似度权重b
    for i in range(iter_num):
        #计算相似度权重(耦合系数)c
        c=tf.keras.layers.Softmax(axis=2)(B)#(batch_size,max_len,out_caps_num)
        c=tf.expand_dims(c*float_mask,axis=-1)#(batch_size,max_len,out_caps_num,1)

        #计算胶囊的输出(激活向量)v
        weighted_uhat=c*caps_uhat#(batch_size,max_Len,out_caps_num, out_caps_dim)
        s=tf.reduce_sum(weighted_uhat,axis=1)# (batch_size, out_caps_num, out_caps_dim)
        #squash非线性函数
        v=_squash(s,axes=[2])#(batch_size, out_caps_num, out_caps_dim)
        v=tf.expand_dims(v,axis=1)#(batch_size, 1, out_caps_num, out_caps_dim)

        #更新相似度权重b
        B=tf.reduce_sum(caps_uhat*v,axis=-1)+B#(batch_size, maxlen, out_caps_num)

    v_ret = tf.squeeze(v, axis=[1])  # shape(batch_size, out_caps_num, out_caps_dim)
    s_ret = s
    return v_ret, s_ret

#定义函数,使用动态路由对RNN结果信息聚合
def routing_masked(in_x, xLen, out_caps_dim, out_caps_num, iter_num=3,
                                dropout=None, is_train=False, scope=None):
    assert len(in_x.get_shape()) == 3 and in_x.get_shape()[-1].value is not None
    b_sz = tf.shape(in_x)[0]
    with tf.variable_scope(scope or 'routing'):
        caps_uhat = shared_routing_uhat(in_x, out_caps_num, out_caps_dim, scope='rnn_caps_uhat')
        attn_ctx, S = masked_routing_iter(caps_uhat, xLen, iter_num)
        attn_ctx = tf.reshape(attn_ctx, shape=[b_sz, out_caps_num*out_caps_dim])
        if dropout is not None:
            attn_ctx = tf.layers.dropout(attn_ctx, rate=dropout, training=is_train)
    return attn_ctx

3.動的ルーティングに基づくRNNモデルの実践-ロイターニュースの分類

3.1、データの読み込み

ここで使用されるデータセットはtf.kerasインターフェースです。データセットには11228のニュースと合計46のトピックが含まれています。具体的なインターフェースは次のとおりです。

tf.keras.datasets.reuters

主な実現方法は次のとおりです。

  • tf.keras.datasets.reuters.load_data関数を使用してデータを ロードします
  • tf.keras.preprocessing.sequence.pad_sequences関数を使用してデータを整列します

具体的なコードの実装は次のとおりです。

#定义参数
NUM_WORDS=2000 #字典的最大长度
MAXLEN=80 #设置句子的最大长度

def load_data(num_words=NUM_WORDS,maxlen=MAXLEN):
    '''加载数据'''
    # 加载数据
    print("load datasets ...")
    (x_train, y_train), (x_test, y_test) = \
        tf.keras.datasets.reuters.load_data(path='./reuters.npz', num_words=num_words)

    #数据预处理:对齐序列数据并计算长度
    #使用tf.keras.preprocessing.sequence函数对齐标签
    # 对于句子长度大于maxlen的,从前面截断,只保留前maxlen个数据;
    #对于句子长度小于maxlen的,需要在句子后面补零
    x_train=tf.keras.preprocessing.sequence.pad_sequences(x_train,maxlen=maxlen,padding='post')
    x_test=tf.keras.preprocessing.sequence.pad_sequences(x_test,maxlen,'post')
    print('Pad sequences x_train shape:', x_train.shape)

    #计算每个句子的真实长度
    len_train=np.count_nonzero(x_train,axis=1)
    len_test = np.count_nonzero(x_test, axis=1)
    
    return  (x_train,y_train,len_train),(x_test,y_test,len_test)

def dataset(batch_size):
    (x_train,y_train,len),_=load_data()
    
    dataset=tf.data.Dataset.from_tensor_slices(((x_train,len),y_train))
    
    dataset=dataset.shuffle(1000).batch(batch_size,drop_remainder=True)#丢弃剩余数据
    
    return dataset

3.2.IndyLSTMユニットを使用してRNNモデルを構築する

主な手順は次のとおりです。

(1)3層のIndyLSTMユニットをtf.nn.dynamic_rnn()関数に渡して、動的RNNモデルを構築します。

(2)routing_masked()関数を使用して、動的ルーティングに基づいてRNNモデルの出力結果を集約します。

(3)分類された結果を使用して損失値を計算し、トレーニング用のオプティマイザーを定義します。

x = tf.placeholder("float", [None, maxlen]) #定义输入占位符
x_len = tf.placeholder(tf.int32, [None, ])#定义输入序列长度占位符
y = tf.placeholder(tf.int32, [None, ])#定义输入分类标签占位符

nb_features = 128   #词嵌入维度  
embeddings = tf.keras.layers.Embedding(num_words, nb_features)(x)

#定义带有IndyLSTMCell的RNN网络
hidden = [100,50,30]#RNN单元个数
stacked_rnn = []
for i in range(3):
    cell = tf.contrib.rnn.IndyLSTMCell(hidden[i])
    stacked_rnn.append(tf.nn.rnn_cell.DropoutWrapper(cell, output_keep_prob=0.8))
mcell = tf.nn.rnn_cell.MultiRNNCell(stacked_rnn)

rnnoutputs,_  = tf.nn.dynamic_rnn(mcell,embeddings,dtype=tf.float32)
out_caps_num = 5 #定义输出的胶囊个数
n_classes = 46#分类个数

outputs = routing_masked(rnnoutputs, x_len,int(rnnoutputs.get_shape()[-1]), out_caps_num, iter_num=3)
print(outputs.get_shape())
pred =tf.layers.dense(outputs,n_classes,activation = tf.nn.relu)



#定义优化器
learning_rate = 0.001
cost = tf.reduce_mean(tf.losses.sparse_softmax_cross_entropy(logits=pred, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

iterator1 = tf.data.Iterator.from_structure(dataset.output_types,dataset.output_shapes)
one_element1 = iterator1.get_next()							#获取一个元素


#训练网络
with tf.Session()  as sess:
    sess.run( iterator1.make_initializer(dataset) )		#初始化迭代器
    sess.run(tf.global_variables_initializer())
    EPOCHS = 20
    for ii in range(EPOCHS):
        alloss = []  									#数据集迭代两次
        while True:											#通过for循环打印所有的数据
            try:
                inp, target = sess.run(one_element1)
                _,loss =sess.run([optimizer,cost], feed_dict={x: inp[0],x_len:inp[1], y: target})
                alloss.append(loss)

            except tf.errors.OutOfRangeError:
                #print("遍历结束")
                print("step",ii+1,": loss=",np.mean(alloss))
                sess.run( iterator1.make_initializer(dataset) )	#从头再来一遍
                break

最終的なトレーニング結果は次のとおりです。

おすすめ

転載: blog.csdn.net/wxplol/article/details/104484067