|
| 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 |
0 commit comments