1. 논문 소개

'Very Deep Convolutional Networks for Large-Scale Image Recognition'

Karen Simonyan, Andrew Zisserman (ICLR 2015)

 

 

(1) Abstract

- Convolution network 깊이가 large-scale image recognition에 미치는 영향에 대한 연구를 진행함

- 매우 작은 convolution filters (3 x 3)16 ~ 19 layers 아키텍처를 구성하여 상당한 성능 개선을 이룸

- 2014년 ILSVRC에서 1, 2위를 차지함

- 다른 데이터셋에서도 잘 일반화되어 sota 결과를 얻을 수 있었음

 

 

(2) Introduction

- ConvNet이 컴퓨터 비전 분야에서의 성능 향상에 도모하는 바가 커지면서, 2012년 AlexNet의 성능을 뛰어넘기 위한 노력을 많이 하고 있음

- 본 논문에서는 'depth'에 집중하기로 함. convolutional layer를 추가함으로써 점차 깊이를 증가시켜나간다.

 

 

(3) ConvNet Configurations

1) Architecture

- input : 224 x 224 RGB image (학습 데이터의 평균으로 빼주기)

 

- kernel : 3 x 3, stride : 1, padding : 1

- MaxPooling : 2 x 2, stride : 2

 

- FC layers : 4096 channels 2개, 마지막엔 1000-way (softmax layer)

- ReLU 사용

- LRN 사용 안 함 (오히려 그런 normalization이 성능 향상에 도움이 안 되었고, 메모리 소모와 연산 시간만 늘렸다고 함)

 

 

2) Configuration

Table 1 : conv<kernel 크기>-<채널 개수>

- 깊이 11 (8 conv, 3 FC) ~ 깊이 19 (16 conv, 3 FC)

 

- 깊이에 따른 파라미터 개수인데, 크게 차이나지 않음

 

- VGG16 구조 도식화

 

 

 

3) Discussion

- VGGNet은 이전의 ILSVRC 대회에서 우수한 성능을 보인 모델들과의 상당한 차이가 있음

- 첫번째 conv layer뿐만 아니라 전체 네트워크에 걸쳐 3 x 3의 매우 작은 receptive field를 사용한다.

 

- 작은 receptive field을 사용하는 것의 이점?

1) 작은 kernel을 여러 개 사용하는 것과 큰 kernel 한 개를 사용하는 것이 동일한 크기의 피처맵을 얻을 수 있음

2) decision function을 더 discriminative하게 만들 수 있음

3) 더 적은 파라미터가 필요함

 

 

예) 2개의 3 x 3 커널을 사용하는 것과 1개의 5 x 5 커널은 동일한 크기의 피처맵 생성!

 

입력 채널, 출력 채널 모두 C개라고 가정하면,

- 2개의 3 x 3 커널 : 2 * (3 * 3 * C * C) = 18C*C

- 1개의 5 x 5 커널 : (5 * 5 * C * C) = 25C * C

 

2개의 3 x 3 커널의 파라미터보다 1개의 5 x 5 커널의 파라미터 개수가 더 많다.

 

- 1 x 1 conv. layers 사용 : receptive field에 영향을 주지 않으면서, non-linearity 증가시키기 위해서 사용 (by. ReLU)

 

- GoogLeNet도 small convolutional filters (1 x 1, 5 x 5, 3 x 3)를 사용했다는 점이 유사하지만, VGGNet 보다 훨씬 복잡하여 계산량이 어마어마하다.

 

 

 

(4) Classification Framework

1) Training

- Mini batch gradient descent (batch size : 256, momentum : 0.9)

- L2 regularization : $5 \cdot 10 ^{-4}$

- Dropout : 0.5

- 초기 learning rate : $10^{-2}$ → validation score 개선 없는 경우 10배 감소시킴 (총 3회 감소)

 

- 네트워크 가중치의 초기화가 중요하므로, 얕은 모델부터 학습 시작하고, 학습 중에 레이어가 변경될 수 있도록 했음

- Random Initialization은 정규 분포 상에서 weight를 샘플링했음 (평균 0, 표준편차 $10^{-2}$)

 

 

2) Testing

- 입력 이미지의 크기에서 training scale을 S, testing scale을 Q라고 했을 때, 둘이 서로 같을 필요는 없음

- input image 크기에 따라 variable spatial resolution을 출력하기 때문에, class score map을 'sum-pooled'을 통해 spatially average 한다.

 

 


 

2. Summary

- 네트워크의 깊이와 모델 성능 영향에 집중

- Convolution 커널 사이즈를 3 x 3으로 고정

- 커널 사이즈가 크면 이미지 사이즈 축소가 급격하게 이뤄져서 더 깊은 층을 만들기 어렵고, 파라미터 개수와 연산량도 더 많이 필요

- 여러 개의 3X3 Convolution 연산을 수행하는 것이 더 뛰어난 Feature 추출 효과를 나타냄

 

- 개별 Block내에서는 동일한 커널 크기와 Channel 개수를 적용하여 동일한 크기의 feature map들을 생성

- 이전 Block 내에 있는 Feature Map 대비 새로운 Block내에 Feature Map 크기는 2배로 줄어 들지만 채널 수는 2배로 늘어남 (맨 마지막 block 제외)

https://pub.towardsai.net/the-architecture-and-implementation-of-vgg-16-b050e5a5920b

 

 


 

3. Code

!pip install torchsummary

import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

from torch.utils.data import DataLoader, Dataset, random_split
from torch.utils.data import random_split

from torchsummary import summary

import torchvision
from torchvision.utils import make_grid
import torchvision.transforms as tr

from tqdm import trange
# mean
tr_meanRGB = [ np.mean(x.numpy(), axis=(1, 2)) for x, _ in cifar10_tr ]

tr_meanR = np.mean([m[0] for m in tr_meanRGB])
tr_meanG = np.mean([m[1] for m in tr_meanRGB])
tr_meanB = np.mean([m[2] for m in tr_meanRGB])

# std
tr_stdRGB = [ np.std(x.numpy(), axis=(1, 2)) for x, _ in cifar10_tr ]

tr_stdR = np.std([m[0] for m in tr_stdRGB])
tr_stdG = np.std([m[1] for m in tr_stdRGB])
tr_stdB = np.std([m[2] for m in tr_stdRGB])

mean = [tr_meanR, tr_meanG, tr_meanB]
std = [tr_stdR, tr_stdG, tr_stdB]


tr_transform = tr.Compose([
    tr.ToTensor(),
    tr.Resize(128),  # 크기 조정
    tr.RandomHorizontalFlip(p=0.7), 
    tr.Normalize(mean=[0.49139965, 0.48215845, 0.4465309], std=[0.060528398, 0.061124973, 0.06764512])
])

te_transform = tr.Compose([
    tr.ToTensor(),
    tr.Resize(128),  # 크기 조정
    tr.Normalize(mean=[0.49139965, 0.48215845, 0.4465309], std=[0.060528398, 0.061124973, 0.06764512])
])

tr_meanRGB, tr_stdRGB 값을 구해서 이후에는 직접 Normalize에 넣어주는 방식으로 진행했음.

 

torch.manual_seed(11)

cifar10_tr = torchvision.datasets.CIFAR10(root='./data', download=True, train=True, transform=tr_transform)

val_size = 10000
train_size = len(cifar10_tr) - val_size
cifar10_tr, cifar10_val = random_split(cifar10_tr, [train_size, val_size])

cifar10_te = torchvision.datasets.CIFAR10(root='./data', download=True, train=False, transform=te_transform)

train 데이터를 train, valid로 나누어주었고, random_split을 사용했음

 

trainloader = DataLoader(cifar10_tr, batch_size=64, shuffle=True)
valloader = DataLoader(cifar10_val, batch_size=64, shuffle=False)
testloader = DataLoader(cifar10_te, batch_size=64, shuffle=False)

train, valid, test 데이터셋을 데이터로더로 넣어주고,

 

 

시각화

for images, _ in trainloader:
    print('images.shape:', images.shape)
    
    plt.figure(figsize=(16,8))
    plt.axis('off')
    plt.imshow(make_grid(images, nrow=16).permute((1, 2, 0)))
    
    break

 

데이터 확인

images, labels = next(iter(trainloader))
print(labels)
print(images.shape, labels.shape)

 

 

VGGNet 모델 구현

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

class VGGNet(nn.Module):
    
    def __init__(self, n_classes=10):
        super().__init__()
        
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        
        self.conv2 = nn.Sequential(
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        
        self.conv3 = nn.Sequential(
            nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        
        self.conv4 = nn.Sequential(
            nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        
        self.conv5 = nn.Sequential(
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        
        self.fc6 = nn.Sequential(
            nn.Dropout(0.5),
            nn.Linear(512*4*4, 4096),
            nn.ReLU(),
        )
        
        self.fc7 = nn.Sequential(
            nn.Dropout(0.5),
            nn.Linear(4096,4096),
            nn.ReLU(),
        )
        
        self.fc8 = nn.Sequential(
            nn.Linear(4096, n_classes),
        )
    
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.conv5(x)
        x = x.view(x.size(0), -1)
        x = self.fc6(x)
        x = self.fc7(x)
        x = self.fc8(x)
        
        return x

 

 

학습 준비

model = VGGNet().to(device)
criterion = nn.CrossEntropyLoss()

optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5, verbose=True)

# early stopping
early_stopping_epochs = 5
best_loss = float('inf')
early_stop_counter = 0

summary(model, input_size=(3, 128, 128))

PyTorch에서는 early stopping을 제공하고 있지 않기 때문에, 직접 구현해주었음

 

 

학습

epochs = 30

for epoch in range(epochs):
    model.train()
    train_loss, correct, total = 0.0, 0, 0
    
    # Train
    for data in trainloader:
        images, labels = data[0].to(device), data[1].to(device)
        
        outputs = model(images)
        
        optimizer.zero_grad()
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        train_loss += loss.item()
        predicted = torch.argmax(outputs, 1)
        
        total += labels.size()[0]
        correct += (predicted == labels).sum().item()
        
    print(f'Epoch [{epoch+1}/{epochs}] || Loss:{train_loss / len(trainloader)} || Accuracy:{100 * correct / total}')


    
    # Valid
    model.eval()
    valid_loss = 0.0
    
    for data in valloader:
        images, labels = data[0].to(device), data[1].to(device)
        outputs = model(images)
        loss = criterion(outputs, labels)
        valid_loss += loss.item()
    
    if valid_loss > best_loss:
        early_stop_counter += 1
    else:
        best_loss = valid_loss
        early_stop_counter = 0
    
    if early_stop_counter >= early_stopping_epochs:
        print("Early Stopping!")
        break

 

 

Test

correct, total = 0, 0

with torch.no_grad():
    model.eval()
    test_loss = 0.0
    
    for data in testloader:
        images, labels = data[0].to(device), data[1].to(device)
        outputs = model(images)
        test_loss += loss.item()
        
        _, predicted = torch.max(outputs.data, axis=1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
        
print(f"Test loss : {test_loss / len(testloader)}")
print(f"Accuracy : {correct / total:.2f}")

1. 문제 설명

주차장의 요금표와 차량이 들어오고(입차) 나간(출차) 기록이 주어졌을 때, 차량별로 주차 요금을 계산하려고 한다.

 

어떤 차량이 입차된 후에 출차된 내역이 없다면, 23:59에 출차된 것으로 간주한다.

 

00:00부터 23:59까지의 입/출차 내역을 바탕으로 차량별 누적 주차 시간을 계산하여 요금을 일괄로 정산한다.

- 누적 주차 시간이 기본 시간 이하라면, 기본 요금을 청구한다.

- 누적 주차 시간이 기본 시간을 초과하면, 기본 요금에 더해서, 초과한 시간에 대해서 단위 시간 마다 단위 요금을 청구한다.

(단, 초과한 시간이 단위 시간으로 나누어 떨어지지 않으면, 올림한다.)

 

차량 번호가 작은 자동차부터 청구할 주차 요금을 차례대로 정수 배열에 담아서 반환하라.

 

 

 

2. 코드

import math

def solution(fees, records):
    default_time, default_fee, unit_time, unit_fee = fees[0], fees[1], fees[2], fees[3]
    
    record_dict = {}
    answer = {}
    
    # 출입 기록 저장
    for record in records:
        a, b, _ = record.split(' ')
        
        if b in record_dict.keys():
            record_dict[b].append(a)
        else:
            record_dict[b] = [a]    

    
    # 정산
    for car, in_out in record_dict.items():
        park_m = 0

        while in_out:
            if len(in_out) == 1:
                in_h, in_m = map(int, in_out[0].split(':'))
                out_h, out_m = 23, 59
                
                if out_m >= in_m:
                    park_m += (out_h - in_h) * 60 + (out_m - in_m)
                else:
                    park_m += (out_h - 1 - in_h) * 60 + (out_m + 60 - in_m)
                break
            
            else:
                in_h, in_m = map(int, in_out[0].split(':'))
                out_h, out_m = map(int, in_out[1].split(':'))
                
                if out_m >= in_m:
                    park_m += (out_h - in_h) * 60 + (out_m - in_m)
                else:
                    park_m += (out_h - 1 - in_h) * 60 + (out_m + 60 - in_m)
                    
                in_out = in_out[2:]
        
        
        if park_m <= default_time:
            answer[car] = default_fee
        else:
            answer[car] = default_fee + math.ceil((park_m - default_time) / unit_time) * unit_fee
        

    return [v for k,v in sorted(answer.items())]

 

 

 

3. 설명

우선 출입 차량에 대해서 딕셔너리로 기록하는데, 이때 in과 out은 순서가 정해져있기 때문에 신경쓰지 않았다.

 

만약 이미 딕셔너리에 저장되어있는 차량이라면, 딕셔너리 value인 리스트에 값을 추가하고,

그렇지 않은 경우에는 리스트를 추가한다.

 

저장한 출입 기록을 for문으로 돌면서 정산을 한다.

만약 in_out이 1인 경우에는 in 만 하고 out을 안 한 경우이기 때문에 나간 시간을 23:59으로 설정해준다.

 

math 라이브러리의 ceil 함수를 사용해서 올림 처리해준다.

LG Aimers에서 연세대학교 정승환 교수님 강의를 듣고 정리한 내용입니다.

틀린 부분이 있다면 댓글 부탁드립니다!

 


 

1. 품질의 정의

품질은 10개의 성질로 정의할 수 있다.

 

(1) 품질의 10가지 성질

- 고객 만족도 : 제품 출시를 앞두고 철저한 사용자 조사를 통해 기능 반영, 높은 만족도 달성

- 일관성 : 동일한 품질의 부품을 만들고, 모든 제품이 동일한 성능과 신뢰성을 유지

- 적합성 : 목적에 적합해야 함. 냉장고는 냉장 기능에 최적화 되어야 함

- 신뢰성 : 제품의 내구성을 높이기 위해 반복적으로 테스트와 개선 작업을 함

- 성능 : 소비자들의 요구를 반영하여 고성능 제품을 개발함

- 안정성 : 품질 검사를 통해 안전하고 믿을 수 있는 제품을 제공함

- 지속 가능성 : 기업, 사회, 환경적 지속 가능한 품질 관리에 앞장섬

- 효율성 : 사용자의 피드백을 반영하여 사용 편의성을 높이고, 처리 속도를 개선함

- 유지 보수성 : 유지 보수가 용이하도록 설계하며, 오랜 기간 동안 품질을 유지함

- 혁신 : 혁신적인 제품을 지속적으로 개발하고 경쟁력을 유지함

 

 

(2) 품질 관리 성공 사례 - SAMSUNG

- 첨단 기술 활용 : AI와 머신러닝을 활용한 자동 검사 시스템, IoT를 통한 예측, 유지보수 등을 도입하여 품질 문제를 사전에 예방함

- 전사적 품질 관리 문화 : 모든 직원이 품질 향상에 기여하도록 하는 문화를 구축하여, 전반적인 품질 관리 수준을 높임

- Six Sigma 도입 : 삼성은 1990년대부터 Six Sigma를 도입하여 공급망 관리를 혁신하고, 제품 결함을 줄여 운영 효율성을 극대화함

- 지속적인 개선 노력: 고객 불만 분석, 루트 원인 분석, 성과 지표 추적 등을 통해 지속적으로 품질을 개선해 옴

 


 

2. 품질 경영의 발전 과정

(1) 산업혁명과 초기 변화

- 산업 혁명은 대규모 분업을 초래하였고, 작업자들은 제품의 일부분만 담당

- 효율성은 증가했지만, 작업자들이 최종 제품을 확인하기 어려워짐에 따라 품질에 대한 책임이 작업조장에게 넘어감

- 초기 품질 검사는 비계획적이고 불완전하게 수행되어, 품질을 떨어뜨리는 결과를 낳음

 

 

(2) 2차 세계대전과 품질관리의 진전

- 2차 세계대전의 영향으로, 품질 관리의 중요성이 증대되었음. 전쟁 중 군수 물자 납품을 빠른 속도로 해야했기 때문

- 샘플링 검사법을 도입하며 품질 보증의 기초를 마련함

- 1950년대 중반에는 품질관리의 영역이 제조 과정에서 제품 설계, 원자재 입고 과정까지 확대됨

- 제품의 전체 라이프사이클을 고려한 품질관리가 시작되었음

 

 

(3) 전략적 접근과 현대적 의미

- 1970년대 후반에는 품질보증에서 전략적 품질경영으로 변화함

- 품질이 기업의 장기적인 성공에 필수적 요소로 인식되었음

- 품질은 기업 이익에 직접적인 영향을 미치며, 전사적 노력이 필요함.

 


 

3. 품질 표준

(1) ISO 9000 국제 품질 표준

https://ko.wikipedia.org/wiki/ISO_9000

- 국제적 인정 : ISO 9000 시리즈는 전 세계적으로 인정받는 품질 관리 표준임

- 품질관리 절차 : ISO 표준은 품질 관리 절차, 세부 문서, 작업 지침, 기록 유지 장려

- ISO 9001:2015 : 최신 버전은 다른 관리 시스템과의 호환성을 높이는 구조를 따름. risk-based thinking을 강조함

- 글로벌 인증 : 전 세계 201개 국가에서 160만 개 이상의 인증을 받은 글로벌 경영에 필수적인 표준임

 

 

(2) ISO 9000 관리 원칙

- 최고 경영진의 리더십

- 고객 만족

- 지속적인 개선 (PDCA, Plan Do Check Act)

- 전 조직 구성원의 참여

- 프로세스 접근법

- 데이터 기반 의사결정

- 시스템적 접근방식

- 상호 이익 공급 관계

 

 

(3) 말콤 볼드리지 국가 품질상

- 1988년 미국 정부에 의해 제정됨 : 당시 미국 경제상황은 제 2차 세계대전으로 인해 최악의 상태였음. 한편, 일본의 경제 및 상품경쟁력은 전성기를 맞고 있었는데, 이에 대해 미국은 일본의 경쟁력에 대해 다방면으로 검토

 

- Total Quality Management (TQM) 실행을 향상시키기 위해 설계됨

- 국가적 차원의 품질상이 필요함을 인식하고 성과 우수성을 달성한 조직을 인정함

 

 

(4) 볼드리지 지표 (Baldridge Criteria)

- 리더십 (120점) + 전략 (85점) + 고객 중심 (85점) + 측정, 분석, 지식 경영 (90점) + 고용 (85점) + 운영 (85점)

- 종합적으로 평가되어 조직의 품질 관리 성과를 판단함

 


 

4. 품질 비용

- 목표는 적은 비용으로 높은 품질 얻기!

 

(1) 품질 비용의 구성요소

- 평가 비용 (Appraisal Costs) : 품질 검사, 테스트 장비 비용 등

- 예방 비용 (Prevention Costs) : 직원 교육, 품질 개선 프로그램, 유지보수 비용 등

- 내부 실패 비용 (Internal Failure Costs) : 재작업 비용, 폐기 비용 등

- 외부 실패 비용 (External Failure Costs) : 고객 불만 처리 비용, 제품 반환 및 교체 비용 등

 

 

(2) 품질 비용의 관리 방법

- 평가 비용 : 효율적인 검사 및 테스트 프로세스 도입. 자동화된 검사 시스템 도입으로 오류 최소화

- 예방 비용 : 품질 교육 및 훈련 프로그램을 통해 직원의 품질 인식 향상. 품질 개선 프로젝트를 통해 결함 발생 가능성 감소

- 내부 실패 비용 : 결함 발생 시 신속한 문제 해결과 재작업으로 비용 최소화. 원인 분석을 통해 재발 방지 대책 마련

- 외부 실패 비용 : 고객 피드백 시스템을 통해 문제를 조기에 발견하고 해결. 제품 보증 및 애프터 서비스 강화로 고객 신뢰 유지

 


 

5. 품질의 리더

+ Recent posts