diff --git a/config/config.json b/config/config.json index 4e85ebf..2c1c72f 100644 --- a/config/config.json +++ b/config/config.json @@ -6,7 +6,7 @@ "twitter_id": "abap34", "github_id": "abap34", "overall_theme": "light", - "custom_css": "themes/modern-light.css", + "custom_css": "themes/lightblue.css", "editor_theme": "ace/theme/xcode", "syntax_theme": "github.min" } \ No newline at end of file diff --git a/posts/caesar.md b/posts/caesar.md index df6b0a0..e56a692 100644 --- a/posts/caesar.md +++ b/posts/caesar.md @@ -34,7 +34,7 @@ twitter_site: @abap34 というかなり簡素な仕組みの解読法ですが、これがどれくらいうまく行くのか気になるところです。 - + チャチャっと調べてみます。 [英語Wikipediaのデータ](https://www.kaggle.com/datasets/mikeortman/wikipedia-sentence) を適当に持ってきて、 diff --git a/posts/githubactions_trap.md b/posts/githubactions_trap.md index d4f902a..a7d24a8 100644 --- a/posts/githubactions_trap.md +++ b/posts/githubactions_trap.md @@ -33,12 +33,12 @@ twitter_site: @abap34 みたいなことをしていますが、これを普通に書くだけでは `fetal: bad obejct xxx...`となります。 - + 全然原因がわからず、 `actions/checkout`を見にいくと、 - + ![Alt text](checkout.png) すいません。 READMEの一番上に書いてありました。 diff --git a/posts/grpc_ml.md b/posts/grpc_ml.md index 317c533..944dacb 100644 --- a/posts/grpc_ml.md +++ b/posts/grpc_ml.md @@ -36,7 +36,7 @@ twitter_site: @abap34 この記事は当初気合いでGoのみで学習済みモデルを呼び出す記事にしようと思っていたのですが、思ってるよりもとってもしんどいことが判明したので急遽ネタを変更しました。 - + ## 実装 [こちらの記事](https://zenn.dev/hsaki/books/golang-grpc-starting/viewer/intro) をとても参考にさせていただきました。ありがとうございます。 diff --git a/posts/hello_scheme.md b/posts/hello_scheme.md index 07a1ee5..147177f 100644 --- a/posts/hello_scheme.md +++ b/posts/hello_scheme.md @@ -32,3 +32,4 @@ Twitterでも有名な人が作ったSchemeの処理系で、サクッと入れ + \ No newline at end of file diff --git a/posts/hurikaeri_2023_0.md b/posts/hurikaeri_2023_0.md index 73ff7f0..c31e38b 100644 --- a/posts/hurikaeri_2023_0.md +++ b/posts/hurikaeri_2023_0.md @@ -33,6 +33,7 @@ twitter_site: @abap34 ![](https://trap.jp/content/images/2023/07/wc-1.png?original=1) + - DacQ - 部内 mini Kaggleです。昨年度にも先輩の間で開発の構想があったみたいなのですが頓挫してしまっていて、えいやとかなり簡素なものですが作りました。 - 部内 PaaS の Neoshowcase を使うとめちゃくちゃ簡単に traP部員だけが使えるように認証できて、感動... diff --git a/posts/jimei.md b/posts/jimei.md index 109d323..f4fa7b4 100644 --- a/posts/jimei.md +++ b/posts/jimei.md @@ -89,6 +89,7 @@ $$ 中身が空の小銭入れは自明小銭入れである。 ::: + これらは - 定義が単一の集合に対して与えられている diff --git a/posts/julia_timer.md b/posts/julia_timer.md index 6cf5ad9..4686671 100644 --- a/posts/julia_timer.md +++ b/posts/julia_timer.md @@ -45,6 +45,7 @@ function rate(x::Timer) end ``` + 使い方は、 ```julia diff --git a/posts/parallel_othello.md b/posts/parallel_othello.md index 22b391b..73c5807 100644 --- a/posts/parallel_othello.md +++ b/posts/parallel_othello.md @@ -37,4 +37,3 @@ Twitterに上げたら実装で参考にした[山本さんから反応があっ - diff --git a/posts/racket.md b/posts/racket.md index 0dfdb15..a590ea1 100644 Binary files a/posts/racket.md and b/posts/racket.md differ diff --git a/posts/rebuild_abap34_com.md b/posts/rebuild_abap34_com.md index 6be4205..844992d 100644 --- a/posts/rebuild_abap34_com.md +++ b/posts/rebuild_abap34_com.md @@ -72,3 +72,4 @@ PyOdideを使って実行環境・ジャッジ同梱のHTMLが吐けるという 弱い。 + diff --git a/public/posts/autodiff.html b/public/posts/autodiff.html index 6dc95b4..9fbaab5 100644 --- a/public/posts/autodiff.html +++ b/public/posts/autodiff.html @@ -1,4 +1,3 @@ - @@ -19,7 +18,8 @@ - + @@ -27,7 +27,7 @@ - + @@ -43,467 +43,466 @@ + body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #333; + color: #F7F7F7; + flex-direction: column; + display: flex; + width: 95%; + } - + h1, + h2 { + color: #F7F7F7; + text-align: center; + margin-bottom: 20px; + } - + outputs = ""; + try { + await pyodide.runPythonAsync(code); + out_area.innerText = outputs; + } catch (error) { + out_area.innerText = error; + out_area.style.color = "orange"; + } + } + + - +
@@ -535,109 +534,114 @@Expression
は式全体を保持します。 root
に最後に評価されるノードが入ります。Node
はノードの抽象クラスです。 これを継承して、各ノードのクラスを定義します。__call__
は特殊メソッドです。 クラス A
のインスタンス a
に対して a()
と書くと a.__call__()
が呼ばれます。__call__
が呼ばれるとルートから葉ノードまで順番に __call__
が呼ばれていき、葉ノード (必ず Constant
か Variable
です) からルートに向けて順番に値が返されていきます。diff
も同じです。pydot
というライブラリを使って木を式をプロットします。import pydot
+
+
+
+
+ となり、正しく計算できています。
+
+
+ 数値微分の計算量
+ 数値微分は中心差分の一変数関数であれば2回の関数評価で微分を計算することができます。
+
+ \(\mathbb{R}^n \to \mathbb{R}^m\) の関数のヤコビアン行列を求める場合は、 \(\Theta(nm)\) 回の評価が必要です。
+
+
+ 数値微分の長所
+ なんと言っても実装が非常に簡単です。
+
+ 数値微分では \(f\) が何であろうと \(f(x + h)\) と \(f(x)\) さえ計算できれば
+ 結果がもとまります。
+
+ (もちろんそれが正しいという保証はありませんが)
+
+ したがってたとえ特殊な関数でもほとんど準備なく微分を計算することができます。
+
+
+ そのため、後述の自動微分などを実装する際には数値微分を用いてテストを行うことが多いです。
+
+
+
+ 数値微分の短所
+ 後述の自動微分などと比較すると誤差が大きくなります。
+
+ また、入力と出力の両方の次元に比例した計算量がかかるため、
+ 入力または出力の次元が大きい場合は計算量が大きくなります。
+
+
+ 数式微分
+
+ 数式微分のアイデア
+ 数式微分は、我々が普段微分を行うときと同じように、陽に導関数を求めます。
+
+ つまり、数値微分のアルゴリズムは、
+
+ (関数, 微分係数を求めたい点) → (微分係数の値)
+
+ でしたが、数式微分は
+
+ (関数) → (導関数)
+
+ というアルゴリズムになります。
+
+ 数式微分では、微分したい式を木あるいはDAGとして表現し、
+ この木構造への操作を行い、導関数となる木を得るような実装が一般的です。
+
+ 例えば、
+
+ \[
+ (x + y) \times \sin(x)
+
+ \]
+
+ という式は、
+
+
+
+ のように表現できます。
+
+
+
+ 数式微分の実装
+ 実装の一例を示そうと思います。
+
+ ここでは、簡単のために三角関数と足し算、掛け算からなる式を数式微分する実装を書きました。
+
+ ソースコードは長いですがほとんどが同じような定義が続くだけなので、そこまで大変ではないと思います。
+
+ 少しだけ解説を加えておきます。
+
+
+ Expression
は式全体を保持します。 root
に最後に評価されるノードが入ります。
+ Node
はノードの抽象クラスです。 これを継承して、各ノードのクラスを定義します。
+ - あとは木構造のノードになりうる、演算子、変数、定数を表すクラスを定義しています。
+ __call__
は特殊メソッドです。 クラス A
のインスタンス a
に対して a()
と書くと
+ a.__call__()
が呼ばれます。
+ __call__
が呼ばれるとルートから葉ノードまで順番に __call__
が呼ばれていき、葉ノード (必ず
+ Constant
か Variable
です) からルートに向けて順番に値が返されていきます。
+
+
+ diff
も同じです。
+ pydot
というライブラリを使って木を式をプロットします。
+
+
+ import pydot
import numpy as np
@@ -1446,89 +1466,92 @@ 数式微分の実装
def diff(self):
return Mul(Constant(-1), Mul(Sin(self.children[0]), self.children[0].diff()))
-
-
-実際に動かしてみましょう。
-
-ここでは式を可視化する便利な関数 plot
を用意しています。
-
-まずは式を定義します。
-
-f = Expression(Mul(Add(Variable(), Constant(1)), Sin(Variable())))
+
+
+ 実際に動かしてみましょう。
+
+ ここでは式を可視化する便利な関数 plot
を用意しています。
+
+ まずは式を定義します。
+
+ f = Expression(Mul(Add(Variable(), Constant(1)), Sin(Variable())))
-
-これは、
-
-\[
-(x + 1) \times \sin(x)
-
- \]
-
-という式です。
-
-
-print(f)
+
+ これは、
+
+ \[
+ (x + 1) \times \sin(x)
+
+ \]
+
+ という式です。
+
+
+ print(f)
-
-すると、
-
-((x + 1) * sin(x))
+
+ すると、
+
+ ((x + 1) * sin(x))
-
-となります。
-
- plot
で可視化すると、
-
-f.plot().write_png('fig/expr.png')
+
+ となります。
+
+ plot
で可視化すると、
+
+ f.plot().write_png('fig/expr.png')
-
-
-
-というふうな木として保持されていました。
-
-では、この式を微分してみましょう。
-
-df = f.diff()
+
+
+
+ というふうな木として保持されていました。
+
+ では、この式を微分してみましょう。
+
+ df = f.diff()
-
-すると、
-
-print(df)
+
+ すると、
+
+ print(df)
-
-(((1 + 0) * sin(x)) + ((x + 1) * (cos(x) * 1)))
+
+ (((1 + 0) * sin(x)) + ((x + 1) * (cos(x) * 1)))
-
-という導関数が得られました。
-実際に数値微分の結果と比較して確かめてみます。
-
-x = np.random.random()
+
+ という導関数が得られました。
+ 実際に数値微分の結果と比較して確かめてみます。
+
+ x = np.random.random()
print(df(x))
print(numerical_diff(f, x, 1e-5))
-
-1.8300012766059257
+
+ 1.8300012766059257
1.830005971548143
-
-どうやら正しそうです。
-
-ですが、得られた式は明らかに非常に冗長です。
-
-例えば 1 + 0
という部分は 1
になるべきですし、 cos(x) * 1
という部分も cos(x)
になるべきです。
-
-
-そこでこの式を簡単にすることを試みます。
-
-
-簡約化
-メソッド simplify
を追加します。
-
-
-例えば Mul
については
-
-class Mul(Node):
+
+ どうやら正しそうです。
+
+ ですが、得られた式は明らかに非常に冗長です。
+
+ 例えば 1 + 0
という部分は 1
になるべきですし、 cos(x) * 1
という部分も cos(x)
+ になるべきです。
+
+
+ そこでこの式を簡単にすることを試みます。
+
+
+ 簡約化
+ メソッド simplify
を追加します。
+
+
+ 例えば Mul
については
+
+ class Mul(Node):
def __init__(self, x, y):
super().__init__()
self.children = [x, y]
@@ -1547,52 +1570,60 @@ 簡約化
else:
return Mul(self.children[0].simplify(), self.children[1].simplify())
-
-
-やや冗長ですが、このようにして 定数の 0
とかけあわせたり 1
とかけあわせたりしたときのノードを置き換えます。
-
-これを diff
のあとに呼び出すことで、簡約された式を得ることができます。
-
-print(df.simplify())
+
+
+ やや冗長ですが、このようにして 定数の 0
とかけあわせたり 1
とかけあわせたりしたときのノードを置き換えます。
+
+ これを diff
のあとに呼び出すことで、簡約された式を得ることができます。
+
+ print(df.simplify())
-
-(sin(x) + ((x + 1) * cos(x)))
+
+ (sin(x) + ((x + 1) * cos(x)))
-
-df.simplify().plot().write_png('fig/expr_diff_simple.png')
+
+ df.simplify().plot().write_png('fig/expr_diff_simple.png')
-
-
-
-このように、簡約された式を得ることができました。
-
-このように得られた式を適切な同様の式に変換するのは非常に重要です。
-
-数式微分では、実装が悪い場合には微分した結果の式が非常に冗長になる危険性が高いです。
-
-というのも、いわゆる積の微分の公式
-
-\[
-(fg)' = f'g + fg'
-
- \]
-
-からわかるように、積が含まれる式を微分すると、微分した結果の式は元の式よりも長くなります。
-
-これが入れ子になれば項の数は指数的に増えていき、文字通り爆発する場合があります。
-
-
-
-多変数への拡張
-\(f: \mathbb{R}^n \to \mathbb{R}\) の勾配 \(\nabla f(\boldsymbol{x})\) を数式微分で求めます。
-
-実装は先ほどとほぼ同様で、
-
-Variable
は x
だけでなく、変数名を持てるようにする__call__
の引数を値ではなく、 {'x': 1, 'y': 2}
のような辞書に変更diff
の引数に偏微分する変数を追加Expression
に 全ての変数を列挙する all_variables
メソッドを追加- 全ての変数をループして
diff
を計算する grad
を追加
-という変更を加えました。
-
-import pydot
+
+
+
+ このように、簡約された式を得ることができました。
+
+ このように得られた式を適切な同様の式に変換するのは非常に重要です。
+
+ 数式微分では、実装が悪い場合には微分した結果の式が非常に冗長になる危険性が高いです。
+
+ というのも、いわゆる積の微分の公式
+
+ \[
+ (fg)' = f'g + fg'
+
+ \]
+
+ からわかるように、積が含まれる式を微分すると、微分した結果の式は元の式よりも長くなります。
+
+ これが入れ子になれば項の数は指数的に増えていき、文字通り爆発する場合があります。
+
+
+
+ 多変数への拡張
+ \(f: \mathbb{R}^n \to \mathbb{R}\) の勾配 \(\nabla f(\boldsymbol{x})\) を数式微分で求めます。
+
+ 実装は先ほどとほぼ同様で、
+
+
+ Variable
は x
だけでなく、変数名を持てるようにする
+ __call__
の引数を値ではなく、 {'x': 1, 'y': 2}
のような辞書に変更
+ diff
の引数に偏微分する変数を追加
+ Expression
に 全ての変数を列挙する all_variables
メソッドを追加
+ - 全ての変数をループして
diff
を計算する grad
を追加
+
+ という変更を加えました。
+
+ import pydot
import numpy as np
@@ -1809,8 +1840,8 @@ 多変数への拡張
def simplify(self):
return Cos(self.children[0].simplify())
-
-f = Expression(
+
+ f = Expression(
Mul(
Add(
Variable('x'),
@@ -1828,22 +1859,22 @@ 多変数への拡張
print('∇f :', f.grad())
print('∇f (simple) :', f.grad(symplify=True))
-
-実行すると、
-
-f : ((x + y) * sin(x))
+
+ 実行すると、
+
+ f : ((x + y) * sin(x))
f(x=1, y=2) : 2.5244129544236893
∇f : {'y': (((0 + 1) * sin(x)) + ((x + y) * (cos(x) * 0))), 'x': (((1 + 0) * sin(x)) + ((x + y) * (cos(x) * 1)))}
∇f (simple) : {'y': sin(x), 'x': (sin(x) + ((x + y) * cos(x)))}
-
-となり、正しく計算できました。
-
-実行できるものはこちらです。
-
-
-
-
-
-
-
-
-
-
-「ソースコード」からの微分
-
-今回の実装例では直接木を書くことでこのパートを回避しましたが実際はプログラムで普通に(?)書かれた式を変換できると嬉しいです、
-
-例えば
-
-def f(x):
+
+
+
+
+
+
+ 「ソースコード」からの微分
+
+ 今回の実装例では直接木を書くことでこのパートを回避しましたが実際はプログラムで普通に(?)書かれた式を変換できると嬉しいです、
+
+ 例えば
+
+ def f(x):
return 2 * x + 1
-
-という関数を受け取り、
-
-def f′(x):
+
+ という関数を受け取り、
+
+ def f′(x):
return 2
-
-に相当するようなオブジェクトを手に入れたいです。
-
-このような変換を実装するのは容易ではないです、
-
-例えば
-
-def f(x):
+
+ に相当するようなオブジェクトを手に入れたいです。
+
+ このような変換を実装するのは容易ではないです、
+
+ 例えば
+
+ def f(x):
if x < 0:
raise ValueError('x must be positive')
else:
for i in range(x):
print(i)
-
-
-
-
-自動微分
-
-最後に紹介するのが、自動微分(automatic differentiation)です。
-
-自動微分は精度、計算量ともに非常に優れており、最適化などの文脈では非常によく使われています。
-
-
-
-付録
-
-浮動小数点数
-前提として、ふつうのコンピュータは有限桁の二進数しか扱えません。
-
-
-そこで、コンピュータ上で実数(のような見た目をしているもの)は、有限桁の二進数で表現されます。
-
-
-しかし、すべての実数が有限桁の二進数で表現できるわけではありません。
-例えば、\(0.1\) は有限桁の二進数で表現することはできません。
-(\(0.1 = 0.00011001100110011\cdots\))
-
-
-したがって、コンピュータ上で \(0.1\) という実数を直接扱うのはむずかしく、近い有限桁の二進数で表現された数値を実際には使うことになります。
-
-この桁数はいくつかのよく使われる規格によって決まりますが、最近の言語ではデフォルトでは倍精度浮動小数点数が使われることが多いです。(Python, Juliaなど)
-
-ふつうの\(^{2}\)倍精度浮動小数点数は、IEEE 754 という規格によって定められていて、符号部1ビット、指数部11ビット、仮数部52ビットという構成になっており、
-符号のビット列が表す整数を \(sign\)、指数部のビット列が表す整数を \(exp\) 、仮数部のビット列が表す整数を \(frac\) とすると、
-
-
-\[
-v = (-1)^{sign} \times 2^{exp - 1023} \times (1 + frac)
-
- \]
-
-という形式で \(v\) を表します。
-実数から \(v\) への変換(丸め)の方法はいくつかあり、IEEE 754では5つの丸めモードが定義されています。\(^3\)
-
-デフォルトでは、roundTiesToEvenという丸めモードを使うように求められており、実際広く使われているようです。
-
-
-roundTiesToEvenの定義
-倍精度浮動小数点数で表される数全体の集合を \(F\)、 \(F\) の最大値と最小値をそれぞれ \(F_{max}\) と \(F_{min}\) とすると、
-
-\(x \in [F_{min}, F_{max}]\) となるような \(x \in \mathbb{R}\) について、
-\(v_1 \leq x \leq v_2\) となるような \(v_1, v_2 \in F\) が存在します。
-
-ここで、
-\(|x - v_1| < |x - v_2|\) であれば \(x\) は \(v_1\) に丸められ、
-\(|x - v_1| > |x - v_2|\) であれば \(x\) は \(v_2\) に丸められます。
-
-そして、\(|x - v_1| = |x - v_2|\) であれば、\(v_1\) と \(v_2\) のうち、最下位ビットが \(0\) である方に丸められます。
-
-
-\(x > F_{max}\) であれば、\(x\) は \(\infty\) に丸められ、
-\(x < F_{min}\) であれば、\(x\) は \(-\infty\) に丸められます。\(^4\)
-
-
-
-
-
-
-
-
-
+
+
+
+
+ 自動微分
+
+ 最後に紹介するのが、自動微分(automatic differentiation)です。
+
+ 自動微分は精度、計算量ともに非常に優れており、最適化などの文脈では非常によく使われています。
+
+
+
+ 付録
+
+ 浮動小数点数
+ 前提として、ふつうのコンピュータは有限桁の二進数しか扱えません。
+
+
+ そこで、コンピュータ上で実数(のような見た目をしているもの)は、有限桁の二進数で表現されます。
+
+
+ しかし、すべての実数が有限桁の二進数で表現できるわけではありません。
+ 例えば、\(0.1\) は有限桁の二進数で表現することはできません。
+ (\(0.1 = 0.00011001100110011\cdots\))
+
+
+ したがって、コンピュータ上で \(0.1\) という実数を直接扱うのはむずかしく、近い有限桁の二進数で表現された数値を実際には使うことになります。
+
+ この桁数はいくつかのよく使われる規格によって決まりますが、最近の言語ではデフォルトでは倍精度浮動小数点数が使われることが多いです。(Python, Juliaなど)
+
+ ふつうの\(^{2}\)倍精度浮動小数点数は、IEEE 754 という規格によって定められていて、符号部1ビット、指数部11ビット、仮数部52ビットという構成になっており、
+ 符号のビット列が表す整数を \(sign\)、指数部のビット列が表す整数を \(exp\) 、仮数部のビット列が表す整数を \(frac\) とすると、
+
+
+ \[
+ v = (-1)^{sign} \times 2^{exp - 1023} \times (1 + frac)
+
+ \]
+
+ という形式で \(v\) を表します。
+ 実数から \(v\) への変換(丸め)の方法はいくつかあり、IEEE 754では5つの丸めモードが定義されています。\(^3\)
+
+ デフォルトでは、roundTiesToEvenという丸めモードを使うように求められており、実際広く使われているようです。
+
+
+ roundTiesToEvenの定義
+ 倍精度浮動小数点数で表される数全体の集合を \(F\)、 \(F\) の最大値と最小値をそれぞれ \(F_{max}\) と \(F_{min}\) とすると、
+
+ \(x \in [F_{min}, F_{max}]\) となるような \(x \in \mathbb{R}\) について、
+ \(v_1 \leq x \leq v_2\) となるような \(v_1, v_2 \in F\) が存在します。
+
+ ここで、
+ \(|x - v_1| < |x - v_2|\) であれば \(x\) は \(v_1\) に丸められ、 \(|x - v_1|> |x - v_2|\) であれば \(x\) は \(v_2\) に丸められます。
+
+ そして、\(|x - v_1| = |x - v_2|\) であれば、\(v_1\) と \(v_2\) のうち、最下位ビットが \(0\) である方に丸められます。
+
+
+ \(x > F_{max}\) であれば、\(x\) は \(\infty\) に丸められ、
+ \(x < F_{min}\) であれば、\(x\) は \(-\infty\) に丸められます。\(^4\)
+
+
+
+
+
+
+
+
-