-
Notifications
You must be signed in to change notification settings - Fork 33
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
Matrixkernel convenience functions and related performance improvements #363
base: master
Are you sure you want to change the base?
Conversation
@@ -43,6 +43,8 @@ end | |||
_fbm(modX, modY, modXY, h) = (modX^h + modY^h - modXY^h) / 2 | |||
|
|||
_mod(x::AbstractVector{<:Real}) = abs2.(x) | |||
_mod(x::AbstractVector{<:AbstractVector{<:Real}}) = sum.(abs2, x) | |||
# two lines above could be combined into the second (dispatching on general AbstractVectors), but this (somewhat) more performant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this new line needed for ?
# two lines above could be combined into the second (dispatching on general AbstractVectors), but this (somewhat) more performant | |
# two lines above could be combined into the second (dispatching on general AbstractVectors), but this is (somewhat) more performant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many other kernels work on arrays of arrays, but the fbm kernels errors as it could not find a method for _mod
. I can try to recreate the example that I let me to add the line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That'd be great! Sounds like something that should be included in the test suite (maybe for all kernels?).
It also seems quite orthogonal to the matrixkernel
addition, in which case it'd be helpful if you could move that out to a separate branch/PR as well :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies for the delay, the error appears when doing
k = FBMKernel()
xs = [rand(5) for _ in 1:4]
kernelmatrix(k, xs)
which produces:
ERROR: LoadError: MethodError: no method matching _mod(::Array{Array{Float64,1},1})
This is needed during one of the matrixkernel
calls. I can segment the fix into a separate pull request, but it is needed for this one. It would be easier for me if it could just be added via this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's very tempting to just keep things together in a single PR from the author's point of view, but I do highly recommend splitting it up - then e.g. it's easier for different people to review the different (smaller) PRs, it's easier to say "ok I'll spend ten minutes reviewing a ten-line PR" instead of thinking "oh when will i ever find enough time to review a several hundred lines PR", and so on!
Co-authored-by: st-- <st--@users.noreply.github.com>
@@ -80,6 +80,13 @@ type enables specialised implementations of e.g. [`kernelmatrix`](@ref) for | |||
|
|||
To find out more about the background, read this [review of kernels for vector-valued functions](https://arxiv.org/pdf/1106.6251.pdf). | |||
|
|||
If you are interested in the matrix-kernel interpretation, Kernelfunction provides a convenience function that computes the resulting kernel for a pair of inputs directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are interested in the matrix-kernel interpretation, Kernelfunction provides a convenience function that computes the resulting kernel for a pair of inputs directly. | |
If you are interested in the matrix-kernel interpretation, KernelFunctions provides a convenience function that computes the resulting kernel for a pair of inputs directly. |
matrixkernel | ||
``` | ||
<!-- Add when exporting IsotopicOutputs --> | ||
<!-- One way to look at this is that applying `matrixkernel` pairwise to a list of inputs results in a block matrix, which when flattened is the same is the `kernelmatrix` computed when providing the same list of inputs as `MOInputIsotopicByOutputs`. --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"flattened" = "reshape from [N,N] array of [Q,Q] arrays to [NQ, NQ] array"? would it be worth being more explicit here?
@@ -15,6 +15,8 @@ | |||
@test repr(k) == "Fractional Brownian Motion Kernel (h = $(h))" | |||
test_ADs(FBMKernel; ADs=[:ReverseDiff]) | |||
|
|||
# ToDo: needs tests for _mod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not specifically for _mod
itself (though of course an equivalence of _mod([1, 2, 3]) == _mod([[1], [2], [3]])
would be good, but also for FBMKernel on higher-dimensional inputs
@@ -43,6 +43,8 @@ end | |||
_fbm(modX, modY, modXY, h) = (modX^h + modY^h - modXY^h) / 2 | |||
|
|||
_mod(x::AbstractVector{<:Real}) = abs2.(x) | |||
_mod(x::AbstractVector{<:AbstractVector{<:Real}}) = sum.(abs2, x) | |||
# two lines above could be combined into the second (dispatching on general AbstractVectors), but this (somewhat) more performant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's very tempting to just keep things together in a single PR from the author's point of view, but I do highly recommend splitting it up - then e.g. it's easier for different people to review the different (smaller) PRs, it's easier to say "ok I'll spend ten minutes reviewing a ten-line PR" instead of thinking "oh when will i ever find enough time to review a several hundred lines PR", and so on!
function kernelmatrix2(k::LinearMixingModelKernel, X, Y) | ||
K = [kernelmatrix(ki, X.x, Y.x) for ki in k.K] | ||
L = size(k.H, 2) | ||
return reduce( | ||
hcat, [reduce(vcat, [sum(k.H[:, i] .* (K .* k.H[:, j])) for i in 1:L]) for j in 1:L] | ||
) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ease of reviewing, it would be great if you could move the performance improvements into a separate PR!
function matrixkernel(k::MOKernel, x, y) | ||
return throw( | ||
ArgumentError( | ||
"This kernel does not have a specific matrixkernel implementation, you can call `matrixkernel(k, x, y, out_dim)`", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"This kernel does not have a specific matrixkernel implementation, you can call `matrixkernel(k, x, y, out_dim)`", | |
"For a $(nameof(typeof(k)), you must explicitly specify the requested output dimension: call `matrixkernel(k, x, y, out_dim)`", |
function _kernelmatrix_kron_helper(::MOInputIsotopicByFeatures, Kfeatures, Koutputs) | ||
return kron(Kfeatures, Koutputs) | ||
end | ||
|
||
function _kernelmatrix_kron_helper(::MOInputIsotopicByOutputs, Kfeatures, Koutputs) | ||
return kron(Koutputs, Kfeatures) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should not be part of this PR?
@@ -4,3 +4,31 @@ | |||
Abstract type for kernels with multiple outpus. | |||
""" | |||
abstract type MOKernel <: Kernel end | |||
|
|||
""" | |||
matrixkernel(k::MOKernel, x, y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
matrixkernel(k::MOKernel, x, y) | |
matrixkernel(k::MOKernel, x, y[, out_dim]) |
& explanation below?
@@ -45,7 +58,7 @@ function kernelmatrix(k::LatentFactorMOKernel, x::MOInput, y::MOInput) | |||
H = [gi.(x.x, permutedims(y.x)) for gi in k.g] | |||
|
|||
# Weighted latent kernel matrix ((N*out_dim) x (N*out_dim)) | |||
W_H = sum(kron(Wi, Hi) for (Wi, Hi) in zip(W, H)) | |||
W_H = sum(_kernelmatrix_kron_helper(x, Hi, Wi) for (Wi, Hi) in zip(W, H)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this work if x
and y
have different types?
# function matrixkernel(k::LatentFactorMOKernel, x, y) | ||
# return matrixkernel(k, x, y, size(k.A, 1)) | ||
# end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# function matrixkernel(k::LatentFactorMOKernel, x, y) | |
# return matrixkernel(k, x, y, size(k.A, 1)) | |
# end |
but would be great to check the implementation for correctness by comparing against this!
As suggested, I am splitting this again into a few smaller PRs, not in the least because in doing so smaller bits spawned more larger issues. |
Summary
Following up from #354 , this pull request contains the
matrixkernel
convenience function, some additional tests, a small fix, and performance improvements that came up along the way.Proposed changes
matrixkernel
MOInput
typesWhat alternatives have you considered?