|
| 1 | +## llama_batch embd |
| 2 | +This document will take a look at the `embd` field of `llama_batch` and see how |
| 3 | +it can be used and for what purposes. |
| 4 | + |
| 5 | +So this is a field similar to the `llama_batch.token` field which is a pointer |
| 6 | +to the input tokens. So we might have a prompt which we tokenize which the |
| 7 | +is bacically splitting the prompt into tokens and looking them up in the models |
| 8 | +vocabulary. This is what is passed to the `llama_batch.token` field. We can see |
| 9 | +in following function that if the batch has tokens then a 1d tensor will be |
| 10 | +created for the `inp_tokens`: |
| 11 | +```c++ |
| 12 | +static struct ggml_tensor * llm_build_inp_embd( |
| 13 | + struct ggml_context * ctx, |
| 14 | + struct llama_context & lctx, |
| 15 | + const llama_hparams & hparams, |
| 16 | + const llama_ubatch & batch, |
| 17 | + struct ggml_tensor * tok_embd, |
| 18 | + const llm_build_cb & cb) { |
| 19 | + const int64_t n_embd = hparams.n_embd; |
| 20 | + |
| 21 | + struct ggml_tensor * inpL; |
| 22 | + |
| 23 | + if (batch.token) { |
| 24 | + lctx.inp_tokens = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, batch.n_tokens); |
| 25 | + cb(lctx.inp_tokens, "inp_tokens", -1); |
| 26 | + ggml_set_input(lctx.inp_tokens); |
| 27 | + |
| 28 | + inpL = ggml_get_rows(ctx, tok_embd, lctx.inp_tokens); |
| 29 | + } else { |
| 30 | + lctx.inp_embd = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n_embd, batch.n_tokens); |
| 31 | + inpL = lctx.inp_embd; |
| 32 | + ggml_set_input(lctx.inp_embd); |
| 33 | + } |
| 34 | + |
| 35 | + cb(inpL, "inp_embd", -1); |
| 36 | + |
| 37 | + return inpL; |
| 38 | +} |
| 39 | +``` |
| 40 | +After the computation graph has been built the `inp_tokens` tensor will be |
| 41 | +populated by the tokens in the batch: |
| 42 | +```c++ |
| 43 | +static void llama_set_inputs(llama_context & lctx, const llama_ubatch & batch) { |
| 44 | + ... |
| 45 | +
|
| 46 | + if (batch.token) { |
| 47 | + const int64_t n_tokens = batch.n_tokens; |
| 48 | +
|
| 49 | + ggml_backend_tensor_set(lctx.inp_tokens, batch.token, 0, n_tokens*ggml_element_size(lctx.inp_tokens)); |
| 50 | + } |
| 51 | +``` |
| 52 | +To be clear this tensor is a 1d tensor of tokens and the type is I32, we don't |
| 53 | +yet have the token embeddings yet (which are float32 values). |
| 54 | +```console |
| 55 | +(gdb) p *lctx.inp_tokens |
| 56 | +$4 = {type = GGML_TYPE_I32, backend = GGML_BACKEND_TYPE_CPU, buffer = 0x0, |
| 57 | +ne = {512, 1, 1, 1}, nb = {4, 2048, 2048, 2048}, op = GGML_OP_NONE, |
| 58 | +op_params = {0 <repeats 16 times>}, flags = 0, grad = 0x0, src = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, |
| 59 | +0x0, 0x0, 0x0}, view_src = 0x0, view_offs = 0, data = 0x0, name = '\000' <repeats 63 times>, extra = 0x0} |
| 60 | +``` |
| 61 | +The token embeddings can be thought of being looked up from the model which has |
| 62 | +learned an embedding representation of the tokens in its vocabulary. These |
| 63 | +embeddings don't have any context yet, so the token embedding for the token |
| 64 | +`cold` will be the same regardless of the context (sentence) in which it is used. |
| 65 | +Notice that `inpL` is then assigned to the tensor operation `ggml_get_rows` |
| 66 | +which will use the tokens as indices to look up the embeddings in the `tok_embd` |
| 67 | +tensor which is a 2d tensor of float32 (or quantized type but not I32 is my main |
| 68 | +point here) values: |
| 69 | +```console |
| 70 | +(gdb) p *tok_embd |
| 71 | +$7 = {type = GGML_TYPE_Q4_K, backend = GGML_BACKEND_TYPE_CPU, buffer = 0x555555a0cf10, |
| 72 | +ne = {4096, 32000, 1, 1}, nb = {144, 2304, 73728000, 73728000}, |
| 73 | +op = GGML_OP_NONE, op_params = {0 <repeats 16 times>}, flags = 0, grad = 0x0, src = {0x0, |
| 74 | +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, view_src = 0x0, view_offs = 0, data = 0x7fff042b4ec0, |
| 75 | +name = "token_embd.weight", '\000' <repeats 46 times>, extra = 0x0} |
| 76 | +``` |
| 77 | +So this is how the lookup is performed. |
| 78 | + |
| 79 | +Now, with that in mind when we have `batch.embd` set to point to embeddings in |
| 80 | +stead of having tokenss in the batch the `inp_embd` tensor will be created |
| 81 | +directly as a 2d tensor of float32 values: |
| 82 | +```c++ |
| 83 | + lctx.inp_embd = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n_embd, batch.n_tokens); |
| 84 | + inpL = lctx.inp_embd; |
| 85 | + ggml_set_input(lctx.inp_embd); |
| 86 | +``` |
| 87 | +And later in the `llama_set_inputs` the `inp_embd` tensor will be populated |
| 88 | +with the embeddings from the batch: |
| 89 | +```c++ |
| 90 | +static void llama_set_inputs(llama_context & lctx, const llama_ubatch & batch) { |
| 91 | + ... |
| 92 | + if (batch.embd) { |
| 93 | + const int64_t n_embd = hparams.n_embd; |
| 94 | + const int64_t n_tokens = batch.n_tokens; |
| 95 | +
|
| 96 | + ggml_backend_tensor_set(lctx.inp_embd, batch.embd, 0, n_tokens*n_embd*ggml_element_size(lctx.inp_embd)); |
| 97 | + } |
| 98 | +``` |
| 99 | +So my understanding of `batch.embd` is that the embeddings we can pass to the |
| 100 | +model should be the context unaware embeddings. I initially thought that these |
| 101 | +embeddings could be the result of a program like `llama_embedding` which can |
| 102 | +output context aware embeddings. I actually tried this but was not able to get |
| 103 | +it to work and while I decided to write this document to sort out my thoughts. |
| 104 | + |
| 105 | +_wip_ |
0 commit comments