目次
こんにちは.高山です.
先日の記事で告知しました手話入門記事の第十五回になります.
今回は手話動画から抽出した追跡点系列に対して,データ拡張を施すことで認識性能を改善する手法を紹介します.
ただし,今回紹介する手法はこれまで紹介してきたデータ拡張と異なり,劣化させたデータを学習することで認識モデルの頑健性を向上させるアプローチになります.
具体的には,手話中の追跡点系列の座標値に対してノイズを加えることで,特徴量の分散を増やします.
図1にノイズ付加の適用例を示します.
骨格追跡は,画像の解像度や明暗に影響を受けやすく,座標値が安定しない場合があります.
また,MediaPipeの全身追跡機能には複雑さの異なる3種類のモデルがあり,軽量モデルは他のモデルに比べて追跡点が細かく振動する現象が発生します.
このような現象はジッタ (Jitter) と呼ばれます.
図1(b) に示すように追跡点系列にノイズを加えることで,ジッタが発生している状況をシミュレーションすることが可能です.
(顔はかなり崩れてしまっていますが)
今回解説するスクリプトはGitHub上に公開しています.
複数の実験を行っている都合で,CPUで動かした場合は結構時間がかるのでご注意ください.
更新履歴 (大きな変更のみ記載しています)
- 2024/09/18: カテゴリを変更しました
- 2024/09/17: タグを更新しました
- 2024/07/29: Gitスクリプトのダウンロード元を
master
からv0.1
タグに変更 - 2024/07/23: 第1節の構成を見直し
- 2024/07/20
- 実験結果を更新し,第1.3項とコード解説 (第4節) を更新
- 記事最終部の実験結果を削除して第1.3節に統合
1. ノイズ付加の処理工程
図2に,追跡点系列へのノイズ付加処理工程を示します.
処理は非常にシンプルで,ノイズスケールの算出とノイズ付加操作,およびマスキングから構成されます.
ノイズは一様分布やガウス分布など用いて,ランダムに値を生成することで作ることができます.
ノイズスケールは分布の幅を決めるパラメータ (例えば,ガウス分布の場合は標準偏差です) で,スケールをサンプル毎にランダムに決定することで様々な量のノイズを付加することができます.
マスキングは,追跡に失敗している点に加わったノイズを除去する操作です.
図2右側は,ノイズ付加前後の画像フレームと特徴マップを示しています.
ノイズの効果が分かりやすいように,ここでは大きめのノイズスケールを与えています.
画像フレームを見ると,ノイズ付加後は最早どのような手話をしているか判別できないほどに崩れています.
一方,特徴マップ上では大まかな特徴を保てていることが分かります.
認識モデルが実際に見ているのは特徴マップなので,このようなデータを入力しても上手く学習してくれます.
逆に言うと,画像フレームに示しているような情報を内部で生成して処理するわけではないので注意してください (そのような認識アプローチも実はあります [Duan'22]).
- [Duan'22] H. Duan., et al. "Revisiting Skeleton-based Action Recognition," CVPR 2022.
2. 実験結果
次節以降では,いつも通り実装の紹介をしながら実験結果をお見せします.
コード紹介記事の方針として記事単体で全処理が分かるように書いており,少し長いので結果を先にお見せしたいと思います.
2.1 頻度の多い10単語を学習させた結果
ノイズ付加処理の実装ではアフィン変換 の時と同様に,下記のようなオプションが考えられます.
- ノイズ付加のタイミング: 正規化前,正規化後
- ノイズの与え方: 全身に同じパラメータ,部位毎に異なるパラメータ
- ノイズスケールの算出方法: 与えた値をそのまま用いる,追跡点を囲む矩形に対する相対値
- ノイズを生成する分布: 一様分布,ガウス分布
図3から図5で,ノイズ付加方法に応じたValidation Lossと認識率の推移を示します.
正規化前に全身に一括でノイズ付与
まず図3ではノイズを正規化前に全身に一括で与えた場合の,Validation Lossと認識率の推移を示しています.
横軸は学習・評価ループの繰り返し数 (Epoch) を示します.
縦軸はそれぞれの評価指標を示します.
各線の色と実験条件の関係は次のとおりです.
- 青線 (Default): Pre-LN構成のTransformer (ノイズ無し)
- 橙線 (+ PreW-A-U): 正規化前に全身に一括で適用.ノイズスケールは直接指定.ノイズ分布は一様分布.
- 緑線 (+ PreW-A-G): 正規化前に全身に一括で適用.ノイズスケールは直接指定.ノイズ分布はガウス分布.
- 赤線 (+ PreW-B-U): 正規化前に全身に一括で適用.ノイズスケールは矩形幅の相対値.ノイズ分布は一様分布.
- 青線 (+ PreW-B-G): 正規化前に全身に一括で適用.ノイズスケールは矩形幅の相対値.ノイズ分布はガウス分布.
デフォルトのモデルには,第九回の記事で紹介した,Pre-LN構成のTransformerモデルを用います.
全体的にロスの挙動が不安定で,認識性能もあまり大きな違いは有りませんでした.
正規化後に全身に一括でノイズ付与
続いて図4ではノイズを正規化後に全身に一括で与えた場合の,Validation Lossと認識率の推移を示しています.
各線の色と実験条件の関係は次のとおりです.
- 青線 (Default): Pre-LN構成のTransformer (ノイズ無し)
- 橙線 (+ PostW-A-U): 正規化後に全身に一括で適用.ノイズスケールは直接指定.ノイズ分布は一様分布.
- 緑線 (+ PostW-A-G): 正規化後に全身に一括で適用.ノイズスケールは直接指定.ノイズ分布はガウス分布.
- 赤線 (+ PostW-B-U): 正規化後に全身に一括で適用.ノイズスケールは矩形幅の相対値.ノイズ分布は一様分布.
- 青線 (+ PostW-B-G): 正規化後に全身に一括で適用.ノイズスケールは矩形幅の相対値.ノイズ分布はガウス分布.
ノイズスケールを直接指定しているケース (PostW-A-U と PostW-A-G) が微妙に良くなっている...かな...? という感じはありますが,全体的な傾向は図4と同じようです.
部位毎にノイズ付与
最後に図5でノイズを部位毎に与えた場合の,Validation Lossと認識率の推移を示しています.
各線の色と実験条件の関係は次のとおりです.
- 青線 (Default): Pre-LN構成のTransformer (ノイズ無し)
- 橙線 (+ PreP-A-U): 正規化前に部位毎に適用.ノイズスケールは直接指定.ノイズ分布は一様分布.
- 緑線 (+ PreP-A-G): 正規化前に部位毎に適用.ノイズスケールは直接指定.ノイズ分布はガウス分布.
- 赤線 (+ PostP-B-U): 正規化後に部位毎に適用.ノイズスケールは矩形幅の相対値.ノイズ分布は一様分布.
- 青線 (+ PostP-B-G): 正規化後に部位毎に適用.ノイズスケールは矩形幅の相対値.ノイズ分布はガウス分布.
なお,ノイズスケールを矩形幅の相対値にして部位毎に適用する場合は,正規化でノイズもスケールされます.
設定の違いがノイズ量に影響を与えなかったので PreP-B-* (正規化前に部位毎に適用.ノイズスケールは矩形幅の相対値.) などは省略しています.
PreP-A-U がやや良い結果に見えますね.
ここまでの結果をまとめると,ノイズスケールは直接指定をした方が良い結果が出る傾向があるように見えます.
全体的に不安定なため,正規化のタイミングや分布の選択は明確な差が有るようには見えませんでした.
2.2 250単語を学習させた結果
データが少なくて学習が不安定になっている可能性がありますので,全データ (250単語) を学習させた場合の挙動を図6から図8に示します.
なお,こちらの実験はメモリや処理時間の都合でColab上では実行が難しいので,ローカル環境で行いました.
データの分割方法やパラメータは10単語のときと同じです.
ただし,学習時間を短縮するためにバッチ数は256に設定しています.
(本来はバッチ数を変えた場合は学習率も調整した方が良いのですが,今回はママで実験を行っています)
正規化前に全身に一括でノイズ付与
まず図6ではノイズを正規化前に全身に一括で与えた場合の,Validation Lossと認識率の推移を示しています.
ロスの挙動は安定していますね.
一方,認識性能は Default と同等か少し悪くなっているように見えます.
正規化後に全身に一括でノイズ付与
続いて図7ではノイズを正規化後に全身に一括で与えた場合の,Validation Lossと認識率の推移を示しています.
こちらも図6と同様に,認識性能は Default と同等か少し悪くなっているようです.
部位毎にノイズ付与
最後に図8でノイズを部位毎に与えた場合の,Validation Lossと認識率の推移を示しています.
ノイズを正規化前に与えるケース (PreP-A-U と PreP-A-G) が少し良い結果になっているようですね.
ノイズを部位毎に異なるパラメータで与える処理は汎化性能を向上する効果がありそうです.
一方,ノイズ分布の違いや,部位サイズに応じてノイズ量を調整する処理はそこまで効果が見られませんでした.
ここまでの実験結果から分かるとおり,ロス,認識性能共に大きな違いは出ませんでしたね...(^^;).
(図2の特徴マップで示された結果からある程度予想できてましたが...)
ただし今回は劣化したデータを学習させていることを考えると,認識性能を保てているという解釈もできるかもしれません.
なお,今回の実験では話を簡単にするために,実験条件以外のパラメータは固定にし,乱数の制御もしていません.
必ずしも同様の結果になるわけではないので,ご了承ください.
3. 前準備
3.1 データセットのダウンロード
ここからは実装方法の説明をしていきます.
まずは,前準備としてGoogle Colabにデータセットをアップロードします.
ここの工程はこれまでの記事と同じですので,既に行ったことのある方は第3.3項まで飛ばしていただいて構いません.
まず最初に,データセットの格納先からデータをダウンロードし,ご自分のGoogle driveへアップロードしてください.
次のコードでGoogle driveをColabへマウントします.
Google Driveのマウント方法については,補足記事にも記載してあります.
1 2 3 |
|
ドライブ内のファイルをColabへコピーします.
パスはアップロード先を設定する必要があります.
# Copy to local.
!cp [path_to_dataset]/gislr_dataset_top10.zip gislr_top10.zip
データセットはZIP形式になっているので unzip
コマンドで解凍します.
!unzip gislr_top10.zip
Archive: gislr_top10.zip
creating: dataset_top10/
inflating: dataset_top10/16069.hdf5
...
inflating: dataset_top10/sign_to_prediction_index_map.json
成功すると dataset_top10
以下にデータが解凍されます.
HDF5ファイルはデータ本体で,手話者毎にファイルが別れています.
JSONファイルは辞書ファイルで,TXTファイルは本データセットのライセンスです.
!ls dataset_top10
16069.hdf5 25571.hdf5 29302.hdf5 36257.hdf5 49445.hdf5 62590.hdf5
18796.hdf5 26734.hdf5 30680.hdf5 37055.hdf5 53618.hdf5 LICENSE.txt
2044.hdf5 27610.hdf5 32319.hdf5 37779.hdf5 55372.hdf5 sign_to_prediction_index_map.json
22343.hdf5 28656.hdf5 34503.hdf5 4718.hdf5 61333.hdf5
単語辞書には単語名と数値の関係が10単語分定義されています.
!cat dataset_top10/sign_to_prediction_index_map.json
{
"listen": 0,
"look": 1,
"shhh": 2,
"donkey": 3,
"mouse": 4,
"duck": 5,
"uncle": 6,
"hear": 7,
"pretend": 8,
"cow": 9
}
ライセンスはオリジナルと同様に,CC-BY 4.0 としています.
!cat dataset_top10/LICENSE.txt
The dataset provided by Natsuki Takayama (Takayama Research and Development Office) is licensed under CC-BY 4.0.
Author: Copyright 2024 Natsuki Takayama
Title: GISLR Top 10 dataset
Original licenser: Deaf Professional Arts Network and the Georgia Institute of Technology
Modification
- Extract 10 most frequent words.
- Packaged into HDF5 format.
次のコードでサンプルを確認します.
サンプルは辞書型のようにキーバリュー形式で保存されており,下記のように階層化されています.
- サンプルID (トップ階層のKey)
|- feature: 入力特徴量で `[C(=3), T, J(=543)]` 形状.C,T,Jは,それぞれ特徴次元,フレーム数,追跡点数です.
|- token: 単語ラベル値で `[1]` 形状.0から9の数値です.
1 2 3 4 5 6 7 8 9 |
|
['1109479272', '11121526', ..., '976754415']
<KeysViewHDF5 ['feature', 'token']>
(3, 23, 543)
[1]
3.2 モジュールのダウンロード
次に,過去の記事で実装したコードをダウンロードします.
本項は前回までに紹介した内容と同じですので,飛ばしていただいても構いません.
コードはGithubのsrc/modules_gislr
にアップしてあります (今後の記事で使用するコードも含まれています).
まず,下記のコマンドでレポジトリをダウンロードします.
(目的のディレクトリだけダウンロードする方法はまだ調査中です(^^;))
!wget https://github.com/takayama-rado/trado_samples/archive/refs/tags/v0.1.zip -O master.zip
--2024-01-21 11:01:47-- https://github.com/takayama-rado/trado_samples/archive/master.zip
Resolving github.com (github.com)... 140.82.112.3
...
2024-01-21 11:01:51 (19.4 MB/s) - ‘master.zip’ saved [75710869]
ダウンロードしたリポジトリを解凍します.
!unzip -o master.zip -d master
Archive: master.zip
641b06a0ca7f5430a945a53b4825e22b5f3b8eb6
creating: master/trado_samples-main/
inflating: master/trado_samples-main/.gitignore
...
モジュールのディレクトリをカレントディレクトリに移動します.
!mv master/trado_samples-main/src/modules_gislr .
他のファイルは不要なので削除します.
!rm -rf master master.zip gislr_top10.zip
!ls
dataset_top10 drive modules_gislr sample_data
3.3 モジュールのロード
主要な処理の実装に先立って,下記のコードでモジュールをロードします.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
【コード解説】
- 標準モジュール
- copy: データコピーライブラリ.Transformerブロック内でEncoder層をコピーするために使用します.
- json: JSONファイル制御ライブラリ.辞書ファイルのロードに使用します.
- math: 数学計算処理ライブラリ
- os: システム処理ライブラリ
- random: ランダム値生成ライブラリ
- sys: Pythonインタプリタの制御ライブラリ.
今回はローカルモジュールに対してパスを通すために使用します.
- functools: 関数オブジェクトを操作するためのライブラリ.
今回はDataLoaderクラスに渡すパディング関数に対して設定値をセットするために使用します.
- inspect.signature: オブジェクトの情報取得ライブラリ.
- pathlib.Path: オブジェクト指向のファイルシステム機能.
主にファイルアクセスに使います.osモジュールを使っても同様の処理は可能です.
高山の好みでこちらのモジュールを使っています(^^;).
- typing: 関数などに型アノテーションを行う機能.
ここでは型を忘れやすい関数に付けていますが,本来は全てアノテーションをした方が良いでしょう(^^;).
- 3rdパーティモジュール
- numpy: 行列演算ライブラリ
- torch: ニューラルネットワークライブラリ
- torchvision: PyTorchと親和性が高い画像処理ライブラリ.
今回はDatasetクラスに与える前処理をパッケージするために用います.
- ローカルモジュール: sys.pathにパスを追加することでロード可能
- dataset: データセット操作用モジュール
- defines: 各部位の追跡点,追跡点間の接続関係,およびそれらへのアクセス処理を
定義したモジュール
- layers: ニューラルネットワークのモデルやレイヤモジュール
- transforms: 入出力変換処理モジュール
- train_functions: 学習・評価処理モジュール
4. ノイズ付加処理の実装
ノイズ付加処理の実装は下記のようになります.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
|
【コード解説】
- 引数
- apply_ratio: データ拡張の適用確率.
- scale_range: ノイズスケールの範囲.
2要素の配列で `(min, max)` のように指定します.
`scale_range` の範囲でランダムにノイズスケール値を算出します.
ただし,`scale_unit="box_ratio"` の場合は,`target_joints`を囲む矩形の
対角線長を算出し,その長さに対する相対値に変換します.
- scale_unit: ノイズスケールの算出方法を指定.
- asis: `scale_range` の範囲でランダムに値を取り出し,そのまま使用します.
- box_ratio: `target_joints`を囲む矩形の対角線長を算出し,`scale_range` を
対角線長に対する相対値として見なして変換します.
- noise_type: ノイズを生成する分布を指定.
- uniform: 一様分布を用います.値は `(-scale, scale)` の範囲で取り出します.
- gauss: ガウス分布を用います.値は平均0, 標準偏差 `scale` の分布から
取り出します.
- target_joints: 変換を適用する追跡点インデクス.
部位毎に適用する場合は,ここの設定を変えたインスタンスを部位毎に生成する
必要があります.
- feature_dim: 特徴量の次元数
- include_conf: Trueの場合,特徴量の末尾は信頼度の次元であるとみなし,
処理を行います.
- 10-17行目: 初期化処理
- 19-69行目: ノイズ付加処理
- 22-23行目: 乱数を生成し,`apply_ratio` 以上だった場合は何もせずに値を返す
- 27-31行目: 信頼度にノイズが入らないように,配列を分離
- 34行目: `target_joints` で指定した追跡点を抽出
- 36-43行目: 追跡失敗フレームにノイズが入らないように,マスクを生成
- 46-52行目: `scale_unit == "box_ratio"` の場合は,`scale_range` を変換
- 55行目: ノイズスケールを算出
- 56-59行目: 指定した分布でノイズを付加
- 62行目: マスクの適用
- 63-69行目: 特徴量を整形し返す
- 71-72行目: print()に対して,クラス名と設定値を返す
5. 認識モデルの動作確認
今回は,第九回の記事で紹介した,Pre-LN構成のTransformerモデルをそのまま用いて実験を行います.
ここではモデルの推論動作が正常に動くかだけ確かめます.
次のコードでデータセットからHDF5ファイルとJSONファイルのパスを読み込みます.
1 2 3 4 5 6 7 8 |
|
dataset_top10/sign_to_prediction_index_map.json
[PosixPath('dataset_top10/2044.hdf5'), PosixPath('dataset_top10/32319.hdf5'), PosixPath('dataset_top10/18796.hdf5'), PosixPath('dataset_top10/36257.hdf5'), PosixPath('dataset_top10/62590.hdf5'), PosixPath('dataset_top10/16069.hdf5'), PosixPath('dataset_top10/29302.hdf5'), PosixPath('dataset_top10/34503.hdf5'), PosixPath('dataset_top10/37055.hdf5'), PosixPath('dataset_top10/37779.hdf5'), PosixPath('dataset_top10/27610.hdf5'), PosixPath('dataset_top10/53618.hdf5'), PosixPath('dataset_top10/49445.hdf5'), PosixPath('dataset_top10/30680.hdf5'), PosixPath('dataset_top10/22343.hdf5'), PosixPath('dataset_top10/55372.hdf5'), PosixPath('dataset_top10/26734.hdf5'), PosixPath('dataset_top10/28656.hdf5'), PosixPath('dataset_top10/61333.hdf5'), PosixPath('dataset_top10/4718.hdf5'), PosixPath('dataset_top10/25571.hdf5')]
次のコードで辞書ファイルをロードして,認識対象の単語数を格納します.
1 2 3 4 5 |
|
次のコードで前処理を定義します.
固定の前処理には,以前に説明した追跡点の選定と,追跡点の正規化を適用して実験を行います.
第2節で述べたとおり,ノイズ付加処理の適用の仕方は様々な設定が考えられます. 今回は下記のコードに示すように,変数毎に異なる設定を定義しました (多数あるので,個々の解説は割愛します).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
|
次のコードで,前処理を適用したHDF5DatasetとDataLoaderをインスタンス化し,データを取り出します.
HDF5Dataset
をインスタンス化する際に,pre_transforms
と transforms
引数に値を渡してデータ拡張を有効にしています (14行目).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
torch.Size([2, 2, 232, 130])
次のコードでモデルをインスタンス化して,動作チェックをします.
追跡点抽出の結果,入力追跡点数は130で,各追跡点はXY座標値を持っていますので,入力次元数は260になります.
出力次元数は単語数なので10になります.
また,Transformer層の入力次元数は64に設定し,PFFN内部の拡張次元数は256に設定しています.
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 32 33 34 35 36 37 38 39 40 |
|
TransformerEnISLR(
(linear): Linear(in_features=260, out_features=64, bias=True)
(activation): ReLU()
(tr_encoder): TransformerEncoder(
(pos_encoder): PositionalEncoding(
(dropout): Dropout(p=0.1, inplace=False)
)
(layers): ModuleList(
(0-1): 2 x TransformerEncoderLayer(
(self_attn): MultiheadAttention(
(w_key): Linear(in_features=64, out_features=64, bias=True)
(w_value): Linear(in_features=64, out_features=64, bias=True)
(w_query): Linear(in_features=64, out_features=64, bias=True)
(w_out): Linear(in_features=64, out_features=64, bias=True)
(dropout_attn): Dropout(p=0.1, inplace=False)
)
(ffw): PositionwiseFeedForward(
(w_1): Linear(in_features=64, out_features=256, bias=True)
(w_2): Linear(in_features=256, out_features=64, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
(activation): ReLU()
)
(dropout): Dropout(p=0.1, inplace=False)
(norm1): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
(norm2): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
)
)
(norm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
)
(head): GPoolRecognitionHead(
(head): Linear(in_features=64, out_features=10, bias=True)
)
)
torch.Size([2, 10])
(2, 2, 75, 75) (2, 2, 75, 75)
6. 学習と評価
6.1 共通設定
では,実際に学習・評価を行います.
まずは,実験全体で共通して用いる設定値を次のコードで実装します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Using 2 cores for data loading.
Using cuda for computation.
6.2 学習・評価の実行
次のコードで学習・バリデーション・評価処理それぞれのためのDataLoaderクラスを作成します.
今回は,ノイズ付加処理の有無に寄る認識性能の違いを見たいので,実験毎にデータセットクラスをインスタンス化します.
1 2 3 4 5 6 7 8 9 10 11 |
|
次のコードでモデルをインスタンス化します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
TransformerEnISLR(
(linear): Linear(in_features=260, out_features=64, bias=True)
(activation): ReLU()
(tr_encoder): TransformerEncoder(
(pos_encoder): PositionalEncoding(
(dropout): Dropout(p=0.1, inplace=False)
)
(layers): ModuleList(
(0-1): 2 x TransformerEncoderLayer(
(self_attn): MultiheadAttention(
(w_key): Linear(in_features=64, out_features=64, bias=True)
(w_value): Linear(in_features=64, out_features=64, bias=True)
(w_query): Linear(in_features=64, out_features=64, bias=True)
(w_out): Linear(in_features=64, out_features=64, bias=True)
(dropout_attn): Dropout(p=0.1, inplace=False)
)
(ffw): PositionwiseFeedForward(
(w_1): Linear(in_features=64, out_features=256, bias=True)
(w_2): Linear(in_features=256, out_features=64, bias=True)
(dropout): Dropout(p=0.1, inplace=False)
(activation): ReLU()
)
(dropout): Dropout(p=0.1, inplace=False)
(norm1): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
(norm2): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
)
)
(norm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
)
(head): GPoolRecognitionHead(
(head): Linear(in_features=64, out_features=10, bias=True)
)
)
次のコードで学習・評価処理を行います.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Start training.
--------------------------------------------------------------------------------
Epoch 1
Start training.
loss:3.729503 [ 0/ 3881]
loss:1.974881 [ 3200/ 3881]
Done. Time:8.072904322999989
Training performance:
Avg loss:2.244985
Start validation.
Done. Time:0.33059089099998573
Validation performance:
Avg loss:2.115503
Start evaluation.
Done. Time:1.345861483999954
Test performance:
Accuracy:18.5%
--------------------------------------------------------------------------------
...
--------------------------------------------------------------------------------
Epoch 50
Start training.
loss:0.218751 [ 0/ 3881]
loss:0.133565 [ 3200/ 3881]
Done. Time:4.735246651000011
Training performance:
Avg loss:0.198414
Start validation.
Done. Time:0.33649530800005323
Validation performance:
Avg loss:0.945739
Start evaluation.
Done. Time:1.2618015409999543
Test performance:
Accuracy:76.0%
Minimum validation loss:0.7086029521056584 at 25 epoch.
Maximum accuracy:79.0 at 43 epoch.
以後,同様の処理を設定毎に繰り返します.
コード構成は同じですので,ここでは説明を割愛させていただきます.
また,この後グラフ等の描画も行っておりますが,本記事の主要点ではないため説明を割愛させていただきます.
今回は追跡点系列に対してノイズを加えて,データ拡張を行う手法を紹介しましたが,如何でしたでしょうか?
ノイズを加えた場合に特性があまり変わらなかったので,解釈に時間がかかってしまいました(^^;)
今回使用したデータセットでは試せませんでしたが,オリジナルの動画がある場合は,ノイズが起きやすい環境や追跡モデルを使用したデータをテストデータとすることで,ノイズに対する頑健性を検証することができます.
他にも,テストデータにノイズを加えて検証をしてみるのも面白いかも知れませんね.
機会があれば試してみたいと思います.
今回紹介した話が,これから手話認識を勉強してみようとお考えの方に何か参考になれば幸いです.