chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import math
|
||||
import numpy as np
|
||||
import tqdm
|
||||
import torch
|
||||
from shutil import copyfile
|
||||
|
||||
from npy_append_array import NpyAppendArray
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="transforms features via a given pca and stored them in target dir"
|
||||
)
|
||||
# fmt: off
|
||||
parser.add_argument('source', help='directory with features')
|
||||
parser.add_argument('--split', help='which split to read', required=True)
|
||||
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
||||
parser.add_argument('--pca-path', type=str, help='pca location. will append _A.npy and _b.npy', required=True)
|
||||
parser.add_argument('--batch-size', type=int, default=2048000, help='batch size')
|
||||
parser.add_argument('--unfiltered', action='store_true', help='process the unfiltered version')
|
||||
# fmt: on
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
source_path = osp.join(args.source, args.split)
|
||||
data_poth = source_path + "_unfiltered" if args.unfiltered else source_path
|
||||
|
||||
print(f"data path: {data_poth}")
|
||||
|
||||
features = np.load(data_poth + ".npy", mmap_mode="r")
|
||||
pca_A = torch.from_numpy(np.load(args.pca_path + "_A.npy")).cuda()
|
||||
pca_b = torch.from_numpy(np.load(args.pca_path + "_b.npy")).cuda()
|
||||
|
||||
os.makedirs(args.save_dir, exist_ok=True)
|
||||
save_path = osp.join(args.save_dir, args.split)
|
||||
|
||||
copyfile(source_path + ".tsv", save_path + ".tsv")
|
||||
copyfile(data_poth + ".lengths", save_path + ".lengths")
|
||||
|
||||
if osp.exists(source_path + ".phn"):
|
||||
copyfile(source_path + ".phn", save_path + ".phn")
|
||||
|
||||
if osp.exists(source_path + ".wrd"):
|
||||
copyfile(source_path + ".wrd", save_path + ".wrd")
|
||||
|
||||
if osp.exists(save_path + ".npy"):
|
||||
os.remove(save_path + ".npy")
|
||||
npaa = NpyAppendArray(save_path + ".npy")
|
||||
|
||||
batches = math.ceil(features.shape[0] / args.batch_size)
|
||||
|
||||
with torch.no_grad():
|
||||
for b in tqdm.trange(batches):
|
||||
start = b * args.batch_size
|
||||
end = start + args.batch_size
|
||||
x = torch.from_numpy(features[start:end]).cuda()
|
||||
x = torch.matmul(x, pca_A) + pca_b
|
||||
npaa.append(x.cpu().numpy())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import sys
|
||||
|
||||
for idx, line in enumerate(sys.stdin):
|
||||
print(f"utt{idx:010d} {line}", end="")
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from fairseq.data import Dictionary
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="filters a lexicon given a unit dictionary"
|
||||
)
|
||||
parser.add_argument("-d", "--unit-dict", help="unit dictionary", required=True)
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
d = Dictionary.load(args.unit_dict)
|
||||
symbols = set(d.symbols)
|
||||
|
||||
for line in sys.stdin:
|
||||
items = line.rstrip().split()
|
||||
skip = len(items) < 2
|
||||
for x in items[1:]:
|
||||
if x not in symbols:
|
||||
skip = True
|
||||
break
|
||||
if not skip:
|
||||
print(line, end="")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--tsv", required=True, type=str)
|
||||
parser.add_argument("--no-skip", action="store_true")
|
||||
parser.add_argument("--keep", action="store_true")
|
||||
params = parser.parse_args()
|
||||
|
||||
|
||||
def get_fname(line):
|
||||
p = os.path.basename(line.split("\t")[0])
|
||||
p = os.path.splitext(p)[0]
|
||||
return p
|
||||
|
||||
|
||||
# filenames to exclude
|
||||
seen = set()
|
||||
with open(params.tsv) as f:
|
||||
if not params.no_skip:
|
||||
root = next(f).rstrip()
|
||||
for line in f:
|
||||
seen.add(get_fname(line))
|
||||
|
||||
for i, line in enumerate(sys.stdin):
|
||||
exists = get_fname(line) in seen
|
||||
keep = (exists and params.keep) or (not exists and not params.keep)
|
||||
if i == 0 or keep:
|
||||
print(line, end="")
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from g2p_en import G2p
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--compact",
|
||||
action="store_true",
|
||||
help="if set, compacts phones",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
compact = args.compact
|
||||
|
||||
wrd_to_phn = {}
|
||||
g2p = G2p()
|
||||
for line in sys.stdin:
|
||||
words = line.strip().split()
|
||||
phones = []
|
||||
for w in words:
|
||||
if w not in wrd_to_phn:
|
||||
wrd_to_phn[w] = g2p(w)
|
||||
if compact:
|
||||
wrd_to_phn[w] = [
|
||||
p[:-1] if p[-1].isnumeric() else p for p in wrd_to_phn[w]
|
||||
]
|
||||
phones.extend(wrd_to_phn[w])
|
||||
try:
|
||||
print(" ".join(phones))
|
||||
except:
|
||||
print(wrd_to_phn, words, phones, file=sys.stderr)
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
for line in sys.stdin:
|
||||
print(line.replace(" ", "").replace("|", " ").strip())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import math
|
||||
import numpy as np
|
||||
import tqdm
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from shutil import copyfile
|
||||
|
||||
from npy_append_array import NpyAppendArray
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="mean pools representations by compressing uniform splits of the data"
|
||||
)
|
||||
# fmt: off
|
||||
parser.add_argument('source', help='directory with features')
|
||||
parser.add_argument('--split', help='which split to read', required=True)
|
||||
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
||||
parser.add_argument('--subsample-rate', type=float, default=0.5, help='size to subsample data to')
|
||||
|
||||
parser.add_argument('--remove-extra', action='store_true', help='if true, removes extra states that cant be pooled, otherwise pads with 0s')
|
||||
# fmt: on
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
source_path = osp.join(args.source, args.split)
|
||||
|
||||
print(f"data path: {source_path}")
|
||||
|
||||
features = np.load(source_path + ".npy", mmap_mode="r")
|
||||
|
||||
os.makedirs(args.save_dir, exist_ok=True)
|
||||
save_path = osp.join(args.save_dir, args.split)
|
||||
|
||||
copyfile(source_path + ".tsv", save_path + ".tsv")
|
||||
|
||||
if os.path.exists(source_path + ".phn"):
|
||||
copyfile(source_path + ".phn", save_path + ".phn")
|
||||
if os.path.exists(source_path + ".wrd"):
|
||||
copyfile(source_path + ".wrd", save_path + ".wrd")
|
||||
|
||||
if os.path.exists(osp.join(args.source, "dict.phn.txt")):
|
||||
copyfile(
|
||||
osp.join(args.source, "dict.phn.txt"),
|
||||
osp.join(args.save_dir, "dict.phn.txt"),
|
||||
)
|
||||
|
||||
if osp.exists(save_path + ".npy"):
|
||||
os.remove(save_path + ".npy")
|
||||
npaa = NpyAppendArray(save_path + ".npy")
|
||||
|
||||
with open(source_path + ".lengths", "r") as lf:
|
||||
lengths = lf.readlines()
|
||||
|
||||
fsz = features.shape[-1]
|
||||
start = 0
|
||||
with torch.no_grad():
|
||||
with open(save_path + ".lengths", "w") as lengths_out:
|
||||
for length in tqdm.tqdm(lengths):
|
||||
length = int(length)
|
||||
end = start + length
|
||||
feats = features[start:end]
|
||||
start += length
|
||||
x = torch.from_numpy(feats).cuda()
|
||||
target_num = math.ceil(length * args.subsample_rate)
|
||||
rem = length % target_num
|
||||
|
||||
if rem > 0:
|
||||
if args.remove_extra:
|
||||
to_rem = target_num - rem
|
||||
target_num -= 1
|
||||
x = x[:-to_rem]
|
||||
else:
|
||||
to_add = target_num - rem
|
||||
x = F.pad(x, [0, 0, 0, to_add])
|
||||
x[-to_add:] = x[-to_add - 1]
|
||||
|
||||
x = x.view(target_num, -1, fsz)
|
||||
x = x.mean(dim=-2)
|
||||
print(target_num, file=lengths_out)
|
||||
npaa.append(x.cpu().numpy())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import numpy as np
|
||||
import tqdm
|
||||
import torch
|
||||
import random
|
||||
from shutil import copyfile
|
||||
|
||||
from npy_append_array import NpyAppendArray
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="transforms features via a given pca and stored them in target dir"
|
||||
)
|
||||
# fmt: off
|
||||
parser.add_argument('source', help='directory with features')
|
||||
parser.add_argument('--split', help='which split to read', required=True)
|
||||
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
||||
parser.add_argument('--cluster-dir', help='where the clusters are')
|
||||
parser.add_argument('--pooling', type=str, default='mean', choices=['mean', 'sample'], help='how to pool')
|
||||
# fmt: on
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
source_path = osp.join(args.source, args.split)
|
||||
cluster_path = osp.join(args.cluster_dir, args.split + ".src")
|
||||
print(f"data path: {source_path}")
|
||||
|
||||
features = np.load(source_path + ".npy", mmap_mode="r")
|
||||
sizes = []
|
||||
offsets = []
|
||||
offset = 0
|
||||
with open(source_path + ".lengths", "r") as len_f:
|
||||
for line in len_f:
|
||||
length = int(line.rstrip())
|
||||
sizes.append(length)
|
||||
offsets.append(offset)
|
||||
offset += length
|
||||
|
||||
clusters = []
|
||||
with open(cluster_path, "r") as cf:
|
||||
for line in cf:
|
||||
line = line.rstrip()
|
||||
items = line.split()
|
||||
items = list(map(int, items))
|
||||
clusters.append(items)
|
||||
|
||||
os.makedirs(args.save_dir, exist_ok=True)
|
||||
save_path = osp.join(args.save_dir, args.split)
|
||||
|
||||
copyfile(source_path + ".tsv", save_path + ".tsv")
|
||||
|
||||
if os.path.exists(source_path + ".phn"):
|
||||
copyfile(source_path + ".phn", save_path + ".phn")
|
||||
if os.path.exists(osp.join(args.source, "dict.phn.txt")):
|
||||
copyfile(
|
||||
osp.join(args.source, "dict.phn.txt"),
|
||||
osp.join(args.save_dir, "dict.phn.txt"),
|
||||
)
|
||||
if os.path.exists(source_path + ".wrd"):
|
||||
copyfile(source_path + ".wrd", save_path + ".wrd")
|
||||
|
||||
if osp.exists(save_path + ".npy"):
|
||||
os.remove(save_path + ".npy")
|
||||
npaa = NpyAppendArray(save_path + ".npy")
|
||||
|
||||
def merge(feats, clust):
|
||||
feats = torch.from_numpy(feats.copy())
|
||||
clust = torch.LongTensor(clust)
|
||||
_, counts = clust.unique_consecutive(return_counts=True)
|
||||
curr = 0
|
||||
|
||||
merged = []
|
||||
for c in counts:
|
||||
c = c.item()
|
||||
start = curr
|
||||
end = curr + c
|
||||
curr += c
|
||||
if args.pooling == "mean":
|
||||
new_x = feats[start:end].mean(dim=0)
|
||||
elif args.pooling == "sample":
|
||||
new_x = feats[start + int(random.random() * c)]
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
merged.append(new_x)
|
||||
|
||||
return torch.stack(merged, dim=0).numpy()
|
||||
|
||||
with open(save_path + ".lengths", "w") as l_f:
|
||||
for size, offset, clust in tqdm.tqdm(
|
||||
zip(sizes, offsets, clusters), total=len(sizes)
|
||||
):
|
||||
end = size + offset
|
||||
feats = features[offset:end]
|
||||
feats = merge(feats, clust)
|
||||
print(len(feats), file=l_f)
|
||||
npaa.append(feats)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import fasttext as ft
|
||||
import os
|
||||
import regex
|
||||
import sys
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="reads text from stdin and outputs normalized, lid-filtered version to stdout"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fasttext-model",
|
||||
help="path to fasttext model",
|
||||
default="lid.187.bin",
|
||||
)
|
||||
parser.add_argument("--lang", help="language id", required=True)
|
||||
parser.add_argument(
|
||||
"--lid-threshold",
|
||||
type=float,
|
||||
help="threshold for this lang id probability",
|
||||
default=0.4,
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
filter_r = regex.compile(r"[^\p{L}\p{N}\p{M}\' \-]")
|
||||
|
||||
lg = args.lang.lower()
|
||||
lg_label = f"__label__{lg}"
|
||||
thresh = args.lid_threshold
|
||||
|
||||
if os.path.exists(args.fasttext_model):
|
||||
model = ft.load_model(args.fasttext_model)
|
||||
else:
|
||||
print(
|
||||
f"fasttext language id model {args.fasttext_model} not found. Proceeding without language filtering. "
|
||||
f"To enable language filtering, please download the latest language id model "
|
||||
f"from https://fasttext.cc/docs/en/language-identification.html",
|
||||
file=sys.stderr,
|
||||
)
|
||||
model = None
|
||||
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
line = filter_r.sub(" ", line)
|
||||
line = " ".join(line.split())
|
||||
|
||||
if model is not None:
|
||||
lid, prob = model.predict(line, k=100)
|
||||
try:
|
||||
target_idx = lid.index(lg_label)
|
||||
except ValueError:
|
||||
continue
|
||||
if target_idx == 0 or prob[target_idx] >= thresh:
|
||||
print(line)
|
||||
else:
|
||||
print(line)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import regex
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
filter_r = regex.compile(r"[^\p{L}\p{N}\p{M}\' \-]")
|
||||
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
line = filter_r.sub(" ", line)
|
||||
line = " ".join(line.split())
|
||||
print(line)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import numpy as np
|
||||
|
||||
import faiss
|
||||
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="compute a pca matrix given an array of numpy features"
|
||||
)
|
||||
# fmt: off
|
||||
parser.add_argument('data', help='numpy file containing features')
|
||||
parser.add_argument('--output', help='where to save the pca matrix', required=True)
|
||||
parser.add_argument('--dim', type=int, help='dim for pca reduction', required=True)
|
||||
parser.add_argument('--eigen-power', type=float, default=0, help='eigen power, -0.5 for whitening')
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Reading features")
|
||||
x = np.load(args.data, mmap_mode="r")
|
||||
|
||||
print("Computing PCA")
|
||||
pca = faiss.PCAMatrix(x.shape[-1], args.dim, args.eigen_power)
|
||||
pca.train(x)
|
||||
b = faiss.vector_to_array(pca.b)
|
||||
A = faiss.vector_to_array(pca.A).reshape(pca.d_out, pca.d_in)
|
||||
|
||||
os.makedirs(args.output, exist_ok=True)
|
||||
|
||||
prefix = str(args.dim)
|
||||
if args.eigen_power != 0:
|
||||
prefix += f"_{args.eigen_power}"
|
||||
|
||||
np.save(osp.join(args.output, f"{prefix}_pca_A"), A.T)
|
||||
np.save(osp.join(args.output, f"{prefix}_pca_b"), b)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="converts words to phones adding optional silences around in between words"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sil-prob",
|
||||
"-s",
|
||||
type=float,
|
||||
default=0,
|
||||
help="probability of inserting silence between each word",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--surround",
|
||||
action="store_true",
|
||||
help="if set, surrounds each example with silence",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lexicon",
|
||||
help="lexicon to convert to phones",
|
||||
required=True,
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
sil_prob = args.sil_prob
|
||||
surround = args.surround
|
||||
sil = "<SIL>"
|
||||
|
||||
wrd_to_phn = {}
|
||||
|
||||
with open(args.lexicon, "r") as lf:
|
||||
for line in lf:
|
||||
items = line.rstrip().split()
|
||||
assert len(items) > 1, line
|
||||
assert items[0] not in wrd_to_phn, items
|
||||
wrd_to_phn[items[0]] = items[1:]
|
||||
|
||||
for line in sys.stdin:
|
||||
words = line.strip().split()
|
||||
|
||||
if not all(w in wrd_to_phn for w in words):
|
||||
continue
|
||||
|
||||
phones = []
|
||||
if surround:
|
||||
phones.append(sil)
|
||||
|
||||
sample_sil_probs = None
|
||||
if sil_prob > 0 and len(words) > 1:
|
||||
sample_sil_probs = np.random.random(len(words) - 1)
|
||||
|
||||
for i, w in enumerate(words):
|
||||
phones.extend(wrd_to_phn[w])
|
||||
if (
|
||||
sample_sil_probs is not None
|
||||
and i < len(sample_sil_probs)
|
||||
and sample_sil_probs[i] < sil_prob
|
||||
):
|
||||
phones.append(sil)
|
||||
|
||||
if surround:
|
||||
phones.append(sil)
|
||||
print(" ".join(phones))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env zsh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
source_dir=$1
|
||||
tgt_dir=$2
|
||||
model=$3
|
||||
|
||||
if [ -z "$4" ]
|
||||
then
|
||||
dim=512
|
||||
else
|
||||
dim=$4
|
||||
fi
|
||||
|
||||
echo "using $dim dim for PCA"
|
||||
|
||||
if [ -z "$5" ]
|
||||
then
|
||||
layer=14
|
||||
else
|
||||
layer=$5
|
||||
fi
|
||||
|
||||
echo "extracting from layer $layer"
|
||||
|
||||
train_split=train
|
||||
valid_split=valid
|
||||
test_split=test
|
||||
|
||||
all_splits=($train_split)
|
||||
|
||||
if [[ -f "$source_dir/valid.tsv" ]]; then
|
||||
all_splits+=('valid')
|
||||
fi
|
||||
|
||||
if [[ -f "$source_dir/test.tsv" ]]; then
|
||||
all_splits+=('test')
|
||||
fi
|
||||
|
||||
echo "processing splits: $all_splits"
|
||||
|
||||
mkdir -p $tgt_dir
|
||||
|
||||
cp $source_dir/*.tsv $tgt_dir
|
||||
cp $source_dir/*.wrd $tgt_dir
|
||||
cp $source_dir/*.ltr $tgt_dir
|
||||
cp $source_dir/*.phn $tgt_dir
|
||||
cp $source_dir/dict* $tgt_dir
|
||||
|
||||
setopt shwordsplit
|
||||
|
||||
for split in $all_splits; do
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_extract_features.py $source_dir --split $split \
|
||||
--save-dir $tgt_dir --checkpoint $model --layer $layer
|
||||
done
|
||||
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_cluster_faiss.py $tgt_dir/${train_split}.tsv \
|
||||
--checkpoint $model --save-dir $tgt_dir -f "CLUS128" --sample-pct 1.0
|
||||
|
||||
for split in $all_splits; do
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_apply_cluster_faiss.py $tgt_dir \
|
||||
--checkpoint $model --path $tgt_dir/CLUS128 --split $split
|
||||
done
|
||||
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/pca.py $tgt_dir/${train_split}.npy --output $tgt_dir/pca --dim $dim
|
||||
|
||||
for split in $all_splits; do
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/apply_pca.py $tgt_dir --split $split --save-dir $tgt_dir/precompute_pca$dim --pca-path $tgt_dir/pca/${dim}_pca --batch-size 1048000
|
||||
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/merge_clusters.py $tgt_dir/precompute_pca$dim --cluster-dir $tgt_dir/CLUS128 \
|
||||
--split $split --save-dir $tgt_dir/precompute_pca${dim}_cls128_mean --pooling mean
|
||||
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/mean_pool.py $tgt_dir/precompute_pca${dim}_cls128_mean \
|
||||
--save-dir $tgt_dir/precompute_pca${dim}_cls128_mean_pooled --split $split
|
||||
done
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env zsh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
lg=$1
|
||||
text_path=$2
|
||||
target_dir=$3
|
||||
min_phones=$4
|
||||
phonemizer=$5
|
||||
lid_path=$6
|
||||
|
||||
if [ -z "$lid_path" ]; then
|
||||
lid_path="lid.187.bin"
|
||||
fi
|
||||
|
||||
ph_lg=${lg:l}
|
||||
if test "$lg" = 'fr'; then
|
||||
ph_lg='fr-fr'
|
||||
elif test "$lg" = 'en'; then
|
||||
ph_lg='en-us'
|
||||
elif test "$lg" = 'pt'; then
|
||||
ph_lg='pt-br'
|
||||
fi
|
||||
|
||||
ESPEAK_PATH=''
|
||||
if test "$phonemizer" = 'espeak'; then
|
||||
ESPEAK_PATH=$(which espeak)
|
||||
elif test "$phonemizer" = 'espeak-ng'; then
|
||||
ESPEAK_PATH=$(which espeak-ng)
|
||||
elif test "$phonemizer" = 'G2P'; then
|
||||
ESPEAK_PATH=''
|
||||
else
|
||||
echo "Unknown phonemizer $phonemizer. Valid options are espeak, espean-ng and G2P"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo $lg
|
||||
echo $ph_lg
|
||||
echo $text_path
|
||||
echo $target_dir
|
||||
echo "min phone seen threshold is $min_phones"
|
||||
|
||||
mkdir -p $target_dir
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/normalize_and_filter_text.py --lang $lg --fasttext-model $lid_path < $text_path | grep -v '\-\-\-' >! $target_dir/lm.upper.lid.txt
|
||||
python $FAIRSEQ_ROOT/fairseq_cli/preprocess.py --dataset-impl mmap --trainpref $target_dir/lm.upper.lid.txt --only-source --destdir $target_dir --thresholdsrc 2 --padding-factor 1 --dict-only
|
||||
cut -f1 -d' ' $target_dir/dict.txt | grep -v -x '[[:punct:]]*' | grep -Pv '\d\d\d\d\d+' >! $target_dir/words.txt
|
||||
|
||||
|
||||
if [ -z "$ESPEAK_PATH" ]; then
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/g2p_wrd_to_phn.py --compact < $target_dir/words.txt > $target_dir/phones.txt
|
||||
else
|
||||
# echoing 1 into corpus will prevent the mismatch lines between lexicon and phones in case the phonemizer fails
|
||||
one=$(echo "1" | PHONEMIZER_ESPEAK_PATH=$ESPEAK_PATH phonemize -p ' ' -w '' -l $ph_lg --language-switch remove-flags)
|
||||
sed 's/$/ 1/' $target_dir/words.txt | PHONEMIZER_ESPEAK_PATH=$ESPEAK_PATH phonemize -o $target_dir/phones.txt -p ' ' -w '' -l $ph_lg -j 70 --language-switch remove-flags
|
||||
echo "one is ${one}"
|
||||
sed -i "s/${one}$//" $target_dir/phones.txt
|
||||
fi
|
||||
|
||||
paste $target_dir/words.txt $target_dir/phones.txt >! $target_dir/lexicon.lst
|
||||
|
||||
python $FAIRSEQ_ROOT/fairseq_cli/preprocess.py --dataset-impl mmap --trainpref $target_dir/phones.txt --only-source --destdir $target_dir/phones --thresholdsrc $min_phones --padding-factor 1 --dict-only
|
||||
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/filter_lexicon.py -d $target_dir/phones/dict.txt < $target_dir/lexicon.lst >! $target_dir/lexicon_filtered.lst
|
||||
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/phonemize_with_sil.py -s 0.25 --surround --lexicon $target_dir/lexicon_filtered.lst < $target_dir/lm.upper.lid.txt >! $target_dir/phones/lm.phones.filtered.txt
|
||||
cp $target_dir/phones/dict.txt $target_dir/phones/dict.phn.txt
|
||||
echo "<SIL> 0" >> $target_dir/phones/dict.phn.txt
|
||||
python $FAIRSEQ_ROOT/fairseq_cli/preprocess.py --dataset-impl mmap --trainpref $target_dir/phones/lm.phones.filtered.txt --workers 70 --only-source --destdir $target_dir/phones --srcdict $target_dir/phones/dict.phn.txt
|
||||
|
||||
$KENLM_ROOT/lmplz -o 4 < $target_dir/lm.upper.lid.txt --discount_fallback --prune 0 0 0 3 >! $target_dir/kenlm.wrd.o40003.arpa
|
||||
$KENLM_ROOT/build_binary $target_dir/kenlm.wrd.o40003.arpa $target_dir/kenlm.wrd.o40003.bin
|
||||
|
||||
lg=$lg python $FAIRSEQ_ROOT/examples/speech_recognition/kaldi/kaldi_initializer.py kaldi_root=$KALDI_ROOT fst_dir=$target_dir/fst/phn_to_words_sil lm_arpa=$target_dir/kenlm.wrd.o40003.arpa wav2letter_lexicon=$target_dir/lexicon_filtered.lst data_dir=$target_dir/phones in_labels=phn "blank_symbol='<SIL>'"
|
||||
lg=$lg python $FAIRSEQ_ROOT/examples/speech_recognition/kaldi/kaldi_initializer.py kaldi_root=$KALDI_ROOT fst_dir=$target_dir/fst/phn_to_words lm_arpa=$target_dir/kenlm.wrd.o40003.arpa wav2letter_lexicon=$target_dir/lexicon_filtered.lst data_dir=$target_dir/phones in_labels=phn
|
||||
|
||||
$KENLM_ROOT/lmplz -o 4 < $target_dir/phones/lm.phones.filtered.txt --discount_fallback >! $target_dir/phones/lm.phones.filtered.04.arpa
|
||||
$KENLM_ROOT/build_binary $target_dir/phones/lm.phones.filtered.04.arpa $target_dir/phones/lm.phones.filtered.04.bin
|
||||
$KENLM_ROOT/lmplz -o 6 < $target_dir/phones/lm.phones.filtered.txt --discount_fallback >! $target_dir/phones/lm.phones.filtered.06.arpa
|
||||
$KENLM_ROOT/build_binary $target_dir/phones/lm.phones.filtered.06.arpa $target_dir/phones/lm.phones.filtered.06.bin
|
||||
|
||||
lg=$lg python $FAIRSEQ_ROOT/examples/speech_recognition/kaldi/kaldi_initializer.py kaldi_root=$KALDI_ROOT fst_dir=$target_dir/fst/phn_to_phn_sil lm_arpa=$target_dir/phones/lm.phones.filtered.06.arpa data_dir=$target_dir/phones in_labels=phn "blank_symbol='<SIL>'"
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
timit_root=$1 # assume it is the upper-cased version
|
||||
tgt_dir=$2
|
||||
model=$3
|
||||
|
||||
set -eu
|
||||
|
||||
setups="matched unmatched"
|
||||
splits="test valid train train_text"
|
||||
|
||||
tgt_dir=$(realpath $tgt_dir)
|
||||
sph2wav=$KALDI_ROOT/tools/sph2pipe_v2.5/sph2pipe
|
||||
wav_dir=$tgt_dir/wav
|
||||
|
||||
|
||||
mkdir -p $tgt_dir $wav_dir
|
||||
find $timit_root/{TRAIN,TEST} -iname "*.WAV" > $tgt_dir/all_sph.flist
|
||||
cat $tgt_dir/all_sph.flist | sed -e 's#//*#/#g' -e 's#.*/\([^/]*\)/\([^/]*\).WAV#\1_\2#g' > $tgt_dir/all.uid
|
||||
paste -d' ' $tgt_dir/{all_sph.flist,all.uid} | \
|
||||
awk -v sph2wav=$sph2wav -v wav_dir=$wav_dir '{print sph2wav " -f wav " $1 " > " wav_dir "/" $2 ".wav"}' \
|
||||
> $tgt_dir/sph2wav.sh
|
||||
bash $tgt_dir/sph2wav.sh
|
||||
cat $tgt_dir/all.uid | awk -v wav_dir=$(pwd)/$wav_dir '{print $1" "wav_dir"/"$1".wav"}' | sort > $tgt_dir/all_wav.scp
|
||||
cut -d' ' -f2 $tgt_dir/all_wav.scp | xargs -I{} soxi -s {} > $tgt_dir/all.dur
|
||||
paste -d' ' $tgt_dir/{all_wav.scp,all.dur} > $tgt_dir/all_wav_dur.scp
|
||||
rm $tgt_dir/{all.uid,all_sph.flist,sph2wav.sh}
|
||||
|
||||
find $timit_root/{TRAIN,TEST} -iname "*.PHN" > $tgt_dir/all_phn60.flist
|
||||
while read line; do
|
||||
if [ ! -f $line ]; then
|
||||
>&2 echo "Cannot find transcription file '$line'" && exit 1;
|
||||
fi
|
||||
cut -f3 -d' ' "$line" | tr '\n' ' ' | perl -ape 's: *$:\n:;'
|
||||
done < $tgt_dir/all_phn60.flist > $tgt_dir/all.phn60
|
||||
cat $tgt_dir/all_phn60.flist | sed -e 's#//*#/#g' -e 's#.*/\([^/]*\)/\([^/]*\).PHN#\1_\2#g' | \
|
||||
paste -d' ' - $tgt_dir/all.phn60 | \
|
||||
$KALDI_ROOT/egs/timit/s5/local/timit_norm_trans.pl -i - -m $KALDI_ROOT/egs/timit/s5/conf/phones.60-48-39.map -to 39 | \
|
||||
sort > $tgt_dir/all.phn
|
||||
echo "done preparing wav and 39-phone transcripts"
|
||||
|
||||
|
||||
for s in $setups; do
|
||||
mkdir -p $tgt_dir/$s
|
||||
for x in $splits; do
|
||||
uid_path=config/timit_${s}/${x}.uid
|
||||
grep -w -f $uid_path $tgt_dir/all.phn | cut -d' ' -f2- > $tgt_dir/$s/$x.phn
|
||||
ln -sf $(realpath $tgt_dir/$s/$x.phn) $tgt_dir/$s/$x.wrd
|
||||
|
||||
echo "/" > $tgt_dir/$s/$x.tsv && grep -w -f $uid_path $tgt_dir/all_wav_dur.scp | cut -d' ' -f2- | sed 's# #\t#' >> $tgt_dir/$s/$x.tsv
|
||||
done
|
||||
|
||||
for x in $splits; do
|
||||
cat $tgt_dir/$s/$x.phn
|
||||
done | tr ' ' '\n' | sort -u | awk '{print $1" "1}' > $tgt_dir/$s/dict.phn.txt
|
||||
ln -sf $(realpath $tgt_dir/$s/dict.phn.txt) $tgt_dir/$s/dict.wrd.txt
|
||||
done
|
||||
echo "done preparing unmatched and matched setups for TIMIT"
|
||||
|
||||
|
||||
for s in $setups; do
|
||||
zsh scripts/prepare_audio.sh $tgt_dir/$s $tgt_dir/$s/feat $model
|
||||
|
||||
lm_dir=$tgt_dir/$s/phones
|
||||
fst_dir=$tgt_dir/$s/fst/phn_to_phn
|
||||
|
||||
python $FAIRSEQ_ROOT/fairseq_cli/preprocess.py --dataset-impl mmap --trainpref $tgt_dir/$s/train_text.phn --workers 10 --only-source --destdir $lm_dir --srcdict $tgt_dir/$s/dict.phn.txt
|
||||
$KENLM_ROOT/lmplz -o 3 < $tgt_dir/$s/train_text.phn --discount_fallback >$lm_dir/train_text_phn.03.arpa
|
||||
$KENLM_ROOT/build_binary $lm_dir/train_text_phn.03.arpa $lm_dir/train_text_phn.03.bin
|
||||
$KENLM_ROOT/lmplz -o 4 < $tgt_dir/$s/train_text.phn --discount_fallback >$lm_dir/train_text_phn.04.arpa
|
||||
$KENLM_ROOT/build_binary $lm_dir/train_text_phn.04.arpa $lm_dir/train_text_phn.04.bin
|
||||
|
||||
python $FAIRSEQ_ROOT/examples/speech_recognition/kaldi/kaldi_initializer.py kaldi_root=$KALDI_ROOT fst_dir=$fst_dir lm_arpa=$lm_dir/train_text_phn.03.arpa data_dir=$tgt_dir/$s in_labels=phn
|
||||
done
|
||||
echo "done preprocessing audio and text for wav2vec-U"
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
"""
|
||||
get intervals from .vads file, specify output data, and this script removes silences and saves the audio data in out path folder
|
||||
paths=shards/train.tsv
|
||||
vads=shards/train.vads
|
||||
python remove_silence.py --paths $paths --vads $vads
|
||||
"""
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import torch
|
||||
import torchaudio
|
||||
import tqdm
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--tsv", default="", type=str)
|
||||
parser.add_argument("--vads", default="", type=str)
|
||||
parser.add_argument("--out", type=str)
|
||||
params = parser.parse_args()
|
||||
|
||||
# load paths
|
||||
paths = []
|
||||
with open(params.tsv) as f:
|
||||
root = next(f).rstrip()
|
||||
for line in f:
|
||||
paths.append(os.path.join(root, line.rstrip().split("\t")[0]))
|
||||
|
||||
# load vads
|
||||
list_intervals = []
|
||||
with open(params.vads) as f:
|
||||
for line in f:
|
||||
interval = [
|
||||
[int(w.split(":")[0]), int(w.split(":")[1])] for w in line.rstrip().split()
|
||||
]
|
||||
list_intervals.append(interval)
|
||||
|
||||
|
||||
# load audio and keep only intervals (i.e. remove silences)
|
||||
for i in tqdm.trange(len(paths)):
|
||||
data, _ = torchaudio.load(paths[i])
|
||||
if len(list_intervals[i]) > 0:
|
||||
data_filtered = torch.cat(
|
||||
[data[0][int(it[0]) : int(it[1])] for it in list_intervals[i]]
|
||||
).unsqueeze(0)
|
||||
else:
|
||||
data_filtered = data
|
||||
|
||||
# YOU MAY NEED TO MODIFY THIS TO GET THE RIGHT SUBPATH
|
||||
# outpath = params.out + '/'.join(paths[i].split('/')[-1])
|
||||
outpath = params.out + "/" + "/".join(paths[i].split("/")[-2:])
|
||||
|
||||
if not os.path.isdir("/".join(outpath.split("/")[:-1])):
|
||||
os.makedirs("/".join(outpath.split("/")[:-1]))
|
||||
if not os.path.exists(outpath):
|
||||
torchaudio.save(outpath, data_filtered, sample_rate=16000)
|
||||
else:
|
||||
print(outpath, "exists!")
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from copy import deepcopy
|
||||
from scipy.signal import lfilter
|
||||
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
import soundfile as sf
|
||||
import os.path as osp
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(description="compute vad segments")
|
||||
parser.add_argument(
|
||||
"--rvad-home",
|
||||
"-r",
|
||||
help="path to rvad home (see https://github.com/zhenghuatan/rVADfast)",
|
||||
required=True,
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def rvad(speechproc, path):
|
||||
winlen, ovrlen, pre_coef, nfilter, nftt = 0.025, 0.01, 0.97, 20, 512
|
||||
ftThres = 0.5
|
||||
vadThres = 0.4
|
||||
opts = 1
|
||||
|
||||
data, fs = sf.read(path)
|
||||
assert fs == 16_000, "sample rate must be 16khz"
|
||||
ft, flen, fsh10, nfr10 = speechproc.sflux(data, fs, winlen, ovrlen, nftt)
|
||||
|
||||
# --spectral flatness --
|
||||
pv01 = np.zeros(ft.shape[0])
|
||||
pv01[np.less_equal(ft, ftThres)] = 1
|
||||
pitch = deepcopy(ft)
|
||||
|
||||
pvblk = speechproc.pitchblockdetect(pv01, pitch, nfr10, opts)
|
||||
|
||||
# --filtering--
|
||||
ENERGYFLOOR = np.exp(-50)
|
||||
b = np.array([0.9770, -0.9770])
|
||||
a = np.array([1.0000, -0.9540])
|
||||
fdata = lfilter(b, a, data, axis=0)
|
||||
|
||||
# --pass 1--
|
||||
noise_samp, noise_seg, n_noise_samp = speechproc.snre_highenergy(
|
||||
fdata, nfr10, flen, fsh10, ENERGYFLOOR, pv01, pvblk
|
||||
)
|
||||
|
||||
# sets noisy segments to zero
|
||||
for j in range(n_noise_samp):
|
||||
fdata[range(int(noise_samp[j, 0]), int(noise_samp[j, 1]) + 1)] = 0
|
||||
|
||||
vad_seg = speechproc.snre_vad(
|
||||
fdata, nfr10, flen, fsh10, ENERGYFLOOR, pv01, pvblk, vadThres
|
||||
)
|
||||
return vad_seg, data
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
sys.path.append(args.rvad_home)
|
||||
import speechproc
|
||||
|
||||
stride = 160
|
||||
lines = sys.stdin.readlines()
|
||||
root = lines[0].rstrip()
|
||||
for fpath in tqdm(lines[1:]):
|
||||
path = osp.join(root, fpath.split()[0])
|
||||
vads, wav = rvad(speechproc, path)
|
||||
|
||||
start = None
|
||||
vad_segs = []
|
||||
for i, v in enumerate(vads):
|
||||
if start is None and v == 1:
|
||||
start = i * stride
|
||||
elif start is not None and v == 0:
|
||||
vad_segs.append((start, i * stride))
|
||||
start = None
|
||||
if start is not None:
|
||||
vad_segs.append((start, len(wav)))
|
||||
|
||||
print(" ".join(f"{v[0]}:{v[1]}" for v in vad_segs))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import numpy as np
|
||||
import tqdm
|
||||
import torch
|
||||
import sys
|
||||
|
||||
import faiss
|
||||
import torch.nn.functional as F
|
||||
|
||||
from wav2vec_cluster_faiss import parse_faiss_specs, Wav2VecFeatureReader
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(description="apply clusters")
|
||||
# fmt: off
|
||||
parser.add_argument('data', help='location of tsv files')
|
||||
parser.add_argument('--split', help='split to process', required=True)
|
||||
parser.add_argument('--labels', help='split to process', default="phn")
|
||||
parser.add_argument('--path', help='path to pca and centroids', required=True)
|
||||
parser.add_argument('--checkpoint', type=str, help='checkpoint for wav2vec model (if using wav2vec features)', required=True)
|
||||
parser.add_argument('--layer', '-l', type=int, help='which layer to read', default=14)
|
||||
parser.add_argument('--max-tsz', type=int, help='batch kmeans up to this much', default=14)
|
||||
# fmt: on
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def get_iterator(args):
|
||||
label_path = osp.join(args.data, f"{args.split}.{args.labels}")
|
||||
if osp.exists(label_path):
|
||||
lp = open(label_path, "r")
|
||||
else:
|
||||
lp = None
|
||||
|
||||
with open(osp.join(args.data, f"{args.split}.tsv"), "r") as fp:
|
||||
lines = fp.read().split("\n")
|
||||
root = lines.pop(0).strip()
|
||||
files = [line.rstrip() for line in lines if len(line) > 0]
|
||||
|
||||
if lp is not None:
|
||||
lbls = [line.rstrip() for line in lp]
|
||||
else:
|
||||
lbls = [None] * len(files)
|
||||
|
||||
num = len(files)
|
||||
reader = Wav2VecFeatureReader(args.checkpoint, args.layer)
|
||||
|
||||
def iterate():
|
||||
for fname, lbl in zip(files, lbls):
|
||||
file = osp.join(root, fname.split("\t")[0])
|
||||
feats = reader.get_feats(file)
|
||||
yield feats.data, fname, lbl
|
||||
|
||||
return iterate, num, root
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
spec = osp.basename(args.path)
|
||||
|
||||
try:
|
||||
faiss_spec = parse_faiss_specs(spec.rstrip("/"))[0]
|
||||
except:
|
||||
print(spec)
|
||||
raise
|
||||
|
||||
print("Faiss Spec:", faiss_spec, file=sys.stderr)
|
||||
|
||||
if faiss_spec.pca:
|
||||
A = torch.from_numpy(np.load(osp.join(args.path, "pca_A.npy"))).cuda()
|
||||
b = torch.from_numpy(np.load(osp.join(args.path, "pca_b.npy"))).cuda()
|
||||
print("Loaded PCA", file=sys.stderr)
|
||||
|
||||
centroids = np.load(osp.join(args.path, "centroids.npy"))
|
||||
print("Loaded centroids", centroids.shape, file=sys.stderr)
|
||||
|
||||
res = faiss.StandardGpuResources()
|
||||
index_flat = (
|
||||
faiss.IndexFlatL2(centroids.shape[1])
|
||||
if not faiss_spec.sphere
|
||||
else faiss.IndexFlatIP(centroids.shape[1])
|
||||
)
|
||||
faiss_index = faiss.index_cpu_to_gpu(res, 0, index_flat)
|
||||
faiss_index.add(centroids)
|
||||
|
||||
generator, num, root = get_iterator(args)
|
||||
iterator = generator()
|
||||
|
||||
had_labels = False
|
||||
label_path = osp.join(args.path, f"{args.split}.{args.labels}")
|
||||
|
||||
with torch.no_grad():
|
||||
with open(osp.join(args.path, f"{args.split}.src"), "w") as fp, open(
|
||||
osp.join(args.path, f"{args.split}.tsv"), "w"
|
||||
) as pp, open(label_path, "w") as lp:
|
||||
print(root, file=pp)
|
||||
for f, fname, lbl in tqdm.tqdm(iterator, total=num):
|
||||
if faiss_spec.pca:
|
||||
f = torch.mm(f, A) + b
|
||||
if faiss_spec.norm:
|
||||
f = F.normalize(f, p=2, dim=-1)
|
||||
|
||||
f = f.cpu().numpy()
|
||||
|
||||
_, z = faiss_index.search(f, 1)
|
||||
|
||||
print(" ".join(str(x.item()) for x in z), file=fp)
|
||||
print(fname, file=pp)
|
||||
|
||||
if lbl is not None:
|
||||
print(lbl, file=lp)
|
||||
had_labels = True
|
||||
if not had_labels:
|
||||
os.remove(label_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import gc
|
||||
import os
|
||||
import os.path as osp
|
||||
import random
|
||||
import numpy as np
|
||||
import tqdm
|
||||
import torch
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
import faiss
|
||||
|
||||
import fairseq
|
||||
import soundfile as sf
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="compute kmeans codebook from kaldi-computed feats"
|
||||
)
|
||||
# fmt: off
|
||||
parser.add_argument('data', help='location of tsv files')
|
||||
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
||||
parser.add_argument('--checkpoint', type=str, help='checkpoint for wav2vec model (if using wav2vec features)', required=True)
|
||||
parser.add_argument('--sample-pct', '-r', type=float, help='percentage of timesteps to sample', default=0)
|
||||
parser.add_argument('--layer', '-l', type=int, help='which layer to read', default=14)
|
||||
parser.add_argument('--faiss-specs', '-f', type=str,
|
||||
help='faiss index specs; separated by space '
|
||||
'format is: PCAx_NORM_CLUSx_SPHERICAL -> '
|
||||
'PCAx if exists first apply PCA '
|
||||
'NORM if exists, normalize the vector by L2 norm '
|
||||
'CLUSx must exist, cluster to x clusters '
|
||||
'SPEHRICAL if exists, apply spherical kmeans',
|
||||
default='l2')
|
||||
# fmt: on
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
faiss_spec = namedtuple("faiss_spec", ["pca", "norm", "n_clus", "sphere", "spec_str"])
|
||||
|
||||
|
||||
def parse_faiss_specs(specs_str):
|
||||
specs = []
|
||||
for ss in specs_str.split():
|
||||
comps = ss.split("_")
|
||||
pca = 0
|
||||
norm = False
|
||||
n_clus = 0
|
||||
sphere = False
|
||||
for c in comps:
|
||||
if c.startswith("PCA"):
|
||||
pca = int(c[3:])
|
||||
elif c == "NORM":
|
||||
norm = True
|
||||
elif c.startswith("CLUS"):
|
||||
n_clus = int(c[4:])
|
||||
elif c == "SPHERICAL":
|
||||
sphere = True
|
||||
assert n_clus > 0
|
||||
specs.append(
|
||||
faiss_spec(pca=pca, norm=norm, n_clus=n_clus, sphere=sphere, spec_str=ss)
|
||||
)
|
||||
return specs
|
||||
|
||||
|
||||
class Wav2VecFeatureReader(object):
|
||||
def __init__(self, cp_file, layer):
|
||||
state = fairseq.checkpoint_utils.load_checkpoint_to_cpu(cp_file)
|
||||
|
||||
self.layer = layer
|
||||
|
||||
if "cfg" in state:
|
||||
w2v_args = state["cfg"]
|
||||
task = fairseq.tasks.setup_task(w2v_args.task)
|
||||
model = task.build_model(w2v_args.model)
|
||||
else:
|
||||
w2v_args = state["args"]
|
||||
task = fairseq.tasks.setup_task(w2v_args)
|
||||
model = task.build_model(w2v_args)
|
||||
model.load_state_dict(state["model"], strict=True)
|
||||
model.eval()
|
||||
model.cuda()
|
||||
self.model = model
|
||||
|
||||
def read_audio(self, fname):
|
||||
"""Load an audio file and return PCM along with the sample rate"""
|
||||
wav, sr = sf.read(fname)
|
||||
assert sr == 16e3
|
||||
|
||||
return wav
|
||||
|
||||
def get_feats(self, loc):
|
||||
x = self.read_audio(loc)
|
||||
with torch.no_grad():
|
||||
source = torch.from_numpy(x).view(1, -1).float().cuda()
|
||||
res = self.model(
|
||||
source=source, mask=False, features_only=True, layer=self.layer
|
||||
)
|
||||
return res["layer_results"][self.layer][0].squeeze(1)
|
||||
|
||||
|
||||
def get_iterator(args):
|
||||
with open(args.data, "r") as fp:
|
||||
lines = fp.read().split("\n")
|
||||
root = lines.pop(0).strip()
|
||||
files = [osp.join(root, line.split("\t")[0]) for line in lines if len(line) > 0]
|
||||
|
||||
if getattr(args, "sample_pct", 0) > 0:
|
||||
files = random.sample(files, int(args.sample_pct * len(files)))
|
||||
num = len(files)
|
||||
reader = Wav2VecFeatureReader(args.checkpoint, args.layer)
|
||||
|
||||
def iterate():
|
||||
for fname in files:
|
||||
feats = reader.get_feats(fname)
|
||||
yield feats.cpu().numpy()
|
||||
|
||||
return iterate, num
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
faiss_specs = parse_faiss_specs(args.faiss_specs)
|
||||
print("Faiss Specs:", faiss_specs)
|
||||
|
||||
feat_path = osp.join(args.save_dir, "features")
|
||||
if osp.exists(feat_path + ".npy"):
|
||||
feats = np.load(feat_path + ".npy")
|
||||
else:
|
||||
generator, num = get_iterator(args)
|
||||
iterator = generator()
|
||||
|
||||
feats = []
|
||||
for f in tqdm.tqdm(iterator, total=num):
|
||||
feats.append(f)
|
||||
|
||||
del iterator
|
||||
del generator
|
||||
|
||||
feats = np.concatenate(feats)
|
||||
|
||||
print(feats.shape)
|
||||
|
||||
os.makedirs(args.save_dir, exist_ok=True)
|
||||
# np.save(feat_path, feats)
|
||||
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
reload = False
|
||||
for spec in faiss_specs:
|
||||
print("Processing spec", spec)
|
||||
|
||||
if reload:
|
||||
print("Reloading...")
|
||||
del feats
|
||||
gc.collect()
|
||||
feats = np.load(feat_path + ".npy")
|
||||
|
||||
save_path = osp.join(args.save_dir, spec.spec_str)
|
||||
os.makedirs(save_path, exist_ok=True)
|
||||
d = feats.shape[-1]
|
||||
x = feats
|
||||
if spec.pca > 0:
|
||||
print("Computing PCA")
|
||||
pca = faiss.PCAMatrix(d, spec.pca)
|
||||
pca.train(x)
|
||||
d = spec.pca
|
||||
b = faiss.vector_to_array(pca.b)
|
||||
A = faiss.vector_to_array(pca.A).reshape(pca.d_out, pca.d_in)
|
||||
np.save(osp.join(save_path, "pca_A"), A.T)
|
||||
np.save(osp.join(save_path, "pca_b"), b)
|
||||
print("Applying PCA")
|
||||
x = pca.apply_py(x)
|
||||
|
||||
if spec.norm:
|
||||
reload = spec.pca <= 0
|
||||
print("Normalizing")
|
||||
faiss.normalize_L2(x)
|
||||
|
||||
print("Computing kmeans")
|
||||
kmeans = faiss.Kmeans(
|
||||
d,
|
||||
spec.n_clus,
|
||||
niter=50,
|
||||
verbose=True,
|
||||
spherical=spec.sphere,
|
||||
max_points_per_centroid=feats.shape[0],
|
||||
gpu=True,
|
||||
nredo=3,
|
||||
)
|
||||
kmeans.train(x)
|
||||
np.save(osp.join(save_path, "centroids"), kmeans.centroids)
|
||||
del kmeans
|
||||
del x
|
||||
gc.collect()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path as osp
|
||||
import tqdm
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from shutil import copyfile
|
||||
|
||||
from npy_append_array import NpyAppendArray
|
||||
|
||||
import fairseq
|
||||
import soundfile as sf
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="compute kmeans codebook from kaldi-computed feats"
|
||||
)
|
||||
# fmt: off
|
||||
parser.add_argument('data', help='location of tsv files')
|
||||
parser.add_argument('--split', help='which split to read', required=True)
|
||||
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
||||
parser.add_argument('--checkpoint', type=str, help='checkpoint for wav2vec ctc model', required=True)
|
||||
parser.add_argument('--layer', type=int, default=14, help='which layer to use')
|
||||
# fmt: on
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
class Wav2VecFeatureReader(object):
|
||||
def __init__(self, cp_file, layer):
|
||||
model, cfg, task = fairseq.checkpoint_utils.load_model_ensemble_and_task(
|
||||
[cp_file]
|
||||
)
|
||||
model = model[0]
|
||||
model.eval()
|
||||
model.cuda()
|
||||
self.model = model
|
||||
self.task = task
|
||||
self.layer = layer
|
||||
|
||||
def read_audio(self, fname):
|
||||
"""Load an audio file and return PCM along with the sample rate"""
|
||||
wav, sr = sf.read(fname)
|
||||
assert sr == 16e3
|
||||
|
||||
return wav
|
||||
|
||||
def get_feats(self, loc):
|
||||
x = self.read_audio(loc)
|
||||
with torch.no_grad():
|
||||
source = torch.from_numpy(x).float().cuda()
|
||||
if self.task.cfg.normalize:
|
||||
assert source.dim() == 1, source.dim()
|
||||
with torch.no_grad():
|
||||
source = F.layer_norm(source, source.shape)
|
||||
source = source.view(1, -1)
|
||||
|
||||
m_res = self.model(source=source, mask=False, features_only=True, layer=self.layer)
|
||||
return m_res["x"].squeeze(0).cpu()
|
||||
|
||||
|
||||
def get_iterator(args):
|
||||
with open(osp.join(args.data, args.split) + ".tsv", "r") as fp:
|
||||
lines = fp.read().split("\n")
|
||||
root = lines.pop(0).strip()
|
||||
files = [osp.join(root, line.split("\t")[0]) for line in lines if len(line) > 0]
|
||||
|
||||
num = len(files)
|
||||
reader = Wav2VecFeatureReader(args.checkpoint, args.layer)
|
||||
|
||||
def iterate():
|
||||
for fname in files:
|
||||
w2v_feats = reader.get_feats(fname)
|
||||
yield w2v_feats
|
||||
|
||||
return iterate, num
|
||||
|
||||
|
||||
def main():
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
os.makedirs(args.save_dir, exist_ok=True)
|
||||
|
||||
def create_files(dest):
|
||||
copyfile(osp.join(args.data, args.split) + ".tsv", dest + ".tsv")
|
||||
if osp.exists(osp.join(args.data, args.split) + ".wrd"):
|
||||
copyfile(osp.join(args.data, args.split) + ".wrd", dest + ".wrd")
|
||||
if osp.exists(osp.join(args.data, args.split) + ".phn"):
|
||||
copyfile(osp.join(args.data, args.split) + ".phn", dest + ".phn")
|
||||
|
||||
if osp.exists(dest + ".npy"):
|
||||
os.remove(dest + ".npy")
|
||||
npaa = NpyAppendArray(dest + ".npy")
|
||||
return npaa
|
||||
|
||||
save_path = osp.join(args.save_dir, args.split)
|
||||
npaa = create_files(save_path)
|
||||
|
||||
generator, num = get_iterator(args)
|
||||
iterator = generator()
|
||||
|
||||
with open(save_path + ".lengths", "w") as l_f:
|
||||
for w2v_feats in tqdm.tqdm(iterator, total=num):
|
||||
print(len(w2v_feats), file=l_f)
|
||||
|
||||
if len(w2v_feats) > 0:
|
||||
npaa.append(w2v_feats.numpy())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
"""
|
||||
Implement unsupervised metric for decoding hyperparameter selection:
|
||||
$$ alpha * LM_PPL + ViterbitUER(%) * 100 $$
|
||||
"""
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import editdistance
|
||||
|
||||
logging.root.setLevel(logging.INFO)
|
||||
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-s", "--hypo", help="hypo transcription", required=True)
|
||||
parser.add_argument(
|
||||
"-r", "--reference", help="reference transcription", required=True
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def compute_wer(ref_uid_to_tra, hyp_uid_to_tra, g2p):
|
||||
d_cnt = 0
|
||||
w_cnt = 0
|
||||
w_cnt_h = 0
|
||||
for uid in hyp_uid_to_tra:
|
||||
ref = ref_uid_to_tra[uid].split()
|
||||
if g2p is not None:
|
||||
hyp = g2p(hyp_uid_to_tra[uid])
|
||||
hyp = [p for p in hyp if p != "'" and p != " "]
|
||||
hyp = [p[:-1] if p[-1].isnumeric() else p for p in hyp]
|
||||
else:
|
||||
hyp = hyp_uid_to_tra[uid].split()
|
||||
d_cnt += editdistance.eval(ref, hyp)
|
||||
w_cnt += len(ref)
|
||||
w_cnt_h += len(hyp)
|
||||
wer = float(d_cnt) / w_cnt
|
||||
logger.debug(
|
||||
(
|
||||
f"wer = {wer * 100:.2f}%; num. of ref words = {w_cnt}; "
|
||||
f"num. of hyp words = {w_cnt_h}; num. of sentences = {len(ref_uid_to_tra)}"
|
||||
)
|
||||
)
|
||||
return wer
|
||||
|
||||
|
||||
def main():
|
||||
args = get_parser().parse_args()
|
||||
|
||||
errs = 0
|
||||
count = 0
|
||||
with open(args.hypo, "r") as hf, open(args.reference, "r") as rf:
|
||||
for h, r in zip(hf, rf):
|
||||
h = h.rstrip().split()
|
||||
r = r.rstrip().split()
|
||||
errs += editdistance.eval(r, h)
|
||||
count += len(r)
|
||||
|
||||
logger.info(f"UER: {errs / count * 100:.2f}%")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
def load_tra(tra_path):
|
||||
with open(tra_path, "r") as f:
|
||||
uid_to_tra = {}
|
||||
for line in f:
|
||||
uid, tra = line.split(None, 1)
|
||||
uid_to_tra[uid] = tra
|
||||
logger.debug(f"loaded {len(uid_to_tra)} utterances from {tra_path}")
|
||||
return uid_to_tra
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3 -u
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
for line in sys.stdin:
|
||||
print(" ".join(list(line.strip().replace(" ", "|"))) + " |")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user