{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#collapse\n", "\n", "# setting things for pretty visualization\n", "\n", "from rich import print\n", "from pyannote.core import notebook, Segment\n", "SAMPLE_EXTENT = Segment(0, 30)\n", "notebook.crop = SAMPLE_EXTENT\n", "\n", "SAMPLE_CHUNK = Segment(15, 20)\n", "SAMPLE_URI = \"sample\"\n", "SAMPLE_WAV = f\"{SAMPLE_URI}.wav\"\n", "SAMPLE_REF = f\"{SAMPLE_URI}.rttm\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# One speaker segmentation model to rule them all" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this blog post, I talk about pyannote.audio [pretrained speaker segmentation model](https://hf.co/pyannote/segmentation), which happens to be one of the [most popular audio model](https://huggingface.co/models?other=audio) available on 🤗 Huggingface model hub. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pyannote.audio import Model\n", "model = Model.from_pretrained(\"pyannote/segmentation\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What does `pyannote/segmentation` do?\n", "\n", "Every `pyannote.audio` model has a `specifications` attribute that tells us a bit more about itself:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Specifications(\n", " problem=<Problem.MULTI_LABEL_CLASSIFICATION: 2>,\n", " resolution=<Resolution.FRAME: 1>,\n", " duration=5.0,\n", " warm_up=(0.0, 0.0),\n", " classes=['speaker#1', 'speaker#2', 'speaker#3'],\n", " permutation_invariant=True\n", ")\n", "\n" ], "text/plain": [ "\u001b[1;35mSpecifications\u001b[0m\u001b[1m(\u001b[0m\n", " \u001b[33mproblem\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mProblem.MULTI_LABEL_CLASSIFICATION:\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m2\u001b[0m\u001b[1m>\u001b[0m,\n", " \u001b[33mresolution\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mResolution.FRAME:\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m1\u001b[0m\u001b[1m>\u001b[0m,\n", " \u001b[33mduration\u001b[0m=\u001b[1;36m5\u001b[0m\u001b[1;36m.0\u001b[0m,\n", " \u001b[33mwarm_up\u001b[0m=\u001b[1m(\u001b[0m\u001b[1;36m0.0\u001b[0m, \u001b[1;36m0.0\u001b[0m\u001b[1m)\u001b[0m,\n", " \u001b[33mclasses\u001b[0m=\u001b[1m[\u001b[0m\u001b[32m'speaker#1'\u001b[0m, \u001b[32m'speaker#2'\u001b[0m, \u001b[32m'speaker#3'\u001b[0m\u001b[1m]\u001b[0m,\n", " \u001b[33mpermutation_invariant\u001b[0m=\u001b[3;92mTrue\u001b[0m\n", "\u001b[1m)\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "print(model.specifications)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These specifications tell us the following about `pyannote/segmentation`:\n", "\n", "* it ingests audio chunks of 5 seconds `duration`\n", "* it addresses a multi-label classification `problem`...\n", "* ... whose possible `classes` are chosen among `speaker#1`, `speaker#2`, and `speaker#3` ...\n", "* ... and are `permutation_invariant` (more about that below)\n", "\n", "We also learn that its output temporal `resolution` is the frame (_i.e._ it outputs a sequence of frame-wise decisions rather than just one decision for the whole chunk). The actual temporal resolution can be obtained through the magic `introspection` attribute (approximately 17ms for `pyannote/segmentation`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.016875" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model.introspection.frames.step" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## OK, but what does `pyannote/segmentation` really do? \n", "\n", "To answer this question, let us consider the audio recording of a 30s conversation between two speakers (the blue one and the red one):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABi8AAADyCAYAAAA1MlYeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdwUlEQVR4nO3dfZBV9X0/8PeCgDzsrjwtC/JQqsgQJSrSUTQViBFlTEY0aRFTfxDSGIqmohPNRNOI0UDVStpqS2xSk2qTAE0UMw1GmSCgZWTUhmqTjA1GBAO4SoVdCS7K3t8fjtsQTAxwl3t29/Wa2Zm95977PZ9777nf/dx933NOValUKgUAAAAAAKAgulS6AAAAAAAAgF8nvAAAAAAAAApFeAEAAAAAABSK8AIAAAAAACgU4QUAAAAAAFAowgsAAAAAAKBQhBcAAAAAAEChCC8AAAAAAIBCEV4AAAAAAACFIrwAAAAAAAAKRXgBAAAAAAAUivACAAAAAAAoFOEFAAAAAABQKMILAAAAAACgUIQXAAAAAABAoQgvKmDWrFmZNm1apcugHbCtAAAAAACdkfCiA2lqasq8efMyYsSI9OzZM2eeeWaefPLJ/W5TKpUyf/78DBkyJD179sykSZPyk5/8pEIVUym/z7Zy//3357zzzsuAAQNSVVWVDRs2VKZYAAAAAKDTOarcA+7bsaPcQ/5OXfv3P6LrK6I333wz3bp1y5//+Z/nv//7v3PfffdlyJAh+dd//dd86EMfyk9/+tMce+yxSZLbbrstixYtyje/+c2ccMIJueWWW3LuuefmueeeS3V19RGv/bXde4/o+vr27n5E11c0B7Ot7N69O2eddVb+5E/+JJ/61KcqXDkAAAAA0JlUlUqlUjkH/OWxw8o53Hs69pdbDvo+3/3ud3PTTTdl48aN6dWrV0499dQ8+OCDueKKK7Jz586ceuqp+Yd/+Ie88cYbmTFjRu6888507/72P71LpVJuv/32fPWrX822bdtywgkn5K/+6q/ysY99LEmyb9++XH755Vm1alW2b9+e4cOHZ+7cubnqqqta1z9r1qzs3Lkzy5cvT5I8/fTTmTp1aq666qrccMMN2bVrV6699tosX748b7zxRsaPH5+vfOUrOfnkk5Mk8+fPz/Lly/OXf/mXueWWW7Jp06bs3r071dXVefDBB3PBBRe0ruuUU07Jhz/84dxyyy0plUoZMmRI5s2bl8997nNJkubm5gwaNCi33nprPv3pTx/Sa3A4zrjx4SO6viduOu+gbt9Zt5Vft2nTpowcOTI//vGPc8oppxzsUw4AAAAAcNDKvudF0W3bti0zZszIbbfdlosuuihNTU157LHH8k6G86Mf/ShHH310Hn300WzatCmf+MQnMmDAgHz5y19OknzhC1/I/fffn8WLF2fUqFFZu3Zt/uzP/iwDBw7MxIkT09LSkqFDh2bZsmUZMGBA1q1bl8svvzyDBw/On/7pnx5Qz+rVqzNt2rQsXLgwf/EXf5FSqZQLLrgg/fr1y4oVK1JbW5u7774755xzTv7nf/4n/fr1S5Js3Lgxy5Yty/e+97107do1b731Vvbt25ejjz56v/F79uyZxx9/PEnywgsvZPv27ZkyZUrr9T169MjEiROzbt26ioQXRdaZtxUAAAAAgErqlOHFW2+9lYsvvjgjRoxIkowdO7b1+u7du+eee+5Jr169cuKJJ+ZLX/pSrr322tx8883Zs2dPFi1alFWrVmXChAlJkj/8wz/M448/nrvvvjsTJ05Mt27dctNNN7WON3LkyKxbty7Lli074B/SDz74YC677LLcfffdmTFjRpLk0UcfzbPPPpuGhob06NEjSfI3f/M3Wb58eb773e/m8ssvT5Ls3bs39913XwYOHNg63oQJE3LzzTdnzJgxGTRoUL7zne9k/fr1GTVqVJJk+/btSZJBgwbtV8egQYPy4osvHv6T28F05m0FAAAAAKCSOl14cfLJJ+ecc87J2LFjc95552XKlCn52Mc+lr59+7Ze36tXr9bbT5gwIa+//nq2bNmShoaGvPHGGzn33HP3G3Pv3r059dRTWy9/9atfzde//vW8+OKL2bNnT/bu3XvA4XbWr1+ff//3f8+//du/5aKLLmpd/vTTT+f1119P/984l8eePXvy/PPPt14eMWLEfv+MTpL77rsvs2fPzrHHHpuuXbtm3LhxufTSS/Of//mf+92uqqpqv8ulUumAZdhWAAAAAAAqpezhRf0zG8o9ZFl17do1K1euzLp16/LII4/kzjvvzA033JD169f/zvtVVVWlpaUlSfKDH/yg9aTG73jnm+/Lli3L1VdfnTvuuCMTJkxIdXV1br/99gPGP+6449K/f//cc889ueCCC1rPk9DS0pLBgwdn9erV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\n", "text/plain": [ "