方言翻訳機制作大作戦 その1.99

# 書いてあること、書いてないこと
### 書いてあること
+ T5モデルの良いところ
+ [sonoisa/t5-japanese: 日本語T5モデル](https://github.com/sonoisa/t5-japanese)を2023年2月時点のGoogle colaboratoryで動作させる方法（そのままだとエラーが出ます）
### 書いてないこと
+ 方言翻訳機の作り方
+ 言語学的知見に基づく話
+ ~~作者の好きな方言について~~

# はじめに

[方言翻訳機制作大作戦 その1 - Qiita](https://qiita.com/Bandolu/items/f5957d8c9b33ab8b595f)
から2年あまりが経ちました。
東北方言に関する書籍を買いあさり軽く数万を飛ばしましたが一向に覚えられる気配がありません。
そんなことを考えていたらChatGPTが流行りだし、自分のNLP熱に再び火がつきました。
そこで少し調べたところHuggingFaceを使ったTransformersの実装が流行りらしく、~~機械と自分の脳味噌はどっちが優れているか確かめたくて~~自分も久々に手を付けようかと思い始めました。
そしてアホなりに色々と勉強をしてみた結果、[T5](https://huggingface.co/t5-base)が方言翻訳には良さそうなんじゃないかと思い至った次第である。

# なぜT5なのか

## 分類タスクに特化していない（sequence2sequenceに特化している）
T5はその名の通り（Text-To-Text Transfer Transformer）、テキストをテキストに変換するタスクに特化している。

かの有名な[cl-tohoku/bert-base-japanese-whole-word-masking · Hugging Face](https://huggingface.co/cl-tohoku/bert-base-japanese-whole-word-masking)を含むBERTはMASKを埋めるタスクやトークン分類タスクに特化している。（少なくともクラスが実装されていない時点で重視はされていない）
## sequence2sequenceに関係するデモ実装例が多い->応用しやすい！
自然言語処理のライブラリやモデルに付属している実装例を見ながら、自分の目的にあったソフトウェアを実装していく手法をとるエンジニアは決して少なくない。少なくとも自分はそうだ。
特に機械学習分野に於いてはそうなりがちなのだが、とにかくかゆいところに手が届かない実装例が多い。ラベル付け系のタスクの場合outputが数個の数値に収まってしまうので、ファインチューニングの勉強にこそなれど実際に使うことができない。
そういうときはせめて入出力だけでも同じ形式（型）になっているタスクに特化したモデルを使う方が良い（と思っている）。
~~方言翻訳なんてそもそも一般的な翻訳タスク特化モデルとは噛み合ってくれないのでどういうモデルを元にするかから考えないといけない~~

## 日本語に特化している
これ重要。mBARTは多言語に対応しているのですがモデルが大きすぎてバッチサイズを1にしても動かないという情報があり、これはColabで実装するモンじゃないなと思いました。
参考：[ファインチューニングでmBARTの日→英翻訳モデルを作成してhuggingfaceで公開してみた](https://zenn.dev/ken_11/articles/f4c117b55c82c5)

# 参考にする予定のリポジトリ
sonoisa様のリポジトリにあるニュース要約タスクを参考にする予定です。日本語2日本語であることには変わりないのでデータセットを同じようにすれば行けると信じている……！
+ [sonoisa/t5-japanese: 日本語T5モデル](https://github.com/sonoisa/t5-japanese)
+ [【日本語モデル付き】2021年に自然言語処理をする人にお勧めしたい事前学習済みモデル - Qiita](https://qiita.com/sonoisa/items/a9af64ff641f0bbfed44)

しかし[PyTorch Lightning](https://pytorch-lightning.readthedocs.io/en/latest/)バージョンの違いなのか、一部のクラスが上手く動作しない様子。無事に動いたら本記事を更新し、変更点を追記いたします。
（エポック数多すぎて検証を終えるまでに5時間くらいかかりそうだと悟った顔）

がんばります。

動きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315665/60a8297c-caaa-87eb-1d88-61be1a80dda9.png)


# t5-japaneseの変更点
これで colaboratory 上でも問題なく動くようになりましたので、こちらに共有いたします。
おそらくpytorch-lightningのバージョンが大きく変わったことで一部の文法に変化が生じたのだと思われます。
詳しくは後日。

### 参考にさせていただきました
+ [Trainer(amp_level='O2') · Discussion #10950 · Lightning-AI/lightning](https://github.com/Lightning-AI/lightning/discussions/10950)
+ [`hparams` raises `AttributeError: can't set attribute` · Discussion #7525 · Lightning-AI/lightning](https://github.com/Lightning-AI/lightning/discussions/7525?utm_source=pocket_saves)

## 環境等
### 前
```shell
!pip install  torch==1.7.1 torchtext==0.8.0 torchvision==0.8.2 torchaudio==0.7.2
!pip install  transformers==4.4.2 pytorch_lightning==1.2.1 sentencepiece
```

### 後
```shell
!pip install  torch torchtext torchvision torchaudio
!pip install  sentencepiece transformers 
!pip install pytorch-lightning==1.8.6
```

## 変更前
```python:t5_japanese_title_generation.ipynb 学習に必要なクラス等の定義
# GPU利用有無
USE_GPU = torch.cuda.is_available()

# 各種ハイパーパラメータ
args_dict = dict(
    data_dir="/content/data",  # データセットのディレクトリ
    model_name_or_path=PRETRAINED_MODEL_NAME,
    tokenizer_name_or_path=PRETRAINED_MODEL_NAME,

    learning_rate=3e-4,
    weight_decay=0.0,
    adam_epsilon=1e-8,
    warmup_steps=0,
    gradient_accumulation_steps=1,

    # max_input_length=512,
    # max_target_length=64,
    # train_batch_size=8,
    # eval_batch_size=8,
    # num_train_epochs=4,

    n_gpu=1 if USE_GPU else 0,
    early_stop_callback=False,
    fp_16=False,
    opt_level='O1',
    max_grad_norm=1.0,
    seed=42,
)
```

```python:t5_japanese_title_generation.ipynb 学習処理クラス

class T5FineTuner(pl.LightningModule):
    def __init__(self, hparams):
        super().__init__()
        self.hparams = hparams

        # 事前学習済みモデルの読み込み
        self.model = T5ForConditionalGeneration.from_pretrained(hparams.model_name_or_path)

        # トークナイザーの読み込み
        self.tokenizer = T5Tokenizer.from_pretrained(hparams.tokenizer_name_or_path, is_fast=True)

    def forward(self, input_ids, attention_mask=None, decoder_input_ids=None, 
                decoder_attention_mask=None, labels=None):
        """順伝搬"""
        return self.model(
            input_ids,
            attention_mask=attention_mask,
            decoder_input_ids=decoder_input_ids,
            decoder_attention_mask=decoder_attention_mask,
            labels=labels
        )

```

```python:t5_japanese_title_generation.ipynb 転移学習を実行
# 学習に用いるハイパーパラメータを設定する
args_dict.update({
    "max_input_length":  512,  # 入力文の最大トークン数
    "max_target_length": 64,  # 出力文の最大トークン数
    "train_batch_size":  8,  # 訓練時のバッチサイズ
    "eval_batch_size":   8,  # テスト時のバッチサイズ
    "num_train_epochs":  8,  # 訓練するエポック数
    })
args = argparse.Namespace(**args_dict)

train_params = dict(
    accumulate_grad_batches=args.gradient_accumulation_steps,
    gpus=args.n_gpu,
    max_epochs=args.num_train_epochs,
    precision= 16 if args.fp_16 else 32,
    amp_level=args.opt_level,
    gradient_clip_val=args.max_grad_norm,
)
```

## 変更後
```python:t5_japanese_title_generation.ipynb 学習に必要なクラス等の定義
# GPU利用有無
USE_GPU = torch.cuda.is_available()

# 各種ハイパーパラメータ
args_dict = dict(
    data_dir="/content/data",  # データセットのディレクトリ
    model_name_or_path=PRETRAINED_MODEL_NAME,
    tokenizer_name_or_path=PRETRAINED_MODEL_NAME,

    learning_rate=3e-4,
    weight_decay=0.0,
    adam_epsilon=1e-8,
    warmup_steps=0,
    gradient_accumulation_steps=1,

    # max_input_length=512,
    # max_target_length=64,
    # train_batch_size=8,
    # eval_batch_size=8,
    # num_train_epochs=4,

    n_gpu=1 if USE_GPU else 0,
    early_stop_callback=False,
    fp_16=False,
    opt_level='O1',
    max_grad_norm=1.0,
    seed=42,
    amp_backend='apex',#colab
)
```

```python:t5_japanese_title_generation.ipynb 学習処理クラス
class T5FineTuner(pl.LightningModule):
    def __init__(self, hparams):
        super().__init__()
        #self.hparams = hparams
        self.hparams.update(vars(hparams))

        # 事前学習済みモデルの読み込み
        self.model = T5ForConditionalGeneration.from_pretrained(hparams.model_name_or_path)

        # トークナイザーの読み込み
        self.tokenizer = T5Tokenizer.from_pretrained(hparams.tokenizer_name_or_path, is_fast=True)

    def forward(self, input_ids, attention_mask=None, decoder_input_ids=None, 
                decoder_attention_mask=None, labels=None):
        """順伝搬"""
        return self.model(
            input_ids,
            attention_mask=attention_mask,
            decoder_input_ids=decoder_input_ids,
            decoder_attention_mask=decoder_attention_mask,
            labels=labels
        )

```
```python:t5_japanese_title_generation.ipynb 転移学習を実行
# 学習に用いるハイパーパラメータを設定する
args_dict.update({
    "max_input_length":  512,  # 入力文の最大トークン数
    "max_target_length": 64,  # 出力文の最大トークン数
    "train_batch_size":  8,  # 訓練時のバッチサイズ
    "eval_batch_size":   8,  # テスト時のバッチサイズ
    "num_train_epochs":  8,  # 訓練するエポック数
    })
args = argparse.Namespace(**args_dict)

train_params = dict(
    accumulate_grad_batches=args.gradient_accumulation_steps,
    gpus=args.n_gpu,
    max_epochs=args.num_train_epochs,
    precision= 16 if args.fp_16 else 32,
    #amp_level=args.opt_level,
    gradient_clip_val=args.ma
```

# 絶賛格闘中の軌跡
[リポジトリ(github)](https://github.com/Bandolu/Hogen)