In [1]:
import Pkg
Pkg.activate("/Users/markus/.julia/env/ppl")
  Activating project at `~/.julia/env/ppl`
In [2]:
using Turing
import PyPlot
using PyCall

plt = pyimport("matplotlib.pyplot");
plt_image = pyimport("matplotlib.image");
plt_box = pyimport("matplotlib.offsetbox");
plt_cm = pyimport("matplotlib.cm");
plt_lines = pyimport("matplotlib.lines");
np = pyimport("numpy");
In [3]:
slope_true = 2
intercept_true = -1
slope_true, intercept_true
Out[3]:
(2, -1)
In [4]:
Turing.Random.seed!(0)
xs = rand(Uniform(-1,1),1000)
ys = rand.(Normal.(slope_true .* xs .+ intercept_true, 0.5))
plt.scatter(xs, ys);
Image
In [5]:
broad_prior_params = (slope_mean=0., slope_sigma=3., intercept_mean=0., intercept_sigma=3.);
informative_prior_params = (slope_mean=0, slope_sigma=0.5, intercept_mean=0., intercept_sigma=0.5);
In [6]:
@model function linear_regression(
        x, y;
        slope_mean, slope_sigma,
        intercept_mean, intercept_sigma
    )
    
    # prior over latents
    slope ~ Normal(slope_mean, slope_sigma)
    intercept ~ Normal(intercept_mean, intercept_sigma)
    
    # likelihood
    for i in 1:length(x)
        # y ≈ slope * x + intercept
        y[i] ~ Normal(slope * x[i] + intercept, 1.)
    end
end
Out[6]:
linear_regression (generic function with 2 methods)
In [7]:
# just a plotting function
function plot_linreg(x, y, result; alpha, ylims)
    fig, ax = plt.subplots(1,1)
    ax.set_ylim(ylims)
    xs = LinRange(minimum(x)-0.5, maximum(x)+0.5, 100)
    for i in 1:length(result)
        slope = result[:slope][i]
        intercept = result[:intercept][i]
        a = alpha isa AbstractVector ? alpha[i] : alpha
        ax.plot(xs, slope.* xs .+ intercept, color="black", alpha=a)
    end
    ax.scatter(x, y, zorder=length(result)+1, edgecolors="black")
end
Out[7]:
plot_linreg (generic function with 1 method)
In [8]:
model = linear_regression(xs,ys; broad_prior_params...);
result = sample(model, Prior(), 1000);
plot_linreg(xs, ys, result; alpha=0.025,ylims=(-10,10));
Image
In [9]:
model = linear_regression(xs,ys; informative_prior_params...);
result = sample(model, Prior(), 1000);
plot_linreg(xs, ys, result; alpha=0.025, ylims=(-10,10));
Image
In [10]:
# p(slope, intercept)
function log_linear_regression_prior(
        slope, intercept,
        x, y;
        slope_mean, slope_sigma,
        intercept_mean, intercept_sigma,
    )
    
    lp = 0.
    
    lp += logpdf(Normal(slope_mean, slope_sigma), slope)
    lp += logpdf(Normal(intercept_mean, intercept_sigma), intercept)
    
    return lp
end

# p(data | slope, intercept)
function log_linear_regression_likelihood(
        slope, intercept,
        x, y;
    )
    
    lp = 0.
    
    for i in 1:length(x)
        lp += logpdf(Normal(slope * x[i] + intercept, 1.), y[i])
    end
    
    return lp
end
Out[10]:
log_linear_regression_likelihood (generic function with 1 method)
In [11]:
function logsumexp(x)
    m = maximum(x)
    if m == -Inf
        return -Inf
    end
    return log(sum(exp, x .- m)) + m
end
Out[11]:
logsumexp (generic function with 1 method)
In [12]:
# just a plotting function
function compare(xs, ys, xmin, xmax, ymin, ymax)
    fig, ax = plt.subplots(ncols=2,figsize=(12,6))
    
    delta = 0.01
    s_range = xmin:delta:xmax
    i_range = ymin:delta:ymax
    S, I = np.meshgrid(s_range, i_range, indexing="xy")
    
    log_z_broad = (slope, intercept) -> log_linear_regression_prior(slope, intercept, xs, ys; broad_prior_params...) + log_linear_regression_likelihood(slope, intercept, xs, ys)
    log_z_informative = (slope, intercept) -> log_linear_regression_prior(slope, intercept, xs, ys; informative_prior_params...) + log_linear_regression_likelihood(slope, intercept, xs, ys)
    
    log_Z_broad = [log_z_broad(s,i) for i in i_range, s in s_range]
    log_Z_broad = log_Z_broad .- logsumexp(log_Z_broad) .- log(delta^2)
    
    log_Z_informative = [log_z_informative(s,i) for i in i_range, s in s_range]
    log_Z_informative = log_Z_informative .- logsumexp(log_Z_informative) .- log(delta^2)
    
    ax[1].contour(S, I, exp.(log_Z_broad), cmap=plt_cm.Blues)
    ax[1].contour(S, I, exp.(log_Z_informative), cmap=plt_cm.Reds)
    ax[1].set_title("#data=$(length(xs))")
    
    legend_elements = [
        plt_lines.Line2D([0], [0], color="tab:red", lw=4, label="informative prior"),
        plt_lines.Line2D([0], [0], color="tab:blue", lw=4, label="broad prior")
    ]
    ax[1].legend(handles=legend_elements)
    
    x_range = -1:delta:1
    
    ax[2].scatter(xs, ys, color="black")
    slope_informative = S[argmax(log_Z_informative)]
    intercept_informative = I[argmax(log_Z_informative)]
    ax[2].plot(x_range, slope_informative .* x_range .+ intercept_informative, color="tab:red")
    
    slope_broad = S[argmax(log_Z_broad)]
    intercept_broad = I[argmax(log_Z_broad)]
    ax[2].plot(x_range, slope_broad .* x_range .+ intercept_broad, color="tab:blue")
end
Out[12]:
compare (generic function with 1 method)
In [13]:
N = 25
compare(xs[1:N], ys[1:N], -3, 3, -3, 3);
compare(xs[1:N], ys[1:N], 1, 3, -1.5, -0.5);
Image
Image
In [ ]: