Skip to content

Commit daf6857

Browse files
committed
docs: add initial notes on llama_batch embd (wip)
1 parent e00fd86 commit daf6857

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

‎notes/llama-batch-embd.md‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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

Comments
 (0)