Github : github.com/junstar92/hangul-syllable-recognition
머신러닝, 딥러닝을 공부하면서 OCR, 특히 한글 OCR에 대해서 한번 직접 구현해보고 싶었고, 한글 텍스트 탐지 및 인식을 시작하기 전에 가볍게 한글 음절 하나를 인식하는 인식기를 제작해보았습니다.
한글은 기본적으로 자음 14자, 모음 10자로 구성되어 있고, 복합으로 사용되는 자음 5자와 모음 11자가 추가로 있습니다.
그리고 하나의 음절은 초성+중성+받침으로 구성될 수 있고, 나타낼 수 있는 경우의 수는 총 11,172개이므로 엄청 많음 음절 단위를 표현할 수 있습니다.
이번 프로젝트에서는 11,172개의 음절이 아닌, KS X 1001 완성형에 포함되며, 자주 사용되는 한글 2350자만을 인식하는 인식기를 제작해보았습니다. 또한, 인쇄체뿐만 아니라 손글씨도 인식할 수 있도록 했습ㄴ다.
Data 수집
학습에 사용될 데이터는 TextRecognitionDataGenerator라는 tool과 AI Hub에서 제공하는 한글 이미지 데이터를 사용하였습니다.
TextRecognitionDataGenerator font파일과 (생성할)한글 리스트를 담은 txt파일로 한글 텍스트 이미지를 생성할 수 있는 Tool이며, 자세한 내용은 tool의 github를 참조하시기 바랍니다.
- Github : github.com/Belval/TextRecognitionDataGenerator
손글씨 또한 인식하는 것이 목표였기 때문에, 다양한 font가 필요했습니다. 그래서 기본적인 인쇄체 font뿐만 아니라 네이버에서 제공되는 손글씨 font도 사용했습니다. font list는 다음과 같습니다.
위 font들을 사용해서 한글 2350자를 포함하는 100,000개의 한글 이미지를 생성했습니다.
두 번째로 AI Hub에서 제공하는 한글 이미지 데이터를 사용하였습니다.(link : https://aihub.or.kr/aidata/133)
현대 한글 11,172자를 가장 많이 활용하는 폰트 50종을 선정하여 학습용 이미지를 제공하고 있으며, 또한, 손글씨도 제공하고 있습니다. 손글씨는 연령층별로 손글씨 작성인력을 확보해 직접 작성 제작한 손글씨 이미지라고 합니다.
AI Hub에서 제공되는 한글 이미지는 조합 가능한 모든 한글 이미지가 존재하기 때문에, 전처리 과정을 통해서 2350자만을 사용하도록 해주어야 했습니다.
그리고, 두 종류의 학습 데이터의 파일명과 이미지의 라벨을 제공하는 방법이 다르기 때문에 저는 각각의 이미지들을 경로와 해당 이미지의 라벨을 저장할 수 있도록 csv 파일을 따로 제작을 하였습니다.
위와 같은 형식으로 구성되어 있으며, 파일 경로를 저장하여서 학습 시에 이미지를 읽어서 학습에 사용할 예정입니다.
Data 수집 결과 TextRecognitionDataGenerator로 생성한 이미지 100,000개와 AI Hub에서 제공하는 글자 이미지 404,474개, 총 504,474개의 글자 이미지를 수집하였습니다.
Dataset 생성 및 전처리
Dataset의 생성 및 전처리는 아래와 같은 과정을 통해 진행됩니다.
df = pd.read_csv(opt.data)
xs = np.array(df['ids'])
ys = np.array(df['labels'])
# train/valid split and shuffle
train_x, valid_x, train_y, valid_y = train_test_split(xs, ys, test_size=0.01)
train_datagen = DataGeneratorByPath(train_x, train_y, opt)
valid_datagen = DataGeneratorByPath(valid_x, valid_y, opt)
여기서 opt는 argparser로 제공되는 argument들을 포함하고 있습니다. 이에 대한 자세한 내용은 github를 참조하시기 바랍니다.
먼저 이미지 파일 경로와 라벨 정보를 담고 있는 csv파일을 읽고, 경로와 라벨 정보를 각각 저장합니다.
그리고 sklearn에서 제공하는 train_test_split 함수를 통해서 train와 validation 데이터로 나누고, 셔플까지 수행합니다.
그리고 DataGeneratorByPath Class를 통해서 tensorflow Dataset을 생성하게 됩니다.
해당 class는 tf.keras.utils.Sequence를 상속합니다.
class DataGeneratorByPath(tf.keras.utils.Sequence):
def __init__(self, file_paths, labels, opt, shuffle=True):
self.file_paths = file_paths
self.labels = labels
self.batch_size = opt.batch_size
self.shuffle = shuffle
self.idx_to_char = list(opt.character)#['?'] + list(opt.character)
self.char_to_idx = {}
for i, char in enumerate(self.idx_to_char):
self.char_to_idx[char] = i#+1
#self.char_to_idx[self.idx_to_char[0]] = 0
self.num_class = len(self.idx_to_char)
self.n_samples = len(self.file_paths)
self.on_epoch_end()
print(f'{self.n_samples} images loaded')
def __len__(self):
return int(np.floor(self.n_samples / self.batch_size))
def on_epoch_end(self):
self.indices = np.arange(self.n_samples)
if self.shuffle:
np.random.shuffle(self.indices)
def __getitem__(self, index):
indices = self.indices[index*self.batch_size:(index+1)*self.batch_size]
files = [self.file_paths[i] for i in indices]
labels = [self.labels[i] for i in indices]
xs = []
ys = []
for file, label in zip(files, labels):
x = load_img(file, target_size=(32,32))
x = img_to_array(x)
x = x / 255.
xs.append(x)
ys.append(self.char_to_idx[label])
return np.array(xs), np.array(ys)
Model
모델은 단순한 CNN 구조를 사용했으며, Clova deep-text-recognition에서 사용한 VGG_Extractor 구조를 참조하였습니다. 아래 paper를 참조했다고 합니다.
paper : https://arxiv.org/pdf/1507.05717.pdf
deep-text-recognition github : https://github.com/clovaai/deep-text-recognition-benchmark
Training and Evaluation
총 25 Epoch 동안 학습을 진행했습니다. 거의 10 epoch에 도달하기 전에 95% 이상의 정확도에 도달하였으며, 21 epoch에서 최대의 validation 성능을 보여주고 있습니다.
22 epoch 이후부터는 training data에 과적합되는 현상을 보여주고 있습니다.
테스트를 위해서 TextRecognitionDataGenerator로 새로운 글자 이미지 80,000개를 생성하여 검증했을 때에는 약 98%의 정확도와 0.086의 loss를 보여주고 있습니다.
App으로 구현
실제 손글씨를 보다 쉽게 테스트하기 위해서 streamlit package를 사용해서 Webapp을 구현하였습니다.
steamlit의 사용은 아래 youtube를 참조하였습니다.
Link : http://asq.kr/DxUZf7Fgzmtmdq
CMD창에서 project 경로에서 아래 커맨드를 통해서 실행할 수 있습니다.
streamlit run webapp.py
실행이 완료되면 아래와 같은 페이지가 생성되며, 직접 글자를 입력하여서 테스트할 수 있습니다. 추가로 모델의 예측결과 중에서 상위 다섯 개의 결과도 보여주고 있습니다.
많은 글자를 테스트를 한 것을 아니지만, 꽤 좋은 결과를 보여주고 있는 것 같긴합니다.
(다양한 글씨로 테스트를 해보지는 않았습니다..)
ㅇ과 ㅁ을 애매하게 쓰면 제대로 예측하지 못하는 경우가 종종 발생하기는 합니다.
전체 코드는 github를 참조해주시고, 다음에는 완전한 한글 Text Detector/Recognition을 구현해보도록 하겠습니다.. !
github.com/junstar92/hangul-syllable-recognition
댓글