사용자:CrMT/연습장/1

쉽게 배우는 머신 러닝

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일, 구글신이 우리 모두에게 어떤 선물을 하사하십니다.

1 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

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

2 준비 단계[편집]

2.1 모르면 힘든 것[편집]

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

이 정도만 있으면 OK! 약팔이

2.2 텐서플로우의 설치[편집]

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

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

2.2.1 유닉스 계열[편집]

  1. Install Python. Python 2.7이나 3.5(혹은 3.4)를 설치합니다. 추후에 바뀔 수 있으나, 아직 3.6은 추천하지 않습니다. (Ubuntu/Linux 64-bit에서 TF 1.0.0RC0를 설치할 경우에만 3.6이 지원됩니다.) Python 공식 홈페이지에 가서 인스톨러 소스를 받거나 command에서 apt-get 등의 명령어로 설치합니다. 우분투(14.04 이상)의 경우 Python은 자동으로 설치되어 있습니다.
  2. Install Anaconda. Anaconda 공식 홈페이지에서 Python 버전에 맞는 Anaconda를 설치합니다. 설치 과정은 Download for *** 탭에 나와 있습니다. 구 버전(Python 3.3~3.5는 Anaconda 4.2.0)은 여기에서 받아주세요. 우분투의 경우 다운로드 폴더로 이동한 후에(cd ~/Downloads 또는 cd ~/다운로드)
    bash Anaconda2-4.3.0-Linux-x86_64.sh # if Python 2.x
    bash Anaconda3-4.2.0-Linux-x86_64.sh # if Python 3.x and not 3.6
    bash Anaconda3-4.3.0-Linux-x86_64.sh # if Python 3.6
    
    중 자신의 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
    conda create -n tensorflow python=3.6 # Python 3.6
    
    그리고
    source activate tensorflow
    
    로 conda environment를 활성화합니다. 그러면 '$'가 있는 줄 앞에 '(tensorflow)'가 생겼을 것입니다.
  4. Ubuntu/Linux 64-bit GPU의 경우 CUDA toolkit 8.0CuDNN v5.1가 필요합니다. CUDA toolkit 8.0은 /usr/local/cuda와 같은 곳에 설치하고, cuDNN을 toolkit 설치 위치에 갖다 놓은 후
    tar xvzf cudnn-8.0-linux-x64-v5.1-ga.tgz
    sudo cp -P cuda/include/cudnn.h /usr/local/cuda/include
    sudo cp -P cuda/lib64/libcudnn* /usr/local/cuda/lib64
    sudo chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn*
    
    로 설치합니다. sudo apt-get install libcupti-dev로 libcupti-dev도 설치해 줍니다.
  5. Install TensorFlow.
Ubuntu/Linux 64-bit Mac OS X
Python 2.7 3.4 3.5 3.6 Python 2.7 3.4 또는 3.5
0.12.1 CPU [1] [2] [3] 미지원 0.12.1 CPU [4] [5]
0.12.1 GPU [6] [7] [8] 미지원 0.12.1 GPU [9] [10]
1.0.0RC0 CPU [11] [12] [13] [14] 1.0.0RC0 CPU [15] [16]
1.0.0RC0 GPU [17] [18] [19] [20] 1.0.0RC0 GPU [21] [22]
알맞은 버전의 주석에 있는 코드를 복사하여 경로를 지정하고,
pip install --ignore-installed --upgrade $TF_BINARY_URL # Python 2
pip3 install --ignore-installed --upgrade $TF_BINARY_URL # Python 3
로 설치합니다.
  1. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.1-cp27-none-linux_x86_64.whl
  2. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.1-cp34-cp34m-linux_x86_64.whl
  3. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.1-cp35-cp35m-linux_x86_64.whl
  4. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.12.1-py2-none-any.whl
  5. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.12.1-py3-none-any.whl
  6. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.1-cp27-none-linux_x86_64.whl
  7. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.1-cp34-cp34m-linux_x86_64.whl
  8. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-0.12.1-cp35-cp35m-linux_x86_64.whl
  9. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-1.0.0rc0-py2-none-any.whl
  10. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow_gpu-0.12.1-py3-none-any.whl
  11. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.0.0rc0-cp27-none-linux_x86_64.whl
  12. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.0.0rc0-cp34-cp34m-linux_x86_64.whl
  13. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.0.0rc0-cp35-cp35m-linux_x86_64.whl
  14. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.0.0rc0-cp36-cp36m-linux_x86_64.whl
  15. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-1.0.0rc0-py2-none-any.whl
  16. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-1.0.0rc0-py3-none-any.whl
  17. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0rc0-cp27-none-linux_x86_64.whl
  18. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0rc0-cp34-cp34m-linux_x86_64.whl
  19. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0rc0-cp35-cp35m-linux_x86_64.whl
  20. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0rc0-cp36-cp36m-linux_x86_64.whl
  21. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow_gpu-1.0.0rc0-py2-none-any.whl
  22. export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow_gpu-1.0.0rc0-py3-none-any.whl

2.2.2 윈도[편집]

  1. Windows 운영체제가 64-bit인지 확인해 주세요. 컴퓨터 (우클릭) - 속성으로 시스템을 들어가면 볼 수 있습니다.
  2. Python 3.5를 설치합니다. http://python.org
  3. Anaconda3 4.2.0(또는 최신 버전)을 설치합니다. https://www.continuum.io/downloads#windows 에서 for Python 3.5, 64-bit installer를 받으시면 됩니다.
  4. 컴퓨터 (우클릭) - 속성 - 고급 시스템 설정 - (하단) 환경 변수를 눌러 시스템 변수 - Path에 MSVCP140.DLL가 있는지 확인해 주세요. 아니라면 https://www.microsoft.com/en-us/download/details.aspx?id=53587 (혹은 여기)에서 vc_redist.x64.exe를 설치하시면 됩니다. 그렇지 않으면 텐서플로우가 그 dll을 사용하지 못해 No module named "_pywrap_tensorflow"라는 에러가 발생한다고 합니다.
  5. (GPU의 경우) CUDA toolkit 8.0cuDNN 5.1를 설치하셔야 합니다.
  6. 이제 Anaconda prompt를 열어, 아래의 원하는 버전을 택하여 붙여 넣으시면 됩니다.
    TF 0.12.1 CPU: 
    pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/cpu/tensorflow-0.12.1-cp35-cp35m-win_amd64.whl
    TF 0.12.1 GPU: 
    pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-0.12.1-cp35-cp35m-win_amd64.whl
    TF 1.0.0RC1 CPU:
    pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/cpu/tensorflow-1.0.0rc1-cp35-cp35m-win_amd64.whl
    TF 1.0.0RC1 GPU:
    pip install --ignore-installed --upgrade https://storage.googleapis.com/tensorflow/windows/gpu/tensorflow_gpu-1.0.0rc1-cp35-cp35m-win_amd64.whl
    

2.2.3 테스트[편집]

python 명령어를 쳐서 Python을 열거나(UN*X) Anaconda Prompt(Windows), 또는 Spyder를 엽니다. 나오는 창에 Python 옆에 'Anaconda'가 써 있는 것을 확인해 주세요. '>>>' 표시가 나오면, 한 줄씩 다음 코드를 칩니다.

import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

이때 Hello, TensorFlow!라는 메시지가 나오면 정상적으로 설치된 겁니다! 축하합니다!

Anaconda는 IDE에서 사용이 불가하며, 대신 Spyder(Anaconda 설치 시에 같이 설치되는 IDE)를 사용해 주세요.

2.2.4 에러[편집]

현재 윈도상의 TF 1.0.0 RC1(RC0)에서 다음과 같은 에러가 나타날 수 있습니다.

E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "BestSplits" device_type: "CPU"') for unknown op: BestSplits
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "CountExtremelyRandomStats" device_type: "CPU"') for unknown op: CountExtrem
elyRandomStats
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "FinishedNodes" device_type: "CPU"') for unknown op: FinishedNodes
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "GrowTree" device_type: "CPU"') for unknown op: GrowTree
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "SampleInputs" device_type: "CPU"') for unknown op: SampleInputs
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "ScatterAddNdim" device_type: "CPU"') for unknown op: ScatterAddNdim
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TopNInsert" device_type: "CPU"') for unknown op: TopNInsert
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TopNRemove" device_type: "CPU"') for unknown op: TopNRemove
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "TreePredictions" device_type: "CPU"') for unknown op: TreePredictions
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "UpdateFertileSlots" device_type: "CPU"') for unknown op: UpdateFertileSlots
E c:\tf_jenkins\home\workspace\release-win\device\cpu\os\windows\tensorflow\core\framework\op_kernel.cc:943] OpKernel ('op: "ReinterpretStringToFloat" device_type: "CPU"') for unknown op: ReinterpretStringToFloat

그냥 무시하시면 됩니다. 여기를 참고해 주세요.

2.3 참고 자료[편집]

3 머신 러닝?[편집]

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

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

3.1 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입니다.

3.2 Unsupervised Learning[편집]

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

3.3 Reinforcement Learning[편집]

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

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

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

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

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

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

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

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

4 어쨌든 시작해 보자!: 선형 회귀[편집]

4.1 일변수 선형 회귀[편집]

가장 간단한 머신 러닝 예제는 선형 회귀입니다. 먼저, 간단하게 실숫값을 가지는 (x, y)가 주어진다고 합시다.

[math]\displaystyle{ (x_{i},~y_i) \in \mathbb R^2 }[/math]

[math]\displaystyle{ x_i }[/math]라는 실수를 주었을 때 [math]\displaystyle{ y_i }[/math]라는 실숫값을 반환하는 데이터를 주고, 의도된 함수(target concept)를 예측해 보라고 할 것입니다. 이때, 선형 회귀(linear regression)는 가설 함수를 다음과 같이 세웁니다:

[math]\displaystyle{ H(x) = wx + b }[/math]

이때, 어떤 한 데이터와의 차이cost function(또는 loss function)으로 측정하게 되는데, 간단히 최소자승법(OLS, ordinary least squares)을 이용하겠습니다. 즉,

[math]\displaystyle{ \mathrm{cost}(x_i) = (H(x_i) - y_i)^2 }[/math]

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

[math]\displaystyle{ \mathrm{cost} = \frac{1}{m} \sum_{i=1}^m \mathrm{cost}(x_i) = \frac 1 m \sum_{i=1}^m (H(x_i) - y_i)^2 }[/math]

이때 [math]\displaystyle{ m }[/math]은 데이터의 개수입니다. 우리는 차이가 가장 적게 나길 원하니, 이 cost function을 최소화해야겠네요. 그런데 최소화는 어떻게 해야 할까요? 중학교를 나왔다면 모두 알고 있는 이차함수의 최대/최소를 이용하여 구할 수도 있지만, 나중에 나올 다양한 cost를 고려하여 조금 더 일반적인 최소화 알고리듬을 적용해 봅시다. TF에서는 여러 최소화 알고리듬을 제공하는데, 가장 이해하기 쉬운 기초적인 것은 아래에 소개할 경사 하강법(gradient descent algorithm)입니다.

4.2 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를 결정하는 것이 필요합니다.

4.3 일변수 선형 회귀를 TF로 구현[편집]

말보다는 코드죠.(?) 일단 써 봅시다. linreg.py를 만들어 다음과 같이 씁니다. (코드 설명은 주석에 되어 있습니다.) 문법은 Python 3.x를 기준으로 하겠습니다.

# -*- 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) 반환합니다.x'''# 최소화 알고리듬
learning_rate = tf.Variable(0.1) # learning_rate를 지정합니다.
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train = optimizer.minimize(cost) # cost를 최소화하는 방향으로 학습을 시키는 코드입니다.

# 위의 코드들을 tf.Session().run()으로 세션에서 실행시킵니다. with문을 쓰는 이유는, 중간에 세션이 풀리는 경우가 가끔 있기에, 이를 방지하기 위함입니다.
with tf.Session() as sess:
	sess.run(tf.global_variables_initializer()) # 텐서플로우에서는 가장 먼저 모든 변수를 초기화해야 합니다.

	for step in range(1001):
		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에 다른 값들(여기에서는 6과 10)을 넣어 y 값을 출력하게 합니다.
(tensorflow) $ python3 linreg.py
0 1.03264 [ 0.77603281] [ 1.26379299]
100 0.00608881 [ 1.90937197] [-0.79398119]
200 4.68818e-05 [ 1.99204767] [-0.98192227]
300 3.60923e-07 [ 1.99930203] [-0.99841368]
400 2.7736e-09 [ 1.99993873] [-0.999861]
500 2.16763e-11 [ 1.99999464] [-0.99998796]
600 3.07902e-13 [ 1.99999928] [-0.99999845]
700 3.07902e-13 [ 1.99999928] [-0.99999845]
800 3.07902e-13 [ 1.99999928] [-0.99999845]
900 3.07902e-13 [ 1.99999928] [-0.99999845]
1000 3.07902e-13 [ 1.99999928] [-0.99999845]
[ 10.99999809  18.99999428]

즉, 기계가 학습한 함수는 [math]\displaystyle{ \tilde y = 1.99999928 x-0.99999845 }[/math]로, 컴퓨터 소수 연산의 오차를 감안하면 완전히 학습했음을 알 수 있습니다. (원래 함수는 y = 2x - 1.) 또한 6을 넣었을 때 10.99999809 (약 11), 10을 넣었을 때 18.99999428 (약 19)로 정확한 결과를 반환했습니다.

원래 데이터가 완전한 선형 관계가 아니라면, 가장 비슷한 선형 추세선을 찾아줍니다. 예를 들어

x_data = [1, 2, 3]
y_data = [1, 3, 5]

으로 주어졌다면,

4.4 다변수 선형 회귀[편집]

하지만 세상의 여러 변수가 일대일로 대응되는 관계를 가진다는 보장은 없습니다. 오히려, 어떤 한 변수가 많은 다른 변수들에 의하여 영향을 받는 경우가 더욱 많죠. 즉 우리는 다음과 같은 다변수 함수를 알아야 합니다.

[math]\displaystyle{ y = f(x_1,~x_2 , ~\cdots,~x_n) =: f(\mathbf x) }[/math]

이때 가설 함수는 다음과 같이 세울 수 있습니다. (wx는 모두 열벡터)

[math]\displaystyle{ H(\mathbf x) = w_1 x_1 + \cdots + w_n x_n + b = \mathbf w^\mathsf T \mathbf x + b }[/math]

또는, 불편한 bias 항을 없애기 위해,

[math]\displaystyle{ \mathbf w = (b,~w_1,~\cdots,~w_n), \quad \mathbf x = (1, x_1,~x_2 , ~\cdots,~x_n) }[/math]

처럼 x 앞에 1을 끼워 넣어

[math]\displaystyle{ H(\mathbf x) = b \cdot 1 + w_1 x_1 + \cdots + w_n x_n = \mathbf w^\mathsf T \mathbf x }[/math]

로 쓰기도 합니다. 이때 우리는 cost를 최소자승법으로 계산하기로 하였으므로, 한 데이터의 cost function은 각 좌표에 대한 합으로 정의할 수 있습니다.

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

또한 전체 cost는 모든 데이터에 대한 평균으로 정의합니다.

[math]\displaystyle{ \mathrm{cost} = \frac{1}{m} \sum_{i=1}^m \mathrm{cost}(\mathbf x_i) = \frac 1 m \sum_{i=1}^m \sum_{j=1}^n (H(x_{ij}) - y_i)^2 }[/math]
# -*- coding: utf-8 -*-
import tensorflow as tf # 텐서플로우를 불러옵니다.

# 데이터를 입력해 줍니다.
x_data = [[1, 1, 1, 1, 1], # 첫째 행은 'b를 없애기 위해' 1로 채우는 행입니다.
          [1, 0, 3, 0, 5],
          [0, 2, 0, 4, 0]] # 이때 len(x_data) = 3입니다.
y_data = [1, 2, 3, 4, 5]

W = tf.Variable(tf.random_uniform([1, len(x_data)], -3.0, 3.0)) # [m, n]는 m행 n열의 행렬(2차원 텐서)이라는 뜻입니다.

# W가 1행 len(x_data)열인 이유는 내적 여러 개를 행렬곱으로 계산하기 위함입니다.
#                         [[_, _, _, _, _],
# W * x_data = [[_, _, _]] [_, _, _, _, _], = [_, _, _, _, _]
#                          [_, _, _, _, _]]

X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

hypothesis = tf.matmul(W, X)
cost = tf.reduce_mean(tf.square(hypothesis  - y_data)) # reduce_mean은 벡터(텐서)의 성분을 평균(mean)을 낸 후 괄호를 없애(reduce) 반환합니다.

# 최소화 알고리듬
learning_rate = tf.Variable(0.1)
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train = optimizer.minimize(cost)

# 세션에서 실행
with tf.Session() as sess:
	sess.run(tf.global_variables_initializer()) # 변수 초기화

	for step in range(1001):
		sess.run(train, feed_dict={X:x_data, Y:y_data})
		if step % 100 == 0:
			print (step, sess.run(cost, feed_dict={X:x_data, Y:y_data}), sess.run(W)) # 그 스텝에서의 weight와 cost를 출력합니다.

	print (sess.run(hypothesis, feed_dict={X:[[1],[1],[5]]})) # X를 열벡터 형태로 입력합니다. 맨 앞은 1이고, 이 입력은 H(1, 5)를 출력하게 합니다.
0 0.663228 [[-1.02151275  1.38295853  0.99940979]]
100 0.000335062 [[-0.04339394  1.01140702  1.01353443]]
200 6.81898e-07 [[-0.00195755  1.00051451  1.00061047]]
300 1.39222e-09 [[ -8.83055473e-05   1.00002325e+00   1.00002754e+00]]
400 2.90186e-12 [[ -3.95542747e-06   1.00000107e+00   1.00000131e+00]]
500 2.06057e-14 [[ -2.98086547e-07   1.00000012e+00   1.00000012e+00]]
600 2.06057e-14 [[ -2.98086547e-07   1.00000012e+00   1.00000012e+00]]
700 2.06057e-14 [[ -2.98086547e-07   1.00000012e+00   1.00000012e+00]]
800 2.06057e-14 [[ -2.98086547e-07   1.00000012e+00   1.00000012e+00]]
900 2.06057e-14 [[ -2.98086547e-07   1.00000012e+00   1.00000012e+00]]
1000 2.06057e-14 [[ -2.98086547e-07   1.00000012e+00   1.00000012e+00]]
[[ 6.00000048]]

5 간단한 분류 모델[편집]

주어진 데이터가 어떤 분류에 속하는지를 컴퓨터에게 가르쳐 보려 합니다. 즉, 'C1, C2, ...,위 Ck'라는 분류가 있을 때, 주어진 (n차원) 데이터 x가 어떤 분류에 속하는지를 알아보고 싶습니다. 하지만, 위(선형 회귀)에서 얻은 결괏값 y는 실숫값입니다. 우리는 이 실숫값을 분류에 속하는지 아닌지의 YES/NO로 바꾸어야 합니다. 즉, 결괏값은 0과 1로만 이루어진 k차원 벡터가 됩니다. 만약 이 분류들이 겹치지 않는다고 가정하면(mutually disjoint), 그 벡터는 단 한 성분에서만 1이고 나머지는 0인 one hot 방식의 벡터가 됩니다.

가장 합리적인 변환은, 제일 큰 값만 살려두기입니다. 즉 결과 벡터 y를 각 분류에 들어가는 정도(들어갈 확률)로 한 후에, 가장 그 확률이 높은 분류를 정답으로 택합니다. 이때 사용하는 함수는 softmax입니다.

[math]\displaystyle{ \mathrm{softmax} ~ S(y_i) = \frac{e^{y_i}}{\sum_j e^{y_j}} }[/math]
[math]\displaystyle{ \mathbf y = \begin{bmatrix}\mathrm P(\mathbf x \in C_1) \\ \mathrm P(\mathbf x \in C_2) \\ \vdots \\ \mathrm P(\mathbf x \in C_k)\end{bmatrix} \xrightarrow{\mathrm{softmax}}\xrightarrow{\mathrm{argmax}} \begin{bmatrix}\mathrm 0 \\ \vdots \\ 1 \\ \vdots \\ 0\end{bmatrix} }[/math]

Loss function은 어떻게 해야 할까요? 이전의 least square 방식을 쓰면 (softmax에 지수함수가 있어서) 볼록하지 않아지고, 즉 여러 optimizer를 쓰기 곤란해집니다. 이럴 때 쓰는 것이 로그가 포함된 cross entropy라고 하는 함수입니다.

[math]\displaystyle{ \displaystyle H(p,q)=\operatorname {E} _{p}[-\log q] = -\sum _{x}p(x)\,\log q(x). }[/math]

텐서플로우에서는 tf.nn.softmax_cross_entropy_with_logits(logits=hypothesis, labels=Y)라는 메서드가 준비되어 있습니다. 이것은 어떤 데이터가 각 분류에 들어갈 확률을 의미합니다. 이것이 의도된 출력값 y와 차이가 적을수록 잘 학습이 된 것이니, 이것을 loss function으로 쓰면 됩니다. 즉,

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=hypothesis, labels=Y))
optimizer = tf.train.{{some optimizer}}({{learning rate}}).minimize(cost)

으로 구현합니다.

5.1 MNIST[편집]

가장 많이 쓰이는 분류 예제는 MNIST입니다. 손으로 쓴 숫자를 인식하는 모델로, 28x28 = 784 픽셀의 숫자 이미지를 인식합니다.

파일:MNIST.png

MNIST 데이터는

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./mnist/data/", one_hot=True)

로 받아옵니다. 가설 함수는 그냥 선형(Y = XW)으로 두고 학습시켜 봅시다.

데이터를 학습할 때에, batch라는 묶음 단위로 한꺼번에 학습을 합니다. mnist.train.num_examples는 데이터의 총 개수이고, 한 batch 당의 데이터 개수로 나눠주면 이 batch가 몇 개 있는지 알아낼 수 있습니다. 물론 그 반대도 가능합니다.

import tensorflow as tf

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./mnist/data/", one_hot=True)

X = tf.placeholder(tf.float32, [None, 784]) # 784 pixel의 이미지를 가져와서
Y = tf.placeholder(tf.float32, [None, 10]) # 0~9의 10 개 성분을 가진 one hot 벡터로 출력합니다.

W = tf.Variable(tf.random_normal([784, 10], stddev=0.01))

hypothesis = tf.matmul(X, W)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=hypothesis, labels=Y))
optimizer = tf.train.AdamOptimizer(0.001).minimize(cost) # Adam optimizer를 사용해 보겠습니다.

##### 학습 #####
init = tf.global_variables_initializer() 
with tf.Session() as sess:
    sess.run(init)

    batch_size = 100
    total_batch = int(mnist.train.num_examples/batch_size)

    for epoch in range(15):
        total_cost = 0

        for i in range(total_batch):
            # 텐서플로우 mnist 모델의 next_batch 함수로 batch를 가져옵니다.
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            sess.run(optimizer, feed_dict={X: batch_xs, Y: batch_ys})
            total_cost += sess.run(cost, feed_dict={X: batch_xs, Y: batch_ys})

        print('Epoch:', '%04d' % (epoch + 1),\
              'Avg. cost =', '{:.3f}'.format(total_cost / total_batch))

    print('Optimization completed.')

    ##### 정확도 확인 #####
    # 가장 확률이 높은 것과 실제 데이터를 비교합니다. 이때 == 대신 tf.equal을 쓰는 것이 추천됩니다.
    check_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(Y, 1)) 
    accuracy = tf.reduce_mean(tf.cast(check_prediction, tf.float32))

    print('Accuracy:', sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels}))
batch 크기에 따른 정확도(차이가 있을 수 있음)
batch_size 1 10 100 1000
accuracy 0.9213 0.9266 0.9261 0.8558

6 ConvNet[편집]