딥러닝 모델을 생성하고 학습시키기 위해 가장 먼저 해야할 일은 데이터를 수집하고 전처리를 하는 겁니다. 인공지능의 본질을 거슬러 올라가면 결국 인공지능이 만들어내는 Insight은 데이터에 그 기반을 둔다 볼 수 있기 때문입니다. 그렇기에 이번에는 글자 데이터를 무작위로 생성해서 데이터셋을 직접 만들어내보도록 하고, 이 데이터셋을 모델이 잘 받아들이고 학습하기 쉽도록 받아들이는 작업을 하겠습니다. 이번 데이터셋을 생성하는데는 IBM Developers에서 제공하는 프로그램을 이용했고, 해당 프로그램에 관한 모든 저작권은 Paul-Van-Eck에 있음을 알립니다.
인공지능을 학습시킬 수 있는 최고의 수단, Google Colab
위의 글에서 이전에 소개시켜드렸다시피 인공지능을 학습시킨다면 여러가지 선택지를 고를 수 있습니다. 그 중에서 우리는 Google Colab을 사용하도록 합니다. 여기에는 여러가지 이유가 있는데, 큰 장점을 몇 가지 꼽자면 클라우드 환경이다(Google Drive와 연동이 되고, 어디서든 코딩을 할 수 있습니다. 심지어 스마트폰에서도!). 애플 제품만 쓰는 사람이 아닌 한 다들 구글 계정 하나쯤은 가지고 있다, Tesla K80 GPU를 사용할 수 있다 정도 되겠습니다. 그러나 클라우드 환경이 필요 없고 사용하시는 시스템이 Nvidia GTX1080Ti 이상으로 굴러간다 싶으면 아나콘다를 설치해서 오프라인으로 인공지능을 공부하시는 편이 낫습니다.
코랩, 구글 드라이브와 연동하기
Google Colab(https://colab.research.google.com/)에 들어가시면 가장먼저 위와 같은 창이 뜰텐데 New Python3 notebook버튼을 클릭해 새 노트북을 만들어줍니다. 새로운 노트북이 만들어진 후 왼쪽의 폴더 모양의 탐색기 아이콘을 누르고 Mount Drive 버튼을 통해 Google Drive와 Colab을 연동시켜줄겁니다. 버튼을 누르면 아래와 같은 코드블럭이 뜰텐데, Shift + Enter 키를 눌러 해당 코드를 실행시켜 줍니다.
from google.colab import drive
drive.mount('/content/drive')
Go to this URL in a browser: https://accounts.google.com/o/oauth2/auth?client_id=947318........
Enter your authorization code: ··········
Mounted at /content/drive
위와 비슷한 내용이 콘솔창에 뜰텐데 시키는대로 비밀번호 입력하고 Authorization code 잘 입력해주면 연동이 됩니다. 일단 연동이 되고 난 후에는 본인 계정의 Google Drive를 왼쪽 탐색기에 drive 이하 폴더를 통해 접근할 수 있습니다. 다른 걸 다 제치고서 구글드라이브 연동을 가장 먼저 한 이유는 Google Colab이 기반으로 하고 있는 Jupyter Notebook의 특성 때문에 그렇습니다. 주피터 노트북에는 '커널'이라는 개념이 존재하는데, 커널이라는 게 한 번 종료되고 나면 기존의 파일들이 날아가 버립니다. 모델을 학습시켰다면 학습시킨 모델이, git을 불러왔다면 불러온 내용들이 날아갑니다. 이런 불상사를 막기 위해 코랩을 켰을 때 우리가 가장 먼저 해야할 일은 구글 드라이브를 연동시켜서 파일을 구글 드라이브에 저장하는 겁니다.(물론 작성하고 있는 .ipynb파일은 기본 저장 경로가 구글 드라이브 안이고, 자동저장이 되고 있기 때문에 날아갈 염려는 안하셔도 됩니다.)
이렇게 구글 드라이브 연동이 끝났다면 다음은 구글 드라이브에 git을 복사하는 절차를 밟아 보겠습니다. Colab에서는 !를 접두어로 명령어를 치면 작성하고 있는 코드를 콘솔 명령어로 인식합니다. 리눅스에서 현재 위치한 디렉터리의 이름을 보여주는 pwd명령어를 그냥 치면 파이썬 상에서 인식되는 거기에 오류가 뜨겠지만, !를 붙여 !pwd를 입력하면 리눅스 명령어라는 것은 인식하고 제대로 동작합니다. 리눅스에서 디렉터리를 제어하는 몇 가지 명령어를 소개합니다.
pwd #현재 작업중인 디렉터리
cd #현재 디렉터리 이하 디렉터리로 이동, 상위로 가고 싶으면 .. 입력
ls #현재 디렉터리 이하에 있는 목록들 나열
mkdir #디렉터리 생성
rm #파일, 디렉터리 제거(디렉터리 제거시에는 -r 옵션을 줘야 함)
위에 제공한 명령어를 바탕으로 주피터 노트북에 명령어를 입력합니다. (-->기호 이후 내용은 콘솔이 응답하는 내용입니다.)
!pwd
--> /content
!ls
--> drive sample_data
import os #위에서 알려드린 cd 명령어가 먹히지 않아 부득이하게 다른 방법을 사용합니다
os.chdir("./drive/My Drive")
!pwd
--> /content/drive/My Drive
우리의 목표는 My Drive까지 이동하는 거였는데, cd 명령어가 띄어쓰기는 인식을 하지 않기 때문에 부득이하게 os모듈을 import하게 되었습니다. os 모듈은 파이썬이 폴더와 파일을 관리할 수 있도록 하는 모듈입니다. 우선 위 코드에서 os.chdir는 리눅스의 cd와 같은 명령입니다. 다른 기능들은 이후에 천천히 알아도 충분히 즐겁게 배울 수 있습니다. 여기까지 오면서 드디어 작업하는 디렉터리의 포커스를 구글 드라이브까지 옮기는데 성공했습니다. 그렇다면 다음의 코드를 작성 후 실행시켜서 https://github.com/IBM/tensorflow-hangul-recognition 에 존재하는 파일들을 구글 드라이브에 복사하도록 합니다.
!git clone https://github.com/IBM/tensorflow-hangul-recognition
-->Cloning into 'tensorflow-hangul-recognition'...
-->remote: Enumerating objects: 567, done.
-->remote: Total 567 (delta 0), reused 0 (delta 0), pack-reused 567
-->Receiving objects: 100% (567/567), 79.23 MiB | 11.37 MiB/s, done.
-->Resolving deltas: 100% (245/245), done.
-->Checking out files: 100% (66/66), done.
해당 작업이 다 끝나면 /content/drive/My Drive/tensorflow-hangul-recognition/tools/ 경로에 존재하는 hangul-image-generator.py안에 들어간 내용을 Colab에 복사해서 .ipynb파일로 만듭니다. 이 때 colab 파일은 원본 .py 파일이 있던 곳과 동일한 위치에 있어야 합니다. 코드는 아래와 같으며, 코드가 존재하는 디렉터리 내 /fonts 에 ttf 폰트 파일을 여러 개 넣어두고, /labels/2350-common-hangul.txt에 따라 2350개의 글자를 만들어냅니다. 그런데 이 때 elastic_distort 함수를 이용해 데이터 이미지를 이리 저리 왜곡을 줘서 데이터의 개수를 늘립니다. 아래 코드에서 우리가 손대야 할 것은 DISTORTION_COUNT, 글자 수 조절입니다.
clone한 git을 잘 뜯어보면 /labels 폴더 아래 2350-common-hangul.txt 뿐 아니라 512-common-hangul.txt, 256-common-hangul.txt 파일도 존재한다는 것을 알 수 있습니다. 이 세가지의 label 파일은 한글 문자를 만드는데 의미있는 데이터셋을 만들어내는 역할을 합니다. 한글 26개 자모의 총 조합의 수를 합치면 대략 11,671개 정도 됩니다. 이걸 다 데이터셋의 일부로 학습시키는 건 너무나도 불필요한 일입니다. '쉙' , '낋' 이런 글자는 거의 쓸 일이 없을 뿐더러 그렇게 되면 모델 출력층에 1만 개에 가까운 유닛이 배치되어야 합니다. 시스템적으로 비효율적입니다. 그렇기 때문에 /labels 이하의 세 레이블이 담긴 텍스트 파일은 약 11,671개 한글 중에서 많이 쓰이는 2350개, 512개, 256개의 한글을 선별했습니다. 그럼 이 중에서 어떤 레이블 파일이 학습하기 적절하며 좋은 정확도를 가지는 모델을 만드는 데 도움이 될지 판단하는 건 우리의 몫입니다.
2360-common-hangul.txt 파일에는 쓸만한 글자도 많겠지만 '갊', '괆' 등 전혀 쓰지 않을 것 같은 글자들 역시 다수 포함되어 있습니다. 256-common-hangul.txt에는 왠지 자주 쓰는 글자까지 누락되어 있을 것 같은 느낌입니다. 그런 이유로 저는 512-common-hangul.txt가 학습 대비 좋은 효율이 나올 것으로 판단했습니다. 512-common-hangul.txt를 레이블로 사용하기 위해 코드 내에서 레이블의 경로를 바꿔줍니다.
만들어지는 데이터셋의 데이터 수는 대략 다음을 통해 계산 가능합니다.
(데이터 수) = (폰트의 개수)*(레이블 : 256 or 512 or 2350)*(DISTORTION_COUNT)
폰트를 40가지, DISTORTION_COUNT를 10으로 잡는다면 학습시킬 수 있는 데이터의 수는 대략 204,800개정도 됩니다. 이 양이 실제로 많은지 적은지는 실제로 학습을 진행해봐야 알겠지만 64*64사이즈의 CNN 학습량 치고 적당하다는 생각이 듭니다. 아, 폰트로 이미지를 만들 때 생성되는 이미지의 크기 역시 코드 내 주어진 IMAGE_WIDTH, IMAGE_HEIGHT 상수를 통해 조절 가능합니다. 원한다면 MNIST 처럼 28*28사이즈로 학습시켜도 무방하겠으나, 한글은 숫자나 알파벳보다 구조가 복잡하기에 64*64이하로 학습시키기에는 조금 부적절해 보입니다.
#!/usr/bin/env python
import argparse
import glob
import io
import os
import random
import numpy
from PIL import Image, ImageFont, ImageDraw
from scipy.ndimage.interpolation import map_coordinates
from scipy.ndimage.filters import gaussian_filter
SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
# Default data paths.
DEFAULT_LABEL_FILE = os.path.join(SCRIPT_PATH,
'../labels/2350-common-hangul.txt')
DEFAULT_FONTS_DIR = os.path.join(SCRIPT_PATH, '../fonts')
DEFAULT_OUTPUT_DIR = os.path.join(SCRIPT_PATH, '../image-data')
# Number of random distortion images to generate per font and character.
DISTORTION_COUNT = 3
# Width and height of the resulting image.
IMAGE_WIDTH = 64
IMAGE_HEIGHT = 64
def generate_hangul_images(label_file, fonts_dir, output_dir):
"""Generate Hangul image files.
This will take in the passed in labels file and will generate several
images using the font files provided in the font directory. The font
directory is expected to be populated with *.ttf (True Type Font) files.
The generated images will be stored in the given output directory. Image
paths will have their corresponding labels listed in a CSV file.
"""
...
def elastic_distort(image, alpha, sigma):
...
원본 파일에 수정을 가해야겠다면 수정을 가하되, 그럴 필요가 없다면 그대로 두고 실행시켜도 됩니다.
fonts 폴더에 원하는 만큼의 폰트를 넣은 후, hangul-image-generator.py를 실행시키면 파이썬은 입력한 파라미터와 폰트 수에 따라 데이터를 수 천~ 수 만 개를 만들어 내고, 파일별 이름에 매치되는 레이블이 담긴 csv 파일 하나를 만들어 낼 겁니다. 이 과정까지 성공했다면 데이터 생성은 그걸로 끝입니다.
잘 안된다면?
그리 친절하게 알려드리지 못한 부분이 있다는 느낌이 듭니다. 잘 되지 않는다면 댓글로 안되는 부분을 알려주시면 도와드리겠습니다. 그래도 안되면 github에서 파일을 오프라인으로 다운로드 받아 데이터셋을 오프라인에서 만드는 방법도 있습니다.
글이 마음에 드셨다면 왼쪽 아래 공감 버튼을 눌러주세요. 고마움을 표현할 수 있는 가장 쉬운 방법입니다.
감사합니다.