colab-logo-1GPT-2の汎用日本語モデルが
いつの間にか公開されていたので、
これにサクっとファインチューニングを施すやつ。

いわゆるフェイク何某にもなりかねないので
一応注意の上、参考程度にしてください。



つまるところ……ある程度、最初から日本語を学習させたBOTに
途中から特定の人物のTwitterから抽出した特徴を転移学習させて
手っ取り早くその人っぽいBOTを作ろう!
というやつ。

今回は、これを最小ステップ(TwitterのIDを指定するくらいの作業)で実現したい。




えぇ、そらもう。

流行りに乗っかりますよ。


#必要そうなものを入れる

!git clone https://github.com/tanreinama/gpt2-japanese
!git clone https://github.com/tanreinama/Japanese-BPEEncoder.git
%cd gpt2-japanese
!pip uninstall tensorflow -y
!pip install -r requirements.txt
!wget https://www.nama.ne.jp/models/gpt2ja-small.tar.bz2
!tar xvfj gpt2ja-small.tar.bz2
!mkdir srcdataset




まずは環境構築。
GPT-2日本語モデルを環境にダウンロードして展開。
ついでにファインチューニング用の
データセット作成用エンコーダとそれ用のディレクトリを作ります。


 

# 転移学習させるTwitterID
tw_id="hibiki_linasha"

# Tweepy用にAPIキーを入れる
CONSUMER_KEY = '*********'
CONSUMER_SECRET = '*********'
ACCESS_TOKEN = '*********'
ACCESS_TOKEN_SECRET = '*********'


# 以下処理
import tweepy
import pandas as pd
import datetime
import re
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth)
def get_tweets():
    tweet_data = []
    for tweet in tweepy.Cursor(api.user_timeline,screen_name = 
    tw_id,exclude_replies = True).items():
        tweet_data.append(tweet.text.replace('\n',''))
    df = pd.DataFrame(tweet_data)
    df.to_csv("rawtweet.txt", sep=",")
    df = pd.read_csv("rawtweet.txt")
    df = df.replace("https(:\/\/[-_\.!~*\'()a-zA-Z0-9;\/?:\@&=\+$,%#]+)", "" ,regex=True)
    df = df.replace("@YouTubeより", "" ,regex=True)
    df = df[~df['0'].str.contains('RT', na = False)]
    df.to_csv("/content/gpt2-japanese/srcdataset/dataset.txt", index=False, sep=",")
    print("Complete.")
get_tweets()
!python /content/Japanese-BPEEncoder/encode_bpe.py 
    --src_dir /content/gpt2-japanese/srcdataset --dst_file finetune


例によってTweepyで特定Twitterの投稿を収集。
APIキーなどは適当に用意してください。

収集したツイートからURLやらリツイートを取り沿いて
処理ディレクトリに放り込み、エンコードしてハイ終了。





# 転移学習させる

import json
import os
import numpy as np
import tensorflow.compat.v1 as tf
import time
import tqdm
from copy import copy
from encode_bpe import BPEEncoder_ja
import model

if int(tf.__version__[0]) > 1:
    from model import HParams as HParams
else:
    from tensorflow.contrib.training import HParams

CHECKPOINT_DIR = 'checkpoint'
SAMPLE_DIR = 'samples'


dataset = 'finetune.npz'
base_model = 'gpt2ja-small'
batch_size = 1
optim = "adam"
learning_rate = 5
warmup_steps = 0
run_name = "gpr2ja-finetune_run12-small"
save_every = 1000
gpu = "0"


def maketree(path):
    try:
        os.makedirs(path)
    except:
        pass

with open('ja-bpe.txt') as f:
    bpe = f.read().split('\n')

with open('emoji.json') as f:
    emoji = json.loads(f.read())

enc = BPEEncoder_ja(bpe, emoji)
n_vocab = len(enc)

def main():
    if 'small' in base_model:
        hparams = HParams(**{
          "n_vocab": n_vocab,
          "n_ctx": 1024,
          "n_embd": 768,
          "n_head": 12,
          "n_layer": 12
        })
    elif 'medium' in base_model:
        hparams = HParams(**{
          "n_vocab": n_vocab,
          "n_ctx": 1024,
          "n_embd": 1024,
          "n_head": 16,
          "n_layer": 24
        })
    elif 'large' in base_model:
        hparams = HParams(**{
          "n_vocab": n_vocab,
          "n_ctx": 1024,
          "n_embd": 1280,
          "n_head": 20,
          "n_layer": 36
        })
    else:
        raise ValueError('invalid model name.')

    config = tf.ConfigProto()
    if int(gpu) >= 0:
        config.gpu_options.allow_growth = True
        config.gpu_options.visible_device_list = gpu
    with tf.Session(config=config,graph=tf.Graph()) as sess:
        context = tf.placeholder(tf.int32, [None, None])
        output = model.model(hparams=hparams, X=context, 
         past=None, reuse=tf.AUTO_REUSE)
        loss = tf.reduce_mean(
            tf.nn.sparse_softmax_cross_entropy_with_logits(
                labels=context[:, 1:], logits=output['logits'][:, :-1]))

        saver = tf.train.Saver()
        ckpt = tf.train.latest_checkpoint(base_model)
        saver.restore(sess, ckpt)

        train_vars = tf.trainable_variables()

        global_step = tf.Variable(0, trainable=False)
        if warmup_steps > 0:
            learning_rate = tf.compat.v1.train.polynomial_decay(
                    learning_rate=1e-10,
                    end_learning_rate=learning_rate,
                    global_step=global_step,
                    decay_steps=warmup_steps
                )
        else:
            learning_rate = 5

        if optim=='adam':
            opt = tf.train.AdamOptimizer(learning_rate=learning_rate,
                                           beta1=0.9,
                                           beta2=0.98,
                                           epsilon=1e-7)
        elif optim=='adagrad':
            opt = tf.train.AdagradOptimizer(learning_rate=learning_rate)
        elif optim=='sgd':
            opt = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
        else:
            raise ValueError('invalid optimizer name.')

        train_vars = tf.trainable_variables()
        opt_grads = tf.gradients(loss, train_vars)
        opt_grads = list(zip(opt_grads, train_vars))
        opt_apply = opt.apply_gradients(opt_grads)

        summaries = tf.summary.scalar('loss', loss)
        summary_log = tf.summary.FileWriter(
            os.path.join(CHECKPOINT_DIR, run_name))

        saver = tf.train.Saver(
            var_list=train_vars,
            max_to_keep=5,
            keep_checkpoint_every_n_hours=2)
        sess.run(tf.global_variables_initializer())

        ckpt = tf.train.latest_checkpoint(base_model)
        saver.restore(sess, ckpt)
        print('Loading checkpoint', ckpt)

        print('Loading dataset...')
        global_chunks = []
        with np.load(dataset) as npz:
            for inditem, item in enumerate(npz.files):
                token_chunk = npz[item]
                current_token = []
                for ind in range(0,len(token_chunk)):
                  current_token.append(np.uint16(token_chunk[ind]))
                  if len(current_token) == hparams.n_ctx:
                      global_chunks.append(current_token)
                      current_token = []
        global_chunk_index = np.random.permutation(len(global_chunks))
        global_chunk_step = 0
        print('Training...')

        def sample_feature():
            nonlocal global_chunks,global_chunk_index,global_chunk_step
            p_input_ids = []

            for b in range(batch_size):
                idx = global_chunk_index[global_chunk_step]
                global_chunk_step += 1
                if global_chunk_step >= len(global_chunk_index):
                    global_chunk_step = 0
                    global_chunk_index = np.random.permutation(len(global_chunks))
                sampled_token = global_chunks[idx]
                ids = copy(global_chunks[idx])
                p_input_ids.append(ids)

            return {context:p_input_ids}

        counter = 1
        counter_path = os.path.join(CHECKPOINT_DIR, run_name, 'counter')
        if os.path.exists(counter_path):
            with open(counter_path, 'r') as fp:
                counter = int(fp.read()) + 1

        maketree(os.path.join(CHECKPOINT_DIR, run_name))

        def save():
            maketree(os.path.join(CHECKPOINT_DIR, run_name))
            print(
                'Saving',
                os.path.join(CHECKPOINT_DIR, run_name,
                             'model-{}').format(counter))
            saver.save(
                sess,
                os.path.join(CHECKPOINT_DIR, run_name, 'model'),
                global_step=counter)
            with open(counter_path, 'w') as fp:
                fp.write(str(counter) + '\n')

        avg_loss = (0.0, 0.0)
        start_time = time.time()

        try:
            for i in range(500):
                if counter % save_every == 0:
                    save()

                (_, v_loss, v_summary) = sess.run(
                    (opt_apply, loss, summaries),
                    feed_dict=sample_feature())

                summary_log.add_summary(v_summary, counter)

                avg_loss = (avg_loss[0] * 0.99 + v_loss,
                            avg_loss[1] * 0.99 + 1.0)

                print(
                    '[{counter} | {time:2.2f}] loss={loss:2.2f} avg={avg:2.2f}'
                    .format(
                        counter=counter,
                        time=time.time() - start_time,
                        loss=v_loss,
                        avg=avg_loss[0] / avg_loss[1]))

                counter = counter+1
                if warmup_steps > 0:
                    global_step = global_step+1
            save()
        except KeyboardInterrupt:
            print('interrupted')
            save()


if __name__ == '__main__':
    main()


なんか付属のファインチューニングコードが
使いにくかったので少しいじったものが上記。
500エポックで止まる省エネ仕様です。

#例文出力

!python gpt2-generate.py --model 
/content/gpt2-japanese/checkpoint/gpr2ja-finetune_run12-small/
    --num_generate 5


学習結果はこちらで出力。
5文章くらいがつらつらと出てきます。
後は煮るなり焼くなり、以下宜しく。