Optimlib adalah perpustakaan C ++ yang ringan dari metode optimasi numerik untuk fungsi nonlinier.
Fitur:
float ) atau presisi ganda ( double ).Daftar algoritma yang tersedia saat ini meliputi:
Dokumentasi lengkap tersedia secara online:
Versi PDF dari dokumentasi tersedia di sini.
API Optimlib mengikuti konvensi yang relatif sederhana, dengan sebagian besar algoritma disebut dengan cara berikut:
algorithm_id(<initial/final values>, <objective function>, <objective function data>);
Inputnya, secara berurutan adalah:
Misalnya, algoritma BFGS disebut menggunakan
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); di mana ColVec_t digunakan untuk mewakili, misalnya, arma::vec atau Eigen::VectorXd tipe.
Optimlib tersedia sebagai perpustakaan bersama yang dikompilasi, atau sebagai perpustakaan header saja, hanya untuk sistem yang sama unix (misalnya, distro berbasis Linux yang populer, serta macOS). Penggunaan perpustakaan ini dengan sistem berbasis Windows, dengan atau tanpa MSVC, tidak didukung .
Optimlib membutuhkan perpustakaan aljabar linier Armadillo atau Eigen C ++. (Perhatikan bahwa Eigen Versi 3.4.0 membutuhkan kompiler C ++ 14 yang kompatibel.)
Sebelum memasukkan file header, tentukan salah satu dari yang berikut:
# define OPTIM_ENABLE_ARMA_WRAPPERS
# define OPTIM_ENABLE_EIGEN_WRAPPERSContoh:
# define OPTIM_ENABLE_EIGEN_WRAPPERS
# include " optim.hpp " Perpustakaan dapat diinstal pada sistem unix-like melalui standar ./configure && make Method.
Klon pertama perpustakaan dan submodul yang diperlukan:
# 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 Set (satu) dari variabel lingkungan berikut sebelum menjalankan configure :
export ARMA_INCLUDE_PATH=/path/to/armadillo
export EIGEN_INCLUDE_PATH=/path/to/eigenAkhirnya:
# build and install with Eigen
./configure -i " /usr/local " -l eigen -p
make
make install Perintah terakhir akan menginstal optimlib ke /usr/local .
Opsi konfigurasi (lihat ./configure -h ):
Utama
-h Cetak Bantuan-i jalur instalasi; Default: Direktori Bangun-f Mode Presisi Floating-Point; Default: double-l tentukan pilihan perpustakaan aljabar linier; Pilih arma atau eigen-m Menentukan perpustakaan Blas dan Lapack untuk ditautkan; Misalnya, -m "-lopenblas" atau -m "-framework Accelerate"-o opsi optimasi kompiler; default ke -O3 -march=native -ffp-contract=fast -flto -DARMA_NO_DEBUG-p Aktifkan fitur paralelisasi openMP ( disarankan )Sekunder
-c Build Cakupan (Digunakan dengan Codecov)-d membangun 'pengembangan'-g Build debugging (bendera optimasi diatur ke -O0 -g )Spesial
--header-only-version menghasilkan versi header Optimlib saja (lihat di bawah) Optimlib juga tersedia sebagai perpustakaan header saja (yaitu, tanpa perlu menyusun perpustakaan bersama). Cukup jalankan configure dengan opsi --header-only-version :
./configure --header-only-version Ini akan membuat direktori baru, header_only_version , berisi salinan optimlib, dimodifikasi untuk bekerja secara sebaris. Dengan versi header -only ini, cukup sertakan file header ( #include "optim.hpp ) dan atur jalur sertakan ke direktori head_only_version (misalnya, -I/path/to/optimlib/header_only_version ).
Untuk menggunakan optimlib dengan paket R, pertama-tama buat versi header-only dari perpustakaan (lihat di atas). Kemudian cukup tambahkan definisi kompiler sebelum memasukkan file optimlib.
# define OPTIM_USE_RCPP_ARMADILLO
# include " optim.hpp "# define OPTIM_USE_RCPP_EIGEN
# include " optim.hpp " Untuk mengilustrasikan optimlib di tempat kerja, pertimbangkan mencari minimum global fungsi Ackley:

Ini adalah fungsi uji yang terkenal dengan banyak minimum lokal. Metode tipe Newton (seperti BFG) sensitif terhadap pilihan nilai awal, dan akan berkinerja agak buruk di sini. Dengan demikian, kami akan menggunakan metode pencarian global-dalam hal ini: evolusi diferensial.
Kode:
# 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 ;
}Pada komputer berbasis x86, contoh ini dapat dikompilasi menggunakan:
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 -loptimKeluaran:
de: Ackley test completed successfully.
elapsed time: 0.028167s
de: solution to Ackley test:
-1.2702e-17
-3.8432e-16
Pada laptop standar, optimlib akan menghitung solusi untuk dalam presisi mesin dalam sebagian kecil detik.
Versi berbasis Armadillo dari contoh ini:
# 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 ;
}Kompilasi dan jalankan:
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 Periksa Direktori /tests untuk contoh tambahan, dan https://optimlib.readthedocs.io/en/latest/ untuk deskripsi terperinci dari setiap algoritma.
Untuk contoh berbasis data, pertimbangkan estimasi kemungkinan maksimum model logit, umum dalam statistik dan pembelajaran mesin. Dalam hal ini kami memiliki ekspresi bentuk tertutup untuk gradien dan Hessian. Kami akan menggunakan metode keturunan gradien populer, Adam (estimasi momen adaptif), dan dibandingkan dengan algoritma yang berbasis di Newton.
# 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 ;
}Keluaran:
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
Dengan menggabungkan Eigen dengan Perpustakaan Autodiff, Optimlib memberikan dukungan eksperimental untuk diferensiasi otomatis.
Contoh Menggunakan diferensiasi otomatis mode maju dengan BFGS untuk fungsi Sphere:
# 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 ;
}Dikompilasi dengan:
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 -loptimLihat dokumentasi untuk detail lebih lanjut tentang topik ini.
Keith O'Hara
Apache Versi 2