부트캠프와 다른 AI학교,
AI는 아이펠에서 배우세요
#인공지능 

Grad-CAM 이용한 딥페이크 (deepfake) 얼굴 탐지

Grad-CAM의 목적은 합성곱 신경망(CNN)의 예측 과정을 시각화하여 더 투명하게 만드는 것입니다. Grad-CAM은 CNN의 합성곱 층이 수집한 공간 정보를 활용하여 소스 이미지의 어떤 영역이 분류 결정에 중요한지 식별합니다.

2024-06-13 | 김성진

(이 글은 Grad-CAM in Deepfake Face Recognition – Personal Record 내용을 번역하였습니다.)

Grad-CAM 과 딥페이크 (deepfake)

Grad-CAM (Gradient-weighted Class Activation Mapping, 그라이언트 클래스 활성화 맵핑)의 목적은 합성곱 신경망 (CNN)의 예측 과정을 시각화하여 더 투명하게 만드는 것입니다. Grad-CAM은 CNN의 합성곱 층이 수집한 공간 정보를 활용하여 소스 이미지의 어떤 영역이 분류 결정에 중요한지 식별합니다.

히트맵에서 빨간색 영역은 활성화 가중치가 높은 영역을 나타내고, 파란색 영역은 활성화 가중치가 낮은 영역을 나타냅니다. 실제 비디오에서 활성화 영역이 얼굴 부위에 집중되어 있다면, 모델이 얼굴 특징을 잘 포착하여 이를 진짜 비디오로 분류했음을 보여줍니다. 반면, 가짜 비디오에서는 얼굴 부위에 활성화 영역이 거의 나타나지 않으며, 이는 모델이 진짜 비디오로 분류하기에 충분한 얼굴 특징을 찾지 못했음을 의미합니다. 따라서, 해당 프레임을 가짜로 분류합니다.

딥페이크란 무엇인가?

딥페이크(Deepfake) 는 인공지능 기술을 이용해 사람의 얼굴을 실제처럼 보이도록 인위적으로 조작한 이미지나 동영상을 말합니다. 다양한 기술을 통해 딥페이크를 만들 수 있으며, 일반적으로 원래 얼굴을 미세하게 변경하여 진짜처럼 보이게 합니다.

신경망이 딥페이크를 어떻게 구분하는가?

신경망이 이미지를 보고 딥페이크인지 아닌지를 결정할 때, 특정 특징들을 관찰합니다. 이러한 특징들은 일반적인 이미지에서는 나타나지 않지만, 딥페이크 이미지에서 나타나는 미세한 차이들일 수 있습니다. 하지만 정확히 어떤 특징들을 보고 판단하는지는 이해하기 어려울 수 있습니다.

Grad-CAM 을 이용해 딥페이크 구분 이해하기

CAM(클래스 활성화 맵)을 사용하면 신경망이 이미지를 분석할 때 어떤 부분을 중요하게 보는지 시각적으로 확인할 수 있습니다. 이를 통해 딥페이크를 구분할 때 신경망이 어떤 특징들을 보고 있는지 알 수 있습니다. Pytorch의 훅(Hook)을 사용하여 모델 내부의 정보를 추출합니다. 훅은 모델의 중간 레이어에서 데이터를 가져와서 CAM을 생성하는 데 사용됩니다.

 

Grad-CAM 딥페이크 탐지 시각화 – FAKE/REAL 분류기

아래 예제에서는 얼굴 이미지를 FAKE(가짜) 또는 REAL(진짜)로 분류하는 EfficientNet-b1 모델을 사용합니다. 이 사전 훈련된 모델은 Kaggle의 Deepfake Detection Challenge에서 약 0.51980의 로그손실 점수를 기록했습니다. 이는 최고 성능의 모델(0.42320)보다는 낮지만, 기본적인 해결책을 제공하기에 충분합니다.

EfficientNet V2-L 기반 Grad-CAM 딥페이크 탐지 시각화 ([2207.10246] GBDF: Gender Balanced DeepFake Dataset Towards Fair DeepFake Detection)

얼굴 탐지기 (Face Detector)

분류기가 얼굴 이미지를 분석하므로, 먼저 비디오에서 얼굴을 잘라내야 합니다. 이를 위해 Pytorch RetinaFace 를 사용합니다. RetinaFace는 매우 정확한 얼굴 인식 시스템이지만, 상대적으로 느릴 수 있습니다.

데이터

Kaggle의 ‘Deepfake Detection Challenge’ 는 딥페이크 비디오의 좋은 데이터 세트를 제공합니다. 각 비디오는 약 10초 길이이며, 메타데이터를 통해 딥페이크의 원본 버전을 확인할 수 있습니다. 이 메타데이터는 프레임별 비교에 유용합니다. 여기서는 이 데이터 세트의 ‘_train_example_videos_’ 를 선택하여 CAM을 생성해 봅니다.

 

Grad-CAM (Gradient-weighted Class Activation Mapping)

클래스 활성화 맵핑 (CAM) 은 현재 문맥에서 이미지가 FAKE(가짜)인지 예측할 때 모델이 중요하게 보는 부분을 나타냅니다. CAM을 만들기 위해 두 가지 주요 요소를 고려해야 합니다:

  • 합성곱 층의 출력 활성화
  • 선택한 클래스(현재 예제에서는 FAKE)의 출력 점수

CAM은 모델의 합성곱 층 출력 활성화에서 만들어져야 합니다. 이는 이러한 활성화가 입력 이미지와 공간적으로 대응(이미지의 특정 위치와 일치)하며, 특정 픽셀(수용 영역)의 중요성을 반영하기 때문입니다. 그러나 출력 활성화만으로는 예측된 클래스의 점수와 관련된 정보가 없기 때문에 충분하지 않습니다. 입력 이미지를 통해 순전파(forward propagation)로 계산되며, 예측된 관심 클래스의 점수와 관련된 정보가 없기 때문입니다. 이를 보완하기 위해, 예측된 클래스 점수의 출력 활성화에 대한 그라디언트를 함께 사용합니다. 그라디언트는 예측 점수가 활성화 값에 얼마나 민감한지를 나타내어, 모델이 특정 클래스를 예측할 때 중요하게 보는 부분을 명확히 합니다.

CAM은 출력 활성화와 출력 점수의 그라디언트를 결합하여 만듭니다. 모델의 합성곱 층에서 얻은 활성화 맵 $A_{ij}^k$​가 있다고 가정합시다. 여기서 $i$$j$는 공간적 차원(너비와 높이)의 인덱스이고, $k$는 채널 차원의 인덱스입니다. $y^c$는 이미지가 FAKE일 확률에 대한 모델의 출력 점수입니다.

먼저, $y^c$에 대한 $A_{ij}^k$의 그라디언트를 공간적 차원에서 평균화합니다. 이는 글로벌 평균 풀링과 유사합니다. 이렇게 해서 얻어진 값 $\alpha_k^c$​는 다음과 같이 계산됩니다:

    $$\alpha_k^c = \frac{1}{Z} \sum_i \sum_j \frac{\partial y^c}{\partial A_{ij}^k}$$

여기서 $Z$는 정규화 상수로, 활성화 맵의 크기(너비와 높이의 곱)를 나타냅니다. 이 과정을 통해 각 채널의 중요도를 반영하는 가중치 $\alpha_k^c$를 구합니다.

출력 점수 $y^c$에 대한 $A_{ij}^k$의 그라디언트 $\frac{\partial y^c}{\partial A_{ij}^k}$​는 채널 $k$가 출력 클래스 $c$에 얼마나 중요한지를 나타냅니다. 따라서 이 그라디언트를 기반으로 채널의 중요도를 반영하는 가중치 $\alpha_k^c$​를 구해, $A_{ij}^k$의 채널 차원을 줄이는 데 사용합니다. 이를 통해 CAM을 생성합니다. CAM은 다음과 같이 계산됩니다:

    $$ L_{ij}^c = ReLU \left( \sum_k \alpha_k^c A_{ij}^k \right) $$

여기서 ReLU 함수는 채널별 가중 평균에 적용됩니다. 문헌에서는 이를 Gradient-weighted Class Activation Mapping (Grad-CAM) 이라고 부르며, $A$가 마지막 합성곱 층의 출력인 경우 단순히 CAM이라고 합니다. 이 방법은 신경망의 어떤 합성곱 층에도 적용할 수 있습니다.

 

파이토치 (Pytorch) 의 훅 (Hooks)

Pytorch에서 클래스 활성화 맵(CAM)을 생성하려면 합성곱 층의 출력 활성화와 그라디언트가 필요합니다. 출력 활성화 $A_{ij}^k$는 순전파(Forward Propagation) 과정에서 계산되고, 그라디언트 $\frac{\partial y^c}{\partial A_{ij}^k}$는 역전파(Backward Propagation) 과정에서 계산됩니다. 이 두 가지 정보를 얻기 위해 Pytorch에서는 훅(Hooks) 를 사용합니다. 특정 층의 출력을 CAM으로 사용하기 위해, 두 개의 훅을 해당 층에 부착합니다. 첫 번째 훅은 순전파 동안 $A_{ij}^k$를 얻는 데 사용되고, 두 번째 훅은 역전파 동안 $\frac{\partial y^c}{\partial A_{ij}^k}$를 얻는 데 사용됩니다. 이를 통해 필요한 모든 정보를 효율적으로 추출하여 CAM을 생성할 수 있습니다.

 

훅 (Hooks) 생성

아래는 훅을 생성하고, 활성화와 그라디언트를 저장하는 코드입니다.

class FwdHook():
def __init__(self, m):
self.hook_handle = m.register_forward_hook(self.hook_func)
def hook_func(self, m, i, o): self.stored = o.detach().clone()
def __enter__(self, *args): return self
def __exit__(self, *args): self.hook_handle.remove()

class BwdHook():
def __init__(self, m):
self.hook_handle = m.register_backward_hook(self.hook_func)
def hook_func(self, m, ig, og): self.stored = og[0].detach().clone()
def __enter__(self, *args): return self
def __exit__(self, *args): self.hook_handle.remove()

이 코드는 Jeremy Howard와 Sylvain Gugger의 “[The Fastbook](https://github.com/fastai/fastbook)”에서 가져온 것입니다. 여기서 FwdHook은 순전파(Forward Propagation)에 사용되고, BwdHook은 역전파(Backward Propagation)에 사용됩니다. 이 두 훅은 `nn.Module` 객체의 `register_forward_hook``register_backward_hook` 메서드를 사용하여 설정됩니다. 이를 통해 순전파 동안 출력 활성화 $A_{ij}^k$와 역전파 동안 그라디언트 $\frac{\partial y^c}{\partial A_{ij}^k}$를 접근할 수 있습니다.

FwdHookBwdHook은 활성화 및 그라디언트를 저장(`stored` 속성)할 수 있도록 허용하며, 필요할 때 안전하게 훅을 해제할 수 있는 컨텍스트 관리자(`__enter__`, `__exit__` 메서드) 역할도 합니다. 두 훅은 거의 동일하지만, 하나는 `forward_hook`을 사용하고 다른 하나는 `backward_hook`을 사용한다는 점에서 차이가 있습니다. 또 다른 작은 차이점은 출력 활성화의 그라디언트가 튜플로 반환되며, 이 튜플에서 첫 번째 요소를 사용한다는 것입니다.

이를 통해 필요한 데이터가 저장되면 훅을 안전하게 해제할 수 있어, 효율적이고 안전하게 CAM 생성을 위한 정보를 추출할 수 있습니다.

 

​얼굴 탐지 (Face Classification)

이제 훅을 모델의 선택된 층에 부착하고, 이미지를 모델에 입력하여 순전파를 수행한 후, FAKE 클래스를 위한 출력 점수로부터 역전파를 수행할 수 있습니다. 이러한 과정을 다음 함수로 요약할 수 있습니다:

def classify_face(im, cam_module):
xb = im[None,...] / 255
xb = xb.sub_(mean_norm[None,:,None,None]).div_(std_norm[None,:,None,None])
xb = xb.to(device)
model.eval()
cls = 0
with BwdHook(cam_module) as bwdhook:
with FwdHook(cam_module) as fwdhook:
output = model(xb)
act = fwdhook.stored[0]
output[0,cls].backward()
grad = bwdhook.stored[0]

gradcam = (grad.mean(dim=(1,2))[:,None,None] * act).sum(dim=0).relu_()
with torch.no_grad(): prob = F.softmax(output[0], dim=-1)[0]
return prob, gradcam
  • im: 관심 있는 이미지의 텐서.
  • cam_module: 관심 있는 층(또는 모듈).

순전파 훅은 역전파 훅 내부에 중첩되어 있습니다. 왜냐하면 순전파가 먼저 발생하므로, 순전파 훅을 통해 출력 활성화를 얻고, 이를 저장한 후 역전파를 수행하여 그라디언트를 얻기 위해서입니다. 역전파는 FAKE 클래스 $y^0$의 출력 점수에 대해 수행됩니다(여기서 0은 FAKE, 1은 REAL을 나타냅니다). $backward$ 가 호출되는 요소는 스칼라여야 합니다.

일반적인 추론 과정과 마찬가지로, 모델은 하나의 이미지를 한 번에 처리하며 `eval` 모드로 설정됩니다. 그러나 여기에선 그라디언트가 필요하므로 `torch.no_grad()` 문맥에서 수행되지 않습니다. 이로써 필요한 모든 정보를 저장한 후 훅을 안전하게 해제할 수 있습니다.

필요한 값들, 즉 합성곱 층의 출력 활성화 $A_{ij}^k$와 그라디언트 $\frac{\partial y^c}{\partial A_{ij}^k}$를 얻고 훅을 제거한 후, Grad-CAM을 생성하는 과정은 다음과 같습니다.

먼저, 그라디언트의 공간적 차원(너비와 높이)에서 평균을 계산합니다.
이를 통해 각 채널의 중요도를 나타내는 가중치 $\alpha_k^c$를 구합니다.
그런 다음, 이 가중치를 출력 활성화 $A_{ij}^k$에 곱하여 각 채널의 중요도를 반영합니다.
마지막으로, 채널 차원을 따라 합산하여 최종적인 Grad-CAM을 생성합니다.

이 과정은 아래의 코드로 표현됩니다:

gradcam = (grad.mean(dim=(1,2))[:,None,None] * act).sum(dim=0)

Grad-CAM 으로 딥페이크 예제 영상 분석

이제 몇 가지 예제 비디오의 CAM을 살펴보겠습니다.

각 예제는 원본 비디오(딥페이크 조작이 없는 비디오)와 그 딥페이크 버전을 사용합니다. 두 비디오에서 무작위로 선택된 다섯 개의 프레임을 사용하여 정렬하고, 각 프레임에서 얼굴을 잘라냅니다.

그림의 첫 번째 행은 원본 얼굴을 보여줍니다.
두 번째 행은 해당하는 딥페이크 얼굴을 보여줍니다.
세 번째 행은 CAM이 오버레이된 딥페이크 얼굴을 보여줍니다.

CAM은 마그마(magma) 색상 맵으로 표시되며, 노란색에 가까울수록 값이 더 높음을 나타냅니다. 딥페이크 얼굴의 오른쪽 상단 모서리에 표시된 숫자는 모델이 해당 얼굴을 딥페이크라고 생각하는 확률입니다.

이를 통해 모델이 이미지를 분석할 때 중요하게 보는 영역을 시각적으로 확인할 수 있습니다.

 

조작된 안경

Fig. 1. (매우 조잡하게 조작된 안경) 모든 딥페이크 이미지에서 안경의 중간 부분이 사라진 것처럼 보입니다. 전반적으로 딥페이크된 얼굴은 꽤 현실적으로 보이며, 눈에 띄는 결함은 없습니다. 그러나 눈 근처 영역이 다른 부분보다 많이 강조됩니다. 이는 비현실적인 안경 때문일 수 있습니다.

 

Fig. 2. (조금 나아진 조작된 안경) 이번 예제는 앞서 본 원본 비디오를 기반으로 합니다. 딥페이크 이미지에서 안경이 좀 더 잘 표현되었습니다. 강조된 영역이 약간 더 넓게 퍼져 있으며, 특히 2번과 3번 프레임에서는 눈 주위에 덜 집중되어 있습니다. 하지만 동시에 딥페이크로 분류될 확률은 더 낮습니다.

 

Fig. 3. (가짜 콧수염) 딥페이크된 얼굴에는 명백히 가짜인 콧수염이 있습니다. 그러나 콧수염은 크게 강조되지 않습니다. 여전히 이미지에서 가장 강조된 부분은 눈과 안경입니다.

 

소파에 앉아 있는 남성

Fig. 4. (소파에 앉아 있는 남성) 이전 예제들과 달리 이 남성은 안경을 쓰고 있지 않습니다. 강조된 영역이 얼굴 전체에 걸쳐 더 넓게 퍼져 있는 것이 눈에 띕니다.

 

녹색 배경 앞에 있는 여성

Fig. 5. (녹색 배경 앞에 있는 여성) 비디오에서 딥페이크된 얼굴의 품질은 매우 좋습니다. 현실적이며 명백한 결함이 없습니다. 강조된 영역도 눈 근처뿐만 아니라 더 넓게 퍼져 있습니다. 그러나 머리를 기울이거나 여성이 웃을 때는 강조된 영역이 덜 보입니다.

 

Fig. 6. (녹색 배경 앞에 있는 여성) 딥페이크된 얼굴은 현실적일 뿐만 아니라 원본과도 유사하게 보입니다. 모델의 신뢰도는 다소 낮아 보입니다. 그럼에도 불구하고 약간의 희미한 강조 영역을 확인할 수 있습니다.

 

 

비디오 프레임

다음 두 개의 최종 예제에서는 CAM에 ReLU가 적용되지 않았습니다. 따라서 전체적으로 더 밝게 보이지만, 여전히 강조된 부분을 구별할 수 있습니다. 또한, CAM이 오버레이된 딥페이크 이미지들만 표시됩니다.

Fig. 7. (비디오 프레임: 두 남자, 사람 얼굴 이미지의 티셔츠)

 

Fig. 8. (두 남자, 사람 얼굴 이미지의 티셔츠) 모델이 가장 확신할 때 실제로 얼굴 바로 바깥쪽 영역에 집중하고 있음을 알 수 있습니다. 일반적으로, 딥페이크일 가능성이 낮은 얼굴에서는 CAM이 더 어둡게(값이 더 작게) 나타납니다.

 

Fig. 9. (비디오 프레임: 두 개의 초상화 앞에 있는 여성)

 

Fig. 10. (두 개의 초상화 앞에 있는 여성) 여기서 5번 프레임만 딥페이크일 가능성이 높습니다. 이 프레임에서는 눈 주변에 강조 표시가 나타납니다. 반면, 딥페이크일 가능성이 낮은 다른 프레임에서는 강조 표시가 얼굴 주변에 나타납니다.

 

참조