いろいろあって長く間が空いてしましましたが、Pythonやります。
微分
そもそも微分ってなんだっけ?
微分とは滑らかなグラフの一瞬の変化の割合を示すもの。
数学的には導関数を求めることとも言われている。
中学数学などでも変化の割合は傾きとも言われていたと思います。
微分を扱うときは数式的に入り込みすぎず、元の式の傾きを求めているということは意識しておいた方がよいでしょう。
具体的にはこれからやっていきましょう。
微分公式と表現
・公式
本来の極限値を使ってしっかり導出する方法が正式ですが、一般的な公式のみ紹介します。
・表現
以下の最も基本的な二次関数を微分を行います。
微分表現は一般的には以下の表現で表されます。
より一般的には③がよく見られたでしょうか。
①や②については高校や大学で多少数学はちゃんとやった人には一般的な表現でしょう。
これだけでは味気ないので復習がてら、Pythonで2次関数グラフを表現しておきましょう。
# ライブラリのインポート import numpy as npy import matplotlib.pyplot as plt # Jupiter Notebookで結果を表示するためのおまじない %matplotlib inline def func1(x): return x*x xline = npy.linspace(-5,5,50) yline = func1(xline) # pltに要素を設定する plt.plot(xline, yline) # pltに要素を設定する plt.plot(xline, yline, color ='black', label='$f(x)=x^2$') # 凡例を表示する plt.legend(loc="upper left") # y軸の表示範囲を設定する plt. ylim(- 1, 20) # タイトルを表示する plt.title('$SampleGraph$') # X軸に名前を付ける plt.xlabel('$x$') # Y軸に名前を付ける plt.ylabel('$y$') # グリッドを表示する plt.grid(True) # グラフを描写する plt.show()
関数の微分
・多項式の微分
公式では単項式で表現を行いましたが、多項式でも同様の公式が適用できます。
・入れ子関数の微分
機械学習における微分では、入れ子になった関数の微分を行うことが多々あるそうです。
入れ子のイメージは以下のような関数です。
入れ子関数の微分はそのままに展開してやる方法もありますが、連鎖律(チェーンルール)で解くのが一般的なようです。
連鎖律という言葉はあまり聞いたことがありませんでしたが、以下のようなものです。
入れ子関数を実際に解こうとすると以下のような流れになります。
①まずは式[1]の部分をそれぞれ微分します。
②導出できた式[2]と式[3]を式[1]に代入します。
③最終的にg(x)を代入し直すと普通に展開して微分を行った場合と同様の結果が得られていることがわかります。
偏微分
そもそも偏微分って?
偏微分ってきいたことがあるでしょうか?
私は大学数学で少し聞いた覚えがある気がするということは、理系出身でないと全く触れたことがないものなのかもしれません。
機械学習に実際に登場する微分はこの偏微分らしいので数学的な理解をしようと思った場合、この偏微分の理解は必須ということになります。
しかし、基本的なものであれば通常の微分自体が理解できていればそこまでは難しくはないと思っています。
・偏微分とは
導入が長くなりましたが、偏微分とは複数の変数をもつ関数に対して、一つの変数に対して微分を行うことです。
複数の変数をもつ関数として以下を考えます。
偏微分では∂(ディー)という記号を用います。
通常の微分で扱っていたdと扱いは同じで問題ないです。
偏微分を行う場合は、一つの変数に対して微分を行うので、その際に他の変数は定数として扱います。
まぁ簡単に言えば、無視して構わないということになります。
一点注意があるとすれば、偏微分を行った変数が含まれていない項は定数扱いなので、消える点でしょうか。
偏微分のグラフ上の表現
Pythonが全然使えていないので、少し使っていきましょう。
通常の微分のグラフ上のイメージは高校数学などでも多少は見ることがありましたが、偏微分はいまいち扱ったことがないので、グラフ上でどのようになるのかをPythonで確認していきます。
題材は以下の関数です。
・xで偏微分
y=-4のとき
・yで偏微分
x=4のとき
# ライブラリのインポート import numpy as npy import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # Jupiter Notebookで結果を表示するためのおまじない %matplotlib inline # 関数の定義 def f(x,y): return 3*x*x + 2*x*y - 2*y*y def f1(x,y): return 2*x - 4*y def f2(x,y): return 6*x + 2*y linelen = 10 # X軸とY軸に0から5までの配列 xline = npy.linspace(-5,5,linelen) yline = npy.linspace(-5,5,linelen) # Z軸に0、0の要素を必要な数だけ用意する zline = npy.zeros((len(xline),len(yline))) # X軸のループ for i0 in range(linelen): # Y軸のループ for i1 in range(linelen): zline[i1,i0] = f( xline[i0], yline[i1]) # グラフのサイズを指定する plt.figure(figsize=(10,5)) # グラフ間の間隔を設定する plt.subplots_adjust(wspace=0.2,hspace=0.5) # x軸とy軸を行列表現にする xline, yline = npy.meshgrid(xline, yline) # 座標系の指定 ax = plt.subplot( 2, 2, 1, projection ='3d') plt.title(1) # 表面の表示 ax.plot_surface( xline, yline, zline, rstride = 1, cstride = 1, alpha = 0.3, color ='blue', edgecolor ='black') # Z軸の表示をする ax.set_zticks((-10, 0, 40)) # グラフの向きを調整する ax.view_init( 20, -100) # X軸に名前を付ける plt.xlabel('$x$') # Y軸に名前を付ける plt.ylabel('$y$') # グラフのサイズを指定する plt.subplot(2,2,2) plt.title("1.:2*x - 4*y") yline1 = npy.linspace(-5,5,20) zline1 = f(4,yline1) plt.plot(yline1, zline1) zline1 = f1(4,yline1) plt.plot(yline1, zline1) # グラフのサイズを指定する plt.subplot(2,2,3) plt.title("2.:6*x + 2*y") xline2 = npy.linspace(-5,5,20) zline2 = f(xline2,-4) plt.plot(xline2, zline2) zline2 = f2(xline2,-4) plt.plot(xline2, zline2) plt.show()
・グラフイメージ
「1.」がx=4の断面をyで偏微分したときのグラフ
「2.」がy=-4の断面をxで偏微分したときのグラフ
やってみたもののいまいちですかね・・・
【勾配ベクトル】
三次元グラフの場合、それぞれ偏微分をとってベクトルとして表現することが可能となります。
これを勾配ベクトルといいます。
勾配ベクトルは初めて聞いた概念ですが、傾きの最も大きい方向と、その大きさを表します。
勾配のグラフ化
かなりこんがらがってきました。
先ほど紹介した勾配ベクトルをグラフにより図示します。
関数は変わらず同じ以下を流用します。
・例
import numpy as np import matplotlib. pyplot as plt %matplotlib inline # 関数の定義 def f(x,y): return 3*x*x + 2*x*y - 2*y*y def f1(x,y): return 2*x - 4*y def f2(x,y): return 6*x + 2*y range_ = 5 dw = 0.5 x = np.arange(- range_, range_ + dw, dw) y = np.arange(- range_, range_ + dw, dw) xrange, yrange = np.meshgrid( x, y) ff = np.zeros(( len(x), len(y))) dff_x = np.zeros(( len(x), len(y))) dff_y = np.zeros(( len(x), len(y))) for i0 in range( len( x)): for i1 in range( len( y)): ff[i1,i0] = f( x[ i0], y[ i1]) dff_x[ i1, i0] = f1( x[i0], y[i1]) dff_y[ i1, i0] = f2( x[i0], y[i1]) plt.figure(figsize =( 15, 7)) plt.subplots_adjust( wspace = 0.3) plt.subplot( 1, 2, 1) plt.title("[1]") cont = plt.contour( xrange, yrange, ff, 10, colors='k') # fの高等線の表示 cont.clabel( fmt ='%d', fontsize = 8) plt.xticks( range(-range_, range_ + 1, 1)) plt.yticks( range(-range_, range_ + 1, 1)) plt.xlim(-range_ - 0.5, range_ + 0.5) plt.ylim(-range_ - 0.5, range_ + 0.5) plt.xlabel('$x$', fontsize = 14) plt.ylabel('$y$', fontsize = 14) # 勾配ベクトルの表示 plt.subplot( 1, 2, 2) plt.title("[2]") plt.quiver( xrange, yrange, dff_x, dff_y) plt.xlabel('$x$', fontsize = 14) plt.ylabel('$y$', fontsize = 14) plt.xticks( range(-range_, range_ + 1, 1)) plt.yticks( range(-range_, range_ + 1, 1)) plt.xlim(-range_ - 0.5, range_ + 0.5) plt.ylim(-range_ - 0.5, range_ + 0.5) plt.show()
・グラフ化
[1]は関数fをz軸に対する高等線を表現しています。
[2]は勾配を表現しています。
何がわかるのか・・・
[2]ではその地点のz軸に対する値の変化を矢印の大きさと方向で示しています。
矢印で表現しているため、少しはイメージが湧きやすいでしょうか。つまり、勾配をベクトルで表現している勾配ベクトルとなります。
矢印をたどっていくと高い地点へ向かいます。逆方向をたどるとより低い方向に向かうことになります。
多変数の入れ子関数
多変数の入れ子関数もありますが、ちょっとしんどいので軽くだけ・・・
多変数なので微分を行う際は偏微分となりますが、入れ子関数であれば連鎖律が適用できます。
実際にニュートラルネットワークで使用するらしい形式のみご紹介。
実際には入れ子も複雑で複数の入れ子が連なる以下のような形になるようです。
これを偏微分(微分)する際には以下のような連鎖律で微分を行うことになります。
Σ(シグマ)の微分
連鎖律を適用した場合、Σの微分を行うことになります。
Σの微分では、Σの計算(総和を求める)をしてから微分を行うという方法が考えられますが、逆に微分を先に行ってからΣの計算を行うという順序で算出することができ、これが公式となっています。
・Σの微分公式
一つ例示して試してみます。
【総和を求めてから微分する】
【微分してから総和を求める】
どちらでも同じ結果が算出できることが確認できます。
実際の機械学習分野では、微分を先に計算してしまった方が楽であったり、微分だけしか計算できない場合もでてくるそうなので、上記の公式は必須となるようです。
ひゃー疲れた。普通にだいたい数学になってしまった。
Pythonを挟むのが難しくてややタイトルに偽りあり。
・今回のソース
python_dev/Python_math3.ipynb at master · wantanblog/python_dev · GitHub