Rで 誤差構造 を ベータ分布、リンク関数 を logit とする 一般化線形モデル を試みます。
本ポストは https://www.jstage.jst.go.jp/article/weed/55/4/55_4_275/_pdf を参照しています。
始めにサンプルデータを作成します。
サンプルデータは 0-100 の範囲を取る説明変数と、ベータ分布 に従い、但しスケールは説明変数と同じく 0-100 とする従属変数の組み合わせとします。
また、本ポストでは説明変数 \(X\) に対する従属変数の期待値(平均値) \(\mu\) の算出を目的とし、精度パラメータ(形状パラメータ \(\alpha\) と \(\beta\) の和 = \(\phi\) )は算出していません。
\[\begin{eqnarray}
\textrm{E}[Y]&=&\mu=\alpha/(\alpha+\beta)\\
\textrm{Var(Y)}&=&(\alpha\beta)/[(\alpha+\beta)^2(\alpha+\beta+1)]\\
\phi&=&\alpha+\beta
\end{eqnarray}\]
library(ggplot2)
<- 20250405
seed set.seed(seed)
<- 200 # サンプルサイズ
n_obs
# 説明変数 X (0から100の範囲とします)
<- runif(n_obs, 0, 100)
x
# 真のパラメータを設定
<- c(-2.0, 0.05) # 真の切片 (beta0) と傾き (beta1)
beta_true <- 15 # 真の精度パラメータ (phi)
phi_true
# デザイン行列を作成 (切片項を含む)
<- cbind(1, x)
X
# 線形予測子 eta = X * beta
<- X %*% beta_true
eta_true
# 平均 mu を計算 (逆ロジット関数)
# mu = exp(eta) / (1 + exp(eta)) = 1 / (1 + exp(-eta))
<- 1 / (1 + exp(-eta_true))
mu_true
# 従属変数 y を生成 (0から1の範囲のベータ分布に従う)
# ベータ分布のパラメータ shape1 = mu * phi, shape2 = (1 - mu) * phi
<- mu_true * phi_true
shape1 <- (1 - mu_true) * phi_true
shape2 <- rbeta(n_obs, shape1 = shape1, shape2 = shape2)
y_01
# データが0や1にならないように調整
<- 1e-6
epsilon <- pmax(epsilon, pmin(1 - epsilon, y_01))
y_01
# 従属変数は 0-100 のスケールとする
<- y_01 * 100
y_100
# 可視化します
<- ggplot(mapping = aes(x = x, y = y_100)) +
(g1 geom_point())
続いて、Iteratively Reweighted Least Squares (IRLS) アルゴリズム を用いてパラメータ(回帰係数 \(\beta\))を推定します。
# モデルフィッティングは y_01 (0-1スケール) を使用
<- y_01
y
# 初期値設定
<- rep(0, ncol(X)) # βをゼロベクトルで初期化
beta_current <- 50 # 最大反復回数
max_iter <- 1e-7 # 収束判定の閾値
tolerance
for (iter in 1:max_iter) {
# (1) 線形予測子 η (eta) の計算
<- X %*% beta_current
eta
# (2) 平均 μ (mu) の計算 (逆ロジット関数)
<- 1 / (1 + exp(-eta))
mu # mu が0や1にならないように調整
<- pmax(epsilon, pmin(1 - epsilon, mu))
mu
# (3) リンク関数の導関数 g'(μ) の計算
# ロジットリンク g(μ) = log(μ / (1 - μ))
# その導関数 g'(μ) = 1 / (μ * (1 - μ))
<- 1 / (mu * (1 - mu))
g_prime_mu
# (4) 分散関数 V(μ) の計算 (ベータ分布の場合、φを含まない部分)
# Var(y) = μ(1-μ) / (1 + φ) なので、V(μ) = μ(1-μ) とする
<- mu * (1 - mu)
v_mu
# (5) IRLSの重み w の計算
# w = 1 / ( V(μ) * (g'(μ))^2 )
# w = 1 / ( (mu * (1 - mu)) * (1 / (mu * (1 - mu)))^2 )
# w = mu * (1 - mu)
# μ が 0 または 1 に近い場合(g'(μ) が大きくなる場合)には重みを小さく
# μ が 0.5 に近い場合(g'(μ) が小さくなる場合)には重みを大きく
# 注意: この重みはφを含まない形。
<- mu * (1 - mu)
weights_irls
# (6) 従属変数(一時的) z の計算
# z = η + (y - μ) * g'(μ)
<- eta + (y - mu) * g_prime_mu
z
# (7) 重み付き最小二乗法 (WLS) による β の更新
# β_new = (X^T W X)^(-1) X^T W z
# ここで W は weights_irls を対角成分に持つ対角行列
<- diag(as.vector(weights_irls)) # ベクトルにしてから対角行列化
W
# X^T W を計算
<- t(X) %*% W
X_T_W # (X^T W X) を計算
<- X_T_W %*% X
X_T_W_X # (X^T W X)^(-1) を計算
<- solve(X_T_W_X)
X_T_W_X_inv # (X^T W z) を計算
<- X_T_W %*% z
X_T_W_z # β_new を計算
<- X_T_W_X_inv %*% X_T_W_z
beta_new
# (8) 収束判定
<- sum((beta_new - beta_current)^2)
delta print(paste("Iteration:", iter, " Change in Beta:", round(delta, 8)))
<- beta_new
beta_current
if (delta < tolerance) {
print(paste("収束しました (反復回数:", iter, ")"))
break
}
if (iter == max_iter) {
warning("最大反復回数に達しました。収束していない可能性があります。")
}
}
<- beta_current beta_estimated
[1] "Iteration: 1 Change in Beta: 2.51005137"
[1] "Iteration: 2 Change in Beta: 0.15653074"
[1] "Iteration: 3 Change in Beta: 0.00383094"
[1] "Iteration: 4 Change in Beta: 2.32e-06"
[1] "Iteration: 5 Change in Beta: 0"
[1] "収束しました (反復回数: 5 )"
推定された回帰係数 \(\beta\) です。
上段が線形予測子の切片、下段が傾きです。
beta_estimated
[,1]
-2.04273337
x 0.05109242
推定された従属変数( \(\hat{y}\) )を確認します。
<- X %*% beta_estimated
eta_pred <- 1 / (1 + exp(-eta_pred))
mu_pred <- g1 + geom_line(mapping = aes(x = x, y = mu_pred * 100), colour = "red", linewidth = 1)) (g2
真の関係と重ねます。
<- g2 + geom_line(mapping = aes(x = x, y = mu_true * 100), colour = "blue", linewidth = 2, linetype = "dashed")) (g3
Rの関数 glm の結果と比較します。
ここで、誤差構造は 疑似二項分布(quasibinomial) とします。
library(dplyr)
<- glm(y_01 ~ x, family = quasibinomial(link = "logit"))
fit_glm_quasi %>% summary() fit_glm_quasi
Call:
glm(formula = y_01 ~ x, family = quasibinomial(link = "logit"))
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -2.042733 0.089845 -22.74 <2e-16 ***
x 0.051092 0.001753 29.14 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for quasibinomial family taken to be 0.05922646)
Null deviance: 84.64 on 199 degrees of freedom
Residual deviance: 11.66 on 198 degrees of freedom
AIC: NA
Number of Fisher Scoring iterations: 5
beta_estimated と一致しています。
glm の結果と比較します。
<- predict(fit_glm_quasi, newdata = data.frame(x = x), type = "response")
mu_pred_glm unique(round(mu_pred - mu_pred_glm, 12))
[,1]
[1,] 0
一致しています。
最後に全てを重ねます。
<- data.frame(x, y = y_100, mu_true = mu_true * 100, mu_pred = mu_pred * 100, mu_pred_glm = mu_pred_glm * 100) %>% tidyr::gather(, , colnames(.)[-1])
tidydf ggplot(data = tidydf, mapping = aes(x = x, y = value, colour = key)) +
geom_point(data = subset(tidydf, key == "y"), colour = "black") +
geom_line(
data = subset(tidydf, key %in% c("mu_true", "mu_pred", "mu_pred_glm")),
mapping = aes(
group = key,
linetype = key,
linewidth = ifelse(key == "mu_true", 1, ifelse(key == "mu_pred", 2, 3))
)+
) scale_linewidth_identity(
breaks = c(1, 2, 3),
labels = c("mu_true", "mu_pred", "mu_pred_glm")
)
以上です。