빅데이타 & 머신러닝/머신러닝

텐서플로우 #2 - 행렬과 텐서플로우

Terry Cho 2016. 12. 26. 13:39

텐서플로우 #2 - 행렬과 텐서플로우


조대협 (http://bcho.tistory.com)


머신러닝은 거의 모든 연산을 행렬을 활용한다. 텐서플로우도 이 행렬을 기반으로 하고, 이 행렬의 차원을 shape 라는 개념으로 표현하는데, 행렬에 대한 기본적이 개념이 없으면 헷갈리기 좋다. 그래서 이 글에서는 간략하게 행렬의 기본 개념과 텐서플로우내에서 표현 방법에 대해서 알아보도록 한다.


행렬의 기본 개념 훝어보기

행과 열

행렬의 가장 기본 개념은 행렬이다. mxn 행렬이 있을때, m은 행, n은 열을 나타내며, 행은 세로의 줄수, 열은 가로줄 수 를 나타낸다. 아래는 3x4 (3행4열) 행렬이다.


곱셈


곱셈은 앞의 행렬에서 행과, 뒤의 행렬의 열을 순차적으로 곱해준다.

아래 그림을 보면 쉽게 이해가 될것이다.



이렇게 앞 행렬의 행과 열을 곱해나가면 결과적으로 아래와 같은 결과가 나온다.


이때 앞의 행렬의 열과, 뒤의 행렬의 행이 같아야 곱할 수 있다.

즉 axb 행렬과 mxn 행렬이 있을때, 이 두 행렬을 곱하려면 b와 m이 같아야 한다.

그리고 이 두 행렬을 곱하면 axn 사이즈의 행렬이 나온다.

행렬의 덧셈과 뺄셈

행렬의 덧셈과 뺄셈은 단순하다. 같은 행과 열에 있는 값을 더하거나 빼주면 되는데, 단지 주의할점은 덧셈과 뺄샘을 하는 두개의 행렬의 차원이 동일해야 한다.


텐서 플로우에서 행렬의 표현

행렬에 대해서 간단하게 되짚어 봤으면, 그러면 텐서 플로우에서는 어떻게 행렬을 표현하는지 알아보자


을 하는 코드를 살펴보자


예제코드

import tensorflow as tf


x = tf.constant([ [1.0,2.0,3.0] ])

w = tf.constant([ [2.0],[2.0],[2.0] ])

y = tf.matmul(x,w)

print x.get_shape()


sess = tf.Session()

init = tf.global_variables_initializer()

sess.run(init)

result = sess.run(y)


print result


실행 결과

(1, 3)
[[ 12.]]



텐서플로우에서 행렬의 곱셈은 일반 * 를 사용하지 않고, 텐서플로우 함수  “tf.matmul” 을 사용한다.

중간에, x.get_shape()를 통해서, 행렬 x의 shape를 출력했는데, shape는 행렬의 차원이라고 생각하면 된다. x는 1행3열인 1x3 행렬이기 때문에, 위의 결과와 같이 (1,3)이 출력된다.


앞의 예제에서는 contant 에 저장된 행렬에 대한 곱셈을 했는데, 당연히 Variable 형에서도 가능하다.


예제 코드

import tensorflow as tf


x = tf.Variable([ [1.,2.,3.] ], dtype=tf.float32)

w = tf.constant([ [2.],[2.],[2.]], dtype=tf.float32)

y = tf.matmul(x,w)


sess = tf.Session()

init = tf.global_variables_initializer()

sess.run(init)

result = sess.run(y)


print result


Constant 및 Variable 뿐 아니라,  PlaceHolder에도 행렬로 저장이 가능하다 다음은 PlaceHolder에 행렬 데이타를 feeding 해주는 예제이다.

입력 데이타 행렬 x는 PlaceHolder 타입으로 3x3 행렬이고, 여기에 곱하는 값 w는 1x3 행렬이다.


예제 코드는 다음과 같다.


예제코드

import tensorflow as tf


input_data = [ [1.,2.,3.],[1.,2.,3.],[2.,3.,4.] ] #3x3 matrix

x = tf.placeholder(dtype=tf.float32,shape=[None,3])

w = tf.Variable([ [2.],[2.],[2.] ], dtype = tf.float32) #3x1 matrix

y = tf.matmul(x,w)


sess = tf.Session()

init = tf.global_variables_initializer()

sess.run(init)

result = sess.run(y,feed_dict={x:input_data})


print result


실행결과

[[ 12.]
[ 12.]
[ 18.]]


이 예제에서 주의 깊게 봐야 할부분은 placeholder x 를 정의하는 부분인데, shape=[None,3] 으로 정의했다 3x3 행렬이기 때문에, shape=[3,3]으로 지정해도 되지만 None 이란, 갯수를 알수 없음을 의미하는 것으로, 텐서플로우 머신러닝 학습에서 학습 데이타가 계속해서 들어오고  학습 때마다 데이타의 양이 다를 수 있기 때문에, 이를 지정하지 않고 None으로 해놓으면 들어오는 숫자 만큼에 맞춰서 저장을 한다.

브로드 캐스팅

텐서플로우 그리고 파이썬으로 행렬 프로그래밍을 하다보면 헷갈리는 개념이 브로드 캐스팅이라는 개념이 있다. 먼저 다음 코드를 보자


예제코드

import tensorflow as tf


input_data = [

    [1,1,1],[2,2,2]

   ]

x = tf.placeholder(dtype=tf.float32,shape=[2,3])

w  =tf.Variable([[2],[2],[2]],dtype=tf.float32)

b  =tf.Variable([4],dtype=tf.float32)

y = tf.matmul(x,w)+b


print x.get_shape()

sess = tf.Session()

init = tf.global_variables_initializer()

sess.run(init)

result = sess.run(y,feed_dict={x:input_data})


print result


실행결과

(2, 3)
[[ 24.]
[ 48.]]


행렬 x는 2x3 행렬이고 w는 3x1 행렬이다. x*w를 하면 2*1 행렬이 나온다.

문제는 +b 인데, b는 1*1 행렬이다. 행렬의 덧셈과 뺄셈은 차원이 맞아야 하는데, 이 경우 더하고자 하는 대상은 2*1, 더하려는 b는 1*1로 행렬의 차원이 다르다. 그런데 어떻게 덧셈이 될까?

이 개념이 브로드 캐스팅이라는 개념인데, 위에서는 1*1인 b행렬을 더하는 대상에 맞게 2*1 행렬로 자동으로 늘려서 (stretch) 계산한다.


브로드 캐스팅은 행렬 연산 (덧셈,뺄셈,곱셈)에서 차원이 맞지 않을때, 행렬을 자동으로 늘려줘서(Stretch) 차원을 맞춰주는 개념으로 늘리는 것은 가능하지만 줄이는 것은 불가능하다.


브로드 캐스팅 개념은 http://scipy.github.io/old-wiki/pages/EricsBroadcastingDoc 에 잘 설명되어 있으니 참고하기 바란다. (아래 그림은 앞의 링크를 참조하였다.)


아래는 4x3 행렬 a와 1x3 행렬 b를 더하는 연산인데, 차원이 맞지 않기 때문에, 행렬 b의 열을 늘려서 1x3 → 4x3 으로 맞춰서 연산한 예이다.


만약에 행렬 b가 아래 그림과 같이 1x4 일 경우에는 열을 4 → 3으로 줄이고, 세로 행을 1→ 4 로 늘려야 하는데, 앞에서 언급한바와 같이, 브로드 캐스팅은 행이나 열을 줄이는 것은 불가능하다.


다음은 양쪽 행렬을 둘다 늘린 케이스 이다.

4x1 행렬 a와 1x3 행렬 b를 더하면 양쪽을 다 수용할 수 있는 큰 차원인 4x3 행렬로 변환하여 덧셈을 수행한다.



텐서플로우 행렬 차원 용어


텐서플로우에서는 행렬을 차원에 따라서 다음과 같이 호칭한다.

행렬이 아닌 숫자나 상수는 Scalar, 1차원 행렬을 Vector, 2차원 행렬을 Matrix, 3차원 행렬을 3-Tensor 또는 cube, 그리고 이 이상의 다차원 행렬을 N-Tensor라고 한다.


그리고 행렬의 차원을 Rank라고 부른다. scalar는 Rank가 0, Vector는 Rank 가 1, Matrix는 Rank가 2가 된다.


그리드형