강의영상

- (1/2) CNN 모형구축 (MNIST 3,7)

- (2/2) 과제설명

import

import torch 
from fastai.vision.all import * 
import graphviz
def gv(s): return graphviz.Source('digraph G{ rankdir="LR"'+ s + ';}')

data

- download data

path = untar_data(URLs.MNIST_SAMPLE)
path.ls()
(#3) [Path('/home/cgb4/.fastai/data/mnist_sample/labels.csv'),Path('/home/cgb4/.fastai/data/mnist_sample/train'),Path('/home/cgb4/.fastai/data/mnist_sample/valid')]

- list

threes=(path/'train'/'3').ls()
sevens=(path/'train'/'7').ls()

- list $\to$ image

Image.open(threes[4])

- image $\to$ tensor

tensor(Image.open(threes[4]))
tensor([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,  72, 156,
         241, 254, 255, 188,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  17, 168, 250, 232,
         147,  79, 143, 254,  25,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0, 109, 231, 164,  39,   0,
           0,   0,  86, 251,  24,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,  81,  40,   0,   0,   0,
           0,   4, 200, 157,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,  92, 249,  27,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           5, 221, 128,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         147, 185,  19,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 137,
         224,  20,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 137, 239,
          68,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  83, 239,  95,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  83, 245, 104,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 179, 254, 224, 217,
         147,  36,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  15,  44, 117, 117,
         196, 237, 104,   7,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           6, 117, 246,  95,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,  85, 241,  22,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0, 225, 102,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0, 170, 131,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  28, 104,   0,   0,
           0,   0,  17, 234,  87,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1, 198, 179,  29,
           0,  42, 199, 235,  17,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  14, 154, 236,
         250, 252, 163,  12,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
        [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0]],
       dtype=torch.uint8)
  • 여기에서 tensor는 파이토치가 아니라 fastai에서 구현한 함수임

- 여러개의 리스트를 모두 텐서로 바꿔보자.

seven_tensor = torch.stack([tensor(Image.open(i)) for i in sevens]).float()/255
three_tensor = torch.stack([tensor(Image.open(i)) for i in threes]).float()/255

- $X$와 $y$를 만들자.

seven_tensor.shape, three_tensor.shape
(torch.Size([6265, 28, 28]), torch.Size([6131, 28, 28]))
y=torch.tensor([0.0]*6265+ [1.0]*6131).reshape(12396,1)
X=torch.vstack([seven_tensor,three_tensor]).reshape(12396,-1)
X.shape, y.shape
(torch.Size([12396, 784]), torch.Size([12396, 1]))

기존의 MLP 모형

${\bf X} \to {\bf WX+b} \to f({\bf WX+b}) \to \dots \to {\bf y}$

  • ${\bf X}=12396 \times 784$ matrix
  • ${\bf y}=12396 \times 1$ (col) vector

- 교재의 모형

gv('''
splines=line
subgraph cluster_1{
    style=filled;
    color=lightgrey;
    "x1"
    "x2"
    ".."
    "x784"
    label = "Layer 0"
}
subgraph cluster_2{
    style=filled;
    color=lightgrey;
    "x1" -> "node1"
    "x2" -> "node1"
    ".." -> "node1"
    
    "x784" -> "node1"
    "x1" -> "node2"
    "x2" -> "node2"
    ".." -> "node2"
    "x784" -> "node2"
    
    "x1" -> "..."
    "x2" -> "..."
    ".." -> "..."
    "x784" -> "..."

    "x1" -> "node30"
    "x2" -> "node30"
    ".." -> "node30"
    "x784" -> "node30"


    label = "Layer 1: ReLU"
}
subgraph cluster_3{
    style=filled;
    color=lightgrey;
    "node1" -> "y"
    "node2" -> "y"
    "..." -> "y"
    "node30" -> "y"
    label = "Layer 2: Sigmoid"
}
''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G cluster_1 Layer 0 cluster_2 Layer 1: ReLU cluster_3 Layer 2: Sigmoid x1 x1 node1 node1 x1->node1 node2 node2 x1->node2 ... ... x1->... node30 node30 x1->node30 x2 x2 x2->node1 x2->node2 x2->... x2->node30 .. .. ..->node1 ..->node2 ..->... ..->node30 x784 x784 x784->node1 x784->node2 x784->... x784->node30 y y node1->y node2->y ...->y node30->y

- 왜 28$\times$28 이미지를 784개의 벡터로 만든 다음에 모형을 돌려야 하는가?

- 기존에 개발된 모형이 회귀분석 기반으로 되어있어서 결국 회귀분석 틀에 짜 맞추어서 이미지자료를 분석하는 느낌

- observation의 차원은 $784$가 아니라 $1\times (28\times 28)$이 되어야 맞다.

X.shape
torch.Size([12396, 784])
X=X.reshape(12396,1,28,28)
X.shape
torch.Size([12396, 1, 28, 28])
plt.imshow(X[776][0])
<matplotlib.image.AxesImage at 0x7f63903c5730>

선형변환 대신에 2d convolution with windowsize=5

c1=torch.nn.Conv2d(1,16,5) # 입력채널=1 (흑백이므로), 출력채널=16, 윈도우크기5 
X.shape, c1(X).shape
(torch.Size([12396, 1, 28, 28]), torch.Size([12396, 16, 24, 24]))
fig, axs = plt.subplots(4,4) 
k=0 
for i in range(4):
    for j in range(4):
        axs[i,j].imshow(c1(X)[776][k].data) 
        k=k+1
fig.set_figheight(8)
fig.set_figwidth(8)
fig.tight_layout()
fig

ReLU() 대신 MaxPool2d + ReLU

MaxPool2d

m1=torch.nn.MaxPool2d(2)
X.shape,c1(X).shape,m1(c1(X)).shape
(torch.Size([12396, 1, 28, 28]),
 torch.Size([12396, 16, 24, 24]),
 torch.Size([12396, 16, 12, 12]))
fig, axs = plt.subplots(4,4) 
k=0 
for i in range(4):
    for j in range(4):
        axs[i,j].imshow(m1(c1(X))[776][k].data) 
        k=k+1
fig.set_figheight(8)
fig.set_figwidth(8)
fig.tight_layout()    

ReLU

a1=torch.nn.ReLU()
X.shape,c1(X).shape, m1(c1(X)).shape, a1(m1(c1(X))).shape
(torch.Size([12396, 1, 28, 28]),
 torch.Size([12396, 16, 24, 24]),
 torch.Size([12396, 16, 12, 12]),
 torch.Size([12396, 16, 12, 12]))
fig, axs = plt.subplots(4,4) 
k=0 
for i in range(4):
    for j in range(4):
        axs[i,j].imshow(a1(m1(c1(X)))[776][k].data) 
        k=k+1
fig.set_figheight(8)
fig.set_figwidth(8)
fig.tight_layout()    
torch.manual_seed(1)
_A= torch.randn((3,3))
_A
tensor([[ 0.6614,  0.2669,  0.0617],
        [ 0.6213, -0.4519, -0.1661],
        [-1.5228,  0.3817, -1.0276]])
a1(_A)
tensor([[0.6614, 0.2669, 0.0617],
        [0.6213, 0.0000, 0.0000],
        [0.0000, 0.3817, 0.0000]])

여기에서 그냥 시그모이드에 태우자.

- 현재상황

a1(m1(c1(X))).shape
torch.Size([12396, 16, 12, 12])

- 펼치자

a1(m1(c1(X))).reshape(12396,-1).shape
torch.Size([12396, 2304])

- 2304의 디멘젼을 1로 만들자.

l1=torch.nn.Linear(in_features=2304,out_features=1) 
l1(a1(m1(c1(X))).reshape(12396,-1))
tensor([[-0.0023],
        [-0.0947],
        [ 0.0029],
        ...,
        [-0.1033],
        [-0.1207],
        [-0.1130]], grad_fn=<AddmmBackward>)

- 시그모이드를 걸자.

a2=torch.nn.Sigmoid() 
a2(l1(a1(m1(c1(X))).reshape(12396,-1)))
tensor([[0.4994],
        [0.4764],
        [0.5007],
        ...,
        [0.4742],
        [0.4699],
        [0.4718]], grad_fn=<SigmoidBackward>)

networks 설계

net = nn.Sequential(
    c1, # 컨볼루션(선형)
    m1, # 맥스풀링(비선형) -- 효과? 이미지를 계층적으로 파악할 수 있게함 
    a1, # 렐루(비선형) 
    
    a1(m1(c1(X))).reshape(12396,-1), ## 이걸 구현해야하는데?? 
    
    l1) 
## 마지막의 a2는 생략한다. torch.nn..BCEWithLogitsLoss()에 내장되어 있을것이므로 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_2953/1160457815.py in <module>
----> 1 net = nn.Sequential(
      2     c1, # 컨볼루션(선형)
      3     m1, # 맥스풀링(비선형) -- 효과? 이미지를 계층적으로 파악할 수 있게함
      4     a1, # 렐루(비선형)
      5 

~/anaconda3/envs/bda2021/lib/python3.8/site-packages/torch/nn/modules/container.py in __init__(self, *args)
     87         else:
     88             for idx, module in enumerate(args):
---> 89                 self.add_module(str(idx), module)
     90 
     91     def _get_item_by_idx(self, iterator, idx) -> T:

~/anaconda3/envs/bda2021/lib/python3.8/site-packages/torch/nn/modules/module.py in add_module(self, name, module)
    370         """
    371         if not isinstance(module, Module) and module is not None:
--> 372             raise TypeError("{} is not a Module subclass".format(
    373                 torch.typename(module)))
    374         elif not isinstance(name, torch._six.string_classes):

TypeError: torch.FloatTensor is not a Module subclass
net = nn.Sequential(
    c1, # 컨볼루션(선형)
    m1, # 맥스풀링(비선형) -- 효과? 이미지를 계층적으로 파악할 수 있게함 
    a1, # 렐루(비선형) 
    
#    a1(m1(c1(X))).reshape(12396,-1), ## 이걸 구현해야하는데?? 
    
    l1) 
## 마지막의 a2는 생략한다. torch.nn..BCEWithLogitsLoss()에 내장되어 있을것이므로 

- 결국 주석처리한 부분을 구현해야함.

- c1,m1,a1,l1의 공통점

  • 무언가를 상속받는 클래스에서 생성된 인스턴스이다.
  • forward메소드가 있다.

- custom layer를 만드는 방법

  • torch.nn.Module을 상속받아서 클래스를 하나 만든다.
  • forward 메소드를 정의한다. (다음레이어로 리턴할 값)
class Flatten(torch.nn.Module):
    def forward(self,x): 
        return x.reshape(12396,-1)
flatten=Flatten()
flatten(a1(m1(c1(X)))).shape
torch.Size([12396, 2304])

- 잘 구현이 된것 같다.

net = nn.Sequential(
    c1, # 컨볼루션(선형)
    m1, # 맥스풀링(비선형) -- 효과? 이미지를 계층적으로 파악할 수 있게함 
    a1, # 렐루(비선형) 
    flatten,#    a1(m1(c1(X))).reshape(12396,-1), ## 이걸 구현해야하는데?? 
    l1) 
## 마지막의 a2는 생략한다. torch.nn..BCEWithLogitsLoss()에 내장되어 있을것이므로 

- 손실함수와 옵티마이저 정의

loss_fn=torch.nn.BCEWithLogitsLoss()
optimizer= torch.optim.Adam(net.parameters())

- step1~4

for epoc in range(200): 
    ## 1 
    yhat=net(X)
    ## 2 
    loss=loss_fn(yhat,y) 
    ## 3 
    loss.backward()
    ## 4 
    optimizer.step()
    net.zero_grad()
plt.plot(y)
plt.plot(a2(yhat.data),'.')
[<matplotlib.lines.Line2D at 0x7f6382540a90>]
ypred=a2(yhat.data)>0.5 
sum(ypred==y)/12396
tensor([0.9927])

- 좀 더 성능이 좋아졌다. (이미 좋았는데 약간 더 좋아짐)

숙제

- torch.nn.MaxPool2d(2) 대신 torch.nn.MaxPool2d(3) 을 사용하여 모형을 학습해보고 결과비교