Published Date : 2019年6月10日10:01
# Colaboratoryで、MeCabを使う準備 !apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab # 続いて、Neologd辞書をインストール、セットアップ !git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git !echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n # 設定ファイルを変更していきます。 !sed -e "s!/var/lib/mecab/dic/debian!/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd!g" /etc/mecabrc > /etc/mecabrc.new !cp /etc/mecabrc /etc/mecabrc.org !cp /etc/mecabrc.new /etc/mecabrc # PythonでMeCabを使えるようにする。 !apt-get -q -y install swig !pip install mecab-python3 # 準備完了。 # オリジナル記事では、Ochasenだけの指定でしたが、 # 自分の環境では変わらず、 # やもなく直接パスを指定。 import MeCab mecab = MeCab.Tagger('-Ochasen -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd') res = mecab.parse('メカブって神ってる') [r.split('\t')[0] for r in res.split('\n') if r.split('\t')[0]!='EOS' and r.split('\t')[0]!=''] #=> ['メカブ', 'って', '神ってる'] # ちなみに、これを通常の辞書で使用すると。。。 import MeCab mecab = MeCab.Tagger('-Ochasen') res = mecab.parse('メカブって神ってる') [r.split('\t')[0] for r in res.split('\n') if r.split('\t')[0]!='EOS' and r.split('\t')[0]!=''] #=> ['メカブ', 'って', '神', 'って', 'る']
# これが若さか。。。 # 的なGPU設定修正箇所。 print('GPU availability:', chainer.cuda.available) print('cuDNN availablility:', chainer.cuda.cudnn_enabled) if chainer.cuda.available and chainer.cuda.cudnn_enabled: xp = cuda.cupy else: xp = np
class DataConverter: def __init__(self, batch_col_size): # 変更部分 Neologdn辞書を使えるようにする。 self.mecab = MeCab.Tagger('-Ochasen -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd') # 形態素解析器
# バッチサイズ32で様子見 EMBED_SIZE = 100 HIDDEN_SIZE = 100 BATCH_SIZE = 32 # ミニバッチ学習のバッチサイズ数 BATCH_COL_SIZE = 16 EPOCH_NUM = 100 # エポック数 N = len(data) # 教師データの数
if (epoch+1)%10 == 0: ed = datetime.datetime.now() print("epoch:\t{}\ttotal loss:\t{}\ttime:\t{}".format(epoch+1, total_loss, ed-st)) st = datetime.datetime.now() # セーブ後にローカルにダウンロード serializers.save_npz("attention_manzai_model_nights_manzai_batch16.npz", model) from google.colab import files files.download("attention_manzai_model_nights_manzai_batch32.npz")
# いちいちマイドライブをマウントするのが # 面倒くさいってかたはファイルをローカルから直接アップロードしてね。 from google.colab import files uploaded = files.upload()
# 前前前回あたりのスクレイピングしたJSONファイルを # アップロードしたら、読み込む。 import json with open('daihon.json','r',encoding='utf-8') as f: daihon_json=json.load(f) # 作戦:「ガンガンいくぜ!」 # Boke Tukkomi mannaka その他不要な頭の部分をカット。 import re def split_roll(x): try: return re.sub(r'^[^:]+: ','',x) except: return x daihon=[[split_roll(d_) for d_ in d] for d in [d['article'] for d in daihon_json]] # 振り分けのため、タイトルを抽出 titles=[d['title'] for d in daihon_json] # 前回のコードです。 # しゃべくり漫才とされている方々のリストを # Wikiから拝借して、 # 振り分ける。 import requests from bs4 import BeautifulSoup res=requests.get('https://ja.wikipedia.org/wiki/%E6%BC%AB%E6%89%8D%E5%B8%AB%E4%B8%80%E8%A6%A7') soup=BeautifulSoup(res.content,'lxml') lis=soup.find_all('li') manzaishi_list=[] for li in lis: try: manzaishi_list.append(li.a.string) except: if li.string.startswith('「現代上方演芸人名鑑」'): break else: manzaishi_list.append(li.string) manzaishi_indices=[] import re for i,title in enumerate(titles): for manzaishi in manzaishi_list: try: if re.search(manzaishi,title): manzaishi_indices.append(i) except: pass manzaishi_titles=[titles[i] for i in manzaishi_indices] manzaishi_daihon=[daihon[i] for i in manzaishi_indices] # ナイツの漫才のみ抽出。 nights_manzai_list=[manzaishi_daihon[idx] for idx,m in enumerate(manzaishi_titles) if 'ナイツ' in m] import numpy as np nights_scripts=np.array([np.array([line for line in lines]) for lines in nights_manzai_list]) nights_script_lines=np.concatenate(nights_scripts) nights_script_lines[1:3] #=> array(['よろしくお願い致します。', '今年、やりたいなって思ってる事がありましてレストランのオーナーになりたいと思ってるんですよ。'],dtype='>U76')
# リストオブリストオブリストにするための処理。 data=[[[nights_script_lines[idx]],[nights_script_lines[idx+1]]] for idx in range(0,len(nights_script_lines)-1)] # 確認。 data[:2] # => # [[['よろしくお願いします。'], ['よろしくお願い致します。']], # [['よろしくお願い致します。'], ['今年、やりたいなって思ってる事がありましてレストランのオーナーになりたいと思ってるんですよ。']]]
# 定数 EMBED_SIZE = 100 HIDDEN_SIZE = 100 BATCH_SIZE = 8 # ミニバッチ学習のバッチサイズ数 BATCH_COL_SIZE = 16 EPOCH_NUM = 100 # エポック数 N = len(data) # 教師データの数 # 教師データの読み込み data_converter = DataConverter(batch_col_size=BATCH_COL_SIZE) # データコンバーター data_converter.load(data) # 教師データ読み込み vocab_size = len(data_converter.vocab) # 単語数 # モデルの宣言 model = AttSeq2Seq(vocab_size=vocab_size, embed_size=EMBED_SIZE, hidden_size=HIDDEN_SIZE, batch_col_size=BATCH_COL_SIZE) opt = optimizers.Adam() opt.setup(model) opt.add_hook(optimizer.GradientClipping(5)) # ここでGPUの設定。 if xp == cuda.cupy: chainer.cuda.get_device(0).use() model.to_gpu(0) model.reset() # 学習開始 print("Train") st = datetime.datetime.now() # 途中経過のロードの場合。 # 例えば、エポック41でセーブしたけど、42ぐらいで落ちてた場合。 # ここらへんのタイミングでロードしておく。 # serializers.load_npz('41_57.61121.network',model) for epoch in range(EPOCH_NUM): # ミニバッチ学習 perm = np.random.permutation(N) # ランダムな整数列リストを取得 total_loss = 0 for i in range(0, N, BATCH_SIZE): enc_words = data_converter.train_queries[perm[i:i+BATCH_SIZE]] dec_words = data_converter.train_responses[perm[i:i+BATCH_SIZE]] model.reset() loss = model(enc_words=enc_words, dec_words=dec_words, train=True) loss.backward() loss.unchain_backward() total_loss += loss.data opt.update() # 落ちたとき対策の途中保存。 output_path = f"{epoch+1}_{total_loss}.network" serializers.save_npz(output_path, model) if (epoch+1)%10 == 0: ed = datetime.datetime.now() print("epoch:\t{}\ttotal loss:\t{}\ttime:\t{}".format(epoch+1, total_loss, ed-st)) st = datetime.datetime.now() # 保存してやるぅ! serializers.save_npz("attention_manzai_model_nights_manzai_batch8.npz", model) # 落ちろ!カトンボ! from google.colab import files files.download("attention_manzai_model_nights_manzai_batch8.npz")
print("\n漫才のようなものスタート") def predict(model, query): enc_query = data_converter.sentence2ids(query, train=False) dec_response = model(enc_words=enc_query, train=False) response = data_converter.ids2words(dec_response) # change print("=<;", ''.join(response).replace('','')) for i in range(10): print(f'{i+1}回目のやりとり') predict(model, input('-> ')) print()
# まずinitializeのMeCab部分をコメントアウト class DataConverter: def __init__(self, batch_col_size): # クラスの初期化 # :param batch_col_size: 学習時のミニバッチ単語数サイズ # self.mecab = MeCab.Tagger() # 形態素解析器 self.vocab = {"":0, " ": 1} # 単語辞書 self.batch_col_size = batch_col_size
# 続いて既に分かち書きされているので、 # Splitメソッドで半角空白で区切りリスト化するだけ。 def sentence2words(self, sentence): # 文章を単語の配列にして返却する # :param sentence: 文章文字列 ##変更箇所## # sentence_words = [] # self.mecab.parse(sentence).split("\n"): # 形態素解析で単語に分解する # w = m.split("\t")[0].lower() # 単語 # if len(m) == 0 or m == "eos": # 不正文字、EOSは省略 continue # sentence_words.append(m) # これだけ sentence_words = [m for m in sentence.split(' ')] ########### sentence_words.append("") # 最後にvocabに登録している を代入する return sentence_words
# 現在のパスからSudachipyを使えるようにする。 import sys sys.path.append('src/sudachipy/') # Sudachiの設定ファイルを読み込む。 import json from sudachipy import config from sudachipy import dictionary from sudachipy import tokenizer with open(config.SETTINGFILE, 'r', encoding='utf-8') as f: settings = json.load(f) # 分かち書きしてくれるオブジェクトを作成。 tokenizer_obj = dictionary.Dictionary(settings).create() # よしなに、単語を分けてくれるモードを指定。 mode = tokenizer.Tokenizer.SplitMode.C # DataConverter内の修正 class DataConverter: # 省略 # self.mecab = MeCab.Tagger() self.sudachi = tokenizer_obj # 省略 # sentence2words内修正 def sentence2words(self, sentence): # 形態素解析で単語に分解する tokens = self.sudachi.tokenize(mode, sentence) sentence_words = [token.surface() for token in tokens] sentence_words.append("") # 最後にvocabに登録している を代入する return sentence_words