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)
어떻게 수정할 수 있을까요?
전체 코드 링크를 첨부합니다.