Linear Regression
using AlgebraicInference
using Catlab.Graphics, Catlab.Programs
using FillArrays
using LinearAlgebra
using StatsPlots
Frequentist Linear Regression
Consider the Gauss-Markov linear model
\[ y = X \beta + \epsilon,\]
where $X$ is an $n \times m$ matrix, $\beta$ is an $m \times 1$ vector, and $\epsilon$ is an $n \times 1$ normally distributed random vector with mean $\mathbf{0}$ and covariance $W$. If $X$ has full column rank, then the best linear unbiased estimator for $\beta$ is the random vector
\[ \hat{\beta} = X^+ (I - (Q W Q)^+ Q W)^\mathsf{T} y,\]
where $X^+$ is the Moore-Penrose psuedoinverse of $X$, and
\[Q = I - X X^+.\]
References:
- Albert, Arthur. "The Gauss-Markov Theorem for Regression Models with Possibly Singular Covariances." SIAM Journal on Applied Mathematics, vol. 24, no. 2, 1973, pp. 182–87.
X = [
1 0
0 1
0 0
]
W = [
0 0 0
0 1 .5
0 .5 1
]
y = [
1
1
1
]
Q = I - X * pinv(X)
β̂ = pinv(X) * (I - pinv(Q * W * Q) * Q * W)' * y
round.(β̂; digits=4)
2-element Vector{Float64}:
1.0
0.5
To solve for $\hat{\beta}$ using AlgebraicInference.jl, we construct an undirected wiring diagram.
wd = @relation (a,) where (a::m, b::n, c::n, d::n) begin
X(a, b)
+(b, c, d)
ϵ(c)
y(d)
end
to_graphviz(wd; box_labels=:name, implicit_junctions=true)
Then we assign values to the boxes in wd
and compute the result.
P = [
1 0 0 1 0 0
0 1 0 0 1 0
0 0 1 0 0 1
]
hom_map = Dict{Symbol, DenseGaussianSystem{Float64}}(
:X => kernel(X, Zeros(3), Zeros(3, 3)),
:+ => kernel(P, Zeros(3), Zeros(3, 3)),
:ϵ => normal(Zeros(3), W),
:y => normal(y, Zeros(3, 3)))
ob_map = Dict(
:m => 2,
:n => 3)
problem = InferenceProblem(wd, hom_map, ob_map)
Σ̂ = solve(problem)
β̂ = mean(Σ̂)
round.(β̂; digits=4)
2-element Vector{Float64}:
1.0
0.5
Bayesian Linear Regression
Let $\rho = \mathcal{N}(m, V)$ be our prior belief about $\beta$. Then our posterior belief $\hat{\rho}$ is a bivariate normal distribution with mean
\[ \hat{m} = m - V X^\mathsf{T} (X V X^\mathsf{T} + W)^+ (X m - y)\]
and covariance
\[ \hat{V} = V - V X^\mathsf{T} (X V X^\mathsf{T} + W)^+ X V.\]
V = [
1 0
0 1
]
m = [
0
0
]
m̂ = m - V * X' * pinv(X * V * X' + W) * (X * m - y)
round.(m̂; digits=4)
2-element Vector{Float64}:
1.0
0.2857
V̂ = V - V * X' * pinv(X * V * X' + W) * X * V
round.(V̂; digits=4)
2×2 Matrix{Float64}:
0.0 0.0
0.0 0.4286
To solve for $\hat{\rho}$ using AlgebraicInference.jl, we construct an undirected wiring diagram.
wd = @relation (a,) where (a::m, b::n, c::n, d::n) begin
ρ(a)
X(a, b)
+(b, c, d)
ϵ(c)
y(d)
end
to_graphviz(wd; box_labels=:name, implicit_junctions=true)
Then we assign values to the boxes in wd
and compute the result.
hom_map = Dict{Symbol, DenseGaussianSystem{Float64}}(
:ρ => normal(m, V),
:X => kernel(X, Zeros(3), Zeros(3, 3)),
:+ => kernel(P, Zeros(3), Zeros(3, 3)),
:ϵ => normal(Zeros(3), W),
:y => normal(y, Zeros(3, 3)))
ob_map = Dict(
:m => 2,
:n => 3)
problem = InferenceProblem(wd, hom_map, ob_map)
Σ̂ = solve(problem)
m̂ = mean(Σ̂)
round.(m̂; digits=4)
2-element Vector{Float64}:
1.0
0.2857
V̂ = cov(Σ̂)
round.(V̂; digits=4)
2×2 Matrix{Float64}:
0.0 0.0
0.0 0.4286
plot()
covellipse!(m, V, aspect_ratio=:equal, label="prior")
covellipse!(m̂, V̂, aspect_ratio=:equal, label="posterior")