{"notes":[{"id":"action-recognition/st-gcn","category":"action-recognition","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"ST-GCN","subtitle":"求心、遠心","author":"Z. Yuan","paperDate":"2018-01-23","publishedOn":"2023-12-11","world":"external/st-gcn","paperTitle":"Spatial Temporal Graph Convolutional Networks for Skeleton-Based Action Recognition","paperUrl":"https://arxiv.org/abs/1801.07455","bibliography":{"title":"Spatial Temporal Graph Convolutional Networks for Skeleton-Based Action Recognition","authors":["Sijie Yan","Yuanjun Xiong","Dahua Lin"],"url":"https://arxiv.org/abs/1801.07455","pdfUrl":"https://arxiv.org/pdf/1801.07455v2","codeUrl":"https://github.com/yysijie/st-gcn","preprint":{"id":"1801.07455","submittedOn":"2018-01-23","url":"https://arxiv.org/abs/1801.07455"},"publication":{"venue":"AAAI","year":2018,"kind":"conference","url":"https://ojs.aaai.org/index.php/AAAI/article/view/12328"},"citation":"@misc{actionrecognitionstgcn2018,\n  title = {{Spatial Temporal Graph Convolutional Networks for Skeleton-Based Action Recognition}},\n  author = {Sijie Yan and Yuanjun Xiong and Dahua Lin},\n  year = {2018},\n  eprint = {1801.07455},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1801.07455}\n}\n","citationPath":"/content/papers/citations/action-recognition/st-gcn.d4a6b81c8471.bib"},"thumbnail":{"src":"/content/papers/assets/75fab9132b72202980c799f04d5fa51cf9749352d5b47ce0d19b571aa8a39acc.png","width":868,"height":292},"locale":"ja","path":"/content/papers/ja/action-recognition/st-gcn.c57e1b9e71e4.md","headings":[{"id":"問題設定","text":"問題設定","depth":2},{"id":"解決方法","text":"解決方法","depth":2},{"id":"時空間グラフ","text":"時空間グラフ","depth":3},{"id":"誰にどの重みを与えるか","text":"誰にどの重みを与えるか","depth":3},{"id":"三つの分割","text":"三つの分割","depth":3},{"id":"行列積にする","text":"行列積にする","depth":3},{"id":"エッジの重要度","text":"エッジの重要度","depth":3},{"id":"ネットワーク全体","text":"ネットワーク全体","depth":3},{"id":"議論","text":"議論","depth":2},{"id":"データセット","text":"データセット","depth":3},{"id":"分割のアブレーション","text":"分割のアブレーション","depth":3},{"id":"他手法との比較","text":"他手法との比較","depth":3},{"id":"結論","text":"結論","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"graph-learning/gcn","relation":"referenced","topic":""}]},{"id":"graph-learning/gcn","category":"graph-learning","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"GCN","subtitle":"一つのパラメーターで足りる？","author":"Z. Yuan","paperDate":"2016-09-09","publishedOn":"2023-10-01","world":"external/gcn","paperTitle":"Semi-Supervised Classification with Graph Convolutional Networks","paperUrl":"https://arxiv.org/abs/1609.02907","bibliography":{"title":"Semi-Supervised Classification with Graph Convolutional Networks","authors":["Thomas N. Kipf","Max Welling"],"url":"https://arxiv.org/abs/1609.02907","pdfUrl":"https://arxiv.org/pdf/1609.02907v4","codeUrl":"https://github.com/tkipf/gcn","preprint":{"id":"1609.02907","submittedOn":"2016-09-09","url":"https://arxiv.org/abs/1609.02907"},"publication":{"venue":"ICLR","year":2017,"kind":"conference","url":"https://arxiv.org/abs/1609.02907"},"citation":"@misc{graphlearninggcn2016,\n  title = {{Semi-Supervised Classification with Graph Convolutional Networks}},\n  author = {Thomas N. Kipf and Max Welling},\n  year = {2016},\n  eprint = {1609.02907},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/1609.02907}\n}\n","citationPath":"/content/papers/citations/graph-learning/gcn.43058ec3e77e.bib"},"thumbnail":{"src":"/content/papers/assets/ded600c9ca8f4eda09d39ffb16b3c9b0ec730780063e55948b29924d00f14794.png","width":1122,"height":400},"locale":"ja","path":"/content/papers/ja/graph-learning/gcn.f7c10b90f2d7.md","headings":[{"id":"問題設定","text":"問題設定","depth":2},{"id":"グラフを正則化項にする","text":"グラフを正則化項にする","depth":3},{"id":"解決方法","text":"解決方法","depth":2},{"id":"スペクトル領域から削っていく","text":"スペクトル領域から削っていく","depth":3},{"id":"gcn-の一層","text":"GCN の一層","depth":3},{"id":"二層で十分","text":"二層で十分","depth":3},{"id":"議論","text":"議論","depth":2},{"id":"実験設定","text":"実験設定","depth":3},{"id":"主な結果","text":"主な結果","depth":3},{"id":"どの伝播がよいか","text":"どの伝播がよいか","depth":3},{"id":"もっと深くすると","text":"もっと深くすると","depth":3},{"id":"wl-アルゴリズムとの関係","text":"WL アルゴリズムとの関係","depth":3},{"id":"四日後の評論","text":"四日後の評論","depth":3},{"id":"論文が挙げる制約","text":"論文が挙げる制約","depth":3},{"id":"結論","text":"結論","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[{"id":"graph-learning/spectral-networks","relation":"referenced","topic":""},{"id":"graph-learning/chebnet","relation":"referenced","topic":""}]}],"text":"動画の人が何をしているかは、映像を見ても、骨格だけを見ても分かります。\n\n骨格は関節座標の列です。姿勢推定で各フレームの頭、肩、肘、手首、股関節、膝、足首を出すと、十秒の動画が数百フレーム、各十数点の系列になります。データは小さく、背景や光に左右されず、踊る、手を振る、転ぶといった身体動作には多くの手がかりがあります。\n\n骨格自体がグラフです。関節がノード、骨がエッジです。香港中文大学の Sijie Yan、Yuanjun Xiong、林達華は、空間と時間を扱う GCN を載せ、ST-GCN と名付けました。\n\n## 問題設定\n\n従来の骨格動作認識には、おおむね二種類ありました。\n\n一つは手設計です。身体を部位に分けて関係を定義したり、関節軌跡の特徴量を計算したりします。データが変われば規則を書き直します。\n\nもう一つは RNN や時間畳み込みで、各フレームの全関節座標を長いベクトルにして時間方向に扱います。骨格の接続は捨てられ、右肘は右手首につながるが左膝にはつながらない、といった関係をモデルがデータから推測します。\n\n求めるのは、手作業で部位を定義せず、接続を直接使って時空間パターンを学ぶモデルです。GCN はグラフ畳み込みの枠組みを与えますが、引用ネットワーク向けなので、骨格には先に解く問題があります。\n\n## 解決方法\n\n### 時空間グラフ\n\n$T$ フレーム、各 $N$ 関節の系列から、無向の時空間グラフを作ります。ノードは各時刻の各関節、計 $T\\times N$ 個で、特徴量は座標と姿勢推定の信頼度です。\n\nエッジは二種類。同じフレームの自然な骨格接続と、隣接フレームの同一関節を結びます。Kinetics は OpenPose の 18 個の 2D 関節、NTU-RGB+D は Kinect の 25 個の 3D 関節です。接続はデータで決まり、手作業で部位を分ける必要はありません。\n\n[![原論文 Figure 1：骨格系列の時空間グラフ。青点は関節で、同一フレームでは骨で接続する。緑のエッジが隣接フレームの同一関節をつなぎ、右下の矢印が時間を示す。](/content/papers/assets/9e7851cb79bcf44e244d3b165ae301cb07adf86ffa6961d683bb9b743c4d2275.png)](/content/papers/assets/9e7851cb79bcf44e244d3b165ae301cb07adf86ffa6961d683bb9b743c4d2275.png)\n\n*原論文 Figure 1（Yan ら）、[**PDF 1 ページ**](https://arxiv.org/pdf/1801.07455v2#page=1)より。原キャプションは省略。*\n\nKinetics の一動画の入力は $(3, T, 18, 2)$。3 は $(x, y, \\text{信頼度})$、18 は関節数、2 は信頼度上位の最大二人です。動画を繰り返して $T=300$ フレームにします。\n\n### 誰にどの重みを与えるか\n\n普通の 2D 畳み込みに戻ります。3×3 なら出力位置 $x$ の値は次の形です。\n\n$$\nf_{out}(x)=\\sum_{h=1}^{3}\\sum_{w=1}^{3}f_{in}\\left(p(x,h,w)\\right)\\cdot w(h,w)\n$$\n\n二関数に分けます。**サンプリング関数** $p$ は見る位置、画像なら周囲 9 画素を決めます。**重み関数** $w$ は各位置に掛ける重みを決めます。格子には左上、真上、右上……という固定位置があるので、9 組の重みを $(c, 3, 3)$ テンソルで参照できます。\n\nグラフのサンプリングは距離 1 以内にすれば簡単です。しかし「左上」「真上」の固定順がなく、重み関数が難しくなります。\n\nそこで隣を固定の $K$ 組に分け、各組にラベルを与え、同じ組で重みを共有します。\n\n$$\nf_{out}(v_{ti})=\\sum_{v_{tj}\\in B(v_{ti})}\\frac{1}{Z_{ti}(v_{tj})}\\,f_{in}(v_{tj})\\cdot w\\left(l_{ti}(v_{tj})\\right)\n$$\n\n$l_{ti}$ は隣を組に対応させ、$Z$ はその組のノード数で寄与を均衡させます。3×3 畳み込みなら、9 画素を一つずつ 9 組にすることになります。\n\n時間には順序があるので単純です。空間の組に「何フレーム前・後」も付ければ時空間近傍の畳み込みになります。範囲 $\\Gamma$ が時間カーネルサイズで、論文は 9 です。\n\n### 三つの分割\n\n規則によって識別できるものが変わります。三種類を試します。\n\n[![原論文 Figure 3：(a) 入力骨格で赤破線が D=1 の近傍を囲む。(b) 分割なしで全近傍が緑。(c) 距離による分割で自分が緑、隣が青。(d) 空間位置による分割で、黒い×が重心、自分が緑、より重心に近い隣が青、遠い隣が黄。](/content/papers/assets/75fab9132b72202980c799f04d5fa51cf9749352d5b47ce0d19b571aa8a39acc.png)](/content/papers/assets/75fab9132b72202980c799f04d5fa51cf9749352d5b47ce0d19b571aa8a39acc.png)\n\n*原論文 Figure 3（Yan ら）、[**PDF 5 ページ**](https://arxiv.org/pdf/1801.07455v2#page=5)より。原キャプションは省略。*\n\n- **分割なし**（uni-labeling、図 b）：全近傍で一組、$K=1$。Kipf と Welling の GCN 規則に等しいと著者は述べます。同じ重みを掛けるので、隣の特徴量を平均して内積を取る形となり、関節間の相対関係が見えません。\n- **距離で分割**（図 c）：自分と距離 1 の隣で $K=2$。別の重みなら「自分から隣を引く」局所差分、たとえば関節間の相対変位を表せます。\n- **空間位置で分割**（図 d）：$K=3$。自分、骨格重心に自分より近い隣の**求心**、遠い隣の**遠心**です。重心はそのフレームの全関節座標の平均、$r_i$ は訓練セットでの関節 $i$ の重心からの平均距離です。身体動作はおおむね内へ縮む、外へ伸ばすの二種類という動機です。\n\n六関節に簡略化した骨格を、空間位置で分けましょう。\n\n```python\nimport numpy as np\nnp.set_printoptions(precision=2, suppress=True)\n\n# 簡略骨格：0 首、1 右肩、2 右肘、3 左肩、4 左肘、5 骨盤\nnames = [\"首\", \"右肩\", \"右肘\", \"左肩\", \"左肘\", \"骨盤\"]\nxy = np.array([[0.0, 1.0], [-0.4, 0.9], [-0.6, 0.5], [0.4, 0.9], [0.6, 0.5], [0.0, 0.2]])\nbones = [(0, 1), (1, 2), (0, 3), (3, 4), (0, 5)]\nV = len(xy)\nA = np.zeros((V, V))\nfor i, j in bones:\n    A[i, j] = A[j, i] = 1\n\ncenter = xy.mean(0)                          # 骨格重心：全関節座標の平均\nr = np.linalg.norm(xy - center, axis=1)      # 各関節から重心への距離\n\nA_root, A_in, A_out = np.eye(V), np.zeros((V, V)), np.zeros((V, V))\nfor i in range(V):\n    for j in np.nonzero(A[i])[0]:\n        if r[j] < r[i]:\n            A_in[i, j] = 1                   # 求心：隣が自分より重心に近い\n        else:\n            A_out[i, j] = 1                  # 遠心：隣が自分より遠い\n\nprint(\"重心への距離:\", r)\nfor i in (0, 1, 2):\n    print(f\"{names[i]}：求心 {[names[j] for j in A_in[i].nonzero()[0]]}、\"\n          f\"遠心 {[names[j] for j in A_out[i].nonzero()[0]]}\")\n```\n\n```text\n重心への距離: [0.33 0.46 0.62 0.46 0.62 0.47]\n首：求心 []、遠心 ['右肩', '左肩', '骨盤']\n右肩：求心 ['首']、遠心 ['右肘']\n右肘：求心 ['右肩']、遠心 []\n```\n\n例は一フレームだけですが、論文は訓練全体の平均距離です。右肩の二つの隣は、首が求心、肘が遠心と分かれます。分割しないと両者を平均してしまいます。\n\n\n> [!TIP]\n> \n> 公式コードには論文と異なる点があります。`net/utils/graph.py` の `spatial` は、座標重心からの距離でなく、**指定した中心関節**までのグラフのホップ数を比べます。OpenPose の 18 点なら首、NTU なら脊椎です。同ホップ数の隣は自分と同じ組にします。\n\n\n### 行列積にする\n\n実装は GCN の形です。分割なしの一層は次のとおりです。\n\n$$\nf_{out}=\\Lambda^{-\\frac12}(A+I)\\Lambda^{-\\frac12}f_{in}W\n$$\n\n複数組では $A+I$ を $K$ 個の行列に分け、$A+I=\\sum_jA_j$ とします。組ごとに正規化し、別々の重みを掛けて足します。\n\n$$\nf_{out}=\\sum_j\\Lambda_j^{-\\frac12}A_j\\Lambda_j^{-\\frac12}f_{in}W_j\n$$\n\n距離分割なら $A_0=I$、$A_1=A$ です。上の首の求心近傍のように一行が全 0 の組もあるので、正規化では次数に $\\alpha=0.001$ を加えます。\n\n先の骨格を $(C, T, V)$ テンソルとして、一度空間畳み込みをします。\n\n```python\ndef normalize(Aj, alpha=0.001):\n    d = Aj.sum(1) + alpha                    # 論文は α を加え、全零行に対処\n    return Aj / np.sqrt(np.outer(d, d))\n\nC_in, C_out, T = 3, 8, 4\nx = np.random.default_rng(0).random((C_in, T, V))      # (チャネル、フレーム、関節)\nWs = [np.random.default_rng(k).normal(size=(C_out, C_in)) for k in range(3)]\n\nout = sum(np.einsum(\"oc,ctv,wv->otw\", Wk, x, normalize(Ak))   # 組ごとに正規化し、別の重みを使う\n          for Wk, Ak in zip(Ws, [A_root, A_in, A_out]))\nprint(\"入力:\", x.shape, \"→ 出力:\", out.shape)\n```\n\n```text\n入力: (3, 4, 6) → 出力: (8, 4, 6)\n```\n\n実際の ST-GCN 単位は、そのあと時間方向に $1\\times\\Gamma$ 畳み込みを行い、前後フレームの同一関節を結びます。\n\n### エッジの重要度\n\n関節は複数部位に属し、重要度も変わり得ます。各層の各隣接行列に、全 1 初期化の学習可能なマスク $M$ を要素積で掛けます。将来はデータ依存の注意にも替えられると述べています。\n\n### ネットワーク全体\n\nまず BatchNorm で座標スケールをそろえます。全ノードで重みを共有するため、スケール差が問題になるからです。ST-GCN を 9 単位、チャネルは 64、64、64、128、128、128、256、256、256、時間カーネル 9 とし、各単位に残差接続、そのあと dropout 0.5 を置きます。4、7 単位目の時間ストライドを 2 にしてプーリングし、最後に大域プーリングで 256 次元にして softmax 分類します。\n\nSGD の学習率は 0.01、10 epoch ごとに 0.1 倍です。Kinetics は dropout の代わりに、ランダムなアフィン変換をフレーム間補間してカメラ移動を模擬する拡張と、訓練では部分区間を抽出しテストでは全系列を使う拡張を行います。\n\n## 議論\n\n### データセット\n\n**Kinetics** は約 30 万の YouTube 動画、400 動作クラス、一件約 10 秒で、訓練 24 万、検証 2 万です。340×256、30 FPS にして OpenPose で骨格を推定し、骨格だけを入力します。\n\n**NTU-RGB+D** は実験室で 40 人を三カメラで同時撮影した、約 56,000 動画、60 クラスです。Kinect が一人 25 個の 3D 関節を追跡します。X-Sub は人、X-View はカメラ視点で訓練・テストを分けます。\n\n### 分割のアブレーション\n\nKinetics の top-1、top-5 精度を比べます。\n\n| 設定 | Top-1 | Top-5 |\n| --- | ---: | ---: |\n| 時間畳み込みのみ（TCN） | 20.3% | 40.0% |\n| 疎な骨格グラフ、重み共有なし | 22.0% | 43.2% |\n| 分割なし | 19.3% | 37.4% |\n| 距離分割、二組の重みは符号だけ反対 | 23.9% | 44.9% |\n| 距離分割 | 29.1% | 51.3% |\n| 空間位置で分割 | 29.9% | 52.2% |\n| 空間位置で分割 + エッジ重要度 | **30.7%** | **52.8%** |\n\n表からいくつか分かります。\n\nまず、分割なしの GCN 風伝播は、グラフなしの時間畳み込みをも下回ります。先の説明どおり、平均により局所差分を失うためです。\n\n次に、自分と隣を分けるだけで 19.3% から 29.1% に跳ねます。二組の重みを符号だけの違いに結んでも分割なしに勝ち、著者は分割自体の重要さを見ています。\n\n三つ目に、空間位置の三組は二組より少しよく、エッジ重要度でさらに約 1 ポイント加わります。\n\n時間畳み込みのベースラインは、全結合の関節グラフで重み共有なしの時空間グラフ畳み込みに相当します。ST-GCN の違いは、疎な自然接続と重み共有です。\n\n### 他手法との比較\n\n| 方法 | Kinetics Top-1 | NTU X-Sub | NTU X-View |\n| --- | ---: | ---: | ---: |\n| Deep LSTM | 16.4% | 60.7% | 67.3% |\n| Temporal ConvNet | 20.3% | 74.3% | 83.1% |\n| C-CNN + MTLN | — | 79.6% | 84.8% |\n| **ST-GCN** | **30.7%** | **81.5%** | **88.3%** |\n\nどちらも、当時の骨格手法で最良です。\n\nただし Kinetics では RGB 57.0%、光フロー 49.5% と、映像との差が大きくあります。手に持つ物や場面が必要な動作が多いためと説明します。身体に強く関係する 30 クラスに絞ると、平均クラス精度は ST-GCN 72.4%、RGB 70.4%、光フロー 72.8% と近づきます。\n\nRGB の二ストリームとの統合では、光フローより ST-GCN の追加が有効です。ImageNet 事前学習なしでは、RGB 70.3%、光フロー追加 71.1%、ST-GCN 追加 71.2%、全部で 71.7% です。\n\n## 結論\n\nST-GCN は骨格系列を時空間グラフにし、GCN の形で畳み込みます。鍵は隣の分割です。分割なしではグラフなしに勝てず、二組にするだけで約 10 ポイント増えます。\n\n画像格子の GCN が平均カーネルだけになる問題にも、実例で答えました。空間構造を持つグラフでは、隣の役割で重みを分けられるかが大きく響きます。\n\n\n> [!TIP]\n> \n> GCN の伝播規則と重み共有の理由は、こちらのノートをどうぞ。\n> \n> - **[[16.09] GCN：一つのパラメーターで足りる？](/blog/papers/graph-learning/gcn)**\n\n\nのちの多くの骨格モデルは ST-GCN を土台にします。[2s-AGCN](https://arxiv.org/abs/1805.07694) は隣接行列もデータから学びます。自分で使うなら、姿勢推定の関節数と中心関節を先に確かめてください。分割はそこに依存します。\n","imageSizes":{"/content/papers/assets/9e7851cb79bcf44e244d3b165ae301cb07adf86ffa6961d683bb9b743c4d2275.png":{"width":454,"height":364},"/content/papers/assets/75fab9132b72202980c799f04d5fa51cf9749352d5b47ce0d19b571aa8a39acc.png":{"width":868,"height":292}}}
