Shuffle sequence lengths (#3926)

This commit is contained in:
Zachary Ng
2025-04-21 06:04:49 +02:00
committed by GitHub
parent 381dccec1a
commit 6efbe25a7e
5 changed files with 149 additions and 1 deletions
+1
View File
@@ -162,6 +162,7 @@ Copyright:
Copyright 2024, Felix Patschkowski <felix.patschkowski@gmail.com>
Copyright 2025, Benjamin A. Beasley <code@musicinmybrain.net>
Copyright 2025, Maksym Prots <imaxprots@gmail.com>
Copyright 2025, Zachary Ng <zachn716@gmail.com>
License: BSD-3-clause
All rights reserved.
+2
View File
@@ -3,6 +3,8 @@
## mlpack ?.?.?
_????-??-??_
* Shuffle sequence lengths (#3926)
* Add ability to compile OpenBLAS for windows (#3922)
* Drop pytest-runner and "setup.py test" support (#3921).
+68
View File
@@ -146,6 +146,74 @@ void ShuffleData(const MatType& inputPoints,
}
}
/**
* Shuffle a cube-shaped dataset and associated labels (or responses) which are
* also cube-shaped. Also shuffle its weights. It is expected that inputPoints,
* inputLabels, and inputWeights have the same number of columns.
*
* Shuffled data will be output into outputPoints, outputLabels, and outputWeights.
*/
template<typename MatType, typename LabelsType, typename WeightsType>
void ShuffleData(const MatType& inputPoints,
const LabelsType& inputLabels,
const WeightsType& inputWeights,
MatType& outputPoints,
LabelsType& outputLabels,
WeightsType& outputWeights,
const std::enable_if_t<!arma::is_SpMat<MatType>::value>* = 0,
const std::enable_if_t<arma::is_Cube<MatType>::value>* = 0,
const std::enable_if_t<arma::is_Cube<LabelsType>::value>* = 0)
{
// Generate ordering.
arma::uvec ordering = arma::shuffle(arma::linspace<arma::uvec>(0,
inputPoints.n_cols - 1, inputPoints.n_cols));
// Properly handle the case where the input and output data are the same
// object.
MatType* outputPointsPtr = &outputPoints;
LabelsType* outputLabelsPtr = &outputLabels;
WeightsType* outputWeightsPtr = &outputWeights;
if (&inputPoints == &outputPoints)
outputPointsPtr = new MatType();
if (&inputLabels == &outputLabels)
outputLabelsPtr = new LabelsType();
if (&inputWeights == &outputWeights)
outputWeightsPtr = new WeightsType();
outputPointsPtr->set_size(inputPoints.n_rows, inputPoints.n_cols,
inputPoints.n_slices);
outputLabelsPtr->set_size(inputLabels.n_rows, inputLabels.n_cols,
inputLabels.n_slices);
outputWeightsPtr->set_size(inputWeights.n_cols);
for (size_t i = 0; i < ordering.n_elem; ++i)
{
outputPointsPtr->tube(0, ordering[i], outputPointsPtr->n_rows - 1,
ordering[i]) = inputPoints.tube(0, i, inputPoints.n_rows - 1, i);
outputLabelsPtr->tube(0, ordering[i], outputLabelsPtr->n_rows - 1,
ordering[i]) = inputLabels.tube(0, i, inputLabels.n_rows - 1, i);
outputWeightsPtr->at(ordering[i]) = inputWeights[i];
}
// Clean up memory if needed.
if (&inputPoints == &outputPoints)
{
outputPoints = std::move(*outputPointsPtr);
delete outputPointsPtr;
}
if (&inputLabels == &outputLabels)
{
outputLabels = std::move(*outputLabelsPtr);
delete outputLabelsPtr;
}
if (&inputWeights == &outputWeights)
{
outputWeights = std::move(*outputWeightsPtr);
delete outputWeightsPtr;
}
}
/**
* Shuffle a dataset and associated labels (or responses) and weights. It is
* expected that inputPoints and inputLabels and inputWeights have the same
+9 -1
View File
@@ -633,7 +633,15 @@ void RNN<
MatType
>::Shuffle()
{
ShuffleData(predictors, responses, predictors, responses);
if (sequenceLengths.n_elem > 0)
{
ShuffleData(predictors, responses, sequenceLengths,
predictors, responses, sequenceLengths);
}
else
{
ShuffleData(predictors, responses, predictors, responses);
}
}
template<
+69
View File
@@ -696,6 +696,75 @@ TEST_CASE("CubeShuffleTest", "[MathTest]")
REQUIRE(counts[i] == data.n_slices);
}
/**
* Make sure shuffling cubes with ragged sequence lengths works.
*/
TEST_CASE("RaggedCubeShuffleTest", "[MathTest]")
{
arma::cube data(3, 5, 5);
arma::cube labels(1, 5, 5);
arma::Row<size_t> lengths(5);
data.fill(-1);
labels.fill(-1);
for (size_t c = 0; c < lengths.n_elem; ++c)
{
lengths[c] = c;
for (size_t s = 0; s < lengths[c]; ++s)
{
data(0, c, s) = s;
data(1, c, s) = c;
labels(0, c, s) = c + s;
}
}
arma::cube outputData, outputLabels;
arma::Row<size_t> outputLengths;
ShuffleData(data, labels, lengths, outputData, outputLabels, outputLengths);
REQUIRE(outputData.n_rows == data.n_rows);
REQUIRE(outputData.n_cols == data.n_cols);
REQUIRE(outputData.n_slices == data.n_slices);
REQUIRE(outputLabels.n_rows == labels.n_rows);
REQUIRE(outputLabels.n_cols == labels.n_cols);
REQUIRE(outputLabels.n_slices == labels.n_slices);
REQUIRE(lengths.n_elem == outputLengths.n_elem);
// Make sure each column has the right number of slices
arma::Row<size_t> sliceCount(5);
for (size_t i = 0; i < outputLabels.n_cols; ++i)
{
for (size_t j = 0; j < outputLabels.n_slices; j++)
{
if (outputLabels(0, i, j) < 0) {
sliceCount[i] = j;
break;
}
}
}
for (size_t i = 0; i < 5; ++i)
REQUIRE(sliceCount[i] == outputLengths[i]);
// Make sure we only have each point once.
arma::Row<size_t> counts(5);
for (size_t c = 0; c < 5; ++c)
{
for (size_t s = 0; s < outputLengths[c]; ++s)
{
REQUIRE(outputData(0, c, s) + outputData(1, c, s)
== outputLabels(0, c, s));
REQUIRE(outputData(2, c, s) == Approx(-1.0).margin(1e-5));
counts[outputLengths[c]]++;
}
}
for (size_t i = 0; i < 5; ++i)
REQUIRE(counts[i] == i);
}
/**
* Make sure shuffling data with weights works.
*/