char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
bcbddcd54
char/aliceai-t5-moe
1// TODO refactor 2 3#include "ggml.h" 4#include "ggml-alloc.h" 5#include "ggml-backend.h" 6#include "ggml-opt.h" 7 8#include <cmath> 9#include <cinttypes> 10#include <cstring> 11#include <random> 12#include <string> 13#include <thread> 14#include <vector> 15 16#define TEST_LOG (...) printf(__VA_ARGS__) 17 18static bool almost_equal (const double a ,const double b ,const double atol ) { 19return fabs (a - b )< atol ; 20} 21 22constexpr int64_t ne_datapoint = 2 ; 23constexpr int64_t ne_label = 1 ; 24constexpr int64_t ndata = 6 ; 25 26struct helper_ctx_data { 27 std::vector < ggml_opt_dataset_t > datasets_supervised ; 28 std::vector < struct ggml_tensor *> data_batch ; 29 std::vector < struct ggml_tensor *> labels_batch ; 30 31ggml_opt_dataset_t dataset_unsupervised ; 32struct ggml_context * ctx_static ; 33struct ggml_context * ctx_compute ; 34struct ggml_opt_params opt_params ; 35ggml_opt_context_t opt_ctx ; 36struct ggml_tensor * inputs ; 37struct ggml_tensor * weights ; 38struct ggml_tensor * outputs ; 39ggml_backend_buffer_t buf ; 40ggml_opt_result_t result ; 41ggml_opt_result_t result2 ; 42}; 43 44// These default values make it easier to check optimization results vs. expected values. 45static ggml_opt_optimizer_params helper_get_test_opt_pars (void * userdata ) { 46ggml_opt_optimizer_params result = ggml_opt_get_default_optimizer_params (userdata ); 47 48result .adamw .alpha = 1.0f ; 49result .adamw .beta1 = 0.0f ; 50result .adamw .beta2 = 0.0f ; 51result .adamw .eps = 0.0f ; 52result .adamw .wd = 0.0f ; 53result .sgd .wd = 0.0f ; 54result .sgd .alpha = 1.0f ; 55 56return result ; 57} 58 59static helper_ctx_data helper_get_ctx_data ( 60enum ggml_opt_optimizer_type optim , 61ggml_backend_sched_t backend_sched , 62ggml_backend_t backend , 63const bool init_opt_ctx = true, 64const bool optimizer_defaults = true, 65int64_t nbatch_logical = 1 , 66int64_t nbatch_physical = 1 , 67enum ggml_opt_loss_type loss_type = GGML_OPT_LOSS_TYPE_SUM ) { 68 std::vector < ggml_opt_dataset_t > datasets (ndata ); 69for (int64_t ndata_shard = 1 ;ndata_shard <=ndata ;++ ndata_shard ) { 70ggml_opt_dataset_t dataset = ggml_opt_dataset_init ( 71GGML_TYPE_F32 ,GGML_TYPE_F32 ,ne_datapoint ,ne_label ,ndata ,ndata_shard ); 72 73float * data = ggml_get_data_f32 (ggml_opt_dataset_data (dataset )); 74float * labels = ggml_get_data_f32 (ggml_opt_dataset_labels (dataset )); 75 76for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 77for (int64_t id = 0 ;id < ne_datapoint ;++ id ) { 78data [idata * ne_datapoint + id ]= 16 * idata + id ; 79 } 80for (int64_t il = 0 ;il < ne_label ;++ il ) { 81labels [idata * ne_label + il ]= 16 * (16 * idata + il ); 82 } 83 } 84 85datasets [ndata_shard - 1 ]= dataset ; 86 } 87 88ggml_opt_dataset_t dataset_unsupervised = ggml_opt_dataset_init ( 89GGML_TYPE_F32 ,GGML_TYPE_F32 ,1 ,0 ,ndata ,/*ndata_shard =*/ 1 ); 90 91float * data = ggml_get_data_f32 (ggml_opt_dataset_data (dataset_unsupervised )); 92 93for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 94data [idata ]= idata ; 95 } 96 97struct ggml_context * ctx_static ; 98struct ggml_context * ctx_compute ; 99 { 100struct ggml_init_params params = { 101/*.mem_size =*/ (2 * ndata + 2 )* ggml_tensor_overhead (), 102/*.mem_buffer =*/ nullptr , 103/*.no_alloc =*/ true, 104 }; 105ctx_static = ggml_init (params ); 106 } 107 { 108struct ggml_init_params params = { 109/*.mem_size =*/ GGML_DEFAULT_GRAPH_SIZE * ggml_tensor_overhead ()+ 3 * ggml_graph_overhead (), 110/*.mem_buffer =*/ nullptr , 111/*.no_alloc =*/ true, 112 }; 113ctx_compute = ggml_init (params ); 114 } 115 116 std::vector < struct ggml_tensor *> data_batch (ndata ); 117 std::vector < struct ggml_tensor *> labels_batch (ndata ); 118for (int64_t ndata_batch = 1 ;ndata_batch <=ndata ;++ ndata_batch ) { 119data_batch [ndata_batch - 1 ]= ggml_new_tensor_1d (ctx_static ,GGML_TYPE_F32 ,ndata_batch * ne_datapoint ); 120labels_batch [ndata_batch - 1 ]= ggml_new_tensor_1d (ctx_static ,GGML_TYPE_F32 ,ndata_batch * ne_label ); 121 } 122 123struct ggml_tensor * inputs = ggml_new_tensor_1d (ctx_static ,GGML_TYPE_F32 ,nbatch_physical ); 124ggml_set_name (inputs ,"inputs" ); 125 126struct ggml_tensor * weights = ggml_new_tensor_1d (ctx_static ,GGML_TYPE_F32 ,1 ); 127ggml_set_name (weights ,"weights" ); 128ggml_set_param (weights ); 129 130struct ggml_tensor * intermediary = ggml_add (ctx_compute ,inputs ,weights ); 131 132struct ggml_tensor * outputs = ggml_scale (ctx_compute ,intermediary ,1.0f ); 133ggml_set_name (outputs ,"outputs" ); 134 135ggml_backend_buffer_t buf = ggml_backend_alloc_ctx_tensors (ctx_static ,backend ); 136const float w0 = float (ndata )/2 ; 137ggml_backend_tensor_set (weights ,& w0 ,0 ,sizeof (float )); 138 139GGML_ASSERT (nbatch_logical %nbatch_physical == 0 ); 140const int32_t opt_period = nbatch_logical /nbatch_physical ; 141 142struct ggml_opt_params opt_params = ggml_opt_default_params (backend_sched ,loss_type ); 143opt_params .ctx_compute = ctx_compute ; 144opt_params .inputs = inputs ; 145opt_params .outputs = outputs ; 146opt_params .opt_period = opt_period ; 147opt_params .optimizer = optim ; 148if (!optimizer_defaults ) { 149opt_params .get_opt_pars = helper_get_test_opt_pars ; 150 } 151GGML_ASSERT (opt_params .get_opt_pars ); 152ggml_opt_context_t opt_ctx = init_opt_ctx ?ggml_opt_init (opt_params ) :nullptr ; 153GGML_ASSERT (!opt_ctx || ggml_opt_context_optimizer_type (opt_ctx )== opt_params .optimizer ); 154 155ggml_opt_result_t result = ggml_opt_result_init (); 156ggml_opt_result_t result2 = ggml_opt_result_init (); 157 158return {datasets ,data_batch ,labels_batch ,dataset_unsupervised ,ctx_static ,ctx_compute ,opt_params ,opt_ctx ,inputs ,weights ,outputs ,buf ,result ,result2 }; 159} 160 161static void helper_free_ctx_data (struct helper_ctx_data ctx_data ) { 162ggml_opt_result_free (ctx_data .result ); 163ggml_opt_result_free (ctx_data .result2 ); 164ggml_opt_free (ctx_data .opt_ctx ); 165ggml_backend_buffer_free (ctx_data .buf ); 166ggml_free (ctx_data .ctx_static ); 167ggml_free (ctx_data .ctx_compute ); 168for (ggml_opt_dataset_t dataset :ctx_data .datasets_supervised ) { 169ggml_opt_dataset_free (dataset ); 170 } 171ggml_opt_dataset_free (ctx_data .dataset_unsupervised ); 172} 173 174static void print_ok (bool subtest_ok ) { 175printf (subtest_ok ?"\033[1;32mOK\033[0m\n" :"\033[1;31mFAIL\033[0m\n" ); 176} 177 178static void helper_after_test ( 179enum ggml_opt_optimizer_type optim , 180const char * func ,const bool high_level ,const std::string options , 181const std::string subtest ,const bool subtest_ok ,int & ntest ,int & npass ) { 182printf (" %s(high_level=%s%s, subtest=%s, optimizer=%s): " , 183func ,high_level ?"yes" :"no" ,options .c_str (),subtest .c_str (),ggml_opt_optimizer_name (optim )); 184print_ok (subtest_ok ); 185if (subtest_ok ) 186npass ++ ; 187ntest ++ ; 188} 189 190static void print_ok (const char * func ,bool subtest_ok ,int & npass ,int & ntest ,const char * args = "" ) { 191printf (" %s(%s): " ,func ,args ); 192print_ok (subtest_ok ); 193if (subtest_ok ) 194npass ++ ; 195++ ntest ; 196} 197 198static std::pair < int ,int > test_dataset ( 199enum ggml_opt_optimizer_type optim , 200ggml_backend_sched_t backend_sched ,ggml_backend_t backend ,const bool shuffle ) { 201int ntest = 0 ; 202int npass = 0 ; 203 204struct helper_ctx_data cd = helper_get_ctx_data (optim ,backend_sched ,backend ); 205 206for (int64_t ndata_shard = 1 ;ndata_shard <=ndata ;++ ndata_shard ) { 207ggml_opt_dataset_t dataset = cd .datasets_supervised [ndata_shard - 1 ]; 208 209if (shuffle ) { 210ggml_opt_dataset_shuffle (cd .opt_ctx ,dataset ,-1 ); 211 } 212 213for (int64_t ndata_batch = 1 ;ndata_batch <=ndata ;++ ndata_batch ) { 214if (ndata_batch %ndata_shard != 0 ) { 215continue ; 216 } 217bool subtest_ok = true; 218 219struct ggml_tensor * data_batch = cd .data_batch [ndata_batch - 1 ]; 220struct ggml_tensor * labels_batch = cd .labels_batch [ndata_batch - 1 ]; 221 222 std::vector < float > data (ggml_nelements (data_batch )); 223 std::vector < float > labels (ggml_nelements (labels_batch )); 224 225 std::vector < int64_t > idata_shuffled ; 226const int64_t nbatches = ndata /ndata_batch ; 227for (int64_t ibatch = 0 ;ibatch < nbatches ;++ ibatch ) { 228ggml_opt_dataset_get_batch (dataset ,data_batch ,labels_batch ,ibatch ); 229 230ggml_backend_tensor_get (data_batch ,data .data (),0 ,ggml_nbytes (data_batch )); 231ggml_backend_tensor_get (labels_batch ,labels .data (),0 ,ggml_nbytes (labels_batch )); 232 233for (int64_t idata_batch = 0 ;idata_batch < ndata_batch ;++ idata_batch ) { 234const int64_t idata = ibatch * ndata_batch + idata_batch ; 235const int64_t idata_found = data [idata_batch * ne_datapoint ] /16 ; 236subtest_ok = subtest_ok && (shuffle || idata_found == idata ); 237idata_shuffled .push_back (idata_found ); 238 239for (int64_t id = 0 ;id < ne_datapoint ;++ id ) { 240if (data [idata_batch * ne_datapoint + id ]!= 16 * idata_found + id ) { 241subtest_ok = false; 242 } 243 } 244for (int64_t il = 0 ;il < ne_label ;++ il ) { 245if (labels [idata_batch * ne_label + il ]!= 16 * (16 * idata_found + il )) { 246subtest_ok = false; 247 } 248 } 249 } 250 } 251 252if (!shuffle || ndata %ndata_batch == 0 ) { 253const int ndata_max = (ndata /ndata_batch )* ndata_batch ; 254 255for (int64_t idata = 0 ;subtest_ok && idata < ndata_max ;++ idata ) { 256int ninstances = 0 ; 257for (int64_t id :idata_shuffled ) { 258ninstances += id == idata ; 259 } 260if (ninstances != 1 ) { 261subtest_ok = false; 262 } 263 } 264 } 265 266printf (" %s(shuffle=%s, ndata_shard=%" PRId64 ", ndata_batch=%" PRId64 "): " , 267__func__ ,shuffle ?"yes" :"no" ,ndata_shard ,ndata_batch ); 268if (subtest_ok ) { 269printf ("\033[1;32mOK\033[0m\n" ); 270npass ++ ; 271 }else { 272printf ("\033[1;31mFAIL\033[0m\n" ); 273 } 274ntest ++ ; 275 } 276 } 277 278helper_free_ctx_data (cd ); 279 280return std::make_pair (npass ,ntest ); 281} 282 283static std::pair < int ,int > test_grad ( 284enum ggml_opt_optimizer_type optim , 285ggml_backend_sched_t backend_sched ,ggml_backend_t backend ) { 286int ntest = 0 ; 287int npass = 0 ; 288 289struct helper_ctx_data cd = helper_get_ctx_data (optim ,backend_sched ,backend ,/*init_opt_ctx =*/ true,/*optimizer_defaults =*/ false, 290/*nbatch_logical =*/ 999999 ,/*nbatch_physical =*/ 1 ); 291 292 std::vector < float > grad_history (ndata ); 293for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 294grad_history [idata ]= NAN ; 295 } 296 297for (int idata = 0 ;idata < ndata ;++ idata ) { 298const float idataf = idata ; 299ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ true); 300// leaked 301ggml_backend_tensor_set (cd .inputs ,& idataf ,0 ,ggml_nbytes (cd .inputs )); 302ggml_opt_eval (cd .opt_ctx ,cd .result ); 303ggml_backend_tensor_get (ggml_opt_grad_acc (cd .opt_ctx ,cd .weights ),grad_history .data ()+ idata ,0 ,sizeof (float )); 304 } 305 306 { 307bool subtest_ok = true; 308for (int idata = 0 ;idata < ndata ;++ idata ) { 309if (grad_history [idata ]!= idata + 1 ) { 310subtest_ok = false; 311 } 312 } 313printf (" %s(): " ,__func__ ); 314if (subtest_ok ) { 315printf ("\033[1;32mOK\033[0m\n" ); 316npass ++ ; 317 }else { 318printf ("\033[1;31mFAIL\033[0m\n" ); 319 } 320ntest ++ ; 321 } 322 323helper_free_ctx_data (cd ); 324 325return std::make_pair (npass ,ntest ); 326} 327 328static void helper_after_test_forward_backward ( 329enum ggml_opt_optimizer_type optim , 330const char * func ,const bool high_level ,const bool shuffle , 331const std::string subtest ,const bool subtest_ok ,int & ntest ,int & npass ) { 332 std::string options = ", shuffle=" ; 333options += shuffle ?"yes" :"no" ; 334helper_after_test (optim ,func ,high_level ,options ,subtest ,subtest_ok ,ntest ,npass ); 335} 336 337static std::pair < int ,int > test_forward_backward ( 338enum ggml_opt_optimizer_type optim , 339ggml_backend_sched_t backend_sched ,ggml_backend_t backend ,const bool high_level ,const bool shuffle ) { 340int ntest = 0 ; 341int npass = 0 ; 342 343struct helper_ctx_data cd = helper_get_ctx_data (optim ,backend_sched ,backend ,/*init_opt_ctx =*/ true,/*optimizer_defaults =*/ false); 344struct ggml_tensor * loss = ggml_opt_loss (cd .opt_ctx ); 345 346 std::vector < float > loss_history (ndata ); 347for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 348loss_history [idata ]= NAN ; 349 } 350 351 { 352int64_t ndata ; 353ggml_opt_result_ndata (cd .result ,& ndata ); 354double loss ; 355double loss_unc ; 356ggml_opt_result_loss (cd .result ,& loss ,& loss_unc ); 357double accuracy ; 358double accuracy_unc ; 359ggml_opt_result_accuracy (cd .result ,& accuracy ,& accuracy_unc ); 360const bool subtest_ok = ndata == 0 && almost_equal (loss ,0.0 ,1e-6 )&& std::isnan (loss_unc )&& std::isnan (accuracy )&& std::isnan (accuracy_unc ); 361helper_after_test_forward_backward (optim ,__func__ ,high_level ,shuffle ,"results_initial" ,subtest_ok ,ntest ,npass ); 362 } 363 364if (high_level ) { 365ggml_opt_dataset_t dataset = cd .dataset_unsupervised ; 366if (shuffle ) { 367ggml_opt_dataset_shuffle (cd .opt_ctx ,dataset ,-1 ); 368 } 369ggml_opt_epoch (cd .opt_ctx ,dataset ,nullptr ,cd .result ,0 ,nullptr ,nullptr ); 370 }else { 371for (int idata = 0 ;idata < ndata ;++ idata ) { 372const float idataf = idata ; 373ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ false); 374ggml_backend_tensor_set (cd .inputs ,& idataf ,0 ,ggml_nbytes (cd .inputs )); 375ggml_opt_eval (cd .opt_ctx ,cd .result ); 376ggml_backend_tensor_get (loss ,loss_history .data ()+ idata ,0 ,sizeof (float )); 377 } 378 } 379 380 { 381float weights ; 382ggml_backend_tensor_get (cd .weights ,& weights ,0 ,sizeof (float )); 383const bool subtest_ok = almost_equal (weights ,ndata /2 ,1e-10 ); 384helper_after_test_forward_backward (optim ,__func__ ,high_level ,shuffle ,"weights_after_forward" ,subtest_ok ,ntest ,npass ); 385 } 386 { 387constexpr double atol = 1e-10 ; 388 389int64_t ndata ; 390ggml_opt_result_ndata (cd .result ,& ndata ); 391bool subtest_ok = ndata == 6 ; 392 393double loss ; 394double loss_unc ; 395ggml_opt_result_loss (cd .result ,& loss ,& loss_unc ); 396subtest_ok = subtest_ok && almost_equal (loss ,33.0 ,atol )&& almost_equal (loss_unc ,sqrt (3.5 ),atol ); 397 398double accuracy ; 399double accuracy_unc ; 400ggml_opt_result_accuracy (cd .result ,& accuracy ,& accuracy_unc ); 401subtest_ok = subtest_ok && std::isnan (accuracy )&& std::isnan (accuracy_unc ); 402 403helper_after_test_forward_backward (optim ,__func__ ,high_level ,shuffle ,"results_after_forward" ,subtest_ok ,ntest ,npass ); 404 } 405 406float w0 ; 407ggml_backend_tensor_get (cd .weights ,& w0 ,0 ,sizeof (float )); 408for (int i = 0 ;i < 10 ;++ i ) { 409ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ true); 410// leaked. 411ggml_opt_eval (cd .opt_ctx ,cd .result ); 412 } 413ggml_backend_tensor_set (cd .weights ,& w0 ,0 ,sizeof (float )); 414 415ggml_opt_reset (cd .opt_ctx ,/*optimizer =*/ false); 416ggml_opt_result_reset (cd .result ); 417 418for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 419loss_history [idata ]= NAN ; 420 } 421 422if (high_level ) { 423ggml_opt_dataset_t dataset = cd .dataset_unsupervised ; 424if (shuffle ) { 425ggml_opt_dataset_shuffle (cd .opt_ctx ,dataset ,-1 ); 426 } 427ggml_opt_epoch (cd .opt_ctx ,dataset ,cd .result ,nullptr ,ndata ,nullptr ,nullptr ); 428 }else { 429for (int idata = 0 ;idata < ndata ;++ idata ) { 430const float idataf = idata ; 431ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ true); 432ggml_backend_tensor_set (cd .inputs ,& idataf ,0 ,ggml_nbytes (cd .inputs )); 433ggml_opt_eval (cd .opt_ctx ,cd .result ); 434ggml_backend_tensor_get (loss ,loss_history .data ()+ idata ,0 ,sizeof (float )); 435 } 436 } 437 438 { 439float weights ; 440ggml_backend_tensor_get (cd .weights ,& weights ,0 ,sizeof (float )); 441const bool subtest_ok = almost_equal (weights ,- ndata * 0.5 ,1e-10 ); 442helper_after_test_forward_backward (optim ,__func__ ,high_level ,shuffle ,"weights_after_forward_backward" ,subtest_ok ,ntest ,npass ); 443 } 444 { 445int64_t ndata ; 446ggml_opt_result_ndata (cd .result ,& ndata ); 447bool subtest_ok = ndata == 6 ; 448 449double loss ; 450double loss_unc ; 451ggml_opt_result_loss (cd .result ,& loss ,& loss_unc ); 452subtest_ok = subtest_ok && almost_equal (loss ,18.0 ,1e-10 )&& (shuffle || loss_unc == 0.0 ); 453 454double accuracy ; 455double accuracy_unc ; 456ggml_opt_result_accuracy (cd .result ,& accuracy ,& accuracy_unc ); 457subtest_ok = subtest_ok && std::isnan (accuracy )&& std::isnan (accuracy_unc ); 458 459helper_after_test_forward_backward (optim ,__func__ ,high_level ,shuffle ,"result_after_forward_backward" ,subtest_ok ,ntest ,npass ); 460 } 461 462helper_free_ctx_data (cd ); 463 464return std::make_pair (npass ,ntest ); 465} 466 467static std::pair < int ,int > test_epoch_vs_fit ( 468enum ggml_opt_optimizer_type optim , 469ggml_backend_sched_t backend_sched ,ggml_backend_t backend ) { 470int ntest = 0 ; 471int npass = 0 ; 472 473float weights_epoch ; 474float weights_fit ; 475 476 { 477struct helper_ctx_data cd = helper_get_ctx_data (optim ,backend_sched ,backend ,/*init_opt_ctx =*/ true); 478ggml_opt_dataset_t dataset = cd .dataset_unsupervised ; 479 480ggml_opt_dataset_shuffle (cd .opt_ctx ,dataset ,-1 ); 481ggml_opt_epoch (cd .opt_ctx ,dataset ,cd .result ,nullptr ,ndata ,nullptr ,nullptr ); 482// leaked. 483 484ggml_backend_tensor_get (cd .weights ,& weights_epoch ,0 ,ggml_nbytes (cd .weights )); 485helper_free_ctx_data (cd ); 486 } 487 { 488struct helper_ctx_data cd = helper_get_ctx_data (optim ,backend_sched ,backend ,/*init_opt_ctx =*/ false); 489ggml_opt_dataset_t dataset = cd .dataset_unsupervised ; 490 491ggml_opt_fit (backend_sched ,cd .ctx_compute ,cd .inputs ,cd .outputs ,dataset ,GGML_OPT_LOSS_TYPE_SUM , 492optim ,ggml_opt_get_default_optimizer_params ,1 ,1 ,0.0f , true); 493 494ggml_backend_tensor_get (cd .weights ,& weights_fit ,0 ,ggml_nbytes (cd .weights )); 495helper_free_ctx_data (cd ); 496 } 497 498const bool subtest_ok = weights_epoch == weights_fit ; 499 500print_ok (__func__ ,subtest_ok ,npass ,ntest ); 501 502return std::make_pair (npass ,ntest ); 503} 504 505static void helper_after_test_idata_split ( 506enum ggml_opt_optimizer_type optim , 507const char * func ,const bool high_level ,const int epoch , 508const std::string subtest ,const bool subtest_ok ,int & ntest ,int & npass ) { 509 std::string options = ", epoch=" ; 510options += std::to_string (epoch ); 511helper_after_test (optim ,func ,high_level ,options ,subtest ,subtest_ok ,ntest ,npass ); 512} 513 514static std::pair < int ,int > test_idata_split ( 515enum ggml_opt_optimizer_type optim , 516ggml_backend_sched_t backend_sched ,ggml_backend_t backend ,const bool high_level ) { 517int ntest = 0 ; 518int npass = 0 ; 519 520struct helper_ctx_data cd = helper_get_ctx_data (optim ,backend_sched ,backend ,/*init_opt_ctx =*/ true,/*optimizer_defaults =*/ false); 521struct ggml_tensor * loss = ggml_opt_loss (cd .opt_ctx ); 522const int idata_split = ndata * 2 /3 ; 523 524 std::vector < float > loss_history (ndata ); 525for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 526loss_history [idata ]= NAN ; 527 } 528 529bool const adamw = optim == GGML_OPT_OPTIMIZER_TYPE_ADAMW ; 530for (int epoch = 1 ;epoch <=4 ;++ epoch ) { 531if (high_level ) { 532ggml_opt_epoch (cd .opt_ctx ,cd .dataset_unsupervised ,cd .result ,cd .result2 ,idata_split ,nullptr ,nullptr ); 533 }else { 534int idata = 0 ; 535for (;idata < idata_split ;++ idata ) { 536const float idataf = idata ; 537ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ true); 538ggml_backend_tensor_set (cd .inputs ,& idataf ,0 ,ggml_nbytes (cd .inputs )); 539ggml_opt_eval (cd .opt_ctx ,cd .result ); 540ggml_backend_tensor_get (loss ,loss_history .data ()+ idata ,0 ,sizeof (float )); 541 } 542for (;idata < ndata ;++ idata ) { 543const float idataf = idata ; 544ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ false); 545ggml_backend_tensor_set (cd .inputs ,& idataf ,0 ,ggml_nbytes (cd .inputs )); 546ggml_opt_eval (cd .opt_ctx ,cd .result2 ); 547ggml_backend_tensor_get (loss ,loss_history .data ()+ idata ,0 ,sizeof (float )); 548 } 549 } 550 551if (adamw ) { 552float weights ; 553ggml_backend_tensor_get (cd .weights ,& weights ,0 ,sizeof (float )); 554const bool subtest_ok = almost_equal (weights ,ndata /2 - epoch * idata_split ,1e-10 ); 555helper_after_test_idata_split (optim ,__func__ ,high_level ,epoch ,"weights" ,subtest_ok ,ntest ,npass ); 556 } 557if (adamw ) { 558constexpr double atol = 1e-10 ; 559 560int64_t ndata_result ; 561ggml_opt_result_ndata (cd .result ,& ndata_result ); 562bool subtest_ok = ndata_result == idata_split ; 563 564double loss ; 565double loss_unc ; 566ggml_opt_result_loss (cd .result ,& loss ,& loss_unc ); 567subtest_ok = subtest_ok && almost_equal (loss ,28.0 - epoch * 16.0 ,atol )&& almost_equal (loss_unc ,0.0 ,atol ); 568 569double accuracy ; 570double accuracy_unc ; 571ggml_opt_result_accuracy (cd .result ,& accuracy ,& accuracy_unc ); 572subtest_ok = subtest_ok && std::isnan (accuracy )&& std::isnan (accuracy_unc ); 573 574helper_after_test_idata_split (optim ,__func__ ,high_level ,epoch ,"results_backward" ,subtest_ok ,ntest ,npass ); 575 } 576if (adamw ) { 577constexpr double atol = 1e-10 ; 578 579int64_t ndata_result ; 580ggml_opt_result_ndata (cd .result2 ,& ndata_result ); 581bool subtest_ok = ndata_result == ndata - idata_split ; 582 583double loss ; 584double loss_unc ; 585ggml_opt_result_loss (cd .result2 ,& loss ,& loss_unc ); 586subtest_ok = subtest_ok && almost_equal (loss ,15.0 - epoch * 8 ,atol )&& almost_equal (loss_unc ,sqrt (0.5 ),atol ); 587 588double accuracy ; 589double accuracy_unc ; 590ggml_opt_result_accuracy (cd .result2 ,& accuracy ,& accuracy_unc ); 591subtest_ok = subtest_ok && std::isnan (accuracy )&& std::isnan (accuracy_unc ); 592 593helper_after_test_idata_split (optim ,__func__ ,high_level ,epoch ,"results_forward" ,subtest_ok ,ntest ,npass ); 594 } 595 596ggml_opt_result_reset (cd .result ); 597ggml_opt_result_reset (cd .result2 ); 598 } 599 600helper_free_ctx_data (cd ); 601 602return std::make_pair (npass ,ntest ); 603} 604 605static void helper_after_test_gradient_accumulation ( 606enum ggml_opt_optimizer_type optim , 607const char * func ,const int nbatch_physical ,const enum ggml_opt_loss_type loss_type ,const int epoch , 608const std::string subtest ,const bool subtest_ok ,int & ntest ,int & npass ) { 609 std::string options = ", nbatch_physical=" ; 610options += std::to_string (nbatch_physical ); 611options += ", loss_type=" ; 612options += loss_type == GGML_OPT_LOSS_TYPE_MEAN ?"mean" :"sum" ; 613options += ", epoch=" ; 614options += std::to_string (epoch ); 615helper_after_test (optim ,func , false,options ,subtest ,subtest_ok ,ntest ,npass ); 616} 617 618static std::pair < int ,int > test_gradient_accumulation ( 619enum ggml_opt_optimizer_type optim , 620ggml_backend_sched_t backend_sched ,ggml_backend_t backend ,const int32_t nbatch_physical ,const enum ggml_opt_loss_type loss_type ) { 621int ntest = 0 ; 622int npass = 0 ; 623 624struct helper_ctx_data cd = helper_get_ctx_data ( 625optim , 626backend_sched ,backend ,/*init_opt_ctx =*/ true,/*optimizer_defaults =*/ false,/*nbatch_logical =*/ 6 ,nbatch_physical ,loss_type ); 627 628 std::vector < float > grad_history (ndata ); 629for (int64_t idata = 0 ;idata < ndata ;++ idata ) { 630grad_history [idata ]= NAN ; 631 } 632 633bool const adamw = optim == GGML_OPT_OPTIMIZER_TYPE_ADAMW ; 634if (adamw ) 635for (int epoch = 1 ;epoch <=4 ;++ epoch ) { 636if (nbatch_physical == 1 ) { 637for (int idata = 0 ;idata < ndata ;++ idata ) { 638const float idataf = idata ; 639ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ true); 640ggml_backend_tensor_set (cd .inputs ,& idataf ,0 ,1 * sizeof (float )); 641ggml_opt_eval (cd .opt_ctx ,cd .result ); 642ggml_backend_tensor_get (ggml_opt_grad_acc (cd .opt_ctx ,cd .weights ),grad_history .data ()+ idata ,0 ,1 * sizeof (float )); 643 } 644 }else if (nbatch_physical == 2 ) { 645for (int idata = 0 ;idata < ndata ;idata += 2 ) { 646const float idataf [2 ]= {float (idata + 0 ),float (idata + 1 )}; 647ggml_opt_alloc (cd .opt_ctx ,/*backward =*/ true); 648ggml_backend_tensor_set (cd .inputs ,idataf ,0 ,2 * sizeof (float )); 649ggml_opt_eval (cd .opt_ctx ,cd .result ); 650 651grad_history [idata + 0 ]= 0.0f ; 652ggml_backend_tensor_get (ggml_opt_grad_acc (cd .opt_ctx ,cd .weights ),grad_history .data ()+ idata + 1 ,0 ,1 * sizeof (float )); 653 } 654 }else { 655GGML_ASSERT (false); 656 } 657 658 { 659GGML_ASSERT (ndata == 6 ); 660constexpr double atol = 1e-6 ; 661bool subtest_ok = true; 662if (loss_type == GGML_OPT_LOSS_TYPE_SUM ) { 663if (nbatch_physical == 1 ) { 664subtest_ok = subtest_ok && almost_equal (grad_history [0 ],1.0 ,atol ); 665subtest_ok = subtest_ok && almost_equal (grad_history [2 ],3.0 ,atol ); 666subtest_ok = subtest_ok && almost_equal (grad_history [4 ],5.0 ,atol ); 667 }else { 668subtest_ok = subtest_ok && almost_equal (grad_history [0 ],0.0 ,atol ); 669subtest_ok = subtest_ok && almost_equal (grad_history [2 ],0.0 ,atol ); 670subtest_ok = subtest_ok && almost_equal (grad_history [4 ],0.0 ,atol ); 671 } 672subtest_ok = subtest_ok && almost_equal (grad_history [1 ],2.0 ,atol ); 673subtest_ok = subtest_ok && almost_equal (grad_history [3 ],4.0 ,atol ); 674subtest_ok = subtest_ok && almost_equal (grad_history [5 ],6.0 ,atol ); 675 }else if (loss_type == GGML_OPT_LOSS_TYPE_MEAN ) { 676if (nbatch_physical == 1 ) { 677subtest_ok = subtest_ok && almost_equal (grad_history [0 ],1.0 /ndata ,atol ); 678subtest_ok = subtest_ok && almost_equal (grad_history [2 ],3.0 /ndata ,atol ); 679subtest_ok = subtest_ok && almost_equal (grad_history [4 ],5.0 /ndata ,atol ); 680 }else { 681subtest_ok = subtest_ok && almost_equal (grad_history [0 ],0.0 /ndata ,atol ); 682subtest_ok = subtest_ok && almost_equal (grad_history [2 ],0.0 /ndata ,atol ); 683subtest_ok = subtest_ok && almost_equal (grad_history [4 ],0.0 /ndata ,atol ); 684 } 685subtest_ok = subtest_ok && almost_equal (grad_history [1 ],2.0 /ndata ,atol ); 686subtest_ok = subtest_ok && almost_equal (grad_history [3 ],4.0 /ndata ,atol ); 687subtest_ok = subtest_ok && almost_equal (grad_history [5 ],6.0 /ndata ,atol ); 688 }else { 689GGML_ASSERT (false); 690 } 691helper_after_test_gradient_accumulation (optim ,__func__ ,nbatch_physical ,loss_type ,epoch ,"grads" ,subtest_ok ,ntest ,npass ); 692 } 693bool const adamw = optim == GGML_OPT_OPTIMIZER_TYPE_ADAMW ; 694if (adamw ) { 695constexpr double atol = 1e-6 ; 696float weights ; 697ggml_backend_tensor_get (cd .weights ,& weights ,0 ,sizeof (float )); 698const bool subtest_ok = almost_equal (weights , (ndata /2 )- epoch ,atol ); 699helper_after_test_gradient_accumulation (optim ,__func__ ,nbatch_physical ,loss_type ,epoch ,"weights" ,subtest_ok ,ntest ,npass ); 700 } 701 { 702constexpr double atol = 1e-6 ; 703int64_t ndata_result ; 704ggml_opt_result_ndata (cd .result ,& ndata_result ); 705bool subtest_ok = almost_equal (ndata_result ,ndata /nbatch_physical ,atol ); 706 707double loss ; 708ggml_opt_result_loss (cd .result ,& loss ,/*loss_unc =*/ nullptr ); 709if (loss_type == GGML_OPT_LOSS_TYPE_SUM ) { 710subtest_ok = subtest_ok && almost_equal (loss , (39.0 - epoch * 6.0 ),atol ); 711 }else if (loss_type == GGML_OPT_LOSS_TYPE_MEAN ) { 712subtest_ok = subtest_ok && almost_equal (loss , (39.0 - epoch * 6.0 ) /ndata ,atol ); 713 }else { 714GGML_ASSERT (false); 715 } 716 717double accuracy ; 718double accuracy_unc ; 719ggml_opt_result_accuracy (cd .result ,& accuracy ,& accuracy_unc ); 720subtest_ok = subtest_ok && std::isnan (accuracy )&& std::isnan (accuracy_unc ); 721 722helper_after_test_gradient_accumulation (optim ,__func__ ,nbatch_physical ,loss_type ,epoch ,"results" ,subtest_ok ,ntest ,npass ); 723 } 724 725ggml_opt_result_reset (cd .result ); 726 } 727 728helper_free_ctx_data (cd ); 729 730return std::make_pair (npass ,ntest ); 731} 732 733float constexpr g_sgd_lr = 1e-4f ; 734 735int constexpr g_sgd_epochs = 900 ; 736 737static ggml_opt_optimizer_params helper_get_regression_opt_pars (void * userdata ) { 738int64_t epoch = * (int64_t * )userdata ; 739ggml_opt_optimizer_params result = ggml_opt_get_default_optimizer_params (nullptr ); 740result .adamw .alpha = 0.1f ; 741result .sgd .alpha = g_sgd_lr * std::pow (.99 ,1000 * (double )epoch /g_sgd_epochs ); 742result .sgd .wd = 1e-10 ; 743return result ; 744} 745 746static std::pair < int ,int > test_regression ( 747enum ggml_opt_optimizer_type optim , 748ggml_backend_sched_t backend_sched ,ggml_backend_t backend ) { 749int ntest = 0 ; 750int npass = 0 ; 751 752// Test for simple regression with f(x) = a*x + b 753 754constexpr int64_t ndata_regression = 201 ; 755constexpr float a_true = 1.2f ; 756constexpr float b_true = 3.4f ; 757 758 std::mt19937 gen (12345 ); 759 std::normal_distribution < float > nd {0.0f ,0.1f }; 760 761ggml_opt_dataset_t dataset = ggml_opt_dataset_init ( 762GGML_TYPE_F32 ,GGML_TYPE_F32 ,1 ,1 ,ndata_regression ,ndata_regression ); 763 764float * data = ggml_get_data_f32 (ggml_opt_dataset_data (dataset )); 765float * labels = ggml_get_data_f32 (ggml_opt_dataset_labels (dataset )); 766 767constexpr float x_min = -100.0f ; 768constexpr float x_max = 100.0f ; 769 770for (int64_t idata = 0 ;idata < ndata_regression ;++ idata ) { 771const float x = x_min + (x_max - x_min )* idata /(ndata_regression - 1 ); 772const float y = a_true * x + b_true + nd (gen ); 773 774data [idata ]= x ; 775labels [idata ]= y ; 776 } 777 778struct ggml_context * ctx_static ; 779struct ggml_context * ctx_compute ; 780 { 781struct ggml_init_params params = { 782/*.mem_size =*/ 3 * ggml_tensor_overhead (), 783/*.mem_buffer =*/ nullptr , 784/*.no_alloc =*/ true, 785 }; 786ctx_static = ggml_init (params ); 787 } 788 { 789struct ggml_init_params params = { 790/*.mem_size =*/ GGML_DEFAULT_GRAPH_SIZE * ggml_tensor_overhead ()+ 3 * ggml_graph_overhead (), 791/*.mem_buffer =*/ nullptr , 792/*.no_alloc =*/ true, 793 }; 794ctx_compute = ggml_init (params ); 795 } 796 797// The first dimension is the dimension of the datapoints, the second dimension is the number of datapoints. 798struct ggml_tensor * x = ggml_new_tensor_2d (ctx_static ,GGML_TYPE_F32 ,1 ,ndata_regression ); 799ggml_set_name (x ,"x" ); 800 801struct ggml_tensor * a = ggml_new_tensor_1d (ctx_static ,GGML_TYPE_F32 ,1 ); 802ggml_set_name (a ,"a" ); 803ggml_set_param (a ); 804 805struct ggml_tensor * b = ggml_new_tensor_1d (ctx_static ,GGML_TYPE_F32 ,1 ); 806ggml_set_name (b ,"b" ); 807ggml_set_param (b ); 808 809struct ggml_tensor * f = ggml_add (ctx_compute ,ggml_mul (ctx_compute ,x ,a ),b ); 810ggml_set_name (f ,"f" ); 811 812ggml_backend_buffer_t buf = ggml_backend_alloc_ctx_tensors (ctx_static ,backend ); 813const float a0 = 1.0f ; 814const float b0 = 3.0f ; 815ggml_backend_tensor_set (a ,& a0 ,0 ,sizeof (float )); 816ggml_backend_tensor_set (b ,& b0 ,0 ,sizeof (float )); 817 818bool const adamw = optim == GGML_OPT_OPTIMIZER_TYPE_ADAMW ; 819int64_t const n_epoch = adamw ?100 :g_sgd_epochs ; 820ggml_opt_fit (backend_sched ,ctx_compute ,x ,f ,dataset ,GGML_OPT_LOSS_TYPE_MEAN_SQUARED_ERROR ,optim , 821helper_get_regression_opt_pars ,n_epoch ,ndata_regression ,0.0f , true); 822 823 { 824float a_fit ; 825ggml_backend_tensor_get (a ,& a_fit ,0 ,sizeof (float )); 826float b_fit ; 827ggml_backend_tensor_get (b ,& b_fit ,0 ,sizeof (float )); 828float tol = adamw ?1e-2 :5e-2 ; 829const bool aok = almost_equal (a_fit ,a_true ,tol ); 830const bool bok = almost_equal (b_fit ,b_true ,tol ); 831const bool subtest_ok = aok && bok ; 832print_ok (__func__ ,adamw ?subtest_ok : true,npass ,ntest ,"subtest=weights" ); 833 } 834 835ggml_backend_buffer_free (buf ); 836ggml_free (ctx_static ); 837ggml_opt_dataset_free (dataset ); 838 839return std::make_pair (npass ,ntest ); 840} 841 842static std::pair < int ,int > test_backend ( 843ggml_backend_sched_t backend_sched ,ggml_backend_t backend ,enum ggml_opt_optimizer_type optim ) { 844int npass = 0 ; 845int ntest = 0 ; 846 847for (bool shuffle : {false, true}) { 848 std::pair < int ,int > partial = test_dataset (optim ,backend_sched ,backend ,shuffle ); 849npass += partial .first ; 850ntest += partial .second ; 851 } 852 { 853 std::pair < int ,int > partial = test_grad (optim ,backend_sched ,backend ); 854npass += partial .first ; 855ntest += partial .second ; 856 } 857for (bool high_level : {false, true}){ 858for (bool shuffle : {false, true}) { 859if (!high_level && shuffle ) { 860continue ; 861 } 862 863 std::pair < int ,int > partial = test_forward_backward (optim ,backend_sched ,backend ,high_level ,shuffle ); 864npass += partial .first ; 865ntest += partial .second ; 866 } 867 } 868 { 869 std::pair < int ,int > partial = test_epoch_vs_fit (optim ,backend_sched ,backend ); 870npass += partial .first ; 871ntest += partial .second ; 872 } 873for (bool high_level : {false, true}){ 874 std::pair < int ,int > partial = test_idata_split (optim ,backend_sched ,backend ,high_level ); 875npass += partial .first ; 876ntest += partial .second ; 877 } 878bool const adamw = optim == GGML_OPT_OPTIMIZER_TYPE_ADAMW ; 879if (adamw ) { 880for (int32_t nbatch_physical : {2 ,1 }) { 881for (enum ggml_opt_loss_type loss_type : {GGML_OPT_LOSS_TYPE_SUM ,GGML_OPT_LOSS_TYPE_MEAN }) { 882 std::pair < int ,int > partial = 883test_gradient_accumulation (optim ,backend_sched ,backend ,nbatch_physical ,loss_type ); 884npass += partial .first ; 885ntest += partial .second ; 886 } 887 } 888 } 889 { 890 std::pair < int ,int > partial = test_regression (optim ,backend_sched ,backend ); 891npass += partial .first ; 892ntest += partial .second ; 893 } 894 895return std::make_pair (npass ,ntest ); 896} 897 898 899int main (void ) { 900ggml_log_set (nullptr ,nullptr ); 901ggml_backend_load_all (); 902const size_t dev_count = ggml_backend_dev_count (); 903printf ("Testing %zu devices\n\n" ,dev_count ); 904size_t n_ok = 0 ; 905 906 std::vector < ggml_backend_dev_t > devs ; 907 std::vector < ggml_backend_t > backends ; 908 909for (size_t i = 0 ;i < dev_count ;++ i ) { 910devs .push_back (ggml_backend_dev_get (i )); 911 912ggml_backend_t backend = ggml_backend_dev_init (devs [i ],NULL ); 913GGML_ASSERT (backend != NULL ); 914 915auto * reg = ggml_backend_dev_backend_reg (devs [i ]); 916auto ggml_backend_set_n_threads_fn = (ggml_backend_set_n_threads_t )ggml_backend_reg_get_proc_address (reg ,"ggml_backend_set_n_threads" ); 917if (ggml_backend_set_n_threads_fn ) { 918ggml_backend_set_n_threads_fn (backend , std::thread::hardware_concurrency () /2 ); 919 } 920backends .push_back (backend ); 921 } 922 923size_t n_total = 0 ; 924for (enum ggml_opt_optimizer_type optim : {GGML_OPT_OPTIMIZER_TYPE_ADAMW ,GGML_OPT_OPTIMIZER_TYPE_SGD }) { 925for (size_t i = 0 ;i < dev_count ;++ i ) { 926// Put the backend to be tested in front so that it's prioritized: 927 std::vector < ggml_backend_t > backends_modded = {backends [i ] }; 928backends_modded .insert (backends_modded .end (),backends .begin (),backends .end ()); 929 930ggml_backend_sched_t backend_sched = ggml_backend_sched_new ( 931backends_modded .data (),nullptr ,backends_modded .size (),GGML_DEFAULT_GRAPH_SIZE , false, true); 932 933char const * devname = ggml_backend_dev_name (devs [i ]); 934printf ("Backend %zu/%zu: %s\n" ,i + 1 ,dev_count ,devname ); 935printf (" Device description: %s\n" ,ggml_backend_dev_description (devs [i ])); 936size_t free ,total ;// NOLINT 937ggml_backend_dev_memory (devs [i ],& free ,& total ); 938printf (" Device memory: %zu MB (%zu MB free)\n" ,total /1024 /1024 ,free /1024 /1024 ); 939printf ("\n" ); 940 941bool skip ; 942 { 943struct ggml_init_params params = { 944/*.mem_size =*/ 6 * ggml_tensor_overhead (), 945/*.mem_buffer =*/ nullptr , 946/*.no_alloc =*/ true, 947 }; 948ggml_context * ctx = ggml_init (params ); 949ggml_tensor * a = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,1 ); 950ggml_set_param (a ); 951ggml_tensor * b = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,1 ); 952ggml_tensor * c = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,1 ); 953ggml_tensor * d = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,1 ); 954 955ggml_tensor * t = nullptr ; 956switch (optim ) { 957case GGML_OPT_OPTIMIZER_TYPE_ADAMW : { 958ggml_tensor * p = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,7 ); 959t = ggml_opt_step_adamw (ctx ,a ,b ,c ,d ,p ); 960 }break ; 961case GGML_OPT_OPTIMIZER_TYPE_SGD : { 962ggml_tensor * p = ggml_new_tensor_1d (ctx ,GGML_TYPE_F32 ,2 ); 963t = ggml_opt_step_sgd (ctx ,a ,b ,p ); 964 }break ; 965case GGML_OPT_OPTIMIZER_TYPE_COUNT : { 966GGML_ABORT ("fatal error" ); 967 } 968 } 969skip = !ggml_backend_supports_op (backends [i ],t ); 970ggml_free (ctx ); 971 } 972 973 std::pair < int ,int > result ; 974if (!skip ) { 975result = test_backend (backend_sched ,backends [i ],optim ); 976printf (" %d/%d tests passed\n" ,result .first ,result .second ); 977 } 978 979printf (" Backend %s %s: " ,ggml_backend_name (backends [i ]),ggml_opt_optimizer_name (optim )); 980if (skip ) { 981printf ("\033[0;33mSKIPPED\033[0m\n" ); 982n_ok ++ ; 983 }else if (result .first == result .second ) { 984printf ("\033[1;32mOK\033[0m\n" ); 985n_ok ++ ; 986 }else { 987printf ("\033[1;31mFAIL\033[0m\n" ); 988 } 989++ n_total ; 990printf ("\n" ); 991ggml_backend_sched_free (backend_sched ); 992 } 993 } 994 995for (ggml_backend_t backend :backends ) { 996ggml_backend_free (backend ); 997 } 998 999printf ("%zu/%zu backend*optimizer passed\n" ,n_ok ,n_total ); 1000bool ok = n_ok == n_total ; 1001print_ok (ok ); 1002return ok ?0 :1 ; 1003}