Merge pull request #2293 from jeffin143/split-shuffle

Add shuffle data paramter to data_split
This commit is contained in:
Ryan Curtin
2020-05-11 21:51:44 -04:00
committed by GitHub
5 changed files with 150 additions and 32 deletions
+2 -1
View File
@@ -6,6 +6,8 @@
* Add adaptive max pooling and adaptive mean pooling layers (#2195).
* Add parameter to avoid shuffling of data in preprocess_split (#2293).
* Add `MatType` parameter to `LSHSearch`, allowing sparse matrices to be used
for search (#2395).
@@ -105,7 +107,6 @@
* Add Soft Shrink Activation Function (#2174).
* Add Hinge Embedding Loss Function (#2229).
* Add Cosine Embedding Loss Function (#2209).
* Add Margin Ranking Loss Function (#2264).
+57 -25
View File
@@ -44,6 +44,8 @@ namespace data {
* @param trainLabel Vector to store training labels into.
* @param testLabel Vector to store test labels into.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true.)
*/
template<typename T, typename U>
void Split(const arma::Mat<T>& input,
@@ -52,7 +54,8 @@ void Split(const arma::Mat<T>& input,
arma::Mat<T>& testData,
arma::Row<U>& trainLabel,
arma::Row<U>& testLabel,
const double testRatio)
const double testRatio,
const bool shuffleData = true)
{
const size_t testSize = static_cast<size_t>(input.n_cols * testRatio);
const size_t trainSize = input.n_cols - testSize;
@@ -61,20 +64,33 @@ void Split(const arma::Mat<T>& input,
trainLabel.set_size(trainSize);
testLabel.set_size(testSize);
const arma::Col<size_t> order =
arma::shuffle(arma::linspace<arma::Col<size_t>>(0, input.n_cols - 1,
input.n_cols));
for (size_t i = 0; i != trainSize; ++i)
if (shuffleData)
{
trainData.col(i) = input.col(order[i]);
trainLabel(i) = inputLabel(order[i]);
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(
0, input.n_cols - 1, input.n_cols));
if (trainSize > 0)
{
trainData = input.cols(order.subvec(0, trainSize - 1));
trainLabel = inputLabel.cols(order.subvec(0, trainSize - 1));
}
if (trainSize < input.n_cols)
{
testData = input.cols(order.subvec(trainSize, input.n_cols - 1));
testLabel = inputLabel.cols(order.subvec(trainSize, input.n_cols - 1));
}
}
for (size_t i = 0; i != testSize; ++i)
else
{
testData.col(i) = input.col(order[i + trainSize]);
testLabel(i) = inputLabel(order[i + trainSize]);
if (trainSize > 0)
{
trainData = input.cols(0, trainSize - 1);
trainLabel = inputLabel.subvec(0, trainSize - 1);
}
if (trainSize < input.n_cols)
{
testData = input.cols(trainSize , input.n_cols - 1);
testLabel = inputLabel.subvec(trainSize , input.n_cols - 1);
}
}
}
@@ -98,29 +114,39 @@ void Split(const arma::Mat<T>& input,
* @param trainData Matrix to store training data into.
* @param testData Matrix to store test data into.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true).
*/
template<typename T>
void Split(const arma::Mat<T>& input,
arma::Mat<T>& trainData,
arma::Mat<T>& testData,
const double testRatio)
const double testRatio,
const bool shuffleData = true)
{
const size_t testSize = static_cast<size_t>(input.n_cols * testRatio);
const size_t trainSize = input.n_cols - testSize;
trainData.set_size(input.n_rows, trainSize);
testData.set_size(input.n_rows, testSize);
const arma::Col<size_t> order =
arma::shuffle(arma::linspace<arma::Col<size_t>>(0, input.n_cols -1,
input.n_cols));
if (shuffleData)
{
arma::uvec order = arma::shuffle(arma::linspace<arma::uvec>(
0, input.n_cols - 1, input.n_cols));
for (size_t i = 0; i != trainSize; ++i)
{
trainData.col(i) = input.col(order[i]);
if (trainSize > 0)
trainData = input.cols(order.subvec(0, trainSize - 1));
if (trainSize < input.n_cols)
testData = input.cols(order.subvec(trainSize, input.n_cols - 1));
}
for (size_t i = 0; i != testSize; ++i)
else
{
testData.col(i) = input.col(order[i + trainSize]);
if (trainSize > 0)
trainData = input.cols(0, trainSize - 1);
if (trainSize < input.n_cols)
testData = input.cols(trainSize , input.n_cols - 1);
}
}
@@ -140,6 +166,8 @@ void Split(const arma::Mat<T>& input,
* @param input Input dataset to split.
* @param label Input labels to split.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true).
* @return std::tuple containing trainData (arma::Mat<T>), testData
* (arma::Mat<T>), trainLabel (arma::Row<U>), and testLabel (arma::Row<U>).
*/
@@ -147,7 +175,8 @@ template<typename T, typename U>
std::tuple<arma::Mat<T>, arma::Mat<T>, arma::Row<U>, arma::Row<U>>
Split(const arma::Mat<T>& input,
const arma::Row<U>& inputLabel,
const double testRatio)
const double testRatio,
const bool shuffleData = true)
{
arma::Mat<T> trainData;
arma::Mat<T> testData;
@@ -155,7 +184,7 @@ Split(const arma::Mat<T>& input,
arma::Row<U> testLabel;
Split(input, inputLabel, trainData, testData, trainLabel, testLabel,
testRatio);
testRatio, shuffleData);
return std::make_tuple(std::move(trainData),
std::move(testData),
@@ -176,17 +205,20 @@ Split(const arma::Mat<T>& input,
*
* @param input Input dataset to split.
* @param testRatio Percentage of dataset to use for test set (between 0 and 1).
* @param shuffleData If true, the sample order is shuffled; otherwise, each
* sample is visited in linear order. (Default true).
* @return std::tuple containing trainData (arma::Mat<T>)
* and testData (arma::Mat<T>).
*/
template<typename T>
std::tuple<arma::Mat<T>, arma::Mat<T>>
Split(const arma::Mat<T>& input,
const double testRatio)
const double testRatio,
const bool shuffleData = true)
{
arma::Mat<T> trainData;
arma::Mat<T> testData;
Split(input, trainData, testData, testRatio);
Split(input, trainData, testData, testRatio, shuffleData);
return std::make_tuple(std::move(trainData),
std::move(testData));
@@ -45,6 +45,13 @@ PROGRAM_INFO("Split Data",
PRINT_CALL("preprocess_split", "input", "X", "training", "X_train", "test",
"X_test", "test_ratio", 0.4) +
"\n\n"
"Also by default the dataset is shuffled and split; you can provide the " +
PRINT_PARAM_STRING("no_shuffle") + " option to avoid shuffling the "
"data; an example to avoid shuffling of data is:"
"\n\n" +
PRINT_CALL("preprocess_split", "input", "X", "training", "X_train", "test",
"X_test", "test_ratio", 0.4, "no_shuffle", true) +
"\n\n"
"If we had a dataset " + PRINT_DATASET("X") + " and associated labels " +
PRINT_DATASET("y") + ", and we wanted to split these into " +
PRINT_DATASET("X_train") + ", " + PRINT_DATASET("y_train") + ", " +
@@ -73,6 +80,7 @@ PARAM_DOUBLE_IN("test_ratio", "Ratio of test set; if not set,"
"the ratio defaults to 0.2", "r", 0.2);
PARAM_INT_IN("seed", "Random seed (0 for std::time(NULL)).", "s", 0);
PARAM_FLAG("no_shuffle", "Avoid shuffling and splitting the data.", "S");
using namespace mlpack;
using namespace mlpack::util;
@@ -83,6 +91,7 @@ static void mlpackMain()
{
// Parse command line options.
const double testRatio = CLI::GetParam<double>("test_ratio");
const bool shuffleData = CLI::GetParam<bool>("no_shuffle");
if (CLI::GetParam<int>("seed") == 0)
mlpack::math::RandomSeed(std::time(NULL));
@@ -129,7 +138,7 @@ static void mlpackMain()
CLI::GetParam<arma::Mat<size_t>>("input_labels");
arma::Row<size_t> labelsRow = labels.row(0);
const auto value = data::Split(data, labelsRow, testRatio);
const auto value = data::Split(data, labelsRow, testRatio, !shuffleData);
Log::Info << "Training data contains " << get<0>(value).n_cols << " points."
<< endl;
Log::Info << "Test data contains " << get<1>(value).n_cols << " points."
@@ -148,7 +157,7 @@ static void mlpackMain()
}
else // We have no labels, so just split the dataset.
{
const auto value = data::Split(data, testRatio);
const auto value = data::Split(data, testRatio, !shuffleData);
Log::Info << "Training data contains " << get<0>(value).n_cols << " points."
<< endl;
Log::Info << "Test data contains " << get<1>(value).n_cols << " points."
@@ -199,4 +199,35 @@ BOOST_AUTO_TEST_CASE(PreprocessSplitUnityTestRatioTest)
labelSize);
}
/**
* Check shuffle_data flag is working as expected.
*/
BOOST_AUTO_TEST_CASE(PreprocessSplitLabelShuffleDataTest)
{
// Load custom dataset.
arma::mat inputData;
data::Load("vc2.csv", inputData);
// Store size of input dataset.
int inputSize = inputData.n_cols;
// Input custom data points and labels.
SetInputParam("input", inputData);
// Input test_ratio.
SetInputParam("test_ratio", (double) 0.1);
SetInputParam("no_shuffle", true);
mlpackMain();
// Now check that the output has desired dimensions.
BOOST_REQUIRE_EQUAL(CLI::GetParam<arma::mat>("training").n_cols,
std::ceil(0.9 * inputSize));
BOOST_REQUIRE_EQUAL(CLI::GetParam<arma::mat>("test").n_cols,
std::floor(0.1 * inputSize));
arma::mat concat = arma::join_rows(CLI::GetParam<arma::mat>("training"),
CLI::GetParam<arma::mat>("test"));
CheckMatrices(inputData, concat);
}
BOOST_AUTO_TEST_SUITE_END();
+49 -4
View File
@@ -94,20 +94,65 @@ void CheckDuplication(const Row<size_t>& trainLabels,
BOOST_REQUIRE_EQUAL(counts[i], 1);
}
BOOST_AUTO_TEST_CASE(SplitDataResultMat)
BOOST_AUTO_TEST_CASE(SplitShuffleDataResultMat)
{
mat input(2, 10);
size_t count = 0; // count for putting unique sequential values
size_t count = 0; // Counter for unique sequential values.
input.imbue([&count] () { return ++count; });
const auto value = Split(input, 0.2);
BOOST_REQUIRE_EQUAL(std::get<0>(value).n_cols, 8); // train data
BOOST_REQUIRE_EQUAL(std::get<1>(value).n_cols, 2); // test data
BOOST_REQUIRE_EQUAL(std::get<0>(value).n_cols, 8); // Train data.
BOOST_REQUIRE_EQUAL(std::get<1>(value).n_cols, 2); // Test data.
mat concat = arma::join_rows(std::get<0>(value), std::get<1>(value));
CheckMatEqual(input, concat);
}
BOOST_AUTO_TEST_CASE(SplitDataResultMat)
{
mat input(2, 10);
size_t count = 0; // Counter for unique sequential values.
input.imbue([&count] () { return ++count; });
const auto value = Split(input, 0.2, false);
BOOST_REQUIRE_EQUAL(std::get<0>(value).n_cols, 8); // Train data.
BOOST_REQUIRE_EQUAL(std::get<1>(value).n_cols, 2); // Test data.
mat concat = arma::join_rows(std::get<0>(value), std::get<1>(value));
// Order matters here.
CheckMatrices(input, concat);
}
BOOST_AUTO_TEST_CASE(ZeroRatioSplitData)
{
mat input(2, 10);
size_t count = 0; // Counter for unique sequential values.
input.imbue([&count] () { return ++count; });
const auto value = Split(input, 0, false);
BOOST_REQUIRE_EQUAL(std::get<0>(value).n_cols, 10); // Train data.
BOOST_REQUIRE_EQUAL(std::get<1>(value).n_cols, 0); // Test data.
mat concat = arma::join_rows(std::get<0>(value), std::get<1>(value));
// Order matters here.
CheckMatrices(input, concat);
}
BOOST_AUTO_TEST_CASE(TotalRatioSplitData)
{
mat input(2, 10);
size_t count = 0; // Counter for unique sequential values.
input.imbue([&count] () { return ++count; });
const auto value = Split(input, 1, false);
BOOST_REQUIRE_EQUAL(std::get<0>(value).n_cols, 0); // Train data.
BOOST_REQUIRE_EQUAL(std::get<1>(value).n_cols, 10); // Test data.
mat concat = arma::join_rows(std::get<0>(value), std::get<1>(value));
// Order matters here.
CheckMatrices(input, concat);
}
BOOST_AUTO_TEST_CASE(SplitLabeledDataResultMat)
{
mat input(2, 10);