[D&A 운영진 딥러닝 스터디] 1주차 2차시

권유진·2022년 1월 6일
0

D&A 운영진 스터디

목록 보기
2/17

퍼셉트론

다수의 신호를 입력 받아 하나의 신호를 출력하는 것

  • 신호가 흐른다/안 흐른다(1/0)으로 나뉜다.
  • 인간의 뇌에 있는 뉴런(neuron)을 본떠 만듦(신호 전달)
  • linear classifier 수행
y={0w1x1+w2x2θ1w1x1+w2x2>θy = \left\{ \begin{matrix} 0& w_1x_1+w_2x_2 \le \theta\\ 1& w_1x_1+w_2x_2 > \theta \end{matrix}\right.

θ\thetabb(편향, bias)로 치환 시,

y={0w1x1+w2x2+b01w1x1+w2x2+b>0y = \left\{ \begin{matrix} 0& w_1x_1+w_2x_2+b \le 0\\ 1& w_1x_1+w_2x_2+b > 0 \end{matrix}\right.

신호의 총 합이 θ\theta를 넘을 때만 1 출력 (θ\theta: 임계값)
\to 뉴런이 활성화 한다.

1. AND 게이트 구현

x1x_1x2x_2yy
000
010
100
111

이를 만족하는 매개변수(w1,w2,θ(b)w_1, w_2, \theta(b))가 무수히 많다.

w1w_1w2w_2θ\theta
0.50.50.7
0.50.51
.........

2. NAND 게이트 구현

AND 게이트 매개변수 반전

w1w_1w2w_2θ\theta
-0.5-0.5-0.7
-0.5-0.5-1
.........

3. OR 게이트 구현

w1w_1w2w_2θ\theta
0.50.50.4
.........

4. XOR 게이트 구현

배타적 논리합, 어느 한쪽만 1인 경우

  • 단층 퍼센트론으로는 구현 불가 (\because 선형으로 분류 불가)
  • 다층 퍼셉트론(비선형)으로 구현 가능!
x1x_1x2x_2s1s_1s2s_2yy
00100
01111
10111
11010

x1x_1, x2x_2는 입력, s1s_1는 NAND의 출력, s2s_2는 OR의 출력

\therefore 이와 같이 퍼셉트론의 층을 깊게 쌓아 매우 복잡한 것 구현 가능

다층 퍼셉트론은 복잡한 함수도 표현할 수 있지만, 가중치를 설정하는 작업을 사람이 수동으로 해야한다.
\to 신경망이 자동으로 해결

# data
X = torch.FloatTensor([ [0,0], [0,1], [1,0], [1,1] ]).to(device)
Y = torch.FloatTensor([ [0], [1], [2], [3] ]).to(device)

# layers
linear1 = torch.nn.Linear(2, 2, bias=True) # input_size = 2, hidden_size = 2, 편향 존재
linear2 = torch.nn.Linear(2, 1, bias=True) # hidden_size=2, output_size = 1, 편향 존재
sigmoid = torch.nn.Sigmoid() # 활성화함수 - 시그모이드

model = torch.nn.Sequential(linear1, sigmoid, linear2, sigmoid).to(device) # 모델 연결

# loss & optimizer
criterion = torch.nn.BCELoss().to(device) # Binary CrossEntropy
optimizer = torch.optim.SGD(model.parameters(), lr=1) # SGD를 통해 model의 파라미터 학습(학습률=1)

# training
for step in range(10001):
    optimizer.zero_grad() # 기울기 고정
    hypothesis = model(X) # 예측값
    cost = criterion(hypothesis, Y) # 실제값과 예측값을 통한 loss 계산
    cost.backward() # 손실함수를 통해 오차역전파법 실행
    optimizer.step() # 오차역전파법을 통해 얻는 기울기를 이용해 파라미터 갱신
    if step%100 == 0: # 100 epochs마다
    	print(f'step: {step}\nloss: {cost.item()}') # step과 loss 출력

신경망(Neural Network)

- 한 층에서의 출력 과정
a=w1x1+w2x2+by=h(a)\begin{aligned} a &= w_1x_1+w_2x_2+b\\ y &= h(a) \end{aligned}

1. 활성화 함수(activation function)

  • 입력 신호의 총합을 출력 신호로 변환하는 함수
  • 입력 신호의 총합이 활성화를 일으키는지 정함
  • 위의 식에서 h를 의미
torch.nn.linear(x)
torch.nn.sigmoid(x)
torch.nn.tanh(x)
torch.nn.relu(x)
torch.nn.leaky_relu(x, 0.01)
  1. 계단 함수(Step Function)
  • 임계값(θ\theta)을 경계로 출력이 변하는 함수
  • 퍼셉트론에서 사용하는 활성화 함수
  • 0과 1로 밖에 출력할 수 없다.(다중 출력 불가)
  1. 선형 함수(Linear Activation Function)
h(x)=cx+bh(x) = cx+b
  • 선형으로 이루어진 함수
  • 다중 출력 가능
  • 오차역전파법(BackPropagation) 불가
  • 여려겹으로 쌓아도 출력 cxc'x로 동일
  1. 시그모이드 함수(Sigmoid Function)
σ(x)=11+ex\sigma(x) = \cfrac{1}{1+e^{-x}}
  • 출력 값 범위가 0~1이다.
  • 라장 많이 사용되는 활성화 함수
  • Saturation(포화상태) 발생
    • 오차역전파법 사용 시 미분 값을 사용해야 하는데, 미분 값이 σ(x)(1σ(x))\sigma(x)(1-\sigma(x))이므로 최대값이 0.25이다.
    • 그러므로 함수의 기울기가 0에 가까워져 Gradient Vanishing 문제 야기
  • non-zero centered
  1. 쌍곡탄젠트 함수(Hyperbolic Tangent Function)
tanh(x)=sinh(x)cosh(x)\tanh(x) = \cfrac{\sinh(x)}{\cosh(x)}
  • 출력 값의 범위: -1 ~ 1
    • zero-centered
  • Gradient Vanishing 문제 야기
    • 미분 시 (1tanh(x))(1+tanh(x))(1-tanh(x))(1+tanh(x))이기 때문
  1. ReLU함수(Rectified Linear Unit)
ReLU(x)={0,  ifx<0x,  ifx0ReLU(x) = \begin{cases} 0, \; if \, x<0\\ x, \; if \, x\ge0 \end{cases}
  • 양의 값에서는 Saturation 발생하지 않음(gradient가 0 or 1)
  • 계산 효율이 뛰어남(빠름)
  • 비선형함수
  • non-zero centered
  • Dying ReLU: 입력 값이 0 이하일 때, gradient가 0이 되어 학습을 하지 못함.
  1. Leaky ReLU
Leaky_ReLU(x)={0.01x,  ifx<0x,  ifx0Leaky\_ReLU(x) = \begin{cases} 0.01x, \; if \, x<0\\ x, \; if \, x\ge0 \end{cases}
  • ReLU와 유사하지만 0 이하의 범위에서 더이상 0이 아니다.
  1. PReLU(Parametric ReLU)
Leaky_ReLU(x)={αx,  ifx<0x,  ifx0Leaky\_ReLU(x) = \begin{cases} \alpha x, \; if \, x<0\\ x, \; if \, x\ge0 \end{cases}
  • 0 이하일 때, 기울기가 α\alpha인 Leaky ReLU
  • α\alpha는 Backpropagation으로 학습
  1. ELU(Exponential Linear Unit)
ELU(x)={α(ex1),  ifx<0x,  ifx0ELU(x) = \begin{cases} \alpha (e^x -1), \; if \, x<0\\ x, \; if \, x\ge0 \end{cases}
  • zero-mean에 가까운 출력 값 보임
  • ReLU의 모든 장점 및 Dying ReLU 문제 해결
  • 지수함수를 사용해 연산비용 추가
  • 큰 음수 값에 쉽게 포화
  1. Maxout Neuron
    maxout(x)=max(w1Tx+b1,wxTx+b2)maxout(x) = max(w_1^Tx+b_1,\, w_x^Tx+b_2)
  • ReLU와 LeakyReLU의 더 일반화 된 형태(두 개의 선형 함수를 취하기 때문)
  • 선형이기 때문에 Saturation 발생하지 않는다.
  • Dropout과 함께 사용하기 좋음
  • neuron 당 parameter 수가 두 배가 됨(계산량 많고 복잡)
  1. 출력층 활성화함수
  • 분류 문제
    • 이진 분류: 시그모이드 함수
    • 다지 분류: 소프트맥스 함수(softmax)
      yk=eakΣk=1neaky_k = \cfrac{e^{a_k}}{\Sigma_{k=1}^{n}{e^{a_k}}}

      지수함수가 매우 쉽게 커지기 때문에 오버플로 문제 발생
      \rarr C를 대입하여 개선(C는 aka_k의 최대값)

      yk=eak+lnCΣk=1neak+lnCy_k = \cfrac{e^{a_k+\ln{C}}}{\Sigma_{k=1}^{n}{e^{a_k+\ln{C}}}}

      시그모이드 함수 일반화한 형태

  • 회귀문제
    • 항등함수
      y=xy = x

신경망 학습

훈련데이터로부터 매개변수의 최적값을 자동으로 획득
      \;\;\; \rarr 자동으로 학습할 때 손실함수(Loss)를 지표로 사용!
            \;\;\;\;\;\; \rarr 손실함수를 최소화하는 매개변수를 찾는다.
                  \;\;\;\;\;\;\;\;\; \rarr 미분, 기울기 이용

손실함수

  1. 오차제곱합(SSE; Sum of Squared Error)
    E=12Σk=1n(yktk)2E = \cfrac{1}{2}\Sigma_{k=1}^{n}{(y_k-t_k)^2}
  2. 교차엔트로피(CEE; Cross-Entropy Error)
    E=Σk=1ntkloge(yk+C)E = -\Sigma_{k=1}^{n}{t_k\log_e{(y_k+C)}}
  • 실제로 정답일 때의 예측값의 자연로그 계산(tk=1t_k=1)
  • 정답일 때의 출력이 전체 값을 정함
  • yk=0y_k=0이면 EEinf-\inf이므로 loge\log_e에 C를 더해줌

Gradient Update

  1. Batch Gradient Descent
    모든 학습 데이터를 사용하여 Graident 계산 및 Update
  2. Stochastic Gradient Descent
    한 개의 학습 데이터를 사용하여 Gradient 계산 및 Update
  3. Mini-batch Gradient Descent
    모든 학습 데이터를 Mini-batch로 쪼개어 한 번의 Gradient 계산 및 Update에 한 개의 Mini-batch 사용

\therefore 속도와 최소값의 수렴을 고려하여 미니배치 경사하강법 채택

  • 각 미니배치별 손실함수 = 각 데이터의 손실함수 평균

    SSE=12NΣnΣk(ynktnk)2CEE=1NΣnΣk(tnklogeynk)SSE = -\cfrac{1}{2N}\Sigma_n\Sigma_k{(y_{nk}-t_{nk})^2}\\ CEE = -\cfrac{1}{N}\Sigma_n\Sigma_k{(-t_{nk}\log_e{y_{nk}})}
  • 정확도를 손실함수로 사용하지 않는 이유

    • 매개변수의 미분값이 대부분의 장소에서 0이기 때문
    • 정확도 개선 시 불연속적으로 개선 ex)  333435ex)\; 33 \rarr 34 \rarr 35
    • 계단 함수를 활성화 함수로 사용하지 않는 이유 중 하나
    • 시그모이드 함수는 어디에서도 기울기가 0이 아니기에 적합
      • Gradient Vanishing 문제 보유하고 있긴 함

경사하강법

기울기를 잘 이용해 함수의 최소값을 찾아가는 방법

  • 현 위치에서 기울기를 구해 기울어진 방향으로 일정 거리만큼 이동하기를 반복
    xt=xt1ηx0ft1x_t = x_{t-1} - \eta \cfrac{\partial}{\partial x_0}f_{t-1}
  • 기울기(경사): 모든 변수의 편미분을 벡터로 정리한 것(수치미분을 이용하여 계산)
    • 기울기가 0이라고 최소값인 것은 아니다. (\because 극소점, 안장점)
    • 기울기는 수치미분을 통해 구하는 것은 단순하지만 시간이 오래걸린다.
      • 이를 개선하여 오차역전파법 사용

MNIST 예제

1) 모두를 위한 딥러닝

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

# hyperparameter
learning_rate = 0.001
training_epochs = 15
batch_size = 100

# load data
import torchvision.datasets as dsets # 데이터 불러오는 패키지
from torchvision import transforms

mnist_train = dsets.MNIST(root='MNIST_data/', train=True,
				transform=transforms.ToTensor())
mnist_test = dsets.MNIST(root='MNIST_data/', train=False,
				transform=transforms.ToTensor())
# root: 데이터 저장 위치, train data 여부, transofrm: 변환 적용 종류

data_loader = torch.utils.DataLoader(DataLoader=mnist_train,
	batch_size=batch_size, shuffle=True, drop_last=True) # 데이터 불러오기
    
# modeling
linear1 = torch.nn.Linear(28*28, 256, bias=True).to(device)
linear2 = torch.nn.Linear(256, 256, bias=True).to(device)
linear3 = torch.nn.Linear(256, 10, bias=True).to(device)
relu = torch.nn.ReLU()

torch.nn.init.normal_(linear1.weight) # 가중치를 정규분포로 초기화
torch.nn.init.normal_(linear2.weight)
torch.nn.init.normal_(linear3.weight)

model.nn.Sequential(linear1, relu, linear2, relu, linear3) # 마지막은 softmax로

criterion = torch.nn.CrossEntropyLoss().to(device) # 내부적으로 softmax 실행
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) # Adam으로 파라미터 최적화

for epoch in range(training_epochs): # epochs만큼 반복 학습
    avg_cost = 0 # 손실함수 초기화
    total_batch = len(data_loader)
    
    for X,Y in data_loader:
      X = X.view(-1, 28*28).to(device) # flatten
      optimizer.zero_grad() # gradient 초기화
      hypothesis = model(X) # 모델 예측값
      cost = criterion(hypothesis, Y) # 손실함수 계산
      cost.backward() # 오차역전파법 수행
      avg_cost += cost/total_batch
    print(f'Epoch: {epoch+1}, cost: {avg_cost}')

2) 파이썬 딥러닝 파이토치

if torch.cuda.is_available():
    device = torch.device('cuda')
else:
    device = torch.device('cpu')
    
batch_size = 32
epochs = 10

# load data
from torchvision import transforms, datasets

train_dataset = datasets.MNIST(root = 'data/MNIST',
                              train = True, download = True,
                              transform = transforms.ToTensor())
# root: 데이터 저장 장소, train: 학습용 데이터 여부
# download: 다운로드 시행할 것인지 여부, transform: 기본적인 전처리
test_dataset = datasets.MNIST(root = 'data/MNIST',
                              train = False,
                              transform = transforms.ToTensor())

train_loader = torch.utils.data.DataLoader(dataset = train_dataset,
                                          batch_size = batch_size,
                                          shuffle = True)
# 다운로드한 데이터셋 mini-batch단위로 분리해 지정
# 순서를 암기할 수 있으므로 shuffle
test_loader = torch.utils.data.DataLoader(dataset = test_dataset,
                                         batch_size = batch_size,
                                         shuffle = False)

# 모델 구조 정의
class Net(torch.nn.Module):
    # pytorch 내에 딥러닝 관련 기본 함수를 포함하는 nn.Module 클래스 상속
    # nn.Module 클래스에서 이용할 수 있는 모든 함수 사용 가능
    def __init__(self): # 인스턴스를 생성했을 시 지니게 되는 성질 정의
        super(Net, self).__init__() # nn.Module 내에 있는 메소드 상속받아 이용
        self.fc1 = torch.nn.Linear(28*28, 512)
        self.fc2 = torch.nn.Linear(512, 256)
        self.fc3 = torch.nn.Linear(256,10)
    def forward(self, x):
        x = x.view(-1, 28*28) # flatten
        x = self.fc1(x)
        x = torch.nn.functional.sigmoid(x)
        x = self.fc2(x)
        x = torch.nn.functional.sigmoid(x)
        x = self.fc3(x)
        return x

# model, optimizer, criterion
model = Net().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, mementum=0.5)
criterion = torch.nn.CrossEntropyLoss() # 내부적으로 softmax 실행

def train(model, train_loader, optimizer, log_interval):
    model.train() # 모델을 학습상태로 지정
    for batch_idx, (image, label) in train_loader:
        image = image.to(device)
        label = label.to(device)
        optimizer.zero_grad() # gradient 초기화
        output = model(image) # input으로 output 계산
        loss = criterion(output, label)
        loss.backward() # 오차역전파법 수행
        optimizer.step() # 파라미터 업데이트
        
        if batch_idx % log_interval == 0:
            print("Train Epoch: {} [{}/{} ({:.0f}%)]\tTrain Loss: {:.6f}".format(
                epoch, batch_idx * len(image), 
                len(train_loader.dataset), 100. * batch_idx / len(train_loader), 
                loss.item()))

def evaluate(model, test_loader):
    model.eval() # 모델을 평가상태로 지정
    test_loss = 0
    correct = 0
    
    with torch.no_grad(): # 평가 과정에서 gradient 업데이트를 하지 않기 위해
        for image, label in test_loader:
            image = image.to(device)
            label = label.to(device)
            output = model(image)
            test_loss += criterion(output, label).item()
            
            prediction = output.max(1, keepdim=True)[1] # 벡터 값 내 최대값으로 예측
            correct += prediction.eq(label.view_as(prediction)).sum().item()
            
    test_loss /= len(test_loader.dataset)
    test_accuracy = 100 * correct /len(test_loader.dataset)
    
    return test_loss, test_accuracy

# 반복 학습
for epoch in range(1, epoch+1):
    train(model, train_loader, optimizer, 200)
    test_loss, test_accuracy = evaluate(model, test_loader)
    
    print("\n[EPOCH: {}], \tTest Loss: {:.4f}, \tTest Accuracy: {:.2f} % \n".format(
        epoch, test_loss, test_accuracy))

참고
모두를 위한 딥러닝 시즌 2 Lab 8-1, 8-2, 9-1
밑바닥부터 시작하는 딥러닝 <사이토 고키>
파이썬 딥러닝 파이토치 (이경택, 방성수, 안상준)

profile
데이터사이언스를 공부하는 권유진입니다.

0개의 댓글