[ML] Jacobian 1


서론

고등학생때 배웠던 미분은 단순히 함수의 접선 기울기를 구하는 것에 그쳤다면 AI/ML 관점에서는 입력을 아주 조금 변화시켰을 때 출력은 얼마나 변화하는가에 대한 관점으로 바꾸어 생각해본다.

예를들어 \(x\)에 작은 변화량 \(\Delta x\)를 주어 \(x\) → \(x\) + \(\Delta x\)가 되었다하자. 그러면 출력의 변화량은 \(\Delta f = f(x + \Delta x) - f(x)\) 가 된다.

우리는 입력을 아주 조금 변화시키기로 했으므로 \(\Delta x\)가 충분히 작다면, \(\Delta f \approx f'(x)\Delta x\)로 표현할 수 있다.

why? \(\Delta f \approx f'(x)\Delta x\)

왜냐하면 미분의 정의는

\[f'(x) = \lim_{\Delta x \to 0} \frac{f(x+\Delta x)-f(x)}{\Delta x}\]

여기서

\[\Delta f = f(x + \Delta x) - f(x)\]

라고 쓰면,

\[f'(x) = \lim_{\Delta x \to 0} \frac{\Delta f}{\Delta x}\]

가 된다.

Δ𝑥가 충분히 작을 때에는

\[\frac{\Delta f}{\Delta x} \approx f'(x)\]

라고 볼 수 있다. 양변에 Δ𝑥를 곱하면

\[\Delta f \approx f'(x)\Delta x\]

가 된다.

직관적으로 보면 \(f'(x) \)는 \(x\)지점에서의 기울기임으로 예를들어 \(f'(x) = 3\)이라면 그 지점 근처에서는 대략 \(x\)가 1만큼 변할 때 \(f(x)\)는 3만큼 변한다는 뜻이다.

따라서 \(x\)를 0.01만큼 변화시키면

\[\Delta f \approx 3 \times 0.01 = 0.03\]

이라고 예상할 수 있다. 즉,

\[\underbrace{\Delta f}_{\text{출력 변화량}}\approx\underbrace{f'(x)}_{\substack{\text{입력 1만큼}\\\text{줄 때 변화량}}}\underbrace{\Delta x}_{\text{입력 변화량}}\]

인 것이다.

이여서 보면 \(\Delta f \approx f'(x)\Delta x\) 식에 \(\Delta f = f(x + \Delta x) - f(x)\) 를 대입하면 \(f(x + \Delta x) \approx f(x) + f'(x)\Delta x\) 이렇게 표현이 가능하다.

\(f(x + \Delta x) \approx f(x) + f'(x)\Delta x\) 이 식은 Jacobian을 이해하는 데 매우 중요하다. \(f'(x)\)는 단순히 그래프에서의 접선의 기울기만을 의미하는 것이 아니라, 현재 위치 \(x\)에서 입력이 아주 조금 변했을 때, 그 변화가 출력에 어느 정도의 변화로 전달되는지를 나타낸다.

결국 미분이란, 현재 위치에서 입력에 아주 작은 변화가 발생했을 때, 그 변화가 출력에 어떻게 전달될지를 선형적으로 근사해주는 도구이다. 이러한 관점을 ‘Local Linear Approximation’이라고 한다.

즉 1차원(입력과 출력이 각각 1개)에서는 \(\Delta f \approx f'(x)\Delta x\)로 하나의 미분값을 가지고 입력 변화/출력 변화의 관계를 표현할 수 있었다. 그렇다면 출력이 여러 개의 값으로 이뤄진 벡터라면 어떻게 될까?

하지만, 입력과 출력이 각각 2개 이상이라면 더 이상 𝑓′(𝑥)이라는 하나의 미분값을 가지고 그 관계를 표현할 수 없다. 그래서 이를 해결하기 위해 등장한 것이 바로 ‘Jacobian’ 이다.

Jacobian이란 비선형 다변수 함수를 특정한 점 주변에서 선형적으로 근사해주는 행렬이다.

수식은 1차원일때 출력의 변화량이 \(\Delta f \approx f'(x)\Delta x\) 음으로 다차원일 때는 \(\Delta f \approx J_{f}(x)\Delta x\)와 같은 형태를 생각할 수 있다. 여기서 \(J_f(x)\)가 바로 Jacobian이다.

자세히 알아보기 전에 먼저 Gradient에서 Jacobian 흐름으로 알아본다.

본론

Gradient: 입력이 여러개, 출력은 1개

실제 AI와 머신러닝에서 다루는 함수는 입력이 하나인 경우보다, 여러 개의 입력값을 동시에 받는 경우가 훨씬 많다.

예를들어 어떤 모델이 나이, 수입, 신용등급을 입력으로 받아 어떤 하나의 예측값을 출력하는 함수라면( \(f : \mathbb{R}^3 \to \mathbb{R}\)) Vector Input이 Scalar Output으로 도출되고

입력이 여러 개가 되면 각 입력 방향에 대한 변화율이 각각 필요하다. 따라서, \(\frac{\partial f}{\partial x_1},\;\frac{\partial f}{\partial x_2},\;\frac{\partial f}{\partial x_3}\) 와 같은 편미분을 계산하게 된다. 이러한 편미분을 하나로 모은 것이 Gradient이다.

즉, Gradient는 \(f : \mathbb{R}^n \to \mathbb{R}\) 형태의 함수에서 입력의 여러방향에 대한 출력의 변화율을 한 번에 표현하는 도구이다.

이번엔 입력벡터(총 개수 n)가 아주 조금 변했다고 해보자 그러면

\[\Delta \mathbf{x} = \begin{bmatrix} \Delta x_1 \\ \Delta x_2 \\ \vdots \\ \Delta x_n \end{bmatrix}\]

이렇게 표현할 수 있고, 출력 변화는 앞서 이야기 했던 Local Linear Approximation을 이용하여 다음과 같이 근사할 수 있다.

\[\Delta f \approx \nabla f(\mathbf{x})^T \Delta \mathbf{x}\]

이를 전개하면

\[\Delta f \approx\frac{\partial f}{\partial x_1}\Delta x_1+\frac{\partial f}{\partial x_2}\Delta x_2+\cdots+\frac{\partial f}{\partial x_n}\Delta x_n\]

예를 들어, \(f(x_1,\;x_2) =x^2_1 + 3x_2\) 함수가 있다.

gradient는

\[\nabla f =\begin{bmatrix}2x_1 \\3\end{bmatrix}\]

가 된다. 현재 위치 \((x_1,\;x_2) = (2,\;1)\)이라면 \(\nabla f(2, 1) = \begin{bmatrix}4 \\3\end{bmatrix}\) 가 되고 만약 입력값이 \(\Delta x = \begin{bmatrix}0.01\\-0.02\end{bmatrix}\)처럼 아주 조금 변했다고 해보자. 그러면 \(\Delta f \approx \nabla f(2,1)^T \Delta \mathbf{x}\)이라 했으므로 \(\Delta f \approx\begin{bmatrix}4 & 3\end{bmatrix}\begin{bmatrix}0.01 \\-0.02\end{bmatrix}\)이 되어서 \(\Delta f \approx 4(0.01) + 3(-0.02) =- 0.02\) 가 된다.

따라서 현재 위치 (2, 1)에서 \(x1\)이 0.01 증가하고 \(x2\)가 0.02 감소하면 출력 \(f\)는 약 0.02 감소했다고 예측이 가능하다.

이제는 입력이 여러 개이고 출력도 여러 개라면 그때부터는 Gradient 한 개만으로 모든 변화 관계를 표현할 수 없다. 이 때 바로 Jacobian이 필요해진다.

Jacobian 입력이 여러개, 출력도 여러개

실제 AI와 머신러닝에서는 출력이 하나가 아닌 경우가 매우 많다. 예를 들어 신경망의 어떤 Layer가 두 개의 입력을 받아 세 개의 값을 출력한다고 생각해 보자.

\[\mathbf{x}=\begin{bmatrix}x_1 \\x_2\end{bmatrix}\text{이고}\quad\mathbf{f}(\mathbf{x})=\begin{bmatrix}f_1(x_1,x_2) \\f_2(x_1,x_2) \\f_3(x_1,x_2)\end{bmatrix}\]

이 함수는 \(f : \mathbb{R}^2 \to \mathbb{R^3}\) 이며 vector Input이 Vector Output으로 도출되는 함수이다.

이 함수는 예를 들어 \(x_1\)을 조금 변화시켰을 때, \(f_1\)은 크게 변할 수도 있고, \(f_2\)는 거의 변하지 않을 수도 있으며, \(f_3\)는 반대 방향으로 변할 수 있다.

우리가 입력이 여러개 출력이 1개였을 때는 Gradient로 이루어진 벡터 1개가 있었다.

출력이 3개라면 Gradient 이루어진 벡터 3개가 필요하다.

\[\nabla f_1 =\begin{bmatrix}\frac{\partial f_1}{\partial x_1} \\[0.5em]\frac{\partial f_1}{\partial x_2}\end{bmatrix},\; \nabla f_2 = \begin{bmatrix} \frac{\partial f_2}{\partial x_1} \\[0.5em] \frac{\partial f_2}{\partial x_2} \end{bmatrix},\;\nabla f_3 = \begin{bmatrix} \frac{\partial f_3}{\partial x_1} \\[0.5em] \frac{\partial f_3}{\partial x_2} \end{bmatrix}\]

그리고 이것을 하나의 구조로 정리하면(각 그라디언트를 행방향으로 나열) 아래와 같다.

\[\begin{bmatrix}\frac{\partial f_1}{\partial x_1} & \frac{\partial f_1}{\partial x_2} \\[0.5em]\frac{\partial f_2}{\partial x_1} & \frac{\partial f_2}{\partial x_2} \\[0.5em]\frac{\partial f_3}{\partial x_1} & \frac{\partial f_3}{\partial x_2}\end{bmatrix}\]

예를들어 \(\mathbf{f}(x_1, x_2)=\begin{bmatrix}x_1^2 + x_2 \\x_1 x_2\end{bmatrix}\)라고 하자. 그러면 첫번째 출력은 \(f_1(x_1, x_2) = x_1^2 + x_2\), 두번째 출력은 \(f_2(x_1, x_2) = x_1 x_2\) 된다.

각각을 x1, x2에 대해서 \(f_1\) 함수를 편미분 하면 \(\frac{\partial f_1}{\partial x_1} = 2x_1\), \(\frac{\partial f_1}{\partial x_2} = 1\). \(f_2\) 함수를 편미분 하면 \(\frac{\partial f_2}{\partial x_1} = x_2\), \(\frac{\partial f_2}{\partial x_2} = x_1\)가 된다.

따라서 자코비안은 \(J_f(x_1, x_2)=\begin{bmatrix}2x_1 & 1 \\x_2 & x_1\end{bmatrix}\) 가 된다. 예를 들어 현재 위치가 \((x_1, x_2) = (2,3)\) 라면 \(J_f(2,3)=\begin{bmatrix}4 & 1 \\3 & 2\end{bmatrix}\)가 된다.

이 행렬은 각 원소 하나의 Input-output 관게를 나타낸다. 즉 \((x_1, x_2) = (2, 3)\) 주변에서 다른 입력은 고정하고 \(x_1\)만 아주 조금 증가시키면 첫 번째 출력 \(f_1\)은 대략 입력 변화량의 4배만큼 변한다는 뜻이다.

자코비안 행렬 (2, 2) 원소인 2의 의미는 \(\frac{\partial f_2}{\partial x_2} = 2\) 로, 같은 위치에서 \(x_2\)의 작은 변화에 대하여 두번째 출력 \(f_2\)가 얼마나 민감하게 반응하는지를 나타낸다.

정리

Jacobian 하나에는 “어떤 입력이 어떤 출력에 얼마나 영향을 주는가?”라는 정 보가 모두 담겨 있는 행렬이라는 것을 배웠다. 다음장에는 Jacobian이 다루는 함수 구조에 대해서 알아본다.