Keras は自己注意メカニズムを実装します

1. 自己注意メカニズムの原理

        自己注意メカニズムの原理は、自己限定集合内の各メンバーの相関度を計算し、各メンバーに他のメンバーの情報を多かれ少なかれ付着させることです。重要なのは、このセットのメンバーはベクトル、スカラー、そしておそらく人々のグループである可能性があるということですコレクションが人々のグループである場合、各メンバー (人々の間) の関連度を簡単に取得できます。コレクション内にいくつかのスカラーがある場合は、Dense_tention を使用して各スカラー間の接続の度合いを計算します。セット内にベクトルがある場合 (つまり、各サンプルに複数の特徴がある場合)、関連度はどのように計算すればよいでしょうか? ——答えは、李紅儀氏が言ったマトリックス形式で計算することです。サンプル特徴の次元を削減することも可能です。たとえば、SEnet では、2 次元行列 Global Maxpooling を 1x1xC の C スカラーに直接変換し、Dense_tention を使用してアテンション分布を直接計算します。これは完璧です。

        つまり、セット内のメンバーの数が限られている限り、それに注意を払うことができます。重要なのは、各メンバーの特徴をスカラーにプールし (スカラーに重み付け)、それぞれをソフトマックスで出力することです。メンバー間の注意力の分散が行われます。ここでは、セルフアテンションのいくつかの応用例を示します: 特徴の次元削減、因果分析、解釈可能性など。

2、コード

        次のコードは、コレクション内にいくつかのベクトルがあり、各ベクトルが D 次元であるという前提に基づいています。セット内に N 個のベクトルがあり、テンソルとして表現される場合、形状は(D,N)になります。これは、keras などの一部のフレームワークがデフォルトで最後のディメンションをサンプル サイズとして使用するために書かれています。ここで、CNN-1D ネットワーク層がいくつかの特徴を抽出します。コンボリューション カーネルは 32、出力は (None,8,32) です。8 はコンボリューション カーネル スライディングによって生成された特徴マップ ベクトルで、これは 32 次元を抽出するのと同等です。潜在特徴ベクトルは 8。これら 32 の特徴間の自己注意を計算する必要があります。最も簡単な方法は、グローバル マックスプーリングを 32 スカラーにし、次にソフトマックスで 32 の注意スコアにし、それらを乗算し直すことです。しかし、8 次元のようにはできませんベクトル 類似性を計算するには、より合理的な方法を使用する必要があります。

        完全なコードは次のとおりです。

from keras import backend as K
from keras.layers import Layer,Permute
 
 
class Self_Attention(Layer):
 
    def __init__(self,left_dim=None,scale_value=1,**kwargs):
        if scale_value<=0:
            raise ValueError('缩放值必须大于0')
        self.scale_value=scale_value
        self.output_dim=left_dim
        super(Self_Attention, self).__init__(**kwargs)
 
    def build(self, input_shape):
        # 为该层创建一个可训练的权重
        #inputs.shape = (batch_size, time_steps, seq_len),取(None, 8, 32)
        print('input_shape is:',input_shape)
        if self.output_dim is None:
            self.output_dim = input_shape[1]
        self.kernel = self.add_weight(name='kernel',
                                      shape=(3, input_shape[1],self.output_dim),
                                      initializer='uniform',
                                      trainable=True)
                                      #8X8大小,当然self.output_dim可以大于8也可以小于8,升维降维效果没试过
        super(Self_Attention, self).build(input_shape)  # 一定要在最后调用它
    # 生成每个成员向量的query、key、value向量
    def call(self, x):
        x=Permute((2,1))(x)
        #8x32变成32x8;原因是8x8右乘8x32keras的结果有问题,只能将x放在左边,即32x8,8x8
        WQ = K.dot(x, self.kernel[0])
        WK = K.dot(x, self.kernel[1])
        WV = K.dot(x, self.kernel[2])
        # 生成成员向量间的关联度
        QK = K.batch_dot(WQ,K.permute_dimensions(WK, [0, 2, 1]))
        # 关联度缩放
        if self.scale_value <0:
            raise ValueError('scale_value必须大于0')
        QK = QK / (self.scale_value**0.5)
        # 输出注意力分数,QK是32x32的矩阵
        QK = K.softmax(QK)
        # 抽取各个成员的信息组成新的向量集合
        WV_=Permute((2,1))(WV)
        V= K.batch_dot(WV_,QK)

        return V
 
    def compute_output_shape(self, input_shape):
        return (input_shape[0],self.output_dim,input_shape[2])

        上記のコードは、前に見た偉人によって書かれたものですが、特定のブログ投稿が見つかりません。コードは私によって変更されました (CNN-1D によって抽出された特徴間の関係を扱うために使用されました)。以下は大きな元のコードです。元のコードは次のとおりです。

from keras import backend as K
from keras.layers import Layer,Permute
 
 
class Self_Attention(Layer):
 
    def __init__(self, output_dim, scale_value,**kwargs):
        self.output_dim = output_dim
        self.scale_value=scale_value
        super(Self_Attention, self).__init__(**kwargs)
 
    def build(self, input_shape):
        # 为该层创建一个可训练的权重
        #inputs.shape = (batch_size, time_steps, seq_len);provide is (8,32),output_dim=16
        # print(input_shape)
        self.kernel = self.add_weight(name='kernel',
                                      shape=(3,input_shape[2], self.output_dim),
                                      initializer='uniform',
                                      trainable=True)
 
        super(Self_Attention, self).build(input_shape)  # 一定要在最后调用它
        # print('self.kernel[0].shape is:',self.kernel[0].shape)#self.kernel[0].shape is: (32, 16)
 
    def call(self, x):
        print(x.shape)
        WQ = K.dot(x, self.kernel[0])
        WK = K.dot(x, self.kernel[1])
        WV = K.dot(x, self.kernel[2])
 
        # print("******************WQ.shape******************",WQ.shape)
 
        # print("K.permute_dimensions(WK, [0, 2, 1]).shape",K.permute_dimensions(WK, [0, 2, 1]).shape)
 
 
        QK = K.batch_dot(WQ,K.permute_dimensions(WK, [0, 2, 1]))
        if self.scale_value <0:
            raise 'scale_value必须大于0'
        QK = QK / (self.scale_value**0.5)
 
        QK = K.softmax(QK)
 
        # print("QK.shape---",QK.shape)
        # print("************************************")
 
        V = K.batch_dot(QK,WV)
        # print("-----------V--------------.shape",V.shape)
        return V
 
    def compute_output_shape(self, input_shape):
 
        return (input_shape[0],input_shape[1],self.output_dim)

おすすめ

転載: blog.csdn.net/weixin_44992737/article/details/130374527