1. Transfer Learning 이란?
규모가 매우 큰 DNN 모델을 학습 시킬 때 처음부터 새로 학습 시키는 것은 학습 속도가 느린 문제가 있다. 이러한 경우 기존에 학습된 비슷한 DNN모델이 있을 때 이 모델의 하위층(lower layer)을 가져와 재사용하는 것이 학습 속도를 빠르게 할 수 있을 뿐만아니라 학습에 필요한 Training set도 훨씬 적다.
예를 들어, 아래의 그림처럼 CIFAR10 데이터셋을 분류(비행기, 자동차, 새, 고양이, 사슴, 개, 개구리, 말, 배, 트럭의 10개 클래스)하는 모델 A가 이 있다고 하자. 그런 다음, 분류된 CIFAR10 이미지에서 자동차의 종류를 분류하는 모델인 B를 학습시킨다고 할 때, 학습된 모델 A에서의 일부분(lower layer)을 재사용하여 모델 B를 학습 시킬 수 있다. 이러한 방법을 Transfer Learning이라고 한다.
2. 텐서플로 모델 재사용하기
텐서플로에서는 사전에 학습된 모델을 복원하여 새로운 모델을 학습시키는 데 사용할 수 있다.
텐서플로에서 제공하는 텐서플로 허브(TensorFlow Hub)는 재사용 가능한 모델을 쉽게 이용할 수 있는 라이브러리 이다. 텐서플로 허브 홈페이지에서 는 이미지, 텍스트, 비디오 등의 분야에서 사전 훈련된 모델들을 검색해 볼 수 있다.
텐서플로 2.0을 사용하면 텐서플로 허브는 따로 설치할 필요가 없고 라이브러리를 따로 불러올 수 있다.
import tensorflow as tf
print(tf.__version__) # 2.7.0
import tensorflow_hub as hub
mobile_net_url = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2"
model = tf.keras.Sequential([
hub.KerasLayer(handle=mobile_net_url,
input_shape=(224, 224, 3),
trainable=False)
])
model.summary()
먼저 tensorflow_hub 라이브러리를 hub라는 약어로 불러온다. 그 다음 합성곱 신경망의 하나인 MobileNet 버전 2를 불러온다.
MobileNet은 계산 부담이 큰 합성곱 신경망 연산 성능이 제한된 모바일 환경에서도 작동 가능하도록 네트워크 구조를 경량화 한 것이다.
MobileNet 버전 2 - 네트워크 구조 확인
ResNet-50(2560만 개), ResNet-12(6000만 개) 에 비해 상대적으로 적은 파라미터 수
ImageNet 데이터로 학습
- ImageNet에는 1천 종류의 이미지 존재(0 ~ 1000)
- 만약 어떠한 것도 분류되지 않는다면은 인덱스 0 반환(background)
from tensorflow.keras.applications import MobileNetV2
mobilev2 = MobileNetV2()
tf.keras.utils.plot_model(mobilev2)
hub.kerasLayer() 명령으로 tf.keras 에서 사용 가능한 레이어로 변환할 수 있다.
model.summary()로 파라미터 개수를 확인해보면 354만개 정도이다. ResNet-50에는 2,560만개, ResNet-152에는 6천만 개의 파라미터가 존재하는 것에 비교하면 상대적으로 파라미터 수가 적은 편에 속한다.
ImageNet 데이터 불러오기
✔ ImageNet_V2를 사용하여 평가를 진행 ( ※ ImageNet의 데이터 중 일부만 모아놓은 것)
- 클래스별 각 10개씩, 총 10,000만장의 사진이 포함되어 있는 TopImages 데이터 사용.
✔ tf.keras.utils.get_file() : 사전에 저장되어 있는 데이터를 불러올 수 있는 함수
- extract = True : tar.gz 형식의 압축파일이 자동으로 해제되어 구글 코렙 가상머신에 저장
- origin : 저장 경로
✔ pathlib.Path() : 경로 설정 함수
import os
import pathlib
content_data_url = 'sample_data'
data_root_orig = tf.keras.utils.get_file(
'imagenetV2',
'https://s3-us-west-2.amazonaws.com/imagenetv2public/imagenetv2-top-images.tar.gz',
cache_dir=content_data_url,
extract=True)
data_root = pathlib.Path(content_data_url +
'/datasets/imagenetv2-top-images-format-val')
print(data_root)
for idx, item in enumerate(data_root.iterdir()):
print(item)
if idx == 9:
break
ImageNet 라벨을 불러오기
#라벨 정보 불러오기
label_file = tf.keras.utils.get_file(
fname='label',
origin=
'https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt'
)
#빈 객체 생성
label_text = None
#text파일 열고 '읽기모드' 설정
with open(label_file, 'r') as f:
#[:-1] 처음부터 끝까지 읽기.'\n' 줄바꿈 기준으로 나눔(split)
label_text = f.read().split('\n')[:-1]
print(len(label_text))
# 1001
print(label_text[:10])
# ['background', 'tench', 'goldfish', 'great white shark', 'tiger shark',
# 'hammerhead', 'electric ray', 'stingray', 'cock', 'hen']
print(label_text[-10:])
# ['buckeye', 'coral fungus', 'agaric', 'gyromitra', 'stinkhorn', 'earthstar',
# 'hen-of-the-woods', 'bolete', 'ear', 'toilet tissue']
텍스트 전처리 하기
#nltk 패키지에서 wordnet을 다운받습니다.
import nltk
nltk.download('wordnet')
from nltk.corpus import wordnet
# wordnet과 인터넷에 올라온 label 텍스트는 조금씩 다르기 때문에 차이를 없애기 위해서 아래의 전처리 작업을 진행합니다.
label_text = [
c.lower().replace('-', '').replace('_', '').replace(' ', '')
for c in label_text
]
print(label_text[:10])
# ['background', 'tench', 'goldfish', 'greatwhiteshark', 'tigershark',
# 'hammerhead', 'electricray', 'stingray', 'cock', 'hen']
print(label_text[-10:])
# ['buckeye', 'coralfungus', 'agaric', 'gyromitra', 'stinkhorn', 'earthstar',
# 'henofthewoods', 'bolete', 'ear', 'toilettissue']
✔︎ 이미지 확인해보기
import PIL.Image as Image
import matplotlib.pyplot as plt
import random
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)
image_count = len(all_image_paths)
print('image_count:', image_count)
plt.figure(figsize=(12, 12))
for c in range(9):
image_path = random.choice(all_image_paths)
plt.subplot(3, 3, c + 1)
plt.imshow(plt.imread(image_path))
idx = int(image_path.split('/')[-2])
plt.title(str(idx) + ', ' + label_text[idx])
plt.axis('off')
plt.show()
10,000장의 이미지 중 랜덤으로 뽑은 9장의 이미지를 확인할 수 있다.
한 가지 유의할 점은 라벨 텍스트는 backgroun가 포함돼 있어서 총 1,001개의 텍스트가 있지만 실제 데이터의 라벨은 0에서 999까지의 1,000개라는 점이다. 이 차이를 무마하기 위해 idx = int(image_path.split('/')[-2]) + 1 에서는 파일 경로의 라벨 디렉터리에 해당하는 부분을 정수로 변환한 다음 1을 더해서 첫 번째 부터 1,000번째 까지의 라벨 텍스트와 동일한 값을 가리키게 한다.
ImageNet 대회에서는 네트워크가 예측하는 값 중 상위 5개 이내에 데이터의 실제 분류가 포함돼 있으면 정답으로 인정하는 TOP-5 정확도를 분류 정확도로 측정했다. 그런데 요즘은 신경망의 성능이 높아져서 TOP-1 정확도, 즉 가장 높은 값이 데이터의 실제 분류와 일치하는 비율을 놓고 경쟁하는 추세다.
MobileNet 정확도 확인 ( ※ 약 10 ~ 15분 소요 )
- Top - 5 정확도 : 전통적으로 ImageNet 대회에서는 신경망이 예측하는 값 중 상위 5개 이내에 데이터의 실제 분류가 포함돼 있으면 정답인정
- Top - 1 정확도 : 신경망이 예측하는 값 중 상위 1개 이내에 데이터의 실제 분류가 포함돼 있으면 정답으로 인정
import cv2
import numpy as np
#빈 객체 생성
top_1 = 0
top_5 = 0
# all_image_paths에 저장되어 있는 경로를 하나씩 불러오며 반복
for image_path in all_image_paths[:]: #cv2.imread() : 사진 읽어들이기
img = cv2.imread(image_path)
#cv2.resize() : 사진 크기 재조정 (픽셀 통일)
img = cv2.resize(img, dsize=(224, 224))
# 최소 최대 정규화
img = img / 255.0
#np.expand_dims( axis = 0 ) : 첫 번째 차원 추가
img = np.expand_dims(img, axis=0)
#예측값에 대한 순위 정보[0]를, argsort()로 index 정렬(오름차순)
#[::-1] Extended Slices 기법으로 내림차순으로 변경, [:5] 그 중 top 5개
top_5_predict = model.predict(img)[0].argsort()[::-1][:5]
#라벨링 번호 도출
idx = int(image_path.split('/')[-2]) + 1
# 신경망은 반복문을 타고 넘어온 n번째 img를 가지고 예측을 수행해서 5개의 TOP 후보(top_5_predict)를 추렸습니다. # 만약 이 n번째 img가 신경망이 가장 높게 예측한 후보 5개 안에 들어있다면은,
if idx in top_5_predict:
# top_5 에 1 추가
top_5 += 1
# 또 만약에 가장 높게 예측한 것[0]과 idx과 같다면은
if top_5_predict[0] == idx: #top_1 에 1을 추가
top_1 += 1
#최종 출력
# 정확하게 예측한 경우 / 전체 사진 수
print('TOP - 5 정확성 :', top_5 / len(all_image_paths) * 100, '%') # TOP - 5 정확성 : 83.52000000000001 %
print('TOP - 1 정확성 :', top_1 / len(all_image_paths) * 100, '%') # TOP - 1 정확성 : 59.06 %
import cv2
plt.figure(figsize=(16, 16))
def softmax(x):
max_elem = np.max(x)
diff = (x - max_elem)
exp = np.exp(diff)
sum_exp = np.sum(exp)
probs = (exp / sum_exp)
return probs
for c in range(3):
image_path = random.choice(all_image_paths)
plt.subplot(3, 2, c * 2 + 1)
plt.imshow(plt.imread(image_path))
idx = int(image_path.split('/')[-2]) + 1
plt.title(str(idx) + ', ' + label_text[idx])
plt.axis('off')
plt.subplot(3, 2, c * 2 + 2)
img = cv2.imread(image_path)
img = cv2.resize(img, dsize=(224, 224))
img = img / 255.0
img = np.expand_dims(img, axis=0)
logits = model.predict(img)[0]
prediction = softmax(logits)
top_5_predict = prediction.argsort()[::-1][:5]
labels = [label_text[index] for index in top_5_predict]
color = ['gray'] * 5
if idx in top_5_predict:
color[top_5_predict.tolist().index(idx)] = 'green'
color = color[::-1]
plt.barh(range(5), prediction[top_5_predict][::-1] * 100, color=color)
plt.yticks(range(5), labels[::-1])
'DL(Deep-Learning) > Tensorflow' 카테고리의 다른 글
[Error] tf.keras.models.load_model 에러 해결방법 (0) | 2022.01.30 |
---|---|
[TF Hub] 전이학습 Transfer Learning (0) | 2022.01.30 |
[Keras] callback 함수 - ReduceLROnPlateau (0) | 2022.01.25 |
[Keras] callback 함수 - EarlyStopping (0) | 2022.01.25 |
[Mac M1] Tensorflow 설치 방법 (0) | 2022.01.24 |