Fixed issues from @zoq's review

This commit is contained in:
Konstantin Sidorov
2017-07-31 12:44:16 +03:00
parent 0c7459a639
commit 16d3bfbcc1
8 changed files with 96 additions and 90 deletions
@@ -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
+61 -40
View File
@@ -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);