728x90

2023-11-15 50th Class

MLP

#️⃣ 레이어 2개로 XOR 구현

이전 코드와 달라진 점

class Model:  
    def __init__(self):  
        self.affine1 = AffineFunction(2, 2)  
        self.sigmoid1 = Sigmoid()  
        self.affine2 = AffineFunction(2, 1)  
        self.sigmoid2 = Sigmoid()
  • Pytorch nn.Linear와 유사하게 만들기 위해 입출력 노드 구현
  • Model에서 afifne 함수는 input size와 output size 를 설정함
  • 따라서 해당 레이어 층에 노드가 2개라면 weight는 (2, 2), bias는 (2,)로 생성됨
  • 기존의 1개짜리 노드로 했을 때는 1차원 벡터였지만 이 기능을 통해 2차원으로 증강됨

self.w = np.random.randn(in_size, out_size)

[[-0.29198432  0.61461488], 
 [-0.79208399  2.26416964]]

self.b = np.random.randn(out_size)

[-1.2587527  -0.13997688]

MLP 아키텍처

Drawing 2023-11-15 18.26.16.excalidraw.png

code

import numpy as np  
from sklearn.datasets import make_blobs  
import matplotlib.pyplot as plt  
  
  
def plot_regression_line(axe, b, w1, w2):  
    X = np.linspace(-10, 10, 100)  
    Y = w1 * X + w2 * X**2 + b  
  
    axe.plot(X, Y)  
  
class AffineFunction:  
    def __init__(self, in_size, out_size):  
        self.w = np.random.randn(in_size, out_size)  
        self.b = np.random.randn(out_size)  
  
    def forward(self, X):  
        self.X = X  
        self.z = np.dot(X, self.w) + self.b  
        return self.z  
  
    def backward(self, dJ_dz, lr):  
        # 0. 기본 식:  
        # w1 := w1 - LR * [w1에 대한 미분값(dz_dw1) * 시그모이드에서 넘어온 미분값(dJ_dz)]  
        # w2 := w2 - LR * [w2에 대한 미분값(dz_dw2) * 시그모이드에서 넘어온 미분값(dJ_dz)]  
        # b := b - LR * [b에 대한 미분값(dz_db) * 시그모이드에서 넘어온 미분값(dJ_dz)]  
  
        # 1. 파라미터 별 미분 값 구하기  
        dz_dw = self.X  
        dz_db = 1  
  
        # 노드 개수를 차원으로 설정  
        curr_dim = 1 if np.isscalar(dJ_dz) else len(dJ_dz)  
  
        # 2. 현재 affine의 weight 미분값 * sigmoid의 미분값  
        dJ_dw = (dz_dw * dJ_dz).reshape(-1, curr_dim)  
        dJ_db = dz_db * dJ_dz  
  
        # 3. learning rate와 미분계수 끼리의 곱을 곱하여 파라미터 업데이트  
        self.w -= lr * dJ_dw  
        self.b -= lr * dJ_db  
  
        return self.w  
  
  
  
class Sigmoid:  
    def forward(self, z):  
        # z가 scalar인 경우 array []를 풀기  
        if z.shape[0] == 1:  
            self.z = z[0]  
            self.a = 1 / (1 + np.exp(-self.z))  
            return self.a  
  
        self.z = z  
        self.a = 1 / (1 + np.exp(-self.z))  
        return self.a  
  
  
    def backward(self, dJ_dpred):  
        # 0. 기본 식: 시그모이드 미분값(da_dz) * 로스 미분값(dJ_dpred)  
  
        # 2개 이상의 노드에서 sigmoid backpropagation을 하는 경우  
        if not np.isscalar(dJ_dpred):  
            dJ_dpred = dJ_dpred.flatten()  
  
        # 1. 시그모이드 미분값 da_dz = a(1-a)        
        da_dz = self.a * (1 - self.a)  
        # 2. 최종 식 da_dz * dJ_pred        
        dJ_dz = da_dz * dJ_dpred  
        return dJ_dz  
  
  
  
class BCELoss:  
    def __call__(self, pred, y):  
        # 1. loss 계산  
        J = -(y * np.log(pred) + (1 - y) * np.log(1 - pred))  
        # 2. loss 미분 값계산:  dJ /d yhat  
        dJ_dpred = (pred - y) / (pred + 1e-7 * (1 - pred + 1e-7))  
        # 분모의 + 1e-7는 ZeroDivision Error 막기 위해 추가함 (0으로 나누면 에러나서)  
        return J, dJ_dpred  
  
  
class Model:  
    def __init__(self):  
        self.affine1 = AffineFunction(2, 2)  
        self.sigmoid1 = Sigmoid()  
        self.affine2 = AffineFunction(2, 1)  
        self.sigmoid2 = Sigmoid()  
  
    def forward(self, X):  
        z1 = self.affine1.forward(X)  
        a1 = self.sigmoid1.forward(z1)  
        z2 = self.affine2.forward(a1)  
        pred = self.sigmoid2.forward(z2)  
        return pred  
  
    def backward(self, dJ_da2, lr):  
        dJ_dz2 = self.sigmoid2.backward(dJ_da2)  
        dJ_da1 = self.affine2.backward(dJ_dz2, lr)  
  
        dJ_dz1 = self.sigmoid1.backward(dJ_da1)  
        dJ_dX = self.affine1.backward(dJ_dz1, lr)  
  
        return dJ_dX  
  
  
  
def calculate_accuracy(preds, targets):  
    rounded_preds = np.round(preds)  
    correct = (rounded_preds == targets).sum().item()  
    accuracy = correct / len(targets)  
    return accuracy  
  
  
def main_routine():  
    N_SAMPLES = 100  
    LR = 0.001  
    EPOCHS = 30  
  
    X = [[0, 0], [0, 1], [1, 0], [1, 1]] * 100  
    y = [0, 1, 1, 0] * 100  
  
    '''Instantiation'''  
    model = Model()  
    loss_fn = BCELoss()  
  
    epoch_accuracy = list()  
    epoch_loss = list()  
  
    # fig, axe = plt.subplots(figsize=(14, 14))  
  
    for epoch in range(EPOCHS):  
        pred_list = list()  
        loss_list = list()  
  
        for X_, y_ in zip(X, y):  
            '''Training'''  
            pred = model.forward(X_)  
            loss, dJ_dpred = loss_fn(pred, y_)  
            model.backward(dJ_dpred, LR)  
  
            '''Metric(loss, accuracy) Calculations'''  
            pred_list.append(pred)  
            loss_list.append(loss)  
  

        epoch_accuracy.append(calculate_accuracy(pred_list, y))  
        epoch_loss.append(sum(loss_list)/len(loss_list))  
  
    '''Result Visualization'''  
    fig, axes = plt.subplots(2, 1, figsize=(10, 5))  
    axes[0].plot(epoch_loss, linestyle='-')  
    axes[0].set_ylabel("BCELoss", fontsize=15)  
    axes[1].plot(epoch_accuracy, linestyle='-')  
    axes[1].set_ylabel("Accuracy", fontsize=15)  
    axes[1].set_xlabel("Epoch", fontsize=15)  
    fig.tight_layout()  
    plt.show()  
  
  
if __name__ == '__main__':  
    main_routine()

xor 231116.png

시각화코드는 작성중

반응형