16
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

日本語GPT-2を使って「小説家になろう」に掲載されていそうなタイトルを生成してみた

Last updated at Posted at 2021-09-20

2021年4月、株式会社rinnaが公開した日本語GPT-2を使ってちょっと遊んでみた話です。

概要

rinna GPT-2の特徴

株式会社rinnaのお知らせを見てみると次の特徴があるらしいです。

  • 学習データはCC-100のデータを使用
  • 70GBのテキストを約1ヶ月学習
  • Perplexityが約18
  • huggingfaceに学習済みモデルを公開
  • モデルのサイズはGPT2-medium

せっかくGPT-2モデルをhuggingfaceの使いやすい形で公開してもらっているのならちょっと触ってみるか、ということで今回色々やってみました。

なぜ「小説家になろう」のタイトルを生成したのか

GPT-2の応用として色々考えられると思いますが、今回は「小説家になろう」風のタイトル生成をやってみました。
理由としては以下の通りです。

  1. 学習データの用意が比較的簡単。
  2. 出力が想定したもの(「小説家になろう」のタイトルっぽい)かどうかわかりやすい。
  3. 出力がそれほど長くない(と考えられる)。

個人的に助かったのは3番目です。
ブログ記事などの文書を出力した場合、実際に正しい出力かどうか眺めるのに時間がかかってしまうので。。。
その点、タイトル生成ならそこまで長くないので、出力結果を眺めるのも簡単です。

(個人的に「小説家になろう」のタイトルは好きなので、それらしいタイトルが作れたら面白いなあ、みたいな理由もありますが。。。)

やったこと

せっかく「小説家になろう」っぽいタイトルを生成するなら、よりそれらしいものを出したくないですか?
おそらくですが、事前学習したGPT-2モデルをそのまま使ってもうまくいかないだろう、ということで今回は一工夫しました。
やったことは次の通りです。

  1. 学習データ(「小説家になろう」のタイトル)の用意
  2. 用意した学習データでGPT-2をfine-tuning
  3. fine-tuningしたGPT-2でタイトル生成

以下、順に解説します。

学習データ(「小説家になろう」のタイトル)の用意

学習データとして、今回「小説家になろう」に投稿されているタイトルを使用しています。
「小説家になろう」のタイトル取得にはなろう小説APIを叩いています。
APIはタイトルと一緒に、その小説の総合ポイント月間ポイントが得られるので、今回はその情報も使用しました。
具体的には、取得したタイトルを総合ポイントの高い順に並べ、上位1万件を学習に使用しました(正確には、学習データに9千件、開発データに千件)。

用意した学習データでGPT-2をfine-tuning

用意した学習データを使ってGPT-2をfine-tuningしていきたいと思います。
手順としては次の通りです。

  1. 学習データの整形
  2. fine-tuningスクリプトの用意
  3. fine-tuning開始

学習データの整形

学習データの整形、といっても今回は大したことはしていません。
用意した学習データを基本的にはそのまま使うのですが、一点だけ特殊な処理を行っています。
具体的に言うと、今回はプロンプトを用いてタイトルを生成しているので、学習データにもプロンプトを付与しています。

用意した学習データの例
[N_TITLE]小説家になろうタイトル
[N_TITLE]小説家になろうタイトル2

このように、プロンプトとして[N_TITLE]を付与し、その後1行1タイトルのテキストデータを整形しています。

fine-tuningスクリプトの用意

今回はfine-tuningをPythonおよびTransformersと呼ばれるライブラリを中心に用いて行いました。
具体的には以下の環境でfine-tuningを行っています。

  • Python3.6
  • transformers==4.5.1
  • datasets==1.7.0
  • sentencepiece==0.1.95

この環境を構築後、Transformersのこのスクリプトrun_clm.py1を少し修正し、fine-tuningを行いました。

Tokenizer周りを修正しています。
というのも、こちらのrinna/japanese-gpt2-mediumの置き場を見ると、T5Tokenizerを宣言するように、とあったので、今回はそのようにrun_clm.pyを修正しています。
具体的には、オリジナルのrun_clm.pyはTokenizerを以下のように設定しているのですが、

オリジナルのrun_clm.py(Tokenizer周り)
    if model_args.tokenizer_name:
        tokenizer = AutoTokenizer.from_pretrained(model_args.tokenizer_name, **tokenizer_kwargs)
    elif model_args.model_name_or_path:
        tokenizer = AutoTokenizer.from_pretrained(model_args.model_name_or_path, **tokenizer_kwargs)
    else:
        raise ValueError(
            "You are instantiating a new tokenizer from scratch. This is not supported by this script."
            "You can do it from another script, save it, and load it from here, using --tokenizer_name."
        )

上記の記述を丸ごと以下のように修正します。

修正後のrun_clm.py(Tokenizer周り)
tokenizer = T5Tokenizer.from_pretrained(model_args.model_name_or_path)
tokenizer.do_lower_case = True

最後のdo_lower_caseも忘れずに設定しましよう(実は今回の検証では忘れてしまったため、大文字を出力しようとすると未知語トークンが出力されるモデルとなりました。。。)。

fine-tuning開始

整形した学習データと、fine-tuningスクリプト(run_clm.py)を使って、fine-tuningを行います。
なお、今回はGoogle Colabを用いています。GPUはT4でした。

fine-tuning時のハイパーパラメータは次の通りです。

fine-tuning実行
!python transformers/examples/language-modeling/run_clm_rinna_gpt2.py \
    --output_dir=narou_finetuned_global_gpt2 \
    --model_type=gpt2 \
    --model_name_or_path=rinna/japanese-gpt2-medium \
    --do_train \
    --train_file=/content/drive/MyDrive/NLP_DATASET/gpt-2/global_text/train.txt \
    --do_eval \
    --validation_file=/content/drive/MyDrive/NLP_DATASET/gpt-2/global_text/dev.txt \
    --evaluation_strategy=steps \
    --per_device_train_batch_size=6 \
    --gradient_accumulation_steps=2 \
    --per_device_eval_batch_size=2 \
    --num_train_epochs=4 \
    --save_steps=50 \
    --logging_dir=narou_finetuned_global_logs \
    --logging_strategy=steps \
    --logging_steps=50 \
    --save_strategy=steps \
    --eval_steps=50 \
    --load_best_model_at_end \
    --block_size 200

train_filevalidation_fileに先ほど用意した学習、開発データを設定してください。
このハイパーパラメータで、Google Colabの実行可能時間内に学習が終了しました。

fine-tuningしたGPT-2でタイトル生成

上記スクリプトでfine-tuningしたモデルを使ってタイトルを生成してみたいと思います。
ちなみに生成に使ったスクリプトはこちらを改変したもの(run_generation.py)です。
改変内容は先ほどと同様にTokenizerに関するものなので、詳細については省略します。

タイトル生成時のハイパーパラメータは次の通りです。

タイトル生成
!python /content/transformers/examples/text-generation/run_generation_gpt2.py \
  --model_type gpt2 \
  --model_name_or_path /content/drive/MyDrive/NLP_MODEL/gpt-2/narou_finetuned_global_gpt2 \
  --prompt [N_TITLE] \
  --length 600 \
  --num_return_sequence 10 \
  --stop_token "</s>" \
  --k 40 \
  --p 0.95 \
  --temperature 0.95 \
  --seed 1

promptのハイパーパラメータを利用することで、モデルにこのプロンプトを与えた状態で生成をさせることができます。
また、model_name_or_pathは先ほどのfine-tuningで作成したモデルが置かれているディレクトリを指すようにしてください(私はfine-tuning後に学習済みモデルを手元のGoogle Driveに写しているので上記のようになっています。)。

生成したタイトル例

実際にfine-tuningしたモデルの生成例が次の通りとなります。

タイトルの生成例
=== GENERATED SEQUENCE 1 ===
[N_TITLE]悪役令嬢の婚約者に転生したけど、もう無理だ
=== GENERATED SEQUENCE 2 ===
[N_TITLE]異世界の異世界食堂
=== GENERATED SEQUENCE 3 ===
[N_TITLE]不登校を乗り切る最強魔道士 〜 僕の知らない『最強』が、僕を笑わせてくれるよ
=== GENERATED SEQUENCE 4 ===
[N_TITLE]この世界は <unk> 
=== GENERATED SEQUENCE 5 ===
[N_TITLE]無知は罪。 俺が無能なのは分かっていても、どうして彼女がお前の味方になってくれないんだ!? 俺のことを好きじゃないと言われても、もう遅い。
=== GENERATED SEQUENCE 6 ===
[N_TITLE]異世界英雄記~転生したら最強の魔王がいました~
=== GENERATED SEQUENCE 7 ===
[N_TITLE]異世界に降り立ったけれど、やることが無さすぎる。
=== GENERATED SEQUENCE 8 ===
[N_TITLE]<unk> ランク冒険者は冒険者ギルドから追放されました
=== GENERATED SEQUENCE 9 ===
[N_TITLE]乙女ゲームの悪役令嬢と公爵令嬢
=== GENERATED SEQUENCE 10 ===
[N_TITLE]ダンジョンダンジョン

大半が異世界に転生に関するタイトルだったり、悪役令嬢に関するタイトルだったりしました。
確かに「小説家になろう」でありそうな特徴的なタイトルが多く生成できていそうです(個人的にはGENERATED SEQUENCE 3は要素てんこ盛りで気になりました。)。
ちなみにこれらのタイトルを「小説家になろう」で検索したところ、全く同一のタイトルは存在しませんでした。
つまり、GPT-2をfine-tuningしたモデルはちゃんと「小説家になろう」に掲載されていそうなタイトルを生成できていそうです。

ただし、GPT-2も完璧ではなくて、GENERATED SEQUENCE 2GENERATED SEQUENCE 10などは同じ単語、フレーズを繰り返してしまいました。
こういったニューラルネットワークを用いた、文などを生成する際の特有の課題はGPT-2でもまだまだありそうです。

また、GENERATED SEQUENCE 4GENERATED SEQUENCE 8の出力に<unk>が入っています。
これは未知語トークンを表していて、おそらく4番の出力はモデルに登録されていない、難しい漢字を出力しようとしたのだと考えられます。
一方で、8番の出力はおそらくSランク冒険者...としたかったのだろうと考えられ、これはモデルにとってSが未知語だったからだと考えられます(fine-tuningスクリプトの用意の章で記述したlowercase忘れがここで響いています。。。)。

[蛇足] GPT-2に生成したタイトルの解説をさせてみたかった。。。

最後に、fine-tuning前の、公開されているGPT-22を使って、生成したタイトルの解説をさせてみようとしてみた話をします。
具体的には、先ほど生成したタイトルの一つである、不登校を乗り切る最強魔道士 〜 僕の知らない『最強』が、僕を笑わせてくれるよがどんな作品であるかGPT-2に解説文を生成させてみました。

スクリプトは先ほどのfine-tuningしたGPT-2でタイトル生成のものを再度使用します。

解説文生成
!python /content/transformers/examples/text-generation/run_generation_gpt2.py \
  --model_type gpt2 \
  --model_name_or_path rinna/japanese-gpt2-medium \
  --prompt "『不登校を乗り切る最強魔道士 〜 僕の知らない『最強』が、僕を笑わせてくれるよ』とは、" \
  --length 800 \
  --num_return_sequence 1 \
  --stop_token "</s>" \
  --k 40 \
  --p 0.95 \
  --temperature 0.95 \
  --seed 1

先ほどいくつかパラメータを変えています。
model_name_or_pathrinna/japanese-gpt2-mediumを指定し、promptはタイトルの解説をしてくれるように指定しています。
また、lengthも先ほどより少し長めの値を指定しています。

このスクリプトで生成した解説文がこちらです(すごく長いので途中まで改行は入れています。)。

生成した解説文
『不登校を乗り切る最強魔道士 〜 僕の知らない『最強』が、僕を笑わせてくれるよ』とは、本日10月12日(水)より、huluにて配信開始となった。
 huluでは『不登校を乗り切る最強魔道士 〜 僕の知らない『最強』が、僕を笑わせてくれるよ』の試写会応募者を対象に、抽選で映画『僕の知らない『最強』が、僕を笑わせてくれるよ』の試写会が1組4名様に当たるプレゼントキャンペーンを実施。 
応募にはhuluのidの登録が必要だが、hulu会員でなくても応募できる。
なお、本キャンペーンは、huluの会員でない方も応募可能だ。
 ・当選者の発表:当選者に対しては、huluよりメールにてご連絡させていただきます。
 ・賞品の発送:当選者には、賞品を配送する手続きが完了してからの発送となります。
 ・当選者には、後日、当選のご連絡をさせていただく場合がございます。
 ・当選者の個人情報は、huluのキャンペーン・サービスの案内以外には使用いたしません。
 ・応募に関する質問にお答えすることはできません。あらかじめご了承ください。
 ・応募は日本国内の方に限らせていただきます。海外の方の応募は無効とさせていただきます。
 ・当選の連絡は、応募された方のみにメールでご連絡いたします。なお、メールはお送りいたしませんので、ご了承ください。
 ・当選の通知後、一定期間ご連絡をいただけない場合は、当選資格が無効となる場合がございます。
 ・当選のご連絡をさせていただくため、本メールに記載されている「登録完了メール」や「発送のご案内」は、応募された方のメールアドレス宛に自動送信させていただいております。
 ・メール受信の制限を設定している場合は、「hulu_jp」からのメールを受信できるように設定を変更してください。 
・hulu公式サイト(https://hulu.jp)から、ログインして応募してください。
 ・キャンペーンの応募に際しては、当社所定の「利用規約」、及び、本キャンペーンで取得した個人情報(氏名、住所、電話番号、メールアドレス)について、本キャンペーン事務局にて適切に管理・保管いたします。
 ・応募者は、ご応募に際して、第三者に対して、本キャンペーンにおける応募内容、本キャンペーンへの参加を許諾するものではありません。
 ・応募者は、応募に際して、本キャンペーン事務局からの電子メール、郵送、またはその他の方法による情報等の発信元を、当社の関連会社、または、当社が指定した第三者に開示・提供することはありません。 ・応募者に対し、当社は、本キャンペーンの内容について、事前に予告をした上で、変更、中止、中止をすることができるものとし、これによって応募者に損害が生じたとしても、当社は一切責任を負いません。 ・応募者は、本キャンペーンへの参加にあたり、当社の業務の全部又は一部を第三者に譲渡し、又は、合併、移転することはできません。 ・応募者は、本キャンペーンへの応募にあたって、本キャンペーン事務局に対して、応募者の個人情報を、当社の関連会社、または、当社が指定した第三者に提供することはできないものとします。 ・応募者は、本キャンペーンへの応募に際して、当社及び当社が加盟する団体、企業等に対して、当該応募内容、本キャンペーンに応募することについて、必要な権利等(第三者による権利侵害の防止、権利侵害の予防、損害賠償請求その他の権利行使の目的を含むものとし、以下同じとします。)を有していることにつき、あらかじめ当社が指定した情報提供者に同意するものとします。 ・応募者は、本キャンペーンが応募者に帰属する、知的財産権、肖像権その他の権利、名誉、プライバシー等、すべての利益を侵害するものではないことを、あらかじめ、当社に対し、無償、無期限又は、無期限で表明し、保証するものとします。 ・当社は、応募者の個人情報が応募者の個人情報を適切に管

huluで試写会参加応募のキャンペーンが実施されたみたいです。。。
プロンプトの入れ方を工夫すればまた違った何かが生成されるとは思うのですが、この辺りはなかなか難しいですね。。。(最初の数文でhulu会員でなくても応募できることが繰り返されているので、この繰り返しは先ほどのタイトル生成と同様、GPT-2の特徴と言えそうです。)

まとめ

GPT-2を使って「小説家になろう」に掲載されていそうなタイトルを生成してみました。
結果としてはそれらしく、なおかつ新規のタイトルが生成できたのかな、という印象です。
一方で、生成されたタイトルの中には同じ表現の繰り返しなどが存在する場合があり、その辺りはまだまだ課題なのかなと感じました。

今回のタイトル生成は以下のGoogle Colabで実行しました。
ご参考いただければと思います。
Google Colab: https://colab.research.google.com/drive/1drZxtZl8jk1DU4c2MJ14AHPDTjNy5bXK?usp=sharing

また、このタイトル生成をWebアプリとして作成しました。
どんなタイトルが生成されるのか気になる方は是非とも触っていただければと思います。
Webアプリ: https://www.narou-generator.com

  1. 2021年8月末時点ではTransformersのバージョンは4.9.2まで出ており、run_clm.pyのパスも少し変わっていました。おそらくtransformers/examples/pytorch/language-modeling/run_clm.pyを使用すれば問題ないと思います(このバージョンでは未検証ですが。。。)。

  2. fine-tuning後のモデルでも解説文生成を試してみましたが、プロンプトの後に出力打ち切りトークン(</s>)を出してしまいうまく生成できませんでした。。。

16
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?