いい加減時代の潮流に乗ろうということで機械学習を学びたいと思う。またRはともかくとしてPythonは未経験であるため、丁度良い書籍として
を用いることにする。
2. 分類
有限個の数値を目的変数とするような分類の問題を取り扱う。
2.1. ロジスティック回帰
目的変数が2個のいずれかの値を取り、個の説明変数からそのいずれであるのかを対応付ける問題を考える。すなわちデータ
から決定則
を導く。
ここでは
となるようなが存在すると仮定する。このような形態の回帰モデルをロジスティック回帰モデルという。
2.1.1 ロジスティック曲線のシミュレーション(R)
# ロジスティック関数 func_logis <- function(x)exp(num_beta_0 + num_beta * x)/(1 + exp(num_beta_0 + num_beta * x)) num_beta_0 <- 0 vc_beta <- c(0, 0.2, 0.5, 0.5, 1, 2, 10) num_m <- length(vc_beta) num_beta <- vc_beta[1] plot(func_logis, xlim = c(-10,10), ylim = c(0,1), xlab = "x", ylab = "P(Y=1|x)", col = 1, main = "ロジスティック曲線") for(i in 2:num_m){ num_beta <- vc_beta[i] par(new = T) plot(func_logis, xlim = c(-10,10), ylim = c(0,1), xlab = "", ylab = "", axes = F, col = i) } legend("topleft", legend = vc_beta, col = 1:length(vc_beta), lwd = 2, cex = 0.8)
2.1.2 ロジスティック曲線のシミュレーション(Python)
import numpy as np import matplotlib.pyplot as plt %matplotlib inline import japanize_matplotlib def logis(x): return np.exp(beta_0 + beta * x) / (1+np.exp(beta_0 + beta * x)) beta_0 = 0 beta_seq = np.array([0, 0.2, 0.5, 1, 2, 10]) x_seq = np.arange(-10,10,0.1) # 図示 plt.xlabel("x") plt.ylabel("P(Y=1|x)") plt.title("ロジスティック曲線") for i in range(beta_seq.shape[0]): beta = beta_seq[i] p = logis(x_seq) plt.plot(x_seq, p, label = '{}'.format(beta)) plt.legend(loc = 'upper left')
2.1.3 パラメータの推定
パラメータの推定は最尤法で行うこととする。すなわち各が独立かつ同一分布に従うとし、尤度
を最大にするような、さらにはそれと同値だが対数関数が単調増加であることを活用し対数尤度
を最大にするようなを求める。ただしロジスティック回帰の最尤法は代数的に解けるわけではないため、数値的に解くことを考える。
2.2 Newton-Raphson法の適用
級の関数に対してを解くことを考える。適当な初期値が与えられているものとして、点からの接線を引いて軸との交点をとし、次に点において同様のことを行うという手順を繰り返す。こうして得られた数列はの解に近づいていく。
点におけるの接線の方程式は一般に
で与えられる。したがってとの交点は
で得られる。試行回数をあらかじめ決めておく、もしくはの上限値を決めてそれよりも絶対誤差が小さくなったときに終了させるなど、予め終了条件を決めておくことで数値解を得ることができる。
一般の次元に関してを満たすようなを求める問題に-法を適用する。すなわち
により逐次的にを更新していく。ただし
とする。
ロジスティック回帰にこれを適用する。負の対数尤度を微分するととして
と書ける。さらにであることに注意すれば、
である。そのようなを用いると、
と書ける。またとおけば
とも書き表すことができる。
2.2.1 2変数関数での適用例(R)
関数
を満たす解を初期値としてを与えて-法で解く。
############################## ### Newton-Raphson法の検討 ### ############################## # f(x,y) = x^2 + y^2 -1, g(x,y) = x+yについて初期値(x,y) = (3,4)から # f(x,y)=0かつg(x,y)=0を満たす(x,y)を数値的に解く # 解く対象となる関数およびその1次偏微分をそれぞれ定義する func_f <- function(vc) vc[1]^2 + vc[2]^2 - 1 func_f_pd_x <- function(vc) 2 * vc[1] func_f_pd_y <- function(vc) 2 * vc[2] func_g <- function(vc) vc[1] + vc[2] func_g_pd_x <- function(vc) 1 func_g_pd_y <- function(vc) 1 # 初期値の設定 vc <- c(3,4) eps <- 10e-14 # 絶対誤差がeps以下になったら終了 err <- Inf # 絶対誤差 num_loop <- 0 # ループ回数 while(err>eps){ vc_bf <- vc vc <- vc_bf - solve(matrix(c(func_f_pd_x(vc_bf),func_f_pd_y(vc_bf), func_g_pd_x(vc_bf),func_g_pd_y(vc_bf)), ncol = 2, byrow = T)) %*% c(func_f(vc_bf),func_g(vc_bf)) num_loop <- num_loop + 1 err <- sqrt(t(vc_bf - vc) %*% (vc_bf - vc)) } # 出力 vc num_loop
2.2.2 2変数関数での適用例(Python)
import numpy as np # 関数f(x,y),g(x,y)およびその1次偏微分を定義 def f(z): return z[0]**2 + z[1]**2-1 def f_pd_x(z): return 2*z[0] def f_pd_y(z): return 2*z[1] def g(z): return z[0] + z[1] def g_pd_x(z): return 1 def g_pd_y(z): return 1 # Newton-Raphson法を適用する z = np.array([3,4]) eps = 10**(-14) err = float('inf') num_loop = 0 while(err>eps): z_bf = z J = np.array([[f_pd_x(z),f_pd_y(z)],[g_pd_x(z),g_pd_y(z)]]) # Jacobian z = z_bf - np.linalg.inv(J)@np.array([f(z),g(z)]) err = np.sqrt((z_bf - z).T@(z_bf - z)) num_loop += 1 # 出力 print(z) print(num_loop)