// Copyright (C) 2009 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_KCENTROId_OVERLOADS_ #define DLIB_KCENTROId_OVERLOADS_ #include "kcentroid_abstract.h" #include "sparse_kernel.h" #include "sparse_vector.h" #include <map> namespace dlib { /* This file contains optimized overloads of the kcentroid object for the following linear cases: kcentroid<linear_kernel<T>> kcentroid<sparse_linear_kernel<T>> kcentroid<offset_kernel<linear_kernel<T>>> kcentroid<offset_kernel<sparse_linear_kernel<T>>> */ // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // Overloads for when kernel_type == linear_kernel // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template <typename T> class kcentroid<linear_kernel<T> > { typedef linear_kernel<T> kernel_type; public: typedef typename kernel_type::scalar_type scalar_type; typedef typename kernel_type::sample_type sample_type; typedef typename kernel_type::mem_manager_type mem_manager_type; explicit kcentroid ( const kernel_type& kernel_, scalar_type tolerance_ = 0.001, unsigned long max_dictionary_size_ = 1000000, bool remove_oldest_first_ = false ) : my_remove_oldest_first(remove_oldest_first_), kernel(kernel_), my_tolerance(tolerance_), my_max_dictionary_size(max_dictionary_size_) { // make sure requires clause is not broken DLIB_ASSERT(tolerance_ >= 0 && max_dictionary_size_ > 0, "\tkcentroid::kcentroid()" << "\n\t You have to give a positive tolerance" << "\n\t this: " << this << "\n\t tolerance_: " << tolerance_ << "\n\t max_dictionary_size_: " << max_dictionary_size_ ); clear_dictionary(); } scalar_type tolerance() const { return my_tolerance; } unsigned long max_dictionary_size() const { return my_max_dictionary_size; } bool remove_oldest_first () const { return my_remove_oldest_first; } const kernel_type& get_kernel () const { return kernel; } scalar_type samples_trained () const { return samples_seen; } void clear_dictionary () { samples_seen = 0; set_all_elements(w, 0); alpha = 0; } scalar_type operator() ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::operator()(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); if (w.size() > 0) { if (x.w.size() > 0) return length(alpha*w - x.alpha*x.w); else return alpha*length(w); } else { if (x.w.size() > 0) return x.alpha*length(x.w); else return 0; } } scalar_type inner_product ( const sample_type& x ) const { if (w.size() > 0) return alpha*trans(w)*x; else return 0; } scalar_type inner_product ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::inner_product(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); if (w.size() > 0 && x.w.size() > 0) return alpha*x.alpha*trans(w)*x.w; else return 0; } scalar_type squared_norm ( ) const { if (w.size() > 0) return alpha*alpha*trans(w)*w; else return 0; } scalar_type operator() ( const sample_type& x ) const { if (w.size() > 0) return length(x-alpha*w); else return length(x); } scalar_type test_and_train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); return (*this)(x); } void train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); } scalar_type test_and_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); return (*this)(x); } void scale_by ( scalar_type cscale ) { alpha *= cscale; } void train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); } void swap ( kcentroid& item ) { exchange(my_remove_oldest_first, item.my_remove_oldest_first); exchange(kernel, item.kernel); exchange(w, item.w); exchange(alpha, item.alpha); exchange(my_tolerance, item.my_tolerance); exchange(my_max_dictionary_size, item.my_max_dictionary_size); exchange(samples_seen, item.samples_seen); } unsigned long dictionary_size ( ) const { if (samples_seen > 0) return 1; else return 0; } friend void serialize(const kcentroid& item, std::ostream& out) { serialize(item.my_remove_oldest_first, out); serialize(item.kernel, out); serialize(item.w, out); serialize(item.alpha, out); serialize(item.my_tolerance, out); serialize(item.my_max_dictionary_size, out); serialize(item.samples_seen, out); } friend void deserialize(kcentroid& item, std::istream& in) { deserialize(item.my_remove_oldest_first, in); deserialize(item.kernel, in); deserialize(item.w, in); deserialize(item.alpha, in); deserialize(item.my_tolerance, in); deserialize(item.my_max_dictionary_size, in); deserialize(item.samples_seen, in); } distance_function<kernel_type> get_distance_function ( ) const { if (samples_seen > 0) { typename distance_function<kernel_type>::sample_vector_type temp_basis_vectors; typename distance_function<kernel_type>::scalar_vector_type temp_alpha; temp_basis_vectors.set_size(1); temp_basis_vectors(0) = w; temp_alpha.set_size(1); temp_alpha(0) = alpha; return distance_function<kernel_type>(temp_alpha, squared_norm(), kernel, temp_basis_vectors); } else { return distance_function<kernel_type>(kernel); } } private: void do_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { set_size_of_w(x); const scalar_type temp = cscale*alpha; if (temp != 0) { w = w + xscale*x/temp; alpha = temp; } else { w = cscale*alpha*w + xscale*x; alpha = 1; } } void set_size_of_w ( const sample_type& x ) { if (x.size() != w.size()) { w.set_size(x.nr(), x.nc()); set_all_elements(w, 0); alpha = 0; } } bool my_remove_oldest_first; kernel_type kernel; sample_type w; scalar_type alpha; scalar_type my_tolerance; unsigned long my_max_dictionary_size; scalar_type samples_seen; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // Overloads for when kernel_type == offset_kernel<linear_kernel> // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template <typename T> class kcentroid<offset_kernel<linear_kernel<T> > > { /*! INITIAL VALUE - x_extra == sqrt(kernel.offset) CONVENTION - x_extra == sqrt(kernel.offset) - w_extra == the value of the extra dimension tacked onto the end of the w vector !*/ typedef offset_kernel<linear_kernel<T> > kernel_type; public: typedef typename kernel_type::scalar_type scalar_type; typedef typename kernel_type::sample_type sample_type; typedef typename kernel_type::mem_manager_type mem_manager_type; explicit kcentroid ( const kernel_type& kernel_, scalar_type tolerance_ = 0.001, unsigned long max_dictionary_size_ = 1000000, bool remove_oldest_first_ = false ) : my_remove_oldest_first(remove_oldest_first_), kernel(kernel_), my_tolerance(tolerance_), my_max_dictionary_size(max_dictionary_size_) { // make sure requires clause is not broken DLIB_ASSERT(tolerance_ >= 0 && max_dictionary_size_ > 0, "\tkcentroid::kcentroid()" << "\n\t You have to give a positive tolerance" << "\n\t this: " << this << "\n\t tolerance_: " << tolerance_ << "\n\t max_dictionary_size_: " << max_dictionary_size_ ); x_extra = std::sqrt(kernel.offset); clear_dictionary(); } scalar_type tolerance() const { return my_tolerance; } unsigned long max_dictionary_size() const { return my_max_dictionary_size; } bool remove_oldest_first () const { return my_remove_oldest_first; } const kernel_type& get_kernel () const { return kernel; } scalar_type samples_trained () const { return samples_seen; } void clear_dictionary () { samples_seen = 0; set_all_elements(w, 0); alpha = 0; w_extra = x_extra; } scalar_type operator() ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::operator()(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); if (w.size() > 0) { if (x.w.size() > 0) { scalar_type temp1 = length_squared(alpha*w - x.alpha*x.w); scalar_type temp2 = alpha*w_extra - x.alpha*x.w_extra; return std::sqrt(temp1 + temp2*temp2); } else { return alpha*std::sqrt(length_squared(w) + w_extra*w_extra); } } else { if (x.w.size() > 0) return x.alpha*std::sqrt(length_squared(x.w) + x.w_extra*x.w_extra); else return 0; } } scalar_type inner_product ( const sample_type& x ) const { if (w.size() > 0) return alpha*(trans(w)*x + w_extra*x_extra); else return 0; } scalar_type inner_product ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::inner_product(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); if (w.size() > 0 && x.w.size() > 0) return alpha*x.alpha*(trans(w)*x.w + w_extra*x.w_extra); else return 0; } scalar_type squared_norm ( ) const { if (w.size() > 0) return alpha*alpha*(trans(w)*w + w_extra*w_extra); else return 0; } scalar_type operator() ( const sample_type& x ) const { if (w.size() > 0) { scalar_type temp1 = length_squared(x-alpha*w); scalar_type temp2 = x_extra - alpha*w_extra; return std::sqrt(temp1 + temp2*temp2); } else { return std::sqrt(length_squared(x) + x_extra*x_extra); } } scalar_type test_and_train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); return (*this)(x); } void train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); } scalar_type test_and_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); return (*this)(x); } void scale_by ( scalar_type cscale ) { alpha *= cscale; w_extra *= cscale; } void train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); } void swap ( kcentroid& item ) { exchange(my_remove_oldest_first, item.my_remove_oldest_first); exchange(kernel, item.kernel); exchange(w, item.w); exchange(alpha, item.alpha); exchange(w_extra, item.w_extra); exchange(x_extra, item.x_extra); exchange(my_tolerance, item.my_tolerance); exchange(my_max_dictionary_size, item.my_max_dictionary_size); exchange(samples_seen, item.samples_seen); } unsigned long dictionary_size ( ) const { if (samples_seen > 0) { if (std::abs(w_extra) > std::numeric_limits<scalar_type>::epsilon()) return 1; else return 2; } else return 0; } friend void serialize(const kcentroid& item, std::ostream& out) { serialize(item.my_remove_oldest_first, out); serialize(item.kernel, out); serialize(item.w, out); serialize(item.alpha, out); serialize(item.w_extra, out); serialize(item.x_extra, out); serialize(item.my_tolerance, out); serialize(item.my_max_dictionary_size, out); serialize(item.samples_seen, out); } friend void deserialize(kcentroid& item, std::istream& in) { deserialize(item.my_remove_oldest_first, in); deserialize(item.kernel, in); deserialize(item.w, in); deserialize(item.alpha, in); deserialize(item.w_extra, in); deserialize(item.x_extra, in); deserialize(item.my_tolerance, in); deserialize(item.my_max_dictionary_size, in); deserialize(item.samples_seen, in); } distance_function<kernel_type> get_distance_function ( ) const { if (samples_seen > 0) { typename distance_function<kernel_type>::sample_vector_type temp_basis_vectors; typename distance_function<kernel_type>::scalar_vector_type temp_alpha; // What we are doing here needs a bit of explanation. The w vector // has an implicit extra dimension tacked on to it with the value of w_extra. // The kernel we are using takes normal vectors and implicitly tacks the value // x_extra onto their end. So what we are doing here is scaling w so that // the value it should have tacked onto it is x_scale. Note that we also // adjust alpha so that the combination of alpha*w stays the same. scalar_type scale; // if w_extra is basically greater than 0 if (std::abs(w_extra) > std::numeric_limits<scalar_type>::epsilon()) { scale = (x_extra/w_extra); temp_basis_vectors.set_size(1); temp_alpha.set_size(1); temp_basis_vectors(0) = w*scale; temp_alpha(0) = alpha/scale; } else { // In this case w_extra is zero. So the only way we can get the same // thing in the output basis vector set is by using two vectors temp_basis_vectors.set_size(2); temp_alpha.set_size(2); temp_basis_vectors(0) = 2*w; temp_alpha(0) = alpha; temp_basis_vectors(1) = w; temp_alpha(1) = -alpha; } return distance_function<kernel_type>(temp_alpha, squared_norm(), kernel, temp_basis_vectors); } else { return distance_function<kernel_type>(kernel); } } private: void do_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { set_size_of_w(x); const scalar_type temp = cscale*alpha; if (temp != 0) { w = w + xscale*x/temp; w_extra = w_extra + xscale*x_extra/temp; alpha = temp; } else { w = cscale*alpha*w + xscale*x; w_extra = cscale*alpha*w_extra + xscale*x_extra; alpha = 1; } } void set_size_of_w ( const sample_type& x ) { if (x.size() != w.size()) { w.set_size(x.nr(), x.nc()); set_all_elements(w, 0); alpha = 0; w_extra = x_extra; } } bool my_remove_oldest_first; kernel_type kernel; sample_type w; scalar_type alpha; scalar_type w_extra; scalar_type x_extra; scalar_type my_tolerance; unsigned long my_max_dictionary_size; scalar_type samples_seen; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // Overloads for when kernel_type == sparse_linear_kernel // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template <typename T> class kcentroid<sparse_linear_kernel<T> > { typedef sparse_linear_kernel<T> kernel_type; public: typedef typename kernel_type::scalar_type scalar_type; typedef typename kernel_type::sample_type sample_type; typedef typename kernel_type::mem_manager_type mem_manager_type; explicit kcentroid ( const kernel_type& kernel_, scalar_type tolerance_ = 0.001, unsigned long max_dictionary_size_ = 1000000, bool remove_oldest_first_ = false ) : my_remove_oldest_first(remove_oldest_first_), kernel(kernel_), my_tolerance(tolerance_), my_max_dictionary_size(max_dictionary_size_) { // make sure requires clause is not broken DLIB_ASSERT(tolerance_ >= 0 && max_dictionary_size_ > 0, "\tkcentroid::kcentroid()" << "\n\t You have to give a positive tolerance" << "\n\t this: " << this << "\n\t tolerance_: " << tolerance_ << "\n\t max_dictionary_size_: " << max_dictionary_size_ ); clear_dictionary(); } scalar_type tolerance() const { return my_tolerance; } unsigned long max_dictionary_size() const { return my_max_dictionary_size; } bool remove_oldest_first () const { return my_remove_oldest_first; } const kernel_type& get_kernel () const { return kernel; } scalar_type samples_trained () const { return samples_seen; } void clear_dictionary () { samples_seen = 0; w.clear(); alpha = 0; } scalar_type operator() ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::operator()(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); return distance(alpha,w , x.alpha,x.w); } scalar_type inner_product ( const sample_type& x ) const { return alpha*dot(w,x); } scalar_type inner_product ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::inner_product(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); return alpha*x.alpha*dot(w,x.w); } scalar_type squared_norm ( ) const { return alpha*alpha*length_squared(w); } scalar_type operator() ( const sample_type& x ) const { return distance(static_cast<scalar_type>(1), x, alpha, w); } scalar_type test_and_train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); return (*this)(x); } void train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); } scalar_type test_and_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); return (*this)(x); } void scale_by ( scalar_type cscale ) { alpha *= cscale; } void train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); } void swap ( kcentroid& item ) { exchange(my_remove_oldest_first, item.my_remove_oldest_first); exchange(kernel, item.kernel); exchange(w, item.w); exchange(alpha, item.alpha); exchange(my_tolerance, item.my_tolerance); exchange(my_max_dictionary_size, item.my_max_dictionary_size); exchange(samples_seen, item.samples_seen); } unsigned long dictionary_size ( ) const { if (samples_seen > 0) return 1; else return 0; } friend void serialize(const kcentroid& item, std::ostream& out) { serialize(item.my_remove_oldest_first, out); serialize(item.kernel, out); serialize(item.w, out); serialize(item.alpha, out); serialize(item.my_tolerance, out); serialize(item.my_max_dictionary_size, out); serialize(item.samples_seen, out); } friend void deserialize(kcentroid& item, std::istream& in) { deserialize(item.my_remove_oldest_first, in); deserialize(item.kernel, in); deserialize(item.w, in); deserialize(item.alpha, in); deserialize(item.my_tolerance, in); deserialize(item.my_max_dictionary_size, in); deserialize(item.samples_seen, in); } distance_function<kernel_type> get_distance_function ( ) const { if (samples_seen > 0) { typename distance_function<kernel_type>::sample_vector_type temp_basis_vectors; typename distance_function<kernel_type>::scalar_vector_type temp_alpha; temp_basis_vectors.set_size(1); temp_basis_vectors(0) = sample_type(w.begin(), w.end()); temp_alpha.set_size(1); temp_alpha(0) = alpha; return distance_function<kernel_type>(temp_alpha, squared_norm(), kernel, temp_basis_vectors); } else { return distance_function<kernel_type>(kernel); } } private: void do_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { const scalar_type temp = cscale*alpha; if (temp != 0) { // compute w += xscale*x/temp typename sample_type::const_iterator i; for (i = x.begin(); i != x.end(); ++i) { w[i->first] += xscale*(i->second)/temp; } alpha = temp; } else { // first compute w = cscale*alpha*w for (typename std::map<unsigned long,scalar_type>::iterator i = w.begin(); i != w.end(); ++i) { i->second *= cscale*alpha; } // now compute w += xscale*x for (typename sample_type::const_iterator i = x.begin(); i != x.end(); ++i) { w[i->first] += xscale*(i->second); } alpha = 1; } } bool my_remove_oldest_first; kernel_type kernel; std::map<unsigned long,scalar_type> w; scalar_type alpha; scalar_type my_tolerance; unsigned long my_max_dictionary_size; scalar_type samples_seen; }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // Overloads for when kernel_type == offset_kernel<sparse_linear_kernel> // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template <typename T> class kcentroid<offset_kernel<sparse_linear_kernel<T> > > { /*! INITIAL VALUE - x_extra == sqrt(kernel.offset) CONVENTION - x_extra == sqrt(kernel.offset) - w_extra == the value of the extra dimension tacked onto the end of the w vector !*/ typedef offset_kernel<sparse_linear_kernel<T> > kernel_type; public: typedef typename kernel_type::scalar_type scalar_type; typedef typename kernel_type::sample_type sample_type; typedef typename kernel_type::mem_manager_type mem_manager_type; explicit kcentroid ( const kernel_type& kernel_, scalar_type tolerance_ = 0.001, unsigned long max_dictionary_size_ = 1000000, bool remove_oldest_first_ = false ) : my_remove_oldest_first(remove_oldest_first_), kernel(kernel_), my_tolerance(tolerance_), my_max_dictionary_size(max_dictionary_size_) { // make sure requires clause is not broken DLIB_ASSERT(tolerance_ >= 0 && max_dictionary_size_ > 0, "\tkcentroid::kcentroid()" << "\n\t You have to give a positive tolerance" << "\n\t this: " << this << "\n\t tolerance_: " << tolerance_ << "\n\t max_dictionary_size_: " << max_dictionary_size_ ); x_extra = std::sqrt(kernel.offset); clear_dictionary(); } scalar_type tolerance() const { return my_tolerance; } unsigned long max_dictionary_size() const { return my_max_dictionary_size; } bool remove_oldest_first () const { return my_remove_oldest_first; } const kernel_type& get_kernel () const { return kernel; } scalar_type samples_trained () const { return samples_seen; } void clear_dictionary () { samples_seen = 0; w.clear(); alpha = 0; w_extra = x_extra; } scalar_type operator() ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::operator()(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); if (samples_seen > 0) { scalar_type temp1 = distance_squared(alpha,w , x.alpha,x.w); scalar_type temp2 = alpha*w_extra - x.alpha*x.w_extra; return std::sqrt(temp1 + temp2*temp2); } else { return 0; } } scalar_type inner_product ( const sample_type& x ) const { if (samples_seen > 0) return alpha*(dot(w,x) + w_extra*x_extra); else return 0; } scalar_type inner_product ( const kcentroid& x ) const { // make sure requires clause is not broken DLIB_ASSERT(x.get_kernel() == get_kernel(), "\tscalar_type kcentroid::inner_product(const kcentroid& x)" << "\n\tYou can only compare two kcentroid objects if they use the same kernel" << "\n\tthis: " << this ); if (samples_seen > 0 && x.samples_seen > 0) return alpha*x.alpha*(dot(w,x.w) + w_extra*x.w_extra); else return 0; } scalar_type squared_norm ( ) const { if (samples_seen > 0) return alpha*alpha*(length_squared(w) + w_extra*w_extra); else return 0; } scalar_type operator() ( const sample_type& x ) const { if (samples_seen > 0) { scalar_type temp1 = distance_squared(1,x,alpha,w); scalar_type temp2 = x_extra - alpha*w_extra; return std::sqrt(temp1 + temp2*temp2); } else { return std::sqrt(length_squared(x) + x_extra*x_extra); } } scalar_type test_and_train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); return (*this)(x); } void train ( const sample_type& x ) { ++samples_seen; const scalar_type xscale = 1/samples_seen; const scalar_type cscale = 1-xscale; do_train(x, cscale, xscale); } scalar_type test_and_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); return (*this)(x); } void scale_by ( scalar_type cscale ) { alpha *= cscale; w_extra *= cscale; } void train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { ++samples_seen; do_train(x, cscale, xscale); } void swap ( kcentroid& item ) { exchange(my_remove_oldest_first, item.my_remove_oldest_first); exchange(kernel, item.kernel); exchange(w, item.w); exchange(alpha, item.alpha); exchange(w_extra, item.w_extra); exchange(x_extra, item.x_extra); exchange(my_tolerance, item.my_tolerance); exchange(my_max_dictionary_size, item.my_max_dictionary_size); exchange(samples_seen, item.samples_seen); } unsigned long dictionary_size ( ) const { if (samples_seen > 0) { if (std::abs(w_extra) > std::numeric_limits<scalar_type>::epsilon()) return 1; else return 2; } else { return 0; } } friend void serialize(const kcentroid& item, std::ostream& out) { serialize(item.my_remove_oldest_first, out); serialize(item.kernel, out); serialize(item.w, out); serialize(item.alpha, out); serialize(item.w_extra, out); serialize(item.x_extra, out); serialize(item.my_tolerance, out); serialize(item.my_max_dictionary_size, out); serialize(item.samples_seen, out); } friend void deserialize(kcentroid& item, std::istream& in) { deserialize(item.my_remove_oldest_first, in); deserialize(item.kernel, in); deserialize(item.w, in); deserialize(item.alpha, in); deserialize(item.w_extra, in); deserialize(item.x_extra, in); deserialize(item.my_tolerance, in); deserialize(item.my_max_dictionary_size, in); deserialize(item.samples_seen, in); } distance_function<kernel_type> get_distance_function ( ) const { if (samples_seen > 0) { typename distance_function<kernel_type>::sample_vector_type temp_basis_vectors; typename distance_function<kernel_type>::scalar_vector_type temp_alpha; // What we are doing here needs a bit of explanation. The w vector // has an implicit extra dimension tacked on to it with the value of w_extra. // The kernel we are using takes normal vectors and implicitly tacks the value // x_extra onto their end. So what we are doing here is scaling w so that // the value it should have tacked onto it is x_scale. Note that we also // adjust alpha so that the combination of alpha*w stays the same. scalar_type scale; // if w_extra is basically greater than 0 if (std::abs(w_extra) > std::numeric_limits<scalar_type>::epsilon()) { scale = (x_extra/w_extra); temp_basis_vectors.set_size(1); temp_alpha.set_size(1); temp_basis_vectors(0) = sample_type(w.begin(), w.end()); dlib::scale_by(temp_basis_vectors(0), scale); temp_alpha(0) = alpha/scale; } else { // In this case w_extra is zero. So the only way we can get the same // thing in the output basis vector set is by using two vectors temp_basis_vectors.set_size(2); temp_alpha.set_size(2); temp_basis_vectors(0) = sample_type(w.begin(), w.end()); dlib::scale_by(temp_basis_vectors(0), 2); temp_alpha(0) = alpha; temp_basis_vectors(1) = sample_type(w.begin(), w.end()); temp_alpha(1) = -alpha; } return distance_function<kernel_type>(temp_alpha, squared_norm(), kernel, temp_basis_vectors); } else { return distance_function<kernel_type>(kernel); } } private: void do_train ( const sample_type& x, scalar_type cscale, scalar_type xscale ) { const scalar_type temp = cscale*alpha; if (temp != 0) { // compute w += xscale*x/temp typename sample_type::const_iterator i; for (i = x.begin(); i != x.end(); ++i) { w[i->first] += xscale*(i->second)/temp; } w_extra = w_extra + xscale*x_extra/temp; alpha = temp; } else { // first compute w = cscale*alpha*w for (typename std::map<unsigned long,scalar_type>::iterator i = w.begin(); i != w.end(); ++i) { i->second *= cscale*alpha; } // now compute w += xscale*x for (typename sample_type::const_iterator i = x.begin(); i != x.end(); ++i) { w[i->first] += xscale*(i->second); } w_extra = cscale*alpha*w_extra + xscale*x_extra; alpha = 1; } } bool my_remove_oldest_first; kernel_type kernel; std::map<unsigned long,scalar_type> w; scalar_type alpha; scalar_type w_extra; scalar_type x_extra; scalar_type my_tolerance; unsigned long my_max_dictionary_size; scalar_type samples_seen; }; // ---------------------------------------------------------------------------------------- } #endif // DLIB_KCENTROId_OVERLOADS_