CodeOnWeb
로그인

머신러닝 초보를 위한 MNIST

간단한 컴퓨터 비전 데이터 셋인 MNIST 를 이용한 숫자 인식을 배우며 머신러닝을 시작해 봅시다.

Tutorials Lablup 2016/03/05, 02:15

내용

TensorFlow 로 시작하는 기계 학습과 딥 러닝 (10)

머신러닝 초보를 위한 MNIST 전문가를 위한 딥러닝 MNIST TensorFlow의 기본 사용법 TensorFlow 역학 101 단어의 벡터 표현 (Vector Representations of Words) 순환 신경망 (Recurrent Neural Network) 시퀀스 변환 모델 (Sequence-to-sequence Models) 만델브로프 집합 (Mandelbrot Set) 편미분 방정식 tf.contrib.learn 빠르게 시작하기

머신러닝 초보를 위한 MNIST

이 연습은 기계 학습 및 TensorFlow 양쪽 모두 처음인 사람들을 위한 것입니다. 만약 MNIST가 무엇인지 이미 알고 있고, 소프트맥스 회귀 (softmax [multinomial logistic] regression) 가 무엇인지 알고 있다면, 훨씬 빠른 연습을 보는 것을 권합니다.

우리가 프로그래밍을 배울 때 관습적으로 가장 처음 해 보는 것은 "Hello World" 를 출력하는 것입니다. 프로그래밍에 Hello World가 있듯이, 기계 학습에는 MNIST가 있습니다.

MNIST 는 간단한 컴퓨터 비전 데이터 세트로, 아래와 같이 손으로 쓰여진 이미지들로 구성되어 있습니다.

file

이 데이터는 각 데이터의 숫자가 무엇인가에 대한 라벨이 함께 붙어 있습니다. 예를 들어 위의 이미지에는 각각 5, 0, 4, 1 이라는 라벨이 붙어 있습니다.

이 연습에서 우리는 이미지를 들여다보고 그 이미지가 어떤 숫자인지 예측하는 학습 모델을 만들겁니다. 우리 목표는 현재 가장 높은 수준을 가진 정교한 모델을 만드는 것-비록 뒤에서 그걸 할 수 있는 코드도 주겠지만-이 아니라 TensorFlow에 발끝만 살짝 담그는 겁니다. 그러니 아주 간단한 모델인 소프트맥스 회귀(Softmax Regression)부터 시작하겠습니다.

이 예시의 실제 코드들은 엄청나게 짧아서 딱 세 줄로도 재미있는 것들을 해 볼 수 있습니다. 하지만 그 뒤에 어떤 아이디어들이 있는지 이해하는 것은 아주 중요합니다. TensorFlow가 동작하는 방법이라거나 기계학습의 핵심 개념 등과 같은 것 말입니다. 그렇기 때문에 우리는 코드를 아주 주의깊게 들여다 보겠습니다.

MNIST 데이터

MNIST 데이터는 Yann LeCun의 웹사이트에서 제공합니다. 편의를 위해 데이터를 자동으로 다운로드하고 설치하는 코드를 포함해 놓았습니다. 코드를 다운로드 하고아래와 같이 import하거나, 그냥 안에 붙여 넣으시면 됩니다.

Note

CodeOnWeb에서는 그냥 실행 버튼을 누르면 됩니다.

다운로드한 데이터는 55000개의 학습 데이터 (mnist.train), 10000개의 테스트 데이터 (mnist.test) 및 5000개의 검증 데이터 (mnist.validation) 세 부분으로 나누어져 있습니다. 이렇게 나눠진 것은 굉장히 중요합니다. 학습하지 않은 별도의 데이터를 이용해서 학습한 결과가 실제로 일반적으로 적용되는지 검증하는 것이 기계 학습의 핵심입니다!

앞에서 이야기했듯, 모든 MNIST 데이터 포인트들은 두 부분으로 되어 있습니다. 손으로 쓴 숫자와 그에 해당하는 라벨입니다. 우리는 이미지를 "xs"라고 부르고 라벨을 "ys"라고 부르겠습니다. 학습 데이터 세트와 테스트 데이터 세트는 xs와 ys를 포함하고 있습니다. 예를 들어, 학습 세트의 이미지는 mnist.train.images 이며, 학습 세트의 라벨은 mnist.train.labels 입니다.

각 이미지는 가로, 세로가 각 28픽셀입니다. 우리는 이걸 숫자로 구성된 큰 행렬로 취급할 수 있습니다:

file

우리는 이 행렬을 28x28 = 784개의 숫자를 갖는 벡터로 단순화할 수 있습니다. 우리가 이미지들에 모두 동일한 방법을 적용하는 한, 어떻게 행렬을 단순화하는지는 중요하지 않습니다. 이 관점에서 보면 MNIST 이미지들은 그저 784차원 벡터 공간 안에서 아주 풍부한 구조를 지닌 점들일 뿐입니다. (주의: 컴퓨터 자원이 많이 필요한 시각화들입니다)

데이터를 단순화하는 과정은 이미지의 2차원 구조를 버립니다. 이게 나쁠까요? 글쎄요. 최고의 컴퓨터 비전 방법들은 이 구조도 전부 이용하고 있고, 우리도 이후의 예제에서 그렇게 해 볼 겁니다. 그러나 우리가 여기서 이용할 간단한 방법인 소프트맥스 회귀에서는 2차원 구조를 사용하지는 않습니다.

결과로 mnist.train.images 는 [55000,784] 형태의 텐서 (n차원 행렬) 를 얻었습니다. 첫번째 차원 인덱스는 이미지에, 두번째 차원 인덱스는 각 이미지의 픽셀에 대응됩니다. 텐서의 각 구성 요소들은 특정한 이미지 안의 특정한 픽셀의 진하기를 0에서 1 사이의 값으로 나타냅니다.

file

MNIST 안에서 대응되는 라벨들은 주어진 각 이미지가 어떤 숫자를 나타내는가를 의미하는 0에서 9 사이의 숫자입니다. 이 연습의 목적으로, 우리는 우리 라벨들을 "one-hot 벡터" 로 사용할겁니다. one-hot 벡터는 하나의 차원만 1이고 나머지 모든 차원들은 0으로 채워진 벡터입니다. 이 경우, $n$번째 숫자는 $n$번째 차원이 1인 벡터로 표시될 것입니다. 예를 들어, 3은 [0,0,0,1,0,0,0,0,0,0] 로 표현됩니다. 따라서 mnist.train.labels 는 [55000, 10] 행렬이 됩니다.

file

이제 모델을 만들 준비가 다 됐습니다!

소프트맥스 회귀 (Softmax Regressions)

우리는 MNIST의 모든 이미지가 0부터 9까지의 숫자인 것을 알고 있습니다. 우리는 학습 모델이 이미지를 보았을 때 각 숫자로 판단할 확률을 얻으려고 합니다. 예를 들어, 우리 모델은 9의 사진을 보고 그 숫자가 80% 정도의 확률로 9라고 확신하지만, 동시에 (숫자 윗쪽의 곡선 영향으로) 8일 가능성도 5%를 주고, 나머지 모든 숫자들에는 확신할 수 없으므로 아주 작은 확률을 줄 것입니다.

이 경우는 소프트맥스 회귀가 자연스럽고 간단한 모델인 고전적인 경우입니다. 만약 어떤 대상이 여러 다양한 것들 중 하나일 확률을 계산하려면 소프트맥스가 가장 적당합니다. 심지어 나중에 우리가 훨씬 더 정교한 모델들을 배웠을 때에도 마지막 단계는 소프트맥스 계층일 것입니다.

소프트맥스 회귀는 두 단계가 있습니다: 첫번째로 우리 입력이 특정 클래스에 해당되는지에 대한 증거를 더하고 그 다음 증거를 확률로 변환합니다.

주어진 이미지가 특정한 클래스에 들어가는지의 증거를 모아 계산하기 위해 픽셀 농도의 가중합을 사용합니다. 특정 클래스 안의 이미지들의 픽셀들에 비해 픽셀 농도가 높을 경우 가중치는 음수이고, 그렇지 않을 경우 가중치는 양수가 됩니다.

아래의 다이어그램은 세가지 클래스 각각에 대해 학습한 모델의 가중치를 보여줍니다. 빨간색은 음수 가중치를, 파란색은 양수 가중치를 나타냅니다.

file

이제 편향(bias)이라고 부르는 추가적인 증거를 더합니다. 기본적으로, 우리는 몇몇 경우들은 입력들에 대해 더 자유롭다고 말할 수 있게 하고 싶죠. 결과적으로 주어진 입력 $x$ 에 대한 클래스 $i$ 의 증거는

$\text{evidence}_i = \sum_j W_{i,~ j} x_j + b_i$

이 됩니다. $W_i$ 는 가중치, $b_i$ 는 클래스 $i$ 에 대한 편향, $j$ 는 입력한 이미지 $x$ 의 픽셀들에 따른 합을 구하기 위한 인덱스입니다. 이제 증거 항목들을 "소프트맥스" 함수를 이용해 예측 확률로 변환합니다:

$y = \text{softmax}(\text{evidence})$

여기서 소프트맥스가 우리가 제공한 선형 함수의 출력 결과를 원하는 형태로 만들기 위해 "활성화" 나 "링크" 함수의 형태로 적용됩니다 -- 이 경우, 10가지 경우에 대한 확률 분포입니다. 이것을 증거 항목들을 각 클래스에 대응하는 확률로 변환하는 과정으로 생각해도 됩니다. 이 과정은 다음과 같이 정의합니다:

$\text{softmax}(x) = \text{normalize}(\exp(x))$

이 식을 전개하면 다음과 같은 결과를 얻습니다:

$\text{softmax}(x)_i = \frac{\exp(x_i)}{\sum_j \exp(x_j)}$

그러나 종종 소프트맥스를 입력을 지수화하고 정규화하는 첫번째 방법으로 생각하는 것이 훨씬 도움이 됩니다. 지수화는 증거에 하나가 더해질 경우 가중치를 곱으로 증가시키는 의미가 됩니다. 반대로 말하면, 증거에서 하나가 줄어들면 가설의 가중치가 원래 가중치의 분수비로 줄어든다는 의미가 됩니다. 어떤 가설도 0이나 음수의 가중치를 가질 수 없습니다. 그런 후 소프트맥스는 이러한 가중치를 정규화해서, 모두 더하면 1이 되는 확률 분포의 형태로 만듭니다. (소프트맥스 함수에 대한 더 많은 직관을 얻고 싶다면, 시각화가 있는 마이클 닐센의 책의 챕터를 참고하세요.)

소프트맥스 회귀를 아래와 같이 그려볼 수 있습니다 (훨씬 많은 $x$가 있다는 것만 제외하면요) 각각의 출력마다, 우리는 가중치의 합을 계산하고, 편향을 더하고, 소프트맥스를 적용합니다.

file

식 형태로 쓰면:

file

우리는 이 과정을 행렬곱과 벡터합으로 바꾸는 식으로 "벡터화" 할 수 있습니다. 벡터화는 계산 효율을 위해서 도움이 됩니다. (또한 생각하기에도 유용합니다)

file

더 간단하게는 그냥 이렇게 쓸 수 있습니다:

$y = \text{softmax}(Wx + b)$

회귀 구현하기

Python 으로 효율적인 수치 연산을 하기 위해서 우리는 보통 행렬곱 등의 무거운 연산을 Python 외부에서 처리하는 NumPy등의 라이브러리들을 사용하는데, 이들은 다른 언어로 구현한 고도로 최적화된 코드를 이용합니다. 불행히도, 모든 작업 단계에서 Python으로 도로 스위칭하는 과정에서도 아주 큰 오버헤드가 여전히 존재할 수 있습니다. 이러한 오버헤드는 GPU를 이용하거나 분산 처리를 이용하는, 데이터 전송에 큰 비용을 지불하는 환경에서 계산을 실행할 경우 특히 큽니다.

TensorFlow 또한 Python 외부에서 무거운 작업들을 실행하지만, 이러한 오버헤드를 피하기 위해 한 단계 더 나아갑니다. Python과 독립된 큰 단일 작업을 실행하는 대신, TensorFlow는 완전히 Python 밖에서 실행되는 서로 상호작용하는 작업들의 그래프를 기술하도록 합니다. (몇몇 기계학습 라이브러리들에서 이러한 접근법을 볼 수 있습니다)

TensorFlow 를 사용하려면 먼저 import 를 해야 합니다.

심볼릭 변수들을 사용하여 상호작용하는 작업들을 기술합니다. 하나 만들어 봅시다:

$x$ 는 특별한 값이 아닙니다. 수식기호인데, 우리가 TensorFlow에게 계산을 하도록 명령할 때 입력할 값입니다. 우리는 MNIST 이미지들의 어떤 숫자들이든 입력할 수 있기를 원하는데, 각 이미지들은 784차원의 벡터로 단조화되어 있습니다. 우리는 이걸 [None, 784] 형태의 부정소숫점으로 이루어진 2차원 텐서로 표현합니다. (None 은 해당 값이 어떤 길이도 될 수 있음을 의미합니다.)

우리는 또한 가중치와 편향값이 필요합니다. 이런 것들을 추가적인 입력으로 다루는 것을 생각해 볼 수 있습니다. 그러나 TensorFlow는 더 나은 방법을 갖고 있습니다: Variable이죠. Variable은 TensorFlow의 상호작용하는 작업 그래프들간에 유지되는 변경 가능한 텐서입니다. 계산 과정에서 사용되거나 심지어 변경될 수도 있습니다. 기계학습 응용 사례들을 위해 일반적으로 모델 파라미터 Variables 를 사용합니다.

우리는 tf.Variable 을 주어서 Variable의 초기값을 만듭니다: 이 경우, 우리는 Wb 를 0으로 채워진 텐서들로 초기화합니다. 우리가 Wb 를 학습할 것이기 때문에, 그것들이 무엇으로 초기화되었는지는 크게 중요하지 않습니다.

W의 형태가 [784, 10] 임을 주의합시다. 우리는 784차원의 이미지 벡터를 곱하여 10차원 벡터의 증거를 만들것이기 때문입니다. $b$[10]의 형태이므로 출력에 더할 수 있습니다.

이제 우리 모델을 구현할 수 있습니다. 딱 한 줄이면 됩니다!

첫번째로, 우리는 tf.matmul(x, W) 표현식으로 $x$$W$를 곱합니다. 이 값은 $W_x$가 있던 우리 식에서 곱한 결과에서 뒤집혀 있는데, 이것은 $x$가 여러 입력으로 구성된 2D 텐서일 경우를 다룰 수 있게 하기 위한 잔재주입니다. 그 다음 b를 더하고, 마지막으로 tf.nn.softmax 를 적용합니다.

됐습니다. 몇몇 짧은 설정 줄들에 이어 단지 한 줄로 우리 모델을 정의할 수 있었습니다. TensorFlow 가 특히 소프트맥스 회귀만을 쉽게 할 수 있게 디자인되어 있어서가 아니라, 기계학습 모델부터 물리학 시뮬레이션까지 다양한 종류의 수치 계산을 다룰 수 있는 아주 유연한 방법을 제공해 주기 때문입니다. 그리고 우선 한 번 정의하면, 다양한 기기에서 우리 모델을 실행할 수 있습니다: 컴퓨터의 CPU, GPU들, 심지어는 핸드폰에서도요!

학습시키기

모델을 학습시키기 위해서, 우선 모델에게 무엇이 좋은지를 정의하는 것이 필요합니다. 음, 사실은 기계학습에서는 우리는 일반적으로 모델에게 있어서 무엇이 나쁜지 정의하고 (비용(cost)이나 손실(loss)이라고 합니다), 나쁜 정도를 최소화하려고 시도합니다. 그러나 두가지는 동일하죠.

아주 일반적이고 굉장히 괜찮은 비용 함수는 "교차 엔트로피 (cross-entropy)" 입니다. 놀랍게도 교차 엔트로피는 정보 이론에서 정보 압축 코드에 대한 생각에서 출발했습니다만, 도박부터 기계학습까지 아주 다양한 분야들에서 중요한 아이디어가 되었습니다. 아래와 같이 정의할 수 있습니다:

$H_{y'}(y) = -\sum_i y'_i \log(y_i)$

$y$는 우리가 예측한 확률 분포이고, $y'$ 는 실제 분포 (우리가 입력할 one-hot 벡터) 입니다. 대략적인 관점에서 보면, 교차 엔트로피는 우리 예측이 참값을 표현하는데 얼마나 비효율적인지를 측정합니다. 교차 엔트로피에 대해 더 상세한 내용을 보는 것은 우리 연습의 범위를 넘어섭니다만, 이해해볼 가치가 충분합니다.

교차 엔트로피를 구현하기 위해 우리는 우선적으로 정답을 입력하기 위한 새 placeholder를 추가해야 합니다:

그 다음 교차 엔트로피 $-\sum y'\log(y)$ 를 구현할 수 있습니다.

첫번째로, tf.logy의 각 원소의 로그값을 계산합니다. 그 다음, y_ 의 각 원소들에, 각각에 해당되는 tf.log(y)를 곱합니다. 마지막으로, tf.reduce_sum은 텐서의 모든 원소를 더합니다.

알아둘 것은 이 값이 단지 한 번의 예측에 대한 참값의 교차 엔트로피값이 아니라 우리가 본 모든 이미지들에 대한 교차 엔트로피의 합이라는 점입니다. 이 예제에서는 각 배치마다 100개의 이미지들이 있습니다: 우리가 100개의 데이터 포인트들에 대해 얼마나 잘 동작하는지 보는것은 하나의 데이터 포인트에 대한 것 보다 훨씬 좋은 기술 방법입니다.

우리 모델이 무엇을 해야 하는 것을 안다면, TensorFlow 에게 가르치는 것은 정말 쉽습니다. TensorFlow는 당신 계산 과정의 전체 그래프를 알고 있기 때문에, 자동적으로 역전파(backpropagation) 알고리즘을 이용하여 비용 최소화에 어떤 변수가 얼마나 영향을 주는지를 효율적으로 계산합니다. 그리고 당신이 선택한 최적화 알고리즘을 적용하여 변수들을 수정하고 비용을 최소화할 수 있습니다.

이 경우 TensorFlow에게 학습도를 0.01로 준 경사 하강법(gradient descent) 알고리즘을 이용하여 교차 엔트로피를 최소화하도록 명령했습니다. 경사 하강법은 TensorFlow 가 각각의 변수들을 비용을 줄이는 방향으로 약간씩 바꾸는 간단한 방법입니다. 그러나 TensorFlow는 또한 많은 다른 최적화 알고리즘들을 제공합니다: 그 중 하나를 이용하는 것은 한 줄을 살짝 수정하는 것처럼 간단합니다.

TensorFlow가 여기서 실제로 (뒤에서) 하는 일은 역전파 및 경사 하강법을 구현한 작업을 당신의 그래프에 추가하는 일입니다. 그리고 실행할 경우 비용을 줄이기 위해 변수들을 살짝 미세조정하는 경사 하강법 학습 과정을 실행하는 단일 작업을 돌려줍니다.

이제 학습을 위한 모델 준비를 마쳤습니다. 실행 전 마지막으로 우리가 만든 변수들을 초기화하는 작업을 추가해야 합니다.

이제 세션에서 모델을 시작하고 변수들을 초기화하는 작업을 실행할 수 있습니다:

학습해봅시다 -- 우리는 학습을 1000번 시켜 볼겁니다!

각 반복 단계마다, 학습 세트로부터 100개의 무작위 데이터들의 일괄 처리(batch)들을 가져옵니다. placeholders를 대체하기 위한 일괄 처리 데이터에 train_step 피딩을 실행합니다.

무작위 데이터의 작은 일괄 처리를 이용하는 방법을 확률적 교육(stochastic training) 이라고 합니다 -- 이 경우에는 확률적 경사 하강법입니다. 이상적으로는 무엇을 해야 할지에 대해 더 나은 직관을 줄 수 있도록 학습의 모든 단계에 모든 데이터를 사용하고 싶습니다만, 그 과정은 비쌉니다. 그래서 그 대신에 우리는 각 시간마다 다른 서브셋을 사용합니다. 이렇게 하면 저렴하면서도 거의 비슷한 효과를 볼 수 있습니다.

Note

비싸거나 저렴한 것은 돈이 아니라 계산에 들어가는 비용을 의미합니다.

모델 평가하기

우리 모델이 얼마나 좋을까요?

음, 우선 맞는 라벨을 예측했는지를 확인합시다. tf.argmax는 특정한 축을 따라 가장 큰 원소의 색인을 알려주는 엄청나게 유용한 함수입니다. 예를 들어 tf.argmax(y,1) 는 진짜 라벨이 tf.argmax(y_,1) 일때 우리 모델이 각 입력에 대하여 가장 정확하다고 생각하는 라벨입니다. 우리는 tf.equal 을 이용해 예측이 실제와 맞았는지 확인할 수 있습니다.

이 결과는 부울 리스트를 줍니다. 얼마나 많은 비율로 맞았는지 확인하려면, 부정소숫점으로 캐스팅한 후 평균값을 구하면 됩니다. 예를 들어, [True, False, True, True][1,0,1,1] 이 되고 평균값은 0.75가 됩니다.

마지막으로, 테스트 데이터를 대상으로 정확도를 확인해 봅시다.

결과는 약 91% 여야 합니다.

이건 좋은걸까요? 음, 그다지입니다. 사실, 굉장히 나쁩니다. 우리가 아주 아주 간단한 모델을 사용했기 때문입니다. 몇몇 작은 변경만으로 97% 를 얻을 수 있습니다. 최고의 모델은 99.7%의 정확도를 보일겁니다! (더 많은 정보를 위해서는 결과 목록을 보세요.)

중요한 것은 우리가 이 모델에서 배운 것들입니다. 아직 이 결과들이 조금 쳐지는 느낌이 든다면 더 나은 결과를 얻는 다음 연습을 보고, TensorFlow를 이용해서 어떻게 더 정교한 모델을 만드는지 배워봅시다!

추가

위의 예제를 한 번에 실행한 결과를 MNIST 예제 샌드박스 로 확인해 봅시다.

96928 읽음
첫 강의입니다
다음 전문가를 위한 딥러닝 MNIST

저자

토론이 없습니다

Please log in to leave a comment

16.5.11618.20190612.allo
문제 보고 · 사용조건 · 개인정보보호
래블업 주식회사 · 대한민국 서울 테헤란로 145 · 대표: 신정규 · 사업자번호: 864-88-00080 · +82 70-8200-2587

거절 확인

닫기
좋아요 책갈피 토론

아래 주소를 복사하세요