5 λΆ
π§ Rustμ λ―ΈλΆνκΈ° 01: μμΉμ λ―ΈλΆ
π Automatic Differentiation Series
λ―ΈλΆμ ν¬λμ μ²μ¬μλ μμ΄μ λ΄ν΄μ΄λλ‘ μμ΄μλ μ λ μ€μν κ°λ μ΄ λμμ΅λλ€. λ¬Έκ³Όλ μ΄κ³Ό λͺ¨λ ꡬλΆμμ΄ κ³ λ±νκ΅λ μ μ΄λ λ€νν¨μμ λ―ΈλΆλ²μ λ°°μ°λ©° μ΄κ³΅κ³λ κ±°μ λͺ¨λ νκ³Όμμ λ―ΈλΆλ°©μ μμ λ€λ£Ήλλ€. 물리νκ³Όμ κ²½μ°λ μ’ λ λ―ΈλΆ μμ‘΄λκ° μ¬νλ°, λΉμ₯ 물리μ μμμ΄λΌκ³ ν μ μλ κ³ μ μνλΆν° μ€μΌλ¬-λΌκ·Έλμ£Ό λ°©μ μ(Euler-Lagrange equation)μ μμ‘΄νλ©° 물리νκ³Όμ ν΅μ¬μ΄λΌ ν μ μλ μ μκΈ°ν, μμμνμ κ±°μ λͺ¨λ μμμ λ―ΈλΆμ΄ λΉ μ§μ§ μμ΅λλ€.
λΉμ°νκ²λ μμΉ κ³μ° λΆμΌμμλ λ―ΈλΆμ νμ λ±μ₯ν©λλ€. λ€λ§, μΈκ°μ΄ λ―ΈλΆμ μ΄ν΄νλ λ°©μκ³Ό μ»΄ν¨ν°κ° μ΄ν΄νλ λ°©μμ μ°¨μ΄κ° μκΈ°μ λ―ΈλΆμ λ°μλ€μ΄λ λ°©λ² μμ μ‘°κΈ λ€λ¦ λλ€. μΌλ¨ λ―Έμ λΆνμμ κ°λ¨νκ² λ°°μ°λ λν¨μμ μ μλ λ€μκ³Ό κ°μ΅λλ€.
$$ f’(x) = \lim_{h \rightarrow 0} \frac{f(x+h) - f(x)}{h} $$
μλ₯Ό λ€μ΄ $f(x) = x^2$μ λ―ΈλΆνλ€λ©΄ λ€μκ³Ό κ°μ΄ κ°λ¨νκ² κ³μ°ν μ μμ΅λλ€.
$$ \lim_{h \rightarrow 0} \frac{(x+h)^2 - x^2}{h} = \lim_{h \rightarrow 0}\frac{2hx + h^2}{h} = 2x $$
νμ§λ§ μ»΄ν¨ν°κ° μ΄ λ¬Έμ λ₯Ό μ νκ² λλ€λ©΄ μλΉν λκ°ν μν©μ λμ λλ€. κ·Ήνμ΄λΌλ κ°λ μ΄ μ»΄ν¨ν°μ ꡬ쑰μ λμΉλκΈ° λλ¬Έμ λλ€. $h$κ° $0$μΌλ‘ κ°λ κ·Ήνμ΄λΌλ κ²μ 0μ νμμ΄ κ°κΉμ΄ μ κ·Όνλ€λ μλ―Έλ‘ $h$μ $0$μ μ°¨μ΄κ° κ·Έ μ΄λ€ μ«μλ³΄λ€ μκ² λμ΄μΌ νλ€λ λ»μΈλ°, μ»΄ν¨ν°λ ꡬ쑰 μ νμμ΄ κ°κΉμ΄ κ°λ κ²μ΄ λΆκ°λ₯ν©λλ€. νμ¬ λλΆλΆμ μ°¨μ§νκ³ μλ 64bit μ»΄ν¨ν°λ $2^{-53}$ μ΄ν, μ¦, λλ΅ $10^{-16}$μ΄νμ μ°¨μ΄λ $0$κ³Ό ꡬλΆν μ μμ΅λλ€. λ°λΌμ μ¬λλ€μ ν¬κ² λ κ°μ§ λ°©μμΌλ‘ μ΄λ₯Ό ν΄κ²°νμμ΅λλ€.
Β
π» μμΉμ λ―ΈλΆ (Numerical Differentiation)
μ»΄ν¨ν°λ κ·Ήνμ λ³Έμ§μ μΌλ‘ λ€λ£° μ μμ§λ§, λλΆλΆμ κ³μ°μμλ $10^{-16}$ μ λλ©΄ μμ£Ό μΆ©λΆν μ λ°λμΌ μ μμ΅λλ€. νΉμ λ¨μλ₯Ό μ‘°μ νλ©΄μ μΆ©λΆν μ λ°λκ° λλλ‘ λ§λλ λ°©λ²λ μ‘΄μ¬ν©λλ€. λ°λΌμ κ·Ήνμ λ€λ£¨λ λμ μμ£Ό μμ $h$λ₯Ό μ΄μ©νμ¬ κ·Ήνμ κ·ΌμΏκ°μ ꡬνμ¬ κ³μ°μ μ΄μ©ν μ μλλ°, μ΄λ¬ν λ°©λ²μ μμΉμ λ―ΈλΆμ΄λΌ ν©λλ€. μΌλ¨ μμ£Ό κ°λ¨νκ² μμΉμ λ―ΈλΆμ ꡬνν΄λ³΄κ² μ΅λλ€.
# Python
def diff(f, x, h):
return (f(x+h) - f(x)) / h
μμΉμ λ―ΈλΆμ Python ꡬνμ λλΌμΈ μ λλ‘ μμ£Ό κ°λ¨ν©λλ€. ν¨μμ λ³μ κ·Έλ¦¬κ³ μ λ°λλ₯Ό λ£μ΄μ£Όλ©΄ λ°λ‘ λ―ΈλΆκ°μ΄ λμ΅λλ€.
// Rust
fn diff<F: Fn(f64) -> f64>(f: F, x: f64, h: f64) -> f64 {
(f(x+h) - f(x)) / h
}
Rust ꡬνλ λΉκ΅μ κ°λ¨ν νΈμ΄μ§λ§, νμ μ λͺ μν΄μΌ λλ μ μ΄ Pythonκ³Όμ μ°¨μ΄λ₯Ό λ§λλλ€. Rustμμ ν¨μλ₯Ό μΈμλ‘ λ°μ λλ μμ κ°μ΄ μ λλ¦ νμ (Generic Type)μΌλ‘ λ°λ κ²μ΄ μ’μ΅λλ€. κ·ΈλμΌ λͺ μμ ν¨μλ ν΄λ‘μ (Closure) κ΅¬λΆ μμ΄ μ¬μ©ν μ μμ΅λλ€. μ΄ μ½λλ€μ μ΄μ©νμ¬ $f(x) = x^2$μ $x=1$μμμ λ―ΈλΆ κ³μλ₯Ό ꡬν΄λ΄ μλ€.
// Rust
fn main() {
println!("{}", diff(f, 1f64, 1e-6));
}
fn diff<F: Fn(f64) -> f64>(f: F, x: f64, h: f64) -> f64 {
(f(x+h)-f(x)) / h
}
fn f(x: f64) -> f64 {
x.powi(2)
}
μ½λμμ μ μ μλ―μ΄ μ λ°λλ $h=10^{-6}$μ λμ νμ¬ κ³μ°νμμ΅λλ€. κ²°κ³Όλ $2.0000009999243673$μΌλ‘ μμ«μ 6λ²μ§Έ μ리κΉμ§λ μ΄λ‘ κ°μΈ $2$μ μΌμΉν¨μ 보μ¬μ€λλ€. μ΄ μμΉμ λ―ΈλΆμ½λλ κ°λ¨νκ³ λΉ λ₯΄κ² λ―ΈλΆ κ°μ ꡬν μ μλ€λ μ₯μ μ΄ μμ§λ§, λν¨μλ₯Ό ꡬνκΈ° μν΄μλ λ°λ³΅μ μΌλ‘ ν¨μλ₯Ό λμ ν΄μΌ λλ€λ μ μμ λΆνΈν¨μ μΌκΈ°ν©λλ€. λ°λΌμ λν¨μλ₯Ό ꡬνκΈ° μν΄μλ μ‘°κΈ λ μ½λλ₯Ό λλ €μΌ ν©λλ€. λ¨Όμ ꡬ쑰체λ₯Ό μ΄μ©νλ κ°μ²΄μ§ν₯μ λ°©λ²μ μ¬μ©νμ¬ κ΅¬νν΄λ³΄κ² μ΅λλ€.
// Rust
struct Derivative<F: Fn(f64) -> f64> {
pub f: F,
pub h: f64,
}
impl<F: Fn(f64) -> f64> Derivative<F> {
fn f(&self, x: f64) -> f64 {
(self.f)(x)
}
fn calc(&self, x: f64) -> f64 {
(self.f(x+self.h) - self.f(x)) / self.h
}
}
μ΄λ κ² νλ©΄ ν¨μμ μ λ°λλ μ΄κΈ° μ μΈμμλ§ μ
λ ₯νλ©΄ λκ³ , calc
λ©μλλ₯Ό μ΄μ©νμ¬ μ¬λ¬ $x$ κ°μμ κ³μ°μ΄ κ°λ₯ν΄μ§λλ€. f
λ©μλλ λ³΄λ€ νΈνκ² self.f(x)
λ₯Ό μ΄μ©νκΈ° μν΄ μ μΈνμμ΅λλ€. λ§μΌ μ΄λ¬ν λ©μλκ° μλ€λ©΄ (self.f)(x)
κΌ΄λ‘ μ
λ ₯ν΄μΌλ§ ν©λλ€. κ·ΈλΌ μ΄μ μ΄ μ½λλ₯Ό μ΄μ©νμ¬ μμμμ μμλ₯Ό ꡬνν΄λ΄
μλ€.
// Rust
fn main() {
let df = Derivative {
f,
h: 1e-6,
};
println!("{}", df.calc(1f64));
}
fn f(x: f64) -> f64 {
x.powi(2)
}
struct Derivative<F: Fn(f64) -> f64> {
pub f: F,
pub h: f64,
}
impl<F: Fn(f64) -> f64> Derivative<F> {
fn f(&self, x: f64) -> f64 {
(self.f)(x)
}
fn calc(&self, x: f64) -> f64 {
(self.f(x+self.h) - self.f(x)) / self.h
}
}
λΉμ°νκ²λ λ΅μ μκΉμ κ²½μ°μ κ°κ² λμ΅λλ€. μ΄λ²μλ μ§μ§ “λν¨μ"λ₯Ό λ§λλ ν¨μν νλ‘κ·Έλλ°μ κ³ κ³ ν¨μ(Higher order function) κ°λ μ μ΄μ©νμ¬ κ΅¬νν΄λ³΄κ² μ΅λλ€.
// Rust
fn derivative<F: Fn(f64) -> f64>(f: F, h: f64) -> impl Fn(f64) -> f64 {
move |x: f64| (f(x+h) - f(x)) / h
}
μΌλ¨ F
λ‘ f64 -> f64
ν¨μ μν μ νλ λͺ¨λ νμ
μ λ°μ μ μλ€λ κ²μ μμμμ κ°μ΅λλ€. λ€λ§ λ°ν νμ
λΆλΆμ λ―μ ν€μλλ€μ΄ μμ΅λλ€.
Rustμ Genericμλ ν¬κ² λ κ°μ§ λ°©μμ΄ μ‘΄μ¬ν©λλ€. 첫 λ²μ§Έλ μμ λ΄€λ F
μ κ°μ΄ Type placeholderλ₯Ό μ¬μ©νλ λ°©μμ΄κ³ , λ λ²μ§Έλ impl Trait
μ²λΌ impl
ν€μλλ₯Ό μ΄μ©νλ λ°©μμ΄ μμ΅λλ€. λ λ°©μ λͺ¨λ ν° μ°¨μ΄λ μμ§λ§, μ¬λ¬ κ°μ νμ
μ΄ κ°μ΄ μ°μΌ λμ κ° νμ
λ€μ΄ κ°μ νμ
μΈμ§, λ€λ₯Έ νμ
μΈμ§ λͺ
νν ν λμλ μ μμ λ°©μμ μ°κ³ , ν κ°μ§ νμ
λ§ μ¬μ©νκ±°λ νμ
μ’
λ₯보λ€λ μν μ΄ μ€μν λμλ νμμ λ°©μμ μ¬μ©ν©λλ€. μλ₯Ό λ€μ΄ μ μ½λλ₯Ό Type placeholderλ₯Ό μ΄μ©νμ¬ κ΅¬ννλ©΄ λ€μκ³Ό κ°μ΅λλ€.
// Rust
fn derivative<F, G>(f: F, h: f64) -> G
where
F: Fn(f64) -> f64,
G: Fn(f64) -> f64,
{
move |x: f64| (f(x+h) - f(x)) / h
}
μλμ ꡬνμ΄ κ°λ
μ± λ©΄μμλ μλ―Έ λ©΄μμ μ’ λ μ’μ ꡬνμ΄μ§λ§, μ΄ ν¨μλ₯Ό μ¬μ©ν λ μ μ½μ΄ μ¬ν νΈμ
λλ€. impl Trait
κΌ΄λ‘ λ°ννλ©΄ Rustλ κ·Έ νμ
μ ν¨μ λ³Έλ¬Έμμ λ°ννλ κ°μΌλ‘ μλ μΆλ‘ νμ¬ μ¬μ©νμ§λ§, Type placeholderλ‘ λ°ννλ©΄ κ·Έ νμ
μ λͺ
νν νκΈ° μ μλ μ»΄νμΌ λμ§ μμ΅λλ€.
λν μμ κ°μ μ½λλ₯Ό μμ±ν λ, ν΄λ‘μ μ μ±μ§μ μ μν΄μΌν©λλ€. ν΄λ‘μ λ μΈμλ‘ λ€μ΄μ¨ κ°μ΄ μλ μ£Όλ³ νκ²½λ κ°μ΄ μΊ‘μ³λ₯Ό νλ μ±μ§μ΄ μλλ°, μ΄λ, μ£Όλ³ λ³μλ€μ΄ ν΄λ‘μ λ°μμλ μμ‘΄ν μ μλ€λ©΄ μ»΄νμΌ μ€λ₯κ° λ°μν©λλ€.
μ μ½λμμλ f
μ h
λ ν¨μμ μΈμλ‘ λ°μκΈ°μ ν¨μμ μ μΈμ΄ λλλ μμ μ λ©λͺ¨λ¦¬κ° ν΄μ λ©λλ€. νμ§λ§ λ°νλλ ν΄λ‘μ λ f
μ h
μ κ°μ μ¬μ©ν΄μΌ ν©λλ€. λ°λΌμ move
ν€μλλ₯Ό μ΄μ©νμ¬ f
μ h
μ μμ κΆμ ν΄λ‘μ μ λ겨주μ΄μΌ ν©λλ€.
μ΄μ μ€λͺ μ΄ λλ¬μΌλ μ΄ κ³ κ³ν¨μλ₯Ό μ΄μ©νμ¬ λν¨μλ₯Ό λ§λ€μ΄ λ³΄κ² μ΅λλ€.
// Rust
fn main() {
let df = derivative(f, 1e-6);
println!("{}", df(1f64));
}
fn f(x: f64) -> f64 {
x.powi(2)
}
fn derivative<F: Fn(f64) -> f64>(f: F, h: f64) -> impl Fn(f64) -> f64 {
move |x: f64| (f(x+h) - f(x)) / h
}
λ΅μ μμ λ κ²½μ°μ μ νν μΌμΉν©λλ€.
κ·ΈλΌ μ΄μ μμΉμ λ―ΈλΆ λ°©μμ μ₯μ κ³Ό λ¨μ μ μμ½ν΄λ³΄κ² μ΅λλ€.
μμΉμ λ―ΈλΆμ μ₯λ¨μ
- μ₯μ
- ꡬννλ κ²μ΄ κ΅μ₯ν μ½λ€.
- μμ£Ό λΉ λ₯΄κ² λ―ΈλΆ κ³μ°μ μνν μ μλ€.
- λ¨μ
- μ€μ°¨κ° μμ΄λ©΄μ μ€μ κ°κ³Ό λ§μ΄ λ€λ₯Έ κ°μ΄ λμ¬ μ μλ€.
κ³μ° μλμ νΈμ μμ ν° μ₯μ μ κ°μ§κ³ μμ§λ§ μ€μ°¨κ° κ³μ μμΌ μ μμΌλ―λ‘ Step sizeλ μμ§λ§ ꡬκ°μ κΈ΄ μμΉλ―ΈλΆλ°©μ μ λ±μ μμΉμ λ―ΈλΆμ μ μ©νκΈ°μ νκ³κ° μμ΅λλ€. λ€νν μ΄λ₯Ό ν΄κ²°νκΈ° μν λ°©λ²λ€μ μ‘΄μ¬ν©λλ€. μ΄μ λν΄μλ λ€μμ λ€λ€λ³΄λλ‘ νκ² μ΅λλ€.