-
Notifications
You must be signed in to change notification settings - Fork 190
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
Add Nx.covariance #1289
Add Nx.covariance #1289
Conversation
nx/lib/nx.ex
Outdated
opts = keyword!(opts, ddof: 0, mean: nil) | ||
|
||
if rank(tensor) != 2 do | ||
raise ArgumentError, "expected tensor of rank 2, got #{rank(tensor)}" |
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.
We should a least accept tensors of shape {..., m, n}
where the first k-2 dimensions are batch dimensions.
For the mean, I think we need to receive it as an argument, so we'd have:
def covariance(tensor), do: covariance(tensor, Nx.mean(tensor, axes: [-2]), [])
def covariance(tensor, opts) when is_list(opts), do: covariance(tensor, Nx.mean(tensor, axes: [-2]), opts)
def covariance(tensor, mean), do: covariance(tensor, mean, [])
def covariance(tensor, mean, opts) do
...
end
Although @josevalim will be able to say for certain if passing the mean as an option is really a problem.
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.
And you'd do mean = to_tensor(mean)
right after tensor = to_tensor(tensor)
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.
Yes, tensors must be arguments. And I would go with this signature:
def covariance(tensor, opts \\ [])
def covariance(tensor, opts) when is_list(opts), do: covariance(tensor, Nx.mean(tensor, axes: [-2]), opts)
def covariance(tensor, mean), do: covariance(tensor, mean, [])
def covariance(tensor, mean, opts) when is_list(opts) do
:)
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 got used to passing arguments as options to the point I forgot Elixir supports multiple-clause functions. 😅
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.
We should at least accept tensors of shape {..., m, n} where the first k-2 dimensions are batch dimensions.
@polvalente Just to clarify: if we take a batch of datasets as input (shape {..., n, d}
), the output should be a batch of covariances (shape {..., d, d}
), right?
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.
Yes
nx/lib/nx.ex
Outdated
rank(opts_mean) != 1 -> | ||
raise ArgumentError, "expected mean to have rank 1, got #{rank(opts_mean)}" | ||
|
||
size(opts_mean) != dim -> | ||
raise ArgumentError, | ||
"expected input tensor and mean to have same dimensions, got #{dim} and #{size(opts_mean)}" | ||
|
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.
We can skip these validations because subtract
should already apply them for us.
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.
One of the reasons why I validated the rank of the mean is because the user can pass a single number and then Nx will broadcast it when subtracting. Do we really want this? If the input tensor is of format n x d
, then I would prefer the user to hardcode a d-dimensional mean vector.
Agreed for the dimension.
There is a function that calculates this in |
@msluszniak I see. I would honestly prefer it in Nx, as the function is rather fundamental and not only necessary for machine learning algorithms. I also thought of using it for unit testing Lines 331 to 337 in f74fcad
|
@msluszniak I didn't know about that one. I believe we should deprecate that in favor of this one in Nx. We could reuse the implementation from Scholar. @krstopro I see you added a |
@polvalente @msluszniak Looking at it, but this one seems more general at the moment (e.g. works with batches and complex input). The main difference is in the way they handle bias. I prefer using Both implementations contain a bug though :) |
I think we should force the output tensor to have no names. So you can rename all axes of |
Yes, I think it's a good idea to use the implementation from |
Yeah, I guess I should do that. I still like the names on the batch axes though.
Agreed. |
The problem is that as we are reducing on a given axis, the semantics start to get fuzzy on what axes should be kept. |
Indeed, better remove all the names. |
Alright, changed the description and added unit tests for batch inputs. Might be ready to merge. |
Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com>
Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com>
Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com>
Co-authored-by: Paulo Valente <16843419+polvalente@users.noreply.github.com>
💚 💙 💜 💛 ❤️ |
Added
Nx.covariance/2
which estimates the covariance matrix from input tensor. Also edited the test for properties of multivariate normal distribution innx_test.ex
.Few things I am not sure about:
Nx.variance
.