사용자:CrMT/연습장/1

< 사용자:CrMT‎ | 연습장
CrMT (토론 | 기여)님의 2017년 1월 23일 (월) 16:06 판 (→‎TF로 구현)

쉽게 배우는 머신 러닝

Google DeepMind의 AlphaGo.

2016년 3월 9일부터 15일까지, 바둑계를 뒤흔든 사건이 하나 있었습니다. 바로 구글 딥마인드에서 제작한 '인공지능'(AI, artificial intelligence) 알파고(AlphaGo)가 이세돌 9단을 4:1의 큰 점수차로 이긴 것이죠. 사실 그 전부터 머신 러닝은 많은 곳에서 쓰였습니다. 단순한 머신 러닝인 '키보드에서 다음에 눌릴 키 예측' 같은 것도 있고, 이미지넷이라고 불리는 대회에서는 주어진 이미지에 해당하는 적당한 레이블을 고르는 것을 학습시킵니다. 그 외에도, 유튜브의 '자동 자막' 기능 역시 음성을 분석하여 자막을 달아주는 기계학습 시스템입니다. (한국어는 아직 미숙하지만 영어는 매우 정확한 캡션을 달아줍니다.) 강화학습이라는 방법으로 여러 게임을 클리어하도록 학습시키기도 하는데, 벽돌깨기 게임에서 벽 쪽의 블럭만을 뚫어 공이 위에서 놀게 하는 방법을 터득하는 등의 놀라운 결과를 보여주기도 하였습니다. 여러 IT 회사에서는 앞다투어 인공지능 개발 팀을 만들고, Apple의 Siri, MS의 Cortana, SK의 누구나 네이버의 AMICA 등의 인공지능 비서 플랫폼을 개발하고 있습니다.

이렇게 머신 러닝은 우리 곁에 즐비합니다. 이 AI의 과도기에 머신 러닝을 쉽게 할 수 있다면 얼마나 좋을까요! 이전에도 많은 AI 개발 소프트웨어들이 있었지만, 2015년 11월 9일, 구글신이 우리 모두에게 어떤 선물을 하사하십니다.

TensorFlow

TF.png

TensorFlow is for everyone. It's for students, researchers, hobbyists, hackers, engineers, developers, inventors and innovators and is being open sourced under the Apache 2.0 open source license.

텐서플로우는 모든 사람에게 열려 있습니다. 텐서플로우는 학생, 연구자, 취미로 머신 러닝을 배우는 사람, 해커, 엔지니어, 개발자, 발명가, 혁신가를 위하여 개발되었고, 아파치 2.0 오픈 소스 라이선스를 따르는 오픈소스입니다.
— TensorFlow™ by Google

구글신께서 주신 선물을 마다할 수야 없죠. 머신 러닝을 배워 봅시다!

준비 단계

모르면 힘든 것

  • 덧셈이 무엇인지, 곱셈이 무엇인지, 함수가 무엇인지
  • 타자를 치는 법
  • 기본적인 터미널의 사용법, 즉, '복사, 붙여넣기를 하는 법': 선택한 후 마우스 오른쪽 클릭으로 복사하며, 복사한 것이 있으면 선택한 것이 없는 상황에서 마우스 오른쪽 클릭으로 붙여 넣습니다.
  • 벡터내적이나 행렬의 곱셈 등 간단한 행렬대수 지식
  • 간단한 Python 문법
  • 인내심: 좋은 하드웨어가 없는 한 머신 러닝은 매우 느립니다. 또한 자원 점유율도 커서 컴퓨터/랩탑이 매우 느려집니다.

이 정도만 있으면 OK!

텐서플로우의 설치

텐서플로우는 원래 유닉스 계열의 OS인 리눅스, macOS만을 지원했으나, 현재는 Windows 64-bit, Anaconda가 있는 Python 3.5를 지원합니다. 요즘 대부분 RAM이 4GB를 넘어 보통 설치된 윈도는 64-bit이므로, Python 3.5와 Anaconda를 설치하면 윈도에도 설치가 가능합니다. 이하, 자료가 많은 Python API를 설치합니다. 또한 유닉스 계열과 윈도 모두 Anaconda 기반에서 설명하며, 유닉스는 우분투 16.04 LTS 기준으로 설명합니다.

공식 홈페이지를 참고했습니다.

유닉스 계열

  1. Install Python. Python 2.7이나 3.5(혹은 3.4)를 설치합니다. 추후에 바뀔 수 있으나, 아직 3.6은 추천하지 않습니다. Python 공식 홈페이지에 가서 인스톨러 소스를 받거나 command에서 apt-get 등의 명령어로 설치합니다. 우분투(14.04 이상)의 경우 Python은 자동으로 설치되어 있습니다.
  2. Install Anaconda. Anaconda 공식 홈페이지에서 Python 버전에 맞는 Anaconda를 설치합니다. 설치 과정은 Download for *** 탭에 나와 있습니다. 우분투의 경우 다운로드 폴더로 이동한 후에(cd ~/Downloads 또는 cd ~/다운로드)
    bash Anaconda2-4.2.0-Linux-x86_64.sh # if Python 2.x
    bash Anaconda3-4.2.0-Linux-x86_64.sh # if Python 3.x
    
    중 자신의 Python 버전에 맞는 줄을 복사하여 터미널에 넣으세요.
  3. Create conda environment. 'tensorflow'라는 conda 환경을 만듭니다. 다음 셋 중 자신의 Python 버전에 맞는 것을 넣어주세요.
    conda create -n tensorflow python=2.7 # Python 2.7
    conda create -n tensorflow python=3.4 # Python 3.4
    conda create -n tensorflow python=3.5 # Python 3.5
    
    그리고
    source activate tensorflow
    
    로 conda environment를 활성화합니다. 그러면 '$'가 있는 줄 앞에 '(tensorflow)'가 생겼을 것입니다.
  4. Install TensorFlow.
    conda install -c conda-forge tensorflow
    
    로 TF를 설치합니다. 단, 이 명령어는 "CPU 버전"의 TF만 설치 가능합니다. GPU 버전의 설치를 위해서는 여기를 참고해 주세요.
  5. Test! python 명령어를 쳐서, Python을 켭니다. 나오는 창에 Python 옆에 'Anaconda'가 써 있는 것을 확인해 주세요. '>>>' 표시가 나오면, 한 줄씩 다음 코드를 칩니다.
    import tensorflow as tf
    hello = tf.constant('Hello, TensorFlow!')
    sess = tf.Session()
    print(sess.run(hello))
    
    이때 Hello, TensorFlow!라는 메시지가 나오면 정상적으로 설치된 겁니다! 축하합니다!

윈도

참고 자료

머신 러닝?

A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.
— Tom M. Mitchell

머신 러닝은 꾸준한 학습(경험)을 통해 점점 성능을 나아지게 하는 프로그램입니다. 머신 러닝은 크게 세 가지로 나눌 수 있는데, 다음과 같습니다.

Supervised Learning

사람이 어떤 함수의 데이터와 결과값을 몇 개 입력하면, 컴퓨터가 그것을 보고 사람이 의도한 함수에 매우 가까운 가설 함수(hypothesis)를 알아맞히는 방법입니다. 이때 식을 줄 수도 있고(회귀 등), 아니면 다른 데이터를 넣었을 때의 결과값을 줄 수도 있습니다(분류 등). 예를 들어, (선형 회귀)

(x, y) = (3, 7), (6, 8), (9, 9)

라는 세 개의 데이터(x)와 결과값(y)을 주었고, 가설 함수가

[math]\displaystyle{ H(x) = wx + b, \qquad }[/math]Hypothesis, weight, bias

의 모양, 즉 일차식으로 주어졌다고 합시다. 그러면 컴퓨터는 w와 b를 바꾸어 가면서, 가설 함수와 데이터 사이의 차이를 최소화합니다. 최소화 과정을 거치면 컴퓨터는 다음의 결과를 낼 것입니다(약간의 오차는 있을 수 있습니다.):

[math]\displaystyle{ H(x) = 0.33333x + 6.00000. }[/math]

꼭 수로만 데이터를 제공할 필요는 없습니다. MNIST는 손으로 쓴 숫자 이미지를 주고 그 숫자가 무엇인지 맞추게 하는데, 이 역시 (이미지, 숫자)라는 데이터로 해석하면 supervised learning입니다. 음성인식 역시 처음에 (소리(음운), 글자)라는 데이터를 주고 학습시키므로 supervised learning입니다.

Unsupervised Learning

단지 데이터(x)만을 줍니다. 이때 컴퓨터는 주어진 데이터를 비슷한 것끼리 모으는 군집화를 할 수도 있고, 더 나아가 그 데이터의 분포를 맞히는 분포 추정을 할 수도 있습니다.

Reinforcement Learning

위 둘과 다르게, 강화 학습은 현 상태를 보고 어떤 행동을 해야 좋을지를 판단합니다. 현 상태(state)에서 컴퓨터가 어떤 행동(Action)을 취하면 그에 따른 점수(Result, 보상)가 매겨집니다. 컴퓨터는 이 보상을 최대화하려고 다음에 어떤 행동을 해야 할 것인지 판단하는 정책(Policy)를 세웁니다. 예를 들어, 자율 운전은 강화 학습의 예입니다. 사람이 운전하듯이, 자율주행 차량에 주어지는 데이터(state)는 카메라 영상이며, 이것으로 얼마나 앞에 무엇이 있는지를 판단합니다. 간단한 설명을 위해 왼쪽(-30도), 앞쪽(0도), 오른쪽(30도)으로의 세 거리를 측정할 수 있다고 합시다. 그리고 그 결과값이

MLRun.png.png
(L, F, R) = (200, 100, 50)

이었다고 해 봅시다. 예를 들어 다음 그림과 같은 상황이 됩니다.

컴퓨터는 처음에는 아무 행동이나 합니다. 아직 정책이 없으니까요. 처음에는 분명 충돌하게 되는데, 이때 보상을 아래 둘 중에 한 가지 방법으로 줍니다.

  • 충돌했을 때 (-)의 보상을 준다.
  • 충돌하지 않으면 (+)의 보상을 준다.

그렇게 몇 번(조금 많이...) 충돌하다 보면, 저런 상황에서는 왼쪽으로 회전해야 한다는 정책이 생기게 됩니다. 하지만, 이게 말처럼 쉬운 건 아닙니다. 제일 중요한 것은 보상을 어떻게 주느냐인데, 다음과 같은 문제가 발생합니다.

  • 보상은 곧바로 주어지지 않을 때가 있습니다. 예를 들어, 바둑의 경우, 승리를 보상으로 잡으면, 매우 늦게 이루어집니다. 또한, 자율주행 차량이 넓은 길을 갈 때, 길이 넓어서 충돌하지 않았지만 비틀비틀 가는 것보다는 직선으로 가는 게 좋은 방법인데 충돌하지 않는 이상 보상이 같아 둘 중에 어떤 정책을 골라야 하는지 모른다거나 할 수 있습니다.

이런 문제 때문에 강화 학습은 꽤 어려운 문제가 됩니다.

어쨌든 시작해 보자!: 선형 회귀

가장 간단한 머신 러닝 예제는 선형 회귀입니다. 먼저, 다음과 같이 (x, y)가 주어진다고 합시다.

[math]\displaystyle{ \mathbf x_i = (x_{1i},~ x_{2i},~\cdots,~x_{ni}), \quad x_{ji},~y_i \in \mathbb R }[/math]

[math]\displaystyle{ \mathbf x_i }[/math]라는 [math]\displaystyle{ \mathbb R^n }[/math]의 n차원 벡터를 주었을 때 [math]\displaystyle{ y_i }[/math]라는 실숫값을 반환하는 함수를 예측해 보라고 할 것입니다. 이때, 선형 회귀는 가설 함수를 다음과 같이 세웁니다:

[math]\displaystyle{ H(\mathbf x) = \mathbf w \cdot \mathbf x + b = w_1 x_1 + w_2 x_2 + \cdots w_n x_n + b. }[/math]

이때, 어떤 한 데이터와의 차이cost function(또는 loss function)으로 측정하게 되는데, 간단히 최소자승법(LSA, least square approximation)과 같은 함수로 둡니다. 즉,

[math]\displaystyle{ \mathrm{cost}_i = \sum_{j=1}^n (H(x_{ji}) - y_i)^2 }[/math]

으로 하고, 모든 데이터에 대한 평균으로 cost function을 정의합니다.

[math]\displaystyle{ \mathrm{cost} = \frac{1}{m} \sum_{i=1}^m \mathrm{cost}_i = \frac 1 m \sum_{i=1}^m \sum_{j=1}^n (H(x_{ji}) - y_i)^2 }[/math]

이때 [math]\displaystyle{ m }[/math]은 데이터의 개수입니다. 우리는 차이가 가장 적게 나길 원하니, 이 cost function을 최소화해야겠네요. 그런데 최소화는 어떻게 해야 할까요? TF에서는 여러 최소화 알고리듬을 제공하는데, 가장 이해하기 쉬운 기초적인 것은 아래에 소개할 경사 하강법(gradient descent algorithm)입니다.

Gradient descent

미분 가능한 함수 [math]\displaystyle{ f }[/math]의 극솟값을 찾는 알고리듬입니다. 식은 다음과 같습니다. 파일:GradDesc.png

[math]\displaystyle{ \mathbf x_{i+1} = \mathbf x_i- \alpha_i \nabla f (\mathbf x_i ). }[/math]

의미를 설명하자면, 가장 경사가 급한 방향으로 이동한다는 뜻입니다. 언덕에서 미끄러지듯이, 가장 값이 작아지는 쪽으로 이동하게 됩니다.

TF에서는 이를 함수로 지원하고 있는데, 그 코드는

optimizer = tf.train.GradientDescentOptimizer(learning_rate)

입니다. 이때 learning_rate는 위에서 [math]\displaystyle{ \alpha_i }[/math]를 말하며, 얼마나 큰 간격으로 이동할 것인지를 결정합니다. 너무 크면 극솟값에 도달하지 못할 수도 있지만, 또 너무 작으면 시간이 매우 오래 걸립니다. 따라서 적절한 learning rate를 결정하는 것이 필요합니다.

TF로 구현

말보다는 코드죠. (?) 일단 써 봅시다. linreg.py를 만들어 다음과 같이 씁니다. (코드 설명은 주석에 되어 있습니다.)

# -*- coding: utf-8 -*-
import tensorflow as tf # 텐서플로우를 불러옵니다.

# 데이터를 입력해 줍니다.
x_data = [1, 2, 3]
y_data = [1, 3, 5]

# 텐서플로우에서 모든 코드는 그 줄에서 실행되는 것이 아니라, 세션에서 run()을 해야 비로소 실행됩니다.
# 일단 아무렇게나 weight와 bias를 잡습니다. random_uniform은 같은 확률로 주어진 구간에서 뽑는다는 것이며, [1]은 1차원 벡터, 즉 실수를 뜻합니다.
w = tf.Variable(tf.random_uniform([1], -3.0, 3.0))
b = tf.Variable(tf.random_uniform([1], -3.0, 3.0))

# Placeholder는 변수가 들어갈 수 있는 자리를 만들어 놓습니다. 여기에 데이터를 대입하는 것을 feed라고 합니다.
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)

# 가설 함수와 cost를 지정합니다.
hypothesis = w * x + b
cost = tf.reduce_mean(tf.square(hypothesis - y_data)) # reduce_mean은 벡터(텐서)의 성분을 평균(mean)을 낸 후 괄호를 없애(reduce) 반환합니다.

# 최소화 알고리듬
learning_rate = tf.Variable(0.1) # learning_rate를 지정합니다.
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train = optimizer.minimize(cost) # cost를 최소화하는 방향으로 학습을 시키는 코드입니다.

sess = tf.Session()
sess.run(tf.global_variables_initializer()) # 텐서플로우에서는 가장 먼저 모든 변수를 초기화해야 합니다.

for step in xrange(2001):
	sess.run(train, feed_dict={x:x_data, y:y_data}) # train이라는 것을 실행시키는데, 데이터 자리에 x_data와 y_data를 넣습니다(feed).
	if step % 100 == 0:
		print step, sess.run(cost, feed_dict={x:x_data, y:y_data}), sess.run(w), sess.run(b) # 그 스텝에서의 weight와 bias를 출력하고, 그때의 cost를 출력합니다.

print sess.run(hypothesis, feed_dict={x:[6, 10]}) # x에 다른 값들을 넣어 값을 출력하게 합니다.