AI-PM의 마지막 강의인 Convolutional Neural Network(CNN)이다. 딥 러닝이라고 했을 때 가장 먼저 생각나는 대표적인 알고리즘이다. 그리고 딥 러닝 붐을 일으킨 장본인이기도 하다.
Copyright © 2018. Alina Inc. All Rights Reserved.
이전 Neural Network에서 실습했던 구조는 다음과 같다. 모든 뉴런들이 서로 거미줄 같이 촘촘하게 연결되어 있었다. 그래서 학습시켜야 할 매개변수들도 무지하게 많았었다. 이러한 구조를 Fully Connected Layer 구조라고 한다.
Copyright © 2018. Alina Inc. All Rights Reserved.
나도 그렇고 이 Fully Connected Layer가 가진 고질적인 문제들이 있다. 위에서 밝히고 있는 바와 같이 매개변수가 너무 많아서 연산이 복잡함은 물론이고, 서로 관계가 없는 픽셀들 사이에도 패턴을 찾으려고 한다.
이것이 무슨 뜻인지 글로만 보아서는 이해가 힘들 것 같아 그림으로 나타내면,
이 그림이 7인 것을 인식하는 것과, 초록새 두 부분 사이에서 패턴을 찾아내는 것에는 전혀 상관이 없다는 것이다.
차라리 빨간색 부분과 같이 ㄱ자와 같은 패턴을 가지면 7이다!! 라고 하면 몰라도...
그리고, 기존의 Fully Connected Neural Network에서는 노란 7과 하얀 7이 완전히 다른 것으로 인식한다. 사실 우리가 보기에는 이미지에서 위치만 바뀌었을 뿐 비슷한 7로 보임에도 말이다.
Copyright © 2018. Alina Inc. All Rights Reserved.
그래서 이러한 단점들을 극복하기 위해서 Convolution연산을 Neural Network에 도입하기로 한 것이다. 특징들을 살펴 보자면, 앞선 단점들을 보완하면서, 연산의 양도 크게 줄였다는 내용이 보인다. 다만, 그로 인해서 단점들이 조금 생겼지만, 그에 비해 장점의 영향력이 너무나도 커서 이미지와 관련된 거의 모든 Neural Network에서는 Convolution을 사용한다고 한다.
Copyright © 2018. Alina Inc. All Rights Reserved.
그럼, Convolution이 뭐냐! 이러한 연산을 말하는 것이다. 합성곱이라고도 하는데, 단순한 곱셈이 아니라 weight를 가진 필터를 옮겨가면서 수행한 곱의 합을 말한다. 다만, 이 과정의 결과는 원래 데이터 크기보다 작을 수밖에 없다.
Copyright © 2018. Alina Inc. All Rights Reserved.
Neural Network를 배우면서 보았던 고양이 실험에서와 같이 Edge등의 간단한 패턴에 뉴런이 반응한다고 했다. 위 그림은 x자와 같은 패턴에 반응하게(이런 패턴이 있으면 더 큰 결과값을 가지도록) Convolution을 수행하는 모습이다.
Copyright © 2018. Alina Inc. All Rights Reserved.
한 가지 더 배워야 할 개념으로 Pooling이라는 것이 있다. 대표되는 값을 사용해서 데이터의 양을 줄이는 방법인데, 그림을 보면 바로 알 수 있을 듯 하다. Max Pooling과 Average Pooling이 있는데, 주로 사용되는 것은 Max Pooling이라고 한다.
Copyright © 2018. Alina Inc. All Rights Reserved.
그럼 앞선 배운 개념들을 이용해서 실질적으로 MNIST Dataset에 대해 CNN을 수행하는 과정에 대해서 알아보자. 지난 방법들에서는 784개의 픽셀들을 전부 늘어놨는데, 필터 처리를 해 주어야 하기 때문에 그렇게 하지 않는다. 필터 처리를 하면 28*28*64개의 데이터가 나온다고 하는데 왜 그러하냐면 한번 필터처리를 할 때마다 데이터 한 장이 나오는데 총 64개의 필터를 사용하므로 그런 것이다.
왜 64개라는 숫자가 나왔느냐 하면, 가로, 세로, 대각선... 이러한 총 64개의 패턴들을 Detect 하겠다는 의미이다. 그런 다음 채널별로 bias(input과 상관없이 더해주는 값)64개를 더해주고, ReLU
(Activation Function)도 거쳐준다.
다음으로 Max Pooling으로 이미지 사이즈를 4->1로 줄여 준다. 그래서 그림에서 보면 28*28*64였던 것이 14*14*64가 되어 있음이 보인다. 그리고 Reshape(또는 re-ordering)을 거치는데, 특별한 것은 아니고 그저 데이터들을 일렬로 주루룩 나열해 주는 것이다.
이렇게 줄을 세운 상황에서 이제 Fully Connected Layer를 구축하여 Ouput을 10개(우리가 원래 하려던 것이 10개의 숫자를 인식하는 것임을 잊지 말자)로 정해 주고 학습을 시킨다.
결론적으로 학습하는 것이 무엇인가~ 하면, Convolution Filter안의 값들을 학습하고, bias들을 학습하고, Fully Connected Layer시 weight와 bias들을 학습한다!
Copyright © 2018. Alina Inc. All Rights Reserved.
결과는 이러하다! Multi-layer Neural Network와 비교해서 정확도는 비슷한데, 학습시켜야 할 매개변수들의 수는 1/4정도로 많이 줄어들었다. 더불어, 이 상황에서 결과 개선을 위해 더 Deep한 Network를 만들고자 한다면, Multi-layer Neural Network의 경우에서 Overfitting이 일어나버려서 오히려 결과가 더 나빠진다. 그러나 CNN의 경우에는 한 층만 더 쌓아도 99%에 육박할 정도로 성능의 개선을 보인다고 하니, 이미지 학습에서는 거의 CNN을 사용한다는 말이 실감이 난다.
일단, 앞서 해보았던 Multi-layer Neural Network 실습의 Convolutional Neural Network를 사용한 버전을 보이는데, 그다지 중요성은 없다고 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | import math import numpy as np import matplotlib.pyplot as plt import tensorflow as tf from tensorflow import keras tf.set_random_seed(173) np.random.seed(173) def get_data(): mnist = keras.datasets.mnist to_categorical = keras.utils.to_categorical (train_data, train_labels), (test_data, test_labels) = mnist.load_data() # uint8 data type을 float32 data type으로 변환해서 float_train_data에 저장합니다. float_train_data = train_data.astype(np.float32) # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다. float_train_data /= 255.0 # 3차원 array을 4차원 array로 변경합니다. float_train_data = np.reshape(float_train_data, [-1, 28, 28, 1]) # label을 one hot encoding을 하여 onehot_train_labels에 저장합니다. onehot_train_labels = to_categorical(train_labels, num_classes=10) # uint8 data type을 float32 data type으로 변환해서 float_test_data에 저장합니다. float_test_data = test_data.astype(np.float32) # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다. float_test_data /= 255.0 # 3차원 array을 4차원 array로 변경합니다. float_test_data = np.reshape(float_test_data, [-1, 28, 28, 1]) # label을 one hot encoding을 하여 onehot_test_labels에 저장합니다. onehot_test_labels = to_categorical(test_labels, num_classes=10) return float_train_data, onehot_train_labels, float_test_data, onehot_test_labels def draw_mnist_image(image, title=''): # 그림에 title을 설정합니다. plt.title(title) # image가 28 * 28이라고 가정하고 그림을 그립니다. plt.imshow(np.reshape(image, [28, 28]), cmap='gray') # 그림을 화면에 보여줍니다. plt.show() x_data, y_data, x_test_data, y_test_data = get_data() x_input = tf.placeholder(tf.float32, [None, 28, 28, 1]) y_input = tf.placeholder(tf.float32, [None, 10]) weight_1_var = tf.Variable(tf.truncated_normal([3, 3, 1, 64], stddev=0.5)) bias_1_var = tf.Variable(tf.zeros([64])) weight_2_var = tf.Variable(tf.truncated_normal([14 * 14 * 64, 10], stddev=0.01)) bias_2_var = tf.Variable(tf.zeros([10])) conv_1 = tf.nn.conv2d(x_input, weight_1_var, strides=[1, 1, 1, 1], padding='SAME') bias_1 = tf.nn.bias_add(conv_1, bias_1_var) relu_1 = tf.nn.relu(bias_1) pool_1 = tf.nn.max_pool(relu_1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') reshape_2 = tf.reshape(pool_1, [-1, 14 * 14 * 64]) matmul_2 = tf.matmul(reshape_2, weight_2_var) logit = matmul_2 + bias_2_var y_output = tf.nn.softmax(logit) cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_input, logits=logit) cost_output = tf.reduce_mean(cross_entropy) learning_rate = 0.01 train_step = tf.train.AdamOptimizer(learning_rate).minimize(cost_output) sess = tf.Session() step = 0 cost = 0 mini_batch = 1024 max_epoch = 3 # variable(weight, bias)들을 초기화합니다. sess.run(tf.global_variables_initializer()) # max_epoch만큼 반복하며 cost가 작아지는 weight, bias들을 찾습니다. for epoch in range(max_epoch): # mini_batch단위만큼 잘라서 반복합니다. for i in range(int(math.ceil(len(x_data) / mini_batch))): # Gradient Descent를 사용하여 cost가 약간 작아지도록 weight, bias들을 변경합니다. _, cost = sess.run( [train_step, cost_output], feed_dict={ x_input: x_data[i * mini_batch:(i + 1) * mini_batch], y_input: y_data[i * mini_batch:(i + 1) * mini_batch]}) step += 1 print('epoch: {}, train_step: {}, cost: {}'.format(epoch, step, cost)) y_pred = sess.run(y_output, feed_dict={x_input: x_test_data}) y_data_class = np.argmax(y_test_data, axis=1) y_pred_class = np.argmax(y_pred, axis=1) y_right_class = y_data_class == y_pred_class accuracy = np.mean(y_right_class) print('accuracy', accuracy) y_wrong_class = y_data_class != y_pred_class for i in range(10): title = 'ground truth: {}, prediction: {}'.format( y_data_class[y_wrong_class][i], y_pred_class[y_wrong_class][i]) draw_mnist_image(x_test_data[y_wrong_class][i], title) for i in range(3): print('y_data_class', y_data_class[y_wrong_class][i]) print('y_test_data', y_test_data[y_wrong_class][i]) print('y_pred_class', y_pred_class[y_wrong_class][i]) print('y_pred', y_pred[y_wrong_class][i]) sess.close() | cs |
지금 내가 사용하는 데스크탑의 CPU가 라이젠 2700X인데, 상당히 시간이 걸렸다. ㅎㄷㄷ... 보통 딥러닝 연구실이나, 교육 프로그램을 보아도, 원격 서버를 사용하거나, 고사양의 GPU를 달아 놓는다. 나는 지금 윈도우 환경이어서 CPU를 혹사시켜서 결과를 얻은 것... (CPU 미안 ㅠ) 98%정도의 정확도를 보였고, 실수한 경우들은 다음과 같았다.
2인데 7이라 함
뭐, 그럴만 했다고 생각한다. 인간이 보았어도 다소 논란의 여지가 있을 법 한 상황이었다.
Copyright © 2018. Alina Inc. All Rights Reserved.
이번에는 딥 러닝의 역사(?)에 대해서 배워보자. 사실 Neural Network라는 것이 생긴 것은 꽤 오래 전이다. 다만, 이를 위해서는 많은 연산과 데이터셋이 필요해서 많이 사용하지 않고 있었는데, 2012년 ImageNet Large Scale Visual Recognition Challenge(길다...)에서 AlexNet이라는 팀이 딥러닝을 이용해 비약적인 성과를 내면서 주목받게 되었다.
당시 딥러닝을 위한 어려움을 해결해준 요소들로 NVIDIA에서 외부 연산을 허용해준 것과, 사진들 활동들이 활성화되면서 데이터들이 많이 쌓인 이유 등이 있다. 위의 Top-5 Error라는 것이 무엇인가 하면, 인공지능에게 총 5번의 맞출 수 있는 기회를 주는 것이다.
강아지 사진이 있을 때
뭐니??
1. 고양이요. 아닌데?
2. 사람이요. 아닌데?
3. 쿠키요. 아닌데?
4. 다람쥐요. 아닌데?
5. 강아지요. 어 맞어!
이렇게 5번의 기회를 주면서 이를 맞추게 되면 잘 학습한 것으로 보는 것이다. 20% 의 벽을 넘지도 못하고 있던 당시 AlexNet이 10% 넘게 개선을 하게 되면서 주목을 끌게 된 것은 당연한 것이었다. 더불어 인간은 보통 5%의 에러를 가진다고 한다.
Copyright © 2018. Alina Inc. All Rights Reserved.
AlexNet의 성공 이후, 수많은 굵직한 기업들도 이 대회에 참가하였다. 구글이나 MS같은 회사들도 참가한 것이 보인다. 그리고 에러도 상당히 개선되어서 인간보다 더 정확한 인공지능들이 등장해 버린 것이다.
Copyright © 2018. Alina Inc. All Rights Reserved.
이렇게 수많은 CNN기법들이 등장하면서 인공지능을 어떻게 만들까 고민하기보다. CNN 중에서도 목적에 맞게 선택해서 쓸 수 있게 되었다. 오른쪽 그래프를 보면 알 수 있는데, 오른쪽으로 갈 수록 연산의 양이 많아지고, 위로 갈수록 정확도가 높아지며, 원의 크기가 클 수록 Network의 사이즈가 커진다. 더불어 이는 2017년 기준이라, 지금도 계속해서 리뉴얼 되고 있는 상황이다.
Copyright © 2018. Alina Inc. All Rights Reserved.
그래서 결론은, .Neural Network를 직접 설계하기보다는, 잘 가져다 쓰자는 것이다. 사실 이전의 예제들에서도 tensorflow, keras에서 제공하는 함수들을 열심히 가져다 썼었다. 항상 그렇지만 엔지니어는 잘 가져다가 참신성을 잘 붙이는 작업이 필요한 직업이다.
그럼, 드디어!! 고양이와 개를 분류하는 분류기를 만들어 보자!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import numpy as np import matplotlib.pyplot as plt from urllib.request import Request, urlopen from PIL import Image from tensorflow.keras.preprocessing.image import img_to_array def load_image(url, image_size): # url로부터 image를 읽어옵니다. image = Image.open(urlopen(Request(url))) # image_size * image_size 크기로 resize합니다. image = image.resize([image_size, image_size]) # img_to_array를 사용하여 numpy array로 변환하고 channel은 rgb만 사용하고 alpha는 사용하지 않습니다. return img_to_array(image)[:, :, 0:3] def draw_image(image): plt.imshow(image.astype(np.uint8)) plt.show() #MODEL_NAME = 'vgg16' MODEL_NAME = 'inception_v3' if MODEL_NAME == 'vgg16': from tensorflow.keras.applications.vgg16 import preprocess_input from tensorflow.keras.applications.vgg16 import decode_predictions from tensorflow.keras.applications.vgg16 import VGG16 model = VGG16() image_size = 224 elif MODEL_NAME == 'inception_v3': from tensorflow.keras.applications.inception_v3 import preprocess_input from tensorflow.keras.applications.inception_v3 import decode_predictions from tensorflow.keras.applications.inception_v3 import InceptionV3 model = InceptionV3() image_size = 299 image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/cat01.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/cat02.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/cat03.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/cat04.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/cat05.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/dog01.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/dog02.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/dog03.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/dog04.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/dog05.jpg' # image_url로부터 image를 읽어서 image에 저장합니다. image = load_image(image_url, image_size) # image를 그립니다. draw_image(image) # image를 model에 넣기 위한 형태로 전처리를 합니다. image = preprocess_input(image) # 3차원 array를 4차원으로 변환하여 복수개의 image의 형태로 만들어 줍니다. images = np.expand_dims(image, axis=0) # image를 model에 넣어서 인식결과를 yhat에 저장합니다. yhat = model.predict(images) # 인식결과를 사람이 읽을 수 있는 형태로 변환한 다음 첫번째 image의 결과를 label에 저장합니다. label = decode_predictions(yhat)[0] # 인식률이 높은 것부터 하나씩 정보를 출력합니다. for i in range(len(label)): print('{}: {}'.format(label[i][1], label[i][2])) | cs |
tensorflow의 keras를 이용해서 이미 완성되어 있는 모델을 가져다 쓴 것이기 때문에 코드가 그리 길지는 않다. 설명을 하자면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #MODEL_NAME = 'vgg16' MODEL_NAME = 'inception_v3' if MODEL_NAME == 'vgg16': from tensorflow.keras.applications.vgg16 import preprocess_input from tensorflow.keras.applications.vgg16 import decode_predictions from tensorflow.keras.applications.vgg16 import VGG16 model = VGG16() image_size = 224 elif MODEL_NAME == 'inception_v3': from tensorflow.keras.applications.inception_v3 import preprocess_input from tensorflow.keras.applications.inception_v3 import decode_predictions from tensorflow.keras.applications.inception_v3 import InceptionV3 model = InceptionV3() image_size = 299 | cs |
vgg16과 Google사의 inception중에서 선택을 할 수가 있다. inception이 상대적으로 연상할 양이 적기 때문에 일단은 이것을 사용하였고, inception에서 원하는 기본 이미지 사이즈가 299*299 이기 때문에 사이즈도 설정해 준다.
1 2 | # image를 model에 넣기 위한 형태로 전처리를 합니다. image = preprocess_input(image) | cs |
preprocess란 무엇인가 하면, CNN의 뉴런들 마다 원하는 이미지 포멧이 다른데(RGB, BGR, -1~0 등등) 이 함수가 적절히 변환을 해서 뉴런들에 할당을 해주는 것이다.
1 2 | # 3차원 array를 4차원으로 변환하여 복수개의 image의 형태로 만들어 줍니다. images = np.expand_dims(image, axis=0) | cs |
array를 변환해준다고 써 있는데, 우리는 지금 이미지 1개를 input으로 넣어주지만, 원래는 여러 개의 이미지들을 넣어주게 되어 있으므로 [이미지 개수, 높이, 넓이, 채널] 이런 input이 필요해서 차원을 높여주는 것이다. 아마 [1, 299, 299, 3] 이렇게 만들어질 것이다.
1 2 | # image를 model에 넣어서 인식결과를 yhat에 저장합니다. yhat = model.predict(images) | cs |
인식의 결과를 사람이 읽기 쉬운 형태로 보여주는 부분이다. 이를 decode prediction이라고 한다. 위에서 보면 이 함수들을 전부 import해 주었다는 것이 보인다.
1 2 3 4 5 | # 인식결과를 사람이 읽을 수 있는 형태로 변환한 다음 첫번째 image의 결과를 label에 저장합니다. label = decode_predictions(yhat)[0] # 인식률이 높은 것부터 하나씩 정보를 출력합니다. for i in range(len(label)): print('{}: {}'.format(label[i][1], label[i][2])) | cs |
이미지 한 장으로 했으므로 첫 번째 결과만 가져와서 보여준다.
샤미즈 고양이라고!! 오홍 제대로 인식했다
그럼 다른 사진을 사용하자~~ 이번에는 혼란을 주기 위해 아래와 같은 심쿵 고양이 사진을 넣어봤다.
확률이 낮긴 하지만, 고양이라고 인식을 하였다~!!
고양이 꼬리를 넣어도~ 잘 인식하고
핡... 심쿵 강아지 사진도 잘 인식한다!!
Copyright © 2018. Alina Inc. All Rights Reserved.
이번에는 Transfer Learning에 대해서 배워 보자. 이게 무엇인가 하면, CNN을 사용하면서 대부분의 매개변수들은 고정(원래 100만 장으로 학습해서 연구자들이 구해 놓을 값)시킨 다음 마지막 뉴런만 건드려서(Class의 수를 조정한다던가) 우리가 원하는 입맛대로 분류기를 만드는 것이다.
이것이 어떻게 가능하는냐 하냐면, 어차피 CNN의 초기 부분에서는 Edge등의 아주 간단한 패턴들을 학습하므로 이는 대부분의 이미지들에게도 마찬가지일 것이기 때문이다. 그래서 만약, 현미경 사진, 천체 사진과 같이 특이한 경우들에 대해서는 Pretrained Model을 사용해도 별 효과가 없을 수도 있다.
나는 이쯤에서 방금 한 기본 CNN과 이것과의 차이가 무엇인지 혼란이 와 버렸다. 그래서 정리를 하자면,
● 기본 CNN : input으로 이미지를 넣어 주면 원래 가지고 있는 Class중에서 골라줌
● Transfer Learning : input으로 우리가 구별하고 싶은 사진, output으로 그게 뭔지 넣어서 기존 CNN 마지막 Fully Connected 부분만 재학습
Copyright © 2018. Alina Inc. All Rights Reserved.
새롭게 배우는 것으로 Data Augmentation이라는 것이 있다. 위 사진을 보면 같은 이미지를 여러 각도로 바꾸기도 하고, 명암을 조절하기도 하고, 색상을 반전시키기도 하면서, 한 이미지로 데이터를 뻥튀기(?)시켜 놓은 것이 보인다. 사실 CNN은 이들을 모두 같다고 인식하지 못한다. 그래서 가진 데이터의 양이 적을 경우 이렇게 양을 불려서 학습을 시킬 수도 있다는 것이다.
결론적으로 Transfer Learning은 가진 데이터가 작아서 CNN을 모두 학습시킬 수는 없을 경우 많이 사용하게 된다. - Data Augmentation과 함께
그럼, Transfer Learning 실습을 한번 해 보자. 손과 눈을 분류해 보는 실습인데, ImageNet에 손과 눈은 분류 항목으로 없다. 그래서 우리가 직접 해 보자는 것이다. train set 100개 validation 50개로 설정했고, VGG16의 weight들을 가져다가 쓴다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | import os import pathlib import tarfile import numpy as np import matplotlib.pyplot as plt import tensorflow as tf from PIL import Image from urllib.request import Request, urlopen from tensorflow.keras import optimizers from tensorflow.keras.applications.vgg16 import preprocess_input from tensorflow.keras.applications.vgg16 import VGG16 from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array from tensorflow.keras.models import Sequential, Model, load_model from tensorflow.keras.layers import Dropout, Flatten, Dense tf.set_random_seed(173) np.random.seed(173) def download_and_unzip(url, path): if not os.path.isfile(path): with urlopen(url) as f: pathlib.Path(path).write_bytes(f.read()) with tarfile.open(path) as tar: tar.extractall() download_and_unzip( 'https://github.com/salopge/datasets/raw/master/cnn_images/data_eye_hand.tgz', 'data_eye_hand.tgz') def load_image(url, image_size): # url로부터 image를 읽어옵니다. image = Image.open(urlopen(Request(url))) # image_size * image_size 크기로 resize합니다. image = image.resize([image_size, image_size]) # img_to_array를 사용하여 numpy array로 변환하고 channel은 rgb만 사용하고 alpha는 사용하지 않습니다. return img_to_array(image)[:, :, 0:3] def draw_image(image): plt.imshow(image.astype(np.uint8)) plt.show() # vgg16의 기본 image size인 224를 image_size 에 저장합니다. image_size = 224 # train set image 가 저장되어 있는 장소를 train_data_dir 에 저장합니다. train_data_dir = 'data/train' # validation set image 가 저장되어 있는 장소를 validation_data_dir 에 저장합니다. validation_data_dir = 'data/val' # mini batch size를 16으로 설정하여 batch_size에 저장합니다. batch_size = 16 # 1 epoch만 학습시키기 위해 epochs에 1을 저장합니다. epochs = 1 # 분류할 종류의 수(눈, 손)를 num_classes에 저장합니다. num_classes = 2 # fully connected layer를 제외한 vgg16 imagenet pretrained model을 생성하여 vgg_conv에 저장합니다. vgg_conv = VGG16( weights='imagenet', include_top=False, input_shape=(image_size, image_size, 3)) # vgg_conv의 모든 layer를 학습되지 않도록 설정합니다. for layer in vgg_conv.layers: layer.trainable = False # model을 새로 생성합니다. model = Sequential() # model에 vgg_conv를 추가합니다. model.add(vgg_conv) # vgg_conv의 마지막 convolution부분을 한 줄로 펼칩니다. model.add(Flatten()) # 1024개의 neuron을 output으로 가지는 fully conntected layer를 만듭니다. # activation function은 relu로 설정합니다. model.add(Dense(1024, activation='relu')) # num_classes(눈, 손)만큼의 neuron을 만들고 softmax를 연결하여 확률이 출력되도록 합니다. model.add(Dense(num_classes, activation='softmax')) # cost function은 logistic regression에 일반적으로 사용하는 cost function을 설정합니다. # optimizer는 Momentum을 사용하고 learning rate는 0.0001, momentum값은 0.9로 설정합니다. # 학습하면서 accuracy를 출력합니다. model.compile( loss='categorical_crossentropy', optimizer=optimizers.SGD(lr=0.0001, momentum=0.9), metrics=['accuracy']) # train set image는 vgg16용 preprocess_input을 사용하여 preprocess를 합니다. # 각종 data augmentation기법을 사용하여 무작위로 확대축소, 회전, 반전 등을 해 줍니다. train_datagen = ImageDataGenerator( preprocessing_function=preprocess_input, horizontal_flip=True, fill_mode='nearest', zoom_range=0.3, width_shift_range=0.3, height_shift_range=0.3, rotation_range=30) # validation set image는 vgg16용 preprocess_input을 사용하여 preprocess를 합니다. val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input) # train_data_dir에 저장되어 있는 image data들을 train set data로 사용합니다. # image data들은 image_size * image_size 크기로 변환하여 사용합니다. # mini batch size는 batch_size로 설정합니다. train_generator = train_datagen.flow_from_directory( train_data_dir, target_size=(image_size, image_size), batch_size=batch_size) # validation_data_dir에 저장되어 있는 image data들을 validation set data로 사용합니다. # image data들은 image_size * image_size 크기로 변환하여 사용합니다. validation_generator = val_datagen.flow_from_directory( validation_data_dir, target_size=(image_size, image_size)) # epochs에 저장되어 있는 epoch수 만큼 눈, 손 분류기를 학습합니다. model.fit_generator( train_generator, epochs=epochs, validation_data=validation_generator, verbose=1) model.save('vgg16_eye_hand.h5') del model model = load_model('vgg16_eye_hand.h5') image_size = 224 #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand01.jpg' image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand02.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand03.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand04.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand05.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand06.png' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand07.jpg' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand08.png' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand09.png' #image_url = 'https://raw.githubusercontent.com/salopge/datasets/master/images/eyehand10.jpg' # image_url로부터 image를 읽어서 image에 저장합니다. image = load_image(image_url, image_size) # image를 그립니다. draw_image(image) # image를 model에 넣기 위한 형태로 전처리를 합니다. image = preprocess_input(image) # 3차원 array를 4차원으로 변환하여 복수개의 image의 형태로 만들어 줍니다. images = np.expand_dims(image, axis=0) # image를 model에 넣어서 인식결과를 yhat에 저장합니다. yhat = model.predict(images) # 인식결과를 출력합니다. # 인식결과는 label의 alphabet순으로(eye, hand순으로) 출력됩니다. print(yhat) | cs |
다음과 같은데, 역시 설명을 더 붙여야 할 것 같다.
1 2 3 4 5 6 | vgg_conv = VGG16( weights='imagenet', include_top=False, input_shape=(image_size, image_size, 3)) for layer in vgg_conv.layers: layer.trainable = False | cs |
VGG16의 Pretrained된 Weight들을 사용하도록 해준다.(Imagenet Data 들에 대해서), 그리고 마지막 Fully Connected 부분은 False로 제외를 시켜 주고, Convolution 필터들의 값은 학습이 되지 않도록 고정해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 1024개의 neuron을 output으로 가지는 fully conntected layer를 만듭니다. # activation function은 relu로 설정합니다. model.add(Dense(1024, activation='relu')) # num_classes(눈, 손)만큼의 neuron을 만들고 softmax를 연결하여 확률이 출력되도록 합니다. model.add(Dense(num_classes, activation='softmax')) # cost function은 logistic regression에 일반적으로 사용하는 cost function을 설정합니다. # optimizer는 Momentum을 사용하고 learning rate는 0.0001, momentum값은 0.9로 설정합니다. # 학습하면서 accuracy를 출력합니다. model.compile( loss='categorical_crossentropy', optimizer=optimizers.SGD(lr=0.0001, momentum=0.9), metrics=['accuracy']) | cs |
마지막의 1024개의 뉴런은 Fully Connected 형태로 직접 설계해 준다. 보면 ReLU를 사용하고, softmax로 0-1사이의 값이 나오도록 해 놓은 것이 보인다. 매개변수들을 최적화 할 때에는 logistic regression때와 같은 cost function을 사용하고, momentum을 이용해 학습시킨다.
1 2 3 4 5 6 7 8 9 10 | # train set image는 vgg16용 preprocess_input을 사용하여 preprocess를 합니다. # 각종 data augmentation기법을 사용하여 무작위로 확대축소, 회전, 반전 등을 해 줍니다. train_datagen = ImageDataGenerator( preprocessing_function=preprocess_input, horizontal_flip=True, fill_mode='nearest', zoom_range=0.3, width_shift_range=0.3, height_shift_range=0.3, rotation_range=30) | cs |
Train Data들에 대해서 Data Augmentation을 수행해 준다. validation data들은 굳이 해 줄 필요가 없어서 preprocessing만 거쳐 준다.
1 2 3 4 | model.save('vgg16_eye_hand.h5') del model model = load_model('vgg16_eye_hand.h5') image_size = 224 | cs |
'
모델의 매개변수들을 저장했다가 불러올 수도 있는데, 이는 시험삼아서 보여주고 있는 부분이다.
2개의 one-hot encoding 결과에서 눈, 손 순서인데, 위 사진에서는 눈이라고 인식하였다.
손도 손이라고 잘 인식을 하는 모습이다. (99% 손이라고 하고 있다.)
그럼 이런 경우에는??? 손 위에 눈 문신이 있다!! 일단 인공지능은 눈이라고 했는데, 뭐 틀린 말은 아니다.
그럼, 자동차를 넣으면??? 뜬금없지만 눈이라고 했다. 헤드 라이트를 눈이라고 인식한 것인가...
이 부분에서 CNN은 이상한 Input을 넣어도 어떻게든 결과는 내놓으려 한다는 것을 알 수 있다.
이렇게 눈도 손도 아닌 물체를 넣어 주었다면, output으로 눈, 손, 둘다 아님 이렇게 학습을 시켰어 야 할 것이다.
Copyright © 2018. Alina Inc. All Rights Reserved.
이번에는 CNN을 이용해서 Image간의 유사도를 측정할 수 있는 활용에 대해서 알아보자. CNN의 마지막 과정에서 몇 층 정도의 Fully Connected Layer가 있다는 것을 계속해서 말하고 있다. 그런데, 이 마지막 과정을 그대로 가져다 Embedding시킨다. 그래로 가져다가 고정시킨다는 뜻이다. 그러면 이는 곧 뉴런 개수 만큼의 차원을 가진 점들로 생각을 할 수가 있다. 위애서는 4096차원이라고 하였는데, 상상하기 쉽지는 않지만, 4096차원에 존재하는 점들로 생각을 할 수가 있는 것이다.
그러면 이 점들 사이의 거리를 계산할 수도 있다는 뜻!! Euclidean distance나 Cosine distance등을 이용해서 거리를 구하면 유사한 사진들을 구별해 낼 수 있다는 것이다! 혹은 비슷한 사진들끼리 묶는 Clustering도 할 수가 있는 것이다. 아래 사진처럼 말이다.
Copyright © 2018. Alina Inc. All Rights Reserved.
보면, 비슷한 모양을 보이는 사진들끼리 묶어서 보이고 있는 것이 보인다. 이렇게 많은 사진을 분류하는 것은 시간도 많이 걸리고 CPU가 혹사당하기에 두 개의 사진만 넣어서 얼마나 유사성을 보이는지 실습을 통해 알아보자.
--------------------------------------코드는 준비중 입니다---------------------------------------
'공부 > AI' 카테고리의 다른 글
Convolutional Neural Network 공부했다.(2) (0) | 2018.12.27 |
---|---|
Neural Network 공부했다. - Multi Layer Neural Network && Autoencoder (0) | 2018.12.24 |
Neural Network 공부했다.- Single Layer Neural Network (0) | 2018.12.22 |
Machine Learning Methodology & analysis 공부했다 (0) | 2018.12.19 |
Recommender System 공부했다. (0) | 2018.12.16 |