@@ -93,13 +93,15 @@ class ModelBase:
93
93
# Mistral format specifics
94
94
is_mistral_format : bool = False
95
95
disable_mistral_community_chat_template : bool = False
96
+ sentence_transformers_dense_modules : bool = False
96
97
97
98
def __init__ (self , dir_model : Path , ftype : gguf .LlamaFileType , fname_out : Path , * , is_big_endian : bool = False ,
98
99
use_temp_file : bool = False , eager : bool = False ,
99
100
metadata_override : Path | None = None , model_name : str | None = None ,
100
101
split_max_tensors : int = 0 , split_max_size : int = 0 , dry_run : bool = False ,
101
102
small_first_shard : bool = False , hparams : dict [str , Any ] | None = None , remote_hf_model_id : str | None = None ,
102
- disable_mistral_community_chat_template : bool = False ):
103
+ disable_mistral_community_chat_template : bool = False ,
104
+ sentence_transformers_dense_modules : bool = False ):
103
105
if type (self ) is ModelBase or \
104
106
type (self ) is TextModel or \
105
107
type (self ) is MmprojModel :
@@ -114,6 +116,7 @@ def __init__(self, dir_model: Path, ftype: gguf.LlamaFileType, fname_out: Path,
114
116
self .lazy = not eager or (remote_hf_model_id is not None )
115
117
self .dry_run = dry_run
116
118
self .remote_hf_model_id = remote_hf_model_id
119
+ self .sentence_transformers_dense_modules = sentence_transformers_dense_modules
117
120
if remote_hf_model_id is not None :
118
121
self .is_safetensors = True
119
122
@@ -5269,6 +5272,53 @@ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iter
5269
5272
@ModelBase .register ("Gemma3TextModel" )
5270
5273
class EmbeddingGemma (Gemma3Model ):
5271
5274
model_arch = gguf .MODEL_ARCH .GEMMA_EMBEDDING
5275
+ module_paths = []
5276
+ dense_features_dims = {}
5277
+
5278
+ def __init__ (self , * args , ** kwargs ):
5279
+ super ().__init__ (* args , ** kwargs )
5280
+ if self .sentence_transformers_dense_modules :
5281
+ # read modules.json to determine if model has Dense layers
5282
+ modules_file = self .dir_model / "modules.json"
5283
+ if modules_file .is_file ():
5284
+ with open (modules_file , encoding = "utf-8" ) as modules_json_file :
5285
+ mods = json .load (modules_json_file )
5286
+ for mod in mods :
5287
+ if mod ["type" ] == "sentence_transformers.models.Dense" :
5288
+ mod_path = mod ["path" ]
5289
+ # check if model.safetensors file for Dense layer exists
5290
+ model_tensors_file = self .dir_model / mod_path / "model.safetensors"
5291
+ if model_tensors_file .is_file ():
5292
+ self .module_paths .append (mod_path )
5293
+ # read config.json of the Dense layer to get in/out features
5294
+ mod_conf_file = self .dir_model / mod_path / "config.json"
5295
+ if mod_conf_file .is_file ():
5296
+ with open (mod_conf_file , encoding = "utf-8" ) as mod_conf_json_file :
5297
+ mod_conf = json .load (mod_conf_json_file )
5298
+ # hparams dense_2_feat_out and dense_3_feat_in are required when loading model's dense weights
5299
+ prefix = self ._get_dense_prefix (mod_path )
5300
+ if mod_conf ["in_features" ] is not None and mod_conf ["out_features" ] is not None :
5301
+ self .dense_features_dims [prefix ] = (mod_conf ["in_features" ], mod_conf ["out_features" ])
5302
+
5303
+ def generate_extra_tensors (self ) -> Iterable [tuple [str , Tensor ]]:
5304
+ from safetensors .torch import load_file
5305
+ module_paths = list (self .module_paths )
5306
+ for i , module_path in enumerate (module_paths ):
5307
+ tensors_file = self .dir_model / module_path / "model.safetensors"
5308
+ local_tensors = load_file (tensors_file )
5309
+ tensor_name = self ._get_dense_prefix (module_path )
5310
+ for name , local_tensor in local_tensors .items ():
5311
+ if not name .endswith (".weight" ):
5312
+ continue
5313
+ orig_name = name .replace ("linear" , tensor_name )
5314
+ name = self .map_tensor_name (orig_name )
5315
+ yield name , local_tensor .clone ()
5316
+
5317
+ @staticmethod
5318
+ def _get_dense_prefix (module_path ) -> str :
5319
+ """Get the tensor name prefix for the Dense layer from module path."""
5320
+ tensor_name = "dense_2" if module_path == "2_Dense" else "dense_3"
5321
+ return tensor_name
5272
5322
5273
5323
def set_gguf_parameters (self ):
5274
5324
super ().set_gguf_parameters ()
@@ -5285,6 +5335,10 @@ def set_gguf_parameters(self):
5285
5335
logger .info (f"Using original sliding_window from config: { orig_sliding_window } "
5286
5336
f"instead of { self .hparams ['sliding_window' ]} " )
5287
5337
self .gguf_writer .add_sliding_window (orig_sliding_window )
5338
+ if self .sentence_transformers_dense_modules :
5339
+ for dense , dims in self .dense_features_dims .items ():
5340
+ logger .info (f"Setting dense layer { dense } in/out features to { dims } " )
5341
+ self .gguf_writer .add_dense_features_dims (dense , dims [0 ], dims [1 ])
5288
5342
5289
5343
self ._try_set_pooling_type ()
5290
5344
@@ -9335,6 +9389,13 @@ def parse_args() -> argparse.Namespace:
9335
9389
)
9336
9390
)
9337
9391
9392
+ parser .add_argument (
9393
+ "--sentence-transformers-dense-modules" , action = "store_true" ,
9394
+ help = ("Whether to include sentence-transformers dense modules."
9395
+ "It can be used for sentence-transformers models, like google/embeddinggemma-300m"
9396
+ "Default these modules are not included." )
9397
+ )
9398
+
9338
9399
args = parser .parse_args ()
9339
9400
if not args .print_supported_models and args .model is None :
9340
9401
parser .error ("the following arguments are required: model" )
@@ -9397,9 +9458,13 @@ def main() -> None:
9397
9458
if args .remote :
9398
9459
hf_repo_id = args .model
9399
9460
from huggingface_hub import snapshot_download
9461
+ allowed_patterns = ["LICENSE" , "*.json" , "*.md" , "*.txt" , "tokenizer.model" ]
9462
+ if args .sentence_transformers_dense_modules :
9463
+ # include sentence-transformers dense modules safetensors files
9464
+ allowed_patterns .append ("*.safetensors" )
9400
9465
local_dir = snapshot_download (
9401
9466
repo_id = hf_repo_id ,
9402
- allow_patterns = [ "LICENSE" , "*.json" , "*.md" , "*.txt" , "tokenizer.model" ] )
9467
+ allow_patterns = allowed_patterns )
9403
9468
dir_model = Path (local_dir )
9404
9469
logger .info (f"Downloaded config and tokenizer to { local_dir } " )
9405
9470
else :
@@ -9467,7 +9532,8 @@ def main() -> None:
9467
9532
split_max_tensors = args .split_max_tensors ,
9468
9533
split_max_size = split_str_to_n_bytes (args .split_max_size ), dry_run = args .dry_run ,
9469
9534
small_first_shard = args .no_tensor_first_split ,
9470
- remote_hf_model_id = hf_repo_id , disable_mistral_community_chat_template = disable_mistral_community_chat_template
9535
+ remote_hf_model_id = hf_repo_id , disable_mistral_community_chat_template = disable_mistral_community_chat_template ,
9536
+ sentence_transformers_dense_modules = args .sentence_transformers_dense_modules
9471
9537
)
9472
9538
9473
9539
if args .vocab_only :
0 commit comments