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.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.2.3 セルフアテンション セルフアテンションのメカニズム
通信網
比較のために血圧をトレーニングする
2.2 LSTM ネットワーク
比較のために LSTM をトレーニングする
2.3 変圧器モデル
2.4 比較
1000 ポイントを使って比較用の絵を描くだけです
3つのコード
詳細なコードデータについてはコメント領域を参照してください。