@@ -46,9 +46,9 @@ def __init__(
4646 weights : torch .Tensor | None = None ,
4747 freeze : bool = False ,
4848 normalize : bool = True ,
49+ freeze_weights : bool = False ,
4950 ) -> None :
50- """
51- Initialize a trainable StaticModel from a StaticModel.
51+ """Initialize a trainable StaticModel from a StaticModel.
5252
5353 :param vectors: The embeddings of the staticmodel.
5454 :param tokenizer: The tokenizer.
@@ -60,6 +60,7 @@ def __init__(
6060 :param weights: The weights of the model. If None, the weights are initialized to zeros.
6161 :param freeze: Whether to freeze the embeddings. This should be set to False in most cases.
6262 :param normalize: Whether to normalize the embeddings.
63+ :param freeze_weights: Whether to freeze the learned token weights.
6364 """
6465 super ().__init__ ()
6566 self .pad_id = pad_id
@@ -68,6 +69,7 @@ def __init__(
6869 self .hidden_dim = hidden_dim
6970 self .n_layers = n_layers
7071 self .normalize = normalize
72+ self .freeze_weights = freeze_weights
7173
7274 self .vectors = vectors
7375 if self .vectors .dtype != torch .float32 :
@@ -93,26 +95,31 @@ def construct_weights(self) -> nn.Parameter:
9395 """Construct the weights for the model."""
9496 if self ._weights is not None :
9597 w = logit (self ._weights )
96- return nn . Parameter ( w . float (), requires_grad = True )
97- weights = torch .zeros (len (self .token_mapping ))
98- weights [self .pad_id ] = - 10_000
99- return nn .Parameter (weights , requires_grad = not self .freeze )
98+ else :
99+ w = torch .zeros (len (self .token_mapping )). float ( )
100+ w [self .pad_id ] = - 10_000
101+ return nn .Parameter (w , requires_grad = not self .freeze_weights )
100102
101103 def construct_head (self ) -> nn .Sequential :
104+ """Constructs a simple classifier head."""
105+ return self .construct_mlp (self .n_layers , self .embed_dim , self .hidden_dim , self .out_dim )
106+
107+ @staticmethod
108+ def construct_mlp (n_layers : int , embed_dim : int , hidden_dim : int , out_dim : int ) -> nn .Sequential :
102109 """Constructs a simple classifier head."""
103110 modules : list [nn .Module ] = []
104- if self . n_layers == 0 :
105- modules .append (nn .Linear (self . embed_dim , self . out_dim ))
111+ if n_layers == 0 :
112+ modules .append (nn .Linear (embed_dim , out_dim ))
106113 else :
107114 # If we have a hidden layer, we should first project to hidden_dim
108115 modules = [
109- nn .Linear (self . embed_dim , self . hidden_dim ),
116+ nn .Linear (embed_dim , hidden_dim ),
110117 nn .ReLU (),
111118 ]
112- for _ in range (self . n_layers - 1 ):
113- modules .extend ([nn .Linear (self . hidden_dim , self . hidden_dim ), nn .ReLU ()])
119+ for _ in range (n_layers - 1 ):
120+ modules .extend ([nn .Linear (hidden_dim , hidden_dim ), nn .ReLU ()])
114121 # We always have a layer mapping from hidden to out.
115- modules .append (nn .Linear (self . hidden_dim , self . out_dim ))
122+ modules .append (nn .Linear (hidden_dim , out_dim ))
116123
117124 linear_modules = [module for module in modules if isinstance (module , nn .Linear )]
118125 if linear_modules :
@@ -137,7 +144,11 @@ def _initialize(self) -> None:
137144
138145 @classmethod
139146 def from_pretrained (
140- cls : type [ModelType ], path : str = "minishlab/potion-base-32m" , * , token : str | None = None , ** kwargs : Any
147+ cls : type [ModelType ],
148+ path : str = "minishlab/potion-base-32m" ,
149+ * ,
150+ token : str | None = None ,
151+ ** kwargs : Any ,
141152 ) -> ModelType :
142153 """Load the model from a pretrained model2vec model."""
143154 if model_name := kwargs .pop ("model_name" , None ):
@@ -148,7 +159,11 @@ def from_pretrained(
148159
149160 @classmethod
150161 def from_static_model (
151- cls : type [ModelType ], * , model : StaticModel , pad_token : str | None = None , ** kwargs : Any
162+ cls : type [ModelType ],
163+ * ,
164+ model : StaticModel ,
165+ pad_token : str | None = None ,
166+ ** kwargs : Any ,
152167 ) -> ModelType :
153168 """Load the model from a static model."""
154169 model .embedding = np .nan_to_num (model .embedding )
@@ -172,23 +187,23 @@ def from_static_model(
172187 )
173188
174189 def _encode (self , input_ids : torch .Tensor ) -> torch .Tensor :
175- """
176- A forward pass and mean pooling.
190+ """A forward pass and mean pooling.
177191
178192 This function is analogous to `StaticModel.encode`, but reimplemented to allow gradients
179193 to pass through.
180194
181195 :param input_ids: A 2D tensor of input ids. All input ids are have to be within bounds.
182196 :return: The mean over the input ids, weighted by token weights.
183197 """
184- w = self .w [input_ids ]
185- w = torch .sigmoid (w )
186198 zeros = (input_ids != self .pad_id ).float ()
187- w = w * zeros
188199 # Add a small epsilon to avoid division by zero
189200 length = zeros .sum (1 ) + 1e-16
190201 input_ids_embeddings = self .token_mapping [input_ids ]
191202 embedded = self .embeddings (input_ids_embeddings )
203+
204+ w = self .w [input_ids ]
205+ w = torch .sigmoid (w )
206+ w = w * zeros
192207 # Weigh each token
193208 embedded = torch .bmm (w [:, None , :], embedded ).squeeze (1 )
194209 # Mean pooling by dividing by the length
@@ -218,8 +233,7 @@ def forward(self, input_ids: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
218233 return self .head (encoded ), encoded
219234
220235 def tokenize (self , texts : list [str ], max_length : int | None = 512 ) -> torch .Tensor :
221- """
222- Tokenize a bunch of strings into a single padded 2D tensor.
236+ """Tokenize a bunch of strings into a single padded 2D tensor.
223237
224238 Note that this is not used during training.
225239
@@ -238,13 +252,22 @@ def device(self) -> torch.device:
238252
239253 def to_static_model (self ) -> StaticModel :
240254 """Convert the model to a static model."""
241- emb = self .embeddings .weight .detach ().cpu ().numpy ()
242- w = torch .sigmoid (self .w ).detach ().cpu ().numpy ()
255+ with torch .no_grad ():
256+ emb = self .embeddings .weight
257+ emb = emb .detach ().cpu ().numpy ()
258+ if self .w is not None :
259+ w = torch .sigmoid (self .w ).detach ().cpu ().numpy ()
260+ else :
261+ w = np .ones (len (emb ))
243262 # If the weights and emb are the same length, the model was not quantized before training.
244263 if len (w ) == len (emb ):
245264 emb = emb * w [:, None ]
246265 return StaticModel (
247- vectors = emb , weights = None , tokenizer = self .tokenizer , normalize = self .normalize , token_mapping = None
266+ vectors = emb ,
267+ weights = None ,
268+ tokenizer = self .tokenizer ,
269+ normalize = self .normalize ,
270+ token_mapping = None ,
248271 )
249272 return StaticModel (
250273 vectors = emb ,
@@ -268,7 +291,12 @@ def _determine_batch_size(self, batch_size: int | None, train_length: int) -> in
268291 return batch_size
269292
270293 def _check_val_split (
271- self , X : list [str ], y : list , X_val : list [str ] | None , y_val : list | None , test_size : float
294+ self ,
295+ X : list [str ],
296+ y : list ,
297+ X_val : list [str ] | None ,
298+ y_val : list | None ,
299+ test_size : float ,
272300 ) -> tuple [list [str ], list [str ], Sequence , Sequence ]:
273301 if (X_val is not None ) != (y_val is not None ):
274302 raise ValueError ("Both X_val and y_val must be provided together, or neither." )
@@ -368,8 +396,7 @@ def _determine_val_check_interval(
368396 return val_check_interval , check_val_every_epoch
369397
370398 def _prepare_dataset (self , X : list [str ], y : torch .Tensor , max_length : int = 512 ) -> TextDataset :
371- """
372- Prepare a dataset.
399+ """Prepare a dataset.
373400
374401 :param X: The texts.
375402 :param y: The labels.
0 commit comments