Skip to content
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

[WIP] RFFGaussPreproc #5010

Merged
merged 2 commits into from
Jun 10, 2020
Merged

[WIP] RFFGaussPreproc #5010

merged 2 commits into from
Jun 10, 2020

Conversation

jonpsy
Copy link
Contributor

@jonpsy jonpsy commented Apr 19, 2020

Addressing #5009

@jonpsy
Copy link
Contributor Author

jonpsy commented Apr 19, 2020

Proposal: Modify set_randomcoefficients
Reason: It takes in lots of parameters and set internal members value to that.
Rather
a) Take const ref and extract values from it
b) set each internal member value manually, there aren't many.

Also, rename to extract_randomcoefficients because it takes values from the arguments and set internal values from there and not vice versa.

EDIT: Implemented it, see the updated PR. Yet to remove is_updated

randomcoeff_additive, randomcoeff_additive + cur_dim_feature_space, 0.0,
2 * pi, m_prng);
m_randomoffset, m_randomoffset + m_dim_feature_space, 0.0,
2 * M_PI, m_prng);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is M_PI coming from? Use the one in std::

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

M_PI is from the cmath include, luckily it's already included it so it all blends in perfectly. We don't have std::pi. We can, however, use const double pi = std::acos(-1); but that's a waste of LoC and even it's precision is lesser than M_PI:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karlnapf C++ library doesn't have pi definition, it relies on cmath. The best we could do is static constexpr double PI = 3.1415... or have an overloaded struct shogun::constants<float>::pi, shogun::constants<double>::pi and so on. @nanusai avoid calculating constants inside an algorithm, make such things static and constexpr if possible

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also calculating why calculate pi if it is a known constant?

Copy link
Contributor Author

@jonpsy jonpsy Apr 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be a misunderstanding, I already am using M_PI. I was just explaining why I was using it.Anyway I guess we all agree on M_PI ;D.

EDIT: Good news, we've already Math::PI inside our shogun::Math

const float64_t Math::PI=M_PI;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want to drop that class eventually, but I guess you can use that for now.

@gf712 shall we put the constexpr in linalg maybe?

@@ -1325,6 +1325,39 @@ namespace shogun
return result;
}

/** Performs the operation B = cos(A)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noooooo
You would cherry pick your other commit instead of force pushing this into here. This just wastes CI cycles.
git is a great tool and it can avoid exactly those situations

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now (with the other PR merged), you need to force push here again. If you had cherry picked the commit, this wouldnt have been necessary (git would have worked it out for us)
next time :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still needs to be removed


SGMatrix<float64_t> res(cur_dim_feature_space, num_vectors);
linalg::matrix_prod(m_basis,matrix,projection);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

SGVector<float64_t> res(cur_dim_feature_space);
assert_fitted();
linalg::matrix_prod(m_basis, vector, projection);
linalg::add(projection, m_offset, projection);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok one more thing. Sorry!
But this code simply replicates the matrix code. Why have it twice? It is pretty much copy pasted ....

You can just call the matrix code from here. All you need to do is to make a matrix from the input vector
return apply_to_feature_matrix(SGMatrix<...>(vector)).get_column_vector(0) (double check the details ... )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you would have to make the column vector persistent somehow as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

cur_dim_input_space, false);
res(od, vec) =
val * cos(randomcoeff_additive[od] + linalg::dot(a, b));
auto width = std::exp(m_log_width * 2.0) * 2.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto width = std::exp(m_log_width * 2.0) * 2.0;
const auto width = std::exp(m_log_width * 2.0) * 2.0;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure the compiler will optimise this, but you might be better off computing this outside the loop?

Copy link
Contributor Author

@jonpsy jonpsy Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler should optimize variable declaration inside the loop. This is also a good practice I've heard because this limits the scope of variable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure? because m_log_width could be changed in another thread, so how can the compiler assume this will always be the same?

Copy link
Contributor Author

@jonpsy jonpsy Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you do have a point. i guess putting the width inside loop doesn't make too much sense either. Okay then

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw I think the optimisations it could do here is reuse the memory allocation on the stack and then the CPU itself can see that you are recomputing the same all the time and put the result in the hot path. If m_log_width was const, then I think the compiler would compute the value only once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

making data members as const would render the setter useless, wouldn't it? I've put width outside the loop, and I think that's for better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am just telling you when there could be optimisations. You could imagine copying it to a const variable to test it out. But yes, you should put it outside the loop to be sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. No more loops btw :)


for (auto row : range(m_dim_output))
m_offset[row] = uniform(m_prng);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is exactly the use case for something like std::transform, which will give you better performance

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

require(num_features > 0, "Input space dimension must be greater than zero");

init_basis(num_features);
m_fitted = true;
}

SGVector<float64_t> RandomFourierGaussPreproc::apply_to_feature_vector(SGVector<float64_t> vector)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you are refactoring this can you change the signature to const SGVector<float64_t>& please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this signature is inherited, would have to change a lot of files for it to work. thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I guess let it be for another PR then :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, there will be a follow up refactoring the whole class structure.

}

SGMatrix<float64_t>
RandomFourierGaussPreproc::apply_to_matrix(SGMatrix<float64_t> matrix)
SGMatrix<float64_t> RandomFourierGaussPreproc::apply_to_matrix(SGMatrix<float64_t> matrix)
Copy link
Member

@gf712 gf712 Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const SGMatrix<float64_t>&

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marked as resolved, but not resolved.
@jonpsy PLEASE dont mark anything as resolved, especially not the ones that are not changed. If you want to do it, do it AFTER you have pushed changes .... but better yet, just leave those comments in, they give us a good feeling of what happened and people can hide them if they want

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, this is for another PR as well.... so next time, just state that rather than hiding it ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not "hiding" per se, we've had VERY similar discussion above if you recall. So to avoid repeating same question..i did that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry i dont mean "hiding" in the sense that you are hiding stuff from us, but rather the label of the button :)
Again, please just don't click it, it causes confusion and takes away time from other stuff.
In a situation like this, just say "will do in next PR when we refactor the interfaces of the base classes". And then it is there and everyone immediately knows what is going on, and if I dont want to see it (or you) everyone can just hide it personally

[this, &normal_dist, &width]() {
auto coeff = std::sqrt(2.0 / width) * normal_dist(m_prng);
return coeff;
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks much nicer in my opinion! And it should reduce cache misses. Minor thing, why don't you just return the right hand side of coeff?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw why not store the full multiplier outside of the loop, including std::sqrt(2.0 / width) ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that could be done. In this situation I would expect a good compiler to just compute it once, because width is const. But to be sure can just compute this outside the lambda

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++


return res;
return static_cast<SGVector<float64_t>>(
apply_to_matrix(static_cast<SGMatrix<float64_t>>(vector)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this actually work and is safe?
@gf712 I didnt know we can just cast these types back and forth ....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

especially the cast from SGMatrix->SGVector ... what if there are more than one column in the matrix (it wont be here) is this checked somewhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought doing this via a constructor would be better/safer (as said in irc @jonpsy )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will just call

SGMatrix(SGVector<T> vec);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and

SGVector(SGMatrix<T> matrix);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just learned that static_cast in fact calls the ctor. Nevermind then :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry my inet lagged and didnt see your reply. Cool!

@karlnapf
Copy link
Member

karlnapf commented Jun 9, 2020

the CI tests still fail?

@gf712
Copy link
Member

gf712 commented Jun 9, 2020

the CI tests still fail?

yes, the data file has to be updated, because @jonpsy switched the random number matrix generation from row to column major (to reduce cache misses). He just has to amend the data commit right?

@karlnapf
Copy link
Member

karlnapf commented Jun 9, 2020

ah of course, yes!
BTW imo there really should be a method that fills random arrays in this way, that takes care of the row/column major, to prevent people shooting into their feet ...

@gf712
Copy link
Member

gf712 commented Jun 9, 2020

BTW imo there really should be a method that fills random arrays in this way, that takes care of the row/column major, to prevent people shooting into their feet ...

But we would just be wrapping around std::generate? I mean we could potentially extend fill_array with an overload for fill_array(matrix, generator)/fill_array(matrix.begin(), matrix.end(), generator)

@karlnapf
Copy link
Member

karlnapf commented Jun 9, 2020

maybe you are right, just thinking about how many times I asked someone to use std::generate or std::transform rather than a loop, and how many times people don't respect linearity in memory by for example iterating row-wise....


return false;
RandomFourierGaussPreproc::RandomFourierGaussPreproc()
: m_log_width(1.0), m_dim_output(100)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the effective kernel width should be 1
Also, could you set these values at the declaration

NormalDistribution<float64_t> normal_dist;
auto sampled_kernel = SGMatrix<float64_t>(m_dim_output, dim_input_space);
const auto width = std::exp(m_log_width * 2.0) * 2.0;
const auto factor = std::sqrt(2.0 / width);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could name this std_dev, but it doesnt really matter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

factor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, as it is the standard deviation of the gaussian you sample from below

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. factor -> std_dev ... but only if your nerves allow ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowed :)

{
NormalDistribution<float64_t> normal_dist;
auto sampled_kernel = SGMatrix<float64_t>(m_dim_output, dim_input_space);
const auto width = std::exp(m_log_width * 2.0) * 2.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gna I oversaw this one earlier. You should use get_width here to avoid double code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(try to never copy paste code)

@karlnapf
Copy link
Member

karlnapf commented Jun 9, 2020

@gf712 if you don't have any further concerns, I will merge this beast ...

@gf712
Copy link
Member

gf712 commented Jun 10, 2020

@gf712 if you don't have any further concerns, I will merge this beast ...

sounds good, I think this is ready to be merged! Great work @jonpsy :)

@karlnapf karlnapf merged commit 9158d3f into shogun-toolbox:develop Jun 10, 2020
@jonpsy jonpsy deleted the RFFPrepoc branch June 10, 2020 09:24
karlnapf pushed a commit that referenced this pull request Jun 24, 2020
* add RFGPbenchmark
* add fit benchmark
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants