Fixed issues from @zoq's review
This commit is contained in:
@@ -22,7 +22,8 @@ namespace tasks /* Task utilities for augmented */ {
|
||||
|
||||
AddTask::AddTask(const size_t bitLen) : bitLen(bitLen)
|
||||
{
|
||||
if (bitLen <= 0) {
|
||||
if (bitLen <= 0)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "AddTask::AddTask(): binary length (" << bitLen << ") "
|
||||
<< "is not positive!"
|
||||
@@ -36,8 +37,8 @@ const void AddTask::Generate(arma::field<arma::mat>& input,
|
||||
const size_t batchSize,
|
||||
bool fixedLength)
|
||||
{
|
||||
arma::field<arma::vec> vecInput = arma::field<arma::colvec>(batchSize);
|
||||
arma::field<arma::vec> vecLabels = arma::field<arma::colvec>(batchSize);
|
||||
input = arma::field<arma::mat>(batchSize);
|
||||
labels = arma::field<arma::mat>(batchSize);
|
||||
size_t sizeA = bitLen, sizeB = bitLen;
|
||||
for (size_t i = 0; i < batchSize; ++i)
|
||||
{
|
||||
@@ -50,21 +51,23 @@ const void AddTask::Generate(arma::field<arma::mat>& input,
|
||||
// Construct sequence of the form
|
||||
// (binary number with sizeA bits) + '+'
|
||||
// + (binary number with sizeB bits).
|
||||
vecInput(i) = arma::randi<arma::colvec>(
|
||||
sizeA + sizeB + 1, arma::distr_param(0, 1));
|
||||
input(i) = arma::randi<arma::mat>(sizeA + sizeB + 1,
|
||||
1,
|
||||
arma::distr_param(0, 1));
|
||||
// Insert special value for '+' delimiter.
|
||||
vecInput(i).at(sizeA) = 0.5;
|
||||
|
||||
labels(i) = arma::zeros(sizeA + sizeB + 1, 1);
|
||||
input(i).at(sizeA, 0) = 0.5;
|
||||
|
||||
int valA = 0;
|
||||
for (size_t k = 0; k < sizeA; ++k)
|
||||
{
|
||||
valA += static_cast<int>(vecInput(i).at(k)) << k;
|
||||
valA += static_cast<int>(input(i).at(k, 0)) << k;
|
||||
}
|
||||
|
||||
int valB = 0;
|
||||
for (size_t k = sizeA + 1; k < sizeA + 1 + sizeB; ++k)
|
||||
{
|
||||
valB += static_cast<int>(vecInput(i).at(k)) << (k - sizeA - 1);
|
||||
valB += static_cast<int>(input(i).at(k, 0)) << (k - sizeA - 1);
|
||||
}
|
||||
|
||||
int tot = valA + valB;
|
||||
@@ -85,27 +88,11 @@ const void AddTask::Generate(arma::field<arma::mat>& input,
|
||||
}
|
||||
binarySeq.push_back(0);
|
||||
}
|
||||
size_t totLen = binarySeq.size();
|
||||
vecLabels(i) = arma::colvec(totLen);
|
||||
for (size_t j = 0; j < totLen; ++j)
|
||||
for (size_t j = 0; j < binarySeq.size(); ++j)
|
||||
{
|
||||
vecLabels(i).at(j) = binarySeq[j];
|
||||
labels(i).at(j, 0) = binarySeq[j];
|
||||
}
|
||||
}
|
||||
Binarize(vecInput, input);
|
||||
Binarize(vecLabels, labels);
|
||||
if (input.n_rows != labels.n_rows) {
|
||||
std::ostringstream oss;
|
||||
oss << "AddTask::Generate(): sequences after application of "
|
||||
<< "Binarize() are not aligned ("
|
||||
<< input.n_rows << " and " << labels.n_rows << ")"
|
||||
<< std::endl;
|
||||
throw std::logic_error(oss.str());
|
||||
}
|
||||
for (size_t i = 0; i < input.n_rows; ++i)
|
||||
{
|
||||
labels.at(i).reshape(input.at(i).n_elem, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const void AddTask::Generate(arma::mat& input,
|
||||
@@ -129,13 +116,6 @@ const void AddTask::Binarize(const arma::field<arma::vec>& input,
|
||||
output = arma::field<arma::mat>(input.n_elem);
|
||||
for (size_t i = 0; i < input.n_elem; ++i)
|
||||
{
|
||||
/*output.at(i) = arma::zeros(3, input.at(i).n_elem);
|
||||
for (size_t j = 0; j < input.at(i).n_elem; ++j)
|
||||
{
|
||||
size_t val = input.at(i).at(j);
|
||||
output.at(i).at(val, j) = 1;
|
||||
}
|
||||
output.at(i).reshape(output.at(i).n_elem, 1);*/
|
||||
output.at(i) = arma::conv_to<arma::mat>::from(input.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ class CopyTask
|
||||
* that has to be repeated by model.
|
||||
* @param nRepeats Number of repeates required to solve the task.
|
||||
* @param addSeparator Flag indicating whether generator
|
||||
* should emit separating symbol after input sequence
|
||||
* should emit separating symbol after input sequence.
|
||||
*/
|
||||
CopyTask(const size_t maxLength,
|
||||
const size_t nRepeats,
|
||||
bool addSeparator = false);
|
||||
const bool addSeparator = false);
|
||||
/**
|
||||
* Generate dataset of a given size.
|
||||
*
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace tasks /* Task utilities for augmented */ {
|
||||
|
||||
CopyTask::CopyTask(const size_t maxLength,
|
||||
const size_t nRepeats,
|
||||
bool addSeparator) :
|
||||
const bool addSeparator) :
|
||||
maxLength(maxLength),
|
||||
nRepeats(nRepeats),
|
||||
addSeparator(addSeparator)
|
||||
@@ -72,13 +72,13 @@ const void CopyTask::Generate(arma::field<arma::mat>& input,
|
||||
vecInput;
|
||||
if (addSeparator)
|
||||
input(i).at(vecInput.n_elem, 0) = 0.5;
|
||||
input(i).col(1).rows(addSeparator+vecInput.n_elem, totSize-1) =
|
||||
arma::ones(totSize-vecInput.n_elem-addSeparator);
|
||||
input(i).col(1).rows(addSeparator + vecInput.n_elem, totSize - 1) =
|
||||
arma::ones(totSize-vecInput.n_elem - addSeparator);
|
||||
input(i) = input(i).t();
|
||||
input(i).reshape(input(i).n_elem, 1);
|
||||
labels(i) = arma::zeros(totSize, 1);
|
||||
labels(i).col(0).rows(addSeparator+vecInput.n_elem, totSize-1) =
|
||||
vecLabel;
|
||||
labels(i).col(0).rows(addSeparator + vecInput.n_elem, totSize - 1) =
|
||||
vecLabel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,13 @@ namespace scorers /* Scoring utilities for augmented */ {
|
||||
*
|
||||
* @param trueOutputs Ground truth sequences.
|
||||
* @param predOutputs Sequences predicted by model.
|
||||
* @param tol Minimum absolute difference value
|
||||
* which is considered as a model failure.
|
||||
*/
|
||||
template<typename MatType>
|
||||
double SequencePrecision(arma::field<MatType> trueOutputs,
|
||||
arma::field<MatType> predOutputs,
|
||||
double tol = 1e-4);
|
||||
const double SequencePrecision(arma::field<MatType> trueOutputs,
|
||||
arma::field<MatType> predOutputs,
|
||||
double tol = 1e-4);
|
||||
} // namespace scorers
|
||||
} // namespace augmented
|
||||
} // namespace ann
|
||||
|
||||
@@ -21,9 +21,9 @@ namespace augmented /* Augmented neural network */ {
|
||||
namespace scorers /* Scoring utilities for augmented */ {
|
||||
|
||||
template<typename MatType>
|
||||
double SequencePrecision(arma::field<MatType> trueOutputs,
|
||||
arma::field<MatType> predOutputs,
|
||||
double tol)
|
||||
const double SequencePrecision(arma::field<MatType> trueOutputs,
|
||||
arma::field<MatType> predOutputs,
|
||||
double tol)
|
||||
{
|
||||
double score = 0;
|
||||
size_t testSize = trueOutputs.n_elem;
|
||||
@@ -42,8 +42,7 @@ double SequencePrecision(arma::field<MatType> trueOutputs,
|
||||
arma::vec delta = arma::vectorise(arma::abs(
|
||||
trueOutputs.at(i) - predOutputs.at(i)));
|
||||
double maxDelta = arma::max(delta);
|
||||
double eps = tol;
|
||||
if (maxDelta < eps)
|
||||
if (maxDelta < tol)
|
||||
{
|
||||
score++;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ class SortTask
|
||||
*
|
||||
* @param maxLength Maximum length of the number sequence.
|
||||
* @param bitLen Binary length of sorted numbers.
|
||||
* @param addSeparator Flag indicating whether generator
|
||||
* should emit separating symbol after input sequence.
|
||||
*/
|
||||
SortTask(const size_t maxLength,
|
||||
const size_t bitLen,
|
||||
@@ -54,6 +56,8 @@ class SortTask
|
||||
* @param input The variable to store input sequences.
|
||||
* @param labels The variable to store output sequences.
|
||||
* @param batchSize The dataset size.
|
||||
* @param fixedLength Flag indicating whether generator
|
||||
* should emit sequences of pairwise equal length.
|
||||
*/
|
||||
const void Generate(arma::field<arma::mat>& input,
|
||||
arma::field<arma::mat>& labels,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @file sort_impl.hpp
|
||||
* @author Konstantin Sidorov
|
||||
*
|
||||
* Implementation of SortTask class
|
||||
* Implementation of SortTask class.
|
||||
*
|
||||
* mlpack is free software; you may redistribute it and/or modify it under the
|
||||
* terms of the 3-clause BSD license. You should have received a copy of the
|
||||
|
||||
@@ -46,39 +46,43 @@ using mlpack::data::Binarize;
|
||||
// The dummy model that simply copies the sequence
|
||||
// the required number of times
|
||||
// (yes, no ML here, we're unit testing :)
|
||||
class HardCodedCopyModel {
|
||||
class HardCodedCopyModel
|
||||
{
|
||||
public:
|
||||
HardCodedCopyModel() : nRepeats(1) {}
|
||||
void Train(
|
||||
arma::field<arma::mat>& predictors,
|
||||
arma::field<arma::mat>& labels) {
|
||||
void Train(arma::field<arma::mat>& predictors,
|
||||
arma::field<arma::mat>& labels)
|
||||
{
|
||||
arma::mat input = predictors.at(0);
|
||||
arma::mat output = labels.at(0);
|
||||
size_t zeroCnt = 0, oneCnt = 0;
|
||||
for (size_t i = 1; i < input.n_rows; i += 2) {
|
||||
for (size_t i = 1; i < input.n_rows; i += 2)
|
||||
{
|
||||
size_t& addVar = (input.at(i, 0) == 0) ? zeroCnt : oneCnt;
|
||||
++addVar;
|
||||
}
|
||||
assert(oneCnt % zeroCnt == 0);
|
||||
nRepeats = oneCnt / zeroCnt;
|
||||
}
|
||||
void Predict(
|
||||
arma::mat& predictors,
|
||||
arma::mat& labels) {
|
||||
void Predict(arma::mat& predictors,
|
||||
arma::mat& labels)
|
||||
{
|
||||
size_t seqLen = (predictors.n_rows / 2) / (nRepeats + 1);
|
||||
size_t outputLen = nRepeats * seqLen;
|
||||
assert(2 * (seqLen + outputLen) == predictors.n_rows);
|
||||
labels.zeros(predictors.n_rows / 2, 1);
|
||||
for (size_t i = 0; i < outputLen; ++i) {
|
||||
for (size_t i = 0; i < outputLen; ++i)
|
||||
{
|
||||
labels.at(seqLen+i) = predictors.at(2 * (i % seqLen));
|
||||
}
|
||||
}
|
||||
void Predict(
|
||||
arma::field<arma::mat>& predictors,
|
||||
arma::field<arma::mat>& labels) {
|
||||
void Predict(arma::field<arma::mat>& predictors,
|
||||
arma::field<arma::mat>& labels)
|
||||
{
|
||||
size_t sz = predictors.n_elem;
|
||||
labels = arma::field<arma::mat>(sz);
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
{
|
||||
Predict(predictors.at(i), labels.at(i));
|
||||
}
|
||||
}
|
||||
@@ -87,6 +91,7 @@ class HardCodedCopyModel {
|
||||
size_t nRepeats;
|
||||
};
|
||||
|
||||
// The dummy model that simply sorts the sequence.
|
||||
class HardCodedSortModel {
|
||||
public:
|
||||
HardCodedSortModel(size_t bitLen) : bitLen(bitLen) {}
|
||||
@@ -95,34 +100,38 @@ class HardCodedSortModel {
|
||||
{
|
||||
assert(predictors.n_elem == labels.n_elem);
|
||||
}
|
||||
void Predict(
|
||||
arma::mat& predictors,
|
||||
arma::mat& labels)
|
||||
void Predict(arma::mat& predictors,
|
||||
arma::mat& labels)
|
||||
{
|
||||
predictors = predictors.t();
|
||||
predictors.reshape(bitLen, predictors.n_elem / bitLen);
|
||||
size_t len = predictors.n_cols;
|
||||
labels.zeros(bitLen, len);
|
||||
vector<pair<int, int>> vals(len);
|
||||
for (size_t j = 0; j < len; ++j) {
|
||||
for (size_t j = 0; j < len; ++j)
|
||||
{
|
||||
int val = 0;
|
||||
for (size_t k = 0; k < bitLen; ++k) {
|
||||
for (size_t k = 0; k < bitLen; ++k)
|
||||
{
|
||||
val <<= 1;
|
||||
val += predictors.at(k, j);
|
||||
}
|
||||
vals[j] = make_pair(val, j);
|
||||
}
|
||||
sort(vals.begin(), vals.end());
|
||||
for (size_t j = 0; j < len; ++j) {
|
||||
for (size_t j = 0; j < len; ++j)
|
||||
{
|
||||
labels.col(j) = predictors.col(vals[j].second);
|
||||
}
|
||||
labels.reshape(predictors.n_elem, 1);
|
||||
}
|
||||
void Predict(arma::field<arma::mat>& predictors,
|
||||
arma::field<arma::mat>& labels) {
|
||||
arma::field<arma::mat>& labels)
|
||||
{
|
||||
auto sz = predictors.n_elem;
|
||||
labels = arma::field<arma::mat>(sz);
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
{
|
||||
Predict(predictors.at(i), labels.at(i));
|
||||
}
|
||||
}
|
||||
@@ -131,22 +140,24 @@ class HardCodedSortModel {
|
||||
size_t bitLen;
|
||||
};
|
||||
|
||||
// The dummy model that simply add two binary numbers.
|
||||
class HardCodedAddModel {
|
||||
public:
|
||||
HardCodedAddModel() {}
|
||||
void Train(arma::field<arma::mat>& predictors,
|
||||
arma::field<arma::mat>& labels)
|
||||
{
|
||||
return;
|
||||
// Nothing to do here.
|
||||
}
|
||||
void Predict(arma::mat& predictors,
|
||||
arma::mat& labels)
|
||||
{
|
||||
int num_A = 0, num_B = 0;
|
||||
int numA = 0, numB = 0;
|
||||
bool num = false; // true iff we have already seen the separating symbol
|
||||
size_t len = predictors.n_elem;
|
||||
size_t cnt = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
double digit = predictors.at(i);
|
||||
if (digit != 0 && digit != 1)
|
||||
{
|
||||
@@ -160,28 +171,31 @@ class HardCodedAddModel {
|
||||
{
|
||||
if (num)
|
||||
{
|
||||
num_B += static_cast<int>(digit) << cnt;
|
||||
numB += static_cast<int>(digit) << cnt;
|
||||
}
|
||||
else
|
||||
{
|
||||
num_A += static_cast<int>(digit) << cnt;
|
||||
numA += static_cast<int>(digit) << cnt;
|
||||
}
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
int total = num_A + num_B;
|
||||
int total = numA + numB;
|
||||
vector<int> binary_seq;
|
||||
while (total > 0) {
|
||||
while (total > 0)
|
||||
{
|
||||
binary_seq.push_back(total & 1);
|
||||
total >>= 1;
|
||||
}
|
||||
if (binary_seq.empty()) {
|
||||
assert(num_A + num_B == 0);
|
||||
if (binary_seq.empty())
|
||||
{
|
||||
assert(numA + numB == 0);
|
||||
binary_seq.push_back(0);
|
||||
}
|
||||
size_t tot_len = binary_seq.size();
|
||||
labels = arma::zeros(tot_len);
|
||||
for (size_t j = 0; j < tot_len; ++j) {
|
||||
size_t totLen = binary_seq.size();
|
||||
labels = arma::zeros(totLen);
|
||||
for (size_t j = 0; j < totLen; ++j)
|
||||
{
|
||||
labels.at(j) = binary_seq[j];
|
||||
}
|
||||
labels.reshape(predictors.n_elem, 1);
|
||||
@@ -191,7 +205,8 @@ class HardCodedAddModel {
|
||||
arma::field<arma::mat>& labels) {
|
||||
size_t sz = predictors.n_elem;
|
||||
labels = arma::field<arma::mat>(sz);
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
{
|
||||
Predict(predictors.at(i), labels.at(i));
|
||||
}
|
||||
}
|
||||
@@ -208,9 +223,11 @@ BOOST_AUTO_TEST_SUITE(AugmentedRNNsTasks);
|
||||
BOOST_AUTO_TEST_CASE(CopyTaskTest)
|
||||
{
|
||||
// Check the setup on various lengths...
|
||||
for (size_t maxLen = 2; maxLen <= 16; ++maxLen) {
|
||||
for (size_t maxLen = 2; maxLen <= 16; ++maxLen)
|
||||
{
|
||||
// .. and various numbers of repetitions.
|
||||
for (size_t nRepeats = 1; nRepeats <= 10; ++nRepeats) {
|
||||
for (size_t nRepeats = 1; nRepeats <= 10; ++nRepeats)
|
||||
{
|
||||
CopyTask task(maxLen, nRepeats);
|
||||
arma::field<arma::mat> trainPredictor, trainResponse;
|
||||
task.Generate(trainPredictor, trainResponse, 8);
|
||||
@@ -230,9 +247,11 @@ BOOST_AUTO_TEST_CASE(CopyTaskTest)
|
||||
// Test of SortTask instance generator.
|
||||
// The data from generator is fed to the dummy hard-coded model above
|
||||
// that should be able to solve the task perfectly.
|
||||
BOOST_AUTO_TEST_CASE(SortTaskTest) {
|
||||
BOOST_AUTO_TEST_CASE(SortTaskTest)
|
||||
{
|
||||
size_t bitLen = 5;
|
||||
for (size_t maxLen = 2; maxLen <= 16; ++maxLen) {
|
||||
for (size_t maxLen = 2; maxLen <= 16; ++maxLen)
|
||||
{
|
||||
SortTask task(maxLen, bitLen);
|
||||
arma::field<arma::mat> trainPredictor, trainResponse;
|
||||
task.Generate(trainPredictor, trainResponse, 8);
|
||||
@@ -251,8 +270,10 @@ BOOST_AUTO_TEST_CASE(SortTaskTest) {
|
||||
// Test of AddTask instance generator.
|
||||
// The data from generator is fed to the dummy hard-coded model above
|
||||
// that should be able to solve the task perfectly.
|
||||
BOOST_AUTO_TEST_CASE(AddTaskTest) {
|
||||
for (size_t bitLen = 2; bitLen <= 16; ++bitLen) {
|
||||
BOOST_AUTO_TEST_CASE(AddTaskTest)
|
||||
{
|
||||
for (size_t bitLen = 2; bitLen <= 16; ++bitLen)
|
||||
{
|
||||
AddTask task(bitLen);
|
||||
arma::field<arma::mat> trainPredictor, trainResponse;
|
||||
task.Generate(trainPredictor, trainResponse, 8);
|
||||
|
||||
Reference in New Issue
Block a user