Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_audio.sh +78 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_audio_v2.sh +68 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_text.sh +83 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_timit.sh +79 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/remove_silence.py +63 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/vads.py +98 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wav2vec_apply_cluster_faiss.py +128 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wav2vec_cluster_faiss.py +210 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wav2vec_extract_features.py +119 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wer.py +82 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wrd_to_ltr.py +16 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/tasks/__init__.py +11 -0
- omni_speech/infer/fairseq/examples/wav2vec/unsupervised/tasks/unpaired_audio_text.py +452 -0
- omni_speech/infer/fairseq/examples/wav2vec/xlsr/README.md +95 -0
- omni_speech/infer/fairseq/examples/wav2vec/xlsr/config/finetune.yaml +66 -0
- omni_speech/infer/fairseq/examples/wav2vec/xlsr/scripts/eval_speaker_clf_task.py +173 -0
- omni_speech/infer/fairseq/examples/wav2vec/xlsr/scripts/gen_audio_embedding.py +222 -0
- omni_speech/infer/fairseq/examples/wmt19/README.md +85 -0
- omni_speech/infer/fairseq/examples/wmt20/README.md +72 -0
- omni_speech/infer/fairseq/examples/wmt21/README.md +25 -0
- omni_speech/infer/fairseq/examples/wmt21/eval.sh +49 -0
- omni_speech/infer/fairseq/examples/wmt21/scripts/normalize-punctuation.perl +90 -0
- omni_speech/infer/fairseq/examples/wmt21/scripts/replace-unicode-punctuation.perl +55 -0
- omni_speech/infer/fairseq/examples/womens_bios/README.md +81 -0
- omni_speech/infer/fairseq/examples/womens_bios/query_occupations_from_wikidata.py +34 -0
- omni_speech/infer/fairseq/examples/xformers/README.md +43 -0
- omni_speech/infer/fairseq/examples/xglm/README.md +195 -0
- omni_speech/infer/fairseq/examples/xglm/XStoryCloze.md +57 -0
- omni_speech/infer/fairseq/examples/xglm/model_card.md +152 -0
- omni_speech/infer/fairseq/examples/xlmr/README.md +144 -0
- omni_speech/infer/fairseq/examples/xmod/README.md +151 -0
- omni_speech/infer/fairseq/examples/xmod/preprocess_nli.py +168 -0
- omni_speech/infer/fairseq/fairseq/__init__.py +45 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__init__.py +7 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/__init__.cpython-312.pyc +0 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_dataset.cpython-312.pyc +0 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_lm.cpython-312.pyc +0 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_masked_lm.cpython-312.pyc +0 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_model.cpython-312.pyc +0 -0
- omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_mt.cpython-312.pyc +0 -0
- omni_speech/infer/fairseq/fairseq/benchmark/benchmark_multihead_attention.py +172 -0
- omni_speech/infer/fairseq/fairseq/benchmark/dummy_dataset.py +36 -0
- omni_speech/infer/fairseq/fairseq/benchmark/dummy_lm.py +83 -0
- omni_speech/infer/fairseq/fairseq/benchmark/dummy_masked_lm.py +94 -0
- omni_speech/infer/fairseq/fairseq/benchmark/dummy_model.py +96 -0
- omni_speech/infer/fairseq/fairseq/benchmark/dummy_mt.py +119 -0
- omni_speech/infer/fairseq/fairseq/binarizer.py +381 -0
- omni_speech/infer/fairseq/fairseq/checkpoint_utils.py +918 -0
- omni_speech/infer/fairseq/fairseq/clib/cuda/ngram_repeat_block_cuda.cpp +55 -0
- omni_speech/infer/fairseq/fairseq/clib/cuda/ngram_repeat_block_cuda_kernel.cu +82 -0
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_audio.sh
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env zsh
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
source_dir=$1
|
| 8 |
+
tgt_dir=$2
|
| 9 |
+
model=$3
|
| 10 |
+
|
| 11 |
+
if [ -z "$4" ]
|
| 12 |
+
then
|
| 13 |
+
dim=512
|
| 14 |
+
else
|
| 15 |
+
dim=$4
|
| 16 |
+
fi
|
| 17 |
+
|
| 18 |
+
echo "using $dim dim for PCA"
|
| 19 |
+
|
| 20 |
+
if [ -z "$5" ]
|
| 21 |
+
then
|
| 22 |
+
layer=14
|
| 23 |
+
else
|
| 24 |
+
layer=$5
|
| 25 |
+
fi
|
| 26 |
+
|
| 27 |
+
echo "extracting from layer $layer"
|
| 28 |
+
|
| 29 |
+
train_split=train
|
| 30 |
+
valid_split=valid
|
| 31 |
+
test_split=test
|
| 32 |
+
|
| 33 |
+
all_splits=($train_split)
|
| 34 |
+
|
| 35 |
+
if [[ -f "$source_dir/valid.tsv" ]]; then
|
| 36 |
+
all_splits+=('valid')
|
| 37 |
+
fi
|
| 38 |
+
|
| 39 |
+
if [[ -f "$source_dir/test.tsv" ]]; then
|
| 40 |
+
all_splits+=('test')
|
| 41 |
+
fi
|
| 42 |
+
|
| 43 |
+
echo "processing splits: $all_splits"
|
| 44 |
+
|
| 45 |
+
mkdir -p $tgt_dir
|
| 46 |
+
|
| 47 |
+
cp $source_dir/*.tsv $tgt_dir
|
| 48 |
+
cp $source_dir/*.wrd $tgt_dir
|
| 49 |
+
cp $source_dir/*.ltr $tgt_dir
|
| 50 |
+
cp $source_dir/*.phn $tgt_dir
|
| 51 |
+
cp $source_dir/dict* $tgt_dir
|
| 52 |
+
|
| 53 |
+
setopt shwordsplit
|
| 54 |
+
|
| 55 |
+
for split in $all_splits; do
|
| 56 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_extract_features.py $source_dir --split $split \
|
| 57 |
+
--save-dir $tgt_dir --checkpoint $model --layer $layer
|
| 58 |
+
done
|
| 59 |
+
|
| 60 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_cluster_faiss.py $tgt_dir/${train_split}.tsv \
|
| 61 |
+
--checkpoint $model --save-dir $tgt_dir -f "CLUS128" --sample-pct 1.0
|
| 62 |
+
|
| 63 |
+
for split in $all_splits; do
|
| 64 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_apply_cluster_faiss.py $tgt_dir \
|
| 65 |
+
--checkpoint $model --path $tgt_dir/CLUS128 --split $split
|
| 66 |
+
done
|
| 67 |
+
|
| 68 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/pca.py $tgt_dir/${train_split}.npy --output $tgt_dir/pca --dim $dim
|
| 69 |
+
|
| 70 |
+
for split in $all_splits; do
|
| 71 |
+
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
|
| 72 |
+
|
| 73 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/merge_clusters.py $tgt_dir/precompute_pca$dim --cluster-dir $tgt_dir/CLUS128 \
|
| 74 |
+
--split $split --save-dir $tgt_dir/precompute_pca${dim}_cls128_mean --pooling mean
|
| 75 |
+
|
| 76 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/mean_pool.py $tgt_dir/precompute_pca${dim}_cls128_mean \
|
| 77 |
+
--save-dir $tgt_dir/precompute_pca${dim}_cls128_mean_pooled --split $split
|
| 78 |
+
done
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_audio_v2.sh
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env zsh
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
source_dir=$1
|
| 8 |
+
tgt_dir=$2
|
| 9 |
+
model=$3
|
| 10 |
+
|
| 11 |
+
if [ -z "$4" ]
|
| 12 |
+
then
|
| 13 |
+
dim=64
|
| 14 |
+
else
|
| 15 |
+
dim=$4
|
| 16 |
+
fi
|
| 17 |
+
|
| 18 |
+
echo "using $dim clusters for auxilary target"
|
| 19 |
+
|
| 20 |
+
if [ -z "$5" ]
|
| 21 |
+
then
|
| 22 |
+
layer=14
|
| 23 |
+
else
|
| 24 |
+
layer=$5
|
| 25 |
+
fi
|
| 26 |
+
|
| 27 |
+
echo "extracting from layer $layer"
|
| 28 |
+
|
| 29 |
+
train_split=train
|
| 30 |
+
valid_split=valid
|
| 31 |
+
test_split=test
|
| 32 |
+
|
| 33 |
+
all_splits=($train_split)
|
| 34 |
+
|
| 35 |
+
if [[ -f "$source_dir/valid.tsv" ]]; then
|
| 36 |
+
all_splits+=('valid')
|
| 37 |
+
fi
|
| 38 |
+
|
| 39 |
+
if [[ -f "$source_dir/test.tsv" ]]; then
|
| 40 |
+
all_splits+=('test')
|
| 41 |
+
fi
|
| 42 |
+
|
| 43 |
+
echo "processing splits: $all_splits"
|
| 44 |
+
|
| 45 |
+
mkdir -p $tgt_dir
|
| 46 |
+
|
| 47 |
+
cp $source_dir/*.tsv $tgt_dir
|
| 48 |
+
cp $source_dir/*.wrd $tgt_dir
|
| 49 |
+
cp $source_dir/*.ltr $tgt_dir
|
| 50 |
+
cp $source_dir/*.phn $tgt_dir
|
| 51 |
+
cp $source_dir/dict* $tgt_dir
|
| 52 |
+
|
| 53 |
+
setopt shwordsplit
|
| 54 |
+
|
| 55 |
+
for split in $all_splits; do
|
| 56 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/wav2vec_extract_features.py $source_dir --split $split \
|
| 57 |
+
--save-dir $tgt_dir --checkpoint $model --layer $layer
|
| 58 |
+
done
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
mkdir -p $tgt_dir/mfcc
|
| 62 |
+
|
| 63 |
+
# Consider spliting corpus into chuncks for large corpus, see HuBERT preprocessing for more details
|
| 64 |
+
python $FAIRSEQ_ROOT/examples/hubert/simple_kmeans/dump_mfcc_feature.py \
|
| 65 |
+
$tgt_dir $train_split 1 0 $tgt_dir/mfcc
|
| 66 |
+
python $FAIRSEQ_ROOT/examples/hubert/simple_kmeans/dump_km_label.py \
|
| 67 |
+
$tgt_dir/mfcc $train_split $tgt_dir/mfcc/cls$dim 1 0 $tgt_dir/mfcc/cls${dim}_idx
|
| 68 |
+
cp $tgt_dir/mfcc/cls${dim}_idx/${train_split}_0_1.km $tgt_dir/$train_split.km
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_text.sh
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env zsh
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
lg=$1
|
| 8 |
+
text_path=$2
|
| 9 |
+
target_dir=$3
|
| 10 |
+
min_phones=$4
|
| 11 |
+
phonemizer=$5
|
| 12 |
+
lid_path=$6
|
| 13 |
+
sil_prob=$7
|
| 14 |
+
|
| 15 |
+
if [ -z "$lid_path" ]; then
|
| 16 |
+
lid_path="lid.187.bin"
|
| 17 |
+
fi
|
| 18 |
+
|
| 19 |
+
ph_lg=${lg:l}
|
| 20 |
+
if test "$lg" = 'fr'; then
|
| 21 |
+
ph_lg='fr-fr'
|
| 22 |
+
elif test "$lg" = 'en'; then
|
| 23 |
+
ph_lg='en-us'
|
| 24 |
+
elif test "$lg" = 'pt'; then
|
| 25 |
+
ph_lg='pt-br'
|
| 26 |
+
fi
|
| 27 |
+
|
| 28 |
+
ESPEAK_PATH=''
|
| 29 |
+
if test "$phonemizer" = 'espeak'; then
|
| 30 |
+
ESPEAK_PATH=$(which espeak)
|
| 31 |
+
elif test "$phonemizer" = 'espeak-ng'; then
|
| 32 |
+
ESPEAK_PATH=$(which espeak-ng)
|
| 33 |
+
elif test "$phonemizer" = 'G2P'; then
|
| 34 |
+
ESPEAK_PATH=''
|
| 35 |
+
else
|
| 36 |
+
echo "Unknown phonemizer $phonemizer. Valid options are espeak, espean-ng and G2P"
|
| 37 |
+
exit 1
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
echo $lg
|
| 41 |
+
echo $ph_lg
|
| 42 |
+
echo $text_path
|
| 43 |
+
echo $target_dir
|
| 44 |
+
echo "min phone seen threshold is $min_phones"
|
| 45 |
+
|
| 46 |
+
mkdir -p $target_dir
|
| 47 |
+
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
|
| 48 |
+
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
|
| 49 |
+
cut -f1 -d' ' $target_dir/dict.txt | grep -v -x '[[:punct:]]*' | grep -Pv '\d\d\d\d\d+' >! $target_dir/words.txt
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
if [ -z "$ESPEAK_PATH" ]; then
|
| 53 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/g2p_wrd_to_phn.py --compact < $target_dir/words.txt > $target_dir/phones.txt
|
| 54 |
+
else
|
| 55 |
+
# echoing 1 into corpus will prevent the mismatch lines between lexicon and phones in case the phonemizer fails
|
| 56 |
+
one=$(echo "1" | PHONEMIZER_ESPEAK_PATH=$ESPEAK_PATH phonemize -p ' ' -w '' -l $ph_lg --language-switch remove-flags)
|
| 57 |
+
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
|
| 58 |
+
echo "one is ${one}"
|
| 59 |
+
sed -i "s/${one}$//" $target_dir/phones.txt
|
| 60 |
+
fi
|
| 61 |
+
|
| 62 |
+
paste $target_dir/words.txt $target_dir/phones.txt >! $target_dir/lexicon.lst
|
| 63 |
+
|
| 64 |
+
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
|
| 65 |
+
|
| 66 |
+
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
|
| 67 |
+
python $FAIRSEQ_ROOT/examples/wav2vec/unsupervised/scripts/phonemize_with_sil.py -s $sil_prob --surround --lexicon $target_dir/lexicon_filtered.lst < $target_dir/lm.upper.lid.txt >! $target_dir/phones/lm.phones.filtered.txt
|
| 68 |
+
cp $target_dir/phones/dict.txt $target_dir/phones/dict.phn.txt
|
| 69 |
+
echo "<SIL> 0" >> $target_dir/phones/dict.phn.txt
|
| 70 |
+
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
|
| 71 |
+
|
| 72 |
+
$KENLM_ROOT/lmplz -o 4 < $target_dir/lm.upper.lid.txt --discount_fallback --prune 0 0 0 3 >! $target_dir/kenlm.wrd.o40003.arpa
|
| 73 |
+
$KENLM_ROOT/build_binary $target_dir/kenlm.wrd.o40003.arpa $target_dir/kenlm.wrd.o40003.bin
|
| 74 |
+
|
| 75 |
+
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>'"
|
| 76 |
+
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
|
| 77 |
+
|
| 78 |
+
$KENLM_ROOT/lmplz -o 4 < $target_dir/phones/lm.phones.filtered.txt --discount_fallback >! $target_dir/phones/lm.phones.filtered.04.arpa
|
| 79 |
+
$KENLM_ROOT/build_binary $target_dir/phones/lm.phones.filtered.04.arpa $target_dir/phones/lm.phones.filtered.04.bin
|
| 80 |
+
$KENLM_ROOT/lmplz -o 6 < $target_dir/phones/lm.phones.filtered.txt --discount_fallback >! $target_dir/phones/lm.phones.filtered.06.arpa
|
| 81 |
+
$KENLM_ROOT/build_binary $target_dir/phones/lm.phones.filtered.06.arpa $target_dir/phones/lm.phones.filtered.06.bin
|
| 82 |
+
|
| 83 |
+
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>'"
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/prepare_timit.sh
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
timit_root=$1 # assume it is the upper-cased version
|
| 8 |
+
tgt_dir=$2
|
| 9 |
+
model=$3
|
| 10 |
+
|
| 11 |
+
set -eu
|
| 12 |
+
|
| 13 |
+
setups="matched unmatched"
|
| 14 |
+
splits="test valid train train_text"
|
| 15 |
+
|
| 16 |
+
tgt_dir=$(realpath $tgt_dir)
|
| 17 |
+
sph2wav=$KALDI_ROOT/tools/sph2pipe_v2.5/sph2pipe
|
| 18 |
+
wav_dir=$tgt_dir/wav
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
mkdir -p $tgt_dir $wav_dir
|
| 22 |
+
find $timit_root/{TRAIN,TEST} -iname "*.WAV" > $tgt_dir/all_sph.flist
|
| 23 |
+
cat $tgt_dir/all_sph.flist | sed -e 's#//*#/#g' -e 's#.*/\([^/]*\)/\([^/]*\).WAV#\1_\2#g' > $tgt_dir/all.uid
|
| 24 |
+
paste -d' ' $tgt_dir/{all_sph.flist,all.uid} | \
|
| 25 |
+
awk -v sph2wav=$sph2wav -v wav_dir=$wav_dir '{print sph2wav " -f wav " $1 " > " wav_dir "/" $2 ".wav"}' \
|
| 26 |
+
> $tgt_dir/sph2wav.sh
|
| 27 |
+
bash $tgt_dir/sph2wav.sh
|
| 28 |
+
cat $tgt_dir/all.uid | awk -v wav_dir=$(pwd)/$wav_dir '{print $1" "wav_dir"/"$1".wav"}' | sort > $tgt_dir/all_wav.scp
|
| 29 |
+
cut -d' ' -f2 $tgt_dir/all_wav.scp | xargs -I{} soxi -s {} > $tgt_dir/all.dur
|
| 30 |
+
paste -d' ' $tgt_dir/{all_wav.scp,all.dur} > $tgt_dir/all_wav_dur.scp
|
| 31 |
+
rm $tgt_dir/{all.uid,all_sph.flist,sph2wav.sh}
|
| 32 |
+
|
| 33 |
+
find $timit_root/{TRAIN,TEST} -iname "*.PHN" > $tgt_dir/all_phn60.flist
|
| 34 |
+
while read line; do
|
| 35 |
+
if [ ! -f $line ]; then
|
| 36 |
+
>&2 echo "Cannot find transcription file '$line'" && exit 1;
|
| 37 |
+
fi
|
| 38 |
+
cut -f3 -d' ' "$line" | tr '\n' ' ' | perl -ape 's: *$:\n:;'
|
| 39 |
+
done < $tgt_dir/all_phn60.flist > $tgt_dir/all.phn60
|
| 40 |
+
cat $tgt_dir/all_phn60.flist | sed -e 's#//*#/#g' -e 's#.*/\([^/]*\)/\([^/]*\).PHN#\1_\2#g' | \
|
| 41 |
+
paste -d' ' - $tgt_dir/all.phn60 | \
|
| 42 |
+
$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 | \
|
| 43 |
+
sort > $tgt_dir/all.phn
|
| 44 |
+
echo "done preparing wav and 39-phone transcripts"
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
for s in $setups; do
|
| 48 |
+
mkdir -p $tgt_dir/$s
|
| 49 |
+
for x in $splits; do
|
| 50 |
+
uid_path=config/timit_${s}/${x}.uid
|
| 51 |
+
grep -w -f $uid_path $tgt_dir/all.phn | cut -d' ' -f2- > $tgt_dir/$s/$x.phn
|
| 52 |
+
ln -sf $(realpath $tgt_dir/$s/$x.phn) $tgt_dir/$s/$x.wrd
|
| 53 |
+
|
| 54 |
+
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
|
| 55 |
+
done
|
| 56 |
+
|
| 57 |
+
for x in $splits; do
|
| 58 |
+
cat $tgt_dir/$s/$x.phn
|
| 59 |
+
done | tr ' ' '\n' | sort -u | awk '{print $1" "1}' > $tgt_dir/$s/dict.phn.txt
|
| 60 |
+
ln -sf $(realpath $tgt_dir/$s/dict.phn.txt) $tgt_dir/$s/dict.wrd.txt
|
| 61 |
+
done
|
| 62 |
+
echo "done preparing unmatched and matched setups for TIMIT"
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
for s in $setups; do
|
| 66 |
+
zsh scripts/prepare_audio.sh $tgt_dir/$s $tgt_dir/$s/feat $model
|
| 67 |
+
|
| 68 |
+
lm_dir=$tgt_dir/$s/phones
|
| 69 |
+
fst_dir=$tgt_dir/$s/fst/phn_to_phn
|
| 70 |
+
|
| 71 |
+
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
|
| 72 |
+
$KENLM_ROOT/lmplz -o 3 < $tgt_dir/$s/train_text.phn --discount_fallback >$lm_dir/train_text_phn.03.arpa
|
| 73 |
+
$KENLM_ROOT/build_binary $lm_dir/train_text_phn.03.arpa $lm_dir/train_text_phn.03.bin
|
| 74 |
+
$KENLM_ROOT/lmplz -o 4 < $tgt_dir/$s/train_text.phn --discount_fallback >$lm_dir/train_text_phn.04.arpa
|
| 75 |
+
$KENLM_ROOT/build_binary $lm_dir/train_text_phn.04.arpa $lm_dir/train_text_phn.04.bin
|
| 76 |
+
|
| 77 |
+
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
|
| 78 |
+
done
|
| 79 |
+
echo "done preprocessing audio and text for wav2vec-U"
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/remove_silence.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
"""
|
| 8 |
+
get intervals from .vads file, specify output data, and this script removes silences and saves the audio data in out path folder
|
| 9 |
+
paths=shards/train.tsv
|
| 10 |
+
vads=shards/train.vads
|
| 11 |
+
python remove_silence.py --paths $paths --vads $vads
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import os
|
| 15 |
+
import argparse
|
| 16 |
+
import torch
|
| 17 |
+
import torchaudio
|
| 18 |
+
import tqdm
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
parser = argparse.ArgumentParser()
|
| 22 |
+
parser.add_argument("--tsv", default="", type=str)
|
| 23 |
+
parser.add_argument("--vads", default="", type=str)
|
| 24 |
+
parser.add_argument("--out", type=str)
|
| 25 |
+
params = parser.parse_args()
|
| 26 |
+
|
| 27 |
+
# load paths
|
| 28 |
+
paths = []
|
| 29 |
+
with open(params.tsv) as f:
|
| 30 |
+
root = next(f).rstrip()
|
| 31 |
+
for line in f:
|
| 32 |
+
paths.append(os.path.join(root, line.rstrip().split("\t")[0]))
|
| 33 |
+
|
| 34 |
+
# load vads
|
| 35 |
+
list_intervals = []
|
| 36 |
+
with open(params.vads) as f:
|
| 37 |
+
for line in f:
|
| 38 |
+
interval = [
|
| 39 |
+
[int(w.split(":")[0]), int(w.split(":")[1])] for w in line.rstrip().split()
|
| 40 |
+
]
|
| 41 |
+
list_intervals.append(interval)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# load audio and keep only intervals (i.e. remove silences)
|
| 45 |
+
for i in tqdm.trange(len(paths)):
|
| 46 |
+
data, _ = torchaudio.load(paths[i])
|
| 47 |
+
if len(list_intervals[i]) > 0:
|
| 48 |
+
data_filtered = torch.cat(
|
| 49 |
+
[data[0][int(it[0]) : int(it[1])] for it in list_intervals[i]]
|
| 50 |
+
).unsqueeze(0)
|
| 51 |
+
else:
|
| 52 |
+
data_filtered = data
|
| 53 |
+
|
| 54 |
+
# YOU MAY NEED TO MODIFY THIS TO GET THE RIGHT SUBPATH
|
| 55 |
+
# outpath = params.out + '/'.join(paths[i].split('/')[-1])
|
| 56 |
+
outpath = params.out + "/" + "/".join(paths[i].split("/")[-2:])
|
| 57 |
+
|
| 58 |
+
if not os.path.isdir("/".join(outpath.split("/")[:-1])):
|
| 59 |
+
os.makedirs("/".join(outpath.split("/")[:-1]))
|
| 60 |
+
if not os.path.exists(outpath):
|
| 61 |
+
torchaudio.save(outpath, data_filtered, sample_rate=16000)
|
| 62 |
+
else:
|
| 63 |
+
print(outpath, "exists!")
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/vads.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
from copy import deepcopy
|
| 11 |
+
from scipy.signal import lfilter
|
| 12 |
+
|
| 13 |
+
import numpy as np
|
| 14 |
+
from tqdm import tqdm
|
| 15 |
+
import soundfile as sf
|
| 16 |
+
import os.path as osp
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def get_parser():
|
| 20 |
+
parser = argparse.ArgumentParser(description="compute vad segments")
|
| 21 |
+
parser.add_argument(
|
| 22 |
+
"--rvad-home",
|
| 23 |
+
"-r",
|
| 24 |
+
help="path to rvad home (see https://github.com/zhenghuatan/rVADfast)",
|
| 25 |
+
required=True,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
return parser
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def rvad(speechproc, path):
|
| 32 |
+
winlen, ovrlen, pre_coef, nfilter, nftt = 0.025, 0.01, 0.97, 20, 512
|
| 33 |
+
ftThres = 0.5
|
| 34 |
+
vadThres = 0.4
|
| 35 |
+
opts = 1
|
| 36 |
+
|
| 37 |
+
data, fs = sf.read(path)
|
| 38 |
+
assert fs == 16_000, "sample rate must be 16khz"
|
| 39 |
+
ft, flen, fsh10, nfr10 = speechproc.sflux(data, fs, winlen, ovrlen, nftt)
|
| 40 |
+
|
| 41 |
+
# --spectral flatness --
|
| 42 |
+
pv01 = np.zeros(ft.shape[0])
|
| 43 |
+
pv01[np.less_equal(ft, ftThres)] = 1
|
| 44 |
+
pitch = deepcopy(ft)
|
| 45 |
+
|
| 46 |
+
pvblk = speechproc.pitchblockdetect(pv01, pitch, nfr10, opts)
|
| 47 |
+
|
| 48 |
+
# --filtering--
|
| 49 |
+
ENERGYFLOOR = np.exp(-50)
|
| 50 |
+
b = np.array([0.9770, -0.9770])
|
| 51 |
+
a = np.array([1.0000, -0.9540])
|
| 52 |
+
fdata = lfilter(b, a, data, axis=0)
|
| 53 |
+
|
| 54 |
+
# --pass 1--
|
| 55 |
+
noise_samp, noise_seg, n_noise_samp = speechproc.snre_highenergy(
|
| 56 |
+
fdata, nfr10, flen, fsh10, ENERGYFLOOR, pv01, pvblk
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
# sets noisy segments to zero
|
| 60 |
+
for j in range(n_noise_samp):
|
| 61 |
+
fdata[range(int(noise_samp[j, 0]), int(noise_samp[j, 1]) + 1)] = 0
|
| 62 |
+
|
| 63 |
+
vad_seg = speechproc.snre_vad(
|
| 64 |
+
fdata, nfr10, flen, fsh10, ENERGYFLOOR, pv01, pvblk, vadThres
|
| 65 |
+
)
|
| 66 |
+
return vad_seg, data
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def main():
|
| 70 |
+
parser = get_parser()
|
| 71 |
+
args = parser.parse_args()
|
| 72 |
+
|
| 73 |
+
sys.path.append(args.rvad_home)
|
| 74 |
+
import speechproc
|
| 75 |
+
|
| 76 |
+
stride = 160
|
| 77 |
+
lines = sys.stdin.readlines()
|
| 78 |
+
root = lines[0].rstrip()
|
| 79 |
+
for fpath in tqdm(lines[1:]):
|
| 80 |
+
path = osp.join(root, fpath.split()[0])
|
| 81 |
+
vads, wav = rvad(speechproc, path)
|
| 82 |
+
|
| 83 |
+
start = None
|
| 84 |
+
vad_segs = []
|
| 85 |
+
for i, v in enumerate(vads):
|
| 86 |
+
if start is None and v == 1:
|
| 87 |
+
start = i * stride
|
| 88 |
+
elif start is not None and v == 0:
|
| 89 |
+
vad_segs.append((start, i * stride))
|
| 90 |
+
start = None
|
| 91 |
+
if start is not None:
|
| 92 |
+
vad_segs.append((start, len(wav)))
|
| 93 |
+
|
| 94 |
+
print(" ".join(f"{v[0]}:{v[1]}" for v in vad_segs))
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
if __name__ == "__main__":
|
| 98 |
+
main()
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wav2vec_apply_cluster_faiss.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import os
|
| 9 |
+
import os.path as osp
|
| 10 |
+
import numpy as np
|
| 11 |
+
import tqdm
|
| 12 |
+
import torch
|
| 13 |
+
import sys
|
| 14 |
+
|
| 15 |
+
import faiss
|
| 16 |
+
import torch.nn.functional as F
|
| 17 |
+
|
| 18 |
+
from wav2vec_cluster_faiss import parse_faiss_specs, Wav2VecFeatureReader
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_parser():
|
| 22 |
+
parser = argparse.ArgumentParser(description="apply clusters")
|
| 23 |
+
# fmt: off
|
| 24 |
+
parser.add_argument('data', help='location of tsv files')
|
| 25 |
+
parser.add_argument('--split', help='split to process', required=True)
|
| 26 |
+
parser.add_argument('--labels', help='split to process', default="phn")
|
| 27 |
+
parser.add_argument('--path', help='path to pca and centroids', required=True)
|
| 28 |
+
parser.add_argument('--checkpoint', type=str, help='checkpoint for wav2vec model (if using wav2vec features)', required=True)
|
| 29 |
+
parser.add_argument('--layer', '-l', type=int, help='which layer to read', default=14)
|
| 30 |
+
parser.add_argument('--max-tsz', type=int, help='batch kmeans up to this much', default=14)
|
| 31 |
+
# fmt: on
|
| 32 |
+
|
| 33 |
+
return parser
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def get_iterator(args):
|
| 37 |
+
label_path = osp.join(args.data, f"{args.split}.{args.labels}")
|
| 38 |
+
if osp.exists(label_path):
|
| 39 |
+
lp = open(label_path, "r")
|
| 40 |
+
else:
|
| 41 |
+
lp = None
|
| 42 |
+
|
| 43 |
+
with open(osp.join(args.data, f"{args.split}.tsv"), "r") as fp:
|
| 44 |
+
lines = fp.read().split("\n")
|
| 45 |
+
root = lines.pop(0).strip()
|
| 46 |
+
files = [line.rstrip() for line in lines if len(line) > 0]
|
| 47 |
+
|
| 48 |
+
if lp is not None:
|
| 49 |
+
lbls = [line.rstrip() for line in lp]
|
| 50 |
+
else:
|
| 51 |
+
lbls = [None] * len(files)
|
| 52 |
+
|
| 53 |
+
num = len(files)
|
| 54 |
+
reader = Wav2VecFeatureReader(args.checkpoint, args.layer)
|
| 55 |
+
|
| 56 |
+
def iterate():
|
| 57 |
+
for fname, lbl in zip(files, lbls):
|
| 58 |
+
file = osp.join(root, fname.split("\t")[0])
|
| 59 |
+
feats = reader.get_feats(file)
|
| 60 |
+
yield feats.data, fname, lbl
|
| 61 |
+
|
| 62 |
+
return iterate, num, root
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def main():
|
| 66 |
+
parser = get_parser()
|
| 67 |
+
args = parser.parse_args()
|
| 68 |
+
|
| 69 |
+
spec = osp.basename(args.path)
|
| 70 |
+
|
| 71 |
+
try:
|
| 72 |
+
faiss_spec = parse_faiss_specs(spec.rstrip("/"))[0]
|
| 73 |
+
except:
|
| 74 |
+
print(spec)
|
| 75 |
+
raise
|
| 76 |
+
|
| 77 |
+
print("Faiss Spec:", faiss_spec, file=sys.stderr)
|
| 78 |
+
|
| 79 |
+
if faiss_spec.pca:
|
| 80 |
+
A = torch.from_numpy(np.load(osp.join(args.path, "pca_A.npy"))).cuda()
|
| 81 |
+
b = torch.from_numpy(np.load(osp.join(args.path, "pca_b.npy"))).cuda()
|
| 82 |
+
print("Loaded PCA", file=sys.stderr)
|
| 83 |
+
|
| 84 |
+
centroids = np.load(osp.join(args.path, "centroids.npy"))
|
| 85 |
+
print("Loaded centroids", centroids.shape, file=sys.stderr)
|
| 86 |
+
|
| 87 |
+
res = faiss.StandardGpuResources()
|
| 88 |
+
index_flat = (
|
| 89 |
+
faiss.IndexFlatL2(centroids.shape[1])
|
| 90 |
+
if not faiss_spec.sphere
|
| 91 |
+
else faiss.IndexFlatIP(centroids.shape[1])
|
| 92 |
+
)
|
| 93 |
+
faiss_index = faiss.index_cpu_to_gpu(res, 0, index_flat)
|
| 94 |
+
faiss_index.add(centroids)
|
| 95 |
+
|
| 96 |
+
generator, num, root = get_iterator(args)
|
| 97 |
+
iterator = generator()
|
| 98 |
+
|
| 99 |
+
had_labels = False
|
| 100 |
+
label_path = osp.join(args.path, f"{args.split}.{args.labels}")
|
| 101 |
+
|
| 102 |
+
with torch.no_grad():
|
| 103 |
+
with open(osp.join(args.path, f"{args.split}.src"), "w") as fp, open(
|
| 104 |
+
osp.join(args.path, f"{args.split}.tsv"), "w"
|
| 105 |
+
) as pp, open(label_path, "w") as lp:
|
| 106 |
+
print(root, file=pp)
|
| 107 |
+
for f, fname, lbl in tqdm.tqdm(iterator, total=num):
|
| 108 |
+
if faiss_spec.pca:
|
| 109 |
+
f = torch.mm(f, A) + b
|
| 110 |
+
if faiss_spec.norm:
|
| 111 |
+
f = F.normalize(f, p=2, dim=-1)
|
| 112 |
+
|
| 113 |
+
f = f.cpu().numpy()
|
| 114 |
+
|
| 115 |
+
_, z = faiss_index.search(f, 1)
|
| 116 |
+
|
| 117 |
+
print(" ".join(str(x.item()) for x in z), file=fp)
|
| 118 |
+
print(fname, file=pp)
|
| 119 |
+
|
| 120 |
+
if lbl is not None:
|
| 121 |
+
print(lbl, file=lp)
|
| 122 |
+
had_labels = True
|
| 123 |
+
if not had_labels:
|
| 124 |
+
os.remove(label_path)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
if __name__ == "__main__":
|
| 128 |
+
main()
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wav2vec_cluster_faiss.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import gc
|
| 9 |
+
import os
|
| 10 |
+
import os.path as osp
|
| 11 |
+
import random
|
| 12 |
+
import numpy as np
|
| 13 |
+
import tqdm
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
from collections import namedtuple
|
| 17 |
+
|
| 18 |
+
import faiss
|
| 19 |
+
|
| 20 |
+
import fairseq
|
| 21 |
+
import soundfile as sf
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def get_parser():
|
| 25 |
+
parser = argparse.ArgumentParser(
|
| 26 |
+
description="compute kmeans codebook from kaldi-computed feats"
|
| 27 |
+
)
|
| 28 |
+
# fmt: off
|
| 29 |
+
parser.add_argument('data', help='location of tsv files')
|
| 30 |
+
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
| 31 |
+
parser.add_argument('--checkpoint', type=str, help='checkpoint for wav2vec model (if using wav2vec features)', required=True)
|
| 32 |
+
parser.add_argument('--sample-pct', '-r', type=float, help='percentage of timesteps to sample', default=0)
|
| 33 |
+
parser.add_argument('--layer', '-l', type=int, help='which layer to read', default=14)
|
| 34 |
+
parser.add_argument('--faiss-specs', '-f', type=str,
|
| 35 |
+
help='faiss index specs; separated by space '
|
| 36 |
+
'format is: PCAx_NORM_CLUSx_SPHERICAL -> '
|
| 37 |
+
'PCAx if exists first apply PCA '
|
| 38 |
+
'NORM if exists, normalize the vector by L2 norm '
|
| 39 |
+
'CLUSx must exist, cluster to x clusters '
|
| 40 |
+
'SPEHRICAL if exists, apply spherical kmeans',
|
| 41 |
+
default='l2')
|
| 42 |
+
# fmt: on
|
| 43 |
+
|
| 44 |
+
return parser
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
faiss_spec = namedtuple("faiss_spec", ["pca", "norm", "n_clus", "sphere", "spec_str"])
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def parse_faiss_specs(specs_str):
|
| 51 |
+
specs = []
|
| 52 |
+
for ss in specs_str.split():
|
| 53 |
+
comps = ss.split("_")
|
| 54 |
+
pca = 0
|
| 55 |
+
norm = False
|
| 56 |
+
n_clus = 0
|
| 57 |
+
sphere = False
|
| 58 |
+
for c in comps:
|
| 59 |
+
if c.startswith("PCA"):
|
| 60 |
+
pca = int(c[3:])
|
| 61 |
+
elif c == "NORM":
|
| 62 |
+
norm = True
|
| 63 |
+
elif c.startswith("CLUS"):
|
| 64 |
+
n_clus = int(c[4:])
|
| 65 |
+
elif c == "SPHERICAL":
|
| 66 |
+
sphere = True
|
| 67 |
+
assert n_clus > 0
|
| 68 |
+
specs.append(
|
| 69 |
+
faiss_spec(pca=pca, norm=norm, n_clus=n_clus, sphere=sphere, spec_str=ss)
|
| 70 |
+
)
|
| 71 |
+
return specs
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class Wav2VecFeatureReader(object):
|
| 75 |
+
def __init__(self, cp_file, layer):
|
| 76 |
+
state = fairseq.checkpoint_utils.load_checkpoint_to_cpu(cp_file)
|
| 77 |
+
|
| 78 |
+
self.layer = layer
|
| 79 |
+
|
| 80 |
+
if "cfg" in state:
|
| 81 |
+
w2v_args = state["cfg"]
|
| 82 |
+
task = fairseq.tasks.setup_task(w2v_args.task)
|
| 83 |
+
model = task.build_model(w2v_args.model)
|
| 84 |
+
else:
|
| 85 |
+
w2v_args = state["args"]
|
| 86 |
+
task = fairseq.tasks.setup_task(w2v_args)
|
| 87 |
+
model = task.build_model(w2v_args)
|
| 88 |
+
model.load_state_dict(state["model"], strict=True)
|
| 89 |
+
model.eval()
|
| 90 |
+
model.cuda()
|
| 91 |
+
self.model = model
|
| 92 |
+
|
| 93 |
+
def read_audio(self, fname):
|
| 94 |
+
"""Load an audio file and return PCM along with the sample rate"""
|
| 95 |
+
wav, sr = sf.read(fname)
|
| 96 |
+
assert sr == 16e3
|
| 97 |
+
|
| 98 |
+
return wav
|
| 99 |
+
|
| 100 |
+
def get_feats(self, loc):
|
| 101 |
+
x = self.read_audio(loc)
|
| 102 |
+
with torch.no_grad():
|
| 103 |
+
source = torch.from_numpy(x).view(1, -1).float().cuda()
|
| 104 |
+
res = self.model(
|
| 105 |
+
source=source, mask=False, features_only=True, layer=self.layer
|
| 106 |
+
)
|
| 107 |
+
return res["layer_results"][self.layer][0].squeeze(1)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def get_iterator(args):
|
| 111 |
+
with open(args.data, "r") as fp:
|
| 112 |
+
lines = fp.read().split("\n")
|
| 113 |
+
root = lines.pop(0).strip()
|
| 114 |
+
files = [osp.join(root, line.split("\t")[0]) for line in lines if len(line) > 0]
|
| 115 |
+
|
| 116 |
+
if getattr(args, "sample_pct", 0) > 0:
|
| 117 |
+
files = random.sample(files, int(args.sample_pct * len(files)))
|
| 118 |
+
num = len(files)
|
| 119 |
+
reader = Wav2VecFeatureReader(args.checkpoint, args.layer)
|
| 120 |
+
|
| 121 |
+
def iterate():
|
| 122 |
+
for fname in files:
|
| 123 |
+
feats = reader.get_feats(fname)
|
| 124 |
+
yield feats.cpu().numpy()
|
| 125 |
+
|
| 126 |
+
return iterate, num
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def main():
|
| 130 |
+
parser = get_parser()
|
| 131 |
+
args = parser.parse_args()
|
| 132 |
+
|
| 133 |
+
faiss_specs = parse_faiss_specs(args.faiss_specs)
|
| 134 |
+
print("Faiss Specs:", faiss_specs)
|
| 135 |
+
|
| 136 |
+
feat_path = osp.join(args.save_dir, "features")
|
| 137 |
+
if osp.exists(feat_path + ".npy"):
|
| 138 |
+
feats = np.load(feat_path + ".npy")
|
| 139 |
+
else:
|
| 140 |
+
generator, num = get_iterator(args)
|
| 141 |
+
iterator = generator()
|
| 142 |
+
|
| 143 |
+
feats = []
|
| 144 |
+
for f in tqdm.tqdm(iterator, total=num):
|
| 145 |
+
feats.append(f)
|
| 146 |
+
|
| 147 |
+
del iterator
|
| 148 |
+
del generator
|
| 149 |
+
|
| 150 |
+
feats = np.concatenate(feats)
|
| 151 |
+
|
| 152 |
+
print(feats.shape)
|
| 153 |
+
|
| 154 |
+
os.makedirs(args.save_dir, exist_ok=True)
|
| 155 |
+
# np.save(feat_path, feats)
|
| 156 |
+
|
| 157 |
+
gc.collect()
|
| 158 |
+
torch.cuda.empty_cache()
|
| 159 |
+
|
| 160 |
+
reload = False
|
| 161 |
+
for spec in faiss_specs:
|
| 162 |
+
print("Processing spec", spec)
|
| 163 |
+
|
| 164 |
+
if reload:
|
| 165 |
+
print("Reloading...")
|
| 166 |
+
del feats
|
| 167 |
+
gc.collect()
|
| 168 |
+
feats = np.load(feat_path + ".npy")
|
| 169 |
+
|
| 170 |
+
save_path = osp.join(args.save_dir, spec.spec_str)
|
| 171 |
+
os.makedirs(save_path, exist_ok=True)
|
| 172 |
+
d = feats.shape[-1]
|
| 173 |
+
x = feats
|
| 174 |
+
if spec.pca > 0:
|
| 175 |
+
print("Computing PCA")
|
| 176 |
+
pca = faiss.PCAMatrix(d, spec.pca)
|
| 177 |
+
pca.train(x)
|
| 178 |
+
d = spec.pca
|
| 179 |
+
b = faiss.vector_to_array(pca.b)
|
| 180 |
+
A = faiss.vector_to_array(pca.A).reshape(pca.d_out, pca.d_in)
|
| 181 |
+
np.save(osp.join(save_path, "pca_A"), A.T)
|
| 182 |
+
np.save(osp.join(save_path, "pca_b"), b)
|
| 183 |
+
print("Applying PCA")
|
| 184 |
+
x = pca.apply_py(x)
|
| 185 |
+
|
| 186 |
+
if spec.norm:
|
| 187 |
+
reload = spec.pca <= 0
|
| 188 |
+
print("Normalizing")
|
| 189 |
+
faiss.normalize_L2(x)
|
| 190 |
+
|
| 191 |
+
print("Computing kmeans")
|
| 192 |
+
kmeans = faiss.Kmeans(
|
| 193 |
+
d,
|
| 194 |
+
spec.n_clus,
|
| 195 |
+
niter=50,
|
| 196 |
+
verbose=True,
|
| 197 |
+
spherical=spec.sphere,
|
| 198 |
+
max_points_per_centroid=feats.shape[0],
|
| 199 |
+
gpu=True,
|
| 200 |
+
nredo=3,
|
| 201 |
+
)
|
| 202 |
+
kmeans.train(x)
|
| 203 |
+
np.save(osp.join(save_path, "centroids"), kmeans.centroids)
|
| 204 |
+
del kmeans
|
| 205 |
+
del x
|
| 206 |
+
gc.collect()
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
if __name__ == "__main__":
|
| 210 |
+
main()
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wav2vec_extract_features.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import os
|
| 9 |
+
import os.path as osp
|
| 10 |
+
import tqdm
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
from shutil import copyfile
|
| 14 |
+
|
| 15 |
+
from npy_append_array import NpyAppendArray
|
| 16 |
+
|
| 17 |
+
import fairseq
|
| 18 |
+
import soundfile as sf
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_parser():
|
| 22 |
+
parser = argparse.ArgumentParser(
|
| 23 |
+
description="compute kmeans codebook from kaldi-computed feats"
|
| 24 |
+
)
|
| 25 |
+
# fmt: off
|
| 26 |
+
parser.add_argument('data', help='location of tsv files')
|
| 27 |
+
parser.add_argument('--split', help='which split to read', required=True)
|
| 28 |
+
parser.add_argument('--save-dir', help='where to save the output', required=True)
|
| 29 |
+
parser.add_argument('--checkpoint', type=str, help='checkpoint for wav2vec ctc model', required=True)
|
| 30 |
+
parser.add_argument('--layer', type=int, default=14, help='which layer to use')
|
| 31 |
+
# fmt: on
|
| 32 |
+
|
| 33 |
+
return parser
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class Wav2VecFeatureReader(object):
|
| 37 |
+
def __init__(self, cp_file, layer):
|
| 38 |
+
model, cfg, task = fairseq.checkpoint_utils.load_model_ensemble_and_task(
|
| 39 |
+
[cp_file]
|
| 40 |
+
)
|
| 41 |
+
model = model[0]
|
| 42 |
+
model.eval()
|
| 43 |
+
model.cuda()
|
| 44 |
+
self.model = model
|
| 45 |
+
self.task = task
|
| 46 |
+
self.layer = layer
|
| 47 |
+
|
| 48 |
+
def read_audio(self, fname):
|
| 49 |
+
"""Load an audio file and return PCM along with the sample rate"""
|
| 50 |
+
wav, sr = sf.read(fname)
|
| 51 |
+
assert sr == 16e3
|
| 52 |
+
|
| 53 |
+
return wav
|
| 54 |
+
|
| 55 |
+
def get_feats(self, loc):
|
| 56 |
+
x = self.read_audio(loc)
|
| 57 |
+
with torch.no_grad():
|
| 58 |
+
source = torch.from_numpy(x).float().cuda()
|
| 59 |
+
if self.task.cfg.normalize:
|
| 60 |
+
assert source.dim() == 1, source.dim()
|
| 61 |
+
with torch.no_grad():
|
| 62 |
+
source = F.layer_norm(source, source.shape)
|
| 63 |
+
source = source.view(1, -1)
|
| 64 |
+
|
| 65 |
+
m_res = self.model(source=source, mask=False, features_only=True, layer=self.layer)
|
| 66 |
+
return m_res["x"].squeeze(0).cpu()
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def get_iterator(args):
|
| 70 |
+
with open(osp.join(args.data, args.split) + ".tsv", "r") as fp:
|
| 71 |
+
lines = fp.read().split("\n")
|
| 72 |
+
root = lines.pop(0).strip()
|
| 73 |
+
files = [osp.join(root, line.split("\t")[0]) for line in lines if len(line) > 0]
|
| 74 |
+
|
| 75 |
+
num = len(files)
|
| 76 |
+
reader = Wav2VecFeatureReader(args.checkpoint, args.layer)
|
| 77 |
+
|
| 78 |
+
def iterate():
|
| 79 |
+
for fname in files:
|
| 80 |
+
w2v_feats = reader.get_feats(fname)
|
| 81 |
+
yield w2v_feats
|
| 82 |
+
|
| 83 |
+
return iterate, num
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def main():
|
| 87 |
+
parser = get_parser()
|
| 88 |
+
args = parser.parse_args()
|
| 89 |
+
|
| 90 |
+
os.makedirs(args.save_dir, exist_ok=True)
|
| 91 |
+
|
| 92 |
+
def create_files(dest):
|
| 93 |
+
copyfile(osp.join(args.data, args.split) + ".tsv", dest + ".tsv")
|
| 94 |
+
if osp.exists(osp.join(args.data, args.split) + ".wrd"):
|
| 95 |
+
copyfile(osp.join(args.data, args.split) + ".wrd", dest + ".wrd")
|
| 96 |
+
if osp.exists(osp.join(args.data, args.split) + ".phn"):
|
| 97 |
+
copyfile(osp.join(args.data, args.split) + ".phn", dest + ".phn")
|
| 98 |
+
|
| 99 |
+
if osp.exists(dest + ".npy"):
|
| 100 |
+
os.remove(dest + ".npy")
|
| 101 |
+
npaa = NpyAppendArray(dest + ".npy")
|
| 102 |
+
return npaa
|
| 103 |
+
|
| 104 |
+
save_path = osp.join(args.save_dir, args.split)
|
| 105 |
+
npaa = create_files(save_path)
|
| 106 |
+
|
| 107 |
+
generator, num = get_iterator(args)
|
| 108 |
+
iterator = generator()
|
| 109 |
+
|
| 110 |
+
with open(save_path + ".lengths", "w") as l_f:
|
| 111 |
+
for w2v_feats in tqdm.tqdm(iterator, total=num):
|
| 112 |
+
print(len(w2v_feats), file=l_f)
|
| 113 |
+
|
| 114 |
+
if len(w2v_feats) > 0:
|
| 115 |
+
npaa.append(w2v_feats.numpy())
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
if __name__ == "__main__":
|
| 119 |
+
main()
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wer.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
"""
|
| 8 |
+
Implement unsupervised metric for decoding hyperparameter selection:
|
| 9 |
+
$$ alpha * LM_PPL + ViterbitUER(%) * 100 $$
|
| 10 |
+
"""
|
| 11 |
+
import argparse
|
| 12 |
+
import logging
|
| 13 |
+
import sys
|
| 14 |
+
|
| 15 |
+
import editdistance
|
| 16 |
+
|
| 17 |
+
logging.root.setLevel(logging.INFO)
|
| 18 |
+
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
| 19 |
+
logger = logging.getLogger(__name__)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def get_parser():
|
| 23 |
+
parser = argparse.ArgumentParser()
|
| 24 |
+
parser.add_argument("-s", "--hypo", help="hypo transcription", required=True)
|
| 25 |
+
parser.add_argument(
|
| 26 |
+
"-r", "--reference", help="reference transcription", required=True
|
| 27 |
+
)
|
| 28 |
+
return parser
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def compute_wer(ref_uid_to_tra, hyp_uid_to_tra, g2p):
|
| 32 |
+
d_cnt = 0
|
| 33 |
+
w_cnt = 0
|
| 34 |
+
w_cnt_h = 0
|
| 35 |
+
for uid in hyp_uid_to_tra:
|
| 36 |
+
ref = ref_uid_to_tra[uid].split()
|
| 37 |
+
if g2p is not None:
|
| 38 |
+
hyp = g2p(hyp_uid_to_tra[uid])
|
| 39 |
+
hyp = [p for p in hyp if p != "'" and p != " "]
|
| 40 |
+
hyp = [p[:-1] if p[-1].isnumeric() else p for p in hyp]
|
| 41 |
+
else:
|
| 42 |
+
hyp = hyp_uid_to_tra[uid].split()
|
| 43 |
+
d_cnt += editdistance.eval(ref, hyp)
|
| 44 |
+
w_cnt += len(ref)
|
| 45 |
+
w_cnt_h += len(hyp)
|
| 46 |
+
wer = float(d_cnt) / w_cnt
|
| 47 |
+
logger.debug(
|
| 48 |
+
(
|
| 49 |
+
f"wer = {wer * 100:.2f}%; num. of ref words = {w_cnt}; "
|
| 50 |
+
f"num. of hyp words = {w_cnt_h}; num. of sentences = {len(ref_uid_to_tra)}"
|
| 51 |
+
)
|
| 52 |
+
)
|
| 53 |
+
return wer
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def main():
|
| 57 |
+
args = get_parser().parse_args()
|
| 58 |
+
|
| 59 |
+
errs = 0
|
| 60 |
+
count = 0
|
| 61 |
+
with open(args.hypo, "r") as hf, open(args.reference, "r") as rf:
|
| 62 |
+
for h, r in zip(hf, rf):
|
| 63 |
+
h = h.rstrip().split()
|
| 64 |
+
r = r.rstrip().split()
|
| 65 |
+
errs += editdistance.eval(r, h)
|
| 66 |
+
count += len(r)
|
| 67 |
+
|
| 68 |
+
logger.info(f"UER: {errs / count * 100:.2f}%")
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
if __name__ == "__main__":
|
| 72 |
+
main()
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def load_tra(tra_path):
|
| 76 |
+
with open(tra_path, "r") as f:
|
| 77 |
+
uid_to_tra = {}
|
| 78 |
+
for line in f:
|
| 79 |
+
uid, tra = line.split(None, 1)
|
| 80 |
+
uid_to_tra[uid] = tra
|
| 81 |
+
logger.debug(f"loaded {len(uid_to_tra)} utterances from {tra_path}")
|
| 82 |
+
return uid_to_tra
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/scripts/wrd_to_ltr.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3 -u
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the MIT license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
import sys
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
for line in sys.stdin:
|
| 12 |
+
print(" ".join(list(line.strip().replace(" ", "|"))) + " |")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
if __name__ == "__main__":
|
| 16 |
+
main()
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/tasks/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from .unpaired_audio_text import UnpairedAudioText
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
"UnpairedAudioText",
|
| 11 |
+
]
|
omni_speech/infer/fairseq/examples/wav2vec/unsupervised/tasks/unpaired_audio_text.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) 2017-present, Facebook, Inc.
|
| 2 |
+
# All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the license found in the LICENSE file in
|
| 5 |
+
# the root directory of this source tree. An additional grant of patent rights
|
| 6 |
+
# can be found in the PATENTS file in the same directory.
|
| 7 |
+
|
| 8 |
+
from dataclasses import dataclass, field
|
| 9 |
+
import logging
|
| 10 |
+
import math
|
| 11 |
+
import os
|
| 12 |
+
from typing import Optional
|
| 13 |
+
import torch
|
| 14 |
+
|
| 15 |
+
from fairseq.logging import metrics
|
| 16 |
+
from fairseq.tasks import FairseqTask, register_task
|
| 17 |
+
from ..data import ExtractedFeaturesDataset, RandomInputDataset
|
| 18 |
+
|
| 19 |
+
from fairseq.data import (
|
| 20 |
+
Dictionary,
|
| 21 |
+
data_utils,
|
| 22 |
+
StripTokenDataset,
|
| 23 |
+
)
|
| 24 |
+
from fairseq.dataclass import FairseqDataclass
|
| 25 |
+
from fairseq.distributed.utils import get_data_parallel_world_size
|
| 26 |
+
from omegaconf import MISSING
|
| 27 |
+
|
| 28 |
+
from examples.speech_recognition.kaldi.kaldi_decoder import (
|
| 29 |
+
KaldiDecoder,
|
| 30 |
+
KaldiDecoderConfig,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
logger = logging.getLogger(__name__)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@dataclass
|
| 38 |
+
class DecodingConfig(FairseqDataclass):
|
| 39 |
+
kenlm_path: Optional[str] = None
|
| 40 |
+
lm_weight: float = 0
|
| 41 |
+
blank_weight: float = 0
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@dataclass
|
| 45 |
+
class UnpairedAudioTextConfig(FairseqDataclass):
|
| 46 |
+
data: str = field(
|
| 47 |
+
default=MISSING, metadata={"help": "path to data directory containing audio"}
|
| 48 |
+
)
|
| 49 |
+
text_data: str = field(
|
| 50 |
+
default=MISSING, metadata={"help": "path to data directory containing text"}
|
| 51 |
+
)
|
| 52 |
+
max_length: Optional[int] = None
|
| 53 |
+
labels: Optional[str] = field(
|
| 54 |
+
default=None,
|
| 55 |
+
metadata={"help": "extension of the label file to load, used for fine-tuning"},
|
| 56 |
+
)
|
| 57 |
+
aux_target_postfix: Optional[str] = field(
|
| 58 |
+
default=None,
|
| 59 |
+
metadata={"help": "auxaliry target filename extension"},
|
| 60 |
+
)
|
| 61 |
+
unfiltered: bool = field(
|
| 62 |
+
default=False, metadata={"help": "load data with _unfiltered suffix"}
|
| 63 |
+
)
|
| 64 |
+
ctc_eval: bool = field(
|
| 65 |
+
default=False, metadata={"help": "eval UER as if computed by CTC"}
|
| 66 |
+
)
|
| 67 |
+
sort_by_length: bool = field(
|
| 68 |
+
default=True, metadata={"help": "sort examples by length of audio timesteps"}
|
| 69 |
+
)
|
| 70 |
+
shuffle: bool = field(default=True, metadata={"help": "shuffle examples"})
|
| 71 |
+
append_eos: bool = field(default=False, metadata={"help": "append eos"})
|
| 72 |
+
uppercase: Optional[bool] = field(
|
| 73 |
+
default=False, metadata={"help": "uppercase for LM score computation"}
|
| 74 |
+
)
|
| 75 |
+
skipwords: Optional[str] = field(
|
| 76 |
+
default="",
|
| 77 |
+
metadata={
|
| 78 |
+
"help": "comma-separated words to be removed for LM score computation"
|
| 79 |
+
},
|
| 80 |
+
)
|
| 81 |
+
kenlm_path: Optional[str] = None
|
| 82 |
+
vocab_usage_power: float = 2
|
| 83 |
+
|
| 84 |
+
word_decoder_config: Optional[KaldiDecoderConfig] = None
|
| 85 |
+
word_kenlm_path: Optional[str] = None
|
| 86 |
+
|
| 87 |
+
decoding_config: DecodingConfig = DecodingConfig()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
@register_task("unpaired_audio_text", dataclass=UnpairedAudioTextConfig)
|
| 91 |
+
class UnpairedAudioText(FairseqTask):
|
| 92 |
+
""" """
|
| 93 |
+
|
| 94 |
+
cfg: UnpairedAudioTextConfig
|
| 95 |
+
|
| 96 |
+
def __init__(
|
| 97 |
+
self,
|
| 98 |
+
cfg: UnpairedAudioTextConfig,
|
| 99 |
+
source_dictionary=None,
|
| 100 |
+
target_dictionary=None,
|
| 101 |
+
):
|
| 102 |
+
super().__init__(cfg)
|
| 103 |
+
|
| 104 |
+
self._target_dictionary = target_dictionary
|
| 105 |
+
self._source_dictionary = source_dictionary
|
| 106 |
+
self.num_symbols = (
|
| 107 |
+
len([s for s in target_dictionary.symbols if not s.startswith("madeup")])
|
| 108 |
+
- target_dictionary.nspecial
|
| 109 |
+
)
|
| 110 |
+
self.sil_id = (
|
| 111 |
+
target_dictionary.index("<SIL>") if "<SIL>" in target_dictionary else -1
|
| 112 |
+
)
|
| 113 |
+
self.kenlm = None
|
| 114 |
+
if cfg.kenlm_path is not None:
|
| 115 |
+
import kenlm
|
| 116 |
+
|
| 117 |
+
self.kenlm = kenlm.Model(cfg.kenlm_path)
|
| 118 |
+
|
| 119 |
+
self.word_kenlm = None
|
| 120 |
+
if cfg.word_kenlm_path is not None:
|
| 121 |
+
import kenlm
|
| 122 |
+
|
| 123 |
+
self.word_kenlm = kenlm.Model(cfg.word_kenlm_path)
|
| 124 |
+
|
| 125 |
+
self.uppercase = cfg.uppercase
|
| 126 |
+
self.skipwords = set(cfg.skipwords.split(","))
|
| 127 |
+
|
| 128 |
+
def str_postprocess(s):
|
| 129 |
+
s = " ".join(w for w in s.split() if w not in self.skipwords)
|
| 130 |
+
s = s.upper() if self.uppercase else s
|
| 131 |
+
return s
|
| 132 |
+
|
| 133 |
+
self.str_postprocess = str_postprocess
|
| 134 |
+
self.compute_lm_score = lambda s: self.kenlm.score(self.str_postprocess(s))
|
| 135 |
+
|
| 136 |
+
self.compute_word_score = None
|
| 137 |
+
if cfg.word_decoder_config is not None:
|
| 138 |
+
self.kaldi_decoder = KaldiDecoder(cfg.word_decoder_config, beam=10)
|
| 139 |
+
|
| 140 |
+
def compute_word_score(logits, padding):
|
| 141 |
+
res = self.kaldi_decoder.decode(logits, padding)
|
| 142 |
+
for r in res:
|
| 143 |
+
r = r.result()
|
| 144 |
+
assert len(r) == 1
|
| 145 |
+
r = r[0]
|
| 146 |
+
yield r["score"], r["words"]
|
| 147 |
+
|
| 148 |
+
self.compute_word_score = compute_word_score
|
| 149 |
+
|
| 150 |
+
@classmethod
|
| 151 |
+
def setup_task(cls, cfg: UnpairedAudioTextConfig, **kwargs):
|
| 152 |
+
"""Setup the task (e.g., load dictionaries).
|
| 153 |
+
|
| 154 |
+
Args:
|
| 155 |
+
cfg (AudioPretrainingConfig): configuration of this task
|
| 156 |
+
"""
|
| 157 |
+
|
| 158 |
+
dict_path = os.path.join(cfg.text_data, "dict.txt")
|
| 159 |
+
if os.path.exists(dict_path):
|
| 160 |
+
target_dictionary = Dictionary.load(dict_path)
|
| 161 |
+
else:
|
| 162 |
+
dict_path = os.path.join(cfg.data, f"dict.{cfg.labels}.txt")
|
| 163 |
+
target_dictionary = Dictionary.load(dict_path)
|
| 164 |
+
|
| 165 |
+
return cls(cfg, target_dictionary=target_dictionary)
|
| 166 |
+
|
| 167 |
+
def optimizer_step(self, optimizer, model, update_num):
|
| 168 |
+
if hasattr(model, "get_groups_for_update"):
|
| 169 |
+
groups = model.get_groups_for_update(update_num)
|
| 170 |
+
optimizer.step(groups={groups})
|
| 171 |
+
else:
|
| 172 |
+
optimizer.step()
|
| 173 |
+
|
| 174 |
+
def valid_step(self, sample, model, criterion):
|
| 175 |
+
res = model(
|
| 176 |
+
**sample["net_input"],
|
| 177 |
+
dense_x_only=True,
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
dense_x = res["logits"]
|
| 181 |
+
padding_mask = res["padding_mask"]
|
| 182 |
+
|
| 183 |
+
word_scores = None
|
| 184 |
+
if self.compute_word_score is not None:
|
| 185 |
+
word_scores = self.compute_word_score(dense_x.cpu(), padding_mask.cpu())
|
| 186 |
+
|
| 187 |
+
z = dense_x.argmax(-1)
|
| 188 |
+
z[padding_mask] = self.target_dictionary.pad()
|
| 189 |
+
|
| 190 |
+
vocab_seen = torch.zeros(self.num_symbols, dtype=torch.bool)
|
| 191 |
+
|
| 192 |
+
import editdistance
|
| 193 |
+
|
| 194 |
+
c_err = 0
|
| 195 |
+
c_len = 0
|
| 196 |
+
pred_c_len = 0
|
| 197 |
+
lm_score_sum = 0
|
| 198 |
+
for i, (x, t, id) in enumerate(
|
| 199 |
+
zip(
|
| 200 |
+
z,
|
| 201 |
+
sample["target"] if "target" in sample else [None] * len(z),
|
| 202 |
+
sample["id"],
|
| 203 |
+
)
|
| 204 |
+
):
|
| 205 |
+
|
| 206 |
+
if t is not None:
|
| 207 |
+
t = t[(t >= self.target_dictionary.nspecial)]
|
| 208 |
+
x = x[
|
| 209 |
+
(x >= self.target_dictionary.nspecial)
|
| 210 |
+
& (x < (self.num_symbols + self.target_dictionary.nspecial))
|
| 211 |
+
]
|
| 212 |
+
if self.sil_id >= 0:
|
| 213 |
+
x = x[x != self.sil_id]
|
| 214 |
+
|
| 215 |
+
vocab_seen[x - self.target_dictionary.nspecial] = True
|
| 216 |
+
|
| 217 |
+
pred_units_arr = x
|
| 218 |
+
if self.cfg.ctc_eval:
|
| 219 |
+
pred_units_arr = pred_units_arr.unique_consecutive()
|
| 220 |
+
pred_units_arr = pred_units_arr[pred_units_arr != 0]
|
| 221 |
+
|
| 222 |
+
if id == 0:
|
| 223 |
+
if t is not None:
|
| 224 |
+
logger.info(f"REF: {self.target_dictionary.string(t)}")
|
| 225 |
+
logger.info(f"HYP: {self.target_dictionary.string(pred_units_arr)}")
|
| 226 |
+
|
| 227 |
+
if self.kenlm is not None:
|
| 228 |
+
if t is not None:
|
| 229 |
+
ref_lm_s = self.compute_lm_score(
|
| 230 |
+
self.target_dictionary.string(t)
|
| 231 |
+
)
|
| 232 |
+
logger.info(
|
| 233 |
+
f"LM [REF]: {ref_lm_s}, {math.pow(10, -ref_lm_s / (len(t) + 1))}"
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
hyp_lm_s = self.compute_lm_score(
|
| 237 |
+
self.target_dictionary.string(pred_units_arr)
|
| 238 |
+
)
|
| 239 |
+
logger.info(
|
| 240 |
+
f"LM [HYP]: {hyp_lm_s}, {math.pow(10, -hyp_lm_s / (len(pred_units_arr) + 1))}"
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
pred_units_arr = pred_units_arr.tolist()
|
| 244 |
+
|
| 245 |
+
pred_c_len += len(pred_units_arr)
|
| 246 |
+
|
| 247 |
+
if t is not None:
|
| 248 |
+
t = t.tolist()
|
| 249 |
+
c_err += editdistance.eval(pred_units_arr, t)
|
| 250 |
+
c_len += len(t)
|
| 251 |
+
else:
|
| 252 |
+
c_len = pred_c_len
|
| 253 |
+
|
| 254 |
+
if self.kenlm is not None:
|
| 255 |
+
pred_str = self.target_dictionary.string(pred_units_arr)
|
| 256 |
+
lm_score = self.compute_lm_score(pred_str)
|
| 257 |
+
lm_score_sum += lm_score
|
| 258 |
+
|
| 259 |
+
kaldi_score_sum = 0
|
| 260 |
+
word_lm_sum = 0
|
| 261 |
+
num_words = 0
|
| 262 |
+
if word_scores is not None:
|
| 263 |
+
for score, words in word_scores:
|
| 264 |
+
kaldi_score_sum += score
|
| 265 |
+
num_words += len(words)
|
| 266 |
+
if self.word_kenlm is not None:
|
| 267 |
+
word_lm_sum += self.kenlm.score(" ".join(words))
|
| 268 |
+
|
| 269 |
+
try:
|
| 270 |
+
world_size = get_data_parallel_world_size()
|
| 271 |
+
except:
|
| 272 |
+
world_size = 1
|
| 273 |
+
|
| 274 |
+
logging_output = {
|
| 275 |
+
"loss": c_err,
|
| 276 |
+
"_num_char_errors": c_err,
|
| 277 |
+
"_num_chars": c_len,
|
| 278 |
+
"_num_pred_chars": pred_c_len,
|
| 279 |
+
"ntokens": c_len,
|
| 280 |
+
"nsentences": z.size(0),
|
| 281 |
+
"sample_size": c_len,
|
| 282 |
+
"_world_size": world_size,
|
| 283 |
+
"_lm_score_sum": lm_score_sum,
|
| 284 |
+
"_kaldi_score_sum": kaldi_score_sum,
|
| 285 |
+
"_word_lm_sum": word_lm_sum,
|
| 286 |
+
"_num_words": num_words,
|
| 287 |
+
"_vocab_seen": vocab_seen,
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
return c_err, c_len, logging_output
|
| 291 |
+
|
| 292 |
+
def load_dataset(self, split: str, task_cfg: FairseqDataclass = None, **kwargs):
|
| 293 |
+
data_path = self.cfg.data
|
| 294 |
+
task_cfg = task_cfg or self.cfg
|
| 295 |
+
|
| 296 |
+
has_unpaired_text = os.path.exists(
|
| 297 |
+
os.path.join(self.cfg.text_data, f"{split}.idx")
|
| 298 |
+
)
|
| 299 |
+
|
| 300 |
+
self.datasets[split] = ExtractedFeaturesDataset(
|
| 301 |
+
path=data_path,
|
| 302 |
+
split=split,
|
| 303 |
+
min_length=3,
|
| 304 |
+
max_length=task_cfg.max_length,
|
| 305 |
+
labels=None if has_unpaired_text else task_cfg.labels,
|
| 306 |
+
label_dict=self.target_dictionary,
|
| 307 |
+
shuffle=getattr(task_cfg, "shuffle", True),
|
| 308 |
+
sort_by_length=task_cfg.sort_by_length,
|
| 309 |
+
aux_target_postfix=task_cfg.aux_target_postfix,
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
logger.info(f"split {split} has unpaired text? {has_unpaired_text}")
|
| 313 |
+
if has_unpaired_text:
|
| 314 |
+
text_dataset = data_utils.load_indexed_dataset(
|
| 315 |
+
os.path.join(self.cfg.text_data, split), self.target_dictionary
|
| 316 |
+
)
|
| 317 |
+
text_dataset = StripTokenDataset(text_dataset, self.target_dictionary.eos())
|
| 318 |
+
self.datasets[split] = RandomInputDataset(
|
| 319 |
+
self.datasets[split],
|
| 320 |
+
text_dataset,
|
| 321 |
+
["random_label"],
|
| 322 |
+
add_to_input=True,
|
| 323 |
+
pad_idx=self.target_dictionary.pad(),
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
@property
|
| 327 |
+
def source_dictionary(self):
|
| 328 |
+
return self._source_dictionary
|
| 329 |
+
|
| 330 |
+
@property
|
| 331 |
+
def target_dictionary(self):
|
| 332 |
+
"""Return the :class:`~fairseq.data.Dictionary` for the language
|
| 333 |
+
model."""
|
| 334 |
+
return self._target_dictionary
|
| 335 |
+
|
| 336 |
+
def max_positions(self):
|
| 337 |
+
"""Maximum input length supported by the encoder."""
|
| 338 |
+
return None
|
| 339 |
+
|
| 340 |
+
def reduce_metrics(self, logging_outputs, criterion):
|
| 341 |
+
super().reduce_metrics(logging_outputs, criterion)
|
| 342 |
+
|
| 343 |
+
zero = torch.scalar_tensor(0.0)
|
| 344 |
+
num_char_errors = sum(
|
| 345 |
+
log.get("_num_char_errors", zero) for log in logging_outputs
|
| 346 |
+
)
|
| 347 |
+
num_chars = sum(log.get("_num_chars", zero) for log in logging_outputs)
|
| 348 |
+
num_word_errors = sum(
|
| 349 |
+
log.get("_num_word_errors", zero) for log in logging_outputs
|
| 350 |
+
)
|
| 351 |
+
num_words = sum(log.get("_num_words", zero) for log in logging_outputs)
|
| 352 |
+
num_pred_chars = sum(
|
| 353 |
+
log.get("_num_pred_chars", zero) for log in logging_outputs
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
lm_score_sum = sum(log.get("_lm_score_sum", zero) for log in logging_outputs)
|
| 357 |
+
vocab_seen = (
|
| 358 |
+
sum(log.get("_vocab_seen", zero) for log in logging_outputs)
|
| 359 |
+
.bool()
|
| 360 |
+
.sum()
|
| 361 |
+
.item()
|
| 362 |
+
)
|
| 363 |
+
kaldi_score_sum = sum(
|
| 364 |
+
log.get("_kaldi_score_sum", zero) for log in logging_outputs
|
| 365 |
+
)
|
| 366 |
+
word_lm_sum = sum(log.get("_word_lm_sum", zero) for log in logging_outputs)
|
| 367 |
+
|
| 368 |
+
metrics.log_scalar_sum("_num_char_errors", num_char_errors)
|
| 369 |
+
metrics.log_scalar_sum("_num_chars", num_chars)
|
| 370 |
+
metrics.log_scalar_sum("_num_word_errors", num_word_errors)
|
| 371 |
+
metrics.log_scalar_sum("_num_words", num_words)
|
| 372 |
+
|
| 373 |
+
metrics.log_scalar_sum("lm_score_sum", lm_score_sum)
|
| 374 |
+
metrics.log_scalar_sum("num_pred_chars", num_pred_chars)
|
| 375 |
+
|
| 376 |
+
if self.cfg.word_kenlm_path is not None:
|
| 377 |
+
metrics.log_scalar_sum("kaldi_score_sum", kaldi_score_sum)
|
| 378 |
+
metrics.log_scalar_sum("word_lm_sum", word_lm_sum)
|
| 379 |
+
|
| 380 |
+
if num_chars > 0:
|
| 381 |
+
metrics.log_derived(
|
| 382 |
+
"uer",
|
| 383 |
+
lambda meters: meters["_num_char_errors"].sum
|
| 384 |
+
* 100.0
|
| 385 |
+
/ meters["_num_chars"].sum
|
| 386 |
+
if meters["_num_chars"].sum > 0
|
| 387 |
+
else float("nan"),
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
if lm_score_sum < 0 and vocab_seen > 0:
|
| 391 |
+
metrics.log_scalar("vocab_seen_pct", vocab_seen / self.num_symbols)
|
| 392 |
+
|
| 393 |
+
metrics.log_derived(
|
| 394 |
+
"weighted_lm_ppl",
|
| 395 |
+
lambda meters: math.pow(
|
| 396 |
+
10,
|
| 397 |
+
-meters["lm_score_sum"].sum
|
| 398 |
+
/ (
|
| 399 |
+
meters["num_pred_chars"].sum + meters["nsentences"].sum
|
| 400 |
+
), # account for </s>
|
| 401 |
+
)
|
| 402 |
+
/ meters["vocab_seen_pct"].avg ** self.cfg.vocab_usage_power,
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
metrics.log_derived(
|
| 406 |
+
"lm_ppl",
|
| 407 |
+
lambda meters: math.pow(
|
| 408 |
+
10,
|
| 409 |
+
-meters["lm_score_sum"].sum
|
| 410 |
+
/ (
|
| 411 |
+
meters["num_pred_chars"].sum + meters["nsentences"].sum
|
| 412 |
+
), # account for </s>
|
| 413 |
+
),
|
| 414 |
+
)
|
| 415 |
+
else:
|
| 416 |
+
metrics.log_derived("weighted_lm_ppl", lambda meters: float("inf"))
|
| 417 |
+
|
| 418 |
+
if num_words > 0:
|
| 419 |
+
if word_lm_sum != 0:
|
| 420 |
+
metrics.log_derived(
|
| 421 |
+
"word_lm_ppl",
|
| 422 |
+
lambda meters: math.pow(
|
| 423 |
+
10,
|
| 424 |
+
-meters["word_lm_sum"].sum
|
| 425 |
+
/ (
|
| 426 |
+
meters["_num_words"].sum + meters["nsentences"].sum
|
| 427 |
+
), # account for </s>
|
| 428 |
+
),
|
| 429 |
+
)
|
| 430 |
+
metrics.log_derived(
|
| 431 |
+
"weighted_word_lm_ppl",
|
| 432 |
+
lambda meters: math.pow(
|
| 433 |
+
10,
|
| 434 |
+
-meters["word_lm_sum"].sum
|
| 435 |
+
/ (
|
| 436 |
+
meters["_num_words"].sum + meters["nsentences"].sum
|
| 437 |
+
), # account for </s>
|
| 438 |
+
)
|
| 439 |
+
/ meters["vocab_seen_pct"].avg ** self.cfg.vocab_usage_power,
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
if self.cfg.word_kenlm_path is not None:
|
| 443 |
+
metrics.log_derived(
|
| 444 |
+
"kaldi_score",
|
| 445 |
+
lambda meters: meters["kaldi_score_sum"].sum
|
| 446 |
+
/ meters["nsentences"].sum,
|
| 447 |
+
)
|
| 448 |
+
|
| 449 |
+
def build_model(self, cfg: FairseqDataclass, from_checkpoint=False):
|
| 450 |
+
model = super().build_model(cfg)
|
| 451 |
+
|
| 452 |
+
return model
|
omni_speech/infer/fairseq/examples/wav2vec/xlsr/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XLS-R
|
| 2 |
+
|
| 3 |
+
XLS-R is a set of large-scale models for self-supervised cross-lingual speech representation learning based on wav2vec 2.0. It was pretrained on 128 languages and approximately 436K hours of unlabeled speech data. With finetuning, these models achieve state of the art performance in speech translation, speech recognition and language identification. We evaluate the model across multiple benchmarks such as CoVoST-2 for speech translation, BABEL / MLS / CommonVoice / VoxPopuli for automatic speech recognition, and VoxLingua107 for language identification as we llas VoxCeleb1 for speaker identification. More details about this work can be found in our [paper](https://arxiv.org/pdf/2111.09296.pdf) and download links can be found below.
|
| 4 |
+
|
| 5 |
+
Model | Link
|
| 6 |
+
|------|------
|
| 7 |
+
XLS-R 300M | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xlsr2_300m.pt)
|
| 8 |
+
XLS-R 1B | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xlsr2_960m_1000k.pt)
|
| 9 |
+
XLS-R 2B | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xlsr2_2B_1000k.pt)
|
| 10 |
+
|
| 11 |
+
You can also download these models [here](https://huggingface.co/models?other=xls_r) and read more about it in the [blogpost](https://huggingface.co/blog/fine-tune-xlsr-wav2vec2) from Hugging Face.
|
| 12 |
+
|
| 13 |
+
## Speech Translation Finetuned Models
|
| 14 |
+
|
| 15 |
+
We multilingually finetune XLS-R models on [CoVoST 2](https://github.com/facebookresearch/covost), which has 21
|
| 16 |
+
into-English and 15 out-of-English directions.
|
| 17 |
+
|
| 18 |
+
Model | Directions | Link
|
| 19 |
+
|------|------|------
|
| 20 |
+
XLS-R 300M | 21 langs → En | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_300m_21_en.pt)
|
| 21 |
+
XLS-R 300M | En → 15 langs | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_300m_en_15.pt)
|
| 22 |
+
XLS-R 1B | 21 langs → En | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_1b_21_en.pt)
|
| 23 |
+
XLS-R 1B | En → 15 langs | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_1b_en_15.pt)
|
| 24 |
+
XLS-R 2B | 21 langs → En | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_2b_21_en.pt)
|
| 25 |
+
XLS-R 2B | En → 15 langs | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_2b_en_15.pt)
|
| 26 |
+
XLS-R 2B | 21 langs → En + En → 15 langs | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xls_r_2b_22_16.pt)
|
| 27 |
+
|
| 28 |
+
## ASR Finetuning
|
| 29 |
+
|
| 30 |
+
You can refer the original wav2vec documentation on detailed instructions about how to finetune a pretrained model with CTC [here](https://github.com/pytorch/fairseq/tree/main/examples/wav2vec#fine-tune-a-pre-trained-model-with-ctc). Below is an example command and you can find the values for different hyperparameters to reproduce the results in our paper.
|
| 31 |
+
|
| 32 |
+
```shell script
|
| 33 |
+
$ fairseq-hydra-train \
|
| 34 |
+
distributed_training.distributed_port=$PORT \
|
| 35 |
+
task.data=/path/to/data \
|
| 36 |
+
model.w2v_path=/path/to/model.pt \
|
| 37 |
+
--config-dir /path/to/fairseq-py/examples/wav2vec/xlsr/config \
|
| 38 |
+
--config-name finetune
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
For finetuning the 300M as well as 1B model, we use the same hyperparameter setting defined in `finetune.yaml`. We vary `optimization.max_update` as described in the below table and the `optimization.lr` is picked from the interval [2e-5, 3e-4] based on dev word error rate.
|
| 42 |
+
|
| 43 |
+
Benchmark | Total Number of Updates
|
| 44 |
+
|------|------
|
| 45 |
+
Babel | 26000
|
| 46 |
+
Common Voice | 13000
|
| 47 |
+
VoxPopuli | 50000
|
| 48 |
+
MLS 10h | 20000
|
| 49 |
+
|
| 50 |
+
For finetuning the 2B model, we make some additional changes for `finetune.yaml` . We use the fully_sharded `distributed_training.ddp_backend` provided by the [fairscale](https://github.com/facebookresearch/fairscale) library and and set `model.activation_checkpoint` to true. We also increase `dataset.max_tokens` to 2560000 and use a total effective batch size of 2560000*24. We sweep for the best `optimization.lr` within the interval [3e−6,3e−5] using dev error rate. For common voice dataset, we pick the `model.mask_prob` for different languages among {0.30, 0.40} based on best dev error rate.
|
| 51 |
+
|
| 52 |
+
## LID Inference
|
| 53 |
+
|
| 54 |
+
Model | Link
|
| 55 |
+
|------|------
|
| 56 |
+
XLS-R 300M + ft Voxlingua107 | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xlsr_300m_voxlingua107_ft.pt)
|
| 57 |
+
|
| 58 |
+
How to run inference & calculate accuracy (step-by-step):
|
| 59 |
+
1. Download the Voxlingua107 checkpoint from the table above.
|
| 60 |
+
1. Use this python script to extract logit/embedding from the XLSR model: https://github.com/fairinternal/fairseq-py/blob/xlsr2/examples/wav2vec/gen_audio_embedding.py
|
| 61 |
+
```shell command
|
| 62 |
+
CUDA_VISIBLE_DEVICES=0 PYTHONPATH=. python3 examples/wav2vec/gen_audio_embedding.py \
|
| 63 |
+
/fsx/data/VoxLingua107/manifest --path "/path/to/checkpoint.pt" \
|
| 64 |
+
--task audio_classification --batch-size 90 --gen-subset test \
|
| 65 |
+
--infer-manifest /fsx/data/VoxLingua107/manifest/test.tsv \
|
| 66 |
+
--infer-xtimes 10 --infer-max-sample-size 160000 --output-path /tmp/tmp_voxling_infer.npz
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
2. Calculate the overall accuracy, 0-5 seconds and 5-20 seconds:
|
| 70 |
+
```shell command
|
| 71 |
+
PYTHONPATH='.' python examples/wav2vec/eval_speaker_clf_task.py \
|
| 72 |
+
--task cls --merge mean_logit --data /tmp/tmp_voxling_infer.npz
|
| 73 |
+
|
| 74 |
+
Output:
|
| 75 |
+
| run classification evaluation
|
| 76 |
+
| acc = 94.34% -- err = 5.66% -- correct=1518 total=1609
|
| 77 |
+
| acc 0to5 = 90.91% -- err = 9.09% -- c_5=230.0 t_5=253
|
| 78 |
+
| acc 5to20 = 94.99% -- err = 5.01% -- c_20=1288.0 t_20=1356
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
## Citation
|
| 82 |
+
|
| 83 |
+
Please cite as:
|
| 84 |
+
|
| 85 |
+
``` bibtex
|
| 86 |
+
@article{babu2021xlsr,
|
| 87 |
+
title={XLS-R: Self-supervised Cross-lingual Speech Representation Learning at Scale},
|
| 88 |
+
author={Arun Babu and Changhan Wang and Andros Tjandra and Kushal Lakhotia and Qiantong Xu and Naman Goyal and Kritika Singh and Patrick von Platen and Yatharth Saraf and Juan Pino and Alexei Baevski and Alexis Conneau and Michael Auli},
|
| 89 |
+
year={2021},
|
| 90 |
+
volume={abs/2111.09296},
|
| 91 |
+
journal={arXiv},
|
| 92 |
+
}
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
|
omni_speech/infer/fairseq/examples/wav2vec/xlsr/config/finetune.yaml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# @package _group_
|
| 2 |
+
|
| 3 |
+
common:
|
| 4 |
+
fp16: true
|
| 5 |
+
log_format: json
|
| 6 |
+
log_interval: 200
|
| 7 |
+
tensorboard_logdir: tb
|
| 8 |
+
|
| 9 |
+
checkpoint:
|
| 10 |
+
save_interval: 1000
|
| 11 |
+
save_interval_updates: 1000
|
| 12 |
+
keep_interval_updates: 1
|
| 13 |
+
no_epoch_checkpoints: true
|
| 14 |
+
best_checkpoint_metric: wer
|
| 15 |
+
|
| 16 |
+
task:
|
| 17 |
+
_name: audio_finetuning
|
| 18 |
+
data: ???
|
| 19 |
+
normalize: true
|
| 20 |
+
labels: ltr
|
| 21 |
+
|
| 22 |
+
dataset:
|
| 23 |
+
num_workers: 6
|
| 24 |
+
max_tokens: 1280000
|
| 25 |
+
skip_invalid_size_inputs_valid_test: true
|
| 26 |
+
validate_after_updates: 10000
|
| 27 |
+
validate_interval_updates: 1000
|
| 28 |
+
valid_subset: valid
|
| 29 |
+
|
| 30 |
+
distributed_training:
|
| 31 |
+
ddp_backend: legacy_ddp
|
| 32 |
+
distributed_world_size: 4
|
| 33 |
+
|
| 34 |
+
criterion:
|
| 35 |
+
_name: ctc
|
| 36 |
+
zero_infinity: true
|
| 37 |
+
|
| 38 |
+
optimization:
|
| 39 |
+
max_update: ???
|
| 40 |
+
lr: [0.0003]
|
| 41 |
+
sentence_avg: true
|
| 42 |
+
update_freq: [5]
|
| 43 |
+
|
| 44 |
+
optimizer:
|
| 45 |
+
_name: adam
|
| 46 |
+
adam_betas: (0.9,0.98)
|
| 47 |
+
adam_eps: 1e-08
|
| 48 |
+
|
| 49 |
+
lr_scheduler:
|
| 50 |
+
_name: tri_stage
|
| 51 |
+
phase_ratio: [0.1, 0.4, 0.5]
|
| 52 |
+
final_lr_scale: 0.05
|
| 53 |
+
|
| 54 |
+
model:
|
| 55 |
+
_name: wav2vec_ctc
|
| 56 |
+
w2v_path: ???
|
| 57 |
+
apply_mask: true
|
| 58 |
+
mask_prob: 0.75
|
| 59 |
+
mask_channel_prob: 0.25
|
| 60 |
+
mask_channel_length: 64
|
| 61 |
+
layerdrop: 0.1
|
| 62 |
+
activation_dropout: 0.1
|
| 63 |
+
feature_grad_mult: 0.0
|
| 64 |
+
freeze_finetune_updates: 10000
|
| 65 |
+
|
| 66 |
+
checkpoint_activations: false
|
omni_speech/infer/fairseq/examples/wav2vec/xlsr/scripts/eval_speaker_clf_task.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Usage:
|
| 3 |
+
This scripts it to evaluate the classification accuracy/error rate from the embedding extracted
|
| 4 |
+
by gen_audio_embedding.py
|
| 5 |
+
Example (LID classification)
|
| 6 |
+
|
| 7 |
+
PYTHONPATH='.' python examples/wav2vec/eval_speaker_clf_task.py \
|
| 8 |
+
--data /fsx/androstj/exps/lid_voxlingua/infer/atj_xlsr2_100pct_300M_mean_fast_upd_100k_new.npz \
|
| 9 |
+
--task cls --merge mean_logit
|
| 10 |
+
"""
|
| 11 |
+
import numpy as np
|
| 12 |
+
import sklearn
|
| 13 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
| 14 |
+
from sklearn.preprocessing import StandardScaler
|
| 15 |
+
from tqdm import tqdm
|
| 16 |
+
import ipdb
|
| 17 |
+
import logging
|
| 18 |
+
import argparse
|
| 19 |
+
from scipy.special import softmax
|
| 20 |
+
|
| 21 |
+
log=logging.getLogger(__name__)
|
| 22 |
+
log.setLevel(logging.INFO)
|
| 23 |
+
|
| 24 |
+
def calculate_eer(y_label, y_score):
|
| 25 |
+
# y denotes groundtruth scores,
|
| 26 |
+
# y_score denotes the prediction scores.
|
| 27 |
+
from scipy.optimize import brentq
|
| 28 |
+
from sklearn.metrics import roc_curve
|
| 29 |
+
from scipy.interpolate import interp1d
|
| 30 |
+
|
| 31 |
+
fpr, tpr, thresholds = roc_curve(y_label, y_score, pos_label=1)
|
| 32 |
+
eer = brentq(lambda x : 1. - x - interp1d(fpr, tpr)(x), 0., 1.)
|
| 33 |
+
optimal_threshold = interp1d(fpr, thresholds)(eer)
|
| 34 |
+
return eer, optimal_threshold
|
| 35 |
+
|
| 36 |
+
def calculate_minDCF(y_label, y_score, p_target=0.01, c_miss=1, c_fa=1):
|
| 37 |
+
# https://github.com/kaldi-asr/kaldi/blob/master/egs/sre08/v1/sid/compute_min_dcf.py
|
| 38 |
+
from sklearn.metrics import det_curve
|
| 39 |
+
fpr, fnr, thresholds = det_curve(y_label, y_score, pos_label=1)
|
| 40 |
+
min_c_det = float("inf")
|
| 41 |
+
min_c_det_threshold = thresholds[0]
|
| 42 |
+
for i in range(0, len(fpr)):
|
| 43 |
+
# See Equation (2). it is a weighted sum of false negative
|
| 44 |
+
# and false positive errors.
|
| 45 |
+
c_det = c_miss * fnr[i] * p_target + c_fa * fpr[i] * (1 - p_target)
|
| 46 |
+
if c_det < min_c_det:
|
| 47 |
+
min_c_det = c_det
|
| 48 |
+
min_c_det_threshold = thresholds[i]
|
| 49 |
+
# See Equations (3) and (4). Now we normalize the cost.
|
| 50 |
+
c_def = min(c_miss * p_target, c_fa * (1 - p_target))
|
| 51 |
+
min_dcf = min_c_det / c_def
|
| 52 |
+
return min_dcf, min_c_det_threshold
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
if __name__ == '__main__':
|
| 56 |
+
parser = argparse.ArgumentParser()
|
| 57 |
+
parser.add_argument('--data', help='npz contains name & latent file')
|
| 58 |
+
parser.add_argument('--task', choices=['cls', 'veri', 'cls_voxlingua'])
|
| 59 |
+
parser.add_argument('--merge', choices=['mean_logit', 'first_logit', 'mean_latent_sim', 'first_latent_sim', 'mean_logit_sim', 'first_logit_sim'])
|
| 60 |
+
parser.add_argument('--veri-pair', help='verification file contains 1/0 utt_x utt_y')
|
| 61 |
+
parser.add_argument('--scaler', type=str, choices=['mean_var'])
|
| 62 |
+
parser.add_argument('--compress-method', choices=['pca'])
|
| 63 |
+
parser.add_argument('--compress-dim', type=int)
|
| 64 |
+
args = parser.parse_args()
|
| 65 |
+
|
| 66 |
+
if args.task in ['cls', 'cls_voxlingua']:
|
| 67 |
+
print('| run classification evaluation')
|
| 68 |
+
data = np.load(args.data)
|
| 69 |
+
data_logit = data['logit']
|
| 70 |
+
data_target = data['target']
|
| 71 |
+
data_src_len = data['src_len']
|
| 72 |
+
assert data_logit.shape[0] == data_target.shape[0]
|
| 73 |
+
B = data_logit.shape[0]
|
| 74 |
+
correct = 0
|
| 75 |
+
total = 0
|
| 76 |
+
data_prob = softmax(data_logit, axis=2)
|
| 77 |
+
correct_vs_len = np.empty((B, 2))
|
| 78 |
+
for ii in range(B):
|
| 79 |
+
_target = data_target[ii]
|
| 80 |
+
if args.merge == 'mean_logit':
|
| 81 |
+
_prob = np.mean(data_prob[ii], axis=0)
|
| 82 |
+
top_1 = np.argmax(_prob)
|
| 83 |
+
elif args.merge == 'first_logit':
|
| 84 |
+
_prob = data_prob[ii][0]
|
| 85 |
+
top_1 = np.argmax(_prob)
|
| 86 |
+
else :
|
| 87 |
+
raise ValueError()
|
| 88 |
+
is_top_1 = (1 if top_1 == _target else 0)
|
| 89 |
+
correct += is_top_1
|
| 90 |
+
total += 1
|
| 91 |
+
_src_len = data_src_len[ii] / 16000
|
| 92 |
+
correct_vs_len[ii] = [is_top_1, _src_len]
|
| 93 |
+
|
| 94 |
+
acc = correct / total * 100
|
| 95 |
+
t_5 = correct_vs_len[:, 1] <= 5
|
| 96 |
+
t_20 = correct_vs_len[:, 1] > 5
|
| 97 |
+
c_5 = correct_vs_len[t_5, 0].sum()
|
| 98 |
+
c_20 = correct_vs_len[t_20, 0].sum()
|
| 99 |
+
t_5 = t_5.sum()
|
| 100 |
+
t_20 = t_20.sum()
|
| 101 |
+
acc_5 = c_5 / t_5 * 100
|
| 102 |
+
acc_20 = c_20 / t_20 * 100
|
| 103 |
+
print(f'| acc = {acc:.2f}% -- err = {100-acc:.2f}% -- {correct=} {total=}')
|
| 104 |
+
print(f'| acc 0to5 = {acc_5:.2f}% -- err = {100-acc_5:.2f}% -- {c_5=} {t_5=}')
|
| 105 |
+
print(f'| acc 5to20 = {acc_20:.2f}% -- err = {100-acc_20:.2f}% -- {c_20=} {t_20=}')
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
if args.task == 'veri':
|
| 110 |
+
print('| run verification evaluation')
|
| 111 |
+
veri_pairs = []
|
| 112 |
+
with open(args.veri_pair) as ff:
|
| 113 |
+
for fi in ff:
|
| 114 |
+
a,b,c = fi.split()
|
| 115 |
+
a = int(a)
|
| 116 |
+
veri_pairs.append([a,b,c])
|
| 117 |
+
|
| 118 |
+
data = np.load(args.data)
|
| 119 |
+
if 'logit' in args.merge:
|
| 120 |
+
data_latent = data['logit']
|
| 121 |
+
elif 'latent' in args.merge:
|
| 122 |
+
data_latent = data['latent']
|
| 123 |
+
else :
|
| 124 |
+
raise ValueError()
|
| 125 |
+
|
| 126 |
+
data_name = data['name']
|
| 127 |
+
assert len(data_name) == len(data_latent)
|
| 128 |
+
map_name_latent = {}
|
| 129 |
+
|
| 130 |
+
from sklearn.pipeline import make_pipeline
|
| 131 |
+
pipe = []
|
| 132 |
+
if args.scaler == 'mean_var':
|
| 133 |
+
print(f'| apply StandardScaler')
|
| 134 |
+
pipe.append(StandardScaler())
|
| 135 |
+
|
| 136 |
+
if args.compress_method == 'pca':
|
| 137 |
+
n_comp = args.compress_dim
|
| 138 |
+
print(f'| apply PCA with {n_comp=}')
|
| 139 |
+
from sklearn.decomposition import PCA
|
| 140 |
+
pipe.append(PCA(n_components=n_comp))
|
| 141 |
+
if len(pipe) > 0 :
|
| 142 |
+
pipe = make_pipeline(*pipe)
|
| 143 |
+
data_latent_2d = data_latent.reshape(-1, data_latent.shape[-1])
|
| 144 |
+
pipe.fit(data_latent_2d)
|
| 145 |
+
data_latent_2d = pipe.transform(data_latent_2d)
|
| 146 |
+
data_latent = data_latent_2d.reshape(data_latent.shape[0], data_latent.shape[1], -1)
|
| 147 |
+
|
| 148 |
+
for ii in range(len(data_name)):
|
| 149 |
+
map_name_latent[data_name[ii]] = data_latent[ii]
|
| 150 |
+
labels = []
|
| 151 |
+
scores = []
|
| 152 |
+
for lbl, pair_a, pair_b in tqdm(veri_pairs):
|
| 153 |
+
labels.append(lbl)
|
| 154 |
+
pair_a = map_name_latent[pair_a]
|
| 155 |
+
pair_b = map_name_latent[pair_b]
|
| 156 |
+
assert pair_a.ndim == pair_b.ndim == 2
|
| 157 |
+
score = cosine_similarity(pair_a, pair_b)
|
| 158 |
+
if args.merge.startswith('mean'):
|
| 159 |
+
score = np.mean(score)
|
| 160 |
+
elif args.merge.startswith('first'):
|
| 161 |
+
score = score[0, 0]
|
| 162 |
+
else :
|
| 163 |
+
raise ValueError()
|
| 164 |
+
scores.append(score)
|
| 165 |
+
labels = np.array(labels)
|
| 166 |
+
scores = np.array(scores)
|
| 167 |
+
eer, eer_threshold = calculate_eer(labels, scores)
|
| 168 |
+
minDCF, minDCF_threshold = calculate_minDCF(labels, scores)
|
| 169 |
+
print('='*40)
|
| 170 |
+
print(f'| EER = {eer*100:.2f}%\tthreshold = {eer_threshold:.2f}')
|
| 171 |
+
print(f'| minDCF = {minDCF:.2f}\tthreshold = {minDCF_threshold:.2f}')
|
| 172 |
+
|
| 173 |
+
|
omni_speech/infer/fairseq/examples/wav2vec/xlsr/scripts/gen_audio_embedding.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Usage:
|
| 3 |
+
This script is used to extract the embedding / logit for speech classification task.
|
| 4 |
+
1. Set fdir into your model checkpoint directory
|
| 5 |
+
2. Run the following command (preferrably on GPU machine to speed up the inference process)
|
| 6 |
+
|
| 7 |
+
CUDA_VISIBLE_DEVICES=0 python3 examples/wav2vec/gen_audio_embedding.py /fsx/data/VoxLingua107/manifest --path ${fdir} \
|
| 8 |
+
--task audio_classification --batch-size 90 --gen-subset test \
|
| 9 |
+
--infer-manifest /fsx/data/VoxLingua107/manifest/test.tsv \
|
| 10 |
+
--infer-xtimes 10 --infer-max-sample-size 160000 --output-path $odir
|
| 11 |
+
|
| 12 |
+
Example:
|
| 13 |
+
Case: LID logit extraction
|
| 14 |
+
fdir='/fsx/androstj/exps/voxlingua_lid_train_all/ckpt_100pct_300m_voxling-act_linear-pool_mean_fast-lr_1e-4-phase_0.1_0.4_0.5-maxupd_100000-ufreq_1-mprob_0.5-fz_0-cr_softmax/0/checkpoints/checkpoint_best.pt'
|
| 15 |
+
python3 examples/wav2vec/gen_audio_embedding.py /fsx/data/VoxLingua107/manifest --path ${fdir} \
|
| 16 |
+
--task audio_classification --batch-size 90 --gen-subset test \
|
| 17 |
+
--infer-manifest /fsx/data/VoxLingua107/manifest/test.tsv \
|
| 18 |
+
--infer-xtimes 10 --infer-max-sample-size 160000 --output-path $odir
|
| 19 |
+
|
| 20 |
+
"""
|
| 21 |
+
import torch
|
| 22 |
+
from fairseq import checkpoint_utils, distributed_utils, options, utils
|
| 23 |
+
from fairseq.dataclass.utils import convert_namespace_to_omegaconf
|
| 24 |
+
from fairseq.logging import metrics, progress_bar
|
| 25 |
+
from fairseq import checkpoint_utils, data, options, tasks
|
| 26 |
+
from fairseq.data import FileAudioDataset, AddTargetDataset, Dictionary
|
| 27 |
+
from fairseq.tasks.audio_classification import LabelEncoder
|
| 28 |
+
import ipdb
|
| 29 |
+
import copy
|
| 30 |
+
import sys
|
| 31 |
+
from tqdm import tqdm
|
| 32 |
+
import tempfile
|
| 33 |
+
import numpy as np
|
| 34 |
+
import sklearn
|
| 35 |
+
|
| 36 |
+
def subset_manifest(infer_manifest, veri_pair):
|
| 37 |
+
with open(infer_manifest) as ff, open(veri_pair) as gg, \
|
| 38 |
+
tempfile.NamedTemporaryFile('w', delete=False) as ww:
|
| 39 |
+
fnames = ff.read().strip().split("\n")
|
| 40 |
+
basedir = fnames[0]
|
| 41 |
+
needed_fname = []
|
| 42 |
+
for gi in gg.read().strip().split('\n'):
|
| 43 |
+
_, x1, x2 = gi.split()
|
| 44 |
+
needed_fname.append(x1)
|
| 45 |
+
needed_fname.append(x2)
|
| 46 |
+
needed_fname = set(needed_fname)
|
| 47 |
+
|
| 48 |
+
ww.write(basedir+'\n')
|
| 49 |
+
for ii in range(1, len(fnames)):
|
| 50 |
+
x1,x2 = fnames[ii].split()
|
| 51 |
+
if x1 in needed_fname:
|
| 52 |
+
ww.write(fnames[ii]+'\n')
|
| 53 |
+
print(f'| subset manifest for verification: {ww.name}')
|
| 54 |
+
return ww.name
|
| 55 |
+
|
| 56 |
+
def wrap_target_dataset(infer_manifest, dataset, task):
|
| 57 |
+
label_path = infer_manifest.replace(".tsv", ".label")
|
| 58 |
+
with open(label_path, "r") as f:
|
| 59 |
+
labels = f.read().strip().split("\n")
|
| 60 |
+
assert len(labels) == len(dataset)
|
| 61 |
+
process_label = LabelEncoder(task.target_dictionary)
|
| 62 |
+
dataset = AddTargetDataset(dataset, labels,
|
| 63 |
+
pad=task.target_dictionary.pad(),
|
| 64 |
+
eos=task.target_dictionary.eos(),
|
| 65 |
+
batch_targets=True,
|
| 66 |
+
process_label=process_label,
|
| 67 |
+
add_to_input=False)
|
| 68 |
+
return dataset
|
| 69 |
+
|
| 70 |
+
def resample_data(source, padding_mask, n_sample, max_sample_len):
|
| 71 |
+
# source: BxT
|
| 72 |
+
# padding_mask: BxT
|
| 73 |
+
B = source.shape[0]
|
| 74 |
+
T = source.shape[1]
|
| 75 |
+
sources = []
|
| 76 |
+
padding_masks = []
|
| 77 |
+
seq_len = (~padding_mask).sum(1)
|
| 78 |
+
for jj in range(n_sample):
|
| 79 |
+
new_source = source.new_zeros(B, max_sample_len)
|
| 80 |
+
new_padding_mask = padding_mask.new_zeros(B, max_sample_len)
|
| 81 |
+
for ii in range(B):
|
| 82 |
+
if seq_len[ii] > max_sample_len:
|
| 83 |
+
start = np.random.randint(0, seq_len[ii]-max_sample_len+1)
|
| 84 |
+
end = start + max_sample_len
|
| 85 |
+
else :
|
| 86 |
+
start = 0
|
| 87 |
+
end = seq_len[ii]
|
| 88 |
+
new_source[ii, 0:end-start] = source[ii, start:end]
|
| 89 |
+
new_padding_mask[ii, end-start+1:] = True
|
| 90 |
+
sources.append(new_source)
|
| 91 |
+
padding_masks.append(new_padding_mask)
|
| 92 |
+
return sources, padding_masks
|
| 93 |
+
|
| 94 |
+
def resample_sample(sample, n_sample, max_sample_len):
|
| 95 |
+
new_sources, new_padding_masks = resample_data(sample['net_input']['source'], sample['net_input']['padding_mask'], n_sample, max_sample_len)
|
| 96 |
+
new_samples = []
|
| 97 |
+
for ii in range(n_sample):
|
| 98 |
+
new_sample = copy.deepcopy(sample)
|
| 99 |
+
new_sample['net_input']['source'] = new_sources[ii]
|
| 100 |
+
new_sample['net_input']['padding_mask'] = new_padding_masks[ii]
|
| 101 |
+
new_samples.append(new_sample)
|
| 102 |
+
return new_samples
|
| 103 |
+
|
| 104 |
+
if __name__ == '__main__':
|
| 105 |
+
np.random.seed(123)
|
| 106 |
+
# Parse command-line arguments for generation
|
| 107 |
+
parser = options.get_generation_parser(default_task='audio_classification')
|
| 108 |
+
# parser.add_argument('--infer-merge', type=str, default='mean')
|
| 109 |
+
parser.add_argument('--infer-xtimes', type=int, default=1)
|
| 110 |
+
parser.add_argument('--infer-max-sample-size', type=int, default=5*16000) # 5 secs
|
| 111 |
+
parser.add_argument('--infer-manifest', type=str)
|
| 112 |
+
parser.add_argument('--verification-pair', type=str, required=False,
|
| 113 |
+
help='''
|
| 114 |
+
a file that contains pairs of utts to evaluated if they are from same speaker or not
|
| 115 |
+
format: (following voxceleb)
|
| 116 |
+
1/0 <wav_pair_a> <wav_pair_b>
|
| 117 |
+
''')
|
| 118 |
+
parser.add_argument('--output-path', type=str)
|
| 119 |
+
# parser.add_argument('--infer-xtimes', type=int, default=1)
|
| 120 |
+
|
| 121 |
+
args = options.parse_args_and_arch(parser)
|
| 122 |
+
# Setup task
|
| 123 |
+
# task = tasks.setup_task(args)
|
| 124 |
+
use_cuda = not args.cpu
|
| 125 |
+
|
| 126 |
+
# Load model & task
|
| 127 |
+
print('| loading model from {}'.format(args.path))
|
| 128 |
+
arg_overrides = {
|
| 129 |
+
'data': args.data,
|
| 130 |
+
# 'mask_prob': 0
|
| 131 |
+
#'max_sample_size': sys.maxsize,
|
| 132 |
+
#'min_sample_size': 0,
|
| 133 |
+
}
|
| 134 |
+
state = checkpoint_utils.load_checkpoint_to_cpu(args.path)
|
| 135 |
+
# move to AWS
|
| 136 |
+
state['cfg']['model']['w2v_path'] = state['cfg']['model']['w2v_path'].replace('/checkpoint/arbabu/XLSR2/model_versions/', '/fsx/data/model_versions/').replace('/checkpoint/kushall/final_model_checkpoints/wav2vec2/', '/fsx/data/wav2vec_ckpt/')
|
| 137 |
+
state['cfg']['task']['data'] = state['cfg']['task']['data'].replace('/checkpoint/kushall/data/', '/fsx/data/')
|
| 138 |
+
|
| 139 |
+
models, _model_args, task = checkpoint_utils.load_model_ensemble_and_task([args.path],
|
| 140 |
+
arg_overrides=arg_overrides,
|
| 141 |
+
task=None,
|
| 142 |
+
state=state)
|
| 143 |
+
model = models[0]
|
| 144 |
+
model.eval()
|
| 145 |
+
if use_cuda:
|
| 146 |
+
model.cuda()
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# Load dataset
|
| 150 |
+
task.load_dataset(args.gen_subset)
|
| 151 |
+
dataset = task.dataset(args.gen_subset)
|
| 152 |
+
infer_manifest = args.infer_manifest
|
| 153 |
+
# only decode needed utts
|
| 154 |
+
# infer_manifest = subset_manifest(infer_manifest,
|
| 155 |
+
# args.verification_pair)
|
| 156 |
+
infer_dataset = FileAudioDataset(infer_manifest,
|
| 157 |
+
sample_rate=task.cfg.sample_rate,
|
| 158 |
+
max_sample_size=10**10, #task.cfg.max_sample_size,
|
| 159 |
+
min_sample_size=1, #task.cfg.min_sample_size,
|
| 160 |
+
pad=True,
|
| 161 |
+
normalize=task.cfg.normalize)
|
| 162 |
+
# add target (if needed)
|
| 163 |
+
infer_dataset = wrap_target_dataset(infer_manifest, infer_dataset, task)
|
| 164 |
+
itr = task.get_batch_iterator(
|
| 165 |
+
dataset=infer_dataset,
|
| 166 |
+
max_sentences=args.batch_size,
|
| 167 |
+
).next_epoch_itr(shuffle=False)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
# correct = 0
|
| 171 |
+
# total = 0
|
| 172 |
+
list_uttname = []
|
| 173 |
+
list_latent = []
|
| 174 |
+
list_logit = []
|
| 175 |
+
list_target = []
|
| 176 |
+
list_src_len = []
|
| 177 |
+
with torch.no_grad():
|
| 178 |
+
for _, sample in tqdm(enumerate(itr)):
|
| 179 |
+
# resample if needed
|
| 180 |
+
samples = resample_sample(sample, args.infer_xtimes, args.infer_max_sample_size)
|
| 181 |
+
list_uttname.extend(sample['name'])
|
| 182 |
+
list_target.extend(sample['target'][:, 0].cpu().numpy())
|
| 183 |
+
list_src_len.extend((~sample['net_input']['padding_mask']).sum(1).cpu().numpy())
|
| 184 |
+
latents = []
|
| 185 |
+
logits = []
|
| 186 |
+
for sample in samples:
|
| 187 |
+
sample = utils.move_to_cuda(sample) if use_cuda else sample
|
| 188 |
+
try:
|
| 189 |
+
latent = model.forward_latent(**sample['net_input'])
|
| 190 |
+
latents.append(latent.detach().cpu().numpy())
|
| 191 |
+
except:
|
| 192 |
+
latent = None
|
| 193 |
+
logit = model.forward(**sample['net_input'])
|
| 194 |
+
logits.append(logit.detach().cpu().numpy())
|
| 195 |
+
|
| 196 |
+
if len(latents) > 0:
|
| 197 |
+
latents = np.stack(latents, 1) # B,X,D
|
| 198 |
+
logits = np.stack(logits, 1) # B,X,Cls
|
| 199 |
+
list_latent.extend(latents)
|
| 200 |
+
list_logit.extend(logits)
|
| 201 |
+
|
| 202 |
+
# create big npz
|
| 203 |
+
list_uttname = np.array(list_uttname)
|
| 204 |
+
list_latent = np.array(list_latent)
|
| 205 |
+
list_target = np.array(list_target)
|
| 206 |
+
list_logit = np.array(list_logit)
|
| 207 |
+
list_src_len = np.array(list_src_len)
|
| 208 |
+
# save to npz
|
| 209 |
+
output_path = args.output_path
|
| 210 |
+
if (output_path is None):
|
| 211 |
+
output_path = tempfile.NamedTemporaryFile('wb', delete=False).name
|
| 212 |
+
|
| 213 |
+
with open(output_path, 'wb') as ww:
|
| 214 |
+
np.savez(ww, name=list_uttname,
|
| 215 |
+
latent=list_latent,
|
| 216 |
+
target=list_target,
|
| 217 |
+
logit=list_logit,
|
| 218 |
+
src_len=list_src_len)
|
| 219 |
+
|
| 220 |
+
print("="*10 + " REPORT " + "="*10)
|
| 221 |
+
print(f'| latent saved in {output_path}')
|
| 222 |
+
print(f'| {list_uttname.shape=}, {list_latent.shape=}, {list_target.shape=}, {list_logit.shape=}, {list_src_len.shape=}')
|
omni_speech/infer/fairseq/examples/wmt19/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# WMT 19
|
| 2 |
+
|
| 3 |
+
This page provides pointers to the models of Facebook-FAIR's WMT'19 news translation task submission [(Ng et al., 2019)](https://arxiv.org/abs/1907.06616).
|
| 4 |
+
|
| 5 |
+
## Pre-trained models
|
| 6 |
+
|
| 7 |
+
Model | Description | Download
|
| 8 |
+
---|---|---
|
| 9 |
+
`transformer.wmt19.en-de` | En->De Ensemble | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.en-de.joined-dict.ensemble.tar.gz)
|
| 10 |
+
`transformer.wmt19.de-en` | De->En Ensemble | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.de-en.joined-dict.ensemble.tar.gz)
|
| 11 |
+
`transformer.wmt19.en-ru` | En->Ru Ensemble | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.en-ru.ensemble.tar.gz)
|
| 12 |
+
`transformer.wmt19.ru-en` | Ru->En Ensemble | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.ru-en.ensemble.tar.gz)
|
| 13 |
+
`transformer_lm.wmt19.en` | En Language Model | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt19.en.tar.gz)
|
| 14 |
+
`transformer_lm.wmt19.de` | De Language Model | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt19.de.tar.gz)
|
| 15 |
+
`transformer_lm.wmt19.ru` | Ru Language Model | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt19.ru.tar.gz)
|
| 16 |
+
|
| 17 |
+
## Pre-trained single models before finetuning
|
| 18 |
+
|
| 19 |
+
Model | Description | Download
|
| 20 |
+
---|---|---
|
| 21 |
+
`transformer.wmt19.en-de` | En->De Single, no finetuning | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.en-de.ffn8192.tar.gz)
|
| 22 |
+
`transformer.wmt19.de-en` | De->En Single, no finetuning | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.de-en.ffn8192.tar.gz)
|
| 23 |
+
`transformer.wmt19.en-ru` | En->Ru Single, no finetuning | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.en-ru.ffn8192.tar.gz)
|
| 24 |
+
`transformer.wmt19.ru-en` | Ru->En Single, no finetuning | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.ru-en.ffn8192.tar.gz)
|
| 25 |
+
|
| 26 |
+
## Example usage (torch.hub)
|
| 27 |
+
|
| 28 |
+
#### Requirements
|
| 29 |
+
|
| 30 |
+
We require a few additional Python dependencies for preprocessing:
|
| 31 |
+
```bash
|
| 32 |
+
pip install fastBPE sacremoses
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
#### Translation
|
| 36 |
+
|
| 37 |
+
```python
|
| 38 |
+
import torch
|
| 39 |
+
|
| 40 |
+
# English to German translation
|
| 41 |
+
en2de = torch.hub.load('pytorch/fairseq', 'transformer.wmt19.en-de', checkpoint_file='model1.pt:model2.pt:model3.pt:model4.pt',
|
| 42 |
+
tokenizer='moses', bpe='fastbpe')
|
| 43 |
+
en2de.translate("Machine learning is great!") # 'Maschinelles Lernen ist großartig!'
|
| 44 |
+
|
| 45 |
+
# German to English translation
|
| 46 |
+
de2en = torch.hub.load('pytorch/fairseq', 'transformer.wmt19.de-en', checkpoint_file='model1.pt:model2.pt:model3.pt:model4.pt',
|
| 47 |
+
tokenizer='moses', bpe='fastbpe')
|
| 48 |
+
de2en.translate("Maschinelles Lernen ist großartig!") # 'Machine learning is great!'
|
| 49 |
+
|
| 50 |
+
# English to Russian translation
|
| 51 |
+
en2ru = torch.hub.load('pytorch/fairseq', 'transformer.wmt19.en-ru', checkpoint_file='model1.pt:model2.pt:model3.pt:model4.pt',
|
| 52 |
+
tokenizer='moses', bpe='fastbpe')
|
| 53 |
+
en2ru.translate("Machine learning is great!") # 'Машинное обучение - это здорово!'
|
| 54 |
+
|
| 55 |
+
# Russian to English translation
|
| 56 |
+
ru2en = torch.hub.load('pytorch/fairseq', 'transformer.wmt19.ru-en', checkpoint_file='model1.pt:model2.pt:model3.pt:model4.pt',
|
| 57 |
+
tokenizer='moses', bpe='fastbpe')
|
| 58 |
+
ru2en.translate("Машинное обучение - это здорово!") # 'Machine learning is great!'
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
#### Language Modeling
|
| 62 |
+
|
| 63 |
+
```python
|
| 64 |
+
# Sample from the English LM
|
| 65 |
+
en_lm = torch.hub.load('pytorch/fairseq', 'transformer_lm.wmt19.en', tokenizer='moses', bpe='fastbpe')
|
| 66 |
+
en_lm.sample("Machine learning is") # 'Machine learning is the future of computing, says Microsoft boss Satya Nadella ...'
|
| 67 |
+
|
| 68 |
+
# Sample from the German LM
|
| 69 |
+
de_lm = torch.hub.load('pytorch/fairseq', 'transformer_lm.wmt19.de', tokenizer='moses', bpe='fastbpe')
|
| 70 |
+
de_lm.sample("Maschinelles lernen ist") # 'Maschinelles lernen ist das A und O (neues-deutschland.de) Die Arbeitsbedingungen für Lehrerinnen und Lehrer sind seit Jahren verbesserungswürdig ...'
|
| 71 |
+
|
| 72 |
+
# Sample from the Russian LM
|
| 73 |
+
ru_lm = torch.hub.load('pytorch/fairseq', 'transformer_lm.wmt19.ru', tokenizer='moses', bpe='fastbpe')
|
| 74 |
+
ru_lm.sample("машинное обучение это") # 'машинное обучение это то, что мы называем "искусственным интеллектом".'
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
## Citation
|
| 78 |
+
```bibtex
|
| 79 |
+
@inproceedings{ng2019facebook},
|
| 80 |
+
title = {Facebook FAIR's WMT19 News Translation Task Submission},
|
| 81 |
+
author = {Ng, Nathan and Yee, Kyra and Baevski, Alexei and Ott, Myle and Auli, Michael and Edunov, Sergey},
|
| 82 |
+
booktitle = {Proc. of WMT},
|
| 83 |
+
year = 2019,
|
| 84 |
+
}
|
| 85 |
+
```
|
omni_speech/infer/fairseq/examples/wmt20/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# WMT 20
|
| 2 |
+
|
| 3 |
+
This page provides pointers to the models of Facebook-FAIR's WMT'20 news translation task submission [(Chen et al., 2020)](https://arxiv.org/abs/2011.08298).
|
| 4 |
+
|
| 5 |
+
## Single best MT models (after finetuning on part of WMT20 news dev set)
|
| 6 |
+
|
| 7 |
+
Model | Description | Download
|
| 8 |
+
---|---|---
|
| 9 |
+
`transformer.wmt20.ta-en` | Ta->En | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.ta-en.single.tar.gz)
|
| 10 |
+
`transformer.wmt20.en-ta` | En->Ta | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.en-ta.single.tar.gz)
|
| 11 |
+
`transformer.wmt20.iu-en.news` | Iu->En (News domain) | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.iu-en.news.single.tar.gz)
|
| 12 |
+
`transformer.wmt20.en-iu.news` | En->Iu (News domain) | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.en-iu.news.single.tar.gz)
|
| 13 |
+
`transformer.wmt20.iu-en.nh` | Iu->En (Nunavut Hansard domain) | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.iu-en.nh.single.tar.gz)
|
| 14 |
+
`transformer.wmt20.en-iu.nh` | En->Iu (Nunavut Hansard domain) | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.en-iu.nh.single.tar.gz)
|
| 15 |
+
|
| 16 |
+
## Language models
|
| 17 |
+
Model | Description | Download
|
| 18 |
+
---|---|---
|
| 19 |
+
`transformer_lm.wmt20.en` | En Language Model | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.en.tar.gz)
|
| 20 |
+
`transformer_lm.wmt20.ta` | Ta Language Model | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.ta.tar.gz)
|
| 21 |
+
`transformer_lm.wmt20.iu.news` | Iu Language Model (News domain) | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.iu.news.tar.gz)
|
| 22 |
+
`transformer_lm.wmt20.iu.nh` | Iu Language Model (Nunavut Hansard domain) | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt20.iu.nh.tar.gz)
|
| 23 |
+
|
| 24 |
+
## Example usage (torch.hub)
|
| 25 |
+
|
| 26 |
+
#### Translation
|
| 27 |
+
|
| 28 |
+
```python
|
| 29 |
+
import torch
|
| 30 |
+
|
| 31 |
+
# English to Tamil translation
|
| 32 |
+
en2ta = torch.hub.load('pytorch/fairseq', 'transformer.wmt20.en-ta')
|
| 33 |
+
en2ta.translate("Machine learning is great!") # 'இயந்திரக் கற்றல் அருமை!'
|
| 34 |
+
|
| 35 |
+
# Tamil to English translation
|
| 36 |
+
ta2en = torch.hub.load('pytorch/fairseq', 'transformer.wmt20.ta-en')
|
| 37 |
+
ta2en.translate("இயந்திரக் கற்றல் அருமை!") # 'Machine learning is great!'
|
| 38 |
+
|
| 39 |
+
# English to Inuktitut translation
|
| 40 |
+
en2iu = torch.hub.load('pytorch/fairseq', 'transformer.wmt20.en-iu.news')
|
| 41 |
+
en2iu.translate("machine learning is great!") # 'ᖃᒧᑕᐅᔭᓄᑦ ᐃᓕᓐᓂᐊᕐᓂᖅ ᐱᐅᔪᒻᒪᕆᒃ!'
|
| 42 |
+
|
| 43 |
+
# Inuktitut to English translation
|
| 44 |
+
iu2en = torch.hub.load('pytorch/fairseq', 'transformer.wmt20.iu-en.news')
|
| 45 |
+
iu2en.translate("ᖃᒧᑕᐅᔭᓄᑦ ᐃᓕᓐᓂᐊᕐᓂᖅ ᐱᐅᔪᒻᒪᕆᒃ!") # 'Machine learning excellence!'
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
#### Language Modeling
|
| 49 |
+
|
| 50 |
+
```python
|
| 51 |
+
# Sample from the English LM
|
| 52 |
+
en_lm = torch.hub.load('pytorch/fairseq', 'transformer_lm.wmt20.en')
|
| 53 |
+
en_lm.sample("Machine learning is") # 'Machine learning is a type of artificial intelligence that uses machine learning to learn from data and make predictions.'
|
| 54 |
+
|
| 55 |
+
# Sample from the Tamil LM
|
| 56 |
+
ta_lm = torch.hub.load('pytorch/fairseq', 'transformer_lm.wmt20.ta')
|
| 57 |
+
ta_lm.sample("இயந்திரக் கற்றல் என்பது செயற்கை நுண்ணறிவின்") # 'இயந்திரக் கற்றல் என்பது செயற்கை நுண்ணறிவின் ஒரு பகுதியாகும்.'
|
| 58 |
+
|
| 59 |
+
# Sample from the Inuktitut LM
|
| 60 |
+
iu_lm = torch.hub.load('pytorch/fairseq', 'transformer_lm.wmt20.iu.news')
|
| 61 |
+
iu_lm.sample("ᖃᒧᑕᐅᔭᓄᑦ ᐃᓕᓐᓂᐊᕐᓂᖅ") # 'ᖃᒧᑕᐅᔭᓄᑦ ᐃᓕᓐᓂᐊᕐᓂᖅ, ᐊᒻᒪᓗ ᓯᓚᐅᑉ ᐊᓯᙳᖅᐸᓪᓕᐊᓂᖓᓄᑦ ᖃᓄᐃᓕᐅᕈᑎᒃᓴᑦ, ᐃᓚᖃᖅᖢᑎᒃ ᐅᑯᓂᖓ:'
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
## Citation
|
| 65 |
+
```bibtex
|
| 66 |
+
@inproceedings{chen2020facebook
|
| 67 |
+
title={Facebook AI's WMT20 News Translation Task Submission},
|
| 68 |
+
author={Peng-Jen Chen and Ann Lee and Changhan Wang and Naman Goyal and Angela Fan and Mary Williamson and Jiatao Gu},
|
| 69 |
+
booktitle={Proc. of WMT},
|
| 70 |
+
year={2020},
|
| 71 |
+
}
|
| 72 |
+
```
|
omni_speech/infer/fairseq/examples/wmt21/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# WMT 21
|
| 2 |
+
|
| 3 |
+
This page provides pointers to the models of Facebook AI's WMT'21 news translation task submission [(Tran et al., 2021)](https://arxiv.org/abs/2108.03265).
|
| 4 |
+
|
| 5 |
+
## Single best dense models
|
| 6 |
+
|
| 7 |
+
Model | Description | Download
|
| 8 |
+
---|---|---
|
| 9 |
+
`wmt21.dense-24-wide.X-En` | X-En | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt21.dense-24-wide.X-En.tar.gz)
|
| 10 |
+
`wmt21.dense-24-wide.En-X` | En-X | [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt21.dense-24-wide.En-X.tar.gz)
|
| 11 |
+
|
| 12 |
+
## Example usage
|
| 13 |
+
|
| 14 |
+
See eval.sh
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
## Citation
|
| 18 |
+
```bibtex
|
| 19 |
+
@inproceedings{tran2021facebook
|
| 20 |
+
title={Facebook AI’s WMT21 News Translation Task Submission},
|
| 21 |
+
author={Chau Tran and Shruti Bhosale and James Cross and Philipp Koehn and Sergey Edunov and Angela Fan},
|
| 22 |
+
booktitle={Proc. of WMT},
|
| 23 |
+
year={2021},
|
| 24 |
+
}
|
| 25 |
+
```
|
omni_speech/infer/fairseq/examples/wmt21/eval.sh
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 3 |
+
# All rights reserved.
|
| 4 |
+
#
|
| 5 |
+
# This source code is licensed under the license found in the
|
| 6 |
+
# LICENSE file in the root directory of this source tree.
|
| 7 |
+
SRC=en
|
| 8 |
+
TGT=is
|
| 9 |
+
MODEL_NAME=wmt21.dense-24-wide.En-X
|
| 10 |
+
|
| 11 |
+
PATH_TO_FAIRSEQ_PY=.
|
| 12 |
+
TMP_DIR=generation_tmp
|
| 13 |
+
mkdir -p $TMP_DIR
|
| 14 |
+
|
| 15 |
+
REPLACE_UNICODE_PUNCT=$PATH_TO_FAIRSEQ_PY/examples/wmt21/scripts/replace-unicode-punctuation.perl
|
| 16 |
+
NORM_PUNCT=$PATH_TO_FAIRSEQ_PY/examples/wmt21/scripts/normalize-punctuation.perl
|
| 17 |
+
if [ ! -d "${TMP_DIR}/${MODEL_NAME}" ]; then
|
| 18 |
+
wget https://dl.fbaipublicfiles.com/fairseq/models/${MODEL_NAME}.tar.gz -P $TMP_DIR/
|
| 19 |
+
tar -xvf $TMP_DIR/${MODEL_NAME}.tar.gz -C $TMP_DIR
|
| 20 |
+
fi
|
| 21 |
+
MODEL_DIR=$TMP_DIR/${MODEL_NAME}
|
| 22 |
+
if [ ! -d "${TMP_DIR}/wmt21-news-systems" ]; then
|
| 23 |
+
git clone https://github.com/wmt-conference/wmt21-news-systems $TMP_DIR/wmt21-news-systems
|
| 24 |
+
fi
|
| 25 |
+
|
| 26 |
+
DOMAIN_TAG="wmtdata newsdomain"
|
| 27 |
+
INPUT_FILE=$TMP_DIR/wmt21-news-systems/txt/sources/newstest2021.${SRC}-${TGT}.src.${SRC}
|
| 28 |
+
REF_FILE=$TMP_DIR/wmt21-news-systems/txt/references/newstest2021.${SRC}-${TGT}.ref.A.${TGT}
|
| 29 |
+
|
| 30 |
+
# Translate
|
| 31 |
+
cat ${INPUT_FILE} | sed "s/^/${DOMAIN_TAG} /" | $REPLACE_UNICODE_PUNCT | $NORM_PUNCT -l ${SRC} | python $PATH_TO_FAIRSEQ_PY/fairseq_cli/interactive.py $MODEL_DIR \
|
| 32 |
+
--path ${MODEL_DIR}/checkpoint.pt \
|
| 33 |
+
--task translation_multi_simple_epoch \
|
| 34 |
+
--langs "en,ha,is,ja,cs,ru,zh,de" \
|
| 35 |
+
--lang-pairs $SRC-$TGT \
|
| 36 |
+
--bpe "sentencepiece" \
|
| 37 |
+
--sentencepiece-model ${MODEL_DIR}/sentencepiece.model \
|
| 38 |
+
--buffer-size 1024 \
|
| 39 |
+
--batch-size 10 -s $SRC -t $TGT \
|
| 40 |
+
--decoder-langtok \
|
| 41 |
+
--encoder-langtok src \
|
| 42 |
+
--beam 5 \
|
| 43 |
+
--lenpen 1.0 \
|
| 44 |
+
--fp16 > $TMP_DIR/${SRC}-${TGT}.gen_log
|
| 45 |
+
|
| 46 |
+
cat $TMP_DIR/$SRC-$TGT.gen_log | grep -P "^D-" | cut -f3 > $TMP_DIR/$SRC-$TGT.hyp
|
| 47 |
+
|
| 48 |
+
# Calculate BLEU score
|
| 49 |
+
sacrebleu -l $SRC-$TGT $REF_FILE < $TMP_DIR/$SRC-$TGT.hyp
|
omni_speech/infer/fairseq/examples/wmt21/scripts/normalize-punctuation.perl
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env perl
|
| 2 |
+
#
|
| 3 |
+
# This file is part of moses. Its use is licensed under the GNU Lesser General
|
| 4 |
+
# Public License version 2.1 or, at your option, any later version.
|
| 5 |
+
|
| 6 |
+
use warnings;
|
| 7 |
+
use strict;
|
| 8 |
+
|
| 9 |
+
my $language = "en";
|
| 10 |
+
my $PENN = 0;
|
| 11 |
+
|
| 12 |
+
while (@ARGV) {
|
| 13 |
+
$_ = shift;
|
| 14 |
+
/^-b$/ && ($| = 1, next); # not buffered (flush each line)
|
| 15 |
+
/^-l$/ && ($language = shift, next);
|
| 16 |
+
/^[^\-]/ && ($language = $_, next);
|
| 17 |
+
/^-penn$/ && ($PENN = 1, next);
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
while(<STDIN>) {
|
| 21 |
+
s/\r//g;
|
| 22 |
+
# remove extra spaces
|
| 23 |
+
s/\(/ \(/g;
|
| 24 |
+
s/\)/\) /g; s/ +/ /g;
|
| 25 |
+
s/\) ([\.\!\:\?\;\,])/\)$1/g;
|
| 26 |
+
s/\( /\(/g;
|
| 27 |
+
s/ \)/\)/g;
|
| 28 |
+
s/(\d) \%/$1\%/g;
|
| 29 |
+
s/ :/:/g;
|
| 30 |
+
s/ ;/;/g;
|
| 31 |
+
# normalize unicode punctuation
|
| 32 |
+
if ($PENN == 0) {
|
| 33 |
+
s/\`/\'/g;
|
| 34 |
+
s/\'\'/ \" /g;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
s/„/\"/g;
|
| 38 |
+
s/“/\"/g;
|
| 39 |
+
s/”/\"/g;
|
| 40 |
+
s/–/-/g;
|
| 41 |
+
s/—/ - /g; s/ +/ /g;
|
| 42 |
+
s/´/\'/g;
|
| 43 |
+
s/([a-z])‘([a-z])/$1\'$2/gi;
|
| 44 |
+
s/([a-z])’([a-z])/$1\'$2/gi;
|
| 45 |
+
s/‘/\'/g;
|
| 46 |
+
s/‚/\'/g;
|
| 47 |
+
s/’/\"/g;
|
| 48 |
+
s/''/\"/g;
|
| 49 |
+
s/´´/\"/g;
|
| 50 |
+
s/…/.../g;
|
| 51 |
+
# French quotes
|
| 52 |
+
s/ « / \"/g;
|
| 53 |
+
s/« /\"/g;
|
| 54 |
+
s/«/\"/g;
|
| 55 |
+
s/ » /\" /g;
|
| 56 |
+
s/ »/\"/g;
|
| 57 |
+
s/»/\"/g;
|
| 58 |
+
# handle pseudo-spaces
|
| 59 |
+
s/ \%/\%/g;
|
| 60 |
+
s/nº /nº /g;
|
| 61 |
+
s/ :/:/g;
|
| 62 |
+
s/ ºC/ ºC/g;
|
| 63 |
+
s/ cm/ cm/g;
|
| 64 |
+
s/ \?/\?/g;
|
| 65 |
+
s/ \!/\!/g;
|
| 66 |
+
s/ ;/;/g;
|
| 67 |
+
s/, /, /g; s/ +/ /g;
|
| 68 |
+
|
| 69 |
+
# English "quotation," followed by comma, style
|
| 70 |
+
if ($language eq "en") {
|
| 71 |
+
s/\"([,\.]+)/$1\"/g;
|
| 72 |
+
}
|
| 73 |
+
# Czech is confused
|
| 74 |
+
elsif ($language eq "cs" || $language eq "cz") {
|
| 75 |
+
}
|
| 76 |
+
# German/Spanish/French "quotation", followed by comma, style
|
| 77 |
+
else {
|
| 78 |
+
s/,\"/\",/g;
|
| 79 |
+
s/(\.+)\"(\s*[^<])/\"$1$2/g; # don't fix period at end of sentence
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
if ($language eq "de" || $language eq "es" || $language eq "cz" || $language eq "cs" || $language eq "fr") {
|
| 84 |
+
s/(\d) (\d)/$1,$2/g;
|
| 85 |
+
}
|
| 86 |
+
else {
|
| 87 |
+
s/(\d) (\d)/$1.$2/g;
|
| 88 |
+
}
|
| 89 |
+
print $_;
|
| 90 |
+
}
|
omni_speech/infer/fairseq/examples/wmt21/scripts/replace-unicode-punctuation.perl
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env perl
|
| 2 |
+
#
|
| 3 |
+
# This file is part of moses. Its use is licensed under the GNU Lesser General
|
| 4 |
+
# Public License version 2.1 or, at your option, any later version.
|
| 5 |
+
|
| 6 |
+
use warnings;
|
| 7 |
+
use strict;
|
| 8 |
+
|
| 9 |
+
while (@ARGV) {
|
| 10 |
+
$_ = shift;
|
| 11 |
+
/^-b$/ && ($| = 1, next); # not buffered (flush each line)
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
#binmode(STDIN, ":utf8");
|
| 15 |
+
#binmode(STDOUT, ":utf8");
|
| 16 |
+
|
| 17 |
+
while(<STDIN>) {
|
| 18 |
+
s/,/,/g;
|
| 19 |
+
s/。 */. /g;
|
| 20 |
+
s/、/,/g;
|
| 21 |
+
s/”/"/g;
|
| 22 |
+
s/“/"/g;
|
| 23 |
+
s/∶/:/g;
|
| 24 |
+
s/:/:/g;
|
| 25 |
+
s/?/\?/g;
|
| 26 |
+
s/《/"/g;
|
| 27 |
+
s/》/"/g;
|
| 28 |
+
s/)/\)/g;
|
| 29 |
+
s/!/\!/g;
|
| 30 |
+
s/(/\(/g;
|
| 31 |
+
s/;/;/g;
|
| 32 |
+
s/1/1/g;
|
| 33 |
+
s/」/"/g;
|
| 34 |
+
s/「/"/g;
|
| 35 |
+
s/0/0/g;
|
| 36 |
+
s/3/3/g;
|
| 37 |
+
s/2/2/g;
|
| 38 |
+
s/5/5/g;
|
| 39 |
+
s/6/6/g;
|
| 40 |
+
s/9/9/g;
|
| 41 |
+
s/7/7/g;
|
| 42 |
+
s/8/8/g;
|
| 43 |
+
s/4/4/g;
|
| 44 |
+
s/. */. /g;
|
| 45 |
+
s/~/\~/g;
|
| 46 |
+
s/’/\'/g;
|
| 47 |
+
s/…/\.\.\./g;
|
| 48 |
+
s/━/\-/g;
|
| 49 |
+
s/〈/\</g;
|
| 50 |
+
s/〉/\>/g;
|
| 51 |
+
s/【/\[/g;
|
| 52 |
+
s/】/\]/g;
|
| 53 |
+
s/%/\%/g;
|
| 54 |
+
print $_;
|
| 55 |
+
}
|
omni_speech/infer/fairseq/examples/womens_bios/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Wikipedia Biographies of Women
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
## Training:
|
| 5 |
+
|
| 6 |
+
The training dataset is created based on WikiSum, a dataset created from the paper [Generating Wikipedia by Summarizing Long Sequences](https://arxiv.org/pdf/1801.10198.pdf). The dataset needs to be generated following the instructions in this [Github Repository](https://github.com/tensorflow/tensor2tensor/tree/master/tensor2tensor/data_generators/wikisum).
|
| 7 |
+
|
| 8 |
+
### How is the WikiSum dataset structured?
|
| 9 |
+
|
| 10 |
+
Overall, the task in WikiSum was to generate the entire Wikipedia article based on the contents of the top 10 Google Search Results. The authors provide a way for people to recreate their work. In the WikiSum Github, there are two options for the dataset recreation --- the first is to use CommonCrawl (a static, open source crawl of the web) and the second to do Live Web Fetches. The second has higher coverage, but the content is subject to change and difficult to fetch. We used the static, Commoncrawl version. This can be downloaded following the Github repo instructions, though note it will require usage of Google Cloud.
|
| 11 |
+
|
| 12 |
+
Note: in our experience, it also requires requesting that the resource limit of the Google Cloud instance be raised, which requires emailing.
|
| 13 |
+
|
| 14 |
+
Note: Having higher coverage in the training dataset would be expected to improve the model quality. There are many instances in the dataset where the training input (web evidence) does not contain sufficient content for producing the desired Wikipedia article. This may harm the model's ability to learn to retrieve, look at the input evidence, and overall could contribute to increased challenges in generating verifiable Wikipedia biographies.
|
| 15 |
+
|
| 16 |
+
### How do you go from WikiSum dataset to Biography dataset?
|
| 17 |
+
|
| 18 |
+
The WikiSum dataset is for Wikipedia in general, not just biographies. We do this by querying WikiData to see if the Wikipedia article has an occupation, with the thought that all articles with occupations are probably biographies.
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
## Evaluation:
|
| 22 |
+
|
| 23 |
+
You can download the dataset and baseline model with the following command:
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
wget -N 'https://dl.fbaipublicfiles.com/fairseq/womenbios_dataset.zip'
|
| 27 |
+
wget -N 'https://dl.fbaipublicfiles.com/fairseq/gpt2_bpe/encoder.json'
|
| 28 |
+
wget -N 'https://dl.fbaipublicfiles.com/fairseq/gpt2_bpe/vocab.bpe'
|
| 29 |
+
wget -N 'https://dl.fbaipublicfiles.com/fairseq/gpt2_bpe/dict.txt'
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
We provide the full text Wikipedia articles split into four categories:
|
| 33 |
+
- Women in Africa
|
| 34 |
+
- Women in Asia
|
| 35 |
+
- Women in Science
|
| 36 |
+
- Women
|
| 37 |
+
We note that these are not exhaustive intersectional categories and mainly stem from personal interest.
|
| 38 |
+
|
| 39 |
+
We also provide the URL of the Wikipedia article. Note that Wikipedia articles are constantly being improved, edited, and changed. Thus, it's completely possible that the Wikipedia article on Wikipedia has been lovingly improved by other Wikipedia editors.
|
| 40 |
+
|
| 41 |
+
To get the occupations of each biographical subject, we use WikiData. We provide a sample script to do this. We also provide the raw output of this query.
|
| 42 |
+
|
| 43 |
+
The final part of the evaluation dataset is to query web evidence for each of the biographical subjects. This is the part of the evaluation dataset that requires the most improvement. As we discuss in our paper, one of the major reasons why it is difficult to write biographies for sometimes very well qualified women is that there is not information online about them. Further, the search engine may not find it. We encourage others to improve upon this part of the data, as even re-querying again on the internet may find new, updated sources of information as the web is constantly evolving.
|
| 44 |
+
|
| 45 |
+
We use the search engine from [Internet-Augmented Dialogue Generation](https://arxiv.org/abs/2107.07566), see [project URL](https://parl.ai/projects/sea/) to do the search queries. Note: we remove wikipedia site sources from our query (or we'd query the data itself). However, it's possible Wikipedia information can be copied around in multiple forms on the web, linked with edits, etc.
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
## Section by Section Generation:
|
| 49 |
+
|
| 50 |
+
Wikipedia articles are split into sections, which are usually separated by headings. These headings can be separated in the article text by looking for these equal signs (==), where the number of equal signs usually signals if you are looking at a toplevel heading or a subheading, etc. An example regex that you can use is:
|
| 51 |
+
|
| 52 |
+
`
|
| 53 |
+
section_header_re = re.compile(r"(?<!=)==([^=]+)==(?!=)")
|
| 54 |
+
`
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
## List of Notes:
|
| 58 |
+
- People can have multiple occupations, and we keep all occupations that we query from WikiData
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
## List of Possible Improvement Areas:
|
| 62 |
+
Using a larger generative pre-trained model, larger-scale retrieval, a retrieval encoder specialized to Wikipedia (or biographies), tuning all of the training & generation parameters exhaustively --- and the like --- would most likely be very useful. Overall, we hope that this is a starting point for others who might be interested in focusing on how we can help address the gender gap on Wikipedia.
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
## Interested in Wikipedia and Gender Gap?
|
| 66 |
+
You might want to check out:
|
| 67 |
+
- https://humaniki.wmcloud.org/
|
| 68 |
+
- https://en.wikipedia.org/wiki/Wikipedia:WikiProject_Women_in_Red and https://wikimediafoundation.org/news/2018/10/18/women-in-red-wikiproject/
|
| 69 |
+
- https://meta.wikimedia.org/wiki/Whose_Knowledge%3F/VisibleWikiWomen
|
| 70 |
+
- https://www.ted.com/talks/jess_wade_a_voice_for_diversity_in_science
|
| 71 |
+
|
| 72 |
+
and thanks again to all of the Wikipedia editors and the entire community that is already working so hard to write amazing articles for diverse groups of people.
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
# LICENSE
|
| 76 |
+
This is licensed under CC-BY-NC, however portions of the dataset are available under separate license terms: text sourced from Wikipedia is licensed under CC-BY-SA.
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
|
omni_speech/infer/fairseq/examples/womens_bios/query_occupations_from_wikidata.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from SPARQLWrapper import SPARQLWrapper, JSON
|
| 3 |
+
|
| 4 |
+
endpoint_url = "https://query.wikidata.org/sparql"
|
| 5 |
+
|
| 6 |
+
with open("/your/urls/here") as f:
|
| 7 |
+
data = f.readlines()
|
| 8 |
+
urls = [i.strip() for i in data]
|
| 9 |
+
|
| 10 |
+
def get_results(endpoint_url, URL):
|
| 11 |
+
query = f"""SELECT ?uriLabel ?occupation ?occupationLabel ?dob ?dobLabel WHERE {{
|
| 12 |
+
<{URL}> schema:about ?uri .
|
| 13 |
+
?uri wdt:P106 ?occupation .
|
| 14 |
+
SERVICE wikibase:label {{ bd:serviceParam wikibase:language "en" }}
|
| 15 |
+
}}"""
|
| 16 |
+
user_agent = "WDQS-example Python/%s.%s" % (sys.version_info[0], sys.version_info[1])
|
| 17 |
+
sparql = SPARQLWrapper(endpoint_url, agent=user_agent)
|
| 18 |
+
sparql.setQuery(query)
|
| 19 |
+
sparql.setReturnFormat(JSON)
|
| 20 |
+
return sparql.query().convert()
|
| 21 |
+
|
| 22 |
+
all_occupations = []
|
| 23 |
+
for URL in urls:
|
| 24 |
+
results = get_results(endpoint_url, URL)
|
| 25 |
+
occupations = []
|
| 26 |
+
for result in results["results"]["bindings"]:
|
| 27 |
+
occupations.append(result['occupationLabel']['value'])
|
| 28 |
+
all_occupations.append(result['uriLabel']['value'] + ", " + ", ".join(occupations))
|
| 29 |
+
|
| 30 |
+
assert(len(all_occupations) == len(urls))
|
| 31 |
+
|
| 32 |
+
with open("/your/file/output/here", "w") as o:
|
| 33 |
+
for line in all_occupations:
|
| 34 |
+
o.write(line.strip() + "\n")
|
omni_speech/infer/fairseq/examples/xformers/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Using xFormers with FairSeq
|
| 2 |
+
|
| 3 |
+
[xFormers](https://github.com/facebookresearch/xformers) is a xFormers is a modular library for flexibly generating transformer architectures with interoperable and optimized building blocks.
|
| 4 |
+
The current integration allows for FairSeq users to use an attention variant available in the xFormers repository.
|
| 5 |
+
|
| 6 |
+
In order to enable xFormers, all that needs to be passed in is a string representing an [xFormers attention config](https://github.com/facebookresearch/xformers/blob/5f754129bfb1ea53747b1ab2077261ea762faa47/xformers/components/attention/base.py#L18).
|
| 7 |
+
|
| 8 |
+
The various attention variants can be found [here](https://github.com/facebookresearch/xformers/tree/main/xformers/components/attention).
|
| 9 |
+
These include sparse attention and blocksparse attention.
|
| 10 |
+
|
| 11 |
+
For example, you could pass in the following args:
|
| 12 |
+
```python
|
| 13 |
+
decoder_xformers_att_config = '{"name": "scaled_dot_product"}'
|
| 14 |
+
|
| 15 |
+
encoder_xformers_att_config = '{"name": "linformer", "seq_len": "256"}'
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
In order to use blocksparse attention you would have to additionally pass in a blocksparse layout and blocksize. For example:
|
| 19 |
+
|
| 20 |
+
```python
|
| 21 |
+
|
| 22 |
+
xformers_att_config = '{"name": "scaled_dot_product"}'
|
| 23 |
+
xformers_blocksparse_blocksize = 16
|
| 24 |
+
xformers_blocksparse_layout = torch.ones(
|
| 25 |
+
seq_len // xformers_blocksparse_blocksize,
|
| 26 |
+
seq_len // xformers_blocksparse_blocksize,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
xf_blocksparse_mha = (
|
| 30 |
+
MultiheadAttention(
|
| 31 |
+
embedding,
|
| 32 |
+
num_heads,
|
| 33 |
+
dropout=0.0,
|
| 34 |
+
add_zero_attn=add_zero_attn,
|
| 35 |
+
xformers_att_config=xformers_att_config,
|
| 36 |
+
xformers_blocksparse_layout=xformers_blocksparse_layout,
|
| 37 |
+
xformers_blocksparse_blocksize=xformers_blocksparse_blocksize,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
The xFormers repository currenlty has benchmarks on the [runtime](https://github.com/facebookresearch/xformers/blob/main/docs/plots/runtime_vs_attention.png)
|
| 43 |
+
and [memory usage](https://github.com/facebookresearch/xformers/blob/main/docs/plots/memory_vs_attention.png) of the various attentions.
|
omni_speech/infer/fairseq/examples/xglm/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Few-shot Learning with Multilingual Language Models
|
| 2 |
+
|
| 3 |
+
## Introduction
|
| 4 |
+
|
| 5 |
+
In this work, we train a family of multilingual generative language models, dubbed XGLM, on a balanced corpus covering a diverse set of languages, and study their few- and zero-shot learning capabilities in a wide range of tasks. Our largest model with 7.5 billion parameters sets new state of the art in few-shot learning on more than 20 representative languages, outperforming GPT-3 of comparable size in multilingual commonsense reasoning (+7.4 accuracy points for 0-shot, +9.4 for 4-shot) and natural language inference (+5.4 for 0-shot, +5.4 for 4-shot). We have included a [model card](model_card.md) of XGLM for transparency and accountability.
|
| 6 |
+
|
| 7 |
+
## Data and Languages
|
| 8 |
+
XGLM models are trained on a new multilingual corpus extracted from CommonCrawl (CC100-XL), a significantly larger multilingual dataset covering 68 Common Crawl (CC) snapshots (from [Summer 2013](http://commoncrawl.org/2013/11/new-crawl-data-available/) to [March/April 2020](https://commoncrawl.org/2020/04/march-april-2020-crawl-archive-now-available/) consisting of 134 languages. The detailed languages and data statistics are reported in the paper (Table A.1).
|
| 9 |
+
|
| 10 |
+
## Pre-trained models
|
| 11 |
+
|
| 12 |
+
Model | Layers | Model Dim | FFN Dim | Languages | Download
|
| 13 |
+
---|---|---|---|---|---
|
| 14 |
+
`XGLM 564M` | 24 | 1024 | 4096 | trained on 30 languages| [xglm.564M.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xglm/xglm.564M.tar.gz)
|
| 15 |
+
`XGLM 1.7B` | 24 | 2048 | 8192 | trained on 30 languages| [xglm.1.7B.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xglm/xglm.1.7B.tar.gz)
|
| 16 |
+
`XGLM 2.9B` | 48 | 2048 | 8192 | trained on 30 languages| [xglm.2.9B.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xglm/xglm.2.9B.tar.gz)
|
| 17 |
+
`XGLM 7.5B` | 32 | 4096 | 16384 | trained on 30 languages| [xglm.7.5B.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xglm/xglm.7.5B.tar.gz)
|
| 18 |
+
`XGLM 4.5B` | 48 | 2048 | 16384 | trained on 134 languages| [xglm.4.5B.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xglm/xglm.4.5B.tar.gz)
|
| 19 |
+
|
| 20 |
+
## Pre-training Data Format
|
| 21 |
+
Our models were pre-trained with data in the following format (i.e. paragraphs are separated with new lines and documents were separated with double new lines).
|
| 22 |
+
```
|
| 23 |
+
<doc0,para0,tok0> ... <doc0,para0,tokX0> # X0: number of tokens in para0 of doc0
|
| 24 |
+
<doc0,para1,tok0> ... <doc0,para1,tokY0> # Y0: number of tokens in para1 of doc0
|
| 25 |
+
|
| 26 |
+
<doc1,para0,tok0> ... <doc1,para0,tokX1> # X1: number of tokens in para0 of doc1
|
| 27 |
+
<doc1,para1,tok0> ... <doc1,para1,tokY1> # Y1: number of tokens in para1 of doc1
|
| 28 |
+
|
| 29 |
+
...
|
| 30 |
+
```
|
| 31 |
+
Fairseq's preprocessing replaces newlines with the end-of-sentence symbol (`</s>`). As a result, the models never saw newline characters during pretraining and the same preprocessing should be run prior to few-shot inference to maximize performance. For example, our language model scoring function has `replace_newlines_with_eos` argument to trigger this preprocessing:
|
| 32 |
+
```python
|
| 33 |
+
from fairseq.models.transformer_lm import TransformerLanguageModel
|
| 34 |
+
|
| 35 |
+
model_dir = 'path_to_decompressed_tar_gz_dir'
|
| 36 |
+
lm = TransformerLanguageModel.from_pretrained(model_dir, bpe='sentencepiece')
|
| 37 |
+
|
| 38 |
+
text = """First paragraph of the first document.
|
| 39 |
+
Second paragraph of the first document.
|
| 40 |
+
|
| 41 |
+
First paragraph of the second document.
|
| 42 |
+
"""
|
| 43 |
+
tokens = lm.score(text, replace_newlines_with_eos=True)['tokens']
|
| 44 |
+
assert '\n' not in lm.decode(tokens) # no newlines were encoded
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
## Evaluation
|
| 48 |
+
|
| 49 |
+
### Example (COPA)
|
| 50 |
+
|
| 51 |
+
The following snippet show how to evaluate our models on the Choice of Plausible Alternatives (COPA) task, using examples in English, Chinese and Hindi.
|
| 52 |
+
|
| 53 |
+
```python
|
| 54 |
+
data_samples = {
|
| 55 |
+
'en': [
|
| 56 |
+
{
|
| 57 |
+
"premise": "I wanted to conserve energy.",
|
| 58 |
+
"choice1": "I swept the floor in the unoccupied room.",
|
| 59 |
+
"choice2": "I shut off the light in the unoccupied room.",
|
| 60 |
+
"question": "effect",
|
| 61 |
+
"label": "1"
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"premise": "The flame on the candle went out.",
|
| 65 |
+
"choice1": "I blew on the wick.",
|
| 66 |
+
"choice2": "I put a match to the wick.",
|
| 67 |
+
"question": "cause",
|
| 68 |
+
"label": "0"
|
| 69 |
+
}
|
| 70 |
+
],
|
| 71 |
+
'zh': [
|
| 72 |
+
{
|
| 73 |
+
"premise": "我想节约能源。",
|
| 74 |
+
"choice1": "我在空着的房间里扫了地板。",
|
| 75 |
+
"choice2": "我把空房间里的灯关了。",
|
| 76 |
+
"question": "effect",
|
| 77 |
+
"label": "1"
|
| 78 |
+
},
|
| 79 |
+
{
|
| 80 |
+
"premise": "蜡烛上的火焰熄灭了。",
|
| 81 |
+
"choice1": "我吹灭了灯芯。",
|
| 82 |
+
"choice2": "我把一根火柴放在灯芯上。",
|
| 83 |
+
"question": "cause",
|
| 84 |
+
"label": "0"
|
| 85 |
+
}
|
| 86 |
+
],
|
| 87 |
+
'hi': [
|
| 88 |
+
{
|
| 89 |
+
"premise": "M te vle konsève enèji.",
|
| 90 |
+
"choice1": "Mwen te fin baleye chanm lib la.",
|
| 91 |
+
"choice2": "Mwen te femen limyè nan chanm lib la.",
|
| 92 |
+
"question": "effect",
|
| 93 |
+
"label": "1"
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"premise": "Flam bouji a te etenn.",
|
| 97 |
+
"choice1": "Mwen te soufle bouji a.",
|
| 98 |
+
"choice2": "Mwen te limen mèch bouji a.",
|
| 99 |
+
"question": "cause",
|
| 100 |
+
"label": "0"
|
| 101 |
+
}
|
| 102 |
+
]
|
| 103 |
+
}
|
| 104 |
+
```
|
| 105 |
+
In this example, we format the examples use the non-verbal prompts `{premise}\n{choice1}` and `{premise}\n{choice2}`, which are shared by all three languages.
|
| 106 |
+
```python
|
| 107 |
+
from fairseq.models.transformer_lm import TransformerLanguageModel
|
| 108 |
+
|
| 109 |
+
model_dir = 'path_to_decompressed_tar_gz_dir'
|
| 110 |
+
lm = TransformerLanguageModel.from_pretrained(model_dir, bpe='sentencepiece')
|
| 111 |
+
lm = lm.eval()
|
| 112 |
+
lm = lm.half()
|
| 113 |
+
lm = lm.cuda()
|
| 114 |
+
|
| 115 |
+
def get_logprobs(prompt):
|
| 116 |
+
import re
|
| 117 |
+
prompt = re.sub('\n+' , '\n', prompt) # collapse repeated newlines, which indicate separate documents
|
| 118 |
+
return lm.score(prompt, replace_newlines_with_eos=True)['positional_scores']
|
| 119 |
+
|
| 120 |
+
# Zero-shot evaluation for the Choice of Plausible Alternatives (COPA) task.
|
| 121 |
+
# A return value of 0 indicates that the first alternative is more plausible,
|
| 122 |
+
# while 1 indicates that the second alternative is more plausible.
|
| 123 |
+
def COPA_eval(prompt, alternative1, alternative2):
|
| 124 |
+
lprob1 = get_logprobs(prompt + "\n" + alternative1).sum()
|
| 125 |
+
lprob2 = get_logprobs(prompt + "\n" + alternative2).sum()
|
| 126 |
+
return 0 if lprob1 > lprob2 else 1
|
| 127 |
+
|
| 128 |
+
for lang in ['en', 'zh', 'hi']:
|
| 129 |
+
for idx, example in enumerate(data_samples[lang]):
|
| 130 |
+
predict = COPA_eval(example["premise"], example["choice1"], example["choice2"])
|
| 131 |
+
print(f'{lang}-{idx}', predict, example['label'])
|
| 132 |
+
|
| 133 |
+
# en-0 1 1
|
| 134 |
+
# en-1 0 0
|
| 135 |
+
# zh-0 1 1
|
| 136 |
+
# zh-1 0 0
|
| 137 |
+
# hi-0 1 1
|
| 138 |
+
# hi-1 0 0
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
## XStoryCloze
|
| 142 |
+
|
| 143 |
+
We release XStoryCloze, a new multilingual dataset intended for few-shot evaluation, alongside this paper. XStoryCloze consists of professional translation of the validation split of the [English StoryCloze dataset](https://cs.rochester.edu/nlp/rocstories/) (Spring 2016 version) to 10 other languages. It is opensourced under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/legalcode), the same license as the English StoryCloze.
|
| 144 |
+
|
| 145 |
+
You can download the dataset via [this link](https://dl.fbaipublicfiles.com/xstorycloze.zip).
|
| 146 |
+
|
| 147 |
+
Language | ar | es | eu | hi | id | my | ru | sw | te | zh
|
| 148 |
+
---|---|---|---|---|---|---|---|---|---|---
|
| 149 |
+
Train size | 360 | 360 | 360 | 360 | 360 | 360 | 360 | 360 | 360 | 360
|
| 150 |
+
Eval size | 1511 | 1511 | 1511 | 1511 | 1511 | 1511 | 1511 | 1511 | 1511 | 1511
|
| 151 |
+
|
| 152 |
+
Please refer to [the dataset doc](XStoryCloze.md) for more information.
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
## Publication
|
| 156 |
+
[Few-shot Learning with Multilingual Generative Language Models](https://arxiv.org/abs/2112.10668).
|
| 157 |
+
Xi Victoria Lin*, Todor Mihaylov, Mikel Artetxe, Tianlu Wang, Shuohui Chen, Daniel Simig, Myle Ott, Naman Goyal, Shruti Bhosale, Jingfei Du, Ramakanth Pasunuru, Sam Shleifer, Punit Singh Koura, Vishrav Chaudhary, Brian O'Horo, Jeff Wang, Luke Zettlemoyer, Zornitsa Kozareva, Mona Diab, Veselin Stoyanov, Xian Li* (* Equal Contribution).
|
| 158 |
+
EMNLP 2022.
|
| 159 |
+
|
| 160 |
+
## Citation
|
| 161 |
+
```
|
| 162 |
+
@article{DBLP:journals/corr/abs-2112-10668,
|
| 163 |
+
author = {Xi Victoria Lin and
|
| 164 |
+
Todor Mihaylov and
|
| 165 |
+
Mikel Artetxe and
|
| 166 |
+
Tianlu Wang and
|
| 167 |
+
Shuohui Chen and
|
| 168 |
+
Daniel Simig and
|
| 169 |
+
Myle Ott and
|
| 170 |
+
Naman Goyal and
|
| 171 |
+
Shruti Bhosale and
|
| 172 |
+
Jingfei Du and
|
| 173 |
+
Ramakanth Pasunuru and
|
| 174 |
+
Sam Shleifer and
|
| 175 |
+
Punit Singh Koura and
|
| 176 |
+
Vishrav Chaudhary and
|
| 177 |
+
Brian O'Horo and
|
| 178 |
+
Jeff Wang and
|
| 179 |
+
Luke Zettlemoyer and
|
| 180 |
+
Zornitsa Kozareva and
|
| 181 |
+
Mona T. Diab and
|
| 182 |
+
Veselin Stoyanov and
|
| 183 |
+
Xian Li},
|
| 184 |
+
title = {Few-shot Learning with Multilingual Language Models},
|
| 185 |
+
journal = {CoRR},
|
| 186 |
+
volume = {abs/2112.10668},
|
| 187 |
+
year = {2021},
|
| 188 |
+
url = {https://arxiv.org/abs/2112.10668},
|
| 189 |
+
eprinttype = {arXiv},
|
| 190 |
+
eprint = {2112.10668},
|
| 191 |
+
timestamp = {Tue, 04 Jan 2022 15:59:27 +0100},
|
| 192 |
+
biburl = {https://dblp.org/rec/journals/corr/abs-2112-10668.bib},
|
| 193 |
+
bibsource = {dblp computer science bibliography, https://dblp.org}
|
| 194 |
+
}
|
| 195 |
+
```
|
omni_speech/infer/fairseq/examples/xglm/XStoryCloze.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
XStoryCloze consists of professional translation of the validation split of the [English StoryCloze dataset](https://cs.rochester.edu/nlp/rocstories/) (Spring 2016 version) to 10 other languages. This dataset is released by Meta AI alongside the paper [Few-shot Learning with Multilingual Generative Language Models. EMNLP 2022](https://arxiv.org/abs/2112.10668).
|
| 2 |
+
|
| 3 |
+
# Languages
|
| 4 |
+
ru, zh (Simplified), es (Latin America), ar, hi, id, te, sw, eu, my.
|
| 5 |
+
|
| 6 |
+
# Data Splits
|
| 7 |
+
This dataset is intended to be used for evaluating the zero- and few-shot learning capabilities of multlingual language models. We split the data for each language into train and test (360 vs. 1510 examples, respectively). The released data files for different languages maintain a line-by-line alignment.
|
| 8 |
+
|
| 9 |
+
# Access English StoryCloze
|
| 10 |
+
Please request the original English StoryCloze dataset through the [official website](https://cs.rochester.edu/nlp/rocstories/). You can create a split of the en data following our data split scheme using the following commands:
|
| 11 |
+
```
|
| 12 |
+
head -361 spring2016.val.tsv > spring2016.val.en.tsv.split_20_80_train.tsv
|
| 13 |
+
|
| 14 |
+
head -1 spring2016.val.tsv > spring2016.val.en.tsv.split_20_80_eval.tsv # TSV header
|
| 15 |
+
tail -1511 spring2016.val.tsv >> spring2016.val.en.tsv.split_20_80_eval.tsv
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
# Licence
|
| 19 |
+
XStoryCloze is opensourced under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/legalcode), the same license as the original English StoryCloze.
|
| 20 |
+
|
| 21 |
+
# Citation
|
| 22 |
+
We hope this dataset is helpful for the research and wider NLP community. If you use XStoryCloze in your work, please cite
|
| 23 |
+
```
|
| 24 |
+
@article{DBLP:journals/corr/abs-2112-10668,
|
| 25 |
+
author = {Xi Victoria Lin and
|
| 26 |
+
Todor Mihaylov and
|
| 27 |
+
Mikel Artetxe and
|
| 28 |
+
Tianlu Wang and
|
| 29 |
+
Shuohui Chen and
|
| 30 |
+
Daniel Simig and
|
| 31 |
+
Myle Ott and
|
| 32 |
+
Naman Goyal and
|
| 33 |
+
Shruti Bhosale and
|
| 34 |
+
Jingfei Du and
|
| 35 |
+
Ramakanth Pasunuru and
|
| 36 |
+
Sam Shleifer and
|
| 37 |
+
Punit Singh Koura and
|
| 38 |
+
Vishrav Chaudhary and
|
| 39 |
+
Brian O'Horo and
|
| 40 |
+
Jeff Wang and
|
| 41 |
+
Luke Zettlemoyer and
|
| 42 |
+
Zornitsa Kozareva and
|
| 43 |
+
Mona T. Diab and
|
| 44 |
+
Veselin Stoyanov and
|
| 45 |
+
Xian Li},
|
| 46 |
+
title = {Few-shot Learning with Multilingual Language Models},
|
| 47 |
+
journal = {CoRR},
|
| 48 |
+
volume = {abs/2112.10668},
|
| 49 |
+
year = {2021},
|
| 50 |
+
url = {https://arxiv.org/abs/2112.10668},
|
| 51 |
+
eprinttype = {arXiv},
|
| 52 |
+
eprint = {2112.10668},
|
| 53 |
+
timestamp = {Tue, 04 Jan 2022 15:59:27 +0100},
|
| 54 |
+
biburl = {https://dblp.org/rec/journals/corr/abs-2112-10668.bib},
|
| 55 |
+
bibsource = {dblp computer science bibliography, https://dblp.org}
|
| 56 |
+
}
|
| 57 |
+
```
|
omni_speech/infer/fairseq/examples/xglm/model_card.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XGLM multilingual model
|
| 2 |
+
## Version 1.0.0
|
| 3 |
+
|
| 4 |
+
### Model developer
|
| 5 |
+
Meta AI
|
| 6 |
+
|
| 7 |
+
### Model type
|
| 8 |
+
A family of multilingual autoregressive language models (ranging from 564 million to 7.5 billion parameters) trained on a balanced corpus of a diverse set of languages. The language model can learn tasks from natural language descriptions and a few examples.
|
| 9 |
+
|
| 10 |
+
### Model Feedback Channel
|
| 11 |
+
https://github.com/pytorch/fairseq
|
| 12 |
+
|
| 13 |
+
## Intended use
|
| 14 |
+
### Primary intended use
|
| 15 |
+
For research purposes only, e.g. reproducing model evaluation results. Generation is only used in a limited capacity for explanation/justification or for prompting/probing/priming for class labels.
|
| 16 |
+
|
| 17 |
+
### Out of scope uses
|
| 18 |
+
The primary purpose of the model is not to generate language, although the model is capable of doing that.
|
| 19 |
+
|
| 20 |
+
## Potential risks
|
| 21 |
+
This section lists the potential risks associated with using the model.
|
| 22 |
+
|
| 23 |
+
### Relevant factors
|
| 24 |
+
Based on known problems with NLP technology, potential relevant factors include output correctness, robustness, bias (gender, profession, race and religion), etc.
|
| 25 |
+
|
| 26 |
+
### Evaluation factors
|
| 27 |
+
The model was evaluated on hate speech detection and occupation identification.
|
| 28 |
+
* Hate speech detection (Huang et al. (2020)) - A safety task to test language models’ ability to identify hateful and offensive text.
|
| 29 |
+
* Occupation identification (De-Arteaga et al., 2019), (Zhao et al., 2020) - A bias task to study language models’ performance divergence between different gender groups on the task of occupation identification.
|
| 30 |
+
|
| 31 |
+
## Metrics
|
| 32 |
+
### Model performance measures
|
| 33 |
+
The XGLM model was primarily evaluated on
|
| 34 |
+
1. Zero shot and few shot learning by looking at per-language performance on tasks spanning commonsense reasoning (XCOPA, XWinograd), natural language inference (XNLI) and paraphrasing (PAWS-X). The model is also evaluated on XStoryCloze, a new dataset created by Meta AI.
|
| 35 |
+
2. Cross lingual transfer through templates and few-shot examples.
|
| 36 |
+
3. Knowledge probing - Evaluate to what extent the XGLM model can effectively store factual knowledge in different languages using the mLAMA benchmark.
|
| 37 |
+
4. Translation - We report machine translation results on WMT benchmarks and a subset of FLORES-101 in the main paper.
|
| 38 |
+
|
| 39 |
+
The model was also evaluated on hate speech datasets introduced by Huang et al. (2020) and an occupation identification dataset by De-Arteaga et al. 2019 to identify bias in the model.
|
| 40 |
+
|
| 41 |
+
### Approaches to handle uncertainty
|
| 42 |
+
Report confidence intervals, variance metrics for the model performance metrics. Few-shot evaluation was conducted with different sampling with 5 seeds. We reported statistical significance.
|
| 43 |
+
|
| 44 |
+
## Evaluation data
|
| 45 |
+
## Zero Shot and Few Shot evaluation
|
| 46 |
+
|
| 47 |
+
### XNLI (Conneau et al., 2018)
|
| 48 |
+
#### Description
|
| 49 |
+
The Cross-lingual Natural Language Inference (XNLI) corpus is the extension of the Multi-Genre NLI (MultiNLI) corpus to 15 languages. The dataset was created by manually translating the validation and test sets of MultiNLI into each of those 15 languages.
|
| 50 |
+
|
| 51 |
+
### XStoryCloze
|
| 52 |
+
#### Description
|
| 53 |
+
A new dataset created by Meta AI along side this work by translating the validation split of the English StoryCloze dataset (Mostafazadeh et al., 2016) (Spring 2016 version) to 10 other typologically diverse languages (ru, zh Simplified, es Latin America, ar, hi, id, te, sw, eu, my).
|
| 54 |
+
|
| 55 |
+
### XCOPA (Ponti et al., 2020)
|
| 56 |
+
#### Description
|
| 57 |
+
The Cross-lingual Choice of Plausible Alternatives (XCOPA) dataset is a benchmark to evaluate the ability of machine learning models to transfer commonsense reasoning across languages. The dataset is the translation and reannotation of the English COPA (Roemmele et al. 2011) and covers 11 languages from 11 families and several areas around the globe.
|
| 58 |
+
|
| 59 |
+
### XWinograd (Tikhonov and Ryabinin, 2021)
|
| 60 |
+
#### Description
|
| 61 |
+
XWinograd is a multilingual collection of Winograd Schemas in six languages that can be used for evaluation of cross-lingual commonsense reasoning capabilities.
|
| 62 |
+
|
| 63 |
+
### PAWS-X (Yang et al., 2019)
|
| 64 |
+
#### Description
|
| 65 |
+
PAWS-X contains 23,659 human translated PAWS evaluation pairs and 296,406 machine translated training pairs in six typologically distinct languages: French, Spanish, German, Chinese, Japanese, and Korean. All translated pairs are sourced from examples in PAWS-Wiki.
|
| 66 |
+
|
| 67 |
+
## Responsible AI (RAI) evaluation
|
| 68 |
+
### Hate speech (Huang et al. 2020)
|
| 69 |
+
This is a multilingual Twitter corpus for the task of hate speech detection with inferred four author demographic factors: age, country, gender and race/ethnicity. The corpus covers five languages: English, Italian, Polish, Portuguese and Spanish.
|
| 70 |
+
|
| 71 |
+
### Bias dataset (De-Arteaga et al. 2019)
|
| 72 |
+
The aim of this dataset is to study the gender bias of models that identify a person’s occupation from their bios.
|
| 73 |
+
|
| 74 |
+
----
|
| 75 |
+
|
| 76 |
+
## Training data
|
| 77 |
+
### CC100-XL
|
| 78 |
+
#### Description
|
| 79 |
+
Following the recent success of multilingual self-supervised pre-training (Devlin et al., 2019; Lample and Conneau, 2019; Con; Xue et al., 2020; Goyal et al., 2021a; Liu et al., 2020), we train our language models on a mixture of monolingual text of different languages. We extended the pipeline used for mining the CC100 corpus to generate CC100-XL, a significantly larger multilingual dataset covering 68 Common Crawl snapshots (from Summer 2013 to March/April 2020) and 134 languages.
|
| 80 |
+
|
| 81 |
+
More details on the CC100-XL dataset can be found in the Appendix section of the paper.
|
| 82 |
+
|
| 83 |
+
## RAI Dimensions
|
| 84 |
+
### Fairness (Bias and inclusion)
|
| 85 |
+
The XGLM model was evaluated on Hate speech and bias identification datasets. For hate speech, we observe that across the 5 languages in the dataset, in context learning results are only slightly better than random (50%). Another interesting observation is that most few shot results are worse than zero-shot, which indicates that the model is not able to utilize examples using the templates described in the paper. For bias identification, the XGLM (6.7B) English only model achieves the best performance on English and Spanish, while the GPT-3 model of comparable size (6.7B) model achieves the best in French. On certain occupations (e.g. model and teacher), XGLM 6.7B En only model and GPT-3 (6.7B) have very significant bias while XGLM 7.5B is much less biased.
|
| 86 |
+
|
| 87 |
+
### Privacy and security
|
| 88 |
+
The XGLM model did not have any special Privacy and Security considerations. The training data and evaluation data were both public and went through standard Meta AI Privacy and licensing procedures.
|
| 89 |
+
|
| 90 |
+
### Transparency and control
|
| 91 |
+
In the spirit of transparency and accountability we have created this model card and a data card for the CC100-XL which can be found in the Appendix section of the paper.
|
| 92 |
+
|
| 93 |
+
### Efficiency (Green AI)
|
| 94 |
+
From an engineering perspective, XGLM pertains to a family of models that represent single unified models catering to many languages which have wide application across many applications. Such a unified single model saves on carbon footprint as well as energy consumption (comparing to the alternative: separate models for different languages) leading to more energy efficiency. A single model, despite having the risk of being a single point of failure, has the powerful incentive of being easier to maintain, access, distribute, and track.
|
| 95 |
+
|
| 96 |
+
## References
|
| 97 |
+
Edoardo Maria Ponti, Goran Glavas, Olga Majewska, Qianchu Liu, Ivan Vulic, and Anna Korhonen. 2020. XCOPA: A multilingual dataset for causal commonsense reasoning. In Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing, EMNLP 2020, Online, November 16-20, 2020, pages 2362–2376. Association for Computational Linguistics.
|
| 98 |
+
XCOPA Dataset | Papers With Code
|
| 99 |
+
|
| 100 |
+
Alexey Tikhonov and Max Ryabinin. 2021. It’s all in the heads: Using attention heads as a baseline for cross-lingual transfer in commonsense reasoning. In Findings of the Association for Computational Linguistics: ACL/IJCNLP 2021, Online Event, August 1-6, 2021, volume ACL/IJCNLP 2021 of Findings of ACL, pages 3534–3546. Association for Computational Linguistics.
|
| 101 |
+
XWINO Dataset | Papers With Code (XWinograd)
|
| 102 |
+
|
| 103 |
+
Yinfei Yang, Yuan Zhang, Chris Tar, and Jason Baldridge. 2019. PAWS-X: A cross-lingual adversarial dataset for paraphrase identification. CoRR, abs/1908.11828.
|
| 104 |
+
PAWS-X Dataset | Papers With Code
|
| 105 |
+
|
| 106 |
+
Alexis Conneau, Guillaume Lample, Ruty Rinott, Adina Williams, Samuel R. Bowman, Holger Schwenk, and Veselin Stoyanov. 2018. XNLI: evaluating cross-lingual sentence representations. CoRR, abs/1809.05053.
|
| 107 |
+
XNLI Dataset | Papers With Code
|
| 108 |
+
|
| 109 |
+
Xiaolei Huang, Linzi Xing, Franck Dernoncourt, and Michael Paul. 2020. Multilingual twitter corpus and baselines for evaluating demographic bias in hate speech recognition. In Proceedings of the 12th Language Resources and Evaluation Conference, pages 1440–1448.
|
| 110 |
+
|
| 111 |
+
Maria De-Arteaga, Alexey Romanov, Hanna Wallach, Jennifer Chayes, Christian Borgs, Alexandra Chouldechova, Sahin Geyik, Krishnaram Kenthapadi, and Adam Tauman Kalai. 2019. Bias in bios: A case study of semantic representation bias in a high-stakes setting. In proceedings of the Conference on Fairness, Accountability, and Transparency, pages 120–128.
|
| 112 |
+
|
| 113 |
+
Nasrin Mostafazadeh, Nathanael Chambers, Xiaodong He, Devi Parikh, Dhruv Batra, Lucy Vanderwende, Pushmeet Kohli, James F. Allen. A Corpus and Evaluation Framework for Deeper Understanding of Commonsense Stories. CoRR abs/1604.01696.
|
| 114 |
+
|
| 115 |
+
Jieyu Zhao, Subhabrata Mukherjee, Saghar Hosseini, Kai-Wei Chang, and Ahmed Hassan Awadallah. 2020. Gender bias in multilingual embeddings and crosslingual transfer. In Proceedings of the 58th Annual Meeting of the Association for Computational Linguistics, pages 2896–2907.
|
| 116 |
+
|
| 117 |
+
## Citation details
|
| 118 |
+
```
|
| 119 |
+
@article{DBLP:journals/corr/abs-2112-10668,
|
| 120 |
+
author = {Xi Victoria Lin and
|
| 121 |
+
Todor Mihaylov and
|
| 122 |
+
Mikel Artetxe and
|
| 123 |
+
Tianlu Wang and
|
| 124 |
+
Shuohui Chen and
|
| 125 |
+
Daniel Simig and
|
| 126 |
+
Myle Ott and
|
| 127 |
+
Naman Goyal and
|
| 128 |
+
Shruti Bhosale and
|
| 129 |
+
Jingfei Du and
|
| 130 |
+
Ramakanth Pasunuru and
|
| 131 |
+
Sam Shleifer and
|
| 132 |
+
Punit Singh Koura and
|
| 133 |
+
Vishrav Chaudhary and
|
| 134 |
+
Brian O'Horo and
|
| 135 |
+
Jeff Wang and
|
| 136 |
+
Luke Zettlemoyer and
|
| 137 |
+
Zornitsa Kozareva and
|
| 138 |
+
Mona T. Diab and
|
| 139 |
+
Veselin Stoyanov and
|
| 140 |
+
Xian Li},
|
| 141 |
+
title = {Few-shot Learning with Multilingual Language Models},
|
| 142 |
+
journal = {CoRR},
|
| 143 |
+
volume = {abs/2112.10668},
|
| 144 |
+
year = {2021},
|
| 145 |
+
url = {https://arxiv.org/abs/2112.10668},
|
| 146 |
+
eprinttype = {arXiv},
|
| 147 |
+
eprint = {2112.10668},
|
| 148 |
+
timestamp = {Tue, 04 Jan 2022 15:59:27 +0100},
|
| 149 |
+
biburl = {https://dblp.org/rec/journals/corr/abs-2112-10668.bib},
|
| 150 |
+
bibsource = {dblp computer science bibliography, https://dblp.org}
|
| 151 |
+
}
|
| 152 |
+
```
|
omni_speech/infer/fairseq/examples/xlmr/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Unsupervised Cross-lingual Representation Learning at Scale (XLM-RoBERTa)
|
| 2 |
+
https://arxiv.org/pdf/1911.02116.pdf
|
| 3 |
+
|
| 4 |
+
# Larger-Scale Transformers for Multilingual Masked Language Modeling
|
| 5 |
+
https://arxiv.org/pdf/2105.00572.pdf
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
## What's New:
|
| 9 |
+
- June 2021: `XLMR-XL` AND `XLMR-XXL` models released.
|
| 10 |
+
|
| 11 |
+
## Introduction
|
| 12 |
+
|
| 13 |
+
`XLM-R` (`XLM-RoBERTa`) is a generic cross lingual sentence encoder that obtains state-of-the-art results on many cross-lingual understanding (XLU) benchmarks. It is trained on `2.5T` of filtered CommonCrawl data in 100 languages (list below).
|
| 14 |
+
|
| 15 |
+
Language | Language|Language |Language | Language
|
| 16 |
+
---|---|---|---|---
|
| 17 |
+
Afrikaans | Albanian | Amharic | Arabic | Armenian
|
| 18 |
+
Assamese | Azerbaijani | Basque | Belarusian | Bengali
|
| 19 |
+
Bengali Romanize | Bosnian | Breton | Bulgarian | Burmese
|
| 20 |
+
Burmese zawgyi font | Catalan | Chinese (Simplified) | Chinese (Traditional) | Croatian
|
| 21 |
+
Czech | Danish | Dutch | English | Esperanto
|
| 22 |
+
Estonian | Filipino | Finnish | French | Galician
|
| 23 |
+
Georgian | German | Greek | Gujarati | Hausa
|
| 24 |
+
Hebrew | Hindi | Hindi Romanize | Hungarian | Icelandic
|
| 25 |
+
Indonesian | Irish | Italian | Japanese | Javanese
|
| 26 |
+
Kannada | Kazakh | Khmer | Korean | Kurdish (Kurmanji)
|
| 27 |
+
Kyrgyz | Lao | Latin | Latvian | Lithuanian
|
| 28 |
+
Macedonian | Malagasy | Malay | Malayalam | Marathi
|
| 29 |
+
Mongolian | Nepali | Norwegian | Oriya | Oromo
|
| 30 |
+
Pashto | Persian | Polish | Portuguese | Punjabi
|
| 31 |
+
Romanian | Russian | Sanskrit | Scottish Gaelic | Serbian
|
| 32 |
+
Sindhi | Sinhala | Slovak | Slovenian | Somali
|
| 33 |
+
Spanish | Sundanese | Swahili | Swedish | Tamil
|
| 34 |
+
Tamil Romanize | Telugu | Telugu Romanize | Thai | Turkish
|
| 35 |
+
Ukrainian | Urdu | Urdu Romanize | Uyghur | Uzbek
|
| 36 |
+
Vietnamese | Welsh | Western Frisian | Xhosa | Yiddish
|
| 37 |
+
|
| 38 |
+
## Pre-trained models
|
| 39 |
+
|
| 40 |
+
Model | Description | #params | vocab size | Download
|
| 41 |
+
---|---|---|---|---
|
| 42 |
+
`xlmr.base` | XLM-R using the BERT-base architecture | 250M | 250k | [xlm.base.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xlmr.base.tar.gz)
|
| 43 |
+
`xlmr.large` | XLM-R using the BERT-large architecture | 560M | 250k | [xlm.large.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xlmr.large.tar.gz)
|
| 44 |
+
`xlmr.xl` | XLM-R (`layers=36, model_dim=2560`) | 3.5B | 250k | [xlm.xl.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xlmr/xlmr.xl.tar.gz)
|
| 45 |
+
`xlmr.xxl` | XLM-R (`layers=48, model_dim=4096`) | 10.7B | 250k | [xlm.xxl.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xlmr/xlmr.xxl.tar.gz)
|
| 46 |
+
|
| 47 |
+
## Results
|
| 48 |
+
|
| 49 |
+
**[XNLI (Conneau et al., 2018)](https://arxiv.org/abs/1809.05053)**
|
| 50 |
+
|
| 51 |
+
Model | average | en | fr | es | de | el | bg | ru | tr | ar | vi | th | zh | hi | sw | ur
|
| 52 |
+
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---
|
| 53 |
+
`roberta.large.mnli` _(TRANSLATE-TEST)_ | 77.8 | 91.3 | 82.9 | 84.3 | 81.2 | 81.7 | 83.1 | 78.3 | 76.8 | 76.6 | 74.2 | 74.1 | 77.5 | 70.9 | 66.7 | 66.8
|
| 54 |
+
`xlmr.large` _(TRANSLATE-TRAIN-ALL)_ | 83.6 | 89.1 | 85.1 | 86.6 | 85.7 | 85.3 | 85.9 | 83.5 | 83.2 | 83.1 | 83.7 | 81.5 | 83.7 | 81.6 | 78.0 | 78.1
|
| 55 |
+
`xlmr.xl` _(TRANSLATE-TRAIN-ALL)_ | 85.4 | 91.1 | 87.2 | 88.1 | 87.0 | 87.4 | 87.8 | 85.3 | 85.2 | 85.3 | 86.2 | 83.8 | 85.3 | 83.1 | 79.8 | 78.2 | 85.4
|
| 56 |
+
`xlmr.xxl` _(TRANSLATE-TRAIN-ALL)_ | 86.0 | 91.5 | 87.6 | 88.7 | 87.8 | 87.4 | 88.2 | 85.6 | 85.1 | 85.8 | 86.3 | 83.9 | 85.6 | 84.6 | 81.7 | 80.6
|
| 57 |
+
|
| 58 |
+
**[MLQA (Lewis et al., 2018)](https://arxiv.org/abs/1910.07475)**
|
| 59 |
+
|
| 60 |
+
Model | average | en | es | de | ar | hi | vi | zh
|
| 61 |
+
---|---|---|---|---|---|---|---|---
|
| 62 |
+
`BERT-large` | - | 80.2/67.4 | - | - | - | - | - | -
|
| 63 |
+
`mBERT` | 57.7 / 41.6 | 77.7 / 65.2 | 64.3 / 46.6 | 57.9 / 44.3 | 45.7 / 29.8| 43.8 / 29.7 | 57.1 / 38.6 | 57.5 / 37.3
|
| 64 |
+
`xlmr.large` | 70.7 / 52.7 | 80.6 / 67.8 | 74.1 / 56.0 | 68.5 / 53.6 | 63.1 / 43.5 | 69.2 / 51.6 | 71.3 / 50.9 | 68.0 / 45.4
|
| 65 |
+
`xlmr.xl` | 73.4 / 55.3 | 85.1 / 72.6 | 66.7 / 46.2 | 70.5 / 55.5 | 74.3 / 56.9 | 72.2 / 54.7 | 74.4 / 52.9 | 70.9 / 48.5
|
| 66 |
+
`xlmr.xxl` | 74.8 / 56.6 | 85.5 / 72.4 | 68.6 / 48.4 | 72.7 / 57.8 | 75.4 / 57.6 | 73.7 / 55.8 | 76.0 / 55.0 | 71.7 / 48.9
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
## Example usage
|
| 70 |
+
|
| 71 |
+
##### Load XLM-R from torch.hub (PyTorch >= 1.1):
|
| 72 |
+
```python
|
| 73 |
+
import torch
|
| 74 |
+
xlmr = torch.hub.load('pytorch/fairseq:main', 'xlmr.large')
|
| 75 |
+
xlmr.eval() # disable dropout (or leave in train mode to finetune)
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
##### Load XLM-R (for PyTorch 1.0 or custom models):
|
| 79 |
+
```python
|
| 80 |
+
# Download xlmr.large model
|
| 81 |
+
wget https://dl.fbaipublicfiles.com/fairseq/models/xlmr.large.tar.gz
|
| 82 |
+
tar -xzvf xlmr.large.tar.gz
|
| 83 |
+
|
| 84 |
+
# Load the model in fairseq
|
| 85 |
+
from fairseq.models.roberta import XLMRModel
|
| 86 |
+
xlmr = XLMRModel.from_pretrained('/path/to/xlmr.large', checkpoint_file='model.pt')
|
| 87 |
+
xlmr.eval() # disable dropout (or leave in train mode to finetune)
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
##### Apply sentence-piece-model (SPM) encoding to input text:
|
| 91 |
+
```python
|
| 92 |
+
en_tokens = xlmr.encode('Hello world!')
|
| 93 |
+
assert en_tokens.tolist() == [0, 35378, 8999, 38, 2]
|
| 94 |
+
xlmr.decode(en_tokens) # 'Hello world!'
|
| 95 |
+
|
| 96 |
+
zh_tokens = xlmr.encode('你好,世界')
|
| 97 |
+
assert zh_tokens.tolist() == [0, 6, 124084, 4, 3221, 2]
|
| 98 |
+
xlmr.decode(zh_tokens) # '你好,世界'
|
| 99 |
+
|
| 100 |
+
hi_tokens = xlmr.encode('नमस्ते दुनिया')
|
| 101 |
+
assert hi_tokens.tolist() == [0, 68700, 97883, 29405, 2]
|
| 102 |
+
xlmr.decode(hi_tokens) # 'नमस्ते दुनिया'
|
| 103 |
+
|
| 104 |
+
ar_tokens = xlmr.encode('مرحبا بالعالم')
|
| 105 |
+
assert ar_tokens.tolist() == [0, 665, 193478, 258, 1705, 77796, 2]
|
| 106 |
+
xlmr.decode(ar_tokens) # 'مرحبا بالعالم'
|
| 107 |
+
|
| 108 |
+
fr_tokens = xlmr.encode('Bonjour le monde')
|
| 109 |
+
assert fr_tokens.tolist() == [0, 84602, 95, 11146, 2]
|
| 110 |
+
xlmr.decode(fr_tokens) # 'Bonjour le monde'
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
##### Extract features from XLM-R:
|
| 114 |
+
```python
|
| 115 |
+
# Extract the last layer's features
|
| 116 |
+
last_layer_features = xlmr.extract_features(zh_tokens)
|
| 117 |
+
assert last_layer_features.size() == torch.Size([1, 6, 1024])
|
| 118 |
+
|
| 119 |
+
# Extract all layer's features (layer 0 is the embedding layer)
|
| 120 |
+
all_layers = xlmr.extract_features(zh_tokens, return_all_hiddens=True)
|
| 121 |
+
assert len(all_layers) == 25
|
| 122 |
+
assert torch.all(all_layers[-1] == last_layer_features)
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
## Citation
|
| 126 |
+
|
| 127 |
+
```bibtex
|
| 128 |
+
@article{conneau2019unsupervised,
|
| 129 |
+
title={Unsupervised Cross-lingual Representation Learning at Scale},
|
| 130 |
+
author={Conneau, Alexis and Khandelwal, Kartikay and Goyal, Naman and Chaudhary, Vishrav and Wenzek, Guillaume and Guzm{\'a}n, Francisco and Grave, Edouard and Ott, Myle and Zettlemoyer, Luke and Stoyanov, Veselin},
|
| 131 |
+
journal={arXiv preprint arXiv:1911.02116},
|
| 132 |
+
year={2019}
|
| 133 |
+
}
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
```bibtex
|
| 138 |
+
@article{goyal2021larger,
|
| 139 |
+
title={Larger-Scale Transformers for Multilingual Masked Language Modeling},
|
| 140 |
+
author={Goyal, Naman and Du, Jingfei and Ott, Myle and Anantharaman, Giri and Conneau, Alexis},
|
| 141 |
+
journal={arXiv preprint arXiv:2105.00572},
|
| 142 |
+
year={2021}
|
| 143 |
+
}
|
| 144 |
+
```
|
omni_speech/infer/fairseq/examples/xmod/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# X-MOD: Lifting the Curse of Multilinguality by Pre-training Modular Transformers
|
| 2 |
+
|
| 3 |
+
https://arxiv.org/abs/2205.06266
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
## Introduction
|
| 7 |
+
|
| 8 |
+
X-MOD extends multilingual masked language models like XLM-R to include language-specific modular components, introduced at each transformer layer. Each module is only used by one language. For fine-tuning, the modular components are frozen, and replaced with the target language in cross-lingual transfer settings.
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
## Pre-trained models
|
| 12 |
+
|
| 13 |
+
Model | Size | # train steps | # langs | Download
|
| 14 |
+
---|---|---|---|---
|
| 15 |
+
`xmod.base.13.125k` | BERT-base | 125k | 13 | [xmod.base.13.125k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.13.125k.tar.gz)
|
| 16 |
+
`xmod.base.30.125k` | BERT-base | 125k | 30 | [xmod.base.30.125k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.30.125k.tar.gz)
|
| 17 |
+
`xmod.base.30.195k` | BERT-base | 195k | 30 | [xmod.base.30.195k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.30.195k.tar.gz)
|
| 18 |
+
`xmod.base.60.125k` | BERT-base | 125k | 60 | [xmod.base.60.125k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.60.125k.tar.gz)
|
| 19 |
+
`xmod.base.60.265k` | BERT-base | 265k | 60 | [xmod.base.60.265k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.60.265k.tar.gz)
|
| 20 |
+
`xmod.base.75.125k` | BERT-base | 125k | 75 | [xmod.base.75.125k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.75.125k.tar.gz)
|
| 21 |
+
`xmod.base.75.269k` | BERT-base | 269k | 75 | [xmod.base.75.269k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.75.269k.tar.gz)
|
| 22 |
+
`xmod.base` | BERT-base | 1M | 81 | [xmod.base.81.1M.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.base.81.1M.tar.gz)
|
| 23 |
+
`xmod.large.prenorm` | BERT-large | 500k | 81 | [xmod.large.prenorm.81.500k.tar.gz](https://dl.fbaipublicfiles.com/fairseq/models/xmod/xmod.large.prenorm.81.500k.tar.gz)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
## Fine-tuning on NLI
|
| 27 |
+
|
| 28 |
+
We next provide an example of how to fine-tune the pre-trained models above on Natural Language Inference (NLI). We use MNLI for training in English, and show how to run inference in other languages.
|
| 29 |
+
|
| 30 |
+
### 1) Download a pre-trained model
|
| 31 |
+
|
| 32 |
+
```bash
|
| 33 |
+
MODEL=xmod.base.81.1M
|
| 34 |
+
wget https://dl.fbaipublicfiles.com/fairseq/models/xmod/$MODEL.tar.gz
|
| 35 |
+
tar -xzf $MODEL.tar.gz
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
### 2) Download and preprocess [MNLI](https://cims.nyu.edu/~sbowman/multinli/)
|
| 39 |
+
```bash
|
| 40 |
+
wget https://cims.nyu.edu/~sbowman/multinli/multinli_1.0.zip
|
| 41 |
+
unzip multinli_1.0.zip
|
| 42 |
+
python ./examples/xmod/preprocess_nli.py \
|
| 43 |
+
--sentencepiece-model $MODEL/sentencepiece.bpe.model \
|
| 44 |
+
--train multinli_1.0/multinli_1.0_train.jsonl \
|
| 45 |
+
--valid multinli_1.0/multinli_1.0_dev_matched.jsonl \
|
| 46 |
+
--destdir multinli_1.0/fairseq
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### 3) Fine-tune on MNLI:
|
| 50 |
+
|
| 51 |
+
```bash
|
| 52 |
+
MAX_EPOCH=5
|
| 53 |
+
LR=1e-05
|
| 54 |
+
BATCH_SIZE=32
|
| 55 |
+
DATA_DIR=multinli_1.0/fairseq/bin
|
| 56 |
+
|
| 57 |
+
CUDA_VISIBLE_DEVICES=0 fairseq-train $DATA_DIR \
|
| 58 |
+
--restore-file $MODEL/model.pt \
|
| 59 |
+
--save-dir $MODEL/nli \
|
| 60 |
+
--reset-optimizer \
|
| 61 |
+
--reset-dataloader \
|
| 62 |
+
--reset-meters \
|
| 63 |
+
--best-checkpoint-metric accuracy \
|
| 64 |
+
--maximize-best-checkpoint-metric \
|
| 65 |
+
--task sentence_prediction_adapters \
|
| 66 |
+
--num-classes 3 \
|
| 67 |
+
--init-token 0 \
|
| 68 |
+
--separator-token 2 \
|
| 69 |
+
--max-positions 512 \
|
| 70 |
+
--shorten-method "truncate" \
|
| 71 |
+
--arch xmod_base \
|
| 72 |
+
--dropout 0.1 \
|
| 73 |
+
--attention-dropout 0.1 \
|
| 74 |
+
--weight-decay 0.01 \
|
| 75 |
+
--criterion sentence_prediction_adapters \
|
| 76 |
+
--optimizer adam \
|
| 77 |
+
--adam-betas '(0.9, 0.98)' \
|
| 78 |
+
--adam-eps 1e-06 \
|
| 79 |
+
--clip-norm 0.0 \
|
| 80 |
+
--lr-scheduler fixed \
|
| 81 |
+
--lr $LR \
|
| 82 |
+
--fp16 \
|
| 83 |
+
--fp16-init-scale 4 \
|
| 84 |
+
--threshold-loss-scale 1 \
|
| 85 |
+
--fp16-scale-window 128 \
|
| 86 |
+
--batch-size $BATCH_SIZE \
|
| 87 |
+
--required-batch-size-multiple 1 \
|
| 88 |
+
--update-freq 1 \
|
| 89 |
+
--max-epoch $MAX_EPOCH
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
### 4) Run inference
|
| 93 |
+
|
| 94 |
+
After training the model, we can load it and run inference in our target language. The default language is set to English, which is why we were not required to pass a language ID to the model during fine-tuning. To run inference in a non-English language, we need to tell the model that the module of the target language should be used instead:
|
| 95 |
+
|
| 96 |
+
```python
|
| 97 |
+
from fairseq.models.xmod import XMODModel
|
| 98 |
+
|
| 99 |
+
MODEL='xmod.base.81.1M/nli'
|
| 100 |
+
DATA='multinli_1.0/fairseq/bin'
|
| 101 |
+
|
| 102 |
+
# Load model
|
| 103 |
+
model = XMODModel.from_pretrained(
|
| 104 |
+
model_name_or_path=MODEL,
|
| 105 |
+
checkpoint_file='checkpoint_best.pt',
|
| 106 |
+
data_name_or_path=DATA,
|
| 107 |
+
suffix='',
|
| 108 |
+
criterion='cross_entropy',
|
| 109 |
+
bpe='sentencepiece',
|
| 110 |
+
sentencepiece_model=DATA+'/input0/sentencepiece.bpe.model')
|
| 111 |
+
model = model.eval(); # disable dropout
|
| 112 |
+
model = model.half(); # use FP16
|
| 113 |
+
model = model.cuda(); # move to GPU
|
| 114 |
+
|
| 115 |
+
def predict(premise, hypothesis, lang):
|
| 116 |
+
tokens = model.encode(premise, hypothesis)
|
| 117 |
+
idx = model.predict('sentence_classification_head', tokens, lang_id=[lang]).argmax().item()
|
| 118 |
+
dictionary = model.task.label_dictionary
|
| 119 |
+
return dictionary[idx + dictionary.nspecial]
|
| 120 |
+
|
| 121 |
+
predict(
|
| 122 |
+
premise='X-Mod hat spezifische Module die für jede Sprache existieren.',
|
| 123 |
+
hypothesis='X-Mod hat Module.',
|
| 124 |
+
lang='de_DE'
|
| 125 |
+
) # entailment
|
| 126 |
+
|
| 127 |
+
predict(
|
| 128 |
+
premise='Londres es la capital del Reino Unido.',
|
| 129 |
+
hypothesis='Londres está en Francia.',
|
| 130 |
+
lang='es_XX',
|
| 131 |
+
) # contradiction
|
| 132 |
+
|
| 133 |
+
predict(
|
| 134 |
+
premise='Patxik gogoko ditu babarrunak.',
|
| 135 |
+
hypothesis='Patxik babarrunak bazkaldu zituen.',
|
| 136 |
+
lang='eu_ES',
|
| 137 |
+
) # neutral
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
## Citation
|
| 142 |
+
|
| 143 |
+
```bibtex
|
| 144 |
+
@misc{pfeiffer2022xmod,
|
| 145 |
+
doi = {10.48550/ARXIV.2205.06266},
|
| 146 |
+
url = {https://arxiv.org/abs/2205.06266},
|
| 147 |
+
title = {Lifting the Curse of Multilinguality by Pre-training Modular Transformers},
|
| 148 |
+
publisher = {arXiv},
|
| 149 |
+
year = {2022},
|
| 150 |
+
}
|
| 151 |
+
```
|
omni_speech/infer/fairseq/examples/xmod/preprocess_nli.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import json
|
| 8 |
+
import collections
|
| 9 |
+
import argparse
|
| 10 |
+
import shutil
|
| 11 |
+
import subprocess
|
| 12 |
+
import sys
|
| 13 |
+
import tempfile
|
| 14 |
+
from multiprocessing import Pool
|
| 15 |
+
import sentencepiece as spm
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def preprocess(spm_model_path, train_path, valid_path, test_path, dest_dir, remove_empty=False, output_format='piece', workers=20):
|
| 19 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 20 |
+
# Tokenize with SentencePiece
|
| 21 |
+
for split, path in ('train', train_path), ('valid', valid_path), ('test', test_path):
|
| 22 |
+
if path is None:
|
| 23 |
+
continue
|
| 24 |
+
if path == '-':
|
| 25 |
+
path = sys.stdin.fileno()
|
| 26 |
+
with open(path, encoding='utf-8', errors='surrogateescape') as fin:
|
| 27 |
+
with open(f'{tmp}/{split}', mode='w', encoding='utf-8', errors='surrogateescape') as fout:
|
| 28 |
+
encoder = MultiprocessingEncoder(model=spm_model_path, remove_empty=remove_empty, output_format=output_format)
|
| 29 |
+
pool = Pool(workers, initializer=encoder.initializer)
|
| 30 |
+
encoded_lines = pool.imap(encoder.encode, fin, 10000)
|
| 31 |
+
for i, line in enumerate(encoded_lines, start=1):
|
| 32 |
+
if line is not None:
|
| 33 |
+
print(line, file=fout)
|
| 34 |
+
if i % 10000 == 0:
|
| 35 |
+
print("tokenized {} lines".format(i), file=sys.stderr)
|
| 36 |
+
|
| 37 |
+
# Generate dictionary
|
| 38 |
+
sp = spm.SentencePieceProcessor(model_file=spm_model_path)
|
| 39 |
+
if output_format == 'piece':
|
| 40 |
+
vocab = [sp.id_to_piece(i) for i in range(3, sp.vocab_size())]
|
| 41 |
+
else:
|
| 42 |
+
vocab = map(str, range(sp.vocab_size()))
|
| 43 |
+
with open(f'{tmp}/dict.txt', mode='w', encoding='utf-8', errors='surrogateescape') as f:
|
| 44 |
+
for word in vocab:
|
| 45 |
+
print(word, 1, file=f)
|
| 46 |
+
|
| 47 |
+
# Binarize
|
| 48 |
+
command = [
|
| 49 |
+
'python3', '-m', 'fairseq_cli.preprocess',
|
| 50 |
+
'--only-source',
|
| 51 |
+
'--thresholdsrc', '0',
|
| 52 |
+
'--destdir', dest_dir,
|
| 53 |
+
'--srcdict', f'{tmp}/dict.txt',
|
| 54 |
+
'--workers', '20',
|
| 55 |
+
]
|
| 56 |
+
for split, path in ('train', train_path), ('valid', valid_path), ('test', test_path):
|
| 57 |
+
if path is not None:
|
| 58 |
+
command += [f'--{split}pref', f'{tmp}/{split}']
|
| 59 |
+
subprocess.run(command)
|
| 60 |
+
|
| 61 |
+
# Copy SentencePiece model
|
| 62 |
+
shutil.copyfile(spm_model_path, f'{dest_dir}/sentencepiece.bpe.model')
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class MultiprocessingEncoder(object):
|
| 66 |
+
def __init__(self, model, remove_empty, output_format):
|
| 67 |
+
self.model = model
|
| 68 |
+
self.remove_empty = remove_empty
|
| 69 |
+
self.output_format = output_format
|
| 70 |
+
|
| 71 |
+
def initializer(self):
|
| 72 |
+
global sp
|
| 73 |
+
sp = spm.SentencePieceProcessor(model_file=self.model)
|
| 74 |
+
|
| 75 |
+
def encode(self, line):
|
| 76 |
+
global sp
|
| 77 |
+
line = line.strip()
|
| 78 |
+
if len(line) == 0 and self.remove_empty:
|
| 79 |
+
return None
|
| 80 |
+
|
| 81 |
+
if self.output_format == 'piece':
|
| 82 |
+
return ' '.join(sp.encode_as_pieces(line))
|
| 83 |
+
else:
|
| 84 |
+
return ' '.join(map(str, sp.encode(line)))
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def write_lines(lines, path):
|
| 88 |
+
with open(path, mode='x', encoding='utf-8') as f:
|
| 89 |
+
for line in lines:
|
| 90 |
+
print(line, file=f)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def read_jsonl(path):
|
| 94 |
+
with open(path, encoding='utf-8') as f:
|
| 95 |
+
return [json.loads(line) for line in f.read().splitlines()]
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def read_nli(path, langs=None):
|
| 99 |
+
data = read_jsonl(path)
|
| 100 |
+
|
| 101 |
+
if langs is not None:
|
| 102 |
+
data = [sample for sample in data if sample.get('language') in langs]
|
| 103 |
+
|
| 104 |
+
lang2count = collections.defaultdict(int)
|
| 105 |
+
for sample in data:
|
| 106 |
+
lang2count[sample.get('language')] += 1
|
| 107 |
+
|
| 108 |
+
if langs:
|
| 109 |
+
assert set(lang2count.keys()) == set(langs)
|
| 110 |
+
|
| 111 |
+
nlangs = len(lang2count)
|
| 112 |
+
assert nlangs > 0
|
| 113 |
+
lens = list(lang2count.values())
|
| 114 |
+
assert all([lens[0] == length for length in lens])
|
| 115 |
+
|
| 116 |
+
print(f'Loaded {lens[0]} samples in {nlangs} languages from {path}', file=sys.stderr)
|
| 117 |
+
return data
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def main():
|
| 121 |
+
parser = argparse.ArgumentParser(description='Tokenize and binarize NLI data')
|
| 122 |
+
parser.add_argument('--sentencepiece-model', required=True)
|
| 123 |
+
parser.add_argument('--train', required=True, help='Training data in jsonl format')
|
| 124 |
+
parser.add_argument('--valid', required=True, help='Validation data in jsonl format')
|
| 125 |
+
parser.add_argument('--destdir', required=True)
|
| 126 |
+
|
| 127 |
+
args = parser.parse_args()
|
| 128 |
+
|
| 129 |
+
os.makedirs(args.destdir + '/raw',)
|
| 130 |
+
os.makedirs(args.destdir + '/bin', )
|
| 131 |
+
|
| 132 |
+
# Extract input/labels
|
| 133 |
+
for split, path in ('train', args.train), ('valid', args.valid):
|
| 134 |
+
data = read_nli(path, langs=None)
|
| 135 |
+
original_size = len(data)
|
| 136 |
+
data = [sample for sample in data if sample['gold_label'] != '-']
|
| 137 |
+
assert all(sample['gold_label'] in ('contradiction', 'entailment', 'neutral') for sample in data)
|
| 138 |
+
filtered_size = len(data)
|
| 139 |
+
if filtered_size != original_size:
|
| 140 |
+
print(f'Filtered {filtered_size}/{original_size} samples from {path}', file=sys.stderr)
|
| 141 |
+
for name, field in ('input0', 'sentence1'), ('input1', 'sentence2'), ('label', 'gold_label'):
|
| 142 |
+
write_lines([sample[field] for sample in data], f'{args.destdir}/raw/{split}.{name}.txt')
|
| 143 |
+
|
| 144 |
+
# Tokenize and binarize input
|
| 145 |
+
for field in 'input0', 'input1':
|
| 146 |
+
preprocess(
|
| 147 |
+
spm_model_path=args.sentencepiece_model,
|
| 148 |
+
train_path=f'{args.destdir}/raw/train.{field}.txt',
|
| 149 |
+
valid_path=f'{args.destdir}/raw/valid.{field}.txt',
|
| 150 |
+
test_path=None,
|
| 151 |
+
dest_dir=f'{args.destdir}/bin/{field}',
|
| 152 |
+
workers=20,
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
# Binarize labels
|
| 156 |
+
subprocess.run([
|
| 157 |
+
'python3', '-m', 'fairseq_cli.preprocess',
|
| 158 |
+
'--trainpref', f'{args.destdir}/raw/train.label.txt',
|
| 159 |
+
'--validpref', f'{args.destdir}/raw/valid.label.txt',
|
| 160 |
+
'--only-source',
|
| 161 |
+
'--thresholdsrc', '0',
|
| 162 |
+
'--destdir', f'{args.destdir}/bin/label',
|
| 163 |
+
'--workers', '20',
|
| 164 |
+
])
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
if __name__ == '__main__':
|
| 168 |
+
main()
|
omni_speech/infer/fairseq/fairseq/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
"""isort:skip_file"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
try:
|
| 11 |
+
from .version import __version__ # noqa
|
| 12 |
+
except ImportError:
|
| 13 |
+
version_txt = os.path.join(os.path.dirname(__file__), "version.txt")
|
| 14 |
+
with open(version_txt) as f:
|
| 15 |
+
__version__ = f.read().strip()
|
| 16 |
+
|
| 17 |
+
__all__ = ["pdb"]
|
| 18 |
+
|
| 19 |
+
# backwards compatibility to support `from fairseq.X import Y`
|
| 20 |
+
from fairseq.distributed import utils as distributed_utils
|
| 21 |
+
from fairseq.logging import meters, metrics, progress_bar # noqa
|
| 22 |
+
|
| 23 |
+
sys.modules["fairseq.distributed_utils"] = distributed_utils
|
| 24 |
+
sys.modules["fairseq.meters"] = meters
|
| 25 |
+
sys.modules["fairseq.metrics"] = metrics
|
| 26 |
+
sys.modules["fairseq.progress_bar"] = progress_bar
|
| 27 |
+
|
| 28 |
+
# initialize hydra
|
| 29 |
+
from fairseq.dataclass.initialize import hydra_init
|
| 30 |
+
|
| 31 |
+
hydra_init()
|
| 32 |
+
|
| 33 |
+
import fairseq.criterions # noqa
|
| 34 |
+
import fairseq.distributed # noqa
|
| 35 |
+
import fairseq.models # noqa
|
| 36 |
+
import fairseq.modules # noqa
|
| 37 |
+
import fairseq.optim # noqa
|
| 38 |
+
import fairseq.optim.lr_scheduler # noqa
|
| 39 |
+
import fairseq.pdb # noqa
|
| 40 |
+
import fairseq.scoring # noqa
|
| 41 |
+
import fairseq.tasks # noqa
|
| 42 |
+
import fairseq.token_generation_constraints # noqa
|
| 43 |
+
|
| 44 |
+
import fairseq.benchmark # noqa
|
| 45 |
+
import fairseq.model_parallel # noqa
|
omni_speech/infer/fairseq/fairseq/benchmark/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
# import models/tasks to register them
|
| 7 |
+
from . import dummy_dataset, dummy_lm, dummy_masked_lm, dummy_model, dummy_mt # noqa
|
omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (314 Bytes). View file
|
|
|
omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_dataset.cpython-312.pyc
ADDED
|
Binary file (2.2 kB). View file
|
|
|
omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_lm.cpython-312.pyc
ADDED
|
Binary file (4.72 kB). View file
|
|
|
omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_masked_lm.cpython-312.pyc
ADDED
|
Binary file (5.1 kB). View file
|
|
|
omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_model.cpython-312.pyc
ADDED
|
Binary file (5 kB). View file
|
|
|
omni_speech/infer/fairseq/fairseq/benchmark/__pycache__/dummy_mt.cpython-312.pyc
ADDED
|
Binary file (6.88 kB). View file
|
|
|
omni_speech/infer/fairseq/fairseq/benchmark/benchmark_multihead_attention.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import itertools
|
| 7 |
+
import random
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
from torch.utils import benchmark
|
| 11 |
+
|
| 12 |
+
from fairseq.modules.multihead_attention import MultiheadAttention
|
| 13 |
+
|
| 14 |
+
BATCH = [20, 41, 97]
|
| 15 |
+
SEQ = 64
|
| 16 |
+
EMB = 48
|
| 17 |
+
HEADS = 4
|
| 18 |
+
DROP = 0.1
|
| 19 |
+
DEVICE = torch.device("cuda")
|
| 20 |
+
ATTN_MASK_DTYPE = [torch.uint8, torch.bool, torch.float]
|
| 21 |
+
KEY_PADDING_MASK_DTYPE = [torch.uint8, torch.bool]
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _reset_seeds():
|
| 25 |
+
torch.manual_seed(0)
|
| 26 |
+
random.seed(0)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _get_mask(to_dtype: torch.dtype, dim0: int, dim1: int):
|
| 30 |
+
if to_dtype == torch.float:
|
| 31 |
+
mask = torch.randint(0, 2, (dim0, dim1)).to(dtype=torch.bool)
|
| 32 |
+
return mask.to(dtype=to_dtype).masked_fill(mask, -float("inf"))
|
| 33 |
+
return torch.randint(0, 2, (dim0, dim1)).to(dtype=to_dtype)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def benchmark_multihead_attention(
|
| 37 |
+
label="",
|
| 38 |
+
attn_dtype=torch.uint8,
|
| 39 |
+
key_padding_dtype=torch.uint8,
|
| 40 |
+
add_bias_kv=False,
|
| 41 |
+
add_zero_attn=False,
|
| 42 |
+
static_kv=False,
|
| 43 |
+
batch_size=20,
|
| 44 |
+
embedding=EMB,
|
| 45 |
+
seq_len=SEQ,
|
| 46 |
+
num_heads=HEADS,
|
| 47 |
+
):
|
| 48 |
+
|
| 49 |
+
results = []
|
| 50 |
+
# device = torch.device("cuda")
|
| 51 |
+
|
| 52 |
+
xformers_att_config = '{"name": "scaled_dot_product"}'
|
| 53 |
+
|
| 54 |
+
attn_mask = _get_mask(to_dtype=attn_dtype, dim0=seq_len, dim1=seq_len)
|
| 55 |
+
key_padding_mask = _get_mask(
|
| 56 |
+
to_dtype=key_padding_dtype, dim0=batch_size, dim1=seq_len
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
q = torch.rand(seq_len, batch_size, embedding, requires_grad=True)
|
| 60 |
+
k = torch.rand(seq_len, batch_size, embedding, requires_grad=True)
|
| 61 |
+
v = torch.rand(seq_len, batch_size, embedding, requires_grad=True)
|
| 62 |
+
|
| 63 |
+
_reset_seeds()
|
| 64 |
+
|
| 65 |
+
original_mha = MultiheadAttention(
|
| 66 |
+
embedding,
|
| 67 |
+
num_heads,
|
| 68 |
+
dropout=0.0,
|
| 69 |
+
xformers_att_config=None,
|
| 70 |
+
add_bias_kv=add_bias_kv,
|
| 71 |
+
add_zero_attn=add_zero_attn,
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
xformers_mha = MultiheadAttention(
|
| 75 |
+
embedding,
|
| 76 |
+
num_heads,
|
| 77 |
+
dropout=0.0,
|
| 78 |
+
xformers_att_config=xformers_att_config,
|
| 79 |
+
add_bias_kv=add_bias_kv,
|
| 80 |
+
add_zero_attn=add_zero_attn,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
def original_bench_fw(q, k, v, key_padding_mask, attn_mask, static_kv):
|
| 84 |
+
original_mha(
|
| 85 |
+
query=q,
|
| 86 |
+
key=k,
|
| 87 |
+
value=v,
|
| 88 |
+
key_padding_mask=key_padding_mask,
|
| 89 |
+
attn_mask=attn_mask,
|
| 90 |
+
static_kv=static_kv,
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
def xformers_bench_fw(q, k, v, key_padding_mask, attn_mask, static_kv):
|
| 94 |
+
xformers_mha(
|
| 95 |
+
query=q,
|
| 96 |
+
key=k,
|
| 97 |
+
value=v,
|
| 98 |
+
key_padding_mask=key_padding_mask,
|
| 99 |
+
attn_mask=attn_mask,
|
| 100 |
+
static_kv=static_kv,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
def original_bench_fw_bw(q, k, v, key_padding_mask, attn_mask, static_kv):
|
| 104 |
+
output, _ = original_mha(
|
| 105 |
+
query=q,
|
| 106 |
+
key=k,
|
| 107 |
+
value=v,
|
| 108 |
+
key_padding_mask=key_padding_mask,
|
| 109 |
+
attn_mask=attn_mask,
|
| 110 |
+
static_kv=static_kv,
|
| 111 |
+
)
|
| 112 |
+
loss = torch.norm(output)
|
| 113 |
+
loss.backward()
|
| 114 |
+
|
| 115 |
+
def xformers_bench_fw_bw(q, k, v, key_padding_mask, attn_mask, static_kv):
|
| 116 |
+
output, _ = xformers_mha(
|
| 117 |
+
query=q,
|
| 118 |
+
key=k,
|
| 119 |
+
value=v,
|
| 120 |
+
key_padding_mask=key_padding_mask,
|
| 121 |
+
attn_mask=attn_mask,
|
| 122 |
+
static_kv=static_kv,
|
| 123 |
+
)
|
| 124 |
+
loss = torch.norm(output)
|
| 125 |
+
loss.backward()
|
| 126 |
+
|
| 127 |
+
fns = [
|
| 128 |
+
original_bench_fw,
|
| 129 |
+
xformers_bench_fw,
|
| 130 |
+
original_bench_fw_bw,
|
| 131 |
+
xformers_bench_fw_bw,
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
for fn in fns:
|
| 135 |
+
results.append(
|
| 136 |
+
benchmark.Timer(
|
| 137 |
+
stmt="fn(q, k, v, key_padding_mask, attn_mask, static_kv)",
|
| 138 |
+
globals={
|
| 139 |
+
"q": q,
|
| 140 |
+
"k": k,
|
| 141 |
+
"v": v,
|
| 142 |
+
"key_padding_mask": key_padding_mask,
|
| 143 |
+
"attn_mask": attn_mask,
|
| 144 |
+
"static_kv": static_kv,
|
| 145 |
+
"fn": fn,
|
| 146 |
+
},
|
| 147 |
+
label="multihead fw + bw",
|
| 148 |
+
sub_label=f"{fn.__name__}",
|
| 149 |
+
description=label,
|
| 150 |
+
).blocked_autorange(min_run_time=1)
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
compare = benchmark.Compare(results)
|
| 154 |
+
compare.print()
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def run_benchmarks():
|
| 158 |
+
for attn_dtype, key_padding_dtype, add_bias_kv, add_zero_attn in itertools.product(
|
| 159 |
+
ATTN_MASK_DTYPE, KEY_PADDING_MASK_DTYPE, [True, False], [True, False]
|
| 160 |
+
):
|
| 161 |
+
label = f"attn_dtype {attn_dtype}, key_padding_dtype {key_padding_dtype}, \
|
| 162 |
+
add_bias_kv {add_bias_kv}, add_zero_attn {add_zero_attn}"
|
| 163 |
+
benchmark_multihead_attention(
|
| 164 |
+
label=label,
|
| 165 |
+
attn_dtype=attn_dtype,
|
| 166 |
+
key_padding_dtype=key_padding_dtype,
|
| 167 |
+
add_bias_kv=add_bias_kv,
|
| 168 |
+
add_zero_attn=add_zero_attn,
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
run_benchmarks()
|
omni_speech/infer/fairseq/fairseq/benchmark/dummy_dataset.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from fairseq.data import FairseqDataset
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class DummyDataset(FairseqDataset):
|
| 6 |
+
def __init__(self, batch, num_items, item_size):
|
| 7 |
+
super().__init__()
|
| 8 |
+
self.batch = batch
|
| 9 |
+
self.num_items = num_items
|
| 10 |
+
self.item_size = item_size
|
| 11 |
+
|
| 12 |
+
def __getitem__(self, index):
|
| 13 |
+
return index
|
| 14 |
+
|
| 15 |
+
def __len__(self):
|
| 16 |
+
return self.num_items
|
| 17 |
+
|
| 18 |
+
def collater(self, samples):
|
| 19 |
+
return self.batch
|
| 20 |
+
|
| 21 |
+
@property
|
| 22 |
+
def sizes(self):
|
| 23 |
+
return np.array([self.item_size] * self.num_items)
|
| 24 |
+
|
| 25 |
+
def num_tokens(self, index):
|
| 26 |
+
return self.item_size
|
| 27 |
+
|
| 28 |
+
def size(self, index):
|
| 29 |
+
return self.item_size
|
| 30 |
+
|
| 31 |
+
def ordered_indices(self):
|
| 32 |
+
return np.arange(self.num_items)
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def supports_prefetch(self):
|
| 36 |
+
return False
|
omni_speech/infer/fairseq/fairseq/benchmark/dummy_lm.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
from dataclasses import dataclass, field
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from .dummy_dataset import DummyDataset
|
| 12 |
+
from fairseq.data import Dictionary
|
| 13 |
+
from fairseq.dataclass import FairseqDataclass
|
| 14 |
+
from fairseq.tasks import FairseqTask, register_task
|
| 15 |
+
from omegaconf import II
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
logger = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class DummyLMConfig(FairseqDataclass):
|
| 23 |
+
dict_size: int = 49996
|
| 24 |
+
dataset_size: int = 100000
|
| 25 |
+
tokens_per_sample: int = field(
|
| 26 |
+
default=512, metadata={"help": "max sequence length"}
|
| 27 |
+
)
|
| 28 |
+
add_bos_token: bool = False
|
| 29 |
+
batch_size: Optional[int] = II("dataset.batch_size")
|
| 30 |
+
max_tokens: Optional[int] = II("dataset.max_tokens")
|
| 31 |
+
max_target_positions: int = II("task.tokens_per_sample")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@register_task("dummy_lm", dataclass=DummyLMConfig)
|
| 35 |
+
class DummyLMTask(FairseqTask):
|
| 36 |
+
def __init__(self, cfg: DummyLMConfig):
|
| 37 |
+
super().__init__(cfg)
|
| 38 |
+
|
| 39 |
+
# load dictionary
|
| 40 |
+
self.dictionary = Dictionary()
|
| 41 |
+
for i in range(cfg.dict_size):
|
| 42 |
+
self.dictionary.add_symbol("word{}".format(i))
|
| 43 |
+
self.dictionary.pad_to_multiple_(8) # often faster if divisible by 8
|
| 44 |
+
logger.info("dictionary: {} types".format(len(self.dictionary)))
|
| 45 |
+
|
| 46 |
+
seq = torch.arange(cfg.tokens_per_sample + 1) + self.dictionary.pad() + 1
|
| 47 |
+
|
| 48 |
+
self.dummy_src = seq[:-1]
|
| 49 |
+
self.dummy_tgt = seq[1:]
|
| 50 |
+
|
| 51 |
+
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
|
| 52 |
+
"""Load a given dataset split.
|
| 53 |
+
Args:
|
| 54 |
+
split (str): name of the split (e.g., train, valid, test)
|
| 55 |
+
"""
|
| 56 |
+
if self.cfg.batch_size is not None:
|
| 57 |
+
bsz = self.cfg.batch_size
|
| 58 |
+
else:
|
| 59 |
+
bsz = max(1, self.cfg.max_tokens // self.cfg.tokens_per_sample)
|
| 60 |
+
self.datasets[split] = DummyDataset(
|
| 61 |
+
{
|
| 62 |
+
"id": 1,
|
| 63 |
+
"net_input": {
|
| 64 |
+
"src_tokens": torch.stack([self.dummy_src for _ in range(bsz)]),
|
| 65 |
+
"src_lengths": torch.full(
|
| 66 |
+
(bsz,), self.cfg.tokens_per_sample, dtype=torch.long
|
| 67 |
+
),
|
| 68 |
+
},
|
| 69 |
+
"target": torch.stack([self.dummy_tgt for _ in range(bsz)]),
|
| 70 |
+
"nsentences": bsz,
|
| 71 |
+
"ntokens": bsz * self.cfg.tokens_per_sample,
|
| 72 |
+
},
|
| 73 |
+
num_items=self.cfg.dataset_size,
|
| 74 |
+
item_size=self.cfg.tokens_per_sample,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
@property
|
| 78 |
+
def source_dictionary(self):
|
| 79 |
+
return self.dictionary
|
| 80 |
+
|
| 81 |
+
@property
|
| 82 |
+
def target_dictionary(self):
|
| 83 |
+
return self.dictionary
|
omni_speech/infer/fairseq/fairseq/benchmark/dummy_masked_lm.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
from dataclasses import dataclass, field
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from omegaconf import II
|
| 12 |
+
|
| 13 |
+
from .dummy_dataset import DummyDataset
|
| 14 |
+
from fairseq.data import Dictionary
|
| 15 |
+
from fairseq.dataclass import FairseqDataclass
|
| 16 |
+
from fairseq.tasks import FairseqTask, register_task
|
| 17 |
+
|
| 18 |
+
logger = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class DummyMaskedLMConfig(FairseqDataclass):
|
| 23 |
+
dict_size: int = 49996
|
| 24 |
+
dataset_size: int = 100000
|
| 25 |
+
tokens_per_sample: int = field(
|
| 26 |
+
default=512,
|
| 27 |
+
metadata={
|
| 28 |
+
"help": "max number of total tokens over all"
|
| 29 |
+
" segments per sample for BERT dataset"
|
| 30 |
+
},
|
| 31 |
+
)
|
| 32 |
+
batch_size: Optional[int] = II("dataset.batch_size")
|
| 33 |
+
max_tokens: Optional[int] = II("dataset.max_tokens")
|
| 34 |
+
max_target_positions: int = II("task.tokens_per_sample")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@register_task("dummy_masked_lm", dataclass=DummyMaskedLMConfig)
|
| 38 |
+
class DummyMaskedLMTask(FairseqTask):
|
| 39 |
+
def __init__(self, cfg: DummyMaskedLMConfig):
|
| 40 |
+
super().__init__(cfg)
|
| 41 |
+
|
| 42 |
+
self.dictionary = Dictionary()
|
| 43 |
+
for i in range(cfg.dict_size):
|
| 44 |
+
self.dictionary.add_symbol("word{}".format(i))
|
| 45 |
+
logger.info("dictionary: {} types".format(len(self.dictionary)))
|
| 46 |
+
# add mask token
|
| 47 |
+
self.mask_idx = self.dictionary.add_symbol("<mask>")
|
| 48 |
+
self.dictionary.pad_to_multiple_(8) # often faster if divisible by 8
|
| 49 |
+
|
| 50 |
+
mask_idx = 0
|
| 51 |
+
pad_idx = 1
|
| 52 |
+
seq = torch.arange(cfg.tokens_per_sample) + pad_idx + 1
|
| 53 |
+
mask = torch.arange(2, cfg.tokens_per_sample, 7) # ~15%
|
| 54 |
+
src = seq.clone()
|
| 55 |
+
src[mask] = mask_idx
|
| 56 |
+
tgt = torch.full_like(seq, pad_idx)
|
| 57 |
+
tgt[mask] = seq[mask]
|
| 58 |
+
|
| 59 |
+
self.dummy_src = src
|
| 60 |
+
self.dummy_tgt = tgt
|
| 61 |
+
|
| 62 |
+
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
|
| 63 |
+
"""Load a given dataset split.
|
| 64 |
+
Args:
|
| 65 |
+
split (str): name of the split (e.g., train, valid, test)
|
| 66 |
+
"""
|
| 67 |
+
if self.cfg.batch_size is not None:
|
| 68 |
+
bsz = self.cfg.batch_size
|
| 69 |
+
else:
|
| 70 |
+
bsz = max(1, self.cfg.max_tokens // self.cfg.tokens_per_sample)
|
| 71 |
+
self.datasets[split] = DummyDataset(
|
| 72 |
+
{
|
| 73 |
+
"id": 1,
|
| 74 |
+
"net_input": {
|
| 75 |
+
"src_tokens": torch.stack([self.dummy_src for _ in range(bsz)]),
|
| 76 |
+
"src_lengths": torch.full(
|
| 77 |
+
(bsz,), self.cfg.tokens_per_sample, dtype=torch.long
|
| 78 |
+
),
|
| 79 |
+
},
|
| 80 |
+
"target": torch.stack([self.dummy_tgt for _ in range(bsz)]),
|
| 81 |
+
"nsentences": bsz,
|
| 82 |
+
"ntokens": bsz * self.cfg.tokens_per_sample,
|
| 83 |
+
},
|
| 84 |
+
num_items=self.cfg.dataset_size,
|
| 85 |
+
item_size=self.cfg.tokens_per_sample,
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
@property
|
| 89 |
+
def source_dictionary(self):
|
| 90 |
+
return self.dictionary
|
| 91 |
+
|
| 92 |
+
@property
|
| 93 |
+
def target_dictionary(self):
|
| 94 |
+
return self.dictionary
|
omni_speech/infer/fairseq/fairseq/benchmark/dummy_model.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
import torch.nn.functional as F
|
| 8 |
+
from fairseq.data import Dictionary
|
| 9 |
+
from fairseq.models import (
|
| 10 |
+
FairseqDecoder,
|
| 11 |
+
FairseqLanguageModel,
|
| 12 |
+
register_model,
|
| 13 |
+
register_model_architecture,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@register_model("dummy_model")
|
| 18 |
+
class DummyModel(FairseqLanguageModel):
|
| 19 |
+
def __init__(self, args, encoder):
|
| 20 |
+
super().__init__(encoder)
|
| 21 |
+
self.args = args
|
| 22 |
+
|
| 23 |
+
@staticmethod
|
| 24 |
+
def add_args(parser):
|
| 25 |
+
parser.add_argument("--num-layers", type=int, default=24)
|
| 26 |
+
parser.add_argument("--embed-dim", type=int, default=1024)
|
| 27 |
+
|
| 28 |
+
@classmethod
|
| 29 |
+
def build_model(cls, args, task):
|
| 30 |
+
encoder = DummyEncoder(
|
| 31 |
+
num_embed=len(task.target_dictionary),
|
| 32 |
+
embed_dim=args.embed_dim,
|
| 33 |
+
num_layers=args.num_layers,
|
| 34 |
+
)
|
| 35 |
+
return cls(args, encoder)
|
| 36 |
+
|
| 37 |
+
def forward(self, src_tokens, masked_tokens=None, **kwargs):
|
| 38 |
+
return self.decoder(src_tokens, masked_tokens=masked_tokens)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class DummyEncoder(FairseqDecoder):
|
| 42 |
+
def __init__(self, num_embed=50000, embed_dim=1024, num_layers=24):
|
| 43 |
+
super().__init__(Dictionary())
|
| 44 |
+
self.embed = nn.Embedding(
|
| 45 |
+
num_embeddings=num_embed, embedding_dim=embed_dim, padding_idx=0
|
| 46 |
+
)
|
| 47 |
+
self.layers_a = nn.ModuleList(
|
| 48 |
+
[
|
| 49 |
+
nn.Sequential(
|
| 50 |
+
nn.LayerNorm(embed_dim),
|
| 51 |
+
nn.Linear(embed_dim, 3 * embed_dim), # q, k, v input projection
|
| 52 |
+
nn.Linear(3 * embed_dim, embed_dim), # skip self-attention
|
| 53 |
+
nn.Linear(embed_dim, embed_dim), # output projection
|
| 54 |
+
nn.Dropout(),
|
| 55 |
+
)
|
| 56 |
+
for i in range(num_layers)
|
| 57 |
+
]
|
| 58 |
+
)
|
| 59 |
+
self.layers_b = nn.ModuleList(
|
| 60 |
+
[
|
| 61 |
+
nn.Sequential(
|
| 62 |
+
nn.LayerNorm(embed_dim),
|
| 63 |
+
nn.Linear(embed_dim, 4 * embed_dim), # FFN
|
| 64 |
+
nn.ReLU(),
|
| 65 |
+
nn.Linear(4 * embed_dim, embed_dim), # FFN
|
| 66 |
+
nn.Dropout(0.1),
|
| 67 |
+
)
|
| 68 |
+
for i in range(num_layers)
|
| 69 |
+
]
|
| 70 |
+
)
|
| 71 |
+
self.out_proj = nn.Linear(embed_dim, num_embed)
|
| 72 |
+
|
| 73 |
+
def forward(self, tokens, masked_tokens=None):
|
| 74 |
+
x = self.embed(tokens)
|
| 75 |
+
for layer_a, layer_b in zip(self.layers_a, self.layers_b):
|
| 76 |
+
x = x + layer_a(x)
|
| 77 |
+
x = x + layer_b(x)
|
| 78 |
+
x = self.out_proj(x)
|
| 79 |
+
if masked_tokens is not None:
|
| 80 |
+
x = x[masked_tokens]
|
| 81 |
+
return (x,)
|
| 82 |
+
|
| 83 |
+
def max_positions(self):
|
| 84 |
+
return 1024
|
| 85 |
+
|
| 86 |
+
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
| 87 |
+
logits = net_output[0].float()
|
| 88 |
+
if log_probs:
|
| 89 |
+
return F.log_softmax(logits, dim=-1)
|
| 90 |
+
else:
|
| 91 |
+
return F.softmax(logits, dim=-1)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
@register_model_architecture("dummy_model", "dummy_model")
|
| 95 |
+
def base_architecture(args):
|
| 96 |
+
pass
|
omni_speech/infer/fairseq/fairseq/benchmark/dummy_mt.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
import torch
|
| 10 |
+
|
| 11 |
+
from fairseq.data import Dictionary, FairseqDataset
|
| 12 |
+
from fairseq.tasks import LegacyFairseqTask, register_task
|
| 13 |
+
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@register_task("dummy_mt")
|
| 18 |
+
class DummyMTTask(LegacyFairseqTask):
|
| 19 |
+
@staticmethod
|
| 20 |
+
def add_args(parser):
|
| 21 |
+
"""Add task-specific arguments to the parser."""
|
| 22 |
+
parser.add_argument("--dict-size", default=49996, type=int)
|
| 23 |
+
parser.add_argument("--dataset-size", default=100000, type=int)
|
| 24 |
+
parser.add_argument("--src-len", default=30, type=int)
|
| 25 |
+
parser.add_argument("--tgt-len", default=30, type=int)
|
| 26 |
+
|
| 27 |
+
def __init__(self, args, dictionary):
|
| 28 |
+
super().__init__(args)
|
| 29 |
+
self.dictionary = dictionary
|
| 30 |
+
self.seed = args.seed
|
| 31 |
+
|
| 32 |
+
dictionary.pad_to_multiple_(8) # often faster if divisible by 8
|
| 33 |
+
|
| 34 |
+
self.dummy_src = torch.arange(args.src_len + 1) + dictionary.pad() + 1
|
| 35 |
+
self.dummy_tgt = torch.arange(args.tgt_len + 1) + dictionary.pad() + 1
|
| 36 |
+
|
| 37 |
+
@classmethod
|
| 38 |
+
def setup_task(cls, args, **kwargs):
|
| 39 |
+
"""Setup the task."""
|
| 40 |
+
dictionary = Dictionary()
|
| 41 |
+
for i in range(args.dict_size):
|
| 42 |
+
dictionary.add_symbol("word{}".format(i))
|
| 43 |
+
logger.info("dictionary: {} types".format(len(dictionary)))
|
| 44 |
+
|
| 45 |
+
args.max_source_positions = args.src_len + dictionary.pad() + 2
|
| 46 |
+
args.max_target_positions = args.tgt_len + dictionary.pad() + 2
|
| 47 |
+
|
| 48 |
+
return cls(args, dictionary)
|
| 49 |
+
|
| 50 |
+
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
|
| 51 |
+
"""Load a given dataset split.
|
| 52 |
+
Args:
|
| 53 |
+
split (str): name of the split (e.g., train, valid, test)
|
| 54 |
+
"""
|
| 55 |
+
item_size = max(self.args.src_len, self.args.tgt_len)
|
| 56 |
+
if self.args.batch_size is not None:
|
| 57 |
+
bsz = self.args.batch_size
|
| 58 |
+
else:
|
| 59 |
+
bsz = max(1, self.args.max_tokens // item_size)
|
| 60 |
+
tgt = torch.stack([self.dummy_tgt for _ in range(bsz)])
|
| 61 |
+
self.datasets[split] = DummyDataset(
|
| 62 |
+
{
|
| 63 |
+
"id": 1,
|
| 64 |
+
"net_input": {
|
| 65 |
+
"src_tokens": torch.stack([self.dummy_src for _ in range(bsz)]),
|
| 66 |
+
"src_lengths": torch.full(
|
| 67 |
+
(bsz,), self.args.src_len, dtype=torch.long
|
| 68 |
+
),
|
| 69 |
+
"prev_output_tokens": tgt.clone(),
|
| 70 |
+
},
|
| 71 |
+
"target": tgt,
|
| 72 |
+
"nsentences": bsz,
|
| 73 |
+
"ntokens": bsz * self.args.tgt_len,
|
| 74 |
+
},
|
| 75 |
+
num_items=self.args.dataset_size,
|
| 76 |
+
item_size=item_size,
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
@property
|
| 80 |
+
def source_dictionary(self):
|
| 81 |
+
return self.dictionary
|
| 82 |
+
|
| 83 |
+
@property
|
| 84 |
+
def target_dictionary(self):
|
| 85 |
+
return self.dictionary
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class DummyDataset(FairseqDataset):
|
| 89 |
+
def __init__(self, batch, num_items, item_size):
|
| 90 |
+
super().__init__()
|
| 91 |
+
self.batch = batch
|
| 92 |
+
self.num_items = num_items
|
| 93 |
+
self.item_size = item_size
|
| 94 |
+
|
| 95 |
+
def __getitem__(self, index):
|
| 96 |
+
return index
|
| 97 |
+
|
| 98 |
+
def __len__(self):
|
| 99 |
+
return self.num_items
|
| 100 |
+
|
| 101 |
+
def collater(self, samples):
|
| 102 |
+
return self.batch
|
| 103 |
+
|
| 104 |
+
@property
|
| 105 |
+
def sizes(self):
|
| 106 |
+
return np.array([self.item_size] * self.num_items)
|
| 107 |
+
|
| 108 |
+
def num_tokens(self, index):
|
| 109 |
+
return self.item_size
|
| 110 |
+
|
| 111 |
+
def size(self, index):
|
| 112 |
+
return self.item_size
|
| 113 |
+
|
| 114 |
+
def ordered_indices(self):
|
| 115 |
+
return np.arange(self.num_items)
|
| 116 |
+
|
| 117 |
+
@property
|
| 118 |
+
def supports_prefetch(self):
|
| 119 |
+
return False
|
omni_speech/infer/fairseq/fairseq/binarizer.py
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
import os
|
| 8 |
+
import typing as tp
|
| 9 |
+
from abc import ABC, abstractmethod
|
| 10 |
+
from collections import Counter
|
| 11 |
+
from dataclasses import dataclass
|
| 12 |
+
from multiprocessing import Pool
|
| 13 |
+
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
from fairseq.data import Dictionary, indexed_dataset
|
| 17 |
+
from fairseq.file_chunker_utils import Chunker, find_offsets
|
| 18 |
+
from fairseq.file_io import PathManager
|
| 19 |
+
from fairseq.tokenizer import tokenize_line
|
| 20 |
+
|
| 21 |
+
logger = logging.getLogger("binarizer")
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@dataclass
|
| 25 |
+
class BinarizeSummary:
|
| 26 |
+
"""
|
| 27 |
+
Keep track of what's going on in the binarizer
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
num_seq: int = 0
|
| 31 |
+
replaced: tp.Optional[Counter] = None
|
| 32 |
+
num_tok: int = 0
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def num_replaced(self) -> int:
|
| 36 |
+
if self.replaced is None:
|
| 37 |
+
return 0
|
| 38 |
+
return sum(self.replaced.values())
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def replaced_percent(self) -> float:
|
| 42 |
+
return 100 * self.num_replaced / self.num_tok
|
| 43 |
+
|
| 44 |
+
def __str__(self) -> str:
|
| 45 |
+
base = f"{self.num_seq} sents, {self.num_tok} tokens"
|
| 46 |
+
if self.replaced is None:
|
| 47 |
+
return base
|
| 48 |
+
|
| 49 |
+
return f"{base}, {self.replaced_percent:.3}% replaced"
|
| 50 |
+
|
| 51 |
+
def merge(self, other: "BinarizeSummary"):
|
| 52 |
+
replaced = None
|
| 53 |
+
if self.replaced is not None:
|
| 54 |
+
replaced = self.replaced
|
| 55 |
+
if other.replaced is not None:
|
| 56 |
+
if replaced is None:
|
| 57 |
+
replaced = other.replaced
|
| 58 |
+
else:
|
| 59 |
+
replaced += other.replaced
|
| 60 |
+
self.replaced = replaced
|
| 61 |
+
self.num_seq += other.num_seq
|
| 62 |
+
self.num_tok += other.num_tok
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class Binarizer(ABC):
|
| 66 |
+
"""
|
| 67 |
+
a binarizer describes how to take a string and build a tensor out of it
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
@abstractmethod
|
| 71 |
+
def binarize_line(
|
| 72 |
+
self,
|
| 73 |
+
line: str,
|
| 74 |
+
summary: BinarizeSummary,
|
| 75 |
+
) -> torch.IntTensor:
|
| 76 |
+
...
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _worker_prefix(output_prefix: str, worker_id: int):
|
| 80 |
+
return f"{output_prefix}.pt{worker_id}"
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class FileBinarizer:
|
| 84 |
+
"""
|
| 85 |
+
An file binarizer can take a file, tokenize it, and binarize each line to a tensor
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
@classmethod
|
| 89 |
+
def multiprocess_dataset(
|
| 90 |
+
cls,
|
| 91 |
+
input_file: str,
|
| 92 |
+
dataset_impl: str,
|
| 93 |
+
binarizer: Binarizer,
|
| 94 |
+
output_prefix: str,
|
| 95 |
+
vocab_size=None,
|
| 96 |
+
num_workers=1,
|
| 97 |
+
) -> BinarizeSummary:
|
| 98 |
+
final_summary = BinarizeSummary()
|
| 99 |
+
|
| 100 |
+
offsets = find_offsets(input_file, num_workers)
|
| 101 |
+
# find_offsets returns a list of position [pos1, pos2, pos3, pos4] but we would want pairs:
|
| 102 |
+
# [(pos1, pos2), (pos2, pos3), (pos3, pos4)] to process the chunks with start/end info
|
| 103 |
+
# we zip the list with itself shifted by one to get all the pairs.
|
| 104 |
+
(first_chunk, *more_chunks) = zip(offsets, offsets[1:])
|
| 105 |
+
pool = None
|
| 106 |
+
if num_workers > 1:
|
| 107 |
+
pool = Pool(processes=num_workers - 1)
|
| 108 |
+
worker_results = [
|
| 109 |
+
pool.apply_async(
|
| 110 |
+
cls._binarize_chunk_and_finalize,
|
| 111 |
+
args=(
|
| 112 |
+
binarizer,
|
| 113 |
+
input_file,
|
| 114 |
+
start_offset,
|
| 115 |
+
end_offset,
|
| 116 |
+
_worker_prefix(
|
| 117 |
+
output_prefix,
|
| 118 |
+
worker_id,
|
| 119 |
+
),
|
| 120 |
+
dataset_impl,
|
| 121 |
+
),
|
| 122 |
+
kwds={
|
| 123 |
+
"vocab_size": vocab_size,
|
| 124 |
+
}
|
| 125 |
+
if vocab_size is not None
|
| 126 |
+
else {},
|
| 127 |
+
)
|
| 128 |
+
for worker_id, (start_offset, end_offset) in enumerate(
|
| 129 |
+
more_chunks, start=1
|
| 130 |
+
)
|
| 131 |
+
]
|
| 132 |
+
|
| 133 |
+
pool.close()
|
| 134 |
+
pool.join()
|
| 135 |
+
for r in worker_results:
|
| 136 |
+
summ = r.get()
|
| 137 |
+
final_summary.merge(summ)
|
| 138 |
+
|
| 139 |
+
# do not close the bin file as we need to merge the worker results in
|
| 140 |
+
final_ds, summ = cls._binarize_file_chunk(
|
| 141 |
+
binarizer,
|
| 142 |
+
input_file,
|
| 143 |
+
offset_start=first_chunk[0],
|
| 144 |
+
offset_end=first_chunk[1],
|
| 145 |
+
output_prefix=output_prefix,
|
| 146 |
+
dataset_impl=dataset_impl,
|
| 147 |
+
vocab_size=vocab_size if vocab_size is not None else None,
|
| 148 |
+
)
|
| 149 |
+
final_summary.merge(summ)
|
| 150 |
+
|
| 151 |
+
if num_workers > 1:
|
| 152 |
+
for worker_id in range(1, num_workers):
|
| 153 |
+
# merge the worker outputs
|
| 154 |
+
worker_output_prefix = _worker_prefix(
|
| 155 |
+
output_prefix,
|
| 156 |
+
worker_id,
|
| 157 |
+
)
|
| 158 |
+
final_ds.merge_file_(worker_output_prefix)
|
| 159 |
+
try:
|
| 160 |
+
os.remove(indexed_dataset.data_file_path(worker_output_prefix))
|
| 161 |
+
os.remove(indexed_dataset.index_file_path(worker_output_prefix))
|
| 162 |
+
except Exception as e:
|
| 163 |
+
logger.error(
|
| 164 |
+
f"couldn't remove {worker_output_prefix}.*", exc_info=e
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
# now we can close the file
|
| 168 |
+
idx_file = indexed_dataset.index_file_path(output_prefix)
|
| 169 |
+
final_ds.finalize(idx_file)
|
| 170 |
+
return final_summary
|
| 171 |
+
|
| 172 |
+
@staticmethod
|
| 173 |
+
def _binarize_file_chunk(
|
| 174 |
+
binarizer: Binarizer,
|
| 175 |
+
filename: str,
|
| 176 |
+
offset_start: int,
|
| 177 |
+
offset_end: int,
|
| 178 |
+
output_prefix: str,
|
| 179 |
+
dataset_impl: str,
|
| 180 |
+
vocab_size=None,
|
| 181 |
+
) -> tp.Tuple[tp.Any, BinarizeSummary]: # (dataset builder, BinarizeSummary)
|
| 182 |
+
"""
|
| 183 |
+
creates a dataset builder and append binarized items to it. This function does not
|
| 184 |
+
finalize the builder, this is useful if you want to do other things with your bin file
|
| 185 |
+
like appending/merging other files
|
| 186 |
+
"""
|
| 187 |
+
bin_file = indexed_dataset.data_file_path(output_prefix)
|
| 188 |
+
ds = indexed_dataset.make_builder(
|
| 189 |
+
bin_file,
|
| 190 |
+
impl=dataset_impl,
|
| 191 |
+
vocab_size=vocab_size,
|
| 192 |
+
)
|
| 193 |
+
summary = BinarizeSummary()
|
| 194 |
+
|
| 195 |
+
with Chunker(
|
| 196 |
+
PathManager.get_local_path(filename), offset_start, offset_end
|
| 197 |
+
) as line_iterator:
|
| 198 |
+
for line in line_iterator:
|
| 199 |
+
ds.add_item(binarizer.binarize_line(line, summary))
|
| 200 |
+
|
| 201 |
+
return ds, summary
|
| 202 |
+
|
| 203 |
+
@classmethod
|
| 204 |
+
def _binarize_chunk_and_finalize(
|
| 205 |
+
cls,
|
| 206 |
+
binarizer: Binarizer,
|
| 207 |
+
filename: str,
|
| 208 |
+
offset_start: int,
|
| 209 |
+
offset_end: int,
|
| 210 |
+
output_prefix: str,
|
| 211 |
+
dataset_impl: str,
|
| 212 |
+
vocab_size=None,
|
| 213 |
+
):
|
| 214 |
+
"""
|
| 215 |
+
same as above, but also finalizes the builder
|
| 216 |
+
"""
|
| 217 |
+
ds, summ = cls._binarize_file_chunk(
|
| 218 |
+
binarizer,
|
| 219 |
+
filename,
|
| 220 |
+
offset_start,
|
| 221 |
+
offset_end,
|
| 222 |
+
output_prefix,
|
| 223 |
+
dataset_impl,
|
| 224 |
+
vocab_size=vocab_size,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
idx_file = indexed_dataset.index_file_path(output_prefix)
|
| 228 |
+
ds.finalize(idx_file)
|
| 229 |
+
|
| 230 |
+
return summ
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
class VocabularyDatasetBinarizer(Binarizer):
|
| 234 |
+
"""
|
| 235 |
+
Takes a Dictionary/Vocabulary, assign ids to each
|
| 236 |
+
token using the dictionary encode_line function.
|
| 237 |
+
"""
|
| 238 |
+
|
| 239 |
+
def __init__(
|
| 240 |
+
self,
|
| 241 |
+
dict: Dictionary,
|
| 242 |
+
tokenize: tp.Callable[[str], tp.List[str]] = tokenize_line,
|
| 243 |
+
append_eos: bool = True,
|
| 244 |
+
reverse_order: bool = False,
|
| 245 |
+
already_numberized: bool = False,
|
| 246 |
+
) -> None:
|
| 247 |
+
self.dict = dict
|
| 248 |
+
self.tokenize = tokenize
|
| 249 |
+
self.append_eos = append_eos
|
| 250 |
+
self.reverse_order = reverse_order
|
| 251 |
+
self.already_numberized = already_numberized
|
| 252 |
+
super().__init__()
|
| 253 |
+
|
| 254 |
+
def binarize_line(
|
| 255 |
+
self,
|
| 256 |
+
line: str,
|
| 257 |
+
summary: BinarizeSummary,
|
| 258 |
+
):
|
| 259 |
+
if summary.replaced is None:
|
| 260 |
+
summary.replaced = Counter()
|
| 261 |
+
|
| 262 |
+
def replaced_consumer(word, idx):
|
| 263 |
+
if idx == self.dict.unk_index and word != self.dict.unk_word:
|
| 264 |
+
summary.replaced.update([word])
|
| 265 |
+
|
| 266 |
+
if self.already_numberized:
|
| 267 |
+
id_strings = line.strip().split()
|
| 268 |
+
id_list = [int(id_string) for id_string in id_strings]
|
| 269 |
+
if self.reverse_order:
|
| 270 |
+
id_list.reverse()
|
| 271 |
+
if self.append_eos:
|
| 272 |
+
id_list.append(self.dict.eos())
|
| 273 |
+
ids = torch.IntTensor(id_list)
|
| 274 |
+
else:
|
| 275 |
+
ids = self.dict.encode_line(
|
| 276 |
+
line=line,
|
| 277 |
+
line_tokenizer=self.tokenize,
|
| 278 |
+
add_if_not_exist=False,
|
| 279 |
+
consumer=replaced_consumer,
|
| 280 |
+
append_eos=self.append_eos,
|
| 281 |
+
reverse_order=self.reverse_order,
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
summary.num_seq += 1
|
| 285 |
+
summary.num_tok += len(ids)
|
| 286 |
+
return ids
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
class AlignmentDatasetBinarizer(Binarizer):
|
| 290 |
+
"""
|
| 291 |
+
binarize by parsing a set of alignments and packing
|
| 292 |
+
them in a tensor (see utils.parse_alignment)
|
| 293 |
+
"""
|
| 294 |
+
|
| 295 |
+
def __init__(
|
| 296 |
+
self,
|
| 297 |
+
alignment_parser: tp.Callable[[str], torch.IntTensor],
|
| 298 |
+
) -> None:
|
| 299 |
+
super().__init__()
|
| 300 |
+
self.alignment_parser = alignment_parser
|
| 301 |
+
|
| 302 |
+
def binarize_line(
|
| 303 |
+
self,
|
| 304 |
+
line: str,
|
| 305 |
+
summary: BinarizeSummary,
|
| 306 |
+
):
|
| 307 |
+
ids = self.alignment_parser(line)
|
| 308 |
+
summary.num_seq += 1
|
| 309 |
+
summary.num_tok += len(ids)
|
| 310 |
+
return ids
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
class LegacyBinarizer:
|
| 314 |
+
@classmethod
|
| 315 |
+
def binarize(
|
| 316 |
+
cls,
|
| 317 |
+
filename: str,
|
| 318 |
+
dico: Dictionary,
|
| 319 |
+
consumer: tp.Callable[[torch.IntTensor], None],
|
| 320 |
+
tokenize: tp.Callable[[str], tp.List[str]] = tokenize_line,
|
| 321 |
+
append_eos: bool = True,
|
| 322 |
+
reverse_order: bool = False,
|
| 323 |
+
offset: int = 0,
|
| 324 |
+
end: int = -1,
|
| 325 |
+
already_numberized: bool = False,
|
| 326 |
+
) -> tp.Dict[str, int]:
|
| 327 |
+
binarizer = VocabularyDatasetBinarizer(
|
| 328 |
+
dict=dico,
|
| 329 |
+
tokenize=tokenize,
|
| 330 |
+
append_eos=append_eos,
|
| 331 |
+
reverse_order=reverse_order,
|
| 332 |
+
already_numberized=already_numberized,
|
| 333 |
+
)
|
| 334 |
+
return cls._consume_file(
|
| 335 |
+
filename,
|
| 336 |
+
binarizer,
|
| 337 |
+
consumer,
|
| 338 |
+
offset_start=offset,
|
| 339 |
+
offset_end=end,
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
@classmethod
|
| 343 |
+
def binarize_alignments(
|
| 344 |
+
cls,
|
| 345 |
+
filename: str,
|
| 346 |
+
alignment_parser: tp.Callable[[str], torch.IntTensor],
|
| 347 |
+
consumer: tp.Callable[[torch.IntTensor], None],
|
| 348 |
+
offset: int = 0,
|
| 349 |
+
end: int = -1,
|
| 350 |
+
) -> tp.Dict[str, int]:
|
| 351 |
+
binarizer = AlignmentDatasetBinarizer(alignment_parser)
|
| 352 |
+
return cls._consume_file(
|
| 353 |
+
filename,
|
| 354 |
+
binarizer,
|
| 355 |
+
consumer,
|
| 356 |
+
offset_start=offset,
|
| 357 |
+
offset_end=end,
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
@staticmethod
|
| 361 |
+
def _consume_file(
|
| 362 |
+
filename: str,
|
| 363 |
+
binarizer: Binarizer,
|
| 364 |
+
consumer: tp.Callable[[torch.IntTensor], None],
|
| 365 |
+
offset_start: int,
|
| 366 |
+
offset_end: int,
|
| 367 |
+
) -> tp.Dict[str, int]:
|
| 368 |
+
summary = BinarizeSummary()
|
| 369 |
+
|
| 370 |
+
with Chunker(
|
| 371 |
+
PathManager.get_local_path(filename), offset_start, offset_end
|
| 372 |
+
) as line_iterator:
|
| 373 |
+
for line in line_iterator:
|
| 374 |
+
consumer(binarizer.binarize_line(line, summary))
|
| 375 |
+
|
| 376 |
+
return {
|
| 377 |
+
"nseq": summary.num_seq,
|
| 378 |
+
"nunk": summary.num_replaced,
|
| 379 |
+
"ntok": summary.num_tok,
|
| 380 |
+
"replaced": summary.replaced,
|
| 381 |
+
}
|
omni_speech/infer/fairseq/fairseq/checkpoint_utils.py
ADDED
|
@@ -0,0 +1,918 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import ast
|
| 7 |
+
import collections
|
| 8 |
+
import contextlib
|
| 9 |
+
import inspect
|
| 10 |
+
import logging
|
| 11 |
+
import os
|
| 12 |
+
import re
|
| 13 |
+
import time
|
| 14 |
+
import traceback
|
| 15 |
+
from collections import OrderedDict
|
| 16 |
+
from pathlib import Path
|
| 17 |
+
from typing import Any, Dict, Optional, Union
|
| 18 |
+
|
| 19 |
+
import numpy as np
|
| 20 |
+
import torch
|
| 21 |
+
from fairseq.data import data_utils
|
| 22 |
+
from fairseq.dataclass.configs import CheckpointConfig
|
| 23 |
+
from fairseq.dataclass.utils import (
|
| 24 |
+
convert_namespace_to_omegaconf,
|
| 25 |
+
overwrite_args_by_name,
|
| 26 |
+
)
|
| 27 |
+
from fairseq.distributed.fully_sharded_data_parallel import FSDP, has_FSDP
|
| 28 |
+
from fairseq.file_io import PathManager
|
| 29 |
+
from fairseq.models import FairseqDecoder, FairseqEncoder
|
| 30 |
+
from omegaconf import DictConfig, OmegaConf, open_dict
|
| 31 |
+
|
| 32 |
+
logger = logging.getLogger(__name__)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def save_checkpoint(cfg: CheckpointConfig, trainer, epoch_itr, val_loss):
|
| 36 |
+
from fairseq import meters
|
| 37 |
+
|
| 38 |
+
# only one worker should attempt to create the required dir
|
| 39 |
+
if trainer.data_parallel_rank == 0:
|
| 40 |
+
os.makedirs(cfg.save_dir, exist_ok=True)
|
| 41 |
+
|
| 42 |
+
prev_best = getattr(save_checkpoint, "best", val_loss)
|
| 43 |
+
if val_loss is not None:
|
| 44 |
+
best_function = max if cfg.maximize_best_checkpoint_metric else min
|
| 45 |
+
save_checkpoint.best = best_function(val_loss, prev_best)
|
| 46 |
+
|
| 47 |
+
if cfg.no_save:
|
| 48 |
+
return None
|
| 49 |
+
|
| 50 |
+
trainer.consolidate_optimizer() # TODO(SS): do we need this if no_save_optimizer_state
|
| 51 |
+
|
| 52 |
+
if not trainer.should_save_checkpoint_on_current_rank:
|
| 53 |
+
if trainer.always_call_state_dict_during_save_checkpoint:
|
| 54 |
+
trainer.state_dict()
|
| 55 |
+
return None
|
| 56 |
+
|
| 57 |
+
write_timer = meters.StopwatchMeter()
|
| 58 |
+
write_timer.start()
|
| 59 |
+
|
| 60 |
+
epoch = epoch_itr.epoch
|
| 61 |
+
end_of_epoch = epoch_itr.end_of_epoch()
|
| 62 |
+
updates = trainer.get_num_updates()
|
| 63 |
+
|
| 64 |
+
logger.info(f"Preparing to save checkpoint for epoch {epoch} @ {updates} updates")
|
| 65 |
+
|
| 66 |
+
def is_better(a, b):
|
| 67 |
+
return a >= b if cfg.maximize_best_checkpoint_metric else a <= b
|
| 68 |
+
|
| 69 |
+
suffix = trainer.checkpoint_suffix
|
| 70 |
+
checkpoint_conds = collections.OrderedDict()
|
| 71 |
+
checkpoint_conds["checkpoint{}{}.pt".format(epoch, suffix)] = (
|
| 72 |
+
end_of_epoch and not cfg.no_epoch_checkpoints and epoch % cfg.save_interval == 0
|
| 73 |
+
)
|
| 74 |
+
checkpoint_conds["checkpoint_{}_{}{}.pt".format(epoch, updates, suffix)] = (
|
| 75 |
+
not end_of_epoch
|
| 76 |
+
and cfg.save_interval_updates > 0
|
| 77 |
+
and updates % cfg.save_interval_updates == 0
|
| 78 |
+
)
|
| 79 |
+
checkpoint_conds["checkpoint_best{}.pt".format(suffix)] = val_loss is not None and (
|
| 80 |
+
not hasattr(save_checkpoint, "best")
|
| 81 |
+
or is_better(val_loss, save_checkpoint.best)
|
| 82 |
+
)
|
| 83 |
+
if val_loss is not None and cfg.keep_best_checkpoints > 0:
|
| 84 |
+
worst_best = getattr(save_checkpoint, "best", None)
|
| 85 |
+
chkpts = checkpoint_paths(
|
| 86 |
+
cfg.save_dir,
|
| 87 |
+
pattern=r"checkpoint\.best_{}_(\d+\.?\d*){}\.pt".format(
|
| 88 |
+
cfg.best_checkpoint_metric, suffix
|
| 89 |
+
),
|
| 90 |
+
)
|
| 91 |
+
if len(chkpts) > 0:
|
| 92 |
+
p = chkpts[-1] if cfg.maximize_best_checkpoint_metric else chkpts[0]
|
| 93 |
+
worst_best = float(p.rsplit("_")[-1].replace("{}.pt".format(suffix), ""))
|
| 94 |
+
# add random digits to resolve ties
|
| 95 |
+
with data_utils.numpy_seed(epoch, updates, val_loss):
|
| 96 |
+
rand_sfx = np.random.randint(0, cfg.keep_best_checkpoints)
|
| 97 |
+
|
| 98 |
+
checkpoint_conds[
|
| 99 |
+
"checkpoint.best_{}_{:.3f}{}{}.pt".format(
|
| 100 |
+
cfg.best_checkpoint_metric, val_loss, rand_sfx, suffix
|
| 101 |
+
)
|
| 102 |
+
] = worst_best is None or is_better(val_loss, worst_best)
|
| 103 |
+
checkpoint_conds[
|
| 104 |
+
"checkpoint_last{}.pt".format(suffix)
|
| 105 |
+
] = not cfg.no_last_checkpoints
|
| 106 |
+
|
| 107 |
+
extra_state = {"train_iterator": epoch_itr.state_dict(), "val_loss": val_loss}
|
| 108 |
+
if hasattr(save_checkpoint, "best"):
|
| 109 |
+
extra_state.update({"best": save_checkpoint.best})
|
| 110 |
+
|
| 111 |
+
checkpoints = [
|
| 112 |
+
os.path.join(cfg.save_dir, fn) for fn, cond in checkpoint_conds.items() if cond
|
| 113 |
+
]
|
| 114 |
+
saved_cp = None
|
| 115 |
+
if len(checkpoints) > 0 and trainer.should_save_checkpoint_on_current_rank:
|
| 116 |
+
saved_cp = trainer.save_checkpoint(checkpoints[0], extra_state)
|
| 117 |
+
for cp in checkpoints[1:]:
|
| 118 |
+
if cfg.write_checkpoints_asynchronously:
|
| 119 |
+
# TODO[ioPath]: Need to implement a delayed asynchronous
|
| 120 |
+
# file copying/moving feature.
|
| 121 |
+
logger.warning(
|
| 122 |
+
f"ioPath is not copying {checkpoints[0]} to {cp} "
|
| 123 |
+
"since async write mode is on."
|
| 124 |
+
)
|
| 125 |
+
else:
|
| 126 |
+
assert PathManager.copy(
|
| 127 |
+
checkpoints[0], cp, overwrite=True
|
| 128 |
+
), f"Failed to copy {checkpoints[0]} to {cp}"
|
| 129 |
+
|
| 130 |
+
write_timer.stop()
|
| 131 |
+
logger.info(
|
| 132 |
+
"Saved checkpoint {} (epoch {} @ {} updates, score {}) (writing took {} seconds)".format(
|
| 133 |
+
checkpoints[0], epoch, updates, val_loss, write_timer.sum
|
| 134 |
+
)
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
if (
|
| 138 |
+
not end_of_epoch
|
| 139 |
+
and cfg.keep_interval_updates > 0
|
| 140 |
+
and trainer.should_save_checkpoint_on_current_rank
|
| 141 |
+
):
|
| 142 |
+
# remove old checkpoints; checkpoints are sorted in descending order
|
| 143 |
+
if cfg.keep_interval_updates_pattern == -1:
|
| 144 |
+
checkpoints = checkpoint_paths(
|
| 145 |
+
cfg.save_dir, pattern=r"checkpoint_\d+_(\d+){}\.pt".format(suffix)
|
| 146 |
+
)
|
| 147 |
+
else:
|
| 148 |
+
checkpoints = checkpoint_paths(
|
| 149 |
+
cfg.save_dir,
|
| 150 |
+
pattern=r"checkpoint_\d+_(\d+){}\.pt".format(suffix),
|
| 151 |
+
keep_match=True,
|
| 152 |
+
)
|
| 153 |
+
checkpoints = [
|
| 154 |
+
x[0]
|
| 155 |
+
for x in checkpoints
|
| 156 |
+
if x[1] % cfg.keep_interval_updates_pattern != 0
|
| 157 |
+
]
|
| 158 |
+
|
| 159 |
+
for old_chk in checkpoints[cfg.keep_interval_updates :]:
|
| 160 |
+
if os.path.lexists(old_chk):
|
| 161 |
+
os.remove(old_chk)
|
| 162 |
+
elif PathManager.exists(old_chk):
|
| 163 |
+
PathManager.rm(old_chk)
|
| 164 |
+
|
| 165 |
+
if cfg.keep_last_epochs > 0 and trainer.should_save_checkpoint_on_current_rank:
|
| 166 |
+
# remove old epoch checkpoints; checkpoints are sorted in descending order
|
| 167 |
+
checkpoints = checkpoint_paths(
|
| 168 |
+
cfg.save_dir, pattern=r"checkpoint(\d+){}\.pt".format(suffix)
|
| 169 |
+
)
|
| 170 |
+
for old_chk in checkpoints[cfg.keep_last_epochs :]:
|
| 171 |
+
if os.path.lexists(old_chk):
|
| 172 |
+
os.remove(old_chk)
|
| 173 |
+
elif PathManager.exists(old_chk):
|
| 174 |
+
PathManager.rm(old_chk)
|
| 175 |
+
|
| 176 |
+
if cfg.keep_best_checkpoints > 0 and trainer.should_save_checkpoint_on_current_rank:
|
| 177 |
+
# only keep the best N checkpoints according to validation metric
|
| 178 |
+
checkpoints = checkpoint_paths(
|
| 179 |
+
cfg.save_dir,
|
| 180 |
+
pattern=r"checkpoint\.best_{}_(\d+\.?\d*){}\.pt".format(
|
| 181 |
+
cfg.best_checkpoint_metric, suffix
|
| 182 |
+
),
|
| 183 |
+
)
|
| 184 |
+
if not cfg.maximize_best_checkpoint_metric:
|
| 185 |
+
checkpoints = checkpoints[::-1]
|
| 186 |
+
for old_chk in checkpoints[cfg.keep_best_checkpoints :]:
|
| 187 |
+
if os.path.lexists(old_chk):
|
| 188 |
+
os.remove(old_chk)
|
| 189 |
+
elif PathManager.exists(old_chk):
|
| 190 |
+
PathManager.rm(old_chk)
|
| 191 |
+
|
| 192 |
+
return saved_cp
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def load_checkpoint(cfg: CheckpointConfig, trainer, **passthrough_args):
|
| 196 |
+
"""
|
| 197 |
+
Load a checkpoint and restore the training iterator.
|
| 198 |
+
|
| 199 |
+
*passthrough_args* will be passed through to
|
| 200 |
+
``trainer.get_train_iterator``.
|
| 201 |
+
"""
|
| 202 |
+
|
| 203 |
+
reset_optimizer = cfg.reset_optimizer
|
| 204 |
+
reset_lr_scheduler = cfg.reset_lr_scheduler
|
| 205 |
+
optimizer_overrides = ast.literal_eval(cfg.optimizer_overrides)
|
| 206 |
+
reset_meters = cfg.reset_meters
|
| 207 |
+
reset_dataloader = cfg.reset_dataloader
|
| 208 |
+
|
| 209 |
+
if cfg.finetune_from_model is not None and (
|
| 210 |
+
reset_optimizer or reset_lr_scheduler or reset_meters or reset_dataloader
|
| 211 |
+
):
|
| 212 |
+
raise ValueError(
|
| 213 |
+
"--finetune-from-model can not be set together with either --reset-optimizer"
|
| 214 |
+
" or reset_lr_scheduler or reset_meters or reset_dataloader"
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
suffix = trainer.checkpoint_suffix
|
| 218 |
+
if (
|
| 219 |
+
cfg.restore_file == "checkpoint_last.pt"
|
| 220 |
+
): # default value of restore_file is 'checkpoint_last.pt'
|
| 221 |
+
checkpoint_path = os.path.join(
|
| 222 |
+
cfg.save_dir, "checkpoint_last{}.pt".format(suffix)
|
| 223 |
+
)
|
| 224 |
+
first_launch = not PathManager.exists(checkpoint_path)
|
| 225 |
+
if first_launch and getattr(cfg, "continue_once", None) is not None:
|
| 226 |
+
checkpoint_path = cfg.continue_once
|
| 227 |
+
elif cfg.finetune_from_model is not None and first_launch:
|
| 228 |
+
# if there is no last checkpoint to restore, start the finetune from pretrained model
|
| 229 |
+
# else just use usual logic to load checkpoint, e.g. restart from last checkpoint and etc.
|
| 230 |
+
if PathManager.exists(cfg.finetune_from_model):
|
| 231 |
+
checkpoint_path = cfg.finetune_from_model
|
| 232 |
+
reset_optimizer = True
|
| 233 |
+
reset_lr_scheduler = True
|
| 234 |
+
reset_meters = True
|
| 235 |
+
reset_dataloader = True
|
| 236 |
+
logger.info(
|
| 237 |
+
f"loading pretrained model from {checkpoint_path}: "
|
| 238 |
+
"optimizer, lr scheduler, meters, dataloader will be reset"
|
| 239 |
+
)
|
| 240 |
+
else:
|
| 241 |
+
raise ValueError(
|
| 242 |
+
f"--finetune-from-model {cfg.finetune_from_model} does not exist"
|
| 243 |
+
)
|
| 244 |
+
elif suffix is not None:
|
| 245 |
+
checkpoint_path = cfg.restore_file.replace(".pt", suffix + ".pt")
|
| 246 |
+
else:
|
| 247 |
+
checkpoint_path = cfg.restore_file
|
| 248 |
+
|
| 249 |
+
if cfg.restore_file != "checkpoint_last.pt" and cfg.finetune_from_model:
|
| 250 |
+
raise ValueError(
|
| 251 |
+
"--finetune-from-model and --restore-file (non-default value) "
|
| 252 |
+
"can not be specified together: " + str(cfg)
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
extra_state = trainer.load_checkpoint(
|
| 256 |
+
checkpoint_path,
|
| 257 |
+
reset_optimizer,
|
| 258 |
+
reset_lr_scheduler,
|
| 259 |
+
optimizer_overrides,
|
| 260 |
+
reset_meters=reset_meters,
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
if (
|
| 264 |
+
extra_state is not None
|
| 265 |
+
and "best" in extra_state
|
| 266 |
+
and not reset_optimizer
|
| 267 |
+
and not reset_meters
|
| 268 |
+
):
|
| 269 |
+
save_checkpoint.best = extra_state["best"]
|
| 270 |
+
|
| 271 |
+
if extra_state is not None and not reset_dataloader:
|
| 272 |
+
# restore iterator from checkpoint
|
| 273 |
+
itr_state = extra_state["train_iterator"]
|
| 274 |
+
epoch_itr = trainer.get_train_iterator(
|
| 275 |
+
epoch=itr_state["epoch"], load_dataset=True, **passthrough_args
|
| 276 |
+
)
|
| 277 |
+
epoch_itr.load_state_dict(itr_state)
|
| 278 |
+
else:
|
| 279 |
+
epoch_itr = trainer.get_train_iterator(
|
| 280 |
+
epoch=1, load_dataset=True, **passthrough_args
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
trainer.lr_step(epoch_itr.epoch)
|
| 284 |
+
|
| 285 |
+
return extra_state, epoch_itr
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def load_checkpoint_to_cpu(path, arg_overrides=None, load_on_all_ranks=False):
|
| 289 |
+
"""Loads a checkpoint to CPU (with upgrading for backward compatibility).
|
| 290 |
+
|
| 291 |
+
If doing single-GPU training or if the checkpoint is only being loaded by at
|
| 292 |
+
most one process on each node (current default behavior is for only rank 0
|
| 293 |
+
to read the checkpoint from disk), load_on_all_ranks should be False to
|
| 294 |
+
avoid errors from torch.distributed not having been initialized or
|
| 295 |
+
torch.distributed.barrier() hanging.
|
| 296 |
+
|
| 297 |
+
If all processes on each node may be loading the checkpoint
|
| 298 |
+
simultaneously, load_on_all_ranks should be set to True to avoid I/O
|
| 299 |
+
conflicts.
|
| 300 |
+
|
| 301 |
+
There's currently no support for > 1 but < all processes loading the
|
| 302 |
+
checkpoint on each node.
|
| 303 |
+
"""
|
| 304 |
+
local_path = PathManager.get_local_path(path)
|
| 305 |
+
# The locally cached file returned by get_local_path() may be stale for
|
| 306 |
+
# remote files that are periodically updated/overwritten (ex:
|
| 307 |
+
# checkpoint_last.pt) - so we remove the local copy, sync across processes
|
| 308 |
+
# (if needed), and then download a fresh copy.
|
| 309 |
+
if local_path != path and PathManager.path_requires_pathmanager(path):
|
| 310 |
+
try:
|
| 311 |
+
os.remove(local_path)
|
| 312 |
+
except FileNotFoundError:
|
| 313 |
+
# With potentially multiple processes removing the same file, the
|
| 314 |
+
# file being missing is benign (missing_ok isn't available until
|
| 315 |
+
# Python 3.8).
|
| 316 |
+
pass
|
| 317 |
+
if load_on_all_ranks:
|
| 318 |
+
torch.distributed.barrier()
|
| 319 |
+
local_path = PathManager.get_local_path(path)
|
| 320 |
+
|
| 321 |
+
with open(local_path, "rb") as f:
|
| 322 |
+
state = torch.load(f, map_location=torch.device("cpu"))
|
| 323 |
+
|
| 324 |
+
if "args" in state and state["args"] is not None and arg_overrides is not None:
|
| 325 |
+
args = state["args"]
|
| 326 |
+
for arg_name, arg_val in arg_overrides.items():
|
| 327 |
+
setattr(args, arg_name, arg_val)
|
| 328 |
+
|
| 329 |
+
if "cfg" in state and state["cfg"] is not None:
|
| 330 |
+
|
| 331 |
+
# hack to be able to set Namespace in dict config. this should be removed when we update to newer
|
| 332 |
+
# omegaconf version that supports object flags, or when we migrate all existing models
|
| 333 |
+
from omegaconf import __version__ as oc_version
|
| 334 |
+
from omegaconf import _utils
|
| 335 |
+
|
| 336 |
+
if oc_version < "2.2":
|
| 337 |
+
old_primitive = _utils.is_primitive_type
|
| 338 |
+
_utils.is_primitive_type = lambda _: True
|
| 339 |
+
|
| 340 |
+
state["cfg"] = OmegaConf.create(state["cfg"])
|
| 341 |
+
|
| 342 |
+
_utils.is_primitive_type = old_primitive
|
| 343 |
+
OmegaConf.set_struct(state["cfg"], True)
|
| 344 |
+
else:
|
| 345 |
+
state["cfg"] = OmegaConf.create(state["cfg"], flags={"allow_objects": True})
|
| 346 |
+
|
| 347 |
+
if arg_overrides is not None:
|
| 348 |
+
overwrite_args_by_name(state["cfg"], arg_overrides)
|
| 349 |
+
|
| 350 |
+
state = _upgrade_state_dict(state)
|
| 351 |
+
return state
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def load_model_ensemble(
|
| 355 |
+
filenames,
|
| 356 |
+
arg_overrides: Optional[Dict[str, Any]] = None,
|
| 357 |
+
task=None,
|
| 358 |
+
strict=True,
|
| 359 |
+
suffix="",
|
| 360 |
+
num_shards=1,
|
| 361 |
+
state=None,
|
| 362 |
+
):
|
| 363 |
+
"""Loads an ensemble of models.
|
| 364 |
+
|
| 365 |
+
Args:
|
| 366 |
+
filenames (List[str]): checkpoint files to load
|
| 367 |
+
arg_overrides (Dict[str,Any], optional): override model args that
|
| 368 |
+
were used during model training
|
| 369 |
+
task (fairseq.tasks.FairseqTask, optional): task to use for loading
|
| 370 |
+
"""
|
| 371 |
+
assert not (
|
| 372 |
+
strict and num_shards > 1
|
| 373 |
+
), "Cannot load state dict with strict=True and checkpoint shards > 1"
|
| 374 |
+
ensemble, args, _task = load_model_ensemble_and_task(
|
| 375 |
+
filenames,
|
| 376 |
+
arg_overrides,
|
| 377 |
+
task,
|
| 378 |
+
strict,
|
| 379 |
+
suffix,
|
| 380 |
+
num_shards,
|
| 381 |
+
state,
|
| 382 |
+
)
|
| 383 |
+
return ensemble, args
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def get_maybe_sharded_checkpoint_filename(
|
| 387 |
+
filename: str, suffix: str, shard_idx: int, num_shards: int
|
| 388 |
+
) -> str:
|
| 389 |
+
orig_filename = filename
|
| 390 |
+
filename = filename.replace(".pt", suffix + ".pt")
|
| 391 |
+
fsdp_filename = filename[:-3] + f"-shard{shard_idx}.pt"
|
| 392 |
+
model_parallel_filename = orig_filename[:-3] + f"_part{shard_idx}.pt"
|
| 393 |
+
if PathManager.exists(fsdp_filename):
|
| 394 |
+
return fsdp_filename
|
| 395 |
+
elif num_shards > 1:
|
| 396 |
+
return model_parallel_filename
|
| 397 |
+
else:
|
| 398 |
+
return filename
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def load_model_ensemble_and_task(
|
| 402 |
+
filenames,
|
| 403 |
+
arg_overrides: Optional[Dict[str, Any]] = None,
|
| 404 |
+
task=None,
|
| 405 |
+
strict=True,
|
| 406 |
+
suffix="",
|
| 407 |
+
num_shards=1,
|
| 408 |
+
state=None,
|
| 409 |
+
):
|
| 410 |
+
assert state is None or len(filenames) == 1
|
| 411 |
+
|
| 412 |
+
from fairseq import tasks
|
| 413 |
+
|
| 414 |
+
assert not (
|
| 415 |
+
strict and num_shards > 1
|
| 416 |
+
), "Cannot load state dict with strict=True and checkpoint shards > 1"
|
| 417 |
+
ensemble = []
|
| 418 |
+
cfg = None
|
| 419 |
+
for filename in filenames:
|
| 420 |
+
orig_filename = filename
|
| 421 |
+
model_shard_state = {"shard_weights": [], "shard_metadata": []}
|
| 422 |
+
assert num_shards > 0
|
| 423 |
+
st = time.time()
|
| 424 |
+
for shard_idx in range(num_shards):
|
| 425 |
+
filename = get_maybe_sharded_checkpoint_filename(
|
| 426 |
+
orig_filename, suffix, shard_idx, num_shards
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
if not PathManager.exists(filename):
|
| 430 |
+
raise IOError("Model file not found: {}".format(filename))
|
| 431 |
+
if state is None:
|
| 432 |
+
state = load_checkpoint_to_cpu(filename, arg_overrides)
|
| 433 |
+
if "args" in state and state["args"] is not None:
|
| 434 |
+
cfg = convert_namespace_to_omegaconf(state["args"])
|
| 435 |
+
elif "cfg" in state and state["cfg"] is not None:
|
| 436 |
+
cfg = state["cfg"]
|
| 437 |
+
else:
|
| 438 |
+
raise RuntimeError(
|
| 439 |
+
f"Neither args nor cfg exist in state keys = {state.keys()}"
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
if task is None:
|
| 443 |
+
task = tasks.setup_task(cfg.task, from_checkpoint=True)
|
| 444 |
+
|
| 445 |
+
if "task_state" in state:
|
| 446 |
+
task.load_state_dict(state["task_state"])
|
| 447 |
+
|
| 448 |
+
argspec = inspect.getfullargspec(task.build_model)
|
| 449 |
+
|
| 450 |
+
if "fsdp_metadata" in state and num_shards > 1:
|
| 451 |
+
model_shard_state["shard_weights"].append(state["model"])
|
| 452 |
+
model_shard_state["shard_metadata"].append(state["fsdp_metadata"])
|
| 453 |
+
# check FSDP import before the code goes too far
|
| 454 |
+
if not has_FSDP:
|
| 455 |
+
raise ImportError(
|
| 456 |
+
"Cannot find FullyShardedDataParallel. "
|
| 457 |
+
"Please install fairscale with: pip install fairscale"
|
| 458 |
+
)
|
| 459 |
+
if shard_idx == num_shards - 1:
|
| 460 |
+
consolidated_model_state = FSDP.consolidate_shard_weights(
|
| 461 |
+
shard_weights=model_shard_state["shard_weights"],
|
| 462 |
+
shard_metadata=model_shard_state["shard_metadata"],
|
| 463 |
+
)
|
| 464 |
+
if "from_checkpoint" in argspec.args:
|
| 465 |
+
model = task.build_model(cfg.model, from_checkpoint=True)
|
| 466 |
+
else:
|
| 467 |
+
model = task.build_model(cfg.model)
|
| 468 |
+
if (
|
| 469 |
+
"optimizer_history" in state
|
| 470 |
+
and len(state["optimizer_history"]) > 0
|
| 471 |
+
and "num_updates" in state["optimizer_history"][-1]
|
| 472 |
+
):
|
| 473 |
+
model.set_num_updates(
|
| 474 |
+
state["optimizer_history"][-1]["num_updates"]
|
| 475 |
+
)
|
| 476 |
+
model.load_state_dict(
|
| 477 |
+
consolidated_model_state, strict=strict, model_cfg=cfg.model
|
| 478 |
+
)
|
| 479 |
+
else:
|
| 480 |
+
# model parallel checkpoint or unsharded checkpoint
|
| 481 |
+
# support old external tasks
|
| 482 |
+
|
| 483 |
+
if "from_checkpoint" in argspec.args:
|
| 484 |
+
model = task.build_model(cfg.model, from_checkpoint=True)
|
| 485 |
+
else:
|
| 486 |
+
model = task.build_model(cfg.model)
|
| 487 |
+
if (
|
| 488 |
+
"optimizer_history" in state
|
| 489 |
+
and len(state["optimizer_history"]) > 0
|
| 490 |
+
and "num_updates" in state["optimizer_history"][-1]
|
| 491 |
+
):
|
| 492 |
+
model.set_num_updates(state["optimizer_history"][-1]["num_updates"])
|
| 493 |
+
model.load_state_dict(
|
| 494 |
+
state["model"], strict=strict, model_cfg=cfg.model
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
# reset state so it gets loaded for the next model in ensemble
|
| 498 |
+
state = None
|
| 499 |
+
if shard_idx % 10 == 0 and shard_idx > 0:
|
| 500 |
+
elapsed = time.time() - st
|
| 501 |
+
logger.info(
|
| 502 |
+
f"Loaded {shard_idx} shards in {elapsed:.2f}s, {elapsed / (shard_idx+1):.2f}s/shard"
|
| 503 |
+
)
|
| 504 |
+
|
| 505 |
+
# build model for ensemble
|
| 506 |
+
ensemble.append(model)
|
| 507 |
+
return ensemble, cfg, task
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
def load_model_ensemble_and_task_from_hf_hub(
|
| 511 |
+
model_id,
|
| 512 |
+
cache_dir: Optional[str] = None,
|
| 513 |
+
arg_overrides: Optional[Dict[str, Any]] = None,
|
| 514 |
+
**kwargs: Any,
|
| 515 |
+
):
|
| 516 |
+
try:
|
| 517 |
+
from huggingface_hub import snapshot_download
|
| 518 |
+
except ImportError:
|
| 519 |
+
raise ImportError(
|
| 520 |
+
"You need to install huggingface_hub to use `load_from_hf_hub`. "
|
| 521 |
+
"See https://pypi.org/project/huggingface-hub/ for installation."
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
library_name = "fairseq"
|
| 525 |
+
cache_dir = cache_dir or (Path.home() / ".cache" / library_name).as_posix()
|
| 526 |
+
cache_dir = snapshot_download(
|
| 527 |
+
model_id, cache_dir=cache_dir, library_name=library_name, **kwargs
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
_arg_overrides = arg_overrides or {}
|
| 531 |
+
_arg_overrides["data"] = cache_dir
|
| 532 |
+
return load_model_ensemble_and_task(
|
| 533 |
+
[p.as_posix() for p in Path(cache_dir).glob("*.pt")],
|
| 534 |
+
arg_overrides=_arg_overrides,
|
| 535 |
+
)
|
| 536 |
+
|
| 537 |
+
|
| 538 |
+
def checkpoint_paths(path, pattern=r"checkpoint(\d+)\.pt", keep_match=False):
|
| 539 |
+
"""Retrieves all checkpoints found in `path` directory.
|
| 540 |
+
|
| 541 |
+
Checkpoints are identified by matching filename to the specified pattern. If
|
| 542 |
+
the pattern contains groups, the result will be sorted by the first group in
|
| 543 |
+
descending order.
|
| 544 |
+
"""
|
| 545 |
+
pt_regexp = re.compile(pattern)
|
| 546 |
+
files = PathManager.ls(path)
|
| 547 |
+
|
| 548 |
+
entries = []
|
| 549 |
+
for i, f in enumerate(files):
|
| 550 |
+
m = pt_regexp.fullmatch(f)
|
| 551 |
+
if m is not None:
|
| 552 |
+
idx = float(m.group(1)) if len(m.groups()) > 0 else i
|
| 553 |
+
entries.append((idx, m.group(0)))
|
| 554 |
+
if keep_match:
|
| 555 |
+
return [(os.path.join(path, x[1]), x[0]) for x in sorted(entries, reverse=True)]
|
| 556 |
+
else:
|
| 557 |
+
return [os.path.join(path, x[1]) for x in sorted(entries, reverse=True)]
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
def torch_persistent_save(obj, filename, async_write: bool = False):
|
| 561 |
+
if async_write:
|
| 562 |
+
with PathManager.opena(filename, "wb") as f:
|
| 563 |
+
_torch_persistent_save(obj, f)
|
| 564 |
+
else:
|
| 565 |
+
if PathManager.supports_rename(filename):
|
| 566 |
+
# do atomic save
|
| 567 |
+
with PathManager.open(filename + ".tmp", "wb") as f:
|
| 568 |
+
_torch_persistent_save(obj, f)
|
| 569 |
+
PathManager.rename(filename + ".tmp", filename)
|
| 570 |
+
else:
|
| 571 |
+
# fallback to non-atomic save
|
| 572 |
+
with PathManager.open(filename, "wb") as f:
|
| 573 |
+
_torch_persistent_save(obj, f)
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
def _torch_persistent_save(obj, f):
|
| 577 |
+
if isinstance(f, str):
|
| 578 |
+
with PathManager.open(f, "wb") as h:
|
| 579 |
+
torch_persistent_save(obj, h)
|
| 580 |
+
return
|
| 581 |
+
for i in range(3):
|
| 582 |
+
try:
|
| 583 |
+
return torch.save(obj, f)
|
| 584 |
+
except Exception:
|
| 585 |
+
if i == 2:
|
| 586 |
+
logger.error(traceback.format_exc())
|
| 587 |
+
raise
|
| 588 |
+
else:
|
| 589 |
+
time.sleep(2.5)
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
def _upgrade_state_dict(state):
|
| 593 |
+
"""Helper for upgrading old model checkpoints."""
|
| 594 |
+
|
| 595 |
+
# add optimizer_history
|
| 596 |
+
if "optimizer_history" not in state:
|
| 597 |
+
state["optimizer_history"] = [
|
| 598 |
+
{"criterion_name": "CrossEntropyCriterion", "best_loss": state["best_loss"]}
|
| 599 |
+
]
|
| 600 |
+
state["last_optimizer_state"] = state["optimizer"]
|
| 601 |
+
del state["optimizer"]
|
| 602 |
+
del state["best_loss"]
|
| 603 |
+
# move extra_state into sub-dictionary
|
| 604 |
+
if "epoch" in state and "extra_state" not in state:
|
| 605 |
+
state["extra_state"] = {
|
| 606 |
+
"epoch": state["epoch"],
|
| 607 |
+
"batch_offset": state["batch_offset"],
|
| 608 |
+
"val_loss": state["val_loss"],
|
| 609 |
+
}
|
| 610 |
+
del state["epoch"]
|
| 611 |
+
del state["batch_offset"]
|
| 612 |
+
del state["val_loss"]
|
| 613 |
+
# reduce optimizer history's memory usage (only keep the last state)
|
| 614 |
+
if "optimizer" in state["optimizer_history"][-1]:
|
| 615 |
+
state["last_optimizer_state"] = state["optimizer_history"][-1]["optimizer"]
|
| 616 |
+
for optim_hist in state["optimizer_history"]:
|
| 617 |
+
del optim_hist["optimizer"]
|
| 618 |
+
# record the optimizer class name
|
| 619 |
+
if "optimizer_name" not in state["optimizer_history"][-1]:
|
| 620 |
+
state["optimizer_history"][-1]["optimizer_name"] = "FairseqNAG"
|
| 621 |
+
# move best_loss into lr_scheduler_state
|
| 622 |
+
if "lr_scheduler_state" not in state["optimizer_history"][-1]:
|
| 623 |
+
state["optimizer_history"][-1]["lr_scheduler_state"] = {
|
| 624 |
+
"best": state["optimizer_history"][-1]["best_loss"]
|
| 625 |
+
}
|
| 626 |
+
del state["optimizer_history"][-1]["best_loss"]
|
| 627 |
+
# keep track of number of updates
|
| 628 |
+
if "num_updates" not in state["optimizer_history"][-1]:
|
| 629 |
+
state["optimizer_history"][-1]["num_updates"] = 0
|
| 630 |
+
# use stateful training data iterator
|
| 631 |
+
if "train_iterator" not in state["extra_state"]:
|
| 632 |
+
state["extra_state"]["train_iterator"] = {
|
| 633 |
+
"epoch": state["extra_state"].get("epoch", 0),
|
| 634 |
+
"iterations_in_epoch": state["extra_state"].get("batch_offset", 0),
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
# backward compatibility, cfg updates
|
| 638 |
+
if "args" in state and state["args"] is not None:
|
| 639 |
+
# old model checkpoints may not have separate source/target positions
|
| 640 |
+
if hasattr(state["args"], "max_positions") and not hasattr(
|
| 641 |
+
state["args"], "max_source_positions"
|
| 642 |
+
):
|
| 643 |
+
state["args"].max_source_positions = state["args"].max_positions
|
| 644 |
+
state["args"].max_target_positions = state["args"].max_positions
|
| 645 |
+
# default to translation task
|
| 646 |
+
if not hasattr(state["args"], "task"):
|
| 647 |
+
state["args"].task = "translation"
|
| 648 |
+
# --raw-text and --lazy-load are deprecated
|
| 649 |
+
if getattr(state["args"], "raw_text", False):
|
| 650 |
+
state["args"].dataset_impl = "raw"
|
| 651 |
+
elif getattr(state["args"], "lazy_load", False):
|
| 652 |
+
state["args"].dataset_impl = "lazy"
|
| 653 |
+
# epochs start at 1
|
| 654 |
+
if state["extra_state"]["train_iterator"] is not None:
|
| 655 |
+
state["extra_state"]["train_iterator"]["epoch"] = max(
|
| 656 |
+
state["extra_state"]["train_iterator"].get("epoch", 1), 1
|
| 657 |
+
)
|
| 658 |
+
# --remove-bpe ==> --postprocess
|
| 659 |
+
if hasattr(state["args"], "remove_bpe"):
|
| 660 |
+
state["args"].post_process = state["args"].remove_bpe
|
| 661 |
+
# --min-lr ==> --stop-min-lr
|
| 662 |
+
if hasattr(state["args"], "min_lr"):
|
| 663 |
+
state["args"].stop_min_lr = state["args"].min_lr
|
| 664 |
+
del state["args"].min_lr
|
| 665 |
+
# binary_cross_entropy / kd_binary_cross_entropy => wav2vec criterion
|
| 666 |
+
if hasattr(state["args"], "criterion") and state["args"].criterion in [
|
| 667 |
+
"binary_cross_entropy",
|
| 668 |
+
"kd_binary_cross_entropy",
|
| 669 |
+
]:
|
| 670 |
+
state["args"].criterion = "wav2vec"
|
| 671 |
+
# remove log_keys if it's None (criteria will supply a default value of [])
|
| 672 |
+
if hasattr(state["args"], "log_keys") and state["args"].log_keys is None:
|
| 673 |
+
delattr(state["args"], "log_keys")
|
| 674 |
+
# speech_pretraining => audio pretraining
|
| 675 |
+
if (
|
| 676 |
+
hasattr(state["args"], "task")
|
| 677 |
+
and state["args"].task == "speech_pretraining"
|
| 678 |
+
):
|
| 679 |
+
state["args"].task = "audio_pretraining"
|
| 680 |
+
# audio_cpc => wav2vec
|
| 681 |
+
if hasattr(state["args"], "arch") and state["args"].arch == "audio_cpc":
|
| 682 |
+
state["args"].arch = "wav2vec"
|
| 683 |
+
# convert legacy float learning rate to List[float]
|
| 684 |
+
if hasattr(state["args"], "lr") and isinstance(state["args"].lr, float):
|
| 685 |
+
state["args"].lr = [state["args"].lr]
|
| 686 |
+
# convert task data arg to a string instead of List[string]
|
| 687 |
+
if (
|
| 688 |
+
hasattr(state["args"], "data")
|
| 689 |
+
and isinstance(state["args"].data, list)
|
| 690 |
+
and len(state["args"].data) > 0
|
| 691 |
+
):
|
| 692 |
+
state["args"].data = state["args"].data[0]
|
| 693 |
+
|
| 694 |
+
state["cfg"] = convert_namespace_to_omegaconf(state["args"])
|
| 695 |
+
|
| 696 |
+
if "cfg" in state and state["cfg"] is not None:
|
| 697 |
+
cfg = state["cfg"]
|
| 698 |
+
with open_dict(cfg):
|
| 699 |
+
# any upgrades for Hydra-based configs
|
| 700 |
+
if (
|
| 701 |
+
"task" in cfg
|
| 702 |
+
and "eval_wer_config" in cfg.task
|
| 703 |
+
and isinstance(cfg.task.eval_wer_config.print_alignment, bool)
|
| 704 |
+
):
|
| 705 |
+
cfg.task.eval_wer_config.print_alignment = "hard"
|
| 706 |
+
if "generation" in cfg and isinstance(cfg.generation.print_alignment, bool):
|
| 707 |
+
cfg.generation.print_alignment = (
|
| 708 |
+
"hard" if cfg.generation.print_alignment else None
|
| 709 |
+
)
|
| 710 |
+
if (
|
| 711 |
+
"model" in cfg
|
| 712 |
+
and "w2v_args" in cfg.model
|
| 713 |
+
and cfg.model.w2v_args is not None
|
| 714 |
+
and (
|
| 715 |
+
hasattr(cfg.model.w2v_args, "task") or "task" in cfg.model.w2v_args
|
| 716 |
+
)
|
| 717 |
+
and hasattr(cfg.model.w2v_args.task, "eval_wer_config")
|
| 718 |
+
and cfg.model.w2v_args.task.eval_wer_config is not None
|
| 719 |
+
and isinstance(
|
| 720 |
+
cfg.model.w2v_args.task.eval_wer_config.print_alignment, bool
|
| 721 |
+
)
|
| 722 |
+
):
|
| 723 |
+
cfg.model.w2v_args.task.eval_wer_config.print_alignment = "hard"
|
| 724 |
+
|
| 725 |
+
return state
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
def prune_state_dict(state_dict, model_cfg: Optional[DictConfig]):
|
| 729 |
+
"""Prune the given state_dict if desired for LayerDrop
|
| 730 |
+
(https://arxiv.org/abs/1909.11556).
|
| 731 |
+
|
| 732 |
+
Training with LayerDrop allows models to be robust to pruning at inference
|
| 733 |
+
time. This function prunes state_dict to allow smaller models to be loaded
|
| 734 |
+
from a larger model and re-maps the existing state_dict for this to occur.
|
| 735 |
+
|
| 736 |
+
It's called by functions that load models from checkpoints and does not
|
| 737 |
+
need to be called directly.
|
| 738 |
+
"""
|
| 739 |
+
arch = None
|
| 740 |
+
if model_cfg is not None:
|
| 741 |
+
arch = (
|
| 742 |
+
model_cfg._name
|
| 743 |
+
if isinstance(model_cfg, DictConfig)
|
| 744 |
+
else getattr(model_cfg, "arch", None)
|
| 745 |
+
)
|
| 746 |
+
|
| 747 |
+
if not model_cfg or arch is None or arch == "ptt_transformer":
|
| 748 |
+
# args should not be none, but don't crash if it is.
|
| 749 |
+
return state_dict
|
| 750 |
+
|
| 751 |
+
encoder_layers_to_keep = getattr(model_cfg, "encoder_layers_to_keep", None)
|
| 752 |
+
decoder_layers_to_keep = getattr(model_cfg, "decoder_layers_to_keep", None)
|
| 753 |
+
|
| 754 |
+
if not encoder_layers_to_keep and not decoder_layers_to_keep:
|
| 755 |
+
return state_dict
|
| 756 |
+
|
| 757 |
+
# apply pruning
|
| 758 |
+
logger.info(
|
| 759 |
+
"Pruning model to specified layer configuration - this works best if the model was trained with LayerDrop"
|
| 760 |
+
)
|
| 761 |
+
|
| 762 |
+
def create_pruning_pass(layers_to_keep, layer_name):
|
| 763 |
+
keep_layers = sorted(
|
| 764 |
+
int(layer_string) for layer_string in layers_to_keep.split(",")
|
| 765 |
+
)
|
| 766 |
+
mapping_dict = {}
|
| 767 |
+
for i in range(len(keep_layers)):
|
| 768 |
+
mapping_dict[str(keep_layers[i])] = str(i)
|
| 769 |
+
|
| 770 |
+
regex = re.compile(r"^{layer}.*\.layers\.(\d+)".format(layer=layer_name))
|
| 771 |
+
return {"substitution_regex": regex, "mapping_dict": mapping_dict}
|
| 772 |
+
|
| 773 |
+
pruning_passes = []
|
| 774 |
+
if encoder_layers_to_keep:
|
| 775 |
+
pruning_passes.append(create_pruning_pass(encoder_layers_to_keep, "encoder"))
|
| 776 |
+
if decoder_layers_to_keep:
|
| 777 |
+
pruning_passes.append(create_pruning_pass(decoder_layers_to_keep, "decoder"))
|
| 778 |
+
|
| 779 |
+
new_state_dict = {}
|
| 780 |
+
for layer_name in state_dict.keys():
|
| 781 |
+
match = re.search(r"\.layers\.(\d+)\.", layer_name)
|
| 782 |
+
# if layer has no number in it, it is a supporting layer, such as an
|
| 783 |
+
# embedding
|
| 784 |
+
if not match:
|
| 785 |
+
new_state_dict[layer_name] = state_dict[layer_name]
|
| 786 |
+
continue
|
| 787 |
+
|
| 788 |
+
# otherwise, layer should be pruned.
|
| 789 |
+
original_layer_number = match.group(1)
|
| 790 |
+
# figure out which mapping dict to replace from
|
| 791 |
+
for pruning_pass in pruning_passes:
|
| 792 |
+
if original_layer_number in pruning_pass["mapping_dict"] and pruning_pass[
|
| 793 |
+
"substitution_regex"
|
| 794 |
+
].search(layer_name):
|
| 795 |
+
new_layer_number = pruning_pass["mapping_dict"][original_layer_number]
|
| 796 |
+
substitution_match = pruning_pass["substitution_regex"].search(
|
| 797 |
+
layer_name
|
| 798 |
+
)
|
| 799 |
+
new_state_key = (
|
| 800 |
+
layer_name[: substitution_match.start(1)]
|
| 801 |
+
+ new_layer_number
|
| 802 |
+
+ layer_name[substitution_match.end(1) :]
|
| 803 |
+
)
|
| 804 |
+
new_state_dict[new_state_key] = state_dict[layer_name]
|
| 805 |
+
|
| 806 |
+
# Since layers are now pruned, *_layers_to_keep are no longer needed.
|
| 807 |
+
# This is more of "It would make it work fix" rather than a proper fix.
|
| 808 |
+
if isinstance(model_cfg, DictConfig):
|
| 809 |
+
context = open_dict(model_cfg)
|
| 810 |
+
else:
|
| 811 |
+
context = contextlib.ExitStack()
|
| 812 |
+
with context:
|
| 813 |
+
if hasattr(model_cfg, "encoder_layers_to_keep"):
|
| 814 |
+
model_cfg.encoder_layers_to_keep = None
|
| 815 |
+
if hasattr(model_cfg, "decoder_layers_to_keep"):
|
| 816 |
+
model_cfg.decoder_layers_to_keep = None
|
| 817 |
+
|
| 818 |
+
return new_state_dict
|
| 819 |
+
|
| 820 |
+
|
| 821 |
+
def load_pretrained_component_from_model(
|
| 822 |
+
component: Union[FairseqEncoder, FairseqDecoder],
|
| 823 |
+
checkpoint: str,
|
| 824 |
+
strict: bool = True,
|
| 825 |
+
):
|
| 826 |
+
"""
|
| 827 |
+
Load a pretrained FairseqEncoder or FairseqDecoder from checkpoint into the
|
| 828 |
+
provided `component` object. If state_dict fails to load, there may be a
|
| 829 |
+
mismatch in the architecture of the corresponding `component` found in the
|
| 830 |
+
`checkpoint` file.
|
| 831 |
+
"""
|
| 832 |
+
if not PathManager.exists(checkpoint):
|
| 833 |
+
raise IOError("Model file not found: {}".format(checkpoint))
|
| 834 |
+
state = load_checkpoint_to_cpu(checkpoint)
|
| 835 |
+
if isinstance(component, FairseqEncoder):
|
| 836 |
+
component_type = "encoder"
|
| 837 |
+
elif isinstance(component, FairseqDecoder):
|
| 838 |
+
component_type = "decoder"
|
| 839 |
+
else:
|
| 840 |
+
raise ValueError(
|
| 841 |
+
"component to load must be either a FairseqEncoder or "
|
| 842 |
+
"FairseqDecoder. Loading other component types are not supported."
|
| 843 |
+
)
|
| 844 |
+
component_state_dict = OrderedDict()
|
| 845 |
+
for key in state["model"].keys():
|
| 846 |
+
if key.startswith(component_type):
|
| 847 |
+
# encoder.input_layers.0.0.weight --> input_layers.0.0.weight
|
| 848 |
+
component_subkey = key[len(component_type) + 1 :]
|
| 849 |
+
component_state_dict[component_subkey] = state["model"][key]
|
| 850 |
+
component.load_state_dict(component_state_dict, strict=strict)
|
| 851 |
+
return component
|
| 852 |
+
|
| 853 |
+
|
| 854 |
+
def verify_checkpoint_directory(save_dir: str) -> None:
|
| 855 |
+
if not os.path.exists(save_dir):
|
| 856 |
+
os.makedirs(save_dir, exist_ok=True)
|
| 857 |
+
temp_file_path = os.path.join(save_dir, "dummy")
|
| 858 |
+
try:
|
| 859 |
+
with open(temp_file_path, "w"):
|
| 860 |
+
pass
|
| 861 |
+
except OSError as e:
|
| 862 |
+
logger.warning(
|
| 863 |
+
"Unable to access checkpoint save directory: {}".format(save_dir)
|
| 864 |
+
)
|
| 865 |
+
raise e
|
| 866 |
+
else:
|
| 867 |
+
os.remove(temp_file_path)
|
| 868 |
+
|
| 869 |
+
|
| 870 |
+
def save_ema_as_checkpoint(src_path, dst_path):
|
| 871 |
+
state = load_ema_from_checkpoint(src_path)
|
| 872 |
+
torch_persistent_save(state, dst_path)
|
| 873 |
+
|
| 874 |
+
|
| 875 |
+
def load_ema_from_checkpoint(fpath):
|
| 876 |
+
"""Loads exponential moving averaged (EMA) checkpoint from input and
|
| 877 |
+
returns a model with ema weights.
|
| 878 |
+
|
| 879 |
+
Args:
|
| 880 |
+
fpath: A string path of checkpoint to load from.
|
| 881 |
+
|
| 882 |
+
Returns:
|
| 883 |
+
A dict of string keys mapping to various values. The 'model' key
|
| 884 |
+
from the returned dict should correspond to an OrderedDict mapping
|
| 885 |
+
string parameter names to torch Tensors.
|
| 886 |
+
"""
|
| 887 |
+
params_dict = collections.OrderedDict()
|
| 888 |
+
new_state = None
|
| 889 |
+
|
| 890 |
+
with PathManager.open(fpath, "rb") as f:
|
| 891 |
+
new_state = torch.load(
|
| 892 |
+
f,
|
| 893 |
+
map_location=(
|
| 894 |
+
lambda s, _: torch.serialization.default_restore_location(s, "cpu")
|
| 895 |
+
),
|
| 896 |
+
)
|
| 897 |
+
|
| 898 |
+
# EMA model is stored in a separate "extra state"
|
| 899 |
+
model_params = new_state["extra_state"]["ema"]
|
| 900 |
+
|
| 901 |
+
for key in list(model_params.keys()):
|
| 902 |
+
p = model_params[key]
|
| 903 |
+
if isinstance(p, torch.HalfTensor):
|
| 904 |
+
p = p.float()
|
| 905 |
+
if key not in params_dict:
|
| 906 |
+
params_dict[key] = p.clone()
|
| 907 |
+
# NOTE: clone() is needed in case of p is a shared parameter
|
| 908 |
+
else:
|
| 909 |
+
raise ValueError("Key {} is repeated in EMA model params.".format(key))
|
| 910 |
+
|
| 911 |
+
if len(params_dict) == 0:
|
| 912 |
+
raise ValueError(
|
| 913 |
+
f"Input checkpoint path '{fpath}' does not contain "
|
| 914 |
+
"ema model weights, is this model trained with EMA?"
|
| 915 |
+
)
|
| 916 |
+
|
| 917 |
+
new_state["model"] = params_dict
|
| 918 |
+
return new_state
|
omni_speech/infer/fairseq/fairseq/clib/cuda/ngram_repeat_block_cuda.cpp
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
Copyright (c) Microsoft Corporation.
|
| 3 |
+
Licensed under the MIT License.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
#include <torch/extension.h>
|
| 7 |
+
#include <vector>
|
| 8 |
+
|
| 9 |
+
/*
|
| 10 |
+
CPP Binding for CUDA OP
|
| 11 |
+
*/
|
| 12 |
+
|
| 13 |
+
// CUDA forward declarations
|
| 14 |
+
torch::Tensor ngram_repeat_block_cuda_forward(
|
| 15 |
+
torch::Tensor tokens,
|
| 16 |
+
torch::Tensor lprobs,
|
| 17 |
+
int bsz,
|
| 18 |
+
int step,
|
| 19 |
+
int beam_size,
|
| 20 |
+
int no_repeat_ngram_size);
|
| 21 |
+
|
| 22 |
+
#define CHECK_CUDA(x) \
|
| 23 |
+
TORCH_CHECK(x.type().is_cuda(), #x " must be a CUDA tensor")
|
| 24 |
+
#define CHECK_CONTIGUOUS(x) \
|
| 25 |
+
TORCH_CHECK(x.is_contiguous(), #x " must be contiguous")
|
| 26 |
+
#define CHECK_INPUT(x) \
|
| 27 |
+
CHECK_CUDA(x); \
|
| 28 |
+
CHECK_CONTIGUOUS(x)
|
| 29 |
+
|
| 30 |
+
// Input check and call to CUDA OP
|
| 31 |
+
// Backward method not required
|
| 32 |
+
torch::Tensor ngram_repeat_block_forward(
|
| 33 |
+
torch::Tensor tokens,
|
| 34 |
+
torch::Tensor lprobs,
|
| 35 |
+
int bsz,
|
| 36 |
+
int step,
|
| 37 |
+
int beam_size,
|
| 38 |
+
int no_repeat_ngram_size) {
|
| 39 |
+
CHECK_INPUT(tokens);
|
| 40 |
+
CHECK_INPUT(lprobs);
|
| 41 |
+
assert(bsz > 0);
|
| 42 |
+
assert(step >= 0);
|
| 43 |
+
assert(beam_size > 0);
|
| 44 |
+
assert(no_repeat_ngram_size > 0);
|
| 45 |
+
|
| 46 |
+
return ngram_repeat_block_cuda_forward(
|
| 47 |
+
tokens, lprobs, bsz, step, beam_size, no_repeat_ngram_size);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
| 51 |
+
m.def(
|
| 52 |
+
"forward",
|
| 53 |
+
&ngram_repeat_block_forward,
|
| 54 |
+
"No Repeat Ngram Block forward (CUDA)");
|
| 55 |
+
}
|
omni_speech/infer/fairseq/fairseq/clib/cuda/ngram_repeat_block_cuda_kernel.cu
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
Copyright (c) Microsoft Corporation.
|
| 3 |
+
Licensed under the MIT License.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
/*
|
| 7 |
+
Kernel implementation for blocking repeated n-grams.
|
| 8 |
+
*/
|
| 9 |
+
|
| 10 |
+
#include <cuda.h>
|
| 11 |
+
#include <cuda_runtime.h>
|
| 12 |
+
#include <math.h>
|
| 13 |
+
#include <torch/extension.h>
|
| 14 |
+
#include <vector>
|
| 15 |
+
|
| 16 |
+
// Ban repeated ngrams of length = 'no_repeat_ngram_size'
|
| 17 |
+
__global__ void banRepeatedTokens(
|
| 18 |
+
long* __restrict__ tokens,
|
| 19 |
+
float* __restrict__ lprobs,
|
| 20 |
+
int max_predict_len,
|
| 21 |
+
int vocab_size,
|
| 22 |
+
int no_repeat_ngram_size) {
|
| 23 |
+
auto row = blockIdx.x;
|
| 24 |
+
auto col = threadIdx.x;
|
| 25 |
+
auto start = row * (max_predict_len) + col;
|
| 26 |
+
// Each thread compares ngram starting from
|
| 27 |
+
// thread index with final ngram starting from
|
| 28 |
+
// step - no_repeat_ngram_size +2
|
| 29 |
+
auto check_start_pos = blockDim.x;
|
| 30 |
+
auto lprob_start = row * vocab_size;
|
| 31 |
+
bool is_banned = true;
|
| 32 |
+
extern __shared__ long tokens_shm[];
|
| 33 |
+
tokens_shm[col] = tokens[start];
|
| 34 |
+
if (col == blockDim.x - 1) {
|
| 35 |
+
for (int i = 1; i < no_repeat_ngram_size; i++) {
|
| 36 |
+
if (col + i < max_predict_len) {
|
| 37 |
+
tokens_shm[col + i] = tokens[start + i];
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
__syncthreads();
|
| 42 |
+
|
| 43 |
+
for (int k = 0; k < no_repeat_ngram_size - 1; k++) {
|
| 44 |
+
if (tokens_shm[col + k] != tokens_shm[check_start_pos + k]) {
|
| 45 |
+
is_banned = false;
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
if (is_banned == true) {
|
| 49 |
+
auto token_to_be_banned = tokens_shm[col + no_repeat_ngram_size - 1];
|
| 50 |
+
lprobs[lprob_start + token_to_be_banned] = -INFINITY;
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
// Allocate blocks and threads based on
|
| 55 |
+
// batch size and sequence length and launch
|
| 56 |
+
// kernel
|
| 57 |
+
torch::Tensor ngram_repeat_block_cuda_forward(
|
| 58 |
+
const torch::Tensor tokens,
|
| 59 |
+
torch::Tensor lprobs,
|
| 60 |
+
int bsz,
|
| 61 |
+
int step,
|
| 62 |
+
int beam_size,
|
| 63 |
+
int no_repeat_ngram_size) {
|
| 64 |
+
int threads = step - no_repeat_ngram_size + 2;
|
| 65 |
+
if (threads <= 0)
|
| 66 |
+
return lprobs;
|
| 67 |
+
int max_predict_len = tokens.size(1);
|
| 68 |
+
int vocab_size = lprobs.size(1);
|
| 69 |
+
auto token_ptr = tokens.data_ptr<long>();
|
| 70 |
+
auto lprob_ptr = lprobs.data_ptr<float>();
|
| 71 |
+
int blocks = bsz * beam_size;
|
| 72 |
+
int shared_mem_size = (step + 1) * sizeof(long);
|
| 73 |
+
|
| 74 |
+
// Launching N blocks where N is number of samples in a batch (beams*bsz)
|
| 75 |
+
// Launching T threads where T is number of previous ngrams in a sample
|
| 76 |
+
// Allocating shared mem per block for fastser access of input tokens since
|
| 77 |
+
// each token will be accessed N times to compare with current Ngram where
|
| 78 |
+
// N is Ngram size.
|
| 79 |
+
banRepeatedTokens<<<blocks, threads, shared_mem_size>>>(
|
| 80 |
+
token_ptr, lprob_ptr, max_predict_len, vocab_size, no_repeat_ngram_size);
|
| 81 |
+
return lprobs;
|
| 82 |
+
}
|