[深さ学習論文レビュー]1AlexNet
*勉強や復習に使う文章.不足や間違いがあればコメントで教えてください2002/02/14リビジョン-bias初期化に問題が発生しました.
AlexNetは、Alex Krizhevsky、Ilya Sutskever、Geofrey Hintonが設計したCNNアーキテクチャで、2012年にILSVRCで大きな差で優勝したことで知られる.AlexNetを記述する論文ImageNet Classification with Deep Convolutional Neural Networksを見てみましょう.
AlexNetは、Sigmoidまたはtanhをのアクティブ化機能として使用していた従来のモデルとは異なり、ReLUを使用している.
RELUはx値が正の値の場合、傾斜が一定であり、勾配消失現象は発生せず、演算オーバーヘッドが小さい.実際、CIFAR-10で比較した結果、25%の訓練ミス率に達する時間が6倍ほど速くなった.
より大きなネットワークのために,2つのGPUを用いて並列演算を実行した.2つのGPUは3番目のボリューム層とフルコネクション(FC)層を除いて独立して訓練された.1個の GPUに比べて訓練誤差は1~2ポイント減少し,時間もやや速くなった. ReLUはSigmoid型関数とは異なり,入力正規化は必須ではない.しかし,AlexNetはローカル応答正規化を用い,誤差を約1−2%低減した.
式はやや複雑に見えますが、簡単に言えば周辺kernel値よりも大きいところを正規化します.i次kernel mapのx,y点の値を(i-n/2)次~(i+n/2)次kernel mapのx,y点の値で割る. 最近はあまり使われない方法だそうです.従来、プールのサイズは、グリッドのように分割されたプールユニットのステップ長と同じである.しかし,AlexNetはプールセルサイズがより大きいオーバーラッププール技術を用い,誤差は約0.4%減少した. は正確な理由はありませんが、重ね合わせを適用した場合、過剰適合は緩和されます.
と照合を防止する最も簡単な方法の1つは、保持ラベル変換(保持ラベル変換)を使用してデータセットを追加することである. 論文では,反転と作物の2つの方法の変異体を用いた.元の256 X 256の画像をランダムに切り取り、224 X 224の画像を生成し、反転バージョンを生成すると、データセットは2048倍に増加します.(*224 X 224は論文の誤字で、streepとpaddingから見ると227が正しい値です.) 第2の方法は、主成分分析(PCA)によりRGB値を変更することである. の論文によると、これらのデータの増強がなければ、深刻で適切な挑戦を受けるだろう. Droputとは、隠蔽層の特定のニューロンの出力をゼロにする確率があることを意味する.これはニューロン人の共通の適応を減らすだろう.すなわち,特定のニューロンに依存できないように不確実性を付与する. この方法もオーバーフィットを大幅に減少させた.
探索は、損失関数がクロスエントロピーを使用することを発見した.多種類の分類はほとんどクロスエントロピーを用いた. Weight減衰を用いた.Weightパラメータについては、通常、L 2正規化法を用いて過大化を回避するためにペナルティが行われる. 角重μ=0,σ=0.01\mu=0,\sigma=0.01μ=0,σ=0.01の正規分布で初期化し、biasは第2、4、5の積層層とFC層のみを1に初期化した.これは正の値を与えるためにRELUの早期学習を加速させるためである. の学習率は、まずすべてのレイヤを整列させ、その後、誤差が減少しない時間に10分で調整される.初期学習率0.01から学習終了まで3回に分けられる. 120万枚の画像を約90回学び、6日間にわたった. 2010年のILSVRCデータによると、top-1およびtop-5部門の誤差率はそれぞれ37.5%および17.0%であった.当時の優勝モデルは47.1%、28.2%で、この数値をはるかに上回った. 2012年度ILSVRCにも参加し、top-5部門の誤差率を16.4%に下げた.
CODEのコード#コード#とtwinjuiのハーモニーを参照して作成します.
損失が減少しないことを確認するまで91個のepochを回して行った.約10時間かかります.
列車損失は0.7150,列車精度は0.9193,最終検証損失は1.1215,検証精度は0.7842であった.
その後test datasetを評価した結果,損失は1.1369,精度は0.7797であった.
オレンジ色はtrain精度、青は検証精度です.
コード実装に誤りがある.Weight初期化では,最初に論文のようにmean=0,stddev=0.01のrandom normalに初期化した.しかし、いくら待っていても、何度試しても被害はある程度までは下がらなかった.もちろん、精度も向上していません.
その結果,重み初期化器を設定せずに学習を開始し,すぐに所望の結果を得た.(上の結果)Conv layerでinitializerを設定すること自体が問題のようで、さらに原因を探す必要があるかもしれません.
Weight initializerは関係ない!問題はバイアス初期化器です.しかし、まだなぜか分からない.Biasを2つ以上の1に設定すると問題が発生し、どちらかを0.8未満に変更すると問題はありません.やはり余計な悩みが必要だ.
オプティマイザも論文のようにSGD(運動量=0.9)を用いた.その後Adam optimizerを用いて再生し,学習速度は約4倍に向上した.一般的にアダムは良さそうですが….当初、AdamはAlexNet論文の観点から2014年年に発表された.
ReLU非線形を用いて,勾配消失現象を解決し,時間を短縮した. Droputとデータの強化により、大幅に減少しました. ローカル応答を使用して正常化します. RELUは、適切な正の値に初期化することが望ましい. はパラメータを正しく初期化していません.原因を把握しようとしているようだ. 若干が適当すぎる.今後もデータの強化を試みます.
https://wikidocs.net/64066 (CNN)
https://taeguu.tistory.com/29 (local response normalization)
https://stats.stackexchange.com/questions/432896/what-is-the-loss-function-used-for-cnn (cross entropy)
https://89douner.tistory.com/60
https://github.com/dansuh17/alexnet-pytorch2https://velog.io/@twinjuy/AlexNet-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0-with-tensorflow(コード実装)
AlexNet
AlexNetは、Alex Krizhevsky、Ilya Sutskever、Geofrey Hintonが設計したCNNアーキテクチャで、2012年にILSVRCで大きな差で優勝したことで知られる.AlexNetを記述する論文ImageNet Classification with Deep Convolutional Neural Networksを見てみましょう.
Architecture
ReLU Non-Linearity
AlexNetは、Sigmoidまたはtanhを
Training on Multiple GPUs
より大きなネットワークのために,2つのGPUを用いて並列演算を実行した.2つのGPUは3番目のボリューム層とフルコネクション(FC)層を除いて独立して訓練された.1個の
Local Response Normalization
Overlapping Pooling
Reducing Overfitting
Data Augmentation
Dropout
Details of Learning
探索
Results
コード実装
CODEのコード#コード#とtwinjuiのハーモニーを参照して作成します.
import numpy as np
import pandas as pd
import tensorflow as tf
# define model parameters
NUM_EPOCHS = 1000
NUM_CLASSES = 10
IMAGE_SIZE = 227
BATCH_SIZE = 16
データプリプロセッシング.twinjuiのコードを使用しました.from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split
from tensorflow.keras.datasets import cifar10
def zero_one_scaler(image):
return image/255.0
def get_preprocessed_ohe(images, labels, pre_func=None):
# preprocessing 함수가 입력되면 이를 이용하여 image array를 scaling 적용.
if pre_func is not None:
images = pre_func(images)
# OHE 적용
oh_labels = to_categorical(labels)
return images, oh_labels
# 학습/검증/테스트 데이터 세트에 전처리 및 OHE 적용한 뒤 반환
def get_train_valid_test_set(train_images, train_labels, test_images, test_labels, valid_size=0.15, random_state=2021):
# 학습 및 테스트 데이터 세트를 0 ~ 1사이값 float32로 변경 및 OHE 적용.
train_images, train_oh_labels = get_preprocessed_ohe(train_images, train_labels)
test_images, test_oh_labels = get_preprocessed_ohe(test_images, test_labels)
# 학습 데이터를 검증 데이터 세트로 다시 분리
tr_images, val_images, tr_oh_labels, val_oh_labels = train_test_split(train_images, train_oh_labels, test_size=valid_size, random_state=random_state)
return (tr_images, tr_oh_labels), (val_images, val_oh_labels), (test_images, test_oh_labels )
# CIFAR10 데이터 재 로딩 및 Scaling/OHE 전처리 적용하여 학습/검증/데이터 세트 생성.
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
print(train_images.shape, train_labels.shape, test_images.shape, test_labels.shape)
(train_images, train_labels), (val_images, val_labels), (test_images, test_labels) = \
get_train_valid_test_set(train_images, train_labels, test_images, test_labels, valid_size=0.2, random_state=2021)
print(train_images.shape, train_labels.shape, val_images.shape, val_labels.shape, test_images.shape, test_labels.shape)
from tensorflow.keras.utils import Sequence
import cv2
import sklearn
# 입력 인자 images_array labels는 모두 numpy array로 들어옴.
# 인자로 입력되는 images_array는 전체 32x32 image array임.
class CIFAR_Dataset(Sequence):
def __init__(self, images_array, labels, batch_size=BATCH_SIZE, augmentor=None, shuffle=False, pre_func=None):
'''
파라미터 설명
images_array: 원본 32x32 만큼의 image 배열값.
labels: 해당 image의 label들
batch_size: __getitem__(self, index) 호출 시 마다 가져올 데이터 batch 건수
augmentor: albumentations 객체
shuffle: 학습 데이터의 경우 epoch 종료시마다 데이터를 섞을지 여부
'''
# 객체 생성 인자로 들어온 값을 객체 내부 변수로 할당.
# 인자로 입력되는 images_array는 전체 32x32 image array임.
self.images_array = images_array
self.labels = labels
self.batch_size = batch_size
self.augmentor = augmentor
self.pre_func = pre_func
# train data의 경우
self.shuffle = shuffle
if self.shuffle:
# 객체 생성시에 한번 데이터를 섞음.
#self.on_epoch_end()
pass
# Sequence를 상속받은 Dataset은 batch_size 단위로 입력된 데이터를 처리함.
# __len__()은 전체 데이터 건수가 주어졌을 때 batch_size단위로 몇번 데이터를 반환하는지 나타남
def __len__(self):
# batch_size단위로 데이터를 몇번 가져와야하는지 계산하기 위해 전체 데이터 건수를 batch_size로 나누되, 정수로 정확히 나눠지지 않을 경우 1회를 더한다.
return int(np.ceil(len(self.labels) / self.batch_size))
# batch_size 단위로 image_array, label_array 데이터를 가져와서 변환한 뒤 다시 반환함
# 인자로 몇번째 batch 인지를 나타내는 index를 입력하면 해당 순서에 해당하는 batch_size 만큼의 데이타를 가공하여 반환
# batch_size 갯수만큼 변환된 image_array와 label_array 반환.
def __getitem__(self, index):
# index는 몇번째 batch인지를 나타냄.
# batch_size만큼 순차적으로 데이터를 가져오려면 array에서 index*self.batch_size:(index+1)*self.batch_size 만큼의 연속 데이터를 가져오면 됨
# 32x32 image array를 self.batch_size만큼 가져옴.
images_fetch = self.images_array[index*self.batch_size:(index+1)*self.batch_size]
if self.labels is not None:
label_batch = self.labels[index*self.batch_size:(index+1)*self.batch_size]
# 만일 객체 생성 인자로 albumentation으로 만든 augmentor가 주어진다면 아래와 같이 augmentor를 이용하여 image 변환
# albumentations은 개별 image만 변환할 수 있으므로 batch_size만큼 할당된 image_name_batch를 한 건씩 iteration하면서 변환 수행.
# 변환된 image 배열값을 담을 image_batch 선언. image_batch 배열은 float32 로 설정.
image_batch = np.zeros((images_fetch.shape[0], IMAGE_SIZE, IMAGE_SIZE, 3), dtype='float32')
# batch_size에 담긴 건수만큼 iteration 하면서 opencv image load -> image augmentation 변환(augmentor가 not None일 경우)-> image_batch에 담음.
for image_index in range(images_fetch.shape[0]):
#image = cv2.cvtColor(cv2.imread(image_name_batch[image_index]), cv2.COLOR_BGR2RGB)
# 원본 image를 IMAGE_SIZE x IMAGE_SIZE 크기로 변환
image = cv2.resize(images_fetch[image_index], (IMAGE_SIZE, IMAGE_SIZE))
# 만약 augmentor가 주어졌다면 이를 적용.
if self.augmentor is not None:
image = self.augmentor(image=image)['image']
# 만약 scaling 함수가 입력되었다면 이를 적용하여 scaling 수행.
if self.pre_func is not None:
image = self.pre_func(image)
# image_batch에 순차적으로 변환된 image를 담음.
image_batch[image_index] = image
return image_batch, label_batch
# epoch가 한번 수행이 완료 될 때마다 모델의 fit()에서 호출됨.
def on_epoch_end(self):
if(self.shuffle):
#print('epoch end')
# 원본 image배열과 label를 쌍을 맞춰서 섞어준다. scikt learn의 utils.shuffle에서 해당 기능 제공
self.images_array, self.labels = sklearn.utils.shuffle(self.images_array, self.labels)
else:
pass
def zero_one_scaler(image):
return image/255.0
train_ds = CIFAR_Dataset(train_images, train_labels, batch_size=BATCH_SIZE, augmentor=None, shuffle=True, pre_func=zero_one_scaler)
val_ds = CIFAR_Dataset(val_images, val_labels, batch_size=BATCH_SIZE, augmentor=None, shuffle=False, pre_func=zero_one_scaler)
インポートしたデータの表示from matplotlib import pyplot
from keras.datasets import cifar10
# summarize loaded dataset
print('Train: X=%s, y=%s' % (train_ds[0][0][0].shape, train_ds[0][1][0].shape))
print('Test: X=%s, y=%s' % (val_ds[0][0][0].shape, val_ds[0][1][0].shape))
# plot first few images
for i in range(9):
# define subplot
pyplot.subplot(330+1+i)
# plot raw pixel data
pyplot.imshow(train_ds[0][0][i])
# show the figure
pyplot.show()
AlexNet定義.from tensorflow.keras.layers import *
class AlexNet(tf.keras.Model):
def __init__(self, num_classes=NUM_CLASSES):
super().__init__()
# self.weight = tf.keras.initializers.RandomNormal(mean=0., stddev=1.)
# self.bias = tf.keras.initializers.Constant(1.)
self.lrn = lambda x: tf.nn.local_response_normalization(x, depth_radius=5, alpha=0.0001, beta=0.75, bias=2)
self.regularizer = tf.keras.regularizers.l2(l2=0.0005)
self.net = tf.keras.Sequential([
Conv2D(
filters=96,
kernel_size=11,
strides=4,
# kernel_initializer=self.weight,
kernel_regularizer=self.regularizer
),
ReLU(),
Lambda(self.lrn),
MaxPool2D(pool_size=3, strides=2),
Conv2D(
filters=256,
kernel_size=5,
padding='same',
# kernel_initializer=self.weight,
# bias_initializer=self.bias,
kernel_regularizer=self.regularizer
),
ReLU(),
Lambda(self.lrn),
MaxPool2D(pool_size=3, strides=2),
Conv2D(
filters=384,
kernel_size=3,
padding='same',
# kernel_initializer=self.weight,
# bias_initializer='self.bias',
kernel_regularizer=self.regularizer
),
ReLU(),
Conv2D(
filters=384,
kernel_size=3,
padding='same',
# kernel_initializer=self.weight,
# bias_initializer=self.bias,
kernel_regularizer=self.regularizer
),
ReLU(),
Conv2D(
filters=256,
kernel_size=3,
padding='same',
# kernel_initializer=self.weight,
# bias_initializer=self.bias,
kernel_regularizer=self.regularizer
),
ReLU(),
MaxPool2D(pool_size=3, strides=2),
Reshape((-1, 256 * 6 * 6)),
Dropout(0.5),
Dense(4096),
ReLU(),
Dropout(0.5),
Dense(4096),
ReLU(),
Dense(num_classes, activation='softmax'),
])
def call(self, inputs):
return tf.reshape(self.net(inputs), (-1, 10))
from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping
rlr_cb = ReduceLROnPlateau(monitor='val_loss', factor=0.001, patience=5, mode='min', verbose=1)
ely_cb = EarlyStopping(monitor='val_loss', patience=15, mode='min', verbose=1)
tb_path = './tensorboard'
tensorboard_cb = tf.keras.callbacks.TensorBoard(log_dir=tb_path)
alexnet = AlexNet(num_classes=NUM_CLASSES)
# alexnet = tf.keras.models.load_model("alexnet")
alexnet.compile(
optimizer=tf.keras.optimizers.SGD(0.0001, momentum=0.9),
# optimizer=tf.keras.optimizers.Adam(0.0001),
loss=tf.keras.losses.CategoricalCrossentropy(),
metrics=['accuracy'],
run_eagerly=True
)
alexnet.fit(train_ds, verbose=1, validation_data=val_ds, epochs=NUM_EPOCHS, callbacks=[tensorboard_cb, rlr_cb, ely_cb])
test_ds = CIFAR_Dataset(test_images, test_labels, batch_size=BATCH_SIZE, augmentor=None, shuffle=False, pre_func=zero_one_scaler)
alexnet.evaluate(test_ds)
alexnet.save("alexnet")
結果
損失が減少しないことを確認するまで91個のepochを回して行った.約10時間かかります.
列車損失は0.7150,列車精度は0.9193,最終検証損失は1.1215,検証精度は0.7842であった.
その後test datasetを評価した結果,損失は1.1369,精度は0.7797であった.
オレンジ色はtrain精度、青は検証精度です.
コード実装に誤りがある.Weight初期化では,最初に論文のようにmean=0,stddev=0.01のrandom normalに初期化した.しかし、いくら待っていても、何度試しても被害はある程度までは下がらなかった.もちろん、精度も向上していません.
その結果,重み初期化器を設定せずに学習を開始し,すぐに所望の結果を得た.(上の結果)Conv layerでinitializerを設定すること自体が問題のようで、さらに原因を探す必要があるかもしれません.
Weight initializerは関係ない!問題はバイアス初期化器です.しかし、まだなぜか分からない.Biasを2つ以上の1に設定すると問題が発生し、どちらかを0.8未満に変更すると問題はありません.やはり余計な悩みが必要だ.
オプティマイザも論文のようにSGD(運動量=0.9)を用いた.その後Adam optimizerを用いて再生し,学習速度は約4倍に向上した.一般的にアダムは良さそうですが….当初、AdamはAlexNet論文の観点から2014年年に発表された.
総括と討論
参考資料
https://wikidocs.net/64066 (CNN)
https://taeguu.tistory.com/29 (local response normalization)
https://stats.stackexchange.com/questions/432896/what-is-the-loss-function-used-for-cnn (cross entropy)
https://89douner.tistory.com/60
https://github.com/dansuh17/alexnet-pytorch2https://velog.io/@twinjuy/AlexNet-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0-with-tensorflow(コード実装)
Reference
この問題について([深さ学習論文レビュー]1AlexNet), 我々は、より多くの情報をここで見つけました https://velog.io/@bumkyu00/딥러닝-논문-리뷰-1.AlexNetテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol