줄리아의 자동미분 패키지 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