4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ニューラルネットワークで決定木のようなモデルを作成する

Posted at

IF文を微分可能にする

ニューラルネットワークで決定木のようなモデルを作成したい。そのためには微分可能で学習可能なパラメータを持つIF文が必要。

IF文の構造

以下のような記述でstatementTrueの場合にはtask_for_trueを実行し、Falseの場合にはtask_for_falseを実行する。

if statement:
    task_for_true
else:
    task_for_false

今回はニューラルネットワークに組み込むのでtaskの部分は$ax+b$のような形になる。
問題はstatementの部分をどうやってニューラルネットワークで表現するかである。

a > bのニューラルネットワーク的表現

x = [0, 1, 2]のようなNumPy配列を考える。x[x > 0]とすると、[1, 2]という結果が返ってくる。
このときx > 0[False, True, True]というbool型の配列として評価され、それを元にTrueに該当する位置の値が返ってきている。また、NumPyのbool型は内部的にはTrue = 1, False = 0となっているので、x.sum() = 3のような演算が可能。
以上のことから、x[x > 0]xの各要素に

$
\displaystyle f( x) =\begin{cases}
1 & ( x >0)\
0 & ( x\leqq 0)
\end{cases}
$

という関数$f( x)$を適用していると見ることができる。

ステップ関数

このままではこの関数は微分できないが、LSTMのゲートなどを参考にするとこの関数を近似して微分可能にする方法がわかる。単純にシグモイド関数通すだけだけど。

$
\displaystyle f( x) =\frac
{1}
{1+e^{-x}}
$

シグモイド関数

もう少し元の関数に近づけるためには$x$に大きな値の係数$s$をかける。

$
\displaystyle f( x) =\frac
{1}
{1+e^{-sx}}
$

係数のあるシグモイド関数

さらに、閾値を$0$以外にするために$x$を$x-t$にする。

$
\displaystyle f( x) =\frac
{1}
{1+e^{-s( x-t)}}
$

平行移動したシグモイド関数

以上より、x > tという条件文は$
\displaystyle f( x) =\frac
{1}
{1+e^{-s( x-t)}}
$によって表現できることがわかる。$s$が正ならx > t、負ならx < tになる。

a == bのニューラルネットワーク的表現

これもa > bの場合のように各要素に同じ関数を適用していると考えていくが、元になる関数は

$
\displaystyle f( x) =\begin{cases}
1 & ( x=0)\
0 & ( x\neq 0)
\end{cases}
$

である。これを近似するには、画像のAttentionなどを参考に正規分布のような山型の関数を使うとよい。$0\sim 1$の値を返すこのような関数としてはtanhの微分でもいいのだが、数値計算的に安定するのでここではシグモイド関数の微分を使ってみる。

$\displaystyle f( x) =sigmoid( x)( 1-sigmoid( x))$

シグモイド関数の微分

a > bの場合と同様に変形すると、よりそれらしくなる。

$\displaystyle f( x) =sigmoid( s( x-t))( 1-sigmoid( s( x-t)))$

平行移動したシグモイド関数の微分

決定木をニューラルネットワークで表現する

処理の手順は以下のようになる。

  1. 条件文に適用する$x$を選ぶ
  2. 閾値より上の値と下の値に分割する
  3. それぞれの値に対して処理を適用する

変数を選択する

サンプル数$N$、特徴の数$K$のデータセット$X$から$k$列目の変数だけを選ぶ。

これは、a == bのところで扱ったように、シグモイド関数の微分に$0, 1, \dots , K-1$を代入したベクトルを$X$にかけて、目的とする変数以外を$0$にする。

N = 10
K = 5
X = np.random.normal(size=(N, K))
sig = lambda x: 1 / (1 + np.exp(-x))
sig_diff = lambda x: 4 * sig(x) * (1 - sig(x))
x_shift = lambda x, s, t: s * (x - t)

selected_col = X * sig_diff(x_shift(np.arange(K), 100, 1))

ニューラルネットワークで学習させることを考えると、最初から$s$に(絶対値が)大きな値を設定すると勾配が$0$になってしまって学習できないと思うので、初期値としては小さい値でよさそう。

条件によって分割する

これは、a > bのところで扱ったようにシグモイド関数を使用する。
データセット$X$で考えると面倒なので、抽出された列$k$を表すベクトル$x$で考える。Deep Learningのフレームワークで実装する場合にはサンプル数$N$のところは考えなくていいので、実際上の問題はない。

N = 10
x = np.random.normal(scale=10, size=N)
sig = lambda x: 1 / (1 + np.exp(-x))
x_shift = lambda x, s, t: s * (x - t)

condition = sig(x_shift(x, 10, 0.1))
xx = x.reshape((-1, 1)) * np.array([condition, 1 - condition]).T

これでxx[:, 0]には条件がTrueのもの、xx[:, 1]には条件がFalseものが取り出される。ここでは$x$が適切にスケーリングされているなら、$s$は学習可能なパラメータでなく大きな値の定数でもよさそう。

それぞれの値に対して処理を適用する

$N\times K$だった入力データ$X$のshapeは、この時点で$N\times K\times 2$になっている。
このうち、前段階で条件に合致した値を$x_{T}$、合致しなかった値を$x_{F}$とすると、

$
\displaystyle y=\begin{cases}
w_{T} x_{T} +b_{T} & \
w_{F} x_{F} +b_{F} &
\end{cases}
$

のような形にしたい。単純にそれぞれ$w$をかけて$b$を足して、Trueの場合とFalseの場合を合わせてしまうとどちらも同じ$b$を適用するのと変わらないので、$b$にも条件文を適用しておく。

N = 10
x = np.random.normal(scale=10, size=N)
sig = lambda x: 1 / (1 + np.exp(-x))
x_shift = lambda x, s, t: s * (x - t)

true = sig(x_shift(x, 10, 0.1))
condition = np.array([true, 1 - true]).T
xx = x.reshape((-1, 1)) * condition

w = np.random.normal(size=(2, 1))
b = np.random.normal(size=2)

bb = b * condition

y = xx.dot(w) + bb

実装

そのうちやる。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?