pyannote-metrics 4.0.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.
- pyannote_metrics-4.0.0/.github/workflows/doc.yml +33 -0
- pyannote_metrics-4.0.0/.github/workflows/release.yml +125 -0
- pyannote_metrics-4.0.0/.github/workflows/test.yml +36 -0
- pyannote_metrics-4.0.0/.gitignore +58 -0
- pyannote_metrics-4.0.0/.python-version +1 -0
- pyannote_metrics-4.0.0/ISSUE_TEMPLATE.md +40 -0
- pyannote_metrics-4.0.0/LICENSE +21 -0
- pyannote_metrics-4.0.0/PKG-INFO +61 -0
- pyannote_metrics-4.0.0/README.md +33 -0
- pyannote_metrics-4.0.0/doc/Makefile +20 -0
- pyannote_metrics-4.0.0/doc/pyannote-metrics.pdf +0 -0
- pyannote_metrics-4.0.0/doc/source/api.rst +8 -0
- pyannote_metrics-4.0.0/doc/source/basics.rst +186 -0
- pyannote_metrics-4.0.0/doc/source/changelog.rst +306 -0
- pyannote_metrics-4.0.0/doc/source/cli.rst +59 -0
- pyannote_metrics-4.0.0/doc/source/conf.py +208 -0
- pyannote_metrics-4.0.0/doc/source/images/diagnostic.png +0 -0
- pyannote_metrics-4.0.0/doc/source/images/pipeline.png +0 -0
- pyannote_metrics-4.0.0/doc/source/images/segmentation.png +0 -0
- pyannote_metrics-4.0.0/doc/source/index.rst +65 -0
- pyannote_metrics-4.0.0/doc/source/pyplots/tutorial.py +29 -0
- pyannote_metrics-4.0.0/doc/source/reference.rst +184 -0
- pyannote_metrics-4.0.0/doc/source/tutorial.rst +44 -0
- pyannote_metrics-4.0.0/notebooks/.ipynb_checkpoints/Better purity metric-checkpoint.ipynb +6 -0
- pyannote_metrics-4.0.0/notebooks/.ipynb_checkpoints/Untitled-checkpoint.ipynb +132 -0
- pyannote_metrics-4.0.0/notebooks/Better purity metric.ipynb +436 -0
- pyannote_metrics-4.0.0/notebooks/Untitled.ipynb +133 -0
- pyannote_metrics-4.0.0/notebooks/index.ipynb +36 -0
- pyannote_metrics-4.0.0/notebooks/pyannote.metrics.diarization.ipynb +336 -0
- pyannote_metrics-4.0.0/notebooks/pyannote.metrics.identification.ipynb +253 -0
- pyannote_metrics-4.0.0/pyproject.toml +59 -0
- pyannote_metrics-4.0.0/setup.py +84 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/__init__.py +34 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/base.py +419 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/binary_classification.py +239 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/cli.py +713 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/detection.py +608 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/diarization.py +648 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/errors/__init__.py +29 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/errors/identification.py +332 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/errors/segmentation.py +109 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/identification.py +274 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/matcher.py +192 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/plot/__init__.py +27 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/plot/binary_classification.py +205 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/py.typed +0 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/segmentation.py +404 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/spotting.py +334 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/types.py +8 -0
- pyannote_metrics-4.0.0/src/pyannote/metrics/utils.py +225 -0
- pyannote_metrics-4.0.0/tests/test_detection.py +147 -0
- pyannote_metrics-4.0.0/tests/test_diarization.py +115 -0
- pyannote_metrics-4.0.0/tests/test_identification.py +73 -0
- pyannote_metrics-4.0.0/uv.lock +1750 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- master
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-and-deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
persist-credentials: false
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v5
|
|
18
|
+
with:
|
|
19
|
+
enable-cache: true
|
|
20
|
+
cache-dependency-glob: uv.lock
|
|
21
|
+
|
|
22
|
+
- name: Install the project
|
|
23
|
+
run: uv sync --extra doc
|
|
24
|
+
|
|
25
|
+
- name: Build documentation
|
|
26
|
+
run: |
|
|
27
|
+
make --directory=doc html
|
|
28
|
+
touch ./doc/build/html/.nojekyll
|
|
29
|
+
- name: Deploy
|
|
30
|
+
uses: peaceiris/actions-gh-pages@v3
|
|
31
|
+
with:
|
|
32
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
33
|
+
publish_dir: ./doc/build/html
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
|
|
2
|
+
|
|
3
|
+
on: push
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
name: Build distribution 📦
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
with:
|
|
13
|
+
persist-credentials: false
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v5
|
|
17
|
+
with:
|
|
18
|
+
enable-cache: true
|
|
19
|
+
cache-dependency-glob: uv.lock
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version-file: ".python-version"
|
|
24
|
+
- name: Build
|
|
25
|
+
run: uv build
|
|
26
|
+
- name: Store the distribution packages
|
|
27
|
+
uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: python-package-distributions
|
|
30
|
+
path: dist/
|
|
31
|
+
|
|
32
|
+
publish-to-pypi:
|
|
33
|
+
name: >-
|
|
34
|
+
Publish Python 🐍 distribution 📦 to PyPI
|
|
35
|
+
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
|
|
36
|
+
needs:
|
|
37
|
+
- build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment:
|
|
40
|
+
name: pypi
|
|
41
|
+
permissions:
|
|
42
|
+
id-token: write
|
|
43
|
+
steps:
|
|
44
|
+
- name: Download all the dists
|
|
45
|
+
uses: actions/download-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: python-package-distributions
|
|
48
|
+
path: dist/
|
|
49
|
+
- name: Install uv
|
|
50
|
+
uses: astral-sh/setup-uv@v5
|
|
51
|
+
with:
|
|
52
|
+
enable-cache: true
|
|
53
|
+
cache-dependency-glob: uv.lock
|
|
54
|
+
- name: Publish distribution 📦 to PyPI
|
|
55
|
+
run: uv publish --trusted-publishing always --publish-url https://upload.pypi.org/legacy/
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
github-release:
|
|
59
|
+
name: >-
|
|
60
|
+
Sign the Python 🐍 distribution 📦 with Sigstore
|
|
61
|
+
and upload them to GitHub Release
|
|
62
|
+
needs:
|
|
63
|
+
- publish-to-pypi
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
|
|
66
|
+
permissions:
|
|
67
|
+
contents: write # IMPORTANT: mandatory for making GitHub Releases
|
|
68
|
+
id-token: write # IMPORTANT: mandatory for sigstore
|
|
69
|
+
|
|
70
|
+
steps:
|
|
71
|
+
- name: Download all the dists
|
|
72
|
+
uses: actions/download-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
name: python-package-distributions
|
|
75
|
+
path: dist/
|
|
76
|
+
- name: Sign the dists with Sigstore
|
|
77
|
+
uses: sigstore/gh-action-sigstore-python@v3.0.0
|
|
78
|
+
with:
|
|
79
|
+
inputs: >-
|
|
80
|
+
./dist/*.tar.gz
|
|
81
|
+
./dist/*.whl
|
|
82
|
+
- name: Create GitHub Release
|
|
83
|
+
env:
|
|
84
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
85
|
+
run: >-
|
|
86
|
+
gh release create
|
|
87
|
+
"$GITHUB_REF_NAME"
|
|
88
|
+
--repo "$GITHUB_REPOSITORY"
|
|
89
|
+
--notes ""
|
|
90
|
+
- name: Upload artifact signatures to GitHub Release
|
|
91
|
+
env:
|
|
92
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
93
|
+
# Upload to GitHub Release using the `gh` CLI.
|
|
94
|
+
# `dist/` contains the built packages, and the
|
|
95
|
+
# sigstore-produced signatures and certificates.
|
|
96
|
+
run: >-
|
|
97
|
+
gh release upload
|
|
98
|
+
"$GITHUB_REF_NAME" dist/**
|
|
99
|
+
--repo "$GITHUB_REPOSITORY"
|
|
100
|
+
|
|
101
|
+
# publish-to-testpypi:
|
|
102
|
+
# name: Publish Python 🐍 distribution 📦 to TestPyPI
|
|
103
|
+
# needs:
|
|
104
|
+
# - build
|
|
105
|
+
# runs-on: ubuntu-latest
|
|
106
|
+
#
|
|
107
|
+
# environment:
|
|
108
|
+
# name: testpypi
|
|
109
|
+
#
|
|
110
|
+
# permissions:
|
|
111
|
+
# id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
112
|
+
#
|
|
113
|
+
# steps:
|
|
114
|
+
# - name: Download all the dists
|
|
115
|
+
# uses: actions/download-artifact@v4
|
|
116
|
+
# with:
|
|
117
|
+
# name: python-package-distributions
|
|
118
|
+
# path: dist/
|
|
119
|
+
# - name: Install uv
|
|
120
|
+
# uses: astral-sh/setup-uv@v5
|
|
121
|
+
# with:
|
|
122
|
+
# enable-cache: true
|
|
123
|
+
# cache-dependency-glob: uv.lock
|
|
124
|
+
# - name: Publish distribution 📦 to PyPI
|
|
125
|
+
# run: uv publish --trusted-publishing always --publish-url https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- develop
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- develop
|
|
10
|
+
- master
|
|
11
|
+
- release/*
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
name: Test
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
python-version:
|
|
21
|
+
- "3.10"
|
|
22
|
+
- "3.11"
|
|
23
|
+
- "3.12"
|
|
24
|
+
env:
|
|
25
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Install uv
|
|
30
|
+
uses: astral-sh/setup-uv@v5
|
|
31
|
+
|
|
32
|
+
- name: Install the project
|
|
33
|
+
run: uv sync --extra test
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
run: uv run pytest tests
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
|
|
5
|
+
# C extensions
|
|
6
|
+
*.so
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
MANIFEST
|
|
10
|
+
.Python
|
|
11
|
+
env/
|
|
12
|
+
bin/
|
|
13
|
+
build/
|
|
14
|
+
develop-eggs/
|
|
15
|
+
dist/
|
|
16
|
+
eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
|
|
26
|
+
# Installer logs
|
|
27
|
+
pip-log.txt
|
|
28
|
+
pip-delete-this-directory.txt
|
|
29
|
+
|
|
30
|
+
# Unit test / coverage reports
|
|
31
|
+
htmlcov/
|
|
32
|
+
.tox/
|
|
33
|
+
.coverage
|
|
34
|
+
.cache
|
|
35
|
+
nosetests.xml
|
|
36
|
+
coverage.xml
|
|
37
|
+
|
|
38
|
+
# Translations
|
|
39
|
+
*.mo
|
|
40
|
+
|
|
41
|
+
# Mr Developer
|
|
42
|
+
.mr.developer.cfg
|
|
43
|
+
.project
|
|
44
|
+
.pydevproject
|
|
45
|
+
|
|
46
|
+
# Rope
|
|
47
|
+
.ropeproject
|
|
48
|
+
|
|
49
|
+
# Django stuff:
|
|
50
|
+
*.log
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Sphinx documentation
|
|
54
|
+
docs/_build/
|
|
55
|
+
|
|
56
|
+
doc/.ipynb_checkpoints
|
|
57
|
+
|
|
58
|
+
.mypy_cache/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
#### Description
|
|
3
|
+
<!-- Example: DiarizationPurity does not return the expected value -->
|
|
4
|
+
|
|
5
|
+
#### Steps/Code to Reproduce
|
|
6
|
+
<!--
|
|
7
|
+
Example:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
from pyannote.core import Annotation, Segment
|
|
11
|
+
reference = Annotation()
|
|
12
|
+
reference[Segment(0, 10)] = 'A'
|
|
13
|
+
reference[Segment(12, 20)] = 'B'
|
|
14
|
+
reference[Segment(24, 27)] = 'A'
|
|
15
|
+
reference[Segment(30, 40)] = 'C'
|
|
16
|
+
hypothesis = Annotation()
|
|
17
|
+
hypothesis[Segment(2, 13)] = 'a'
|
|
18
|
+
hypothesis[Segment(13, 14)] = 'd'
|
|
19
|
+
hypothesis[Segment(14, 20)] = 'b'
|
|
20
|
+
hypothesis[Segment(22, 38)] = 'c'
|
|
21
|
+
hypothesis[Segment(38, 40)] = 'd'
|
|
22
|
+
|
|
23
|
+
from pyannote.metrics.diarization import DiarizationPurity
|
|
24
|
+
purity = DiarizationPurity()
|
|
25
|
+
print "Purity = {0:.3f}".format(purity(reference, hypothesis))
|
|
26
|
+
```
|
|
27
|
+
If the code is too long, feel free to put it in a public gist and link
|
|
28
|
+
it in the issue: https://gist.github.com
|
|
29
|
+
-->
|
|
30
|
+
|
|
31
|
+
#### Expected Results
|
|
32
|
+
<!-- Example: Output should be "Purity = 0.667". Please paste or describe the expected results.-->
|
|
33
|
+
|
|
34
|
+
#### Actual Results
|
|
35
|
+
<!-- Please paste or specifically describe the actual output or traceback. -->
|
|
36
|
+
|
|
37
|
+
#### Versions
|
|
38
|
+
<!-- Please paste the output of `pip freeze | grep pyannote` below -->
|
|
39
|
+
|
|
40
|
+
<!-- Thanks for contributing! -->
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 CNRS (Hervé BREDIN - http://herve.niderb.fr)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyannote-metrics
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: A toolkit for reproducible evaluation, diagnostic, and error analysis of speaker diarization systems
|
|
5
|
+
Author-email: Hervé BREDIN <herve@pyannote.ai>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: numpy>=2.2.2
|
|
9
|
+
Requires-Dist: pandas>=2.2.3
|
|
10
|
+
Requires-Dist: pyannote-core>=6.0.0
|
|
11
|
+
Requires-Dist: pyannote-database>=6.0.0
|
|
12
|
+
Requires-Dist: scikit-learn>=1.6.1
|
|
13
|
+
Requires-Dist: scipy>=1.15.1
|
|
14
|
+
Provides-Extra: cli
|
|
15
|
+
Requires-Dist: docopt>=0.6.2; extra == 'cli'
|
|
16
|
+
Requires-Dist: sympy>=1.13.3; extra == 'cli'
|
|
17
|
+
Requires-Dist: tabulate>=0.9.0; extra == 'cli'
|
|
18
|
+
Provides-Extra: doc
|
|
19
|
+
Requires-Dist: ipython>=8.32.0; extra == 'doc'
|
|
20
|
+
Requires-Dist: matplotlib>=3.10.0; extra == 'doc'
|
|
21
|
+
Requires-Dist: sphinx-rtd-theme>=3.0.2; extra == 'doc'
|
|
22
|
+
Requires-Dist: sphinx>=8.1.3; extra == 'doc'
|
|
23
|
+
Provides-Extra: plot
|
|
24
|
+
Requires-Dist: matplotlib>=3.10.0; extra == 'plot'
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest>=8.3.4; extra == 'test'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# pyannote.metrics
|
|
30
|
+
|
|
31
|
+
> a toolkit for reproducible evaluation, diagnostic, and error analysis of speaker diarization systems
|
|
32
|
+
|
|
33
|
+
An overview of `pyannote.metrics` is available as an [InterSpeech 2017 paper](docs/pyannote-metrics.pdf): it is recommended to read it first, to quickly get an idea whether this tool is for you.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
$ pip install pyannote.metrics
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
The documentation is available at [http://pyannote.github.io/pyannote-metrics](http://pyannote.github.io/pyannote-metrics).
|
|
44
|
+
|
|
45
|
+
Sample notebooks are available [here](http://nbviewer.ipython.org/github/pyannote/pyannote-metrics/blob/master/notebooks/index.ipynb).
|
|
46
|
+
|
|
47
|
+
## Citation
|
|
48
|
+
|
|
49
|
+
If you use `pyannote.metrics` in your research, please use the following citation:
|
|
50
|
+
|
|
51
|
+
```bibtex
|
|
52
|
+
@inproceedings{pyannote.metrics,
|
|
53
|
+
author = {Herv\'e Bredin},
|
|
54
|
+
title = {{pyannote.metrics: a toolkit for reproducible evaluation, diagnostic, and error analysis of speaker diarization systems}},
|
|
55
|
+
booktitle = {{Interspeech 2017, 18th Annual Conference of the International Speech Communication Association}},
|
|
56
|
+
year = {2017},
|
|
57
|
+
month = {August},
|
|
58
|
+
address = {Stockholm, Sweden},
|
|
59
|
+
url = {http://pyannote.github.io/pyannote-metrics},
|
|
60
|
+
}
|
|
61
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# pyannote.metrics
|
|
2
|
+
|
|
3
|
+
> a toolkit for reproducible evaluation, diagnostic, and error analysis of speaker diarization systems
|
|
4
|
+
|
|
5
|
+
An overview of `pyannote.metrics` is available as an [InterSpeech 2017 paper](docs/pyannote-metrics.pdf): it is recommended to read it first, to quickly get an idea whether this tool is for you.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
$ pip install pyannote.metrics
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Documentation
|
|
14
|
+
|
|
15
|
+
The documentation is available at [http://pyannote.github.io/pyannote-metrics](http://pyannote.github.io/pyannote-metrics).
|
|
16
|
+
|
|
17
|
+
Sample notebooks are available [here](http://nbviewer.ipython.org/github/pyannote/pyannote-metrics/blob/master/notebooks/index.ipynb).
|
|
18
|
+
|
|
19
|
+
## Citation
|
|
20
|
+
|
|
21
|
+
If you use `pyannote.metrics` in your research, please use the following citation:
|
|
22
|
+
|
|
23
|
+
```bibtex
|
|
24
|
+
@inproceedings{pyannote.metrics,
|
|
25
|
+
author = {Herv\'e Bredin},
|
|
26
|
+
title = {{pyannote.metrics: a toolkit for reproducible evaluation, diagnostic, and error analysis of speaker diarization systems}},
|
|
27
|
+
booktitle = {{Interspeech 2017, 18th Annual Conference of the International Speech Communication Association}},
|
|
28
|
+
year = {2017},
|
|
29
|
+
month = {August},
|
|
30
|
+
address = {Stockholm, Sweden},
|
|
31
|
+
url = {http://pyannote.github.io/pyannote-metrics},
|
|
32
|
+
}
|
|
33
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Minimal makefile for Sphinx documentation
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
# You can set these variables from the command line.
|
|
5
|
+
SPHINXOPTS =
|
|
6
|
+
SPHINXBUILD = sphinx-build
|
|
7
|
+
SPHINXPROJ = pyannotemetrics
|
|
8
|
+
SOURCEDIR = source
|
|
9
|
+
BUILDDIR = build
|
|
10
|
+
|
|
11
|
+
# Put it first so that "make" without argument is like "make help".
|
|
12
|
+
help:
|
|
13
|
+
uv run --extra doc $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
14
|
+
|
|
15
|
+
.PHONY: help Makefile
|
|
16
|
+
|
|
17
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
18
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
19
|
+
%: Makefile
|
|
20
|
+
uv run --extra doc $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
Binary file
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Principles
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
`pyannote.metrics` provides a set of classes to compare the output of speaker diarization (hereafter called `hypothesis`) systems to manual annotations (`reference`). Let us first instantiate a sample `reference` and `hypothesis`.
|
|
5
|
+
|
|
6
|
+
.. ipython::
|
|
7
|
+
|
|
8
|
+
In [10]: from pyannote.core import Segment, Timeline, Annotation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
In [11]: reference = Annotation(uri='file1')
|
|
12
|
+
....: reference[Segment(0, 10)] = 'A'
|
|
13
|
+
....: reference[Segment(12, 20)] = 'B'
|
|
14
|
+
....: reference[Segment(24, 27)] = 'A'
|
|
15
|
+
....: reference[Segment(30, 40)] = 'C'
|
|
16
|
+
|
|
17
|
+
In [12]: hypothesis = Annotation(uri='file1')
|
|
18
|
+
....: hypothesis[Segment(2, 13)] = 'a'
|
|
19
|
+
....: hypothesis[Segment(13, 14)] = 'd'
|
|
20
|
+
....: hypothesis[Segment(14, 20)] = 'b'
|
|
21
|
+
....: hypothesis[Segment(22, 38)] = 'c'
|
|
22
|
+
....: hypothesis[Segment(38, 40)] = 'd'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
.. plot:: pyplots/tutorial.py
|
|
26
|
+
|
|
27
|
+
This basically tells us that, according to the manual annotation, speaker `A` speaks in timeranges [0s, 10s] and [24s, 27s].
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
.. note::
|
|
31
|
+
|
|
32
|
+
Overlapping segments are supported. See :mod:`pyannote.core` documentation for more details.
|
|
33
|
+
|
|
34
|
+
`pyannote.metrics` follows an object-oriented paradigm.
|
|
35
|
+
Most evaluation metrics (e.g. :class:`DiarizationErrorRate` below) inherit from :class:`BaseMetric`.
|
|
36
|
+
As such, they share a common set of methods.
|
|
37
|
+
|
|
38
|
+
For instance, once instantiated, they can be called directly to compute the value of the evaluation metric.
|
|
39
|
+
|
|
40
|
+
.. ipython::
|
|
41
|
+
:okwarning:
|
|
42
|
+
|
|
43
|
+
In [10]: from pyannote.metrics.diarization import DiarizationErrorRate
|
|
44
|
+
|
|
45
|
+
In [1]: metric = DiarizationErrorRate()
|
|
46
|
+
|
|
47
|
+
In [1]: metric(reference, hypothesis)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
Accumulation & reporting
|
|
51
|
+
------------------------
|
|
52
|
+
|
|
53
|
+
The same metric instance can be used to evaluate multiple files.
|
|
54
|
+
|
|
55
|
+
.. ipython::
|
|
56
|
+
:okwarning:
|
|
57
|
+
|
|
58
|
+
In [11]: other_reference = Annotation(uri='file2')
|
|
59
|
+
....: other_reference[Segment(0, 5)] = 'A'
|
|
60
|
+
....: other_reference[Segment(6, 10)] = 'B'
|
|
61
|
+
....: other_reference[Segment(12, 13)] = 'B'
|
|
62
|
+
....: other_reference[Segment(15, 20)] = 'A'
|
|
63
|
+
|
|
64
|
+
In [12]: other_hypothesis = Annotation(uri='file2')
|
|
65
|
+
....: other_hypothesis[Segment(1, 6)] = 'a'
|
|
66
|
+
....: other_hypothesis[Segment(6, 7)] = 'b'
|
|
67
|
+
....: other_hypothesis[Segment(7, 10)] = 'c'
|
|
68
|
+
....: other_hypothesis[Segment(11, 19)] = 'b'
|
|
69
|
+
....: other_hypothesis[Segment(19, 20)] = 'a'
|
|
70
|
+
|
|
71
|
+
In [12]: metric = DiarizationErrorRate()
|
|
72
|
+
|
|
73
|
+
In [12]: metric(reference, hypothesis)
|
|
74
|
+
|
|
75
|
+
In [12]: metric(other_reference, other_hypothesis)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
You do not need to keep track of the result of each call yourself: this is done automatically.
|
|
79
|
+
For instance, once you have evaluated all files, you can use the overriden :func:`~pyannote.metrics.base.BaseMetric.__abs__` operator to get the accumulated value:
|
|
80
|
+
|
|
81
|
+
.. ipython::
|
|
82
|
+
|
|
83
|
+
In [12]: abs(metric)
|
|
84
|
+
|
|
85
|
+
:func:`~pyannote.metrics.base.BaseMetric.report` provides a convenient summary of the result:
|
|
86
|
+
|
|
87
|
+
.. ipython::
|
|
88
|
+
|
|
89
|
+
In [12]: report = metric.report(display=True)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
The internal accumulator can be reset using the :func:`~pyannote.metrics.base.BaseMetric.report` method:
|
|
93
|
+
|
|
94
|
+
.. ipython::
|
|
95
|
+
|
|
96
|
+
In [12]: metric.reset()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
Evaluation map
|
|
100
|
+
--------------
|
|
101
|
+
|
|
102
|
+
Though audio files can always be processed entirely (from beginning to end), there are cases where reference annotations are only available for some regions of the audio files.
|
|
103
|
+
All metrics support the provision of an evaluation map that indicate which part of the audio file should be evaluated.
|
|
104
|
+
|
|
105
|
+
.. ipython::
|
|
106
|
+
|
|
107
|
+
In [2]: uem = Timeline([Segment(0, 10), Segment(15, 20)])
|
|
108
|
+
|
|
109
|
+
In [2]: metric(reference, hypothesis, uem=uem)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
Components
|
|
113
|
+
----------
|
|
114
|
+
|
|
115
|
+
Most metrics are computed as the combination of several components.
|
|
116
|
+
For instance, the diarization error rate is the combination of false alarm (non-speech regions classified as speech), missed detection (speech regions classified as non-speech) and confusion between speakers.
|
|
117
|
+
|
|
118
|
+
Using ``detailed=True`` will return the value of each component:
|
|
119
|
+
|
|
120
|
+
.. ipython::
|
|
121
|
+
:okwarning:
|
|
122
|
+
|
|
123
|
+
In [13]: metric(reference, hypothesis, detailed=True)
|
|
124
|
+
|
|
125
|
+
The accumulated value of each component can also be obtained using the overriden :func:`~pyannote.metrics.base.BaseMetric.__getitem__` operator:
|
|
126
|
+
|
|
127
|
+
.. ipython::
|
|
128
|
+
:okwarning:
|
|
129
|
+
|
|
130
|
+
In [13]: metric(other_reference, other_hypothesis)
|
|
131
|
+
|
|
132
|
+
In [13]: metric['confusion']
|
|
133
|
+
|
|
134
|
+
In [13]: metric[:]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
Define your own metric
|
|
138
|
+
----------------------
|
|
139
|
+
|
|
140
|
+
It is possible (and encouraged) to develop and contribute new evaluation metrics.
|
|
141
|
+
|
|
142
|
+
All you have to do is inherit from :class:`BaseMetric` and implement a few methods:
|
|
143
|
+
``metric_name``, ``metric_components``, ``compute_components``, and ``compute_metric``:
|
|
144
|
+
|
|
145
|
+
.. code-block:: python
|
|
146
|
+
|
|
147
|
+
def is_male(speaker_name):
|
|
148
|
+
# black magic that returns True if speaker is a man, False otherwise
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
class MyMetric(BaseMetric):
|
|
152
|
+
# This dummy metric computes the ratio between male and female speakers.
|
|
153
|
+
# It does not actually use the reference annotation...
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def metric_name(cls):
|
|
157
|
+
# Return human-readable name of the metric
|
|
158
|
+
|
|
159
|
+
return 'male / female ratio'
|
|
160
|
+
|
|
161
|
+
@classmethod:
|
|
162
|
+
def metric_components(cls):
|
|
163
|
+
# Return component names from which the metric is computed
|
|
164
|
+
|
|
165
|
+
return ['male', 'female']
|
|
166
|
+
|
|
167
|
+
def compute_components(self, reference, hypothesis, **kwargs):
|
|
168
|
+
# Actually compute the value of each component
|
|
169
|
+
|
|
170
|
+
components = {'male': 0., 'female': 0.}
|
|
171
|
+
|
|
172
|
+
for segment, _, speaker_name in hypothesis.itertracks(yield_label=True):
|
|
173
|
+
if is_male(speaker_name):
|
|
174
|
+
components['male'] += segment.duration
|
|
175
|
+
else:
|
|
176
|
+
components['female'] += segment.duration
|
|
177
|
+
|
|
178
|
+
return components
|
|
179
|
+
|
|
180
|
+
def compute_metric(self, components):
|
|
181
|
+
# Actually compute the metric based on the component values
|
|
182
|
+
|
|
183
|
+
return components['male'] / components['female']
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
See :class:`pyannote.metrics.base.BaseMetric` for more details.
|