Optimlib是非線性函數數值優化方法的輕量級C ++庫。
特徵:
float )或雙精度( double )庫可用。當前可用算法的列表包括:
完整的文檔可在線提供:
該文檔的PDF版本可在此處找到。
Optimlib API遵循相對簡單的慣例,大多數算法以以下方式調用:
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 "可以通過標準./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最終命令將在/usr/local中安裝OptimLib。
配置選項(請參閱./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也可以作為僅標題庫(即,無需編譯共享庫)提供。只需使用--header-only-version選項運行configure :
./configure --header-only-version這將創建一個新的目錄header_only_version ,其中包含OptimLib的副本,並在內聯工作中修改為工作。使用此僅標頭版本,只需包含標頭文件( #include "optim.hpp ),然後將Inclage路徑設置為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函數的全球最小值:

這是許多局部最小值的眾所周知的測試功能。牛頓型方法(例如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/以獲取每個算法的詳細說明。
對於基於數據的示例,請考慮對logit模型的最大似然估計,該模型在統計和機器學習中常見。在這種情況下,我們對梯度和黑森州具有閉合形式的表達方式。我們將採用流行的梯度下降方法Adam(自適應力矩估計),並與純牛頓的算法進行比較。
# 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
通過將特定元素與自動庫庫相結合,Optimlib為自動分化提供了實驗支持。
示例使用前向模式自動分化與BFG用於球體功能:
# 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