CoolFace
Modelpublic

Felipe97/llama-cpp-compiled

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