3. Tensor Operations
t1=tf.constant([1,2,3])
t2=tf.constant([10,20,30])
print(t1+t2)
tf.Tensor([11 22 33], shape=(3,), dtype=int32)
tensor의 constant 변수끼리 더하면 결과로 원소들의 합을 출력한다.(Elementwise)
py_list1=[1,2,3]
py_list2=[10,20,30]
print(py_list1+py_list2)
[1, 2, 3, 10, 20, 30]
반면 파이썬(numpy)의 리스트 변수끼리 덧셈 연산을 하면 리스트 두개가 concat된다.
원소끼리 합을 하고싶다면 for문을 이용해서 원소 하나하나 직접 덧셈해주어야 한다.
print(t1-t2)
print(t1*t2)
print(t1/t2)
print(t1%t2)
print(t1//t2)
tf.Tensor([ -9 -18 -27], shape=(3,), dtype=int32)
tf.Tensor([10 40 90], shape=(3,), dtype=int32)
tf.Tensor([0.1 0.1 0.1], shape=(3,), dtype=float64)
tf.Tensor([1 2 3], shape=(3,), dtype=int32)
tf.Tensor([0 0 0], shape=(3,), dtype=int32)
모든 연산이 Elementwise하게 됨을 알 수 있다.
t1=tf.random.normal(shape=(3,4),mean=0,stddev=5)
t2=tf.random.normal(shape=(3,4),mean=0,stddev=5)
t1=tf.cast(t1,dtype=tf.int16)
t2=tf.cast(t2,dtype=tf.int16)
print(t1.numpy())
print(t2.numpy())
print(t1+t2)
[[ 2 3 0 6]
[ 2 3 -5 -7]
[ 0 -1 0 6]]
[[ 2 3 -2 -3]
[ 1 -1 -2 4]
[-1 0 -1 0]]
tf.Tensor(
[[ 4 6 -2 3]
[ 3 2 -7 -3]
[-1 -1 -1 6]], shape=(3, 4), dtype=int16)
크기(3,4)이고 평균 0, 표준편차가 5인 행렬 두개를 만들고 덧셈 연산을 해주면 위와 같이 Elementwise하게 연산되었다. print에서 numpy를 사용한 이유는 행렬을 좀더 깔끔하게 보여주기 위해 변환한 것이다.
t1=tf.random.normal(shape=(3,4),mean=0,stddev=5)
t2=tf.random.normal(shape=(1,4),mean=0,stddev=5)
t1=tf.cast(t1,dtype=tf.int16)
t2=tf.cast(t2,dtype=tf.int16)
t3=t1+t2
print(t1.numpy(),'\n')
print(t2.numpy(),'\n')
print(t3.numpy(),'\n')
[[-7 0 5 -5]
[-6 -4 1 -2]
[ 0 0 -6 -4]]
[[ 2 1 -8 2]]
[[ -5 1 -3 -3]
[ -4 -3 -7 0]
[ 2 1 -14 -2]]
위 연산은 조금 특이한 점이 있다. t1은 크기가 (3,4)이고 t2는 (1,4)이다. 그런데 덧셈 연산 결과를 보면 크기가 (3,4)이다.
이는 (1,4)행렬을 (3,4)행렬에 맞게 Expansion되어 각 행에 t2가 더해졌다고 생각하면 된다. (Broadcasting)
이를 잘 활용하면 for문 하나를 줄일 수 있는 유용한 방법이다.
tf.cast()는 형변환이다.
t1=tf.random.normal(shape=(3,4),mean=0,stddev=5)
t2=tf.random.normal(shape=(3,1),mean=0,stddev=5)
t1=tf.cast(t1,dtype=tf.int16)
t2=tf.cast(t2,dtype=tf.int16)
t3=t1+t2
print(t1.numpy(),'\n')
print(t2.numpy(),'\n')
print(t3.numpy(),'\n')
[[ 4 10 -8 -8]
[ 4 3 6 1]
[ 4 4 6 -4]]
[[1]
[0]
[0]]
[[ 5 11 -7 -7]
[ 4 3 6 1]
[ 4 4 6 -4]]
열을 기준으로 든 예시도 마찬가지이다.
t1=tf.random.normal(shape=(3,1),mean=0,stddev=5)
t2=tf.random.normal(shape=(1,4),mean=0,stddev=5)
t1=tf.cast(t1,dtype=tf.int16)
t2=tf.cast(t2,dtype=tf.int16)
t3=t1+t2
print(t1.numpy(),'\n')
print(t2.numpy(),'\n')
print(t3.numpy(),'\n')
[[ 1]
[-4]
[ 6]]
[[-4 3 -7 -2]]
[[ -3 4 -6 -1]
[ -8 -1 -11 -6]
[ 2 9 -1 4]]
행,열 모두 Expansion 될 수 있다.
t1=tf.random.normal(shape=(3,4),mean=0,stddev=5)
t1=tf.cast(t1,dtype=tf.int16)
print(t1.numpy(),'\n')
t2=tf.reduce_sum(t1)
print(t2.numpy(),'\n')
t2=tf.reduce_sum(t1,axis=0)
print(t2.numpy(),'\n')
[[-7 -2 2 3]
[10 -3 -5 -3]
[ 0 10 -1 5]]
9
[ 3 5 -4 5]
reduce_sum은 모든 원소의 합이다.
각 행끼리, 각 열끼리 더하고 싶다면 axis를 사용하면 된다.
보통 행을 axis=0, 열을 axis=1로 칭하기 때문에 각 행의 합 옵션은 axis=0, 각 열의 합은 axis=1이라고 생각할 수 있으니 그 반대이다. 즉 axis=0일 땐 각 열의 합이고 axis=1일 땐 각 행의 합이 된다.
axis=0일 땐 행을 없애고 합을 구해라 라고 생각하면 좀 더 이해하기 쉽다.
t1=tf.random.normal(shape=(128,128,3),mean=0,stddev=5)
t1=tf.cast(t1,dtype=tf.int16)
t2=tf.reduce_sum(t1,axis=2)
print(t2.shape,'\n')
(128, 128)
이미지 데이터로 생각해보면 우리가 Channelwise로 연산을 하고 싶다면 axis=2(channel을 제외한 합을 구하라)라고 설정하면 된다.
tf.reduce_sum
tf.reduce_prod
tf.reduce_max
tf.reduce_min
tf.reduce_mean
tf.reduce_std
tf.reduce_variance
tf.reduce_all
tf.reduce_any
연산만 다를 뿐 위 개념은 모든 연산에서 동일하다.