毎日テキストマイニング

180日間、毎日テキストマイニングをするブログです

2018/09/01【70日目】昨日のRNNのコードを読み込んでいく

昨日のコピペしていったコードを見ていきたいと思います。

前処理

まずは必要なライブラリのimportとテキストの読み込みから。

from __future__ import print_function
import mxnet as mx
from mxnet import nd, autograd
import numpy as np

mx.random.seed(1)

with open("all_kaiseki.txt") as f:
    text = f.read()

print(text[0:500])

実行結果はこんな感じです。

tweet "# アイドル修業中 ♡ 公演 来 て くれ て 、 見 て くれ て 、 ありがとう ござい  た ???! ドラフト 3 期生 の # 齋藤 陽 菜 ちゃん ( は ー たん )( 1 枚 目 ) と # 石綿 星 南 ちゃん ( せ な たん )( 2 枚 目 )
character_list = list(set(text))
vocab_size = len(character_list)
print(character_list[:-10])
print("Length of vocab: %s" % vocab_size)

単語ごとにリストの中に入れている様子ですね。

['確', '…', '濃', 'k', '葉', '하', '‥', 'ん', '敷', '運', '瞬', '勉', '毎', '礼', '第', '賢', '晴', '연', '肯', '益', '抱', 'A', '員', '経', '!', '라', 'ー', '警', '束', '落', '代', '資', '蔵', '惜', '失', '炎', '⑱', 'ー', '冷', '투', '繋', 'ナ', 'バ', '茂', '容', '尽', 'テ', 'む', '団', '当']

それで次に各文字ごとにIDをふっています。

character_dict = {}
for e, char in enumerate(character_list):
    character_dict[char] = e
print(character_dict)

インデックスと文字を辞書にして取り出せるようにしています。

time_numerical = [character_dict[char] for char in text]
print(len(time_numerical))
print(time_numerical[:20])
print("".join([character_list[idx] for idx in time_numerical[:50]]))

実行結果。

256514
[933, 1537, 870, 870, 933, 744, 1272, 1240, 744, 1492, 1074, 153, 130, 1594, 1885, 1607, 744, 898, 744, 1679]
tweet "# アイドル修業中 ♡ 公演 来 て くれ て 、 見 て くれ て 、 ありがとう 

コンピュータが読み込めるようにワンホットエンコーディングを処理してます。

def one_hots(numerical_list, vocab_size=vocab_size):
    result = nd.zeros((len(numerical_list), vocab_size))
    for i, idx in enumerate(numerical_list):
        result[i, idx] = 1.0
    return result

print(one_hots(time_numerical))
[[ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 ..., 
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]]
<NDArray 256514x1886 @cpu(0)>

すごい行列数になっていますが0と1に変換できました。 テキストに戻すには次のコード。

def textify(embedding):
    result = ""
    indices = nd.argmax(embedding, axis=1).asnumpy()
    for idx in indices:
        result += character_list[int(idx)]
    return result
textify(one_hots(time_numerical[:50]))

学習の開始

ここから学習です。 シークエンスの長さはtweetが140なので、140に設定しておきます(正しいかはわかりません)。

seq_length = 140
num_samples = (len(time_numerical) - 1) // seq_length
dataset = one_hots(time_numerical[:seq_length*num_samples]).reshape((num_samples, seq_length, vocab_size))
textify(dataset[1])

実行結果

') が 劇場 フル 出演 デビュー でし た ~!✨ 出 て くれ … https :// t .  / PuFkSwSEnZ " " 今日 は # 山根 涼 羽 ずん ちゃんと 、 カフェ に 行っ たり お 買い物 し  た ☕?? 大切 な 同期 と の 時間 で とっても'

次はbatch_sizeを決めるのですが、batch_sizeって何だって話ですが、どうやら学習させる単位のまとまりだそうです。 どのくらいの数を指定すればいいのかわからないので256を指定します。

batch_size = 256
print('# of sequences in dataset: ', len(dataset))
num_batches = len(dataset) // batch_size
print('# of batches: ', num_batches)
train_data = dataset[:num_batches*batch_size].reshape((batch_size, num_batches, seq_length, vocab_size))
# swap batch_size and seq_length axis to make later access easier
train_data = nd.swapaxes(train_data, 0, 1)
train_data = nd.swapaxes(train_data, 1, 2)
print('Shape of data set: ', train_data.shape)
# of sequences in dataset:  1832
# of batches:  7
Shape of data set:  (7, 140, 256, 1886)

次のコードにいきます。

labels = one_hots(time_numerical[1:seq_length*num_samples+1])
train_label = labels.reshape((batch_size, num_batches, seq_length, vocab_size))
train_label = nd.swapaxes(train_label, 0, 1)
train_label = nd.swapaxes(train_label, 1, 2)
print(train_label.shape)

数値をいろいろ操作しているようです。nd.swapaxesは軸の入れ替えをしているようです。目的は不明。 実行するとこんな感じになります。

(7, 140, 256, 1886)

次はパラメーターの設定。各パラメータが何を意味しているのか謎です。

num_inputs = vocab_size
num_hidden = 256
num_outputs = vocab_size

Wxh = nd.random_normal(shape=(num_inputs,num_hidden)) * .01
Whh = nd.random_normal(shape=(num_hidden,num_hidden)) * .01
bh = nd.random_normal(shape=num_hidden) * .01
Why = nd.random_normal(shape=(num_hidden,num_outputs)) * .01
by = nd.random_normal(shape=num_outputs) * .01

params = [Wxh, Whh, bh, Why, by]

for param in params:
    param.attach_grad()__

次にsoftmax関数の作成。これは出力される値を確率に変換する式のことをSoftmaxというらしいです。 ここまできたらやっとrnnの実装。

def simple_rnn(inputs, state, temperature=1.0):
    outputs = []
    h = state
    for X in inputs:
        h_linear = nd.dot(X, Wxh) + nd.dot(h, Whh) + bh
        h = nd.tanh(h_linear)
        yhat_linear = nd.dot(h, Why) + by
        yhat = softmax(yhat_linear, temperature=temperature)
        outputs.append(yhat)
    return (outputs, h)

思ったよりも楽ではなかったMXnetでした。流れが掴めたのでよしとします。

今日の結果

今日の呟きは82件でした。

'嬉しい': 12, '可愛い': 8, '楽しい': 6, 'よい': 4, '良い': 3, '早い': 2, 'ほしい': 2, 'つらい': 1, '美味しい': 1, 'おしい': 1, 'あざとい': 1, '無い': 1, 'すごい': 1, 'すっごい': 1, 'よろしい': 1, 'いい': 1, 'はやい': 1, 'たのしい': 1, '多い': 1, '新しい': 1, '古い': 1, '遅い': 1, '肌寒い': 1, 'ない': 1
'部': 44, 'アクシュカイ': 43, '名古屋': 36, '今日': 25, 'レーン': 16, '月': 15, '嬉しい': 12, 'さん': 11, '皆さん': 10, 'なごや': 9, '方': 9, '可愛い': 8, '日': 8, '券': 8, 'ちゃん': 7, 'ん': 7, '朝': 7, '当日': 7, '楽しい': 6, 'お願い': 6, 'の': 6, 'たくさん': 6, 'さ': 5, 'みなさん': 5, '個別': 5, '雨': 5, '私服': 5, '握手': 5, '時間': 5
'部': 44, 'アクシュカイ': 43, '名古屋': 36, 'する': 30, '今日': 25, 'くださる': 17, 'レーン': 16, '月': 15, '来る': 15, 'なる': 14, '嬉しい': 12, 'さん': 11, '皆さん': 10, 'てる': 10, 'なごや': 9, '方': 9, '可愛い': 8, '日': 8, '券': 8, 'くれる': 8, 'ちゃん': 7, 'ん': 7, '朝': 7, '当日': 7, 'すぎる': 7, 'いる': 7, '会う': 7, 

要約するとこんな感じです。

ポートメッセ 名古屋 握手会 朝 から 来 て くださっ た 方 ありがとう ござい  た ☺ ️ ✨ 朝 から な のに いっぱい 来 て くれ て 嬉しかっ た よ ~ 舞台 も いく ね ! って 待っ てる ね ?❤ ️ 9 / 7 の 15 時 〜 の チケット が まだ 沢山 余っ てる ので 皆さん 来 …
今日 は 、 名古屋 握手会 ありがとう ござい  た ?
本日 の 握手会 会場 。 薄着 し て た メンバー は 肌寒 さ から ブランケット を 羽織っ たり し て い た の  が 、 なぜ か 私 の レーン に は 扇風機 が ! ! 汗 っ かき な の ばれ てる 、 、 ! ( 笑 ) ( 今日 は 出番 は なかっ た  が ?

f:id:rimt:20180904230343p:plain

f:id:rimt:20180904230408p:plain