비선형성
이전 강의의 강의자료 후반에도 있던 여러개의 층으로 된 신경망 그림이다.

여기서 중간 층들은 스스로 다음 층에 넘겨줄 좋은 값이 무엇인지 학습하게 된다.
이 특성이 다른 머신러닝보다 신경망을 더 강력하게 만든 요인이다.

층 사이의 계산이 어떻게 되는지 자세히 알아보자.
각각의 주황색 노드는 로지스틱 회귀를 한다고 생각할 수 있다.
옆의 그림에서
$a_1 = f(W_{11} x_1 + W_{12} x_2 + W_{13} x_3 + b_1)$
$a_2 = f(W_{21} x_1 + W_{22} x_2 + W_{23} x_3 + b_2)$
처럼 계산된다.
위 식의 모든 변수는 스칼라인데 이를 벡터로 바꾸면 식을 간단하게 쓸 수 있다.
$ z = Wx +b$
$a = f(z)$
$f()$는 비선형 함수인데, 이 함수는 $z$의 내부 원소들에 대해 모두 각각 적용된다.
이 $f()$함수, 즉 비선형 함수나 활성화 함수라고 부르는 얘들은 어디서 등장한 아이디어일까?
맨 처음 어떤 뉴런이 활성화 될지 말지 정하는 것을 특정한 임계값(threshold,$\theta$)를 사용해 나타내자는 아이디어를 냈을 때이다.
즉 뉴런에서 계산한 값이 $\theta$를 넘으면 활성화로 간주하고 1을, 아니라면 0을 출력하는 방식이다.
하지만 기울기,gradient가 없어 학습시 어떤 방향으로 이동해 학습해야하는지 알기 어렵다.

우연히도 위 강의 슬라이드에 언급되는 McCulloch & Pitts의 1943년 논문을 읽고 정리해 놓은 글이 이 블로그에 있으니 참고해봐도 좋을 듯하다.
[유명 딥러닝 논문] 1. A Logical Calculus of the Ideas Immanent in Nervous Activity
ai관련해 더 자세히 알아보고 싶어 논문을 읽어보기로 했다.읽을 논문을 어떻게 정할까 고민하다가 famous deep learning papers를 모아놓은 아래의 사이트를 발견했다.그래서 이 사이트의 논문을 하나
april2901.tistory.com
어쨋든, 그래서 경사가 있는 함수가 좋은데, 이런 함수로 여러 종류가 사용되었다.

1. sigmoid 함수 : 잘 썼던 함수이지만 출력에 음수는 없어 계산 과정 중에 값이 점차 커질 수 있다.
2. tanh 함수 : 그래서 sigmoid함수를 변형해 범위를 -1~1로 만든 함수이다. $tanh(z)=2sigmoid(2z)-1$
3. hard tanh 함수 : 그러나 위 함수는 식을 보면 지수 계산이 많다. 그래서 형태는 비슷하게 유지하며 식을 간단히 바꾼 함수이다.
4. ReLU 함수 : 식이 매우 간단한 위 함수가 잘 작동하는걸 보고 새롭게 간단히 만들어 본 함수이다.
하지만 음수 부분은 기울기가 없어 처음 얘기했던 학습이 어려운 문제가 있을 수 있다. 하지만 그래도 효과적이라는 것이 발견되고 이게 거의 표준이 되었다.
5. Leaky ReLU 함수 : ReLU의 음수 부분을 조금 수정한 함수이다.
6. 기타 : 요즘의 트랜스포머 모델에 쓰이는 $swish(x) = x logistic(x), GELU(x) = x 𝑃( 𝑋 ≤ x) , 𝑋~𝑁(0,1) ≈ x logistic(1.702x)$이런 함수들도 있다.
어쩃든 중요한 건 이런 비선형성이 왜 필요한가? 이다.
우리가 사진에서 뭔가를 알아보는 것처럼 복잡한 기능을 만들때 단순히 선형변환인 벡터 곱셈은 몇번을 해도 하나의 선형변환으로 축약이 되기 때문이다. 여러 층을 사용해도 의미가 없어진다.
Gradient계산
신경망 학습은 가중치를 학습시키는 것이다.
그래서 각 가중치가 loss에 얼마나 영향을 주는지를 알기 위해 gradient를 계산해야한다.
먼저 기본적인 수학을 조금 살펴보고 넘어가보자.
n개의 입력과 1개의 출력을 갖는 함수가
$f(x) = f(x_1, x_2, ..., x_n)$이라고 하면,

이번엔 입력 n개와 출력 m개를 가진 함수를 보면,

아래와 같다.
이런 다입력 다출력 함수에 대해 모든 편미분을 모아놓은 아래의 행렬을 자코비안 Jacobian이라고 한다.

$z=(z_1, z_2,...,z_n)$라는 벡터에 대해서 각 원소별로 f를 적용할 때,
jacobian $\frac{\partial h}{\partial z}$의 각 원소는 아래처럼 계산된다.

그 외 몇가지 기본적인 jacobian들이다.
기존에 알던 1변수 미적분학에 비교해 생각해보면 당연하다는 생각이 든다.
하지만 실제 행렬을 풀어서 왜 이런 결과가 나오는지 원소 별로 계산 해보면 생각보다 쉽지는 않을 수 있다.
이렇게 직접 계산을 해보면 1 대신 항등행렬이 나오는 이유나 차원이 어떻게 되는지를 자연스럽게 알 수 있다.

<신경망 식에 적용해보기>
이전 글의 마지막에 봤던 아래 그림에 적용해보자.

여기서 $\frac{\partial s}{\partial b} $를 계산해보자.
아래처럼 먼저 chain rule을 사용해 식을 풀고, 계산하면 된다.
마지막의 원안에 점이 있는 기호⊙는 하다마르 곱이라고 부르는 element wise 곱이다.

이번에는 $\frac{\partial s}{\partial W}$를 구한다고 해보자.
그러면 앞의 식과 겹치는 부분이 있는 것을 알 수 있다.
중복 계산을 피하기 위해 $\delta$기호를 사용해 오른쪽처럼 한번만 계산하고 활용할 수 있다.


$\frac{\partial s}{\partial W}$의 차원은 1x(mn)이 되는데, 이는 한쪽으로만 너무 긴 벡터가 되고, 계산의 편의성을 위해 $W$와 모양을 똑같이 맞춰주는 것이 좋다.
수학적으로는 1xmn이지만, 이 분야에서는 shape convention이 있다.
즉 미분하는 행렬에 결과의 차원을 맞추는 것이다.
$\frac{\partial s}{\partial W} = \delta \frac{\partial z}{\partial W}$에서
$\frac{\partial z}{\partial W}$는 $x$임을 알 수 있다. ( $ z=Wx+b$)
하지만 shape convention을 위해 아래처럼 각 항에 transpose를 취한다.

여기까지가 기본적인 행렬의 미적분학이다.
이제 역전파로 넘어가보자.
Backpropagation
아래 그림처럼 입력부터 하나씩 연산을 하면서 식을 계산하는 법을 순전파(forward propagation)이라고 한다.


순전파를 해서 값을 계산할 수 있지만, 그 이후에는 gradient를 통해 학습을 진행해야한다.
이 방법을 역전파(backpropagation)이라고 한다.
아래 그림처럼 오른쪽에서 시작해 점차 입력값의 방향으로 이동하는 방식이다.
각 과정에서 각 연산에 대한 결과의 변화량을 계산한다.

하나의 노드를 기준으로 살펴보면, 노드는 먼저 상위 노드에서 upstream gradient를 받는다.
이를 local gradient와 곱하면 그 아래 노드를 위한 downstream gradient를 계산할 수 있다.

이번에는 입력이 여러개인 노드의 예시이다.
똑같이 하나의 upstream gradient를 받지만 두 개의 downstream gradient를 계산해야한다.
노드에 들어오는 각 input에 대한 local gradient를 따로 계산 후 이를 사용해 downstream gradient를 각각 계산한다.

아래와 같은 식에 실제로 적용해보자.
과정을 정리하면
1. 그래프를 그린다.
2. 순전파를 계산한다.
3. 역전파를 계산한다.

이 경우에서 보면 아래같은 속성들을 알 수 있다.
+ 노드 : upstream에서 온 값을 그대로 내려보낸다
* 노드 : 순방향 계수(forward coefficient)들이 서로 바뀌어 upstream값과 곱해진다
max 노드 : 입력 중 하나로만 역전파가 된다
이 방법들은 그래프에서 사이클이 없다면 항상 적용가능하다.
사이클이 없는 그래프라면 topological sort를 통해 정렬을 하고,
순전파를 하고 최종 출력을 1로 초기화 한다음 역전파를 실행하면 된다.
만약 순전파보다 역전파에서 더 큰 시간복잡도를 갖는다면 효율적이지 못한 계산을 하고 있다는 뜻이다.
위에서 본 $\delta$처럼 중복 계산이 포함되게 되면 이런 일이 벌어진다.
물론 컴퓨터가 다 계산을 해주지만, 전부 자동적으로 해주던 프로그램은 인기가 없었고, 요즘은 순전파,역전파만 알아서 해주고,활성화 함수와 그 local gradient등은 다른 클래스로 코딩을 해서 가져오는 방식이 사용된다.
아래의 예시 코드를 보면, 순전파/역전파는 forward()/backward()라는 함수만으로 알아서 되는 것을 확인할 수 있다.

하지만 아래처럼 각 계산 함수에 대한 클래스는 사람이 직접 코딩해야한다.

'AI > 자연어처리(NLP)' 카테고리의 다른 글
| [Stanford 강의] Assignment1 (0) | 2025.11.26 |
|---|---|
| [NLP 논문] GloVe: Global Vectors for Word Representation (0) | 2025.11.24 |
| [NLP 논문] Distributed Representations of Words and Phrasesand their Compositionality (0) | 2025.11.20 |
| [NLP 논문] Efficient Estimation of Word Representations in Vector Space (2) | 2025.11.19 |
| [Stanford 강의] Lecture 2 : Word Vectors and Language Models (0) | 2025.11.15 |