いい加減時代の潮流に乗ろうということで機械学習を学びたいと思う。またRはともかくとしてPythonは未経験であるため、丁度良い書籍として
を用いることにする。
1. 線形回帰
を目的変数、を説明変数として
という単回帰モデルを考えよう。(単)回帰モデルの場合、最小二乗法での推定が定番である。
1.1 最小二乗法によるパラメータ推定
問題設定をしておこう。観測値としてが得られているとする。このとき、残差を
で定義する。これは実際の観測値とモデルによる推計値との隔たりを表している。
それらの総和として残差平方和を
で与える。二乗しているのは、残差の単なる総和では常にになるからで、平方絶対和では数学的に取り扱いにくいからでもある。
さてこの残差平方和を最小化するようなを求めるのが最小二乗法である。一般に2次関数が下に凸であることを踏まえれば、はをおよびでそれぞれ偏微分したものの連立方程式
の解である。したがって
である。
まず
とおくと、より
である。
次により
分母分子をともにで割ることにより、
ここで
であるから、前者を分母に後者を分子にそれぞれ加算することで
が成り立つ。以上をまとめれば、
である。
1.2 中心化
前節で求めたについて、
と置き換えるとであり
であるから
が成り立つ。
これは座標平面上にをプロットしたものを考えたときに、それらを方向に、方向ににだけ平行移動したものである。すなわち直線
について、後者は前者の傾きをそのままに原点を通るように平行移動したものを表すことになる。
1.3 RおよびPythonによる実装
入力データは以下のCSVファイルを用いた:
https://drive.google.com/file/d/10iv9jZYqvYpFJx2wiRPxJi2Lyiu-essp/view?usp=sharing
1.3.1 Rの実装
########################################################## ### 最小二乗法により単回帰モデルのパラメータを推定する ### ########################################################## ### 真の値: ### beta_0 = 4.2 ### beta_1 = 0.5 # 初期設定 options(stringsAsFactors = F) # ディレクトリの設定 dir.input <- "...適当な入力ファイルのあるディレクトリを指定..." dir.output <- "...適当な出力先を指定…" # パラメータ推定の関数 EstParamLM <- function(vc.x,vc.y){ x.mean <- mean(vc.x) y.mean <- mean(vc.y) # β_1の推定 beta.1 <- (vc.x-x.mean) %*% (vc.y-y.mean) beta.1 <- beta.1/((vc.x-x.mean) %*% (vc.x-x.mean)) # β_0の推定 beta.0 <- y.mean - beta.1 * x.mean return(c(beta.0, beta.1)) } # 実際に推定する df.input <- read.csv(paste0(dir.input,"01_01_TestData.csv"),header = T) vc.start <- Sys.time() vc.EstParam <- EstParamLM(df.input[,"x"],df.input[,"y"]) vc.end <- Sys.time() - vc.start # 推定結果と計算時間を出力 vc.AbsError <- vc.EstParam-c(4.2,0.5) # 絶対誤差 vc.RelError <- vc.AbsError/c(4.2,0.5) # 絶対誤差 vc.out <- c("R",vc.EstParam,vc.AbsError,vc.RelError,as.numeric(vc.end)) df.output <- as.data.frame(t(vc.out)) colnames(df.output) <- c("言語","EstBeta0","EstBeta1","AbsErrorBeta0","AbsErrorBeta1","RelErrorBeta0","RelErrorBeta1","CalcTime[s]") write.csv(x = df.output, file = paste0(dir.output,"01_01_Output_R.csv"),row.names = F)
1.3.2 Pythonの実装
import csv import numpy as np import time def EstParamLM(x,y): x_bar, y_bar = np.mean(x), np.mean(y) beta1 = np.dot(x - x_bar, y - y_bar)/np.linalg.norm(x - x_bar) ** 2 beta0 = y_bar - beta1 * x_bar return [beta0, beta1] # 読み込みファイルの整理 dir_input <- '...適当な入力ファイルのあるディレクトリを指定...' dir_output <- '...適当な出力先を指定…' file_input = open(dir_input + '01_01_TestData.csv', 'r', encoding='utf-8', errors='', newline='') #リスト形式 f = csv.reader(file_input, delimiter=',', doublequote=True, lineterminator='\r\n', quotechar='"', skipinitialspace=True) header = next(f) # ヘッダーを読み飛ばす lst_z = np.asarray([row for row in f], dtype=np.float32) lst_x = np.asarray([row[0] for row in lst_z], dtype=np.float32) lst_y = np.asarray([row[1] for row in lst_z], dtype=np.float32) start = time.time() beta0,beta1 = EstParamLM(lst_x,lst_y) elapsed_time = time.time() - start print(beta0),print(beta1) with open(dir_output + '01_01_Output_Python.csv', 'w') as f: writer = csv.writer(f) writer.writerow(['言語','EstBeta0','EstBeta1','AbsErrorBeta0','AbsErrorBeta1','RelErrorBeta0','RelErrorBeta1','CalcTime[s]']) writer.writerow(['Python',beta0, beta1, beta0-4.2, beta1-0.5,(beta0-4.2)/4.2, (beta1-0.5)/0.5,format(elapsed_time)])