[GSoC] Augmented RNN models - benchmarking framework#1005
[GSoC] Augmented RNN models - benchmarking framework#1005zoq merged 66 commits intomlpack:masterfrom
Conversation
|
Can one of the admins verify this patch? |
| } | ||
|
|
||
| template<typename ModelType> | ||
| double CopyTask::Evaluate(ModelType& model) { |
There was a problem hiding this comment.
Now that I see the interface, I see a couple of problems that we have to discuss:
Right now the way we train a model e.g. RNN or LogisticRegression, etc. is we define all necessary model parameters (layer, regularization constant, etc.) and call the Train function. The Train takes the predictor and responses as input followed by an optional optimizer instance where we define the number of iterations/epochs, the threshold to terminate the optimization before we reach the maximum number of iterations, etc..
arma::mat predictor, responses;
Model model(...);
Optimizer optimizer(...);
model.Train(predictor, responses, optimizer);The Evaluate function somehow replicates the functionality of the optimizer like the number of epochs. I'm not sure we should replicate the behavior since the way we train a model might be different from class to class. Like none of the existing classes can handle arma::field as input. There is no option to specify optimizer parameter, or what if we like to use the upcoming cross-validation feature as part of the evaluation pipeline. So I think instead of providing an evaluation function that takes a model and trains it we should see the Tasks as a generator that generates input/output samples for our model to train on. This enables us to train the model independently from the task. I guess besides the code replication another problem is I can't think of an easy solution to incorporate curriculum learning which might be necessary to get stable results into the class without providing a bunch of parameters for each task. So I propose the following:
CopyTask
{
public:
CopyTask(...) {}
void Sample(arma::mat& predictor, arma::mat& responses)
{
// Generate predictor responses pair or pairs with the given parameter.
}
}
CopyTask copyTask;
copyTask.Sample(predictor, responses);
model.Train(predictor, responses, optimizer);
Let me know what you think.
There was a problem hiding this comment.
This sounds like a good idea to me, because I've already ran into similar issue when trying to implement SortTask - it was a huge copy-paste from CopyTask, which failed to work nonetheless 😢.
However, I would also propose to create one more header file with evaluation functions (sklearn-ish), like this:
#include <score.hpp>
auto score_fn = scorers::sequence_precision; // number of sequences model has got right.
// As an option:
// auto precision = scorers::bit_precision;
// = number of dataset bits model has got right.
// ... CopyTask defined as in the previous comment
copyTask.Sample(predictor_train, responses_train);
copyTask.Sample(predictor_test, responses_test);
arma::mat responses_test_pred; // or whatever type is required;
model.Train(predictor_train, responses_train, optimizer);
model.Predict(predictor_test, responses_test_pred);
auto score = score_fn(responses_test, responses_test_pred);IMHO, this will be even more in accord with the concern separation principle.
At the moment, I'm at an internship at our local Web-development company, though, so I hope to try out the ideas in this thread tonight or in the next few days.
There was a problem hiding this comment.
It's just an idea but do you think since the score function depends on the task we should integrate the function into the Generator/Task class?
There was a problem hiding this comment.
Maybe it is true, but I am not sure that the score function is the part of the problem. IMHO the task should specify only inputs and their respective outputs. The reason is that there are usually several viable ways to evaluate the distance between predictions and ground truths (e.g., in regression, you can use Euclidean / Manhattan metrics, use L1 / L2 regularizer with different degrees of regularization).
Even here, in the case of CopyTask, there are two plausible ways to evaluate the performance: bit precision and sequence precision.
There was a problem hiding this comment.
That's true, there are often multiple ways to calculate the performance, and I think there are situations where you like to use the same functions during training, so I agree writing a new class/classes for the scoring functions is a good idea.
|
Wow, it was unreasonably buggy coding from my side, but it looks like I've finally got it right :) So now, if I correctly remember the schedule, all it takes for Week 1 is to implement the data generator for |
| * @param maxLength Maximum length of sequence that has to be repeated by model. | ||
| * @param nRepeats Number of repeates required to solve the task. | ||
| */ | ||
| CopyTask(int maxLength, int nRepeats); |
There was a problem hiding this comment.
I would use const size_t maxLength and const size_t nRepeats here, we mostly use size_t instead of int if support of negative values isn't needed.
| * @param labels The variable to store output sequences. | ||
| * @param batchSize The dataset size. | ||
| */ | ||
| void GenerateData( |
There was a problem hiding this comment.
Minor style issue, please take a look at the Method Declarations sections in the style guide: https://github.com/mlpack/mlpack/wiki/DesignGuidelines#method-declarations.
| private: | ||
| // Maximum length of a sequence. | ||
| int maxLength; | ||
| // Nomber of repeats the model has to perform to complete the task. |
There was a problem hiding this comment.
Minor spelling issue; Numberinstead of Nomber.
| int nRepeats; | ||
| }; | ||
| } | ||
| } |
There was a problem hiding this comment.
Can you add a comment for the closing namespace here, to improve the readability?
| maxLength(maxLength), | ||
| nRepeats(nRepeats) | ||
| { | ||
| // Just storing task-specific paramters. |
There was a problem hiding this comment.
Minor spelling issue: parameters instead of paramters.
| ) { | ||
| input = arma::field<arma::irowvec>(batchSize); | ||
| labels = arma::field<arma::irowvec>(batchSize); | ||
| for (int i = 0; i < batchSize; ++i) { |
There was a problem hiding this comment.
Can you take a look at the Brace Placement section: https://github.com/mlpack/mlpack/wiki/DesignGuidelines#brace-placement. I wonder why the Style Check doesn't complain about this.
| input = arma::field<arma::irowvec>(batchSize); | ||
| labels = arma::field<arma::irowvec>(batchSize); | ||
| for (int i = 0; i < batchSize; ++i) { | ||
| size_t size = maxLength; |
There was a problem hiding this comment.
I think we can remove size and just use maxLength, what do you think?
There was a problem hiding this comment.
Maybe we can use random value of size, so that we can feed variable-length data to our models?
There was a problem hiding this comment.
Interesting idea, but I'm not sure we should do that inside the Generator class, since the required distribution depends on the user? If someone likes inputs with another length he could just call with another length right? Also, maybe it's a good idea, to use arma::mat instead of arma::field, since this way if someone likes to train only with a static length, he can directly use the output, since the model takes arma::mat as input.
| labels = arma::field<arma::irowvec>(batchSize); | ||
| for (int i = 0; i < batchSize; ++i) { | ||
| size_t size = maxLength; | ||
| arma::irowvec item = arma::randi<arma::irowvec>(size, arma::distr_param(0, 1)); |
There was a problem hiding this comment.
I think we can avoid the extra copy here and use input(i) instead of `ìtem``, maybe I missed something?
Not sure what evaluation method you mean here, can you explain further? |
I mean that even though other tasks are still evaluated by sequence precision, |
|
Thanks for the clarification, now I get what you mean 👍 In a previous comment you said you are currently at an internship, does the internship end this week or next week or something else? Just wanted to make sure you are able to work full-time on the project. |
Yes, both the internship and the entrance exam to Yandex School of Data Analysis have recently wrapped up (exam, the last event so far, was held on Sunday). So from here, I am able to work full-time. |
|
Thanks again for the clarification, the course looks interesting: https://yandexdataschool.com/edu-process/program/data-analysis not sure which one you did, but I guess every course is somehow interesting, at least the description looks promising. Hopefully, your exam went well. Just a reminder, would be great to see the first weekly report today or tomorrow. |
|
Fixed the style issues you've mentioned in the review, now switching to the other two tasks. By the way, what's up with Jenkins? It's being much more severe that it used to be.
That (Data Analysis) is precisely the program I've applied to :) However, I'm also going to take on some courses from Big Data program (e.g., parallel computation course - when you know that LAPACK is supreme to just writing |
|
The Jenkins Style Checks only checked files in mlpack/core now it also checks files in methods and tests. I fixed almost all issues that are not related to any PR here: #1019, so realistically you should only look at the files you changed and try to fix the issues pointed out by cpplint.
Right, I think in mlpack's context switching OpenBLAS with MKL would also result in some noticeable performance improvements at least in some situations. |
|
Travis CI has also got crazy - now |
|
Sometimes test fail because they e.g. didn't reach the expected error in the specified number of iterations using the given starting point, for more information take a look at #922 Realistically, you don't have to worry about failing test that are not related with your code. |
|
Now I have a problem I can't easily solve. I need to generate a random N*5 matrix (N 5-bit numbers). However, when I use I also tried setting manually all values to numbers from RNG (the current implementation), but it has the same problem. How to fix it? |
|
using: arma::Mat<size_t> output = arma::randi<arma::Mat<size_t>>(10, 5, arma::distr_param(0, 1));I get: Isn't that what you expected? |
Yes, but this one didn't work either :( Speaking of this, can I use Speaking about the weekly report, I think it would be better to end tomorrow third task + fix the mentioned bug and then (once again, tomorrow) write the complete report on Week 1. What do you think? |
|
So, do you mean my output is what you like, but it's not working for you? If that's the case, can you reduce the problem to a simple case? Easier to debug the problem. Sending the weekly report tomorrow, sounds fine for me. |
Yes, I was trying to achieve your output. I have resolved the problem - it was due to my really stupid bug in debug output producing. I have also implemented After that, I'm planning to write the Week 1 report. Then (according to our schedule) we are switching to some real (but not augmented) models - I hope to implement LSTM baseline and record its scores. |
|
Whoops, my bad, there were only my errors. Trying to fix them. |
|
Finally a clear style-check run :) |
Sounds good, I think we could also compare against GRU (once it's merged), without changing a lot of code. |
|
AppVeyor is running for a really long time. How can I dequeue my previous commits from here: https://ci.appveyor.com/project/mlpack/mlpack/history UPDATE They have just been dequeued. Thanks ;) |
|
I'm not sure if you are allowed to cancel your job at the moment, you could login into appveyor and see if you do, but in the meantime let me do it for you. |
| class AddTask | ||
| { | ||
| public: | ||
| AddTask(size_t bitLen); |
There was a problem hiding this comment.
Can you comment each parameter as we did for the other methods? Also, I think it would be good idea, to improve the general task description e.g. we could a simple example how to use the code and show an example output. What do you think?
| public: | ||
| AddTask(size_t bitLen); | ||
|
|
||
| void GenerateData(arma::field<arma::irowvec>& input, |
There was a problem hiding this comment.
Do you think it would be cleaner to rename the function to Generate?
| * @param maxLength Maximum length of sequence that has to be repeated by model. | ||
| * @param nRepeats Number of repeates required to solve the task. | ||
| */ | ||
| CopyTask(size_t maxLength, size_t nRepeats); |
There was a problem hiding this comment.
Can you use the const qualifier here const maxLength and const nRepeats.
| * @param labels The variable to store output sequences. | ||
| * @param batchSize The dataset size. | ||
| */ | ||
| void Generate(arma::field<arma::irowvec>& input, |
There was a problem hiding this comment.
The data should be col major, also I was thinking that even if the data is binary, it might be good to use arma::col instead of `àrma::icolhere since the most of the models takearma::mat`` as input, and that way we could avoid a cast. What do you think?
| */ | ||
| void Generate(arma::field<arma::irowvec>& input, | ||
| arma::field<arma::irowvec>& labels, | ||
| size_t batchSize); |
There was a problem hiding this comment.
Can we use const size_t batchSize here, to be consistent with the rest of the codebase.
| // Just storing task-specific parameters. | ||
| } | ||
|
|
||
| void CopyTask::Generate(arma::field<arma::irowvec>& input, |
There was a problem hiding this comment.
Minor style issue, can you align the labels and batchSize parameter with the input parameter.
| * | ||
| * @param input The variable to store input sequences. | ||
| * @param labels The variable to store output sequences. | ||
| * @param batchSize The dataset size. |
There was a problem hiding this comment.
Perhaps batchSize isn't the best parameter name here? Do you think size could work?
There was a problem hiding this comment.
I would like to leave the long name, because with size variable would be not as clear which size is meant (sample size? maximum sequence length? size of sequence elements?). The long name, in contrast, eliminates any chance to slip later.
| { | ||
| input = arma::field<arma::irowvec>(batchSize); | ||
| labels = arma::field<arma::irowvec>(batchSize); | ||
| std::srand(unsigned(std::time(0))); |
There was a problem hiding this comment.
I don't think it's a good idea to set a seed here, what if someone likes to generate the same output e.g. to debug some problem? If someone needs different results in a single run he can set math::RandomSeed(std::time(NULL));before the run.
| std::srand(unsigned(std::time(0))); | ||
| for (size_t i = 0; i < batchSize; ++i) { | ||
| // Random uniform length from [2..maxLength] | ||
| size_t size = 2 + std::rand() % (maxLength - 1); |
There was a problem hiding this comment.
We could use RandInt from core/math/random.hpp here.
| // Random uniform length from [2..maxLength] | ||
| size_t size = 2 + std::rand() % (maxLength - 1); | ||
| input(i) = arma::randi<arma::irowvec>(size, arma::distr_param(0, 1)); | ||
| arma::irowvec item_ans = arma::irowvec(nRepeats * size); |
There was a problem hiding this comment.
Do you think we can use repmat here, to simplify the code?
| // In case it hasn't been included yet. | ||
| #include "copy.hpp" | ||
|
|
||
| #include <cstdlib> |
There was a problem hiding this comment.
Looks like we can remove #include <cstdlib>and #include <ctime>.
| // Random uniform length from [2..maxLength] | ||
| size_t size = RandInt(2, maxLength+1); | ||
| input(i) = arma::randi<arma::colvec>(size, arma::distr_param(0, 1)); | ||
| arma::colvec item_ans = arma::conv_to<arma::colvec>::from( |
There was a problem hiding this comment.
Maybe I missed something, but it looks like we could avoid an extra copy here if we directly use labels(i) for the output of arma::conv_to<arma::colvec>::from(...
| arma::field<arma::colvec> predOutputs) | ||
| { | ||
| double score = 0; | ||
| auto testSize = trueOutputs.n_elem; |
There was a problem hiding this comment.
We should try to avoid auto: I think if people go through the code using auto does not support the general code understanding, since you have to look further to figure out the return values. Also here is a quote from the armadillo page:
Can I use the C++11 auto keyword with Armadillo objects and/or expressions?
Use of C++11 auto is not recommended with Armadillo objects and expressions. Armadillo has a template meta-programming framework which creates lots of short lived temporaries that are not handled by auto.
|
|
||
| for (size_t i = 0; i < testSize; i++) | ||
| { | ||
| auto prediction = trueOutputs.at(i); |
There was a problem hiding this comment.
Can we work with trueOutputs instead of a copy? I guess the compiler would optimize that, but would be easy for us to do it.
| } | ||
| else | ||
| { | ||
| for (size_t j = 0; j < prediction.n_elem; ++j) { |
There was a problem hiding this comment.
Maybe you could simplify the expression here by doing something like: arma::accu(output.at(j) == prediction.at(j)) == prediction.n_elem, not sure if the expression necessary faster or slower.
There was a problem hiding this comment.
I've looked through armadillo's docs and found an even better way: http://arma.sourceforge.net/docs.html#approx_equal
This method resolves all our problems in this place:
- It should be fast - it's armadillo native method (should be parallelized) + should stop after first mismatch (equality check, nothing more)
- No more code duplication - it accepts all armadillo objects, whether it's a matrix, vector or whatever else.
There was a problem hiding this comment.
The only problem I see is, that approx_equalwas introduced in Version 6.700, but we support >= 4.200.0, so I guess what we could do here is to backport the function as we did for the e.g. ind2sub in core/arma_extend.
| return score; | ||
| } | ||
|
|
||
| double SequencePrecision(arma::field<arma::mat> trueOutputs, |
There was a problem hiding this comment.
We could combine the functions for arma::field<arma::mat> and arma::field<arma::colvec> and could avoid some codeduplication by using a template:
template<typename eT>
double SequencePrecision(arma::field<eT> trueOutputs, arma::field<eT> predOutputs)
{
...
}| // Random uniform length from [2..maxLength] | ||
| size_t size = RandInt(2, maxLength+1); | ||
| input(i) = arma::randi<arma::mat>(bitLen, size, arma::distr_param(0, 1)); | ||
| arma::mat item_ans = arma::mat(bitLen, size); |
There was a problem hiding this comment.
Please use camel casing for all names, for more information take a look at: https://github.com/mlpack/mlpack/wiki/DesignGuidelines#naming-conventions
| size_t size = RandInt(2, maxLength+1); | ||
| input(i) = arma::randi<arma::mat>(bitLen, size, arma::distr_param(0, 1)); | ||
| arma::mat item_ans = arma::mat(bitLen, size); | ||
| vector<pair<int, int>> vals(size); |
There was a problem hiding this comment.
Really neat code, I was wondering if we could use arma::sort_index to replace the key of std::pair.
| #include <vector> | ||
| #include <algorithm> | ||
| #include <utility> | ||
| #include <cstdlib> |
There was a problem hiding this comment.
I think we can remove #include <cstdlib>and #include <ctime> here.
|
I keep trying to create an LSTM baseline :) Right now, it kinda works, but there are two big problems:
Can you help me resolve those issues, please? The current unit test code is in the last commit. |
|
I looked into the issue and setting the correct sequence length and it turns out we have to do a little bit more as setting the rho value since the input size is only set once (https://github.com/mlpack/mlpack/blob/master/src/mlpack/methods/ann/rnn_impl.hpp#L206). Also, I think it would be convenient if the model propagates the rho value through the model, that way we only have to change it once. Meaning we need another routine that does this for us. |
|
Here is a quick patch https://gist.github.com/zoq/24f8b2e4826d837d604f9613615763bb Let me know if I should push the changes to my fork if that's easier to read for you. and here is my output for nRepeat = 2 and the output for nRepeat = 3 As for the repeat issue, without any further information, I can't see how the model should predict the right output. |
|
Implemented the changed you described, now it works nicely for nRepeats=1. Interstingly, that on my machine nRepeats=3 crashes (but your output suggests that the model has learned to copy but failed to learn to repeat) with the same message: Right now I'm implementing similar test for However, I would like to know whether unit test is the appropriate place for running a baseline solution. Maybe I should implement it somewhere else, e.g. some kind of examples binary? |
src/mlpack/tests/CMakeLists.txt
Outdated
| radical_test.cpp | ||
| random_forest_test.cpp | ||
| random_test.cpp | ||
| random_forest_test.cpp |
There was a problem hiding this comment.
Looks like there is a merge artifact, if you remove the duplication travis should build the PR again.
| #include <mlpack/methods/ann/layer/parametric_relu.hpp> | ||
| #include <mlpack/methods/ann/layer/reinforce_normal.hpp> | ||
| #include <mlpack/methods/ann/layer/select.hpp> | ||
| #include <mlpack/methods/ann/layer/cross_entropy_error.hpp> |
There was a problem hiding this comment.
Not sure what happened here, but we should not remove the recently introduced cross entropy error function.
| weights[0] = 2; | ||
| // Increasing length by 1 double the number of valid numbers. | ||
| for (size_t i = 1; i < bitLen - 1; ++i) | ||
| weights[i] = 2 * weights[i - 1]; |
There was a problem hiding this comment.
What do you think should we use linspace in combination with exp2 here?
src/mlpack/core/math/random.hpp
Outdated
| * accepts unnormalized probabilties as long as they are | ||
| * non-negative and sum to a positive number. | ||
| * @return A random integer sampled from the specified distribution. | ||
| */ |
There was a problem hiding this comment.
I would go with arma::vec for the weights instead of std::vector feels more consistent with the rest of the codebase, what do you think?
src/mlpack/core/math/random.hpp
Outdated
| runningSum += el; | ||
| slots[i] = runningSum; | ||
| } | ||
| } |
There was a problem hiding this comment.
I think we can remove the check here, we already throw and exception if Probabilities < 0.
There was a problem hiding this comment.
That's not the case: this check is needed for correctly processing [0 0 0 ... 0] weights.
There was a problem hiding this comment.
Ah, right, thanks for the clarification!
src/mlpack/core/math/random.hpp
Outdated
| */ | ||
| inline int RandInt(const arma::vec& weights) | ||
| { | ||
| // Build cumulative probabilities from event probabilities. |
There was a problem hiding this comment.
What about using arma::vechere, that way it's parallelised and we could write slots /= runningSum; instead of iterating over the vector.
There was a problem hiding this comment.
True, but is there any "arma-native" implementation of binary search? Since we compute cumulative sums, we won't really get benefits from parallelization - the cumsum is "inherently" sequential.
UPD Read your next commentary and understood that here we can use armadillo vector as if it was STL vector.
There was a problem hiding this comment.
True, cumsum is sequential, I guess the only operation that we could parallelise here is the accumulation. The performance boost should be negligible, we mainly save some lines of code.
src/mlpack/core/math/random.hpp
Outdated
| { | ||
| // Build cumulative probabilities from event probabilities. | ||
| std::vector<double> slots(weights.size()); | ||
| double runningSum = 0; |
There was a problem hiding this comment.
I was thinking if that check is necessary, since it was made clear in the documentation, we could simplify the expression:
arma::vec slots = cumsum(weights);
slots /= arma::accu(weights);
return std::lower_bound(slots.begin(), slots.end(), Random()) - slots.begin();
Let me know what you think.
There was a problem hiding this comment.
Well, we still need to run that check - just to alert user if he tries to use it in the wrong way (intentialloy or otherwise)
There was a problem hiding this comment.
You mean the probabilities check? I'm not sure we have to, but I don't mind it here, we could use arma::any if you like or just leave the for loop.
There was a problem hiding this comment.
Reimplemented with arma::any. I think we do need those checks - it would lead to unpredicted consequences on weight vectors with negative elements. The binary search part is not robust to this case, and it is still nonsensical, so I like the idea to cut it out before the lower_bound run.
There was a problem hiding this comment.
Sounds fine for me, thanks for the input.
|
|
||
| AddTask::AddTask(const size_t bitLen) : bitLen(bitLen) | ||
| { | ||
| if (bitLen <= 0) { |
There was a problem hiding this comment.
Can you go through the code and put he { on a new line?
| // We have two binary numbers with exactly two digits (10 and 11). | ||
| // weights[0] = 2; | ||
| // Increasing length by 1 double the number of valid numbers. | ||
| /*for (size_t i = 1; i < bitLen - 1; ++i) |
| using namespace mlpack::ann::augmented::tasks; | ||
| using namespace mlpack::ann::augmented::scorers; | ||
|
|
||
| using namespace mlpack::ann; |
There was a problem hiding this comment.
I think we can remove the namespaces here.
| void Predict(arma::field<arma::mat>& predictors, | ||
| arma::field<arma::mat>& labels) | ||
| { | ||
| auto sz = predictors.n_elem; |
There was a problem hiding this comment.
I think we can directly use predictors.n_elem; instead of sz here. In general we should avoid to use auto when using armadillo:
Use of C++11 auto is not recommended with Armadillo objects and expressions. Armadillo has a template meta-programming framework which creates lots of short lived temporaries that are not handled by auto.
In this case it would be just fine, but I think using it directly without using an alias is also just fine, an we can save another line.
| predictors.reshape(3, predictors.n_elem / 3); | ||
| assert(predictors.n_rows == 3); | ||
| int num_A = 0, num_B = 0; | ||
| bool num = false; // true iff we have already seen the separating symbol |
There was a problem hiding this comment.
I guess, you mean if, also can you use the correct punctuation here?
There was a problem hiding this comment.
No, I mean "iff" in the meaning "if an only if" - just like it is normally done in math texts
There was a problem hiding this comment.
Ah, I see, didn't thought about that in the current context.
| assert(predictors.n_rows == 3); | ||
| int num_A = 0, num_B = 0; | ||
| bool num = false; // true iff we have already seen the separating symbol | ||
| size_t len = predictors.n_cols; |
There was a problem hiding this comment.
I think there is no need to create an alias here, what do you think?
| BOOST_AUTO_TEST_SUITE(RandomTest); | ||
|
|
||
| // Test for RandInt() sampler from discrete uniform distribution. | ||
| BOOST_AUTO_TEST_CASE(DiscreteUniformRandomTest) |
There was a problem hiding this comment.
Thanks for write a test for the method!
src/mlpack/core/math/random.hpp
Outdated
| if (el < 0) { | ||
| std::ostringstream oss; | ||
| // Check that constraints on weights parameter are satisfied. | ||
| if (arma::min(weights) < 0) { |
There was a problem hiding this comment.
I know I'm really picky about the style, can you go through the code and adjust the lines with the following pattern: ) {.
There was a problem hiding this comment.
Ran pcregrep -Mnr --color "\) \{\n" src/mlpack/ - is it okay if I fix this pattern elsewhere?
|
Temporary fixed the disk space issue, can you go through the issues listed here: http://masterblaster.mlpack.org/job/pull-requests%20mlpack%20style%20checks/656/cppcheckResult/source.all/?before=5&after=5 |
zoq
left a comment
There was a problem hiding this comment.
I think it looks good and we should merge it. We should let this sit for 3 days, before merge to give anyone else time to comment.
rcurtin
left a comment
There was a problem hiding this comment.
Thanks so much for the hard work with this PR. I agree that it is ready, if you can take care of the comment about RandInt().
src/mlpack/core/math/random.hpp
Outdated
| * @param weights Probabilities of individual numbers. Note that the function | ||
| * accepts unnormalized probabilties as long as they are | ||
| * non-negative and sum to a positive number. | ||
| * @return A random integer sampled from the specified distribution. |
There was a problem hiding this comment.
I think that instead of adding this overload of RandInt() we could use the functionality already available with DiscreteDistribution. Instead of
x = RandInt(weights);
we can create a DiscreteDistribution and use it:
DiscreteDistribution d(weights);
x = d.Random();
What do you think?
There was a problem hiding this comment.
(As I have mentioned in the IRC chat) I don't see the concise way to use it in 1-dim setting - although I do admit that I didn't understand the n-dim API.
There was a problem hiding this comment.
Ah sorry, the code I gave above was wrong. You could do:
DiscreteDistribution d(1); // One-dimensional discrete distribution.
d.Probabilities(0) = std::move(weights);
x = d.Random();
I think this will do what you need, let me know if not.
| size_t totSize = vecInput.n_elem + addSeparator + vecLabel.n_elem; | ||
| input(i) = arma::zeros(totSize, 2); | ||
| input(i).col(0).rows(0, vecInput.n_elem-1) = | ||
| vecInput; |
There was a problem hiding this comment.
Some small style issues here---the wrapped lines should be indented 4 spaces, not 2, and vecInput.n_elem-1 should be vecInput.n_elem - 1. :)
| input(i).rows(origPtr, origPtr + bitLen - 1); | ||
| ptr += bitLen; | ||
| origPtr += bitLen; | ||
| sepInput.at(ptr, 0) = 0.5; |
There was a problem hiding this comment.
Ok, I see---thanks for the clarification.
rcurtin
left a comment
There was a problem hiding this comment.
Thanks for the refactoring. I'm glad we can keep the tests too. :) I only have a couple minor comments that you can address if you like (it'll make the code just a bit cleaner), otherwise I agree this is ready for merge.
| weights = arma::exp2(arma::linspace(1, bitLen - 1, bitLen - 1)); | ||
|
|
||
| mlpack::distribution::DiscreteDistribution d(1); | ||
| d.Probabilities(0) = std::move(weights); |
There was a problem hiding this comment.
You could compress this a bit, I think:
d.Probabilities(0) = arma::exp2(arma::linspace(1, bitLen - 1, bitLen - 1));
then you don't need the weights object. :)
| weights = arma::exp2(arma::linspace(1, maxLength - 1, maxLength - 1)); | ||
|
|
||
| mlpack::distribution::DiscreteDistribution d(1); | ||
| d.Probabilities(0) = std::move(weights); |
There was a problem hiding this comment.
Same here, this can be compressed too.
src/mlpack/tests/random_test.cpp
Outdated
| { | ||
| arma::vec armaWeights(weightSet); | ||
| mlpack::distribution::DiscreteDistribution d(1); // One-dimensional discrete distribution. | ||
| d.Probabilities(0) = std::move(armaWeights); |
There was a problem hiding this comment.
You might be able to compress this a little bit too---d.Probabilities(0) = arma::vec(weightSet).
| { | ||
| arma::vec weights(maxLength - 1); | ||
|
|
||
| mlpack::distribution::DiscreteDistribution d(1); |
There was a problem hiding this comment.
We have to include #include <mlpack/core/dists/discrete_distribution.hpp> to avoid an undefined method issue. The same for the Add task.
|
Thanks for the great contribution! |
This PR is part of my GSoC project "Augmented RNNs".
Imeplemented:
CopyTaskfor evaluating models on the sequence copy problem, showcasing benchmarking framework;CopyTask).