개요
컴퓨터 비전 딥러닝 모델을 실행하려면, 필요한 훈련 데이터가 적절하게 준비되어야 한다. 여기에는 단순히 데이터의 저장 경로를 지정하고, 올바른 레이블을 부여하는 것 뿐만 아니라 데이터 확장(data augmentation)을 포함한 데이터 전처리(preprocessing) 과정이 요구된다.
사전 훈련된(pretrained) 딥러닝 모델을 사용하는 전이학습(transfer learning)을 시행할 때는, 정해진 크기의 입력을 요구하는 경우가 많다. 입력 이미지의 크기를 이에 맞춰 조절할 필요가 있다.
훈련 데이터의 수가 부족할 때는 회전, 이동, 반전 등의 변형을 가해서 이미지 데이터를 확장(augmentation)할 수 있다. 데이터가 확장되어 훈련샘플의 수가 늘어나면, 과적합(overfitting)을 줄일 수 있다.
어떤 방식이 가장 효율적인지 판단하기에는 아직 그에 걸맞은 경험이 많이 부족하다. 다행히도 친절하고 깊이 있는 글들이 웹에는 많다. 따라서 이 글은, 누군가의 검색 끝에 스쳐가면서, 더 좋은 글로 옮겨가기 위한 작은 읽을 거리가 되기를 기대하면서 시작해보려 한다.
Colab에서 캐글 API를 이용한 훈련 데이터 다운로드
참고)
Google Colab과 Kaggle 연동하기
Kaggle API
연습을 위한 훈련 데이터는 온라인에 넘쳐 난다. 물론 실제 문제를 해결하려면, 모델 구축과정보다 올바른 데이터를 구하고 정제하는 과정이 더 중요할 때가 많다. 하지만 다행히도 열정적이고 너그러운 데이터 사이언티스트와 연구자들이 많은 자료를 공개해 두었다. 공개된 자료는 제공되는 링크를 이용하거나, 자료 주소를 확보해 curl 명령어로 직접 받을 수 있다.
로컬에서는 어떤 방식으로든 가능하겠지만, kaggle에서 때로 훈련 데이터의 주소가 직접 공개되지 않아서 Colab에서 사용하기 곤란한 경우가 있었다. 자료를 직접 받아서 colab에 업로드하거나, 구글드라이브에 업로드하고 연동하는 방법도 있지만, 조금 사용하기에 번거로웠다. 따라서 참고한 링크의 내용처럼 kaggle API를 설치하여 자료를 colab에 업로드한다.
1) Colab에 캐글 API 설치
!pip install kaggle
주피터 노트북에서처럼 명령어 앞에 ! 를 붙이면, 터미널의 커맨드라인에서의 실행과 동일하게 처리해준다. kaggle api는 공개된 패키지이므로, pip 명령어로 설치할 수 있다.
2) Kaggle API 토큰 준비
kaggle API를 이용하려면 API 자격증명(credential)을 가지고 있는 kaggle.json 파일을 준비해야 한다. kaggle.json 파일은 Kaggle 회원가입 후에 오른쪽상단 계정버튼에서 My Account > API 메뉴로 들어가 Create New API Token 버튼을 클릭해 얻을 수 있다.
파일 안에는 kaggle api에 대한 사용자의 자격을 나타내주는 username과 key에 대한 키-값(key-value) 쌍이 들어있다. 이 키-값을 colab에게 인식시켜주는 방법은 크게 2가지가 있다.
3-1)colab에 kaggle 자격증명 등록하기 - ① kaggle.json 파일 업로드
colab 왼쪽의 폴더 모양의 파일 아이콘을 누른 후 세션장소에 업로드 버튼을 눌러서 kaggle.json 파일을 업로드 해준다. 아니면 첫번째 참고 블로그에서처럼 google.colab.files.upload( )함수를 실행해 파일 업로드 버튼을 생성하여 kaggle.json 파일을 업로드해줘도 좋다.
그런데 kaggle.json파일 업로드 후에 바로 kaggle api를 실행시키면 시스템은 kaggle.json파일을 찾을 수가 없다. ( root/.kaggle/ 에서 kaggle.json 파일을 찾을 수 없다는 오류 메세지를 출력한다.)
kaggle api는 자격 증명에 대한 kaggle.json 파일을 Windows os에서는 C:\Users\<Windows-username>\.kaggle\kaggle.json을, Unix기반 시스템에서는 ~/.kaggle/kaggle.json 에서 각각 탐색한다. 이 글을 쓰는 현재 Colab에서는 Ubuntu 18.04 LTS를 사용하고 있다. 따라서 root 아래에 숨김폴더 .kaggle을 만들어 kaggle.json파일을 저장할 필요가 있다.
!mkdir -p ~/.kaggle
!mv kaggle.json ~/.kaggle
이제 kaggle api를 사용해서 캐글 데이터셋을 다운로드 할 수 있다.
참고로 kaggle-api github repository에서는 보안을 위해 파일의 사용권한을 변경할 것을 권고하고 있다. 처음 kaggle.json 파일의 권한은 (644, -rw-r--r--)로서 소유자뿐만 아니라 그룹 소유자, 기타 사용자에게도 읽기권한을 부여하고 있다. 보안을 위해 소유자를 제외한 사용자에게는 읽기권한을 부여하지 않는 것이 안전하기는 하다.
!chmod 600 ~/.kaggle/kaggle.json
개인적으로는 따로 kaggle.json 파일을 수정할 것이 아니므로 400(-r--------) 권한도 괜찮을 것 같다.
3-2)colab에 kaggle 자격증명 등록하기 - ② 환경변수 지정
kaggle api 사용을 위해서 자격증명의 내용을 colab 시스템에게 알려줘야 한다는 점에서 3-1)의 내용과 다른 점은 없다. 그런데 새로운 방법을 찾다보면 마우스를 쓰고, 파일을 업로드하는 과정이 필연적으로 슬퍼지게 된다. 따라서 kaggle.json 파일의 내용을 직접 시스템 환경변수로 입력시켜준다.
앞서 언급했듯이 kaggle.json에는 username과 key의 값이 들어 있다. 이를 각각 KAGGLE_USERNAME, KAGGLE_KEY라는 환경변수로 지정한다. 그런데 export로 직접 환경변수를 설정하는 권한은 colab에서 부여되지 않는다. 따라서 운영체제에 등록된 환경변수에 접근할 수 있는 os 모듈의 environ 속성을 사용한다.
import os
os.environ['KAGGLE_USERNAME']='hayunjong83'
os.environ['KAGGLE_KEY']='200704key-value'
이제 kaggle api를 사용해서 캐글 데이터셋을 다운로드 할 수 있다.
4) 캐글에서 Dogs vs. Cats Redux 데이터셋 받기
캐글의 Dogs vs. Cats Redux: Kernel Edition에서 데이터를 받고 압축을 해제 해준다.
!kaggle competitions download -c dogs-vs-cats-redux-kernels-edition
!unzip -q train.zip
!unzip -q test.zip
개vs고양이 데이터셋에서 훈련데이터 준비
참고)
How to Classify Photos of Dogs and Cats (with 97% accuracy)
Image Recognition: Dogs vs Cats! (92%)
Building powerful image classification models using very little data
캐글에 공개되어 있는 Cats vs. Dogs 데이터셋은 레이블된 25,000장의 훈련데이터와 12,500장의 테스트 데이터로 구성되어 있다. 첫 번째 참고링크에 따르면, 개와 고양이 사진으로만 구성된 이 데이터셋은 직관적으로 이해하기 쉽고, (이미지의 크기를 바꾸는 전처리 작업을 수행했을 때)메모리에 들어갈 수 있을만큼 충분히 작은 크기이기 때문에, 이미지 분류를 위한 합성곱 신경망(CNN, Convolutional Neural Network) 분야의 "Hello World"로 생각해도 좋다. 그래서 많은 블로그 글들이 이 데이터셋을 이용해 스스로 만든 CNN 모델의 정확도와 이미 학습된 모델을 이용한 전이학습의 정확도를 비교하는 글을 소개한다고 볼 수 있다.
이 글에서는 구성된 딥러닝 모델에 주입할 훈련데이터를 준비하는 것을 목적으로 하고 있다. 따라서 크게 두 가지 방법을 소개하려고 한다. 첫 번째는 케라스에서 기본적으로 많이 쓰이는 ImageDataGenerator 클래스를 사용한다. 두 번째는 numpy연산을 이용해 훈련데이터를 Numpy 배열로 만들어 제공하는 방법이다. 현 시점에서 알고 있는 각 방법의 차이와 장,단점을 각각의 방식 설명 앞에 간단하게 덧붙인다.
1) Keras의 ImageDataGenerator 클래스 이용
참고) Keras의 Image data preprocessing API
ImageDataGenerator클래스에 대한 API 문서를 보면 실시간 데이터 확장(real-tim data augmentation)이 가능하다. 즉, 학습을 하면서 이미지에 임의의 변형을 가하여 확장할 수 있고, 정규화도 적용할 수 있다. 따라서 이미지 확장을 위해 새로운 함수를 정의하거나, 다른 파이썬 라이브러리를 활용하는 대신 ImageDataGenerator만으로 쉽게 과적합을 방지할 수 있다.
무엇보다 ImageDataGenerator 클래스의 사용이 numpy 배열로 된 이미지 데이터와 다른 점은 이 클래스는 사용할 배치 단위로 텐서 이미지를 생성한다는 점이다. 따라서 모든 이미지 데이터가 디바이스 메모리에 로드될 필요가 없어서, 상대적으로 더 큰 이미지를 사용한다거나 더 큰 배치를 이용할 수 있게 된다. 대신에 그만큼 실행속도가 느려질 수밖에 없다.
사용하는 훈련데이터가 레이블을 가지고 있다면, 생성된 제네레이터에 훈련데이터와 레이블을 매개변수로 쓰는 flow 메소드를 활용한다. 반면, 훈련데이터가 따로 생성된 레이블 없이 각 클래스별로 하위 디렉토리로 나눠져 있으면 flow_from_directory에 데이터를 가진 상위 디렉토리(/train, /test)를 넘겨주면 된다. 두 번째 참고 블로그의 내용처럼, cats vs. dogs 데이터에는 잘못된 이미지(dirty image)가 존재하는 편이므로, 보통 이런 이미지를 걸러내면서 훈련이미지를 하위 디렉토리로 분류하는 편인것 같다. 따라서 여기에서도 flow_from_directory를 사용하는 경우를 소개하려고 한다.
2) 훈련데이터를 Numpy 배열로 생성
참고)
imgaug
albumentations
케라스 뿐만 아니라 다른 프레임워크에서도 활용할 수 있으려면, 훈련 데이터를 numpy 배열로 생성하여 npy 파일로 저장해두면 좋다. 위에서 언급했듯이 numpy 배열은 모든 이미지를 디바이스 메모리로 로드하게 되므로, 실행 속도 면에서는 더 빠르다. 하지만 상대적으로 디바이스 메모리 크기가 작은 GPU를 쓴다면 이미지의 크기와 모델의 크기에 더 신경쓸 필요가 있게 된다. (복권이 되면 제일 먼저 V100을 사고 싶다.)
또, 상대적으로 이미지 데이터 확장을 직접 수행하거나 외부 파이썬 라이브러리에 의존하는 과정이 복잡할 수 있다. 그러나 위에 소개된 imgaug나 albumentations 같은 라이브러리의 성능과 편의성을 생각하면, 어느 정도 훈련데이터 처리가 익숙해진 뒤에는 문제가 되지 않을 것으로 생각된다.
3) 실행 -1)
개 이미지는 dog.0.jpg, dog.1.jpg와 같은 파일명을 갖고 있다. 마찬가지로 고양이 이미지도 cat.0.jpg와 같은 파일명으로 지정되어 있다. flow_from_directory 메소드를 사용할 때는 클래스별 디렉토리를 classes 인자에 리스트로 넘겨줄 수 있다. 그러나 classes인자가 설정되지 않았을 때에는 자동적으로 directory 인자로 지정된 경로에서 하위 디렉토리를 개별의 클래스로 인식하게 된다.
현재 colab에서 train 디렉토리 안에 있는 고양이 사진을 train/cats로 옮기고, 개 사진을 train/dogs로 이동시켜준다.
import os
labels=['dogs', 'cats']
for label in labels:
os.makedirs(os.path.join('train', label), exist_ok = True)
!find train -name 'dog.*' -exec mv {} train/dogs/ \;
!find train -name 'cat.*' -exec mv {} train/cats/ \;
우선 파일을 이동시킬 하위 디렉토리를 만들어준다.
find 명령어를 사용하여 각 클래스별로 파일들을 검색하고, 하위 디렉토리로 이동하게 만든다. name 옵션은 지정된 문자열을 가진 파일을 디렉토리에서 찾아내고, exec 옵션은 \; 로 끝이 지정된 명령어를 실행해준다. find로 탐색된 파일들은 { } 인자를 통해 리스트로 전달되는데, 이 파일들을 train/dogs/와 train/cats/로 각각 이동(mv)시켜주는 명령어를 실행하겠다는 의미가 된다.
이번 글에서는 훈련데이터 경로를 지정하는 방법을 소개하는데 큰 의의를 두고 있기 때문에, 이미지 확장에 대해서는 Keras image data preprocessing api 페이지의 내용을 참고하는 것으로 하고, 충분한 데이터 수를 고려하여 이미지 확장을 실행하지 않고 넘어간다.
그런데 여기서 생각해봐야 할 것은, 위에서 현재 훈련데이터로부터 검증 데이터셋을 따로 디렉토리로 마련하지 않았다는 점이다. 이 경우 검증 데이터셋을 자동으로 준비해주는 validation_split 값을 ImageDataGenerator 생성 시에 적용하고, flow_from_directory를 쓸 때 subset 인자를 지정해서 해결할 수 있다. 그러면 validation_split 비율만큼의 검증데이터셋(subset='validation)과 훈련데이터셋(subset='training')을 사용할 수 있다.
마지막으로 ImageDataGenerator를 사용했을 때는, 배치 단위로 모델을 학습시켜주는 fit_generator 메소드를 사용할 수 있다.
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2)
train_generator = datagen.flow_flow_from_directory(
'train',
target_size=(img_height, img_width),
batch_size = batch_size,
class_mode='binary',
subset='training')
validation_generator = datagen.flow_from_directory(
'train',
target_size=(img_height, img_width),
batch_size = batch_size,
class_mode='binary',
subset='validation')
model.fit_generator(
train_generator,
steps_per_epoch = train_generator.samples // batch_size,
epochs = epochs,
validation_data = validation_generator,
validataion_steps = validation_generator.samples // batch_size)
그런데 사실 위의 코드는 훈련 이미지에 이미지 확장을 적용하지 않았기 때문에 문제가 없었지만, ImageDataGenerator에 image augmentation을 수행하면서 validation_split 값으로 검증 데이터셋을 마련하면, 검증데이터에도 변형과 확장이 일어나게 된다. 보통 검증데이터셋에 대하여 데이터 확장을 하지 않는다는 것을 고려하면, 이는 문제가 될 수도 있다. 이 경우, 검증 데이터셋을 위한 디렉토리가 마련되어 있는 경우처럼 두 개의 ImageDataGenerator를 생성할 수밖에 없다.
정리하면 validation_split을 쓰는 경우, image_augmentation이 검증 데이터에 적용되는 것을 피하기 위해 동일한 시드의 두 개의 데이터 제네레이터를 써야 한다. 이상에 관하여 다음의 글들을 참고하면 좋다. (링크 1, 링크 2, 링크 3)
3) 실행 -2)
두 번째 방식은 파일을 읽어서, 그 파일에 상응하는 레이블을 같은 인덱스에 기록하는 리스트를 생성한 뒤, Numpy 배열로 변환하면 된다. 고양이의 레이블을 '0'으로 정하고, 개의 레이블을 '1'로 설정하겠다. 그리고 이미지를 배열로 바꾸는 과정은 How to Classify Photos of Dogs and Cats와 같이 keras.preprocessing.image 패키지의 load_img( )와 img_to_array( )를 이용해서 이미지를 읽고, 배열로 바꿨다. 이 함수를 각각 OpenCV의 cv2.imread( )와 Numpy의 numpy.array( )를 사용해도 동일한 결과를 얻게 된다.(참고)
이미 실행-1)에서 처럼 각각 train/dogs/ 와 train/cats/ 로 각 사진이 해당 폴더로 옮겨졌다고 가정한다.
import os
from numpy import asarray
from numpy import save
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_array
subsets=['cats', 'dogs']
train_img, train_label = list(), list()
for subset in subsets:
img_dir = os.path.join('train', subset)
label = 0.0
if subset == 'dogs':
label = 1.0
for file in os.listdir(img_dir):
img = load_img(os.path.join(img_dir, file),
target_size=(image_height, image_width))
img = img_to_array(img)
train_img.append(img)
train_label.append(label)
train_img = asarray(train_img)
train_label = asarray(train_label)
save('train_img.npy', train_img)
save('train_label.npy', train_label)
4) 잘못된 훈련 이미지 걸러내기
Image Recognition: Dogs vs Cats!(92%)에 따르면 개vs고양이 데이터셋에는 카툰이나 관련없는 이미지가 몇 개 존재한다. 다음은 그런 이미지들의 인덱스(dog.XXXX.jpg에서 XXXX에 해당하는 숫자) 정보다.
bad_dog_ids = [5604, 6413, 8736, 8898, 9188,
9517, 10161, 10190, 10237, 10401, 10797, 11186]
bad_cat_ids = [2939, 3216, 4688, 4833, 5418,
6215, 7377, 8456, 8470, 11565, 12272]
이들 이미지는 훈련에 도움이 되지 않기때문에 제거하는 편이 더 낫다. 사실 위에서 각 이미지별로 하위 디렉토리로 분류한 것은 각 분류별 이미지 시각화, 레이블 생성 및 관리와 더불어 잘못된 이미지 제거과정이 조금 더 명확하게 느껴지기 위해서였다. 안전하게 잘못된 이미지를 Trash 디렉토리로 이동시키는 것은 링크된 블로그를 참고하면 좋을 것 같다. 여기서는 위의 이미지들을 os.remove( )를 통해 삭제하도록 하였다.
import os
bad_cat_ids = [2939, 3216, 4688, 4833, 5418, 6215, 7377,
8456, 8470, 11565, 12272]
bad_dog_ids = [5604, 6413, 8736, 8898, 9188, 9517, 10161,
10190, 10237, 10401, 10797, 11186]
subsets=['cats', 'dogs']
for subset in subsets:
category = subset[:-1]
if category == 'cat':
ids = bad_cat_ids
else:
ids = bad_dog_ids
for id in ids:
fname = category +'.'+ str(id) + '.jpg'
dirty_img = os.path.join('train',subset,fname)
os.remove(dirty_img)
'A.I. > 구현' 카테고리의 다른 글
PyTorch 컴퓨터 비전 전이학습 예제를 따라해보자. (0) | 2021.03.12 |
---|---|
PyTorch Dataloader에서 원본 파일명을 알고 싶다. (0) | 2020.11.25 |
TensorFlow Lite 공식예제)꽃 인식 안드로이드 앱 -(2) (0) | 2020.05.10 |
TensorFlow Lite 공식예제)꽃 인식 안드로이드 앱 -(1) (0) | 2020.05.10 |
TensorFlow Lite 개요 (0) | 2020.05.10 |
댓글