Rの関数nnet.defaultとstats::glmの等価性の確認

Rの関数 nnet.defaultstats::glm の等価性を確認します

本ポストはこちらの続きです。

Rの関数:multinom {nnet}
Rの関数から multinom {nnet} を確認します。multinom関数とはmultinom関数は、多項ロジスティック回帰モデルの当てはめに用いられます。このモデルは、目的変数が3つ以上のカテゴリを持つ名義尺度である場合に適していま...

nnet.defaultglm の等価性の確認

nnetパッケージの基幹関数 nnet.default を特定の引数(size = 0, skip = TRUE, entropy = TRUE)で設定した場合、その結果が stats パッケージの glm 関数による二項ロジスティック回帰の結果と一致することを確認します。

サンプルデータの作成

始めに、二項ロジスティック回帰に適したサンプルデータを作成します。

glm関数用に因子型の目的変数 (Y_factor) を、nnet.default関数用に0と1からなる数値型の目的変数 (Y_numeric) をそれぞれ用意します。

# 再現性を確保するために乱数の種を設定
seed <- 20251119
set.seed(seed)

# サンプルサイズを設定
n <- 1000

# 2つの説明変数を作成
X1 <- rnorm(n, mean = 2, sd = 1.5)
X2 <- rnorm(n, mean = -1, sd = 2)

# 真の係数を設定(切片、X1の係数、X2の係数)
b0 <- -0.5 # 切片 (Intercept)
b1 <- 1.2 # X1の係数
b2 <- -0.8 # X2の係数

# 線形予測子(ロジット)を計算
logit <- b0 + b1 * X1 + b2 * X2

# ロジットを確率に変換(ロジスティック関数)
prob <- 1 / (1 + exp(-logit))

# 確率に基づいて0か1の目的変数を生成
Y_numeric <- rbinom(n, size = 1, prob = prob)

# データフレームを作成
sample_data <- data.frame(
  Y_numeric = Y_numeric,
  Y_factor = as.factor(Y_numeric), # glm用の因子型
  X1 = X1,
  X2 = X2
)

# 作成したデータの一部を確認
cat("--- 作成されたサンプルデータの一部を確認 ---\n")
print(str(sample_data))
--- 作成されたサンプルデータの一部を確認 ---
'data.frame':   1000 obs. of  4 variables:
 $ Y_numeric: int  1 1 1 1 1 1 1 1 1 1 ...
 $ Y_factor : Factor w/ 2 levels "0","1": 2 2 2 2 2 2 2 2 2 2 ...
 $ X1       : num  2.977 0.744 1.811 2.871 5.79 ...
 $ X2       : num  -2.75 -1.89 -5.57 -3.03 3.48 ...
NULL

stats::glmによるロジスティック回帰モデルの構築

stats::glm関数を用いて、基準となるロジスティック回帰モデルを構築します。目的変数には因子型のY_factorを使用します。

# glm関数でロジスティック回帰モデルを構築
model_glm <- glm(Y_factor ~ X1 + X2, data = sample_data, family = binomial(link = "logit"))

# モデルの要約情報から係数を表示
cat("--- stats::glm の実行結果(係数) ---\n")
print(summary(model_glm)$coefficients)
--- stats::glm の実行結果(係数) ---
              Estimate Std. Error    z value     Pr(>|z|)
(Intercept) -0.5284406 0.15348781  -3.442883 5.755475e-04
X1           1.1384059 0.09559947  11.908077 1.074261e-32
X2          -0.8390814 0.07160474 -11.718237 1.027889e-31

nnet.defaultによるモデルの構築

multinom関数の内部ロジックに従い、二項分類のケースを再現します。すなわち、目的変数に数値型のY_numericを使用し、引数にsize = 0, skip = TRUE, entropy = TRUEを指定します。

# nnet パッケージを読み込む
library(nnet)

# nnet.default の入力形式に合わせる
x_vars <- sample_data[, c("X1", "X2")]
y_numeric <- sample_data$Y_numeric # 0/1の数値ベクトルを使用

# nnet.default関数でモデルを構築
model_nnet <- nnet.default(
  x = x_vars,
  y = y_numeric,
  size = 0, # 隠れ層なし
  entropy = TRUE, # 二項分類モード
  skip = TRUE, # 入力と出力を直結
  trace = FALSE,
  maxit = 200
)

# モデルの要約情報から重み(係数)を表示
cat("--- nnet::nnet.default の実行結果(重み) ---\n")
print(summary(model_nnet)$wts)
--- nnet::nnet.default の実行結果(重み) ---
[1] -0.5284406  1.1384059 -0.8390814

結果の比較と結論

最後に、両方のモデルから得られた係数(glm)と重み(nnet)を比較します。nnet.defaultは、この設定においてglmと同じ順序(切片、X1、X2)で重みを出力します。

# glm から係数を抽出
glm_coeffs <- coef(model_glm)

# nnet から重みを抽出
nnet_weights <- summary(model_nnet)$wts

# 比較用のデータフレームを作成(並べ替えは不要)
comparison_df <- data.frame(
  Parameter = c("Intercept (切片)", "X1", "X2"),
  glm_Coefficient = glm_coeffs,
  nnet_Weight = nnet_weights,
  row.names = NULL
)

cat("--- 係数と重みの比較 ---\n")
print(comparison_df)

cat("\n--- 両者の係数は一致しているか? ---\n")
all.equal(comparison_df$glm_Coefficient, comparison_df$nnet_Weight)
--- 係数と重みの比較 ---
         Parameter glm_Coefficient nnet_Weight
1 Intercept (切片)      -0.5284406  -0.5284406
2               X1       1.1384059   1.1384059
3               X2      -0.8390814  -0.8390814

--- 両者の係数は一致しているか? ---
[1] TRUE

以上です。