wtpsplit 2.2.0__tar.gz → 2.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. wtpsplit-2.2.2/PKG-INFO +620 -0
  2. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/README.md +38 -0
  3. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/pyproject.toml +5 -1
  4. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/setup.py +5 -2
  5. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/__init__.py +50 -4
  6. wtpsplit-2.2.2/wtpsplit/aitune_integration.py +279 -0
  7. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/extract.py +133 -14
  8. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/models.py +16 -4
  9. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/adaptertrainer.py +33 -41
  10. wtpsplit-2.2.2/wtpsplit/train/hf_compat.py +31 -0
  11. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/train.py +2 -1
  12. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/trainer.py +18 -23
  13. wtpsplit-2.2.2/wtpsplit/train/transformers_trainer_imports.py +51 -0
  14. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/utils.py +3 -0
  15. wtpsplit-2.2.2/wtpsplit.egg-info/PKG-INFO +620 -0
  16. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit.egg-info/SOURCES.txt +3 -1
  17. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit.egg-info/requires.txt +5 -1
  18. wtpsplit-2.2.0/PKG-INFO +0 -33
  19. wtpsplit-2.2.0/wtpsplit/data/priors_generated.py +0 -250
  20. wtpsplit-2.2.0/wtpsplit.egg-info/PKG-INFO +0 -33
  21. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/LICENSE +0 -0
  22. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/setup.cfg +0 -0
  23. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/configs.py +0 -0
  24. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/__init__.py +0 -0
  25. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/language_info.csv +0 -0
  26. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/punctuation.json +0 -0
  27. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/punctuation.txt +0 -0
  28. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/punctuation_xlmr.txt +0 -0
  29. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/punctuation_xlmr_unk.txt +0 -0
  30. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data/sentence_stats.json +0 -0
  31. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data_acquisition/__init__.py +0 -0
  32. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data_acquisition/extract_all_data.py +0 -0
  33. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data_acquisition/extract_eval_data.py +0 -0
  34. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data_acquisition/extract_sentences.py +0 -0
  35. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/data_acquisition/extract_shared_task_data.py +0 -0
  36. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/__init__.py +0 -0
  37. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/adapt.py +0 -0
  38. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/evaluate_sepp_nlg_subtask1.py +0 -0
  39. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/extrinsic.py +0 -0
  40. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/intrinsic_baselines.py +0 -0
  41. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/intrinsic_baselines_multilingual.py +0 -0
  42. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/intrinsic_pairwise.py +0 -0
  43. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/intrinsic_ted.py +0 -0
  44. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/legal_baselines.py +0 -0
  45. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/llm_sentence.py +0 -0
  46. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/punct_annotation.py +0 -0
  47. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/evaluation/punct_annotation_wtp.py +0 -0
  48. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/extract_batched.py +0 -0
  49. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/__init__.py +0 -0
  50. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/evaluate.py +0 -0
  51. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/train_SM.py +0 -0
  52. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/train/train_lora.py +0 -0
  53. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/__init__.py +0 -0
  54. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/clean_tweets.py +0 -0
  55. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/constraints.py +0 -0
  56. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/create_dummy_data.py +0 -0
  57. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/download_spacy.py +0 -0
  58. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/priors.py +0 -0
  59. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit/utils/remove_unks.py +0 -0
  60. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit.egg-info/dependency_links.txt +0 -0
  61. {wtpsplit-2.2.0 → wtpsplit-2.2.2}/wtpsplit.egg-info/top_level.txt +0 -0
@@ -0,0 +1,620 @@
1
+ Metadata-Version: 2.4
2
+ Name: wtpsplit
3
+ Version: 2.2.2
4
+ Summary: Universal Robust, Efficient and Adaptable Sentence Segmentation
5
+ Home-page: https://github.com/segment-any-text/wtpsplit
6
+ Author: Markus Frohmann, Igor Sterner, Benjamin Minixhofer
7
+ Author-email: markus.frohmann@gmail.com
8
+ License: MIT
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: transformers>=4.22.2
13
+ Requires-Dist: huggingface-hub
14
+ Requires-Dist: numpy>=1.0
15
+ Requires-Dist: scikit-learn>=1
16
+ Requires-Dist: tqdm
17
+ Requires-Dist: skops
18
+ Requires-Dist: pandas>=1
19
+ Requires-Dist: mosestokenizer
20
+ Provides-Extra: adapters
21
+ Requires-Dist: adapters>=1.0.1; extra == "adapters"
22
+ Provides-Extra: onnx-gpu
23
+ Requires-Dist: onnxruntime-gpu>=1.13.1; extra == "onnx-gpu"
24
+ Provides-Extra: onnx-cpu
25
+ Requires-Dist: onnxruntime>=1.13.1; extra == "onnx-cpu"
26
+ Provides-Extra: aitune
27
+ Requires-Dist: aitune>=0.3.0; extra == "aitune"
28
+ Requires-Dist: requests; extra == "aitune"
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: license-file
36
+ Dynamic: provides-extra
37
+ Dynamic: requires-dist
38
+ Dynamic: requires-python
39
+ Dynamic: summary
40
+
41
+ <h1 align="center">wtpsplit🪓</h1>
42
+ <h3 align="center">Segment any Text - Robustly, Efficiently, Adaptably⚡</h3>
43
+
44
+ This repository allows you to segment text into sentences or other semantic units. It implements the models from:
45
+
46
+ - **SaT** &mdash; [Segment Any Text: A Universal Approach for Robust, Efficient and Adaptable Sentence Segmentation](https://arxiv.org/abs/2406.16678) by Markus Frohmann, Igor Sterner, Benjamin Minixhofer, Ivan Vulić and Markus Schedl (**state-of-the-art, encouraged**).
47
+ - **WtP** &mdash; [Where’s the Point? Self-Supervised Multilingual Punctuation-Agnostic Sentence Segmentation](https://aclanthology.org/2023.acl-long.398/) by Benjamin Minixhofer, Jonas Pfeiffer and Ivan Vulić (*previous version, maintained for reproducibility*).
48
+
49
+ The namesake WtP is maintained for consistency. Our new followup SaT provides robust, efficient and adaptable sentence segmentation across 85 languages at higher performance and less compute cost. Check out the **state-of-the-art** results in 8 distinct corpora and 85 languages demonstrated in our [Segment any Text paper](https://arxiv.org/abs/2406.16678).
50
+
51
+ ![System Figure](./configs/system-fig.png)
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ pip install wtpsplit
57
+ ```
58
+ Or one of the following for ONNX support:
59
+ ```bash
60
+ pip install wtpsplit[onnx-gpu]
61
+ pip install wtpsplit[onnx-cpu]
62
+ ```
63
+
64
+ ## Usage
65
+
66
+ ```python
67
+ from wtpsplit import SaT
68
+
69
+ sat = SaT("sat-3l")
70
+ # optionally run on GPU for better performance
71
+ # also supports TPUs via e.g. sat.to("xla:0"), in that case pass `pad_last_batch=True` to sat.split
72
+ sat.half().to("cuda")
73
+
74
+ sat.split("This is a test This is another test.")
75
+ # returns ["This is a test ", "This is another test."]
76
+
77
+ # do this instead of calling sat.split on every text individually for much better performance
78
+ sat.split(["This is a test This is another test.", "And some more texts..."])
79
+ # returns an iterator yielding lists of sentences for every text
80
+
81
+ # use our '-sm' models for general sentence segmentation tasks
82
+ sat_sm = SaT("sat-3l-sm")
83
+ sat_sm.half().to("cuda") # optional, see above
84
+ sat_sm.split("this is a test this is another test")
85
+ # returns ["this is a test ", "this is another test"]
86
+
87
+ # use trained lora modules for strong adaptation to language & domain/style
88
+ sat_adapted = SaT("sat-3l", style_or_domain="ud", language="en")
89
+ sat_adapted.half().to("cuda") # optional, see above
90
+ sat_adapted.split("This is a test This is another test.")
91
+ # returns ['This is a test ', 'This is another test']
92
+ ```
93
+
94
+ ## Faster PyTorch inference (`torch.compile` / TorchInductor)
95
+
96
+ For repeated inference without exporting to ONNX, compile the PyTorch model with [TorchInductor](https://pytorch.org/docs/stable/torch.compiler.html) via `optimize()` (PyTorch 2.0+). This is **optional** - the first `split` after `optimize()` can be slow while graphs are built. Call `optimize()` **after** `to()` / `half()` so the compiled graph matches *your* device and dtype.
97
+
98
+ ```python
99
+ from wtpsplit import SaT
100
+
101
+ sat = SaT("sat-3l-sm")
102
+ sat.half().to("cuda")
103
+ sat.optimize() # backend="inductor" (default); dynamic shapes on
104
+
105
+ sat.split("This is a test This is another test.")
106
+ ```
107
+
108
+ - **SaT** and **WtP** PyTorch checkpoints only — not available with `ort_providers` / ONNX.
109
+ - `backend` synonyms: `"inductor"`, `"torchinductor"`.
110
+ - Chunk length and the last batch size change between calls, so `dynamic=True` is the default. `mode="reduce-overhead"` (CUDA graphs) is faster only when every forward uses the same shapes; otherwise use the default or `mode="max-autotune-no-cudagraphs"`.
111
+ - Optional on NVIDIA Ampere+: `torch.set_float32_matmul_precision("high")` before inference (faster fp32 matmuls).
112
+
113
+ ### NVIDIA AITune (auto backend selection, CUDA)
114
+
115
+ [NVIDIA AITune](https://github.com/ai-dynamo/aitune) benchmarks backends (TensorRT, Torch-TensorRT, Torch Inductor, …) on your GPU and picks a fast path. Requires Linux, CUDA, and a separate install:
116
+
117
+ ```bash
118
+ pip install wtpsplit[aitune] --extra-index-url https://pypi.nvidia.com
119
+ ```
120
+
121
+ ```python
122
+ sat = SaT("sat-3l-sm")
123
+ sat.half().to("cuda")
124
+ # first_wins tries TensorRT → Torch-TensorRT → Inductor when available
125
+ sat.optimize(backend="aitune")
126
+ # or only Inductor via AITune (faster tuning, no TensorRT dependency):
127
+ # sat.optimize(backend="aitune", aitune_strategy="inductor_only", aitune_max_batches=4)
128
+
129
+ sat.split("This is a test This is another test.")
130
+ ```
131
+
132
+ ## ONNX Support
133
+
134
+ 🚀 You can now enable even faster ONNX inference for `sat` and `sat-sm` models! 🚀
135
+
136
+ ```python
137
+ sat = SaT("sat-3l-sm", ort_providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
138
+ ```
139
+
140
+ ```python
141
+ >>> from wtpsplit import SaT
142
+ >>> texts = ["This is a sentence. This is another sentence."] * 1000
143
+
144
+ # PyTorch GPU
145
+ >>> model_pytorch = SaT("sat-3l-sm")
146
+ >>> model_pytorch.half().to("cuda");
147
+ >>> %timeit list(model_pytorch.split(texts))
148
+ # 144 ms ± 252 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
149
+ # quite fast already, but...
150
+
151
+ # onnxruntime GPU
152
+ >>> model_ort = SaT("sat-3l-sm", ort_providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
153
+ >>> %timeit list(model_ort.split(texts))
154
+ # 94.9 ms ± 165 μs per loop (mean ± std. dev. of 7 runs, 10 loops each
155
+ # ...this should be ~50% faster! (tested on RTX 3090)
156
+ ```
157
+
158
+ If you wish to use LoRA in combination with an ONNX model:
159
+
160
+ - Run `scripts/export_to_onnx_sat.py` with `use_lora: True` and an appropriate `output_dir: <OUTPUT_DIR>`.
161
+ - If you have a local LoRA module, use `lora_path`.
162
+ - If you wish to load a LoRA module from the HuggingFace hub, use `style_or_domain` and `language`.
163
+ - Load the ONNX model with merged LoRA weights:
164
+ `sat = SaT(<OUTPUT_DIR>, onnx_providers=["CUDAExecutionProvider", "CPUExecutionProvider"])`
165
+
166
+ ## Available Models
167
+
168
+ If you need a general sentence segmentation model, use `-sm` models (e.g., `sat-3l-sm`)
169
+ For speed-sensitive applications, we recommend 3-layer models (`sat-3l` and `sat-3l-sm`). They provide a great tradeoff between speed and performance.
170
+ The best models are our 12-layer models: `sat-12l` and `sat-12l-sm`.
171
+
172
+ | Model | English Score | Multilingual Score |
173
+ | :--------------------------------------------------------------------------- | ------------: | -----------------: |
174
+ | [sat-1l](https://huggingface.co/segment-any-text/sat-1l) | 88.5 | 84.3 |
175
+ | [sat-1l-sm](https://huggingface.co/segment-any-text/sat-1l-sm) | 88.2 | 87.9 |
176
+ | [sat-3l](https://huggingface.co/segment-any-text/sat-3l) | 93.7 | 89.2 |
177
+ | [sat-3l-lora](https://huggingface.co/segment-any-text/sat-3l/tree/main/loras) | 96.7 | 94.8 |
178
+ | [sat-3l-sm](https://huggingface.co/segment-any-text/sat-3l-sm) | 96.5 | 93.5 |
179
+ | [sat-6l](https://huggingface.co/segment-any-text/sat-6l) | 94.1 | 89.7 |
180
+ | [sat-6l-sm](https://huggingface.co/segment-any-text/sat-6l-sm) | 96.9 | 95.1 |
181
+ | [sat-9l](https://huggingface.co/segment-any-text/sat-9l) | 94.3 | 90.3 |
182
+ | [sat-12l](https://huggingface.co/segment-any-text/sat-12l) | 94.0 | 90.4 |
183
+ | [sat-12l-lora](https://huggingface.co/segment-any-text/sat-12l/tree/main/loras) | 97.3 | 95.9 |
184
+ | [sat-12l-sm](https://huggingface.co/segment-any-text/sat-12l-sm) | 97.4 | 96.0 |
185
+
186
+ The scores are macro-average F1 score across all available datasets for "English", and macro-average F1 score across all datasets and languages for "Multilingual". "adapted" means adapation via LoRA; check out the [paper](https://arxiv.org/abs/2406.16678) for details.
187
+
188
+ For comparison, here the English scores of some other tools:
189
+
190
+ | Model | English Score |
191
+ | :------------------------------------------------------- | ------------: |
192
+ | PySBD | 69.6 |
193
+ | SpaCy (sentencizer; monolingual) | 92.9 |
194
+ | SpaCy (sentencizer; multilingual) | 91.5 |
195
+ | Ersatz | 91.4 |
196
+ | Punkt (`nltk.sent_tokenize`) | 92.2 |
197
+ | [WtP (3l)](https://huggingface.co/benjamin/wtp-canine-s-3l) | 93.9 |
198
+
199
+ Note that this library also supports previous [`WtP`](https://arxiv.org/abs/2305.18893) models.
200
+ You can use them in essentially the same way as `SaT`models:
201
+
202
+ ```python
203
+ from wtpsplit import WtP
204
+
205
+ wtp = WtP("wtp-bert-mini")
206
+ # similar functionality as for SaT models
207
+ wtp.split("This is a test This is another test.")
208
+ ```
209
+
210
+ For more details on WtP and reproduction details, see the [WtP doc](./README_WTP.md).
211
+
212
+ ## Paragraph Segmentation
213
+
214
+ Since SaT are trained to predict newline probablity, they can segment text into paragraphs in addition to sentences.
215
+
216
+ ```python
217
+ # returns a list of paragraphs, each containing a list of sentences
218
+ # adjust the paragraph threshold via the `paragraph_threshold` argument.
219
+ sat.split(text, do_paragraph_segmentation=True)
220
+ ```
221
+
222
+ ## (NEW! v2.2+) Length-Constrained Segmentation
223
+
224
+ Control segment lengths with `min_length` and `max_length` parameters. This is useful when you need segments within specific size limits (e.g., for embedding models, storage, or downstream processing).
225
+
226
+ ### Basic Usage
227
+
228
+ ```python
229
+ from wtpsplit import SaT
230
+
231
+ sat = SaT("sat-3l-sm")
232
+
233
+ text = (
234
+ "In the beginning God created the heaven and the earth. "
235
+ "And the earth was without form, and void; and darkness was upon the face of the deep. "
236
+ "And the Spirit of God moved upon the face of the waters. "
237
+ "And God said, Let there be light: and there was light. "
238
+ "And God saw the light, that it was good: and God divided the light from the darkness. "
239
+ "And God called the light Day, and the darkness he called Night. "
240
+ "And the evening and the morning were the first day."
241
+ )
242
+
243
+ # Split with a maximum segment length of 120 characters
244
+ segments = sat.split(text, max_length=120)
245
+ for i, s in enumerate(segments):
246
+ print(f"[{len(s):3d} chars] {s}")
247
+ # [ 55 chars] In the beginning God created the heaven and the earth.
248
+ # [ 86 chars] And the earth was without form, and void; and darkness was upon the face of the deep.
249
+ # [112 chars] And the Spirit of God moved upon the face of the waters. And God said, Let there be light: and there was light.
250
+ # [ 86 chars] And God saw the light, that it was good: and God divided the light from the darkness.
251
+ # [115 chars] And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day.
252
+
253
+ assert "".join(segments) == text # text is perfectly preserved
254
+
255
+ # Enforce both min and max length
256
+ sat.split(text, min_length=80, max_length=200)
257
+
258
+ # Use the greedy algorithm for minimally faster (but less optimal) results
259
+ sat.split(text, max_length=120, algorithm="greedy")
260
+ ```
261
+
262
+ ### Priors for Length Preference
263
+
264
+ Use priors to influence segment length distribution. Available priors:
265
+
266
+ | Prior | Best For |
267
+ |-------|----------|
268
+ | `"uniform"` (default) | Just enforce max_length, let model decide |
269
+ | `"gaussian"` | Prefer segments around a target length (intuitive) |
270
+ | `"lognormal"` | Right-skewed preference (more tolerant of longer segments) |
271
+ | `"clipped_polynomial"` | Must be very close to target length |
272
+
273
+ ```python
274
+ # Gaussian prior (recommended): prefer segments around target_length
275
+ sat.split(text, max_length=100, prior_type="gaussian",
276
+ prior_kwargs={"target_length": 50, "spread": 10})
277
+
278
+ # Log-normal prior: right-skewed (more tolerant of longer segments)
279
+ sat.split(text, max_length=100, prior_type="lognormal",
280
+ prior_kwargs={"target_length": 70, "spread": 25})
281
+
282
+ # Clipped polynomial: hard cutoff at ±spread from target
283
+ sat.split(text, max_length=100, prior_type="clipped_polynomial",
284
+ prior_kwargs={"target_length": 60, "spread": 25})
285
+ ```
286
+
287
+ ### Language-Aware Defaults
288
+
289
+ Pass `lang_code` to use language-specific defaults for `target_length` and `spread` (based on language-specific corpus statistics):
290
+
291
+ ```python
292
+ # German has longer average sentences → auto-uses target_length=90, spread=35
293
+ sat.split(text, max_length=150, prior_type="gaussian",
294
+ prior_kwargs={"lang_code": "de"})
295
+
296
+ # Chinese has shorter sentences → auto-uses target_length=45, spread=15
297
+ sat.split(text, max_length=100, prior_type="gaussian",
298
+ prior_kwargs={"lang_code": "zh"})
299
+ ```
300
+
301
+ When using LoRA with a language, this happens automatically:
302
+
303
+ ```python
304
+ sat = SaT("sat-3l", style_or_domain="ud", language="de")
305
+ sat.split(text, max_length=150, prior_type="gaussian") # auto-uses German defaults
306
+ ```
307
+
308
+ ### How It Works
309
+
310
+ The Viterbi algorithm finds globally optimal segmentation points that balance:
311
+ - The model's sentence boundary predictions (where natural splits occur)
312
+ - Your length preferences (via the prior; if provided)
313
+
314
+ **Text Reconstruction:**
315
+ ```python
316
+ # With constraints (max_length or min_length):
317
+ original_text = "".join(segments) # segments may contain newlines
318
+
319
+ # Without constraints (SaT default with split_on_input_newlines=True):
320
+ original_text = "\n".join(segments)
321
+ ```
322
+
323
+ > **Note**: When using length constraints, segments may contain newlines. If you want to remove them, you can just post-process the output.
324
+
325
+ > **Note**: When `max_length` is set, the `threshold` parameter is ignored. The Viterbi/greedy algorithms use raw model probabilities directly instead of threshold-based filtering.
326
+
327
+ For more details, see the [Length Constraints Documentation](./docs/LENGTH_CONSTRAINTS.md).
328
+
329
+ ## Adaptation
330
+
331
+ SaT can be domain- and style-adapted via LoRA. We provide trained LoRA modules for Universal Dependencies, OPUS100, Ersatz, and TED (i.e., ASR-style transcribed speeches) sentence styles in 81 languages for `sat-3l`and `sat-12l`. Additionally, we provide LoRA modules for legal documents (laws and judgements) in 6 languages, code-switching in 4 language pairs, and tweets in 3 languages. For details, we refer to our [paper](https://arxiv.org/abs/2406.16678).
332
+
333
+ We also provided verse segmentation modules for 16 genres for `sat-12-no-limited-lookahead`.
334
+
335
+ Load LoRA modules like this:
336
+
337
+ ```python
338
+
339
+ # requires both lang_code and style_or_domain
340
+ # for available ones, check the <model_repository>/loras folder
341
+ sat_lora = SaT("sat-3l", style_or_domain="ud", language="en")
342
+ sat_lora.split("Hello this is a test But this is different now Now the next one starts looool")
343
+ # now for a highly distinct domain
344
+ sat_lora_distinct = SaT("sat-12l", style_or_domain="code-switching", language="es-en")
345
+ sat_lora_distinct.split("in the morning over there cada vez que yo decía algo él me decía algo")
346
+ ```
347
+
348
+ You can also freely adapt the segmentation threshold, with a higher threshold leading to more conservative segmentation:
349
+
350
+ ```python
351
+
352
+ sat.split("This is a test This is another test.", threshold=0.4)
353
+ # works similarly for lora; but thresholds are higher
354
+ sat_lora.split("Hello this is a test But this is different now Now the next one starts looool", threshold=0.7)
355
+ ```
356
+
357
+ ## Advanced Usage
358
+
359
+ ### Get the newline or sentence boundary probabilities for a text:
360
+
361
+ ```python
362
+ # returns newline probabilities (supports batching!)
363
+ sat.predict_proba(text)
364
+ ```
365
+
366
+ ### Load a SaT model in [HuggingFace `transformers`](https://github.com/huggingface/transformers):
367
+
368
+ ```python
369
+ # import library to register the custom models
370
+ import wtpsplit.models
371
+ from transformers import AutoModelForTokenClassification
372
+
373
+ model = AutoModelForTokenClassification.from_pretrained("segment-any-text/sat-3l-sm") # or some other model name; see https://huggingface.co/segment-any-text
374
+ ```
375
+
376
+ ### Adapt to your own corpus via LoRA
377
+
378
+ Our models can be efficiently adapted via LoRA in a powerful way. Only 10-100 training segmented training sentences should already improve performance considerably. To do so:
379
+
380
+ Clone the repository and install requirements:
381
+
382
+ ```
383
+ git clone https://github.com/segment-any-text/wtpsplit
384
+ cd wtpsplit
385
+ pip install -r requirements.txt
386
+ pip install adapters==0.2.1 --no-dependencies
387
+ cd ..
388
+ ```
389
+
390
+ 1. Create data in this format:
391
+
392
+ ```python
393
+ import torch
394
+
395
+ torch.save(
396
+ {
397
+ "language_code": {
398
+ "sentence": {
399
+ "dummy-dataset": {
400
+ "meta": {
401
+ "train_data": ["train sentence 1", "train sentence 2"],
402
+ },
403
+ "data": [
404
+ "test sentence 1",
405
+ "test sentence 2",
406
+ ]
407
+ }
408
+ }
409
+ }
410
+ },
411
+ "dummy-dataset.pth"
412
+ )
413
+ ```
414
+
415
+ Note that there should not be any newlines within individual sentences! This now raises an error. Instead, each entry of a list should be a sentence, and there should be no "\n" characters. So your corpus should already be well-split.
416
+
417
+ 2. Create/adapt config; provide base model via `model_name_or_path` and training data .pth via `text_path`:
418
+
419
+ `configs/lora/lora_dummy_config.json`
420
+
421
+ We recommend starting using this config, and adapting `model_name_or_path`, `output_dir`, and `text_path` if needed.
422
+ You may also wish to adapt other aspects such as `adapter_config` and batch sizes, but this is more experimental.
423
+
424
+ 3. Train LoRA:
425
+
426
+ ```
427
+ python3 wtpsplit/train/train_lora.py configs/lora/lora_dummy_config.json
428
+ ```
429
+
430
+ 4. Once training is done, provide your saved module's path to SaT:
431
+
432
+ ```python
433
+
434
+ sat_lora_adapted = SaT("model-used", lora_path="dummy_lora_path")
435
+ sat_lora_adapted.split("Some domains-specific or styled text")
436
+ ```
437
+
438
+ **Important:** Use the **same model variant** for inference as for training (e.g. `sat-12l-sm` and `sat-12l` have different configs; an adapter trained on one cannot be loaded on the other).
439
+
440
+ Adjust the dataset name, language and model in the above to your needs.
441
+
442
+ ## Reproducing the paper
443
+
444
+ `configs/` contains the configs for the runs from the paper for base and sm models as well as LoRA modules. Launch training for each of them like this:
445
+
446
+ ```
447
+ python3 wtpsplit/train/train.py configs/<config_name>.json
448
+ python3 wtpsplit/train/train_sm.py configs/<config_name>.json
449
+ python3 wtpsplit/train/train_lora.py configs/<config_name>.json
450
+ ```
451
+
452
+ In addition:
453
+
454
+ - `wtpsplit/data_acquisition` contains the code for obtaining evaluation data and raw text from the mC4 corpus.
455
+ - `wtpsplit/evaluation` contains the code for:
456
+ - evaluation (i.e. sentence segmentation results) via `intrinsic.py`.
457
+ - short-sequence evaluation (i.e. sentence segmentation results for pairs/k-mers of sentences) via `intrinsic_pairwise.py`.
458
+ - LLM baseline evaluation (`llm_sentence.py`), legal baseline evaluation (`legal_baselines.py`)
459
+ - baseline (PySBD, nltk, etc.) evaluation results in `intrinsic_baselines.py` and `intrinsic_baselines_multi.py`
460
+ - Raw results in JSON format are also in `evaluation_results/`
461
+ - Statistical significane testing code and results ara in `stat_tests/`
462
+ - punctuation annotation experiments in `punct_annotation.py` and `punct_annotation_wtp.py` (WtP only)
463
+ - extrinsic evaluation on Machine Translation in `extrinsic.py` (WtP only)
464
+
465
+ Ensure to install packages from `requirements.txt` beforehand.
466
+
467
+ ## Supported Languages
468
+
469
+ <details>
470
+ <summary>Table with supported languages</summary>
471
+
472
+ | iso | Name |
473
+ | :-- | :-------------- |
474
+ | af | Afrikaans |
475
+ | am | Amharic |
476
+ | ar | Arabic |
477
+ | az | Azerbaijani |
478
+ | be | Belarusian |
479
+ | bg | Bulgarian |
480
+ | bn | Bengali |
481
+ | ca | Catalan |
482
+ | ceb | Cebuano |
483
+ | cs | Czech |
484
+ | cy | Welsh |
485
+ | da | Danish |
486
+ | de | German |
487
+ | el | Greek |
488
+ | en | English |
489
+ | eo | Esperanto |
490
+ | es | Spanish |
491
+ | et | Estonian |
492
+ | eu | Basque |
493
+ | fa | Persian |
494
+ | fi | Finnish |
495
+ | fr | French |
496
+ | fy | Western Frisian |
497
+ | ga | Irish |
498
+ | gd | Scottish Gaelic |
499
+ | gl | Galician |
500
+ | gu | Gujarati |
501
+ | ha | Hausa |
502
+ | he | Hebrew |
503
+ | hi | Hindi |
504
+ | hu | Hungarian |
505
+ | hy | Armenian |
506
+ | id | Indonesian |
507
+ | ig | Igbo |
508
+ | is | Icelandic |
509
+ | it | Italian |
510
+ | ja | Japanese |
511
+ | jv | Javanese |
512
+ | ka | Georgian |
513
+ | kk | Kazakh |
514
+ | km | Central Khmer |
515
+ | kn | Kannada |
516
+ | ko | Korean |
517
+ | ku | Kurdish |
518
+ | ky | Kirghiz |
519
+ | la | Latin |
520
+ | lt | Lithuanian |
521
+ | lv | Latvian |
522
+ | mg | Malagasy |
523
+ | mk | Macedonian |
524
+ | ml | Malayalam |
525
+ | mn | Mongolian |
526
+ | mr | Marathi |
527
+ | ms | Malay |
528
+ | mt | Maltese |
529
+ | my | Burmese |
530
+ | ne | Nepali |
531
+ | nl | Dutch |
532
+ | no | Norwegian |
533
+ | pa | Panjabi |
534
+ | pl | Polish |
535
+ | ps | Pushto |
536
+ | pt | Portuguese |
537
+ | ro | Romanian |
538
+ | ru | Russian |
539
+ | si | Sinhala |
540
+ | sk | Slovak |
541
+ | sl | Slovenian |
542
+ | sq | Albanian |
543
+ | sr | Serbian |
544
+ | sv | Swedish |
545
+ | ta | Tamil |
546
+ | te | Telugu |
547
+ | tg | Tajik |
548
+ | th | Thai |
549
+ | tr | Turkish |
550
+ | uk | Ukrainian |
551
+ | ur | Urdu |
552
+ | uz | Uzbek |
553
+ | vi | Vietnamese |
554
+ | xh | Xhosa |
555
+ | yi | Yiddish |
556
+ | yo | Yoruba |
557
+ | zh | Chinese |
558
+ | zu | Zulu |
559
+
560
+ </details>
561
+
562
+ For details, please see our [Segment any Text paper](https://arxiv.org/abs/2406.16678).
563
+
564
+ ## Community Ports
565
+
566
+ - **Rust**: [wtsplit-rs](https://github.com/19h/wtsplit-rs) by [@19h](https://github.com/19h)
567
+
568
+ *Note: Community ports are independently maintained and may have different feature sets or update schedules.*
569
+
570
+ ## Citations
571
+
572
+ For the `SaT` models, please kindly cite our paper:
573
+
574
+ ```
575
+ @inproceedings{frohmann-etal-2024-segment,
576
+ title = "Segment Any Text: A Universal Approach for Robust, Efficient and Adaptable Sentence Segmentation",
577
+ author = "Frohmann, Markus and
578
+ Sterner, Igor and
579
+ Vuli{\'c}, Ivan and
580
+ Minixhofer, Benjamin and
581
+ Schedl, Markus",
582
+ editor = "Al-Onaizan, Yaser and
583
+ Bansal, Mohit and
584
+ Chen, Yun-Nung",
585
+ booktitle = "Proceedings of the 2024 Conference on Empirical Methods in Natural Language Processing",
586
+ month = nov,
587
+ year = "2024",
588
+ address = "Miami, Florida, USA",
589
+ publisher = "Association for Computational Linguistics",
590
+ url = "https://aclanthology.org/2024.emnlp-main.665",
591
+ pages = "11908--11941"
592
+ }
593
+
594
+ ```
595
+
596
+ For the library and the WtP models, please cite:
597
+
598
+ ```
599
+ @inproceedings{minixhofer-etal-2023-wheres,
600
+ title = "Where{'}s the Point? Self-Supervised Multilingual Punctuation-Agnostic Sentence Segmentation",
601
+ author = "Minixhofer, Benjamin and
602
+ Pfeiffer, Jonas and
603
+ Vuli{\'c}, Ivan",
604
+ booktitle = "Proceedings of the 61st Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers)",
605
+ month = jul,
606
+ year = "2023",
607
+ address = "Toronto, Canada",
608
+ publisher = "Association for Computational Linguistics",
609
+ url = "https://aclanthology.org/2023.acl-long.398",
610
+ pages = "7215--7235"
611
+ }
612
+ ```
613
+
614
+ ## Acknowledgments
615
+
616
+ This research was funded in whole or in part by the Austrian Science Fund (FWF): P36413, P33526, and DFH-23, and by the State of Upper Austria and the Federal Ministry of Education, Science, and Research, through grants LIT-2021-YOU-215. In addition, Ivan Vulic and Benjamin Minixhofer have been supported through the Royal Society University Research Fellowship ‘Inclusive and Sustainable Language Technology for a Truly Multilingual World’ (no 221137) awarded to Ivan Vulić. This research has also been supported with Cloud TPUs from Google’s TPU Research Cloud (TRC). This work was also supported by compute credits from a Cohere For AI Research Grant, these grants are designed to support academic partners conducting research with the goal of releasing scientific artifacts and data for good projects. We also thank Simone Teufel for fruitful discussions.
617
+
618
+ ---
619
+
620
+ For any questions, please create an issue or send an email to markus.frohmann@gmail.com, and I will get back to you as soon as possible.
@@ -51,6 +51,44 @@ sat_adapted.split("This is a test This is another test.")
51
51
  # returns ['This is a test ', 'This is another test']
52
52
  ```
53
53
 
54
+ ## Faster PyTorch inference (`torch.compile` / TorchInductor)
55
+
56
+ For repeated inference without exporting to ONNX, compile the PyTorch model with [TorchInductor](https://pytorch.org/docs/stable/torch.compiler.html) via `optimize()` (PyTorch 2.0+). This is **optional** - the first `split` after `optimize()` can be slow while graphs are built. Call `optimize()` **after** `to()` / `half()` so the compiled graph matches *your* device and dtype.
57
+
58
+ ```python
59
+ from wtpsplit import SaT
60
+
61
+ sat = SaT("sat-3l-sm")
62
+ sat.half().to("cuda")
63
+ sat.optimize() # backend="inductor" (default); dynamic shapes on
64
+
65
+ sat.split("This is a test This is another test.")
66
+ ```
67
+
68
+ - **SaT** and **WtP** PyTorch checkpoints only — not available with `ort_providers` / ONNX.
69
+ - `backend` synonyms: `"inductor"`, `"torchinductor"`.
70
+ - Chunk length and the last batch size change between calls, so `dynamic=True` is the default. `mode="reduce-overhead"` (CUDA graphs) is faster only when every forward uses the same shapes; otherwise use the default or `mode="max-autotune-no-cudagraphs"`.
71
+ - Optional on NVIDIA Ampere+: `torch.set_float32_matmul_precision("high")` before inference (faster fp32 matmuls).
72
+
73
+ ### NVIDIA AITune (auto backend selection, CUDA)
74
+
75
+ [NVIDIA AITune](https://github.com/ai-dynamo/aitune) benchmarks backends (TensorRT, Torch-TensorRT, Torch Inductor, …) on your GPU and picks a fast path. Requires Linux, CUDA, and a separate install:
76
+
77
+ ```bash
78
+ pip install wtpsplit[aitune] --extra-index-url https://pypi.nvidia.com
79
+ ```
80
+
81
+ ```python
82
+ sat = SaT("sat-3l-sm")
83
+ sat.half().to("cuda")
84
+ # first_wins tries TensorRT → Torch-TensorRT → Inductor when available
85
+ sat.optimize(backend="aitune")
86
+ # or only Inductor via AITune (faster tuning, no TensorRT dependency):
87
+ # sat.optimize(backend="aitune", aitune_strategy="inductor_only", aitune_max_batches=4)
88
+
89
+ sat.split("This is a test This is another test.")
90
+ ```
91
+
54
92
  ## ONNX Support
55
93
 
56
94
  🚀 You can now enable even faster ONNX inference for `sat` and `sat-sm` models! 🚀
@@ -4,7 +4,11 @@ target-version = ["py39", "py310", "py311", "py312"]
4
4
 
5
5
  [tool.ruff]
6
6
  line-length = 120
7
- lint.ignore = ["E741"]
7
+
8
+ [tool.ruff.lint]
9
+ # Pre-0.16 default. Ruff 0.16 enables 413 rules by default; CI installs latest ruff.
10
+ select = ["E4", "E7", "E9", "F"]
11
+ ignore = ["E741"]
8
12
 
9
13
  [tool.ruff.lint.per-file-ignores]
10
14
  "test.py" = ["E501"]