본문 바로가기

혼자서꿍시렁

딥러닝 실패사례 : 말 해 줄거라 생각했다


머신러닝을 공부하다 경험한 개인적인 딥러닝 실패사례입니다. 나름 이 분야의 전문가가 일찌감치 실패할 것이다!라고 충고해줬으나 이 같은 사례도 공유하면 도움이 되는 분들이 계시지 않을까 하는 마음에 소신껏 진행하고 실패해서는 잘 정리해 보고자 합니다. ㅎ


예전에 '내가 재밌게 본 영화를 남들도 재미있어할까?' 라는 가정으로 그간 기록해 뒀던 내 영화 점수들을 가지고 대중의 영화 점수 기록과 상관분석을 진행해 봤었습니다. 그때는 안타깝게도 영화 점수를 기록하면서 함께 기록했던 영화의 제목들이 영화진흥위원회 사이트에 기록된 제목들과 일치하지 않음으로 인해 까다로운 전처리 과정과 시도해 볼 수 있는 실험에 한계가 있었습니다. 하지만, 생각보다 결과가 좋게 나온 터에 이번에는 제 점수 목록을 가지고 또 다른 시도를 해봤습니다.


"내가 매긴 영화 점수와 네이버 영화의 네티즌 점수, 기자/평론가 점수를 수집해서 머신러닝을 진행한 후 네티즌 점수와 기자/평론가 점수를 제시하면 나의 점수를 예측해 줄 것이다."


가설은 위와 같이 설정해 봤습니다.

이번에는 큰 마음을 먹고 모든 데이터를 최대한 활용해 볼 수 있도록 데이터의 전처리(수집과 교정) 과정에 더 힘을 쏟았습니다.


- 처음에 놓였던 문제들


1. 내가 기록한 영화의 제목들과 네이버 영화의 영화 제목들은 대부분 다르다

2. 네이버가 제공하는 영화 검색 Open API는 기자/평론가 점수를 제공해 주지 않는다



전처리


2번 문제의 해결을 위해 "BeautifulSoup" 파이선 라이브러리를 활용하여 네이버 영화 정보를 크롤링하기로 마음먹었습니다. 네, Open API는 제공하지 않으나 웹페이지 상에서는 기자/평론가 점수를 제공하고 있기 때문입니다. 나머지 1번 항목은 감당하기로 마음을 먹었던 터라 이 문제를 해결하는 데에 약 이틀 이상의 '노가다' 시간을 보냈습니다. 어떻게 보냈을까요?




위의 이미지처럼,


"미녀는 괴로워 vs 미녀는 괴로워 - 칸나 씨 대성공입니다! - 2008, 야마다 유|야마자키 시즈요|, http://movie.naver.com...


와 같이 내가 입력해 놓은 제목과 네이버에서 가지고 온 정보를 표시하면서 두 개가 같은 것인지를 묻는 파이선 코드를 작성했습니다.


1. 내 영화 DB에서 영화 제목 하나를 빼낸다

2. 네이버 Open API를 이용해서 해당 영화 제목으로 검색한 결과들을 1개씩 보여준다

3. "y"를 입력하면 네이버 영화 정보의 영화 ID를 이용해서 네이버 영화 웹사이트를 크롤링하여 해당 영화의 기자/평론가 점수를 뽑는다. 그리고 영화 제목과 영화 ID, 기타 정보들을 내 영화 DB의 해당 영화 정보에 채운다

4. "y" 이외의 것을 입력하면 검색한 결과의 다음 항목을 보여준다


네. 그간 제가 기록했던 영화 점수의 수가 1871개였습니다. 1871개를 네이버 영화 정보 1871개와 매칭 시키기 위해 약 이틀 이상의 시간을 위와 같은 행동에 쏟은 것이지요. 크흡 T^T


마침내 내 영화 정보와 네이버 정보를 매칭 시키는 데에 성공했으나 아래와 같은 이슈들이 생겨났습니다.


1. 네이버에 등록되어 있지 않은 영화가 있었다

2. 중복해서 기록했던 영화가 있었다 (가끔 다시 봤던 영화들을 또 기록한 경우)

3. 네티즌 점수가 없거나 기자/평론가 점수가 없거나 둘 다 없는 영화가 있었다


1번 이슈는 그냥 IMDB의 정보로 채우고 무시하기로 했습니다. 그리고 2번은 네이버 ID를 이용해 중복항목들을 제거해 버렸고요. 1871개가 1815로 줄더군요ㅠㅠ 마찬가지로 3번 항목도 다룰 데이터에서 제하기로 했습니다. 그랬더니... 그랬더니... 1815개가 825개로 줄어 버렸습니다. 엉엉...ㅠㅠ


* 그렇게 정리하게 된 점수 테이블입니다. - gunman_scores.txt

* 형식은 아래와 같습니다.

  영화제목::글쓴이점수(1~5점)::네티즌점수(0~10)::기자/평론가점수(0~10)::\n



학습 결과



아래는 딥러닝 코드를 이용해서 위의 데이터로 학습한 결과입니다. 네, 참담합니다.

정확도가 50% 이하에서 왔다 갔다 하네요.


아래 코드는 "밑바닥부터 시작하는 딥러닝" 서적에 실린 코드를 기반으로 작성하였습니다.

# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
from two_layer_net import TwoLayerNet
import re


def deepLearn(gun_scores, naver_scores, train_size, batch_size):
    x_train = naver_scores[:train_size]
    t_train = gun_scores[:train_size]

    x_test = naver_scores[train_size:]
    t_test = gun_scores[train_size:]

    network = TwoLayerNet(input_size=2, hidden_size=10, output_size=5)

    # 하이퍼파라미터
    iters_num = 10000  # 반복 횟수를 적절히 설정한다.
    train_size = x_train.shape[0]
    learning_rate = 0.1

    train_loss_list = []
    train_acc_list = []
    test_acc_list = []

    # 1에폭당 반복 수
    iter_per_epoch = max(train_size / batch_size, 1)

    for i in range(iters_num):
        # 미니배치 획득
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        t_batch = t_train[batch_mask]

        # 기울기 계산
        #grad = network.numerical_gradient(x_batch, t_batch)
        grad = network.gradient(x_batch, t_batch)

        # 매개변수 갱신
        for key in ('W1', 'b1', 'W2', 'b2'):
            network.params[key] -= learning_rate * grad[key]

        # 학습 경과 기록
        loss = network.loss(x_batch, t_batch)
        train_loss_list.append(loss)

        # 1에폭당 정확도 계산
        if i % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            test_acc = network.accuracy(x_test, t_test)
            train_acc_list.append(train_acc)
            test_acc_list.append(test_acc)
            print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

    x = np.arange(len(train_acc_list))
    plt.plot(x, train_acc_list, label='train acc')
    plt.plot(x, test_acc_list, label='test acc', linestyle='--')
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.ylim(0, 1.0)
    plt.legend(loc='lower right')
    plt.show()

#
def readScore():
    gun_score = []
    naver_score = []

    f = open("gunman_scores.txt", 'r')
    while True:
        line = f.readline()
        if not line: break
        scores = re.split("::", line)

        myscore = int(scores[1])
        netiscore = float(scores[2])
        spscore = float(scores[3])

        naver_score.append([netiscore, spscore])

        scoreboard = [0, 0, 0, 0, 0]
        scoreboard[myscore - 1] = 1
        gun_score.append(scoreboard)
    f.close()

    naver_score = np.array(naver_score)
    gun_score = np.array(gun_score)

    return naver_score, gun_score

naver_score, gun_score = readScore()
train_size = 600
batch_size = 100
deepLearn(gun_score, naver_score, train_size, batch_size)


그리고 아래는 텐서 플로우를 이용한 학습 결과입니다.




네, 역시 참담합니다. ㅠㅠ

아래 코드는 "텐서 플로 첫걸음" 서적에 실린 코드를 참고하여 작성했습니다.

# -*- coding: utf-8 -*-

import tensorflow as tf
import numpy as np
import re
import matplotlib.pyplot as plt

def deepLearn(gun_scores, naver_scores, train_size, batch_size):
    x_train = naver_scores[:train_size]
    t_train = gun_scores[:train_size]

    x_test = naver_scores[train_size:]
    t_test = gun_scores[train_size:]

    W1 = tf.Variable(tf.random_uniform([2, 10], -1.0, 1.0))
    #W2 = tf.Variable(tf.random_uniform([10, 10], -1.0, 1.0))
    #W3 = tf.Variable(tf.random_uniform([10, 10], -1.0, 1.0))
    #W4 = tf.Variable(tf.random_uniform([10, 10], -1.0, 1.0))
    #W5 = tf.Variable(tf.random_uniform([10, 10], -1.0, 1.0))
    W6 = tf.Variable(tf.random_uniform([10, 5], -1.0, 1.0))

    b1 = tf.Variable(tf.zeros([10]))
    #b2 = tf.Variable(tf.zeros([10]))
    #b3 = tf.Variable(tf.zeros([10]))
    #b4 = tf.Variable(tf.zeros([10]))
    #b5 = tf.Variable(tf.zeros([4]))
    b6 = tf.Variable(tf.zeros([5]))

    x = tf.placeholder("float", [None, 2])

    L2 = tf.sigmoid(tf.matmul(x, W1) + b1)
    #L3 = tf.sigmoid(tf.matmul(L2, W2) + b2)
    #L4 = tf.sigmoid(tf.matmul(L3, W3) + b3)
    #L5 = tf.sigmoid(tf.matmul(L4, W4) + b4)
    #L6 = tf.sigmoid(tf.matmul(L5, W5) + b5)
    hypothesis = tf.nn.softmax(tf.matmul(L2, W6) + b6)

    y_ = tf.placeholder("float", [None, 5])
    cross_entropy = -tf.reduce_sum(y_ * tf.log(hypothesis))
    train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

    sess = tf.Session()
    sess.run(tf.global_variables_initializer())

    train_acc_list = []
    test_acc_list = []

    for i in range(1000):
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        y_batch = t_train[batch_mask]
        sess.run(train_step, feed_dict={x: x_batch, y_: y_batch})
        correct_prediction = tf.equal(tf.argmax(hypothesis,1), tf.argmax(y_,1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
        #if i % 100 == 0:
        acc_train = sess.run(accuracy, feed_dict={x: x_batch, y_: y_batch})
        acc_test = sess.run(accuracy, feed_dict={x: x_test, y_: t_test})
        train_acc_list.append(acc_train)
        test_acc_list.append(acc_test)


    x = np.arange(len(train_acc_list))
    plt.plot(x, train_acc_list, label='train acc')
    plt.plot(x, test_acc_list, label='test acc', linestyle='--')
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.ylim(0, 1.0)
    plt.legend(loc='lower right')
    plt.show()
#
def readScore():
    gun_score = []
    naver_score = []

    f = open("gunman_scores.txt", 'r')
    while True:
        line = f.readline()
        if not line: break
        scores = re.split("::", line)

        myscore = int(scores[1])
        netiscore = float(scores[2])
        spscore = float(scores[3])

        naver_score.append([netiscore, spscore])

        scoreboard = [0, 0, 0, 0, 0]
        scoreboard[myscore-1] = 1
        gun_score.append(scoreboard)
    f.close()

    naver_score = np.array(naver_score)
    gun_score = np.array(gun_score)

    return naver_score, gun_score

train_size = 600
batch_size = 100
naver_score, gun_score = readScore()

deepLearn(gun_score, naver_score, train_size, batch_size)


답답했습니다. 이에, 네티즌 점수와 내 점수, 기자/평론가 점수와 내 점수의 공분산과 상관계수 값들을 뽑아 봤습니다. 어차피 데이터의 양이 적으니(ㅠㅠ) 제 PC에서 이것저것을 막 뽑아 봤습니다.





"COV"는 공분산 값, "CORR"은 상관계수 값입니다. 상관계수 값만 본다면 저와 네이버 점수들보다는 네이버 네티즌과 기자/평론가의 점수들이 서로 더 강한 상관관계를 가지는 것 같습니다.



마무리


1. 실패요인을 계속해서 공부하고 있습니다. 데이터 수가 너무 적어서 학습이 되지 않았다기보다 지극히 주관적인 점수들을 가지고 상관관계를 찾으려 한 것 같습니다. ㅜㅜ


2. 영화의 장르, 출연진, 감독 정보등을 학습데이터에 추가해서 진행해 볼까 생각중입니다. 이러면 성공하겠죠?


참고 자료

진행을 위해 작성했던 코드와 참고 자료 목록입니다.


gunman의 영화 점수, 네티즌 영화 점수, 기자/평론가 점수 목록

네이버 Open API를 로컬 PC에서 호출하기

네이버 영화 정보와 전문가 평점 얻기

김성훈 교수님의 모두를 위한 머신러닝/딥러닝 강의

"밑바닥부터 시작하는 딥러닝" 서적의 코드들

"텐서 플로 첫걸음" 서적에 실린 코드들

"제대로 알고 쓰는 R 통계 분석"



* 이 글은 이 집구석 주인장의 브런치에도 동시에 게재됩니다.