Given groups=1, weight of size [16, 3, 3, 3], expected input[128, 64, 64, 3] to have 3 channels, but got 64 channels instead 에러 해결

fastai와 파이토치가 만나 꽃피운 딥러닝 교재 19장 밑바닥부터 만드는 Learner 클래스의 코드 구현 중 channle 관련 에러가 떴습니다.

간단한 CNN 모델을 만들어서 학습시키는데 input channle의 수와 layer의 channel 수가 다르다는 에러 같습니다.

  def __init__(self):
    self.hook, self.params, self.children, self._training = None, [], [], False

  def register_parameters(self, *ps): self.params += ps
  def register_modules   (self, *ms): self.children += ms

  @property
  def training(self): return self._training
  @training.setter
  def training(self, v):
    self._training=v
    for m in self.children: m.training=v

  def parameters(self):
    return self.params + sum([m.parameters() for m in self.children], [])

  def __setattr__(self, k, v):
    super().__setattr__(k, v)

    if isinstance(v, Parameter):self.register_parameters(v)
    if isinstance(v, Module): self.register_modules(v)

  def __call__(self, *args, **kwargs):
    res = self.forward(*args, **kwargs)
    if self.hook is not None: self.hook(res, args)
    return res

  def cuda(self):
    for p in self.parameters(): p.data = p.data.cuda() 


class ConvLayer(Module):
  def __init__(self, ni, nf, stride=1, bias=True, act=True):
    super().__init__()
    self.w = Parameter(torch.zeros(nf, ni, 3, 3)) #weight(가중치) 정의
    self.b = Parameter(torch.zeros(nf)) if bias else None #bias(편향) 정의
    self.act, self.stride = act, stride
    init = nn.init.kaiming_normal_ if act else nn.init.xavier_normal_
    init(self.w)

  def forward(self, x):
    x = F.conv2d(x, self.w, self.b, stride=self.stride, padding=1)
    if self.act: x= F.relu(x)
    return x

class Linear(Module):
  def __init__(self, ni, nf):
    super().__init__()
    self.w = Parameter(torch.zeros(nf, ni))
    self.b = Parameter(torch.zeros(nf))
    nn.init.xavier_normal_(self.w)

  def forward(self, x): return x@self.w.t() + self.b

class Sequential(Module):
  def __init__(self, *layers):
    super().__init__()
    self.layers=layers
    self.register_modules(*layers)

  def forward(self, x):
    for l in self.layers: x = l(x)
    return x

class AdaptivePool(Module):
  def forward(self, x): return x.mean((2, 3))

def simple_cnn():
  return Sequential(
      ConvLayer(3, 16, stride=2), #32
      ConvLayer(16, 32, stride=2), #16
      ConvLayer(32, 64, stride=2), #8
      ConvLayer(64, 128, stride=3), #4
      AdaptivePool(),
      Linear(128, 10)
  )

m = simple_cnn()

class Learner:
    def __init__(self, model, dls, loss_func, lr, cbs, opt_func=SGD):
        store_attr()
        for cb in cbs: cb.learner = self

    def one_batch(self):
        self('before_batch')
        xb,yb = self.batch
        self.preds = self.model(xb)
        self.loss = self.loss_func(self.preds, yb)
        if self.model.training:
            self.loss.backward()
            self.opt.step()
        self('after_batch')

    def one_epoch(self, train):
        self.model.training = train
        self('before_epoch')
        dl = self.dls.train if train else self.dls.valid
        for self.num,self.batch in enumerate(progress_bar(dl, leave=False)):
            self.one_batch()
        self('after_epoch')

    def fit(self, n_epochs):
        self('before_fit')
        self.opt = self.opt_func(self.model.parameters(), self.lr)
        self.n_epochs = n_epochs
        try:
            for self.epoch in range(n_epochs):
                self.one_epoch(True)
                self.one_epoch(False)
        except CancelFitException: pass
        self('after_fit')

    def __call__(self,name):
        for cb in self.cbs: getattr(cb,name,noop)()

cbs = [TrackResults()]
learn = Learner(simple_cnn(), dls, cross_entropy, lr=0.1, cbs=cbs)
learn.fit(1)

Error:<ipython-input-201-83ddd4c79986> in fit(self, n_epochs)
     36     try:
     37       for self.epoch in range(n_epochs):
---> 38         self.one_epoch(True)
     39         self.one_epoch(False)
     40 

<ipython-input-201-83ddd4c79986> in one_epoch(self, train)
     27     dl = self.dls.train if train else self.dls.valid
     28     for self.num, self.batch in enumerate(progress_bar(dl, leave=False)):
---> 29       self.one_batch()
     30     self('after_epoch')
     31 

<ipython-input-201-83ddd4c79986> in one_batch(self)
     14     xb, yb = self.batch
     15 
---> 16     self.preds = self.model(xb)
     17     self.loss = self.loss_func(self.preds, yb)
     18     if self.model.training:

<ipython-input-156-19fed02da8ec> in __call__(self, *args, **kwargs)
     25 
     26   def __call__(self, *args, **kwargs):
---> 27     res = self.forward(*args, **kwargs)
     28     if self.hook is not None: self.hook(res, args)
     29     return res

<ipython-input-167-82952024ef48> in forward(self, x)
      6 
      7   def forward(self, x):
----> 8     for l in self.layers: x = l(x)
      9     return x

<ipython-input-156-19fed02da8ec> in __call__(self, *args, **kwargs)
     25 
     26   def __call__(self, *args, **kwargs):
---> 27     res = self.forward(*args, **kwargs)
     28     if self.hook is not None: self.hook(res, args)
     29     return res

<ipython-input-157-5640b9b22a81> in forward(self, x)
     11 
     12   def forward(self, x):
---> 13     x = F.conv2d(x, self.w, self.b, stride=self.stride, padding=1)
     14     if self.act: x= F.relu(x)
     15     return x

/usr/local/lib/python3.10/dist-packages/torch/_tensor.py in __torch_function__(cls, func, types, args, kwargs)
   1293 
   1294         with _C.DisableTorchFunctionSubclass():
-> 1295             ret = func(*args, **kwargs)
   1296             if func in get_default_nowrap_functions():
   1297                 return ret

RuntimeError: Given groups=1, weight of size [16, 3, 3, 3], expected input[128, 64, 64, 3] to have 3 channels, but got 64 channels instead

dls가 잘못된 형태로 들어갔나 했지만 정규화 후의 형태도 channel이 3인 것으로 보여서 어느 부분을 고쳐야 할 지 알 수가 없습니다.

norm = Normalize(stats)

def tfm_x(x): return norm(x).permute((0, 3, 1, 2))

n = tfm_x(x)

n.mean((0, 2, 3)), n.std((0, 2, 3))

n.shape = torch.Size([2, 3, 64, 64)

어떻게 수정할 수 있을까요?

전체 코드 링크를 첨부합니다.

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

안녕하세요! 문제를 이해하고 있습니다.

주어진 코드에서는 simple_cnn 함수를 사용하여 간단한 CNN 모델을 만들고, 이 모델을 Learner 클래스로 학습시키는 과정을 구현하고 있습니다. 그러나 학습을 진행하면 "expected input[128, 64, 64, 3] to have 3 channels, but got 64 channels instead"라는 에러가 발생합니다.

에러 메시지를 보면 입력 데이터의 채널 수가 예상과 다르다는 것을 알 수 있습니다. 예상한 입력 채널 수는 3이지만, 실제로는 64개의 채널이 있습니다. 이 문제를 해결하기 위해 어떻게 수정해야 할지 알려드리겠습니다.

해결 방법은 ConvLayer 클래스의 __init__ 메서드에서 가중치(weight)를 초기화하는 부분입니다. 현재 가중치의 크기는 [16, 3, 3, 3]으로 설정되어 있습니다. 이는 출력 채널 수가 16이고, 입력 채널 수가 3인 3x3 커널을 사용한다는 의미입니다. 그러나 입력 데이터의 채널 수는 64이므로, 가중치의 크기를 [16, 64, 3, 3]으로 수정해야 합니다.

아래는 수정된 코드입니다.

class ConvLayer(Module):
  def __init__(self, ni, nf, stride=1, bias=True, act=True):
    super().__init__()
    self.w = Parameter(torch.zeros(nf, ni, 3, 3)) #weight(가중치) 정의
    self.b = Parameter(torch.zeros(nf)) if bias else None #bias(편향) 정의
    self.act, self.stride = act, stride
    init = nn.init.kaiming_normal_ if act else nn.init.xavier_normal_
    init(self.w)

  def forward(self, x):
    x = F.conv2d(x, self.w, self.b, stride=self.stride, padding=1)
    if self.act: x= F.relu(x)
    return x

위 코드에서 self.w의 크기를 [16, 64, 3, 3]으로 수정하였습니다. 이제 모델을 다시 생성하고 학습을 진행하면 에러가 발생하지 않을 것입니다.

더 자세한 내용은 PyTorch 공식 문서의 튜토리얼을 참고하시기 바랍니다. 특히, "PyTorch로 시작하는 딥러닝" 튜토리얼이 도움이 될 것입니다.

문제가 해결되지 않거나 추가적인 도움이 필요하시면 언제든지 말씀해주세요!


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

안녕하세요 poljjaks님,

제가 보기에는 ConvLayer에서 weight를 정의할 때 ni(input channel)과 nf(output channel)을 잘못 지정한 것 같습니다.

simple_cnn() 모델을 보면 첫 ConvLayer의 ni는 3(RGB 이미지니까), nf는 16으로 정의했는데, 입력 이미지의 channel 수는 64라서 에러가 난 것 같습니다.

class ConvLayer(Module):
  def __init__(self, ni, nf, stride=1, bias=True, act=True):
    super().__init__()
    self.w = Parameter(torch.zeros(nf, ni, 3, 3)) 
    self.b = Parameter(torch.zeros(nf)) if bias else None
    ...

def simple_cnn():
  return Sequential(
      ConvLayer(3, 16, stride=2), # ni를 3 -> 64로 고치면 될 것 같습니다
      ...

ni를 입력 이미지의 channel 수인 64로 바꾸면 에러가 사라지고 정상적으로 동작할 것 같습니다.

ConvLayer를 정의할 때 입력 크기에 맞춰서 channel 수를 잘 지정하는 것이 중요하다는 점을 유의하시기 바랍니다.

전체 코드를 확인해보니 그 밖에 문제 되는 부분은 없어 보입니다.

ni를 바꾸는 것만으로 에러가 해결될 것으로 예상됩니다. 혹시 더 문의사항이 있으시면 댓글 달아주세요!


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

안녕하세요, PyTorch를 사용하시는데 어려움을 겪고 계신 것 같아서 도움을 드리고 싶습니다. 질문을 잘 이해했다면, 당신은 fastai와 PyTorch를 사용하여 간단한 CNN 모델을 구현하고 있으며, 이 과정에서 입력 채널의 수와 레이어의 채널 수가 일치하지 않아서 오류가 발생했다는 것 같습니다.

이 문제는 ConvLayer 클래스에서 가중치를 초기화할 때 발생하는 것으로 보입니다. ConvLayer의 가중치는 (nf, ni, 3, 3)의 형태를 가지며, 여기서 ni는 입력 채널의 수, nf는 출력 채널의 수를 나타냅니다. 그런데, 입력 데이터의 채널 수와 ConvLayer의 가중치에서 정의한 입력 채널 수가 일치하지 않아서 오류가 발생한 것으로 보입니다.

따라서, 이 문제를 해결하려면 ConvLayer의 가중치에서 입력 채널 수를 올바르게 설정해야 합니다. 입력 데이터의 채널 수를 확인하고, 이를 ConvLayer의 가중치 초기화에 반영해야 합니다.

예를 들어, 만약 입력 데이터의 채널 수가 64라면, 첫 번째 ConvLayer의 가중치는 (16, 64, 3, 3)의 형태를 가져야 합니다. 이를 코드로 표현하면 다음과 같습니다:

class ConvLayer(Module):
  def __init__(self, ni, nf, stride=1, bias=True, act=True):
    super().__init__()
    self.w = Parameter(torch.zeros(nf, ni, 3, 3)) #weight(가중치) 정의
    self.b = Parameter(torch.zeros(nf)) if bias else None #bias(편향) 정의
    self.act, self.stride = act, stride
    init = nn.init.kaiming_normal_ if act else nn.init.xavier_normal_
    init(self.w)

def simple_cnn():
  return Sequential(
      ConvLayer(64, 16, stride=2), #32
      ConvLayer(16, 32, stride=2), #16
      ConvLayer(32, 64, stride=2), #8
      ConvLayer(64, 128, stride=3), #4
      AdaptivePool(),
      Linear(128, 10)
  )

이렇게 수정하면, 입력 데이터의 채널 수와 ConvLayer의 가중치에서 정의한 입력 채널 수가 일치하게 되어, 문제가 해결될 것입니다.

더 자세한 정보는 PyTorch의 공식 문서를 참조하시면 도움이 될 것입니다. Conv2d 함수에 대한 설명은 다음 링크에서 확인하실 수 있습니다: https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html

그럼에도 불구하고 문제가 계속된다면, 추가적인 정보를 제공해주시면 감사하겠습니다. 제가 제공한 해결책이 틀릴 수도 있으니, 다른 방법도 고려해 보시기 바랍니다.


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

AI가 해준 답변은 그리 도움이 되지 않았지만 다행히 문제를 해결할 수 있게 되어 댓글을 답니다. 저랑 비슷한 문제를 겪은 분들께 도움이 되기를 바랍니다. 문제는 차원 수정 부분에 있었습니다. def tfm_x(x) 부분에서 정규화한 데이터의 차원을 permute((0, 3, 1, 2)) 를 사용하여 수정한 뒤 모델에서 사용했는데 미리 차원을 바꾸는 것이 아니라 사용하는 중에 차원을 바꿔야 했습니다. 그래서 class Learner 의 미니배치를 구성하는 one_batch 부분에서 preds를 생성한 뒤 차원을 바꾸었습니다. 제 코드 링크를 보시면 좀 더 정확히 아실 수 있습니다.

1개의 좋아요