Skip to content
Snippets Groups Projects
Commit a700c4e4 authored by Milos CERNAK's avatar Milos CERNAK
Browse files

Merge branch 'ivector-update' into 'master'

Add ivector+PLDA support

See merge request !5
parents 84de208d a92b3655
No related branches found
No related tags found
1 merge request!5Add ivector+PLDA support
Pipeline #
...@@ -332,6 +332,7 @@ def ubm_full_train(feats, dubm, fubmfile, num_gselect=20, num_iters=4, ...@@ -332,6 +332,7 @@ def ubm_full_train(feats, dubm, fubmfile, num_gselect=20, num_iters=4,
delete=False, suffix='.dump') as estfile: delete=False, suffix='.dump') as estfile:
cmd5 += [ cmd5 += [
opt, opt,
'--binary=false',
'--min-gaussian-weight=' + '--min-gaussian-weight=' +
str(min_gaussian_weight), str(min_gaussian_weight),
inModel, inModel,
...@@ -352,8 +353,10 @@ def ubm_full_train(feats, dubm, fubmfile, num_gselect=20, num_iters=4, ...@@ -352,8 +353,10 @@ def ubm_full_train(feats, dubm, fubmfile, num_gselect=20, num_iters=4,
shutil.copyfile(estfile.name, fubmfile) shutil.copyfile(estfile.name, fubmfile)
os.unlink(estfile.name) os.unlink(estfile.name)
os.unlink(dubmfile.name) os.unlink(dubmfile.name)
return fubmfile # ToDo : covert to a text format with open(fubmfile) as fp:
fubmtxt = fp.read()
return fubmtxt
def ubm_enroll(feats, ubm): def ubm_enroll(feats, ubm):
"""Performes MAP adaptation of GMM-UBM model. """Performes MAP adaptation of GMM-UBM model.
......
...@@ -27,9 +27,9 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -27,9 +27,9 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
feats : numpy.ndarray feats : numpy.ndarray
A 2D numpy ndarray object containing MFCCs. A 2D numpy ndarray object containing MFCCs.
fubm : str fubm : str
A path to full-diagonal UBM file A full-diagonal UBM
ivector_extractor : str ivector_extractor : str
A path to the ivector extractor A path for the ivector extractor
num_gselect : :obj:`int`, optional num_gselect : :obj:`int`, optional
Number of Gaussians to keep per frame. Number of Gaussians to keep per frame.
...@@ -51,7 +51,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -51,7 +51,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
Returns Returns
------- -------
str str
A path to the iVector extractor. A text formatted trained Kaldi IvectorExtractor.
""" """
...@@ -63,6 +63,12 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -63,6 +63,12 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
binary6 = 'ivector-extractor-acc-stats' binary6 = 'ivector-extractor-acc-stats'
binary7 = 'ivector-extractor-est' binary7 = 'ivector-extractor-est'
# Convert full diagonal UBM string to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.fump') as fubmfile:
with open(fubmfile.name, 'wt') as fp:
fp.write(fubm)
# 1. Create Kaldi training data structure # 1. Create Kaldi training data structure
# ToDo: implement Bob's function for that # ToDo: implement Bob's function for that
with tempfile.NamedTemporaryFile(delete=False, suffix='.ark') as arkfile: with tempfile.NamedTemporaryFile(delete=False, suffix='.ark') as arkfile:
...@@ -79,7 +85,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -79,7 +85,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
with tempfile.NamedTemporaryFile(delete=False, suffix='.dubm') as \ with tempfile.NamedTemporaryFile(delete=False, suffix='.dubm') as \
dubmfile, tempfile.NamedTemporaryFile(suffix='.log') as logfile: dubmfile, tempfile.NamedTemporaryFile(suffix='.log') as logfile:
cmd1 += [ cmd1 += [
fubm, fubmfile.name,
dubmfile.name, dubmfile.name,
] ]
pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile) pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile)
...@@ -94,7 +100,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -94,7 +100,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
cmd2 += [ cmd2 += [
'--ivector-dim=' + str(ivector_dim), '--ivector-dim=' + str(ivector_dim),
'--use-weights=' + str(use_weights).lower(), '--use-weights=' + str(use_weights).lower(),
fubm, fubmfile.name,
iefile.name, iefile.name,
] ]
pipe2 = Popen(cmd2, stdin=PIPE, stdout=PIPE, stderr=logfile) pipe2 = Popen(cmd2, stdin=PIPE, stdout=PIPE, stderr=logfile)
...@@ -130,7 +136,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -130,7 +136,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
cmd4 = [binary4] # fgmm-global-gselect-to-post cmd4 = [binary4] # fgmm-global-gselect-to-post
cmd4 += [ cmd4 += [
'--min-post=' + str(min_post), '--min-post=' + str(min_post),
fubm, fubmfile.name,
'ark:' + arkfile.name, 'ark:' + arkfile.name,
'ark:' + gselfile.name, 'ark:' + gselfile.name,
'ark:-', 'ark:-',
...@@ -187,6 +193,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -187,6 +193,7 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
logfile: logfile:
cmd7 += [ cmd7 += [
'--num-threads=4', '--num-threads=4',
'--binary=false',
inModel, inModel,
accfile.name, accfile.name,
estfile.name, estfile.name,
...@@ -203,8 +210,11 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -203,8 +210,11 @@ def ivector_train(feats, fubm, ivector_extractor, num_gselect=20,
shutil.copyfile(inModel, ivector_extractor) shutil.copyfile(inModel, ivector_extractor)
os.unlink(inModel) os.unlink(inModel)
os.unlink(fubmfile.name)
return ivector_extractor # ToDo: covert to the string
with open(ivector_extractor) as fp:
ietxt = fp.read()
return ietxt
def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20, def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20,
...@@ -216,9 +226,9 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -216,9 +226,9 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20,
feats : numpy.ndarray feats : numpy.ndarray
A 2D numpy ndarray object containing MFCCs. A 2D numpy ndarray object containing MFCCs.
fubm : str fubm : str
A path to full-diagonal UBM file A full-diagonal UBM
ivector_extractor : str ivector_extractor : str
A path to global GMM file. An ivector extractor model
num_gselect : :obj:`int`, optional num_gselect : :obj:`int`, optional
Number of Gaussians to keep per frame. Number of Gaussians to keep per frame.
min_post : :obj:`float`, optional min_post : :obj:`float`, optional
...@@ -244,12 +254,24 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -244,12 +254,24 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20,
# ivector-extract --verbose=2 $srcdir/final.ie "$feats" ark,s,cs:- \ # ivector-extract --verbose=2 $srcdir/final.ie "$feats" ark,s,cs:- \
# ark,scp,t:$dir/ivector.JOB.ark,$dir/ivector.JOB.scp || exit 1; # ark,scp,t:$dir/ivector.JOB.ark,$dir/ivector.JOB.scp || exit 1;
# Convert full diagonal UBM string to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.fump') as fubmfile:
with open(fubmfile.name, 'wt') as fp:
fp.write(fubm)
# Convert IvectorExtractor string to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.ie') as iefile:
with open(iefile.name, 'wt') as fp:
fp.write(ivector_extractor)
# Initialize the i-vector extractor using the FGMM input # Initialize the i-vector extractor using the FGMM input
cmd1 = [binary1] # fgmm-global-to-gmm cmd1 = [binary1] # fgmm-global-to-gmm
with tempfile.NamedTemporaryFile(delete=False, suffix='.dubm') as \ with tempfile.NamedTemporaryFile(delete=False, suffix='.dubm') as \
dubmfile, tempfile.NamedTemporaryFile(suffix='.log') as logfile: dubmfile, tempfile.NamedTemporaryFile(suffix='.log') as logfile:
cmd1 += [ cmd1 += [
fubm, fubmfile.name,
dubmfile.name, dubmfile.name,
] ]
pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile) pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile)
...@@ -278,7 +300,7 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -278,7 +300,7 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20,
cmd2 = [binary3] # fgmm-global-gselect-to-post cmd2 = [binary3] # fgmm-global-gselect-to-post
cmd2 += [ cmd2 += [
'--min-post=' + str(min_post), '--min-post=' + str(min_post),
fubm, fubmfile.name,
'ark:-', 'ark:-',
'ark,s,cs:' + gselfile.name, 'ark,s,cs:' + gselfile.name,
'ark:-', 'ark:-',
...@@ -303,7 +325,7 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -303,7 +325,7 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20,
cmd4 = [binary5] # ivector-extract cmd4 = [binary5] # ivector-extract
cmd4 += [ cmd4 += [
ivector_extractor, iefile.name,
'ark:-', 'ark:-',
'ark,s,cs:' + postfile.name, 'ark,s,cs:' + postfile.name,
'ark:-', 'ark:-',
...@@ -320,10 +342,14 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20, ...@@ -320,10 +342,14 @@ def ivector_extract(feats, fubm, ivector_extractor, num_gselect=20,
# read ark from pipe1.stdout # read ark from pipe1.stdout
ret = [mat for name, mat in io.read_vec_flt_ark( ret = [mat for name, mat in io.read_vec_flt_ark(
pipe4.stdout)][0] pipe4.stdout)][0]
os.unlink(fubmfile.name)
os.unlink(iefile.name)
return ret return ret
def plda_train(feats, enroller_file): def plda_train(feats, plda_file, mean_file):
"""Implements Kaldi egs/sre10/v1/plda_scoring.sh """Implements Kaldi egs/sre10/v1/plda_scoring.sh
Parameters Parameters
...@@ -332,13 +358,16 @@ def plda_train(feats, enroller_file): ...@@ -332,13 +358,16 @@ def plda_train(feats, enroller_file):
feats : numpy.ndarray feats : numpy.ndarray
A 2D numpy ndarray object containing MFCCs. A 2D numpy ndarray object containing MFCCs.
enroller_file : str plda_file : str
A path to adapted GMM file A path to the trained PLDA model
mean_file : str
A path to the global PLDA mean file
Returns Returns
------- -------
str str
A path to trained PLDA model. Trained PLDA model and global mean (2D str array)
""" """
# ivector-compute-plda ark:$plda_data_dir/spk2utt \ # ivector-compute-plda ark:$plda_data_dir/spk2utt \
...@@ -349,6 +378,7 @@ def plda_train(feats, enroller_file): ...@@ -349,6 +378,7 @@ def plda_train(feats, enroller_file):
binary2 = 'ivector-compute-plda' binary2 = 'ivector-compute-plda'
binary3 = 'ivector-mean' binary3 = 'ivector-mean'
ret = []
logger.debug("-> PLDA calculation") logger.debug("-> PLDA calculation")
# 1. Create Kaldi training data structure # 1. Create Kaldi training data structure
# import ipdb; ipdb.set_trace() # import ipdb; ipdb.set_trace()
...@@ -382,6 +412,7 @@ def plda_train(feats, enroller_file): ...@@ -382,6 +412,7 @@ def plda_train(feats, enroller_file):
with tempfile.NamedTemporaryFile(suffix='.plda') as pldafile, \ with tempfile.NamedTemporaryFile(suffix='.plda') as pldafile, \
tempfile.NamedTemporaryFile(suffix='.log') as logfile: tempfile.NamedTemporaryFile(suffix='.log') as logfile:
cmd2 += [ cmd2 += [
'--binary=false',
'ark,t:' + spkfile.name, 'ark,t:' + spkfile.name,
'ark:-', 'ark:-',
pldafile.name, pldafile.name,
...@@ -394,7 +425,11 @@ def plda_train(feats, enroller_file): ...@@ -394,7 +425,11 @@ def plda_train(feats, enroller_file):
logtxt = fp.read() logtxt = fp.read()
logger.debug("%s", logtxt) logger.debug("%s", logtxt)
shutil.copyfile(pldafile.name, enroller_file + '.plda') shutil.copyfile(pldafile.name, plda_file)
with open(plda_file) as fp:
pldatxt = fp.read()
ret.append(pldatxt)
# compute global mean # compute global mean
# ivector-normalize-length scp:${plda_ivec_dir}/ivector.scp \ # ivector-normalize-length scp:${plda_ivec_dir}/ivector.scp \
...@@ -415,30 +450,34 @@ def plda_train(feats, enroller_file): ...@@ -415,30 +450,34 @@ def plda_train(feats, enroller_file):
logtxt = fp.read() logtxt = fp.read()
logger.debug("%s", logtxt) logger.debug("%s", logtxt)
shutil.copyfile(meanfile.name, enroller_file + '.plda.mean') shutil.copyfile(meanfile.name, mean_file)
with open(mean_file) as fp:
pldameantxt = fp.read()
ret.append(pldameantxt)
os.unlink(spkfile.name) os.unlink(spkfile.name)
os.unlink(arkfile.name) os.unlink(arkfile.name)
return enroller_file + '.plda' return ret
def plda_enroll(feats, enroller_file): def plda_enroll(feats, pldamean):
"""Implements Kaldi egs/sre10/v1/plda_scoring.sh """Implements Kaldi egs/sre10/v1/plda_scoring.sh
Parameters Parameters
---------- ----------
feats : numpy.ndarray feats : numpy.ndarray
A 2D numpy ndarray object containing iVectors. A 2D numpy ndarray object containing iVectors (of a single speaker).
enroller_file : str pldamean : str
A path to enrolled/adapted GMM file. A path to the global PLDA mean file
Returns Returns
------- -------
str str
A path to enrolled PLDA model. A path to enrolled PLDA model (average iVectors).
""" """
...@@ -447,7 +486,13 @@ def plda_enroll(feats, enroller_file): ...@@ -447,7 +486,13 @@ def plda_enroll(feats, enroller_file):
binary3 = 'ivector-normalize-length' binary3 = 'ivector-normalize-length'
binary4 = 'ivector-subtract-global-mean' binary4 = 'ivector-subtract-global-mean'
binary5 = 'ivector-normalize-length' binary5 = 'ivector-normalize-length'
# Convert full diagonal UBM string to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.mean') as meanfile:
with open(meanfile.name, 'wt') as fp:
fp.write(pldamean)
# ivector-normalize-length scp:$dir/ivector.scp ark:- \| \ # ivector-normalize-length scp:$dir/ivector.scp ark:- \| \
# ivector-mean ark:$data/spk2utt ark:- ark:- ark,t:$dir/num_utts.ark \| \ # ivector-mean ark:$data/spk2utt ark:- ark:- ark,t:$dir/num_utts.ark \| \
# ivector-normalize-length ark:- # ivector-normalize-length ark:-
...@@ -460,18 +505,17 @@ def plda_enroll(feats, enroller_file): ...@@ -460,18 +505,17 @@ def plda_enroll(feats, enroller_file):
tempfile.NamedTemporaryFile( tempfile.NamedTemporaryFile(
delete=False, suffix='.ark') as arkfile, \ delete=False, suffix='.ark') as arkfile, \
open(arkfile.name, 'wb') as f: open(arkfile.name, 'wb') as f:
i = 0
j = 0 # features for a single speaker (said 'spk0')
spkid = 'spk' + str(i) j = 0
spkfile.write(spkid) spkid = 'spk0'
for utt in feats: spkfile.write(spkid)
# print i, j for utt in feats:
spkutt = spkid + 'utt' + str(j) spkutt = spkid + 'utt' + str(j)
io.write_vec_flt(f, utt, key=spkutt.encode('utf-8')) io.write_vec_flt(f, utt, key=spkutt.encode('utf-8'))
spkfile.write(' ' + spkutt) spkfile.write(' ' + spkutt)
j += 1 j += 1
spkfile.write("\n") spkfile.write("\n")
i += 1
cmd1 = [binary1] # ivector-normalize-length cmd1 = [binary1] # ivector-normalize-length
cmd1 += [ cmd1 += [
...@@ -491,7 +535,7 @@ def plda_enroll(feats, enroller_file): ...@@ -491,7 +535,7 @@ def plda_enroll(feats, enroller_file):
] ]
cmd4 = [binary4] # ivector-subtract-global-mean cmd4 = [binary4] # ivector-subtract-global-mean
cmd4 += [ cmd4 += [
enroller_file + '.plda.mean', meanfile.name,
'ark:-', 'ark:-',
'ark:-', 'ark:-',
] ]
...@@ -502,7 +546,7 @@ def plda_enroll(feats, enroller_file): ...@@ -502,7 +546,7 @@ def plda_enroll(feats, enroller_file):
cmd5 += [ cmd5 += [
'ark:-', 'ark:-',
'ark:' + spkarkfile.name, 'ark,t:' + spkarkfile.name,
] ]
pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile) pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile)
...@@ -516,17 +560,23 @@ def plda_enroll(feats, enroller_file): ...@@ -516,17 +560,23 @@ def plda_enroll(feats, enroller_file):
stdout=PIPE, stderr=logfile) stdout=PIPE, stderr=logfile)
pipe5.communicate() pipe5.communicate()
logger.debug("PLDA enrollment DONE ->") logger.debug("PLDA enrollment DONE ->")
# with open(logfile.name) as fp: with open(logfile.name) as fp:
# logtxt = fp.read() logtxt = fp.read()
# logger.debug("%s", logtxt) logger.debug("%s", logtxt)
# get text format
with open(spkarkfile.name) as fp:
ivectortxt = fp.read()
ret = ivectortxt
os.unlink(spkfile.name) os.unlink(spkfile.name)
os.unlink(arkfile.name) os.unlink(arkfile.name)
os.unlink(meanfile.name)
return spkarkfile.name return ret
def plda_score(feats, model, ubm): def plda_score(feats, model, plda, globalmean, smoothing=0):
"""Implements Kaldi egs/sre10/v1/plda_scoring.sh """Implements Kaldi egs/sre10/v1/plda_scoring.sh
Parameters Parameters
...@@ -535,14 +585,20 @@ def plda_score(feats, model, ubm): ...@@ -535,14 +585,20 @@ def plda_score(feats, model, ubm):
A 2D numpy ndarray object containing iVectors. A 2D numpy ndarray object containing iVectors.
model : str model : str
A path to enrolled/adapted PLDA model. A speaker model (average iVectors).
ubm : str plda : str
A path to the PLDA model. A PLDA model.
globalmean : str
A global PLDA mean.
smoothing: float
Factor used in smoothing within-class covariance
(add this factor times between-class covar).
Returns Returns
------- -------
float float
A score. A PLDA score.
""" """
# import ipdb; ipdb.set_trace() # import ipdb; ipdb.set_trace()
# ivector-plda-scoring --normalize-length=true \ # ivector-plda-scoring --normalize-length=true \
...@@ -557,6 +613,25 @@ def plda_score(feats, model, ubm): ...@@ -557,6 +613,25 @@ def plda_score(feats, model, ubm):
# "cat '$trials' | cut -d\ --fields=1,2 |" $scores_dir/plda_scores || \ # "cat '$trials' | cut -d\ --fields=1,2 |" $scores_dir/plda_scores || \
# exit 1; # exit 1;
# Convert to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.spk') as spkfile:
with open(spkfile.name, 'wt') as fp:
fp.write(model)
# Convert to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.ubm') as pldafile:
with open(pldafile.name, 'wt') as fp:
fp.write(plda)
# Convert to a file
with tempfile.NamedTemporaryFile(
delete=False, suffix='.mean') as meanfile:
with open(meanfile.name, 'wt') as fp:
fp.write(globalmean)
logger.debug("-> PLDA scoring") logger.debug("-> PLDA scoring")
# 1. # 1.
...@@ -577,7 +652,7 @@ def plda_score(feats, model, ubm): ...@@ -577,7 +652,7 @@ def plda_score(feats, model, ubm):
cmd2 = [binary3] # ivector-subtract-global-mean cmd2 = [binary3] # ivector-subtract-global-mean
cmd2 += [ cmd2 += [
ubm + '.plda.mean', meanfile.name,
'ark:-', 'ark:-',
'ark:-', 'ark:-',
] ]
...@@ -597,11 +672,11 @@ def plda_score(feats, model, ubm): ...@@ -597,11 +672,11 @@ def plda_score(feats, model, ubm):
# plda smooting # plda smooting
with tempfile.NamedTemporaryFile(delete=False, suffix='.plda') as \ with tempfile.NamedTemporaryFile(delete=False, suffix='.plda') as \
plda, tempfile.NamedTemporaryFile(suffix='.log') as logfile: pldasmooth, tempfile.NamedTemporaryFile(suffix='.log') as logfile:
cmd1 += [ cmd1 += [
'--smoothing=0.0', '--smoothing='+str(smoothing),
ubm + '.plda', pldafile.name,
plda.name, pldasmooth.name,
] ]
pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile) pipe1 = Popen(cmd1, stdin=PIPE, stdout=PIPE, stderr=logfile)
pipe1.communicate() pipe1.communicate()
...@@ -613,8 +688,8 @@ def plda_score(feats, model, ubm): ...@@ -613,8 +688,8 @@ def plda_score(feats, model, ubm):
score, tempfile.NamedTemporaryFile(suffix='.log') as logfile: score, tempfile.NamedTemporaryFile(suffix='.log') as logfile:
cmd4 += [ cmd4 += [
'--normalize-length=true', '--normalize-length=true',
plda.name, pldasmooth.name,
'ark:' + model, 'ark:' + spkfile.name,
'ark:-', 'ark:-',
trials.name, trials.name,
score.name, score.name,
...@@ -634,10 +709,17 @@ def plda_score(feats, model, ubm): ...@@ -634,10 +709,17 @@ def plda_score(feats, model, ubm):
with open(score.name) as fp: with open(score.name) as fp:
scoretxt = fp.readline() scoretxt = fp.readline()
ret = float(scoretxt.split()[2]) if scoretxt.split():
ret = float(scoretxt.split()[2])
else:
ret = -1
os.unlink(plda.name) os.unlink(pldasmooth.name)
os.unlink(trials.name) os.unlink(trials.name)
os.unlink(score.name) os.unlink(score.name)
os.unlink(spkfile.name)
os.unlink(pldafile.name)
os.unlink(meanfile.name)
return ret return ret
...@@ -45,10 +45,10 @@ def test_ubm_full_train(): ...@@ -45,10 +45,10 @@ def test_ubm_full_train():
dubm = bob.kaldi.ubm_train(array, temp_dubm_file, num_gauss=2, dubm = bob.kaldi.ubm_train(array, temp_dubm_file, num_gauss=2,
num_gselect=2, num_iters=2) num_gselect=2, num_iters=2)
# Train small full GMM # Train small full GMM
ubm = bob.kaldi.ubm_full_train(array, dubm, temp_fubm_file, fubm = bob.kaldi.ubm_full_train(array, dubm, temp_fubm_file,
num_gselect=2, num_iters=2) num_gselect=2, num_iters=2)
assert os.path.exists(ubm) assert fubm.find('FullGMM')
def test_ubm_enroll(): def test_ubm_enroll():
......
...@@ -36,7 +36,7 @@ def test_ivector_train(): ...@@ -36,7 +36,7 @@ def test_ivector_train():
ivector = bob.kaldi.ivector_train(array, fubm, temp_ivec_file, ivector = bob.kaldi.ivector_train(array, fubm, temp_ivec_file,
num_gselect=2, ivector_dim=20, num_iters=2) num_gselect=2, ivector_dim=20, num_iters=2)
assert os.path.exists(ivector) assert ivector.find('IvectorExtractor')
def test_ivector_extract(): def test_ivector_extract():
...@@ -72,36 +72,43 @@ def test_ivector_extract(): ...@@ -72,36 +72,43 @@ def test_ivector_extract():
def test_plda_train(): def test_plda_train():
temp_file = bob.io.base.test_utils.temporary_filename() plda_file = bob.io.base.test_utils.temporary_filename()
mean_file = bob.io.base.test_utils.temporary_filename()
features = pkg_resources.resource_filename( features = pkg_resources.resource_filename(
__name__, 'data/feats-mobio.npy') __name__, 'data/feats-mobio.npy')
feats = np.load(features) feats = np.load(features)
# Train PLDA # Train PLDA
plda = bob.kaldi.plda_train(feats, temp_file) plda = bob.kaldi.plda_train(feats, plda_file, mean_file)
assert os.path.exists(temp_file + '.plda') assert plda[0].find('Plda')
assert os.path.exists(temp_file + '.plda.mean') assert os.path.exists(mean_file)
def test_plda_enroll(): def test_plda_enroll():
temp_file = bob.io.base.test_utils.temporary_filename() plda_file = bob.io.base.test_utils.temporary_filename()
mean_file = bob.io.base.test_utils.temporary_filename()
features = pkg_resources.resource_filename( features = pkg_resources.resource_filename(
__name__, 'data/feats-mobio.npy') __name__, 'data/feats-mobio.npy')
feats = np.load(features) feats = np.load(features)
# Train PLDA # Train PLDA
plda = bob.kaldi.plda_enroll(feats, temp_file) plda = bob.kaldi.plda_train(feats, plda_file, mean_file)
assert os.path.exists(plda) # Enroll; plda[0] - PLDA model, plda[1] - PLDA global mean
enrolled = bob.kaldi.plda_enroll(feats[0], plda[1])
assert enrolled.find('spk36')
def test_plda_score(): def test_plda_score():
temp_file = bob.io.base.test_utils.temporary_filename() plda_file = bob.io.base.test_utils.temporary_filename()
mean_file = bob.io.base.test_utils.temporary_filename()
spk_file = bob.io.base.test_utils.temporary_filename()
test_file = pkg_resources.resource_filename( test_file = pkg_resources.resource_filename(
__name__, 'data/test-mobio.ivector') __name__, 'data/test-mobio.ivector')
features = pkg_resources.resource_filename( features = pkg_resources.resource_filename(
...@@ -111,10 +118,10 @@ def test_plda_score(): ...@@ -111,10 +118,10 @@ def test_plda_score():
test_feats = np.loadtxt(test_file) test_feats = np.loadtxt(test_file)
# Train PLDA # Train PLDA
plda = bob.kaldi.plda_train(train_feats, temp_file) plda = bob.kaldi.plda_train(train_feats, plda_file, mean_file)
# Enroll PLDA (average speaker) # Enroll PLDA (for the first speaker)
enrolled = bob.kaldi.plda_enroll(train_feats[0], temp_file) enrolled = bob.kaldi.plda_enroll(train_feats[0], plda[1])
# Score PLDA # Score PLDA
score = bob.kaldi.plda_score(test_feats, enrolled, temp_file) score = bob.kaldi.plda_score(test_feats, enrolled, plda[0], plda[1])
assert np.allclose(score, [-23.9922], 1e-03, 1e-05) assert np.allclose(score, [-23.9922], 1e-03, 1e-05)
...@@ -7,11 +7,11 @@ ...@@ -7,11 +7,11 @@
import bob.kaldi import bob.kaldi
import bob.io.audio import bob.io.audio
import tempfile import tempfile
import os import numpy
======================= ================================
Using Kaldi in Python Speaker recognition evaluation
======================= ================================
MFCC Extraction MFCC Extraction
--------------- ---------------
...@@ -75,33 +75,30 @@ are supported, speakers can be enrolled and scored: ...@@ -75,33 +75,30 @@ are supported, speakers can be enrolled and scored:
>>> print ('%.3f' % score) >>> print ('%.3f' % score)
0.282 0.282
Following guide describes how to run whole speaker recognition experiments: iVector + PLDA training and evaluation
--------------------------------------
1. To run the UBM-GMM with MAP adaptation speaker recognition experiment, run: The implementation is based on Kaldi recipe SRE10_. It includes
ivector extrator training from full-diagonal GMMs, PLDA model
training, and PLDA scoring.
.. code-block:: sh .. doctest::
verify.py -d 'mobio-audio-male' -p 'energy-2gauss' -e 'mfcc-kaldi' -a 'gmm-kaldi' -s exp-gmm-kaldi --groups {dev,eval} -R '/your/work/directory/' -T '/your/temp/directory' -vv
2. To run the ivector+plda speaker recognition experiment, run:
.. code-block:: sh
verify.py -d 'mobio-audio-male' -p 'energy-2gauss' -e 'mfcc-kaldi' -a 'ivector-plda-kaldi' -s exp-ivector-plda-kaldi --groups {dev,eval} -R '/your/work/directory/' -T '/your/temp/directory' -vv
3. Results:
+---------------------------------------------------+--------+--------+ >>> plda_file = tempfile.NamedTemporaryFile()
| Experiment description | EER | HTER | >>> mean_file = tempfile.NamedTemporaryFile()
+---------------------------------------------------+--------+--------+ >>> spk_file = tempfile.NamedTemporaryFile()
| -e 'mfcc-kaldi', -a 'gmm-kadi', 100GMM | 18.53% | 14.52% | >>> test_file = pkg_resources.resource_filename('bob.kaldi', 'test/data/test-mobio.ivector')
+---------------------------------------------------+--------+--------+ >>> features = pkg_resources.resource_filename('bob.kaldi', 'test/data/feats-mobio.npy')
| -e 'mfcc-kaldi', -a 'gmm-kadi', 512GMM | 17.51% | 12.44% | >>> train_feats = numpy.load(features)
+---------------------------------------------------+--------+--------+ >>> test_feats = numpy.loadtxt(test_file)
| -e 'mfcc-kaldi', -a 'ivector-plda-kaldi', 64GMM | 12.26% | 11.97% | >>> # Train PLDA model; plda[0] - PLDA model, plda[1] - global mean
+---------------------------------------------------+--------+--------+ >>> plda = bob.kaldi.plda_train(train_feats, plda_file.name, mean_file.name)
| -e 'mfcc-kaldi', -a 'ivector-plda-kaldi', 256GMM | 11.35% | 11.46% | >>> # Speaker enrollment (calculate average iVectors for the first speaker)
+---------------------------------------------------+--------+--------+ >>> enrolled = bob.kaldi.plda_enroll(train_feats[0], plda[1])
>>> # Calculate PLDA score
>>> score = bob.kaldi.plda_score(test_feats, enrolled, plda[0], plda[1])
>>> print ('%.4f' % score)
-23.9922
.. include:: links.rst .. include:: links.rst
...@@ -22,3 +22,4 @@ ...@@ -22,3 +22,4 @@
.. _zc.buildout: http://pypi.python.org/pypi/zc.buildout/ .. _zc.buildout: http://pypi.python.org/pypi/zc.buildout/
.. _mr.developer: http://pypi.python.org/pypi/mr.developer/ .. _mr.developer: http://pypi.python.org/pypi/mr.developer/
.. _kaldi: http://kaldi-asr.org/ .. _kaldi: http://kaldi-asr.org/
.. _sre10: https://github.com/kaldi-asr/kaldi/tree/master/egs/sre10/v1
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment