「未来を予測する少年」
ある春の午後、高校生のケンジは学校のコンピュータ室に座っていた。彼は数学とコンピュータサイエンスが得意で、最近は機械学習に夢中になっていた。彼の学校では、毎年秋に行われる「AIマスターズコンペ」という機械学習のコンペティションがあり、今年のテーマは「株式予測」だった。
ケンジはこのコンペに参加するため、まずはデータの準備から始めた。彼が手に入れたのは、過去の株価データで、具体的には10銘柄の株価データが含まれており、合計1000日分のデータがあった。このデータの中から未来の株価を予測するためのモデルを構築することに決めた。
ケンジは、シーケンスデータを扱うために「アテンション」メカニズムを用いることにした。具体的には、10銘柄の株価データ、5日間分のシーケンスデータを一つのデータ単位として扱い、それをアテンションで処理するモデルを設計した。このデータから生成される「コンテキストベクター」、つまり意味を集約したベクトルが、予測のための重要な要素となる。
アテンションメカニズムでは、まず入力データに対して「アテンションウェイト」を計算する。これにより、各タイムステップのデータがどれだけ重要かを示すスコアが得られる。スコアが大きいほど、そのデータが重要であると判断され、その値に大きな重みがかけられる。一方、スコアが小さいデータには小さな重みがかけられ、その価値が相対的に小さくなる。これによって、重要な情報が強調され、重要でない情報が抑制される。
最後に、アテンションによって調整されたデータを「加重平均化」して、コンテキストベクターを求める。このコンテキストベクターが、株価データから重要な情報を集約した意味あるベクトルとして、未来の株価予測のためにニューラルネットワークに入力される。
数週間後、コンペティションの日がやってきた。ケンジは自分のモデルを提出し、結果を待った。発表の日、彼はドキドキしながら結果を確認した。なんと、彼のアテンションモデルが最高の成績を収めていたのだ。ケンジは驚きと喜びでいっぱいだった。アテンションメカニズムを使って、10銘柄の株価データを5日間分のシーケンスで処理し、未来の株価を高精度で予測する手法が評価されたのだった。
彼の成功は、データの次元数やシーケンス長をうまく活用し、意味を集約したコンテキストベクターを用いることで予測精度が向上することを証明した。ケンジはその後も機械学習に情熱を注ぎ続け、さらに多くの挑戦を続けていった。
シンプルなボラティリティモデルを使用してトレーニングデータを生成
このような疑似株価データが10銘柄、1000日分生成されます。
このコードでは、10種類の株の5日間の株価データを使ってコンテキストベクターを計算します。アテンションを使って、この50個の数値からなるデータ(10銘柄 × 5日間)を、1つの10次元のベクトルにまとめる処理を行います。
具体的には、次のようなことをしています:
データの形:
最初は、10銘柄の5日間のデータがあるので、データは「10銘柄 × 5日間」という形で、合計50個の数値から成ります。
意味を持たせる:
アテンションを使って、この50個の数値が何を意味するかを「1つのベクトル」に変換します。これを「コンテキストベクター」と呼びます。コンテキストベクターは、このデータ全体の意味をまとめたもので、50個の数値の情報を簡潔に表現します。
ベクトルの役割:
このベクトルは、単に翌日の株価を予測するためではなく、過去5日間単位の株価データを「意味づけ」して表現しています。つまり、詳細なデータを1つの簡単なベクトルで表現することで、全体の情報を簡潔にまとめています。
このように、アテンションは複雑なデータをシンプルなベクトルに変換し、そのベクトルがそのデータの「意味」を表すようにするのです。
アテンション機構の簡単な説明
アテンションは、どの部分が重要かを見つけるための方法です。以下のように動作します。
ベクトルと内積:
ベクトルというのは、データの特徴を表した数字の列です。アテンションでは、2つのベクトルがどれだけ似ているかを調べます。そのために「内積」という計算をします。内積が大きいほど、2つのベクトルは似ていると考えます。
重要度の判断:
内積の値を使って、どのデータが重要かを決めます。内積の値が大きければ、そのデータは重要だと見なされます。逆に、内積の値が小さいデータは重要度が低いとされます。
アテンションウェイト:
内積の結果を使って「アテンションウェイト」を計算します。これは、各データの重要度を示す数字です。重要度が高いデータには大きなウェイトを、重要度が低いデータには小さなウェイトを掛けます。重要なデータには大きな値を掛け、不要なデータには小さな値を掛けることで、不要な情報を削除していきます。
コンテキストベクターの計算:
それぞれのデータにアテンションウェイトを掛けた後、加重平均をとり、それらをまとめて「コンテキストベクター」を作ります。コンテキストベクターは、重要なデータの情報が集約された結果です。
まとめ
アテンションは、データの中から重要な部分を見つけ出し、その情報を使って最も意味のある結果を得る方法です。内積を使って似ているデータを見つけ、重要度に応じて重みを掛けてから、加重平均をとり最終的な情報をまとめていきます。特に重要なのは、重要なデータだけを強調し、大部分の不要なデータには小さな値を掛けて消去することで、より正確で有用な情報を得ることです。
シーケンス長を5にした場合、アテンションメカニズムを用いて5つの入力ベクトルの重要度(アテンションウェイト)を計算し、それらを集約して1つのコンテキストベクトルとして出力します。
具体的には、以下のようなプロセスが行われます。
入力シーケンス:
形状: (batch_size, seq_len, input_dim)
例: (batch_size, 5, 10)
5つのシーケンスデータ(各ベクトルの次元は10)
アテンション計算:
アテンションメカニズムを使用して、各入力ベクトルに対して重要度を計算します。
各ベクトルに対して自己アテンションスコアが計算されます。
計算されたアテンションスコアを用いて、加重和を取り、コンテキストベクトルを生成します。
コンテキストベクトルの出力:
形状: (batch_size, input_dim)(flatten前: (batch_size, seq_len, input_dim))
例: (batch_size, 10)
各シーケンスデータの特徴を集約した1つのベクトル
このコンテキストベクトルは、5つのシーケンスデータの情報を要約し、1つのベクトルに集約したものです。このベクトルが次の層(例えば、全結合層のニューラルネットワーク)に渡され、最終的な出力が得られます。アテンションメカニズムによって、モデルはシーケンス内の重要な要素に焦点を当て、それに基づいて最終的な判断を行います。
モデル評価。
アテンションメカニズムを使って、10銘柄の株価データを5日間分のシーケンスで処理し、未来の株価を高精度で予測する手法のコード。
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, models, optimizers, losses
import matplotlib.pyplot as plt
# サンプルデータの生成関数
def generate_stock_data(num_stocks, seq_len, num_samples, volatility=0.01):
np.random.seed(0)
initial_prices = np.random.uniform(100, 200, size=num_stocks)
prices = np.zeros((num_samples, seq_len + 1, num_stocks))
for i in range(num_samples):
daily_returns = np.random.normal(0, volatility, size=(seq_len + 1, num_stocks))
prices[i] = initial_prices * np.exp(np.cumsum(daily_returns, axis=0))
initial_prices = prices[i, -1] # 最後の価格を次のサンプルの初期価格に
return prices
# アテンションを使用するモデル
class StockPredictionModel(tf.keras.Model):
def __init__(self, seq_len, input_dim, output_dim):
super(StockPredictionModel, self).__init__()
self.attention = layers.MultiHeadAttention(num_heads=4, key_dim=input_dim)
self.dropout = layers.Dropout(0.2)
self.flatten = layers.Flatten()
self.fc1 = layers.Dense(128, activation='relu')
self.fc2 = layers.Dense(output_dim)
self.seq_len = seq_len
def call(self, inputs):
# アテンション計算
attn_output = self.attention(inputs, inputs)
attn_output = self.dropout(attn_output)
# 平坦化
flattened = self.flatten(attn_output)
# 2層のニューラルネットワーク
x = self.fc1(flattened)
x = self.fc2(x)
return x
# トレーニングと評価の関数
def train_and_evaluate(seq_len, X, y):
# モデル定義とコンパイル
model = StockPredictionModel(seq_len=seq_len, input_dim=X.shape[2], output_dim=X.shape[2])
model.compile(optimizer=optimizers.Adam(learning_rate=0.001), loss=losses.MeanSquaredError())
# 訓練
history = model.fit(X, y, epochs=20, batch_size=32, verbose=0)
# モデルの評価
loss = model.evaluate(X, y, verbose=0)
return history, loss
# シーケンス長の設定
seq_lens = [5, 10, 15] # 例として3つのシーケンス長
num_stocks = 10
num_samples = 1000 # サンプル数を1000に設定
# データの生成
all_histories = []
all_losses = []
for seq_len in seq_lens:
prices = generate_stock_data(num_stocks, seq_len, num_samples)
X = prices[:, :-1] # シーケンスデータ
y = prices[:, -1] # 次の日の株価
history, loss = train_and_evaluate(seq_len, X, y)
all_histories.append(history)
all_losses.append(loss)
print(f"Sequence Length: {seq_len}, Validation Loss: {loss}")
# トレーニングロスのプロット
plt.figure(figsize=(12, 8))
for idx, seq_len in enumerate(seq_lens):
plt.plot(all_histories[idx].history['loss'], label=f'Seq Len {seq_len}')
plt.title('Training Loss over Epochs for Different Sequence Lengths')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()
解説
データ生成:
各銘柄の初期価格をランダムに設定し、ボラティリティを使用して日々のリターンをランダムに生成しています。
prices配列には、各サンプルごとの5日分の株価データと次の日の株価が格納されます。
モデル構成:
アテンションメカニズムを使用して、5日分の各銘柄の株価データを集約します。
集約したデータを平坦化し、2層の全結合ネットワークに通します。
最終的に10銘柄の次の日の株価を予測します。
訓練と評価:
訓練データ (X) は5日分の株価データ、ラベル (y) は次の日の株価です。
モデルを訓練し、評価を行います。
データのシェイプの詳細
入力データ (X):
訓練時のシェイプ: (num_samples, seq_len, input_dim) = (1000, 5, 10)
各サンプルは5日分の株価データを持ち、それぞれの銘柄について10次元の入力ベクトルが存在します。
ラベル (y):
訓練時のシェイプ: (num_samples, output_dim) = (1000, 10)
次の日の10銘柄の株価を予測するための正解データです。
この構成により、モデルは過去5日間の株価データを基に、次の日の各銘柄の株価を予測することができます。
シンプルなボラティリティモデルを使用してトレーニングデータを生成するコード。
import numpy as np
import matplotlib.pyplot as plt
# パラメータ設定
num_days = 200 # 日数
initial_price = 100 # 初期価格
volatility = 0.02 # ボラティリティ (例: 2%)
# 株価データの生成
np.random.seed(42) # 再現性のためシードを設定
daily_returns = np.random.normal(0, volatility, size=num_days)
price = initial_price * np.exp(np.cumsum(daily_returns))
# 株価データのプロット
plt.figure(figsize=(10, 6))
plt.plot(price, marker='o', linestyle='-', markersize=2)
plt.title('Simulated Stock Prices over 200 Days')
plt.xlabel('Days')
plt.ylabel('Price')
plt.grid(True)
plt.show()
解説
パラメータ設定:
num_days = 200: シミュレーションする日数を200日に設定。
initial_price = 100: 初期株価を100に設定。
volatility = 0.02: ボラティリティ(価格の変動率)を2%に設定。
株価データの生成:
np.random.seed(42): 再現性のために乱数シードを設定します。
np.random.normal(0, volatility, size=num_days): 各日の価格変動を正規分布に基づいて生成します。平均0、標準偏差volatilityの正規分布から200個のサンプルを生成します。
initial_price * np.exp(np.cumsum(daily_returns)): 初期価格から始まり、累積された日々のリターンを指数関数的に累積することで、各日の株価を計算します。
株価データのプロット:
生成した価格データをプロットします。縦軸が価格、横軸が日数を表します。