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

Minimize Neural Layer Unit Test Redundancies #4424

Conversation

saatvikshah
Copy link
Contributor

@saatvikshah saatvikshah commented Nov 25, 2018

I've currently made changes for the RectifiedLinearLayer and the Softmax layer. Since its my first task, I wanted to be sure these changes look alright, before fixing the remaining Neural*Layer tests.

Addresses #4233

I could probably also make the fixes needed to address #4232 as part of this PR if thats alright with you'll?

.gitignore Outdated
@@ -291,6 +291,10 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk

# CMake specific
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, this would be CLion specific will fix.

Copy link
Member

Choose a reason for hiding this comment

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

we usually don't add such specifics to the gitignore. Devs do that locally

Copy link
Member

@karlnapf karlnapf left a comment

Choose a reason for hiding this comment

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

Thanks, this is a really useful cleanup! We really appreciate you getting into this.

I made a few comments that should be discussed/changed. We can then iterate, do a few changes, and merge it.

Thanks again!

.gitignore Outdated
@@ -291,6 +291,10 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk

# CMake specific
Copy link
Member

Choose a reason for hiding this comment

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

we usually don't add such specifics to the gitignore. Devs do that locally

protected:
void SetUp()
{
CMath::init_random(100);
Copy link
Member

Choose a reason for hiding this comment

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

can't we move way more of the shared code into the fixture?

@@ -55,4 +58,44 @@ SGStringList<T> generateRandomStringData(
return strings;
}

struct NeuralLayerTestUtil
Copy link
Member

Choose a reason for hiding this comment

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

this code is so specific, I suggest we move it into a helper file within neuralnets

struct NeuralLayerTestUtil
{
template <typename T>
static SGMatrix<T> create_rand_sgmat(
Copy link
Member

Choose a reason for hiding this comment

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

I think this is not a function we should have in neuralnet unittests.
Rather, this should be somewhere where other random number generators sit, like Random.h or something more appropriate. Random normal matrices are used in a lot of places in shogun

auto data_batch = SGMatrix<T>(num_rows, num_cols);
for (int32_t i = 0; i < data_batch.num_rows * data_batch.num_cols; i++)
{
data_batch[i] = CMath::random(lower_bound, upper_bound);
Copy link
Member

Choose a reason for hiding this comment

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

actually, there should also be a more efficient way to general an array of random normals. Check the array filling procedures in Random.h

{
layer->initialize_neural_layer(layers, input_indices);
SGVector<float64_t> params(layer->get_num_parameters());
SGVector<bool> param_regularizable(layer->get_num_parameters());
Copy link
Member

Choose a reason for hiding this comment

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

reg_params

SGMatrix<float64_t> x;
CNeuralInputLayer* input;
std::tie(x, input) =
NeuralLayerTestUtil::create_rand_input_layer<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.

it is nice to pull this kind of stuff out!


using namespace shogun;

class NeuralSoftmaxLayerTest : public ::testing::Test
Copy link
Member

Choose a reason for hiding this comment

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

this fixture is almost identical to the other one. Is there a reason to have this second one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep will do so - I'll add a common fixture with the utility functions in NeuralLayerUtil right inside the unit/neuralnets folder

@saatvikshah
Copy link
Contributor Author

saatvikshah commented Nov 26, 2018

I had 2 C++ in Shogun related questions:

  1. Upto what version of C++ is permitted: Edit: Resolved - its C++14
  2. Is there an option to run the tests with Address Sanitizer turned on?

@saatvikshah
Copy link
Contributor Author

Addressed all your comments.
P.S. I ran clang-format-3.8 directly(the check-format.sh script doesnt work on my Macbook) which ended up changing some additional lines.

@karlnapf
Copy link
Member

Ok cool, let me check this

{
public:
template <typename T>
SGMatrix<T> create_rand_sgmat(
Copy link
Member

Choose a reason for hiding this comment

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

see my earlier comment: this is not nn specific but should be a global shogun option

#include <shogun/lib/SGMatrix.h>
#include <shogun/lib/SGStringList.h>
#include <shogun/lib/SGVector.h>
#include "shogun/lib/SGMatrix.h"
Copy link
Member

Choose a reason for hiding this comment

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

why this?

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 believed that its standard convention - system files <> vs. project files "". Mainly useful since it helps differentiate project includes vs. system includes.
I can revert if Shogun follows a different convention regarding this.

I had made that change earlier when I had added NeuralLayerUtil. Decided to leave that in since I felt its a style improvement.

Copy link
Member

Choose a reason for hiding this comment

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

yes but in order to compile the shogun tests we add the include path via -I so it by definition is a system include. Check out the other headers ... we are doing this everywhere :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh ok, thanks for clarifying. I should also reverse the "" includes I've done in the other classes then.
Ill just revert my changes on this file then. If needed even the #pragma once can be added later.

@@ -3,12 +3,11 @@
*
* Authors: Giovanni De Toni, Heiko Strathmann
*/
#ifndef __UTILS_H__
#define __UTILS_H__
#pragma once
Copy link
Member

Choose a reason for hiding this comment

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

we will need to discuss whether we allow this...
@vigsterkr @lisitsyn ?

int32_t num_rows, int32_t num_cols, T lower_bound, T upper_bound) const
{
auto data_v = SGVector<T>(num_rows * num_cols);
data_v.random(lower_bound, upper_bound);
Copy link
Member

Choose a reason for hiding this comment

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

e.g. we could have a random method in SGMatrix (because we have one in vector already)
this code is a bit too much noise for my taste, especially in a test

Copy link
Member

@karlnapf karlnapf left a comment

Choose a reason for hiding this comment

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

This is almost good to go. Nice work! :)
Just the one comment about random matrices.
I would suggest to add SGMatrix::random, and then do an optional elementwise multiplication using linalg

@saatvikshah saatvikshah force-pushed the feature/neural_layer_test_redundancies branch from 957bb76 to 1edd4cb Compare December 2, 2018 04:01
@saatvikshah
Copy link
Contributor Author

Addressed comments - Further reduced a number of redundancies - Added shogun::linalg ops wherever possible.

template <class T>
void SGMatrix<T>::random(T min_value, T max_value)
{
for (index_t idx = 0; idx < num_rows * num_cols; ++idx)
Copy link
Member

Choose a reason for hiding this comment

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

pls, instead of a loop, use the CRandom::fill_array* functions that in some situations can be much much faster

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 was making it consistent with SGVector but sure - yes I noticed it uses SIMD instructions.
I think SGVector::random should be changed too accordingly - not sure whether should be done in this or a different PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's two catches here here though:

  1. SGMatrix::random should support the following types: int32/64; uint32/64,float32/64/128. fill_array* doesnt support int32/64 and float32/128. We will have to default back to the for loop implementation above for these types.
  2. The uint32/64 returns a random 32 or 64-bit integer(not within any range). The float64 returns a float between [0, 1). Additional processing steps will be needed to scale them to [min, max]. Finally, the uint version only vectorizes random number generation if certain strict conditions are met, else it falls back to a for loop.

Copy link
Member

Choose a reason for hiding this comment

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

  1. yes you are exactly right, this needs template specialization, and a loop fallback is fine imo
    Let's do it for SGMatrix::random in this PR
  2. I suggest we keep the interface without arguments, i.e. (SGMatrix::random() and SGMatrix::random_normal), and then the caller is responsible for scaling things to the appropriate bounds, using linalg

very nicely thought trough! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, though I'd like to clarify (2) again. I get the SGMatrix::random() function which would just fill the array with a uniform random distribution(using fill_array*). For random_normal I would need to further apply something like the Box-Muller transform right possibly using shogun::linalg ops.

Copy link
Member

Choose a reason for hiding this comment

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

@saatvikshah1994
we have two options here:

  1. Make a loop over the CRandom::random_normal insice `SGMatrix::random (since CRandom doesn't support SIMD random normal generation
  2. Implement a random_normal_array which
    a. Has a default implementation with a loop for now
    b. First uses fill_array and then re-uses the box muller trainsform code that is already present (would need some refactoring)

I suggest we keep things simple for now:

  • offer CRandom::random_normal_array and use a loop for now
  • if you want: make another PR once this is merged where you do option 2b from above

We definitely only want SGMatrix::random_normal to be a wrapper, i.e. no transforms or other complicated things in there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@karlnapf thanks this makes sense.
I had one suggestion though - I feel that the entire random() and random_normal() impl should go in its own separate (issue + PR) combo. Reason being that (1) the implementation is less related to the immediate issue of minimizing the test redundancies and (2) the changes should ideally be propagated to SGVector as well - overall then a significant portion of this PR would mix up two disparate issues. For this PR, I could just use the original NeuralLayerFixture::create_rand_matrix which would then be replaced in the second PR.

I'm up for whichever way you lean towards.

Copy link
Member

@karlnapf karlnapf left a comment

Choose a reason for hiding this comment

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

Apart from the loop in random, this is ready to be merged

}
}
shogun::linalg::add_vector(
shogun::linalg::matrix_prod(weights, x), biases, A_ref);
Copy link
Member

Choose a reason for hiding this comment

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

you could store this in a variable as it is not optimized out or anything anyways

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 sure what you mean? add_vector itself stores its result in the position denoted by A_ref and returns void.

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 just mean to not nest linalg calls for readability. minor

#include <shogun/neuralnets/NeuralInputLayer.h>
#include <shogun/neuralnets/NeuralSoftmaxLayer.h>

#include <memory>
Copy link
Member

Choose a reason for hiding this comment

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

do you use anything from this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no will remove

@karlnapf
Copy link
Member

karlnapf commented Dec 5, 2018 via email

@saatvikshah saatvikshah mentioned this pull request Dec 6, 2018
@saatvikshah saatvikshah force-pushed the feature/neural_layer_test_redundancies branch from 9011c8e to d2e478e Compare December 6, 2018 04:30
protected:
void SetUp() final
{
CMath::init_random(SEED);
Copy link
Member

Choose a reason for hiding this comment

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

do we need to set the seed? would prefer not to (portability)

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 clear to me why this makes it less portable? I kept it to ensure consistency on each run + it was being done earlier :). You've approved the PR so should I change it still?

Copy link
Member

Choose a reason for hiding this comment

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

Different environments -> different random numbers for the same seed

If all the tests pass without it, I would prefer without ... let me know.

unit tests are one binary, so if you change the state of libshogun, all other tests might be affected. Even if not, it is better practice to only ensure something if it is needed.

Yes, all approved otherwise! :)

Copy link
Member

@karlnapf karlnapf left a comment

Choose a reason for hiding this comment

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

Looks pretty good to me!
Thanks for this, really useful

@saatvikshah
Copy link
Contributor Author

Once this is merged, I'll need to sift through the remaining Neural*Layer tests and apply similar changes.

@karlnapf
Copy link
Member

karlnapf commented Dec 6, 2018

Once this is merged, I'll need to sift through the remaining Neural*Layer tests and apply similar changes.

great!

Copy link
Member

@karlnapf karlnapf left a comment

Choose a reason for hiding this comment

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

Ah sorry, there is one thing: you need to rebase against develop, since you have a data submodule commit in there. See the first changed file in the diff

Suggested improvements + linalg usage

Addressed review comments

Remove random seed
@saatvikshah saatvikshah force-pushed the feature/neural_layer_test_redundancies branch from 9d148fc to 7b5dcc4 Compare December 8, 2018 05:14
@karlnapf karlnapf merged commit 22d3861 into shogun-toolbox:develop Dec 8, 2018
@karlnapf
Copy link
Member

karlnapf commented Dec 8, 2018

Thanks for this.
Please keep an eye in the [CI}(https://dev.azure.com/shogunml/shogun/_build?definitionId=2) and the buildbot. If anything breaks that wasn't broken before, we should fix it first thing.

@saatvikshah
Copy link
Contributor Author

The CI output looks correct but the buildbot seems to have failed here: http://buildbot.shogun-toolbox.org:8080/#/ on multiple machines although the errors reported seem arbitrary.

@karlnapf
Copy link
Member

karlnapf commented Dec 8, 2018

Yep CI is indeed fine. Buildbot, just check the builder list for cases that were green until we merged this PR (last build). They are all fine. The broken builds need fixing, most of them at least, with the recently broken ones having a higher priority. It is usually a tiny bit of work to figure out what causes errors, but we usually have logs to see which commit broke them.

vigsterkr pushed a commit to vigsterkr/shogun that referenced this pull request Mar 9, 2019
* Fixed redundancies in RectifiedLinear and Softmax Layer unit tests
* Add common fixture class + formatting
* Remove random seed
ktiefe pushed a commit to ktiefe/shogun that referenced this pull request Jul 30, 2019
* Fixed redundancies in RectifiedLinear and Softmax Layer unit tests
* Add common fixture class + formatting
* Remove random seed
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

2 participants