Kerasメモ(VAE)
変分オートエンコーダ(Variational Autoencoder)を使ったイメージ生成を試してみる。
モデル
6epochのトレーニングが終わった状態のdecoderからイメージを生成してみる。
latent space(潜在的意味空間)の次元は「2」で、
latent space上の「点」はppf(inverse CDF)から生成したものを渡す。
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])
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 )
コード全体
元データのままでは確認しにくいので、検証用のデータを作成する。
データ数(行数)は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
書籍を読んでいて、コードの誤りと思われる部分があったのでメモしておく。
例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 } }
コード全体
以下の箇所は
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メモ(テキストデータ操作)
未加工の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)
学習モデル
推論モデル
うまく動いているか、確認。
まずは、損失の比較から。
左がAttentionレイヤ無しのモデル、右がAttentionレイヤ有りのモデルでの損失。
続いて、横軸にInput sequence、縦軸にOutput sequenceをとった"Attention Map"と呼ばれるもので比較。
左が書籍のコードで生成、右が今回作ったコードで生成した"Attention Map"
作りがよくないせいなのか、関連付けてほしくないところで反応が出てしまっている(赤枠)
また、書籍のコードのように、横に広がるように少しずつ反応が出るほうが好ましいと考える(青枠)
他のパターンでも同じような出力だった。
改善方法はこれから確認するとして、ひとまず記録として残しておく。
Kerasメモ(seq2seqで日付フォーマット変換)
前回の足し算に引き続き、以下の書籍を参考にして、seq2seqを使った日付フォーマット変換を試してみる。
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」に増えている。
学習モデル
推論モデル
学習時のパラメータを以下のとおり設定。
batch_size = 128 # Batch size for training. epochs = 15 # Number of epochs to train for. train_model.compile(optimizer='adam', loss='categorical_crossentropy')
モデルの損失は以下のとおり推移。
学習後、検証データを推論モデルに渡した結果の一部。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']