Tensorflow解説、映画レビューのテキスト分類
映画レビューのテキスト分類
上記の記事を読んでつまづいたところのメモです。
imdb.load_data()とは?
imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
IMDBというのは映画のレビューデータを、1(ポジティブ)と0(ネガティブ)に分類したデータセットです。
別のチュートリアル(Text classification with TensorFlow Hub: Movie reviews
)では、下記のようなコードでデータを読み込んでいました。
import tensorflow_datasets as tfds
train_data, validation_data, test_data = tfds.load(
name="imdb_reviews",
split=('train[:60%]', 'train[60%:]', 'test'),
as_supervised=True)
複数の書き方がありややこしいですが、IMDBのデータを読み込んでいるという点では同じです。
num_words=10000とは?
imdb.load_data()の引数にnum_words=10000とあります。
これはレビューデータに登場数する頻出後1万語だけを採用するという意味です。
頻出後以外の単語はOOV(Out Of Vocabulary)として分類されます。
「OOV」は「その他」みたいな扱いです。
train_dataの中身
imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
imdb.load_data(num_words=10000)
で読み込んだ
train_data
の中身を見てみます。
train_data
array([list([1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]),
list([1, 194, 1153, 194, 8255, 78, 228, 5, 6, 1463, 4369, 5012, 134, 26, 4, 715, 8, 118, 1634, 14, 394, 20, 13, 119, 954, 189, 102, 5, 207, 110, 3103, 21, 14, 69, 188, 8, 30, 23, 7, 4, 249, 126, 93, 4, 114, 9, 2300, 1523, 5, 647, 4, 116, 9, 35, 8163, 4, 229, 9, 340, 1322, 4, 118, 9, 4, 130, 4901, 19, 4, 1002, 5, 89, 29, 952, 46, 37, 4, 455, 9, 45, 43, 38, 1543, 1905, 398, 4, 1649, 26, 6853, 5, 163, 11, 3215, 2, 4, 1153, 9, 194, 775, 7, 8255, 2, 349, 2637, 148, 605, 2, 8003, 15, 123, 125, 68, 2, 6853, 15, 349, 165, 4362, 98, 5, 4, 228, 9, 43, 2, 1157, 15, 299, 120, 5, 120, 174, 11, 220, 175, 136, 50, 9, 4373, 228, 8255, 5, 2, 656, 245, 2350, 5, 4, 9837, 131, 152, 491, 18, 2, 32, 7464, 1212, 14, 9, 6, 371, 78, 22, 625, 64, 1382, 9, 8, 168, 145, 23, 4, 1690, 15, 16, 4, 1355, 5, 28, 6, 52, 154, 462, 33, 89, 78, 285, 16, 145, 95]),
list([1, 14, 47, 8, 30, 31, 7, 4, 249, 108, 7, 4, 5974, 54, 61, 369, 13, 71, 149, 14, 22, 112, 4, 2401, 311, 12, 16, 3711, 33, 75, 43, 1829, 296, 4, 86, 320, 35, 534, 19, 263, 4821, 1301, 4, 1873, 33, 89, 78, 12, 66, 16, 4, 360, 7, 4, 58, 316, 334, 11, 4, 1716, 43, 645, 662, 8, 257, 85, 1200, 42, 1228, 2578, 83, 68, 3912, 15, 36, 165, 1539, 278, 36, 69, 2, 780, 8, 106, 14, 6905, 1338, 18, 6, 22, 12, 215, 28, 610, 40, 6, 87, 326, 23, 2300, 21, 23, 22, 12, 272, 40, 57, 31, 11, 4, 22, 47, 6, 2307, 51, 9, 170, 23, 595, 116, 595, 1352, 13, 191, 79, 638, 89, 2, 14, 9, 8, 106, 607, 624, 35, 534, 6, 227, 7, 129, 113]),
...,
list([1, 11, 6, 230, 245, 6401, 9, 6, 1225, 446, 2, 45, 2174, 84, 8322, 4007, 21, 4, 912, 84, 2, 325, 725, 134, 2, 1715, 84, 5, 36, 28, 57, 1099, 21, 8, 140, 8, 703, 5, 2, 84, 56, 18, 1644, 14, 9, 31, 7, 4, 9406, 1209, 2295, 2, 1008, 18, 6, 20, 207, 110, 563, 12, 8, 2901, 2, 8, 97, 6, 20, 53, 4767, 74, 4, 460, 364, 1273, 29, 270, 11, 960, 108, 45, 40, 29, 2961, 395, 11, 6, 4065, 500, 7, 2, 89, 364, 70, 29, 140, 4, 64, 4780, 11, 4, 2678, 26, 178, 4, 529, 443, 2, 5, 27, 710, 117, 2, 8123, 165, 47, 84, 37, 131, 818, 14, 595, 10, 10, 61, 1242, 1209, 10, 10, 288, 2260, 1702, 34, 2901, 2, 4, 65, 496, 4, 231, 7, 790, 5, 6, 320, 234, 2766, 234, 1119, 1574, 7, 496, 4, 139, 929, 2901, 2, 7750, 5, 4241, 18, 4, 8497, 2, 250, 11, 1818, 7561, 4, 4217, 5408, 747, 1115, 372, 1890, 1006, 541, 9303, 7, 4, 59, 2, 4, 3586, 2]),
list([1, 1446, 7079, 69, 72, 3305, 13, 610, 930, 8, 12, 582, 23, 5, 16, 484, 685, 54, 349, 11, 4120, 2959, 45, 58, 1466, 13, 197, 12, 16, 43, 23, 2, 5, 62, 30, 145, 402, 11, 4131, 51, 575, 32, 61, 369, 71, 66, 770, 12, 1054, 75, 100, 2198, 8, 4, 105, 37, 69, 147, 712, 75, 3543, 44, 257, 390, 5, 69, 263, 514, 105, 50, 286, 1814, 23, 4, 123, 13, 161, 40, 5, 421, 4, 116, 16, 897, 13, 2, 40, 319, 5872, 112, 6700, 11, 4803, 121, 25, 70, 3468, 4, 719, 3798, 13, 18, 31, 62, 40, 8, 7200, 4, 2, 7, 14, 123, 5, 942, 25, 8, 721, 12, 145, 5, 202, 12, 160, 580, 202, 12, 6, 52, 58, 2, 92, 401, 728, 12, 39, 14, 251, 8, 15, 251, 5, 2, 12, 38, 84, 80, 124, 12, 9, 23]),
list([1, 17, 6, 194, 337, 7, 4, 204, 22, 45, 254, 8, 106, 14, 123, 4, 2, 270, 2, 5, 2, 2, 732, 2098, 101, 405, 39, 14, 1034, 4, 1310, 9, 115, 50, 305, 12, 47, 4, 168, 5, 235, 7, 38, 111, 699, 102, 7, 4, 4039, 9245, 9, 24, 6, 78, 1099, 17, 2345, 2, 21, 27, 9685, 6139, 5, 2, 1603, 92, 1183, 4, 1310, 7, 4, 204, 42, 97, 90, 35, 221, 109, 29, 127, 27, 118, 8, 97, 12, 157, 21, 6789, 2, 9, 6, 66, 78, 1099, 4, 631, 1191, 5, 2642, 272, 191, 1070, 6, 7585, 8, 2197, 2, 2, 544, 5, 383, 1271, 848, 1468, 2, 497, 2, 8, 1597, 8778, 2, 21, 60, 27, 239, 9, 43, 8368, 209, 405, 10, 10, 12, 764, 40, 4, 248, 20, 12, 16, 5, 174, 1791, 72, 7, 51, 6, 1739, 22, 4, 204, 131, 9])],
dtype=object)
中身は上記の通り。
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]
こういう感じの配列が1万5千件並んでいます。
"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <UNK> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"
前述の数字の羅列を単語に戻すとこんな感じ。
1が<START>(文章の始まりを表す特別な記号)、
14がthis、
22がfilm、
という感じで数字と単語が1対1で対応しています。
文章中によく登場するtheという単語は4の数字が割り当てられています。
同じ単語は毎回同じ数字に置き換えです。
imdb.load_data(num_words=10000)
imdb.load_data()
のオプションに
num_words=10000
というのを設定しました。
これは単語数を1万語に絞るという意味です。
2万5千件から作成された単語リストには、実は約8万種類の単語が登場するんですが、その内の頻出単語1万件だけを利用して、それ以外の単語は<UNK>(unknown)として扱います。
単語数が多いと学習に時間がかかったりモデルの精度が落ちる場合があるため、重要性が低い単語は切り捨てて事象を単純化するテクニックです。
imdb.get_word_index()
word_index = imdb.get_word_index()
imdb.get_word_index()
を使うと、数字と単語の対応関係を知ることができます。
words = [(k, v) for k, v in word_index.items()]
words[:10]
[('fawn', 34701),
('tsukino', 52006),
('nunnery', 52007),
('sonja', 16816),
('vani', 63951),
('woods', 1408),
('spiders', 16115),
('hanging', 2345),
('woody', 2289),
('trawling', 52008)]
keyとvalueを取り出してリスト化すると、先頭10件はこんな感じ。
word_indexは辞書データなので、取り出したデータは順序がばらばらです。
words = sorted(words, key=lambda x: x[1])
words[:10]
[('the', 1),
('and', 2),
('a', 3),
('of', 4),
('to', 5),
('is', 6),
('br', 7),
('in', 8),
('it', 9),
('i', 10)]
数字の方を基準に並べ替えると、先頭10件は上記の通り。
レビューの英文の中で出現頻度が高い単語順に並んでいます。
words[-10:]
[('ev', 88575),
('chicatillo', 88576),
('transacting', 88577),
('sics', 88578),
('wheelers', 88579),
("pipe's", 88580),
('copywrite', 88581),
('artbox', 88582),
("voorhees'", 88583),
("'l'", 88584)]
リストの末尾10件はこんな感じ。
88584まで数字があるので、88584個の単語があるってことですね。
最後のアイテムは、'l'という「なんだこれ?」という意味のなさそうな単語になっています。
imdb.load_data(num_words=10000)
のように
num_words=10000
を指定したので、頻出単語1万件以外はunknownな単語として扱われます。
<UNK>は2番で予約済み
words[:22]
[('the', 1),
('and', 2),
('a', 3),
('of', 4),
('to', 5),
('is', 6),
('br', 7),
('in', 8),
('it', 9),
('i', 10),
('this', 11),
('that', 12),
('was', 13),
('as', 14),
('for', 15),
('with', 16),
('movie', 17),
('but', 18),
('film', 19),
('on', 20),
('not', 21),
('you', 22)]
「this film was just brilliant casting location scenery story direction」
という文章を数字の置き換えると
「1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458」となっていました。
しかし、words[:22]の中身をみると
('this', 11),
('film', 19),
('was', 13),
となっています。
「this film was」が「11, 19, 13」にならないとおかしくない?
実際には「this film was」は「14, 22, 16」という数字に置き換えられています。
想定よりも数字が3つずつずれています。
word_indexの中身の
('the', 1),
('and', 2),
('a', 3),
('of', 4),
というのは、
・theが1番出現頻度が高かったです。
・andが2番目に出現頻度が高かったです。
という意味です。
テキストを数値に置き換えるときにtheに何の数字を割り当てるかはまた別の話。
word_index["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2 # unknown
word_index["<UNUSED>"] = 3
<PAD>(パディング)は0、
<START>(文章の始まり)は1、
<UNK>(未知語)は2、
<UNUSED>は3に置き換えるというのがルールとして決まっています。
(UNUSEDが何なのかは不明。あとで正体が分かったら追記します)
theを1に置き換えてしまうと<START>と見分けがつきません。
そこで、imdb.load_data()では、
theは4(出現頻度は1位だったけど)
andは5(出現頻度は2位だったけど)
というように、出現頻度の順位+3を置き換え用の数字に使っています。
tf.keras.preprocessing.sequence.pad_sequences()の使い方を覚える
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=word_index["<PAD>"],
padding='post',
maxlen=256)
word_index["<PAD>"]の中身は0です。
したがって、上記のコードは下記のように書いても同じです。
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post',
maxlen=256)
pad_sequencesを実行する前は
len(train_data[0])
218
train_data[0]の長さは218でした。
import numpy
numpy.array(train_data[0])
array([ 1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458,
4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43,
838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150,
4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536,
1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6,
147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22,
71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247,
4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5,
62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244,
16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619,
5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6,
22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82,
2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7,
3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317,
46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15,
297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18,
4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30,
5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226,
65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472,
113, 103, 32, 15, 16, 5345, 19, 178, 32])
train_dta[0]の中身は上記の通り数字の羅列です。
この数字が218個ならんでいます。
IMDBのデータセットの元データは映画のレビューデータが英文で書かれています。
英文は毎回文章の長さが違います。
そのため、単語を数字に置き換えたものも毎回長さが違います。
単語の長さが違うと機械学習の処理ができません。
そこで、単語の長さを統一してくれるのが
tf.keras.preprocessing.sequence.pad_sequences()
の役割です。
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post',
maxlen=256)
上記のコードを実行すると
len(train_data[0])
256
train_data[0]の長さが
maxlen=256
で指定した長さに変化しました。
train_data[0]
array([ 1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458,
4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43,
838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150,
4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536,
1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6,
147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22,
71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247,
4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5,
62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244,
16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619,
5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6,
22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82,
2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7,
3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317,
46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15,
297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18,
4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30,
5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226,
65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472,
113, 103, 32, 15, 16, 5345, 19, 178, 32, 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, 0], dtype=int32)
train_data[0]の中身を見ると、末尾に0がいくつも追加されています。
0パディング(0の付け足し)で長さが256になるように調節しているわけです。
padding='post'とは?
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post',
maxlen=256)
上記からpadding='post'の部分を削除してみます。
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
maxlen=256)
すると0の付け足しが末尾ではなく先頭に変わります。
train_data[0]
array([ 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, 0, 1, 14, 22, 16, 43, 530,
973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36,
256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9,
35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336,
385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447,
4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4,
1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530,
38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12,
16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8,
106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4,
130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135,
48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52,
5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952,
15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43,
530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13,
104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26,
141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476,
26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224,
92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12,
16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345,
19, 178, 32], dtype=int32)
padding='post'のオプションは
「0パディングを後ろにつける」
という意味です。
value=0とは?
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=1,
padding='post',
maxlen=256)
value=0
の部分を
value=1
に変えてみました。
train_data[0]
array([ 1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458,
4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43,
838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150,
4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536,
1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6,
147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22,
71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247,
4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5,
62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244,
16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619,
5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6,
22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82,
2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7,
3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317,
46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15,
297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18,
4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30,
5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226,
65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472,
113, 103, 32, 15, 16, 5345, 19, 178, 32, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1], dtype=int32)
末尾に1が付け足されました。
maxlen=256とは?
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post',
maxlen=256)
上記から
maxlen=256
の部分を削除してみます。
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post)
するとtrain_dataの中にある1万5千件のレビューデータの中から一番長文のものを自動で検出して、その長さに合わせてくれます。
len(train_data[0])
2494
train_data[0]の長さが2494になりました。
train_data[0]の元の長さは218だったので、末尾に2276個の0がパディングされたってことですね。
ということは、maxlen=256を指定した場合、もとも2494の長さがあったレビューデータなんかは256まで長さを短縮されてしまったってことですよね?
文章を途中でちょん切って短くしているのかな?
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post',
maxlen=10)
試しに
maxlen=10
で
pad_sequences()
を実行してみました。
train_data[0]
array([4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32],
dtype=int32)
train_data[0]の中身が10個に縮小されています。
train_data[0]はもともと下記のような数字が並んでいました。
array([ 1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458,
4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43,
838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150,
4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536,
1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6,
147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22,
71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247,
4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5,
62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244,
16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619,
5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6,
22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82,
2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7,
3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317,
46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15,
297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18,
4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30,
5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226,
65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472,
113, 103, 32, 15, 16, 5345, 19, 178, 32])
pad_sequences()
で
maxlen=10
のオプションを指定した結果、末尾の10個が採用されていますね。
先頭10個じゃないんだ。
tf.keras.layers.Embedding()とは?
# 入力の形式は映画レビューで使われている語彙数(10,000語)
vocab_size = 10000
model = tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(vocab_size, 16))
model.add(tf.keras.layers.GlobalAveragePooling1D())
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
tf.keras.layers.Embedding()は
単語をワードベクトルに変換してくれる。
「this」という単語を「14」という数字で表すというところまでは事前に処理済み。
今度は、「14」という数字をたとえば、
[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343 ,
-1.7357955 , -1.9954445 , 1.2989551 , 5.081598 , -1.1041286 ,
-2.0503852 , -0.72675157, -0.65675956, 0.24436149, -3.7208383 ,
2.0954835]
という数値配列(ワードベクトル)に変換する。
この処理をやってくれるのがEmbedding。
このワードベクトルの数字は学習の過程で変化する。
変化した結果、意味が近い単語同氏はワードベクトルの数字も近くなる。
各数字の意味はブラックボックスだが、たとえば
[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343 ,
-1.7357955 , -1.9954445 , 1.2989551 , 5.081598 , -1.1041286 ,
-2.0503852 , -0.72675157, -0.65675956, 0.24436149, -3.7208383 ,
2.0954835]
というベクトルの先頭の数字1.765786の部分は、「プラスの値だったら名詞で、マイナスの値だったら動詞」みたいな意味を持っていたりする。
あくまでも例えなので、どの部分の数字がどういう意味を持っているかはブラックボックス。
しかし、各数字が人間には言語化できない何らかの意味を持ってはいる。
Output Shapeの(None, None, 16)の16 とは?
vocab_size = 10000
model = tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(vocab_size, 16))
model.add(tf.keras.layers.GlobalAveragePooling1D())
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, None, 16) 160000
_________________________________________________________________
global_average_pooling1d (Gl (None, 16) 0
_________________________________________________________________
dense (Dense) (None, 16) 272
_________________________________________________________________
dense_1 (Dense) (None, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
model.summary()
をすると
作成したmodelの構造をみることができる。
model.add(tf.keras.layers.Embedding(vocab_size, 16))
で追加した層(Layer)が
embedding (Embedding) (None, None, 16) 160000
という行に相当する。
(None, None, 16)
の16の部分は、
tf.keras.layers.Embedding(vocab_size, 16)
と書いたため。
「this」という単語を「14」という数字に置き換えて、
14という数字を以下のようなワードベクトルに置き換える。
[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343 ,
-1.7357955 , -1.9954445 , 1.2989551 , 5.081598 , -1.1041286 ,
-2.0503852 , -0.72675157, -0.65675956, 0.24436149, -3.7208383 ,
2.0954835]
という作業をするときの、ワードベクトルの数字のサイズを16個の数字で構成するように指定した。
仮に
tf.keras.layers.Embedding(vocab_size, 5)
と設定すれば、ワードベクトルは
[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343]
みたいな5個の数字で構成される。
あくまでも例えだが、
ベクトルの先頭の数字1.765786の部分は、「プラスの値だったら名詞で、マイナスの値だったら動詞」みたいな意味を持っていたりする。
そして、2番目の数字の-3.882232の部分は「プラスだったら他動詞で、マイナスだったら自動詞」。
3番目の数字の3.9213233の部分は「3付近だったら人間で、2付近だったら動物で、1付近だったら植物で」みたいな意味があるかもしれない。
(あくまでも例えばの話です)
もしも
tf.keras.layers.Embedding(vocab_size, 1)
みたいにしてしまうとワードベクトルは
[ 1.765786]
こんな感じで数字1個だけになります。
これだと桁数が少なすぎて1万種類の単語を上手に表現できません。
「名詞か動詞か」「自動詞か他動詞か」「生物か無生物か」みたいなあらゆる意味を1つの数字で表現するのは無理があるからです。
桁数が少なくて上手に意味を表せないことを「表現力に乏しい」といいます。
Output Shapeの1つ目のNoneは何?
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, None, 16) 160000
_________________________________________________________________
global_average_pooling1d (Gl (None, 16) 0
_________________________________________________________________
dense (Dense) (None, 16) 272
_________________________________________________________________
dense_1 (Dense) (None, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
Output Shapeのところには
(None, None, 16)
(None, 16)
(None, 16)
(None, 1)
とすべて先頭がNoneになっています。
このNoneはbatch_sizeです。
この後の処理で
model.fit()
というのを使って学習を実行します。
history = model.fit(partial_x_train,
partial_y_train,
epochs=40,
batch_size=512,
validation_data=(x_val, y_val),
verbose=1)
上記のように
batch_size=512
とすれば512件ずつデータを処理します。
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (512, None, 16) 160000
_________________________________________________________________
global_average_pooling1d (Gl (512, 16) 0
_________________________________________________________________
dense (Dense) (512, 16) 272
_________________________________________________________________
dense_1 (Dense) (512, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
つまり、model.fit()を実行する段階では上記のように
(512, None, 16)
(512, 16)
(512, 16)
(512, 1)
というOutput Shapeになります。
しかし、model.fit()を実行する前の段階では何件ずつデータが処理されるかは不明なので、
(None, None, 16)
(None, 16)
(None, 16)
(None, 1)
となっています。
Output Shapeの(None, None, 16)の2つ目のNoneは何?
Embedding層のOutput Shapeは
(None, None, 16)
となっています。
この真ん中のNoneには256が入ります。
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0,
padding='post',
maxlen=256)
train_dataを0パディングするときに
maxlen=256
と指定しました。
この256の数字です。
array([ 1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458,
4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43,
838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150,
4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536,
1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6,
147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22,
71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247,
4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5,
62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244,
16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619,
5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6,
22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82,
2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7,
3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317,
46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15,
297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18,
4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30,
5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226,
65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472,
113, 103, 32, 15, 16, 5345, 19, 178, 32, 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, 0], dtype=int32)
train_data[0]の中身は上記のような256桁の数字でした。
train_dataにはこういった感じの「256個の数字」であらわされたレビューデータが1万5千件入っています。
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (512, 256, 16) 160000
_________________________________________________________________
global_average_pooling1d (Gl (512, 16) 0
_________________________________________________________________
dense (Dense) (512, 16) 272
_________________________________________________________________
dense_1 (Dense) (512, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
model.fit()を実行する段階では上記のように
EmbeddingのOutput Shapeは
(512, 256, 16)
となりますが、
model.fit()をする前の段階では各データのサイズが不明なので
(None, None, 16)
となります。
tf.keras.layers.GlobalAveragePooling1D()とは?
# 入力の形式は映画レビューで使われている語彙数(10,000語)
vocab_size = 10000
model = tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(vocab_size, 16))
model.add(tf.keras.layers.GlobalAveragePooling1D())
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, None, 16) 160000
_________________________________________________________________
global_average_pooling1d (Gl (None, 16) 0
_________________________________________________________________
dense (Dense) (None, 16) 272
_________________________________________________________________
dense_1 (Dense) (None, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
embedding
のところのOutput Shapeは
(None, None, 16)
global_average_pooling1d
のところのOutput Shapeは
(None, 16)
これはmodel.fit()のところでは実際には
(512, 256, 16)
と
(512,16)
というshapeになります。
英単語1つにつき、こんな感じの16個の数字で構成されたベクトルが生成されます。
[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343 ,
-1.7357955 , -1.9954445 , 1.2989551 , 5.081598 , -1.1041286 ,
-2.0503852 , -0.72675157, -0.65675956, 0.24436149, -3.7208383 ,
2.0954835]
レビューデータは単語が256個ずつになるように前処理を行いました。
そのため、各train_dataの中身はembeddingレイヤーを通過するときに
[[ 1.765786 , -3.882232 , 3.9134233 , -1.5557289 , -3.3362343 ,
-1.7357955 , -1.9954445 , 1.2989551 , 5.081598 , -1.1041286 ,
-2.0503852 , -0.72675157, -0.65675956, 0.24436149, -3.7208383 ,
2.0954835],
[ 1.8804485 , -2.5852382 , 3.4066997 , 1.0982676 , -4.056685 ,
-4.891284 , -2.785554 , 1.3874227 , 3.8476458 , -0.9256538 ,
-1.896706 , 1.2113281 , 0.11474707, 0.76209456, -4.8791065 ,
2.906149],
…
…
[ 0.71152234, -0.6353217 , 1.7385626 , -1.1168286 , -0.5451594 ,
-1.1808156 , 0.09504455, 1.4653089 , 0.66059524, 0.79308075,
-2.2268345 , 0.07446612, -1.4075904 , -0.70645386, -1.907037 ,
1.4419787]]
こんな感じで16個のベクトルが256行並んだarrayに変換されます。
tf.keras.layers.GlobalAveragePooling1D()
では、これら256個のベクトルを平均した1つのベクトルを出力します。
[(1.765786 + 1.8804485 + ...... + 0.71152234)/256,
(-3.882232 + (-2.5852382) + ...... + (-0.6353217))/256,
…
…
(2.0954835 + 2.906149 + ...... + 1.4419787)/256]
こういう感じの計算をしてくれます。
「256x16次元だと扱いにくいので全部混ぜて1x16次元に単純化しちゃえ」ということです。