Brier score) for
        samples in X, virtual labels in T and parameters w.
        It assumes a multi-class softmax classifier.
        This method implements gradients for two different square losses, that
        are specified in the object's attribute self.method:
            'OSL' :Optimistic Superset Loss. It assumes that the true label is
                   the nonzero weak label with highest posterior probability
                   given the model.
            'VLL' :Virtual Labels Loss.

        Args:
            :w:  1-D nympy array. A flattened version of the weight matrix of
                 the multiclass softmax. This 1-D arrangement is required by
                 the scipy optimizer that will use this method.
                :X:  Input data. An (NxD) matrix of N samples with dimension D
                :T:  Target class. An (NxC) array of target vectors.
                     The meaning and format of each target vector t depends on
                     the selected log-loss version:
                     - 'OSL': t is a binary vector.
                     - 'VLL': t is a virtual label vector
            Returns:
                :G:  Gradient of the Log-loss
        """

        n_dim = X.shape[1]
        W2 = w.reshape((n_dim, self.n_classes))
        p = self.softmax(np.dot(X, W2))

        if self.method == 'OSL':
            D = self.hardmax(T*p)
            Q = (p - D) * p
        else:
            Q = (p - T) * p

        sumQ = np.sum(Q, axis=1, keepdims=True)
        G = np.dot(X.T, Q - sumQ * p)

        return G.reshape((n_dim*self.n_classes))

    def gradLogLoss(self, w, X, T):

        """
        Compute the gradient of the regularized log loss (cross-entropy) for
        samples in X, virtual labels in T and parameters w.
        The regularization parameter is taken from the object attribute
        self.params['alpha']
        It assumes a multi-class softmax classifier.
        This method implements gradients for two different log-losses, that are
        specified in the object's attribute self.method:
            'OSL' :Optimistic Superset Loss. It assumes that the true label is
                   the nonzero weak label with highest posterior probability
                   given the model.
            'VLL' :Virtual Labels Loss.
            'EM'  :Expected Log likelihood (i.e. the expected value of the
                   complete data log-likelihood after the E-step). It assumes
                   that a mixing matrix is known and contained in
                   self.params['M']

        Args:
            :w:  1-D nympy array. A flattened version of the weight matrix of
                 the multiclass softmax. This 1-D arrangement is required by
                 the scipy optimizer that will use this method.
                :X:  Input data. An (NxD) matrix of N samples with dimension D
                :T:  Target class. An (NxC) array of target vectors.
                     The meaning and format of each target vector t depends on
                     the selected log-loss version:
                     - 'OSL': t is a binary vector.
                     - 'VLL': t is a virtual label vector
                     - 'EM': t is an integer index of a weak label
            Returns:
                :G:  Gradient of the Log-loss
        """

        n_dim = X.shape[1]
        W2 = w.reshape((n_dim, self.n_classes))
        p = self.softmax(np.dot(X, W2))

        if self.method == 'OSL':
            D = self.hardmax(T*p)
            G = np.dot(X.T, p - D)

        elif self.method == 'EM':
            M = self.params['M']
            Q = p * M[T, :].T
            Q = Q / np.sum(Q, axis=0)
            G = np.dot(X.T, p - Q)

        else:
            bias = np.sum(T, axis=1, keepdims=True)
            G = np.dot(X.T, p*bias - T) - self.params['alpha']*W2
            # G = np.dot(X.T, p - T)

        return G.reshape((n_dim*self.n_classes))

    def gradLoss(self, w, X, T):

        if self.params['loss'] == 'CE':

            g = self.gradLogLoss(w, X, T)

        elif self.params['loss'] =='square':

            g = self.gradSquareLoss(w, X, T)

        else:

            exit('Unknown loss')

        return g

    def gd(self, X, T):
        """
        Trains a logistic regression classifier by a gradient descent method
        """

        # Initialize variables
        n_dim = X.shape[1]
        W = np.random.randn(n_dim, self.n_classes)
        w1 = W.reshape((n_dim*self.n_classes))

        # Running the gradient descent algorithm
        for n in range(self.params['n_it']):

            w1 = W.reshape((n_dim*self.n_classes))

            G = self.gradLoss(w1, X, T).reshape((n_dim, self.n_classes))

            W -= self.params['rho']*G

        return W

    def fit(self, X, Y):
        """
        Fits a logistic regression model to instances in X given the labels in
        Y

        Args:
            :X :Input data, numpy array of shape[n_samples, n_features]
            :Y :Target for X, with shape [n_samples].
                Each target can be a index in [0,..., self.n_classes-1] or
                a binary vector with dimension self.n_classes

        Returns:
            :self
        """

        self.n_dim = X.shape[1]

        # If labels are 1D, transform them into binary label vectors
        if len(Y.shape) == 1:

            # If the alphabet is not [0, 1,..., n_classes-1] transform
            # labels into these values.
            # if not(set(self.classes_) < set(xrange(self.n_classes))):
            #     alphabet_inv = dict(zip(self.classes_,range(self.n_classes)))
            #     Y0 = np.array([alphabet_inv[c] for c in Y])
            # else:
            #     Y0 = Y

            T = self.index2bin(Y, self.n_classes)

        else:
            T = Y

        # Optimization
        if self.optimizer == 'GD':
            self.W = self.gd(X, T)
        else:
            w0 = 1*np.random.randn(X.shape[1]*self.n_classes)
            res = sp.optimize.minimize(
                self.loss, w0, args=(X, T), method=self.optimizer,
                jac=self.gradLoss, hess=None, hessp=None, bounds=None,
                constraints=(), tol=None, callback=None,
                options={'disp': False, 'gtol': 1e-20,
                         'eps': 1.4901161193847656e-08,'return_all': False,
                        'maxiter': None, 'norm': np.inf})
            #    options=None)
            self.W = res.x.reshape((self.n_dim, self.n_classes))

            # if res.status!= 0:
            #     print "{0}-{1}: Status {2}. {3}. {4}".format(
            #         self.method, self.optimizer, res.status, res.success,
            #         res.message)

            # wtest = res.x
            # error = sp.optimize.check_grad(
            #     self.logLoss, self.gradLogLoss, wtest, X, T)
            # print "Check-grad error = {0}".format(error)

        return self    # w, nll_tr

    def predict(self, X):

        # Compute posterior probability of class 1 for weights w.
        p = self.softmax(np.dot(X, self.W))

        # Class
        D = np.argmax(p, axis