Update README.md
Browse files
README.md
CHANGED
@@ -44,4 +44,71 @@ const model = await AutoModel.from_pretrained(MODEL_ID, {
|
|
44 |
|
45 |
|
46 |
// the types are wildy incorrect... but this should get you what you need!
|
47 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
|
46 |
// the types are wildy incorrect... but this should get you what you need!
|
47 |
+
```
|
48 |
+
|
49 |
+
#### Rough Outline of Getting Attentions
|
50 |
+
|
51 |
+
> This may not be the best way but the documentation is truly lacking and this does the job :-P
|
52 |
+
|
53 |
+
```typescript
|
54 |
+
/**
|
55 |
+
* Collect attentions across layers from a model.forward(...) output.
|
56 |
+
*
|
57 |
+
* ⚠️ Transformers.js variation:
|
58 |
+
* - Some builds return `{ attentions: Tensor[] }`.
|
59 |
+
* - Others return a dict with `attention_1`, `attention_2`, ... per layer.
|
60 |
+
*
|
61 |
+
* @internal
|
62 |
+
* @param out Raw dictionary from `model.forward(...)`.
|
63 |
+
* @returns Array of attention tensors (one per layer) with dims `[1, H, T, T]`.
|
64 |
+
*/
|
65 |
+
function collectAttentions(out: Record<string, Tensor>): XtTensor[] {
|
66 |
+
// Prefer array form if present (runtime feature; TS types don’t guarantee it).
|
67 |
+
const anyOut = out as unknown as { attentions?: XtTensor[] };
|
68 |
+
if (Array.isArray(anyOut.attentions)) return anyOut.attentions;
|
69 |
+
|
70 |
+
// Otherwise gather attention_1..attention_N and sort numerically by suffix.
|
71 |
+
const keys = Object.keys(out)
|
72 |
+
.filter((k) => /^attention_\d+$/i.test(k))
|
73 |
+
.sort(
|
74 |
+
(a, b) => parseInt(a.split('_')[1], 10) - parseInt(b.split('_')[1], 10),
|
75 |
+
);
|
76 |
+
|
77 |
+
return keys.map((k) => out[k] as unknown as XtTensor);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Tokenization:
|
82 |
+
* Prefer the public callable form `tokenizer(text, {...})` which returns tensors.
|
83 |
+
* In case your wrapper only exposes a `_call` (private-ish) we fall back to it here.
|
84 |
+
* The return includes `input_ids` and `attention_mask` tensors.
|
85 |
+
*/
|
86 |
+
const enc =
|
87 |
+
typeof (tokenizer as typeof tokenizer._call) === 'function' ?
|
88 |
+
// eslint-disable-next-line @typescript-eslint/await-thenable
|
89 |
+
await (tokenizer as typeof tokenizer._call)(text, {
|
90 |
+
add_special_tokens: true,
|
91 |
+
})
|
92 |
+
: tokenizer._call(text, { add_special_tokens: true }); // <-- documented hack
|
93 |
+
|
94 |
+
// Convert tensor buffers (may be BigInt) → number[] for downstream processing.
|
95 |
+
const input_ids = Array.from(
|
96 |
+
(enc.input_ids as Tensor).data as ArrayLike<number | bigint>,
|
97 |
+
).map(Number);
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Forward pass with attentions.
|
101 |
+
*
|
102 |
+
* Another "crazy" bit: different Transformers.js builds expose attentions differently. We:
|
103 |
+
* - accept `{ attentions: Tensor[] }`, or
|
104 |
+
* - collect `attention_1, attention_2, ...` and sort them.
|
105 |
+
* Also, `Tensor` has no `.get(...)` so we do **flat buffer indexing** with `dims`.
|
106 |
+
*/
|
107 |
+
const out = (await model.forward({
|
108 |
+
input_ids,
|
109 |
+
attention_mask: windowAttentionMask,
|
110 |
+
output_attentions: true,
|
111 |
+
})) as unknown as Record<string, Tensor>;
|
112 |
+
|
113 |
+
const attentions = collectAttentions(out)
|
114 |
+
```
|