見出し画像

gpt2-japaneseをファインチューニングして太宰治っぽい文を生成する。

どうも。
技術文章を書くのは初めてなのでお手柔らかにお願いします。

GPT-2の日本語モデル「gpt2-japanese」で公開されているsmallモデルとファインチューニング用コードを使って、太宰治の「人間失格」をファインチューニングします。太宰ブシの文章が生成できたら成功です。

1.環境構築

とにもかくにも環境を整えましょう。GPUを積んでいるマシンがあればそちらを使用しても構いません。私は持っていないのでGoogle Colabで作成しました。

(1)Google Colabを開き、「ノートブックを新規作成」を押す。
(2)メニュー「ランタイム→ランタイムのタイプの変更→ハードウェアアクセラレータ」を「None」から「GPU」に変更する。
(3)「保存」を押す。

Google Colabの登録を事前に済ませておいてください。無償でGPUが使えます。

2.学習用コードのダウンロード

以下のコマンドで、「gpt2-japanese」をインストールします。

!git clone https://github.com/tanreinama/gpt2-japanese
%cd gpt2-japanese
!pip uninstall tensorflow -y
!pip install -r requirements.txt

Google Colabではコマンドの頭に「!」や「%」を付けるとLinuxコマンドが使えます。「!」は一時的な処理、「%」は永続的な処理です。ディレクトリの移動などは%を使いましょう。
バージョンを統一するため、3行目で一度tensorflowをアンインストールします。
4行目でrequirements.txtに書いてあるライブラリをインストールします。

「gpt2-japanese」はGPT-2の日本語生成モデルです。詳細は以下のリンクをご参照ください。

3.学習済みモデルのダウンロード

!wget https://www.nama.ne.jp/models/gpt2ja-small.tar.bz2
!tar xvfj gpt2ja-small.tar.bz2

smallとmediumの2種類が公開されています。small以上だとGoogle Colabで動かなくなるらしいので今回はsmallを使います。

4.モデルの動作確認

!python gpt2-generate.py --model gpt2ja-small --num_generate 1 --context "国境の長いトンネルを抜けると雪国であった。"

ダウンロードしたモデルだけでも文章の生成は可能です。
--model 使用するモデル
--num_generate 生成する文章の数
 --context この文章に続く文が生成される

試しに川端康成の「雪国」の書き出し「国境の長いトンネルを抜けると雪国であった。」で始まる文章を生成してみます。

それにしても、今までに比べてここは人の手が入っておらず、自然と皆が移動を始めたのだった。
「ただいま、お帰りなさい」
入った門の先は大きなドーム状の施設がそこにはあり、そこに一人の痩せた男が座っていた。彼は痩せた男に微笑みかけ、
「これをお受け取りなさい。私はある程度まで回復できると思います」
と、言うと手を差し伸べた。それを見た男は、
「何ですか? そんな物は貰ってなんかありませんよ」
と、言うと中から1つの物を取り出し男にくれた。そして、
「この私の回復術を見てみな。この子の使える回復術も有る、どれ、この子を治してくれる物を持ってこい。ただの薬などどうでも良い。どうして治せないと思った? 何故治せないと言うのだ?」
と、聞くと、
「あなたを治す為です。これがあなたの一番の仕事なんですよ!」
と、言うと女は、男に微笑みかけ手を男の方に向けて話し始めた。
「治せるようになるまで、生きていきなさい!」
と、語り始めた。
「でもね、あなたに何も施せない。治せなくても、あなたはあなたである必要があるのさ。だから、それに応える為に私の治療術の研究を始めたのさ、もっといろいろ試してね。この子の薬

小説っぽい文章が生成されました。
なんだかファンタジーっぽいですが。

5.ファインチューニングさせる文章の取得

学習させる「人間失格」の文章は青空文庫から取得します。

「テキストファイル(ルビあり)」をダウンロードしてください。
 zipファイルを解凍して「ningen_shikkaku.txt」を取り出します。

6.Google Driveの接続

from google.colab import drive 
drive.mount('/content/drive')

Google ColabとGoogle Driveを接続すると、Google DriveにアップロードしたファイルをGoogle Colabで処理できるようになります。
上記のコマンドを入力すると認証用のURLと入力フォームが表示されます。URLにアクセスしてauthorization codeをコピーし、入力フォームにペーストしてください。

7.前処理

ダウンロードしたテキストファイルをGoogle DriveにアップロードしてGoogle Colabで処理してもいいのですが、エディターを使いたいのでローカルで処理しました。

import sys
import re

path = 'ningen_shikkaku.txt'
bindata = open(path, "rb")
lines = bindata.readlines()
for line in lines:
   text = line.decode('Shift_JIS')    # Shift_JISで読み込み
   text = re.split(r'\r',text)[0]     # 改行削除
   text = text.replace('|','')       # ルビ前記号削除
   text = re.sub(r'《.+?》','',text)    # ルビ削除
   text = re.sub(r'[#.+?]','',text)  # 入力者注削除
   text = text + '<|endoftext|>'
   print(text)
   file = open('data.txt','a',encoding='utf-8').write(text)  # UTF-8に変換

不要な改行やルビを除き、行ごとに<|endoftext|>を挿入しています。<|endoftext|>は文末を表すトークンです。この単位の文を学習させます。

<処理前>

人間失格

太宰治

-------------------------------------------------------
【テキスト中に現れる記号について】
《》:ルビ
(例)従姉妹《いとこ》
|:ルビの付く文字列の始まりを特定する記号
(例)昔気質かたぎ
[#]:入力者注 主に外字の説明や、傍点の位置の指定
(例)[#3字下げ]はしがき[#「はしがき」は大見出し]
-------------------------------------------------------

[#3字下げ]はしがき[#「はしがき」は大見出し]

 私は、その男の写真を三葉、見たことがある。

 一葉は、その男の、幼年時代、とでも言うべきであろうか、十歳前後かと推定される頃の写真であって、その子供が大勢の女のひとに取りかこまれ、(それは、その子供の姉たち、妹たち、それから、従姉妹《いとこ》たちかと想像される)庭園の池のほとりに、荒い縞の袴《はかま》をはいて立ち、首を三十度ほど左に傾け、醜く笑っている写真である。醜く? けれども、鈍い人たち(つまり、美醜などに関心を持たぬ人たち)は、面白くも何とも無いような顔をして、
「可愛い坊ちゃんですね」

<処理後>

人間失格<|endoftext|>太宰治<|endoftext|><|endoftext|>-------------------------------------------------------<|endoftext|>【テキスト中に現れる記号について】<|endoftext|><|endoftext|>《》:ルビ<|endoftext|>(例)従姉妹<|endoftext|><|endoftext|>:ルビの付く文字列の始まりを特定する記号<|endoftext|>(例)昔気質<|endoftext|><|endoftext|>[#]:入力者注 主に外字の説明や、傍点の位置の指定<|endoftext|>(例)はしがき<|endoftext|>-------------------------------------------------------<|endoftext|><|endoftext|>はしがき<|endoftext|><|endoftext|><|endoftext|> 私は、その男の写真を三葉、見たことがある。<|endoftext|> 一葉は、その男の、幼年時代、とでも言うべきであろうか、十歳前後かと推定される頃の写真であって、その子供が大勢の女のひとに取りかこまれ、(それは、その子供の姉たち、妹たち、それから、従姉妹たちかと想像される)庭園の池のほとりに、荒い縞の袴をはいて立ち、首を三十度ほど左に傾け、醜く笑っている写真である。醜く? けれども、鈍い人たち(つまり、美醜などに関心を持たぬ人たち)は、面白くも何とも無いような顔をして、<|endoftext|>「可愛い坊ちゃんですね」<|endoftext|>

更にタイトルや見出し等の不要な文を除き、エディターの置換機能を使って
<|endoftext|>の後ろに改行を入れます。
行数:803、単語数:7521、文字数(スペース含む):84224でした。

<更に処理後>

私は、その男の写真を三葉、見たことがある。<|endoftext|>
 一葉は、その男の、幼年時代、とでも言うべきであろうか、十歳前後かと推定される頃の写真であって、その子供が大勢の女のひとに取りかこまれ、(それは、その子供の姉たち、妹たち、それから、従姉妹たちかと想像される)庭園の池のほとりに、荒い縞の袴をはいて立ち、首を三十度ほど左に傾け、醜く笑っている写真である。醜く? けれども、鈍い人たち(つまり、美醜などに関心を持たぬ人たち)は、面白くも何とも無いような顔をして、<|endoftext|>
「可愛い坊ちゃんですね」<|endoftext|>

8.エンコード

まずはテキストデータをGoogle Driveにアップロードします。

contentdriveMyDrivegpt2-japningen.txtgpt2-japaneseencode_bpe.py

テキストデータを学習させるために、エンコードして学習データに変換します。

#!git clone https://github.com/tanreinama/Japanese-BPEEncoder.git
#!python ./Japanese-BPEEncoder/encode_bpe.py --src_dir "/content/drive/MyDrive/gpt2-jap" --dst_file finetune

--src_dir txtファイルがあるフォルダ
--dst_file 書き出されるファイル名

エンコード付近でエラーが出ることが多いです。うまくいかない時はローカルに落として試してみてください。完了すると「finetune.npz」がJapanese-BPEEncoderフォルダ内に書き出されます。

9.ファインチューニング

ようやく学習に進みます。run_finetune.pyを実行しますが、その前にコードを少し書き換えます。

        try:
           for i in range(300): # for文に変更
           # while True: # コメントアウト
          
              ・・・
              
           save() # 保存を追加
       except KeyboardInterrupt:
           print('interrupted')
           save()

学習回数を300回転に変更します。最後にsaveを追加するのを忘れないでください。学習結果が保存されません。
finetune.npzをGoogle Driveに用意してスタートです。

!python run_finetune.py --base_model gpt2ja-small --dataset /content/drive/MyDrive/gpt2-jap_dataset/ningen/finetune.npz --run_name gpr2ja-finetune_run1

--base_model ベースとなるモデル
--dataset データセット
--run_name 出力されるファイル名

学習が完了するとcheckpointフォルダにgpr2ja-finetune_run1フォルダが作成されます。こちらの環境では約2時間ほどで完了しました。

10.文章生成

いよいよ、ファインチューニングしたモデルで文章を生成してみます。
「gpr2ja-finetune_run1」フォルダを「gpr2ja-finetune_run1-small」に変更してください

!python gpt2-generate.py --model checkpoint/gpr2ja-finetune_run2-small --num_generate 10

--model ファインチューニングしたモデル
--num_generate 文章の生成数

結果は以下の通りです。

それは、どう見てもダメな奴のようでした。自分にも、この能力があるので、人間の子どもたちのように、能力をさえ怖れることはありませんでした。自分はそんな化け物の能力など、まるで眼中にないように、平気で生きているのでした。
========
「そんなに心配しなくても、僕は、いつも女のひとところにいきますから」
========
「お酒は、お酒だけでしょう?」
========
お前、何か恥ずかしい事でも、何でもないのか? 何もかも忘れて、お前は本当にそれでいいのか? 自分は、ただ、恥ずかしくてたまらぬのだから、恥ずかしくて、とてもいえぬのだから、恥ずかしくて、とてもいい加減、恥ずかしいのだから。それなのに、笑って、本当にいい加減、何でもない事、ただひととおなそれそのあとに、自分はとことん、お互いを知合いくらいの絆を持っているのかどうか、そのくらいの考えはどうしてもできんのだ。
========
「それは、とても、光栄の極み」
========
「それは、どうも」
========
「いやいや、あなた、これは……」
========
「……僕たちは、ただ一緒に歩いて、遊んでるだけなのに」
========
自分は、とても落ちつく場所で、自分に声をかけるのも、返事をするのも、一苦労だ、しかし、それは自分に必要のない、所謂「サー」の言いわけ。つまり自分は、ただの「女」なのだ、自分には、男には、女の「生き」のための、「覚悟」が出来ているのだ、と言い聞かせて、そこの人間性の「器」に酔いしれてしまっているのだった。
========
そうですね、私も、あなたを殺そうとは思いませんでした。けれども、やはり、生きているべきではないと思ったのです。あの時の、人間の恐怖。人間の言葉でない、あの厳粛なまなざし、生きてさえくれば自分もその内に移ってしまうのではないか、そんなどうでもいいものたちに対して、たとい、いっそ、殺してくれようとも、生きてさえくれば自分もその他人と同じに同じになるのではないか、そう考え、生きているのが、自分にはとてもいいことだと思ったのでした。けれども、おそろしい事に、この事に依って、自分の正体を完全に隠蔽されてしまったのでした。人間に、自分の正体を隠蔽せられたという事は、ただ人間に人間の「生」さを賭けて突きつけられた「生」さのための、脅迫の念では無かったのです。人間に、人間の正体を隠蔽せられたという事は、ただ人間にまた人間の「生」さを賭けて突きつせられた「生」さのための脅迫のように思われました。けれども、自分には、隠蔽せられたという気持が全く無かったのです。あなたのように完全に人間の「生」さを賭けて突きつせられた「生」さのために生きているとは思えませんでした。

文章として成り立ってない箇所は多々ありますが、句読点の多さや男、女、行き、死にに関して語られてる場面が多く見受けられます。
これは成功と見なしていいのではないでしょうか。

まとめ

最低限必要な機能に関してのみ記述しました。皆さんも〇〇っぽい文章を生成してみてください。
以下の記事を参考にしました。
https://note.com/npaka/n/ne55d063e1ed8
https://konogi-tools.com/blog2/?p=2306

この記事が気に入ったらサポートをしてみませんか?