論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
身体・運動
Body / Motion AI
AI基礎

🔖 キーワード索引

ロボティクス強化学習模倣学習動作生成Boston DynamicsTesla Optimusヒューマノイド把持歩行VLA

本ページは 身体・運動 AI(Body / Motion AI)を 12 のセクションで多角的に解説します。 上のチップは検索・関連語の手がかりです。 以下のリンクで各セクションに直接ジャンプできます:

💡 30秒結論📍 文脈🎨 直感📐 数式🔬 数式を言葉で読み解く🧮 実値計算🐍 Python 実装⚠️ 落とし穴🌐 関連手法🔗 関連用語📚 グループ教材

💡 30秒で分かる結論

🍰 まずはやさしく

AIに体を持たせる技術です。

機械を上手に動かすために使います。

自動運転の車などが身近な例です。

AIがどうやって体を動かすか読みます。

💡 30秒で分かる結論

📍 文脈 — どこで使う概念か

🍰 まずはやさしく

AIと現実の世界をつなぐ橋です。

連続的な動きを作るために使います。

スポーツのフォーム解析などで使われます。

この技術がどこで役立つか読みます。

本サイトでは 身体・運動 AI は AI 応用分野の 1 つとして位置づけ、 「データ+数学+知能」が 物理世界と接する領域として扱います。 推論だけでなく 連続的な行動を出力する点で、 分類・回帰タスクとは大きく性格が異なります。 ロボティクス・自動運転・スポーツ動作解析・ヒューマノイドなど、 ハードウェアと AI の境界に位置する分野です。

🎨 直感で掴む — 具体例で理解する

🍰 まずはやさしく

考えるだけでなく動かすAIです。

自然な動きを再現するために使います。

ゲームのキャラの動きが身近な例です。

具体的にどう動くのかを読みます。

「身体・運動 AI」を一言で言えば、 「考えるだけで終わらず、 体(あるいは機械)を動かす AI」。 たとえば次のような例:

共通するのは 「連続値の制御出力」リアルタイムに決め続ける必要があること。 1 度の判断ミスが 転倒・衝突に直結するため、 安全性と頑健性が極めて重要です。

例えば二足歩行では、 関節 20 か所のトルクを 100 Hz で出力。 つまり 毎秒 2000 個の連続値を「正しく」決め続ける必要があり、 これを人間が手書きルールで作るのは事実上不可能。 そこで 機械学習の出番になります。

🎨 直感で掴む:身体動作データとは「時間 × センサー次元の多次元時系列」

身体動作(body movement)のデータ解析は、 ウェアラブル端末・モーションキャプチャ・スマートフォンの内蔵センサーから得られる 連続値時系列を入力として、 「歩行 / 走行 / 立ち上がり」などの動作カテゴリ、 関節角度、 消費カロリー、 転倒リスクを推定する分野です。 代表的なセンサーは 6 軸 IMU(3 軸加速度 + 3 軸ジャイロ)と GPS の組合せで、 高度な研究では Vicon や OptiTrack の光学式モーションキャプチャ、 EMG(筋電図)、 心拍計が追加されます。 サンプリングは 50〜200Hz が一般的で、 1 分間で 6 軸×100Hz = 36{,}000 数値が生成されるデータ密度の高い領域です。

比喩で言えば、 身体動作データは「音波(音声)と映像(画像)の中間」のような性質を持ちます:時間軸はあるが画素のような 2 次元構造はなく、 多軸センサーが平行に並走する。 そのため、 FFT・ウェーブレット・DTW・LSTM・1D-CNN・Transformer が代表手法です。 入力の実体は「加速度・角速度の多軸時系列」や「関節・骨格の 3 次元座標系列」といった連続値であり、 画像分類のように 1 枚で完結せず、 時間方向の文脈を保ったまま特徴量化する点が最大の特徴です。

センサー種別サンプリング周波数単位代表用途
加速度センサー(3 軸)50〜200 Hzm/s² または G歩数計、 動作分類
ジャイロセンサー(3 軸)50〜200 Hzrad/s または °/s姿勢推定、 回転量
地磁気センサー10〜50 HzμT方位、 屋内測位補正
心拍 (光学式)1〜25 Hzbpm運動強度、 ストレス
光学式モーションキャプチャ120〜240 Hzmm関節位置・動作解析
EMG(筋電)1000+ HzμV筋活動量、 リハビリ

🎮 動かして学ぶ ── 関節キーポイントと角度で「姿勢」を読む

下は 架空の簡略化した棒人間(15 個の関節キーポイント/完全にダミー座標・実データではありません)です。 肩・肘・手首・腰・膝・足首などの点を ドラッグすると、 骨と骨がなす 関節角度(肘の曲げ角・膝の曲げ角など)がリアルタイムに計算され、 「立つ/座る/腕を上げる」を 角度ルールだけで自動判定します。 これが 姿勢推定(pose estimation) の最小核 ── 画像から関節点(キーポイント)を取り出し、 その 幾何(角度と距離)だけで動作を特徴づける、という発想の体験版です。

やっていることは 3 ステップ:(1) 関節を キーポイント(点)として表す → (2) 隣り合う骨ベクトルの内積から 関節角度を、 点同士の 距離を計算する → (3) 「膝角 < 150° なら座る/手首が肩より上なら腕を上げる/それ以外は立つ」という 角度ルールで姿勢を分類。 ノイズやオクルージョン(隠れ)を入れると、 推定がどう崩れるかも体感できます。

自動判定(角度ルール)
右肘 角度
左肘 角度
右膝 角度
左膝 角度
胴体 傾き
右手首‐肩 距離
オクルージョン誤差
操作:関節の白点をドラッグ(タッチ可)で姿勢を変形。 黄色アーク=右肘角、 橙アーク=右膝角。 ノイズ σ を上げるとキーポイントが毎フレーム揺れ、 角度と判定がちらつく(検出器ノイズの再現)。 オクルージョンを選ぶとその関節は隠れ(赤枠)、 反対側の関節を胴体軸で 鏡映して推定します ── 左右対称な姿勢なら誤差ゼロですが、 左右非対称に動かすと推定点(赤枠)が真値(薄赤)からズレ、 「オクルージョン誤差」が増えます。

🔍 何が起きているか:姿勢分類の主役は 関節角度です。 肘・膝の角度は「隣り合う 2 本の骨ベクトル $\mathbf{u},\mathbf{v}$ の内積」から $\theta=\arccos\!\big(\tfrac{\mathbf{u}\cdot\mathbf{v}}{\|\mathbf{u}\|\|\mathbf{v}\|}\big)$ で一意に決まります(実装は数値安定な atan2(|cross|, dot))。 立つ=膝ほぼ 180°・腕は下、 座る=膝が曲がる(<150°)、 腕を上げる=手首の $y$ が肩より上 ── この 角度・距離のルールだけで、 生の座標を見ずに姿勢を切り分けられます。 実際の姿勢推定も「画像→キーポイント→角度特徴→分類」という同じ骨格です。

💡 直感 ── 点と角度で姿勢を「読む」

人が姿勢をひと目で読むとき、 見ているのは 関節の位置関係です。 「肘が曲がっている」「手が頭より高い」「膝が直角」── これらはすべて キーポイント間の角度と距離に還元できます。 姿勢推定の要点は、 数百万画素の画像を 十数個の点にまで圧縮し、 その点群の幾何だけで動作を語ること。 上のウィジェットで肘角・膝角が姿勢と連動して変わるのを見れば、 「点と角度で姿勢は完全に記述できる」という直感がつかめます。 姿勢を 特徴づけるのは、 生座標そのものではなく、 角度(回転不変・平行移動不変)や 正規化した距離(体格でスケール補正)です。

⚠️ 落とし穴 ── 「動かして崩れる」ポイント

🚀 発展 ── 角度ルールの先へ

📐 定義・数式

🍰 まずはやさしく

動きをルールにした数式です。

正しい動作を計算するために使います。

ロボットの関節の角度を決める例です。

動きをどう計算するかを読みます。

強化学習で動作を学ぶ際の方策(policy)累積報酬

【方策最適化の基本式】
$$\pi^*(a|s) = \arg\max_\pi \mathbb{E}_{\pi}\!\left[ \sum_{t=0}^{T} \gamma^t r(s_t, a_t) \right]$$
状態 $s$ で行動 $a$ を取る確率分布 $\pi$ を、 累積報酬の期待値を最大化するように学習

制御工学側の表現としては、 動作方程式と最適化問題の組み合わせ:

【モデル予測制御 (MPC)】
$$u^*_t = \arg\min_{u_{t:t+H}} \sum_{k=0}^{H} \|x_{t+k} - x^{\mathrm{ref}}\|^2_Q + \|u_{t+k}\|^2_R$$
未来 $H$ ステップ先まで予測し、 状態誤差と制御量の二乗和を最小化する制御量 $u$ を毎時刻計算

📐 数式・定義

身体・運動 AIを数式 / 形式定義で表す:

$$\boldsymbol{\tau} = M(\boldsymbol{q})\ddot{\boldsymbol{q}} + C(\boldsymbol{q}, \dot{\boldsymbol{q}})\dot{\boldsymbol{q}} + G(\boldsymbol{q})$$

ロボット動力学方程式:関節トルク $\boldsymbol{\tau}$ は、 慣性 $M$・コリオリ $C$・重力 $G$ の和で決まる。 運動 AI はこの $\boldsymbol{\tau}$ を最適化する。

📐 数式:関節角度、 FFT、 DTW

① 関節角度(Euler 角ベース):3 次元空間で隣接する 2 つの骨の方向ベクトル $\mathbf{u}, \mathbf{v}$ がなす角度は内積から導けます。

$$ \theta = \arccos\!\left( \frac{\mathbf{u} \cdot \mathbf{v}}{\|\mathbf{u}\| \|\mathbf{v}\|} \right), \quad \theta \in [0, \pi] $$

② FFT(高速フーリエ変換)による周波数特徴:歩行や走行は周期的なので、 加速度信号 $x(t)$ をスペクトルに変換すると「歩行周波数」のピークが現れます。 離散 FFT は次の通り:

$$ X_k = \sum_{n=0}^{N-1} x_n \cdot e^{-i \cdot 2\pi k n / N}, \quad k = 0, 1, \ldots, N-1 $$

パワースペクトル密度 $P_k = |X_k|^2 / N$ のピークが 1.5〜2.5 Hz にあれば歩行、 2.5〜4 Hz は走行、 と判定できます。

③ DTW(Dynamic Time Warping):2 つの時系列 $X = (x_1, \ldots, x_N)$、 $Y = (y_1, \ldots, y_M)$ の「形は似てるが時間軸が伸縮した」類似度を測る指標。 動的計画法で最適アライメントを求めます:

$$ D(i, j) = d(x_i, y_j) + \min\bigl\{ D(i-1, j),\ D(i, j-1),\ D(i-1, j-1) \bigr\} $$

$$ \text{DTW}(X, Y) = D(N, M) $$

🔬 数式を言葉で読み解く

🔬 記号・要素の読み解き

$s_t$(状態)
時刻 $t$ での観測値(関節角度、 速度、 カメラ画像、 IMU 等)
$a_t$(行動)
その時刻に出力する制御量(モーター指令、 ハンドル角度、 推力指令)
$r(s,a)$(報酬)
「望ましさ」を数値化。 「前進」「省エネ」「安定」など。 設計次第で挙動が激変
$\gamma$(割引率)
0〜1 の値。 1 に近いほど遠い未来の報酬も重視する
$\pi$(方策)
状態を入れると行動を返す関数。 通常はニューラルネット(数千万パラメータ)
$x^{\mathrm{ref}}$(参照軌道)
MPC で「こう動いてほしい」目標の状態列

🔬 数式を言葉で読み解く

上の数式に出てきた記号を 1 つずつ解説します。 数式が出てくる試験問題(統計検定・G 検定・基本情報)では、 各記号の意味を答えられるかが分岐点:

記号意味
$\boldsymbol{q}$関節角度ベクトル
$\dot{\boldsymbol{q}}, \ddot{\boldsymbol{q}}$速度・加速度
$M(\boldsymbol{q})$慣性行列
$C$コリオリ・遠心項
$G(\boldsymbol{q})$重力項
$\boldsymbol{\tau}$関節に与えるトルク(制御出力)

🔬 発展トピック

「身体・運動」を入門レベルで習得した次に進むべき発展テーマ:

① 理論的拡張

基本概念を 確率論・情報理論・最適化理論の観点で再定式化すると、 隣接する手法との理論的な関係が見えてきます。 たとえば 正則化は事前分布の最大事後推定と等価クロスエントロピー損失は KL ダイバージェンスを最小化、 といった対応関係を押さえると教科書間の往復が楽になります。

② 実装的拡張

scikit-learn 標準実装の外側に出ると、 GPU 対応・分散学習・低精度浮動小数点(fp16/bf16)・量子化(int8)・グラフ最適化(TorchScript・ONNX Runtime)など、 推論性能を 10–100 倍引き上げるテクニックが豊富にあります。 本番運用では モデル精度と推論コストのトレードオフを意識した実装が鍵。

③ 評価・解釈の拡張

予測精度だけでなく SHAP・LIME・Permutation Importance によるモデル解釈、 Calibration(確率の校正)Counterfactual ExplanationFairness 指標(demographic parity, equalized odds 等)を組合せると、 業務応用での説得力が一段増します。

④ 業界応用

医療(薬機法・GxP)・金融(モデル管理ガイドライン)・公共(個人情報保護法)など、 業界固有の規制・ガイドラインを モデル設計段階から埋め込むのが現代のスタンダード。 「身体・運動」を業務適用するときは、 ドメインの専門家・法務との早期コラボレーションが成否を分けます。

🔬 数式を言葉で読み解く(身体・運動データ解析の体系)

身体・運動データ(Body Movement Data)は、 ヒトや動物の身体運動を時系列・空間的に記録した数値データの総称である。 ここでは「モーションキャプチャ」「IMU センサー」「動画解析」「スポーツ科学」「リハビリ応用」「ジェスチャー認識」の 6 領域を、 数式と代表データ形式を軸に整理する。 身体動作データは「サンプリング周波数 × 関節数 × 軸数」の三次元テンソル $X \in \mathbb{R}^{T \times J \times D}$ として扱われ、 $T$ はフレーム数、 $J$ は関節数(OpenPose では 25、 SMPL では 24)、 $D$ は座標次元(2D 画像なら 2、 3D モーキャプなら 3)である。 いずれの領域でも入力の実体は「加速度・角速度の多軸時系列」または「関節・骨格の 3 次元座標系列」であり、 サンプリング周波数・関節数・軸数の 3 つが解析設計の出発点となる。

🎯 1. モーションキャプチャ系(Marker-based / Markerless)

光学式モーションキャプチャでは、 被験者の関節付近に貼られた反射マーカーの位置 $\mathbf{p}_j(t) = (x_j, y_j, z_j)$ を 100-1000 Hz で記録する。 関節角度 $\theta(t)$ は隣接マーカー間ベクトル $\mathbf{v}_1, \mathbf{v}_2$ の内積から $\theta = \arccos\left(\frac{\mathbf{v}_1 \cdot \mathbf{v}_2}{|\mathbf{v}_1||\mathbf{v}_2|}\right)$ で求める。 マーカーレス系(OpenPose, MediaPipe)は深層学習で 2D キーポイントを推定し、 三角測量で 3D 化する。 得られる $\mathbf{p}_j(t)$ の座標系列から、 歩幅・関節可動域・重心動揺といった臨床指標を算出できる。

関節角度の時系列散布
図 1: 歩行 1 サイクル中の膝関節角度(横軸: 時間 [s]、 縦軸: 角度 [deg])。 立脚相と遊脚相で異なる軌跡を描く。

🎯 2. IMU センサー系(加速度・角速度・地磁気)

IMU(Inertial Measurement Unit)は加速度 $\mathbf{a}(t) \in \mathbb{R}^3$、 角速度 $\boldsymbol{\omega}(t) \in \mathbb{R}^3$、 地磁気 $\mathbf{m}(t) \in \mathbb{R}^3$ を同時計測する 9 軸センサーである。 姿勢 $\mathbf{q}(t)$(四元数)は相補フィルタ $\mathbf{q}_{t+1} = (1-\alpha) \cdot \mathbf{q}_{\text{gyro}} + \alpha \cdot \mathbf{q}_{\text{acc+mag}}$ で推定し、 $\alpha \approx 0.02$ が典型値。 歩数推定は加速度ノルム $|\mathbf{a}| = \sqrt{a_x^2 + a_y^2 + a_z^2}$ のピーク検出で行う。 Apple Watch・Fitbit などのウェアラブルはこの原理で日々の歩数・活動量を推定し、 加速度ノルムの時系列を特徴量化して行動を分類している。

動作パターン分布
図 2: 1 日あたり歩数の分布ヒストグラム(多数の被験者の加速度ログから集計)。 右に裾を引く対数正規型に近い。

🎯 3. スポーツ科学・パフォーマンス分析

投球速度・走行速度・ジャンプ高 $h = \frac{v_0^2 \sin^2\theta_0}{2g}$ といった運動学的指標と、 関節トルク $\tau = I \alpha$($I$: 慣性モーメント、 $\alpha$: 角加速度)といった力学的指標がある。 トラックマン(野球)、 Catapult(サッカー)、 STATSports(ラグビー)などの GPS+IMU 一体型デバイスは、 選手の総走行距離・スプリント回数・心拍数を試合中リアルタイム記録する。 これらの多軸時系列から疲労・傷害リスクを推定し、 トレーニング負荷(ACWR など)の管理に機械学習が活用されている。

選手レベル別比較
図 3: 競技レベル別の走行速度分布の比較(箱ひげ図)。 上位群ほど中央値が高く、 ばらつきが小さい傾向。

🎯 4. リハビリテーション応用

脳卒中後の上肢リハビリでは、 Fugl-Meyer Assessment スコアと身体動作データ(reaching task の軌道滑らかさ:jerk $j = \frac{d^3 x}{dt^3}$ の積算)が強く相関する。 リハビリ進捗の定量化指標 NJM(Normalized Jerk Metric)$\text{NJM} = \sqrt{\frac{1}{2} \int_0^T j^2 \, dt \cdot \frac{T^5}{L^2}}$ は健常者で 5-15、 重度患者で 200 を超える。 高齢者転倒予測モデルでは IMU + 機械学習(XGBoost)で AUC 0.85 を達成し、 日常の加速度ログから転倒の予兆となる歩容変化を検出できる。

🎯 5. ジェスチャー認識・HCI

手話認識・サインジェスチャ識別では LSTM/Transformer で時系列特徴量を分類する。 入力テンソル $X \in \mathbb{R}^{T \times 21 \times 3}$(MediaPipe Hands の 21 キーポイント × 3 軸)を、 双方向 LSTM 隠れ状態 $\mathbf{h}_t = \text{BiLSTM}(\mathbf{x}_t, \mathbf{h}_{t-1}, \mathbf{h}_{t+1})$ で符号化し、 ソフトマックス分類器で語彙 ID を出力する。 ASL(米国手話)データセット WLASL2000 で SOTA 精度 70%+。 動的時間伸縮(DTW)距離 $D(i,j) = d(i,j) + \min(D(i-1,j), D(i,j-1), D(i-1,j-1))$ も古典手法として現役。

🎯 6. 行動分類・日常活動認識(HAR)

スマートフォン IMU からの行動認識(Human Activity Recognition: HAR)は「歩く・走る・座る・立つ・階段昇降」など 6-12 クラスを CNN+LSTM で分類する。 公開データセット UCI HAR(30 人 × 6 行動 × 50Hz × 2.56 秒窓)でテスト精度 96% 超。 PAMAP2 では心拍数も統合し、 安静時心拍 60 bpm vs 階段昇降 130 bpm を識別できる。 加速度・角速度・心拍を統合した特徴量で「日常活動量(MVPA 分・歩数)」を定量化し、 健康アウトカムとの関連を検証する研究が広がっている。

✅ 理解度チェック(3 問)

  1. Q1: モーションキャプチャで関節角度を計算する式は?
    A: 隣接マーカー間ベクトル $\mathbf{v}_1, \mathbf{v}_2$ の内積から $\theta = \arccos\left(\frac{\mathbf{v}_1 \cdot \mathbf{v}_2}{|\mathbf{v}_1||\mathbf{v}_2|}\right)$。
  2. Q2: IMU の 9 軸とは何か?
    A: 加速度 3 軸、 角速度 3 軸、 地磁気 3 軸の合計 9 軸。
  3. Q3: リハビリ進捗指標 NJM の健常者 vs 重度患者の典型値は?
    A: 健常者 5-15、 重度患者は 200 超。

💬 まとめ: 身体・運動データ解析は「センサー → 特徴量 → モデル → 応用」の 4 段階で構造化でき、 各段階で異なる数式・ツールが必要となる。 個人スケールのセンサーログに加え、 公的統計のマクロ指標(本ページ後半の SSDSE-B-2026 演習を参照)と接続すれば、 動作データから地域スケールの健康傾向まで視野を広げられる。 これは「定量的健康科学」「スマートシティ」「介護予防」の根幹技術である。

🧪 SSDSE-B-2026 で「地域特性 × 出生率」を偏相関で多角的に検証する

SSDSE-B-2026 には「スポーツ実施率・歩数・健康寿命・国民医療費・要介護認定率」といった身体活動そのものや健康アウトカムの変数は収録されていない。 そこで本セクションでは、 実在する都道府県の地域特性指標(着工新設住宅戸数・延べ宿泊者数・医療施設密度・高齢化率・出生率)を題材に、 単純相関 → 偏相関という交絡統制の標準ワークフローを実データで体験する。 個人スケールの IMU データ解析(本ページ前半)と、 公共統計スケールの多変量解析を接続する「型」を身につけるのが狙いである。 使用データは SSDSE-B-2026.csv(564 行 = 47 都道府県 × 12 年分、 112 列)で、 年次列 SSDSE-B-2026==2023 を抽出して用いる。 相関・偏相関の数値はすべて 2023 年 47 都道府県で実測したものである。

📋 使用変数の整理(SSDSE-B-2026 実在列のみで構成)

身体活動・健康寿命の直接変数は SSDSE-B に無いため、 実在列から派生計算した地域特性指標で交絡統制のワークフローを実演する。 いずれも 2023 年 47 都道府県の実測値。

記号SSDSE-B 列(派生計算)指標単位典型レンジ(2023, 47 都道府県)
$X_1$H1800 / A1101 × 1000着工新設住宅戸数(人口千対)3.6 - 8.9
$X_2$G7101 / A1101延べ宿泊者数(1 人当たり)0.6 - 13.7
$X_3$I510120 / A1101 × 10⁵一般病院数(人口 10 万対)施設3.1 - 16.1
$X_4$I5102 / A1101 × 10⁵一般診療所数(人口 10 万対)施設62 - 113
$Z$A1303 / A1101 × 100高齢化率(65 歳以上割合)=交絡変数%22.8 - 39.1
$Y$A4101 / A1101 × 1000出生率(人口千対)=アウトカム4.0 - 8.6

🐍 ステップ 1: 単純相関の総当たり

このコードでやること: SSDSE-B-2026 の 2023 年(SSDSE-B-2026==2023)から地域特性 6 変数を派生計算し、 pandas.DataFrame.corr() でピアソン相関の総当たり行列を作成、 ヒートマップで可視化する。

📥 入力データ(読み込み後 head の例):

都道府県 X1_住宅 X2_宿泊 X3_病院 X4_診療 Z_高齢化 Y_出生 0 北海道 5.59 6.44 9.11 66.83 33.01 4.80 1 青森県 3.95 3.28 6.08 71.79 35.22 4.81 2 岩手県 5.53 4.28 6.53 75.58 35.00 4.67 3 宮城県 6.92 4.05 4.77 76.15 29.24 5.45 4 秋田県 3.78 2.85 5.25 88.18 39.06 3.95
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
1
2
3
4
5
6
7
8
9
10
11

📤 実行例(相関行列、 主要部分):

X1_住宅 X2_宿泊 X3_病院 X4_診療 Z_高齢化 Y_出生 X1_住宅 1.000 0.120 -0.263 -0.070 -0.782 0.577 X2_宿泊 0.120 1.000 -0.079 -0.029 -0.259 0.325 X3_病院 -0.263 -0.079 1.000 0.227 0.543 0.032 X4_診療 -0.070 -0.029 0.227 1.000 0.191 0.055 Z_高齢化 -0.782 -0.259 0.543 0.191 1.000 -0.615 Y_出生 0.577 0.325 0.032 0.055 -0.615 1.000

💬 読み方: 着工新設住宅戸数(X1)・延べ宿泊者数(X2)は出生率(Y)と r ≈ +0.58, +0.33 の正相関を示し、 一見「建設・交流が活発な地域ほど出生率が高い」ように見える。 しかし高齢化率(Z)は出生率と r = −0.62、 住宅着工とも −0.78 と強く相関しており、 Z が最有力の交絡候補である。 逆に一般病院数(X3)は出生率とほぼ無相関(r = +0.03)に見えるが、 Z とは +0.54(過疎・高齢地ほど人口当たり病院が多い)で、 この見かけの無相関は Z による抑制(suppression)の疑いがある。 単純相関だけで結論せず、 次の偏相関で高齢化率を統制する。

🐍 ステップ 2: 高齢化率を統制した偏相関

このコードでやること: 高齢化率 $Z$ を条件として、 地域特性指標(住宅着工・延べ宿泊・一般病院数)と出生率の偏相関係数を計算する。 $r_{XY \cdot Z} = (r_{XY} - r_{XZ}r_{YZ}) / \sqrt{(1 - r_{XZ}^2)(1 - r_{YZ}^2)}$。

1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9

📤 実行例(偏相関):

n r CI95% p-val pearson 47 0.197 [-0.10, 0.46] 0.1895 ← X1住宅着工 vs Y出生率 | 高齢化率 pearson 47 0.218 [-0.08, 0.48] 0.1459 ← X2延べ宿泊 vs Y出生率 | 高齢化率 pearson 47 0.552 [ 0.31, 0.73] 0.0001 ← X3一般病院数 vs Y出生率 | 高齢化率

💬 読み方: 住宅着工 × 出生率は単純相関 +0.58 だったが、 高齢化率を統制した偏相関では r=0.20(p=0.19)へ縮小し有意でなくなる → 見かけの相関の大半は高齢化率による交絡だった。 延べ宿泊 × 出生率も +0.33 → 0.22(p=0.15)と同様。 対照的に一般病院数 × 出生率は単純相関 +0.03(ほぼ無相関)だったのに、 高齢化率を統制すると r=0.55(p<0.001)の強い正相関が出現する(抑制効果 suppression)。 高齢化率が「人口比の病院数を増やしつつ出生率を下げる」ため、 単純相関では真の関連が打ち消されていた。 交絡変数は見かけを水増しも真値を隠しもする——偏相関で両方向を点検すべき典型例である。

📈 IMU 6 軸ストリーム解析:歩行サイクル分割と歩幅推定の実装パターン

スマートウォッチや腰部 IMU から得られる 100 Hz の加速度・角速度ストリームから、 歩行イベント(踵接地・つま先離地)を検出し、 ケーデンス・歩幅・対称性を算出するのは身体運動解析の中核タスクである。 ここでは scipy.signal で完結する実装と、 外部統計の全国歩数平均(後述の概算値。 SSDSE-B に歩数列は無い)との接続を示す。

📊 歩行解析パラメータの典型範囲

パラメータ記号健常成人転倒高リスク高齢者パーキンソン病計算式
ケーデンス$f_c$110 - 120 spm85 - 100 spm90 - 130 spm(変動大)$60 / T_{step}$
歩幅$L_s$0.65 - 0.75 m0.35 - 0.50 m0.30 - 0.55 m$v \cdot T_{step}$
歩行速度$v$1.2 - 1.4 m/s0.6 - 0.9 m/s0.8 - 1.1 m/s$L_s \cdot f_c / 60$
両脚支持期割合$DS\%$20 - 25 %30 - 40 %25 - 35 %$T_{DS} / T_{cycle}$
左右対称指数$SI$< 5 %10 - 25 %5 - 20 %$2|L - R| / (L + R) \cdot 100$
加速度 RMS$a_{RMS}$0.30 - 0.50 g0.15 - 0.25 g0.20 - 0.35 g$\sqrt{\frac{1}{N}\sum a_i^2}$

🐍 加速度ピーク検出による歩数カウント

このコードでやること: 腰部 IMU の鉛直加速度ストリーム(CSV 形式、 100 Hz)を読み込み、 scipy.signal.find_peaks で踵接地時刻を検出し、 平均ケーデンスと歩数を算出する。 外部統計の全国歩数平均と比較する。

📥 入力データ例(PhysioNet Daphnet 形式):

time_s ax_g ay_g az_g gx_dps gy_dps gz_dps 0.000 0.012 0.025 0.985 1.42 -0.32 0.18 0.010 0.045 0.034 1.024 3.21 -0.41 0.22 0.020 0.082 0.041 1.082 5.85 -0.55 0.31 0.030 0.124 0.052 1.165 8.92 -0.62 0.42 0.040 0.158 0.061 1.241 11.20 -0.71 0.48
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

📤 実行例:

歩数: 248 歩 / 132.4 秒 ケーデンス: 112.3 spm # 1 日換算(覚醒時間 16 時間で同強度を 20 % の時間維持と仮定) 推定 1 日歩数 ≈ 112.3 × 60 × 16 × 0.20 ≈ 21,560 歩(高活動) # 全国平均(成人男女)≈ 6,800 歩(外部統計の概算・SSDSE-B に歩数列なし)→ 上位 5 % 相当 # 厚労省「健康日本 21」目標値 8,000 歩を 169 % 達成

💬 読み方: ピーク検出による歩数カウントは 誤差 ±2 % で実用域、 ただし「歩行ではない上下動」(階段昇降・ジョギング)と混同するため、 厳密にはバンドパスフィルタの設計(0.5 - 3 Hz)と prominence 閾値の調整が重要。 外部統計の全国・地域歩数平均(概算)との接続では「個人の 1 日歩数 → 平均との Z スコア」算出により、 当該地域の中での自分の位置を把握できる。

📊 IMU データ品質チェックリスト

項目確認内容異常時の対処
サンプリング周波数歩行解析は 50 Hz 以上、 跳躍・走行は 200 Hz 以上推奨低周波だとピーク取りこぼし → 機材変更
重力校正静止時 $\|\mathbf{a}\| = 1.000 \pm 0.005$ gずれが大きい場合は装着直し / キャリブレーション
軸定義装着方向と座標系(右手系 / 左手系)の一致軸ラベル付け間違いは符号反転で致命的誤り
欠損BLE 通信切れによる NaN / 連続欠損5 サンプル以下は線形補間、 それ以上は当該区間除外
時刻同期複数センサー使用時のタイムスタンプ整合NTP 同期 or 撮影と同期するための「手叩き」キャリブレーション
ドリフトジャイロの DC オフセット(静止時にゼロでない)計測開始時に 5 秒静止 → 平均値を引く

🧮 数値例・実値計算

シンプルな例:振り子立て課題(CartPole)。

要素内容
状態 $s$(位置, 速度, 角度, 角速度) の 4 次元
行動 $a$左に押す / 右に押す の 2 択
報酬 $r$1 ステップ立っていれば +1
最大長500 ステップ

同じ枠組みで、 二足歩行なら状態 100 次元・行動 20 次元、 自動運転ならカメラ画像(数百万次元)まで扱えます。

🧮 SSDSE-B 実値計算 — 都道府県データで手を動かす

身体・運動 AI は SSDSE-B-2026 に直接対応する変数はないが、 「介護・看護人材の需要予測」という応用文脈を想定し、 高齢者人口(A1303)と一般病院数(I510120)の比から運動支援ロボット需要の地域差を試算する。 なお H1800 は「病院数」ではなく 着工新設住宅戸数であり、 病院数の正しい列は I510120(一般病院数)である点に注意。

使用データ:SSDSE-B-2026.csv(独立行政法人 統計センター提供、 564 行 = 47 都道府県 × 12 年分、 112 列の社会経済指標)。 年次列で 2023 年を抽出して用いる。 出典

🎯 このコードでやること: SSDSE-B-2026 を読み込み。

📥 入力例 # 入力: data/raw/SSDSE-B-2026.csv (564 行 = 47 都道府県 × 12 年 × 112 指標)→ 2023 年抽出 # 2023 年 先頭 3 行(A1101 = 総人口、 A4101 = 出生数、 F3101 = 新規求職申込件数): # pref A1101 A4101 F3101 # 北海道 5092000 24430 156458 # 青森県 1184000 5696 43713 # 岩手県 1163000 5432 40955
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].rename(columns={df.columns[2]: 'pref'})

# 65 歳以上人口 1 万人あたりの一般病院数 → 介護ロボ需要の代理指標
df['hospitals_per_10k_elderly'] = df['I510120'] / df['A1303'] * 10000
df_sorted = df[['pref', 'A1303', 'I510120', 'hospitals_per_10k_elderly']]\
              .sort_values('hospitals_per_10k_elderly', ascending=False)
print('▼ 介護運動ロボ需要が高い地域 Top 5')
print(df_sorted.head(5).to_string(index=False))
📤 実行例 # df.shape 読み込み直後 = (564, 112)、 2023 年抽出後 = (47, 112) ▼ 介護運動ロボ需要が高い地域 Top 5 pref A1303 I510120 hospitals_per_10k_elderly 高知県 242000 107 4.421488 徳島県 246000 90 3.658537 鹿児島県 524000 191 3.645038 大分県 375000 126 3.360000 佐賀県 252000 82 3.253968

💬 読み方: skiprows=[1] で英語ヘッダ行を飛ばし、 encoding='cp932' で文字化けを回避。 年次列 SSDSE-B-2026==2023 で 1 年分に絞ると 47 行になる。

▲ 上記コードはそのまま実行可能。 CP932 エンコーディング・skiprows=[1](英語ヘッダ行をスキップ)・年次抽出・列名の英数字コード(A1101 = 総人口、 I510120 = 一般病院数 など)に注意。

🧮 実値で計算:SSDSE-B-2026 から「県別運動活発度」を推定

身体動作センサーログそのものは SSDSE には含まれませんが、 SSDSE-B-2026 には以下のような 運動量プロキシ指標があります:

これらを組合せて「動作活発度指標」を都道府県ごとに計算し、 上位 5・下位 5 を見比べます。 また、 ウェアラブルセンサーの 100Hz 加速度ログを模擬した合成解析(実データ部分は SSDSE で完結)として、 FFT・DTW のコードも実行します。

指標運動量への影響符号
教養娯楽費 (L322109)高いほどスポーツ・行楽参加が多い+
保健医療費 (L322106)高いほど運動不足の兆候(負の相関)
高齢化率 (A1303/A1101)高いほど運動量が低下
年平均気温 (B4101)温暖なほど屋外活動可能性が増える+

🧮 数式に値を入れて手で計算する: 加速度センサーから歩数推定

合成 5 秒データで加速度ノルムのピークから歩数を推定する。

Step 1: 加速度ノルム (5 サンプル)

t [s]axayaz|a|
0.00.50.09.89.813
1.01.20.510.510.587
2.00.00.09.59.500
3.01.50.310.810.910
4.00.20.09.69.602

Step 2: 閾値 10.0 超ピーク数 = 歩数推定

|a| > 10.0 を満たすのは t=1.0, t=3.0 の 2 ステップ → 推定 2 歩

🐍 Python で再現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import numpy as np
acc = np.array([
  [0.5, 0.0, 9.8],
  [1.2, 0.5, 10.5],
  [0.0, 0.0, 9.5],
  [1.5, 0.3, 10.8],
  [0.2, 0.0, 9.6],
])
norm = np.linalg.norm(acc, axis=1)
steps = (norm > 10.0).sum()
print(f"|a| = {norm.round(3)}")
print(f"歩数 = {steps}")

📤 実行結果

|a| = [ 9.813 10.587 9.5 10.91 9.602] 歩数 = 2

💬 手計算 (Step 2) 2 歩と Python 出力が完全一致。

🐍 Python 実装例

最小コードで動かしてみる例:

🎯 このコードでやること: 予測を取得。

📥 入力例 # 入力: 前段の処理結果(DataFrame または ndarray)を前提 # 例: df.shape == (47, 12)、 X.shape == (47, 5)、 y.shape == (47,)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 振り子立てを強化学習で解く(4-8行)
from stable_baselines3 import PPO
import gymnasium as gym

env = gym.make('CartPole-v1')
model = PPO('MlpPolicy', env, verbose=0)
model.learn(total_timesteps=50000)

# 学習済み方策で動作を出力
obs, _ = env.reset()
action, _ = model.predict(obs)
📤 実行例 (明示的な print なし。 Jupyter 上では最終行が表示される)

💬 読み方: 「身体運動・身体性」の典型パターン。 列名や引数を変えると応用可能。

🐍 Python 実装バリエーション

「身体・運動 AI」を扱う代表的なライブラリ別実装。 同じ目的でも書き方が違うため、 自分のプロジェクトの依存関係に合わせて選択する:

① pandas + numpy(最小依存)

🎯 このコードでやること: SSDSE-B-2026 を読み込み。

📥 入力例 # 入力: data/raw/SSDSE-B-2026.csv (564 行 = 47 都道府県 × 12 年 × 112 指標)→ 2023 年抽出 # 2023 年 先頭 3 行(A1101 = 総人口、 A4101 = 出生数、 F3101 = 新規求職申込件数): # pref A1101 A4101 F3101 # 北海道 5092000 24430 156458 # 青森県 1184000 5696 43713 # 岩手県 1163000 5432 40955
1
2
3
4
5
6
7
8
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].rename(columns={df.columns[2]: 'pref'})

print('行数:', len(df), '列数:', df.shape[1])
print(df[['pref', 'A1101', 'A4101', 'A5101', 'F3101']].head())
📤 実行例 行数: 47 列数: 112 pref A1101 A4101 A5101 F3101 0 北海道 5092000 24430 47388 156458 12 青森県 1184000 5696 15226 43713 24 岩手県 1163000 5432 14903 40955 36 宮城県 2264000 12328 42940 64047 48 秋田県 914000 3611 10002 30175

💬 読み方: skiprows=[1] で日本語見出し行を飛ばし(英語コード行を残す)、 encoding='cp932' で文字化けを回避。 年次列で 2023 を抽出して 47 行に絞る。

② scikit-learn(学習・評価)

🎯 このコードでやること: 学習用と評価用にデータを分割、回帰モデルを学習、予測を取得、精度を評価。

📥 入力例 # 入力: 前段の処理結果(DataFrame または ndarray)を前提 # 例: df.shape == (47, 12)、 X.shape == (47, 5)、 y.shape == (47,)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split
import numpy as np

X = df[['A1101', 'A1303']].fillna(0).values
y = df['A4101'].values
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)
m = LinearRegression().fit(X_tr, y_tr)
pred = m.predict(X_te)
print(f'R²   = {r2_score(y_te, pred):.3f}')
print(f'RMSE = {np.sqrt(mean_squared_error(y_te, pred)):.2f}')
📤 実行例(実測) R² = 0.982 RMSE = 3438.97

💬 読み方: random_state=42 を固定すると再現性が確保される / テスト指標が学習指標より極端に低い場合は過学習を疑う。

③ scipy.stats(統計検定・分布)

🎯 このコードでやること: 「身体運動・身体性」の最小コード。

📥 入力例 # 入力: 前段の処理結果(DataFrame または ndarray)を前提 # 例: df.shape == (47, 12)、 X.shape == (47, 5)、 y.shape == (47,)
1
2
3
4
5
6
7
8
9
from scipy import stats

# 例: 2 変数の Pearson 相関 + p 値
r, p = stats.pearsonr(df['A1101'], df['A4101'])
print(f'相関係数 r = {r:.3f}, p 値 = {p:.2e}')

# 例: 1 標本 t 検定(平均が一定値と異なるか)
t, p = stats.ttest_1samp(df['A4101'], popmean=df['A4101'].mean())
print(f't = {t:.3f}, p = {p:.3f}')
📤 実行例 (結果はターミナルに出力されます) 例: 期待される出力は数値・配列形・要約統計です

💬 読み方: 「身体運動・身体性」の典型パターン。 列名や引数を変えると応用可能。

④ 可視化(matplotlib + seaborn)

🎯 このコードでやること: 「身体運動・身体性」の最小コード。

📥 入力例 # 入力: 前段の処理結果(DataFrame または ndarray)を前提 # 例: df.shape == (47, 12)、 X.shape == (47, 5)、 y.shape == (47,)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import matplotlib.pyplot as plt
import seaborn as sns

fig, ax = plt.subplots(figsize=(8,5))
sns.scatterplot(data=df, x='A1101', y='A4101', ax=ax)
ax.set_xlabel('総人口')
ax.set_ylabel('出生数')
ax.set_title(f'{len(df)} 都道府県の関係')
plt.tight_layout()
plt.savefig('out.png', dpi=120)
plt.close()
📤 実行例 (明示的な print なし。 Jupyter 上では最終行が表示される)

💬 読み方: 「身体運動・身体性」の典型パターン。 列名や引数を変えると応用可能。

🐍 Python:SSDSE から動作活発度、 加速度 FFT、 DTW を実装

① SSDSE 都道府県別の運動活発度ランキング

🎯 このコードでやること: SSDSE-B-2026 から「教養娯楽費 / 保健医療費 / 高齢化率 / 平均気温」を取得し、 標準化したうえで合算した 運動活発度スコアを都道府県ごとに計算する。

📥 入力データ (SSDSE-B-2026.csv 2023 年抜粋): 都道府県 教養娯楽費 L322109 保健医療費 L322106 高齢化率 平均気温 B4101 北海道 296,888 74,341 0.330 11.0 東京都 365,012 79,210 0.232 17.6 沖縄県 213,540 65,832 0.243 24.3
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)

# 高齢化率 = 65 歳以上人口 / 総人口
df['aging_rate'] = df['A1303'] / df['A1101']

# 標準化(z-score)して合算
def z(s): return (s - s.mean()) / s.std()
df['activity_score'] = (
    + z(df['L322109'])  # 教養娯楽費(+)
    - z(df['L322106'])  # 保健医療費(−)
    - z(df['aging_rate'])  # 高齢化率(−)
    + z(df['B4101'])  # 平均気温(+)
)

ranked = df[['Prefecture', 'activity_score']].sort_values('activity_score', ascending=False)
print('=== Top 5(運動活発度・高) ===')
print(ranked.head(5).to_string(index=False))
print('=== Bottom 5(運動活発度・低) ===')
print(ranked.tail(5).to_string(index=False))
📤 実行例: === Top 5(運動活発度・高) === 東京都 3.12 神奈川県 2.41 愛知県 2.07 埼玉県 1.85 千葉県 1.62 === Bottom 5(運動活発度・低) === 秋田県 -2.74 高知県 -2.12 島根県 -2.05 岩手県 -1.93 山形県 -1.86

💬 読み方: 都市部(東京・神奈川・愛知)で活発度が高く、 高齢化が進んだ北東北・山陰で低い。 ただしこれは「教養娯楽費が高い → 活発」という相関の話で、 因果ではない。 動作センサーログを 47 県分集めれば本物の運動量比較ができるが、 公開データではこの代理指標が限界。

② 都道府県別「気温変動」の FFT で年周期成分を抽出

🎯 このコードでやること: 身体動作の周期性解析と同じ FFT 手法を、 SSDSE-B-2026 の都道府県別年平均気温時系列(2012-2023 の 12 年分)に適用し、 トレンド成分の強さを評価する。 これは「歩行周波数抽出」と同じ信号処理パイプラインで、 実データ(公的統計)で挙動を確認できる。

📥 入力: SSDSE-B-2026 の B4101(年平均気温)2012-2023 年・47 都道府県 例: 東京都の系列 ≒ 16.3, 15.9, 16.2, 16.4, 16.0, 15.8, 16.8, 16.5, 16.7, 16.6, 17.0, 17.6
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])

# 東京都の B4101(年平均気温)時系列 12 年分
tokyo = df[df['Prefecture'] == '東京都'].sort_values('SSDSE-B-2026')
signal = tokyo['B4101'].values
years = tokyo['SSDSE-B-2026'].values
print('東京都 年平均気温:', signal)

# 平均除去(DC 成分を抜く)
sig = signal - signal.mean()

# FFT(年単位サンプリング = 1Hz)
X = np.fft.rfft(sig)
freqs = np.fft.rfftfreq(len(sig), d=1.0)
psd = (np.abs(X) ** 2) / len(sig)

print('\n周波数(周期/年) と PSD:')
for f, p in zip(freqs, psd):
    period = (1/f) if f > 0 else float('inf')
    print(f'  freq={f:.3f}/yr  period={period:5.1f} 年  PSD={p:7.4f}')
📤 実行例: 東京都 年平均気温: [16.3 15.9 16.2 16.4 16.0 15.8 16.8 16.5 16.7 16.6 17.0 17.6] 周波数(周期/年) と PSD: freq=0.000/yr period= inf 年 PSD=0.0000 freq=0.083/yr period= 12.0 年 PSD=0.2640 freq=0.167/yr period= 6.0 年 PSD=0.0157 freq=0.250/yr period= 4.0 年 PSD=0.0090 freq=0.333/yr period= 3.0 年 PSD=0.0042 freq=0.417/yr period= 2.4 年 PSD=0.0012 freq=0.500/yr period= 2.0 年 PSD=0.0285

💬 読み方: 周期 12 年成分の PSD が最大 = 12 年に 1 度の長期上昇トレンド(温暖化)を捉えている。 ウェアラブルの加速度ログでは同じコードで 2Hz 付近に歩行周期のピークが出る。 信号処理パイプラインは「動作」「気温」「経済指標」のどれでも同じ。

③ DTW で 3 都道府県の人口推移を類似度評価

🎯 このコードでやること: 身体動作の波形類似度比較と同じ DTW を、 SSDSE-B-2026 の都道府県別人口推移時系列に適用。 「形は似てるがスケール / 速度が違う」県を見つける。 同じアルゴリズムが「歩行リズム」「経済時系列」のどちらでも機能することを示す。

📥 入力: SSDSE-B-2026 の A1101(総人口)2012-2023、 12 年分 東京都・神奈川県・北海道の 3 県を比較 各系列は z-score 正規化(スケールを揃える)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import numpy as np
import pandas as pd

def dtw(a, b):
    n, m = len(a), len(b)
    D = np.full((n+1, m+1), np.inf)
    D[0, 0] = 0
    for i in range(1, n+1):
        for j in range(1, m+1):
            cost = abs(a[i-1] - b[j-1])
            D[i, j] = cost + min(D[i-1, j], D[i, j-1], D[i-1, j-1])
    return D[n, m]

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])

def series(pref):
    s = df[df['Prefecture'] == pref].sort_values('SSDSE-B-2026')['A1101'].values
    return (s - s.mean()) / s.std()   # z-score 正規化

A_tokyo = series('東京都')
B_kanagawa = series('神奈川県')
C_hokkaido = series('北海道')

print(f'DTW(東京, 神奈川) = {dtw(A_tokyo, B_kanagawa):.3f}  (両方とも増加トレンド)')
print(f'DTW(東京, 北海道) = {dtw(A_tokyo, C_hokkaido):.3f}  (片方増加、 片方減少)')
print(f'DTW(東京, 東京) = {dtw(A_tokyo, A_tokyo):.3f}  (完全一致)')

# ユークリッド距離との比較
print()
print(f'Euclid(東京, 神奈川) = {np.linalg.norm(A_tokyo - B_kanagawa):.3f}')
print(f'Euclid(東京, 北海道) = {np.linalg.norm(A_tokyo - C_hokkaido):.3f}')
📤 実行例: DTW(東京, 神奈川) = 0.582 (両方とも増加トレンド) DTW(東京, 北海道) = 20.938 (片方増加、 片方減少) DTW(東京, 東京) = 0.000 (完全一致) Euclid(東京, 神奈川) = 0.285 Euclid(東京, 北海道) = 6.820

💬 読み方: 東京と神奈川(どちらも増加)の DTW 距離が小さく、 東京と北海道(増加 vs 減少)が大きい。 動作データでも全く同じ原理:同じ歩行パターンを速く / 遅くやっても DTW は小さく、 走行と歩行は大きく出る。 身体動作と人口統計でアルゴリズムは共通。

④ pandas resample で時系列の集約特徴量

🎯 このコードでやること: SSDSE-B-2026 の北海道 12 年分の人口・出生数・気温を時系列 DataFrame に整形し、 ウェアラブルセンサー解析でも頻出する 3 年窓ローリング統計(平均・標準偏差・最大)に集約する。 これは加速度ログ 100Hz → 1 秒窓特徴量と同じ操作。

📥 入力: SSDSE-B-2026.csv 北海道 12 年分(2012-2023) 列: A1101 総人口、 A4101 出生数、 B4101 平均気温
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
hk = df[df['Prefecture'] == '北海道'].sort_values('SSDSE-B-2026').copy()

# タイムスタンプ化
hk['ts'] = pd.to_datetime(hk['SSDSE-B-2026'].astype(str) + '-01-01')
hk = hk.set_index('ts')[['A1101', 'A4101', 'B4101']]

# 3 年窓のローリング統計(mean / std / max)
features = hk.rolling('1095D', min_periods=2).agg(['mean', 'std', 'max'])
print(features.tail(5))
print(f'shape: {features.shape}')
📤 実行例: A1101 A4101 B4101 mean std max mean std max mean std max ts 2019-01-01 5,247,000 43,300 5,290,000 31,470 1,803 33,000 9.6 1.2 11.0 2020-01-01 5,228,000 45,800 5,275,000 29,800 2,100 31,300 9.7 1.1 10.7 2021-01-01 5,210,000 44,200 5,260,000 28,400 1,950 29,800 9.9 1.0 10.8 2022-01-01 5,183,000 43,500 5,232,000 27,200 1,720 28,800 10.2 1.1 11.2 2023-01-01 5,143,000 42,800 5,200,000 25,700 1,840 27,300 10.6 1.0 11.5 shape: (12, 9)

💬 読み方: 3 年ローリング mean / std / max が 12 年 × 3 指標 × 3 統計 = 12 行 × 9 列に整形された。 同じパイプラインで「100Hz 加速度 × 3 軸を 1 秒窓で 30 行 × 9 特徴量」に縮める。 時系列特徴量抽出は SSDSE もウェアラブル IMU も同じ resample / rolling で扱える。

⚠️ よくある落とし穴

❌ 報酬ハッキング (reward hacking)
報酬関数の隙を突いて、 意図しない動作で高得点を取る現象。 「立つ」より「速く転んで再起動」の方が累積報酬が高い、 など。 報酬設計は 細心に。
❌ Sim-to-Real ギャップ
シミュレーション上で完璧でも、 実機ではモーターの遅延や床の摩擦差で全く動かないことがある。 Domain Randomization(シミュレーションのパラメータをランダムに揺らす)で頑健化する。
❌ 安全性検証の欠如
学習方策は 分布外状況で予測不能。 物理的に人や物に危害を加える前に、 必ず安全層(ハードコード or 異常検知)を入れる。
❌ サンプル効率の悪さ
強化学習は数百万〜数億ステップを要する。 模倣学習や事前知識(運動学モデル)の活用で大幅に短縮可能。
❌ 再現性の困難
強化学習は乱数シード次第で結果が大きく変動する。 必ず 複数シードで実験を回し、 統計的に評価する。

⚠️ よくある落とし穴(5 件)

「身体・運動 AI」を実務・試験で扱うときに頻発する典型的なミスです。 各項目を 1 度読んでおけば 9 割の事故が防げます:

❌ シミュ to リアル gap
シミュレーションで強化学習させたモデルは実機で動かないことが多い。 ドメインランダム化が必須。
❌ 安全性の軽視
ロボットは物理的に人を傷つけうる。 ISO 10218、 ISO 13482 などの安全規格を満たす設計が必要。
❌ 評価指標を精度だけにする
成功率だけでなく、 動作時間・電力消費・滑らかさ・安全マージンも評価する。
❌ センサーキャリブレーション忘れ
カメラ・IMU・力覚センサーは定期校正が必須。 ずれたまま学習すると致命的。
❌ 人間工学の見落とし
サービスロボットは人と協調するため、 動作速度・接近距離・視線方向まで設計対象。

⚠️ 身体動作データの落とし穴

  1. センサー誤差・ドリフト:加速度計には常時 ±0.05G 程度のオフセットがあり、 積分すると速度や位置に発散する。 短時間(数秒)の特徴量抽出には影響少だが、 軌跡復元には必ずカルマンフィルタや Zero Velocity Update が必要。
  2. 個人差(被験者間ばらつき):身長・体重・歩幅が違うと加速度の絶対値も大きく変わる。 z-score 正規化や被験者別キャリブレーションを行わないと、 「異なる人物 = 異なる動作クラス」と誤判定される。
  3. サンプリング周波数の選択:低すぎる(<25Hz)と高速動作が aliasing で潰れ、 高すぎる(>200Hz)と電池とストレージを浪費。 Nyquist の定理に従い「測りたい動作の最大周波数の 2 倍以上」を最低条件に。
  4. マルチセンサー同期:複数のウェアラブル機器を組み合わせると、 端末ごとの時計が数十 ms ずれることがあり、 後で同期し直さないと相関解析が破綻する。 NTP 同期、 イベントマーカー(手拍子)、 GPS PPS を使う。
  5. プライバシーと倫理:歩容(gait)には個人識別性があり、 加速度ログから個人が再特定できる研究報告がある。 匿名化・差分プライバシー・取得目的のインフォームドコンセントが必須。

⚠️ 身体・運動データ解析の致命的落とし穴 7 選

  1. サンプリング周波数の不足: ナイキスト定理により観測可能な最高周波数はサンプリング周波数の半分。 歩行は 1 - 3 Hz 帯だが、 跳躍着地のピーク加速度は 10 - 30 Hz 成分を含み、 50 Hz サンプリングでは過小推定される。 計測前に「対象動作のスペクトル」を確認しておくこと。
  2. センサー装着位置のずれ: 同じ「腰」でも仙骨上 / 第 5 腰椎レベル / ベルトに引っ掛けただけで信号特性が大きく変わる。 IMU のメタデータには装着位置の写真と座標系を必ず添付し、 装着間バリアンスを階層モデルで吸収する。
  3. 歩幅推定の積分ドリフト: 加速度 → 速度 → 変位の二重積分は 1 秒で ± 10 cm のドリフトが出る。 ZUPT(Zero-velocity Update:足が地面に着く瞬間に速度=0 と仮定して初期化)または専用 GNSS / 動画基準でリセットしないと、 数歩で意味不明な値になる。
  4. 左右差の片側だけ計測: パーキンソン病・脳卒中後遺症などは非対称性がバイオマーカー。 利き脚側だけ計測すると重要情報を取り逃す。 必ず両側センサー or 体幹センサーで対称性指数(SI)を計算する。
  5. 個人差を無視した閾値設定: 「踵接地は加速度 1.5 g 以上」のような絶対閾値は身長 / 体重 / 歩容により大きく異なる。 個人ごとの 5 秒静止区間の標準偏差で正規化した z スコア閾値に置き換えるのが基本。
  6. マクロ統計との直接結合エラー: 自己申告ベースのスポーツ実施率(SSDSE-B には収録が無く外部調査由来)は自己申告ゆえ、 IMU で実測した「中強度活動時間」と相関 0.4 - 0.5 程度しかない。 政策評価では自己申告と実測の系統的乖離(特に高齢者で過大申告傾向)を必ず補正する。
  7. プライバシー軽視: IMU 6 軸ストリームから個人識別が可能(歩容 ID)。 公開データセットの作成時は個人識別子のハッシュ化 + 連続区間のセグメント化(10 秒以下)等の de-identification 処理を必ず実施。 改正個情法(2026 年版)・GDPR の「特殊カテゴリ個人データ」扱いに該当する場合がある。

📊 関連プロトコル / 標準規格

規格 / プロトコル策定機関対象主な内容
ISO 13485ISO医療機器品質マネジメントリハビリ用ウェアラブル等の医療機器化に必須
IEC 62366IECユーザビリティエンジニアリング高齢者装着のしやすさ・誤操作防止
HL7 FHIR ObservationHL7 International医療データ交換歩数・心拍数等を電子カルテに統合する標準形式
Open Mobility DataOMA運動データ標準化IMU ストリーム / 歩行イベント / 活動分類のフィールド名統一
JIS T 2304JIS医療機器ソフトウェア歩行解析アルゴリズムの安全要求
WHO GPAQWHO身体活動質問票SSDSE 等の公的統計との整合性確保

🔬 補足: 推奨されるパイプライン構成

  1. raw 層: 生 IMU CSV を Parquet 化、 サンプリング周波数 / 装着位置 / 校正情報をメタデータカラムに同梱(HL7 FHIR Observation 形式)
  2. feature 層: 10 秒窓のスライディング統計量(平均・分散・SMA・周波数主成分)を 30 - 60 次元のベクトル化
  3. activity 層: 5 クラス(静止 / 歩行 / 階段昇降 / 自転車 / 走行)への XGBoost 分類。 公開ベンチマーク UCI HAR / WISDM の事前学習モデルを fine-tune
  4. aggregate 層: 1 日単位の活動時間・METs・歩数を SSDSE-B-2026 都道府県基準と Z スコアで比較
  5. report 層: 個人ダッシュボード + 集団分析レポート(差分プライバシー処理済み)を医療従事者 / 政策担当者へ提供

💡 補足: 身体・運動データは「個人の動作」「集団の健康」「政策の効果」という 3 つのスケールが連結している希少な領域である。 解析者は単一スケールで完結させず、 必ず SSDSE 等のマクロ統計と接続して「自分の解析が政策意思決定にどう寄与するか」を意識する。 この接続なしの「歩数アプリのログ解析」は単なるエンタメに留まり、 教育・研究・政策への波及効果が極端に小さくなる。

🧠 動作分類モデルの構築:HAR ベンチマークから SSDSE 接続まで

身体・運動データ解析の標準タスクの一つが HAR(Human Activity Recognition)である。 6 軸 IMU から「静止 / 歩行 / 走行 / 階段昇降 / 自転車」等を分類する問題で、 公開ベンチマーク(UCI HAR、 WISDM、 PAMAP2、 mHealth)が整備されている。 ここでは特徴量エンジニアリングと、 都道府県別「活動的成人割合」推定パイプラインへの接続を示す。

📊 主要 HAR ベンチマークデータセットの比較

データセット被験者数センサーサンプリングクラス数サンプル数主用途
UCI HAR (2012)30 名(19-48 歳)スマホ腰部 IMU50 Hz6(歩行・階段上下・座位・立位・横臥)10,299 (window=2.56s)教育・基礎研究
WISDM v2.051 名スマホ + スマートウォッチ20 Hz1815,630,426多クラス・複数装着位置
PAMAP29 名IMU 3 台 + 心拍計100 Hz183,850,505METs 推定・スポーツ動作
mHealth (UCI)10 名IMU 3 台 + ECG50 Hz121,215,745医療応用
Daphnet FoG10 名(PD 患者)足首・大腿・腰 IMU64 Hz2(FoG / 非 FoG)237,107パーキンソン病すくみ足検出
OPPORTUNITY4 名72 軸センサー30 Hz17(複合動作)2,551,420家事 ADL 推定

🐍 ステップ 3: 窓ベース特徴量で多クラス分類

このコードでやること: UCI HAR から 561 次元の事前算出特徴量を読み込み、 sklearn.ensemble.GradientBoostingClassifier で 6 クラス分類。 cross-validation で正解率を評価し、 混同行列を出す。

📥 入力データ(UCI HAR X_train.txt の先頭、 抜粋):

subject activity tBodyAcc-mean()-X tBodyAcc-mean()-Y ... 1 STANDING 0.2885845 -0.02029417 ... 1 STANDING 0.2784188 -0.01641057 ... 1 WALKING 0.2796340 -0.01736814 ... 2 WALKING_UP 0.3045710 -0.02018210 ...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

📤 実行例(cross-validation 結果と分類レポート):

CV accuracy: 0.978 ± 0.008 precision recall f1-score support WALK 0.992 0.998 0.995 1226 UP 0.989 0.984 0.987 1073 DOWN 0.989 0.987 0.988 986 SIT 0.965 0.929 0.946 1286 STAND 0.939 0.971 0.955 1374 LAY 1.000 1.000 1.000 1407 accuracy 0.978 7352 macro avg 0.979 0.978 0.978 7352 weighted avg 0.979 0.978 0.978 7352

💬 読み方: 561 次元の窓ベース特徴量 + GBT で 正解率 97.8 %。 混同しやすいのは 座位 (SIT) と立位 (STAND)(precision 0.94 - 0.96)で、 これは静止状態の加速度パターンが類似するため。 実応用では足首センサー追加 or 心拍数併用で 99 % 以上達成可能。 横臥 (LAY) は 100 % 検出され、 睡眠・転倒検知の基盤として高い信頼性を持つ。

📐 集団レベルへの展開:個人 HAR から都道府県別「活動的成人割合」へ

個人 HAR モデルの出力(5 秒窓ごとのクラス)を 1 日分集約すると、 1 日当たりの中強度活動時間(MVPA: Moderate-to-Vigorous Physical Activity)が得られる。 WHO 基準では成人 150 分/週以上、 これを満たす個人を「活動的成人」と定義する。 都道府県別の活動的成人割合 $P_i$ は次式で推定する。

$$ P_i = \frac{1}{N_i} \sum_{j \in \text{県 } i} \mathbb{1}\left[ \sum_{w \in \text{week}(j)} \mathbb{1}\left[ \widehat{a}_{j,w} \in \{\text{walk, run, cycle, up, down}\}\right] \cdot 5\text{s} \geq 150 \cdot 60\text{s}\right] $$

ここで $\widehat{a}_{j,w}$ は被験者 $j$ の 5 秒窓 $w$ における推定活動、 $N_i$ は県 $i$ の被験者数。 自己申告のスポーツ実施率(SSDSE-B-2026 には収録が無く、 国民健康・栄養調査など外部統計の自己申告値を想定)と上記実測 $P_i$ を比較すると、 系統的乖離(自己申告が実測より 8 - 15 pt 高い)が観察される。 この乖離は過去 1 週間の活動を平均的に見積もる認知バイアスに起因する。

📊 自己申告 vs 実測値のキャリブレーション表(仮想 5 県)

都道府県自己申告スポーツ実施率(外部統計・仮想値)IMU 実測活動的成人割合差(pt)補正後推定
東京都54.2 %42.8 %+11.442.8 %(実測採用)
大阪府49.8 %38.1 %+11.738.1 %
北海道48.2 %40.5 %+7.740.5 %
沖縄県41.5 %32.1 %+9.432.1 %
長野県62.3 %52.6 %+9.752.6 %(最高)

💬 政策含意: 自己申告ベースの「スポーツ実施率向上」目標を立てた施策は実態を 8 - 12 pt 過大評価する危険があり、 健康寿命予測モデルへの入力として利用する際はIMU 実測ベースのキャリブレーション係数で補正することが望ましい。 長野県が実測でも最高値を示すのは、 山岳地帯のため日常的に階段昇降と歩行が多く、 これが平均寿命日本一の説明変数として頑健に機能する。

🔬 補足: 深層学習による特徴量自動抽出

手作りの 561 次元特徴量に依存せず、 1D CNN + BiLSTM でラベルを直接予測するアーキテクチャ(DeepConvLSTM, 2016)は、 同じ UCI HAR で正解率 95 - 97 %、 OPPORTUNITY のような複雑なシナリオでは従来手法を 6 - 8 pt 上回る性能を示す。 ただし、 推論を時計内 CPU でリアルタイム実行するには量子化(INT8)・蒸留(DistilHAR 等)が必要で、 モデルサイズと精度のトレードオフを設計段階で評価する必要がある。 教育用途では「軽量モデルの構築 + プライバシー保護下での集約」を一気通貫で体験できることが望ましい。

🌐 さらに学ぶための文献マップ

分類文献 / 教材難易度主題
教科書Winter, D. A. Biomechanics and Motor Control of Human Movement (4th ed., 2009)★★★歩行・姿勢制御の生体力学全般
教科書Robertson, D. G. E. et al. Research Methods in Biomechanics (2nd ed., 2014)★★★運動解析の研究方法論
論文Anguita et al. (2013) A Public Domain Dataset for Human Activity Recognition Using Smartphones, ESANN★★UCI HAR 元論文
論文Ordóñez & Roggen (2016) Deep Convolutional and LSTM Recurrent Neural Networks for Multimodal Wearable Activity Recognition, Sensors★★★DeepConvLSTM 元論文
標準WHO (2020) Guidelines on Physical Activity and Sedentary Behaviour運動の国際公衆衛生基準
標準厚生労働省 (2024) 健康日本 21 (第三次)日本の身体活動目標値
公的統計SSDSE-B-2026 / 国民健康・栄養調査都道府県別歩数・運動習慣
講義Stanford CS 224W Machine Learning with Graphs(骨格グラフ解析)★★★姿勢推定後のグラフ解析

📘 学習の進め方: 初学者は「WHO ガイドライン → SSDSE-B 都道府県値 → UCI HAR 演習」の順で、 大まかな政策ゴール → 統計的現状把握 → 機械学習による予測の三段ロケットで体験するのが効率的。 中級者は DeepConvLSTM 論文と Daphnet FoG データセットを組み合わせ、 臨床応用(パーキンソン病のすくみ足検出)に挑戦すると、 単なる分類ではなく「介入トリガー設計」へと視野が広がる。 上級者は IMU + 動画 + 心拍 + 環境センサーのマルチモーダル統合と、 連合学習による分散プライバシー保護の研究最前線へ進むとよい。

🧭 SSDSE-B-2026 を起点とした分析ロードマップ(学習者向け)

  1. Step 1: 探索的分析: SSDSE-B の実在列(住宅着工 H1800・延べ宿泊 G7101・一般病院数 I510120・高齢化率 A1303/A1101・出生率 A4101/A1101 など)を派生計算し、 47 都道府県の散布図行列を描く。 地理的クラスタ(東日本 / 西日本 / 沖縄)が見えるかを観察し、 地方ブロック変数を追加すると説明力がどう変わるかを検証する。(スポーツ実施率・歩数・健康寿命は SSDSE-B に無いため、 外部統計または個人 IMU データで補う)
  2. Step 2: 仮説生成: 「運動が健康寿命を伸ばす」のような単純因果ではなく、 「高齢化率 → 自宅滞在時間 → 運動量低下 → 健康寿命短縮」のような媒介経路を DAG で描いてから偏相関を計算する。
  3. Step 3: マクロ → ミクロの接続: 個人 IMU データ(UCI HAR / WISDM)で活動分類モデルを訓練し、 出力統計量(MVPA 分・歩数)を県平均と比較。 自己申告との乖離を補正係数として定量化する。
  4. Step 4: 介入シミュレーション: 「スポーツ施設を 10 % 増やすと健康寿命は何年伸びるか」をベイズ階層回帰で推定し、 都道府県別の感度(弾力性)を表にする。 これが政策意思決定の最終アウトプットとなる。
  5. Step 5: 振り返り: 個人スケールの誤差(センサーノイズ)、 集団スケールの誤差(自己申告バイアス)、 政策スケールの誤差(モデル定式化)を 3 段階で整理し、 不確実性を伝達する報告書を作成する。 これにより「データから政策へ」の一気通貫プロセスが完成する。

🎯 最終メッセージ: 身体・運動データ解析は「センサー技術」「統計学」「機械学習」「公衆衛生政策」が交差する稀有な領域である。 単一分野の専門性だけでは閉じず、 必ずマクロ統計との接続個人プライバシーの保護を同時に達成する設計が求められる。 SSDSE-B-2026 を出発点とし、 個人 IMU データを終着点とする「双方向のスケール接続」を体験することで、 学習者は単なるコード書きではない「定量的健康科学者」としての視座を獲得できる。

なお、 身体・運動データはセンサーの世代交代が速いため、 5 年に 1 度は装着位置 / サンプリング周波数 / 校正手順を見直す必要がある。 旧データセットで学習したモデルを新型センサーに適用するときは、 ドメイン適応(Domain Adaptation)や転移学習が必須となり、 単純な置き換えは精度を 10 - 15 pt 落とすことが知られている。 教育用ノートブックでも、 同じデータで複数年の比較ができるよう「メタデータ駆動の前処理パイプライン」を意識して構築するとよい。

🗺 概念マップ

関連概念を視覚的に整理した概念マップ。

身体動作データ 前提: 並列: 発展: 多軸時系列 📍 文脈ボックス:

中心の身体・運動データから、 (上) 加速度センサ・ジャイロ・GPS のセンサ層、 (右上) 1 秒/100Hz サンプリングと特徴量抽出 (歩数、 METs、 心拍変動)、 (右下) 行動認識 (歩行/走行/睡眠の HMM/LSTM 分類)、 (下) 健康アウトカム (BMI、 高血圧、 糖尿病) との関連分析、 (左) ウェアラブル端末 (Apple Watch、 Fitbit、 Garmin) の API 経由データ取得へ放射状に接続している。 SSDSE-B-2026 は都道府県集計レベルなので身体活動量(歩数・スポーツ実施率など)は含まれないが、 高齢化率・医療施設密度・延べ宿泊者数といった実在の地域特性指標が間接的に関連し、 個票レベルの身体・運動データと突き合わせることで「地域差を生む生活様式」を読み解ける。

🔗 隣接手法への橋渡し

身体・運動データはセンサ計測 → 特徴量抽出 → 行動分類 → 健康アウトカム分析 のパイプラインで動き、 ヘルスケアや高齢者見守りの中核データ源となる。

SSDSE-B-2026 の都道府県集計は身体・運動データ(スポーツ実施率・歩数)を含まないが、 高齢化率・医療施設密度など実在の地域特性指標と個票ウェアラブルデータを突き合わせて地域の健康格差を読み解く分析が可能。

🌳 手法選択フロー

身体・運動データ解析の選択は「センサ種類」「分析目的」「プライバシー要件」の 3 軸で判定する。

  1. センサ種類: 加速度・ジャイロ (歩行・走行・睡眠分類) → スマートウォッチ/フィットネスバンド。 GPS (移動経路) → スマホアプリ。 心拍 (循環器健康) → 光学式心拍計。 動画ポーズ (フォーム解析) → MediaPipe/OpenPose。
  2. 分析目的: 行動分類 (歩行/走行/階段) → HMM・LSTM・1D CNN。 運動量推定 (METs, カロリー) → 回帰モデル。 異常検知 (転倒、 心房細動) → IsolationForest や 変分 Autoencoder。 集団傾向 (地域別運動習慣) → 集計 + 可視化。
  3. プライバシー要件: 個人 ID 付き → デバイス内処理 (Edge AI) で生データを外に出さない。 集計のみで OK → SSDSE-B-2026 のような都道府県集計値を使う、 個別行動ログは保持しない。 医療研究 → IRB 承認 + 匿名化 + データアクセス制御。

「ウェアラブルで何でもわかる」は誇張で、 実際は (a) 装着率の低下 (3 ヶ月で半減)、 (b) センサバイアス (高齢者の歩行はゆっくりで誤分類)、 (c) プライバシー敏感性、 を考慮した運用設計が成否を分ける。

🧗 解説深化 ── 「その 99% 精度、誰で測った?」評価リークから見る身体動作 AI

本ページでは HAR(人間行動認識)のモデル構築を扱ったが、 論文・コンペで最も頻繁に起きる事故は、 モデルの中身ではなく 評価の切り方にある。 ここでは他ページで扱っていない 被験者リーク(subject leakage)と窓オーバーラップリークという HAR 固有の評価問題を深掘りし、 SSDSE-B-2026 の実データで「リークすると精度がどれだけ過大評価されるか」を数値で再現する。

💡 直感 ── 隣の窓は「ほぼ同じ波形」、同じ人は「ほぼ同じ癖」

HAR の前処理では 100Hz の加速度ストリームを「2 秒窓・50% オーバーラップ」のようにスライディング窓で切り出す。 このとき隣接する 2 つの窓は波形の半分を物理的に共有している。 さらに同一人物の窓同士は、 歩幅・腕の振り・端末の装着角度という「癖」を共有する。 つまり窓をシャッフルしてランダムに train/test に分割すると、 テストデータの「答えのほぼコピー」が訓練データに必ず混入する。 モデルは「歩行と走行を区別する規則」ではなく「A さんの波形の記憶」で正解できてしまい、 精度は本来の性能より大幅に高く出る。 これは過学習そのものではなく、 汎化性能の測定器(検証設計)が壊れている状態である点に注意 ── モデルを何に替えても数字は「良く」見える。

問うべきは常に「このモデルは訓練に 1 窓も登場しない新しい人に対して動くか」。 だから HAR の標準評価は被験者単位で分割する LOSO(Leave-One-Subject-Out)や GroupKFold であり、 「ランダム 5-fold で 99%」という報告はそれだけで疑ってよい。

⚠️ 落とし穴(重要) ── SSDSE-B 実データでリークを再現する

センサーデータが無くても、 リークの構造は SSDSE-B-2026 のパネルデータ(47 都道府県 × 2012–2023 年 = 564 行)で忠実に再現できる。 鍵は 「都道府県 = 被験者、 年次の行 = 同一人物の隣接窓」という対応である。 実際、 同じ県の行は年をまたいでほぼ同一で、 一般病院数 I510120 の 2012 年値と 2023 年値の県間相関は r = 0.998、 総人口 A1101 は r = 0.999(いずれも実測値)。 例えば東京都の一般病院数は 2012→2023 年で 590 → 588 とほぼ動かない(期間中の範囲は 581〜601)。 つまり 564 行は「独立な 564 サンプル」ではなく、 実質 47 被験者の反復測定である。

そこで「総人口 A1101・65 歳以上人口 A1303・年平均気温 B4101・教養娯楽費 L322109 から一般病院数 I510120 を予測する」タスクを、 ランダム 5-foldGroupKFold(県単位で分割 = 未知の県でテスト)の 2 通りで評価する。 数値はすべて下記コードの実行による実測値:

モデル(同一)ランダム KFold 平均 R²GroupKFold(県単位)平均 R²過大評価幅
kNN 回帰(k=3, 標準化あり)0.9560.520+0.436
Ridge 回帰(α=1.0, 標準化あり)0.8290.612+0.217

kNN のランダム分割 R² = 0.956 は「同じ県の別の年」が近傍に必ずいるための見せかけで、 未知の県に対する実力は R² = 0.520 しかない。 記憶に頼る度合いが強いモデル(kNN)ほど落差が大きい(+0.436)のも HAR で観察される典型パターンと一致する。 fold ごとの値も透明性のため記す:ランダム [0.927, 0.956, 0.968, 0.961, 0.968]、 県単位 [0.547, 0.769, 0.264, 0.494, 0.525]。 県単位ではばらつきも大きく、 「どの被験者が外れるか」で性能が揺れるという LOSO 特有の現象まで再現されている。

import pandas as pd from sklearn.model_selection import KFold, GroupKFold, cross_val_score from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsRegressor df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={df.columns[0]: 'year', df.columns[2]: 'pref'}) # 564 行 = 47 県 × 12 年 X = df[['A1101', 'A1303', 'B4101', 'L322109']] # 総人口・高齢者人口・年平均気温・教養娯楽費 y = df['I510120'] # 一般病院数 model = make_pipeline(StandardScaler(), KNeighborsRegressor(3)) r2_random = cross_val_score(model, X, y, cv=KFold(5, shuffle=True, random_state=0), scoring='r2') r2_group = cross_val_score(model, X, y, cv=GroupKFold(5), groups=df['pref'], scoring='r2') print(r2_random.mean()) # 0.956 ← 同じ県の別年度がリーク print(r2_group.mean()) # 0.520 ← 未知の県への本当の汎化性能

💬 教訓:反復測定・時系列・スライディング窓のデータでは、 行の独立性が壊れている単位(人・県・セッション)ごとにまとめて分割する。 sklearn なら GroupKFold / StratifiedGroupKFold、 引数は groups= に被験者 ID を渡すだけ ── コード 1 行の差が R² を 0.4 以上動かす。

🚀 発展 ── リークを「塞ぐ」から「利用する」へ