import numpy
import utils
import scipy.linalg
import time
import copy
from readout import *
from lsqnonneg import lsqnonneg
import pdb
class LinearClassification(Readout):
def __init__(self, nClasses = 2, addBias=True):
super(LinearClassification, self).__init__()
self.nClasses = nClasses
self.addBias = addBias
self.time = -1.0
def train(self, trainSet_X, trainSet_Y):
'''train linear classifier'''
t0 = time.time()
trainSet_X = numpy.array(trainSet_X)
nDim = trainSet_X.shape[1]
nSamples = len(trainSet_Y)
if nSamples != trainSet_X.shape[0]:
s='Number of training samples (%d) '\
'is different from number of target values (%d)!\n' % (trainSet_X.shape[0], nSamples)
raise ReadoutException(s)
# 1. Convert the target values into range 1:nClasses => indexedY
(self.uniqueY, indexedY) = unique_index(trainSet_Y)
n = len(self.uniqueY);
if n != self.nClasses:
s='Number of actual class values (%d) is different from this.nClasses = %d!\n' % (len(self.uniqueY), self.nClasses)
raise ReadoutException(s)
# 2. make -1/+1 vector representation
Y = numpy.zeros((nSamples,n))
for i in range(n):
l = numpy.where(numpy.asarray(indexedY) == i)[0]
for j in l:
Y[j,i] = 1
Y = numpy.asarray(Y*2-1);
if self.addBias:
trainSet_X = numpy.concatenate((trainSet_X, numpy.ones((trainSet_X.shape[0],1))), 1)
# 3. Find least squares solutions
(Q, R)=scipy.linalg.qr(trainSet_X, overwrite_a=1)
if self.nClasses > 2: # now we solve the nClasses > 2 regression problems
self.W = numpy.zeros((nDim+1, self.nClasses))
for i in range(self.nClasses):
self.W[:,i] = scipy.linalg.lstsq(R, numpy.dot(Q.T, Y[:,i]))[0]
elif self.nClasses == 2:
self.W = scipy.linalg.lstsq(R, numpy.dot(Q.T, Y[:,1]))[0]
else:
raise ReadoutException('nClasses == 1!?!?')
self.trained=True
self.time = time.time() - t0
def apply(self, X):
if not self.trained:
raise ReadoutException('readout is not trained!')
X = numpy.asarray(X)
if self.addBias:
X = numpy.concatenate((X, numpy.ones((X.shape[0], 1))), 1)
if self.nClasses == 2:
maxI = (numpy.dot(X, self.W)>=0).astype(numpy.int32) # maxI \in {1,2}
else:
S = numpy.zeros((X.shape[0], self.nClasses))
for i in range(self.nClasses):
S[:,i] = numpy.dot(X, self.W[:,i])
maxV=S.max(1)
maxI=S.argmax(1)
if maxI.ndim < 1: maxI = numpy.asarray([maxI])
return utils.flatten(numpy.asarray(self.uniqueY).take(maxI).tolist())
def analyse(self, set_X, set_Y):
''' LINEAR_CLASSIFICATION Analyse a trained linear classifier(s) on data
Return values
err - classification error
mse - mean squared error (not very meaningful)
cc - correlation coefficient bewteen target and classifier output
score - error measurement which takes into account the confusion matrix
CM - the confusion matrix: CM(i,j) is the number of examples
classified as class i while beeing actually class j.
mi - mutual information between
target and classifier output (calculated from CM).
hx - entropy of target values
hy - entropy of classifier output
'''
if not self.trained:
raise ReadoutException('readout is not trained!')
set_X = numpy.array(set_X)
set_Y = copy.copy(set_Y)
undef = numpy.where(numpy.isnan(set_Y))[0]
# might be replaced by take()
for ind in undef:
set_X[ind,:] = []
set_Y[ind] = []
set_Y = numpy.asarray(set_Y)
if numpy.prod(set_Y.shape) == 0:
return (nan, nan, nan, nan, [], nan, nan, nan, nan)
O = self.apply(set_X) # apply the classifier
mae = (O != set_Y.astype(numpy.double)).mean() # mean classification error
mse = ((O - set_Y.astype(numpy.double))**2).mean() # mean squared error: not very meaningful here
cc = corr_coeff(O, set_Y.astype(numpy.double)) # correlatio coefficient
CM = confusion_matrix(O, set_Y.astype(numpy.double), self.uniqueY) # confusion matrx
if self.nClasses == 2:
kappa = ((1.0-mae)-0.5)/(1.0-0.5)
else:
kappa = numpy.nan
# error score: takes into account false and positive negatives
# only for 2 classes
score = CM[0,1]/(1+CM[0,0]+CM[1,0]/(1+CM[1,1]))
# calculate mutual information and entropies between target and classifier output
(mi,hx,hy,hxy) = mi_from_count(CM)
return (mae, mse, cc, score, CM, mi, hx, hy, kappa)
class LinearNonNegClassification(LinearClassification):
def __init__(self, nClasses = 2, addBias=True, addNegBias=False, swapLabels=False):
super(LinearNonNegClassification, self).__init__(nClasses=nClasses, addBias=addBias)
self.addNegBias = addNegBias
self.usefull_dims = None
self.swapLabels = swapLabels
def apply(self, X):
if not self.trained:
raise ReadoutException('readout is not trained!')
X = numpy.array(X)
if self.usefull_dims is not None:
X = X[:, self.usefull_dims]
if self.addBias:
X = numpy.concatenate((X, numpy.ones((X.shape[0], 1))), 1)
if self.addNegBias:
X = numpy.concatenate((X, -1*numpy.ones((X.shape[0], 1))), 1)
if self.nClasses == 2:
if self.addNegBias:
maxI = (numpy.dot(X, self.W)>=0).astype(numpy.int32) # maxI \in {1,2}
else:
maxI = (numpy.dot(X, self.W)>=0.5).astype(numpy.int32) # maxI \in {1,2}
else:
S = numpy.zeros((X.shape[0], self.nClasses))
for i in range(self.nClasses):
S[:,i] = numpy.dot(self.W[i,:], X.T)
maxV=S.max(1)
maxI=S.argmax(1)
if maxI.ndim < 1:
maxI = numpy.asarray([maxI])
Y=numpy.array(utils.flatten(numpy.asarray(self.uniqueY).take(maxI).tolist()))
if self.swapLabels:
Y = (numpy.array(Y*2-1)*(-1) + 1)/2;
return Y.tolist()
def train(self, trainSet_X, trainSet_Y):
'''train linear classifier'''
t0 = time.time()
# convert to matrix
trainSet_X = numpy.array(trainSet_X)
sx = abs(trainSet_X).sum(0)
sx_w=numpy.where(sx>0)[0]
if len(sx_w) > 0:
self.usefull_dims = sx_w
trainSet_X = trainSet_X[:, self.usefull_dims]
else:
self.usefull_dims=None
nDim = trainSet_X.shape[1]
trainSet_Y = numpy.array(trainSet_Y)
nSamples = len(trainSet_Y)
if nSamples != trainSet_X.shape[0]:
s='Number of training samples (%d) '\
'is different from number of target values (%d)!\n' % (trainSet_X.shape[0], nSamples)
raise ReadoutException(s)
# 1. Convert the target values into range 1:nClasses => indexedY
(self.uniqueY, indexedY) = unique_index(trainSet_Y)
n = len(self.uniqueY);
if n != self.nClasses:
s='Number of actual class values (%d) is different from this.nClasses = %d!\n' % (len(self.uniqueY), self.nClasses)
raise ReadoutException(s)
# 2. make -1/+1 vector representation
Y = numpy.zeros((nSamples,n))
for i in range(n):
l = numpy.where(numpy.asarray(indexedY) == i)[0].tolist()
for j in l:
Y[j,i] = 1
if self.swapLabels:
Y = (numpy.array(Y*2-1)*(-1) + 1)/2;
if self.addNegBias:
Y = numpy.array(Y*2-1);
if self.addBias:
trainSet_X = numpy.concatenate((trainSet_X, numpy.ones((trainSet_X.shape[0],1))), 1)
if self.addNegBias:
trainSet_X = numpy.concatenate((trainSet_X, -1.0*numpy.ones((trainSet_X.shape[0], 1))), 1)
nDim = trainSet_X.shape[1]
# 3. Find least squares solutions
if self.nClasses > 2:
# now we solve the nClasses > 2 regression problems
self.W = numpy.zeros((self.nClasses, nDim))
for i in range(self.nClasses):
self.W[i,:] = lsqnonneg(trainSet_X, Y[:,i])[0]
elif self.nClasses == 2:
self.W = lsqnonneg(trainSet_X, Y[:,1])[0]
else:
raise ReadoutException('nClasses == 1!?!?')
self.trained=True
self.time = time.time() - t0
if __name__=='__main__':
trainSet_X = [[1,2,3,2,2],[1,3,3,4,5],[1,4,3,4,5],[2,3,2,1,1]]
trainSet_X = [[1,2,3],[1,3,3],[2,4,3],[1,1,3],[4,4,5]]
trainSet2_Y = [0,1,1,0,1]
trainSet3_Y = [0,1,2,0,2]
testSet_X = [[1,2,4],[2,4,2],[1,3,3]]
testSet2_Y = [0,1,1]
testSet3_Y = [0,1,2]
trainSet_X0 = [[1,2,0,3],[1,3,0,3],[2,4,0,3],[1,1,0,3],[4,4,0,5]]
testSet_X0 = [[1,2,0,4],[2,4,0,2],[1,3,0,3]]
print 'Linear Classification:'
print '\n2 Classes:'
lin_readout2 = LinearClassification(2)
lin_readout2.train(trainSet_X, trainSet2_Y)
testY2 = lin_readout2.apply(testSet_X)
print lin_readout2.analyse(testSet_X, testSet2_Y)
print "training time 2 classes: ", lin_readout2.time
print "W:", lin_readout2.W
print '\n3 Classes:'
lin_readout3 = LinearClassification(3)
lin_readout3.train(trainSet_X, trainSet3_Y)
testY3 = lin_readout3.apply(testSet_X)
print lin_readout3.analyse(testSet_X, testSet3_Y)
print "training time 3 classes: ", lin_readout3.time
print "W:", lin_readout3.W
# make training data tr
print '\nrandom 2 Classes:'
num_samples = 50
num_features = 11
tr_X=numpy.random.uniform(0, 1, (num_samples, num_features))-0.5
tr_Y=(tr_X.sum(1) > 0).astype(numpy.int32)
# do the training
LC = LinearClassification()
LC.train(tr_X, tr_Y);
print "training time random samples: ", LC.time
print "W:", LC.W
# make test data te
te_X=numpy.random.uniform(0,1, (num_samples, num_features))-0.5
te_Y=(tr_X.sum(1) > 0).astype(numpy.int32)
# apply the trained classifier and measure error
(mae,mse,cc,score,CM,mi,hx,hy,kappa) = LC.analyse(te_X, te_Y)
print '\n\nLinear Non Negative Classification:'
print '\nrandom 2 Classes:'
lin_nnreadout2 = LinearNonNegClassification(2, addNegBias=False)
lin_nnreadout2.train(trainSet_X, trainSet2_Y)
print lin_nnreadout2.analyse(testSet_X, testSet2_Y)
print "training time 2 classes: ", lin_nnreadout2.time
print "W:", lin_nnreadout2.W
lin_nnreadout2neg = LinearNonNegClassification(2, addNegBias=True)
lin_nnreadout2neg.train(trainSet_X, trainSet2_Y)
print lin_nnreadout2neg.analyse(testSet_X, testSet2_Y)
print "training time 2 classes (neg): ", lin_nnreadout2neg.time
print "W:", lin_nnreadout2neg.W
lin_nnreadout3 = LinearNonNegClassification(3, addNegBias=True)
lin_nnreadout3.train(trainSet_X, trainSet3_Y)
testY3 = lin_nnreadout3.apply(testSet_X)
print lin_nnreadout3.analyse(testSet_X, testSet3_Y)
print "training time 3 classes: ", lin_nnreadout3.time
print "W:", lin_nnreadout3.W
print '\nlearning with zero value dimensions:'
lin_nnreadout4 = LinearNonNegClassification(3, addNegBias=True)
lin_nnreadout4.train(trainSet_X0, trainSet3_Y)
testY3 = lin_nnreadout4.apply(testSet_X0)
print lin_nnreadout4.analyse(testSet_X0, testSet3_Y)
print "training time 3 classes: ", lin_nnreadout3.time
print "W:", lin_nnreadout3.W