Source code for iv_scoring

# -*- coding: utf-8 -*-
#
# This file is part of SIDEKIT.
#
# SIDEKIT is a python package for speaker verification.
# Home page: http://www-lium.univ-lemans.fr/sidekit/
#
# SIDEKIT is a python package for speaker verification.
# Home page: http://www-lium.univ-lemans.fr/sidekit/
#    
# SIDEKIT is free software: you can redistribute it and/or modify
# it under the terms of the GNU LLesser General Public License as 
# published by the Free Software Foundation, either version 3 of the License, 
# or (at your option) any later version.
#
# SIDEKIT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with SIDEKIT.  If not, see <http://www.gnu.org/licenses/>.

__license__ = "LGPL"
__author__ = "Anthony Larcher"
__copyright__ = "Copyright 2014-2015 Anthony Larcher"
__license__ = "LGPL"
__version__ = "1.0"
__maintainer__ = "Anthony Larcher"
__email__ = "anthony.larcher@univ-lemans.fr"
__status__ = "Production"
__docformat__ = 'reStructuredText'

#import os
import numpy as np
import scipy as sp
from sidekit.bosaris import Ndx
from sidekit.bosaris import Scores
from sidekit.statserver import StatServer
import logging


[docs]def cosine_scoring(enroll, test, ndx, wccn = 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 :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' # Remove missing models and test segments clean_ndx = ndx.filter(enroll.modelset , test.segset, True) # Align StatServers to match the clean_ndx enroll.align_models(clean_ndx.modelset) test.align_segments(clean_ndx.segset) if wccn is not None: enroll.rotate_stat1(wccn) if enroll != test: test.rotate_stat1(wccn) # Cosine scoring enroll.norm_stat1() if enroll != test: test.norm_stat1() S = np.dot(enroll.stat1, test.stat1.transpose()) Score = Scores() Score.scoremat = S Score.modelset = clean_ndx.modelset Score.segset = clean_ndx.segset Score.scoremask = clean_ndx.trialmask return Score
[docs]def mahalanobis_scoring(enroll, test, ndx, M): """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 :parm 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 :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' # Remove missing models and test segments clean_ndx = ndx.filter(enroll.modelset , test.segset, True) # Align StatServers to match the clean_ndx enroll.align_models(clean_ndx.modelset) test.align_segments(clean_ndx.segset) # Mahalanobis scoring S = np.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 * np.sum(np.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
[docs]def two_covariance_scoring(enroll, test, ndx, W, B): """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 :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' # Remove missing models and test segments clean_ndx = ndx.filter(enroll.modelset , test.segset, True) # Align StatServers to match the clean_ndx enroll.align_models(clean_ndx.modelset) test.align_segments(clean_ndx.segset) # Two covariance scoring scoring S = np.zeros((enroll.modelset.shape[0], test.segset.shape[0])) iW = sp.linalg.inv(W) iB = sp.linalg.inv(B) G = reduce(np.dot, [iW, sp.linalg.inv(iB + 2*iW), iW]) H = reduce(np.dot, [iW, sp.linalg.inv(iB + iW), iW]) s2 = np.sum(np.dot(enroll.stat1, H) * enroll.stat1 ,axis=1) s3 = np.sum(np.dot(test.stat1, H) * test.stat1 , axis=1) for ii in range(enroll.modelset.shape[0]): A = enroll.stat1[ii, :] + test.stat1 s1 = np.sum(np.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
[docs]def PLDA_scoring(enroll, test, ndx, mu, F, G, Sigma): """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 G: the within-class co-variance matrix of the PLDA :param Sigma: the residual covariance matrix :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' # Remove missing models and test segments clean_ndx = ndx.filter(enroll.modelset , test.segset, True) # Align StatServers to match the clean_ndx enroll.align_models(clean_ndx.modelset) test.align_segments(clean_ndx.segset) # Center the i-vectors around the PLDA mean enroll.center_stat1(mu) test.center_stat1(mu) # If models are not unique, compute the mean per model, display a warning if not np.unique(enroll.modelset).shape == enroll.modelset.shape: logging.warning("Enrollment models are not unique, average i-vectors") enroll = enroll.mean_stat_per_model() # Compute temporary matrices invSigma = np.linalg.inv(Sigma) I_iv = np.eye(mu.shape[0], dtype='float') I_ch = np.eye(G.shape[1], dtype='float') I_spk = np.eye(F.shape[1], dtype='float') A = np.linalg.inv(G.T.dot(invSigma).dot(G) + I_ch) B = F.T.dot(invSigma).dot(I_iv - G.dot(A).dot(G.T).dot(invSigma)) K = B.dot(F) K1 = np.linalg.inv(K + I_spk) K2 = np.linalg.inv(2*K + I_spk) # Compute the Gaussian distribution constant alpha1 = np.linalg.slogdet(K1)[1] alpha2 = np.linalg.slogdet(K2)[1] constant = alpha2 / 2.0 - alpha1; # Compute verification scores score = sidekit.Scores() score.scoremat = np.zeros(ndx.trialmask.shape) score.modelset = clean_ndx.modelset score.segset = clean_ndx.segset score.scoremask = ndx.trialmask # Project data in the space that maximizes the speaker separability test_tmp = B.dot(test.stat1.T) enroll_tmp = B.dot(enroll.stat1.T) # Loop on the models for model_idx in range(enroll.modelset.shape[0]): s2 = enroll_tmp[:, model_idx].dot(K1).dot(enroll_tmp[:, model_idx]) mod_plus_test_seg = test_tmp + np.atleast_2d(enroll_tmp[:, model_idx]).T tmp1 = test_tmp.T.dot(K1) tmp2 = mod_plus_test_seg.T.dot(K2) for seg_idx in range(test.segset.shape[0]): s1 = tmp1[seg_idx, :].dot(test_tmp[:, seg_idx]) s3 = tmp2[seg_idx, :].dot(mod_plus_test_seg[:, seg_idx]) score.scoremat[model_idx, seg_idx] = ( s3 - s1 - s2)/2. + constant return score