관리 메뉴

Storage Gonie

모두를 위한 딥러닝 제24강 ML lab 08: Tensor Manipulation(연산함수) 본문

데이터 사이언스/모두를 위한 딥러닝

모두를 위한 딥러닝 제24강 ML lab 08: Tensor Manipulation(연산함수)

Storage Gonie 2018. 9. 29. 20:58
반응형

* 키워드 : slicing, shape, rank, axis, matmul, broadcasting, reduce_mean, reduce_sum, argmax, reshape, squeeze, expand_dims, one_hot, cast, stack, ones_like, zeros_like, zip


#-*- coding: euc-kr -*-

# https://www.tensorflow.org/api_guides/python/array_ops
import tensorflow as tf
import numpy as np
import pprint
tf.set_random_seed(777) # for reproducibility

pp = pprint.PrettyPrinter(indent=4)
sess = tf.InteractiveSession()


# array & slicing 1

- slicing은 [a:b]를 사용한 것을 말함

- [a:b] 에서 a는 무조건 시작 index가 되지만, b는 b바로 앞 index까지를 의미한다. (단, b자리에 아무것도 없는 경우는 예외.)

- rank는 맨 앞의 연속적인 ' [ '의 개수를 세면 됨.

- shape은 rank 숫자의 개수대로 모양을 가지며, 맨 뒤에 채워질 값은 맨 안쪽의 element 개수, 그 앞엔 그 다음 바깥의 element 개수 ...

t = np.array([0., 1., 2., 3., 4., 5., 6.])

pp.pprint(t)
# array([0., 1., 2., 3., 4., 5., 6.])
print(t)
# [0. 1. 2. 3. 4. 5. 6.]
print(t.ndim) # rank
# 1
print(t.shape) # shape
# (7,)
print(t[0], t[1], t[-1])
# (0.0, 1.0, 6.0)
print(t[2:5], t[4:-1])
# (array([2., 3., 4.]), array([4., 5.]))
print(t[:2], t[3:])
# (array([0., 1.]), array([3., 4., 5., 6.]))


# array & slicing 2

t = np.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.], [10., 11., 12.]])

pp.pprint(t)
#array([[ 1., 2., 3.],
# [ 4., 5., 6.],
# [ 7., 8., 9.],
# [10., 11., 12.]])

print(t.ndim) # rank
# 2

print(t.shape) # shape
# (4, 3)

print(t[:2])
#[[1. 2. 3.]
# [4. 5. 6.]]

print(t[1:3])
#[[4. 5. 6.]
# [7. 8. 9.]]


# Tensor에서의 Shape, Rank, Axis

- axis는 rank의 수의 개수만큼 존재한다고 보면됨.
- axis는 축이라고 부르며, rank가 4 이면 4개의 축이 0부터 존재.

- axis는 뒤에 나오는 tf.reduce_mean() 혹은 tf.argmax() 사용법을 보면 이해됨. 연산의 방향을 결정하는 요소임.

t = tf.constant([1,2,3,4])

print(tf.shape(t).eval())
# [4]
print(tf.rank(t).eval())
# 1
t = tf.constant([[1,2],
[3,4]])
print(tf.shape(t).eval())
# [2 2]
print(tf.rank(t).eval())
# 2
t = tf.constant([[[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],[[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]]]])

print(tf.shape(t).eval())
# [1 2 3 4]

print(tf.rank(t).eval())
# 4
[                        # axis = 0
[ # axis = 1
[ # axis = 2
[1,2,3,4], # axis = 3 or -1
[5,6,7,8],
[9,10,11,12]
],
[
[13,14,15,16],
[17,18,19,20],
[21,22,23,24]
]
]
]


# matmul(행렬곱) vs. multiply(일반곱)

- 텐서로 행렬곱셈을 수행할 때는 일반 '*'이 아닌 tf.matmul()를 사용해야한다. 그렇지 않으면 의도치 않은 결과가 나올 수 있다.

matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])

# shape(1,2) * shape(2,1) = shape = (1,1)에 대한 예상했던 결과
print(tf.matmul(matrix1, matrix2).eval())
#[[12.]]

# shape(1,2) * shape(2,1) = shape = (1,1)에 대한 의도치 않았던 결과, 뒷 행렬의 각각의 원소가 앞 행렬에 각각 곱해진 결과.
print((matrix1*matrix2).eval())
#[[6. 6.]
# [6. 6.]]


# broadcasting에서 주의할 점

- broadcasting에 대한 이해가 잘 된 상태에서 사용하면 좋긴 하지만 되도록 행렬의 크기를 맞춰준 다음에 사용하는게 좋음.

- 참고링크 : https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html

# case1) 브로드캐스팅을 사용하지 않은 경우
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2., 2.]])
print((matrix1+matrix2).eval())
# [[5. 5.]]

# case2) 뒷 값이 앞 행렬과 똑같은 크기로 맞춰주어 덧셈이 이루어짐
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant(3.)
print((matrix1+matrix2).eval())
# [[6. 6.]]

# case3) rank가 다를 경우도 행렬의 덧셈 가능
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([2., 2.])
print((matrix1+matrix2).eval())
# [[5. 5.]]

# case4) 뒷 행렬, 앞 행렬 모두에서 extend가 이뤄나 덧셈이 이루어짐
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])
print((matrix1+matrix2).eval())
#[[5. 5.]
# [5. 5.]]


# reduce_mean

- int형을 연산하면 결과 값도 int로 나오니 주의하자. 항상 숫자뒤에 '.' 을 찍어줘야 float형으로 인식하여 소수점까지 결과가 나옴.

- axis를 인자로 주어 연산의 방향을 결정할 수 있다.

- axis가 0일때 세로, axis가 1일때 가로 방향이라고 지칭하였지만 실은 가로세로가 아닌 [] 영역으로 구분한다.

x = [1, 2]

print(tf.reduce_mean(x).eval()) # 주의! int형을 연산하면 결과 값도 int로 나와서 예상과 다를 수 있다.
# 1

x = [[1., 2.],
[3., 4.]]

print(tf.reduce_mean(x).eval()) # axis를 인자로 주지 않으면 모든 원소를 더한 뒤 평균을 구한다.
# 2.5

print(tf.reduce_mean(x, axis=0).eval()) # axis 0 방향인 세로 방향으로 연산
# [2. 3.]

print(tf.reduce_mean(x, axis=1).eval()) # axis 1 방향인 가로 방향으로 연산
# [1.5, 3.5]

print(tf.reduce_mean(x, axis=-1).eval()) # 바로 위의 연산과 같음
# [1.5, 3.5]


# reduce_sum

- reduce_mean과 사용방법은 같음

x = [[1., 2.],
[3., 4.]]

print(tf.reduce_sum(x).eval()) # axis를 인자로 주지 않으면 모든 원소를 더함
# 10.0

print(tf.reduce_sum(x, axis=0).eval()) # axis 0 방향인 세로 방향으로 연산
# [4., 6.]

print(tf.reduce_sum(x, axis=1).eval()) # axis 1 방향인 가로 방향으로 연산
# [3., 7.]

print(tf.reduce_sum(x, axis=-1).eval()) # 바로 위의 연산과 같음
# [3., 7.]

print(tf.reduce_mean(tf.reduce_sum(x, axis=-1)).eval())
# 5.0


# argmax

- 비교 대상 요소중에서 큰쪽의 index를 알려줌

x = [[0, 1, 2],
[2, 1, 0]]

print(tf.argmax(x).eval()) # default로 axis가 0일 때의 결과를 줌
# [1 0 0]

print(tf.argmax(x, axis=0).eval()) # axis 0 방향인 세로 방향에서 큰 값을 가지는 index를 결과로 줌
# [1 0 0]

print(tf.argmax(x, axis=1).eval()) # axis 1 방향인 가로 방향에서 큰 값을 가지는 index를 결과로 줌
# [2 0]

print(tf.argmax(x, axis=-1).eval()) # 바로 위의 연산과 같음
# [2 0]

# reshape

t = np.array([[[0, 1, 2],
[3, 4, 5]],

[[6, 7, 8],
[9, 10, 11]]])

print(t.ndim) # rank
# 3

print(t.shape)
# (2, 2, 3)

print(tf.reshape(t, shape=[-1, 3]).eval()) # 2차원으로 변경, 보통 맨 뒤의 숫자는 원래 shape의 맨 뒤 숫자와 같음, 맨앞의 -1은 알아서 하란뜻
#[[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]

print(tf.reshape(t, shape=[-1, 1, 3]).eval()) # 두개로 나뉘어 있던 것을 합침
#[[[ 0 1 2]]
# [[ 3 4 5]]
# [[ 6 7 8]]
# [[ 9 10 11]]]


# squeeze

- squeeze는 짜내다 라는 뜻인데 [ ]를 제거해서 하나로 만들어 줌.

print(tf.squeeze([[0], [1], [2]]).eval()) # squeeze는 짜내는 뜻인데 [ ]를 제거해서 하나로 만들어줌
# [0, 1, 2]


# expand_dims

- 한 차원이 늘어남

- 뒤의 인자는 axis 값으로 추정됨.

# case1) 뒤의 인자가 0이면 shape의 맨 앞에 1이 생기면서 한차원 늘어난다.
print(tf.expand_dims([0, 1, 2], 0).eval()) # shape(3) -> (1,3)
#[[0 1 2]]

print(tf.expand_dims([[0, 1], [2, 3]], 0).eval()) # shape(2,2) -> (1,2,2)
#[[[0 1]
# [2 3]]]

# case2) 뒤의 인자가 1이상이면 shape의 가운데 그 숫자가 들어가면서 한차원 늘어난다.
print(tf.expand_dims([0, 1, 2], 1).eval()) # shape(3) -> (3,1)
# [[0]
# [1]
# [2]]

print(tf.expand_dims([[0, 1], [2, 3]], 1).eval()) # shape(2,2) -> (2, 1, 2)
#[[[0 1]]
# [[2 3]]]

print(tf.expand_dims([[0, 1], [2, 3]], 2).eval()) # shape(2,2) -> (2, 2, 2)
#[[[0]
# [1]]
# [[2]
# [3]]]


# one_hot

- 주의! one_hot으로 변경해주면 한차원 증가하므로 마지막에 reshape을 해줘야 한다.

- depth는 만들어질 행렬의 제일 안쪽 원소의 개수를 설정하는 것임. 보통, class의 개수대로 설정함

- y label을 one-hot으로 변경할 때 많이 사용됨.

print(tf.one_hot([[0], [1], [2], [0]], depth=3).eval())
#[[[1. 0. 0.]]
# [[0. 1. 0.]]
# [[0. 0. 1.]]
# [[1. 0. 0.]]]

t = tf.one_hot([[0], [1], [2], [0]], depth=3)
print(tf.reshape(t, shape=[-1, 3]).eval())
#[[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]
# [1. 0. 0.]]


# cast

- 데이터 타입을 변경해줄 때 사용

- True, False 값을 0 또는 1의 숫자로 변경해 줄때도 사용

print(tf.cast([1.8, 2.2, 3.3, 4.9], tf.int32).eval())
#[1 2 3 4]

print(tf.cast([True, False, True, True], tf.int32).eval())
#[1 0 1 0]

print(tf.cast([True, False, 1 == 1, 0 == 1], tf.int32).eval())
#[1 0 1 1]


# stack

- 말 그대로 쌓는 것임. axis로 쌓이는 방향을 설정할 수 있음.

x = [1, 4]
y = [2, 5]
z = [3, 6]

print(tf.stack([x, y, z]).eval())
#[[1 4]
# [2 5]
# [3 6]]

print(tf.stack([x, y, z], axis=0).eval())
#[[1 4]
# [2 5]
# [3 6]]

print(tf.stack([x, y, z], axis=1).eval())
#[[1 2 3]
# [4 5 6]]

print(tf.stack([x, y, z], axis=-1).eval())
#[[1 2 3]
# [4 5 6]]


# ones_like & zeros_like

- 인자로 받은 행렬과 같은 크기의 행렬을 만들어 주며 ones의 경우 1로 채워진 행렬을 반환, zeros의 경우 0으로 채워진 행렬을 반환.

x = [[0, 1, 2],
[2, 1, 0]]

print(tf.ones_like(x).eval())
#[[1 1 1]
# [1 1 1]]


print(tf.zeros_like(x).eval())
#[[0 0 0]
# [0 0 0]]


# zip

- 여러군데서 하나씩 꺼낼 때 사용하는 것으로, 각각의 리스트의 원소를 순차적으로 하나씩 뽑아서 만든 리스트를 반환해줌

print(zip([1, 2, 3], [4, 5, 6], [7, 8, 9]))
# [(1, 4, 7), (2, 5, 8), (3, 6, 9)] -> type : list

for x, y, z in zip([1, 2, 3], [4, 5, 6], [7, 8, 9]):
print(x, y, z)
#(1, 4, 7)
#(2, 5, 8)
#(3, 6, 9)


반응형
Comments