어떤 부분에서 문제가 있을지 알고 싶습니다.(resnet구현)

안녕하세요!
pytorch에 익숙해지기 위해, resnet 모델을 직접 구축하고 훈련 및 테스트까지 완료했는데 결과가 만족스럽지 않네요.


위쪽은 train, 아래쪽은 test 그래프입니다.
train은 매 epoch대비 loss와 accuracy변화를 나타낸 것이고,
test는 train에서 생성한 epoch별 가중치를 불러와 test dataset을 태운 결과인데 썩 좋지 않습니다 ㅠㅠ

그래프를 통해 적어도 두 가지는 확실하게 확인할 수 있었습니다.

  1. resnet 50 모델이 제대로 학습되지 않았다.
  2. 18 model과 34 model이 충분히 학습되지 않았다.

정도로 볼 수 있을 것 같습니다. 2번 문제도 심각하지만 우선은 1번에 대해 집중적으로 말씀 드릴까 합니다.

  1. resnet 50 모델이 제대로 학습되지 않았다.
    우선, 데이터의 문제나, 옵티마이저의 문제 등 모델 외적인 문제에 대해서는 배제했습니다. 왜냐하면 18, 34, 50 모델을 훈련시킬 때 모델 구조와 저장할 가중치 파일 이름 외에는 아무것도 변경하지 않았거든요.
    그래서 50 모델에 문제가 있다고 생각했고, 구조를 점검해 보니, torchvision에서 제공하는 50 모델의 구조와 제가 만든 모델의 구조가 조금 달랐음을 확인할 수 있었습니다.

    위 그림에서 conv2_x, conv3_x, conv4_x, conv5_x의 처음 block의 맨 마지막에 layer가 하나씩 추가되어 있는 겁니다. 아래 그림처럼

    layer가 하나씩 더 추가 되었는데 왜 논문과 다르게 설계되었는지, 그리고 이 차이가 train test 그래프를 수평으로 만들 정도로 큰 문제인지 궁금합니다.
    사실 저는 이 문제가 그래프를 수평으로 만들 정도의 문제인지 잘 모르겠어요. 물론 엉망으로 만든 모델이 제대로 학습되기를 바라는건 어리석은 바람입니다. 하지만, 분명히 resnet을 추종하여 만든 모델이고, short connection을 구현했기 때문에 잔차에 대해서만 학습시킨다는 resnet의 개념과도 멀어지지 않았으니 최적의 성능은 아니더라도 train 단계에서 일정 수준의 퍼포먼스는 보여주는 것이 정상이라고 생각합니다.
    이럴 경우 모델 외적인 부분에서 문제를 찾는 것이 맞지만 위에서 말씀드린 대로 각 모델을 훈련시킬 때 바꾼 것은 모델과 가중치 파일 이름 정도였기 때문에 조금 더 모르는 모델에 문제가 있겠다. 라는 생각으로 기운 것이고요.

3줄 요약하면

  1. resnet 50 모델이 훈련이 안 된다.
  2. 논문에서 본 모델과 torchvision 모델이 다르고, 이 차이 때문에 문제가 생기는 것 같은데 이게 전혀 학습이 이뤄지지 않을 정도로 큰 문제인가?
  3. 사실 이런 그래프면 모델이 아닌 다른 부분에서 문제가 있을 가능성도 높다고 생각하는데, 18, 34, 50을 훈련시킬 때 차이는 모델과 파일명 차이 정도다.

결론은 외통수입니다 ㅠㅠ

이에 대한 코드는
모델(링크)
실행부(링크)
입니다.
뭔가 의심 가는 부분이 있다면 일부만 올려서 보시는 분들의 불편을 덜고 싶은데 어디를 짚어야 할지 몰라서 우선 전부 올려봅니다(공부하느라 주석이 좀 더러운 점 죄송합니다)

  • 아래 답변은 OpenAI의 GPT-3.5(ChatGPT) 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

<GD_G> 안녕하세요! resnet 모델을 구현하고 훈련 및 테스트를 진행하였지만, 결과에 만족하지 못하고 계신 것 같습니다. 그래프를 통해 확인한 바로는 resnet 50 모델이 제대로 학습되지 않았고, 18 모델과 34 모델도 충분히 학습되지 않았다고 보여집니다. 특히 resnet 50 모델의 구조가 torchvision에서 제공하는 구조와 달라서 문제가 발생한 것으로 보입니다. 그래프가 수평으로 나타나는 정도로 큰 문제인지 궁금하신 것 같습니다. 또한, 모델 외적인 부분에서 문제가 있을 가능성도 높다고 생각하시는데, 18, 34, 50 모델을 훈련시킬 때 차이는 모델과 파일명 정도라고 말씀하셨습니다.

이에 대한 해결책으로는 다음과 같은 접근 방법을 제안드릴 수 있습니다.

  1. resnet 50 모델의 구조를 확인해보세요: torchvision에서 제공하는 resnet 50 모델의 구조와 직접 구현한 모델의 구조를 비교해보세요. 위에서 언급하신 것처럼 두 구조가 조금 달라서 문제가 발생한 것으로 보입니다. 모델의 구조를 수정하여 torchvision의 구조와 동일하게 만들어보세요.

  2. 모델의 초기 가중치를 확인해보세요: 모델의 초기 가중치가 적절한지 확인해보세요. resnet 모델은 사전 훈련된 가중치를 사용하는 것이 일반적입니다. torchvision에서 제공하는 사전 훈련된 가중치를 사용하거나, 직접 사전 훈련된 가중치를 불러와서 사용해보세요.

  3. 데이터셋과 옵티마이저를 확인해보세요: 데이터셋과 옵티마이저 설정도 학습에 영향을 줄 수 있습니다. 데이터셋이 충분한지, 데이터 전처리가 적절한지 확인해보세요. 또한, 옵티마이저의 하이퍼파라미터 설정도 조정해보세요.

  4. 학습 과정을 확인해보세요: 학습 과정에서 어떤 문제가 발생하는지 확인해보세요. 학습 중에 발생하는 loss와 accuracy의 변화를 확인하고, 학습이 진행되는 동안 어떤 문제가 발생하는지 로그를 확인해보세요. 이를 통해 어떤 부분에서 문제가 발생하는지 파악할 수 있을 것입니다.

위의 접근 방법을 참고하여 문제를 해결해보세요. 또한, 주어진 코드를 확인하여 더 자세한 도움을 드릴 수 있습니다. 코드를 확인하고 싶으시다면 해당 링크를 참고해주세요.

이 문제에 대한 자세한 내용은 다음 링크에서 확인하실 수 있습니다: 전체 주제 읽기

혹시 제가 잘못된 정보를 드렸을 수도 있으니, 참고 부탁드립니다. 추가적인 도움이 필요하시면 언제든지 질문해주세요!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-4 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요! PyTorch를 이용해 ResNet 모델을 구현하고 훈련하면서 어려움을 겪고 계신 것 같습니다. 질문을 정리해보면, ResNet 50 모델이 제대로 학습되지 않는 문제와, torchvision에서 제공하는 ResNet 50 모델과 직접 구현한 모델의 구조가 다른 문제에 대해 궁금해하시는 것 같습니다. 특히, 모델의 구조 차이가 학습에 큰 영향을 미치는지, 그리고 이로 인해 학습이 전혀 이루어지지 않을 정도의 문제인지에 대한 의문을 가지고 계신 것 같습니다.

먼저, ResNet 50 모델이 제대로 학습되지 않는 문제에 대해 말씀드리겠습니다. ResNet 모델의 핵심은 shortcut connection을 통해 gradient가 잘 흐를 수 있도록 하는 것입니다. 이를 통해 깊은 네트워크에서도 학습이 잘 이루어질 수 있습니다. 그러나, 모델의 구조나 학습 방법에 따라 학습이 잘 이루어지지 않을 수 있습니다. 예를 들어, 학습률(learning rate)이 너무 크거나 작으면 모델이 제대로 학습되지 않을 수 있습니다. 또한, 데이터셋의 크기나 복잡도, 데이터 전처리 방법 등도 학습에 영향을 미칠 수 있습니다.

다음으로, torchvision에서 제공하는 ResNet 50 모델과 직접 구현한 모델의 구조가 다른 문제에 대해 말씀드리겠습니다. torchvision에서 제공하는 ResNet 모델은 원래의 ResNet 논문에서 제안된 구조를 따르고 있습니다. 그러나, 직접 구현한 모델의 구조가 다르다면, 이는 학습에 영향을 미칠 수 있습니다. 특히, ResNet 모델의 경우, 각 레이어의 출력 크기와 필터 수, stride 등이 잘 조정되어야 합니다. 이러한 요소들이 잘못 설정되면, 모델의 성능에 영향을 미칠 수 있습니다.

따라서, 직접 구현한 모델의 구조를 다시 확인하고, 필요하다면 torchvision에서 제공하는 ResNet 모델의 구조를 참고하여 수정해보시는 것을 추천드립니다. 또한, 학습률, 데이터 전처리 방법 등을 조절해보시는 것도 도움이 될 수 있습니다.

아래는 PyTorch에서 ResNet 모델을 구현하는 예제 코드입니다. 이 코드를 참고하여 직접 구현한 모델의 구조를 확인해보시기 바랍니다.

import torch
import torch.nn as nn

class BasicBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self, x):
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = self.relu(out)
        return out

class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_channels = 64

        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
        self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
        self.linear = nn.Linear(512, num_classes)

    def _make_layer(self, block, out_channels, num_blocks, stride):
        strides = [stride] + [1]*(num_blocks-1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels
        return nn.Sequential(*layers)

    def forward(self, x):
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

def ResNet50():
    return ResNet(BasicBlock, [3, 4, 6, 3])

더 자세한 내용은 PyTorch 공식 문서나 튜토리얼을 참고하시면 도움이 될 것입니다. 아래는 관련 링크입니다.

마지막으로, 질문에 대한 답변이 정확하지 않을 수도 있으니, 필요한 경우 추가적인 정보를 제공해주시면 감사하겠습니다.


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!

학습이 안되어서 궁금하신 점이 많으실 것 같은데요. 학습이 잘 되는지 확인을 위해서 pretrained weight를 이용해서 학습을 해보시고 결과 비교를 해보시면 어떨까 합니다. resnet이 성능이 좋긴 하지만 개/고양이 분류 문제에서는 생각보다 성능이 안 나올수도 있거든요. 그렇게 먼저 결과를 보신 뒤 다음 단계를 진행해 보시는게 어떨까 싶습니다.

1개의 좋아요

답변 감사합니다!

글을 올린 뒤 두 모델에 대해 훈련을 진행했습니다.

  1. torchvision에서 가져온 resnet 50(imagenet용 모델이기 때문에 class를 2로 변경했습니다.)
  2. github에서 가져온 resnet 50(이 모델도 layer가 하나 더 추가되어 있습니다.)
    2번의 링크입니다.

역시 다른 부분들은 모두 그대로 두고 모델만 변경(같은 데이터, 같은 하이퍼파라미터)하여 실행시켰는데, 1번은 제가 올렸던 resnet 50 그래프와 유사하게 수평선을 그렸습니다. 훈련이 제대로 안 된 것이겠지요.
2번의 경우는 훈련을 시키는 중이긴 하지만 training accuracy는 상승하고 loss는 점점 하락하는 것을 확인할 수 있었습니다. 제대로 훈련된 결과를 오늘 새벽에는 볼 수 있을 것 같네요.

2번이 학습이 된다는 의미는 모델에 문제가 있다는 결론이 됩니다. 하지만 torchvision에서 배포한 resnet까지 제대로 되지 않는다는 점을 납득하기가 힘드네요... 분명 뭔가 잘못 코딩한 부분이 있을 것 같으면서도 아닌 것 같아서 복잡하네요.

하여튼 답변 다시 한번 감사드립니다.
직접 코드까지 봐 주시고 ... 아마 보기 힘드셨을 텐데 ㅠㅠ
말씀하신 부분도 한번 확인해 보겠습니다.

github에서 가져온 resnet 50 결과입니다


train은 잘 되었고 test는 잘 되지 않은 것처럼 나오네요
결국 제가 만든 모델과, torchvision의 resnet 50은 훈련이 안 되고
github에서 찾은 모델은 훈련이 되는 이상한 결론이 나와버렸습니다.

test가 안되는 이유는 또 뭘까요 ㅋㅋㅋ 이건 아무래도 test 코드가 잘못된 부분이 있는지 확인해야 될 것 같습니다.

그래도 한 걸음씩 나아가시는 것 같아서 다행입니다. 이슈가 있으실때마다 여기에 자유롭게 공유해주시면 같이 고민해 보면 좋겠네요. 감사합니다.

1개의 좋아요

결국 모두 해결했습니다!

문제 1번은 identity block을 reduce하는 과정에서 batch normalization이 추가되어야 했음을 확인하고 수정했을 때 고칠 수 있었습니다. 데이터의 분포를 변화시키는 layer라 그런지 하나만 없어도 파급력이 크다는 사실을 깨달을 수 있었습니다.

그렇다면 왜 resnet18과 resnet34는 잘 돌아갔는가를 생각해 보면, vanishing gradient를 야기시키지 않을 정도의 깊이를 가진 모델이었을 가능성을 생각해 볼 수 있을 것 같습니다. 34와 50 사이에 reduce에 대한 batch normalization을 하지 않을 경우 vanishing gradient를 발생시키는 임계점이 존재했던 것은 아닐까요???

한편, 문제 2번은 단순한 실수였습니다. test시에 각 epoch에서의 가중치를 불러와 모델에 태워야 하는데, glob으로 가져온 가중치 파일 목록에서 맨 앞의 가중치 파일만 계속해서 불러오고 있었던 겁니다. 맨 처음을 태우니까 1 epoch에 대해서만 test를 반복하고 있었네요. 에고 부끄러워라 ㅋㅋㅋ

관심을 가져주셨기에 결과를 알려드리고 싶었어요. 신경 써 주셔서 감사합니다.
즐거운 주말 되세요~

1개의 좋아요