File size: 5,280 Bytes
9eb8ea2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ddf7f94
 
 
 
 
 
 
 
 
 
 
 
 
9eb8ea2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
{
 "nbformat": 4,
 "nbformat_minor": 5,
 "metadata": {
  "colab": {
   "provenance": [],
   "gpuType": "T4"
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  },
  "accelerator": "GPU"
 },
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PakUrdu-Conversational-ASR \u2014 full-quality inference\n",
    "\n",
    "Run the **full r14 model** (merged adapter, fp16) on a **free GPU** \u2014 nothing installs on your machine.\n",
    "\n",
    "**Setup (one click):** enable a GPU accelerator before running:\n",
    "- **Colab:** `Runtime \u2192 Change runtime type \u2192 T4 GPU`\n",
    "- **Kaggle:** `Settings \u2192 Accelerator \u2192 GPU T4 x2` (or P100)\n",
    "\n",
    "Then upload an Urdu audio clip (wav/mp3/m4a\u2026) and run the cells top to bottom.\n",
    "\n",
    "More: [model card](https://huggingface.co/KhiredNetworks/PakUrdu-Conversational-ASR) \u00b7 ",
    "[browser demo](https://huggingface.co/spaces/KhiredNetworks/PakUrdu-ASR-Demo) (runs on your device, no GPU needed) \u00b7 ",
    "[proof notebook](https://www.kaggle.com/code/adeelejaz8611/e1-sr-proof-r14-sr133) (re-derives the certified 7.96% WER end-to-end)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1 \u2014 Load the model (base + fine-tuned adapter, merged)\n",
    "\n",
    "Downloads ~1.6 GB once per session; takes a couple of minutes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# One-time environment fix \u2014 some cloud images ship torchao 0.10, which peft's\n",
    "# import check rejects. torchao is only needed for torchao-quantized adapters\n",
    "# (not this one), so removing it is safe and instant.\n",
    "%pip uninstall -y -q torchao\n",
    "print(\"environment ok \u2014 continue below\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch, torchaudio\n",
    "from peft import PeftModel\n",
    "from transformers import WhisperForConditionalGeneration, WhisperProcessor\n",
    "\n",
    "BASE = \"openai/whisper-large-v3-turbo\"\n",
    "ADAPTER = \"KhiredNetworks/PakUrdu-Conversational-ASR\"\n",
    "\n",
    "processor = WhisperProcessor.from_pretrained(BASE)\n",
    "model = WhisperForConditionalGeneration.from_pretrained(BASE, torch_dtype=torch.float16)\n",
    "model = PeftModel.from_pretrained(model, ADAPTER).merge_and_unload().to(\"cuda\").eval()\n",
    "print(\"model ready \u2014 full fp16 r14 (merged adapter)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 \u2014 Point it at your audio\n",
    "\n",
    "How to get your clip in:\n",
    "- **Colab:** folder icon (left) \u2192 Upload, or run `from google.colab import files; files.upload()`\n",
    "- **Kaggle:** `+ Add Input \u2192 Upload` (attach the clip as a dataset); the path looks like `/kaggle/input/<your-dataset>/clip.wav`\n",
    "\n",
    "Then set the path below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "AUDIO_PATH = \"your_audio.wav\"   # <- set this to your clip's path"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3 \u2014 Transcribe\n",
    "Greedy decode, `language=ur` (the intended configuration). Clips longer than 30 s are\n",
    "windowed (25 s windows, 1.5 s overlap) and stitched, so long audio works too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SR = 16000\n",
    "\n",
    "def merge_texts(parts):\n",
    "    # drop the duplicated overlap words at each window seam\n",
    "    out = []\n",
    "    for p in parts:\n",
    "        words = (p or \"\").strip().split()\n",
    "        drop = 0\n",
    "        for k in range(min(len(out), len(words), 12), 0, -1):\n",
    "            if out[len(out) - k:] == words[:k]:\n",
    "                drop = k\n",
    "                break\n",
    "        out.extend(words[drop:])\n",
    "    return \" \".join(out)\n",
    "\n",
    "@torch.no_grad()\n",
    "def transcribe(path):\n",
    "    speech, sr = torchaudio.load(path)          # (channels, samples)\n",
    "    speech = speech.mean(dim=0)\n",
    "    if sr != SR:\n",
    "        speech = torchaudio.functional.resample(speech, sr, SR)\n",
    "    texts, start, CHUNK, OVERLAP = [], 0, 25 * SR, int(1.5 * SR)\n",
    "    while start < len(speech):\n",
    "        end = min(start + CHUNK, len(speech))\n",
    "        feats = processor(speech[start:end].numpy(), sampling_rate=SR,\n",
    "                          return_tensors=\"pt\").input_features.half().to(\"cuda\")\n",
    "        ids = model.generate(feats, language=\"ur\", task=\"transcribe\", do_sample=False)\n",
    "        texts.append(processor.batch_decode(ids, skip_special_tokens=True)[0].strip())\n",
    "        if end == len(speech):\n",
    "            break\n",
    "        start = end - OVERLAP\n",
    "    return merge_texts(texts)\n",
    "\n",
    "print(transcribe(AUDIO_PATH))"
   ]
  }
 ]
}