時系列特徴量の標準化(平均0・分散1)を行い、シーケンス入力用に整形して保存する前処理
print("✅ スケーリング(高速版)")
TAIL = 3
Xtr_main = X_train[:, :, : -TAIL]
Xtr_tail = X_train[:, :, -TAIL:]
Xtr_num = Xtr_main[:, :, keep_numeric_idx].reshape(-1, keep_numeric_idx.size)
means = Xtr_num.mean(axis=0)
stds = Xtr_num.std(axis=0); stds[stds == 0] = 1.0
Xtr_view = Xtr_main[:, :, keep_numeric_idx]
Xtr_view -= means.reshape(1, 1, -1)
Xtr_view /= stds.reshape(1, 1, -1)
def scale_inplace(X, keep_idx, means, stds):
X_main = X[:, :, : -TAIL]
view = X_main[:, :, keep_idx]
view -= means.reshape(1, 1, -1)
view /= stds.reshape(1, 1, -1)
scale_inplace(X_val, keep_numeric_idx, means, stds)
scale_inplace(X_test, keep_numeric_idx, means, stds)
keep_for_seq = keep_numeric_idx.astype(np.int64)
def build_seq(X):
X_main = X[:, :, : -TAIL]
X_tail = X[:, :, -TAIL:]
X_main_kept = np.take(X_main, keep_for_seq, axis=2)
return np.concatenate([X_main_kept, X_tail], axis=2).astype(np.float32)
X_train_seq = build_seq(X_train)
X_val_seq = build_seq(X_val)
X_test_seq = build_seq(X_test)
numeric_cols_final = [feature_columns[i] for i in keep_for_seq.tolist()]
extra_cols = ["similarity_max", "history_len_norm_seq", "pad_flag"]
feature_names_seq = numeric_cols_final + extra_cols
with open("final_feature_names_seq.json", "w", encoding="utf-8") as f:
json.dump(feature_names_seq, f, ensure_ascii=False, indent=2)
print("✅ final_feature_names_seq 保存済み")
scale_stats = {
"keep_numeric_idx": keep_numeric_idx.tolist(),
"means": means.astype(np.float32).tolist(),
"stds": stds.astype(np.float32).tolist(),
"TAIL": int(TAIL),
"keep_for_seq": keep_for_seq.tolist(),
}
with open("scale_stats.json", "w", encoding="utf-8") as f:
json.dump(scale_stats, f, ensure_ascii=False)
print("✅ scale_stats 保存済み")
このコードは、学習用の数値特徴をトレイン分布で標準化(スケーリング)し、推論時の再現性のために 統計量と最終特徴名を保存、さらに 埋め込み等の末尾3特徴(TAIL)を非スケールで保持したまま最終テンソルを組み立てる高速前処理です。構文の役割も併せて解説します。
- TAIL=3(末尾3特徴は非スケール)
TAIL = 3
Xtr_main = X_train[:, :, : -TAIL] # スケール対象(本体)
Xtr_tail = X_train[:, :, -TAIL:] # 末尾3つ(類似度・履歴長・パディング等)
末尾3つ(類似度・履歴長・パディング等)
末尾3チャネルは そのままのスケールで保持 したい補助特徴の想定(例:similarity_max
, history_len_norm_seq
, pad_flag
)。
- トレインから平均・標準偏差を算出(分布リーク防止)
Xtr_num = Xtr_main[:, :, keep_numeric_idx].reshape(-1, keep_numeric_idx.size)
means = Xtr_num.mean(axis=0)
stds = Xtr_num.std(axis=0); stds[stds == 0] = 1.0
keep_numeric_idx:数値列だけを選ぶインデックス(カテゴリ/埋め込み列は除外)。reshape(-1, d):時系列次元をフラット化して 全時点の分布 で平均・分散を推定。
std==0 を 1 に補正:定数列によるゼロ割りを防止。
- ブロードキャストで高速インプレース標準化
Xtr_view = Xtr_main[:, :, keep_numeric_idx]
Xtr_view -= means.reshape(1, 1, -1)
Xtr_view /= stds.reshape(1, 1, -1)
reshape(1,1,-1)
:(N, S, Dnum) に自然に放送可能な形へ。
代入演算(-=
/=
)で ビューに直接反映 → 余分なコピーを避け高速。
- 検証・テストにも同じ統計量を適用
def scale_inplace(X, keep_idx, means, stds): ...
scale_inplace(X_val, keep_numeric_idx, means, stds)
scale_inplace(X_test, keep_numeric_idx, means, stds)
トレインで得た means/stds
を固定して適用(将来情報の混入を回避)。X[:, :, : -TAIL]
のうち数値列のみを対象に標準化。
- 最終シーケンスの組み立て(数値のみ+TAILを結合)
keep_for_seq = keep_numeric_idx.astype(np.int64)
def build_seq(X):
X_main = X[:, :, : -TAIL]
X_tail = X[:, :, -TAIL:]
X_main_kept = np.take(X_main, keep_for_seq, axis=2)
return np.concatenate([X_main_kept, X_tail], axis=2).astype(np.float32)
np.take(..., axis=2)
:チャネル軸で必要な数値特徴だけを抽出(順序を保持)。
スケール済み数値と 非スケールのTAIL を連結し、学習用テンソルを確定。
- 最終特徴名と統計量の保存(再現性の担保)
numeric_cols_final = [feature_columns[i] for i in keep_for_seq.tolist()]
extra_cols = ["similarity_max", "history_len_norm_seq", "pad_flag"]
feature_names_seq = numeric_cols_final + extra_cols
# → final_feature_names_seq.json
scale_stats = {
"keep_numeric_idx": ...,
"means": ...,
"stds": ...,
"TAIL": 3,
"keep_for_seq": ...
}
# → scale_stats.json
モデル入出力とログ解析で重要な 列順・列名 を final_feature_names_seq.json
に保存。
インデックス・平均・標準偏差・TAIL などのメタを scale_stats.json
に保存し、推論・再学習で同一処理を再現可能に。
補足(細かな構文ポイント)
: -TAIL
/-TAIL:
:末尾からのスライスでチャネルを二分。- インプレース演算 は一時配列の生成を避け、メモリ帯域の節約と速度向上に寄与。
- 標準化は 数値列だけ。カテゴリ(埋め込み用ID)はスケールせず、そのまま別経路で Embedding に入力する設計。
- 結果の
X_train_seq/X_val_seq/X_test_seq
は float32 で、「スケール済み数値 + 非スケールTAIL」 の構成になっています。
この手順により、リークのない標準化・再現性のある特徴順・効率的なテンソル構築が実現され、学習と推論の整合性が高まります。
コメント