logo

줄리아의 자동미분 패키지 Zygote.jl 📂줄리아

줄리아의 자동미분 패키지 Zygote.jl

개요

줄리아에서는 머신러닝, 특히 딥러닝에 관련된 자동 미분automatic Differentiation을 위해 Zygote.jl이라는 패키지를 사용한다1. 개발진들은 이 패키지가 차세대 자동 미분 시스템으로써 줄리아로 미분가능한 프로그래밍differentiable Programming을 할 수 있게 해준다는 점을 어필하며, 실제로 써보면 놀라울 정도로 직관적임을 알 수 있다.

자동 미분이 아니라 도함수에 관련된 패키지 자체가 궁금하다면 Calculus.jl 패키지를 참고하자.

코드

일변수함수

어마어마하게 간단하다. 원래 우리가 미분하던 것처럼 함수 이름 뒤에 프라임 '을 찍으면 마치 실제로 도함수를 가지고 계산하는 것처럼 미분계수가 계산된다.

julia> using Zygote

julia> p(x) = 2x^2 + 3x + 1
p (generic function with 1 method)

julia> p(2)
15

julia> p'(2)
11.0

julia> p''(2)
4.0

다변수함수

gradient() 함수를 사용한다.

julia> g(x,y) = 3x^2 + 2y + x*y
g (generic function with 1 method)

julia> gradient(g, 2,-1)
(11.0, 4.0)

만약 조금만 더 직관적으로 코드를 작성하고 싶다면 다음과 같이 \nabla 로 다시 함수를 정의해서 써보는 것도 좋다.

julia> ∇(f, v...) = gradient(f, v...)
∇ (generic function with 1 method)

julia> ∇(g, 2, -1)
(11.0, 4.0)

벡터값함수

주어진 함수가 $f(x) = \begin{bmatrix} x & x^{2} & x^{3}\end{bmatrix}$라고 하자. 그러면 $f^{\prime}(x) = \begin{bmatrix} 1 & 2x & 3x^{2}\end{bmatrix}$이다. 수학적으로 그래디언트는 스칼라 함수에 대해 정의되므로, $f$를 gradient에 입력하면 에러가 발생한다.

julia> f(x) = [x, x^2, x^3]
f (generic function with 1 method)

julia> gradient(f, 1)
ERROR: Output is an array, so the gradient is not defined. Perhaps you wanted jacobian.

jacobian을 사용하라는 안내에 따라서 도함수를 계산해보면 다음과 같다. 참고로 함숫값은 반드시 벡터로 정의되어야한다. 튜플로 정의한 경우에는 에러를 일으킨다.

julia> jacobian(f, 2)
([1, 4, 12],)

julia> f₂(x) = (x, x^2, x^3)
f₂ (generic function with 1 method)

julia> jacobian(f₂, 2)
ERROR: ArgumentError: jacobian expected a function which returns an array, or a scalar, got Tuple{Int64, Int64, Int64}

다변수벡터값함수

주어진 함수가 $F(x, y) = \begin{bmatrix} x^{2} & xy & y^{3} \end{bmatrix}$라고 하자. 그러면 $F$의 전 도함수는 다음과 같다.

$$ F^{\prime}(x, y) = \begin{bmatrix} \partial_{x} (x^{2}) & \partial_{y} (x^{2}) \\[1em] \partial_{x} (xy) & \partial_{y} (xy) \\[1em] \partial_{x} (y^{3}) & \partial_{y} (y^{3}) \end{bmatrix} = \begin{bmatrix} 2x & 0 \\ y & x \\ 0 & 3y^{2} \end{bmatrix} $$

따라서 $F(1, 2) = \begin{bmatrix} 2 & 0 \\ 2 & 1 \\ 0 & 12 \end{bmatrix}$이다. 잘 계산되는지 확인해보면,

julia> F(x, y) = [x^2, x*y, y^3]
F (generic function with 1 method)

julia> J = jacobian(F, 1, 2)
([2, 2, 0], [0, 1, 12])

이때 변수를 jacobian(F, [1, 2])와 같이 벡터가 아니라 위의 코드처럼 jacobian의 인자로 입력해야한다는 것에 주의하자. 또한 반환값이 각 벡터의 튜플인데 이를 자코비안 행렬로 얻으려면,

julia> stack(J[i] for i ∈ 1:2)
3×2 Matrix{Int64}:
 2   0
 2   1
 0  12

전체 코드

using Zygote

p(x) = 2x^2 + 3x + 1

p(2)
p'(2)
p''(2)

g(x,y) = 3x^2 + 2y + x*y
gradient(g, 2,-1)

∇(f, v...) = gradient(f, v...)
∇(g, 2, -1)

f(x) = [x, x^2, x^3]
gradient(f, 1)

jacobian(f, 2)

f₂(x) = (x, x^2, x^3)
jacobian(f₂, 2)

F(x, y) = [x^2, x*y, y^3]
J = jacobian(F, 1, 2)

stack(J[i] for i ∈ 1:2)

환경

  • OS: Windows
  • julia: v1.10.0
  • Zygote v0.6.69