Tulmot / Sklearn-Multilabel

Ensembles de clasificadores Multi-Label en Scikit-Learn
2 stars 0 forks source link

Error encontrado en RO en el método predict_proba #70

Closed Tulmot closed 6 years ago

Tulmot commented 6 years ago

Encontramos un error cuando realizamos iteraciones sobre el método predict_proba de la clase Random Oracles, ya que como lo probamos con un árbol cuando en una de las iteraciones si tiene el mismo labelset devuelve una predicción con tamaño uno, por lo tanto para corregir esto lo que hacemos es calcular las probabilidades con el método predict, ya que este si funciona correctamente.

alvarag commented 6 years ago

El caso degenerado del que se habla en la issue:

In [95]: from sklearn import tree
     ...: X = [[0, 0],[0,1]]
     ...: Y = [[0,1],[1,1]]
     ...: clf = tree.DecisionTreeClassifier()
     ...: clf = clf.fit(X, Y)
     ...: clf

Out[95]:
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
             max_features=None, max_leaf_nodes=None,
             min_impurity_split=1e-07, min_samples_leaf=1,
             min_samples_split=2, min_weight_fraction_leaf=0.0,
             presort=False, random_state=None, splitter='best')

In [96]: clf.predict([[0,0]])
Out[96]: array([[ 0.,  1.]])

In [97]: clf.predict_proba([[0,0]])
Out[97]: [array([[ 1.,  0.]]), array([[ 1.]])]
jjrodriguez commented 6 years ago

El problema viene de que está considerando que la segunda salida solo tiene un valor.

No considera problemas multilabel, sino problemas con varias clases de salida, cada clase podría tener un número diferente de valores. Por ejemplo, podríamos tener que los valores de una de las salidas fueran [4,7,10] y entonces nos devuelve un array con 3 valores.

Tulmot commented 6 years ago

He solucionado el problema como dijimos utilizando el predict, subo el código y si podeis mirarlo a ver si os parece correcto como lo he hecho, y ya después cierro la tarea. El código esta en base_random_oracles

alvarag commented 6 years ago

Ahora entiendo cómo está funcionando @jjrodriguez, yo lo que pensaba era que el problema venía por pocas clases pero veo que viene por lo que comentas. Gracias.

jjrodriguez commented 6 years ago

@Tulmot no veo que en predict_proba se esté llamando al predict_proba de los clasificadores base. Creo que la idea era llamar al predict solo en los casos en los que el predict_proba no devolvía lo que esperamos.

Por otro lado no veo que en predict_proba se inicialice _classifiers_prediction. Si llamas a predict_proba despues de llamar a predict, estará inicializado, pero se puede llamar a predict_proba sin haber llamado a predict antes.

Tulmot commented 6 years ago

Tengo el siguiente código, el problema es que en una de las iteraciones el shape es (1,2) pero uno de los arrays tiene un elemento, adjunto foto para que se entienda mejor: imagen Lo que pone de (1,2) es el shape. Entonces en los oráculos que tienen shape (1,1) me los hace bien pero en este caso que he dicho no se si tendre que poner otra condición o como.

    def predict_proba(self, X):
        """The predicted class probabilities of an input sample is computed as
        the mean predicted class probabilities of the base estimators in the
        ensemble.
         Parameters
        ----------
        X : It's a matrix of form = [n_instances, n_features]
            The training input samples. Sparse matrices are accepted only if
            they are supported by the base estimator.
        Returns
        -------
        p : It's a matrix of form = [n_samples, n_classes]
            The class probabilities of the input samples. The order of the
            classes corresponds to that in the attribute `classes_`.
        """

        def predict_prob(num):
            """Según el numero devolvemos una probabilidad o otra """
            if num == 1:
                return [[0., 1.]]
            else:
                return [[1., 0.]]

        def list_predict_proba(inst_oracles):
            """Predecimos la probabilidad de cada una de las instancias con el
            oraculo correspondiente mas cercano"""
            oracle_near = inst_oracles[n_features:]
            instance = inst_oracles[:n_features]
            oracle_near = list(oracle_near)
            index_classifier = oracle_near.index(1)
            prediction_proba=self._classifiers_train[index_classifier].predict_proba(
                [instance])
            print(prediction_proba[0].shape)
            if(prediction_proba[0].shape==(1,1)):
                call_predict=self._classifiers_train[index_classifier].predict(
                        [instance])
                print(list(np.asarray(list(map(
                        predict_prob, call_predict[0])))))
                return list(np.asarray(list(map(
                        predict_prob, call_predict[0]))))
            else:
                print(self._classifiers_train[index_classifier].predict_proba(
                        [instance]))

                return self._classifiers_train[index_classifier].predict_proba(
                        [instance])

        n_features = X.shape[1]
        m_oracle = np.concatenate((X, self._nearest_oracle(X)), axis=1)
        self._classifiers_prediction_proba = list(map(
            list_predict_proba, m_oracle))
        self._classifiers_prediction_proba=np.concatenate((
                self._classifiers_prediction_proba), axis=1)
        self._classifiers_prediction_proba = np.asarray(
            self._classifiers_prediction_proba)
        convert_array=lambda prob : np.asarray(prob)
        self._classifiers_prediction_proba=list(map(convert_array,self._classifiers_prediction_proba))
        return self._classifiers_prediction_proba
alvarag commented 6 years ago

En vez de comprobar el shape, puedes utilizar esta instrucción que te da el elemento más pequeño de la lista que le pasas

min(lista, key=len)
Tulmot commented 6 years ago

Te entedido hacerlo así:

            if(min(prediction_proba[0], key=len).shape[0]==1):
                call_predict=self._classifiers_train[index_classifier].predict(
                        [instance])
                print(list(np.asarray(list(map(
                        predict_prob, call_predict[0])))))
                return list(np.asarray(list(map(
                        predict_prob, call_predict[0]))))
            else:
                print(self._classifiers_train[index_classifier].predict_proba(
                        [instance]))

                return self._classifiers_train[index_classifier].predict_proba(
                        [instance])

De todas formas sigo en las mismas imagen

Nose porque pero el elemento que tiene 1 no le detecta porque me dice que el tamaño es 2

alvarag commented 6 years ago

Haz commit del código completo para que podamos ver qué esta haciendo tu clase al completo, sino solo por fragmentos es muy difícil. ¿El if está funcionando bien?

Tulmot commented 6 years ago

El if nose porque en la iteración 2 me si que tiene un array con tamaño 1 y me lo devuelve bien, pero en la iteración 4 que vuelve a salir un array con tamaño uno, me dice que el más pequeño tiene longitud dos por ello no entra en el if y va al else y falla.

La diferencia es que en la iteración 2 el array de tamaño 1 sale en la posición 0: imagen Mientras que en la iteración 4 sale en la posición 2 y 4: imagen

Voy a darle una vuelta a ver si veo el fallo de todas formas pongo el código por si ves tu antes el fallo:

    def list_predict_proba(inst_oracles):
            """Predecimos la probabilidad de cada una de las instancias con el
            oraculo correspondiente mas cercano"""
            oracle_near = inst_oracles[n_features:]
            instance = inst_oracles[:n_features]
            oracle_near = list(oracle_near)
            index_classifier = oracle_near.index(1)
            prediction_proba=self._classifiers_train[index_classifier].predict_proba(
                [instance])
            print(min(prediction_proba[0], key=len).shape[0])
            if(min(prediction_proba[0], key=len).shape[0]==1):
                call_predict=self._classifiers_train[index_classifier].predict(
                        [instance])
                print(list(np.asarray(list(map(
                        predict_prob, call_predict[0])))))
                return list(np.asarray(list(map(
                        predict_prob, call_predict[0]))))
            else:
                print(prediction_proba)

                return prediction_proba

        n_features = X.shape[1]
        m_oracle = np.concatenate((X, self._nearest_oracle(X)), axis=1)
        self._classifiers_prediction_proba = list(map(
            list_predict_proba, m_oracle))
        #print(self._classifiers_prediction_proba)
        self._classifiers_prediction_proba=np.concatenate((
                self._classifiers_prediction_proba), axis=1)
        self._classifiers_prediction_proba = np.asarray(
            self._classifiers_prediction_proba)
        convert_array=lambda prob : np.asarray(prob)
        self._classifiers_prediction_proba=list(map(convert_array,self._classifiers_prediction_proba))
        #print(self._classifiers_prediction_proba)
        return self._classifiers_prediction_proba
alvarag commented 6 years ago

Estás cogiendo solamente la primera posición...

if(min(prediction_proba[0], key=len).shape[0]==1):

Deberías coger:

if(min(prediction_proba[0], key=len)==1):
Tulmot commented 6 years ago

El problema es que si lo pongo asi lo que me devuelve (min(prediction_proba[0], key=len) es un array es decir por ejemplo [0. , 1.] por eso hacia el .shape para obtener el tamaño de ese array... y de todas formas tampoco lo hace bien porque me devuelve: imagen he comprobado que eso siempre me devuelve la primera posición, no me devuelve el más pequeño de todos

alvarag commented 6 years ago

De acuerdo, había visto mal el paréntesis. Yo probé ayer la función min y me funcionaba con una lista de arrays, haz la prueba desde jupyter o ipython para ver cuál puede ser el problema.

Tulmot commented 6 years ago

con esto funciona:

prediction_proba=[[ 1.,  0.], [ 0.,  1.], [ 1.], [ 1.,  0.], [ 1.]]
print(min(prediction_proba, key=len))

el problema es que yo tengo:

prediction_proba=[[[ 1.,  0.]], [[ 0.,  1.]], [[ 1.]], [[ 1.,  0.]], [[ 1.]]]
print(min(prediction_proba, key=len))

voy a probar en jupyter a ver si consigo de alguna manera obtenerlo.

Tulmot commented 6 years ago

Si no se me ocurre recorrer la lista y como al recorrer la lista tendria algo como [[ 1., 0.]] y voy quedandome con la lista del tamaño mas pequeño que al final será [[ 1.]]

jjrodriguez commented 6 years ago

Quizás con print(min(prediction_proba, key=(lambda x:len(x[0]))))

alvarag commented 6 years ago

¡Eso mismo estaba probando yo ahora mismo Juanjo! Y a mi me funciona correctamente.

Tulmot commented 6 years ago

Me funciona pero porque eso siempre me devuelve longitud 1, entonces siempre entra al if

Código:

def predict_proba(self, X):
        """The predicted class probabilities of an input sample is computed as
        the mean predicted class probabilities of the base estimators in the
        ensemble.
         Parameters
        ----------
        X : It's a matrix of form = [n_instances, n_features]
            The training input samples. Sparse matrices are accepted only if
            they are supported by the base estimator.
        Returns
        -------
        p : It's a matrix of form = [n_samples, n_classes]
            The class probabilities of the input samples. The order of the
            classes corresponds to that in the attribute `classes_`.
        """

        def predict_prob(num):
            """Según el numero devolvemos una probabilidad o otra """
            if num == 1:
                return [[0., 1.]]
            else:
                return [[1., 0.]]

        def list_predict_proba(inst_oracles):
            """Predecimos la probabilidad de cada una de las instancias con el
            oraculo correspondiente mas cercano"""
            oracle_near = inst_oracles[n_features:]
            instance = inst_oracles[:n_features]
            oracle_near = list(oracle_near)
            index_classifier = oracle_near.index(1)
            prediction_proba=self._classifiers_train[index_classifier].predict_proba(
                [instance])
            print((min(prediction_proba, key=(lambda x:len(x[0])))).shape[0])
            print(prediction_proba)
            if((min(prediction_proba, key=(lambda x:len(x[0])))).shape[0]):
                call_predict=self._classifiers_train[index_classifier].predict(
                        [instance])
                return list(np.asarray(list(map(
                        predict_prob, call_predict[0]))))
            else:
                return prediction_proba

        n_features = X.shape[1]
        m_oracle = np.concatenate((X, self._nearest_oracle(X)), axis=1)
        self._classifiers_prediction_proba = list(map(
            list_predict_proba, m_oracle))
        #print(self._classifiers_prediction_proba)
        self._classifiers_prediction_proba=np.concatenate((
                self._classifiers_prediction_proba), axis=1)
        self._classifiers_prediction_proba = np.asarray(
            self._classifiers_prediction_proba)
        convert_array=lambda prob : np.asarray(prob)
        self._classifiers_prediction_proba=list(map(convert_array,self._classifiers_prediction_proba))
        #print(self._classifiers_prediction_proba)
        return self._classifiers_prediction_proba

Salida: 1 [array([[ 1.]]), array([[ 1.]]), array([[ 1., 0.]]), array([[ 1., 0.]]), array([[ 1., 0.]])] 1 [array([[ 1., 0.]]), array([[ 0., 1.]]), array([[ 1., 0.]]), array([[ 1., 0.]]), array([[ 0., 1.]])]

Son dos iteraciones diferentes la primera deberia devolver 1 y la segunda 2 pero como veis el 1 que aparece encima de los arrays es el tamaño siempre devuelve 1

jjrodriguez commented 6 years ago
print(array([[ 1., 0.]]).shape[0])
1
print(array([[ 1., 0.]]).shape)
(1, 2)

¿Es shape[0] lo que quieres?

Tulmot commented 6 years ago

vale..fallo mio, ya funciona correctamente.

Cierro y ahora subo el código.

jjrodriguez commented 6 years ago

El comentario para el que no he visto respuesta:

_ @Tulmot no veo que en predict_proba se esté llamando al predict_proba de los clasificadores base. Creo que la idea era llamar al predict solo en los casos en los que el predict_proba no devolvía lo que esperamos.

Por otro lado no veo que en predict_proba se inicialice _classifiers_prediction. Si llamas a predict_proba despues de llamar a predict, estará inicializado, pero se puede llamar a predictproba sin haber llamado a predict antes.

Tulmot commented 6 years ago

pero como lo tengo hecho ahora si que puedo llamar al predict proba antes de llamar al predict:

if((min(prediction_proba, key=(lambda x: len(x[0])))).shape[1]):
    call_predict = self._classifiers_train[
          index_classifier].predict([instance])
    return list(np.asarray(list(map(
          predict_prob, call_predict[0]))))
else:
    return prediction_proba

Porque en el caso que me ocurrar el error de que una de las longitudes es 1, dentro de ese if llamo al predict, entonces entiendo que aunque no lo inicialice nunca va a fallar porque da igual que no haya ejecutado anteriormente predict.

jjrodriguez commented 6 years ago

OK