본문 바로가기
CV/Coding

LeNet

by BaekDaBang 2024. 4. 17.

1. Introduction

 

입력이미지 → C1 TANH   S2   C3   TANH   S4   C5   TANH   FC6   SOFTMAX7
- C는 합성곱층, S는 풀링층(서브샘플링층), FC는 전결합층
- 1998년에는 ReLU가 발견되기 이전이었고, tanh와 시그모이드 함수가 주로 사용되는 것이 일반적이었음

 

LeNet-5는 미리 설정된 일정엔 맞춰 학습률을 감소시키는 학습률 감쇠를 사용
- 처음 2 에포크에는 0.005, 그 다음 3 에포크는 0.0002, 다시 그 다음 4 에포크에는 0.00005, 그 이후로는 0.00001의 학습률이 적용됨.
- 논문의 실험에서는 20 에포크까지 학습을 수행

 

2. Code

Hyperparameter

# 하이퍼파라미터
from torch.optim import Adam, lr_scheduler

epochs = 3
batch_size = 128
lr=1e-3
loss = nn.CrossEntropy().cuda()

optimizer = Adam(model.aprameters(),
                 lr=lr,
                 eps=1e-07)

 

Model

import torch.nn as nn

class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.conv1 = nn.Conv2d(1,6,kernel_size=5,stride=1,padding='same')
        self.conv2 = nn.Conv2d(6,16,kernel_size=5,stride=1,padding='valid')
        self.conv3 = nn.Conv2d(16,120,kernel_size=5,padding='valid')
        self.pool = nn.AvgPool2d(kernel_size=2)
        self.tanh = nn.Tanh()
        self.flat = nn.Flatten()
        self.fc1 = nn.Linear(120, 84)
        self.fc2 = nn.Linear(84, 10)
        
    def forward(self, image):
        out = self.tanh(self.conv1(image))
        out = self.pool(out)
        out = self.tanh(self.conv2(out))
        out = self.pool(out)
        out = self.tanh(self.conv3(out))
        out = self.flat(out)
        out = self.tanh(self.fc1(out))
        out = self.fc2(out)
        
        return out

 

Train

import time
from tqdm import tqdm

# 모델 시각화를 위해 정확도를 저장할 리스트 생성
train_acc = list()
eval_acc = list()

for epoch in range(epochs):
    start = time.time()
    print(f'Epoch {epoch}/{epochs}')
    
    train_loss = 0
    correct = 0
    
    # learning rate scheduling
    # 0~2:0.0005, 3~5:0.0002, 6~9:0.00005, 10~20:0.00001
    if epoch <= 2 :
        optimizer.param_groups[0]['lr'] = 0.0005
    elif epoch > 2 and epoch <= 5 :
        optimizer.param_groups[0]['lr'] = 0.0002
    elif epoch > 5 and epoch <= 9 :
        optimizer.param_groups[0]['lr'] = 0.00005
    else :
        optimizer.param_groups[0]['lr'] = 0.00001
    
    
    # train
    for x,y in tqdm(train_loader):
        optimizer.zero_grad()
        y_pred = model(x.cuda())
        cost = loss(y_pred, y.cuda())
        
        cost.backward()
        optimizer.step()
        train_loss += cost.item()
        
        pred = y_pred.data.max(1, keepdim=True)[1]  # 각 클래스의 확률 값 중 가장 큰 값을 가지는 클래스의 인덱스를 pred 변수로 받음
        
        # 맞은 개수를 구하는 과정
        correct += pred.cpu().eq(y.data.view_as(pred)).sum()  # view_as함수는 들어가는 인수의 모양으로 맞춰주고, 
                                                              #.eq()를 통해 y_pred와 y의 값이 동일한지 판단하여 True 개수 구하기
                
        train_loss /= len(trainloader) 
        train_accuracy = correct / len(trainloader.dataset)
        train_accuracies.append(train_accuracy)
        
    
    # eval
    eval_loss = 0
    correct = 0
    with torch.no_grad():  # 학습하지 않음
        model.eval()
        for x,y in tqdm(test_loader):
            y_pred = model(x.cuda()) 
            cost = loss(y_pred,y.cuda())## loss 함수를 이용하여 test 데이터의 오차를 계산함
            eval_loss += cost
            
            pred = y_pred.data.max(1, keepdim=True)[1]  # 각 클래스의 확률 값 중 가장 큰 값을 가지는 클래스의 인덱스를 pred 변수로 받음
            
            correct += pred.cpu().eq(y.data.view_as(pred)).cpu().sum()  # view_as함수는 들어가는 인수의 모양으로 맞춰주고, 
                                                                        #.eq()를 통해 y_pred와 y의 값이 동일한지 판단하여 True 개수 구하기
            
        eval_loss /= len(testloader)
        eval_accuracy = correct / len(testloader.dataset)
        eval_accuracies.append(eval_accuracy)
        
        ## test 데이터의 loss를 기준으로 이전 loss 보다 작을 경우 체크포인트 저장
        if eval_loss < best_loss:
            torch.save({
                'epoch': epoch,
                'model': model,
                'model_state_dict': model.state_dict(),
                'optimizer_state_dict': optimizer.state_dict(),
                'loss': cost.item,
                }, './bestCheckPiont.pth')
            
            print(f'Epoch {epoch:05d}: val_loss improved from {best_loss:.5f} to {eval_loss:.5f}, saving model to bestCheckPiont.pth')
            best_loss = eval_loss
    
        else:
            print(f'Epoch {epoch:05d}: val_loss did not improve')
            
        model.train()
        
    print(f'{int(time.time() - start)}s - loss: {train_loss:.5f} - acc: {train_accuracy:.5f} - val_loss: {eval_loss:.5f} - val_acc: {eval_accuracy:.5f}')

 

Evaluation

# Load Checkpoint
best_model = torch.load('./bestCheckPiont.pth')['model']  # 전체 모델을 통째로 불러옴, 클래스 선언 필수
best_model.load_state_dict(torch.load('./bestCheckPiont.pth')['model_state_dict'])  # state_dict를 불러 온 후, 모델에 저장

# Eval
correct = 0
with torch.no_grad():  # 학습하지 않기 위해
    best_model.eval()
    for x, target in tqdm(testloader):
        y_pred = best_model(x.cuda())
        
        pred = y_pred.data.max(1, keepdim=True)[1]
        correct += pred.cpu().eq(target.data.view_as(pred)).cpu().sum()
        
    eval_accuracy = correct / len(testloader.dataset)

print(f"Test accuracy: {100.* eval_accuracy:.4f}%")

'CV > Coding' 카테고리의 다른 글

ResNet  (0) 2024.04.22
GoogLeNet  (0) 2024.04.22
VGG  (0) 2024.04.22
AlexNet  (0) 2024.04.22