#はじめに
pythonのkerasを用いて機械学習の練習を行う際に必ずといっていほど目にするcifar10とmnist。今回は機械学習の第一歩としてcifar10に着目して取り組んでみます。また、これを見てくれた機械学習にこれから取り組みたい人にとって1つでも参考になればと思います。
###目次
・使ったもの
・データセットの入手、作成
・ネットワークを構築
・学習させてみる
###使ったもの
・numpy (毎度おなじみのやつ, 保存に使っただけで使う必要なし)
・keras(tensorfow?)
なければ、conda install keras でインストールを。
###データセットの入手、作成
まずはデータセットを手に入れます。今回はCIFAR-10データセットを使用しました。CIFAR-10とはkerasに同梱されており、32×32ピクセルのカラー画像が60000枚存在します。これらの画像はすべて、10種類のいずれかに分けられます。
早速読み込んでいきます。
from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
次に、このデータを使いやすいように加工を施していきます。
from keras.utils import to_categorical
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
ざっくりいうと画像データは1から255の整数で構成され、入力値が-1から1の間にあると嬉しいので255で割り、to_categoricalで、y_train, y_testをone-hotエンコーディングの形状にしています。
私はいろいろな場合で試したかったので、この4つのデータをnp.savezで保存してしまいました。
###one-hot エンコーディングとは
ざっくりいうと、分類する種類の数の長さを持ったベクトルで、ある要素が1でそれ以外はすべて0であるベクトルに変換することを指します。ロードした段階でのy_train、y_testを例えば、
print(y_train[4])
と実行して覗いてみると、[1]が表示されます。この数字を上記のベクトルに変換すると、
[0, 1, 0, 0, 0, 0, 0, 0, 0]となります。
他に、y_trainの画像が5に分類される場合は
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]となります。
###ネットワークを構築
いよいよ本題、ネットワークの構築に取り掛かかります。個人的になれているfunctional APIという手法で構築しました。
from keras.layers import Dense, Flatten, Conv2D, BatchNormalization,
Dropout, LeakyReLU, Input
from keras.models import Model
class Network():
def __init__(self, input_dim, filter, kernel, stride, use_batch, use_dropout, output_dim, lr):
self.input_dim = input_dim
self.filter = filter
self.kernel = kernel
self.stride = stride
self.n_layers = len(filter)
self.use_batch = use_batch
self.use_dropout = use_dropout
self.lr = lr
self.crate()
def create(self):
input_layer = Input(shape=self.input_dim)
x = input_layer
for i in range(self.n_layers):
conv = Conv2D(filters=self.filter[i], kernel_size=self.kernel[i], strides=self.stride[i], padding='same')
x = conv(x)
if self.use_batch:
x = BatchNormalization(momentum=self.use_batch)(x)
x = LeakyReLU()(x)
if self.use_dropout:
x = Dropout(rate=0.5)(x)
x = Flatten()(x)
x = Dense(units=self.output_dim, activation='sigmoid')(x)
output_layer = x
self.model = Model(input_layer, output_layer)
あとでいろいろ値を変えて学習させたかったのでNetworkというクラスを定義。
###説明
####Input
その名の通り、データが入力される層です。データの形をshapeとして代入します。
####Conv2D
画像を取り扱う際によく用いられる畳み込み層と呼ばれるものです。画像の一部の値とフィルタの値を乗算しその値を返す、これを画像全体に渡るまで繰り返すことでその画像の特徴をうまく抽出することができるというものです。
####BatchNormalization
ニューラルネットワークを訓練する際、重みが大きくなりすぎるとやがて勾配爆発というものを引き起こします。それを防ぐための層だと簡単に理解すればいいと思います。(というか自分もそれくらいしかまだ理解が追いついていない、、)
####LeakyReLU
活性化関数を通す層。ReLUだと0より小さい値をすべて0と返しますが、LeakyReLUは負の値を返します。これにより、負の値を生かすことができます。
####Dropout
この層は、過剰に学習させすぎるのを防ぐためにいくつかのノードを選択して出力を0にします。
####Flatten
入力されてきたものの次元を1にします。
####Conv2Dの各変数について
input_dimとは、その名の通り最初に入力される次元です。今回の場合は(32, 32, 3)となります。(32, 32)は画像のサイズ、3番目の3はチャンネルの個数というものを表し、今回は(赤、緑、青)の個数となります。(白黒画像だと1?)
filtersはフィルターの数を表しており、kernel_sizeはその大きさです。sridesはフィルターを移動させる大きさです。
####おおざっぱな流れ
まとめると、画像データを学習させる際によく用いられるConv2Dという畳み込み層、バッチ正規化を施すBatchNomalization、LeakyReLUという活性化関数、Dropout層を繰り返し、Flattenでならして最後にDense層を通すというもの。
最後に入力層、出力層でmodelを定義して完成です。
from keras.optimizers import Adam
def compile(self, lr):
self.model.compile(optimizer=Adam(lr=self.lr), loss='categorical_crossentropy', metrics=['accuracy'])
def train(self, data1, data2, batch_size, epochs):
self.model.fit(data1, data2, batch_size=batch_size, epochs=epochs, shuffle='True')
ここから訓練していきます。
###学習させてみる
indim = (32, 32, 3)
fil = [32, 64, 64, 32]
ker = [3, 3, 3, 3]
stri = [1, 2, 1, 2]
usebatch = 0.25
usedrop = True
Lr = 0.0005
Epochs = 10
Batchsize = 32
NETWORK = Network(input_dim=indim, filters=fil, kernel=ker, stride=stri, use_batch=usebatch, use_dropout=usedrop, lr=Lr)
NETWORK.model.compile(lr=Lr)
NETWORK.model.train(data1=x_train, data2=y_train, batch_size=Batchsize, epochs=Epochs)
accuracyは62%ほど。
個人的にバッチ正規化のイメージがまだあまりうまくつかめていないので、今後の課題にしたいところです。あとはフィルターやストライドの数値をどのようにすれば精度を上げられるのか、今後の課題です。
これがこれが気象の解析において活用できるときが来るのかはわかりませんが、可能性はなくはないと思うのでなにか考えてみたいところですね。
##参考文献
[生成Deep Learning]
・David foster 著
・松田晃一、小沼千絵 訳
・オライリージャパン
・2020年発行