[STBDA] 11wk: MaxPool2D, Conv2D

Author

김보람

Published

July 13, 2023

해당 강의노트는 전북대학교 최규빈교수님 STBDA2022 자료임

imports

import tensorflow as tf
import tensorflow.experimental.numpy as tnp
import numpy as np
import matplotlib.pyplot as plt
tnp.experimental_enable_numpy_behavior()
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()
X = tf.constant(x_train.reshape(-1,28,28,1),dtype=tf.float64)
y = tf.keras.utils.to_categorical(y_train)
XX = tf.constant(x_test.reshape(-1,28,28,1),dtype=tf.float64)
yy = tf.keras.utils.to_categorical(y_test)

- 첫시도

net1 = tf.keras.Sequential()
net1.add(tf.keras.layers.Flatten())
net1.add(tf.keras.layers.Dense(500,activation='relu'))
net1.add(tf.keras.layers.Dense(500,activation='relu'))
net1.add(tf.keras.layers.Dense(500,activation='relu'))
net1.add(tf.keras.layers.Dense(500,activation='relu'))
net1.add(tf.keras.layers.Dense(10,activation='softmax'))
net1.compile(optimizer='adam', loss=tf.losses.categorical_crossentropy,metrics='accuracy')
net1.fit(X,y,epochs=5)
Epoch 1/5
1875/1875 [==============================] - 5s 3ms/step - loss: 1.0329 - accuracy: 0.7892
Epoch 2/5
1875/1875 [==============================] - 5s 2ms/step - loss: 0.4472 - accuracy: 0.8415
Epoch 3/5
1875/1875 [==============================] - 5s 2ms/step - loss: 0.4179 - accuracy: 0.8497
Epoch 4/5
1875/1875 [==============================] - 5s 2ms/step - loss: 0.3942 - accuracy: 0.8598
Epoch 5/5
1875/1875 [==============================] - 5s 2ms/step - loss: 0.3756 - accuracy: 0.8668
<keras.callbacks.History at 0x7f07e021dfa0>
net1.evaluate(XX,yy)
313/313 [==============================] - 0s 754us/step - loss: 0.4255 - accuracy: 0.8505
[0.4255034327507019, 0.8504999876022339]
net1.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 784)               0         
                                                                 
 dense (Dense)               (None, 500)               392500    
                                                                 
 dense_1 (Dense)             (None, 500)               250500    
                                                                 
 dense_2 (Dense)             (None, 500)               250500    
                                                                 
 dense_3 (Dense)             (None, 500)               250500    
                                                                 
 dense_4 (Dense)             (None, 10)                5010      
                                                                 
=================================================================
Total params: 1,149,010
Trainable params: 1,149,010
Non-trainable params: 0
_________________________________________________________________

- 두번째 시도

net2 = tf.keras.Sequential()
net2.add(tf.keras.layers.Conv2D(30,(2,2),activation='relu'))
net2.add(tf.keras.layers.MaxPool2D())
net2.add(tf.keras.layers.Conv2D(30,(2,2),activation='relu'))
net2.add(tf.keras.layers.MaxPool2D())
net2.add(tf.keras.layers.Flatten())
#net2.add(tf.keras.layers.Dense(500,activation='relu'))
net2.add(tf.keras.layers.Dense(10,activation='softmax'))
net2.compile(optimizer='adam', loss=tf.losses.categorical_crossentropy,metrics='accuracy')
net2.fit(X,y,epochs=5)
Epoch 1/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.7960 - accuracy: 0.8061
Epoch 2/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.3814 - accuracy: 0.8644
Epoch 3/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.3400 - accuracy: 0.8769
Epoch 4/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.3182 - accuracy: 0.8854
Epoch 5/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.3011 - accuracy: 0.8914
<keras.callbacks.History at 0x7f07b4176850>
net2.evaluate(XX,yy)
313/313 [==============================] - 0s 815us/step - loss: 0.3572 - accuracy: 0.8707
[0.3571898341178894, 0.8707000017166138]
net2.summary()
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 27, 27, 30)        150       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 13, 13, 30)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 12, 12, 30)        3630      
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 6, 6, 30)         0         
 2D)                                                             
                                                                 
 flatten_1 (Flatten)         (None, 1080)              0         
                                                                 
 dense_5 (Dense)             (None, 10)                10810     
                                                                 
=================================================================
Total params: 14,590
Trainable params: 14,590
Non-trainable params: 0
_________________________________________________________________
14590/ 1149010
0.012697887746842934
c1, m1, c2, m2, flttn, dns = net2.layers
print(X.shape) # 입력이미지 = 2D
print(c1(X).shape) #2D
print(m1(c1(X)).shape)  #2D
print(c2(m1(c1(X))).shape) #2D
print(m2(c2(m1(c1(X)))).shape) #2D
print(flttn(m2(c2(m1(c1(X))))).shape)# 1D
print(dns(flttn(m2(c2(m1(c1(X)))))).shape)# 1D
(60000, 28, 28, 1)
(60000, 27, 27, 30)
(60000, 13, 13, 30)
(60000, 12, 12, 30)
(60000, 6, 6, 30)
(60000, 1080)
(60000, 10)

MaxPool2D

테스트1

- 레이어생성

m=tf.keras.layers.MaxPool2D()

- 입력데이터

XXX = tnp.arange(1*4*4*1).reshape(1,4,4,1)
XXX.reshape(1,4,4)
<tf.Tensor: shape=(1, 4, 4), dtype=int64, numpy=
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15]]])>

흑백 이미지다 생각하고 뒤에 채널 생략하자 걍 보기쉽게

- 입력데이터가 레이어를 통과한 모습

m(XXX).reshape(1,2,2)
<tf.Tensor: shape=(1, 2, 2), dtype=int64, numpy=
array([[[ 5,  7],
        [13, 15]]])>

- MaxPool2D layer의 역할: (2,2)윈도우를 만들고 (2,2)윈도우에서 max를 뽑아 값을 기록, 윈도우를 움직이면서 반복

테스트2

XXX = tnp.arange(1*6*6*1).reshape(1,6,6,1)
XXX.reshape(1,6,6)
<tf.Tensor: shape=(1, 6, 6), dtype=int64, numpy=
array([[[ 0,  1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10, 11],
        [12, 13, 14, 15, 16, 17],
        [18, 19, 20, 21, 22, 23],
        [24, 25, 26, 27, 28, 29],
        [30, 31, 32, 33, 34, 35]]])>
m(XXX).reshape(1,3,3)
<tf.Tensor: shape=(1, 3, 3), dtype=int64, numpy=
array([[[ 7,  9, 11],
        [19, 21, 23],
        [31, 33, 35]]])>

테스트3

m=tf.keras.layers.MaxPool2D(pool_size=(3, 3))
XXX = tnp.arange(1*6*6*1).reshape(1,6,6,1)
XXX.reshape(1,6,6)
<tf.Tensor: shape=(1, 6, 6), dtype=int64, numpy=
array([[[ 0,  1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10, 11],
        [12, 13, 14, 15, 16, 17],
        [18, 19, 20, 21, 22, 23],
        [24, 25, 26, 27, 28, 29],
        [30, 31, 32, 33, 34, 35]]])>
m(XXX).reshape(1,2,2)
<tf.Tensor: shape=(1, 2, 2), dtype=int64, numpy=
array([[[14, 17],
        [32, 35]]])>

테스트4

m=tf.keras.layers.MaxPool2D(pool_size=(2, 2))
XXX = tnp.arange(1*5*5*1).reshape(1,5,5,1)
XXX.reshape(1,5,5)
<tf.Tensor: shape=(1, 5, 5), dtype=int64, numpy=
array([[[ 0,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14],
        [15, 16, 17, 18, 19],
        [20, 21, 22, 23, 24]]])>
m(XXX).reshape(1,2,2)
<tf.Tensor: shape=(1, 2, 2), dtype=int64, numpy=
array([[[ 6,  8],
        [16, 18]]])>
m=tf.keras.layers.MaxPool2D(pool_size=(2, 2),padding="same")
XXX = tnp.arange(1*5*5*1).reshape(1,5,5,1)
XXX.reshape(1,5,5)
<tf.Tensor: shape=(1, 5, 5), dtype=int64, numpy=
array([[[ 0,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14],
        [15, 16, 17, 18, 19],
        [20, 21, 22, 23, 24]]])>
m(XXX).reshape(1,3,3)
<tf.Tensor: shape=(1, 3, 3), dtype=int64, numpy=
array([[[ 6,  8,  9],
        [16, 18, 19],
        [21, 23, 24]]])>

테스트5

XXX = tnp.arange(2*4*4*1).reshape(2,4,4,1)
XXX.reshape(2,4,4)
<tf.Tensor: shape=(2, 4, 4), dtype=int64, numpy=
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15]],

       [[16, 17, 18, 19],
        [20, 21, 22, 23],
        [24, 25, 26, 27],
        [28, 29, 30, 31]]])>
m(XXX).reshape(2,2,2)
<tf.Tensor: shape=(2, 2, 2), dtype=int64, numpy=
array([[[ 5,  7],
        [13, 15]],

       [[21, 23],
        [29, 31]]])>

테스트6

XXX = tnp.arange(1*4*4*3).reshape(1,4,4,3)
XXX[...,0]
<tf.Tensor: shape=(1, 4, 4), dtype=int64, numpy=
array([[[ 0,  3,  6,  9],
        [12, 15, 18, 21],
        [24, 27, 30, 33],
        [36, 39, 42, 45]]])>
m(XXX)[...,0]
<tf.Tensor: shape=(1, 2, 2), dtype=int64, numpy=
array([[[15, 21],
        [39, 45]]])>

Conv2D

테스트1

- 레이어생성

cnv = tf.keras.layers.Conv2D(1,(2,2))

- XXX생성

XXX = tnp.arange(1*4*4*1,dtype=tf.float64).reshape(1,4,4,1)
XXX.reshape(1,4,4)
<tf.Tensor: shape=(1, 4, 4), dtype=float64, numpy=
array([[[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.]]])>

dytpe이 int형이면 에러가 나므로 float형으로 바꿔주기

cnv(XXX).reshape(1,3,3)
<tf.Tensor: shape=(1, 3, 3), dtype=float32, numpy=
array([[[3.1202679, 3.3676739, 3.61508  ],
        [4.109892 , 4.3572974, 4.6047034],
        [5.099516 , 5.346921 , 5.594327 ]]], dtype=float32)>
  • XXX에서 cnv(XXX)로 가는 맵핑을 찾는건 쉽지 않아보인다.
  • 심지어 랜덤으로 결정되는 부분도 있어보임 \(\to\) 계속 값이 바뀜

- 코드정리 + 시드통일

tf.random.set_seed(43052)
cnv = tf.keras.layers.Conv2D(1,(2,2))
XXX = tnp.arange(1*4*4*1,dtype=tf.float64).reshape(1,4,4,1)

- conv의 입출력

print(XXX.reshape(1,4,4))
print(cnv(XXX).reshape(1,3,3))
tf.Tensor(
[[[ 0.  1.  2.  3.]
  [ 4.  5.  6.  7.]
  [ 8.  9. 10. 11.]
  [12. 13. 14. 15.]]], shape=(1, 4, 4), dtype=float64)
tf.Tensor(
[[[ -5.372491  -6.5789    -7.785309]
  [-10.198126 -11.404535 -12.610944]
  [-15.023762 -16.230171 -17.436579]]], shape=(1, 3, 3), dtype=float32)

- conv연산 추론

type(cnv.weights)
list
tf.reshape(cnv.weights[0],(2,2))
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[-0.2408014 ,  0.3118649 ],
       [-0.70300657, -0.5744659 ]], dtype=float32)>
0 * -0.2408014 + 1 * 0.3118649 + 4 * -0.70300657 + 5 * -0.5744659 + 0
-5.372490879999999

- 내가 정의한 weights를 대입하여 conv 연산 확인

cnv.get_weights()[0].shape
(2, 2, 1, 1)
w = np.array([1/4,1/4,1/4,1/4],dtype=np.float32).reshape(2, 2, 1, 1)
b = np.array([3],dtype=np.float32)
cnv.set_weights([w,b])
XXX.reshape(1,4,4)
<tf.Tensor: shape=(1, 4, 4), dtype=float64, numpy=
array([[[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.]]])>
cnv(XXX).reshape(1,3,3)
<tf.Tensor: shape=(1, 3, 3), dtype=float32, numpy=
array([[[ 5.5,  6.5,  7.5],
        [ 9.5, 10.5, 11.5],
        [13.5, 14.5, 15.5]]], dtype=float32)>
np.mean([0,1,4,5])+3, np.mean([1,2,5,6])+3, np.mean([2,3,6,7])+3
(5.5, 6.5, 7.5)

tf.keras.layers.Conv2D(1,kernel_size=(2,2)) 요약

- 요약

  1. size=(2,2)인 윈도우를 만듬.

  2. XXX에 윈도우를 통과시켜서 (2,2)크기의 sub XXX 를 얻음. sub XXX의 각 원소에 conv2d.weights[0]의 각 원소를 element-wise하게 곱한다.

  3. (2)의 결과를 모두 더한다. 그리고 그 결과에 다시 conv2d.weights[1]을 수행

  4. 윈도우를 이동시키면서 반복!

테스트2

- 레이어와 XXX생성

tf.random.set_seed(43052)
cnv = tf.keras.layers.Conv2D(1,(3,3))
XXX = tnp.arange(1*5*5*1,dtype=tf.float64).reshape(1,5,5,1)
XXX.reshape(1,5,5) ## 입력: XXX
<tf.Tensor: shape=(1, 5, 5), dtype=float64, numpy=
array([[[ 0.,  1.,  2.,  3.,  4.],
        [ 5.,  6.,  7.,  8.,  9.],
        [10., 11., 12., 13., 14.],
        [15., 16., 17., 18., 19.],
        [20., 21., 22., 23., 24.]]])>
cnv(XXX).reshape(1,3,3) ## 출력: conv(XXX)
<tf.Tensor: shape=(1, 3, 3), dtype=float32, numpy=
array([[[-3.3962166, -3.7455084, -4.0948005],
        [-5.1426764, -5.4919686, -5.84126  ],
        [-6.889136 , -7.238429 , -7.5877204]]], dtype=float32)>
tf.reshape(cnv.weights[0],(1,3,3)) ## 커널의 가중치
<tf.Tensor: shape=(1, 3, 3), dtype=float32, numpy=
array([[[ 0.10026586, -0.17371601,  0.16699821],
        [-0.4737161 ,  0.19852251,  0.09610051],
        [ 0.09313911, -0.29948625, -0.05739981]]], dtype=float32)>
tf.reduce_sum(XXX.reshape(1,5,5)[0,:3,:3] * tf.reshape(cnv.weights[0],(3,3)))
<tf.Tensor: shape=(), dtype=float64, numpy=-3.396216869354248>

테스트3

XXX = tf.constant([[3,3,2,1,0],[0,0,1,3,1],[3,1,2,2,3],[2,0,0,2,2],[2,0,0,0,1]],dtype=tf.float64).reshape(1,5,5,1)
XXX.reshape(1,5,5)
<tf.Tensor: shape=(1, 5, 5), dtype=float64, numpy=
array([[[3., 3., 2., 1., 0.],
        [0., 0., 1., 3., 1.],
        [3., 1., 2., 2., 3.],
        [2., 0., 0., 2., 2.],
        [2., 0., 0., 0., 1.]]])>
cnv = tf.keras.layers.Conv2D(1,(3,3))
cnv.weights
[]

처음엔 weights가 없다가

cnv(XXX).reshape(1,3,3)
<tf.Tensor: shape=(1, 3, 3), dtype=float32, numpy=
array([[[-0.34136963, -0.7676697 ,  2.3211648 ],
        [ 1.1140423 , -0.15018106, -0.2899468 ],
        [ 0.866542  , -2.0289044 , -1.186438  ]]], dtype=float32)>
cnv.weights[0]
<tf.Variable 'conv2d_8/kernel:0' shape=(3, 3, 1, 1) dtype=float32, numpy=
array([[[[ 0.16768384]],

        [[-0.33952343]],

        [[-0.37316394]]],


       [[[ 0.5341035 ]],

        [[ 0.27497447]],

        [[-0.38560677]]],


       [[[ 0.19056737]],

        [[-0.26161364]],

        [[ 0.49799764]]]], dtype=float32)>

XXX가 cnv를 통과하면서 weights가 생긴다.

_w = tf.constant([[0,1,2],[2,2,0],[0,1,2]],dtype=tf.float64).reshape(3,3,1,1)
_b = tf.constant([0],dtype=tf.float64)
cnv.set_weights([_w,_b])
cnv(XXX).reshape(1,3,3)
<tf.Tensor: shape=(1, 3, 3), dtype=float32, numpy=
array([[[12., 12., 17.],
        [10., 17., 19.],
        [ 9.,  6., 14.]]], dtype=float32)>

테스트4

tf.random.set_seed(43052)
cnv = tf.keras.layers.Conv2D(1,(2,2))
XXX = tnp.arange(2*5*5*1,dtype=tf.float64).reshape(2,5,5,1)
print(XXX.reshape(2,5,5))
cnv(XXX) # weights를 초기화 시키기 위해서 레이어를 1회 통과
cnv.set_weights([w,b])
print(cnv(XXX).reshape(2,4,4))
tf.Tensor(
[[[ 0.  1.  2.  3.  4.]
  [ 5.  6.  7.  8.  9.]
  [10. 11. 12. 13. 14.]
  [15. 16. 17. 18. 19.]
  [20. 21. 22. 23. 24.]]

 [[25. 26. 27. 28. 29.]
  [30. 31. 32. 33. 34.]
  [35. 36. 37. 38. 39.]
  [40. 41. 42. 43. 44.]
  [45. 46. 47. 48. 49.]]], shape=(2, 5, 5), dtype=float64)
tf.Tensor(
[[[ 6.  7.  8.  9.]
  [11. 12. 13. 14.]
  [16. 17. 18. 19.]
  [21. 22. 23. 24.]]

 [[31. 32. 33. 34.]
  [36. 37. 38. 39.]
  [41. 42. 43. 44.]
  [46. 47. 48. 49.]]], shape=(2, 4, 4), dtype=float32)
np.mean([0,1,5,6])+3,np.mean([25,26,30,31])+3,
(6.0, 31.0)

테스트5

-

tf.random.set_seed(43052)
cnv = tf.keras.layers.Conv2D(4,(2,2),activation='relu')
XXX = tnp.arange(1*2*2*1,dtype=tf.float64).reshape(1,2,2,1)
print(XXX.reshape(1,2,2))
tf.Tensor(
[[[0. 1.]
  [2. 3.]]], shape=(1, 2, 2), dtype=float64)
cnv(XXX)
<tf.Tensor: shape=(1, 1, 1, 4), dtype=float32, numpy=
array([[[[1.7573347 , 0.84183925, 1.7484205 , 0.        ]]]],
      dtype=float32)>
cnv.weights[0] # (2,2) 커널의 크기 // 1은 XXX의 채널수 // 4는 conv(XXX)의 채널수
<tf.Variable 'conv2d_10/kernel:0' shape=(2, 2, 1, 4) dtype=float32, numpy=
array([[[[-0.00448787,  0.17636931,  0.17748284,  0.5281223 ]],

        [[-0.01255804,  0.0121181 , -0.1567888 ,  0.513507  ]]],


       [[[ 0.3668834 ,  0.32297856,  0.32239443, -0.5187031 ]],

        [[ 0.3453753 ,  0.06125468,  0.42014015, -0.36382926]]]],
      dtype=float32)>
cnv.weights[0][...,0].reshape(2,2) ## conv(XXX)의 첫번째채널 출력을 얻기 위해 곱해지는 w
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[-0.08230966, -0.36398047],
       [ 0.19759327,  0.33916563]], dtype=float32)>
tf.reduce_sum(XXX.reshape(1,2,2) * cnv.weights[0][...,0].reshape(2,2)) ### conv(XXX)의 첫번째 채널 출력결과
<tf.Tensor: shape=(), dtype=float64, numpy=1.0487029552459717>

- 계산결과를 확인하기 쉽게 하기 위한 약간의 트릭

tf.random.set_seed(43052)
cnv = tf.keras.layers.Conv2D(4,(2,2))
XXX = tnp.array([1]*1*2*2*1,dtype=tf.float64).reshape(1,2,2,1)
print(XXX.reshape(1,2,2))
tf.Tensor(
[[[1. 1.]
  [1. 1.]]], shape=(1, 2, 2), dtype=float64)
  • 이렇게 XXX를 설정하면 cnv(XXX)의 결과는 단지 cnv의 weight들의 sum이 된다.
cnv(XXX)
<tf.Tensor: shape=(1, 1, 1, 4), dtype=float32, numpy=
array([[[[ 0.09046876, -0.6207629 , -0.25241536, -0.7710641 ]]]],
      dtype=float32)>
cnv.weights[0] # (2,2) 커널의 크기 // 1은 XXX의 채널수 // 4는 conv(XXX)의 채널수
<tf.Variable 'conv2d_24/kernel:0' shape=(2, 2, 1, 4) dtype=float32, numpy=
array([[[[-0.08230966, -0.15132892, -0.12760344, -0.38952267]],

        [[-0.36398047,  0.07347518, -0.08780673,  0.46633136]]],


       [[[ 0.19759327, -0.46042526, -0.15406173, -0.34838456]],

        [[ 0.33916563, -0.08248386,  0.11705655, -0.49948823]]]],
      dtype=float32)>
cnv.weights[0][...,0].reshape(2,2) ## conv(XXX)의 첫번째채널 출력을 얻기 위해 곱해지는 w
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[-0.08230966, -0.36398047],
       [ 0.19759327,  0.33916563]], dtype=float32)>
tf.reduce_sum(cnv.weights[0][...,0])
#tf.reduce_sum(XXX.reshape(1,2,2) * cnv.weights[0][...,0].reshape(2,2)) ### conv(XXX)의 첫번째 채널 출력결과
<tf.Tensor: shape=(), dtype=float32, numpy=0.090468764>

테스트6

- 결과확인을 쉽게하기 위해서 XXX를 1로 통일

tf.random.set_seed(43052)
cnv = tf.keras.layers.Conv2D(4,(2,2))
XXX = tnp.array([1]*1*2*2*3,dtype=tf.float64).reshape(1,2,2,3)
cnv(XXX)
<tf.Tensor: shape=(1, 1, 1, 4), dtype=float32, numpy=
array([[[[ 0.3297621, -0.4498347, -1.0487393, -1.580095 ]]]],
      dtype=float32)>
cnv.weights[0] ## (2,2)는 커널의 사이즈 // 3은 XXX의채널 // 4는 cnv(XXX)의 채널
<tf.Variable 'conv2d_33/kernel:0' shape=(2, 2, 3, 4) dtype=float32, numpy=
array([[[[-0.06956434, -0.12789628, -0.10784459, -0.32920673],
         [-0.30761963,  0.06209785, -0.07421023,  0.3941219 ],
         [ 0.16699678, -0.38913035, -0.13020593, -0.29443866]],

        [[ 0.28664726, -0.0697116 ,  0.09893084, -0.4221446 ],
         [-0.23161241, -0.16410837, -0.36420006,  0.12424195],
         [-0.14245945,  0.36286396, -0.10751781,  0.1733647 ]]],


       [[[ 0.02764335,  0.15547717, -0.42024496, -0.31893867],
         [ 0.22414821,  0.3619454 , -0.00282967, -0.3503708 ],
         [ 0.4610079 , -0.17417148,  0.00401336, -0.29777044]],

        [[-0.1620284 , -0.42066965, -0.01578814, -0.4240524 ],
         [ 0.37925082,  0.24236053,  0.3949356 , -0.20996472],
         [-0.30264795, -0.28889188, -0.3237777 ,  0.37506342]]]],
      dtype=float32)>
cnv.weights[0][...,0] ## cnv(XXX)의 첫번째 채널결과를 얻기 위해서 사용하는 w
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[-0.06956434, -0.30761963,  0.16699678],
        [ 0.28664726, -0.23161241, -0.14245945]],

       [[ 0.02764335,  0.22414821,  0.4610079 ],
        [-0.1620284 ,  0.37925082, -0.30264795]]], dtype=float32)>
tf.reduce_sum(cnv.weights[0][...,0]) ### cnv(XXX)의 첫번째 채널의 결과
<tf.Tensor: shape=(), dtype=float32, numpy=0.32976213>
print(tf.reduce_sum(cnv.weights[0][...,0]))
print(tf.reduce_sum(cnv.weights[0][...,1]))
print(tf.reduce_sum(cnv.weights[0][...,2]))
print(tf.reduce_sum(cnv.weights[0][...,3])) ### cnv(XXX)의 결과
tf.Tensor(0.32976213, shape=(), dtype=float32)
tf.Tensor(-0.44983464, shape=(), dtype=float32)
tf.Tensor(-1.0487392, shape=(), dtype=float32)
tf.Tensor(-1.5800952, shape=(), dtype=float32)
w_red = cnv.weights[0][...,0][...,0]
w_green = cnv.weights[0][...,0][...,1]
w_blue = cnv.weights[0][...,0][...,2]
tf.reduce_sum(XXX[...,0] * w_red + XXX[...,1] * w_green + XXX[...,2] * w_blue) ## cnv(XXX)의 첫채널 출력결과
<tf.Tensor: shape=(), dtype=float64, numpy=0.32976213097572327>

hw

아래와 같은 흑백이미지가 있다고 하자.

0 0 0 1 1 1
0 0 0 1 1 1
0 0 0 1 1 1
0 0 0 1 1 1
0 0 0 1 1 1
0 0 0 1 1 1

위의 이미지에 아래와 같은 weight를 가진 필터를 적용하여 convolution한 결과를 계산하라. (bias는 0으로 가정한다)

-1 1
-1 1