MCPcopy Create free account
hub / github.com/OpenNMT/CTranslate2 / TEST

Function TEST

tests/attention_test.cc:129–171  ·  view source on GitHub ↗

Merged self+cross attention (T5Gemma2 style): self-attention layer also projects encoder memory through a separate `memory_kv` linear and concatenates the result onto the self-attention K/V before softmax.

Source from the content-addressed store, hash-verified

127// projects encoder memory through a separate `memory_kv` linear and concatenates
128// the result onto the self-attention K/V before softmax.
129TEST(MergedAttentionTest, ForwardMergedProducesOutput) {
130 constexpr dim_t NUM_HEADS = 4, NUM_KV = 1, D_HEAD = 16;
131 constexpr dim_t D_MODEL = NUM_HEADS * D_HEAD;
132 constexpr dim_t QKV_ROWS = (NUM_HEADS + 2 * NUM_KV) * D_HEAD;
133 constexpr dim_t KV_ROWS = 2 * NUM_KV * D_HEAD;
134
135 class MergedMockModel : public models::Model {
136 public:
137 MergedMockModel() {
138 register_variable("attn/linear_0/weight",
139 StorageView({QKV_ROWS, D_MODEL}, std::vector<float>(QKV_ROWS * D_MODEL, 0.01f)));
140 register_variable("attn/linear_1/weight",
141 StorageView({D_MODEL, NUM_HEADS * D_HEAD},
142 std::vector<float>(D_MODEL * NUM_HEADS * D_HEAD, 0.01f)));
143 register_variable("attn/memory_kv/weight",
144 StorageView({KV_ROWS, D_MODEL}, std::vector<float>(KV_ROWS * D_MODEL, 0.01f)));
145 register_variable("attn/q_norm/gamma", StorageView({D_HEAD}, std::vector<float>(D_HEAD, 1.0f)));
146 register_variable("attn/k_norm/gamma", StorageView({D_HEAD}, std::vector<float>(D_HEAD, 1.0f)));
147 register_variable("attn/num_heads_kv", StorageView(static_cast<int32_t>(NUM_KV)));
148 set_compute_type(ComputeType::FLOAT32, Device::CPU, 0, false);
149 }
150 protected:
151 std::unique_ptr<Model> clone() const override { return nullptr; }
152 };
153
154 MergedMockModel model;
155 layers::MultiHeadAttention attention(model, "attn", NUM_HEADS, /*self_attention=*/true);
156 ASSERT_TRUE(attention.has_merged_encoder_attention());
157
158 constexpr dim_t B = 1, Q_LEN = 1, MEM_LEN = 5;
159 StorageView queries({B, Q_LEN, D_MODEL}, std::vector<float>(B * Q_LEN * D_MODEL, 1.0f));
160 StorageView memory({B, MEM_LEN, D_MODEL}, std::vector<float>(B * MEM_LEN * D_MODEL, 1.0f));
161 StorageView output(DataType::FLOAT32);
162 StorageView self_k(DataType::FLOAT32), self_v(DataType::FLOAT32);
163 StorageView mem_k(DataType::FLOAT32), mem_v(DataType::FLOAT32);
164
165 attention.forward_merged(queries, &memory, nullptr, nullptr, output,
166 &self_k, &self_v, &mem_k, &mem_v, nullptr, nullptr, /*offset=*/0);
167
168 EXPECT_EQ(output.shape(), (Shape{B, Q_LEN, D_MODEL}));
169 EXPECT_EQ(mem_k.shape(), (Shape{B, NUM_HEADS, MEM_LEN, D_HEAD}));
170 EXPECT_EQ(self_k.shape(), (Shape{B, NUM_HEADS, Q_LEN, D_HEAD}));
171}
172
173// MHA: Each head has independent K/V
174TEST_F(CrossAttentionTest, StandardMultiHeadAttention) {

Callers

nothing calls this directly

Calls 3

forward_mergedMethod · 0.80
shapeMethod · 0.45

Tested by

no test coverage detected