aaahmet's picture
tokenizer from mirror (fallback upstream)
50e8e3d verified
Raw
History Blame Contribute Delete
5.48 kB
import os
import random
import sentencepiece
import torch
import torchaudio
import torchvision
from huggingface_hub import hf_hub_download
NOISE_FILENAME = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "babble_noise.wav"
)
def _dl(filename):
# prefer our self-contained public mirror; fall back to upstream
for repo in ("aaahmet/silent-lip-reader-model", "AD1TEYA/lip-reading-model"):
try:
return hf_hub_download(repo_id=repo, filename=filename)
except Exception:
continue
raise RuntimeError(f"could not download {filename}")
SP_MODEL_PATH = _dl("unigram5000.model")
DICT_PATH = _dl("unigram5000_units.txt")
class FunctionalModule(torch.nn.Module):
def __init__(self, functional):
super().__init__()
self.functional = functional
def forward(self, input):
return self.functional(input)
class AdaptiveTimeMask(torch.nn.Module):
def __init__(self, window, stride):
super().__init__()
self.window = window
self.stride = stride
def forward(self, x):
# x: [T, ...]
cloned = x.clone()
length = cloned.size(0)
n_mask = int((length + self.stride - 0.1) // self.stride)
ts = torch.randint(0, self.window, size=(n_mask, 2))
for t, t_end in ts:
if length - t <= 0:
continue
t_start = random.randrange(0, length - t)
if t_start == t_start + t:
continue
t_end += t_start
cloned[t_start:t_end] = 0
return cloned
class AddNoise(torch.nn.Module):
def __init__(
self,
noise_filename=NOISE_FILENAME,
snr_target=None,
):
super().__init__()
self.snr_levels = [snr_target] if snr_target else [-5, 0, 5, 10, 15, 20, 999999]
self.noise, sample_rate = torchaudio.load(noise_filename)
assert sample_rate == 16000
def forward(self, speech):
# speech: T x 1
# return: T x 1
speech = speech.t()
start_idx = random.randint(0, self.noise.shape[1] - speech.shape[1])
noise_segment = self.noise[:, start_idx : start_idx + speech.shape[1]]
snr_level = torch.tensor([random.choice(self.snr_levels)])
noisy_speech = torchaudio.functional.add_noise(speech, noise_segment, snr_level)
return noisy_speech.t()
class VideoTransform:
def __init__(self, subset):
if subset == "train":
self.video_pipeline = torch.nn.Sequential(
FunctionalModule(lambda x: x / 255.0),
torchvision.transforms.RandomCrop(88),
torchvision.transforms.Grayscale(),
AdaptiveTimeMask(10, 25),
torchvision.transforms.Normalize(0.421, 0.165),
)
elif subset == "val" or subset == "test":
self.video_pipeline = torch.nn.Sequential(
FunctionalModule(lambda x: x / 255.0),
torchvision.transforms.CenterCrop(88),
torchvision.transforms.Grayscale(),
torchvision.transforms.Normalize(0.421, 0.165),
)
def __call__(self, sample):
# sample: T x C x H x W
# rtype: T x 1 x H x W
return self.video_pipeline(sample)
class AudioTransform:
def __init__(self, subset, snr_target=None):
if subset == "train":
self.audio_pipeline = torch.nn.Sequential(
AdaptiveTimeMask(6400, 16000),
AddNoise(),
FunctionalModule(
lambda x: torch.nn.functional.layer_norm(x, x.shape, eps=1e-8)
),
)
elif subset == "val" or subset == "test":
self.audio_pipeline = torch.nn.Sequential(
AddNoise(snr_target=snr_target)
if snr_target is not None
else FunctionalModule(lambda x: x),
FunctionalModule(
lambda x: torch.nn.functional.layer_norm(x, x.shape, eps=1e-8)
),
)
def __call__(self, sample):
# sample: T x 1
# rtype: T x 1
return self.audio_pipeline(sample)
class TextTransform:
"""Mapping Dictionary Class for SentencePiece tokenization."""
def __init__(
self,
sp_model_path=SP_MODEL_PATH,
dict_path=DICT_PATH,
):
# Load SentencePiece model
self.spm = sentencepiece.SentencePieceProcessor(model_file=sp_model_path)
# Load units and create dictionary
units = open(dict_path, encoding="utf8").read().splitlines()
self.hashmap = {unit.split()[0]: unit.split()[-1] for unit in units}
# 0 will be used for "blank" in CTC
self.token_list = ["<blank>"] + list(self.hashmap.keys()) + ["<eos>"]
self.ignore_id = -1
def tokenize(self, text):
tokens = self.spm.EncodeAsPieces(text)
token_ids = [self.hashmap.get(token, self.hashmap["<unk>"]) for token in tokens]
return torch.tensor(list(map(int, token_ids)))
def post_process(self, token_ids):
token_ids = token_ids[token_ids != -1]
text = self._ids_to_str(token_ids, self.token_list)
text = text.replace("\u2581", " ").strip()
return text
def _ids_to_str(self, token_ids, char_list):
token_as_list = [char_list[idx] for idx in token_ids]
return "".join(token_as_list).replace("<space>", " ")