From 55d21096de290b0de78a7448d24746a94d3902e0 Mon Sep 17 00:00:00 2001 From: Davis King Date: Wed, 5 Dec 2012 23:11:55 -0500 Subject: [PATCH] Changed the feature extraction interfaces for the sequence labeling and assignment problem learning tools to also include an optional num_nonnegative_weights() method. This method can be used to tell any machine learning tools which elements of the learned parameter vector should be non-negative. As part of this change, I also removed the previous methods for doing this from the structural_assignment_trainer since they are now redundant. --- dlib/svm/assignment_function_abstract.h | 17 +++++ dlib/svm/num_nonnegative_weights.h | 76 +++++++++++++++++++ dlib/svm/sequence_labeler_abstract.h | 17 +++++ dlib/svm/structural_assignment_trainer.h | 21 +---- .../structural_assignment_trainer_abstract.h | 19 ----- .../structural_sequence_labeling_trainer.h | 3 +- 6 files changed, 114 insertions(+), 39 deletions(-) create mode 100644 dlib/svm/num_nonnegative_weights.h diff --git a/dlib/svm/assignment_function_abstract.h b/dlib/svm/assignment_function_abstract.h index 8bd4870bd..ed9fcc580 100644 --- a/dlib/svm/assignment_function_abstract.h +++ b/dlib/svm/assignment_function_abstract.h @@ -75,6 +75,23 @@ namespace dlib is "good"). !*/ + unsigned long num_nonnegative_weights ( + ) const; + /*! + ensures + - returns the number of elements of the w parameter vector which should be + non-negative. That is, this feature extractor is intended to be used + with w vectors where the first num_nonnegative_weights() elements of w + are >= 0. That is, it should be the case that w(i) >= 0 for all i < + num_nonnegative_weights(). + - Note that num_nonnegative_weights() is just an optional method to allow + you to tell a tool like the structural_assignment_trainer that the + learned w should have a certain number of non-negative elements. + Therefore, if you do not provide a num_nonnegative_weights() method in + your feature extractor then it will default to a value of 0, indicating + that all elements of the w parameter vector may be any value. + !*/ + }; // ---------------------------------------------------------------------------------------- diff --git a/dlib/svm/num_nonnegative_weights.h b/dlib/svm/num_nonnegative_weights.h new file mode 100644 index 000000000..f7a171d1f --- /dev/null +++ b/dlib/svm/num_nonnegative_weights.h @@ -0,0 +1,76 @@ +// Copyright (C) 2012 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_NUM_NONNEGATIVE_WEIGHtS_H__ +#define DLIB_NUM_NONNEGATIVE_WEIGHtS_H__ + +#include "../enable_if.h" + +namespace dlib +{ + + namespace impl2 + { + template < + typename T, + unsigned long (T::*funct)()const + > + struct hnnf_helper + { + typedef char type; + }; + + template + char has_num_nonnegative_weights_helper( typename hnnf_helper::type = 0 ) { return 0;} + + struct two_bytes + { + char a[2]; + }; + + template + two_bytes has_num_nonnegative_weights_helper(int) { return two_bytes();} + + template + struct work_around_visual_studio_bug + { + const static unsigned long U = sizeof(has_num_nonnegative_weights_helper('a')); + }; + + + // This is a template to tell you if a feature_extractor has a num_nonnegative_weights function or not. + template ::U > + struct has_num_nonnegative_weights + { + static const bool value = false; + }; + + template + struct has_num_nonnegative_weights + { + static const bool value = true; + }; + + + } + + // call fe.num_nonnegative_weights() if it exists, otherwise return 0. + template + typename enable_if,unsigned long>::type num_nonnegative_weights ( + const feature_extractor& fe + ) + { + return fe.num_nonnegative_weights(); + } + + template + typename disable_if,unsigned long>::type num_nonnegative_weights ( + const feature_extractor& /*fe*/ + ) + { + return 0; + } + +} + +#endif // DLIB_NUM_NONNEGATIVE_WEIGHtS_H__ + diff --git a/dlib/svm/sequence_labeler_abstract.h b/dlib/svm/sequence_labeler_abstract.h index 9a26d7514..87075a4ac 100644 --- a/dlib/svm/sequence_labeler_abstract.h +++ b/dlib/svm/sequence_labeler_abstract.h @@ -146,6 +146,23 @@ namespace dlib - This function only calls set_feature() with feature_index values < num_features() !*/ + unsigned long num_nonnegative_weights ( + ) const; + /*! + ensures + - returns the number of elements of the w parameter vector which should be + non-negative. That is, this feature extractor is intended to be used + with w vectors where the first num_nonnegative_weights() elements of w + are >= 0. That is, it should be the case that w(i) >= 0 for all i < + num_nonnegative_weights(). + - Note that num_nonnegative_weights() is just an optional method to allow + you to tell a tool like the structural_sequence_labeling_trainer that the + learned w should have a certain number of non-negative elements. + Therefore, if you do not provide a num_nonnegative_weights() method in + your feature extractor then it will default to a value of 0, indicating + that all elements of the w parameter vector may be any value. + !*/ + }; // ---------------------------------------------------------------------------------------- diff --git a/dlib/svm/structural_assignment_trainer.h b/dlib/svm/structural_assignment_trainer.h index 73b62d638..101c5e0e4 100644 --- a/dlib/svm/structural_assignment_trainer.h +++ b/dlib/svm/structural_assignment_trainer.h @@ -7,6 +7,7 @@ #include "../algs.h" #include "../optimization.h" #include "structural_svm_assignment_problem.h" +#include "num_nonnegative_weights.h" namespace dlib @@ -55,16 +56,6 @@ namespace dlib return num_threads; } - bool learns_nonnegative_weights ( - ) const { return learn_nonnegative_weights; } - - void set_learns_nonnegative_weights ( - bool value - ) - { - learn_nonnegative_weights = value; - } - void set_epsilon ( double eps_ ) @@ -193,13 +184,7 @@ namespace dlib matrix weights; - unsigned long num_nonnegative = 0; - if (learn_nonnegative_weights) - { - num_nonnegative = fe.num_features(); - } - - solver(prob, weights, num_nonnegative); + solver(prob, weights, num_nonnegative_weights(fe)); return assignment_function(weights,fe,force_assignment); @@ -208,7 +193,6 @@ namespace dlib private: - bool learn_nonnegative_weights; bool force_assignment; double C; oca solver; @@ -225,7 +209,6 @@ namespace dlib eps = 0.1; num_threads = 2; max_cache_size = 40; - learn_nonnegative_weights = false; } feature_extractor fe; diff --git a/dlib/svm/structural_assignment_trainer_abstract.h b/dlib/svm/structural_assignment_trainer_abstract.h index b579a673c..e731a72d6 100644 --- a/dlib/svm/structural_assignment_trainer_abstract.h +++ b/dlib/svm/structural_assignment_trainer_abstract.h @@ -52,7 +52,6 @@ namespace dlib - #get_max_cache_size() == 40 - #get_feature_extractor() == a default initialized feature_extractor - #forces_assignment() == false - - #learns_nonnegative_weights() == false !*/ explicit structural_assignment_trainer ( @@ -67,7 +66,6 @@ namespace dlib - #get_max_cache_size() == 40 - #get_feature_extractor() == fe - #forces_assignment() == false - - #learns_nonnegative_weights() == false !*/ const feature_extractor& get_feature_extractor ( @@ -164,23 +162,6 @@ namespace dlib - returns a copy of the optimizer used to solve the structural SVM problem. !*/ - bool learns_nonnegative_weights ( - ) const; - /*! - ensures - - The output of training is a weight vector that defines the behavior of an - assignment_function object. If learns_nonnegative_weights() == true then - the resulting weight vector will always have non-negative entries. - !*/ - - void set_learns_nonnegative_weights ( - bool value - ); - /*! - ensures - - #learns_nonnegative_weights() == value - !*/ - void set_c ( double C ); diff --git a/dlib/svm/structural_sequence_labeling_trainer.h b/dlib/svm/structural_sequence_labeling_trainer.h index c0a857c9f..b9744b89c 100644 --- a/dlib/svm/structural_sequence_labeling_trainer.h +++ b/dlib/svm/structural_sequence_labeling_trainer.h @@ -7,6 +7,7 @@ #include "../algs.h" #include "../optimization.h" #include "structural_svm_sequence_labeling_problem.h" +#include "num_nonnegative_weights.h" namespace dlib @@ -219,7 +220,7 @@ namespace dlib for (unsigned long i = 0; i < loss_values.size(); ++i) prob.set_loss(i,loss_values[i]); - solver(prob, weights); + solver(prob, weights, num_nonnegative_weights(fe)); return sequence_labeler(weights,fe); }