<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}]"}