Optimlib هي مكتبة C ++ خفيفة الوزن من طرق التحسين العددية للوظائف غير الخطية.
سمات:
float ) أو مكتبة دقة مزدوجة ( double ).تتضمن قائمة الخوارزميات المتاحة حاليًا:
الوثائق الكاملة متوفرة على الإنترنت:
يتوفر إصدار PDF من الوثائق هنا.
يتبع واجهة برمجة تطبيقات Optimlib اتفاقية بسيطة نسبيًا ، مع استدعاء معظم الخوارزميات بالطريقة التالية:
algorithm_id(<initial/final values>, <objective function>, <objective function data>);
المدخلات ، بالترتيب ، هي:
على سبيل المثال ، يتم استدعاء خوارزمية BFGS باستخدام
bfgs (ColVec_t& init_out_vals, std::function< double ( const ColVec_t& vals_inp, ColVec_t* grad_out, void * opt_data)> opt_objfn, void* opt_data); حيث يتم استخدام ColVec_t لتمثيل ، على سبيل المثال ، arma::vec أو Eigen::VectorXd أنواع.
تتوفر OptimLib كمكتبة مشتركة مجمعة ، أو كمكتبة رأس فقط ، للأنظمة المتشابهة لـ UNIX فقط (على سبيل المثال ، توزيعات Linux الشائعة ، وكذلك MacOS). لا يتم دعم استخدام هذه المكتبة مع الأنظمة المستندة إلى Windows ، مع أو بدون MSVC.
يتطلب OptimLib إما مكتبات الجبر الخطي Armadillo أو Eigen C ++. (لاحظ أن إصدار Eigen 3.4.0 يتطلب برنامج التحويل البرمجي المتوافق مع C ++ 14.)
قبل تضمين ملفات الرأس ، حدد أحد ما يلي:
# define OPTIM_ENABLE_ARMA_WRAPPERS
# define OPTIM_ENABLE_EIGEN_WRAPPERSمثال:
# define OPTIM_ENABLE_EIGEN_WRAPPERS
# include " optim.hpp " يمكن تثبيت المكتبة على أنظمة UNIX-Alike عبر المعيار ./configure && make
أول استنساخ المكتبة وأي علامات فرعية ضرورية:
# clone optim into the current directory
git clone https://github.com/kthohr/optim ./optim
# change directory
cd ./optim
# clone necessary submodules
git submodule update --init تعيين (واحد) من متغيرات البيئة التالية قبل تشغيل configure :
export ARMA_INCLUDE_PATH=/path/to/armadillo
export EIGEN_INCLUDE_PATH=/path/to/eigenأخيراً:
# build and install with Eigen
./configure -i " /usr/local " -l eigen -p
make
make install سيقوم الأمر النهائي بتثبيت OptimLib في /usr/local .
خيارات التكوين (انظر ./configure -h ):
أساسي
-h الطباعة-i التثبيت ؛ الافتراضي: دليل الإنشاء-f وضع الدقة في نقطة العائمة ؛ الافتراضي: double-l حدد اختيار مكتبة الجبر الخطي ؛ اختر arma أو eigen-m حدد مكتبات Blas و Lapack للربط مع ؛ على سبيل المثال ، -m "-lopenblas" أو -m "-framework Accelerate"-o ؛ الإعدادات الافتراضية لـ -O3 -march=native -ffp-contract=fast -flto -DARMA_NO_DEBUG-p تمكين ميزات التوازي OpenMP ( موصى بها )ثانوي
-c بناء تغطية (يستخدم مع CODECOV)-d "تطوير"-g -O0 -gخاص
--header-only-version بإنشاء نسخة رأس فقط من Optimlib (انظر أدناه) تتوفر Optimlib أيضًا كمكتبة رأس فقط (أي ، دون الحاجة إلى تجميع مكتبة مشتركة). ما عليك سوى تشغيل configure باستخدام خيار --header-only-version :
./configure --header-only-version سيؤدي ذلك إلى إنشاء دليل جديد ، header_only_version ، يحتوي على نسخة من OptimLib ، تم تعديله للعمل على أساس مضمّن. باستخدام هذا الإصدار فقط ، قم ببساطة بتضمين ملفات الرأس ( #include "optim.hpp ) وضبط المسار على دليل head_only_version (على سبيل المثال ، -I/path/to/optimlib/header_only_version ).
لاستخدام OptimLib مع حزمة R ، قم أولاً بإنشاء نسخة رأس فقط من المكتبة (انظر أعلاه). ثم ببساطة إضافة تعريف المترجم قبل تضمين ملفات OptimLib.
# define OPTIM_USE_RCPP_ARMADILLO
# include " optim.hpp "# define OPTIM_USE_RCPP_EIGEN
# include " optim.hpp " لتوضيح Optimlib في العمل ، فكر في البحث عن الحد الأدنى العالمي لوظيفة Ackley:

هذه وظيفة اختبار معروفة مع العديد من الحد الأدنى المحلي. تعتبر طرق Newton-type (مثل BFGs) حساسة لاختيار القيم الأولية ، وستؤدي أداءً ضعيفًا هنا. على هذا النحو ، سنستخدم طريقة بحث عالمية-في هذه الحالة: التطور التفاضلي.
شفرة:
# define OPTIM_ENABLE_EIGEN_WRAPPERS
# include " optim.hpp "
# define OPTIM_PI 3.14159265358979
double
ackley_fn ( const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void * opt_data)
{
const double x = vals_inp ( 0 );
const double y = vals_inp ( 1 );
const double obj_val = 20 + std::exp ( 1 ) - 20 * std::exp ( - 0.2 * std::sqrt ( 0.5 *(x*x + y*y)) ) - std::exp ( 0.5 *( std::cos ( 2 * OPTIM_PI * x) + std::cos ( 2 * OPTIM_PI * y)) );
return obj_val;
}
int main ()
{
Eigen::VectorXd x = 2.0 * Eigen::VectorXd::Ones ( 2 ); // initial values: (2,2)
bool success = optim::de (x, ackley_fn, nullptr );
if (success) {
std::cout << " de: Ackley test completed successfully. " << std::endl;
} else {
std::cout << " de: Ackley test completed unsuccessfully. " << std::endl;
}
std::cout << " de: solution to Ackley test: n " << x << std::endl;
return 0 ;
}على أجهزة الكمبيوتر المستندة إلى X86 ، يمكن تجميع هذا المثال باستخدام:
g++ -Wall -std=c++14 -O3 -march=native -ffp-contract=fast -I/path/to/eigen -I/path/to/optim/include optim_de_ex.cpp -o optim_de_ex.out -L/path/to/optim/lib -loptimالإخراج:
de: Ackley test completed successfully.
elapsed time: 0.028167s
de: solution to Ackley test:
-1.2702e-17
-3.8432e-16
على جهاز كمبيوتر محمول قياسي ، ستقوم OptimLib بحساب حل داخل دقة الماكينة في جزء من الثانية.
النسخة المستندة إلى Armadillo من هذا المثال:
# define OPTIM_ENABLE_ARMA_WRAPPERS
# include " optim.hpp "
# define OPTIM_PI 3.14159265358979
double
ackley_fn ( const arma::vec& vals_inp, arma::vec* grad_out, void * opt_data)
{
const double x = vals_inp ( 0 );
const double y = vals_inp ( 1 );
const double obj_val = 20 + std::exp ( 1 ) - 20 * std::exp ( - 0.2 * std::sqrt ( 0.5 *(x*x + y*y)) ) - std::exp ( 0.5 *( std::cos ( 2 * OPTIM_PI * x) + std::cos ( 2 * OPTIM_PI * y)) );
return obj_val;
}
int main ()
{
arma::vec x = arma::ones ( 2 , 1 ) + 1.0 ; // initial values: (2,2)
bool success = optim::de (x, ackley_fn, nullptr );
if (success) {
std::cout << " de: Ackley test completed successfully. " << std::endl;
} else {
std::cout << " de: Ackley test completed unsuccessfully. " << std::endl;
}
arma::cout << " de: solution to Ackley test: n " << x << arma::endl;
return 0 ;
}تجميع وتشغيل:
g++ -Wall -std=c++11 -O3 -march=native -ffp-contract=fast -I/path/to/armadillo -I/path/to/optim/include optim_de_ex.cpp -o optim_de_ex.out -L/path/to/optim/lib -loptim
./optim_de_ex.out تحقق من دليل /tests للحصول على أمثلة إضافية ، و https://optimlib.readthedocs.io/en/latest/ للحصول على وصف مفصل لكل خوارزمية.
للحصول على مثال قائم على البيانات ، ضع في اعتبارك أقصى قدر من تقدير الاحتمالية لنموذج السجل ، وهو شائع في الإحصاءات والتعلم الآلي. في هذه الحالة ، أغلقت تعبيرات الشكل للتدرج والهسيان. سنستخدم طريقة نزول تدرج شعبية ، آدم (تقدير اللحظة التكيفية) ، ومقارنة بخوارزمية نقية مقرها نيوتن.
# define OPTIM_ENABLE_ARMA_WRAPPERS
# include " optim.hpp "
// sigmoid function
inline
arma::mat sigm ( const arma::mat& X)
{
return 1.0 / ( 1.0 + arma::exp (-X));
}
// log-likelihood function data
struct ll_data_t
{
arma::vec Y;
arma::mat X;
};
// log-likelihood function with hessian
double ll_fn_whess ( const arma::vec& vals_inp, arma::vec* grad_out, arma::mat* hess_out, void * opt_data)
{
ll_data_t * objfn_data = reinterpret_cast < ll_data_t *>(opt_data);
arma::vec Y = objfn_data-> Y ;
arma::mat X = objfn_data-> X ;
arma::vec mu = sigm (X*vals_inp);
const double norm_term = static_cast < double >(Y. n_elem );
const double obj_val = - arma::accu ( Y% arma::log (mu) + ( 1.0 -Y)% arma::log ( 1.0 -mu) ) / norm_term;
//
if (grad_out)
{
*grad_out = X. t () * (mu - Y) / norm_term;
}
//
if (hess_out)
{
arma::mat S = arma::diagmat ( mu%( 1.0 -mu) );
*hess_out = X. t () * S * X / norm_term;
}
//
return obj_val;
}
// log-likelihood function for Adam
double ll_fn ( const arma::vec& vals_inp, arma::vec* grad_out, void * opt_data)
{
return ll_fn_whess (vals_inp,grad_out, nullptr ,opt_data);
}
//
int main ()
{
int n_dim = 5 ; // dimension of parameter vector
int n_samp = 4000 ; // sample length
arma::mat X = arma::randn (n_samp,n_dim);
arma::vec theta_0 = 1.0 + 3.0 * arma::randu (n_dim, 1 );
arma::vec mu = sigm (X*theta_0);
arma::vec Y (n_samp);
for ( int i= 0 ; i < n_samp; i++)
{
Y (i) = ( arma::as_scalar ( arma::randu ( 1 )) < mu (i) ) ? 1.0 : 0.0 ;
}
// fn data and initial values
ll_data_t opt_data;
opt_data. Y = std::move (Y);
opt_data. X = std::move (X);
arma::vec x = arma::ones (n_dim, 1 ) + 1.0 ; // initial values
// run Adam-based optim
optim:: algo_settings_t settings;
settings. gd_method = 6 ;
settings. gd_settings . step_size = 0.1 ;
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now ();
bool success = optim::gd (x,ll_fn,&opt_data,settings);
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now ();
std::chrono::duration< double > elapsed_seconds = end-start;
//
if (success) {
std::cout << " Adam: logit_reg test completed successfully. n "
<< " elapsed time: " << elapsed_seconds. count () << " s n " ;
} else {
std::cout << " Adam: logit_reg test completed unsuccessfully. " << std::endl;
}
arma::cout << " n Adam: true values vs estimates: n " << arma::join_rows (theta_0,x) << arma::endl;
//
// run Newton-based optim
x = arma::ones (n_dim, 1 ) + 1.0 ; // initial values
start = std::chrono::system_clock::now ();
success = optim::newton (x,ll_fn_whess,&opt_data);
end = std::chrono::system_clock::now ();
elapsed_seconds = end-start;
//
if (success) {
std::cout << " newton: logit_reg test completed successfully. n "
<< " elapsed time: " << elapsed_seconds. count () << " s n " ;
} else {
std::cout << " newton: logit_reg test completed unsuccessfully. " << std::endl;
}
arma::cout << " n newton: true values vs estimates: n " << arma::join_rows (theta_0,x) << arma::endl;
return 0 ;
}الإخراج:
Adam: logit_reg test completed successfully.
elapsed time: 0.025128s
Adam: true values vs estimates:
2.7850 2.6993
3.6561 3.6798
2.3379 2.3860
2.3167 2.4313
2.2465 2.3064
newton: logit_reg test completed successfully.
elapsed time: 0.255909s
newton: true values vs estimates:
2.7850 2.6993
3.6561 3.6798
2.3379 2.3860
2.3167 2.4313
2.2465 2.3064
من خلال الجمع بين Eigen مع مكتبة Autodiff ، يوفر OptimLib دعمًا تجريبيًا للتمايز التلقائي.
مثال باستخدام التمايز التلقائي للوضع الأمامي مع BFGs لوظيفة الكرة:
# define OPTIM_ENABLE_EIGEN_WRAPPERS
# include " optim.hpp "
# include < autodiff/forward/real.hpp >
# include < autodiff/forward/real/eigen.hpp >
//
autodiff::real
opt_fnd ( const autodiff::ArrayXreal& x)
{
return x. cwiseProduct (x). sum ();
}
double
opt_fn ( const Eigen::VectorXd& x, Eigen::VectorXd* grad_out, void * opt_data)
{
autodiff::real u;
autodiff::ArrayXreal xd = x. eval ();
if (grad_out) {
Eigen::VectorXd grad_tmp = autodiff::gradient (opt_fnd, autodiff::wrt (xd), autodiff::at (xd), u);
*grad_out = grad_tmp;
} else {
u = opt_fnd (xd);
}
return u. val ();
}
int main ()
{
Eigen::VectorXd x ( 5 );
x << 1 , 2 , 3 , 4 , 5 ;
bool success = optim::bfgs (x, opt_fn, nullptr );
if (success) {
std::cout << " bfgs: forward-mode autodiff test completed successfully. n " << std::endl;
} else {
std::cout << " bfgs: forward-mode autodiff test completed unsuccessfully. n " << std::endl;
}
std::cout << " solution: x = n " << x << std::endl;
return 0 ;
}ترجمة مع:
g++ -Wall -std=c++17 -O3 -march=native -ffp-contract=fast -I/path/to/eigen -I/path/to/autodiff -I/path/to/optim/include optim_autodiff_ex.cpp -o optim_autodiff_ex.out -L/path/to/optim/lib -loptimراجع الوثائق لمزيد من التفاصيل حول هذا الموضوع.
كيث أوهارا
إصدار Apache 2