Skip to content

Commit 156a522

Browse files
committed
feat: small helper method to reduce overlap
1 parent a8ce637 commit 156a522

1 file changed

Lines changed: 77 additions & 69 deletions

File tree

‎model2vec/model.py‎

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,60 @@ def _set_max_length_in_tokenizer(self, max_length: int | None) -> None:
238238
else:
239239
self.tokenizer.enable_truncation(max_length)
240240

241+
def _encode_dispatch(
242+
self,
243+
sentences: str | Sequence[str],
244+
*,
245+
max_length: int | None,
246+
batch_size: int,
247+
show_progress_bar: bool,
248+
use_multiprocessing: bool,
249+
multiprocessing_threshold: int,
250+
batch_fn: Any,
251+
batch_args: tuple[Any, ...] = (),
252+
) -> tuple[list[Any], bool]:
253+
"""Shared truncation, batching, and multiprocessing dispatch logic for `encode` and `encode_as_sequence`.
254+
255+
:param sentences: The sentence or sentences to encode.
256+
:param max_length: The maximum length of the sentences. Any tokens beyond this length are truncated.
257+
:param batch_size: The batch size to use.
258+
:param show_progress_bar: Whether to show the progress bar.
259+
:param use_multiprocessing: Whether to use multiprocessing.
260+
:param multiprocessing_threshold: The threshold in number of sentences for using multiprocessing.
261+
:param batch_fn: The function to apply to each batch of sentences.
262+
:param batch_args: Additional positional arguments passed to `batch_fn` after the batch.
263+
:return: A tuple of the per-batch results and whether the input was a single sentence.
264+
"""
265+
was_single = False
266+
if isinstance(sentences, str):
267+
sentences = [sentences]
268+
was_single = True
269+
if max_length is not None:
270+
m = max_length * self.median_token_length
271+
sentences = [sentence[:m] for sentence in sentences]
272+
273+
sentence_batches = list(self._batch(sentences, batch_size))
274+
total_batches = math.ceil(len(sentences) / batch_size)
275+
276+
self._set_max_length_in_tokenizer(max_length)
277+
try:
278+
if use_multiprocessing and len(sentences) > multiprocessing_threshold:
279+
# Disable parallelism for tokenizers
280+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
281+
282+
results = ProgressParallel(n_jobs=-1, use_tqdm=show_progress_bar, total=total_batches)(
283+
delayed(batch_fn)(batch, *batch_args) for batch in sentence_batches
284+
)
285+
else:
286+
results = [
287+
batch_fn(batch, *batch_args)
288+
for batch in tqdm(sentence_batches, total=total_batches, disable=not show_progress_bar)
289+
]
290+
finally:
291+
self._set_max_length_in_tokenizer(self.max_length)
292+
293+
return results, was_single
294+
241295
@overload
242296
def encode_as_sequence(
243297
self,
@@ -295,41 +349,18 @@ def encode_as_sequence(
295349
:param multiprocessing_threshold: The threshold in number of sentences for using multiprocessing.
296350
:return: The encoded sentences with an embedding per token.
297351
"""
298-
was_single = False
299-
if isinstance(sentences, str):
300-
sentences = [sentences]
301-
was_single = True
302-
if max_length is not None:
303-
m = max_length * self.median_token_length
304-
sentences = [sentence[:m] for sentence in sentences]
305-
306-
# Prepare all batches
307-
sentence_batches = list(self._batch(sentences, batch_size))
308-
total_batches = math.ceil(len(sentences) / batch_size)
309-
310-
self._set_max_length_in_tokenizer(max_length)
311-
try:
312-
# Use joblib for multiprocessing if requested, and if we have enough sentences
313-
if use_multiprocessing and len(sentences) > multiprocessing_threshold:
314-
# Disable parallelism for tokenizers
315-
os.environ["TOKENIZERS_PARALLELISM"] = "false"
316-
317-
results = ProgressParallel(n_jobs=-1, use_tqdm=show_progress_bar, total=total_batches)(
318-
delayed(self._encode_batch_as_sequence)(batch) for batch in sentence_batches
319-
)
320-
out_array: list[np.ndarray] = []
321-
for r in results:
322-
out_array.extend(r)
323-
else:
324-
out_array = []
325-
for batch in tqdm(
326-
sentence_batches,
327-
total=total_batches,
328-
disable=not show_progress_bar,
329-
):
330-
out_array.extend(self._encode_batch_as_sequence(batch))
331-
finally:
332-
self._set_max_length_in_tokenizer(self.max_length)
352+
results, was_single = self._encode_dispatch(
353+
sentences,
354+
max_length=max_length,
355+
batch_size=batch_size,
356+
show_progress_bar=show_progress_bar,
357+
use_multiprocessing=use_multiprocessing,
358+
multiprocessing_threshold=multiprocessing_threshold,
359+
batch_fn=self._encode_batch_as_sequence,
360+
)
361+
out_array: list[np.ndarray] = []
362+
for r in results:
363+
out_array.extend(r)
333364

334365
if was_single:
335366
return out_array[0]
@@ -380,45 +411,22 @@ def encode(
380411
:param **kwargs: Any additional arguments. These are ignored.
381412
:return: The encoded sentences. If a single sentence was passed, a vector is returned.
382413
"""
383-
was_single = False
384-
if isinstance(sentences, str):
385-
sentences = [sentences]
386-
was_single = True
387414
if isinstance(max_length, _UnsetType):
388415
max_length = self.max_length
389416
if normalize is None:
390417
normalize = self.normalize
391-
if max_length is not None:
392-
m = max_length * self.median_token_length
393-
sentences = [sentence[:m] for sentence in sentences]
394-
395-
# Prepare all batches
396-
sentence_batches = list(self._batch(sentences, batch_size))
397-
total_batches = math.ceil(len(sentences) / batch_size)
398418

399-
self._set_max_length_in_tokenizer(max_length)
400-
try:
401-
# Use joblib for multiprocessing if requested, and if we have enough sentences
402-
if use_multiprocessing and len(sentences) > multiprocessing_threshold:
403-
# Disable parallelism for tokenizers
404-
os.environ["TOKENIZERS_PARALLELISM"] = "false"
405-
406-
results = ProgressParallel(n_jobs=-1, use_tqdm=show_progress_bar, total=total_batches)(
407-
delayed(self._encode_batch)(batch, normalize) for batch in sentence_batches
408-
)
409-
out_array = np.concatenate(results, axis=0)
410-
else:
411-
# Don't use multiprocessing
412-
out_arrays: list[np.ndarray] = []
413-
for batch in tqdm(
414-
sentence_batches,
415-
total=total_batches,
416-
disable=not show_progress_bar,
417-
):
418-
out_arrays.append(self._encode_batch(batch, normalize))
419-
out_array = np.concatenate(out_arrays, axis=0)
420-
finally:
421-
self._set_max_length_in_tokenizer(self.max_length)
419+
results, was_single = self._encode_dispatch(
420+
sentences,
421+
max_length=max_length,
422+
batch_size=batch_size,
423+
show_progress_bar=show_progress_bar,
424+
use_multiprocessing=use_multiprocessing,
425+
multiprocessing_threshold=multiprocessing_threshold,
426+
batch_fn=self._encode_batch,
427+
batch_args=(normalize,),
428+
)
429+
out_array = np.concatenate(results, axis=0)
422430

423431
if was_single:
424432
return out_array[0]

0 commit comments

Comments
 (0)