ichou1のブログ

主に音声認識、時々、データ分析のことを書く

Kerasメモ(VAE)

変分オートエンコーダ(Variational Autoencoder)を使ったイメージ生成を試してみる。


https://www.oreilly.co.jp/books/images/picture978-4-87311-857-4.gif
「RとKerasによるディープラーニング」

モデル

f:id:ichou1:20190321114042p:plain

6epochのトレーニングが終わった状態のdecoderからイメージを生成してみる。

latent space(潜在的意味空間)の次元は「2」で、
latent space上の「点」はppf(inverse CDF)から生成したものを渡す。

f:id:ichou1:20190323094613p:plain

n <- 15            # Number of rows / columns of digits
grid_x <- qnorm(seq(0.05, 0.95, length.out = n))
grid_y <- qnorm(seq(0.05, 0.95, length.out = n))

qnorm(seq(0.05, 0.95, length.out = 15))
 [1] -1.644854e+00 -1.204047e+00 -9.208230e-01 -6.971414e-01 -5.039654e-01
 [6] -3.280721e-01 -1.618442e-01 -1.391458e-16  1.618442e-01  3.280721e-01
[11]  5.039654e-01  6.971414e-01  9.208230e-01  1.204047e+00  1.644854e+00

(python版)

from scipy.stats import norm
n = 15  # figure with 15x15 digits
grid_x = norm.ppf(np.linspace(0.05, 0.95, n))
grid_y = norm.ppf(np.linspace(0.05, 0.95, n))

norm.ppf(np.linspace(0.05, 0.95, 15))
array([-1.64485363e+00, -1.20404696e+00, -9.20822976e-01, -6.97141435e-01,
       -5.03965367e-01, -3.28072108e-01, -1.61844167e-01, -1.39145821e-16,
        1.61844167e-01,  3.28072108e-01,  5.03965367e-01,  6.97141435e-01,
        9.20822976e-01,  1.20404696e+00,  1.64485363e+00])

f:id:ichou1:20190323110233p:plain

Kerasメモ(時系列データの学習サンプル生成)

https://www.oreilly.co.jp/books/images/picture978-4-87311-857-4.gif
「RとKerasによるディープラーニング」

イエナ天候データセットから学習用の入力データを生成する部分のメモ。

このデータセットは、10分ごとに、気温や気圧といった14種類の数値を記録したものになる。
(1時間で6サンプル、1日で144サンプル集まる)

1時間おき(step=6)に、10日分(lookback=1440)の学習データを生成する場合、以下のようにgeneratorを指定する。
ここで、1時間おきにとしているのは、ある時点(t)の数値と、10分後(t+1)の数値が、ほぼ似通っていることによる。
10日分で240個(=1440 / 6)の時系列データが得られる。

train_gen <- generator(
  data,
  lookback = 1440,  # observations will go back 10 days
  delay = 144,      # targets will be 24 hours in the future
  min_index = 1,
  max_index = 10000,
  shuffle = FALSE,
  step = 6,         # observations will be sampled at one data point per hour
  batch_size = 128
)
コード全体

deep-learning-with-r-notebooks/6.3-advanced-usage-of-recurrent-neural-networks.Rmd at master · jjallaire/deep-learning-with-r-notebooks · GitHub

元データのままでは確認しにくいので、検証用のデータを作成する。
データ数(行数)は1万、1列目は1から、2列目は10001から1刻みで増加。

data <- data.matrix(cbind(seq(1, 10000, 1), seq(10001, 20000, 1)))
> dim(data)
[1] 10000     2

> data
          [,1]  [,2]
    [1,]     1 10001
    [2,]     2 10002
    [3,]     3 10003
    [4,]     4 10004
    [5,]     5 10005
...
 [9996,]  9996 19996
 [9997,]  9997 19997
 [9998,]  9998 19998
 [9999,]  9999 19999
[10000,] 10000 20000

1回目のgenerator実行により得られた出力。

samples(入力サンプル)

テンソルの形状は、(batch_size, timesteps, input_features)=(128, 240, 2)

, , 1
       [,1] [,2] [,3] ... [,238] [,239] [,240]
  [1,]    1    7   13 ...   1427   1433   1440
  [2,]    2    8   14 ...   1428   1434   1441
  [3,]    3    9   15 ...   1429   1435   1442
 ...                  ...
[126,]  126  132  138 ...   1552   1558   1565
[127,]  127  133  139 ...   1553   1559   1566
[128,]  128  134  140 ...   1554   1560   1567

, , 2
        [,1]  [,2]  [,3] ... [,238] [,239] [,240]
  [1,] 10001 10007 10013 ...  11427  11433  11440
  [2,] 10002 10008 10014 ...  11428  11434  11441
  [3,] 10003 10009 10015 ...  11429  11435  11442
 ...                     ...
[126,] 10126 10132 10138 ...  11552  11558  11565
[127,] 10127 10133 10139 ...  11553  11559  11566
[128,] 10128 10134 10140 ...  11554  11560  11567

239列目と240列目の差分が「6」(=step)ではなく、「7」になっているのは、seq関数出力の小数点以下を切り捨てていることによる。

> seq(1, 1440, length.out=240)
  [1]    1.000000    7.020921   13.041841   19.062762   25.083682   31.104603
 ...
[235] 1409.895397 1415.916318 1421.937238 1427.958159 1433.979079 1440.000000
targets(ラベル)

テンソルの形状は、(batch_size, output_features)=(128, 1)
144サンプル後(=1日後)の2列目の値が該当する。

  [1] 11585
  [2] 11586
  [3] 11587
 ...  
[126] 11710
[127] 11711
[128] 11712

ここでは、1日と10分(=144+1)後の値になっているので、以下のコードを

targets[[j]] <- data[rows[[j]] + delay, 2]

下記のとおり修正すればよい。

targets[[j]] <- data[rows[[j]] + delay - 1, 2]


"shuffle = TRUE"にしてgeneratorを実行した場合の出力は以下のとおり。

samples(入力サンプル)
, , 1
       [,1] [,2] [,3] ... [,238] [,239] [,240]
  [1,] 2404 2410 2416 ...   3830   3836   3843
  [2,] 5336 5342 5348 ...   6762   6768   6775
  [3,] 2132 2138 2144 ...   3558   3564   3571
 ...                  ... 
[126,] 3071 3077 3083 ...   4497   4503   4510
[127,] 1617 1623 1629 ...   3043   3049   3056
[128,] 1432 1438 1444 ...   2858   2864   2871

, , 2
        [,1]  [,2]  [,3] ... [,238] [,239] [,240]
  [1,] 12404 12410 12416 ...  13830  13836  13843
  [2,] 15336 15342 15348 ...  16762  16768  16775
  [3,] 12132 12138 12144 ...  13558  13564  13571
 ...                     ... 
[126,] 13071 13077 13083 ...  14497  14503  14510 
[127,] 11617 11623 11629 ...  13043  13049  13056
[128,] 11432 11438 11444 ...  12858  12864  12871
targets(ラベル)
  [1] 13988
  [2] 16920
  [3] 13716
...
[126] 14655
[127] 13201
[128] 13016


開始位置となるindexをシャッフルしているのであり、時系列データは保たれる。

shuffle = TRUE
rows <- sample(c((min_index+lookback):max_index), size = batch_size)
shuffle = FALSE
rows <- c(i:min(i+batch_size-1, max_index))

Kerasメモ(Embeddingレイヤ) その3

https://www.oreilly.co.jp/books/images/picture978-4-87311-857-4.gif
「RとKerasによるディープラーニング」

書籍を読んでいて、コードの誤りと思われる部分があったのでメモしておく。

例6-11 GloVe 単語埋め込み行列の準備
embedding_dim <- 100

embedding_matrix <- array(0, c(max_words, embedding_dim))

for (word in names(word_index)) {
  index <- word_index[[word]]
  if (index < max_words) {
    embedding_vector <- embeddings_index[[word]]
    if (!is.null(embedding_vector))
      # Words not found in the embedding index will be all zeros.
      embedding_matrix[index+1,] <- embedding_vector
  }
}
コード全体

deep-learning-with-r-notebooks/6.1-using-word-embeddings.Rmd at master · jjallaire/deep-learning-with-r-notebooks · GitHub

以下の箇所は

embedding_matrix[index+1,] <- embedding_vector

正しくは

embedding_matrix[index,] <- embedding_vector

と思われる。
(Rでの添字は1始まりなので)

word_indexを生成している部分の確認。

tokenizer <- text_tokenizer(num_words=max_words) %>% 
    fit_text_tokenizer(texts)

word_index <- tokenizer$word_index

中身を確認してみる。

> tokenizer$index_word[0]
named list()

> tokenizer$index_word[1]
$`1`
[1] "the"

確認。

> max_words <- 50
> embedding_dim <- 100
> embedding_matrix <- array(0, c(max_words, embedding_dim))
> dim(embedding_matrix)
[1]  50 100

> embedding_matrix[1,]
  [1] 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 0
 [38] 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 0
 [75] 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

> embedding_matrix[0,]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
     [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
     [,27] [,28] [,29] [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38]
     [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50]
     [,51] [,52] [,53] [,54] [,55] [,56] [,57] [,58] [,59] [,60] [,61] [,62]
     [,63] [,64] [,65] [,66] [,67] [,68] [,69] [,70] [,71] [,72] [,73] [,74]
     [,75] [,76] [,77] [,78] [,79] [,80] [,81] [,82] [,83] [,84] [,85] [,86]
     [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98]
     [,99] [,100]

以下のブログによると、
R言語でディープラーニング — パロアルトインサイト

元々はpythonで書かれたコードをRに書き換えた模様。

Allaire氏はKerasの開発者であるFrancois Chollet氏と共著で「Deep Learning with R」という本を出していますが、これはChollet氏が既に出版した「Deep Learning with Python」という本をベースにコードを全てRに書き換えただけのものと説明していました。

Kerasメモ(Embeddingレイヤ) その2

既に学習済みの単語埋め込みベクトルを使ってみる。
Using pre-trained word embeddings in a Keras model

今回はGloVe word embeddingsを選択。

Specifically, we will use the 100-dimensional GloVe embeddings of 400k words computed on a 2014 dump of English Wikipedia.

2014年英語版Wikipediaをもとに、40万語の単語(またはドットやカンマといったトークン)を100次元で表現したものになる。

ダウンロードサイズは1GB近い。

/% du -h glove.6B.zip         
823M	glove.6B.zip

100次元だけでなく、50次元や200次元で表現したバージョンも入っている。

% zipinfo glove.6B.zip
Archive:  glove.6B.zip
Zip file size: 862182613 bytes, number of entries: 4
-rw-rw-r--  3.0 unx 171350079 tx defN 14-Aug-05 05:15 glove.6B.50d.txt
-rw-rw-r--  3.0 unx 347116733 tx defN 14-Aug-05 05:14 glove.6B.100d.txt
-rw-rw-r--  3.0 unx 693432828 tx defN 14-Aug-05 05:14 glove.6B.200d.txt
-rw-rw-r--  3.0 unx 1037962819 tx defN 14-Aug-28 04:19 glove.6B.300d.txt
4 files, 2249862459 bytes uncompressed, 862181945 bytes compressed:  61.7%

% du -h glove.6B.100d.txt 
332M	glove.6B.100d.txt

ファイルの中身

The -0.038194 -0.24487 0.72812 -0.39961 0.083172 ...
, -0.10767 0.11053 0.59812 -0.54361 0.67396 ...
. -0.33979 0.20941 0.46348 -0.64792 -0.38377 ...
of -0.1529 -0.24279 0.89837 0.16996 0.53516 ...
to -0.1897 0.050024 0.19084 -0.049184 -0.089737 ... 
and -0.071953 0.23127 0.023731 -0.50638 0.33923 ...
in 0.085703 -0.22201 0.16569 0.13373 0.38239 ...
a -0.27086 0.044006 -0.02026 -0.17395 0.6444 ...
" -0.30457 -0.23645 0.17576 -0.72854 -0.28343 ...
's 0.58854 -0.2025 0.73479 -0.68338 -0.19675 ...
...

ファイル読み込み(pythonでの記述、冒頭のThe Keras Blogより抜粋)

embeddings_index = {}
f = open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'))
for line in f:
    values = line.split()
    word = values[0]
    coefs = np.asarray(values[1:], dtype='float32')
    embeddings_index[word] = coefs
f.close()

環境はpython2.7

>>> type(embeddings_index)
<type 'dict'>

>>> len(embeddings_index)
400000

>>> sys.getsizeof(embeddings_index)
12583048

# from pympler import asizeof
>>> asizeof.asizeof(embeddings_index)
204712744L

# 大文字は小文字に変換される
>>> embeddings_index['The']
*** KeyError: 'The'

>>> embeddings_index['the'].shape
(100,)

>>> embeddings_index['the']
array([-0.038194, -0.24487 ,  0.72812 , -0.39961 ,  0.083172,  0.043953,
       ...
       -0.52028 , -0.1459  ,  0.8278  ,  0.27062 ], dtype=float32)

メモリ上の読み込みサイズは、ざっと195MB。
読み込んだ辞書(embeddings_index)をもとに、単語埋め込みベクトルを作成する。

EMBEDDING_DIM = 100
# tokenizer.word_index : type 'dict'
# tokenizer.word_index : 298
embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
for word, i in word_index.items():
    embedding_vector = embeddings_index.get(word)
    if embedding_vector is not None:
        # words not found in embedding index will be all-zeros.
        embedding_matrix[i] = embedding_vector

Tokenizerクラスで作った辞書(word_index)のindexは1始まりなので、行方向のサイズはプラス1とする(index=0は使わない)

トークン'the'(index=1)の例
>>> tokenizer.word_index['the']
1

>>> embeddings_index['the'][:5]
array([-0.038194, -0.24487 ,  0.72812 , -0.39961 ,  0.083172],
      dtype=float32)

>>> embedding_matrix[1][:5]
array([-0.038194  , -0.24487001,  0.72812003, -0.39961001,  0.083172  ])

単語埋め込みベクトルのサイズ。

>>> embedding_matrix.shape
(299, 100)

>>> type(embedding_matrix[0][0])
<type 'numpy.float64'>

>>> sys.getsizeof(embedding_matrix)
239256L

>>> asizeof.asizeof(embedding_matrix)
239256L

>>> print asizeof.asized(embedding_matrix, detail=1).format()
array([[ 0.        ,  0.        ,  0. ....9,
         0.61399001, -0.052185  ]]) size=239256 flat=239256

これをEmbeddingレイヤに読み込む。
学習しないので、trainable=Falseを設定する。

embedding_layer = Embedding(len(word_index) + 1,
                            EMBEDDING_DIM,
                            weights=[embedding_matrix],
                            trainable=False)

Kerasメモ(テキストデータ操作)

https://www.oreilly.co.jp/books/images/picture978-4-87311-857-4.gif
「RとKerasによるディープラーニング」より)


未加工のIMDBデータセットを学習用に準備する操作のメモ。

データセットは、ラベル(pos : 好意的/ neg : 否定的)ごとのフォルダにレビュー記事が格納されている。

aclImdb/train/pos : 12,500記事
aclImdb/train/neg : 12,500記事

aclImdb/test/neg : 12,500記事
aclImdb/test/neg : 12,500記事

データ例(aclImdb/train/pos/0_9.txt)
Bromwell High is a cartoon comedy. It ran at the same time as some other programs about school life, such as "Teachers". (snip)
データ例(aclImdb/train/pos/10000_8.txt)

HTMLタグが入っている。

Homelessness (or Houselessness as George Carlin stated) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. (snip) if they'll be next to end up on the streets.<br /><br />But (snip)

今回は2ファイルだけを読み込むとする。

imdb_dir <- "~/Downloads/aclImdb"
train_dir <- file.path(imdb_dir, "train")

file_list_pos <- c("0_9.txt", "10000_8.txt")

labels <- c()
texts <- c()

for (label_type in c("pos")) {
    label <- switch(label_type, pos = 1)
    dir_name <- file.path(train_dir, label_type)
    for (fname in file_list_pos) {
        fname <- file.path(dir_name, fname)
        texts <- c(texts, readChar(fname, file.info(fname)$size))
        labels <- c(labels, label)
    }
}

読み込まれたデータを確認。
indexは1から始まる。

> length(texts)
[1] 2

> texts[0]
character(0)

> texts[1]
[1] "Bromwell High is a cartoon comedy. It ran at the same time ...."
> nchar(texts[1])
[1] 806

> texts[2]
[1] "Homelessness (or Houselessness as George Carlin stated) ..."
> nchar(texts[2])
[1] 2366

テキスト内の単語をベクトル化する。

# データセット内の最頻出50語だけ
tokenizer <- text_tokenizer(num_words=50) %>% 
    fit_text_tokenizer(texts)

word_index <- tokenizer$word_index
# cat("Found", length(word_index), "unique tokens.\n")
# Found 298 unique tokens.

テキストをシーケンスに変換する。

sequences <- texts_to_sequences(tokenizer, texts)

変換結果の確認。

> sequences[1]
[[1]]
 [1] 21 22 10  3 45 11 46  1  8 32 47 33 48 34  8 35 49 23  1  2 12 21 10  2 10
[26] 35  1  2  1  9 36 37 38  1  5  1  5  1 15 16 38 39 15  1 23  3  2  1 33 15
[51] 46 22  3  2  5 35  2 21 22 15 12  5 49 12 21 22 10 13  3 12 11

> length(sequences[[1]])
[1] 71

indexをwordに変換してみる。

# tokenizer$index_word[index]
# original : 
#  Bromwell High is a cartoon comedy. It ran at the same time as some ...
bromwell high is a comedy it at the ...

頻度上位num_wordsの単語以外は読み飛ばされる。
UNKOWNに置き換えてくれるわけではない。

# 記事の11語以降を切り捨て
# truncatingは"pre"がdefault
> data <- pad_sequences(sequences, maxlen=10, truncating="post")
> data[1,]
 [1] 21 22 10  3 45 11 46  1  8 32

これがトレーニング用のインプットになる。

# シャッフル
indices <- sample(1:2)
> indices
[1] 2 1

x_train <- data[indices,]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    6    8   17    3    2    4    1   12   24     9
[2,]   21   22   10    3   45   11   46    1    8    32

> dim(x_train)
[1]  2 10

Kerasメモ(seq2seqで日付フォーマット変換) その2(Attention)

前回の続き。

Kerasで書いたモデルにAttentionレイヤを追加してみる。
カスタムレイヤを定義して、書籍のコードを移植。

Encoder側のLSTMレイヤについては、"return_sequences=True"とし、各timestepごとの出力を返すようにする。

encoder = LSTM(units=n_units,
               return_sequences=True,
               return_state=True) 
学習モデル

f:id:ichou1:20190226211645p:plain

推論モデル

f:id:ichou1:20190226211207p:plain

うまく動いているか、確認。
まずは、損失の比較から。
左がAttentionレイヤ無しのモデル、右がAttentionレイヤ有りのモデルでの損失。
f:id:ichou1:20190223111242p:plain


続いて、横軸にInput sequence、縦軸にOutput sequenceをとった"Attention Map"と呼ばれるもので比較。
左が書籍のコードで生成、右が今回作ったコードで生成した"Attention Map"
f:id:ichou1:20190225213820p:plain

作りがよくないせいなのか、関連付けてほしくないところで反応が出てしまっている(赤枠)
また、書籍のコードのように、横に広がるように少しずつ反応が出るほうが好ましいと考える(青枠)

他のパターンでも同じような出力だった。
f:id:ichou1:20190225215652p:plain
f:id:ichou1:20190225215914p:plain

改善方法はこれから確認するとして、ひとまず記録として残しておく。

Kerasメモ(seq2seqで日付フォーマット変換)

前回の足し算に引き続き、以下の書籍を参考にして、seq2seqを使った日付フォーマット変換を試してみる。

https://www.oreilly.co.jp/books/images/picture978-4-87311-836-9.gif
O'Reilly Japan - ゼロから作るDeep Learning ❷

データの長さは固定で「40」、値がない部分はブランクで埋める。

  • 変換前の長さ : 「29」固定
  • 変換後の長さ : 「11」固定(区切りにあたる"_"含む)
データ例(date.txt)
10/15/94                     _1994-10-15
thursday, november 13, 2008  _2008-11-13
july 18, 1970                _1970-07-18
...

サンプル数は全体で「50,000」、学習用に90%、残り10%を検証用に分割する。

出現する文字は59種類。
数字の「0から9」、英字、プラス記号、ブランク、アンダーバー、スラッシュ等。

Embeddingレイヤを通して16次元の特徴ベクトルに変換する。

書籍ではAttentionレイヤを使った例を扱っているが、ひとまず、使わないモデルを試してみる。
構成は前回の足し算の時と同じ。
LSTMレイヤのユニット数は「256」に増えている。

学習モデル

f:id:ichou1:20190222223006p:plain

推論モデル

f:id:ichou1:20190222224206p:plain

学習時のパラメータを以下のとおり設定。

batch_size = 128  # Batch size for training.
epochs = 15  # Number of epochs to train for.
train_model.compile(optimizer='adam', loss='categorical_crossentropy')

モデルの損失は以下のとおり推移。
f:id:ichou1:20190219080645p:plain
f:id:ichou1:20190219080818p:plain

学習後、検証データを推論モデルに渡した結果の一部。3番目は誤りパターン。

例1

input
Input sentence: ['1', '0', '/', '1', '5', '/', '9', '4', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
decode結果と正解
Decoded sentence: ['1', '9', '9', '4', '-', '1', '0', '-', '1', '5']
Label sentence:   ['1', '9', '9', '4', '-', '1', '0', '-', '1', '5']

例2

input
Input sentence: ['t', 'h', 'u', 'r', 's', 'd', 'a', 'y', ',', ' ', 'n', 'o', 'v', 'e', 'm', 'b', 'e', 'r', ' ', '1', '3', ',', ' ', '2', '0', '0', '8', ' ', ' ']
decode結果と正解
Decoded sentence: ['2', '0', '0', '8', '-', '1', '1', '-', '1', '3']
Label sentence:   ['2', '0', '0', '8', '-', '1', '1', '-', '1', '3']

例3

input
Input sentence: ['S', 'a', 't', 'u', 'r', 'd', 'a', 'y', ',', ' ', 'J', 'u', 'l', 'y', ' ', '1', '8', ',', ' ', '1', '9', '7', '0', ' ', ' ', ' ', ' ', ' ', ' ']
decode結果と正解
Decoded sentence: ['1', '9', '7', '0', '-', '0', '7', '-', '1', '4']
Label sentence:   ['1', '9', '7', '0', '-', '0', '7', '-', '1', '8']