-
Notifications
You must be signed in to change notification settings - Fork 36
Add Latent-factor multi-output kernel #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
4f9a656
0fd2927
84b5656
24192a7
6b70af1
398bef9
0754b0a
e30e025
c22eaac
a747d6c
d891330
ecfca19
7c90a48
2c8c9bd
0b4226d
31a0981
0026b6b
75a425e
fd12690
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
@doc raw""" | ||
LatentFactorMOKernel( | ||
g::AbstractVector{<:Kernel}, | ||
e::AbstractVector{<:Kernel}, | ||
A::AbstractMatrix | ||
) | ||
|
||
A semiparametric kernel for problems involving multiple response variables. | ||
|
||
``k((x, p), (y, p)) = k_p(x, y) = \Sum^{Q}_{q=1} A_{pq}g_q(x, y) + e_p(x, y)`` | ||
|
||
# Arguments | ||
- `g::AbstractVector{<:Kernel}`: an array of kernels | ||
sharanry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `e::AbstractVector{<:Kernel}`: an array of kernels | ||
sharanry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `A::AbstractMatrix`: an matrix of weights for the kernels of shape (length(e), length(g)) | ||
sharanry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
# Reference: | ||
- [Seeger, Teh, and Jordan (2005)](https://infoscience.epfl.ch/record/161465/files/slfm-long.pdf) | ||
|
||
""" | ||
struct LatentFactorMOKernel{Tg, Te, TA <: AbstractMatrix} <: Kernel | ||
g::Tg | ||
e::Te | ||
A::TA | ||
function LatentFactorMOKernel(g, e, A::AbstractMatrix) | ||
all(isa.(g, Kernel)) || error("`g` should be an collection of kernels") | ||
all(isa.(e, Kernel)) || error("`e` should be an collection of kernels") | ||
sharanry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(length(e), length(g)) == size(A) || | ||
error("Size of A not compatible to the given array of kernels") | ||
return new{typeof(g), typeof(e), typeof(A)}(g, e, A) | ||
end | ||
end | ||
|
||
function (κ::LatentFactorMOKernel)((x, px)::Tuple{Vector, Int}, (y, py)::Tuple{Vector, Int}) | ||
if px == py | ||
return sum([κ.g[i](x, y) * κ.A[px, i] for i in 1:length(κ.g)]) + | ||
κ.e[px](x, y) | ||
else | ||
return 0.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This introduces a type instability, maybe we can avoid it by computing some dummy value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure what you mean. How do we ensure the computed dummy value is of the same type without actually executing it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not completely sure, I was still wondering what's the best way to do it. Sometimes one can perform some possibly cheaper dummy operation by, e.g., using zeros instead of actually indexing etc. Alternatively one could always evaluate the first branch and just call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you worried about the first branch not giving a function (κ::LatentFactorMOKernel)((x, px)::Tuple{Vector, Int}, (y, py)::Tuple{Vector, Int})
if px == py
return Float64(sum([κ.g[i](x, y) * κ.A[px, i] for i in 1:length(κ.g)]) +
κ.e[px](x, y))
else
return Float64(0.0)
end
end I am not sure if this would cause any problems with AD. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it could happen for dual numbers (or basically any other number types). Type instability occurs even if the kernels return values of type Float32 and the elements of A are of type Float32. IMO this example also shows that it's not a good idea to enforce Float64. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @willtebbutt @theogf Any suggestions on this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not completely clear on why there's this zero branch as the SLFM produces non-zero covariance between all outputs. Am I missing something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am confused. Which kernel(s) from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @willtebbutt Is this resolved? I am still unsure how to handle cases when |
||
end | ||
end | ||
|
||
function kernelmatrix(k::LatentFactorMOKernel, x::MOInput, y::MOInput) | ||
x.out_dim == y.out_dim || error("`x` and `y` should have the same output dimension") | ||
x.out_dim == size(k.A, 1) || | ||
error("Kernel not compatible with the given multi-output inputs") | ||
return k.(x, permutedims(collect(y))) | ||
sharanry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
function Base.show(io::IO, k::LatentFactorMOKernel) | ||
print(io, "Semi-parametric Latent Factor Multi-Output Kernel") | ||
end | ||
|
||
function Base.show(io::IO, ::MIME"text/plain", k::LatentFactorMOKernel) | ||
print( | ||
io, | ||
"Semi-parametric Latent Factor Multi-Output Kernel\n\tgᵢ: ", | ||
[string(gi, "\n\t\t") for gi in k.g]..., | ||
"\n\teᵢ: ", | ||
[string(ei, "\n\t\t") for ei in k.e]..., | ||
) | ||
sharanry marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
@testset "independent" begin | ||
x = MOInput([rand(5) for _ in 1:4], 3) | ||
y = MOInput([rand(5) for _ in 1:4], 3) | ||
x1 = MOInput([rand(5) for _ in 1:4], 3) | ||
x2 = MOInput([rand(5) for _ in 1:4], 3) | ||
|
||
k = IndependentMOKernel(GaussianKernel()) | ||
@test k isa IndependentMOKernel | ||
@test k isa Kernel | ||
@test k.kernel isa KernelFunctions.BaseKernel | ||
@test k(x[2], y[2]) isa Real | ||
@test k(x1[2], x2[2]) isa Real | ||
|
||
@test kernelmatrix(k, x, y) == kernelmatrix(k, collect(x), collect(y)) | ||
@test kernelmatrix(k, x, x) == kernelmatrix(k, x) | ||
@test kernelmatrix(k, x1, x2) == kernelmatrix(k, collect(x1), collect(x2)) | ||
@test kernelmatrix(k, x1, x1) == kernelmatrix(k, x1) | ||
@test string(k) == "Independent Multi-Output Kernel\n\tSquared Exponential Kernel" | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
@testset "slfm" begin | ||
x1 = MOInput([rand(5) for _ in 1:4], 2) | ||
x2 = MOInput([rand(5) for _ in 1:4], 2) | ||
|
||
k = LatentFactorMOKernel( | ||
[MaternKernel(), SqExponentialKernel(), FBMKernel()], | ||
[ExponentialKernel(), PeriodicKernel(5)], | ||
rand(2, 3) | ||
) | ||
@test k isa LatentFactorMOKernel | ||
@test k isa Kernel | ||
@test k(x1[2], x2[2]) isa Real | ||
|
||
@test kernelmatrix(k, x1, x2) == kernelmatrix(k, collect(x1), collect(x2)) | ||
@test kernelmatrix(k, x1, x1) == kernelmatrix(k, x1) | ||
|
||
end |
Uh oh!
There was an error while loading. Please reload this page.