Main Content

컨벌루션 신경망의 활성화 시각화하기

이 예제에서는 컨벌루션 신경망에 영상을 입력하고 신경망의 여러 계층의 활성화 결과를 표시하는 방법을 보여줍니다. 활성화 결과를 살펴보고 원본 영상과 활성화 영역을 비교해 보면 신경망이 어떤 특징을 학습했는지 알아낼 수 있습니다. 앞쪽 계층의 채널은 색이나 경계 같은 단순한 특징을 학습하는 반면 더 깊은 계층의 채널은 눈과 같은 복잡한 특징을 학습한다는 것을 확인해 보겠습니다. 이런 식으로 특징을 식별하면 신경망이 무엇을 학습했는지 이해하는 데 도움이 됩니다.

이 예제를 실행하려면 Deep Learning Toolbox™와 Image Processing Toolbox™가 필요합니다.

사전 훈련된 신경망과 데이터 불러오기

사전 훈련된 SqueezeNet 신경망을 불러옵니다.

net = squeezenet;

영상을 읽어 들이고 표시합니다. 나중에 사용할 수 있도록 크기를 저장합니다.

im = imread('face.jpg');
imshow(im)

imgSize = size(im);
imgSize = imgSize(1:2);

신경망 아키텍처 보기

신경망을 분석하여 어느 계층을 볼 수 있는지 살펴봅니다. 컨벌루션 계층은 학습 가능한 파라미터를 사용하여 컨벌루션을 수행합니다. 신경망은 종종 한 채널마다 한 개의 유용한 특징을 식별하도록 학습합니다. 첫 번째 컨벌루션 계층에는 64개의 채널이 있는 것을 확인합니다.

analyzeNetwork(net)

영상 입력 계층은 입력 크기를 지정합니다. 영상을 신경망에 통과시키기 전에 크기를 조정할 수 있으나, 이 신경망은 더 큰 영상도 처리할 수 있습니다. 신경망에 크기가 큰 영상을 입력하면 활성화도 커집니다. 그러나 이 신경망은 227×227 크기의 영상에 대해 훈련했으므로 이보다 큰 사물이나 특징을 인식하도록 훈련되지 않았습니다.

첫 번째 컨벌루션 계층의 활성화 표시하기

영상에서 컨벌루션 계층의 어느 영역이 활성화되는지 관찰하고 원본 영상의 대응하는 영역과 비교하여 특징들을 살펴봅니다. 컨벌루션 신경망의 각 계층은 여러 개의 2차원 배열로 이루어져 있습니다. 이러한 2차원 배열을 채널이라고 합니다. 영상을 신경망에 통과시킨 후 conv1 계층의 출력 활성화 결과를 살펴봅니다.

act1 = activations(net,im,'conv1');

활성화 값은 3차원 배열로 반환됩니다. 이 배열의 세 번째 차원은 conv1 계층의 채널을 참조합니다. imtile 함수를 사용하여 활성화 값을 표시하기 위해 배열을 4차원으로 형태 변경합니다. imtile에 대한 입력값의 세 번째 차원은 영상 색을 나타냅니다. 이 활성화 결과에는 색이 없으므로 세 번째 차원이 크기 1을 갖도록 설정합니다. 네 번째 차원은 채널을 참조합니다.

sz = size(act1);
act1 = reshape(act1,[sz(1) sz(2) 1 sz(3)]);

이제 활성화 결과를 표시할 수 있습니다. 각 활성화 값은 임의의 값을 가질 수 있으므로 mat2gray를 사용하여 출력값을 정규화합니다. 최소 활성화 값은 0, 최대 활성화 값은 1이 되도록 모든 활성화 값이 스케일링됩니다. 64개의 영상을 8×8 그리드에 표시합니다. 한 그리드마다 계층의 각 채널 하나씩입니다.

I = imtile(mat2gray(act1),'GridSize',[8 8]);
imshow(I)

특정 채널의 활성화 조사하기

활성화 그리드의 타일 각각은 conv1 계층의 각 채널의 출력값입니다. 흰색 픽셀은 강한 양의 활성화 반응을, 검은색 픽셀은 강한 음의 활성화 반응을 나타냅니다. 회색이 대부분인 채널은 입력 영상에서 그만큼 강한 활성화 반응이 나타나지 않은 채널입니다. 채널에서 활성화된 픽셀의 위치는 원본 영상의 동일한 위치에 대응됩니다. 한 채널에서 어떤 위치에 흰 픽셀이 있으면 이 채널의 해당 위치가 강하게 활성화되었음을 나타냅니다.

채널 22의 활성화 값이 원본 영상과 같은 크기를 갖도록 크기를 조정하고 표시합니다.

act1ch22 = act1(:,:,:,22);
act1ch22 = mat2gray(act1ch22);
act1ch22 = imresize(act1ch22,imgSize);

I = imtile({im,act1ch22});
imshow(I)

채널에서 더 밝은 흰색 픽셀이 원본 영상에서 빨간색 영역에 대응되므로 이 채널은 빨간색 픽셀에서 활성화된다는 것을 확인할 수 있습니다.

활성화가 가장 강한 채널 찾기

활성화가 강한 채널을 프로그래밍 방식으로 조사하여 흥미로운 채널을 찾아볼 수도 있습니다. max 함수를 사용하여 활성화가 가장 큰 채널을 찾은 다음 그 활성화 채널의 크기를 조정하고 표시합니다.

[maxValue,maxValueIndex] = max(max(max(act1)));
act1chMax = act1(:,:,:,maxValueIndex);
act1chMax = mat2gray(act1chMax);
act1chMax = imresize(act1chMax,imgSize);

I = imtile({im,act1chMax});
imshow(I)

원본 영상과 비교하면 이 채널이 경계에서 활성화되는 것을 알 수 있습니다. 이 채널은 왼쪽이 밝고 오른쪽이 어두운 경계를 양으로 활성화하고 왼쪽이 어둡고 오른쪽이 밝은 경계를 음으로 활성화합니다.

심층 계층 조사하기

대부분의 컨벌루션 신경망은 첫 번째 컨벌루션 계층에서 색이나 경계 같은 특징을 검출하도록 학습합니다. 심층 컨벌루션 계층에서는 더 복잡한 특징을 검출하도록 학습합니다. 뒤에 오는 계층은 이전 계층의 특징들을 조합하여 특징을 구축합니다. conv1 계층을 조사한 것과 같은 방식으로 fire6-squeeze1x1 계층을 조사합니다. 활성화 값을 계산하고, 크기를 조정하고, 그리드에 표시합니다.

act6 = activations(net,im,'fire6-squeeze1x1');
sz = size(act6);
act6 = reshape(act6,[sz(1) sz(2) 1 sz(3)]);

I = imtile(imresize(mat2gray(act6),[64 64]),'GridSize',[6 8]);
imshow(I)

상세히 조사하기에는 영상이 너무 많으므로 흥미로운 몇 가지 영상만 살펴봅니다. fire6-squeeze1x1 계층에서 활성화가 가장 강한 채널을 표시합니다.

[maxValue6,maxValueIndex6] = max(max(max(act6)));
act6chMax = act6(:,:,:,maxValueIndex6);
imshow(imresize(mat2gray(act6chMax),imgSize))

여기서는 가장 강한 활성화 채널이 다른 채널에 비해 흥미로운 세부 특징을 보이지 않으며, 양의 활성화(밝은 부분)뿐 아니라 음의 활성화(어두운 부분) 반응도 강하게 나타나고 있습니다. 이 채널은 얼굴에 중점을 두는 채널일 가능성이 큽니다.

그리드에 표시된 모든 채널 중 눈에서 활성화하는 채널이 있을 수 있습니다. 채널 14와 47을 상세히 조사해 보겠습니다.

I = imtile(imresize(mat2gray(act6(:,:,:,[14 47])),imgSize));
imshow(I)

많은 채널이 밝은 활성화 영역과 어두운 활성화 영역을 모두 가지고 있습니다. 이들 영역은 각각 양의 활성화와 음의 활성화를 나타냅니다. 그러나 fire6-squeeze1x1 계층 뒤에 ReLU(Rectified Linear Unit) 계층이 오기 때문에 양의 활성화 값만 사용됩니다. 양의 활성화 값만 조사하려면 분석을 반복하여 fire6-relu_squeeze1x1 계층의 활성화 결과를 시각화하십시오.

act6relu = activations(net,im,'fire6-relu_squeeze1x1');
sz = size(act6relu);
act6relu = reshape(act6relu,[sz(1) sz(2) 1 sz(3)]);

I = imtile(imresize(mat2gray(act6relu(:,:,:,[14 47])),imgSize));
imshow(I)

fire6-squeeze1x1 계층의 활성화 결과와 비교해 보면 fire6-relu_squeeze1x1 계층의 활성화 결과는 영상에서 얼굴 특징이 강한 영역을 명확하게 찾아내고 있습니다.

채널이 눈을 인식하는지 테스트하기

fire6-relu_squeeze1x1 계층의 채널 14와 47이 눈에서 활성화되는지 확인합니다. 신경망에 한쪽 눈이 감긴 새 영상을 입력하고 이 영상의 활성화 결과를 원본 영상의 활성화와 비교합니다.

한쪽 눈이 감긴 영상을 읽어 들여 표시한 다음 fire6-relu_squeeze1x1 계층의 활성화 값을 계산합니다.

imClosed = imread('face-eye-closed.jpg');
imshow(imClosed)

act6Closed = activations(net,imClosed,'fire6-relu_squeeze1x1');
sz = size(act6Closed);
act6Closed = reshape(act6Closed,[sz(1),sz(2),1,sz(3)]);

원래 영상과 해당 활성화 결과를 하나의 Figure에 플로팅합니다.

channelsClosed = repmat(imresize(mat2gray(act6Closed(:,:,:,[14 47])),imgSize),[1 1 3]);
channelsOpen = repmat(imresize(mat2gray(act6relu(:,:,:,[14 47])),imgSize),[1 1 3]);
I = imtile(cat(4,im,channelsOpen*255,imClosed,channelsClosed*255));
imshow(I)
title('Input Image, Channel 14, Channel 47');

채널 14와 47 모두 양쪽 눈에서 활성화되었으며 입 주변도 어느 정도 활성화되었음을 확인할 수 있습니다.

신경망은 눈을 학습하라는 지시를 받은 적이 없지만, 여러 영상 클래스를 구분하는 데 눈이 유용한 특징임을 학습했습니다. 기존의 머신러닝 방식에서는 종종 문제에 맞게 특징을 수동으로 설계했지만 심층 컨벌루션 신경망은 스스로 유용한 특징을 학습할 수 있습니다. 예를 들어, 신경망이 눈을 식별하는 법을 학습하면 실제 표범과 표범 무늬 러그를 구분할 수 있습니다.

참고 항목

| | | | | |

관련 항목