<style> .reveal, .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, "Microsoft JhengHei", Meiryo, sans-serif; } h1, h2, h3, h4, h5, h6 { text-transform: none !important; } .color-yellow{ color: yellow; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; text-align: left; padding: 10px 0; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .reveal .slides span { text-align: left; display: inline-block; } p, li { font-size: 0.88em !important; } li>p { font-size: 1em !important; } </style> # 2021年末LT大会 ###### tags: `JuliaTokai` `prezentation` --- # Juliaの微分法<br>いろいろ [堀川 由人, ほりたみゅ, @Hyrodium](https://hyrodium.github.io) ---- ## おしながき 自動微分に入門した話をします * 自己紹介 * コンピュータで扱える微分法 * 自動微分とは? * コード例 * まとめ --- ## 自己紹介 Julia言語がスキ! [個人サイト](https://hyrodium.github.io)をDocumenter.jlで作っています↓ ![](https://i.imgur.com/x9loEor.png =500x) ---- ## 2021年 買って良かったもの ![](https://pbs.twimg.com/media/E-BQsv0UYAAYfm2?format=jpg&name=orig =450x) FLUXのレーザー加工機 台湾から個人輸入しました ~~30まんえん💸💸~~ ---- ### レーザー加工機でMandelbrot集合 ![](https://pbs.twimg.com/media/E-WC-YmVcAcMhjf?format=jpg&name=orig =450x) * JuliaでMandelbrot集合を計算 * Inkscapeでラスタ画像をベクタ画像に変換 * レーザー加工機で紙を切断 --- ## コンピュータで扱える微分法 * 数値微分 * 自動微分 * 記号計算 ---- ### コンピュータで扱える微分法 (概要) * 数値微分 * 適当な小さい実数$h$を設定して$\frac{f(x+h)-f(x)}{h}$を計算 * 適切な近似のためには$h$は小さい方が良い * 浮動小数点数の精度ためには$h$は大きい方が良い * 自動微分 * Chain rule(積の微分法、Leibniz則)などを使って微分を計算 * 関数の中身がブラックボックスだと計算できない * 記号計算 * 記号的に微分を計算する * e.g. $2x\cos(3\sqrt{x}) \mapsto 2\cos(3\sqrt{x})-3\sqrt{x}\sin(3\sqrt{x})$ * 積分と違って微分は素直に計算できる ---- ### コンピュータで扱える微分法 (特徴) * 数値微分 * :smile: 単純・実装しやすい * :unamused: 誤差あり * :slightly_smiling_face: 元の関数より計算コスト増える * 自動微分 * :star-struck: 勝手に微分してくれる (ライブラリに頼れば) * :heart_eyes: 高速 * 記号計算 * :thinking_face: 数値演算に比べれば遅い * :blush: 数式が一度得られれば再計算は不要 ---- ### 微分に関するJuliaパッケージ [JuliaDiffでの解説](https://juliadiff.org/) * 数値微分 * [FiniteDiff.jl](https://github.com/JuliaDiff/FiniteDiff.jl) / [FiniteDifferences.jl](https://github.com/JuliaDiff/FiniteDifferences.jl) * 自分で差分を実装するよりは高速(らしい) * 自動微分 * [**ForwardDiff.jl**](https://github.com/JuliaDiff/ForwardDiff.jl) / [Zygote.jl](https://github.com/FluxML/Zygote.jl) / [Diffractor.jl](https://github.com/JuliaDiff/Diffractor.jl) * 色々なパッケージがある * 記号計算 * [Symbolics.jl](https://github.com/JuliaSymbolics/Symbolics.jl) / [SymbolicUtils.jl](https://github.com/JuliaSymbolics/SymbolicUtils.jl) * 「記号的に微分 → Juliaコード生成する」が最強 * ただし制約がある --- ## 自動微分とは? 他の資料を参照した方が良いですね * [自動微分を実装して理解する(前編)](https://qiita.com/lotz/items/39c52f08cc9b5d8439ca) * [自動微分で遊ぼう](http://rydotyosh.github.io/docs/adintro/index.html) * [Zygote.jlでの微分計算の仕組み](https://atelierarith.github.io/zygote_flux_tutorial/zygote_internals.html) --- ## コード例 * $\sin(x)$の微分 * 数値微分 * 自動微分 * 記号計算 * 発展:平方根を自前で計算 ---- ### コード例 (数値微分) ```julia= using Plots h = 1e-5 Δ(f) = t->(f(t+h)-f(t-h))/2h plot(sin,-4,4, label="0th", ylim=(-2,2)) plot!(Δ(sin),-4,4, label="1st") plot!(Δ(Δ(sin)),-4,4, label="2nd") plot!(Δ(Δ(Δ(sin))),-4,4, label="3rd") ``` ![](https://i.imgur.com/ayki9Ba.png) ---- ### コード例 (自動微分) ```julia= using Plots, ForwardDiff d(f) = t->ForwardDiff.derivative(f,t) plot(sin,-4,4, label="0th", ylim=(-2,2)) plot!(d(sin),-4,4, label="1st") plot!(d(d(sin)),-4,4, label="2nd") plot!(d(d(d(sin))),-4,4, label="3rd") ``` ![](https://i.imgur.com/o6JBU3b.png) ---- ### コード例 (記号計算) ```julia= using Symbolics @variables t D = Differential(t) f1 = expand_derivatives(D(sin(t))) f2 = expand_derivatives(D(D(sin(t)))) f3 = expand_derivatives(D(D(D(sin(t))))) F1 = eval(build_function(f1,t)) F2 = eval(build_function(f2,t)) F3 = eval(build_function(f3,t)) plot(sin,-4,4, label="0th", ylim=(-2,2)) plot!(F1,-4,4, label="1st") plot!(F2,-4,4, label="1st") plot!(F3,-4,4, label="1st") ``` ![](https://i.imgur.com/MoAbtGP.png) ---- ### 発展的な例 (`my_sqrt` ①) 二分法で平方根を計算 ```julia= function my_sqrt(x) s_min, s_max = 0, (x+1)/2 s_mean = (s_min+s_max)/2 for _ in 1:100 if s_mean^2 < x s_min = s_mean else s_max = s_mean end s_mean = (s_min+s_max)/2 end return s_mean end ``` ---- ### 発展的な例 (`my_sqrt` ②) 組み込みの`sqrt`と一致 ```julia= sqrt(2) # 1.4142135623730951 my_sqrt(2) # 1.414213562373095 plot(sqrt,0,3, label="sqrt") plot!(my_sqrt,0,3, label="my_sqrt") ``` ![](https://i.imgur.com/z8sZiVh.png =500x) ---- ### 発展的な例 (`my_sqrt` ③) 自動微分してみる (一致しないが!) ```julia= plot(x -> 1/2sqrt(x),1,3, label="true derivative") plot!(x -> ForwardDiff.derivative(sqrt,x),1,3, label="sqrt'") plot!(x -> ForwardDiff.derivative(my_sqrt,x),1,3, label="my_sqrt'") ``` ![](https://i.imgur.com/HnZ5T6Z.png =500x) ---- ### 発展的な例 (`my_sqrt` ④) 二分法の反復回数を減らしてみる (`100` → `4`) ![](https://i.imgur.com/1jV7Pmq.png =450x) ![](https://i.imgur.com/2rabuur.png =450x) 区分直線としての傾きを返していた! ---- ### 発展的な例 (`my_sqrt` ⑤) `my_sqrt`に対して正しく自動微分するには? → [二重数](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%87%8D%E6%95%B0)に対するメソッドを自前で定義! ```julia= function my_sqrt(d::ForwardDiff.Dual{T}) where T v = my_sqrt(ForwardDiff.value(d)) dv = 1/2v return ForwardDiff.Dual{T}(v, dv*ForwardDiff.partials(d)) end ``` ![](https://i.imgur.com/TT4hVP4.png =430x) --- ## まとめ * コンピュータでの微分には色々ある * 自動微分は速くて正確ですごい! * Juliaは多重ディスパッチが便利! * 普通に関数を定義すれば自動微分できる * cf. [自動微分ライブラリの比較](https://ntacoffee.com/automatic-differentiation-libraries/)(python) * (話せなかったこと) * 計算速度の比較 * ForwardDiff以外の自動微分パッケージ * Zygote, Diffractor, ChainRulesなど
{"metaMigratedAt":"2023-06-16T16:35:23.000Z","metaMigratedFrom":"YAML","title":"2021年末LT大会","breaks":true,"lang":"ja","dir":"ltr","robots":"noindex, nofollow","slideOptions":"{\"theme\":\"white\",\"transition\":\"slide\"}","contributors":"[{\"id\":\"41421433-16a1-4a57-ac11-6f7b7becb765\",\"add\":6164,\"del\":46}]"}
    435 views