白猫のメモ帳

JavaとかC#とかJavaScriptとかHTMLとか機械学習とか。

機械学習で使う微分って結局どこまでわかってれば良いのさ

こんにちは。

年度が変わりましたね。
そして、春ですね。

久しぶりに機械学習関連の記事を書こうかなと思ったのですが、
ある解説の途中でその前提知識の解説とかが入って、すぐ脇道に逸れてしまうんですよね。

とはいえ、詳しくは他の人が書いてるからググってね!ってすると、
戻ってきた頃には「で、なんだっけ?」ってなっちゃうし、難しいものです。

特に微分積分線形代数は最低限必要みたい言われるのですが、最初から全部が必要ってわけではないと思うんです。
というわけで、今回はとりあえずこれ覚えとけばなんとかなるんじゃない?
っていう微分テクニックについて書いてみようかと思います。

なんで微分するのって話はこちらをどうぞ。
shironeko.hateblo.jp

初等関数の微分

これは別にテクニックとかじゃなくてこうなるよってだけです。
何なら覚えなくても、毎回調べればいいと思います。
「なんか式の形がめっちゃ変わったんだけど!!」みたいなときに、「あ、なるほど微分するとこうなるのか」くらいに思えれば。

初等関数っていうのはWikipediaには、

実数または複素数の1変数関数で、代数関数、指数関数、対数関数、三角関数、逆三角関数および、それらの合成関数を作ることを有限回繰り返して得られる関数のことである

なんて書いてありますが、基本的な関数くらいでいいと思いますよ。

種別 関数 微分 備考
べき乗 x^{a} ax^{a-1}
指数 a^{x} a^{x} \log a  \log a \log_{e} aと書いたり \ln aと書いたりもします
自然指数 e^{x} e^{x} e^{x} \exp xと書いたりもします
対数  \log_{a} x  \displaystyle \frac{1}{x \log a}
自然対数  \log x  \displaystyle \frac{1}{x}
正弦  \sin(x)  \cos(x)
余弦  \cos(x)  -\sin(x)
正接  \tan(x)  \displaystyle \frac{1}{\cos^{2}(x)}

指数と対数はアンダーフロー問題とかあるのでちょいちょい出てきたりしますが、三角関数はあんまり?
他にもいろいろある気はしますがとりあえずこんなもんでよいかと。

偏微分

高校数学の微分を忘れ去られた記憶から頑張って思い出してみると、
2次関数だったり3次関数だったり、1つの変数を指定することで値が定まる1変数関数だけを対象にしていました。
(というかちゃんと覚えてないんですけど、偏微分って高校数学の範囲じゃないですよね?)

たとえば、1変数 xの関数

 f(x) = x^{3} + 5x^{2} + 2x - 4 ・・・①

微分すると、

 \displaystyle \frac{df(x)}{dx} = 3x^{2} + 10x + 2 ・・・②

になります。

微分の表現方法には f'(x)みたいなのと、
 \displaystyle \frac{df(x)}{dx}もしくは \displaystyle \frac{dy}{dx}みたいなのがありますが、意味は同じです。
偏微分の話をするときには \displaystyle \frac{df(x)}{dx}が良さそうなので、今回はこっちを使います。

でも、世の中の関数って1変数で表せることってあんまりないですよね。
例えば、距離は速さ×時間だし、BMIは体重÷身長の2乗なわけです。
機械学習で扱うデータだって1次元のデータなんてものは基本的にはないでしょう。

とすると、多変数の関数

 f(x,y,z) = 2x^{2} + xy^{2} - 3z ・・・③

とかだって微分できないと困るわけですよね。

微分っていうのは変化量を求めることです。
3xを微分すると3になるっていうのは、いつもいつでもxの変化量は3ということです。
axを微分するとaになるっていうのは、aの値によってxの変化量は変わるよってことです。
 x^{2}微分すると2xになるっていうのは、xの値によってxの変化量は変わるよってことです。

ということは、他の変数の値によって変化量が変わるという表現を使ってよいのであれば、
上の例はxで微分することも、yで微分することも、zで微分することもできるはずです。

この、「多変数関数をある一つの変数で微分する(他の変数を定数とみなす)」ことを偏微分といいます。

そして、関数 f(x,y,z)をxで微分することを、 \displaystyle \frac{∂f(x,y,z)}{∂x}と表現します。

大切なことは、なにで(「なんで」ではなくて「なにで」)微分するかというのが必ず決まっていることです。
単純に「距離を微分すると何になる?」っていうのは答えがありませんが、
距離を時間で偏微分すると速さになるし、速さで偏微分すると時間になるわけです。

というわけで、③の式をxで偏微分すると、

 \displaystyle \frac{∂f(x,y,z)}{∂x} = 4x + y^{2} ・・・④

yで偏微分すると、

 \displaystyle \frac{∂f(x,y,z)}{∂y} = 2xy ・・・⑤

になるということです。

合成関数の微分

もうひとつ、よく使うテクニックに合成関数の微分があります。
合成関数とはその名の通り複数の関数を合成したものです。

例えば2つのxの関数

 f(x) = ax^{2} + b ・・・⑥
 g(x) = x^{3} ・・・⑦

があるとき、

 g(f(x)) = (ax^{2} + b)^{3} ・・・⑧

のように関数を組み合わせることができます。

じゃあこれを微分したらどうなるのっていうと、

 g(f(x)) = (ax^{2} + b)^{3}
        = (a^{2}x^{4} + 2abx^{2} + b^{2}) (ax^{2} + b)
        = a^{3}x^{6} + 2a^{2}bx^{4} + ab^{2}x^{2} + a^{2}bx^{4} + 2ab^{2}x^{2} + b^{3}
        = a^{3}x^{6} + 3a^{2}bx^{4} + 3ab^{2}x^{2} + b^{3} ・・・⑨

なので(あってる?)

 \displaystyle \frac{dg(f(x))}{dx} = 6a^{3}x^{5} + 12a^{2}bx^{3} + 6ab^{2}x
          = 6ax(a^{2}x^{4} + 2abx^{2} + b^{2})
         = 6ax(ax^{2} + b)^{2}・・・⑩

となるわけです。
とてもつらい。

ここでわかりやすくするために⑧の式を

 u = f(x) ・・・⑪
 y = g(u) ・・・⑫

とおいてみた場合、

 \displaystyle \frac{dy}{dx} = \frac{dy}{du} \frac{du}{dx}  ・・・⑬

という変形が成り立ちます。これを合成関数の微分といいます。

 u = ax^{2} + b ・・・⑭
 y = u^{3} ・・・⑮

ですから、

 \displaystyle \frac{dy}{dx} = \frac{dy}{du} \frac{du}{dx} 
     = 3u^{2} ・2ax 
     = 3(ax^{2} + b)^{2} ・2ax 
    = 6ax(ax^{2} + b)^{2}・・・⑯

⑩の式とちゃんと一致しましたね。

これがプログラム的に考えると何が便利かというと、
複数の関数を通してその最終結果を微分する場合、
それぞれの途中の関数の微分ができていれば簡単に計算できてしまうことなんですよね。
C#っぽい構文ですが、C#では累乗はMath.Powを使います。擬似コードということで。

const int A = 5;
const int B = 3;
int F(int x) {
    return A * x ^ 3 + B;
}
int DF(int x) {
    return 2 * A * x;
}
int G(int x) {
    return x ^ 3;
}
int DG(int x) {
    return 3 * x ^ 2;
}
// これはなくて良い
int DFG(int x) {
    return 6 * A * x * (A * x ^ 2 + B) ^ 2 ; 
}

があるときに、

int x = 2;
int y = G(F(x));
int y_dash = DG(F(x)) * DF(x);

みたいな感じですね。

合成関数の微分を使わない場合、先にDFGを計算しておかなくてはいけないし、
例えば関数Hが現れたら {}_3C_2で3種類の微分の結果を先に計算しておかないといけなくなってしまいます。
一方で合成関数の微分を使う場合、関数ごとにその微分の関数も用意しておけば全体の微分ができます。超便利!

で、いつ使うの?

っていわれると難しい。
ニューラルネットワーク作るとめっちゃ使うよってくらい。
でも、他の説明の途中でこういう説明挟み込むのはやっぱり大変なので1つ記事にしました。

いつかそういう場面に出会ったときに、「あぁなるほど!これは便利だ!」ってなるんですよ。
えぇ、なるんですとも。

C#のジェネリックでワイルドカードが使えないのはどうすればいいの?

こんにちは。

今年度も残すところ一ヶ月になりましたね。
いやはやあっという間です。

今回はC#ジェネリックについて。
最初に書いておきますが、解決には至りません。単純な疑問です。

便利なはてな

まずはJavaでこんなクラスを作って、

abstract class HogeBase {
}
class Hoge1 extends HogeBase {
}
class Hoge2 extends HogeBase {
}

このHogeBaseのインスタンスを型を意識する形でFugaクラスで内包しようとすると、

class Fuga<T extends HogeBase> {
    T hoge;
}

まぁこうなりますよね。
で、更にこのFugaクラスをもうT型を意識しなくていい形でPiyoクラスで内包しようとすると、

class Piyo {
    Fuga<? extends HogeBase> fuga;
}

こんな感じでワイルドカードが使えます。

はてなはどこへいった

同じことをC#でやりたいので、

abstract class HogeBase
{
}
class Hoge1 : HogeBase
{
}
class Hoge2 : HogeBase
{
}

こうなって、

class Fuga<T> where T : HogeBase
{
    T hoge;
}

ワイルドカードがないので親の型をそのまま入れる感じになるのでしょうか?

class Piyo
{
    //Fuga<?> fuga;                     // ワイルドカードかけない
    //Fuga<T> fuga where T : HogeBase;  // でもこんな文法もない
    Fuga<HogeBase> fuga;                // しょうがないので親の型にしておく?
}

再帰ジェネリックを使うと

HogeBaseを再帰ジェネリッククラスにしてみます。
こうするとHogeBase型から派生クラスのインスタンスとかが作れて便利なことがあります。

abstract class HogeBase<T> where T : HogeBase<T>
{
}
class Hoge1 : HogeBase<Hoge1>
{
}
class Hoge2 : HogeBase<Hoge2>
{
}

で、こうなって、

class Fuga<T> where T : HogeBase<T>
{
    T hoge;
}

あれ、どうやっても無理なのでは…。
どこまでいってもクラスジェネリックをつけなくてはならないとか辛すぎです。

class Piyo
{
    //Fuga<?> fuga;                     // ワイルドカードかけない
    //Fuga<T> fuga where T : HogeBase;  // でもこんな文法もない
    //Fuga<HogeBase<?>> fuga;           // 親の型にすらワイルドカードが必要になってしまった
}

こうやって、特に意味のないインタフェース作らないとだめですか?

interface IFuga { }

class Fuga<T> : IFuga where T : HogeBase<T>
{
    T hoge;
}

class Piyo
{
    IFuga fuga;
}

正解がわからないー!!

リモートデスクトップはローカルのドライブをマウントできる

こんにちは。

気がつけば2月ももう半ば、今年1個目の記事です…ははは。
わたしはお仕事が忙しかったり、風邪を引いたり、インフルエンザに罹ったりしました。
体調管理大事!

リモートデスクトップは賢い

さて、Windowsを使っているとリモートデスクトップは割と日常的に使う機能です。
CUIの良さもわかるけど、やっぱりGUIいいよね。

で、RDPはとても賢いので、接続元(ローカルマシン)から接続先(リモートマシン)に対して、
ファイルのコピーとかもできちゃいます。(逆向きもできます)
なのですが、RDPのウィンドウの内外を往復しているとだんだんどっちの世界にいるかわからなくなってきます。

こんなときに便利なのがマウント機能です。

ドライブをマウントしよう

リモートデスクトップ接続のウィンドウで「ローカルリソース」というタブを選択すると、
「ローカルデバイスとリソース」という項目があり、その中に「詳細」設定ボタンがあります。

f:id:Shiro-Neko:20190216102041p:plain

f:id:Shiro-Neko:20190216102059p:plain

ドライブのところから「ローカルディスク」とかにチェックを入れて接続をすると…

f:id:Shiro-Neko:20190216102249p:plain

リモートマシンで「リダイレクトされたドライブとフォルダー」という項目があり、
ローカルマシンのドライブが参照できるようになりました。

f:id:Shiro-Neko:20190216102852p:plain

パスは「\\tsclient\C」みたいな感じになるみたいですね。

f:id:Shiro-Neko:20190216103058p:plain

ちなみに

ローカルディスクだけではなく、光学ドライブとかリムーバブルメディアとかもマウントすることができます。
光学ドライブがないマシンにソフトのインストールとかするときに便利かもしれませんね。
まぁ、最近はソフトのインストールとかはオンラインが主流な感じですが。