変圧器に基づく多変量風力発電予測 TF2

        Transformer は現在人気があります。資格のあるアルゴリズム ポーターとして、トレンドを追い続けるのは当然のことです。この論文では、tensorflow2 フレームワークに基づいて変圧器モデルを構築し、それを多変量風力発電負荷予測に使用します。実験結果は、従来の LSTM と比較して、この方法は精度が高いことを示していますが、欠点も明らかです。この方法は、従来の方法を超えるためにはより多くのデータ トレーニング効果が必要であり、多くの GPU リソースを消費します (テスト段階) 、ワンタイム入力 すべてのテスト セット データは直接 OOM になり、バッチで入力する必要があります)、CPU はさらに遅くなります。

        今日は 2023 年の初日です。まず 2022 年の収穫を要約しましょう。

         それからトピックに入ります

1 変圧器ネットワーク構造

        元のトランスフォーマー ネットワークは、テキスト翻訳、入力 = 出力 (出力は出力の別の形式) などのタスクを処理するために使用されます。そのため、元のネットワークは、最初にエンコードとデコードを行う構造であり、データはエンコーダーが隠されたデータを取得するためにネットワークに入ります。には機能が含まれているため、デコーダを使用してそれを別の式に復元します。そして、風力発電を予測するとき、入力と出力は同じものではないため、ネットワーク内のデコーダーを削除し、エンコーダーの出力を抽出された特徴として直接使用し、全結合層を出力層として接続しました。出力電力値。具体的な理解には、入力層→エンコーダー→出力層が含まれます。

1.1 入力層と出力層

        この論文で使用したデータ形式は図 1 に示されており、データには 6 つの特徴が含まれており、入力データと出力データの生成にはローリング シーケンス モデリング手法が使用されています。具体的には、入力タイムステップm、出力タイムステップnを設定し、1回目からm回目までの全データを入力、m+1回目からm+n回目までの実際の発電電力を出力とします。 、最初のサンプルとして、2 回目から m+1 回目までのすべてのデータを入力として取得し、m+2 回から m+n+1 回目までの実際の発電電力を 2 番目のサンプルとして出力として取得します。 。同様に、入力データと出力データはこのスクロール方法を通じて取得されます。m が 10、n が 3 の場合、入力層の次元は [なし、10、6]、出力層の次元は [なし、3] です。モデルのトレーニング後は、すべてを入力するだけで済みます。過去10瞬間のデータから、未来の3瞬間の発電量の予測値を予測できます。

図1
図1 データ構造

1.2 エンコーダ 

1.2.1 埋め込み層

        埋め込み層は、実際には単純な全結合層です。元の次元を d_model に変換します。これには 2 つの目的があります: 1) 元のデータに対して特徴変換を実行する。元のデータには 6 つの特徴しかありません。これらの特徴が関連、完全に接続された層を使用すると、機能が相互作用し、機能を変換する役割を果たすことができます; 2) 2 つ目は、ネットワークの複雑性を高めることです。将来マルチヘッド セルフ アテンション メカニズムを使用するには、マルチヘッドの数は機能で割り切れる必要があり、たとえば元の 6 つの機能を 64 に変換すると、1、2、4、8、16、32、64 個のヘッドを使用でき、選択性が高くなります。 6 だけの場合は、1 、2、3 しか設定できません。より多くのヘッドを使用すると、ネットワークの複雑さが増加する可能性があります。数万の風力データがある場合、ネットワークは単純すぎて、風力発電の特性を効果的に学習できません。こんなに長いシーケンス。

        プログラムでは、d_model を 16 に設定し、埋め込み層以降のデータは次のようになります。

d_model=16
embedding = tf.keras.layers.Dense(d_model)
x=np.random.rand(64,10,6)#64是batchsize 10是输入时间步 6是6个特征
y=embedding (x) #y的shape变成 64,10,16

1.2.2 位置コーディング

        Transformer はサインコサイン位置エンコーディングを使用します。位置コードは、異なる周波数のサイン関数とコサイン関数を使用して生成され、対応する位置の入力ベクトル (埋め込み層の出力データ) に追加されます。位置ベクトルの次元は、位置ベクトルの次元と一致する必要があります。単語ベクトル

def get_angles(pos, i, d_model):
    # 这里的i等价与公式中的2i和2i+1
    angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
    return pos * angle_rates

def positional_encoding(maximum_position_encoding, d_model):
    
    angle_rads = get_angles(np.arange(maximum_position_encoding)[:, np.newaxis], np.arange(d_model)[np.newaxis, :],d_model)
    # apply sin to even indices in the array; 2i
    angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])
    # apply cos to odd indices in the array; 2i+1
    angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])
    pos_encoding = angle_rads[np.newaxis, ...]
    return tf.cast(pos_encoding, dtype=tf.float32)
x=np.random.rand(64,10,16)
pos_enc=positional_encoding(5000, 16)
x +=pos_enc[:,x.shape[1],:] #x的shape还是[64,10,16]

 1.2.3 セルフアテンション セルフアテンションのメカニズム

        セルフ アテンション ネットワークについては多くの説明がありますが、ここでは計算手順を簡単に理解します: 埋め込み層と位置エンコード後のデータは 64 x 10 x 16 で、64 はバッチサイズを意味し、10 は時間ステップを意味し、16 は特徴次元です。i 番目のサンプルの入力 1 の出力を取得する場合 (各サンプルには 10 個の入力が含まれており、各入力の次元は 16 です)、次のように処理します。

1. 3 つの完全に接続された層を構築し、各次元は d_model、入力 input-1、それぞれ 3 つの変数、つまり Q、K、V を取得します。QKV はinput-1 の別の式として理解できます。1、input-1 の Q を使用します
。 input-1、input-2、...、input-16 の K' にそれぞれ 1 を掛けて、この時点で 16 個のスコアが得られました。このスコアは、input-1、input-2、...、input-16 と比較した input-1 の重要性です

        ここで、他の入力と比較して、入力 1 のスコアがなぜ重要なのか疑問に思うでしょう。私の理解は、Q と K' の積は内積であるということです。いつドット積? ドット積はコサイン類似度の分子です。QK' はコサイン類似度にほぼ等しいです。2 つの変数がより類似している場合、QK' は大きくなります。1 つの変数が最も重要である場合は、次のように推測できます。他の変数を近似的に置き換えることができる場合、他の変数のドット積が大きくなるのと同じです。(相関関係とも理解できます、同じ理屈です)

2. 次に、これらのスコアに対してソフトマックスを取得し、重要度を正規化します。
3. 次に、この重要度にinput-1、input-2、 ...、input-16の値ベクトルを乗算し、合計します。
4. この時点で、input-1 の出力が得られました。

1.2.4 マルチヘッドアテンション

        マルチヘッド アテンションは、各ヘッドの一部の特徴のみを計算するため、理解できます。たとえば、最初のヘッドは入力 1 から入力 4 のみを計算し、2 番目のヘッドは入力 5 から入力 8 を計算します。16 の特徴は 4頭。

1.2.5 エンコーダ

        エンコーダーは、埋め込み層 + 位置エンコーディング + マルチヘッド アテンション レイヤー + マルチヘッド アテンション レイヤー + マルチヘッド アテンション レイヤー + ... + マルチヘッド アテンション レイヤーを入力して出力特徴を取得し、完全な出力を取得するために接続されたレイヤー

def scaled_dot_product_attention(q, k, v, mask):
    """Calculate the attention weights.
    q, k, v must have matching leading dimensions.
    k, v must have matching penultimate dimension, i.e.: seq_len_k = seq_len_v.
    The mask has different shapes depending on its type(padding or look ahead) 
    but it must be broadcastable for addition.
    Args:
    q: query shape == (..., seq_len_q, depth)
    k: key shape == (..., seq_len_k, depth)
    v: value shape == (..., seq_len_v, depth_v)
    mask: Float tensor with shape broadcastable to (..., seq_len_q, seq_len_k). Defaults to None.
    
    Returns:
      output, attention_weights
    """
    matmul_qk = tf.matmul(q, k, transpose_b=True)  # (..., seq_len_q, seq_len_k)
    # scale matmul_qk
    dk = tf.cast(tf.shape(k)[-1], tf.float32)   ## 64
    scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
    # add the mask to the scaled tensor.
    if mask is not None:
        scaled_attention_logits = scaled_attention_logits + (mask * -1e9)  
    # softmax is normalized on the last axis (seq_len_k) so that the scores
    # add up to 1.
    attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)  # (..., seq_len_q, seq_len_k)
    output = tf.matmul(attention_weights, v)  # (..., seq_len_q, depth_v)
    return output, attention_weights

class MultiHeadAttention(tf.keras.layers.Layer):
  def __init__(self, d_model, num_heads):
    super(MultiHeadAttention, self).__init__()
    self.num_heads = num_heads
    self.d_model = d_model
    assert d_model % self.num_heads == 0# d_model self.num_heads 要能够整除
    self.depth = d_model // self.num_heads
    self.wq = tf.keras.layers.Dense(d_model)
    self.wk = tf.keras.layers.Dense(d_model)
    self.wv = tf.keras.layers.Dense(d_model)
    self.dense = tf.keras.layers.Dense(d_model)
  def split_heads(self, x, batch_size):
    """Split the last dimension into (num_heads, depth).
    Transpose the result such that the shape is (batch_size, num_heads, seq_len, depth)
    """
    x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
    return tf.transpose(x, perm=[0, 2, 1, 3])
  def call(self, v, k, q, mask):
    batch_size = tf.shape(q)[0]
    q = self.wq(q)  # (batch_size, seq_len, d_model)
    k = self.wk(k)  # (batch_size, seq_len, d_model)
    v = self.wv(v)  # (batch_size, seq_len, d_model)
    q = self.split_heads(q, batch_size)  # (batch_size, num_heads, seq_len_q, depth)
    k = self.split_heads(k, batch_size)  # (batch_size, num_heads, seq_len_k, depth)
    v = self.split_heads(v, batch_size)  # (batch_size, num_heads, seq_len_v, depth)
    # scaled_attention.shape == (batch_size, num_heads, seq_len_q, depth)
    # attention_weights.shape == (batch_size, num_heads, seq_len_q, seq_len_k)
    scaled_attention, attention_weights = scaled_dot_product_attention(q, k, v, mask)
    scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])  # (batch_size, seq_len_q, num_heads, depth)
    concat_attention = tf.reshape(scaled_attention, 
                                  (batch_size, -1, self.d_model))  # (batch_size, seq_len_q, d_model)
    output = self.dense(concat_attention)  # (batch_size, seq_len_q, d_model)
    return output, attention_weights

def point_wise_feed_forward_network(d_model, dff):
  return tf.keras.Sequential([
      tf.keras.layers.Dense(dff, activation='relu'),  # (batch_size, seq_len, dff)
      tf.keras.layers.Dense(d_model)                  # (batch_size, seq_len, d_model)
  ])

class EncoderLayer(tf.keras.layers.Layer):
  def __init__(self, d_model, num_heads, dff, rate=0.1):
    super(EncoderLayer, self).__init__()
    self.mha = MultiHeadAttention(d_model, num_heads)
    self.ffn = point_wise_feed_forward_network(d_model, dff)
    self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
    self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
    self.dropout1 = tf.keras.layers.Dropout(rate)
    self.dropout2 = tf.keras.layers.Dropout(rate)
  def call(self, x, training, mask):
    attn_output, _ = self.mha(x, x, x, mask)  # (batch_size, input_seq_len, d_model)
    attn_output = self.dropout1(attn_output, training=training)
    out1 = self.layernorm1(x + attn_output)  # (batch_size, input_seq_len, d_model)
    ffn_output = self.ffn(out1)  # (batch_size, input_seq_len, d_model)
    ffn_output = self.dropout2(ffn_output, training=training)
    out2 = self.layernorm2(out1 + ffn_output)  # (batch_size, input_seq_len, d_model)
    return out2

class Encoder(tf.keras.layers.Layer):
  def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size,
               maximum_position_encoding, rate=0.1):
    super(Encoder, self).__init__()
    self.d_model = d_model
    self.num_layers = num_layers
    self.embedding = tf.keras.layers.Dense(d_model)
    self.pos_encoding = positional_encoding(maximum_position_encoding, self.d_model)
    self.enc_layers = [ EncoderLayer(d_model, num_heads, dff, rate) for _ in range(num_layers) ]
    self.dropout = tf.keras.layers.Dropout(rate)
  def call(self, x, training, mask):
    seq_len = tf.shape(x)[1]
    # adding embedding and position encoding.
    x = self.embedding(x)  # (batch_size, input_seq_len, d_model)
    # print('------------------\n',seq_len)
#    x=tf.tile(tf.expand_dims(x,2),self.d_model)
    x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))
    # print(x.shape)
    x += self.pos_encoding[:, :seq_len, :]
    x = self.dropout(x, training=training)
    # print(x.shape)
    # print(mask.shape)
    for i in range(self.num_layers):
      x = self.enc_layers[i](x, training, mask)
    return x  # (batch_size, input_seq_len, d_model)

2 戦闘

        徹底的な分析を経て、ネットワークをトレーニングする準備が整いました。

2.1 BPネットワーク

目次

1 変圧器ネットワーク構造

1.1 入力層と出力層

1.2.1 埋め込み層

1.2.2 位置コーディング

 1.2.3 セルフアテンション セルフアテンションのメカニズム

1.2.4 マルチヘッドアテンション

1.2.5 エンコーダ

2 戦闘

2.1 BPネットワーク

2.2 LSTM ネットワーク

2.3 変圧器モデル

2.4 比較

3つのコード


通信網

        比較のために血圧をトレーニングする

2.2 LSTM ネットワーク

        比較のために LSTM をトレーニングする

 

2.3 変圧器モデル

 

 

2.4 比較

        1000 ポイントを使って比較用の絵を描くだけです

 

3つのコード

        詳細なコードデータについてはコメント領域を参照してください。 

おすすめ

転載: blog.csdn.net/qq_41043389/article/details/128513767
tf2
TF2
tf2