- 연산 결과를 그림으로 표현하다보면, 표현 가능한 범위를 넘어설 때가 있습니다.
- 이럴 때 어쩔 수 없이 표현 가능한 범위로 우겨넣어 표현하지만, 오해를 유발합니다.
- 최대한 오해를 방지하려는 노력으로 이해해 주시면 좋겠습니다.
1. 지난 글에 이어서
지난 글에 이런 부분이 있습니다.

상황을 다시 설명하면 이렇습니다.
- 데이터의 범위가 0
1에서 -22 정도로 바뀝니다. - 이미지로는 0~1 밖을 표현할 수 없습니다.
- 그래서 min-max scaling으로 0~1로 강제합니다.
- 결과적으로 채도가 낮아집니다.
- 데이터의 범위가 0
결과만 보면 BN은 데이터 값의 범위를 좁히는 것이라고 오해하기 너무 좋습니다.
아쉬운 마음에 밀도함수도 같이 그려봤지만 x 범위가 달라 오해를 키웁니다.
이번 글에서는 앞 글에서 유발한 오해를 주워담아보겠습니다.
2. 시각화 설계
matplotlib tutorial: Customizing Figure Layouts Using Gridspec and Other Functions
위 그림은 사실 matplotlib으로 저장한 파일들을 파워포인트에서 조합한 것입니다.
파워포인트의 효율성이 좋아서 종종 쓰는 방법이지만 제대로 다시 그려봅니다.

matplotlib으로 이런 그림을 그릴 때는 gridspec이 좋습니다.
- 합쳐질 그림들을 포함해서 3행 6열의 자리를 만듭니다.
- 밀도함수가 들어올 자리 6개는 자리 하나당 subplot 하나를 만듭니다.
- 이미지가 들어올 자리 3개는 자리 4개당 subplot 하나를 만듭니다.
코드로 표현하면 이렇습니다.
1 | import tensorflow as tf |

3. batch normalization 수행
- 자리가 생겼으니, 이제 자리를 채울 그림들을 다듬어봅니다.
- 같은 그림들을 읽어서 batch normalization을 수행합니다.
- 이전 글과 정확히 동일한 코드입니다.
1 | from tensorflow.keras.models import Sequential |
4. 데이터 분포 시각화
- 시각화 코드를 준비합니다.
- 지난번엔 밀도함수와 이미지를 따로 그렸지만 이번엔 한번에 출력되도록 하겠습니다.
- axes도 함께 출력해서 출력된 그림을 수정하기 좋게 합니다.
1 | def plot(imgs, xlim=None, suptitle=None): |
4.1. 입력 데이터
- 입력 이미지와 채널별 데이터 분포를 그려봅니다.
- batch normalization 결과와 비교하기 위해 밀도함수 범위를 넓게 잡습니다.
1 | fig, axes = plot(imgs, 3.5, "original images") |

- 데이터를 255로 나누어 넣었기 때문에 데이터가 0~1 사이에 분포하고 있습니다.
- matplotlib은 이미지 데이터로 int를 받으면 0
255를 기준으로 그림을 표시하고, float를 받으면 01 기준으로 표시합니다. - x 범위를 넓게 잡았기 때문에 분포 범위가 매우 좁게 그려졌습니다.
4.2. Batch Normalization 데이터
- batch normalization 결과를 그려봅니다.
- 0~1 외의 범위는 그림으로 표현되지 않기 때문에, 밀도함수를 살짝 가려서 그림으로 안보이는 부분이라는 표시를 했습니다.
1 | fig, axes = plot(bn1_numpy, 3.5, "batch normalized images") |


- 일부 데이터가 표현되지 않는다는 경고 메시지가 뜹니다.
- 이전 글과는 다르게 눈이 쨍한 그림이 나옵니다.
min-max scaling을 하지 않았기 때문입니다. - 밀도함수의 범위가 확 넓어진 것이 눈에 뜨입니다.
- 이미지에 시커먼 부분과 새하얀 부분이 있는데, 시각화 범위를 벗어난 곳입니다.
4.3. min-max scaling
- 이미지에서 놓치는 부분 없이 표현하기 위해 min-max scaling을 합니다.
- 여기서 얻은 밀도함수는 원본과 정성비교만 가능합니다.
1 | bn_min, bn_max = bn1_numpy.min(), bn1_numpy.max() |

- 이미지의 채도가 떨어졌습니다.
- min-max scaling 전에는 범위가 넓어 드러나지 않았지만, 데이터 분포가 중앙으로 몰리면서 R, G, B 값들이 비슷해져 회색조에 가까워진 것으로 추정됩니다.
4.4. 밀도함수 정성비교
- 원본과 batch normalization을 거친 이미지를 나란히 놓고 비교합니다.
- 앞에서 만든 시각화 함수로 커버되지 않는 범위이므로 함수를 새로 만듭니다.
- 밀도함수만 그리면 어떤 이미지에서 나왔는지 알 수 없기 때문에, 이미지를 작게 삽입합니다.
1 | from matplotlib import patches |

데이터의 존재 범위가 채널별로 독립적으로 변화되었습니다.
다른 이미지의 데이터 분포에 의해 서로 영향을 받았습니다.
예를 들어, 맨 오른쪽 이미지에서 B 채널과 G 채널이 가운데로 심하게 쏠렸는데 앞 두 이미지에서 두 색상이 쌍봉을 그리며 양 극단에 위치한 영향으로 보입니다.
seaborn의 kdeplot은
hue인자 외에는 범례를 생성해주지 않습니다.객체를 받아
ax.legend()명령으로 넣으려고 하면Legend does not support <AxesSubplot:ylabel='Density'> instances.경고가 발생합니다.부족한 기능을 수동으로 메웠습니다: 네모 상자와 텍스트를 넣어 보완했습니다.
만족스러운 그림을 얻었습니다.
최종 그림에 이 분포들을 삽입하기로 하고, 이제 이미지로 넘어갑니다.
5. 이미지 시각화
- 원본 이미지 하나를 그려봅니다.
1 | plt.imshow(imgs[1]) |

- 원본 이미지에서 왼쪽 반을, batch normalization된 이미지에서 오른쪽 반을 가져옵니다.
- 의도적으로 min-max scaling되지 않은 이미지를 사용하겠습니다.
- 이미지의 왼쪽은 컬러 이미지 기준 3D numpy array의 두 번째 차원이 절반 이하라는 의미입니다.
- 마찬가지로 이미지의 오른쪽은 두 번째 차원이 절반 이상이라는 의미입니다.
1 | img_half = img_shape[0]//2 # 이미지를 자를 좌표. |

- 이 둘을 합치겠습니다.
- numpy array를 붙이는 명령은
numpy.concatenate()입니다. - 경계선에 점선도 하나 그어줍니다.
1 | img1_c = np.concatenate((img1_org, img1_bn), axis=1) |

6. 집대성
- 위에서 그린 부분 기술들을 한데 모았습니다.
1 | fig = plt.figure(figsize=(12, 6)) |
