이전 편에서는 Scrapy 를 사용하여 크롤러를 제작하는 방법과 텔레그램 봇을 이용하여 배포하는 과정을 설명했고, 이번 편에서는 뉴스 봇에서 작동하고 있는 Natural Language Processing(자연어처리)을 어떻게 구현하였는지를 설명하고 있습니다.

시간이 없어서 한글로는 작성하지 못했습니다.

영어로 작성된 글은 아래링크에서 보실 수 있습니다.

 

Scrape News and Corporate Announcements in Real-Time — 3(NLP)

Previous posts can be found in the links below.

charliethewanderer.medium.com

 

'개인프로젝트' 카테고리의 다른 글

인공지능 딥러닝으로 사람 얼굴 평가하기  (0) 2021.01.29
블로그 이미지

찰리와마약공장

,

http://charliethewanderer1.ddns.net/faceeva/

 

http://charliethewanderer1.ddns.net/faceeva/

 

charliethewanderer1.ddns.net

완성된 형태의 어플리케이션을 웹을 통해 서비스 하고 있다. 위의 링크를 따라 들어가면 테스트 해볼 수 있다.
20~30대의 한국인 얼굴 500장의 이미지로 학습된 모델이라 경우에 따라 정확하지 않을 수 있으니, 결과값에 실망하지 말자.

 

English Version: Score Faces with Deep Learning. http://charliethewanderer1.ddns.net/face… | by Charlie_the_wanderer | Feb, 2021 | Medium

 

Score Faces with Deep Learning

http://charliethewanderer1.ddns.net/faceeva/

charliethewanderer.medium.com

 

 이 프로젝트는 작년 9월 경에 시작하여 약 3주 가량 소요된 프로젝트다. Coursera를 통해 파이썬, 머신러닝, 딥러닝에 대한 개념을 배우면서, 직접 개발과 배포를 해보고자, 이미지 인식과 관련된 주제를 정하여 프로젝트를 시작했다.

 

 처음에는 '정수리와 이마 이미지를 통해 탈모 진단하기' 를 비롯하여 기타 몇몇 아이디어들이 있었다. 여러 아이디어를 바탕으로 각각에 맞는 데이터셋을 구하기 위해 이런 저런 시도를 하다보니, 데이터셋을 구축하는 것이 생각보다 굉장히 까다로운 과정이라는 것을 깨달았다. 예를 들어, 탈모를 진단하는 모델을 만드는 경우에는, 탈모를 진단 할 수 있는 부위가 찍힌 이미지가 필요하고, 해당 이미지 속 사람이 탈모인지 아닌지를 진단하여 레이블 값을 제공할 수 있어야만 했다. 그러나, 그러한 이미지를 구하기도 어려웠을 뿐더러, 내가 의사가 아니기에, 탈모를 직접 진단할 수가 없었다. 마찬가지로, 대부분의 경우, 특정 주제와 관련된 데이터셋을 구축하는게 매우 어려웠기 때문에, 비교적 데이터셋을 구축하기 쉬운 '딥러닝으로 사람얼굴 평가하기' 라는 주제를 선택하게 되었다.

 

모델 개발 과정

 데이터를 수집하는 방법은 소위 노가다였다. 구글의 검색결과에 보이는 모든 사람의 이미지를 모조리 다운로드하고, 가장 못생긴 사람을 1, 가장 잘생긴(이쁜) 사람을 5의 레이블로 점수를 메기어 총 5단계의 class로 나누었다. 다만, 추후 모델 predict시, 휴대폰 카메라로 찍힌 이미지가 input이 될 것을 감안했기 때문에, 모델 학습에 쓰일 이미지 또한 최대한 고화질로 수집했으며, 여자, 남자, 레이블 별로 균일하게 50장씩 수집하여, balanced dataset이 되도록 데이터셋을 구축했다. 그러나 이 과정에서도, 여러가지 고려할점이 많았다.

 우선, 사람은 사실, 사람의 얼굴을 평가할때 얼굴만을 가지고 평가하는 것이 아니라는 것이었다. 사람은, 얼굴을 평가할때, 그 사람의 옷, 헤어스타일, 체격, 그 사람의 평판 등 얼굴 이외의 외부정보들을 무의식적으로 의사결정에 포함하게 된다.

 예를 들어, 연예인 유재석과, 유재석과 똑같이 생긴 일반인 유재석이 있다고 가정해 보자. 당신은 어느 사람에게 좀더 호감이 가겠는가? 둘의 얼굴은 똑같을지라도, 두 사람에게 느끼는 감정과 느낌은 당연히 다를 수밖에 없을 것이다.

따라서, 이러한 외부요인들로 인한 영향을 최소화 하기 위해서는, 이미지의 레이블을 평가할 때 이를 의식하고 최대한 객관적인 자세에서 평가해야 했다. 물론, 나의 주관이 당연히 반영될 수밖에 없었지만 말이다.

 또한, 모델의 학습에 쓸 이미지도 별도의 전처리를 해야 했다. 모든 이미지가 얼굴만 나와있는 것은 당연히 아니었기 때문에, 얼굴을 제외한 옷, 배경 등을 제외하고자 Python 의 opencv 라이브러리를 활용해서 수집된 이미지에서 얼굴만을 인식시켜 크롭하는 과정을 아래와 같이 추가했다.

def get_face_location(image_path):
    
    #Load the cascade
    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

    #Read the input image
    image_path = image_path
    img = cv2.imread(image_path)

    #Convert into grayscale
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #Detect faces
    faces = face_cascade.detectMultiScale(gray_img, 1.03, 6)
    
    #Check whether there are more than one face or not
    detecting_result = 2 # 2 is normal
    if faces == ():
        detecting_result = 0 # "There's no face detected"
    elif len(faces) > 1:
        detecting_result = 1 # "There's more than one face detected"

    #Suppress other boxes
    max_box_coordinates = (0, 0, 0, 0)
    for (x, y, w, h) in faces:
        if w * h >= max_box_coordinates[2]*max_box_coordinates[3]:
            max_box_coordinates = (x, y, w, h)

    return (max_box_coordinates, img, detecting_result)

 나의 경우, 얼굴인식을 위해 opencv 의 haarcascade 알고리즘을 사용했다. 얼굴인식 알고리즘은 haarcascade 말고도 Convolutional Neural Network를 활용하는 MTCNN과 SSD 등의 다양한 알고리즘들이 있다. 

haarcascade알고리즘에 대한 자세한 설명은 OpenCV: Cascade Classifier 에서 확인이 가능하다.

 

OpenCV: Cascade Classifier

Next Tutorial: Cascade Classifier Training Goal In this tutorial, We will learn how the Haar cascade object detection works. We will see the basics of face detection and eye detection using the Haar Feature-based Cascade Classifiers We will use the cv::Cas

docs.opencv.org

  각 알고리즘은 장단점이 존재한다. haarcascade 알고리즘은 CNN과 같은 최신 딥러닝 기술을 활용하지 않기 때문에, MTCNN과 같은 최신 알고리즘보다 성능이 낮고, 얼굴의 각도가 틀어지거나, 얼굴의 일부가 가려지는 경우와 같은 돌발상황에서는 잘 인식하지 못하는 편이다. 그러나, 최신 알고리즘에 비해 필요로 하는 연산량이 훨씬 적기 때문에, 

 같은 시간에 훨씬 많은 이미지를 처리해 낼 수 있고, 라즈베리파이와 같은 초소형 컴퓨팅 칩에서도 쾌적하게 돌아간다는 장점을 갖고 있다. 이것이 내가 haarcascade를 선택한 중요한 이유 중 하나이다.

 특히, 나의 프로젝트에서는, 사용자가 직접 자신의 얼굴을 촬영하여 모델에 전송해 주는 것을 가정하고 있으므로, 사용자 또한 얼굴을 올바르게 정면에서 찍어야 한다는 것을 인지하고 있다는 가정이 가능하다. 또한, 라즈베리파이를 서버로 사용했으므로, 컴퓨팅 성능이 제한적이었다. 따라서, 돌발상황에 대한 Generalize성능보다는, 많은 사용자의 사진을 최대한 신속하고 빠르게 처리할 수 있는 효율성이 더 중요했다.

 

이미지 크롭과정을 설계한 이후에, 본격적인 모델 설계작업을 시작했다. 

 구축된 데이터셋과 레이블을 바탕으로 모델 학습을 시키려고 생각해보니, 성별과 관련된 문제가 남아있었다. '남자와 여자를 구분하지 않고, 학습시켜야 하나?', '남자와 여자를 구분하여 학습을 시킬까?', 어느 쪽도 딱히 틀리거나 정답은 아니었다. 곰곰히 생각해 본 결과, 사람의 사고방식을 흉내내기로 했다. 사람은 상대의 얼굴을 평가할 때, 상대방이 여자인지 남자인지 먼저 판단한 후, 얼굴을 평가하게 된다. 즉, 같은 얼굴이더라도 상대방이 남자라고 생각하고 보는 것과, 여자라고 생각하고 보는 것의 느낌과 평가결과는 크게 다르다는 것이다. 

 또한, 인공지능이 남자와 여자를 구분하는 것을 보여주는 것이 사용자로 하여금 좀더 재미와 호기심을 유발할 것이라고 생각하여, 성별을 구분하는 모델을 얼굴평가 모델에 앞에 이어 붙였다. 즉, 본격적인 얼굴평가는 성별이 먼저 구분된 이후에 이루어지도록 말이다.

Pipeline

딥러닝 모델은 tensorflow를 이용하여 구현했다. 처음에는 아래와 같이 직접 코드를 작성하여, 구축한 데이터셋으로 학습시켰지만, 유의미한 training set정확도와 dev set정확도가 나오지 않았다. 그도 그럴 것이 데이터셋이 겨우 사진 500장으로 구성되어, 학습량이 매우 부족했기 때문이었다.

def get_model():
     inputs = tf.keras.layers.Input(shape=(350, 350, 3))
    h = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(inputs)
    h = tf.keras.layers.MaxPooling2D((3, 3))(h)
    h = tf.keras.layers.BatchNormalization()(h)
    h = tf.keras.layers.Conv2D(128, (3, 3), activation='relu')(h)
    h = tf.keras.layers.MaxPooling2D((3, 3))(h)
    h = tf.keras.layers.Conv2D(128, (3,3), activation='relu')(h)
    h = tf.keras.layers.MaxPooling2D((3,3))(h)
    h = tf.keras.layers.Dropout(0.15)(h)
    h = tf.keras.layers.Flatten()(h)
    h = tf.keras.layers.Dense(128, activation='relu')(h)
    h = tf.keras.layers.Dense(256, activation='relu')(h)
    outputs = tf.keras.layers.Dense(5, activation='softmax')(h)
    
    model = Model(inputs=inputs, outputs=outputs)
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])    
    model.summary()
    
    return model

 부족한 데이터셋을 극복하기 위한 방법은 크게 세가지가 있다.

 첫번째는, 그냥 데이터를 더 모아오는 것이다. 굉장히 무식해 보이지만, 사실 이 방법이 유일한 방법이거나, 생각보다 가장 빠른방법인 경우가 많다.

 두번째는, 데이터셋을 합성(synthesize)해 내는 것이다. tensorflow에서는 이것이 쉽게 가능한데, ImageDataGenerator 와 같은 클래스를 이용하면, 데이터셋에 이미있는 이미지에 명암, 기울기, 크롭, 좌우반전 등의 다양한 변화를 가한 새로운 이미지를 생성시켜 준다. 다만, 이는 절대로 만능이 아니다. 모델이 좀더 예상치 못한 데이터에 generalize 할 수 있도록 도와주는 도구 수준이라고 이해하는 것이 옳다. 

 마지막은, Transfer Learning(전이학습)이 가능하면, 이를 활용하는 것이다. Transfer Learning은 인간으로 비유하면, 원래 알고 있던 지식을 응용하여 다른 곳에 써먹는 것으로 생각하면 이해가 쉽다.

 이미지 인식을 활용하는 딥러닝 모델들은 궁극적인 목표작업은 다를지라도, 밑단의 레이어에서 일어나는 작업들은 비슷한 경우가 많다. 예를 들어, 강아지와 고양이를 구분하는 이미지 인식 모델이 있다고 가정해 보자. 궁극적인 목표작업은 강아지와 고양이를 구분해 내는 것이지만, 밑단의 레이어에서는 사물의 경계선을 구분해 내거나 명암을 구분해 내는 작업들이 일어난다. 즉, 인간의 얼굴을 구분하는 것이든, 고양이와 강아지를 구분하는 것이든, 이미지를 분석하는 과정에서 일어나는 기초작업들은 비슷하다는 것이다. 따라서, 이미 학습된 다른 이미지 인식 모델의 밑부분만 가져와서 윗부분에 내가 하고자 하는 목표 작업 레이어만 붙여버린후, 내가 갖고 있는 데이터로 또 한번 학습시키면, 이것이 바로 Transfer Learning이 되는 것이다. 당연히 비슷한 종류의 목표작업이나, 이미지를 학습했던 모델을 가져오는 것이 가장 Transfer Learning을 하기에 좋으며, 어느 부분까지 사용하고 어느 부분부터는 잘라서 버릴 것인지 선택하는 것은 사용자의 선택에 달려있다.

 

 나는 MobilenetV2 라는 딥러닝 모델을 사용하기로 했다. 모델에 대한 자세한 설명과 사용방법은 다음 링크에서 확인할 수 있다. Tf2 Preview | Mobilenet V2 | Feature Vector | TensorFlow Hub (tfhub.dev)

 

TensorFlow Hub

 

tfhub.dev

MobilenetV2에 대한 간략한 설명은 다음과 같다.

MobileNet V2 is a family of neural network architectures for efficient on-device image classification and related tasks.

소형 디바이스에서의 이미지 분류와 관련된 작업을 위한 Neural network 아키텍쳐의 종류중 하나라고 소개를 하고 있다. 즉, MobileNetV2는, 개발부터 스마트폰, 라즈베리파이 등의 소형 컴퓨터에서의 작동을 염두에 두고 개발된 효율성 위주의 아키텍처라고 볼 수 있다. 또한 ILSVRC-2012-CLS 데이터셋으로 image classification 작업이 이미 학습되어있는 상태로, 사용자는 이 모델을 그대로 불러와서, 원하는 부분만 잘라내어 사용할 수 있다.

#fetch mobilenet_v2 model
def get_mobilenet_model():
    mobilenet_v2 = hub.KerasLayer("https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4", output_shape=[1280], trainable=False)
    
    model = tf.keras.Sequential([
        tf.keras.layers.InputLayer(input_shape=(224, 224, 3)),
        mobilenet_v2,
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(5, activation='softmax')
    ])
    
    model.build((None,)+(224,224,3))
    model.summary()
    
    return model

Transfer Learning을 이용하면 윗부분만 갈아치우면 되기 때문에, 코드가 굉장히 간략해진다. 

두번째 줄에 있는 KerasLayer method에 'trainable=False'를 지정함으로써, mobilenet의 output 레이어를 날려버린 채로 불러왔다. 그리고, 얼굴을 총 5개의 단계로 분류하는 것이 최종 목표이므로, 5개의 unit을 갖고 있는 Dense 레이어를 mobilenet 모델위에 올렸다.

 

마찬가지로, 성별 구분을 위한 딥러닝 모델도 Mobilenet을 활용하여 다음과 같이 만들었다.

유일한 차이점은, 성별 구분은 binary classification(이진분류) 이기 때문에 output layer의 유닛 수가 1이고, activation 함수가 softmax가 아닌 sigmoid 함수라는 것이다.

#fetch mobilenet_v2 model for binary classification
def get_mobilenet_model_gender():
    mobilenet_v2 = hub.KerasLayer("https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4", output_shape=[1280], trainable=False)
    
    model = tf.keras.Sequential([
        tf.keras.layers.InputLayer(input_shape=(224, 224, 3)),
        mobilenet_v2,
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(1, activation='sigmoid')
    ])
    
    model.build((None,)+(224,224,3))
    model.summary()
    
    return model
    

 

이제 모델 트레이닝을 위한 대부분의 작업이 끝났다. 

 

directory_man_train = 'dataset/cropped_train_set/man'
directory_man_val = 'dataset/cropped_val_set/man'

datagen = ImageDataGenerator(rotation_range=20,
                            horizontal_flip=True,
                            brightness_range=[0.6, 1.3],
                            shear_range=0.15,
                            rescale=(1./255.))

train_generator_man = datagen.flow_from_directory(directory_man_train,
                                             target_size=(224, 224),
                                             batch_size=1,
                                             class_mode='sparse')

val_datagen = ImageDataGenerator(rescale=(1./255.))

val_generator_man = val_datagen.flow_from_directory(directory_man_val,
                                                   target_size=(224, 224),
                                                   batch_size=1,
                                                   class_mode='sparse')

 위의 코드는 이미지 500장에 대한 합성데이터를 만들어 내기 위한 과정이다.

내가 보유하고 있는 데이터의 숫자가 절대적으로 부족했기 때문에,

모델 overfitting의 가능성을 낮추기 위한 데이터합성 과정은 필수적이었다.

tensorflow의 ImageDataGenerator 클래스는 기존 이미지에 명함변화, 휘도변화, 색도변화, 기울기변화, 크롭 등을 적용시킨 새로운 이미지를 메모리 상에서 생성하여, 모델의 학습 데이터로 활용할 수 있게 해 준다. 아래의 이미지를 보면, 무슨 말인지 쉽게 이해가 갈 것이다.

이미지 1장이 12장으로 뻥튀기 되었지만, 사실은 모두 같은 이미지다.

 

데이터셋의 양이 많지 않았기 때문에 CPU를 이용하여 학습을 시키더라도 성별분류, 남자얼굴분류, 여자얼굴분류 총 3개의 모델을 학습시키는데에 10분도 걸리지 않았다. 그러나, 결과는 놀라웠다. 겨우 500장의 이미지로 학습을 했을 뿐인데, validation 정확도가 80%를 넘었다. 특히 놀라웠던 부분은, 성별 분류의 경우 정확도가 90% 이상이었다는 것이다. 

 또한, 애초에 balanced dataset 으로 학습시켰기 때문에, accuracy 이외에 precision 혹은 recall, roc auc score 등의 metric들도 이상이 없었다.

 사람의 눈으로 보아도 남성 같이 생긴 여성의 경우, 남성일 확률이 크게 올라갔고, 그 반대도 마찬가지였다. validation set 안에 있는 이미지가 아닌, 아예 데이터셋에 없는 이미지를 여러번 모델에 집어넣어본 결과, 내가 보는 눈과 크게 다르지 않아서, 모델의 generalize 성능도 500장으로 학습된 것 치고는 꽤 괜찮다는 것을 느낄 수 있었다.

  

 

모델 배포 과정

 이제, '만들어진 모델을 라즈베리파이에 올리기만 하면 되겠구나!' 라는 기쁨에 찬 생각을 했다.

그러나 이는 곧 절망으로 바뀌게 된다. 사실, 이제부터가 시작이었던 것이다. 

 

 라즈베리파이는 데비안 리눅스 기반의 라즈비안OS로 돌아가기 때문에, 리눅스를 어느정도 사용할 줄 알아야만 했다. 뿐만아니라, 라즈베리파이는 모니터가 없기 때문에 VSCODE의 SSH 원격 접속을 통해 CLI 환경에서 모든 것을 해결해야 했다. 또한, X86 아키텍처가 아닌 ARM 아키텍처의 CPU를 사용하기 때문에, 패키지 호환성에 대한 문제도 존재했다.

이제까지 윈도우의 GUI환경에만 익숙했던 나는, 리눅스, VSCODE, CLI 환경, ARM환경, 이 모든 것을 동시에 배우느라 정말 진땀을 뺐다. 거기에 conda 가상환경까지 사용하게 되니, 한 발자국을 내딛을 때마다 수많은 에러메세지와 마주치면서, 포기하고 싶을 때가 한 두번이 아니었다.

 

'도대체 왜 굳이 리눅스를 쓰는거야?', '가상환경은 대체 왜 만들어?', '대체 내가 설치한 파일은 어디에 있는거야?' , '내 컴퓨터인데 왜 자꾸 권한이 없데?', 'ARM은 안 된다네.. 그냥 노트북으로 할까?' 등의 수많은 질문과 답답함이 머릿속을 가득 채웠다.

 방법을 찾다 찾다 지치면, 한숨 자고 일어나서 다시 도전해 보고, '아직 못 찾은거지, 방법은 있을거야' 라는 마인드로 전세계 웹사이트를 뒤지면서 문제를 해결해 나갔다. 그리고 결과적으로는 내 생각이 맞았다. 누군가 나보다 먼저 물어본 사람이 항상 존재했다. 

 이러한 과정을 거치면서 지금은 내가 갖고있던 모든 질문들이 스스로 대답이 되었고, 이때 힘들게 배운 경험들과 지식들을 정말 잘 써먹고 있다. 그러나 다시는 돌아가고 싶지 않은 경험이다. (도커를 최대한 활용하자..)

 우선 라즈베리파이에 내가 만든 모델을 배포하기 이전에, 라즈베리파이에 웹서버와 웹 어플리케이션을 구동할 수 있는 프레임워크가 있어야만 했다. 웹 프레임 워크는 기존에 Coursera에서 배운 Python 기반의 Django 프레임워크를 활용하기로 하였다. 다행히, Django를 설치하는데에 필요한 모든 패키지와 라이브러리들이 ARM 리눅스와 잘 호환이 됐다.

 페이지의 디자인과 데이터의 입출력을 위한 CSS, HTML, 그리고 Django 데이터베이스 작업이 어느정도 마무리가 된 이후, tensorflow를 설치하려고 했는데, 이게 웬걸? 아직 tensorflow는 라즈베리파이를 지원하지 않는단다. 웬 청천벽력같은 소리인가? 

 잘 찾아보니, tensorflow lite를 이용하면 tensorflow 모델의 학습은 아니더라도 실행(inference)은 가능하다는 것을 깨닫고, 내가 만든 모델을 tesnorflow lite 코드로 다음과 같이 변환해주었다. 

#Load gender classifying model
model_gender = tf.keras.models.load_model('./savedmodel/gender_checkpoint')
#Load scoring model for a man 
model_man = tf.keras.models.load_model('./savedmodel/man_checkpoint')
#Load scoring model for a woman
model_woman = tf.keras.models.load_model('./savedmodel/woman_checkpoint')

converter = tf.lite.TFLiteConverter.from_saved_model('./savedmodel/gender_checkpoint')
tflite_model = converter.convert()
open("converted_model_gender.tflite", "wb").write(tflite_model)

converter = tf.lite.TFLiteConverter.from_saved_model('./savedmodel/man_checkpoint')
tflite_model = converter.convert()
open("converted_model_man.tflite", "wb").write(tflite_model)

converter = tf.lite.TFLiteConverter.from_saved_model('./savedmodel/woman_checkpoint')
tflite_model = converter.convert()
open("converted_model_woman.tflite", "wb").write(tflite_model)

tensorflow 모델을 tensorflow lite 모델로 변환하는 것은 매우 간단했다. 저장된 tensorflow 모델을 불러들인 이후, tensorflow lite로 변환시켜주는 method를 사용하면 끝이다.

 

그 이후, 라즈베리파이 서버로, 저장된 tflite 모델 파일과 코드를 옮기고, Django의 View에 연결시키면 배포준비가 완료된다. 자바스크립트를 다룰 줄 알면, 데이터의 입출력과정의 일부를 프론트엔드에서 해결할 수도 있지만, 나의 경우에는 아직 자바스크립트에 익숙하지 않아서, 대부분의 연산과 어플리케이션 작동이 Django의 views.py 에서 일어나도록 설계하였다. 즉, Python 코드에서 모든 백엔드 연산이 처리된 이후, 결과값만을 전달하도록 만들었다. 다만 인식된 얼굴에 사각형을 그리는 작업의 경우는, 백엔드에서 받아온 좌표값을 이용하여 프론트엔드에서 처리하도록 만들었다.

import numpy as np
import tflite_runtime.interpreter as tflite
import cv2
from PIL import Image
import os

base_dir = os.path.dirname(os.path.abspath(__file__))

#Load TFLite models
interpreter_gender = tflite.Interpreter(os.path.join(base_dir, 'converted_model_gender.tflite'))
interpreter_man = tflite.Interpreter(os.path.join(base_dir, 'converted_model_man.tflite'))
interpreter_woman = tflite.Interpreter(os.path.join(base_dir, 'converted_model_woman.tflite'))

#Feed an interpreter and array data and get output_pred
def get_output_tflite(interpreter, input_data):
    
    input_details = interpreter.get_input_details()
    output_details = interpreter.get_output_details()
    interpreter.allocate_tensors()
    
    interpreter.set_tensor(input_details[0]['index'], input_data)
    interpreter.invoke()
    output_data = interpreter.get_tensor(output_details[0]['index'])
    return output_data

def get_face_location(image_path):
    
    #Load the cascade
    face_cascade = cv2.CascadeClassifier(os.path.join(base_dir,'haarcascade_frontalface_default.xml'))

    #Read the input image with PIL Image
    image_path = image_path
    img = Image.open(image_path)
    img = np.array(img)
    #rgb to bgr
    img = img[...,::-1]

    #Convert into grayscale
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #Detect faces
    faces = face_cascade.detectMultiScale(gray_img, 1.03, 6)
    
    #Check whether there are more than one face or not
    detecting_result = 2 # 2 is normal
    if faces == ():
        detecting_result = 0 # "There's no face detected"
    elif len(faces) > 1:
        detecting_result = 1 # "There's more than one face detected"

    #Suppress other boxes
    max_box_coordinates = (0, 0, 0, 0)
    for (x, y, w, h) in faces:
        if w * h >= max_box_coordinates[2]*max_box_coordinates[3]:
            max_box_coordinates = (x, y, w, h)

    return (max_box_coordinates, img, detecting_result)

def cropped_image(image_ndarray, x, y, w, h, detecting_result):
    
    if detecting_result == 0:
        #print("There is no face detected")
        return image_ndarray
        
        
    else:
        cropped_img = image_ndarray[y:y+h, x:x+w]
        return cropped_img

def get_face_rank(pred):
    
    if pred == 0:
        string = "ㅆㅎㅌㅊ 입니다..힘내세요.."
    elif pred == 1:
        string = "ㅎㅌㅊ 입니다.."
    elif pred == 2:
        string = "ㅍㅌㅊ 입니다.."
    elif pred == 3:
        string = "ㅅㅌㅊ 입니다.."
    else:
        string = "ㅆㅅㅌㅊ 입니다. 축하합니다!"
    
    return string

def tflite_model_predict(image_path):
    image_path = image_path   

    face_coordinates, img, detecting_result = get_face_location(image_path)
    x, y, w, h = face_coordinates
    cropped_img_original = cropped_image(img, x, y, w, h, detecting_result)
    cropped_img = cv2.resize(cropped_img_original, (224, 224))
    cropped_img = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2RGB)/255.
    
    pred_sex_num = get_output_tflite(interpreter_gender, np.array(np.expand_dims(cropped_img, axis=0), dtype=np.float32))
    pred_sex = np.round(pred_sex_num)
    # Classify gender first
    if pred_sex == 0:
        sex = 'man'
    else:
        sex = 'woman'
    # Scoring a face based on the gender
    if sex == 'man':
        pred = get_output_tflite(interpreter_man, np.array(np.expand_dims(cropped_img, axis=0), dtype=np.float32))
    else:
        pred = get_output_tflite(interpreter_woman, np.array(np.expand_dims(cropped_img, axis=0), dtype=np.float32))
    
    return pred, np.argmax(pred[0]), cropped_img_original, detecting_result, pred_sex_num, face_coordinates

 

완성된 어플리케이션의 작동화면은 다음과 같다.

원하는 사진을 고른후 UPLOAD를 클릭하면, 백엔드에서 딥러닝 모델이 작동하여 성별의 예측값과 얼굴평가의 예측된 레이블값 출력하여 준다. 소요시간은 1~2초 내외이며, 웬만한 스마트폰보다 성능이 낮은 라즈베리파이 위에서 작동한다는 것을 고려하면 굉장히 훌륭한 속도라고 생각된다.

레이블은 1점이 가장 안좋은 점수이며 5점이 가장 높은 점수이다.

500장의 20대 ~ 30대 한국인 이미지로 학습시켰기 때문에, 나이대가 다르거나, 인종이 다른 경우 정확한 예측이 어려울 수 있으며, 데이터셋의 숫자가 작아, 엉뚱한 결과가 나올수도 있기 때문에, 점수가 낮더라도 신경쓰지 않아도 된다.

 

 

프로젝트를 마무리하며 느낀 점

지금와서 돌아보면 별 것 아닌것처럼 느껴지지만, 당시에는 프로젝트를 진행하면서 어려움을 많이 겪었다.

 어려움은 크게 두 가지로 나눌 수 있었다.

 첫번째로는, 딥러닝을 제대로 활용하기 위해서는, 딥러닝 자체뿐만아니라 데이터와 데이터 전처리에 대한 개념들을 잘 숙지하고 있어야 한다는 것이었다. Coursera 에서의 수업과제를 할때는 이미 주제와 데이터셋이 준비가 되어있었기 때문에, 모델의 파라미터 설정을 통하여, 모델의 성능을 높이는 데에만 집중을 했었다. 그러나, 현실에서는, 문제 해결을 위한 데이터셋을 구축하고 모델의 파이프라인을 설계하는 것이 딥러닝 모델 자체의 성능을 높이는 것보다 더 중요했다.

 첫 단추를 잘 못 끼우면 그 뒤의 일들은 의미가 없어지기 때문이다. 특히, 데이터의 레이블을 메기는 과정 또한, 레이블을 메기는 사람의 주관과 판단 기준이 모델 학습에 직접적인 영향을 미칠 수 있으므로, 매우 신중하게 진행해야 했다는 점도, 데이터 자체의 중요성을 깨닫게 해주는 소중함 경험이 되었다.

 

 두번째로는, 배포를 하기 위해서는, 다른 분야의 지식도 충분히 알고 있어야 한다는 점이었다. 머신러닝과 딥러닝만 할줄 안다는 것은, 요리로 따지면, 요리의 레시피만 알고, 재료를 어디서 사는지, 어떻게 손질하는지는 모르는 것과 같다. 즉, 여러 분야를 두루두루 알고있어야 배포가 가능하다는 것이다. 이러한 이유로, 프로그래밍 언어 뿐만 아니라, 가상환경과 IDE를 활용하여 체계적인 개발환경을 구축하는 방법, 리눅스 운영체제와, DOM 모델과 같은 웹의 구조, Database, SQL의 기본구조, 네트워크 기초 등 많은 분야의 지식들을 빠르게 배워나가야 했고, 이러한 지식들 또한 머신러닝과 딥러닝 못지않게 중요하다는 것을 깨달을 수 있었다.

 

마지막으로 느낀 점은, 이미지를 활용하는 딥러닝 모델은 데이터셋을 구할 수 있는 환경이 매우 제한적이라는 것이다. 쉽게 scraping이 가능한 text 데이터와는 달리, 이미지 데이터는 scraping이 제한적이고, 데이터셋을 구축한다고 하더라도, 이미지의 특성상 용량이 크기 때문에, 개인이 많은 양의 데이터를 저장하여 사용하기가 어렵다는 것이다.

 이러한 데이터셋 구축의 어려움 때문에, 비교적 접근이 쉬운, text 데이터를 활용하는 Natural Language Processing에 더 많은 시간을 투자하는 것이 효율적일 것이라는 생각을 하는 계기가 되었다.

 

 

 

 

블로그 이미지

찰리와마약공장

,

미시건 대학교의 Data Science Specialization 과정은, 이름 그대로, 데이터사이언스에 대하여 폭넓게 가르친다. 데이터 전처리와 시각화부터 머신러닝을 활용하여 모델을 설계하는 작업까지 모두 배울 수 있는 수업이었는데, 개인적으로 정말 많은 것을 얻어갈 수 있었던 Specialization 과정 중 하나였다.

 

 매 코스마다 내주는 높은 난이도의 코딩 과제는, 수업 내용으로는 모자라서, 직접 추가 자료와 라이브러리 Documentation 들을 인터넷에서 찾아서 해결해야 했다. 이러한 과정은 꽤 힘들었지만, 지금 돌아와서 보면 이때 배운 내용들이 어렵게 배운만큼 정말 큰 도움이 된다고 느낀다. 또한, 일부 과제의 경우는 정해진 해답이 없이, 자신이 주제를 정하여 데이터 분석 과제를 수행한 후, 수업을 듣는 수강생들끼리 서로의 과제물을 채점하는 peer review형식으로 채점이 되었는데, 같은 문제를 가지고 다양한 방법으로 접근하는 열정적인 학생들을 보면서, 많은 아이디어를 얻음과 동시에, 큰 동기부여가 되었다.

 

 이 수업을 수강하면서 느낀점을 크게 세가지로 요약할 수 있을 것 같다.

 

 첫번째는, 데이터분석에서 절대로 빼놓을 수 없는 Python 의 pandas 라이브러리와 matplotlib 라이브러리를 제대로 이해하고 배웠다는 점이다. pandas 라이브러리는 쉽게 이야기하면 마이크로소프트의 엑셀과 비슷한 녀석이라고 생각하면 된다. 데이터를 열과 행으로 구성된 깔끔한 표의 형태로 나타낼 수 있도록 도와줄 뿐만 아니라, 프로그래밍 언어로 가능한 모든 연산을 데이터에 쉽게 적용할 수 있도록 해주고, 데이터를 자르고 합치는 모든 번거로운 작업들을 코드 몇줄로 끝나도록 해주는 마법같은 라이브러리다.

 마이크로소프트 엑셀의 경우는, 데이터를 입력하는 것은 편해도, 사실, 원하는 값을 얻기 위해, 데이터를 조작(manipulation) 하기에는 매우 무겁고 비효율적인 도구이다. 간단한 예시로, 엑셀은 데이터 행의 수가 몇 만줄만 되어도, 프로그램이 굉장히 무거워 질 뿐만아니라, 두 개의 엑셀 파일을 특정 컬럼의 데이터를 기준으로 교집합인 데이터 행만 합치거나, 특정 컬럼의 데이터만 추출해서 가공한 후, 새로운 컬럼을 생성하고 싶을 때, 이를 쉽게 할 수 있는 마땅한 방법이 거의 없다. 반면에, pandas 의 경우는 데이터의 행이 수백만 줄이 되더라도 성능 저하가 거의 없고, 아무리 복잡한 데이터 조작(manipulation)이더라도 코드 몇 줄만 작성하면 작업을 쉽게 끝낼 수 있다. 더군다나 pandas를 이용하면 엑셀파일도 읽어들일 수 있고, 작업한 내용을 엑셀파일로 저장할 수 있기까지 하니, 엑셀 노가다 경험이 많았던 나로서는 충격이 아닐수가 없었다.

 matplotlib의 경우는, 데이터를 바탕으로 차트와 플롯을 그려주는 라이브러리로, 엑셀에서 표를 바탕으로 차트를 그리는 것을 생각하면 이해가 쉬울 것이다. pandas와도 찰떡궁합으로, 데이터 전처리가 올바르게 되었는지 시각화 하거나, 결과물의 시각화를 통해 타인의 이해를 돕는데에 매우 유용한 라이브러리 중 하나이다.

 

 두번째는, Feature Engineering의 중요성에 대해 다시 한번 깨달았다는 점이다. Feature Engineering 이란, 수집한 데이터를 가공하는 과정에서, 데이터의 특성을 이해한 후, 유의미한 데이터를 골라내고, 머신러닝 모델에 집어넣을 수 있도록 재단하는 작업이다. 이 과정은 머신러닝 모델 뿐만아니라, 데이터 그 자체에 대한 깊이있는 이해를 필요로 한다.

 

 간단한 예로, 집 값을 예측하는 머신러닝 모델을 만든다고 생각해 보자. 우리가 갖고있는 데이터에는 집의 넓이(제곱미터), 방의 개수(숫자), 집의 종류('아파트'or'단독주택'or'오피스텔'), 집의 위치(주소),  총 4가지 값이 있다고 가정하자. 그럼 이 데이터들을 어떻게 활용하여야 할까?

 

 기본적으로 머신러닝 모델에 데이터를 투입하기 위해서는, 투입되는 데이터의 사칙연산이 가능해야만 한다. 집의 넓이와 방의 개수의 경우는 numeric value(숫자값)로서, 더하고 빼고, 곱하고 나누는 것이 가능하여 머신러닝 모델에 투입될 수 있다. 그러나 categorical value(종류값)인 집의 종류는 '아파트 + 단독주택' 이 아무런 의미가 없는 것처럼 일반적인 연산이 불가능하여 별도의 처리가 필요하다. 뿐만아니라, 집의 주소는 심지어 그냥글자이다. 글자를 어떻게 유의미한 값으로 변환해서 머신러닝 모델에 활용할 수 있을까?

 방법은 여러가지가 있겠지만, categorical value인 집의 종류의 경우는 one-hot encoding을 이용하면 집의 종류값을 여러개의 컬럼으로 쪼갠 숫자값으로 변환시킬 수 있다. 예시의 경우에는 '아파트' 를 (1,0,0) '단독주택' 을 (0,1,0), '오피스텔'을 (0,0,1) 로 변환하면, 사칙연산 및 비교가 가능해 지기 때문에, 머신러닝 모델에 투입할 수 있게 될 것이다.

 마지막으로 주소값의 경우는 글자이기 때문에, 이를 그대로 활용할수는 없다. 따라서, 주소값을 수치화하기 위해서, 주소에 해당되는 우편번호 값이나 경도값과 위도값을 주소값 대신 사용하여, 머신러닝 모델에 집어넣으면 될 것이다.

 하지만 아직 모든 것이 끝난 것은 아니다. 이대로 학습을 하게 되면, feature 간의 scale(범위)이 다르기 때문에, 학습 정확도가 떨어지거나, 학습이 오래걸릴 수 있다. 예를 들어, 집의 넓이의 값은 대부분 10~150 정도 범위의 숫자일 것이다. 반면에 방의 개수는 1~5 정도의 범위안에 들어가는 숫자가 대부분일 텐데, 이런 경우, 모델이 학습을 하는 과정에서 방의 개수 보다는 집의 넓이의 값에 더 큰 영향을 받을 가능성이 높아진다. 따라서, 이를 방지하기 위해 Feature Scaling혹은 Normalization과 같은 작업이 필요하게 된다.

 이러한 특성들 때문에, Feature Engineering 과정은 분석자의 도메인지식이 매우 중요한 역할을 하는 부분 중 하나이다. 기업의 회계정보를 담고 있는 재무제표를 다룬다고 생각해보자. 관련 지식이 없다면, 재무제표에서 어떤 수치가 유의미한 수치인지 골라내는 데에도 어려움을 겪을 것이다. 또한, 유의미한 수치를 골라낸 이후에도, 만약 feature 자체가 feature들 간의 상관성을 갖고 있는 경우, 무작정 Feature Scaling을 해버리면 데이터가 내포하는 의미가 훼손되기 때문에 많은 고민을 해야한다. 더군다나 시계열 데이터가 섞여있는 경우에는, Data Leakage가 일어나지 않도록 유의해야 하는데, 이러한 모든 과정들이 머신러닝, 딥러닝 모델을 만들고 학습시키는 것보다 더 많은 노력과 시간을 필요로 한다는 점을 깨달을 수 있었다.

 

 마지막으로 느낀점은, 머신러닝과 딥러닝은 데이터사이언스의 일부분일뿐이라는 것이다. 아무리 알파고가 사람보다 바둑을 잘두고, 인공지능 왓슨이 의사보다 암을 잘 찾아내도, 컴퓨터는 아직까지도 계산기일 뿐이다. 머신러닝 알고리즘은 수십년 전부터 존재해왔었던 것들이며, 현대에 들어서 인터넷의 등장으로 데이터의 수집이 용이해지고, 컴퓨터의 성능이 비약적으로 발전하면서 상용화가 가능하게 되자, 마치 없었던 것이 처음 발견된 것처럼 인식되는 것이다. 

 즉, 컴퓨터는 추론을 할 수 없다. 추론을 하는 것처럼 흉내내는 것일 뿐이다. 그리고 흉내내는 방법은 인간이 하나하나 데이터를 주입시켜 가르쳐 주어야만 한다. 예를 들어보자. 어린아이, 혹은 심지어, 강아지들도 컵이 책상위에서 떨어지면 피하는 시늉을 한다. 왜그럴까? 컵이 공중에 뜨게 되면 떨어진다고 누군가에게 교육을 받아서 인지하는 것일까? 아니다. 여러번의 관찰을 통해 자연스럽게 사물은 공중에 뜨면 떨어진다는 것을 은연중에 깨닫는 것이다. 즉, 중력의 개념을 자연스레 이해하게 된다. 그리고 이러한 개념을 한번 깨닫게 되면 컵뿐만이 아니라 사과, 펜, 휴대폰 등 다른 물건들도 책상을 이탈하면 똑같이 떨어진다는 것을 알게 된다. 그러나 컴퓨터는 아니다. 컵이 책상에서 이탈하면 떨어진다는 사실을 10만번의 예시를 주입시켜 학습시키고 나면, 컴퓨터도 컵이 떨어질 것이라는 것은 사람만큼 정확하게 예측할 수 있다. 그러나, 컵이아닌 사과 혹은 휴대폰이 떨어지는 경우에는 전혀 답을 내놓지 못하게 된다. 

 이를 극복하기 위해 최근, Transfer Learning(전이학습)을 통해 미리학습된 딥러닝 모델을 다른분야에도 적용할 수 있도록 알고리즘에 대한 연구가 계속 되고 있지만, 아직까지 자연어처리 분야를 제외하면, Transfer Learning은 굉장히 제한적인 것이 현실이다. 즉, 사람은 하나를 가르쳐 주면 열을 알지만, 컴퓨터는 하나를 가르쳐주면 하나만 안다.

 따라서, 여러가지 태스크에 대한 머신러닝 모델을 설계할 때 가장 중요한 것은 바로 데이터의 수집과, 처리이다. 앞서 말했던 것과 같이, 데이터를 어떻게 수집하느냐, 어떻게 유의미한 feature 들을 추출, 가공하느냐가 머신러닝과 딥러닝만큼이나 중요하다는 것을 몸소 깨달을 수 있었다.

 

 

 

 

 

블로그 이미지

찰리와마약공장

,

인간이 배우는 스킬은 크게 두 가지로 나눌 수 있다. 첫 번째는 가치를 발견하는 통찰력, 대인 관계 능력 등의 추상적인 능력인 소프트스킬이고, 두 번째는 직무 스킬과 같이 증명하기가 쉬운, 하드스킬이다. Coursera의 딥러닝 Specialization 과정을 수료한 이후, 곰곰히 생각해보니, 나의 하드스킬이 굉장히 부족하다고 판단했다. 머신러닝과 딥러닝이 정확히 무엇이고, 어떻게 활용이 가능한지, 미래에 어떠한 가치를 지니게 될지 등의 추상적인 개념의 학습은 충분히 되었지만, 그러한 아이디어를 직접 프로그래밍 코드에 녹여내어, 배포하는 단계가 여전히 막막하게 느껴졌기 때문이다.

 

 그도 그럴것이, 이제껏 주먹구구식으로 코드를 짜깁기하면서, 프로그래밍 언어를 체계적으로 공부한 적이 없었으므로, 다시 Python을 기초부터 차근차근 닦아나가기로 마음먹었다. Python은 문법이 직관적이서 배우기가 쉽고 수많은 머신러닝/딥러닝 라이브러리를 지원하기 때문에 데이터사이언스에서 주축이 되는 언어이다. 

 수업은 미시건 대학교의 Python Specialization을 택해 수강했고, 동시에, 웹을 통해 배포하는 방법을 배우기 위해, 같은 대학의 Django Specialization도 함께 수강했다.

 

 이것 저것 수업을 듣다보니 퍼즐 조각이 하나하나 맞추어지는 느낌이었다. 내가 모르던 부분을 알게되고, 내가 궁금해하던 부분을 해결하게 되면, 한 단계 더 나아간 새로운 아이디어가 생겼다. 어느 한 수업을 듣다가도 다른 수업에서 들었던 내용과 연관된 아이디어가 떠올랐다. 마치 눈 앞의 시야가 점차 확장되는 느낌이었고, 이 느낌은 다시 학습의 원동력이 되었다.

 

 내 손으로 직접 구현할 수 있는 부분이 많아지자, 개인 프로젝트의 아이디어도 떠올랐다. '딥러닝을 이용하여 사람의 얼굴을 평가해 보면 어떨까?' 라는 재미난 생각이 들었다. 어느정도 머신러닝, 딥러닝도 배웠겠다, 프로그래밍도 그럭저럭 할 줄 알겠다, 웹을 구축해서 어플리케이션 배포도 할줄 알겠다. '이건 각이 나온다' 라는 생각이 들었다. 

 그런데 웬걸, 준비과정부터가 만만치가 않았다. '서버는 어떻게 만들지?, 데이터는 어디서 구하지?, 데이터의 분류는 어떻게 하지?, 리눅스는 대체 왜이래?'

 

 내가 하고 싶었던건 세련된 머신러닝, 딥러닝이었는데 실상은 거기까지 가기도 전에 지쳐버리는 상황이 온 것이다. 리눅스 서버를 돌리기 위해 라즈베리파이를 구입했는데, 근 일주일을, 호환되는 라이브러리를 찾아서 설치하고 개발환경을 세팅하는데에 썼었던 것 같다. 사실, 말이 좋아 세팅이지, 정말, 수도 없는 에러를 만나 구글링을 하면서 리눅스와 개발환경에 대해 몸으로 배워가는 시간이었다.

 

 드디어 딥러닝에 대해 생각할 수 있는 단계가 오자, 그 동안 내가 온실 속 화초였다는 것을 느끼게 된다. 강의에서 내주던 과제에서는 십수만개의 데이터 세트가 미리 분류된 레이블값과 함께 항상 깔끔하게 준비되어 있었다. 하지만, 현실은데이터셋을 구하기는 커녕, 레이블도 내가 직접 메기어야 하는 상황이었다. 

 

 나의 경우엔, 사람들의 얼굴을 평가하는 딥러닝 모델을 만들기 위해서, 사람들의 얼굴 사진과, 이에 대한 점수값을 딥러닝 모델에 학습 데이터로 넣어주어야 했다. 직접 사진을 수집하면서 많은 문제점들을 발견하게 된다. 곰곰히 생각해보면, 사람은 사실, 사람의 얼굴을 평가할 때 그 사람의 얼굴만을 평가하지는 않는다. 즉, 그 사람이 입은 옷, 그 사람의 체격, 헤어스타일, 그 사람이 유명한 사람일 경우 그 사람의 평판, 사진을 찍은 곳의 배경 등, 얼굴자체와는 관련이 없는 정보들 또한 사람의 의사결정에 영향을 미치게 된다. '그럼 내가 컴퓨터한테 어떠한 정보를 전달해야 객관적인 얼굴평가가 이루어 지도록 학습시킬 수 있을까?' 라는 생각이 자연스레 들수밖에 없다. 뿐만아니라, 모델은 학습데이터의 레이블을 메기는 사람의 주관대로 학습이 될수밖에 없다는 한계점도 깨닫게 된다. 즉, 같은 얼굴을 두고도, 눈이 높은 사람은 점수를 낮게 메길 것이고, 눈이 낮은 사람은 점수를 높게 메길수도 있다. 따라서 레이블을 제공한 사람의 주관이 그대로 모델로 흡수된다는 것이다.

 

 결국 주관이 개입된다는 점은 어쩔수 없기에, 얼굴 이외의 정보를 최대한 배제시킨 후 모델을 학습시키기로 했다. 수집한 사진에서 얼굴만을 크롭해서 크롭된 얼굴사진만을 가지고 학습을 시작하는 것이다. 

 이를 위해, Python opencv 라이브러리를 통해 얼굴을 자동인식하여 크롭하는 과정을 전처리 과정에 추가하고, 딥러닝 모델은 컴퓨팅 성능이 제한적인 라즈베리파이에서 사용하기 위해, MobilenetV2를 이용하여 약 500 장의 사진으로 학습시켰다. 적은 데이터셋을 바탕으로 최대한의 효율을 내기 위하여, 성별 구분 모델을 얼굴평가 모델 앞에 추가하였고, 각 레이블별 데이터 수를 똑같이 수집하여 Imbalanced dataset이 되지 않도록 데이터셋을 구축했다.

 결과는 꽤나 놀라웠다. 사실 데이터 샘플의 수가 500장 정도밖에 되지 않기 때문에, 유의미한 성능을 기대하지는 않았다. 그러나 테스트 이미지를 넣어보니 내가 판단하는 점수와 크게 다르지 않아서, 딥러닝의 잠재력을 다시금 깨닫게 되었다.

 위의 프로젝트는 웹으로 배포하고 있으며 http://charliethewanderer1.ddns.net/faceeva/

 

http://charliethewanderer1.ddns.net/faceeva/

 

charliethewanderer1.ddns.net

누구나 방문하여 이용해 볼 수 있도록 서버를 항시 켜 놓고 있다. 참고로 말하지만, 해당 프로젝트는 겨우 사진 500장으로 학습되어 정확한 값을 예측 못할 수 있으니, 이상하게 나오더라도 실망하지 말자.

 

프로젝트 이후에 느낀점은 다음과 같다.

 우선, 내가 얼마나 부족한지 깨달았다. 항상 Coursera 수업에서 들어왔던 내용이지만, 쉽게 지나쳤던 내용들이 내 뼈를 때리는 경우가 많았다. 특히 데이터를 다루는데에 있어서, Skewed data가 무엇인지, dev set과 test set의 차이점, feature engineering이 무엇인지 등 매우 기본적인 개념들이 가장 중요하다는 것을 깨달았다. 사실 데이터를 다루는 첫 단추가 꼬여버리면 아무리 좋은 컴퓨터와 딥러닝모델을 써도 시간과 전기세만 낭비하는 셈이 되어버리니까.

 

 둘째로는, 딥러닝은 모델아키텍처가 아닌, 데이터셋이 제일 중요하다는 것이다. 즉, 데이터셋을 구할 수 없으면, 아무리 좋은 아이디어와, 딥러닝 모델 아키텍처도 그림위의 떡이다. 데이터 자체는 자동화하여 수집할 수 있다고 하더라도, 각 데이터 샘플에 대응되는 레이블 할당은 일부를 제외하면 자동화가 불가능하다. 사람이 하나하나 노가다로 레이블을 부여할 수밖에 없는 것이다. 따라서 프로젝트를 계획할 때, '내가 데이터셋을 구하거나, 추출 하는 것이 가능한가?' 라는 질문을 우선시 하게 되었다.

 

 마지막으로, 데이터사이언스는 개발자마인드가 필수적이라는 것이다. 즉, 프로그래밍과 개발환경세팅에 친숙해야 하며, 리눅스도 잘해야 한다. 요리의 레시피를 줄줄 외우고 있으면 뭐하는가? 정작 재료를 어디서 살 줄도 모르고, 손질도 할 줄 모르면 요리를 못하는데. 또한 요리를 하는데 들어가는 시간의 대부분은 조리가 아닌, 재료를 구입하고, 손질하는데에 들어가지 않는가? 데이터사이언스도 마찬가지로 보면 되는 것 같다.

 이처럼 학습과, 배운내용의 실행을 반복하는 Empirical cycle을 반복하는 것이 데이터사이언스의 핵심이라는 것을 깨닫고, 데이터의 전처리를 체계적으로 배우기 위해, 또다시 미시건 대학교의 Data Science Specialization 을 수강하게 된다.

블로그 이미지

찰리와마약공장

,

English Version: The Reason I Started Data Science as a Business-Student — 2 | by Charlie_the_wanderer | Feb, 2021 | Medium

 

The Reason I Started Data Science as a Business-Student — 2

I started the machine learning course with thoughts of ‘Let’s just test my English skills.’, ‘I’m a business student, so I would be…

charliethewanderer.medium.com

 

머신러닝 강의를 처음 듣기 시작했을 때는, '내 영어실력을 테스트 해 보자' , '난 문과생이니, 머신러닝을 교양수준정도만 배워도 만족해' 라는 2가지 생각으로 시작했었다. 하지만 수업을 계속 듣고 공부를 하면 할수록, 머신러닝은 천재들만을 위한 어려운 개념이 아니었다는 것을 깨닫게 되었다. 문과 출신이더라도, 고등수학에서 요구되는 수준의 수학지식만 갖고 있으면, 전체적인 개념은 이해하기에 크게 어렵지 않았다. 물론 벡터 연산에 익숙해 지는데에 많은 시간이 걸렸지만. 이쯤 되자, 학교공부와 코세라공부의 주객이 전도됐다. 학교공부는 시험기간에만 벼락치기를 하게 됐고, 오히려 코세라의 머신러닝 강의를 꾸준히 공부하기 시작했다. 마치 학교공부에 머신러닝 공부할 시간을 빼앗기는 것처럼. 

 

 머신러닝 수업을 수료한 이후, 이어서 Deep Learning Specialization 과정에 등록했고 이쪽 분야를 진지하게 나의 커리어로 고려하기 시작했다. 코세라의 Specialization 과정은 일반 코스(1~2개월)과정을 여러개 묶어서 장기과정(5~6개월 이상)으로 짜놓은 커리큘럼을 뜻한다. 

 마찬가지로 딥러닝 과정에 대한 만족도는 굉장히 높았고, 매 코스마다 배워가는것들이 확실했다. 특히, 직접 코드를 짜서 일정 점수이상을 맞지 못하면 다음으로 넘어갈 수 없도록 하는 Coursera의 학습 시스템은, 매 학습동안 긴장의 끈을 놓지 않도록 동기를 부여해 주는 역할을 했다. 

 

 하지만 모든 수업이 완벽할 수는 없듯이, 이 수업 또한 몇가지 의문점들을 불러일으켰다. 그 중 하나는, 수업자체가 딥러닝의 알고리즘과 활용에 집중되어 있다 보니, 데이터를 수집하거나 전처리하는 과정은 다 생략이 되어있다는 것이었다. 즉, 모든 데이터들은 딥러닝 모델에 바로 넣을 수 있도록 미리 다듬어서 준비가 되어있었다. 이를 고려하면, 추후 내가 구직을 하거나, 직접 프로젝트를 실행하려고 할때 데이터를 직접 다루지 못한다는 점이 큰 한계로 다가올 것이 틀림없었다. 

 이처럼, 단순한 배움이 아닌, 취업과 관련한 생각이 들었을 때, '내가 괜한 짓을 했나?' 라는 생각을 할 정도로, 꽤나 큰 혼란이 이따금씩 찾아 왔었다. 특히, '관련학위도 없이, 딥러닝과 머신러닝만 온라인으로 배워서 취직이 가능할까?', '내가 너무 순진했었나?' 등의 심란한 생각이 들기 시작했다. '내 주변에는 머신러닝은 커녕, IT계열과 관련한 사람들도 없는데, 누구한테 물어봐야 할까?' 라는 생각을 하다가, 인터넷으로 관련 정보를 찾아봤다. 그러다가 Medium 이라는 사이트를 알게 됐다. 미국에 기반을 두고 구독형 블로그 서비스를 제공하는 사이트인데, 내가 그토록 찾던 정보들이 가득했다.

 딥러닝 알고리즘과 관련하여 수업만으로는 이해가 되지 않던 부분들, 취업에 대한 이야기, 과대 포장된 딥러닝의 현실 등, 전세계의 현직자와 연구자, 학생들이 다같이 어우러져 만들어내는 컨텐츠들은, 정보에 목말랐던 나에게 오아시스와도 같았다. 그 중에서 가장 도움이 되었던 부분은 데이터사이언스의 의미와 역할을 확실히 알 수 있었던 점이다. 이를 통해, 내가 앞으로 어떠한 방향으로 미래를 준비해야 할지를 깨닫게 되었고, 내가 고민하던 커리어 문제에 대한 해답을 얻었다. 이때부터, 이전까지 막연하게만 생각하던 머신러닝 엔지니어가 아닌, 데이터사이언티스트가 되어야겠다고 결심을 하게 된다. 

 

 그럼 머신러닝 엔지니어와 데이터사이언티스트의 차이는 뭘까? 둘의 차이는 요리로 비유해 보면 이해하기가 쉽다. 머신러닝 엔지니어는 요리에 있어서 요리의 레시피 자체만을 연구하는 사람이다. 즉, 재료를 어디서 사는지, 재료를 어떻게 손질하는지는 신경을 쓰지 않고, 오로지 맛에만 몰두하여 레시피만 연구하는 연구원이라고 생각하면 된다. 따라서 매우 전문성이 높은 사람이어야만 하고, 전문성을 증명할 수 있는 고학력이 필수적이다. 반면에, 데이터사이언티스트는 어느정도 주어진 레시피를 바탕으로 재료 구입부터 요리까지 도맡아서 하는 사람이라고 생각하면 된다. 데이터사이언티스트는 레시피를 직접 연구하지는 않기 때문에, 머신러닝 엔지니어만큼의 전문성을 요구하지는 않지만, 이것 저것 두루두루 할줄 아는 능력이 필요하다. 좀 더 기술적인 입장에서 설명해보자면, 머신러닝 엔지니어는 머신러닝/딥러닝 아키텍쳐 개발에 집중하는 사람으로, 모델의 성능을 1% 라도 높히기 위해 연구하는 사람들이다. 당연히 매우 높은 수학적 지식을 요구하며, 업무의 특성상 좀 더 학술적인 성격을 띈다. 반면에 데이터사이언티스트는 머신러닝 엔지니어들에 의해 이미 검증된 모델을 이용하여 실용적인 문제 해결에 집중하는 경향이 크다. 따라서 좀더 산업적인 측면이 크다고 볼 수 있다.

 

 나의 경우 비전공자로서, 머신러닝과 딥러닝을 학술적으로 접근하기는 사실상 불가능하고 생각했다. 따라서, 대학에서 배운 경영학 지식을 살려서 데이터 분석에 활용하는 편이 훨씬 경쟁력이 있을 것이라고 판단했고, 앞으로의 공부와 프로젝트방향도 이에 맞게 설정할 수 있게 되었다.

 

블로그 이미지

찰리와마약공장

,

English version https://charliethewanderer.medium.com/the-reason-i-started-data-science-as-a-business-student-1-b49294c03886

 

The Reason I Started Data Science as a Business-Student — 1

It has been almost a year that I’ve been studying data science. It feels like I finally get the hang of it, thanks to several projects…

medium.com

 

 이제 데이터사이언스를 공부한지 약 1년이 다되어 간다. 그간 공부한 지식을 바탕으로 개인 프로젝트도 진행하면서, 이제야 어느정도 데이터사이언스에 대한 감을 잡아가고 있다. 사실 데이터사이언스라고 하면, 그 범위가 너무나도 광활한게 사실이다. 많은 데이터를 다루는 일들은 전부 데이터사이언스의 범주안에 속하기 때문에, 단순 엑셀작업부터, 데이터를 수집하기 위한 프로그래밍, 수집한 데이터를 저장하기 위한 데이터베이스 구축작업, 저장한 데이터를 불러와 전처리하는 작업, 전처리가 끝난 데이터를 바탕으로 예측모델을 만드는 머신러닝, 딥러닝 모델설계 작업까지 모두 데이터사이언스라고 불리는 것이다. 이해하기 쉽게 요리로 비유를 하자면, 데이터사이언스는 요리이며, 데이터 수집, 저장, 전처리, 머신러닝들은 각각 '마트가서 장보기', '장 봐온 재료 잘 정리해서 냉장고에 넣기', '요리 전 음식 손질하기', '손질된 재료를 가지고 요리하기'에 비유될 수 있겠다.

 

 관련 전공자가 아니라면, 여기까지만 들어도 무슨소리인가 싶을 것이다. 내가 1년 전에 그랬으니까. 사실 1년 전, 나는 데이터사이언스라는 용어도 알지 못했고, 이쪽에 발을 들여놓을 것이라고는 생각지도 못했다. 기껏해야 알고 있었던 용어들은 AI, 인공지능, 머신러닝 정도였는데, 당시 군 전역후 대학 3학년이었던 나는, 문과생으로서, 이런 첨단 분야에 도전하기에는 너무 늦었다고 생각했었다. 그리고, 모든 복학생들이 그렇듯이, 미래에 대한 불안감으로 무작정 내가 그나마 잘할 수 있는 걸 찾기 시작했다. 그리고 내가 찾은 답은 당연스럽게도, 영어였다. 그 이후, 내가 정말 영어를 남들 앞에서 '잘한다' 라고 자신있게 말할 수 있을 정도가 되기 위해, 제대로 공부하기 시작했다. 그리고 궁극적으로는 bilingual(2개의 언어를 모국어로 하는 사람)처럼 영어를 구사하는 것이 나의 목표가 되었다. 그런데 이것이 놀랍게도 나의 데이터사이언스를 향한 시발점이 된다.

 

 사실 영어공부에 관련해서는 따로 포스트를 정해 정리해야할 정도로 할 이야기가 많지만, 그래도 최대한 간략하게 써 보려고 한다. 나는 영어를 '공부한다' 기 보다는 '배운다' 라는 마인드로 접근했었던 것 같다. 토익, 토플과 같은 시험문제는 거의 풀어보지 않았고, 모국어를 처음 습득하는 아기의 마인드로 언어를 학습하려 노력했다. 아기들은 시험문제를 풀고, 문제집을 보면서 언어를 익히지 않는다. 최대한 많이 듣고, 읽고, 말하고, 쓰면서 익힌다. 어른이라고 방법이 크게 다르지 않다고 생각한다. 나는 내가 읽고싶은 영어책을 정하고 아마존에서 E북을 산 뒤 차근차근 천천히 모르는 표현과 단어를 정리하며 읽었다. 학교를 다니며 읽다보니, 첫 정독에 4개월이 걸렸다. 두번째 정독에는 1개월이 걸렸고 세번째 정독에는 일주일이 안 걸렸다. 책을 눈으로 다읽고 난 뒤에는, 해당 책의 오디오북을 사서 귀로 읽었다. 학교를 오고가는 지하철에서는 CNN10을 유튜브를 통해 계속 들으며 놓친부분을 정리하고, 하루에 개수를 정해놓고 뉴스기사또한 꾸준히 정독했다.

 

 나는 모르는 영어단어들을 발견하는즉시, 네이버 단어장에 등록하는 습관이 지금도 있다. 영어를 제대로 공부하기 시작한지 약 반 년이 지나자, 등록된 단어의 수가 5천에 다다랐는데, 그 이후 1년이 지난 지금까지도, 더 이상 등록단어의 숫자가 크게 오르지 않았다. 즉, 약 5천 개의 단어와 표현을 알고 있으면, 대부분의 영어는 그 안에서 돌고 돈다는 뜻이다. 그때 당시, 마침 토익 점수가 없어서, 시험을 신청하고 문제유형만 며칠간 파악하고 시험을 봤다. 얼마 뒤, 시험 결과가 나왔는데 점수는 960점이었다. 첫 시험 치고는 높은점수였지만, 마냥 좋지만은 않았다. '내가 아직 영어를 원어민처럼 구사하지 못하는데 점수가 무슨의미가 있을까?, 어떻게 해야 내가 영어를 잘한다고 스스로 판단할 수 있을까?' 이러한 생각을 했었던 것 같다. 그 이후, 이런 저런 생각을 하다가 불현듯 생각난 것이, '영어로 무언가를 배울 수 있다면 영어를 잘한다고 할 수 있지 않을까?' 라는 생각이었다. 그리고 들어갔던 사이트가 Coursera라는 미국의 인터넷 강의 플랫폼이었다. 당시 Coursera에서는 머신러닝강좌를 무료로 제공하고 있었는데(아마 지금도 무료일 것이다.), 이 강좌를 수강함으로써 내 자신을 테스트해보고 싶었다. 즉, 머신러닝과 같은 추상적인 개념을 영어로 배울 수 있다면, 이 것이야말로 영어를 할줄 안다는 방증이 된다고 생각했다. 또, 머신러닝과 인공지능이 4차산업혁명을 이끌 원동력 중 하나라고 미디어에서 허구헌날 떠들어 대니, '대체 이게 뭐길래 그렇게 난리들일까?' 라는 나의 호기심또한 해결할 수 있는 일석이조의 기회라고 느꼈다.

 

 그렇게 시작된 머신러닝 강의는 이제껏 살아오던 나의 인생관을 완전히 뒤바꿔 놓게 된다. 사실 머신러닝이라는 내용자체 보다는, 영어라는 것을 왜 할 줄 알아야하고, 영어를 할줄 알면 무엇을 할 수 있는지에 대한 지각을 얻음으로써 오는 충격이 컸다. 우선, 강의의 질과, 학습시스템이 국내 대학과 인터넷 강의 플랫폼(K-MOOC)과는 차원이 달랐다. 내가 다니던 학교도 인서울 4년제의 나쁘지 않은 학교였고, 많은 돈을 등록금으로 내고 다녔지만, 일부의 수업을 제외하면 대부분의 수업 내용이 수박 겉핥기 같다거나 원서를 그대로 복붙해서 읽어주는듯한 느낌을 지울수가 없는게 사실이었다. 기본적으로 교수 1명이 모든 학생을 신경쓸 수 없기에, 이는 어찌보면 당연한 것으로 간주했었는데, 사실 이건 당연한 것이 아니었다.

 

 Coursera에서 수업을 들으며 머신러닝의 개념을 서서히 이해하고, 숙제를 제출하여 실시간으로 피드백을 받고, 같은 수업을 수강하는 전세계의 학생들과 의견을 공유하는 일련의 과정에서, '이렇게 어려운 개념을 쉽게 설명하는 것이 가능하구나, 온라인 학습이 이렇게 효과적일 수도 있구나', 라는 생각이 들었고, 머신러닝에 대해서도 많이 배우게 됐다. 이로인해 '영어를 모른다는 이유 하나로 이런 배움의 혜택을 누리지 못한다면 얼마나 아쉬운 일일까?' 라는 생각이 들기 시작했고, 영어를 바라보는 시각이 바뀌었다. 이전까지는 영어를 단순히 목표로 생각했다면, 이 이후로는 영어를 하나의 도구로써 생각하고, 이것을 최대한 잘 활용하여 나의 시야와 지식을 넓혀야겠다는 생각을 가지게 됐다. 

 

 여러분이 돈을 만들어 낼 수 있는 도깨비방망이를 발견했다고 쳐보자. 여러분들은 도깨비방망이를 팔아서 돈을 벌 것인가? 아니면 도깨비방망이를 이용해서 직접 돈을 만들어 낼 것인가?

블로그 이미지

찰리와마약공장

,