Commit 306cd1a2 authored by Davis King's avatar Davis King

Improved isotonic_regression class.

parent cb481f27
......@@ -5,6 +5,7 @@
#include "isotonic_regression_abstract.h"
#include <vector>
#include <utility>
namespace dlib
{
......@@ -22,22 +23,13 @@ namespace dlib
iterator obegin
)
{
for (auto i = begin; i != end; ++i)
{
blocks.emplace_back(*i);
while (blocks.size() > 1 && prev_block().avg > current_block().avg)
{
// merge the last two blocks.
prev_block() = prev_block() + current_block();
blocks.pop_back();
}
}
do_isotonic_regression(begin, end);
// unpack results to output
// unpack blocks to output
for (auto& block : blocks)
{
for (size_t k = 0; k < block.num; ++k)
*obegin++ = block.avg;
set_val(*obegin++, block.avg);
}
blocks.clear();
......@@ -47,8 +39,108 @@ namespace dlib
std::vector<double>& vect
) { (*this)(vect.begin(), vect.end(), vect.begin()); }
template <typename T, typename U>
void operator() (
std::vector<std::pair<T,U>>& vect
) { (*this)(vect.begin(), vect.end(), vect.begin()); }
template <
typename const_iterator,
typename iterator
>
void fit_with_linear_output_interpolation (
const_iterator begin,
const_iterator end,
iterator obegin
)
{
do_isotonic_regression(begin, end);
// Unpack blocks to output, but here instead of producing the step function
// output we linearly interpolate. Note that this actually fits the data less
// than the step-function, but in many applications might be closer to what you
// really when when using isotonic_regression than the step function.
for (size_t i = 0; i < blocks.size(); ++i)
{
auto& block = blocks[i];
double prev = (blocks.front().avg + block.avg)/2;
if (i > 0)
prev = (blocks[i-1].avg+block.avg)/2;
double next = (blocks.back().avg + block.avg)/2;
if (i+1 < blocks.size())
next = (blocks[i+1].avg+block.avg)/2;
for (size_t k = 0; k < block.num; ++k)
{
const auto mid = block.num/2.0;
if (k < mid)
{
const double alpha = k/mid;
set_val(*obegin++, (1-alpha)*prev + alpha*block.avg);
}
else
{
const double alpha = k/mid-1;
set_val(*obegin++, alpha*next + (1-alpha)*block.avg);
}
}
}
blocks.clear();
}
void fit_with_linear_output_interpolation (
std::vector<double>& vect
) { fit_with_linear_output_interpolation(vect.begin(), vect.end(), vect.begin()); }
template <typename T, typename U>
void fit_with_linear_output_interpolation (
std::vector<std::pair<T,U>>& vect
) { fit_with_linear_output_interpolation(vect.begin(), vect.end(), vect.begin()); }
private:
template <
typename const_iterator
>
void do_isotonic_regression (
const_iterator begin,
const_iterator end
)
{
blocks.clear();
// Do the actual isotonic regression. The output is a step-function and is
// stored in the vector of blocks.
for (auto i = begin; i != end; ++i)
{
blocks.emplace_back(get_val(*i));
while (blocks.size() > 1 && prev_block().avg > current_block().avg)
{
// merge the last two blocks.
prev_block() = prev_block() + current_block();
blocks.pop_back();
}
}
}
template <typename T>
static double get_val(const T& v) { return v;}
template <typename T, typename U>
static double get_val(const std::pair<T,U>& v) { return v.second;}
template <typename T>
static void set_val(T& v, double val) { v = val;}
template <typename T, typename U>
static void set_val(std::pair<T,U>& v, double val) { v.second = val;}
struct block_t
{
......
......@@ -4,6 +4,7 @@
#ifdef DLIB_ISOTONIC_ReGRESSION_ABSTRACT_H_
#include <vector>
#include <utility>
namespace dlib
{
......@@ -31,9 +32,10 @@ namespace dlib
);
/*!
requires
- [begin,end) is an iterator range of float or doubles.
- obegin points to an iterator range at least std::distance(begin,end) in
size which is capable of storing double or float values.
- [begin,end) is an iterator range of float or doubles or a range of
std::pair<T,double> or std::pair<T,float> where T an be anything.
- obegin points to an iterator range at least std::distance(begin,end).
- obegin points to an iterator range of objects of type float, double, std::pair<T,float>, or std::pair<T,double>.
ensures
- Given the range of real values stored in [begin,end), this method performs isotonic regression
on this data and writes the results to obegin. To be specific:
......@@ -44,6 +46,10 @@ namespace dlib
OUT[i] <= OUT[i+1], i.e. that OUT is monotonic.
- It is OK for [begin,end) to overlap with the range pointed to by obegin.
That is, this function can run in-place.
- Note that when the inputs or outputs are std::pairs this algorithm only
looks at the .second field of the pair. It therefore still treats these
iterator ranges as ranges of reals since it only looks at the .second
field, which is a real number. The .first field is entirely ignored.
!*/
void operator() (
......@@ -56,6 +62,63 @@ namespace dlib
- #vect.size() == vect.size()
!*/
template <typename T, typename U>
void operator() (
std::vector<std::pair<T,U>>& vect
) { (*this)(vect.begin(), vect.end(), vect.begin()); }
/*!
ensures
- performs in-place isotonic regression. Therefore, #vect will contain the
isotonic regression of vect.
- #vect.size() == vect.size()
!*/
template <
typename const_iterator,
typename iterator
>
void fit_with_linear_output_interpolation (
const_iterator begin,
const_iterator end,
iterator obegin
);
/*!
requires
- [begin,end) is an iterator range of float or doubles or a range of
std::pair<T,double> or std::pair<T,float> where T an be anything.
- obegin points to an iterator range at least std::distance(begin,end).
- obegin points to an iterator range of objects of type float, double, std::pair<T,float>, or std::pair<T,double>.
ensures
- This function behaves just like (*this)(begin,end,obegin) except that the
output is interpolated. To explain, not that the optimal output of
isotonic regression is a step function. However, in many applications
that isn't really what you want. You want something smoother. So
fit_with_linear_output_interpolation() does isotonic regression and then
linearly interpolates the step function into a piecewise linear function.
!*/
void fit_with_linear_output_interpolation (
std::vector<double>& vect
) { fit_with_linear_output_interpolation(vect.begin(), vect.end(), vect.begin()); }
/*!
ensures
- performs in-place isotonic regression. Therefore, #vect will contain the
isotonic regression of vect.
- #vect.size() == vect.size()
!*/
template <typename T, typename U>
void fit_with_linear_output_interpolation (
std::vector<std::pair<T,U>>& vect
) { fit_with_linear_output_interpolation(vect.begin(), vect.end(), vect.begin()); }
/*!
ensures
- performs in-place isotonic regression. Therefore, #vect will contain the
isotonic regression of vect.
- #vect.size() == vect.size()
!*/
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment