nano-laser 0.1.0__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.
- nano_laser-0.1.0/.github/workflows/prcheck.yml +29 -0
- nano_laser-0.1.0/.github/workflows/pre-commit.yml +18 -0
- nano_laser-0.1.0/.github/workflows/publish.yml +50 -0
- nano_laser-0.1.0/.github/workflows/tests.yml +42 -0
- nano_laser-0.1.0/.gitignore +10 -0
- nano_laser-0.1.0/.pre-commit-config.yaml +23 -0
- nano_laser-0.1.0/.python-version +1 -0
- nano_laser-0.1.0/CHANGELOG.md +21 -0
- nano_laser-0.1.0/CONTRIBUTING.md +8 -0
- nano_laser-0.1.0/LICENSE +29 -0
- nano_laser-0.1.0/PKG-INFO +108 -0
- nano_laser-0.1.0/README.md +92 -0
- nano_laser-0.1.0/RELEASE.md +100 -0
- nano_laser-0.1.0/justfile +35 -0
- nano_laser-0.1.0/notebooks/check_laser3.ipynb +132 -0
- nano_laser-0.1.0/pyproject.toml +81 -0
- nano_laser-0.1.0/scripts/check_laser3.py +132 -0
- nano_laser-0.1.0/scripts/laser_encoders_reference.py +26 -0
- nano_laser-0.1.0/scripts/results/laser3_parity.jsonl +147 -0
- nano_laser-0.1.0/src/nano_laser/__init__.py +12 -0
- nano_laser-0.1.0/src/nano_laser/download_models.py +70 -0
- nano_laser-0.1.0/src/nano_laser/language_list.py +564 -0
- nano_laser-0.1.0/src/nano_laser/laser_tokenizer.py +130 -0
- nano_laser-0.1.0/src/nano_laser/models.py +502 -0
- nano_laser-0.1.0/tests/data/make_reference.py +25 -0
- nano_laser-0.1.0/tests/data/reference.npz +0 -0
- nano_laser-0.1.0/tests/data/sentences.json +20 -0
- nano_laser-0.1.0/tests/test_languages.py +31 -0
- nano_laser-0.1.0/tests/test_models.py +125 -0
- nano_laser-0.1.0/tests/test_parity.py +44 -0
- nano_laser-0.1.0/uv.lock +1417 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: PR Checks
|
|
2
|
+
|
|
3
|
+
concurrency:
|
|
4
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
5
|
+
cancel-in-progress: true
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
paths:
|
|
12
|
+
- 'src/nano_laser/**'
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
changelog:
|
|
16
|
+
name: CHANGELOG
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
if: github.event_name == 'pull_request'
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0
|
|
24
|
+
|
|
25
|
+
- name: Check that CHANGELOG has been updated
|
|
26
|
+
run: |
|
|
27
|
+
# If this step fails, this means you haven't updated the CHANGELOG.md
|
|
28
|
+
# file with notes on your contribution.
|
|
29
|
+
git diff --name-only $(git merge-base origin/main HEAD) | grep '^CHANGELOG.md$' && echo "Thanks for helping keep our CHANGELOG up-to-date!"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Prek checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
prek:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
|
|
18
|
+
- uses: j178/prek-action@v1
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a release tag is pushed, with trusted publishing: no API token is stored.
|
|
4
|
+
# PyPI side: add a trusted publisher for BurkimbIA/nano-laser, workflow publish.yml, environment pypi.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*.*.*"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v5
|
|
17
|
+
with:
|
|
18
|
+
version: "latest"
|
|
19
|
+
|
|
20
|
+
- name: Check that the tag is the project version
|
|
21
|
+
run: |
|
|
22
|
+
version="$(uv version --short)"
|
|
23
|
+
if [ "${GITHUB_REF_NAME}" != "v${version}" ]; then
|
|
24
|
+
echo "Tag ${GITHUB_REF_NAME} does not match project.version ${version}" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
|
|
36
|
+
publish:
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment:
|
|
40
|
+
name: pypi
|
|
41
|
+
url: https://pypi.org/project/nano-laser/
|
|
42
|
+
permissions:
|
|
43
|
+
id-token: write
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/download-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
|
|
50
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
tests:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.13"
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: false
|
|
25
|
+
version: "latest"
|
|
26
|
+
|
|
27
|
+
- name: Cache uv
|
|
28
|
+
uses: actions/cache@v4
|
|
29
|
+
with:
|
|
30
|
+
path: ~/.cache/uv
|
|
31
|
+
key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
|
|
32
|
+
restore-keys: |
|
|
33
|
+
uv-${{ runner.os }}-
|
|
34
|
+
|
|
35
|
+
- name: Install just
|
|
36
|
+
uses: extractions/setup-just@v3
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies (dev)
|
|
39
|
+
run: just install
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: just test
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
3
|
+
rev: 0.13.4
|
|
4
|
+
hooks:
|
|
5
|
+
- id: uv-lock
|
|
6
|
+
|
|
7
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
8
|
+
rev: v0.14.10
|
|
9
|
+
hooks:
|
|
10
|
+
- id: ruff
|
|
11
|
+
args: [ --fix ]
|
|
12
|
+
types_or: [ python, pyi ]
|
|
13
|
+
|
|
14
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
15
|
+
rev: v6.0.0
|
|
16
|
+
hooks:
|
|
17
|
+
- id: check-merge-conflict
|
|
18
|
+
- id: mixed-line-ending
|
|
19
|
+
|
|
20
|
+
exclude: |
|
|
21
|
+
unsloth_compiled_cache/.*
|
|
22
|
+
|llms-finetune/gemma3/unsloth_compiled_cache/.*
|
|
23
|
+
|.*\.ipynb$
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## v0.1.0 - 2026-10-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `nano_laser`: LASER2 and LASER3 encoders in plain torch, a drop-in replacement for `laser_encoders` 0.0.2 without fairseq.
|
|
15
|
+
- Parity tests against embeddings from `laser_encoders`, run when the models are cached locally.
|
|
16
|
+
- A Colab notebook comparing `nano_laser` with `laser_encoders` on all 147 LASER3 models; all match.
|
|
17
|
+
- A workflow that publishes to PyPI with trusted publishing when a `vX.Y.Z` tag is pushed.
|
|
18
|
+
|
|
19
|
+
### Security
|
|
20
|
+
|
|
21
|
+
- Bumped the `uv-pre-commit` hook to uv 0.13.4. Earlier versions ship the compromised emSign ECC Root CA - G3 root, which certifi 2026.10.19 and uv 0.13.4 remove.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Contributing
|
|
2
|
+
============
|
|
3
|
+
|
|
4
|
+
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
5
|
+
for inclusion in the work by you shall be licensed as in [LICENSE](LICENSE),
|
|
6
|
+
without any additional terms or conditions.
|
|
7
|
+
|
|
8
|
+
See [the readme](README.md#contributing) for contribution workflow suggestions.
|
nano_laser-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, BurkimbIA
|
|
4
|
+
Copyright (c) Facebook, Inc. and its affiliates (LASER, the code adapted in nano_laser)
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: nano-laser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LASER sentence encoders in plain torch: a drop-in replacement for laser_encoders, without fairseq
|
|
5
|
+
Author: Facebook AI Research
|
|
6
|
+
Author-email: MNIKIEMA <nikiemamahamadi01@gmail.com>, Salif Sawadogo <sawallesalfo@gmail.com>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: numpy>=1.21.3
|
|
11
|
+
Requires-Dist: sacremoses==0.1.0
|
|
12
|
+
Requires-Dist: sentencepiece>=0.1.99
|
|
13
|
+
Requires-Dist: torch>=2.5
|
|
14
|
+
Requires-Dist: unicategories>=0.1.2
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# nano-laser
|
|
18
|
+
|
|
19
|
+
The [LASER](https://github.com/facebookresearch/LASER) sentence encoders (LASER2 and the LASER3 language
|
|
20
|
+
models) in plain torch. It is a drop-in replacement for [`laser_encoders`](https://pypi.org/project/laser-encoders/),
|
|
21
|
+
without fairseq: the same API, the same checkpoints, the same embeddings.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
The package is not on PyPI yet; install it from GitHub:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install git+https://github.com/BurkimbIA/nano-laser.git
|
|
29
|
+
# or, in a uv project
|
|
30
|
+
uv add git+https://github.com/BurkimbIA/nano-laser.git
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Append `@<commit or tag>` to the URL to pin a version. It needs Python ≥ 3.10 and torch ≥ 2.5.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Replace the import, nothing else:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from nano_laser import LaserEncoderPipeline # was: from laser_encoders import LaserEncoderPipeline
|
|
41
|
+
|
|
42
|
+
encoder = LaserEncoderPipeline(lang="mos_Latn")
|
|
43
|
+
embeddings = encoder.encode_sentences(["Laafi bala?"], normalize_embeddings=True) # (1, 1024)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`initialize_tokenizer`, `initialize_encoder`, `SentenceEncoder`, `LaserTokenizer` and the module layout
|
|
47
|
+
(`nano_laser.models`, `nano_laser.laser_tokenizer`, `nano_laser.download_models`, `nano_laser.language_list`)
|
|
48
|
+
mirror `laser_encoders`. The models are downloaded from the same place into the same cache,
|
|
49
|
+
`~/.cache/laser_encoders`, so models already downloaded by `laser_encoders` are reused.
|
|
50
|
+
|
|
51
|
+
Like `laser_encoders`, the encoder takes one sentence per batch by default. Batch on GPU with the
|
|
52
|
+
`SentenceEncoder` options, which `initialize_encoder` and `LaserEncoderPipeline` now pass on:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
encoder = LaserEncoderPipeline(lang="dyu_Latn", max_sentences=64) # or max_tokens=..., cpu=True, fp16=True
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Parity
|
|
59
|
+
|
|
60
|
+
Against `laser_encoders` 0.0.2 (fairseq 0.12.2, torch 2.1.2), on 85 sentences of 0 to 2,000 tokens:
|
|
61
|
+
|
|
62
|
+
| model | max abs. difference (cpu) | min cosine (cpu and gpu, batched or not) |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| LASER2 (LSTM) | 1.3e-6 | 0.9999996 |
|
|
65
|
+
| LASER3 mos_Latn, dyu_Latn, fuv_Latn (transformer) | 1.4e-5 | 0.9999998 |
|
|
66
|
+
|
|
67
|
+
Every LASER3 model is also checked, on a Colab T4 with 18 sentences, one at a time and in batches of 8
|
|
68
|
+
([`notebooks/check_laser3.ipynb`](notebooks/check_laser3.ipynb)): all 147 give the same tokens and embeddings,
|
|
69
|
+
with a min cosine of 0.9999997 and a max abs. difference of 2.0e-5. They come in three kinds:
|
|
70
|
+
|
|
71
|
+
| LASER3 models | languages |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| 12-layer transformer, `maxpool` pooling | 135 |
|
|
74
|
+
| LSTM, like LASER2 | 10: `als_Latn`, `cym_Latn`, `hye_Armn`, `kat_Geor`, `khk_Cyrl`, `khm_Khmr`, `ltz_Latn`, `mlt_Latn`, `quy_Latn`, `ydd_Hebr` |
|
|
75
|
+
| 12-layer transformer, `cls` pooling on a leading `<s>` | 2: `lij_Latn`, `wol_Latn` |
|
|
76
|
+
|
|
77
|
+
The results per language are in [`scripts/results/laser3_parity.jsonl`](scripts/results/laser3_parity.jsonl).
|
|
78
|
+
|
|
79
|
+
`tests/test_parity.py` checks this against `tests/data/reference.npz` whenever the models are in the cache
|
|
80
|
+
(or in `LASER_MODEL_DIR`); it is skipped otherwise, so CI downloads nothing.
|
|
81
|
+
|
|
82
|
+
## Differences from laser_encoders
|
|
83
|
+
|
|
84
|
+
- No fairseq: the transformer encoder and fairseq's `Dictionary` are re-implemented in `nano_laser/models.py`.
|
|
85
|
+
- Checkpoints load with `torch.load(weights_only=True)`: they only hold tensors and an `argparse.Namespace`.
|
|
86
|
+
- LASER3 works with `max_sentences > 1`. In `laser_encoders` on torch ≥ 1.12, fairseq's BetterTransformer path fails on
|
|
87
|
+
left-padded batches (`RuntimeError: Mask Type should be defined`).
|
|
88
|
+
- With `sentemb_criterion="cls"`, each sentence of a batch gets its own `<s>` state whatever the padding side.
|
|
89
|
+
`laser_encoders` takes the `<s>` states in position order, which is only right when they share a position:
|
|
90
|
+
true for the two `cls` models (`lij_Latn`, `wol_Latn`), which pad on the right.
|
|
91
|
+
- Importing the package does not call `logging.basicConfig`, and a failed download raises instead of saving the
|
|
92
|
+
error page as the model.
|
|
93
|
+
|
|
94
|
+
## Development
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
just install # uv sync and the pre-commit hooks
|
|
98
|
+
just test
|
|
99
|
+
just lint
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
To regenerate the parity reference, see `tests/data/make_reference.py`.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
[BSD 3-Clause](LICENSE), Copyright (c) 2026, BurkimbIA. The code adapted from LASER keeps its copyright,
|
|
107
|
+
Copyright (c) Facebook, Inc. and its affiliates, also under the BSD license; each such file says so in its header.
|
|
108
|
+
The LASER model weights are downloaded from Facebook and are not covered by this license.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# nano-laser
|
|
2
|
+
|
|
3
|
+
The [LASER](https://github.com/facebookresearch/LASER) sentence encoders (LASER2 and the LASER3 language
|
|
4
|
+
models) in plain torch. It is a drop-in replacement for [`laser_encoders`](https://pypi.org/project/laser-encoders/),
|
|
5
|
+
without fairseq: the same API, the same checkpoints, the same embeddings.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
The package is not on PyPI yet; install it from GitHub:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install git+https://github.com/BurkimbIA/nano-laser.git
|
|
13
|
+
# or, in a uv project
|
|
14
|
+
uv add git+https://github.com/BurkimbIA/nano-laser.git
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Append `@<commit or tag>` to the URL to pin a version. It needs Python ≥ 3.10 and torch ≥ 2.5.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Replace the import, nothing else:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from nano_laser import LaserEncoderPipeline # was: from laser_encoders import LaserEncoderPipeline
|
|
25
|
+
|
|
26
|
+
encoder = LaserEncoderPipeline(lang="mos_Latn")
|
|
27
|
+
embeddings = encoder.encode_sentences(["Laafi bala?"], normalize_embeddings=True) # (1, 1024)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`initialize_tokenizer`, `initialize_encoder`, `SentenceEncoder`, `LaserTokenizer` and the module layout
|
|
31
|
+
(`nano_laser.models`, `nano_laser.laser_tokenizer`, `nano_laser.download_models`, `nano_laser.language_list`)
|
|
32
|
+
mirror `laser_encoders`. The models are downloaded from the same place into the same cache,
|
|
33
|
+
`~/.cache/laser_encoders`, so models already downloaded by `laser_encoders` are reused.
|
|
34
|
+
|
|
35
|
+
Like `laser_encoders`, the encoder takes one sentence per batch by default. Batch on GPU with the
|
|
36
|
+
`SentenceEncoder` options, which `initialize_encoder` and `LaserEncoderPipeline` now pass on:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
encoder = LaserEncoderPipeline(lang="dyu_Latn", max_sentences=64) # or max_tokens=..., cpu=True, fp16=True
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Parity
|
|
43
|
+
|
|
44
|
+
Against `laser_encoders` 0.0.2 (fairseq 0.12.2, torch 2.1.2), on 85 sentences of 0 to 2,000 tokens:
|
|
45
|
+
|
|
46
|
+
| model | max abs. difference (cpu) | min cosine (cpu and gpu, batched or not) |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| LASER2 (LSTM) | 1.3e-6 | 0.9999996 |
|
|
49
|
+
| LASER3 mos_Latn, dyu_Latn, fuv_Latn (transformer) | 1.4e-5 | 0.9999998 |
|
|
50
|
+
|
|
51
|
+
Every LASER3 model is also checked, on a Colab T4 with 18 sentences, one at a time and in batches of 8
|
|
52
|
+
([`notebooks/check_laser3.ipynb`](notebooks/check_laser3.ipynb)): all 147 give the same tokens and embeddings,
|
|
53
|
+
with a min cosine of 0.9999997 and a max abs. difference of 2.0e-5. They come in three kinds:
|
|
54
|
+
|
|
55
|
+
| LASER3 models | languages |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| 12-layer transformer, `maxpool` pooling | 135 |
|
|
58
|
+
| LSTM, like LASER2 | 10: `als_Latn`, `cym_Latn`, `hye_Armn`, `kat_Geor`, `khk_Cyrl`, `khm_Khmr`, `ltz_Latn`, `mlt_Latn`, `quy_Latn`, `ydd_Hebr` |
|
|
59
|
+
| 12-layer transformer, `cls` pooling on a leading `<s>` | 2: `lij_Latn`, `wol_Latn` |
|
|
60
|
+
|
|
61
|
+
The results per language are in [`scripts/results/laser3_parity.jsonl`](scripts/results/laser3_parity.jsonl).
|
|
62
|
+
|
|
63
|
+
`tests/test_parity.py` checks this against `tests/data/reference.npz` whenever the models are in the cache
|
|
64
|
+
(or in `LASER_MODEL_DIR`); it is skipped otherwise, so CI downloads nothing.
|
|
65
|
+
|
|
66
|
+
## Differences from laser_encoders
|
|
67
|
+
|
|
68
|
+
- No fairseq: the transformer encoder and fairseq's `Dictionary` are re-implemented in `nano_laser/models.py`.
|
|
69
|
+
- Checkpoints load with `torch.load(weights_only=True)`: they only hold tensors and an `argparse.Namespace`.
|
|
70
|
+
- LASER3 works with `max_sentences > 1`. In `laser_encoders` on torch ≥ 1.12, fairseq's BetterTransformer path fails on
|
|
71
|
+
left-padded batches (`RuntimeError: Mask Type should be defined`).
|
|
72
|
+
- With `sentemb_criterion="cls"`, each sentence of a batch gets its own `<s>` state whatever the padding side.
|
|
73
|
+
`laser_encoders` takes the `<s>` states in position order, which is only right when they share a position:
|
|
74
|
+
true for the two `cls` models (`lij_Latn`, `wol_Latn`), which pad on the right.
|
|
75
|
+
- Importing the package does not call `logging.basicConfig`, and a failed download raises instead of saving the
|
|
76
|
+
error page as the model.
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
just install # uv sync and the pre-commit hooks
|
|
82
|
+
just test
|
|
83
|
+
just lint
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
To regenerate the parity reference, see `tests/data/make_reference.py`.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
[BSD 3-Clause](LICENSE), Copyright (c) 2026, BurkimbIA. The code adapted from LASER keeps its copyright,
|
|
91
|
+
Copyright (c) Facebook, Inc. and its affiliates, also under the BSD license; each such file says so in its header.
|
|
92
|
+
The LASER model weights are downloaded from Facebook and are not covered by this license.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Release process
|
|
2
|
+
|
|
3
|
+
This guide describes how to release `nano-laser`.
|
|
4
|
+
|
|
5
|
+
## Versioning
|
|
6
|
+
|
|
7
|
+
The project follows [Semantic Versioning](https://semver.org/):
|
|
8
|
+
|
|
9
|
+
- `MAJOR`: breaking API, configuration, or workflow changes.
|
|
10
|
+
- `MINOR`: backwards-compatible features.
|
|
11
|
+
- `PATCH`: backwards-compatible bug fixes, dependency fixes, and security patches.
|
|
12
|
+
|
|
13
|
+
The release version is the value of `project.version` in [`pyproject.toml`](pyproject.toml). The release tag and changelog heading must use the same version, formatted as `vX.Y.Z`.
|
|
14
|
+
|
|
15
|
+
## Prepare the release
|
|
16
|
+
|
|
17
|
+
Start from an up-to-date `main` branch and create a release branch:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git checkout main
|
|
21
|
+
git pull --ff-only origin main
|
|
22
|
+
git checkout -b chore/release-vX.Y.Z
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Choose the appropriate version bump:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv version --bump major
|
|
29
|
+
uv version --bump minor
|
|
30
|
+
uv version --bump patch
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This updates `pyproject.toml` and `uv.lock`. Do not edit only one of them.
|
|
34
|
+
|
|
35
|
+
Update `CHANGELOG.md` as follows:
|
|
36
|
+
|
|
37
|
+
1. Move the release-ready entries from `Unreleased` into `## vX.Y.Z - YYYY-MM-DD`.
|
|
38
|
+
2. Add a new empty `## Unreleased` section directly below the changelog intro.
|
|
39
|
+
3. Review the entries for user-facing accuracy and remove internal-only detail that does not help users upgrade.
|
|
40
|
+
|
|
41
|
+
## Verify and open the release PR
|
|
42
|
+
|
|
43
|
+
Run the release checks:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv lock --check
|
|
47
|
+
just format
|
|
48
|
+
just lint
|
|
49
|
+
just typecheck
|
|
50
|
+
just test
|
|
51
|
+
just pre-commit
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Review the resulting diff. It should include the intended version change, lockfile update, and changelog update, with any relevant documentation or configuration changes.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git add pyproject.toml uv.lock CHANGELOG.md
|
|
58
|
+
git commit -m "chore: release vX.Y.Z"
|
|
59
|
+
git push -u origin chore/release-vX.Y.Z
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Open a pull request targeting `main` and merge it after review and all enabled CI checks pass.
|
|
63
|
+
|
|
64
|
+
## Tag and publish
|
|
65
|
+
|
|
66
|
+
After the release PR is merged, tag the exact `main` commit with an annotated tag, then push it:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git checkout main
|
|
70
|
+
git pull --ff-only origin main
|
|
71
|
+
git tag -a vX.Y.Z -m "Release vX.Y.Z"
|
|
72
|
+
git push origin vX.Y.Z
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Create the GitHub Release using generated notes, then review and edit the notes before publishing:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Post-release checks
|
|
82
|
+
|
|
83
|
+
- Confirm the Git tag points to the merged release commit.
|
|
84
|
+
- Confirm the GitHub Release title, version, and notes are correct.
|
|
85
|
+
- Confirm that `CHANGELOG.md` has an empty `Unreleased` section for the next change.
|
|
86
|
+
|
|
87
|
+
## Publish to PyPI
|
|
88
|
+
|
|
89
|
+
Pushing the tag runs [`publish.yml`](.github/workflows/publish.yml): it checks that the tag is `v` + `project.version`,
|
|
90
|
+
builds the sdist and wheel with `uv build`, and publishes them to PyPI through trusted publishing.
|
|
91
|
+
|
|
92
|
+
One-time setup, before the first release:
|
|
93
|
+
|
|
94
|
+
- On PyPI, add a trusted publisher for the project `nano-laser`: owner `BurkimbIA`, repository `nano-laser`,
|
|
95
|
+
workflow `publish.yml`, environment `pypi` (a "pending publisher" if the project does not exist yet).
|
|
96
|
+
- On GitHub, create the `pypi` environment (Settings → Environments), optionally with required reviewers so that
|
|
97
|
+
each upload waits for an approval.
|
|
98
|
+
|
|
99
|
+
After the workflow succeeds, confirm that https://pypi.org/project/nano-laser/ shows the new version and that
|
|
100
|
+
`pip install nano-laser==X.Y.Z` installs it.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# display help information
|
|
2
|
+
default:
|
|
3
|
+
@just --list
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# clean the project
|
|
7
|
+
clean:
|
|
8
|
+
rm -rf .venv/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# install the dependencies
|
|
12
|
+
install:
|
|
13
|
+
uv sync --all-groups
|
|
14
|
+
uvx prek install
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# format with ruff
|
|
18
|
+
format:
|
|
19
|
+
uvx ruff format
|
|
20
|
+
|
|
21
|
+
# lint with ruff
|
|
22
|
+
lint:
|
|
23
|
+
uvx ruff check
|
|
24
|
+
|
|
25
|
+
# Typecheck using ty
|
|
26
|
+
typecheck:
|
|
27
|
+
uvx ty check
|
|
28
|
+
|
|
29
|
+
# Run pre-commit [lint, format]"
|
|
30
|
+
pre-commit: lint format
|
|
31
|
+
uvx prek run
|
|
32
|
+
|
|
33
|
+
# test with pytest
|
|
34
|
+
test:
|
|
35
|
+
uv run pytest
|