def gaussian_backend_test(test_ss, params, diag=False, compute_llr=True): """ Process data through a Gaussian-Backend which parameters (mean and variance) have been estimated using Gaussian_Backend_Train. If compute_llr is set to true, return the log-likelihood ratio, if not, return rthe log-likelihood on each Gaussian distrbution. Default is True. :param test_ss: a StatServer which stat1 are vectors to classify :param params: Gaussian Backend parameters, a tupple of mean, covariance and constante computed with Gaussian_Backend_Train :param diag: boolean, if true: use the diagonal version of the covariance matrix, if not the full version :param compute_llr: boolean, if true, return the log-likelihood ratio, if not, return rthe log-likelihood on each Gaussian distrbution. """ gb_mean, gb_sigma, gb_cst = params scores = Scores() scores.modelset = gb_mean.modelset scores.segset = test_ss.segset scores.scoremat = numpy.ones( (gb_mean.modelset.shape[0], test_ss.segset.shape[0])) scores.scoremask = numpy.ones(scores.scoremat.shape, dtype='bool') if diag: gb_gmm = Mixture() gb_gmm.w = numpy.ones(gb_mean.modelset.shape[0], dtype='float') / gb_mean.modelset.shape[0] gb_gmm.mu = gb_mean.stat1 if gb_sigma.ndim == 2: gb_gmm.invcov = numpy.tile(1 / numpy.diag(gb_sigma), (gb_mean.modelset.shape[0], 1)) elif gb_sigma.ndim == 2: gb_gmm.invcov = numpy.tile(1. / gb_sigma, (gb_mean.modelset.shape[0], 1)) gb_gmm._compute_all() scores.scoremat = gb_gmm.compute_log_posterior_probabilities( test_ss.stat1).T else: assert gb_sigma.ndim == 2 scores.scoremat *= gb_cst inv_sigma = numpy.linalg.inv(gb_sigma) # Compute scores for all trials per language for lang in range(gb_mean.modelset.shape[0]): scores.scoremat[lang, :] -= 0.5 * (gb_mean.stat1[lang, :].dot( inv_sigma).dot(gb_mean.stat1[lang, :].T) - 2 * numpy.sum( test_ss.stat1.dot(inv_sigma) * gb_mean.stat1[lang, :], axis=1) + numpy.sum( test_ss.stat1.dot(inv_sigma) * test_ss.stat1, axis=1)) if compute_llr: scores.scoremat = compute_log_likelihood_ratio(scores.scoremat) assert scores.validate() return scores
def two_covariance_scoring(enroll, test, ndx, W, B, check_missing=True): """Compute the 2-covariance scores between to sets of vectors. The list of trials to perform is given in an Ndx object. Within and between class co-variance matrices have to be pre-computed. :param enroll: a StatServer in which stat1 are i-vectors :param test: a StatServer in which stat1 are i-vectors :param ndx: an Ndx object defining the list of trials to perform :param W: the within-class co-variance matrix to consider :param B: the between-class co-variance matrix to consider :param check_missing: boolean, default is True, set to False not to check missing models :return: a score object """ assert isinstance(enroll, StatServer), 'First parameter should be a directory' assert isinstance(test, StatServer), 'Second parameter should be a StatServer' assert isinstance(ndx, Ndx), 'Third parameter should be an Ndx' assert enroll.stat1.shape[1] == test.stat1.shape[ 1], 'I-vectors dimension mismatch' assert enroll.stat1.shape[1] == W.shape[ 0], 'I-vectors and co-variance matrix dimension mismatch' assert enroll.stat1.shape[1] == B.shape[ 0], 'I-vectors and co-variance matrix dimension mismatch' # If models are not unique, compute the mean per model, display a warning if not numpy.unique(enroll.modelset).shape == enroll.modelset.shape: logging.warning("Enrollment models are not unique, average i-vectors") enroll = enroll.mean_stat_per_model() # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll, test, ndx) else: clean_ndx = ndx # Two covariance scoring scoring S = numpy.zeros((enroll.modelset.shape[0], test.segset.shape[0])) iW = scipy.linalg.inv(W) iB = scipy.linalg.inv(B) G = reduce(numpy.dot, [iW, scipy.linalg.inv(iB + 2 * iW), iW]) H = reduce(numpy.dot, [iW, scipy.linalg.inv(iB + iW), iW]) s2 = numpy.sum(numpy.dot(enroll.stat1, H) * enroll.stat1, axis=1) s3 = numpy.sum(numpy.dot(test.stat1, H) * test.stat1, axis=1) for ii in range(enroll.modelset.shape[0]): A = enroll.stat1[ii, :] + test.stat1 s1 = numpy.sum(numpy.dot(A, G) * A, axis=1) S[ii, :] = s1 - s3 - s2[ii] score = Scores() score.scoremat = S score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask return score
def cosine_scoring(enroll, test, ndx, wccn=None, check_missing=True): """Compute the cosine similarities between to sets of vectors. The list of trials to perform is given in an Ndx object. :param enroll: a StatServer in which stat1 are i-vectors :param test: a StatServer in which stat1 are i-vectors :param ndx: an Ndx object defining the list of trials to perform :param wccn: numpy.ndarray, if provided, the i-vectors are normalized by using a Within Class Covariance Matrix :param check_missing: boolean, if True, check that all models and segments exist :return: a score object """ assert isinstance(enroll, StatServer), 'First parameter should be a StatServer' assert isinstance(test, StatServer), 'Second parameter should be a StatServer' assert isinstance(ndx, Ndx), 'Third parameter should be an Ndx' enroll_copy = copy.deepcopy(enroll) test_copy = copy.deepcopy(test) # If models are not unique, compute the mean per model, display a warning #if not numpy.unique(enroll_copy.modelset).shape == enroll_copy.modelset.shape: # logging.warning("Enrollment models are not unique, average i-vectors") # enroll_copy = enroll_copy.mean_stat_per_model() # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll_copy, test_copy, ndx) else: clean_ndx = ndx if wccn is not None: enroll_copy.rotate_stat1(wccn) if enroll_copy != test_copy: test_copy.rotate_stat1(wccn) # Cosine scoring enroll_copy.norm_stat1() if enroll_copy != test_copy: test_copy.norm_stat1() s = numpy.dot(enroll_copy.stat1, test_copy.stat1.transpose()) score = Scores() score.scoremat = s score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask return score
def mahalanobis_scoring(enroll, test, ndx, m, check_missing=True): """Compute the mahalanobis distance between to sets of vectors. The list of trials to perform is given in an Ndx object. :param enroll: a StatServer in which stat1 are i-vectors :param test: a StatServer in which stat1 are i-vectors :param ndx: an Ndx object defining the list of trials to perform :param m: mahalanobis matrix as a ndarray :param check_missing: boolean, default is True, set to False not to check missing models :return: a score object """ assert isinstance(enroll, StatServer), 'First parameter should be a StatServer' assert isinstance(test, StatServer), 'Second parameter should be a StatServer' assert isinstance(ndx, Ndx), 'Third parameter should be an Ndx' assert enroll.stat1.shape[1] == test.stat1.shape[ 1], 'I-vectors dimension mismatch' assert enroll.stat1.shape[1] == m.shape[ 0], 'I-vectors and Mahalanobis matrix dimension mismatch' # If models are not unique, compute the mean per model, display a warning if not numpy.unique(enroll.modelset).shape == enroll.modelset.shape: logging.warning("Enrollment models are not unique, average i-vectors") enroll = enroll.mean_stat_per_model() # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll, test, ndx) else: clean_ndx = ndx # Mahalanobis scoring s = numpy.zeros((enroll.modelset.shape[0], test.segset.shape[0])) for i in range(enroll.modelset.shape[0]): diff = enroll.stat1[i, :] - test.stat1 s[i, :] = -0.5 * numpy.sum(numpy.dot(diff, m) * diff, axis=1) score = Scores() score.scoremat = s score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask return score
def svm_scoring(svm_filename_structure, test_sv, ndx, num_thread=1): """Compute scores for SVM verification on multiple threads (two classes only as implementeed at the moment) :param svm_filename_structure: structure of the filename where to load the SVM models :param test_sv: StatServer object of super-vectors. stat0 are set to 1 and stat1 are the super-vector to classify :param ndx: Ndx object of the trials to perform :param num_thread: number of thread to launch in parallel :return: a Score object. """ # Remove missing models and test segments existing_models, model_idx = sidekit.sv_utils.check_file_list( ndx.modelset, svm_filename_structure) clean_ndx = ndx.filter(existing_models, test_sv.segset, True) score = Scores() score.scoremat = numpy.zeros(clean_ndx.trialmask.shape) score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask tmp = multiprocessing.Array(ctypes.c_double, score.scoremat.size) score.scoremat = numpy.ctypeslib.as_array(tmp.get_obj()) score.scoremat = score.scoremat.reshape(score.modelset.shape[0], score.segset.shape[0]) # Split the list of segment to process for multi-threading los = numpy.array_split(numpy.arange(clean_ndx.segset.shape[0]), num_thread) jobs = [] for idx in los: p = multiprocessing.Process(target=svm_scoring_singleThread, args=(svm_filename_structure, test_sv, clean_ndx, score, idx)) jobs.append(p) p.start() for p in jobs: p.join() return score
def PLDA_scoring_uncertainty(enroll, test, ndx, mu, F, Sigma, p_known=0.0, scaling_factor=1., test_uncertainty=None, Vtrans=None, check_missing=True): """ :param enroll: :param test: :param ndx: :param mu: :param F: :param Sigma: :param p_known: :param scaling_factor: :param test_uncertainty: :param Vtrans: :param check_missing: :return: """ assert isinstance(enroll, StatServer), 'First parameter should be a StatServer' assert isinstance(test, StatServer), 'Second parameter should be a StatServer' assert isinstance(ndx, Ndx), 'Third parameter should be an Ndx' assert enroll.stat1.shape[1] == test.stat1.shape[1], 'I-vectors dimension mismatch' assert enroll.stat1.shape[1] == F.shape[0], 'I-vectors and co-variance matrix dimension mismatch' assert enroll.stat1.shape[1] == G.shape[0], 'I-vectors and co-variance matrix dimension mismatch' enroll_ctr = copy.deepcopy(enroll) test_ctr = copy.deepcopy(test) # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll_ctr, test_ctr, ndx) else: clean_ndx = ndx # Center the i-vectors around the PLDA mean enroll_ctr.center_stat1(mu) test_ctr.center_stat1(mu) # Align StatServers to match the clean_ndx enroll_ctr.align_models_average(clean_ndx.modelset) test_ctr.align_segments(clean_ndx.segset) # Compute constant component of the PLDA distribution scoremat = numpy.zeros((enroll_ctr.stat1.shape[0], test_ctr.stat1.shape[0]), dtype='float') invSigma = scipy.linalg.inv(Sigma) K1 = scipy.linalg.inv(numpy.eye(F.shape[1]) + F.T.dot(invSigma * scaling_factor).dot(F)) FK1Ft = F.dot(K1).dot(F.T) Xtilda_e = FK1Ft.dot(invSigma * scaling_factor).dot(enroll_ctr.stat1.T).T for t in range(test_ctr.stat1.shape[0]): xt = test_ctr.stat1[t,:] Pr = numpy.eye(F.shape[1]) - numpy.outer(test.stat1[t, :],test.stat1[t, :]) Cunc = Pr.dot(Vtrans.transpose()).dot(numpy.diag(test_uncertainty[t, :]).dot(Vtrans)).dot(Pr) prec_den = scipy.linalg.inv(F.dot(F.T) + Sigma + Cunc) denom = -0.5 * xt.dot(prec_den).dot(xt) +0.5 * numpy.linalg.slogdet(prec_den)[1] prec_num = scipy.linalg.inv(FK1Ft+Sigma+Cunc) Xec = Xtilda_e - xt numer = -0.5 * numpy.einsum('ij, ji->i', Xec.dot(prec_num), Xec.T) + 0.5 * numpy.linalg.slogdet(prec_num)[1] scoremat[:, t] = scaling_factor * (numer - denom) # Compute verification scores score = Scores() score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask score.scoremat = scoremat # Case of open-set identification, we compute the log-likelihood # by taking into account the probability of having a known impostor # or an out-of set class if p_known != 0: N = score.scoremat.shape[0] open_set_scores = numpy.empty(score.scoremat.shape) tmp = numpy.exp(score.scoremat) for ii in range(N): # open-set term open_set_scores[ii, :] = score.scoremat[ii, :] \ - numpy.log(p_known * tmp[~(numpy.arange(N) == ii)].sum(axis=0) / (N - 1) + (1 - p_known)) score.scoremat = open_set_scores return score
def fast_PLDA_scoring(enroll, test, ndx, mu, F, Sigma, test_uncertainty=None, Vtrans=None, p_known=0.0, scaling_factor=1., check_missing=True): """Compute the PLDA scores between to sets of vectors. The list of trials to perform is given in an Ndx object. PLDA matrices have to be pre-computed. i-vectors are supposed to be whitened before. :param enroll: a StatServer in which stat1 are i-vectors :param test: a StatServer in which stat1 are i-vectors :param ndx: an Ndx object defining the list of trials to perform :param mu: the mean vector of the PLDA gaussian :param F: the between-class co-variance matrix of the PLDA :param Sigma: the residual covariance matrix :param p_known: probability of having a known speaker for open-set identification case (=1 for the verification task and =0 for the closed-set case) :param check_missing: boolean, if True, check that all models and segments exist :return: a score object """ # assert isinstance(enroll, StatServer), 'First parameter should be a StatServer' # assert isinstance(test, StatServer), 'Second parameter should be a StatServer' # assert isinstance(ndx, Ndx), 'Third parameter should be an Ndx' # assert enroll.stat1.shape[1] == test.stat1.shape[1], 'I-vectors dimension mismatch' # assert enroll.stat1.shape[1] == F.shape[0], 'I-vectors and co-variance matrix dimension mismatch' # assert enroll.stat1.shape[1] == G.shape[0], 'I-vectors and co-variance matrix dimension mismatch' enroll_ctr = copy.deepcopy(enroll) test_ctr = copy.deepcopy(test) # If models are not unique, compute the mean per model, display a warning if not numpy.unique(enroll_ctr.modelset).shape == enroll_ctr.modelset.shape: logging.warning("Enrollment models are not unique, average i-vectors") enroll_ctr = enroll_ctr.mean_stat_per_model() # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll_ctr, test_ctr, ndx) else: clean_ndx = ndx # Center the i-vectors around the PLDA mean enroll_ctr.center_stat1(mu) test_ctr.center_stat1(mu) # If models are not unique, compute the mean per model, display a warning if not numpy.unique(enroll_ctr.modelset).shape == enroll_ctr.modelset.shape: logging.warning("Enrollment models are not unique, average i-vectors") enroll_ctr = enroll_ctr.mean_stat_per_model() # Compute constant component of the PLDA distribution invSigma = scipy.linalg.inv(Sigma) I_spk = numpy.eye(F.shape[1], dtype='float') K = F.T.dot(invSigma * scaling_factor).dot(F) K1 = scipy.linalg.inv(K + I_spk) K2 = scipy.linalg.inv(2 * K + I_spk) # Compute the Gaussian distribution constant alpha1 = numpy.linalg.slogdet(K1)[1] alpha2 = numpy.linalg.slogdet(K2)[1] plda_cst = alpha2 / 2.0 - alpha1 # Compute intermediate matrices Sigma_ac = numpy.dot(F, F.T) Sigma_tot = Sigma_ac + Sigma Sigma_tot_inv = scipy.linalg.inv(Sigma_tot) Tmp = numpy.linalg.inv(Sigma_tot - Sigma_ac.dot(Sigma_tot_inv).dot(Sigma_ac)) Phi = Sigma_tot_inv - Tmp Psi = Sigma_tot_inv.dot(Sigma_ac).dot(Tmp) # Compute the different parts of PLDA score model_part = 0.5 * numpy.einsum('ij, ji->i', enroll_ctr.stat1.dot(Phi), enroll_ctr.stat1.T) seg_part = 0.5 * numpy.einsum('ij, ji->i', test_ctr.stat1.dot(Phi), test_ctr.stat1.T) # Compute verification scores score = Scores() score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask score.scoremat = model_part[:, numpy.newaxis] + seg_part + plda_cst score.scoremat += enroll_ctr.stat1.dot(Psi).dot(test_ctr.stat1.T) score.scoremat *= scaling_factor # Case of open-set identification, we compute the log-likelihood # by taking into account the probability of having a known impostor # or an out-of set class if p_known != 0: N = score.scoremat.shape[0] open_set_scores = numpy.empty(score.scoremat.shape) tmp = numpy.exp(score.scoremat) for ii in range(N): # open-set term open_set_scores[ii, :] = score.scoremat[ii, :] \ - numpy.log(p_known * tmp[~(numpy.arange(N) == ii)].sum(axis=0) / (N - 1) + (1 - p_known)) score.scoremat = open_set_scores return score
def full_PLDA_scoring(enroll, test, ndx, mu, F, G, Sigma, p_known=0.0, scaling_factor=1., check_missing=True): """Compute PLDA scoring :param enroll: a StatServer in which stat1 are i-vectors :param test: a StatServer in which stat1 are i-vectors :param ndx: an Ndx object defining the list of trials to perform :param mu: the mean vector of the PLDA gaussian :param F: the between-class co-variance matrix of the PLDA :param G: the within-class co-variance matrix of the PLDA :param Sigma: the residual covariance matrix :param p_known: probability of having a known speaker for open-set identification case (=1 for the verification task and =0 for the closed-set case) :param check_missing: boolean, default is True, set to False not to check missing models """ enroll_copy = copy.deepcopy(enroll) test_copy = copy.deepcopy(test) # If models are not unique, compute the mean per model, display a warning # if not numpy.unique(enroll_copy.modelset).shape == enroll_copy.modelset.shape: # logging.warning("Enrollment models are not unique, average i-vectors") # enroll_copy = enroll_copy.mean_stat_per_model() # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll_copy, test_copy, ndx) else: clean_ndx = ndx # Center the i-vectors around the PLDA mean enroll_copy.center_stat1(mu) test_copy.center_stat1(mu) # Compute temporary matrices invSigma = scipy.linalg.inv(Sigma) I_iv = numpy.eye(mu.shape[0], dtype='float') I_ch = numpy.eye(G.shape[1], dtype='float') I_spk = numpy.eye(F.shape[1], dtype='float') A = numpy.linalg.inv(G.T.dot(invSigma * scaling_factor).dot(G) + I_ch) # keep numpy as interface are different B = F.T.dot(invSigma * scaling_factor).dot(I_iv - G.dot(A).dot(G.T).dot(invSigma * scaling_factor)) K = B.dot(F) K1 = scipy.linalg.inv(K + I_spk) K2 = scipy.linalg.inv(2 * K + I_spk) # Compute the Gaussian distribution constant alpha1 = numpy.linalg.slogdet(K1)[1] alpha2 = numpy.linalg.slogdet(K2)[1] constant = alpha2 / 2.0 - alpha1 # Compute verification scores score = Scores() score.scoremat = numpy.zeros(clean_ndx.trialmask.shape) score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask # Project data in the space that maximizes the speaker separability test_tmp = B.dot(test_copy.stat1.T) enroll_tmp = B.dot(enroll_copy.stat1.T) # score qui ne dépend que du segment tmp1 = test_tmp.T.dot(K1) # Compute the part of the score that is only dependent on the test segment S1 = numpy.empty(test_copy.segset.shape[0]) for seg_idx in range(test_copy.segset.shape[0]): S1[seg_idx] = tmp1[seg_idx, :].dot(test_tmp[:, seg_idx])/2. # Compute the part of the score that depends only on the model (S2) and on both model and test segment S2 = numpy.empty(enroll_copy.modelset.shape[0]) for model_idx in range(enroll_copy.modelset.shape[0]): mod_plus_test_seg = test_tmp + numpy.atleast_2d(enroll_tmp[:, model_idx]).T tmp2 = mod_plus_test_seg.T.dot(K2) S2[model_idx] = enroll_tmp[:, model_idx].dot(K1).dot(enroll_tmp[:, model_idx])/2. score.scoremat[model_idx, :] = numpy.einsum("ij, ji->i", tmp2, mod_plus_test_seg)/2. score.scoremat += constant - (S1 + S2[:, numpy.newaxis]) score.scoremat *= scaling_factor # Case of open-set identification, we compute the log-likelihood # by taking into account the probability of having a known impostor # or an out-of set class if p_known != 0: N = score.scoremat.shape[0] open_set_scores = numpy.empty(score.scoremat.shape) tmp = numpy.exp(score.scoremat) for ii in range(N): # open-set term open_set_scores[ii, :] = score.scoremat[ii, :] \ - numpy.log(p_known * tmp[~(numpy.arange(N) == ii)].sum(axis=0) / (N - 1) + (1 - p_known)) score.scoremat = open_set_scores return score
def gmm_scoring(ubm, enroll, ndx, feature_server, num_thread=1): """Compute log-likelihood ratios for sequences of acoustic feature frames between a Universal Background Model (UBM) and a list of Gaussian Mixture Models (GMMs) which only mean vectors differ from the UBM. :param ubm: a Mixture object used to compute the denominator of the likelihood ratios :param enroll: a StatServer object which stat1 attribute contains mean super-vectors of the GMMs to use to compute the numerator of the likelihood ratios. :param ndx: an Ndx object which define the list of trials to compute :param feature_server: a FeatureServer object to load the features :param num_thread: number of thread to launch in parallel :return: a Score object. """ assert isinstance(ubm, Mixture), 'First parameter should be a Mixture' assert isinstance(enroll, StatServer), 'Second parameter should be a StatServer' assert isinstance(ndx, Ndx), 'Third parameter should be a Ndx' assert isinstance( feature_server, FeaturesServer), 'Fourth parameter should be a FeatureServer' # Remove missing models and test segments if feature_server.features_extractor is None: existing_test_seg, test_seg_idx = sidekit.sv_utils.check_file_list( ndx.segset, feature_server.feature_filename_structure) clean_ndx = ndx.filter(enroll.modelset, existing_test_seg, True) elif feature_server.features_extractor.audio_filename_structure is not None: existing_test_seg, test_seg_idx = \ sidekit.sv_utils.check_file_list(ndx.segset, feature_server.features_extractor.audio_filename_structure) clean_ndx = ndx.filter(enroll.modelset, existing_test_seg, True) else: clean_ndx = ndx s = numpy.zeros(clean_ndx.trialmask.shape) dims = s.shape with warnings.catch_warnings(): warnings.simplefilter('ignore', RuntimeWarning) tmp_stat1 = multiprocessing.Array(ctypes.c_double, s.size) s = numpy.ctypeslib.as_array(tmp_stat1.get_obj()) s = s.reshape(dims) # Split the list of segment to process for multi-threading los = numpy.array_split(numpy.arange(clean_ndx.segset.shape[0]), num_thread) jobs = [] for idx in los: p = multiprocessing.Process(target=gmm_scoring_singleThread, args=(ubm, enroll, ndx, feature_server, s, idx)) jobs.append(p) p.start() for p in jobs: p.join() score = Scores() score.scoremat = s score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask return score
def cosine_scoring(enroll, test, ndx, wccn=None, check_missing=True, device=None): """Compute the cosine similarities between to sets of vectors. The list of trials to perform is given in an Ndx object. :param enroll: a StatServer in which stat1 are i-vectors :param test: a StatServer in which stat1 are i-vectors :param ndx: an Ndx object defining the list of trials to perform :param wccn: numpy.ndarray, if provided, the i-vectors are normalized by using a Within Class Covariance Matrix :param check_missing: boolean, if True, check that all models and segments exist :return: a score object """ assert isinstance(enroll, StatServer), 'First parameter should be a StatServer' assert isinstance(test, StatServer), 'Second parameter should be a StatServer' assert isinstance(ndx, Ndx), 'Third parameter should be an Ndx' enroll_copy = copy.deepcopy(enroll) test_copy = copy.deepcopy(test) # If models are not unique, compute the mean per model, display a warning #if not numpy.unique(enroll_copy.modelset).shape == enroll_copy.modelset.shape: # logging.warning("Enrollment models are not unique, average i-vectors") # enroll_copy = enroll_copy.mean_stat_per_model() # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll_copy, test_copy, ndx) else: clean_ndx = ndx if wccn is not None: enroll_copy.rotate_stat1(wccn) if enroll_copy != test_copy: test_copy.rotate_stat1(wccn) # Cosine scoring enroll_copy.norm_stat1() if enroll_copy != test_copy: test_copy.norm_stat1() s_size_in_bytes = enroll_copy.stat1.shape[0] * test_copy.stat1.shape[0] * 4 if device == None: device = torch.device("cuda:0" if torch.cuda.is_available() and s_size_in_bytes < 3e9 else "cpu") else: device = device if torch.cuda.is_available( ) and s_size_in_bytes < 3e9 else torch.device("cpu") score = Scores() score.scoremat = torch.einsum( 'ij,kj', torch.FloatTensor(enroll_copy.stat1).to(device), torch.FloatTensor(test_copy.stat1).to(device)).cpu().numpy() score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = clean_ndx.trialmask return score
def jfa_scoring(ubm, enroll, test, ndx, mean, sigma, V, U, D, batch_size=100, num_thread=1, check_missing=True): """Compute a verification score as a channel point estimate of the log-likelihood ratio. Detail of this scoring can be found in [Glembeck09]. [Glembek09] Ondrej Glembek, Lukas Burget, Najim Dehak, Niko Brummer, and Patrick Kenny, "Comparison of scoring methods used in speaker recognition with joint factor analysis." in Acoustics, Speech and Signal Processing, 2009. ICASSP 2009. IEEE International Conference on. IEEE, 2009. Note that input statistics should not be whitened as it is done within this function. :param ubm: the Mixture object used to compute sufficient statistics :param enroll: a StatServer object which contains zero- and first-order statistics. :param test: a StatServer object which contains zero- and first-order statistics. :param ndx: an Ndx object which trial mask will be copied into the output Scores object :param mean: mean vector of the JFA model :param sigma: residual covariance vector of the JFA model :param V: between class covariance matrix of the JFA model :param U: within class covariance matrix of the JFA model :param D: MAP covariance matrix for the JFA model :param batch_size: size of the batch to reduce memory footprint :param num_thread: number of parallel process to run :param check_missing: boolean, if True, check that all model exist :return: a Scores object """ assert isinstance(ubm, Mixture), '1st parameter must be a Mixture' assert isinstance(enroll, StatServer), '2nd parameter must be a StatServer' assert isinstance(test, StatServer), '3rd parameter must be a StatServer' assert isinstance(ndx, Ndx), '4th parameter shomustuld be a Ndx' # Remove missing models and test segments if check_missing: clean_ndx = _check_missing_model(enroll, test, ndx) else: clean_ndx = ndx print("taille de clean_ndx.trial_mask = {}".format( clean_ndx.trialmask.shape)) # Sum enrolment statistics per model in case of multi-session enroll = enroll.sum_stat_per_model()[0] # Whiten enroll and test statistics enroll.whiten_stat1(ubm.get_mean_super_vector(), ubm.get_invcov_super_vector()) test.whiten_stat1(ubm.get_mean_super_vector(), ubm.get_invcov_super_vector()) # Estimate Vy and DZ from the enrollment trn_y, trn_x, trn_z = enroll.estimate_hidden(mean, sigma, V, U, D, batch_size=batch_size, num_thread=num_thread) M = ((trn_y.stat1.dot(V.T)) + (trn_z.stat1 * D)) # Estimate Ux from the test tmp = copy.deepcopy(test) test_y, test_x, test_z = tmp.estimate_hidden(mean, sigma, None, U, None, batch_size, num_thread) # remove Ux weighted from the test statistics Ux = copy.deepcopy(test) Ux.stat1 = test_x.stat1.dot(U.T) test = test.subtract_weighted_stat1(Ux) # sum zero-order statistics for each test segment test_stat0_sum = test.stat0.sum(axis=1) # Compute score as the dot product of the enrollment supervector and the first # order statistics divided by the sum of the zero-order stats scores = Scores() scores.modelset = enroll.modelset scores.segset = test.segset scores.scoremask = clean_ndx.trialmask scores.scoremat = M.dot((test.stat1 / test_stat0_sum[:, None]).T) return scores