必要なパッケージを読み込む。
::p_load(tidyverse,
pacman knitr)
次に、ggplot2のテーマとフォントの設定を行う。自分好みの設定がある場合は自由に変えてよい。
if (.Platform$OS.type == "windows") {
if (require(fontregisterer)) {
<- "Yu Gothic"
my_font else {
} <- "Japan1"
my_font
}else if (capabilities("aqua")) {
} <- "HiraginoSans-W3"
my_font else {
} <- "IPAexGothic"
my_font
}
theme_set(theme_gray(base_size = 9,
base_family = my_font))
以下の母回帰関数を仮定する。 \[ \begin{aligned} Y_i &= \beta_0 + \beta_1 X_i + \beta_2 X_i^2 + \varepsilon_i \\ \varepsilon_i & \sim \mbox{Normal}(0, \sigma) \end{aligned} \]
誤差項 \(\varepsilon\) の標準偏差は\(\sigma = 1\) として、\(\boldsymbol{\beta}\) はランダムに決定する。
set.seed(2021-10-11)
<- 1
sigma <- runif(1, min = -1, max = 1)
beta0 <- runif(1, min = -1, max = 1)
beta1 <- runif(1, min = -1, max = 1) beta2
上の設定で、データ(観測値、訓練データ)を抽出する関数を作る。
<- function(N = 50, beta0, beta1, beta2, sigma = 1) {
sample_data tibble(x = rnorm(N, mean = 0, sd = 1)) %>%
mutate(y = beta0 + beta1 * x + beta2 * x^2 + rnorm(N, mean = 0, sd = sigma))
}
\(N = 50\) で標本抽出を行う。
<- sample_data(N = 50,
training beta0 = beta0,
beta1 = beta1,
beta2 = beta2)
このデータから \(\hat{f}(x)\) を学習するためのモデルために、\(x\)の基底関数を使った以下の4つのモデルを考える。
<- alist(m1 = y ~ x,
models_s m2 = y ~ x + I(x^2),
m3 = y ~ x + I(x^2) + I(x^3),
m4 = y ~ x + I(x^2) + I(x^3) + I(x^4)) %>%
map(formula)
それぞれのモデルを使って最小二乗学習を行う。
<- models_s %>%
fits_s map(lm, data = training)
予測値 \(\hat{y}\) をデータフレームに加える
<- fits_s %>%
trained map(predict) %>%
as_tibble() %>%
bind_cols(training)
学習結果を使い、\(x \in [-4, 4]\) の範囲で \(\hat{f}(x)\)のグラフを観測値の散布図に重ねて描く。
<- tibble(x = seq(from = -4, to = 4, length.out = 100))
res_df <- fits_s %>%
yhats map(predict, newdata = res_df) %>%
as_tibble() %>%
bind_cols(res_df) %>%
pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat")
<- ggplot() +
p geom_point(data = training, aes(x = x, y = y)) +
geom_line(data = yhats, aes(x = x, y = y_hat, color = model))
plot(p)
訓練データに対するMSE を計算する。
%>%
trained pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat") %>%
group_by(model) %>%
summarize(MSE = mean((y - y_hat)^2)) %>%
kable(digits = 3)
model | MSE |
---|---|
m1 | 1.559 |
m2 | 1.373 |
m3 | 1.373 |
m4 | 1.364 |
m4のMSEが最も小さく、データには最もよくフィットしていることがわかる。
念のため、決定係数を確認する。
lapply(1:4, function(x) summary(fits_s[[x]])$r.squared) %>%
kable(digits = 3)
|
|
|
|
やはり、m4 の決定係数が最も大きい。
新たなデータを抽出し、抽出した \(x\)に学習モデルを当てはめたとき、抽出した\(y\)を正しく予測できるか確かめよう。 まず、データを抽出する。
<- sample_data(N = 50,
newdf beta0 = beta0,
beta1 = beta1,
beta2 = beta2)
学習モデルを使って \(\hat{y}\)を計算する。
<- fits_s %>%
pred map(predict, newdata = newdf) %>%
as_tibble() %>%
mutate(y = newdf$y) %>%
pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat")
(検証データに対する)予測のMSEを計算する。
%>%
pred group_by(model) %>%
summarize(MSE = mean((y - y_hat)^2)) %>%
kable(digits = 4)
model | MSE |
---|---|
m1 | 1.1511 |
m2 | 0.8699 |
m3 | 0.8697 |
m4 | 0.9842 |
m3 のMSEが最も小さい。すなわち、この特定のデータに関する予測は、m3が最も良い。
別の標本を抽出して、もう1度確かめよう。
<- sample_data(N = 50,
newdf beta0 = beta0,
beta1 = beta1,
beta2 = beta2)
学習モデルを使って \(\hat{y}\)を計算する。
<- fits_s %>%
pred map(predict, newdata = newdf) %>%
as_tibble() %>%
mutate(y = newdf$y) %>%
pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat")
(検証データに対する)予測のMSEを計算する。
%>%
pred group_by(model) %>%
summarize(MSE = mean((y - y_hat)^2)) %>%
kable(digits = 4)
model | MSE |
---|---|
m1 | 1.4201 |
m2 | 1.2605 |
m3 | 1.2614 |
m4 | 1.3300 |
今回は、m2の予測誤差が最も小さい。すなわち、この特定のデータに関する予測は、m2が最も良い。
バイアス\(\mathbb{E}[\hat{f}(x)] - f(x)\) と分散 \(\mathbb{V}[\hat{f}(x)] = \mathbb{E}\left[( \hat{f}(x) - \mathbb{E}[\hat{f}(x)])^2 \right]\) を計算するために、標本抽出と学習を繰り返し、それぞれのモデルについて1,000個の \(\hat{f}\)を手に入れよう。そして、それぞれについて、次の\(x\) (x_test
)から\(\hat{f}(x)\)を求める。
<- c(-3, -1.5, 0, 1.5, 3)
x_test <- beta0 + beta1 * x_test + beta2 * x_test^2
f_x names(f_x) <- paste("x =", x_test)
4つのモデルそれぞれについて、1,000個の\(\hat{f}(x)\)をシミュレートする。
<- 1000
n_trials <- m2_res <- m3_res <- m4_res <-
m1_res matrix(NA, ncol = length(x_test), nrow = n_trials,
dimnames = list(NULL, names(f_x)))
for (i in 1:n_trials) {
<- sample_data(N = 20, beta0, beta1, beta2)
df <- models_s %>%
fits map(lm, data = df)
<- fits %>%
preds map(predict, newdata = tibble(x = x_test))
<- preds[[1]]
m1_res[i, ] <- preds[[2]]
m2_res[i, ] <- preds[[3]]
m3_res[i, ] <- preds[[4]]
m4_res[i, ] }
バイアスを計算する。
<- colMeans(m1_res) - f_x
bias_m1 <- colMeans(m2_res) - f_x
bias_m2 <- colMeans(m3_res) - f_x
bias_m3 <- colMeans(m4_res) - f_x
bias_m4 rbind(bias_m1, bias_m2, bias_m3, bias_m4)
## x = -3 x = -1.5 x = 0 x = 1.5 x = 3
## bias_m1 2.19747712 0.35192828 -0.252391655 0.38451731 2.26265518
## bias_m2 0.05829404 0.01443367 -0.006526547 -0.00458661 0.02025348
## bias_m3 -0.02720893 0.02115474 -0.004223326 0.02161864 0.22364241
## bias_m4 0.17380162 0.01977264 -0.008354480 0.07458547 0.91192772
ある程度複雑なモデルのほうが、バイアスが小さくなる、すなわちデータへの当てはまりがよくなることがわかる。ただし、観測データの範囲の外側については、m4のバイアスが大きくなることが見て取れる。
次に、分散を計算する。
<- apply(m1_res, 2, var)
var_m1 <- apply(m2_res, 2, var)
var_m2 <- apply(m3_res, 2, var)
var_m3 <- apply(m4_res, 2, var)
var_m4 rbind(var_m1, var_m2, var_m3, var_m4)
## x = -3 x = -1.5 x = 0 x = 1.5 x = 3
## var_m1 0.9341418 0.2756142 0.05892877 0.2840856 0.9510848
## var_m2 4.8659515 0.3771458 0.08286757 0.3787034 4.8921944
## var_m3 51.6297154 0.8047427 0.09438242 1.1259269 66.0720183
## var_m4 1632.0171083 6.6139021 0.12992508 7.7465269 1724.3047314
複雑なモデルになるほど、分散が大きいことがわかる。つまり、複雑なモデルが学習する\(\hat{f}\)は、データによって大きく変動する。したがって、新しいデータに対する予測はうまくいかない可能性が高い。 観測データの範囲の外側については、その傾向が特に顕著である。
以下の母回帰関数を仮定する。 \[ \begin{aligned} Y_i &= \beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \varepsilon_i \\ \varepsilon_i & \sim \mbox{Normal}(0, \sigma) \end{aligned} \]
誤差項 \(\varepsilon\) の標準偏差は\(\sigma = 1\) として、\(\boldsymbol{\beta}\) を適当に決める
<- 0.5
beta0 <- 0.8
beta1 <- -0.6 beta2
上の設定で、データ(観測値、訓練データ)を抽出する関数を作る。
<- function(N = 50,
sample_data2 beta0 = 0.5,
beta1 = 0.8,
beta2 = -0.6,
sigma = 1) {
tibble(x1 = rnorm(N, mean = 0, sd = 1),
x2 = rnorm(N, mean = 0, sd = 1),
x3 = rnorm(N, mean = 0, sd = 1),
x4 = rnorm(N, mean = 0, sd = 1),
x5 = rnorm(N, mean = 0, sd = 1),
x6 = rnorm(N, mean = 0, sd = 1),
x7 = rnorm(N, mean = 0, sd = 1),
x8 = rnorm(N, mean = 0, sd = 1),
x9 = rnorm(N, mean = 0, sd = 1),
x10 = rnorm(N, mean = 0, sd = 1)) %>%
mutate(y = beta0 + beta1 * x1 + beta2 * x2 + rnorm(N, mean = 0, sd = sigma))
}
\(N = 50\) で標本抽出を行う。
<- sample_data2(N = 50) training
このデータから \(\hat{f}(x)\) を学習するためのモデルために、\(x\)の基底関数を使った以下の4つのモデルを考える。
<- alist(m1 = y ~ x1,
models_m m2 = y ~ x1 + x2,
m3 = y ~ x1 + x2 + x3,
m4 = y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10) %>%
map(formula)
それぞれのモデルを使って最小二乗学習を行う。
<- models_m %>%
fits_m map(lm, data = training)
予測値 \(\hat{y}\) をデータフレームに加える
<- fits_m %>%
trained map(predict) %>%
as_tibble() %>%
bind_cols(training)
訓練データに対するMSE を計算する。
%>%
trained pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat") %>%
group_by(model) %>%
summarize(MSE = mean((y - y_hat)^2))
## # A tibble: 4 × 2
## model MSE
## <chr> <dbl>
## 1 m1 1.10
## 2 m2 0.782
## 3 m3 0.756
## 4 m4 0.654
m4のMSEが最も小さく、データには最もよくフィットしていることがわかる。
念のため、決定係数を確認する。
lapply(1:4, function(x) summary(fits_m[[x]])$r.squared)
## [[1]]
## [1] 0.3277038
##
## [[2]]
## [1] 0.5208046
##
## [[3]]
## [1] 0.5366069
##
## [[4]]
## [1] 0.5992128
やはり、m4 の決定係数が最も大きい。 つまり、y
とは無関係のx3
〜 x10
を使ったモデルのほうが、データへの当てはまりはよい。
新たなデータを抽出し、抽出した \(x\)に学習モデルを当てはめたとき、抽出した\(y\)を正しく予測できるか確かめよう。 まず、データを抽出する。
<- sample_data2(N = 50) newdf
学習モデルを使って \(\hat{y}\)を計算する。
<- fits_m %>%
pred map(predict, newdata = newdf) %>%
as_tibble() %>%
mutate(y = newdf$y) %>%
pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat")
(検証データに対する)予測のMSEを計算する。
%>%
pred group_by(model) %>%
summarize(MSE = mean((y - y_hat)^2))
## # A tibble: 4 × 2
## model MSE
## <chr> <dbl>
## 1 m1 0.925
## 2 m2 0.587
## 3 m3 0.618
## 4 m4 0.904
m2 のMSEが最も小さい。すなわち、この特定のデータに関する予測は、m2が最も良い。
別の標本を抽出して、もう1度確かめよう。
<- sample_data2(N = 50) newdf
学習モデルを使って \(\hat{y}\)を計算する。
<- fits_m %>%
pred map(predict, newdata = newdf) %>%
as_tibble() %>%
mutate(y = newdf$y) %>%
pivot_longer(cols = m1:m4,
names_to = "model",
values_to = "y_hat")
(検証データに対する)予測のMSEを計算する。
%>%
pred group_by(model) %>%
summarize(MSE = mean((y - y_hat)^2))
## # A tibble: 4 × 2
## model MSE
## <chr> <dbl>
## 1 m1 0.874
## 2 m2 0.751
## 3 m3 0.851
## 4 m4 1.07
今回も、m2の予測誤差が最も小さい。すなわち、この特定のデータに関する予測は、m2が最も良い。
複雑なモデルにすればデータへの当てはまりは良くなるが、それは予測に貢献しないばかりか、かえって予測性能が低くなるようだ。つまり、モデルを複雑(柔軟)にすると、過剰適合が起こる。