char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Daniel Beveniuscmake : introduce semantic versioning (#26839)680a9ae63

char/aliceai-t5-moe
18.1 KiB515 linesraw
1#include "ggml.h"
2#include "gguf.h"
3
4#include "arg.h"
5#include "build-info.h"
6#include "common.h"
7#include "llama.h"
8#include "pca.hpp"
9#include "mean.hpp"
10
11#include <clocale>
12
13#ifdef GGML_USE_CUDA
14#include "ggml-cuda.h"
15#endif
16
17#ifdef GGML_USE_METAL
18#include "ggml-metal.h"
19#endif
20
21#include <algorithm>
22#include <climits>
23#include <cstdio>
24#include <cstring>
25#include <fstream>
26#include <iostream>
27#include <string>
28#include <tuple>
29#include <vector>
30
31
32//////////////////////////////////////////////////
33// utils
34
35template <class Iter>
36static std::string tokens_to_str(llama_context * ctx, Iter begin, Iter end) {
37    std::string ret;
38    for (; begin != end; ++begin) {
39        ret += common_token_to_piece(ctx, *begin);
40    }
41
42    return ret;
43}
44
45static void print_usage(int, char ** argv) {
46    printf("\nexample usage:\n");
47    printf("\n    CPU only:   %s -m ./llama-3.Q4_K_M.gguf\n", argv[0]);
48    printf("\n    with GPU:   %s -m ./llama-3.Q4_K_M.gguf -ngl 99\n", argv[0]);
49    printf("\n    advanced:   %s -m ./llama-3.Q4_K_M.gguf -ngl 99 --pca-iter 2000 --pca-batch 100\n", argv[0]);
50    printf("\n    using mean: %s -m ./llama-3.Q4_K_M.gguf --method mean\n", argv[0]);
51    printf("\n");
52}
53
54//////////////////////////////////////////////////
55
56
57// cb_eval is reused for each pair of positive - negative prompt
58struct callback_data {
59    ggml_context * ctx_ggml = nullptr;   // holds v_pos, v_neg, v_diff_filtered
60
61    int n_layers = 0;
62    int n_tokens = 0;
63    bool is_eval_pos = true;
64
65    // each element of the vector correspond to one layer
66    std::vector<struct ggml_tensor *> v_pos; // vector of matrices of size [n_embd, n_tokens]
67    std::vector<struct ggml_tensor *> v_neg; // vector of matrices of size [n_embd, n_tokens]
68    std::vector<struct ggml_tensor *> v_diff_filtered;   // vector of matrices of size [n_embd, n_nonzero_rows]. NOTE: n_nonzero_rows maybe different for each layer
69
70    // save a tensor into either v_pos or v_neg (decided by is_eval_pos)
71    void save_tensor_for_layer(struct ggml_tensor * t) {
72        GGML_ASSERT(t->type == GGML_TYPE_F32);
73
74        if (ctx_ggml == nullptr) {
75            // alloc a new ctx_ggml if needed
76            struct ggml_init_params params_ggml = {
77                /*.mem_size   =*/ ggml_tensor_overhead() * n_layers * 3u,
78                /*.mem_buffer =*/ NULL,
79                /*.no_alloc   =*/ true,
80            };
81            ctx_ggml = ggml_init(params_ggml);
82        }
83
84        // copy tensor data
85        auto n_bytes = ggml_nbytes(t);
86        struct ggml_tensor * t_layer = ggml_new_tensor_2d(ctx_ggml, t->type, t->ne[0], t->ne[1]);
87        t_layer->data = malloc(n_bytes); // TODO @ngxson : get rid of this malloc somehow
88        ggml_backend_tensor_get(t, t_layer->data, 0, n_bytes);
89        ggml_set_name(t_layer, ggml_get_name(t));
90        //print_debug_tensor(t_layer);
91
92        if (is_eval_pos) {
93            v_pos.push_back(t_layer);
94        } else {
95            v_neg.push_back(t_layer);
96        }
97    }
98
99    // calculate diff (v_pos - v_neg) and place the result back to v_pos
100    // all zero rows in the diff tensor will also be removed
101    // NOTE: final layer is ignored. we only have (n_layers - 1) to process
102    std::vector<struct ggml_tensor *> calc_diff() {
103        for (float il = 0; il < v_pos.size(); il++) {
104            float * a = (float *) v_pos[il]->data;
105            float * b = (float *) v_neg[il]->data;
106            size_t n_elem = ggml_nelements(v_pos[il]);
107            for (size_t j = 0; j < n_elem; j++) {
108                a[j] -= b[j];
109            }
110            //print_debug_tensor(v_pos[i]);
111            auto diff_filtered = filter_nonzero_rows(v_pos[il]);
112            v_diff_filtered.push_back(diff_filtered);
113        }
114        return v_diff_filtered; // for convenient, we return the result std::vector
115    }
116
117    // delete zero rows from a given 2D tensor
118    struct ggml_tensor * filter_nonzero_rows(struct ggml_tensor * a) {
119        //printf("filter_nonzero_rows\n");
120        auto is_row_all_zeros = [](struct ggml_tensor * t, int row, float eps) -> bool {
121            // check if given row containing all zero elements
122            int n_cols = t->ne[0]; // hint: should be equal to n_embd
123            for (int col = 0; col < n_cols; ++col) {
124                if (ggml_get_f32_nd(t, col, row, 0, 0) > eps) {
125                    return false;
126                }
127            }
128            return true;
129        };
130        std::vector<int> rows_to_copy; // the idx of non-zero cols (to be copied to row of diff_filtered)
131        for (int i_row = 0; i_row < a->ne[1]; i_row++) {
132            if (!is_row_all_zeros(a, i_row, 1e-6)) {
133                rows_to_copy.push_back(i_row);
134            }
135        }
136
137        // get "n_nonzero_rows" for the output "diff_filtered"
138        int n_nonzero_rows = rows_to_copy.size();
139        //printf("n_nonzero_rows: %d\n", n_nonzero_rows);
140        int n_embd = a->ne[0];
141        GGML_ASSERT(n_nonzero_rows > 0);
142
143        // diff_filtered: [n_embd, n_nonzero_rows]
144        struct ggml_tensor * diff_filtered = ggml_new_tensor_2d(
145            ctx_ggml, GGML_TYPE_F32, n_embd, n_nonzero_rows);
146        ggml_format_name(diff_filtered, "diff_filtered_%s", a->name);
147        diff_filtered->data = malloc(ggml_nbytes(diff_filtered));
148
149        // copy non-zero rows
150        for (int dest_row = 0; dest_row < n_nonzero_rows; dest_row++) {
151            int src_row = rows_to_copy[dest_row];
152            for (int i = 0; i < n_embd; i++) {
153                float src_elem = ggml_get_f32_nd(a, i, src_row, 0, 0);
154                ggml_set_f32_nd(diff_filtered, i, dest_row, 0, 0, src_elem);
155            }
156        }
157
158        //print_debug_tensor(diff_filtered);
159
160        return diff_filtered;
161    }
162
163    // we don't implement destructor, because we want to reuse callback_data. we just want to free the tensors
164    void reset() {
165        for (auto ptr : v_pos) free(ptr->data);
166        for (auto ptr : v_neg) free(ptr->data);
167        for (auto ptr : v_diff_filtered) free(ptr->data);
168        v_pos.clear();
169        v_neg.clear();
170        v_diff_filtered.clear();
171        if (ctx_ggml) {
172            ggml_free(ctx_ggml);
173        }
174        ctx_ggml = nullptr;
175    }
176};
177
178/**
179 * process_ctx is used to store the ggml context for pre-post processing the diff vectors
180 * in short, input => v_diff and output => v_final
181 */
182struct train_context {
183    ggml_context * ctx_ggml;
184    int n_embd;
185    int n_layers;
186
187    /* pair of prompts to be used for generating final vector */
188    std::vector<std::string> positive_entries;
189    std::vector<std::string> negative_entries;
190
191    // each element of the vector correspond to one layer
192    // NOTE: the last layer is discard. therefore, we will have (n_layers - 1) elements here
193    // NOTE (2): v_diff is transposed from v_diff_tmp
194    std::vector<struct ggml_tensor *> v_diff;  // vector of matrices of size [m, n_embd] where m ~ n_tokens * n_completions (v_diff contains no zero-rows)
195    std::vector<struct ggml_tensor *> v_final; // vector of vectors of size [n_embd] to be written to file
196
197    // to easily re-alloc when concat v_diff, we temporary store v_diff in a vector instead of a tensor
198    // v_diff_tmp will get converted unto v_diff later on
199    std::vector<std::vector<uint8_t>> v_diff_tmp;
200
201    train_context(int n_embd_, int n_layers_) {
202        n_embd = n_embd_;
203        n_layers = n_layers_;
204        struct ggml_init_params params_ggml = {
205            /*.mem_size   =*/ ggml_tensor_overhead() * (n_layers - 1) * 2u,
206            /*.mem_buffer =*/ NULL,
207            /*.no_alloc   =*/ true,
208        };
209        ctx_ggml = ggml_init(params_ggml);
210        for (int il = 0; il < n_layers - 1; il++) {
211            std::vector<uint8_t> empty;
212            v_diff_tmp.push_back(empty);
213            auto t = ggml_new_tensor_1d(ctx_ggml, GGML_TYPE_F32, n_embd);
214            t->data = malloc(ggml_nbytes(t)); // TODO: get rid of malloc if possible
215            v_final.push_back(t);
216        }
217    }
218
219    // add new rows into existing tensor in v_diff_tmp
220    void concat_diff_tmp(const std::vector<struct ggml_tensor *> & diff_filtered) {
221        GGML_ASSERT((int) diff_filtered.size() == n_layers - 1);
222        for (int il = 0; il < n_layers - 1; il++) {
223            auto t = diff_filtered[il];
224            auto & diff_tmp = v_diff_tmp[il];
225            size_t curr_size = diff_tmp.size();
226            diff_tmp.resize(curr_size + ggml_nbytes(t));
227            memcpy(diff_tmp.data() + curr_size, t->data, ggml_nbytes(t));
228        }
229    }
230
231    // build the v_diff tensors from v_diff_tmp (v_diff need to be transposed)
232    // TODO @ngxson : maybe add option NOT to transpose v_diff; will be useful for "mean" method
233    void build_v_diff(bool transpose) {
234        printf("build_v_diff\n");
235        for (int il = 0; il < n_layers - 1; il++) {
236            auto & diff_tmp = v_diff_tmp[il];
237            int n_elem = diff_tmp.size() / sizeof(float);
238            GGML_ASSERT(n_elem % n_embd == 0);
239            int n_rows = n_elem / n_embd;
240            struct ggml_tensor * diff = transpose
241                ? ggml_new_tensor_2d(ctx_ggml, GGML_TYPE_F32, n_rows, n_embd)
242                : ggml_new_tensor_2d(ctx_ggml, GGML_TYPE_F32, n_embd, n_rows);
243            ggml_set_name(diff, (std::string("diff_") + std::to_string(il)).c_str());
244            diff->data = malloc(ggml_nbytes(diff)); // TODO: get rid of this malloc if possible
245            if (transpose) {
246                // copy data & transpose
247                float * arr = (float *) diff_tmp.data();
248                for (int ir = 0; ir < n_rows; ++ir) {
249                    for (int ic = 0; ic < n_embd; ++ic) {
250                        float f = arr[ir*n_embd + ic];
251                        ggml_set_f32_nd(diff, ir, ic, 0, 0, f);
252                    }
253                }
254            } else {
255                // only copy
256                memcpy(diff->data, diff_tmp.data(), ggml_nbytes(diff));
257            }
258            v_diff.push_back(diff);
259            print_debug_tensor(diff);
260            // free memory of diff_tmp
261            diff_tmp.resize(0);
262        }
263    }
264
265    ~train_context() {
266        for (auto ptr : v_final) free(ptr->data);
267        for (auto ptr : v_diff) free(ptr->data);
268        // no need to free v_diff_tmp, since we didn't use malloc
269        ggml_free(ctx_ggml);
270    }
271};
272
273struct tokenized_prompt {
274    std::vector<llama_token> tokens_pos;
275    std::vector<llama_token> tokens_neg;
276    size_t max_seq_len;
277
278    tokenized_prompt(llama_context * ctx, std::string pos, std::string neg) {
279        const llama_model * model = llama_get_model(ctx);
280        const llama_vocab * vocab = llama_model_get_vocab(model);
281        const bool add_bos = llama_vocab_get_add_bos(vocab);
282        tokens_pos = common_tokenize(ctx, pos, add_bos, true);
283        tokens_neg = common_tokenize(ctx, neg, add_bos, true);
284        max_seq_len = std::max(tokens_pos.size(), tokens_neg.size());
285        padding_seq(ctx, tokens_pos, max_seq_len);
286        padding_seq(ctx, tokens_neg, max_seq_len);
287    }
288
289    void padding_seq(llama_context * ctx, std::vector<llama_token> & tokens, size_t len) {
290        // TODO: customize padding token
291        std::vector<llama_token> pad_tokens = common_tokenize(ctx, " ", false);
292        llama_token pad_tok = pad_tokens.back();
293        while (tokens.size() < len) {
294            tokens.push_back(pad_tok);
295        }
296    }
297};
298
299//////////////////////////////////////////////////
300
301template <typename T>
302static std::string to_string(const T & val) {
303    std::stringstream ss;
304    ss << val;
305    return ss.str();
306}
307
308static std::vector<std::string> ctrlvec_load_prompt_file(std::string path, bool skip_empty_lines) {
309    std::vector<std::string> output;
310    std::ifstream file(path);
311    if (!file.is_open()) {
312        fprintf(stderr, "error: unable to open file: %s\n", path.c_str());
313        exit(1);
314    }
315    std::string line;
316    while (std::getline(file, line)) {
317        bool is_skip = skip_empty_lines && line.empty();
318        if (!is_skip) {
319            string_process_escapes(line);
320            output.push_back(line);
321        }
322    }
323    file.close();
324    return output;
325}
326
327//////////////////////////////////////////////////
328
329static bool cb_eval(struct ggml_tensor * t, bool ask, void * user_data) {
330    auto * cb_data = (callback_data *) user_data;
331    static const char * l_out_name = "l_out";
332    const bool is_l_out = strncmp(t->name, l_out_name, strlen(l_out_name)) == 0;
333
334    if (ask) {
335        return is_l_out;
336    }
337
338    if (!is_l_out || t->ne[1] != cb_data->n_tokens) {
339        return true;
340    }
341
342    // save the tensor to current context
343    cb_data->save_tensor_for_layer(t);
344    return true;
345}
346
347static bool get_hidden_layers(llama_context * ctx, std::vector<llama_token> & tokens) {
348    llama_memory_clear(llama_get_memory(ctx), true);
349    if (llama_decode(ctx, llama_batch_get_one(tokens.data(), tokens.size()))) {
350        fprintf(stderr, "%s : failed to eval\n", __func__);
351        return false;
352    }
353    return true;
354}
355
356static void export_gguf(const std::vector<struct ggml_tensor *> & v_ctrl, const std::string fname, const std::string model_hint) {
357    struct gguf_context * ctx = gguf_init_empty();
358
359    const std::string arch = "controlvector";
360    gguf_set_val_str(ctx, "general.architecture", arch.c_str());
361    gguf_set_val_str(ctx, (arch + ".model_hint").c_str(), model_hint.c_str());
362    gguf_set_val_i32(ctx, (arch + ".layer_count").c_str(), v_ctrl.size());
363
364    for (size_t i = 0; i < v_ctrl.size(); ++i) {
365        gguf_add_tensor(ctx, v_ctrl[i]);
366        print_debug_tensor(v_ctrl[i]);
367        printf("Added tensor: %s\n", v_ctrl[i]->name);
368    }
369
370    printf("%s: writing file...\n", __func__);
371    gguf_write_to_file(ctx, fname.c_str(), false);
372    printf("%s: wrote file '%s'\n", __func__, fname.c_str());
373    gguf_free(ctx);
374}
375
376/**
377 * Load prompt files and completion file.
378 * Then format each pair of prompt + completion to make an entry.
379 */
380static int prepare_entries(common_params & params, train_context & ctx_train) {
381    // load prompts
382    std::vector<std::string> positive_prompts = ctrlvec_load_prompt_file(params.cvector_positive_file, true);
383    std::vector<std::string> negative_prompts = ctrlvec_load_prompt_file(params.cvector_negative_file, true);
384    if (positive_prompts.size() != negative_prompts.size()) {
385        fprintf(stderr, "number of positive and negative prompts must be equal\n");
386        return 1;
387    }
388    if (positive_prompts.empty()) {
389        fprintf(stderr, "must provide at least one prompt pair\n");
390        return 1;
391    }
392    ctx_train.positive_entries = positive_prompts;
393    ctx_train.negative_entries = negative_prompts;
394    return 0;
395}
396
397int main(int argc, char ** argv) {
398    std::setlocale(LC_NUMERIC, "C");
399
400    common_params params;
401
402    params.out_file = "control_vector.gguf";
403
404    common_init();
405
406    if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_CVECTOR_GENERATOR, print_usage)) {
407        return 1;
408    }
409
410    if (params.n_pca_iterations % params.n_pca_batch != 0) {
411        fprintf(stderr, "PCA iterations must by multiply of PCA batch size\n");
412        return 1;
413    }
414
415
416    callback_data cb_data;
417
418    // pass the callback to the backend scheduler
419    // it will be executed for each node during the graph computation
420    params.cb_eval = cb_eval;
421    params.cb_eval_user_data = &cb_data;
422    params.warmup = false;
423
424    llama_print_build_info(llama_version());
425    llama_backend_init();
426    llama_numa_init(params.numa);
427
428    // load the model to get hparams
429    auto llama_init = common_init_from_params(params);
430
431    auto * model = llama_init->model();
432    auto * ctx   = llama_init->context();
433
434    // int n_ctx = llama_n_ctx(ctx);
435    int n_layers = llama_model_n_layer(model);
436    int n_embd = llama_model_n_embd(model);
437
438    // get model hint param (a.k.a model arch name)
439    char model_hint[128];
440    llama_model_meta_val_str(model, "general.architecture", model_hint, 128);
441
442    // init train_context
443    train_context ctx_train(n_embd, n_layers);
444
445    // load and prepare entries for training
446    prepare_entries(params, ctx_train);
447
448    // we have to pretokenize everything because otherwise we don't know how much overhead to allocate ctx_diffs_wrapped
449    std::vector<tokenized_prompt> tokenized_prompts;
450    size_t n_total_tokens = 0;
451    for (size_t i = 0; i < ctx_train.positive_entries.size(); ++i) {
452        tokenized_prompt t(ctx, ctx_train.positive_entries[i], ctx_train.negative_entries[i]);
453        n_total_tokens += 2 * t.max_seq_len;
454        tokenized_prompts.push_back(std::move(t));
455    }
456
457    std::cout << "n_total_tokens: " << n_total_tokens << std::endl;
458
459    for(size_t i = 0; i < ctx_train.positive_entries.size(); ++i) {
460        bool success = false;
461        tokenized_prompt t = tokenized_prompts[i];
462        cb_data.n_layers = n_layers;
463        cb_data.n_tokens = t.max_seq_len;
464
465        printf("Evaluating prompt[%d/%d]: \"%s\" - \"%s\" (%d tokens)\n",
466            (int) i+1, (int) ctx_train.positive_entries.size(),
467            tokens_to_str(ctx, t.tokens_pos.cbegin(), t.tokens_pos.cend()).c_str(),
468            tokens_to_str(ctx, t.tokens_neg.cbegin(), t.tokens_neg.cend()).c_str(),
469            (int) t.max_seq_len);
470
471        cb_data.is_eval_pos = true;
472        success = get_hidden_layers(ctx, t.tokens_pos);
473        if (!success) break;
474
475        cb_data.is_eval_pos = false;
476        success = get_hidden_layers(ctx, t.tokens_neg);
477        if (!success) break;
478
479        // calculate diff and remove all zero rows
480        auto v_diff_filtered = cb_data.calc_diff();
481
482        // save & concat the filtered v_diff to ctx_train
483        ctx_train.concat_diff_tmp(v_diff_filtered);
484
485        // reset for next iteration
486        cb_data.reset();
487    }
488
489    // done with the model, we can now free it to make gain some memory
490    printf("Done evaluate prompts, unload model...\n");
491
492    bool use_pca = params.cvector_dimre_method == DIMRE_METHOD_PCA;
493
494    // prepare ctx_train for PCA
495    ctx_train.build_v_diff(use_pca);
496
497    if (use_pca) {
498        // run PCA
499        PCA::pca_params pca_params;
500        pca_params.n_threads    = params.cpuparams.n_threads;
501        pca_params.n_batch      = params.n_pca_batch;
502        pca_params.n_iterations = params.n_pca_iterations;
503        PCA::run_pca(pca_params, ctx_train.v_diff, ctx_train.v_final);
504    } else {
505        // run mean
506        mean::run(ctx_train.v_diff, ctx_train.v_final);
507    }
508
509    // write output vectors to gguf
510    export_gguf(ctx_train.v_final, params.out_file, model_hint);
511
512    llama_backend_free();
513
514    return 0;
515}