Help us understand the problem. What is going on with this article?

1週間でディープラーニング基礎とKerasを勉強して、ResNetを実装してみた

はじめに

ディープラーニングの勉強を始めました。
機械学習もPythonも初心者です。

せっかくなので、1週間で勉強した手順をメモ程度に残します。
これからディープラーニングの勉強を始める方の参考になれば幸いです。

ネットで調べてみる(数時間)

まず、どのように勉強を進めれば良いか見通すためにネットを漁りました。
そして全体像を整理しました。

  • 「人工知能」のアプローチの一つが「機械学習」。
  • 機械学習の手法は「教師あり学習」「教師なし学習」「強化学習」に大分される。
  • 教師あり学習で解く問題は「回帰」と「分類」に大分される。
  • 教師あり学習の手法の一つが「ニューラルネットワーク」。
  • ニューラルネットワークで層を深く重ねて学習する手法が「ディープラーニング」。

おすすめ資料

機械学習チュートリアル@Jubatus Casual Talks
機械学習の情報を手法を中心にざっくり整理
深層学習(ディープラーニング)の原理、CNN、RNN、LSTM,GANを図解で解説 | TickTack World

本を読んでみる(5日)

ネットを漁った結果、ニューラルネットワークの仕組みをしっかり理解する必要があると思いました。

そこで下記の本で勉強しました。
実装を通して、とても丁寧に仕組みを学べます。

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

要点メモ

  • 1章
    • Pythonは数学的計算が簡単に書けて実行速度も速い。なので機械学習と相性が良い。
    • 対話モードで手軽に実験できるので楽しい……!
  • 2章
    • パーセプトロンは単純な構造ながら、複雑な関数を表現できる。
    • 重み、バイアスで出力が変化し、層の数を増やすことで複雑化できる。
  • 3章
    • ニューラルネットワークはパーセプトロンの活性化関数を変更しただけ。
    • キーワード:Step関数、Sigmoid関数、ReLU関数、恒等関数、Softmax関数。
  • 4章
    • 損失関数の勾配(=微分)に学習率を掛けて重み・バイアスを学習していく(=勾配降下法)。
    • キーワード:二乗和誤差、交差エントロピー誤差。ミニバッチ処理(>>確率的勾配降下法=SGD)。エポック=試行回数/教師データ総数。
  • 5章
    • 誤差を逆伝播して重み・バイアスを更新していくと高速に学習できる(=誤差逆伝播法)。
    • 活性化関数やAffine(=重み・バイアス)をレイヤーとしてモジュール化でき、モデル構築が容易になる。
  • 6章
    • 勾配消失、過学習を防ぐための様々な手法:Momentum、AdaGrad(>>RMSprop)、Adam。Xavierの初期化、Heの初期化。Batch Normalization、Weight decay、Dropout。教師データと検証データとテストデータ。
    • 過学習はモデルの表現力が高いほど、教師データが少ないほど起こる。
  • 7章
    • CNNを用いると全結合(=Affine)では無視される三次元データの特徴を学習できる。
    • キーワード:Convolution(=畳み込み)、Pooling(>>MaxPooling、AveragePooling)。チャンネル数、カーネルサイズ、スライド、パディング。im2col(>>メモリを多く消費)。
    • CNNは層を重ねるごとに、より抽象的な特徴をとらえる。
  • 8章
    • 精度向上、高速化ための手法:Data Augmentation。GPUの使用、分散学習、ビット精度の削減。ResNetのショートカット。
    • 応用例:物体検知(>>FasterRCNN)、セグメンテーション(>>FCN)、画像キャプション(>>NIC)、画像スタイル変換、画像生成(>>DCGAN)、強化学習への応用(>>DQN)。

フレームワークを使ってみる(1〜2日)

本での学びを踏まえて、Kerasで簡単な実装をしてみました。
仕組みを理解したおかげで、使い方はほぼ見ただけで分かります。

環境構築

環境はDockerで作りました。

FROM python:3
USER root
RUN pip install --upgrade pip
RUN pip install numpy
RUN pip install matplotlib
RUN pip install keras
RUN pip install tensorflow
app:
  build: .
  tty: true
  working_dir: /root/app/
  volumes:
    - ./app:/root/app:z

MNIST

まずは単純な隠れ2層でのMNISTです。

import numpy as np
import matplotlib.pylab as plt
from keras.datasets import *
from keras.utils import *
from keras.models import *
from keras.layers import *

#データ
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train  = x_train.reshape(-1, 784)
x_test   = x_test.reshape(-1, 784)
x_train  = x_train.astype('float32')
x_test   = x_test.astype('float32')
x_train /= 255
x_test  /= 255
y_train  = to_categorical(y_train, 10)
y_test   = to_categorical(y_test, 10)

#モデル
model = Sequential()
model.add(Dense(units=50, activation='relu'))
model.add(Dense(units=50, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

#訓練
epochs = 10
history = model.fit(x=x_train, y=y_train, epochs=epochs, batch_size=100, validation_split=0.2)

#グラフ
epochs = range(epochs)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(epochs, history.history['acc'], label='training')
plt.plot(epochs, history.history['val_acc'], label='validation')
plt.title('acc')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(epochs, history.history['loss'], label='training')
plt.plot(epochs, history.history['val_loss'], label='validation')
plt.title('loss')
plt.legend()
plt.savefig('mnist.png')

#テスト
score = model.evaluate(x=x_test, y=y_test)
print('test_loss:', score[0])
print('test_acc:', score[1])
test_loss: 0.10822790691759437
test_acc: 0.9698

CNN(CIFAR-10)

次に8章p.242で出題されていたCNNモデルです。
CIFAR-10で試しました。

import numpy as np
import matplotlib.pylab as plt
from keras.datasets import cifar10
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Activation

#データ
(x_train, y_train),(x_test, y_test) = cifar10.load_data()
x_train  = x_train.astype('float32')
x_test   = x_test.astype('float32')
x_train /= 255
x_test  /= 255
y_train  = to_categorical(y_train, 10)
y_test   = to_categorical(y_test, 10)

#モデル
model = Sequential()
model.add(Conv2D(filters=16, kernel_size=3, padding='same', activation='relu'))
model.add(Conv2D(filters=16, kernel_size=3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
model.add(Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'))
model.add(Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(units=50, activation='relu'))
model.add(Dropout(rate=0.5))
model.add(Dense(units=10))
model.add(Dropout(rate=0.5))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

#訓練
epochs = 20
history = model.fit(x=x_train, y=y_train, epochs=epochs, batch_size=100, validation_split=0.2)

#グラフ
epochs = range(epochs)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(epochs, history.history['acc'], label='training')
plt.plot(epochs, history.history['val_acc'], label='validation')
plt.title('acc')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(epochs, history.history['loss'], label='training')
plt.plot(epochs, history.history['val_loss'], label='validation')
plt.title('loss')
plt.legend()
plt.savefig('cnn.png')

#テスト
score = model.evaluate(x=x_test, y=y_test)
print('test_loss:', score[0])
print('test_acc:', score[1])
test_loss: 0.8809946607589721
test_acc: 0.7253

ResNet(CIFAR-100)

最後に8章p.254で紹介されていた、ResNetのショートカットを実装してみました。
1本道のネットワークではないため、Sequentialではなくfunctional APIで実装する必要があります。

import numpy as np
import matplotlib.pylab as plt
from keras.datasets import *
from keras.utils import *
from keras.models import *
from keras.layers import *

#データ
(x_train, y_train),(x_test, y_test) = cifar100.load_data()
labels = np.max(y_train) + 1
x_train  = x_train.astype('float32')
x_test   = x_test.astype('float32')
x_train /= 255
x_test  /= 255
y_train  = to_categorical(y_train, labels)
y_test   = to_categorical(y_test, labels)

#モデル
inputs = Input(shape=x_train.shape[1:])
f = 64
ki = 'he_normal'
kr = regularizers.l2(1e-11)
x = Conv2D(filters=f, kernel_size=7, padding='same', kernel_initializer=ki, kernel_regularizer=kr)(inputs)
x = MaxPooling2D(pool_size=2)(x)
n = 5
for i in range(n):
    shortcut = x
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Dropout(rate=0.3)(x)
    x = Conv2D(filters=f*(2**i), kernel_size=1, padding='same', kernel_initializer=ki, kernel_regularizer=kr)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Conv2D(filters=f*(2**i), kernel_size=3, padding='same', kernel_initializer=ki, kernel_regularizer=kr)(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)
    x = Conv2D(filters=f*(2**(i+2)), kernel_size=1, padding='same', kernel_initializer=ki, kernel_regularizer=kr)(x)
    x = Concatenate()([x, shortcut])
    if i != (n - 1):
        x = MaxPooling2D(pool_size=2)(x)
x = GlobalAveragePooling2D()(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dropout(rate=0.4)(x)
x = Dense(units=labels, kernel_initializer=ki, kernel_regularizer=kr)(x)
x = BatchNormalization()(x)
x = Activation('softmax')(x)
x = Dropout(rate=0.4)(x)
model = Model(inputs=inputs, outputs=x)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

#訓練
epochs = 50
history = model.fit(x=x_train, y=y_train, epochs=epochs, batch_size=100, validation_split=0.2)

#グラフ
epochs = range(epochs)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(epochs, history.history['acc'], label='training')
plt.plot(epochs, history.history['val_acc'], label='validation')
plt.title('acc')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(epochs, history.history['loss'], label='training')
plt.plot(epochs, history.history['val_loss'], label='validation')
plt.title('loss')
plt.legend()
plt.show()

#テスト
score = model.evaluate(x=x_test, y=y_test)
print('test_loss:', score[0])
print('test_acc:', score[1])

流石にCPUでは時間がかかりすぎるので、Google Colaboratoryを使いました。
(コードを貼るだけで動く……簡単……しかも爆速……無料……!!)

Google Colaboratory

test_loss: 2.3938468505859376
test_acc: 0.5221

正答率50%にたどり着くまで、レイヤー構成やパラメーターを試行錯誤しました。
実践的な学びになるので、ぜひ自分でモデルを組んでみることをお勧めします。

ResNetのチューニングはこちらが大変参考になりました。

Residual Network(ResNet)の理解とチューニングのベストプラクティス

おわりに

今は下記の本で応用編の勉強を進めています。
基礎勉強で得た知識が、しっかり土台となっている実感があります。

直感 Deep Learning ―Python×Kerasでアイデアを形にするレシピ

また、ディープラーニングだけではできないことも見えてきました。
下記の本を入門として、他の機械学習の手法も学んでいこうかと思います。

機械学習のエッセンス -実装しながら学ぶPython,数学,アルゴリズム- (Machine Learning)

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away