🎓

コンペで学んだことまとめ

2023/04/30に公開

はじめに

今回の記事はコンペで学んだことのまとめです.今回のコンペでは主に綺麗なコードの書き方やシェルスクリプトの活用方法などについて学んだため,そのことについてまとめていきます.

コンペについて

研究室のB3の新人研修でモデルの性能を競うコンペを計3回行いました.今回は第3回のコンペについて記事を書きます.

タスク

タスクは,日本語のTwitterテキストについての感情極性分類で,書き手の感情極性を -2, -1, 0, 1, 2 で5クラス分類を行います.数字が大きくなるほどポジティブ,小さくなるほどネガティブとなります.また,評価指標はQuadratic Weighted Kappaです.

データセット

学習用:3万件,検証用:1,250件,評価用:1,250件,提出用:2,500件で,それぞれのデータの分割を変更せずに使用します.

制約

コンペごとに制約が課せられ,

  • 第1回:ニューラルネットワークを使用しない
  • 第2回:外部データを使用しない
  • 第3回:制約なし

となります.第3回の今回はなんの制約もないので,BERTやLUKEなどの大規模言語モデルを使用することができます.

trainファイルの作成

まず,trainファイルを作成します.こちらのサイトを参考に,学習用のPythonファイルを作成しました.
https://qiita.com/m__k/items/2c4e476d7ac81a3a44af
主な変更点としては,

  • データの読み込み部分を今回使用するデータのパスに書き換えたこと
  • 評価指標をQWKに変更したこと
  • パラメータ部分をargparseを用いてシェルスクリプトから変更できるようにしたこと

です.

評価指標をQWKに変更

以下のように記述することで評価指標をQWKに変更することができます.

weight_kappa = cohen_kappa_score(target, pred, weights='quadratic')

学んだことその1:argparseの使い方

argparseについて

argparseとはコマンドライン引数を簡単に扱うことができるようになるモジュールで,sys.argv[0]みたいな感じで受け取るのではなく,args.batch_sizeのような感じで名前がついてなんの引数を扱っているのかわかりやすくなります.また,デフォルトの値を指定しておいたり,その引数が必須かどうかを指定したりなど,いろいろ便利なことができます.詳しくは以下のサイトをご覧ください.
https://docs.python.org/ja/3/library/argparse.html

argparseの使い方

使い方としては,argparseのインポートを行います.

import argparse

次に以下のように記述することで引数を受け取る設定をします.

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    # train parameter
    parser.add_argument("--model", required=True)
    parser.add_argument("--seed", type=int, default=42)
    parser.add_argument("--learning_rate", type=float, default=5e-5)
    parser.add_argument("--lr_scheduler_type", type=str, default="linear")
    parser.add_argument("--batch_size", default=16, type=int)
    parser.add_argument("--gradient_accumulation_steps", default=1, type=int) # ミニバッチの実行を累積する回数
    parser.add_argument("--epochs", type=int, default=100)
    parser.add_argument("--early_stopping_patience", type=int, default=10)
    # PATH
    parser.add_argument("--save_model_dir", default="[モデルのパス]")
    parser.add_argument("--sub_file", default="[提出用ファイルのパス]")
    args = parser.parse_args()
    main(args)

最後にmain関数内で引数を受け取れるように,例えば以下のように記述します.

def main(args): 
    seed_everything(args.seed)
    training_args = TrainingArguments(
        output_dir=args.save_model_dir,
        per_device_train_batch_size=args.batch_size,
        per_device_eval_batch_size=args.batch_size,
        lr_scheduler_type=args.lr_scheduler_type,
        gradient_accumulation_steps=args.gradient_accumulation_steps,
        num_train_epochs=args.epochs
    )

これでargparseを用いてPythonファイル側で引数を受け取る準備ができました.続いて,引数を渡す方のシェルスクリプト側の記述方法について説明します.

Pythonファイルに引数を渡す方法は以下の通りです.横棒2本の後に,先ほどPythonファイル側で指定した変数名を記述し,その後に渡したい引数を書きます.改行するときはバックスラッシュを挟むのを忘れないようにしてください.

python ../source/train.py \
    --model "studio-ousia/luke-japanese-large-lite" \
    --learning_rate 3e-5 \
    --batch_size 16 \
    --gradient_accumulation_steps 4 \
    --epochs 100 \
    --early_stopping_patience 3 \
    --save_model_dir [保存先ディレクトリのパス] \
    --sub_file ../submission/LUKE_large_lite.csv

学んだことその2:シェルの引数入力

シェルスクリプトを実行する際に特定のオプションが入力された場合にこの処理を実行してくださいと設定することができます.

例えば以下のように記述することで,オプションgが指定された場合は変数GPU_IDを入力された引数の値に変更し,オプションhが指定された場合はこのシェルスクリプトの使い方が表示されるようになっています.オプションを指定した際に引数を受け取りたい場合は,getopts g:というように,オプションの後にコロンをつけます.

while getopts g: OPT
do
    case $OPT in
    g ) GPU_ID=$OPTARG
        ;;
    h ) echo "Usage: $0 [-g GPU_ID]" 1>&2
        exit 1
        ;;
    esac
done

例えばGPUの指定をするコマンドを記述することで,シェルスクリプトを実行する際に使いたいGPUの番号を指定できるようになります.簡単にGPUの変更ができ,とても便利で大変重宝しています.

export CUDA_VISIBLE_DEVICES=${GPU_ID}

学んだことその3:importの順番を整える

isortというPythonライブラリを使用することでimportの順番を綺麗に整列してくれます.isortの使い方は以下の通りです.

まず,以下のコマンドでインストールします.

pip install isort

そして,以下のコマンドでを使用することができます.

isort [ファイル名]

学んだことその4:ディレクトリ構造

このコンペをする前はディレクトリ構造のこととか全くよくわからなくてそこらへんのディレクトリにほいほいファイルを作成したせいで授業の実習ではディレクトリ構造がとんでもないことになっていたのですが,このコンペを通して綺麗なディレクトリ構造の作り方や維持の仕方を知ることができました.

作成するディレクトリは以下の通りです.

  • data:コンペで使用する学習・検証・評価データを入れる
  • model:学習したモデルが入る
  • script:Pythonファイルを実行するためのシェルスクリプトを入れる
  • source:学習やアンサンブルを行うPythonファイルを入れる
  • submission:コンペの提出用のファイルが入る

コンペで使用したディレクトリの構造は以下のようになっています.長すぎるので一部のファイルは省略しましたが,イメージとしてはこんな感じです.

.
├── data
│   ├── dev.txt
│   ├── test.txt
│   ├── train.txt
├── model
├── script
│   ├── LUKE_large.sh
│   ├── LUKE_large_lite.sh
│   ├── tohoku_BERT_weight_kappa.sh
├── source
│   ├── ensamble.py
│   └── train.py
├── submission
│   ├── LUKE_large.csv
│   ├── LUKE_large_lite.csv
│   └── tohoku_BERT_weight_kappa.csv
└── theme_compe3_py3.9.yml

学んだことその5:コンペのコツ

最後に,コンペを行う上でのコツについても学んだのでそれぞれ説明していきます.

コツ1:Pythonファイルは増やさずシェルスクリプトを増やす

むやみにPythonファイルの中身を変更して増やしていくのではなく,Pythonファイルは固定で,引数を受け取ってパラメータを変更できるようにします.

パラメータの指定はシェルスクリプトで行うことで楽にパラメータを変更することができるようになるとともに,後々どんなパラメータを使用したかもパッと見やすくなります.

コツ2:devのスコアをシェルスクリプトにメモしておく

このパラメータでこのdevのスコアが出たというのをシェルスクリプトにメモしておくと,次にパラメータを変更する際の材料になります.

スペシャルサンクス

研究室のM1の先輩に隅から隅まで教えてもらいました.色々学べてとてもよかったです.ありがとうございました.

おわりに

今回のコンペを通して,コンペだけでなく普段の研究でもとても役立つ知識を学ぶことができました.あと,他の人に教えてもらうことで知りたかったことと,それ以外の役立つ知識も一緒に教えてもらうこともできたため,自分で調べるだけではなく他人を頼ることも重要なんだなと感じました.

また,迷惑かなと思って聞きたいことがあってもなかなか聞けないことが多かったですが,今回もそうですが聞いてみれば大体みんな快く教えてくれるので,今度からわからないことがあった時は周りの人にもっとすぐ頼っていこうと思います.そして,私も人に頼られた時はめちゃめちゃ困り事を解決してあげられるかっこいい人になりたいと思います.

2023/04/30

Discussion