Rで線形代数:一般逆行列(擬似逆行列)

Rで 線形代数:一般逆行列(擬似逆行列) を確認します。

1. 一般逆行列(擬似逆行列)とは

用途

通常の逆行列は、正方行列(行と列の数が同じ)、かつ正則(行列式が0でない)な場合にしか定義できませんでした。

しかし、実際のデータ分析では、

  • サンプルの数と特徴量の数が異なる長方形行列
  • 変数間に強い相関(多重共線性)があるために生じる特異行列

などを扱う場面があります。

これらの「逆行列を持たない行列」に対しても、逆行列と似たような操作を行えるのが一般逆行列(Generalized Inverse)です。

定義

ここでは、ムーア・ペンローズ逆行列(擬似逆行列 (Pseudoinverse)、一般逆行列) A⁺ についてに説明します。

ムーア・ペンローズ逆行列 A⁺ は、以下の4つの条件を満たす唯一の行列として定義されます。

  1. A A⁺ A = A
  2. A⁺ A A⁺ = A⁺
  3. (A A⁺)ᵀ = A A⁺A A⁺ は対称行列)
  4. (A⁺ A)ᵀ = A⁺ AA⁺ A も対称行列)

性質

連立一次方程式 Ax = b を解く際、

  • 通常の逆行列(解がただ1つ存在): x = A⁻¹b で、方程式を完璧に満たす唯一の解が得られます。

  • 一般逆行列(解が無数に存在、または存在しない): x = A⁺b で計算される解は、状況に応じて「最も良い」解を与えてくれます。

    1. 解が無数に存在する場合: A⁺ は、無数にある解の中で最もノルム(ベクトルの長さ)が小さい解(最小ノルム解)を与えます。
    2. 解が存在しない場合: A⁺ は、Axb の差の2乗和 ||Ax - b||²最小にする解(最小二乗解)を与えます。これは、方程式の完全な解はないが、「最も誤差が小さい近似解」を見つけることに相当します。

この最小二乗解を与える性質が、回帰分析で利用されます。

通常の逆行列との関係

もし行列 A が正則(通常の逆行列 A⁻¹ を持つ)ならば、そのムーア・ペンローズ逆行列 A⁺通常の逆行列 A⁻¹ と完全に一致します。 つまり、一般逆行列は通常の逆行列の概念を、長方形行列や特異行列を含むより広い範囲の行列に適用できるように一般化・拡張したものです。


2. Rによるシミュレーション

# --------------------------------------------------
# 一般逆行列のシミュレーション
# --------------------------------------------------

library(MASS)

# --- シナリオ1: 正則行列の場合 ---
# 一般逆行列が、通常の逆行列と一致することを確認

cat("--- シナリオ1: 正則行列の場合 ---\n")
A <- matrix(c(2, 1, 3, 4), nrow = 2)
cat("正則行列 A:\n")
print(A)

# 通常の逆行列
A_inv <- solve(A)
cat("\n通常の逆行列 (solve(A)):\n")
print(A_inv)

# 一般逆行列
A_ginv <- ginv(A)
cat("\n一般逆行列 (ginv(A)):\n")
print(A_ginv)

# 両者が一致するか確認
cat("\n両者は一致するか? ->", all.equal(A_inv, A_ginv), "\n\n")


# --- シナリオ2: 長方形行列(回帰分析での応用) ---
# 最小二乗解を求める例

cat("--- シナリオ2: 長方形行列(回帰分析での応用) ---\n")
seed <- 20250623
set.seed(seed)

# y = 5 + 2x + (ノイズ) という関係のデータを作成
x <- 1:20
y <- 5 + 2 * x + rnorm(20, mean = 0, sd = 3)

# 線形回帰モデル y = β0 + β1*x は、行列で Y = Xβ と書ける
# X は「デザイン行列」
X <- cbind(1, x) # 第1列は切片(β0)用、第2列はx(β1)用
cat("デザイン行列 X (最初の6行のみ):\n")
print(head(X))
cat("目的変数 y (最初の6個のみ):\n")
print(head(y))

# Xは 20x2 の長方形行列なので、通常の逆行列は持たない
# 一般逆行列を使って係数βの最小二乗解を求める
# β_hat = (X^T X)^-1 X^T y と同じ結果になる
beta_hat_ginv <- ginv(X) %*% y
cat("\nginv()で求めた回帰係数 (β0, β1):\n")
print(t(beta_hat_ginv))

# Rの標準的な回帰分析関数 lm() の結果と比較
model <- lm(y ~ x)
cat("\nlm()で求めた回帰係数:\n")
print(coef(model))

cat("\n→ 長方形行列の一般逆行列を使うことで、回帰分析の最小二乗解が求められることがわかります。\n\n")


# --- シナリオ3: 特異行列の場合 ---
# 通常の逆行列は計算できないが、一般逆行列は計算できる例

cat("--- シナリオ3: 特異行列の場合 ---\n")

# 2行目が1行目の2倍になっている特異行列
B <- matrix(c(1, 2, 2, 4), nrow = 2)
cat("特異行列 B:\n")
print(B)

# solve(B)はエラーになる
cat("\nsolve(B) を実行するとエラーになります。\n")
result <- tryCatch(
  {
    solve(B)
  },
  error = function(e) {
    return(e) # エラーオブジェクトを返す
  }
)
print(result$message)

# ginv(B)は計算可能
B_ginv <- ginv(B)
cat("\n一般逆行列 ginv(B):\n")
print(B_ginv)

# 一般逆行列の性質 B * B+ * B = B を確認
B_check <- B %*% B_ginv %*% B
cat("\n性質の確認 (B %*% ginv(B) %*% B):\n")
print(round(B_check, 10))
cat("→ 元の行列 B に戻りました。\n")
--- シナリオ1: 正則行列の場合 ---
正則行列 A:
     [,1] [,2]
[1,]    2    3
[2,]    1    4

通常の逆行列 (solve(A)):
     [,1] [,2]
[1,]  0.8 -0.6
[2,] -0.2  0.4

一般逆行列 (ginv(A)):
     [,1] [,2]
[1,]  0.8 -0.6
[2,] -0.2  0.4

両者は一致するか? -> TRUE 

--- シナリオ2: 長方形行列(回帰分析での応用) ---
デザイン行列 X (最初の6行のみ):
       x
[1,] 1 1
[2,] 1 2
[3,] 1 3
[4,] 1 4
[5,] 1 5
[6,] 1 6
目的変数 y (最初の6個のみ):
[1]  6.158808 10.453372 10.543494 13.546001 12.768363 18.823930

ginv()で求めた回帰係数 (β0, β1):
         [,1]     [,2]
[1,] 4.286856 2.043078

lm()で求めた回帰係数:
(Intercept)           x 
   4.286856    2.043078 

→ 長方形行列の一般逆行列を使うことで、回帰分析の最小二乗解が求められることがわかります。

--- シナリオ3: 特異行列の場合 ---
特異行列 B:
     [,1] [,2]
[1,]    1    2
[2,]    2    4

solve(B) を実行するとエラーになります。
[1] "Lapack routine dgesv: system is exactly singular: U[2,2] = 0"

一般逆行列 ginv(B):
     [,1] [,2]
[1,] 0.04 0.08
[2,] 0.08 0.16

性質の確認 (B %*% ginv(B) %*% B):
     [,1] [,2]
[1,]    1    2
[2,]    2    4
→ 元の行列 B に戻りました。

以上です。