내 풀이 링크:https://github.com/lionkingchuchu/cs231n.git
GitHub - lionkingchuchu/cs231n: cs231n Spring 2022 Assignment
cs231n Spring 2022 Assignment. Contribute to lionkingchuchu/cs231n development by creating an account on GitHub.
github.com
다음은 Network Visualization 에 대한 과제이다. 유튜브에 있는 cs231n강의는 Network visualization을 RNN 강의 뒤에서 다루지만, 과제에서는 왠지 모르게 Assignment 2에 있다. 유튜브의 강의가 2017년 버전이고 Assignment는 2022년 버전이라 그런 것 같다. 과제는 강의를 안봤어도 시키는것만 따라하면 구현할 수 있지만, 뒤편 강의를 미리 듣고 과제를 풀면 이해하는데 도움이 됀다.
먼저 이 과제는 빠른 속도로 Alexnet, VGGnet에 버금가는 성능을 가진 Squeezenet을 사용하고, ImageNet의 데이터를 사용해서 saliency map, fooling images, class visualization 등을 수행한다.
먼저 saliency map은 주어진 이미지 데이터 픽셀 중 어떤 픽셀이 모델의 classification에 큰 영향을 끼치는 지 알려준다. saliency map을 구하는 방법은 먼저 salinecy map을 얻고싶은 데이터들 (N, C, H, W) model에 통과시켜 scores를 얻고 정답 클래스의 점수를 loss함수로 각 이미지의 gradient (dx) 를 구한다. 각 이미지당 차원이 (C, H, W)이기때문에 gradient도 (C, H, W)일 것이다. 사진 데이터는 RGB 채널 3개므로 gradient는 (3, H, W) 일 것이고 이 gradient를 RGB채널에 대해 최대값을 얻으면, 각 픽셀마다의 최대 gradient (H, W) 를 구할 수 있다. 이를 visualization 해주면 된다.
def compute_saliency_maps(X, y, model):
# Make sure the model is in "test" mode
model.eval()
# Make input tensor require gradient
X.requires_grad_()
saliency = None
# *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
scores = model(X)
loss = torch.sum(scores.gather(1,y.view(-1,1)).squeeze())
loss.backward()
saliency = torch.abs(X.grad)
saliency,_ = torch.max(saliency,1)
# *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
return saliency
구현은 model.eval()로 test mode로 돌리고, X.requires_grad()를 해주어야지 이미지의 gradient (dx)를 구할 수 있다. 아까 말한것처럼 loss함수를 각 데이터의 scores중 정답 클래스의 점수의 합을 loss 함수로 잡고, backward를 통해 X의 grad를 구해주고, 절대값을 씌워주고 RGB채널에 대해 max해주면 saliency를 얻을 수 있다.
위에 보이는 것처럼 classification에 가장 중요한 픽셀은 해당 물체가 있는 곳이므로, 해당 class의 물체가 있는 픽셀들이 주로 활성화 된 것을 볼 수 있다.
Inline Question 1
A friend of yours suggests that in order to find an image that maximizes the correct score, we can perform gradient ascent on the input image, but instead of the gradient we can actually use the saliency map in each step to update the image. Is this assertion true? Why or why not?
Your Answer: It is not true because saliency map use maximum absolute value of each pixels between 3 channels of RGB. It may help finding the most deterministic area of the pixel, but it won't always increase the score since some pixels' negative value have been modified because of the absoluting process.
여기서 다음 문제로 만약 correct score를 최대화 시키기 위해서, 이미지의 gradient ascent를 하는것 말고 saliency map을 더해주는 과정으로 correct score를 올릴 수 있는지? 묻는 문제이다. 답은 아니다. 왜냐하면 saliency map을 구하는 과정에서 각 픽셀의 grads를 절대값화 시키고, 최대값화 시키기 때문에 절대값 화 시키는 과정에서 gradient가 음수였던 것이 양수로 변할 수 있기 때문이다. 그러므로 항상 correct score가 올라가지는 않을 것이다.
다음은 fooling image 구현이다. fooling image는 아까 saliency map과 비슷한데, 먼저 이미지 데이터를 model을 통해 scores를 구하고, loss 함수를 정답 class가 아닌 임의의 우리가 원하는 정답 외의 class (target class) 의 점수로 image gradient를 구하는 것이다. 아까는 정답 class로 loss 함수를 만들었지만, 이번에는 target class로 loss 함수를 만들었기에 만약 gradient ascent를 한다면 target class의 점수가 증가 할 것이다. 이 과정을 이미지가 target class로 분류될 때까지 반복한다.
예시로 강아지 이미지가 있다면, 이를 고양이 class의 점수를 통해 dx grad를 구해 gradient ascent하는 것을 반복하여, model이 고양이 이미지라고 착각할 때까지 고양이 점수를 높여나간다. 그렇다면 fooling image를 하면 과연 강아지 이미지를 고양이 화 시킬 수 있을까? 사진의 강아지가 귀가 뾰족해지거나 수염이 생기는 등 고양이의 특성을 가진 강아지가 나올 것인가? 라는 생각을 잠시 했었다.
def make_fooling_image(X, target_y, model):
# Initialize our fooling image to the input image, and make it require gradient
X_fooling = X.clone()
X_fooling = X_fooling.requires_grad_()
learning_rate = 1
# *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
for iterations in range(100):
scores = model(X_fooling)
loss = scores[:,target_y]
loss.backward()
with torch.no_grad():
X_fooling += learning_rate * X_fooling.grad / torch.sum(X_fooling.grad)
X_fooling.grad.zero_()
scores = model(X_fooling)
_,pred = scores.max(1)
if pred == target_y:
break
# *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
return X_fooling
구현은 위와 같다. 아까처럼 X.grad를 target_y를 loss 함수로 만들어 구하고, 구한 X.grad를 이미지에 gradient ascent 해주며 이미지를 점점 변경시켜 간다. 변경후 모델의 prediction이 target_y 가 되었으면 충분히 fool 되었다는 뜻이므로 중지한다.
위는 새의 이미지를 상어로 오인할 때까지 fooling 한 결과이다. 사진에 조금 노이즈가 있지만 사람이 보았을 때에는 새가 다름없지만, model은 상어로 오인하였다. 두 이미지의 차이를 보면 우리가 이해할 수 없는 차이가 있다. 우리가 생각했던 강아지를 고양이화 시킨다던가 새를 상어화 시키는 등의 결과는 일어나지 않았다..
다음은 class visualization이다. 어떤 사진을 주고, 아까 과정처럼 target class, regularization을 loss 함수로 사용해 X.grad를 구하고, 이미지의 gradient ascent를 취해준다. 기존의 loss 함수에서 Regularization을 더한다면 아까처럼 전혀 알수 없는 노이즈가 추가되기보다 조금 더 general한 class 구현을 할수 있지 않을까?
def class_visualization_update_step(img, model, target_y, l2_reg, learning_rate):
# *****START OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
scores = model(img)
loss = scores[:,target_y]
loss -= l2_reg * torch.sum(torch.square(img))
loss.backward()
with torch.no_grad():
img += learning_rate * img.grad
img.grad.zero_()
# *****END OF YOUR CODE (DO NOT DELETE/MODIFY THIS LINE)*****
구현은아까와 비슷한데, score에 regularization을 빼준다. 뺴주는 이유는 gradient ascent를 사용하기 때문이다. 다르게 설명하면 target_y의 점수가 증가하는 방향이고, regularization은 이에 반대하는 방향으로 작용해야 하기 때문이다.
아래는 임의의 노이즈 이미지로부터 모래시계 class를 visualize한 것이다.
생각보다 우리와 친숙한 이미지는 나오지 않았지만, 그래도 노이즈 이미지에서 어느정도 해당 class의 형태가 보이는 이미지들이 나왔다.
'cs231n' 카테고리의 다른 글
cs231n Assignment 3: Q2-1 (Attention 설명) (1) | 2023.02.26 |
---|---|
cs231n Assignment 3: Q1 (Vanilla RNN 구현) (0) | 2023.02.25 |
cs231n Assignment 2: Q5 (Pytorch 사용해보기) (0) | 2023.02.20 |
cs231n Assignment 2: Q4 (CNN, Group Normalization 구현) (1) | 2023.02.18 |
cs231n Assignment 2: Q3 (Dropout 구현) (0) | 2023.02.15 |