3
4

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 5 years have passed since last update.

GPT-2で作るJoke BotからSlack Botまで #3 コードの編集

Last updated at Posted at 2019-06-04

イントロ

この記事は主に自分のパソコンの上じゃなくてColaboratoryで深層学習をしたいという人向けです。もちろん、もし自分のパソコンでしてみたい方々がいてもできるようにやり方を書きます。
よってJoke Botを作りたい人が対象者といっても誤りはないかもしれません。この内容は全部このノートブックこのpython fileにあるのでぜひ参考までに見てください。Part 1 と Part 2 はここここにあるのでぜひ最初に見ておいてください。

トレーニングをする前に

いきなりテキストファイルからは学習できません。なのでエンコーディングしないといけません。これは

 python encode.py input_text_file.txt output_data.txt.npz

とすればできます。実際にどのようにエンコーディングしているかは英語ですがこれに書いたので興味あればぜひ見てみてください!

Colaboratoryとは?

ColaboratoryはGoogleがくれる無料のGPUが付いているjupyter notebookです。前までは結構遅いGPUでしたが最近はT4という結構速いGPUが付き始めたのでよくなりました。google driveでjupyter notebookを開こうとするとできます。

欠点

ただ、ちょっとした欠点もあります。学習したモデルが簡単に保存できないというのと12時間でどんな作業をしているかは関係なくノートブックが初期化されてしまうことです。つまり、モデルのデーターが削除されやすいわけです。よって、ふつうはColaboratoryは単純なテストなどに使われます。例えば数時間モデルを学習した結果を報告することなどに使われて数日や数か月にわたる学習などには大して使われません。これを解決する方法はGoogle Driveにモデルを保存することです。こうすると次回トレーニングが終わったところから始めるにしても簡単に続けられ、何日間も何週間にわたっても学習し続けることができます。このためにpydriveというライブラリを使います。

ColaboratoryとGoogle Driveのつなげ方

正直言うと初めてこれをしようとしたときはうまくいきませんでした。結構いろいろしなくてはいけないことなどがあってほかの人のコードを見る以外勉強する方法がありませんでした。よって、とりあえずここにpydriveを使う方法を書いておきます。

  1. ユーザー認証
    一番最初にしないといけないことはユーザー認証です。これは

    from pydrive.auth import GoogleAuth
    from pydrive.drive import GoogleDrive
    from google.colab import auth
    from oauth2client.client import GoogleCredentials

    1. Authenticate and create the PyDrive client.

    auth.authenticate_user()
    gauth = GoogleAuth()
    gauth.credentials = GoogleCredentials.get_application_default()
    drive = GoogleDrive(gauth)

と入れればできます。これがやることはclient.jsonというファイルを保存することです。これがないとgoogle driveに何の操作もできません。しかし、client.jsonというファイルはセキュリティーの都合上などで何度も何度も突然消えてしまうので私はgoogle driveに操作するときは毎回このコードを動かしています。
2. Google Driveの必要なファイルを全部取得する
これは個々のコードが元です.
単純に

def ListFolder(parent, show=False):
  filelist=[]
  file_list = drive.ListFile({'q': "'%s' in parents and trashed=false" % parent}).GetList()
  for f in file_list:
    if f['mimeType']=='application/vnd.google-apps.folder': # if folder
         if f['title'] == model_folder_name or f['title'] == past_folder_name:          
             filelist.append({"id":f['id'],"title":f['title'], "mimeType": 'application/vnd.google-apps.folder', "list":ListFolder(f['id'])})
         else:
             for f1 in ListFolder(f['id']):
                  filelist.append(f1)
    else:
        filelist.append({"id":f['id'],"title":f['title'],"mimeType":"file",\
        "title1":f['alternateLink']})
  return filelist
fl = ListFolder('root')

という感じにすれば全部とれます。ここでmodel_folder_nameとpast_folder_nameは欲しいフォルダーの名前です。別に抜いても大丈夫ですがそうすればすべてのフォルダーのすべてのデータがflに入るのでそれは少しいやだなーと思ってやりました。ここで作ったflというのはあとでGoogle driveからファイルをとったりするときに使えます。
3. Folderを取得する
例えば実際にフォルダーをGoogle driveからとる際には

for f in fl:
  if f['title'] == model_folder_name:
    for part in f["list"]:
      fil = drive.CreateFile({'id':part['id']})
      fil.GetContentFile(part['title'])
      os.rename(part['title'], "./run1/"+part['title'])

のようにすればフォルダーの中のファイルが全部run1に保存されます。fil.GetContentFileは欲しいフォルダーの名前を付けてgoogle driveのファイルを取得します。
ただ、フォルダーをアップロードする都合上すこし名前が変になってしまいまして、

for f in fl:
  if f['title'] == model_folder_name:
    for part in f["list"]:
      fil = drive.CreateFile({'id':part['id']})
      fil.GetContentFile(part['title'][18:])
      os.rename(part['title'][18:], "./run1/"+part['title'][18:])

としないといけませんでした。
4. フォルダーをアップロードする

for content in os.listdir(parent_folder):
				f = drive.CreateFile({"parents": [{"kind": "drive#fileLink", "id": google_drive's parent_folder id}]})
				f.SetContentFile(parent_folder+"/"+content)
				f.Upload()

見ての通り.SetContentFileの段階でparent_folder + "/" + contentとファイルを指定するのですが、Google Driveにアップロードしたファイルcontentという名前ではなくparent_folder + "/" + contentと名前を付けるので[18:]とファイルを取得する際にparent_folder+"/"を切り捨てる作業をしないといけませんでした。もし解決策を知っている方がいたらぜひ教えてください。

train.pyの編集

ほとんどのこのノートブックのコードは結構簡単なので説明はもしリクエストがあれば答えますが今は長くしたくないので省いておきます。
よって、一気にtrain.pyをどのように編集したか説明します。train.pyは

python ./train.py --dataset jokes_400_2_3.0_1.txt.npz --batch_size 2 --sample_every 100 --save_every 1000

とすることによって、トレーニングが始まります。しかし、少し残念ながらGoogle Driveに保存されません。よって、少しコードを変えて

!python ./train.py --dataset jokes_400_2_3.0_1.txt.npz --batch_size 2 --sample_every 100 --save_every 1000 \
--model_folder_name $model_folder_name --past_model_folder_name $past_folder_name

としてmodel_folder_nameというGoogle Driveのフォルダーに今のモデル、past_folder_nameというGoogle Driveのファイルに過去もモデルと今のモデルを保存するようにしました。
このノートブックに行けば変更できますが初期の値は"model"と"past"です。これらのフォルダーは最初からGoogle Driveで作っておく必要があります。
変更したところはsaveという関数です!ここで毎回保存するようにコードができていたのでここでアップロードするようにしました。

def get_folder_id(name):
		global fl
		for i in fl:
			if i["title"] == name:
				return i['id']
	def get_children(name):
		global fl
		for i in fl:
			if i["title"] == name:
				return i.get("list", [])
	def save():
		global fl
		for content in os.listdir(os.path.join(f"./{CHECKPOINT_DIR}", args.run_name)):
			if content[0] != "e":#summary file
				os.remove(os.path.join(f"./{CHECKPOINT_DIR}", args.run_name)+"/"+content)
		maketree(os.path.join(CHECKPOINT_DIR, args.run_name))
		print(
			'Saving',
			os.path.join(CHECKPOINT_DIR, args.run_name,
						 'model-{}').format(counter))
		saver.save(
			sess,
			os.path.join(CHECKPOINT_DIR, args.run_name, 'model'),
			global_step=counter)
		with open(counter_path, 'w') as fp:
			fp.write(str(counter) + '\n')
		from pydrive.auth import GoogleAuth
		from pydrive.drive import GoogleDrive
		from google.colab import auth
		from oauth2client.client import GoogleCredentials

		# 1. Authenticate and create the PyDrive client do this every time because sometimes the client.json just dissapears.
		auth.authenticate_user()
		gauth = GoogleAuth()
		gauth.credentials = GoogleCredentials.get_application_default()
		drive = GoogleDrive(gauth)
		#Delete folders from model_folder_name
		for file in get_children(args.model_folder_name):
			f = drive.CreateFile({"id": file["id"]})
			f.Delete()
		#Upload to model_folder_name
		for content in os.listdir(os.path.join(f"./{CHECKPOINT_DIR}", args.run_name)):
			f = drive.CreateFile({"parents": [{"kind": "drive#fileLink", "id": get_folder_id(args.model_folder_name)}]})
			f.SetContentFile(os.path.join(f"./{CHECKPOINT_DIR}", args.run_name)+"/"+content)
			f.Upload()
		#Upload to past_model_folder_name
		for content in os.listdir(os.path.join(f"./{CHECKPOINT_DIR}", args.run_name)):
			f = drive.CreateFile({"parents": [{"kind": "drive#fileLink", "id": get_folder_id(args.past_model_folder_name)}]})
			f.SetContentFile(os.path.join(f"./{CHECKPOINT_DIR}", args.run_name)+"/"+content)
			f.Upload()
		#update folder list
		fl = ListFolder("root")

という感じです。ちょっと説明が苦手なのでコードを一気に載せてすみません。コードにしたら結構でかいですがしたことは単純に

  1. model_folder_nameのGoogle Driveのフォルダーのファイルを全部削除する。
  2. model_folder_nameとpast_foler_nameのフォルダーにファイルをアップロードする。
  3. File listを更新する
    だけです。もしわからないことや変なところなどがあればぜひ教えてください!ちょっと長くなったので次回実際にトレーニングの結果を報告します。
3
4
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
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?