STUDY/ML DL

MLP를 행렬로 표현하기: 왜 비선형 Activation이 필요할까?

sed 2026. 5. 11. 13:02
SMALL

MLP를 행렬로 표현하기


이번에는 MLP, 즉 다층 퍼셉트론을 행렬과 벡터로 표현해보고자 한다.

처음에는 뉴런 하나하나의 식을 직접 쓰는 방식이 더 직관적일 수 있다.

 

예를 들어 입력값이 `x1`, `x2`이고, 어떤 노드 하나로 들어가는 weight가 `w1`, `w2`, bias가 `b1`이라면 다음처럼 쓸 수 있다.

x1 * w1 + x2 * w2 + b1

이 식은 하나의 노드에 들어가는 값을 계산한 것이다.

 

그런데 노드가 많아지고 layer가 깊어지면 이런 식을 하나하나 쓰는 것은 너무 복잡해진다.

그래서 신경망은 보통 행렬과 벡터를 이용해서 표현한다.

 

 

하나의 layer를 행렬로 표현하기

입력값이 두 개 있다고 하자.

x = [x1, x2]

 

그리고 첫 번째 hidden layer에 노드가 3개 있다고 해보자.

입력 노드 2개 → hidden layer 노드 3개

 

이때 입력 벡터의 shape은 다음과 같다.

x shape = (1, 2)

 

입력값이 2개이고, 이를 행 벡터로 표현했기 때문이다.

 

이제 이 입력을 hidden layer의 3개 노드로 보내려면 weight matrix가 필요하다. 입력은 2개이고 출력 노드는 3개이므로 weight matrix의 shape은 다음과 같다.

W1 shape = (2, 3)

 

즉, 입력 2개를 받아서 hidden node 3개를 만들어내는 행렬이다.

bias는 hidden node마다 하나씩 필요하다. hidden node가 3개이므로 bias vector의 shape은 다음과 같다.

b1 shape = (1, 3)

 

그러면 첫 번째 layer의 계산은 다음처럼 쓸 수 있다.

xW1 + b1

 

shape을 함께 보면 다음과 같다.

x      : (1, 2)
W1     : (2, 3)
xW1    : (1, 3)
b1     : (1, 3)
xW1+b1 : (1, 3)

 

결과가 (1, 3)이라는 것은 hidden layer의 노드 3개에 들어가는 값이 한 번에 계산되었다는 뜻이다.

 

즉, 원래는 각 노드마다 다음과 같은 계산을 해야 했다.

첫 번째 노드: x1*w1 + x2*w2 + b1
두 번째 노드: x1*w3 + x2*w4 + b2
세 번째 노드: x1*w5 + x2*w6 + b3

 

하지만 행렬을 사용하면 이 전체 계산을 한 줄로 표현할 수 있다.

xW1 + b1

 

그래서 논문이나 딥러닝 교재에서는 신경망을 보통 행렬과 벡터로 표현한다.

노드가 3개면 직접 쓸 수도 있지만, 노드가 100개, 1000개가 되면 개별 식을 모두 쓰는 것은 현실적으로 어렵기 때문이다.

 

 

Activation function까지 포함하기

신경망에서는 weight를 곱하고 bias를 더한 뒤 activation function을 통과시킨다.

따라서 첫 번째 layer는 다음처럼 표현할 수 있다.

h = f1(xW1 + b1)

 

여기서 f1은 활성함수 activation function이다.

예를 들어 ReLU, sigmoid, tanh 같은 함수가 activation function에 해당한다.

 

이 식의 의미는 간단하다.

입력 x
→ W1을 곱함
→ b1을 더함
→ activation function 통과
→ hidden layer 출력 h

 

즉, 하나의 layer는 다음과 같은 구조를 가진다.

행렬 곱 + bias 더하기 + activation function

 

두 번째 layer까지 표현하기

이제 hidden layer의 출력 h를 다음 layer로 보낸다고 하자.

 

첫 번째 layer의 출력은 hidden node가 3개였으므로 shape이 (1, 3)이다.

h shape = (1, 3)

 

두 번째 layer에서 출력 노드가 2개라면, weight matrix의 shape은 다음과 같다.

W2 shape = (3, 2)

 

입력으로 3개의 값을 받고, 출력으로 2개의 값을 만들기 때문이다.

 

bias는 출력 노드가 2개이므로 다음과 같다.

b2 shape = (1, 2)

 

그러면 두 번째 layer는 이렇게 쓸 수 있다.

y = f2(hW2 + b2)

 

앞에서 h = f1(xW1 + b1)이었으므로 전체 신경망은 다음처럼 표현할 수 있다.

y = f2(f1(xW1 + b1)W2 + b2)

 

이 식이 바로 두 개의 layer를 가진 MLP를 행렬과 벡터로 표현한 것이다.

 

shape을 정리하면 다음과 같다.

x      : (1, 2)

W1     : (2, 3)
b1     : (1, 3)
xW1+b1 : (1, 3)
h      : (1, 3)

W2     : (3, 2)
b2     : (1, 2)
hW2+b2 : (1, 2)
y      : (1, 2)

 

즉, 이 신경망은 입력 2개를 받아서 최종적으로 출력 2개를 내보내는 구조이다.

입력 2개 → hidden node 3개 → 출력 2개

 

 

 

신경망은 결국 함수이다

 

위 식을 다시 보면 신경망은 결국 하나의 함수라고 볼 수 있다.

y = f2(f1(xW1 + b1)W2 + b2)

 

입력 x가 들어오고, weight matrix와 bias vector를 이용한 연산을 거친 뒤, 최종 출력 y가 나온다.

즉, 신경망은 다음과 같은 함수이다.

입력 x → 신경망 함수 → 출력 y

 

그래서 신경망을 복잡한 함수 근사기라고도 볼 수 있다. 입력과 출력 사이의 관계를 학습하는 함수라고 이해하면 된다.

 

그렇다면 이런 생각을 할 수 있다.

“layer를 많이 쌓으면 더 복잡한 함수를 만들 수 있지 않을까?”

 

대체로 맞는 말이다. 하지만 중요한 조건이 하나 있다.

그 조건은 바로 activation function이 비선형이어야 한다는 것이다.

 

Linear activation을 쓰면 어떻게 될까?

Activation function에는 여러 종류가 있지만, 가장 단순한 형태는 linear activation이다.

Linear activation은 들어온 값을 그대로 내보내는 함수이다.

f(x) = x

 

즉, 입력이 3이면 3이 그대로 나오고, 입력이 -2이면 -2가 그대로 나온다.

 

이제 아까 신경망 식을 다시 보자.

y = f2(f1(xW1 + b1)W2 + b2)

 

만약 f1과 f2가 모두 linear activation이라면, activation function은 아무 일도 하지 않는다.  

들어온 값을 그대로 내보내기 때문이다.

 

그러면 식은 다음처럼 단순해진다.

y = (xW1 + b1)W2 + b2

 

이를 전개하면 다음과 같다.

y = xW1W2 + b1W2 + b2

 

여기서 W1W2는 결국 하나의 새로운 weight matrix로 볼 수 있다.

W = W1W2

 

그리고 b1W2 + b2도 하나의 새로운 bias vector로 볼 수 있다.

b = b1W2 + b2

 

그러면 전체 식은 결국 이렇게 된다.

y = xW + b

 

즉, 두 개의 layer를 쌓았지만 결과적으로는 하나의 linear layer와 다를 바가 없어진다.

 

 

깊게 쌓아도 하나의 선형식이 된다

linear activation만 사용하면 layer를 아무리 많이 쌓아도 결국 하나의 선형식으로 합쳐질 수 있다.

 

예를 들어 다음 두 식을 비교해보자.

a1 * a2 * x + b

 

a * x + b

 

둘 다 완벽하게 학습되었다고 했을 때, 표현할 수 있는 함수의 종류는 본질적으로 같다.

왜냐하면 a1 * a2는 결국 하나의 기울기 a로 합쳐질 수 있기 때문이다.

a = a1 * a2

 

즉, a1과 a2를 따로 학습한다고 해서 더 복잡한 함수를 표현할 수 있는 것은 아니다.

결국 x 앞에 곱해지는 하나의 계수 역할을 할 뿐이다.

 

신경망에서도 마찬가지이다.

xW1W2 + b1W2 + b2

 

이 식은 결국 다음처럼 쓸 수 있다.

xW + b

 

따라서 linear activation만 사용하면 깊은 신경망을 만들어도 표현력은 하나의 fully connected layer 수준에 머문다.

즉, layer를 깊게 쌓는 효과를 얻지 못한다.

 

 

왜 비선형 activation이 필요한가?

신경망이 깊어질수록 더 복잡한 함수를 표현하려면 중간에 비선형 activation이 필요하다.

비선형 activation이 들어가면 다음과 같은 식을 단순히 하나의 xW + b로 합칠 수 없다.

 

y = f2(f1(xW1 + b1)W2 + b2)

 

여기서 f1, f2가 ReLU, sigmoid, tanh 같은 비선형 함수라면, 중간에서 값의 형태가 꺾이거나 압축되거나 변형된다.

이 비선형 변환 때문에 신경망은 단순한 직선 관계뿐만 아니라 훨씬 복잡한 관계를 표현할 수 있게 된다.

 

즉, 비선형 activation은 신경망의 표현력을 높여주는 핵심 요소이다.

 

정리하면 다음과 같다.

linear activation만 사용
→ 여러 layer를 쌓아도 하나의 선형식으로 합쳐짐
→ 깊어지는 효과가 거의 없음

non-linear activation 사용
→ layer를 합쳐서 하나의 선형식으로 만들 수 없음
→ 복잡한 함수 표현 가능

 

 

선형 관계와 비선형 관계

선형 관계는 단순히 직선으로 표현할 수 있는 관계이다.

y = ax + b

 

위 식은 직선을 나타낸다. 입력 x가 변할 때 출력 y가 일정한 비율로 변한다. 단순한 선형 회귀가 이런 형태이다.

 

반면 실제 데이터는 선형 관계만 가지는 경우가 많지 않다.

예를 들어 이미지 분류를 생각해보자. 강아지와 고양이를 구분하는 문제는 단순한 직선 하나로 해결하기 어렵다. 픽셀값들의 조합이 매우 복잡하고, 귀 모양, 눈 위치, 털 패턴, 배경 등 여러 정보가 얽혀 있기 때문이다.

 

이런 복잡한 관계를 표현하려면 비선형성이 필요하다.

그래서 신경망에서는 ReLU 같은 비선형 activation을 사용한다.

 

 

ReLU 예시

대표적인 비선형 activation function으로 ReLU가 있다.

 

ReLU는 다음과 같이 정의된다.

ReLU(x) = max(0, x)

 

즉, 양수는 그대로 통과시키지만, 음수는 0으로 만든다.

 

입력: -2 → 출력: 0
입력: -1 → 출력: 0
입력:  0 → 출력: 0
입력:  1 → 출력: 1
입력:  2 → 출력: 2

 

이렇게 단순한 함수지만, 0을 기준으로 함수가 꺾인다. 이 꺾이는 지점 때문에 ReLU는 비선형 함수이다.

ReLU 같은 비선형 activation이 중간에 들어가면, 여러 layer를 하나의 선형식으로 합칠 수 없게 된다. 그래서 깊은 신경망이 의미를 가지게 된다.

 

 

그렇다면 linear activation은 필요 없을까?

여기까지 보면 linear activation은 쓸모없는 것처럼 보일 수 있다.

하지만 그렇지는 않다. Linear activation도 필요한 곳이 있다.

 

대표적인 경우가 회귀 문제의 마지막 layer이다.

회귀 문제에서는 출력값이 특정 범위로 제한되지 않아야 하는 경우가 많다. 예를 들어 집값 예측, 온도 예측, 매출 예측처럼 연속적인 숫자를 예측하는 경우가 그렇다.

이런 문제에서는 출력값이 이론적으로 매우 작은 값부터 매우 큰 값까지 나올 수 있어야 한다.

출력 범위: -∞ ~ ∞

 

그래서 마지막 layer에는 별도의 비선형 activation을 두지 않거나, linear activation을 사용한다.

 

반대로 sigmoid를 마지막에 사용하면 출력이 0과 1 사이로 제한된다. tanh를 사용하면 출력이 -1과 1 사이로 제한된다. 이런 함수들은 회귀 문제의 출력층에는 적합하지 않을 수 있다.

따라서 회귀 문제에서는 마지막 layer를 linear로 두는 경우가 많다.

 

 

“비선형이 좋다면서 마지막도 비선형이면 더 좋은 것 아닌가?”

이런 생각을 할 수 있다.

“출력 범위가 -∞부터 ∞까지 나오면서도 비선형인 함수를 쓰면 더 좋지 않을까?”

 

예를 들어 x^3 같은 함수는 비선형이면서 출력 범위도 -∞부터 ∞까지 가능하다.

하지만 굳이 마지막 layer에 이런 특이한 activation을 넣을 필요는 크지 않다.

 

신경망의 중간 layer들에서 이미 충분히 비선형성을 만들 수 있기 때문이다.

만약 비선형 표현력이 부족하다면, 마지막 activation을 특이하게 바꾸기보다는 중간 layer를 더 잘 설계하거나 layer를 추가하는 방식이 더 일반적이다.

 

마지막 layer는 모델이 만들어낸 값을 원하는 출력 형태로 내보내는 역할을 한다.

회귀 문제에서는 값을 제한하지 않는 것이 중요하므로 linear output을 사용하는 것이 자연스럽다.

 

 

중간 layer에서도 linear activation을 쓰는 경우

Linear activation이 중간 layer에서 사용되는 경우도 있다.

대표적인 예로 MobileNetV2linear bottleneck 구조가 있다.

 

MobileNetV2 논문에서는 좁은 bottleneck layer에서 non-linearity를 제거하는 것이 표현력을 유지하는 데 중요하다고 설명한다.

즉, 차원이 작은 공간에서 ReLU 같은 비선형 함수를 적용하면 정보 손실이 커질 수 있기 때문에, 좁은 layer에서는 linear projection을 사용한다.

 

이유를 간단히 생각해보자.

ReLU는 음수 값을 모두 0으로 만든다.

ReLU(-2) = 0
ReLU(-1) = 0
ReLU(0)  = 0
ReLU(1)  = 1

 

즉, 음수 쪽 정보는 사라진다.

 

차원이 충분히 큰 layer에서는 어느 정도 정보가 사라져도 다른 차원들이 정보를 보완할 수 있다.

하지만 차원이 작은 bottleneck layer에서는 정보가 이미 압축되어 있기 때문에, 여기에 ReLU를 적용하면 중요한 정보가 날아갈 수 있다.

 

그래서 MobileNetV2에서는 좁은 bottleneck 부분에 linear layer를 사용해 정보 손실을 줄이려 했다.

다만 중간의 확장된 layer에서는 ReLU6 같은 비선형 activation을 사용한다.

핵심은 모든 곳에서 linear를 쓰자는 것이 아니라, 정보 손실이 큰 특정 위치에서는 linear가 더 적절할 수 있다는 것이다.

 

 

정리

MLP는 행렬과 벡터를 이용해 간단하게 표현할 수 있다.

 

하나의 layer는 다음 구조를 가진다.

xW + b

 

여기에 activation function을 적용하면 다음과 같다.

f(xW + b)

 

두 개의 layer를 가진 신경망은 다음처럼 쓸 수 있다.

y = f2(f1(xW1 + b1)W2 + b2)

 

이 식을 보면 신경망은 결국 입력을 받아 출력을 만드는 하나의 함수라는 것을 알 수 있다.

 

하지만 activation function이 모두 linear라면 여러 layer를 쌓아도 결국 하나의 선형식으로 합쳐진다.

y = xW + b

 

따라서 linear activation만 사용하면 깊은 신경망을 만들어도 깊어지는 효과를 제대로 얻을 수 없다.

 

신경망이 복잡한 함수를 표현하려면 중간에 ReLU, sigmoid, tanh 같은 비선형 activation이 필요하다. 이 비선형성이 있어야 여러 layer를 쌓는 의미가 생긴다.

 

다만 linear activation이 항상 나쁜 것은 아니다. 회귀 문제의 마지막 layer에서는 출력 범위를 제한하지 않기 위해 linear activation을 사용한다. 또한 MobileNetV2의 linear bottleneck처럼 정보 손실을 줄이기 위해 중간 layer에서 linear를 사용하는 경우도 있다.

LIST