Skip to content

Commit 310c7fc

Browse files
authored
Merge pull request #463 from InfiniTensor/feat/support-video-nsa
feat: support video nsa
2 parents 36e2207 + d9f14e6 commit 310c7fc

19 files changed

Lines changed: 2052 additions & 11 deletions

‎csrc/engine/infer_engine.cpp‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ InferEngine::Input::to_model_input(infinicore::Device device) const {
156156
to_device_vec(pixel_values),
157157
to_device_vec(image_bound),
158158
to_device_vec(tgt_sizes),
159+
visual_token_ranges,
159160
};
160161

161162
infinilm::global_state::get_forward_context().attn_metadata = {
@@ -167,7 +168,8 @@ InferEngine::Input::to_model_input(infinicore::Device device) const {
167168
input.slot_mapping};
168169

169170
global_state::get_forward_context().mm_metadata = {
170-
image_req_ids};
171+
image_req_ids,
172+
visual_token_ranges};
171173

172174
return input;
173175
}

‎csrc/engine/rank_worker.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class RankWorker {
6060
std::optional<std::vector<infinicore::Tensor>> tgt_sizes;
6161
/// req_id for each pixel_values among a batch
6262
std::optional<std::vector<size_t>> image_req_ids;
63+
/// Flattened [start, end) visual token ranges in the packed language sequence.
64+
std::optional<std::vector<size_t>> visual_token_ranges;
6365

6466
float temperature{1};
6567

‎csrc/global_state/forward_context.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ struct AttentionMetadata {
4242

4343
struct MultiModalMetadata {
4444
std::optional<std::vector<size_t>> image_req_ids;
45+
// Flattened [start, end) token ranges in the current packed language sequence.
46+
std::optional<std::vector<size_t>> visual_token_ranges;
4547
};
4648

4749
struct ForwardContext {

‎csrc/models/infinilm_model.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class InfinilmModel : public infinicore::nn::Module {
4343
/// Target patch sizes for each image (MiniCPM-V).
4444
/// Vector of tensors shape: [n_path, 2] if pre-flattened.
4545
std::optional<std::vector<infinicore::Tensor>> tgt_sizes;
46+
/// Flattened [start, end) visual token ranges in the packed language sequence.
47+
std::optional<std::vector<size_t>> visual_token_ranges;
4648
};
4749

4850
struct Output {

‎csrc/models/videonsa/videonsa_attention.cpp‎

Lines changed: 469 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#pragma once
2+
3+
#include "../../layers/attention/attention.hpp"
4+
#include <array>
5+
6+
#include <optional>
7+
8+
namespace infinilm::models::videonsa {
9+
10+
class VideoNSAAttention : public infinilm::layers::attention::Attention {
11+
public:
12+
VideoNSAAttention(std::shared_ptr<infinilm::config::ModelConfig> model_config,
13+
size_t layer_idx,
14+
const infinicore::Device &device);
15+
16+
infinicore::Tensor forward(const infinicore::Tensor &positions,
17+
const infinicore::Tensor &hidden_states) const;
18+
19+
void process_weights_after_loading() override;
20+
void reset_runtime_state() const override;
21+
22+
protected:
23+
INFINICORE_NN_MODULE(infinilm::layers::linear::ReplicatedLinear, g_proj_1);
24+
INFINICORE_NN_MODULE(infinilm::layers::linear::ReplicatedLinear, g_proj_2);
25+
26+
mutable std::optional<infinicore::Tensor> nsa_k_cmp_cache_;
27+
mutable std::optional<infinicore::Tensor> nsa_v_cmp_cache_;
28+
mutable bool nsa_cmp_cache_ready_ = false;
29+
mutable std::optional<infinicore::Tensor> mrope_sin_cache_;
30+
mutable std::optional<infinicore::Tensor> mrope_cos_cache_;
31+
std::array<int, 3> mrope_section_ = {0, 0, 0};
32+
size_t mrope_rotary_dim_ = 0;
33+
bool mrope_interleaved_ = false;
34+
size_t total_num_attention_heads_ = 0;
35+
size_t max_position_embeddings_ = 0;
36+
};
37+
38+
} // namespace infinilm::models::videonsa
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include "videonsa_for_conditional_generation.hpp"
2+
#include "../../global_state/global_state.hpp"
3+
#include "../models_registry.hpp"
4+
#include "infinicore/ops/cat.hpp"
5+
#include <stdexcept>
6+
#include <string>
7+
8+
namespace infinilm::models::videonsa {
9+
10+
namespace {
11+
12+
std::shared_ptr<infinilm::config::ModelConfig> text_config_from(std::shared_ptr<infinilm::config::ModelConfig> model_config) {
13+
nlohmann::json &config_json = model_config->get_config_json();
14+
nlohmann::json text_config_json = config_json.contains("text_config") ? config_json["text_config"] : config_json;
15+
text_config_json["model_type"] = "videonsa";
16+
if (!text_config_json.contains("torch_dtype") && config_json.contains("torch_dtype")) {
17+
text_config_json["torch_dtype"] = config_json["torch_dtype"];
18+
}
19+
if (!text_config_json.contains("head_dim")) {
20+
text_config_json["head_dim"] = text_config_json["hidden_size"].get<size_t>() / text_config_json["num_attention_heads"].get<size_t>();
21+
}
22+
if (!text_config_json.contains("attention_bias")) {
23+
text_config_json["attention_bias"] = true;
24+
}
25+
return std::make_shared<infinilm::config::ModelConfig>(text_config_json);
26+
}
27+
28+
} // namespace
29+
30+
VideoNSAForConditionalGeneration::VideoNSAForConditionalGeneration(std::shared_ptr<infinilm::config::ModelConfig> model_config,
31+
const infinicore::Device &device) {
32+
model_config_ = model_config;
33+
auto text_config = text_config_from(model_config);
34+
const size_t hidden_size = text_config->get<size_t>("hidden_size");
35+
const size_t vocab_size = text_config->get<size_t>("vocab_size");
36+
const auto &dtype{text_config->get_dtype()};
37+
38+
INFINICORE_NN_MODULE_INIT(model, text_config, device);
39+
INFINICORE_NN_MODULE_INIT(visual, model_config->get_config_json()["vision_config"], dtype, device);
40+
INFINICORE_NN_MODULE_INIT(lm_head, hidden_size, vocab_size, false, dtype, device);
41+
}
42+
43+
void VideoNSAForConditionalGeneration::replace_embeddings(infinicore::Tensor inputs_embeds,
44+
const infinicore::Tensor &vision_hidden,
45+
const infinicore::Tensor &image_bound) const {
46+
auto bounds_cpu = image_bound->to(infinicore::Device::cpu());
47+
auto out_slice = inputs_embeds->squeeze(0);
48+
auto bound_slice = bounds_cpu->squeeze(0);
49+
auto bound_count = bound_slice->size(0);
50+
size_t vision_offset = 0;
51+
for (size_t i = 0; i < bound_count; ++i) {
52+
auto bound = bound_slice->narrow({{0, i, 1}});
53+
auto bound_ptr = reinterpret_cast<const int64_t *>(bound->data());
54+
auto start = bound_ptr[0];
55+
auto end = bound_ptr[1];
56+
if (end <= start) {
57+
continue;
58+
}
59+
const size_t len = static_cast<size_t>(end - start);
60+
auto patch_embed = vision_hidden->narrow({{0, vision_offset, len}});
61+
out_slice->narrow({{0, size_t(start), len}})->copy_from(patch_embed);
62+
vision_offset += len;
63+
}
64+
}
65+
66+
infinilm::InfinilmModel::Output VideoNSAForConditionalGeneration::forward(const infinilm::InfinilmModel::Input &input) const {
67+
if (input.pixel_values.has_value() && input.pixel_values.value().size() > 0) {
68+
if (!input.image_bound.has_value() || !input.tgt_sizes.has_value()) {
69+
throw std::runtime_error("VideoNSAForConditionalGeneration: image_bound and tgt_sizes must be provided with pixel_values");
70+
}
71+
auto input_ids = input.input_ids.value();
72+
auto inputs_embeds = model_->embed_tokens(input_ids);
73+
auto input_offsets_cpu = input.input_offsets.value()->to(infinicore::Device::cpu());
74+
int32_t *offsets = reinterpret_cast<int32_t *>(input_offsets_cpu->data());
75+
76+
const auto &image_req_ids = global_state::get_forward_context().mm_metadata.image_req_ids.value();
77+
if (input.pixel_values->size() != image_req_ids.size() || input.image_bound->size() != image_req_ids.size() || input.tgt_sizes->size() != image_req_ids.size()) {
78+
throw std::runtime_error("VideoNSAForConditionalGeneration: multimodal tensor lists must match image_req_ids");
79+
}
80+
81+
std::vector<infinicore::Tensor> pixel_tensors;
82+
std::vector<infinicore::Tensor> grid_tensors;
83+
pixel_tensors.reserve(image_req_ids.size());
84+
grid_tensors.reserve(image_req_ids.size());
85+
for (size_t media_idx = 0; media_idx < image_req_ids.size(); ++media_idx) {
86+
pixel_tensors.push_back(input.pixel_values.value().at(media_idx));
87+
grid_tensors.push_back(input.tgt_sizes.value().at(media_idx));
88+
}
89+
auto batched_pixels = pixel_tensors.size() == 1 ? pixel_tensors.front() : infinicore::op::cat(pixel_tensors, 0);
90+
auto batched_grids = grid_tensors.size() == 1 ? grid_tensors.front() : infinicore::op::cat(grid_tensors, 0);
91+
auto batched_vision_hidden = visual_->forward(batched_pixels, batched_grids);
92+
93+
size_t vision_offset = 0;
94+
for (size_t media_idx = 0; media_idx < image_req_ids.size(); ++media_idx) {
95+
const size_t req_id = image_req_ids[media_idx];
96+
auto bounds_cpu = input.image_bound.value().at(media_idx)->to(infinicore::Device::cpu())->squeeze(0);
97+
auto bound_count = bounds_cpu->size(0);
98+
auto bounds = reinterpret_cast<const int64_t *>(bounds_cpu->data());
99+
size_t vision_len = 0;
100+
for (size_t i = 0; i < bound_count; ++i) {
101+
auto start = bounds[i * 2];
102+
auto end = bounds[i * 2 + 1];
103+
if (end > start) {
104+
vision_len += static_cast<size_t>(end - start);
105+
}
106+
}
107+
108+
auto vision_hidden = batched_vision_hidden->narrow({{0, vision_offset, vision_len}});
109+
auto req_embeds = inputs_embeds->narrow({{1, size_t(offsets[req_id]), size_t(offsets[req_id + 1] - offsets[req_id])}});
110+
replace_embeddings(req_embeds, vision_hidden, input.image_bound.value().at(media_idx));
111+
vision_offset += vision_len;
112+
}
113+
114+
auto hidden_states = model_->forward_embeds(inputs_embeds, input.position_ids.value());
115+
auto logits = lm_head_->forward(hidden_states);
116+
return {logits};
117+
}
118+
119+
auto hidden_states = model_->forward(input);
120+
auto logits = lm_head_->forward(hidden_states);
121+
return {logits};
122+
}
123+
124+
void VideoNSAForConditionalGeneration::reset_cache(const cache::CacheConfig *cache_config) {
125+
if (nullptr == cache_config) {
126+
InfinilmModel::reset_cache(nullptr);
127+
return;
128+
}
129+
cache_config_ = cache_config->unique_copy();
130+
131+
auto text_config = text_config_from(model_config_);
132+
auto &kv_cache_vec = infinilm::global_state::get_forward_context().kv_cache_vec;
133+
kv_cache_vec.clear();
134+
const backends::AttentionBackend attention_backend = infinilm::global_state::get_infinilm_config().attention_backend;
135+
kv_cache_vec = std::move(default_allocate_kv_cache_tensors(cache_config, text_config, attention_backend));
136+
}
137+
138+
std::shared_ptr<infinilm::config::ModelConfig> create_videonsa_model_config(std::shared_ptr<infinilm::config::ModelConfig> model_config) {
139+
const std::string &model_type = model_config->get<std::string>("model_type");
140+
if ("videonsa" != model_type) {
141+
throw std::runtime_error("infinilm::models::videonsa::create_videonsa_model_config: model_type is not videonsa");
142+
}
143+
144+
nlohmann::json &config_json = model_config->get_config_json();
145+
if (config_json.contains("text_config")) {
146+
nlohmann::json &text_config_json = config_json["text_config"];
147+
if (!text_config_json.contains("head_dim")) {
148+
text_config_json["head_dim"] = text_config_json["hidden_size"].get<size_t>() / text_config_json["num_attention_heads"].get<size_t>();
149+
}
150+
if (!text_config_json.contains("attention_bias")) {
151+
text_config_json["attention_bias"] = true;
152+
}
153+
if (!config_json.contains("torch_dtype") && text_config_json.contains("torch_dtype")) {
154+
config_json["torch_dtype"] = text_config_json["torch_dtype"];
155+
}
156+
} else {
157+
if (!config_json.contains("head_dim")) {
158+
config_json["head_dim"] = model_config->get<size_t>("hidden_size") / model_config->get<size_t>("num_attention_heads");
159+
}
160+
if (!config_json.contains("attention_bias")) {
161+
config_json["attention_bias"] = true;
162+
}
163+
}
164+
return model_config;
165+
}
166+
167+
} // namespace infinilm::models::videonsa
168+
169+
namespace {
170+
INFINILM_REGISTER_CAUSAL_LM_MODEL(
171+
videonsa,
172+
infinilm::models::videonsa::VideoNSAForConditionalGeneration,
173+
infinilm::models::videonsa::create_videonsa_model_config);
174+
} // namespace
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
3+
#include "../../layers/common_modules.hpp"
4+
#include "videonsa_attention.hpp"
5+
#include "videonsa_vision.hpp"
6+
7+
namespace infinilm::models::videonsa {
8+
9+
using VideoNSAMLP = infinilm::layers::MLP;
10+
using VideoNSADecoderLayer = infinilm::layers::causal_lm_templates::TextDecoderLayer<VideoNSAAttention, VideoNSAMLP>;
11+
using VideoNSATextModel = infinilm::layers::causal_lm_templates::TextModel<VideoNSADecoderLayer>;
12+
13+
class VideoNSAForConditionalGeneration : public InfinilmModel {
14+
public:
15+
VideoNSAForConditionalGeneration(std::shared_ptr<infinilm::config::ModelConfig> model_config,
16+
const infinicore::Device &device);
17+
18+
Output forward(const Input &input) const override;
19+
void reset_cache(const cache::CacheConfig *cache_config) override;
20+
21+
protected:
22+
void replace_embeddings(infinicore::Tensor inputs_embeds,
23+
const infinicore::Tensor &vision_hidden,
24+
const infinicore::Tensor &image_bound) const;
25+
26+
INFINICORE_NN_MODULE(VideoNSATextModel, model);
27+
INFINICORE_NN_MODULE(VideoNSAVisionModel, visual);
28+
INFINICORE_NN_MODULE(infinilm::layers::linear::ReplicatedLinear, lm_head);
29+
};
30+
31+
std::shared_ptr<infinilm::config::ModelConfig> create_videonsa_model_config(std::shared_ptr<infinilm::config::ModelConfig> model_config);
32+
33+
} // namespace infinilm::models::videonsa

0 commit comments

Comments
 (0)