強化学習について

強化学習について調べた際のメモです。

  • 達成したいゴールがあるが、各行動に対する評価は与えられない場合
  • 「行動の選択肢」と「報酬」
  • 強化学習という名前は脳の学習メカニズムであるオペラント学習に由来する
  • 報酬は「連続した行動の結果」に対して与えられる(フィードバックに時間差がある)
  • 報酬から各行動に対する評価を自分で与える
  • ステップごとの評価を与えなくて良いので教師あり学習よりも複雑な問題を扱える
  • 学習に時間がかかる
  • 状態S、モデルT(状態遷移の関数)、行動A、報酬Rでモデル化 → Markov Decision Process (MDP)
  • 時間的な制約下で報酬を最大化するような、ポリシーπ(S → Aの関数)を獲得する問題
    • 時間割引をすることでローリスクな行動ばかり取ってしまうのを防ぐ
  • Value Iteration (幅優先探索?), Policy Iteration (深さ優先探索?)
  • Q値 = 状態行動価値 = 「状態 s で行動 a を選択後、ずっと最適政策をとりつづけるときの利得の期待値」
  • Sarsa
    • 実際行動してみて一番価値が大きい行動を探す
  • モンテカルロ法
    • 報酬が得られるまで行動してみて、報酬を分配して各行動を評価する
  • Q-Learning
    • Tが不要
    • 期待値(実際の報酬)と見込みの差分から学習する → TD学習
    • 「状態sを離散化した数」×「行動の種類」の表
    • 次の状態の価値を現在推定される値の最大値とする
  • 探索と活用のトレードオフ (exploration and exploitation dilemma)
    • ε-greedy法、Boltzmann分布を使った方法
  • Deep Q-Leaning (DQN)
    • 2013年、Deepmindのチームによる論文1
    • 行動と状態の組み合わせの爆発(状態行動空間の爆発) → ニューラルネットで近似
    • 入力は状態s、出力は行動aの分類
    • Q-LearningでのTD誤差を誤差関数とし、Qをニューラルネット(CNN)化する
    • Experience Replay, Fixed Target, 報酬のclipping
    • Prioritised Experience Replay (学習が進んでいない状態を優先して学習), Duelling DQN (Q関数を状態価値関数とAdvantage関数に分離)
    • Deep Recurrent Q-Network (DRQN)もある
  • A3C (Asynchronous Advantage Actor-Critic): 並列計算、Advantageによる報酬の計算、Actor-Critic
  • TRPO, Generalised Advantage Estimator, UNREAL, PPO

Refs

【追記】 - ゼロから始める深層強化学習(NLP2018講演資料)/ Introduction of Deep Reinforcement Learni…

GANについて

GAN(Generative Adversarial Network)について調べた際のメモです。

GANとは

  • Ian Goodfellow (@goodfellow_ian)が2014年に発表1
  • 生成モデル: 訓練データの分布と生成データの分布が同じになるよう学習
  • 訓練データに対してパラメータが小さく、重要な特徴を学習する
  • GeneratorとDiscriminatorに分かれる
  • Generatorは訓練データと同じようなデータを生成するように学習
  • Discriminatorは与えられたデータが訓練のものか、生成されたものかを見分けるように学習
  • Generatorの学習 → Discriminatorの学習 → Generatorの学習 → ...、とイタチごっこをする
    • Dの学習をk回繰り返したらGを1回学習する、の繰り返し(k=1が多い)
  • Generatorはノイズを元にデータを生成する
  • DCGAN(Deep Convolutional GAN): CNNを使ったモデル → 画像の生成 (元の論文2ではベッドルームの画像の生成)
    • プーリングしない、全結合しない、BatchNormalization、Leaky ReLU
  • LAPGAN, SRGAN, pix2pix, StackGAN, SimGAN, 3D-GAN, CycleGAN, DiscoGAN...
  • MSEだと平均的な(ぼやけた)画像になる(VAE)が、GANだとシャープな画像が生成できる
    • 4Kイメージを8Kにしたり
  • 生成した画像に和や差の演算ができる
  • Deep Learningで課題である「教師なし学習」「タグ無しデータ」を解決する可能性がある

Refs

VSCodeでのGOPATH設定

{
    "go.inferGopath": true,
    "go.gopath": "/your/path/for/specific/project"
}

この順番じゃないとダメ。逆にするとinferGopathがgopathを上書きしてしまう1っぽい。

やっぱり上書きされてしまった。ちゃんとgo.gopathを書いてあげないといけないみたい。。。

{
    "go.gopath": "${workspaceRoot}:${workspaceRoot}/vendor"
}

  1. 「Setting go.inferGopath overrides all of the above」– GOPATH in the VS Code Go extension · Microsoft/vscode-go Wiki · GitHub

Seq2Seqを使った英日翻訳機

RNN(Recurrent Neural Network)の実例の一つに翻訳機があります。 今回はkerasのRecurrentレイヤーを使い、Seq2Seq(Encoder-Decoder)モデルの英日翻訳機を実装してみます。

データセット

Kerasのexampleでは文字単位での英仏翻訳が行われていますが、今回は英日の翻訳なので田中コーパス1をデータセットとして使い、単語レベルでの翻訳を行います。

1.田中コーパスのダウンロード

$ wget ftp://ftp.monash.edu/pub/nihongo/examples.utf.gz
$ gunzip examples.utf.gz

2.整形

田中コーパスのデータは以下のようになっています。

$ head examples.utf
A: ムーリエルは20歳になりました。 Muiriel is 20 now.#ID=1282_4707
B: は 二十歳(はたち){20歳} になる[01]{になりました}
A: すぐに戻ります。 I will be back soon.#ID=1284_4709
B: 直ぐに{すぐに} 戻る{戻ります}
A: すぐに諦めて昼寝をするかも知れない。   I may give up soon and just nap instead.#ID=1300_4727
B: 直ぐに{すぐに} 諦める{諦めて} 昼寝 を 為る(する){する} かも知れない
A: 愛してる。  I love you.#ID=1434_4851
B: 愛する{愛してる}
A: ログアウトするんじゃなかったよ。 I shouldn't have logged off.#ID=1442_4858
B: ログアウト~ 為る(する){する} の{ん} だ{じゃなかった} よ[01]

このファイルに対し、1) Aの行を抜き出し 2) #以下を取り除いて 3) tabで区切り 4) それぞれ日本語(教師データ)・英語(学習データ)ファイルとして保存します。

(日本語の場合は一文字をデータの最小単位とするため、半角スペースを間にいれました。)

文章のベクトル化

入力である文章はそのままではモデルへの入力として使う事ができません。keras.preprocessing.text.Tokenizerクラスにより文章をベクトルに直します。 (文章の先頭と末尾には系列の先頭・終了を表すタグをつけておきます。)

from keras.preprocessing.text import Tokenizer

def load_dataset(file_path):
    tokenizer = Tokenizer(filters="")
    texts = []
    for line in open(file_path, 'r'):
        texts.append("<s> " + line.strip() + " </s>")

    tokenizer.fit_on_texts(texts)
    return tokenizer.texts_to_sequences(texts), tokenizer

train_X, tokenizer_e = load_dataset('tanaka_corpus_e.txt')
train_Y, tokenizer_j = load_dataset('tanaka_corpus_j.txt')

これにより、

  • <s> Muiriel is 20 now. </s>[1, 16504, 7, 1851, 170, 2]
  • <s> ム ー リ エ ル は 2 0 歳 に な り ま し た 。 </s>[2, 142, 38, 93, 328, 71, 4, 134, 106, 505, 8, 9, 33, 21, 11, 7, 1, 3]

と表現することができました。

モデル

 学習時

Encoderの入力にtrain_Xデータ、Decoderの入力にtrain_Yデータを使い、教師データとしては入力として用いたtrain_Yの一時刻先のデータを使います。

f:id:KK462:20180218020543j:plain

from keras.models import Model
from keras.layers import Input, Embedding, Dense, LSTM
emb_dim = 256
hid_dim = 256

## エンコーダ

encoder_inputs = Input(shape=(seqX_len,))
# Embed処理: https://keras.io/ja/layers/embeddings/
encoder_embedded = Embedding(word_num_e, emb_dim, mask_zero=True)(encoder_inputs)

encoder = LSTM(hid_dim, return_state=True) # 内部状態を返すよう、return_state=Trueとしておく
_, state_h, state_c = encoder(encoder_embedded) # outputsは捨てる

## デコーダ

decoder_inputs = Input(shape=(seqY_len,))
decoder_embedding = Embedding(word_num_j, emb_dim)
decoder_embedded = decoder_embedding(decoder_inputs)

decoder = LSTM(hid_dim, return_sequences=True, return_state=True) # 一入力ごとの出力を得るため、return_sequences=Trueとしておく
# initial_stateにencoder_statesを与えることでエンコーダとデコーダを繋ぐ
decoder_outputs, _, _ = decoder(decoder_embedded, initial_state=encoder_states)
decoder_dense = Dense(word_num_j, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)

model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy')

学習

## 教師データの用意
# 1. 各データ先頭を捨てる
next_inputs = train_Y[:, 1:]
# 2. 末尾を0でパディングする
decoder_target_data = np.hstack((next_inputs, np.zeros((len(train_Y),1), dtype=np.int32)))
# 3. 出力は(系列の数, 入力の数, 単語のベクトル)の3次元なので一次元上げる
decoder_target_data = np.expand_dims(decoder_target_data, -1)

model.fit([train_X, train_Y], decoder_target_data, batch_size=128, epochs=15, verbose=2, validation_split=0.2)

予測時

予測時はt-1時点でのデコーダの出力がtの入力となるため、RNNのループを手で回します。

f:id:KK462:20180218200616j:plain

モデル

# エンコーダ:最終的な状態を返す
encoder_model = Model(encoder_inputs, encoder_states)

decoder_state_input_h = Input(shape=hid_dim,)
decoder_state_input_c = Input(shape=hid_dim,)
decoder_state_inputs = [decoder_state_input_h, decoder_state_input_c]

decoder_inputs = Input(shape=(1,))
decoder_embedded = decoder_embedding(decoder_inputs)
decoder_outputs, state_h, state_c = decoder(
    decoder_embedded, initial_state=decoder_state_inputs
)
decoder_states = [state_h, state_c]
decoder_outputs = decoder_dense(decoder_outputs)
# デコーダ
decoder_model = Model(
    [decoder_inputs] + decoder_state_inputs, # t-1での出力・状態を受け取る
    [decoder_outputs] + decoder_states
)

出力

def decode_sequence(input_seq):
    # エンコーダから最終的な状態を得る
    states_value = encoder_model.predict(input_seq)
    bos_eos = tokenizer_j.texts_to_sequences(["<s>", "</s>"])
    # 最初の入力として先頭文字<s>を与える
    target_seq = np.array(bos_eos[0])
    output_seq = bos_eos[0]

    # ループを回す
    while True:
        # 前回の出力と状態ベクトルで予測
        output_tokens, h, c = decoder_model.predict(
            [target_seq] + states_value
        )
        sampled_token_index = [np.argmax(output_tokens[0, -1, :])]
        output_seq += sampled_token_index

        if (sampled_token_index == bos_eos[1] or len(output_seq) > 1000):
            break
        
        # 入力・状態の更新
        target_seq = np.array(sampled_token_index)
        states_value = [h, c]
    
    return output_seq

detokenizer_e = dict(map(reversed, tokenizer_e.word_index.items()))
detokenizer_j = dict(map(reversed, tokenizer_j.word_index.items()))
input_seq = pad_sequences([test_X[0]], seqX_len, padding='post')

print(' '.join([detokenizer_e[i] for i in test_X[0]])) # 英文
print(' '.join([detokenizer_j[i] for i in decode_sequence(input_seq)])) # 予測
print(' '.join([detokenizer_j[i] for i in test_Y[0]])) # 正解

結果

データセットを50,000にし、5エポック回してみます。

<s> he never makes a show of his learning. </s>
<s> 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は 彼 は
<s> 彼 は 決 し て 自 分 の 学 識 を 見 せ び ら か せ な い 。 </s>

うーん。。。

コード全体

Next Step

Ref

kerasを使ったreuter記事分類のexampleをなぞる

github.com

kerasにはデータセットが用意されています1

今回はその中からReuter記事データの分類をしてみます。 基本的にはこちらのexampleに示されているコードをなぞる形です。

kerasのインストール

pythonは3系を使います。

TensorFlowのインストール

keras自身のインストールに先立ち、バックエンドとして使用する機械学習ライブラリをインストールする必要があります。TensorFlowやTheano,CNTKなどから選ぶ事ができますが2、今回はTensorFlowを利用します。

$ sudo apt-get install python3-pip python3-dev
$ sudo pip3 install tensorflow

kerasのインストール

$ sudo pip3 install keras

Reuter記事の分類

データセットのロード

from keras.datasets import reuters
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=1000, test_split=0.2)

記事のデータセットエンコードされ、配列になっています。それぞれの単語にはデータセット全体の中で頻度の多い方から順にインデックス番号が振られています。

x_train.shape # (8982,)
x_train[0][:10] # [1, 4, 2, 2, 9, 697, 2, 111, 8, 25]

引数のnum_wordsは最頻出上位○○位の値、test_splitはtrainデータとtestデータの割合(今回は4:1)を指定しています。

データの前処理

入力データ(x)は配列から行列の表現に変換する必要があります。

from keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(num_words=1000)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test  = tokenizer.sequences_to_matrix(x_test, mode='binary')

tokenizerのイニシャライザには先ほどと同じ値を指定します。sequences_to_matrixmodeにbinaryを指定すると、文中にインデックス番号の単語が含まれていれば1、そうでなければ0が値となるベクトルに変換されます。

教師データ(y)はone-hot表現に変換します。

from keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test  = to_categorical(y_test)

モデルの構築

exampleよりもシンプルなモデルにします。出力のサイズ(分類の数)は46です。

from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(Dense(512, input_shape=(1000,), activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(128, activation='relu'))
model.add(Dense(46, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

学習

model.fit(x_train, y_train, batch_size=32, epochs=5, verbose=2, validation_data=(x_test, y_test))

(メモ: GCEのn1-standard-1タイプインスタンスで各エポックあたり6s)

評価

score = model.evaluate(x_test, y_test, verbose=0)
score[0] # 損失関数の値: 1.0874391948125879
score[1] # 正答率: 0.7738201247191493

コード全体 → reuter_mnist.py · GitHub

疑問

  • modeの値にbinary以外を指定するとどうなるんだろう...

Refs

publickeyを使ったsshの設定

1.鍵を用意する

$ ssh-keygen -t rsa

2.公開鍵をインスタンス上の ~/.ssh/authorized_keys に追記する

$ cat ~/.ssh/ssh_test.pub | ssh ([USER]@)[HOSTNAME] "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys"

3.秘密鍵を使ってsshする

$ ssh -i ~/.ssh/ssh_test ([USER]@)[HOSTNAME]

4.~/.ssh/config を設定する

Host ssh-test
         HostName [HOSTNAME]
         User [USER]
         IdentityFile /path/to/home/.ssh/ssh_test
$ ssh ssh-test

Refs

Solrについて

Solrとは

Solr is the popular, blazing-fast, open source enterprise search platform built on Apache Lucene™.

Apache Solr -

こちらの記事が要点を抑えていてわかりやすいです。

qiita.com

形態素解析

文章を単語ごとに分解する方法。

例:「東京特許許可局」 →「東京」「特許」「許可局」

意味のわからないトークンが作られない一方、例えば「京特」で調べた時に引っかからないという事が起こる。

N-gram

文章をN文字で機械的に分解する方法。

例:「東京特許許可局」N=2 →「東京」「京特」「特許」「許許」「許可」「可局」

検索する文字に対して漏れなく結果を返せる反面、それが全く関連のないノイズとなりえる。

転置インデックス

データソースとトークンの間のM:N関係。

動かしてみる

こちらの記事を参考にdockerで動かしてみます。

$ docker pull solr
$ docker run -d --name=solr -p8983:8983 solr

NOTE: docker run をすると8983ポートでsolrが起動します。もし以下のTutorialのようにSolrCloudを試したい場合は違うポートを繋いでおき、コンテナ内で別プロセスを立ち上げるのが楽です。またその後任意のコマンドを実行する際にはポートを指定する必要があるので注意。

疑問

  • Schemaの書き方
  • 検索の適合度
  • 地理検索機能の実例

Refs