-
-
Notifications
You must be signed in to change notification settings - Fork 250
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
Adding Multi-Task ElasticNet support #194
base: master
Are you sure you want to change the base?
Conversation
@YuhanLiin i'm implementing the My question is how to restrict the trait bounds to implement the |
Is it possible to make functions like |
@YuhanLiin Thanks for all the remarks! I passed your comments on the latest commit. As for making
For |
for i in 0..x.shape()[0] { | ||
for t in 0..n_tasks { | ||
r[[i, t]] += x_j[i] * old_w_j[t]; | ||
} | ||
} |
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.
Isn't this operation equivalent to r += x_j.dot(old_w_j.t())
? If so you can replace these types of for loops with general_mat_mul(1, x_j, old_w_j.t(), 1, r)
.
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 operation is equivalent to np.outer
in Python (see: https://numpy.org/doc/stable/reference/generated/numpy.outer.html for a more detailed explanation). I don't think there is a built-in equivalent in ndarray. It is not yet available in the ndarray
crate (see: rust-ndarray/ndarray#1148) .
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.
outer(x, w)
is equivalent to x
as a column vector multiplied with w
as a row vector. The code in the ndarray
PR you linked does the same thing. Using general_mat_mul
allows you to add the matrix product of x
and w
to r
in one operation. You do need to convert x
and y
into 2D arrays though, like in the 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.
Ideal way to convert 1D arrays into 2D is insert_axis
. Something like x.view().insert_axis(Axis(0_or_1))
Making |
let norm_cols_x = x.map_axis(Axis(0), |col| col.dot(&col)); | ||
let mut gap = F::one() + tol; | ||
let d_w_tol = tol; | ||
let tol = tol * y.fold(F::zero(), |sum, &y_ij| sum + y_ij.powi(2)); |
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.
Instead of fold
you can do y.iter().map(|x| x*x).sum()
. Since iter
is called before map
it won't create a new array. For 1D arrays it's even simpler since you can just call y.dot(&y)
to dot product y
with itself.
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.
You should also apply this change to all the other similar fold
calls
gap | ||
} | ||
|
||
fn variance_params<F: Float + Lapack, T: AsTargets<Elem = F>, D: Data<Elem = F>, I: Dimension>( |
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 AsTargets<Elem = F>
is very complex to deal with. I can't do much with it, since I need to have an ArrayBase
in order to call ndim()
, shape()
and to make the computation target - y_est
. Do you know how I can circumvent this issue? I don't understand the need for an AsTarget
trait in the first place. At least it should support multi-task targets.
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.
Actually it should have some way to retrieve the dimension of the targets.
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.
Currently AsTargets
has the method as_multi_targets
, which returns a 2D array view, so you can call it to retrieve the target for both cases. For the single target case this returns an array of dimension (n, 1). This means your code needs to treat single-task and multi-task-with-only-one-task as equivalent cases. y_est
will need to be a 2D array in all cases; for single-task just insert Axis(1)
into y_est
.
After your other PR, this should be bounded with AsMultiTargets
(now that I think about it, we need AsMultiTargets
as a super-trait of AsSingleTarget
for this to work).
@@ -429,7 +426,7 @@ fn duality_gap<'a, F: Float>( | |||
} else { | |||
(F::one(), r_norm2) | |||
}; | |||
let l1_norm = w.fold(F::zero(), |sum, w_i| sum + w_i.abs()); | |||
let l1_norm = w.map(|w_i| w_i.abs()).sum(); |
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.
Use dot
let w_norm2 = w.fold(F::zero(), |sum, &wij| sum + wij.powi(2)); | ||
let r_norm2 = r.map(|rij| rij.powi(2)).sum(); | ||
let w_norm2 = w.map(|wij| wij.powi(2)).sum(); |
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.
Call iter()
before map
to prevent creating a new array
Since #206 has been merged, ElasticNet is now easier to adapt to the multi-task case. I'm still working on it.
|
Work continued in #238 |
The goal of this PR is to add multi-task ElasticNet to the
elasticnet
crate.A quick roadmap: