본문 바로가기
  • GDG on campus Ewha Tech Blog
3-2기 스터디/NLP 입문

[1주차] NLP Chapter1. 신경망 복습

by chaerin314 2022. 4. 6.

Chapter1. 신경망 복습

1.1. 수학과 파이썬 복습

1.2. 신경망의 추론

    • 2층 신경망
    • 입력층/은닉층/출력층 = (X) / (Affine+Sigmoid) / (Affine+S)
      • Affine
      •    $X \cdot W+B$ : 신경망의 순전파에서 수행하는 행렬의 곱
      • Sigmoid
      •    $\sigma(x) = {1 \over1+e^{-x}} $ : 비선형 활성화 함수

*추론 과정에서는 출력층에서 굳이 Softmax를 적용시키지 않고 가장 높은 score를 정답으로 판단한다.

1.3. 신경망의 학습

  • 2층 신경망 입력층/은닉층/출력층 = (X) / (Affine+Sigmoid) / (Affine+Softmax+Cross Entropy Error+L)
    • Softmax
    •    $y = {e^{a_k} \over \sum e^{a}}$ : 출력층의 활성화 함수, 다중클래스 분류 문제에서 사용
    • Cross Entropy Error
      • $L = -\sum t \cdot logy$ : 보통 다중클래스 분류 신경망에서 사용되는 loss function
         *N개씩 미니배치 처리시 $L = -{1\over N}\sum\sum t \cdot logy$
  • Back-propagation
    • 덧셈 노드 : gradient distrubutor
    • Sum 노드 : gradient distrubutor (N개로)
    • 곱셈 노드 : gradient switcher
    • 분기 노드 : 기울기들의 합
    • Repeat 노드 : 기울기 N개의 합
      • *Repeat $\leftrightarrow$Sum (반대 관계)
    • MatMul 노드 : ${{\partial L}\over {\partial x}} = {{\partial L}\over {\partial y}}W^T$
  • Deep copy

파이썬의 생략 기호는 데이터를 덮어쓴다.

  • 신경망 학습의 순서 (1~3 반복)
    1. 1. 미니배치 2. 기울기 계산 3. 매개변수 갱신
  • SGD (Stochastic Gradient Descent)
  • 무작위로 선택된 데이터(미니배치)에 대해 기울기를 이용하는 방식

1.4. 신경망으로 문제를 풀다

    1. 신경망 구현
class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size):
        I, H, O = input_size, hidden_size, output_size

        # 가중치와 편향 초기화
        W1 = 0.01 * np.random.randn(I, H)
        b1 = np.zeros(H)
        W2 = 0.01 * np.random.randn(H, O)
        b2 = np.zeros(O)

        # 계층 생성
        self.layers = [
            Affine(W1, b1),
            Sigmoid(),
            Affine(W2, b2)
        ]
        self.loss_layer = SoftmaxWithLoss()

        # 모든 가중치와 기울기를 리스트에 모은다.
        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

    def predict(self, x):
        for layer in self.layers:
            x = layer.forward(x)
        return x

    def forward(self, x, t):
        score = self.predict(x)
        loss = self.loss_layer.forward(score, t)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout
    2. 학습용 코드
# 하이퍼파라미터 설정
max_epoch = 300
batch_size = 30
hidden_size = 10
learning_rate = 1.0

# 데이터 읽기, 모델과 옵티마이저 생성
x, t = spiral.load_data()
model = TwoLayerNet(input_size=2, hidden_size=hidden_size, output_size=3)
optimizer = SGD(lr=learning_rate)

# 학습에 사용하는 변수
data_size = len(x)
max_iters = data_size // batch_size
total_loss = 0
loss_count = 0
loss_list = []

for epoch in range(max_epoch):
    # 데이터 뒤섞기
    idx = np.random.permutation(data_size)
    x = x[idx]
    t = t[idx]

    for iters in range(max_iters):
        batch_x = x[iters*batch_size:(iters+1)*batch_size]
        batch_t = t[iters*batch_size:(iters+1)*batch_size]

        # 기울기를 구해 매개변수 갱신
        loss = model.forward(batch_x, batch_t)
        model.backward()
        optimizer.update(model.params, model.grads)

        total_loss += loss
        loss_count += 1

        # 정기적으로 학습 경과 출력
        if (iters+1) % 10 == 0:
            avg_loss = total_loss / loss_count
            print('| 에폭 %d |  반복 %d / %d | 손실 %.2f'
                  % (epoch + 1, iters + 1, max_iters, avg_loss))
            loss_list.append(avg_loss)
            total_loss, loss_count = 0, 0
  • Trainer 클래스
  • fit()은 2(학습용 코드)와 비슷함
class Trainer:
    def __init__(self, model, optimizer): # model = 1(TwoLayerNet)
        self.model = model
        self.optimizer = optimizer
        self.loss_list = []
        self.eval_interval = None
        self.current_epoch = 0

    def fit(self, x, t, max_epoch=10, batch_size=32, max_grad=None, eval_interval=20):
        data_size = len(x)
        max_iters = data_size // batch_size
        self.eval_interval = eval_interval
        model, optimizer = self.model, self.optimizer
        total_loss = 0
        loss_count = 0

        start_time = time.time()
        for epoch in range(max_epoch):
            # 뒤섞기
            idx = numpy.random.permutation(numpy.arange(data_size))
            x = x[idx]
            t = t[idx]

            for iters in range(max_iters):
                batch_x = x[iters*batch_size:(iters+1)*batch_size]
                batch_t = t[iters*batch_size:(iters+1)*batch_size]

                # 기울기 구해 매개변수 갱신
                loss = model.forward(batch_x, batch_t)
                model.backward()
                params, grads = remove_duplicate(model.params, model.grads)  # 공유된 가중치를 하나로 모음
                if max_grad is not None:
                    clip_grads(grads, max_grad)
                optimizer.update(params, grads)
                total_loss += loss
                loss_count += 1

                # 평가
                if (eval_interval is not None) and (iters % eval_interval) == 0:
                    avg_loss = total_loss / loss_count
                    elapsed_time = time.time() - start_time
                    print('| 에폭 %d |  반복 %d / %d | 시간 %d[s] | 손실 %.2f'
                          % (self.current_epoch + 1, iters + 1, max_iters, elapsed_time, avg_loss))
                    self.loss_list.append(float(avg_loss))
                    total_loss, loss_count = 0, 0

            self.current_epoch += 1

    def plot(self, ylim=None):
        x = numpy.arange(len(self.loss_list))
        if ylim is not None:
            plt.ylim(*ylim)
        plt.plot(x, self.loss_list, label='train')
        plt.xlabel('반복 (x' + str(self.eval_interval) + ')')
        plt.ylabel('손실')
        plt.show()

1.5. 계산 고속화

  • 비트 정밀도
  • 넘파이는 64bit 부동소수점 수를 표준으로 사용하나, 신경망의 추론과 학습은 32bit로도 인식률을 거의 떨어뜨리는 일 없이 수행할 수 있다. 메모리 측면, 데이터 병목현상, 계산 속도 등의 이유로 데이터 타입을 32bit로 전환하여 사용한다.

    $\to$ .astype(np.float32) or .astype('f')

  • GPU
  • 딥러닝의 대량의 곱하기 연산을 빠르게 수행할 수 있다. 쿠파이(전용 컴퓨팅, 플랫폼 설치해야함) 등

댓글