🎓 レベル:標準 | 重要度:B(標準)
📎 前提:ラグ特徴量と木モデル(教師あり回帰への変換・窓特徴) | 数理:再帰型ニューラルネットワーク・Transformer(機械学習テキスト)
⚙️ 要最新確認:深層時系列(RNN/LSTM/Transformer・N-BEATS・PatchTST・TFT、および TimesFM/Chronos/Moirai 等の基盤モデル)は API・SOTA が高速に動く領域です。本ノートの実行コードは torch/tf を使わず scikit-learn 1.6.1 の MLP で完結させ(本環境に深層フレームワーク未導入)、深層アーキテクチャの詳細は機械学習テキストへリンクします。実装時は各ライブラリの最新版・最新ベンチマークを確認してください。
要点(BLUF)
- 系列モデル=過去の窓(window) を入力に次の値(や複数先)を出す写像。ラグ特徴量と木モデル のラグ特徴を「固定長の窓」として束ね、写像にニューラルネットを使ったものと見られます。
- 本環境で動く最小の系列ニューラルネットはフィードフォワード MLP(窓を入力にする=time-delay neural network)。非線形を学べますが、標準化(StandardScaler)が必須——入力スケールが大きいと勾配法が進まず学習が破綻します。
- 小・中規模データでは MLP が木や線形を明確に上回ることは稀。深層(RNN/LSTM/Transformer)が活きるのは大規模・多系列・長い依存のとき。詳細は機械学習テキスト(再帰型ニューラルネットワーク・Transformer)へ。
1. 窓(window)を入力にする系列モデル
ラグ特徴量と木モデル では個別のラグ()を特徴に選びました。系列モデルは発想を一歩進め、直近 点をまるごと入力ベクトルにします:
にニューラルネットを使えば、窓の中の非線形・交互作用を学べます。最も単純なのは多層パーセプトロン(MLP)——窓を入力層に並べたフィードフォワードNNで、古くは time-delay neural network(TDNN) と呼ばれました。学習は誤差逆伝播+勾配降下(再帰型ニューラルネットワーク のニューラルネット基礎は機械学習テキスト)。
ニューラルネットで決定的に重要なのが入力の標準化です。勾配降下は入力のスケールに敏感で、 の水準が大きい(例:100 前後)まま渡すと、初期の重み×大きな入力で活性が飽和し、勾配がほぼ消えて学習が進みません。各特徴を平均0・分散1にする StandardScaler を訓練データだけで当て(テストにも同じ変換を適用=漏洩防止)、Pipeline に組むのが定石です。
flowchart LR
Y["原系列 y_t"] --> W["スライディング窓 [y_t-W, ..., y_t-1]"]
W --> S["StandardScaler(訓練のみで当てる)"]
S --> NN["MLP(フィードフォワードNN)g_theta"]
NN --> P["次の値 y_t の予測(時間順ホールドアウトで検証)"]
コード①:窓入力の MLP を 線形・木とホールドアウト比較
非線形(しきい値AR=レジーム切替) +季節の系列で、窓 を入力に MLP・ランダムフォレスト・線形(Ridge) を時間順ホールドアウト(シャッフル禁止)で比べます。MLP と Ridge は StandardScaler を Pipeline で前置。MLP には訓練残差から予測区間も付けます。
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib # 日本語ラベル用
from sklearn.neural_network import MLPRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
# 真の構造:しきい値AR(レジーム切替=非線形)+ 季節。線形では捉えにくい
rng = np.random.default_rng(0)
n, period = 600, 12
t = np.arange(n)
season = 4*np.sin(2*np.pi*t/period)
nl = np.zeros(n)
for i in range(2, n):
if nl[i-1] < 0: # レジーム1:強い持続(係数 0.9)
nl[i] = 0.9*nl[i-1] + rng.normal(0, 0.6)
else: # レジーム2:反転(係数 -0.7)
nl[i] = -0.7*nl[i-1] + rng.normal(0, 0.6)
y = 30 + season + nl
# スライディング窓:過去 W 点の並びを入力ベクトルに、次の1点を出力に
W = 12
X = np.array([y[i-W:i] for i in range(W, n)])
target = np.array([y[i] for i in range(W, n)])
n_test = 100 # 時間順ホールドアウト(シャッフル禁止)
Xtr, Xte = X[:-n_test], X[-n_test:]
ytr, yte = target[:-n_test], target[-n_test:]
models = {
"線形(Ridge)": make_pipeline(StandardScaler(), Ridge(alpha=1.0)),
"木(RF)": RandomForestRegressor(n_estimators=400, random_state=0),
"NN(MLP)": make_pipeline(StandardScaler(),
MLPRegressor(hidden_layer_sizes=(20,), max_iter=4000,
alpha=1e-3, random_state=0)),
}
preds = {}
for name, m in models.items():
m.fit(Xtr, ytr)
preds[name] = m.predict(Xte)
rmse = np.sqrt(np.mean((preds[name] - yte)**2))
print(f"ホールドアウト RMSE {name:>12} = {rmse:.3f}")
# MLP の予測区間:訓練残差の標準偏差から ±1.96σ(正規近似)
mlp = models["NN(MLP)"]
sigma = (ytr - mlp.predict(Xtr)).std(ddof=1)
pm = preds["NN(MLP)"]
lo, hi = pm - 1.96*sigma, pm + 1.96*sigma
cover = int(np.sum((yte >= lo) & (yte <= hi)))
print(f"MLP 95%予測区間 カバレッジ = {cover}/{n_test} 平均幅 = {np.mean(hi-lo):.2f}")
tt = np.arange(W, n)[-n_test:]
plt.figure(figsize=(9, 4.5))
plt.plot(np.arange(W, n)[-160:], target[-160:], color="0.6", lw=1, label="実測")
plt.plot(tt, pm, color="C1", lw=1.8, label="MLP 予測")
plt.fill_between(tt, lo, hi, color="C1", alpha=0.2, label="MLP 95%予測区間")
plt.xlabel("時点 t"); plt.ylabel("y"); plt.legend()
plt.title("スライディング窓 + MLP(フィードフォワードNN)の予測")
plt.tight_layout(); plt.show()
出力:
ホールドアウト RMSE 線形(Ridge) = 0.650
ホールドアウト RMSE 木(RF) = 0.629
ホールドアウト RMSE NN(MLP) = 0.693
MLP 95%予測区間 カバレッジ = 95/100 平均幅 = 2.60
出力の意味:3手法はほぼ横一線——木(RF) 、線形 、MLP 。非線形のしきい値AR を仕込んだので木と MLP は非線形を学べますが、データ規模(訓練 点)では線形に対する非線形の上積みはわずかで、むしろ木が僅差で最良でした。MLP は競合はするもののこの規模では最良ではない——「窓にNNを通せば最強」ではないと分かります。MLP の予測区間は訓練残差の で作り、カバレッジ ()と較正も良好(点予測だけでなく区間を必ず添えるのは時系列の鉄則、予測の評価指標と時系列CV)。
コード②:標準化は必須——なしだと MLP は行き詰まる
同じ系列の水準を 100 にして入力スケールを大きくし、StandardScaler のあり/なしで MLP を比べます。標準化なしで勾配法がどれだけ詰まるかを見ます。
import warnings; warnings.simplefilter("ignore")
import numpy as np
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
# 同じ非線形系列だが水準を 100 にして「入力スケールが大きい」状況にする
rng = np.random.default_rng(0)
n, period = 600, 12
t = np.arange(n)
season = 4*np.sin(2*np.pi*t/period)
nl = np.zeros(n)
for i in range(2, n):
nl[i] = (0.9*nl[i-1] if nl[i-1] < 0 else -0.7*nl[i-1]) + rng.normal(0, 0.6)
y = 100 + season + nl # 水準100=入力が大きいまま
W = 12
X = np.array([y[i-W:i] for i in range(W, n)])
target = np.array([y[i] for i in range(W, n)])
Xtr, Xte = X[:-100], X[-100:]
ytr, yte = target[:-100], target[-100:]
rmse = lambda p: np.sqrt(np.mean((p - yte)**2))
# 標準化なし:入力(約100)をそのまま勾配法に渡す
raw = MLPRegressor(hidden_layer_sizes=(20,), max_iter=4000, alpha=1e-3,
random_state=0).fit(Xtr, ytr)
# 標準化あり:平均0・分散1にしてから同じMLP
scaled = make_pipeline(StandardScaler(),
MLPRegressor(hidden_layer_sizes=(20,), max_iter=4000, alpha=1e-3,
random_state=0)).fit(Xtr, ytr)
print(f"MLP 標準化なし RMSE = {rmse(raw.predict(Xte)):.3f} (勾配が進まず行き詰まる)")
print(f"MLP 標準化あり RMSE = {rmse(scaled.predict(Xte)):.3f}")
出力:
MLP 標準化なし RMSE = 3.939 (勾配が進まず行き詰まる)
MLP 標準化あり RMSE = 0.746
出力の意味:標準化なしの MLP は RMSE で行き詰まり(入力が約100と大きく、初期から活性が飽和して勾配が進まない)、標準化ありは と5倍以上の差。木モデル(ラグ特徴量と木モデル)はスケールに鈍感で標準化不要でしたが、勾配ベースのニューラルネットでは標準化が事実上必須です。Pipeline に StandardScaler を組み、訓練だけで当てて(テストに同じ変換を適用)漏洩を防ぐ——これがニューラル予測の前処理の基本です(特徴量エンジニアリングと前処理、機械学習テキスト)。
2. RNN・LSTM・Transformer・基盤モデル(概念のみ・詳細はML テキストへ)
MLP は窓を「ただの固定長ベクトル」として食べるので、並び順や可変長の長期依存を構造的には扱えません。これを設計で取り込むのが系列特化アーキテクチャです(いずれも数理は機械学習テキストへ)。
- RNN / LSTM:隠れ状態を逐次更新して過去を要約。素の RNN は勾配消失で長期記憶が苦手なので、LSTM がゲート(入力・忘却・出力ゲート)で「何を覚え・何を捨てるか」を学習し、長期依存を保ちます(再帰型ニューラルネットワーク)。カルマンフィルタ(カルマンフィルタ)の「状態を逐次更新」と発想が似ていますが、更新則をデータから学ぶ点が違います。
- Transformer(注意機構):逐次処理をやめ、系列内の任意の2点を**注意(attention)**で直接結びつける。長距離依存を並列に学べ、PatchTST(系列をパッチに区切ってトークン化)などが時系列のSOTAを更新してきました(注意機構・Transformer)。
- 基盤モデル(foundation models):TimesFM・Chronos・Moirai・Lag-Llama など、大量の時系列で事前学習しゼロショット予測するモデルが2025〜2026に相次ぎ登場(要最新確認)。少数データでも転移が効く可能性があり、古典手法・木MLとのベンチマーク比較が活発です。
いつ深層が要るか:単一・短〜中規模・素直な季節トレンドなら、ARIMA/ETS(ARMA・ARIMAモデル・ETSモデルと状態空間表現)や木ML(ラグ特徴量と木モデル)で十分なことが多い(本ノートのコード①も MLP が最良ではなかった)。深層が活きるのは、多数の系列を一括学習(global model)・長い依存や複雑な非線形・外生変数が豊富・データが大規模のとき。小データに深層を当てると過学習しやすく、リーク混入の検査も難しくなります。
3. 数式の直観
- 窓入力 = 有限記憶の写像: は「直近 点が次を決める」という有限次マルコフ近似。 が短いと長期依存を取り逃し、長いと次元が増えて過学習——窓長は重要なハイパーパラメータで、時系列CV で選ぶ(訓練・検証・テストと交差検証)。
- 標準化が効く理由:勾配 は入力 に比例()。 の桁が大きい・特徴間でスケールが違うと、損失曲面が引き伸ばされ最急降下がジグザグして進まない。標準化は曲面を球状に近づけ、勾配降下を素直にします。
- MLP vs 木 vs 線形:線形はバイアス大・バリアンス小、木は交互作用に強く中庸、MLP は表現力が高い分データを要求(バリアンス大)。小データではバリアンスが効いて単純なモデルが勝ちやすい——バイアス・バリアンス分解(汎化と過学習・バイアスバリアンス分解、機械学習テキスト)そのものです。
⚠️ よくある誤解・落とし穴
- 「ニューラルネットは標準化しなくても動く」ではない:勾配ベースの学習は標準化が事実上必須(コード②で vs )。木モデルはスケールに鈍感で不要——モデルで前処理が変わります。
- 「深層を使えば必ず精度が上がる」ではない:小・中規模では木や線形が競合・優越しがち(コード①)。深層は大規模・多系列・長期依存で初めて本領を発揮。まずベースラインと木MLを置いて、超えてから深層を検討します(バックテストと予測の評価)。
- 「系列モデルは漏洩しにくい」ではない:むしろ窓・標準化・正規化で未来情報が混入しやすい。標準化は訓練のみで当てる、窓は時点 までで作る、CV はシャッフルしない(予測の評価指標と時系列CV)。
- 「点予測だけでよい」ではない:ニューラル予測でも区間が要る。残差ブートストラップ・分位点回帰・MC ドロップアウト等で予測区間を出します(本ノートは残差 の簡便版)。
- 「最新 API はそのまま使える」ではない:深層時系列は版で大きく変わる(要最新確認)。本ノートは sklearn の MLP で代用——実装は torch/keras 等で最新ドキュメントに従ってください。
関連ノート
- 第8章 機械学習予測 目次(第8章の見取り図)
- ラグ特徴量と木モデル(窓・ラグ特徴の作り方・木との比較相手)
- バックテストと予測の評価(深層を含む手法の honest な比較)
- 予測の評価指標と時系列CV(時間順ホールドアウト・漏洩・区間)
- カルマンフィルタ(逐次状態更新=RNN の発想の親戚)
- 再帰型ニューラルネットワーク(機械学習テキスト・RNN/LSTM)
- 注意機構(機械学習テキスト・attention)
- Transformer(機械学習テキスト・Transformer)
- 特徴量エンジニアリングと前処理(機械学習テキスト・標準化と前処理)
- 汎化と過学習・バイアスバリアンス分解(機械学習テキスト・なぜ小データで単純が勝つか)
- 時系列分析・予測テキスト 全体目次