TensorFlowのチュートリアルでつまづいたところメモ(advanced.ipynb)
概要 > エキスパート向け > コードを今すぐ実行 https://www.tensorflow.org/overview?hl=ja
TensorFlow 2 quickstart for experts
https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/quickstart/advanced.ipynb?hl=ja#scrollTo=3wF5wszaj97Y
上記を読んでて分かりにくいと思った部分に対する補足説明記事です。
tf.newaxisって何だ?
# Add a channels dimension
x_train = x_train[..., tf.newaxis].astype("float32")
x_test = x_test[..., tf.newaxis].astype("float32")
tf.newaxisって何だ?
In: print(tf.newaxis)
Out: None
tf.newaxisの中身はNoneでした。
# Add a channels dimension
# x_train = x_train[..., tf.newaxis].astype("float32")
# x_test = x_test[..., tf.newaxis].astype("float32")
# Add a channels dimension
x_train = x_train[..., None].astype("float32")
x_test = x_test[..., None].astype("float32")
したがって、tf.newaxisの代わりにNoneと書いても意味は同じです。
何かの状態によってtf.newaxisの中身が変わるのかな?
今回はたまたま中身がNoneだったのかな?
と思って調べたら、tf.newaxisは常にNoneになるようです。
では、tf.newaxisは何のために存在するのか?
tf.newaxisはいわゆる説明変数です。
x_train = x_train[..., None].astype("float32")
と書くと、他人がコードを見たときに「このNoneっていったい何だろう?」と思います。
x_train = x_train[..., tf.newaxis].astype("float32")
と書くことで、「ここでは次元を追加していますよ」というのが客観的にわかるようになっています。
TensorfFlow界におけるコーディングマナーの話ですね。
x_trainの中身はMNISTのデータです。
手書き数字の画像データが入っています。
In: x_train.shape
Out: (60000, 28, 28)
縦28x横28のピクセルデータが6万件入っています。
# Add a channels dimension
# x_train = x_train[..., tf.newaxis].astype("float32")
# x_test = x_test[..., tf.newaxis].astype("float32")
元のx_trainに対して上記のコードを実行すると
In: x_train.shape
Out: (60000, 28, 28, 1)
x_train.shapeが (60000, 28, 28)が (60000, 28, 28, 1)に変化しました。
次元が1つ増えていますね。
tf.data.Dataset.from_tensor_slicesって何だ?
train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(32)
test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
tf.data.Dataset.from_tensor_slices((x_train, y_train))って何だ?
.shuffle(10000)や.batch(32)の部分は無視して、まずは
tf.data.Dataset.from_tensor_slices((x_train, y_train))の部分だけ考えます。
# train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train))
dataset_x = tf.data.Dataset.from_tensor_slices((x_train))
dataset_y = tf.data.Dataset.from_tensor_slices((y_train))
train_ds = zip(dataset_x, dataset_y)
train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train))
上記の1行は、以下の3行とだいたい同じ意味です。
dataset_x = tf.data.Dataset.from_tensor_slices((x_train))
dataset_y = tf.data.Dataset.from_tensor_slices((y_train))
train_ds = zip(dataset_x, dataset_y)
xとyにそれぞれtf.data.Dataset.from_tensor_slicesをして、zipでつなげたやつ。
では、
dataset_y = tf.data.Dataset.from_tensor_slices((y_train))
とは何か?
In: type(y_train)
Out: numpy.ndarray
y_trainのデータ型はnumpy.ndarrayでした。
In: type(dataset_y)
Out: tensorflow.python.data.ops.dataset_ops.TensorSliceDataset
dataset_y = tf.data.Dataset.from_tensor_slices((y_train))
を実行したことでデータ型がTensorSliceDatasetというのに変わっています。
In: print(y_train[0])
Out: 5
y_trainからデータを1つだけ取り出すと、中身は5でした。
手書き数字の画像(28x28サイズのピクセルデータ)がtrain_xの方に入っていて、1つ目の画像は答えが「5」ということですね。
In: type(y_train[0])
Out: numpy.uint8
y_train[0]のデータ型はnumpy.uint8でした。
簡単にいうと数値です。
In: print(dataset_y[0])
Out: ---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-74-17806f6dd8e7> in <module>()
----> 1 print(dataset_y[0])
TypeError: 'TensorSliceDataset' object does not support indexing
dataset_yの1つ目の要素の中身を見ようとしたらエラーになりました。
dataset_y[0]という書き方がダメみたい。
In: for y in dataset_y:
print(y)
break
Out: tf.Tensor(5, shape=(), dtype=uint8)
dataset_yはリストではないので[0]という書き方ができません。
しかし、for文を使うと中身を取り出せます。
for文で中身取り出してprint()すると
tf.Tensor(5, shape=(), dtype=uint8)
というものが表示されました。
y_trainの先頭の要素は
5
でした。
dataset_yの先頭の要素は
tf.Tensor(5, shape=(), dtype=uint8)
です。
dataset_y = tf.data.Dataset.from_tensor_slices((y_train))
上記の1行を実行することで
データの中身が
5
のような数値から
tf.Tensor(5, shape=(), dtype=uint8)
というものに変換されています。
tensorflowで機械学習するためにtf.Tensorという特殊な形式に変換しているってことですね。
Conv2D(32, 3, activation='relu')って何?
class MyModel(Model):
def __init__(self):
super(MyModel, self).__init__()
self.conv1 = Conv2D(32, 3, activation='relu')
self.flatten = Flatten()
self.d1 = Dense(128, activation='relu')
self.d2 = Dense(10)
def call(self, x):
x = self.conv1(x)
x = self.flatten(x)
x = self.d1(x)
return self.d2(x)
上記はModelのサブクラスを定義するコード。
Conv2D(32, 3, activation='relu')って何だ?
ConvはConvolutionの略。
畳みこみニューラルネットワーク(Convolutional Newral Network)用のやつですね。
Conv2Dなので、2Dなので2次元画像なんかを畳み込むために使います。
Conv2D(32, 3, activation='relu')
の1つ目の引数、32となっているところ。
これは畳み込みフィルタの数。
1枚の画像を32枚に複製してから学習とか推論のときに利用します。
複製するときに、黒さを強調したり、縦線を強調したり、ちょっとぼやかしてみたり、パターンが異なる32枚の画像を作りだします。
パターンが異なる画像を生成して処理に利用することでニューラルネットワークが賢くなります。
Conv2D(32, 3, activation='relu')
の2つ目の引数、3となっているところ。
これは、ウィンドウサイズに3x3を使いますという意味。
Conv2D(32, 3, activation='relu')
は
Conv2D(32, (3,3), activation='relu')
のように書いても同じ意味。
たとえば、元の画像28x28サイズだったとする。
3x3ウィンドウを使うと、画像の左上の3x3マスだけを使って画像にフィルタをかける、1マス右にずらして3x3マスだけを見て画像にフィルタをかける、……というのを元画像の端から端まで実行する。
フィルタをかけるというのは、たとえば3x3=9マスの平均値を取るとか。