En este documento estará recogida todas las prácticas enumeradas con solución para hacer un ctrl + F en el PDF para que sea más fácil de buscar las estructuras del código
"""
Tema 1 : Introducción a Problemas Complejos de Aprendizaje Supervisado
Sesión 1 : Datos Multi-etiqueta
Bloque A : Ejemplo sencillo mediante aprendizaje mono-etiqueta
"""
# %% 0 Generación de datos
import random
from sklearn.svm import SVC
import numpy as np
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
def plotME(X,xA,yA,LValues,LNames='Etiqueta'):
"""
Dibuja los ejemplos de un conjunto multietiqueta sobre los atributos xA,yA.
Para cada etiqueta se colorearán los ejemplos con valor 1
"""
nL=len(LValues)
X=np.array(X)
LValues=np.array(LValues)
s=3
fig,axs=plt.subplots(1,nL,sharey=True,figsize=[nL*s,1*s])
for il in range(nL):
if il==0:
axs[il].set_ylabel('Atributo {}'.format(yA))
colors=list(map(lambda l:'blue' if l==1 else 'grey',LValues[il,:]))
axs[il].scatter(X[:,xA],X[:,yA],c=colors)
axs[il].set_title('{} {}'.format(LNames,il) if type(LNames)==str else LNames[il])
axs[il].set_xlabel('Atributo {}'.format(xA))
# fig.savefig('DistEtiSencillo.pdf',dpi=300,bbox_inches='tight')
def genSencillo(RG,e,a):
"""
Crea un dataset con e ejemplos, a atributos y con 3 etiquetas: Y0, Y1, Y2
Creamos la X con valores de una distribución uniforme en [0;1) para cada valor
Y0 será 1 si la suma del primer y segundo atributo son menores de tr, si no 0
Y1 será 1 si la suma del primer y segundo atributo son mayores de (2-tr), si no 0
Y2 será 1 si se cumple la condición de Y0 o la de Y1, si no 0
Parámetros:
- RG : random.Random() instancia
- e : número de ejemplos
- a : número de atributos
Retorna:
[X,[Y0,Y1,Y2]] : - X : matriz de atributos
: - Yx: vector de la etiqueta x
"""
X =np.zeros([e,a])
Y0=np.zeros(e)
Y1=np.zeros(e)
Y2=np.zeros(e)
th=0.75
for ie in range(e):
for ia in range(a):
X[ie][ia]=RG.random()
Y0[ie]=1 if X[ie][0]+X[ie][1]<th else 0
Y1[ie]=1 if X[ie][0]+X[ie][1]>(2-th) else 0
Y2[ie]=1 if X[ie][0]+X[ie][1]<th or X[ie][0]+X[ie][1]>(2-th) else 0
return [X,[Y0,Y1,Y2]]
def calculaAccuracy(MLS,XTr,YTr,XTe,YTe):
"""
Dado un sistema de aprendizaje (Machine Learning System) un conjunto de
entrenamiento y otro de test, realiza un proceso de aprendizaje sobre el
entrenamiento, evalúa el test y calcula la accuracy
Parámetros:
- MLS : Sistema de Aprendizaje de clasificación
- [XTr,YTr] : Conjunto de entrenamiento
- [XTe,YTe] : Conjunto de test
Retorna:
[acc,pred]: - acc : valor de la accuracy en test
- pred: predicción para cada ejemplo de test
"""
MLS.fit(XTr,YTr) # Entrenamiento
pred=MLS.predict(XTe) # Predicción
acc=accuracy_score(pred,YTe)
return [acc,pred]
# Se va a crear un conjunto de datos mediante la función genSencillo
eTr=200 # Número de ejemplos de train
eTe=200 # Número de ejemplos de test
a=4 # Número de atributos
# Generador de números aleatorios
RG=random.Random()
# Semilla para conseguir repetibilidad
RG.seed(1)
# Se generan datasets train,test de tipo 'sencillo'
[XTr,YTr]=genSencillo(RG,eTr,a)
[XTe,YTe]=genSencillo(RG,eTe,a)
# Pintar las categorías en función de los atributos 0 y 1
plotME(XTr,0,1,YTr)
#%% Clasificador lineal por etiqueta
# Acierto si se aprendiera con un sistema de clasificación lineal
# Se guardan las predicciones en preds
MLS=SVC(kernel='linear',C=1)
preds=[]
for l in range(len(YTr)):
[acc,pred]=calculaAccuracy(MLS,XTr,YTr[l],XTe,YTe[l])
preds.append(pred)
print('Etiqueta {} accuracy={}'.format(l,acc))
#%% Utilizar las 2 etiquetas primeras para apremnder la tercera
# ¿Se puede aprender mejor la tercera etiqueta usando la información de las dos
#primeras?
TrY0Y1=np.column_stack([YTr[0],YTr[1]])
TeY0Y1=np.column_stack([YTe[0],YTe[1]])
[acc,pred]=calculaAccuracy(MLS,TrY0Y1,YTr[2],TeY0Y1,YTe[2])
print('\nEtiqueta 2 aprendiendo de las etiquetas 0 y 1 y evaluando las etiquetas 0 y 1 del test:\n accuracy={}'.format(acc))
# Pero esto es trampa (cheating), los valores de todas las categorías de test
#son desconocidas durante el train.
# Pero sí se pueden utilizar las predicciones
TeP0P1=np.column_stack([preds[0],preds[1]])
[acc,pred]=calculaAccuracy(MLS,TrY0Y1,YTr[2],TeP0P1,YTe[2])
print('\nEtiqueta 2 aprendiendo de las etiquetas 0 y 1 y evaluando las predicciones 0 y 1 del test:\n accuracy={}'.format(acc))
#%% Ejercicio 1
# Realiza un aprendizaje de la etiqueta 2 usando tanto las X como las otras 2 etiquetas.
# Calcula e imprime el acierto en test.
#¿Cómo de útil es añadir la matriz X para aprender la etiqueta 2?
#<TODO BEGIN Ejercicio 1>
XTrY0Y1=np.column_stack([XTr,YTr[0],YTr[1]])
XTeP0P1=np.column_stack([XTe,preds[0],preds[1]])
[acc,pred]=calculaAccuracy(MLS,XTrY0Y1,YTr[2],XTeP0P1,YTe[2])
print('\nEtiqueta 2 aprendiendo de las X y etiquetas 0 y 1 y evaluando las X y predicciones 0 y 1 del test:\n accuracy={}'.format(acc))
#<TODO END Ejercicio 1>
"""
Tema 1 : Introducción a Problemas Complejos de Aprendizaje Supervisado
Sesión 1 : Datos Multi-etiqueta
Bloque B : Características de Datos multi-etiqueta
"""
# Los datasets multi-etiqueta tienen como particularidad que la categoría es un
# array de valores 0|1.
# Ejemplo: dataset de e ejemplos, a atributos y l etiquetas con valores
#aleatorios
import numpy as np
import random
e=10 # Ejemplos
a=3 # Atributos
l=5 # Etiquetas
# Generador de números aleatorios
RG=random.Random()
# Fijar semilla para conseguir repetibilidad
RG.seed(1)
th=0.5
X=np.zeros([e,a])
Y=np.zeros([e,l])
for ie in range(e):
for ia in range(a):
X[ie][ia]=RG.uniform(0,1)
for il in range(l):
v=RG.uniform(0,1)
Y[ie][il]=0 if v <th else 1
print('Y: ejemplos={} etiquetas={}\n{}'.format(e,l,Y))
#%% Medidas categoría
# Vamos a calcular dos medidas de la categoría:
# Cardinalidad(ca): la media del número de unos en cada ejemplo
cardi=Y.sum(1).mean() # NOTA: el '1' indica el eje por el que hace la media (por ejemplos)
print('Cardinalidad={}'.format(cardi))
#%% Ejercicio 1
#Calcula la densidad(De), que es la media de la proporción de unos en cada ejemplo
# Ver "Estadísticas sobre los datos" en "TransparenciasTema1-1.pdf"
# https://www.campusvirtual.uniovi.es/mod/resource/view.php?id=275838
#<TODO BEGIN Ejercicio 1>
densi=Y.mean(1).mean()
print('Densidad ={}'.format(densi))
#<TODO END Ejercicio 1>
# No hace falta calcular las 2 medidas, solo hace falta una y la otra se calcula
#mediante la ecuación: Ca=De*l
#%% Ejercicio 2
# Calcula el número de combinaciones de etiquetas distintas (Dis) que hay en Y.
# NOTA: puedes utilizar str para convertir cada ejemplo/fila de Y en una cadena
#y luego puedes usar np.unique para obtener el conjunto de éstas sin repeticiones
#<TODO BEGIN Ejercicio 1>
dis=len(np.unique(list(map(str,Y))))
print('Distintas ={}'.format(dis))
#<TODO END Ejercicio 2>
"""
Tema 1 : Introducción a Problemas Complejos de Aprendizaje Supervisado
Sesión 1 : Datos Multi-etiqueta
Bloque C : Características de Datos multi-regresión
"""
# Los datasets multi-regresión tienen como particularidad que la categoría es un
# array de valores numéricos.
# Ejemplo: dataset de e ejemplos, a atributos y r categorías numéricas con valores
#aleatorios
import numpy as np
import random
e=10 # Ejemplos
a=3 # Atributos
r=4 # Categorías numéricas
# Generador de números aleatorios
RG=random.Random()
# Fijar semilla para conseguir repetibilidad
RG.seed(1)
X=np.zeros([e,a])
Y=np.zeros([e,r])
for ie in range(e):
for ia in range(a):
X[ie][ia]=RG.uniform(0,1)
for ir in range(r):
Y[ie][ir]=RG.uniform(0,1)
print('Y: ejemplos={} categorías numéricas={}\n{}'.format(e,r,Y))
#%% Información de categorías
# Unos valores típicos a calcular son la media, mínimo y máximo de cada categoría:
for ir in range(r):
print('Categoría:{} media={:0.4f} min={:0.4f} max={:0.4f}'.format(ir,np.mean(Y[:,ir]),
np.min(Y[:,ir]),
np.max(Y[:,ir])))
#%% Ejercicio 1
# Calcular la distancia(Error) media de cada categoría numérica con su media
#<TODO BEGIN Ejercicio 1>
for ir in range(r):
med=np.mean(Y[:,ir]) # Media de la categoría
print('Categoría:{} media={:0.4f} distancia media a la media={:0.4f}'.
format(ir,med,np.mean(abs(Y[:,ir]-med))))
#<TODO END Ejercicio 1>
"""
Tema 1 : Introducción a Problemas Complejos de Aprendizaje Supervisado
Sesión 1 : Datos Multi-etiqueta
Bloque D : Lectura de datos multi-etiqueta
"""
import numpy as np
from scipy.io import arff
import re
def loadarff_denso_numerico(nombre_fichero,l,eaf=True):
"""
Esta función lee ficheros .arff con las siguientes condiciones:
- denso : ha de tener valor para todos los atributos (*1)
- numerico: todos los valores han de ser numéricos (*2)
(*1) No lee ficheros con representación dispersa. Esta restricción viene
data por la función scipy.io.arff.loadarff()
(*2) También leería correctamente atributos nominales representados por '0' o '1'
Parámetros:
- nombre_fichero : cadena, nombre del fichero (con extensión .arff)
- l : entero, número de etiquetas. Si None las lee del fichero
.arff suponiendo formato MEKA
- eaf : booleano, etiquetas al final si True si no, etiquetas al principio
Retorna:
-[X,Y] : X atributos e Y etiquetas del conjunto de datos como np.array
"""
f=open(nombre_fichero)
data, meta = arff.loadarff(f)
if l==None: # Lee l de @relation -C <l> (Meka .arff files)
f.seek(0)
rel=f.readline()
sl=rel[rel.find('-C')+3:]
l=int(re.search('[0-9]+',sl)[0])
f.close()
data=data.tolist()
X=[None]*len(data)
Y=[None]*len(data)
a=len(data[0])-l # Número de atributos
for ie in range(len(data)):
if eaf: # Si etiquetas al final
X[ie]=list(map(float,data[ie][:a]))
Y[ie]=list(map(float,data[ie][a:]))
else: # Etiquetas al principio
X[ie]=list(map(float,data[ie][l:]))
Y[ie]=list(map(float,data[ie][:l]))
return [np.array(X),np.array(Y)]
#%% Librería MULAN
# La librería MULAN (https://sourceforge.net/projects/mulan/) tiene
# varios conjuntos de datos: multilabel (https://mulan.sourceforge.net/datasets-mlc.html).
# Están en formato .arff, en este formato van seguidos los atributos y las etiquetas.
# En MULAN las etiquetas van al final (después de los atributos)
# La función loadarff_denso_numerico permite la lectura de algunos ficheros .arff
#usando la función scipy.io.arff.loadarff. Hay que indicarle el número de etiquetas
#que hay en ese conjunto de datos.
# Mira la descripción de la función y el ejemplo de uso que hay a continuación.
#%% Ejemplo 1
# El fichero emotions.arff se ha descargado de MULAN
nombre_fichero='MultiLabelDatasets/emotions.arff'
numero_etiquetas=6
[X,Y]=loadarff_denso_numerico(nombre_fichero,numero_etiquetas)
# Imprimamos el número de ejemplos, cardinalizad y densidad
# Podemos compararlos con los de la web de descarga
print('\nInformación de: {}'.format(nombre_fichero))
print('Núm: ejemplos:{} atributos:{} etiquetas:{}'.format(len(X),len(X[0]),len(Y[0])))
print('Cardinalidad ={:5.3f}'.format(Y.sum(1).mean()))
print('Densidad ={:5.3f}'.format(Y.mean(1).mean()))
print('Distintos ={}'.format(len(np.unique(list(map(str,Y))))))
#%% Ejercicio 1
# Obtiene de la web de MULAN CAL500.arff
# Léelo, calcula e impreme la cardinalidad, densidad y el número de combinaciones
#distintas. ¿Coinciden con los de la WEB? (¡Debería!)
#<TODO BEGIN Ejercicio 1>
nombre_fichero='MultiLabelDatasets/CAL500.arff'
numero_etiquetas=174
[X,Y]=loadarff_denso_numerico(nombre_fichero,numero_etiquetas)
# Imprimamos el número de ejemplos, cardinalizad y densidad
# Podemos compararlos con los de la web de descarga
print('\nInformación de: {}'.format(nombre_fichero))
print('Núm: ejemplos:{} atributos:{} etiquetas:{}'.format(len(X),len(X[0]),len(Y[0])))
print('Cardinalidad ={:5.3f}'.format(Y.sum(1).mean()))
print('Densidad ={:5.3f}'.format(Y.mean(1).mean()))
print('Distintas ={}'.format(len(np.unique(list(map(str,Y))))))
#<TODO END Ejercicio 1>
#%% Librería MEKA
# La librería MEKA también tiene datasets multi-etiqueta en formato .arff, pero
#en este caso las etiquetas van al principio y en la linea marcada por '@ relation'
#a contiuación de '-C ' está el número de etiquetas.
# Se puede obtener información más detallada en el capítulo 3 del tutorial:
# https://master.dl.sourceforge.net/project/meka/meka-1.9.1/Tutorial.pdf
# Librería: http://waikato.github.io/meka
# Conjunto de datos: https://sourceforge.net/projects/meka/files/Datasets/
#%% Ejercicio 2
# Descargar de MEKA Music.arff, léelo usando loadarff_denso_numerico con los parámetros
#adecuados. Calcula e impreme la cardinalidad, densidad y el número de combinaciones
# Es el conjunto de datos emotions de MULAN que en MEKA se llama music. Comprueba
#que las características son las mismas.
#<TODO BEGIN Ejercicio 2>
nombre_fichero='MultiLabelDatasets/Music.arff'
[X,Y]=loadarff_denso_numerico(nombre_fichero,l=None,eaf=False)
l=len(Y[0])
print('\nInformación de: {}'.format(nombre_fichero))
print('Núm: ejemplos:{} atributos:{} etiquetas:{}'.format(len(X),len(X[0]),len(Y[0])))
print('Cardinalidad ={:5.3f}'.format(Y.sum(1).mean()))
print('Densidad ={:5.3f}'.format(Y.mean(1).mean()))
print('Distintas ={}'.format(len(np.unique(list(map(str,Y))))))
#<TODO END Ejercicio 2>
#%% Ejercicio 3
# Descargar de MEKA movielens.arff, está en 'Other Datasets'/'MovieLens'
# Tiene como indicar de etiquetas '-C 943' pues parece que está tratando de
#aprender los gustos de los usuarios a partir del tipo de película, pero lo
#vamos a hacer al revés, vamos a aprender el tipo de película en función de los
#gustos de los usuarios.
# Lee movielens tomando como etiquetas los 19 últimos atributos.
# Elmina el último atributo de las X (movieID), es un identificador único.
# Binariza los atributos. Tienen un valor ordinal [0..5] y desconocido (-1).
# Se pretende que un valor desconocido se represente por un array de ceros.
# Asigna a la variable X01 el nuevo conjunto de atributos
# PISTA: Usa adecuadamente sklearn.preprocessing.OneHotEncoder
#<TODO BEGIN Ejercicio 3>
from sklearn.preprocessing import OneHotEncoder
nombre_fichero='MultiLabelDatasets/movielens.arff'
[X,Y]=loadarff_denso_numerico(nombre_fichero,l=19)
X=X[:,0:(len(X[0])-1)]
enc = OneHotEncoder(handle_unknown='ignore',sparse=False,
categories=list([0,1,2,3,4,5] for i in range(len(X[0]))))
enc.fit(X)
X01=enc.transform(X)
#<TODO END Ejercicio 3>
# Tras ejecutar este código se imprime el valor del original y binarizado
# para el primer ejemplo ejemplo, atributos 1 y 2
nombres_enc=enc.get_feature_names_out()
for at in [1,2]:
print('\nPrimer ejemplo, atributo {}: {:1.0f}'.format(at,X[0][at]))
print('Primer ejemplo, atributo {} binarizado:'.format(at))
for i in range(len(nombres_enc)):
if nombres_enc[i].find('x{}_'.format(at))>=0:
print(' {}={:1.0f}'.format(nombres_enc[i],X01[0][i]))
"""
Tema 1 : Introducción a Problemas Complejos de Aprendizaje Supervisado
Sesión 1 : Datos Multi-etiqueta
Bloque E : Lectura de datos multi-regresión
"""
import numpy as np
# Se han dejado en MultiRegressionDatasets dos ficheros multi-regresion.
# Mira el contenido en un editor de texto
# Para cargar ficheros .arff se puede utilizar la función loadarff_denso_numerico
#que se ha visto en el bloque anterior y que se ha incluído en el pacage lectores
from lectores.loadarff_denso_numerico import loadarff_denso_numerico
#%% Ejemplo 1
# Leer el fichero rf1.arff que indica en @relation el número de etiquetas y estas
#están al final de los atributos.
# Obtener la media de cada categoría.
nombre_fichero='MultiRegressionDatasets/rf1.arff'
[X,Y]=loadarff_denso_numerico(nombre_fichero,l=None,eaf=True)
r=len(Y[0])
print('fichero: {} ejemplos:{} categorías:{}'.format(nombre_fichero,len(X),r))
for ir in range(r):
print('Categoría:{:2} media={:8.4f}'.format(ir,np.mean(Y[:,ir])))
#%% Ejercicio 1
# Leer waterqual.arff que tiene como categorías los 16 primeros atributos
# Imprimir el nombre del fichero, el número de ejemplos y de cagtegorías
# Para cada categoría imprimir su media y la distancia media de los ejemplos a su media
#<TODO BEGIN Ejercicio 1>
nombre_fichero='MultiRegressionDatasets/waterqual.arff'
[X,Y]=loadarff_denso_numerico(nombre_fichero,l=16,eaf=False)
r=len(Y[0])
print('fichero: {} ejemplos:{} categorías:{}'.format(nombre_fichero,len(X),r))
for ir in range(r):
med=np.mean(Y[:,ir])
print('Categoría:{:2} media={:8.4f} distancia media a la media={:0.4f}'.format(ir,med,np.mean(abs(Y[:,ir]-med))))
#<TODO END Ejercicio 1>
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Bloque A : Aprendizaje multi-etiqueta
"""
from sklearn.multioutput import MultiOutputClassifier
from sklearn.svm import SVC
import random
import numpy as np
# Una manera trivial de aprender a partir de un conjunto multietiqueta es
#aprender etiqueta a etiqueta, a esto se llama: Binary Relevance (BR)
# En sklearn la clase que lo implementa es MultiOutputClassifier
# https://scikit-learn.org/stable/modules/generated/sklearn.multioutput.MultiOutputClassifier.html
# El problema multietiqueta se transforma en uno por cada etiqueta donde
#con los mismos atributos se aprende de manera independiente cada etiqueta.
# Para aprender ese problema monoetiqueta necesita un sistema de aprendeizaje apropiado.
#%% Ejemplo 1
# A partir del conjunto de datos emotions crear un modelo
#MultiOutputClassifier tomando como sistema monoetiqueta SVC lineal con C=1
from lectores.loadarff_denso_numerico import loadarff_denso_numerico
[X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',6)
BR=MultiOutputClassifier(SVC(kernel='linear',C=1))
BR.fit(X,Y)
# Otra manera de realizar un aprendendizaje multietiqueta es mediante una
#cadena de clasifciadores (CC).
# En sklearn la clase que lo implementa es ClassifierChain
# https://scikit-learn.org/stable/modules/generated/sklearn.multioutput.ClassifierChain.html
#%% Ejercicio 1
# A partir del conjunto de datos emotions crear un modelo
#ClassifierChain tomando como sistema monoetiqueta SVC lineal con C=1
# Realiza una predicción en reescritura
#Imprimir el número de fallos (valores de las etiquetas diferentes entre Y (real) y
# la predicción) en la predicción (preds).
#
# PISTA: abs(Y-preds) genera una matriz donde hay un 1 donde hay un fallo
#<TODO BEGIN Ejercicio 1>
from sklearn.multioutput import ClassifierChain
CC=ClassifierChain(base_estimator=SVC(kernel='linear',C=1))
CC.fit(X,Y)
preds=CC.predict(X)
print('Orden CC: original: Fallos={}'.format(int(np.sum(abs(Y-preds)))))
#<TODO END Ejercicio 1>
#%% Ejercicio 2
# Repetir el experimento del ejercicio 1 pero cambiando el orden las etiquetas en CC
# Para lo cual dar valor al parámetro 'order'.
# Para generar órdenes aleatorios se puede utilizar:
# RG=random.Random()
# RG.seed(1)
# orden=list(range(l)) # l es el número de etiquetas
# RG.shuffle(orden)
# Repetir 5 veces la última instrucción e imprimir en cada iteración el orden
#y el número de fallos en la predicción
#<TODO BEGIN Ejercicio 2>
# [X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',6)
RG=random.Random()
seed=1
RG.seed(seed)
l=6
orden=list(range(l))
for i in range(5):
RG.shuffle(orden)
CC=ClassifierChain(SVC(kernel='linear',C=1),order=orden)
CC.fit(X,Y)
preds=CC.predict(X)
print('Orden CC:{} Fallos={}'.format(orden,int(np.sum(abs(Y-preds)))))
#<TODO END Ejercicio 2>
#%% LabelPowerset
# Otra estrategia es aprender bloques de etiquetas de manera conjunta en vez de una a una.
# Funciona especialmente bien cuando hay pocas combinaciones diferentes de etiquetas
# El sistema LabelPowerset (LPS) realiza esta aproximación utilizando todas las
#combinaciones de etiquetas del entrenamiento. Solo funciona correctamente si
#esperamos encontrar las mismas combinaciones en el test.
# http://scikit.ml/api/skmultilearn.problem_transform.lp.html
# Podemos encontrar este y otros sistemas de aprendizaje en Scikit-multilearn
# http://scikit.ml/
#%% Ejemplo 2
# A partir del conjunto de datos emotions crear un modelo
#LabelPowerset tomando como sistema monoetiqueta RandomForestClassifier
# Realiza una predicción en reescritura
from skmultilearn.problem_transform import LabelPowerset
from sklearn.ensemble import RandomForestClassifier
[X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',6)
LPS=LabelPowerset(RandomForestClassifier())
LPS.fit(X,Y)
pred=LPS.predict(X).toarray() # .toarray() pasa de sparse a dense
# El sistema RAkEL realiza un proceso LPS pero sobre subconjuntos de etiquetas
# Los subconjuntos pueden formar una partición (RakelD) http://scikit.ml/api/skmultilearn.ensemble.rakeld.html#skmultilearn.ensemble.RakelD
#o pueden tener superposición (RakelO) http://scikit.ml/api/skmultilearn.ensemble.rakelo.html#skmultilearn.ensemble.RakelO
#%% Ejemplo 3
# A partir del conjunto de datos emotions crear un modelo
#RakelO y otro RakelD tomando como sistema monoetiqueta
#RandomForestClassifier(min_samples_split=0.1)
# Realiza una predicción en reescritura para cada sistema y contar los ejemplos
#en los que hay alguna diferencia en la predicción
#NOTA 1: RakelO puede fallar/no terminar si no se indica expresamente el
# parámetro 'model_count'. El valor recomendado es el doble de etiquetas.
#NOTA 2: Fíjate una manera de comprobar si una predicción en su conjunto
# (para todas las etiquetas es igual a otra)
from multilearnNew.RakelNew import RakelONew,RakelDNew
l=6
[X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',l)
RaO=RakelONew(base_classifier=RandomForestClassifier(min_samples_split=0.1),model_count=l*2) # Instancia el modelo RAKEL Overlapping. En esta variante, los subconjuntos de etiquetas se eligen de forma aleatoria y pueden solaparse (una misma etiqueta puede aparecer en varios subproblemas).
RaD=RakelDNew(base_classifier=RandomForestClassifier(min_samples_split=0.1)) #Instancia el modelo RAKEL Disjoint. A diferencia del anterior, esta variante divide el total de etiquetas en subgrupos que no se solapan entre sí (
RaO.fit(X,Y)
predRaO=RaO.predict(X).toarray() #Realiza las predicciones sobre el mismo conjunto X con el modelo RakelO. Dado que las salidas multietiqueta de estas librerías suelen devolverse como matrices dispersas (sparse matrices), .toarray() las convierte en una matriz densa estándar de NumPy para poder operar fácilmente con ella.
RaD.fit(X,Y)
predRaD=RaD.predict(X).toarray()
EDif=0 # Ejemplos diferentes
for ie in range(len(X)):
if sum(abs(predRaO[ie]-predRaD[ie]))>0: #Resta elemento a elemento. Si son iguales da 0. Si difieren da 1 o -1.
EDif=EDif+1
print('Ejemplos con predicción diferente entre RakelO y RakelD={}'.format(EDif))
#%% Random Forest
# Hay otros sistemas de aprendizaje multietiqueta que no necesitan de un sistema
#monoetiqueta, por ejemplo RandomForestClassifier (RFC)
# https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html
# Este sistema puede usarse para aprendizaje mono-etiqueta, como se ha hecho en
#este bloque, o puede usarse directamente como sistema multi-etiqueta
#%% Ejercicio 3
# Repetir el experimento del ejercicio 1 para el sistema RFC en vez del ClassifierChains.
#No necesita sistema monoetiqueta. No dar valor a ningún parámetro.
# ¿Cómo interpretas el resultado? ¿Este resultado indica que es mejor que CC?
#<TODO BEGIN Ejercicio 3>
RFC=RandomForestClassifier()
RFC.fit(X,Y)
preds=RFC.predict(X)
print('RFC: Fallos={}'.format(int(np.sum(abs(Y-preds)))))
#<TODO END Ejercicio 3>
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Bloque B : Aprendizaje multi-regresión
"""
from sklearn.multioutput import MultiOutputRegressor
from sklearn.multioutput import RegressorChain
from sklearn.ensemble import RandomForestRegressor
# De manera análoga a la clasificación se puede realizar la regresión multi-salida.
# El sistema MultiOutputRegressor realiza un aprendizaje de cada salida de manera
#independiente
#%% Ejercicio 1.
# Lee en MultiRegressoinDatsets rf1.arff. Crea un modelo MultiOutputRegressor y
#otro RegressorChain de manera que ambos tengan como sistema monoetiqueta el
#del vecino más cercano (regresión). También para el sistema RandomForestRegressor
#(Este no tiene sistema mono-etiqeuta asociado)
# Calcula el ejemplo media (para cada atributo su valor medio) e imprime su
# evaluación para cada sistema
#
# PISTA: En el siguiente enlace encontrarás una implementación del vecino más
# cercano para regresión
# https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html
#
# PISTA: Para evaluar un ejemplo hay que crear un conjunto de datos de un ejemplo
# de la forma [ [v1, ... ,vn] ] , donde v1 ... vn son los valores de los
# atributos del ejemplo. Un conjunto de datos con un ejemplo con 2 atributos
# con valores 3 y 4 sería: [[3,4]]
from lectores.loadarff_denso_numerico import loadarff_denso_numerico
# Leer el conjunto de datos
[X,Y]=loadarff_denso_numerico('MultiRegressionDatasets/rf1.arff',l=None)
#<TODO BEGIN Ejercicio 1>
from sklearn.neighbors import KNeighborsRegressor
# Crear el modelo MultiOutputRegressor
MOR=MultiOutputRegressor(KNeighborsRegressor())
MOR.fit(X,Y)
# Crear el modelo RegressorChain
RC=RegressorChain(KNeighborsRegressor())
RC.fit(X,Y)
# Crear el modelo RandomForestRegressor
RFR=RandomForestRegressor()
RFR.fit(X,Y)
# Calcular el ejemplo media
EMed=X.mean(axis=0)
# Crear un dataset con ese ejemplo
DMed=[EMed]
# Evaluar el ejemplo media para MultiOutputRegressor y RegressorChain
PMOR=MOR.predict(DMed)
PRC=RC.predict(DMed)
PRFR=RFR.predict(DMed)
print('Predicción del ejemplo media:')
print(' MultiOutputRegressor : {}'.format(PMOR))
print(' RegressorChain : {}'.format(PRC))
print(' RandomForestRegressor : {}'.format(PRFR))
#<TODO END Ejercicio 1>
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Bloque C : Medias de calidad
"""
import numpy as np
# Como en clasificación multi-etiqueta la categoría de un ejemplo está formado
#por una secuencia de 0,1 y su predicción también, es necesario establecer
#medias de calidad diferentes de las de clasificación mono-etiqueta.
# En sklearn hay información sobre las medias de calidad en:
#https://scikit-learn.org/stable/modules/model_evaluation.html
# Se utilizará este ejemplo muy sencillo para comprobar como funcionan las
#diferentes medidas
Y=[[1,0,0,0,0],
[1,1,0,0,0],
[0,1,1,0,1],
[0,0,0,1,0],
[1,0,1,1,0]]
Y=np.array(Y)
P=[[1,0,0,1,0],
[1,0,0,0,0],
[0,1,1,0,1],
[0,0,1,0,0],
[0,0,1,1,0]
]
P=np.array(P)
print('Y=\n{}\n'.format(Y))
print('P=\n{}\n'.format(P))
# accuracy_score funciona igual que en clasificación mono-etiqueta:
#proporción de predicciones correctas. Una predicción es correcta si es correcta
#para todas las etiquetas.
# La medida zero_one_loss es la opuesta: proporción de predicciones nal clasificadas.
#Una predicción está mal clasificada si hay, al menos, una etiqueta mal clasificada.
# La medida hamming_loss retorna la proporción de etiquetas mas clasificadas.
# accuracy_score(y_true=[[0,0,1]],y_pred=[[0,0,1]])
# Out[4]: 1.0
# accuracy_score(y_true=[[0,0,1]],y_pred=[[0,0,0]])
# Out[5]: 0.0
# zero_one_loss(y_true=[[0,0,1]],y_pred=[[0,0,0]])
# Out[6]: 1.0
# hamming_loss(y_true=[[0,0,1]],y_pred=[[0,0,0]])
# Out[7]: 0.3333333333333333
#%% Ejemplo 1:
# Calcula accuracy_score para (Y,P)
from sklearn.metrics import accuracy_score
print('accuracy_score(P,Y) ={:<6.4g}'.format(accuracy_score(P,Y)))
# Las medias f1, recall, precision y jaccard se utilizarán en multi-etiqueta
#con la opción average='samples', lo que indica que se calcula ese valor para cada
#ejemplo, comparando su categoría con la predicción, y luego se calcula la
#media de ese valor para todos sus ejemplos.
from sklearn.metrics import f1_score,recall_score,precision_score,jaccard_score
print('{:32}={:<6.4g}'.format('f1(Y,P,average=\'samples\')', f1_score(Y,P,average='samples')))
print('{:32}={:<6.4g}'.format('recall(Y,P,average=\'samples\')', recall_score(Y,P,average='samples')))
print('{:32}={:<6.4g}'.format('precision(Y,P,average=\'samples\')',precision_score(Y,P,average='samples')))
print('{:32}={:<6.4g}'.format('jaccard(Y,P,average=\'samples\')', jaccard_score(Y,P,average='samples')))
#%% Ejercicio 1
# Calcular e imprimir el valor de f1 de cada ejemplo.
# Calcular e imprimir la media de los valores de f1 anteriores.
# ¿Conincide con el valor f1_samples?
#<TODO BEGIN Ejercicio 1>
print('')
f1s=[]
for ie in range(len(Y)):
Yie=[Y[ie]] # Conjunto de valores reales que contiene solo el valor Y[ie]
Pie=[P[ie]] # Conjunto de predicciones que contiene solo la predicción P[ie]
af1=f1_score(Yie,Pie,average='samples')
f1s.append(af1)
print('Ejemplo {} Y={} P={} f1={:<6.4g}'.format(ie,Y[ie],P[ie],af1))
print('f1 medio = {:<6.4g}'.format(np.mean(f1s)))
#<TODO END Ejercicio 1>
#%% Medidas de calidad multi-regresión
# Las medidas de calidad mono-regresión se pueden usar como multi-regresión,
#sin embargo, su uso ha de estar restringido al caso en que las magnitudes de
#las diferentes salidas sean las mismas.
# Si no es así, habría que hacer un proceso previo para lograrlo, por ejemplo
#usando StandardScaler
# Creación del conjunto de datos de escala diferente para cada salida
import random
from sklearn.preprocessing import StandardScaler
ne=5 # Número de ejemplos
ns=3 # Número de salidas
no=0.1 # Proporción de ruido
RG=random.Random()
RG.seed(1)
Y=[None]*ne
P=[None]*ne
Escala=[None]*ns
for osa in range(ns):
Escala[osa]=RG.uniform(1,10)
for ie in range(ne):
YRow=[None]*ns
PRow=[None]*ns
for isa in range(ns):
v=RG.gauss(0,1)*Escala[isa]+Escala[isa]
YRow[isa]=v
PRow[isa]=v+RG.gauss(0,1)*no
Y[ie]=YRow
P[ie]=PRow
Y=np.array(Y)
P=np.array(P)
print('Y=\n{}\n'.format(Y))
print('P=\n{}\n'.format(P))
# Estandarizar
SS=StandardScaler()
SS.fit(Y)
ssY=SS.transform(Y)
ssP=SS.transform(P)
# Calcular medidas de calidad
from sklearn.metrics import mean_absolute_error,mean_squared_error
print('mean_absolute_error(ssY,ssP)={:<6.4f}'.format(mean_absolute_error(ssY,ssP)))
print('mean_squared_error(ssY,ssP) ={:<6.4f}'.format(mean_squared_error(ssY,ssP)))
#%% Medidas de calidad relativas
# También se suelen utilizar medidas relativas, son las que la medida se divide
#entre la medida de calidad que proporcionaría el sistema media (el que predice
#siempre la media)
# Se puede utilizar sin necesidad de ningún tipo de ajuste de magnitudes siempre
#que se realice el proceso salida a salida.
# Después de calcular la medida realtiva de cada salida, se calculará la media
#de todas ellas
# Para calcular el error de cada salida se puede utilizar multioutput=‘raw_values'
#%% Ejemplo 2:
# Calcular el error cuadrático medio de cada salida del problema del ejemplo anterior
#sin realizar ningún cambio de escala
# Imprimir los resultados
ECMsalidas=mean_squared_error(Y,P,multioutput='raw_values')
print('mean_squared_error(Y,P) ={}'.format(ECMsalidas))
#%% Ejercicio 2:
# Calcular el error relativo cuadrático medio del problema del ejemplo anterior
#suponiendo que la predicción del sistema Media para cada salida es lo almacenado
#en el array Escala
#<TODO BEGIN Ejercicio 2>
# Calcular el Erorr Cuadrático Medio del sistema Media
# La predicción es Escala por cada ejemplo de test
PMedia=[Escala]*ne
# Cálculo del Error Cuadrático Medio del sistema Media
# (Obtener los valores individuales de cada salida, no la media)
ECMMedia=mean_squared_error(Y,PMedia,multioutput='raw_values')
# Divido, en cada salida, el Error Cuadrático medio entre el Error Cuadrático
#Medio del sistema Media
ERCMsalidas=ECMsalidas/ECMMedia
# La media de lo anterior es el valor buscado
ERCM=np.mean(ERCMsalidas)
print('Error relativo cuadrático medio={:<6.4f}'.format(ERCM))
#<TODO END Ejercicio 2>
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Bloque D : Estimación de la Calidad
"""
from lectores.loadarff_denso_numerico import loadarff_denso_numerico
from sklearn.metrics import f1_score,recall_score,precision_score,jaccard_score
from sklearn.metrics import make_scorer
from sklearn.metrics import mean_squared_error,mean_absolute_error
from sklearn.multioutput import MultiOutputClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import cross_validate
from sklearn.preprocessing import StandardScaler
from sklearn.dummy import DummyRegressor
# La manera típica de estimar la calidad de un proceso de aprendizaje es mediante
#validación cruzada: cross_val_score y cross_validate realizan este proceso
# La principal diferencia es que cross_validate permite calcular varias medidas
#a la vez
#%% Ejemplo 1
# Para el conjunto de datos MultiLabelDatasets/emotions.arr estimar f1 del
# sistema SVC con kernel lineal y C=1:
# a) Mediante rescritura
# b) Mediante cross_val_score
# c) Mediante cross_validate calculando además, la precisión y la cobertura
# FÍJATE: en el parámetro scoring y como obtener los valores de las medidas en CVEvals
[X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',6)
BR=MultiOutputClassifier(SVC(kernel='linear',C=1))
print('SVC, emotions:')
# a) Reescritura
BR.fit(X,Y)
P=BR.predict(X)
print('Reescritura f1 ={:<6.4g}'.format(f1_score(Y,P,average='samples')))
# b) cross_val_score
CVS=cross_val_score(BR,X,Y,scoring=make_scorer(f1_score,average='samples'),cv=3)
print('cross_val_score')
print(' CV f1 ={:<6.4g}'.format(CVS.mean()))
# c) cross_validate
# Conjunto de medidas a calcular
scoring={'precision':make_scorer(precision_score,average='samples',zero_division=0),
'cobertura':make_scorer(recall_score,average='samples'),
'f1' :make_scorer(f1_score,average='samples')}
CVEvals=cross_validate(BR,X,Y,scoring=scoring,cv=3)
print('cross_validate')
for scorName in scoring.keys():
print(' Measure {:11}={:<6.4g}'.format(scorName,CVEvals['test_{}'.format(scorName)].mean()))
# Para regresión se pueden utilizar las medidas mono-etiqueta, pero hay que
#realizar una estandarización de las categorías
#%% Ejemplo 2
# Para el conjunto de datos MultiRegressionDatasets/waterqual.arr que tiene 16
#salidas que están al principio estimar:
# a) En reescritura: Error Relativo Cuadrático Medio (ERCM)
# b) Usando cross_val_score: ERCM
# c) Usando cross_validate: ERCM y Error Relativo Absoluto Medio (ERAM)
# FÍJATE: - El uso de StandardScaler para estandarizar todas las categorías y
# poder combinar medidas calcualdas en cada categoría.
# - El uso de DummyRegressor para calcular el error del sistema media.
[X,Y]=loadarff_denso_numerico('MultiRegressionDatasets/waterqual.arff',16,eaf=False)
print('\nRFR, waterqual:')
# Estandarizar las cagetorías
SS=StandardScaler()
SS.fit(Y)
Y=SS.transform(Y)
Media=DummyRegressor()
# a) Reescritura
# RFR
RFR=RandomForestRegressor(random_state=1)
RFR.fit(X,Y)
P=RFR.predict(X)
# Media
Media.fit(X,Y)
PM=Media.predict(X)
ReesERCM=mean_squared_error(Y,P)/mean_squared_error(Y,PM)
print('Reescritura ERCM ={:<6.4g}'.format(ReesERCM.mean()))
# b) cross_val_score
# RFR
RFR=RandomForestRegressor(random_state=1)
CVS=cross_val_score(RFR,X,Y,scoring=make_scorer(mean_squared_error),cv=3)
#Media
CVSM=cross_val_score(Media,X,Y,scoring=make_scorer(mean_squared_error),cv=3)
print('cross_val_score')
print(' ERCM ={:<6.4g}'.format((CVS/CVSM).mean()))
# c) cross_validate
RFR=RandomForestRegressor(random_state=1)
# Conjunto de medidas a calcular
scoring={'ECM':make_scorer(mean_squared_error),
'EAM':make_scorer(mean_absolute_error)}
CVEvals=cross_validate(RFR,X,Y,scoring=scoring,cv=3)
CVEvalsM=cross_validate(Media,X,Y,scoring=scoring,cv=3)
print('cross_validate')
# ERCM
sCVRFR=CVEvals['test_ECM']
sCVRFRM=CVEvalsM['test_ECM']
print(' ERCM ={:<6.4g}'.format((sCVRFR/sCVRFRM).mean()))
# ERAM
sCVRFR=CVEvals['test_EAM']
sCVRFRM=CVEvalsM['test_EAM']
print(' ERAM ={:<6.4g}'.format((sCVRFR/sCVRFRM).mean()))
#%% Ejercicio 1
# Del sistema RegressorChain , con sistema mono-categoría KNeighborsRegressor
#estimar el error para el conjunto de datos del ejemplo anterior mediante la
#medida definina por la función ASError
# Variar el parámetro n_neighbors desde 1 hasta 10 y obtener una estimación de
#la calidad para cada uno de los valores
# Salida:
# n_neighbors= 1 ASError=1.5960
# n_neighbors= 2 ASError=1.2959
# n_neighbors= 3 ASError=1.2187
# n_neighbors= 4 ASError=1.1687
# n_neighbors= 5 ASError=1.1308
# n_neighbors= 6 ASError=1.1141
# n_neighbors= 7 ASError=1.1062
# n_neighbors= 8 ASError=1.0980
# n_neighbors= 9 ASError=1.0883
# n_neighbors=10 ASError=1.0834
import numpy as np
def ASError(Y,P):
"""
Medida de calidad que calcula dada la diferencia de 2 valores si esa diferencia
es menor de 1 utiliza esa diferencia en valor absoluto y si no utiliza la
diferencia al cuadrado.
- Parametros:
Y : matrix exjemplos x salidas con los valores reales
P : matrix exjemplos x salidas con los valores predichos
- Retorna:
La media de las medidas de calidad de cada par real/predicho
"""
AS=0
for r in range(len(Y)):
for c in range(len(Y[r])):
vy=Y[r][c]
vp=P[r][c]
d=np.abs(vy-vp)
if d<1:
AS=AS+d
else:
AS=AS+d*d
return AS/(len(Y)*len(Y[0]))
# <TODO BEGIN Ejerccio 1>
from sklearn.neighbors import KNeighborsRegressor
from sklearn.multioutput import RegressorChain
for k in range(1,10+1):
Reg=KNeighborsRegressor(n_neighbors=k)
RC=RegressorChain(Reg)
CVEvals=cross_validate(RC,X,Y,scoring={'ASError':make_scorer(ASError)},cv=3)
print('n_neighbors={:2} ASError={:6.4f}'.format(k,np.mean(CVEvals['test_ASError'])))
# <TODO END Ejerccio 1>
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Bloque E : Ajuste de hiper-parámetros
"""
from lectores.loadarff_denso_numerico import loadarff_denso_numerico
from sklearn.metrics import f1_score,recall_score,precision_score,jaccard_score
from sklearn.metrics import make_scorer
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import mean_squared_error
from sklearn.svm import SVR
from sklearn.ensemble import RandomForestClassifier
from sklearn.multioutput import RegressorChain
from sklearn.model_selection import cross_validate,cross_val_score
# Para ajuste hiper-parámetros de los sistemas multi-salida que no utilizan otro
#sistema de aprendizaje mono-salida se utilizan las mismas herramientas que
#en el aprendizaje mono-salida: por ejemplo GridSearchCV
#%% Ejemplo 1
# Para el conjunto de datos MultiLabelDatasets/emotions.arrf estimar f1,
#precisión y cobertura usando validación cruzada y el sistema RandomForestClassifier
#ajustando mediante GridSearch:
# min_samples_split entre [0.01,0.05,0.1]
# max_features entre [sqrt', 'log2', None]
[X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',6)
RFC=RandomForestClassifier(random_state=1)
RFC_HP={'min_samples_split':[0.01,0.05,0.1],
'max_features':['sqrt', 'log2', None]
}
GSRFC=GridSearchCV(RFC,RFC_HP,cv=2,scoring=make_scorer(f1_score,average='samples'))
scoring={'precision':make_scorer(precision_score,average='samples',zero_division=0),
'cobertura':make_scorer(recall_score,average='samples'),
'f1' :make_scorer(f1_score,average='samples')}
CVEvals=cross_validate(GSRFC,X,Y,cv=3,scoring=scoring)
print('emotions validación cruzada RFC()')
for scorName in scoring.keys():
print(' Measure {:11}={:<6.4g}'.format(scorName,CVEvals['test_{}'.format(scorName)].mean()))
# Para los sistemas multi-salida que utilizan otros sistemas mono-salida
#los parámetros de estos últimos se ajustan independientemente, pues si se trataran
#de ajustar de manera global tendríamos un problema de orden exponencial.
#%% Ejemplo 2
# Para el conjunto de datos MultiRegressionDatasets/waterqual.arff
# a) estimar el error cuadrático medio en validación cruzada de 3 folds para el
# sistema RegressorChain que utiliza sistema de regresión mono-salida SVR con
# kernel rbf donde se ajustan por GridSearch con una validación cruzada de 2 folds
# y tratando de mejorar el error cuadrático medio los siguientes hyper-parámetros:
# C entre [10**-2,1,10**2]
# gamma entre [2**-2,1,2**2]
# b) Crear el modelo del que se estimó el error (del punto a))
# c) Evaluar el primer ejemplo
# PISTA: Hay que estandarizar las salidas (Y) por que están en escalas diferentes
# PISTA: GridSearchCV necesita greater_is_better=False porque
# mean_squared_error es mejor cuanto más pequeña.
# Sin embargo para cross_val_score no es necesario porque este proceso
# solo obtiene el valor de la métrica, no la compara con otra.
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
[X,Y]=loadarff_denso_numerico('MultiRegressionDatasets/waterqual.arff',16,eaf=False)
# a) Estimar el error
print('Medias y std de cada salida:')
for i in range(len(Y[0])):
print('Y salida {} media={:5.2f} std={:5.2f}'.format(i,Y[:,i].mean(),Y[:,i].std()))
# Estandarizar las salidas
SS=StandardScaler()
YSS=SS.fit_transform(Y)
# Crear el objeto regresor
Reg=SVR(kernel='rbf')
# Hiper-parámetros y sus posibles valores
SVR_HP={'C' :[10**-2,1,10**2],
'gamma':[ 2**-2,1, 2**2]}
# Grid-Search sobre el regresor
GSReg=GridSearchCV(Reg,SVR_HP,cv=2,
scoring=make_scorer(mean_squared_error,greater_is_better=False))
# Cadena que utiliza como regresor el GridSearch anterior
RC=RegressorChain(GSReg)
# Validación cruzada sobre la cadena
CVEvals=cross_val_score(RC,X,YSS, # OJO: Salidas estandarizadas
cv=3,
scoring=make_scorer(mean_squared_error))
print('waterqual validación cruzada RC(SVR):')
print(' error cuadrático medio={:<6.4g}'.format(CVEvals.mean()))
# b) Generar el Modelo
RC.fit(X,YSS)
# c) Predecir el ejemplo 0
PSS=RC.predict([X[0]])
print('Predicción estandarizada:',PSS[0])
P=SS.inverse_transform(PSS)
print('Predicción original:',P[0])
#%% Ejercicio 1
# Repetir la experimentación del Ejemplo 1 pero usando clasificador de cadenas
#que utiliza como sistema mono-salida SVC con kernel lineal donde el
#hyper-parámetro C es optimizado mediante grid-search entre
#[0.001,0.01,0.1,1,10,100,1000] tratando de optimizar el error de clasificación
# Utilizar el conjunto emotions
# Obtener e imprimir: precision, cobertura, f1
[X,Y]=loadarff_denso_numerico('MultiLabelDatasets/emotions.arff',6)
#<TODO BEGIN Ejerccio 1>
from sklearn.multioutput import ClassifierChain
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# Crear el objeto clasificador
Cla=SVC(kernel='linear')
# Hiper-parámetros y sus posibles valores
SVC_HP={'C':[0.001,0.01,0.1,1,10,100,1000]}
# Grid-Search sobre el regresor
GSSVC=GridSearchCV(Cla,SVC_HP,cv=2,scoring=make_scorer(accuracy_score))
# Cadena que utiliza como clasificador el GridSearch anterior
CC=ClassifierChain(GSSVC)
# Métricas a utilizar
scoring={'precision':make_scorer(precision_score,average='samples',zero_division=0),
'cobertura':make_scorer(recall_score,average='samples'),
'f1' :make_scorer(f1_score,average='samples')}
# Validación cruzada sobre la cadena
CVEvals=cross_validate(CC,X,Y,cv=3,scoring=scoring)
# Imprimir los valores de las métricas
print('emotions validación cruzada CC(SVC)')
for scorName in scoring.keys():
print(' Measures {:11}={:<6.4g}'.format(scorName,CVEvals['test_{}'.format(scorName)].mean()))
#<TODO END Ejerccio 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Actividad 1
"""
# Actividad 1
# Copia este fichero al proyecto T1_S2...
# Utilizar el dataset Yeast: (https://mulan.sourceforge.net/datasets-mlc.html)
# Que se encuentra en MultiLabelDatasets/yeast.arff
# Mirar el fichero yeast.xml para averiguar cuantas etiquetas tiene
# Quedarse con 500 ejemplos de manera aleatoria
# Para el sistema RakelO, con sistema monoetiqueta SVC con C=1 y kernel lineal,
#modificar el parámetro labelset_size desde 2 hasta el número de etiquetas.
# Para cada uno de estos valores de número de etiquetas
#estimar utilzando validación cruzadda con 3 folds
# a) el valor de f1
# b) la proporción de ejemplos para los que falla al menos una etiqueta
# Poner los comentarios apropiados para explicar lo que se hace y porqué
# PISTA: seleccionar 500 ejemplos aleatorios:
# RG=random.Random()
# RG.seed(1)
# orden=list(range(len(X)))
# random.shuffle(orden)
# selecc=orden[:500]
# X=X[selecc,:]
# Y=Y[selecc,:]
# PISTA: b) la proporción de ejemplos para los que falla al menos una etiqueta
# Fíjate en los apuntes de teoría: Funciones de pérdida basadas en ejemplos
# ¿Hay alguna medida de pérdida (loss) que retorne 1 si hay almenos una etiqeuta
#mal y 0 si todas las etiquetas están bien?
# Ejemplo de salida
# RakelO es un algoritmo con acciones aleatorias que no usa sem¡illa, por tanto
#en diferentes ejecuciones podrían obtenerse diferentes salidas
# labelset_size: 2 f1=0.6147 Falla al menos una etiqueta=0.852
# labelset_size: 3 f1=0.6179 Falla al menos una etiqueta=0.848
# labelset_size: 4 f1=0.6288 Falla al menos una etiqueta=0.824
# labelset_size: 5 f1=0.6301 Falla al menos una etiqueta=0.8121
# labelset_size: 6 f1=0.6223 Falla al menos una etiqueta=0.822
# labelset_size: 7 f1=0.6251 Falla al menos una etiqueta=0.804
# labelset_size: 8 f1=0.6137 Falla al menos una etiqueta=0.808
# labelset_size: 9 f1=0.6042 Falla al menos una etiqueta=0.806
# labelset_size:10 f1=0.6039 Falla al menos una etiqueta=0.788
# labelset_size:11 f1=0.5951 Falla al menos una etiqueta=0.8
# labelset_size:12 f1=0.5915 Falla al menos una etiqueta=0.788
# labelset_size:13 f1=0.5873 Falla al menos una etiqueta=0.786
# labelset_size:14 f1=0.5873 Falla al menos una etiqueta=0.786
# NOTA User la siguiente implementación de Rakel
from multilearnNew.RakelNew import RakelONew
#<TODO BEGIN Actividad 1>
#<TODO END Actividad 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Multi-salida
Actividad 2
"""
# Actividad 2
# Utilizar el dataset MultiRegressionDatasets/rf1.arff
# No hace falta indicar el número de etiquetas, está indicado en el fichero
#en la primera línea
# Utilizar 100 ejemplos aleatorios
# Imprimir la estimación del MAE y MSE en validación cruzada con 3 folds de
#los sistemas 1.1, 1.2, 2.1 y 2.2:
# 1) Random Forest como regresor multisalida ajustando los hyper-parámetros:
# min_samples_leaf entre [0.01,0.05,0.1]
# max_depth entre [5,10,50]
# 1.1) mediante un GridSearch tomando el de mejor error absoluto
# 1.2) mediante un GridSearch tomando el de mejor error cuadrático
#
# 2) Regresor de cadenas tomando como regresor mono-salida un Random Forest
# ajustando los hyper-parámetros:
# min_samples_leaf entre [0.01,0.05,0.1]
# max_depth entre [5,10,50]
# 2.1) mediante un GridSearch tomando el de mejor error absoluto
# 2.2) mediante un GridSearch tomando el de mejor error cuadrático
#
# Además imprimir el tiempo total que tarda en aprender (fit) en la validación
#cruzada para cada sistema
# NOTAS: MAE : Mean Absolute Error
# MSE : Mean Squared Error
# Utilizar un validación cruzada de 2 folds para la estimación de la medida
# en GridSearch
# PISTA: El resultado de una validación cruzada (CV) incluye el tiempo de entrenamiento.
# Al evaluar lo retornado por una CV en el intérprete de comandos se muestran
# los diferentes elementos que contiene.
# Ejemplo de salida
# 1.1): MAE=7.54896 MSE=219.46257 Tiempo fit= 4.98
# 1.2): MAE=7.61465 MSE=220.41069 Tiempo fit= 4.89
# 2.1): MAE=6.68225 MSE=192.23341 Tiempo fit=40.04
# 2.2): MAE=6.81427 MSE=194.17516 Tiempo fit=39.13
#<TODO BEGIN Actividad 2>
#<TODO END Actividad 2>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo
"""
import numpy as np
from sklearn.svm import LinearSVC
from sklearn.base import BaseEstimator
from sklearn.metrics import roc_auc_score
#%% linearRank class
class linearRank(BaseEstimator):
"""
Class that learns a linear model that generates a global ranking from
batchs of partial ranks.
"""
def __init__(self,LinealClass=None,verbose=0):
"""
Params:
LinealClass : sklearn linear classificator.
Method LinealClass.decision_function must be defined
Default=sklearn.svm.LinearSVC(C=1,fit_intercept=False)
verbose : integer. if 0 no verbosity.
"""
if LinealClass!=None:
self.LinealClass=LinealClass
else:
self.LinealClass=LinearSVC(C=1,fit_intercept=False)
self.verbose=verbose
def fit(self,X,Y):
"""
Learns a linear model using the partial ranks of X defined by Y.
Params:
X : list of examples. An example is a list of values.
Y : if Y is a list of values:
These values are used to compare examples. The examples with
the same value are not compared.
if Y is a list of lists of two values.
The first value is used to campare examples in the batch.
The second value is the batchId. All examples with the same bathcId
own to the same batch. Each example will be only compared to
the examples of their batch.
"""
# Calculate batchs Indices
[batchs,oneBatch]=_getBatchsInd(Y)
if self.verbose>=1:
print('There are {} batchs of length:'.format(len(batchs)),end='')
for b in batchs:
print(' {}'.format(len(b)),end='')
if self.verbose>=2:
print('(indices:',end='')
for bi in b:
print(' {}'.format(bi),end='')
print(')',end='')
print()
# Generate comparisons
cX=[]
cY=[]
for bInd in batchs: # for each batch
if self.verbose>=3:
print('Batch indices:',end='')
for b in bInd:
print(' {}'.format(b))
print('')
for i in range(len(bInd)-1): # for each pair (i,j) of examples in batch
xi=X[i]
yi=Y[i] if oneBatch else Y[i][0]
for j in range(i,len(bInd)):
xj=X[j]
yj=Y[j] if oneBatch else Y[j][0]
if yi!=yj: # There is a comparation
cX.append(np.subtract(xi,xj))
cY.append(np.sign(yi-yj))
cX.append(np.subtract(xj,xi))
cY.append(np.sign(yj-yi))
if self.verbose>=3:
print(' Generated pair (Y[{}]={},Y[{}]={}'
.format())
if self.verbose>=1:
print('Generated {} pairs of comparisons'.format(int(len(cX)/2)))
# Learn the model
self.LinealClass.fit(cX,cY)
if self.verbose>=1:
print('Model learned from {} examples.'.format(len(cX)),end='')
if self.verbose>=2:
print(' W=',end='')
for wv in self.LinealClass.coef_[0]:
print(' {:6.4f}'.format(wv),end='')
print()
return self
def predict(self,X):
"""
Predicts X using the learned model in fit.
Params:
X : list of examples. An example is a list of values.
Returns:
P : list of predictions. Each prediction is a numeric value.
If example A has a prediction greater than example B
then A is preferred to (ordered before than) B.
"""
P=self.LinealClass.decision_function(X)
if self.verbose>=1:
print('Predicted {} examples'.format(len(X)))
return P
#%% multiBatchAUC
def multiBatchAUC(Y,P):
"""
Calculates an AUC measure for each batch in Y.
Params
Y : see linearRank.fit, Y param
P : list of values, predictions.
Returns
average of each Batch's AUC
"""
[batchs,oneBatch]=_getBatchsInd(Y)
if not oneBatch:
Y=list(map(lambda x:x[0],Y))# Take the ranker values
AUC=0
for bInd in batchs:
# Get the batch's Y and P
bY=[Y[i] for i in bInd]
bP=[P[i] for i in bInd]
bAUC=roc_auc_score(bY,bP)
AUC=AUC+bAUC
AUC=AUC/len(batchs)
return AUC
#%% CIndex(Y,P)
def CIndex(Y,P):
"""
Calculates a C-index (concordance index) measure
C-index is calculated as the proportion of corrected ordered pair of examples
https://proceedings.neurips.cc/paper/2007/file/33e8075e9970de0cfea955afd4644bb2-Paper.pdf
Params
Y : list of values, real values.
P : list of values, predictions.
Returns
C-Index value. The proportion of all ordered pairs in Y that concordance
(same order) in P
If Y is void or all values or Y are the same (no ordered pairs) None is returned
"""
E=len(Y) # number of examples
Ci=0
nPairs=0
for i in range(E-1):
for j in range(i,E):
if Y[i]!=Y[j]: # There is a pair that is ordered
nPairs=nPairs+1
if P[i]==P[j]: # If tie
Ci=Ci+0.5 # half sucess (0.5 of 1)
elif np.sign(P[i]-P[j])==np.sign(Y[i]-Y[j]): # Same order
Ci=Ci+1 # Full sucess (1 of 1)
if nPairs==0:
return None
else:
return Ci/nPairs
#%% multiBatchCIndex
def multiBatchCIndex(Y,P):
"""
Calculates a C-index (concordance index) measure for each batch in Y.
Params
Y : see linearRank.fit, Y param
P : list of values, predictions.
Returns
Average of each Batch's C-index. If a C-Index is None it will not be used
in the avarage
If all batchs have a None C-Index None is returned
"""
[batchs,oneBatch]=_getBatchsInd(Y)
if not oneBatch:
Y=list(map(lambda x:x[0],Y))# Take the ranker values
Ci=0
validBatchs=0
for bInd in batchs:
# Get the batch's Y and P
bY=[Y[i] for i in bInd]
bP=[P[i] for i in bInd]
bCi=CIndex(bY,bP)
if bCi!=None:
Ci=Ci+bCi
validBatchs=validBatchs+1
if validBatchs>0:
return Ci/validBatchs
else:
return None
#%% _getBatchsInd function
def _getBatchsInd(Y):
if type(Y[0]) not in (list,np.ndarray): # There are no batch Ids
batchs=[list(range(len(Y)))] # There are only one batch
oneBatch=True
return [batchs,oneBatch]
# There are batch Ids
Bn=np.array(list(map(lambda p:p[1],Y))) # Batchs numbers
B=np.unique(Bn) # Different batchs
batchs=[]
for b in B:
batchs.append(np.where(Bn==b)[0].tolist())
oneBatch=False
return [batchs,oneBatch]
#%% Example of use
# X=[[1,2],[3,4],[5,4],[8,9],[4,7]]
# Y=[1,1,0,1,0]
# Y=[[1,0],[1,0],[0,1],[1,1],[0,0]]
# Ranker=linearRank(verbose=2)
# Ranker.fit(X,Y)
# P=Ranker.predict(X)
# print('AUC={:6.4f}'.format(multiBatchAUC(Y,P)))
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 3 : Ranking
Bloque A : Conjunto de datos de ranking
"""
#%%
# Los conjuntos de datos de ranking están formados por lotes.
# Cada lote tiene varios ejemplos con un valor numérico cada uno
# El valor numérico (suele ser un entero) solo sirve para establecer preferencias
#entre los ejemplos, de manera que dados dos ejemplos, si el ejemplo A tiene un valor
#numérico mayor el ejemplo B, esto indica que se prefiere el A al B.
# Si el valor numérico es el mismo, no hay indicación de preferencia entre ellos.
# Estas preferencias solo son aplicables dentro de un bloque, nunca se pueden
#establecer preferencias entre ejemplos de diferentes bloques.
# En esta práctica se indicará que un ejemplo A se prefiere a otro B de la
#siguiente manera: A>~B. Si no se establecen preferencias se indica A=~B.
# La estructura de datos propuesta para guardar la información anterior consiste
#en que la categoría de cada ejemplo sea una lista con dos valores:
#Y=[[RankVal1,Lote1],[RankVal2,Lote2],...]
# Donde RankVal es el valor de ranking, el que se compara para establecer preferencias
#y Lote es el identificador del lote (generalmente un entero)
#Cuando hay un solo lote la categoría puede ser una lista de RankVal
#%% Ejemplo 1. Un lote
# En una competicción de robots se ha logrado la siguiente clasificación:
# R1 > ~R3 >~ R4 >~ R2
#Donde cada robot está definido como:
# R1 : [1, 34]
# R2 : [3, 25]
# R3 : [2, 14]
# R4 : [2, 45]
# Crear la estructura de datos que almacene la información de esas preferencias
# Robots:
R1=[1, 34]
R2=[3, 25]
R3=[2, 14]
R4=[2, 45]
# Orden (solo un lote)
X=[R1,R3,R4,R2]
Y=[4 ,3 ,2 ,1 ]
#%% Ejemplo 2. Varios Lotes
# Hay varios críticos que han realizado las siguientes preferencias de restaurantes:
# R2 >~ R1,
# R2 >~ R4 =~ R3 >~ R1
# R2 >~ R3 >~ R4 >~ R1
# Los atributos que definen cada restaurante son:
# R1 : [9.4, 5.0]
# R2 : [0.5, -1]
# R3 : [3.5, 0]
# R4 : [7.5, 2.3]
# Crear la estructura de datos que almacene la información de esas preferencias
# Restaurantes
R1=[9.4, 5.0]
R2=[0.5, -1]
R3=[3.5, 0]
R4=[7.5, 2.3]
X=[R2 ,R1 ,R2 ,R4 ,R3 ,R1 ,R2 ,R3 ,R4 ,R1]
Y=[[2,1] ,[1,1] ,[3,2] ,[2,2] ,[2,2] ,[1,2] ,[4,3] ,[3,3] ,[2,3] ,[1,3]]
#%% Ejercicio 1
# En un laboratorio de pruebas se hacen pruebas a muestras de acero.
# Las muestras de aceros vienen determinadas por la composición química siguiente:
# A1 : Fe=99.50%, C=0.5%
# A2 : Fe=98% , C=0.5%, Cd=1.5%
# A3 : Fe=99% , Cd=1%
# Se han realizado tres pruebas:
# P1 : A1 >~ A2
# P2 : A2 >~ A1 >~ A3
# P3 : A1 >~ A3
# Crear la estructura de datos que almacene la información de esas preferencias
#<TODO BEGIN Ejerccio 1>
A1=[99.50,0.5,0]
A2=[98 ,0.5,1.5]
A3=[99 ,0 ,1]
X=[A1 ,A2 ,A2 ,A1 ,A3 ,A1 ,A3]
Y=[[2,1] ,[1,1] ,[3,2] ,[2,2] ,[1,2] ,[2,3] ,[1,3]]
#<TODO END Ejerccio 1>
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 3 : Ranking
Bloque B : Aprendizaje a partir de ranking bipartito en un solo lote
"""
#%%
# El objetivo del aprendizaje automático de rankings es crear un modelo que dado
#un ejemplo le otorgue un valor numérico de manera que si este valor es mayor
#que el dado a otro ejemplo indica que se prefiere el primero al segundo.
# Estamos en la situación que el valor de ranking (la categoría) solo tiene dos
#valores se llamarán positivo y negativo.
# En esta práctica se trabajará solo con un lote.
# El objetivo es el ordenar los ejemplos del test de manera que los positivos
#estén antes que los negativos.
# La clase linearRank realiza el proceso de aprendizaje de un modelo lineal
#%% conjunto de datos aleatorio
import random
import numpy as np
from Rank import linearRank
from sklearn.metrics import roc_auc_score
from sklearn.metrics import RocCurveDisplay
def genXY(RG,w,nEje,ruido=0):
X=[None]*nEje
Y=[None]*nEje
at=len(w)
for e in range(nEje):
x=list(RG.gauss(0,1) for i in range(at))
X[e]=x
vRuido=RG.uniform(1,2)*RG.choice([-1,+1])*ruido # Uniform distribution in (-2*ruido;-1] U [+1;+2*ruido)
vx=np.dot(x,w)# Producto escalar
v=vx+vRuido
# Valor 1 si positivo, 0 si negativo
if v>0:
Y[e]=1
else:
Y[e]=0
return [X,Y]
nAtt=10 # Atributos
RG=random.Random() # Generador de aletoriedad
RG.seed(1)
w=list(RG.gauss(0,1) for i in range(nAtt)) # Pesos de la función lineal que dará las evaluaciones
nEjeTr=100 # Ejemplos de entrenamiento
nEjeTe=100 # Ejemplos de test
ruido=1 # Ruido. 0 no hay ruido, cuanto mayor, más ruido
[XTr,YTr]=genXY(RG,w,nEjeTr,ruido)
[XTe,YTe]=genXY(RG,w,nEjeTe,ruido)
print('Generado entranamiento({} ejemplos) test({} ejemplos)'.format(len(XTr),len(XTe)))
#%% Realizar un entrenamiento y obtener una evaluación del test
# Clasificador, lineal y sin término independiente.
# Genera como salida la evaluación de la función lineal
lRank=linearRank()
print('Generando modelos a partir del train y evaluando el test')
Model=lRank.fit(XTr,YTr)
Eval=Model.predict(XTe)
#%% Evaluar la calidad de los modelos de ranking
# La calidad de los modelos de ranking bipartitos se mide usando AUC
#La medida roc_auc_score realiza este cálculo
print('Evaluando la calidad de los modelos de ranking')
AUC=roc_auc_score(YTe,Eval)
print('AUC={:6.4f}'.format(AUC))
RocCurveDisplay.from_predictions(YTe,Eval)
#%% Ejercicio 1
# Variar el ruido desde 0 hasta 10. Para cada nivel de ruido generar un conjunto
#de datos con ese ruido. Crear un modelo de ranking con el entrenamiento y
#calcular el AUC del test. Imprimir el AUC.
# EXTRA: Hacer una gráfica en la que vea como varía el AUC en función del ruido
AUCs=[]# Se almacenarán los AUC en esta lista
ruidos=list(range(10+1)) # Ruidos de 0 a 10
#<TODO BEGIN Ejerccio 1>
# Cálculo del AUC por cada nivel de ruido
for ruido in ruidos:
# Generación del conjunto de ejemplos
[XTr,YTr]=genXY(RG,w,nEjeTr,ruido)
[XTe,YTe]=genXY(RG,w,nEjeTe,ruido)
# Aprendizaje / evaluación
Model=lRank.fit(XTr,YTr)
Eval=Model.predict(XTe)
# Cálculo del AUC
AUC=roc_auc_score(YTe,Eval)
# Almnacenar el AUC en AUCs
AUCs.append(AUC)
# Imprimir
print('ruido={:2} AUC={:6.4f}'.format(ruido,AUC))
#<TODO END Ejerccio 1>
# Gráfica de AUC en función del ruido
import matplotlib.pyplot as plt
plt.figure()
plt.title('Ejercicio 1 (Extra). AUC en función del ruido')
plt.xlabel('Ruido')
plt.xticks(ruidos)
plt.ylabel('AUC')
plt.grid()
plt.plot(ruidos,AUCs)
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 2 : Ranking
Bloque C : Aprendizaje de ranking multipartito de un solo lote
"""
#%%
# Estamos en la situación que el valor de ranking (la categoría) tiene
#diferentes valores numéricos, puden ser valores discretos o en coma flotante.
# En esta práctica se trabajará solo con un lote.
# El objetivo es el ordenar los ejemplos del test de manera que los ejemplos
#con valores mayores estén antes que los ejemplos con valores menores.
# La métrica CIndex (Concordance index) calcula la proporción de pares
#correctamente ordenados
#%% Leer qsar_aquatic_toxicity.csv
# Leer datos de datasets/qsar_aquatic_toxicity.csv
#https://archive.ics.uci.edu/ml/datasets/QSAR+aquatic+toxicity
# La última columna es la categoría (calidad del agua), es un valor en coma flotante
#pero nos interesa solo el valor entero, así que se aplicará la función round
# El resto de columnas (toas menos la última) será los atributos
# Crear valiables X,Y donde se guarden los datos de este conjunto
# Se tomarán solo los 100 primeros ejemplos (por razones de eficiencia)
import csv
X=[]
Y=[]
with open('datasets/qsar_aquatic_toxicity.csv') as csvfile:
Reader=csv.reader(csvfile,delimiter=';',quoting=csv.QUOTE_NONNUMERIC)
for row in Reader:
X.append(row[:len(row)-1])
Y.append(round(row[len(row)-1]))
X=X[:100]
Y=Y[:100]
#%% Estimación de CIndex
# Realizar una validación cruzada con 3 folds del sistema linearRank con el
#conjuto de datos anterior. Estimar CIndex
from Rank import linearRank,CIndex
from sklearn.model_selection import cross_validate
from sklearn.metrics import make_scorer
Ranker=linearRank(verbose=1)
cv=cross_validate(Ranker,X,Y,scoring=make_scorer(CIndex),cv=3)
print('CIndex={:6.4f}'.format(cv['test_score'].mean()))
#%% Ejercicio 1
# Los valores de la Y son enteros en [0;10], pero solo interesa la calidad del
#agua según los siguientes criterios:
# Mala : [0; 4]
# Regular : [5; 7]
# Buena : [8:10]
# Modifica los valores de la Y de manera que las aguas con el mismo criterio tengan
#un mismo valor y sea acorde a Buena>~Regular>~Mala
# Repite el experimento anterior y calcula la estimación del CIndex para este caso
# El CIndex, ¿Ha mejorado o empeorado?
#<TODO BEGIN Ejerccio 1>
# Generando Y3 donde Mala->0, Regular->1, Buena->2
Y3=[]
for v in Y:
if v <=4:
c=0
elif v<=7:
c=1
else:
c=2
Y3.append(c)
# Experimentación
cv=cross_validate(Ranker,X,Y3,scoring=make_scorer(CIndex),cv=3)
print('Y3: CIndex={:6.4f}'.format(cv['test_score'].mean()))
#<TODO END Ejerccio 1>
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 3 : Ranking
Bloque D : Aprendizaje de ranking con varios lotes
"""
#%%
# Como se indicó en el bloque A, los conjuntos de ejemplos de ranking están agrupados
#por lotes. Solo los objetos del mismo lote formarán pares (preferencias)
#%% Ejemplo restaurantes
# Datos del bloque A
# Restaurantes
R1=[9.4, 5.0]
R2=[0.5, -1]
R3=[3.5, 0]
R4=[7.5, 2.3]
X=[R2 ,R1 ,R2 ,R4 ,R3 ,R1 ,R2 ,R3 ,R4 ,R1]
Y=[[2,1] ,[1,1] ,[3,2] ,[2,2] ,[2,2] ,[1,2] ,[4,3] ,[3,3] ,[2,3] ,[1,3]]
# Restaurantes a ordenar y sus nombres (test)
RN1=[7.4, 6]
RN2=[4.5, 0.5]
RN3=[0.9, -2.5]
RN4=[2.6, 2.9]
RN5=[6.0, 4.6]
Nombres=['RN1','RN2','RN3','RN4','RN5']
# Partiendo del ejemplo de los restuarantes (Bloque A) se quieren ordenar cinco
#nuevos restaurantes (RN*) de los que no hay críticas.
# Detallar el proceso e imprimir el nombre los restaurantes nuevos de mejor a peor
#junto con su valoración del ranking
# SOLUCIÓN:
# Crear un modelo de ranking con los datos del bloque A
from Rank import linearRank
Ranker=linearRank()
Ranker.fit(X,Y)
# Crear el conjunto de test y evaluarlo con el modelo anterior
XTe=[RN1,RN2,RN3,RN4,RN5]
P=Ranker.predict(XTe)
# Ordenar de mayor puntuación a menor e imprimir la valoración del ranking
import numpy as np
orden=np.argsort(-P) # Como artgsort ordena de menos a más, se cambia el signo de P para que ordene P de más a menos
for i in orden:
print('{} valor ranking:{:+6.4f}'.format(Nombres[i],P[i]))
#%% Medida del error
# Un grupo de críticos ha valorado algunos de los nuevos restaurantes.
#Calcular una medida de calidad para la predicción anterior (P) tomando como
# correcto(Y) lo valorado por los criticos:
# Valoraciónes de los críticos
# Cada fila es un crítico y las 5 columnas son los 5 restaurantes.
#Cada crítico solo valoró (cuanto más mejor) alguno de los restaurantes,
# si no hay valoración se indica con None
Valoraciones=[
#RN1 ,RN2 ,RN3 ,RN4 ,RN5 <- Restaurante (Objeto)
[3 ,None,None,4 ,2], # Crítico 1
[2 ,4 ,None,None,8], # Crítico 2
[None,27 ,53 ,45 ,27]] # Crítico 3
# SOLUCIÓN:
# Por cada Crítico se crea un lote con los restaurantes que ha valorado.
# Se añaden a YTe el par [valoración,lote] y en PTe P[restaurante]
YTe=[] # Valor y lote en cada ejemplo de test
PTe=[] # Predicción del modelo en cada ejemplo de test
Res=[] # (el restaurante no es necesario para la solución, se muestra para mejora académica)
for ilote in range(len(Valoraciones)): # Por cada lote
lote=Valoraciones[ilote]
for irest in range(len(lote)): # Por cada restaurante (objeto)
valor=lote[irest]
if valor!=None:
YTe.append([valor,ilote])
PTe.append(P[irest])
Res.append(irest)
# Imprimir YTe, P y el restaurante (el restaurante no es necesario para la solución, se muestra para mejora académica)
print('\nYTe | PTe | Nombre restaurante (Objeto)')
for i in range(len(YTe)):
print('[{:2},{}] | {:+6.4f} | {}'.format(YTe[i][0],YTe[i][1],PTe[i],Nombres[Res[i]]))
# Como es multi-evaluado se utilizará CIndex y como hay lotes se utilizará:
from Rank import multiBatchCIndex
ResCI=multiBatchCIndex(YTe,PTe)
print('\nCIndex de las valoraciones del modelo sobre los nuevos restaurantes={:6.4f}'
.format(ResCI))
#%% Ejercicio 1
# El restaurante NR6 gusta más que el NR7. Entrena un modelo con los datos
#de los restaurantes del bloque A (X,Y) y los nuevos (XTe,YTe), evalúa NR6 y NR7 e
# imprime los valores de ranking que ha predicho el modelo.
#Comprueba si este modelo es coherente con NR6 >~ NR7
NR6=[3.5, 0.5]
NR7=[6.1, 4.7]
#<TODO BEGIN Ejercicio 1>
# En el ejemplo anterior se había creado la YTe, esto es, para cada valoración de un experto
#se indica el par, [valoración,lote], donde el lote coincide con el experto, ahora hay que hacer
#lo mismo pero el lote no ha de coincidir con ninguno de los Y, por lo tanto simplemente se repite
#el proceso anterior pero se suma (maxLotePrev+1) a iterador del blucle. Donde maxLotePrev es
#el valor máximo de todos los lotes de Y.
# Falta indicar los atributos (X) del restaurante. Se concatenan los atributos de los ejemplos que se
#evalúan. Estos atributos están en XTe
XTeObjLote=[] # Atributos del ejemplo de test
YTeObjLote=[] # [Valor,Lote] del ejemplo de test
maxLotePrev=np.array(Y)[:,1].max() # Obtener el valor máximo de id de lote de los ejemplos anteriores
for ilote in range(len(Valoraciones)): # Por cada lote
lote=Valoraciones[ilote]
for irest in range(len(lote)): # Por cada restaurante (objeto)
valor=lote[irest]
if valor!=None:
XTeObjLote.append(XTe[irest]) # Concata los atributos del restaurante
YTeObjLote.append([valor,ilote+(maxLotePrev+1)]) # Los lotes serán valores consecutivos a partir del máximo del idLote
# Crear un nuevo conjunto de datos concatenando los 2 anteriores
XTodoTr=X+XTeObjLote
YTodoTr=Y+YTeObjLote
# Entrenar el modelo
Ranker=linearRank()
Ranker.fit(XTodoTr,YTodoTr)
# Crear el conjunto de test y evaluarlo
XTodoTe=[NR6,NR7]
PTodoTe=Ranker.predict(XTodoTe)
print('Valor de ranking para NR6:{:+6.4f}'.format(PTodoTe[0]))
print('Valor de ranking para NR7:{:+6.4f}'.format(PTodoTe[1]))
if PTodoTe[0] > PTodoTe[1]:
print('El modelo SÍ es coherente con RN6 >~ RN7 ya que Model(RN6)={:+6.4f} > Model(RN7)={:+6.4f}'
.format(PTodoTe[0],PTodoTe[1]))
else:
print('El modelo NO es coherente con RN6 >~ RN7 ya que Model(RN6)={:+6.4f} < Model(RN7)={:+6.4f}'
.format(PTodoTe[0],PTodoTe[1]))
#<TODO END Ejercicio 1>
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 3 : Ranking
Bloque E : Estimación de la medida de calidad con varios lotes
"""
#%%
# Como se indicó en el bloque A, los conjuntos de ejemplos de ranking están agrupados
#por lotes. Para estimar una medida de calidad se realizará una validación cruzada
#pero agrupando por lote, no por ejemplo.
#%% Ejemplo restaurantes
# Datos del bloque A
# Restaurantes
R1=[9.4, 5.0]
R2=[0.5, -1]
R3=[3.5, 0]
R4=[7.5, 2.3]
X=[R2 ,R1 ,R2 ,R4 ,R3 ,R1 ,R2 ,R3 ,R4 ,R1]
Y=[[2,1] ,[1,1] ,[3,2] ,[2,2] ,[2,2] ,[1,2] ,[4,3] ,[3,3] ,[2,3] ,[1,3]]
#%% Estimación del error con lotes
from Rank import linearRank
from Rank import multiBatchCIndex
from sklearn.model_selection import cross_validate
from sklearn.metrics import make_scorer
from sklearn.model_selection import GroupKFold
ranker=linearRank()
# Los grupos son los lotes
groups=[]
for [value,idLote] in Y:
groups.append(idLote)
# Se ha de utilizar una validación cruzada donde se indica en groups los lotes
#y se utiliza como cv un objeto de la clase GroupKFold
CVscores=cross_validate(ranker,X,Y,scoring=make_scorer(multiBatchCIndex),
groups=groups,cv=GroupKFold(n_splits=3)) # No puede haber más folds que lotes
print('CIndex (proporción de pares correctamente ordenados en cada lote): {:6.4f}'
.format(CVscores['test_score'].mean()))
#%% Ejercicio 1
# Haz de crítico y añade unas nuevas preferencias sobre restaurantes.
# Escribe las preferencias como R1 >~ R2, o R2 <~ R4
# Codifica estas preferencias y añádelas a X e Y
# Estima el error con el máximo número de folds
#<TODO BEGIN Ejercicio 1>
# Preferencias como crítico
# R2 <~ R4
# R3 >~ R4 >~ R1
# Codificación de las preferencias en los lotes 4 y 5
XN=[ R2 , R4 , R3 ,R4 , R1]
YN=[[2,4],[3,4],[3,5],[2,5],[1,5]] # Se empieza en el lote 4, que el último fue el 3
# Se concatenan los datos
XAll=X+XN
YAll=Y+YN
# Los grupos son los lotes
groups=[]
for [value,idLote] in YAll:
groups.append(idLote)
# Estimar la medida de calidad mediante cross validation
CVscores=cross_validate(ranker,XAll,YAll,scoring=make_scorer(multiBatchCIndex),
groups=groups,cv=GroupKFold(n_splits=5))# Hay 5 grupos, así que max de folds = 5
# Imprimir
print('Datos nuevos: CIndex (proporción de pares correctamente ordenados en cada lote): {:6.4f}'
.format(CVscores['test_score'].mean()))
#<TODO END Ejercicio 1>
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 3 : Ranking
Actividad 1
"""
#%% Actividad Presencial 1
def getObjeto(row):
idEje=int(row[0])
XEje=[]
for i in range(2,len(row)):
if i==4: # Minor grouop, hay 12 diferentes del 0 al 11 (ver README-en.txt)
v=int(row[i])
for iv in range(12):
if iv==v:
XEje.append(1)
else:
XEje.append(0)
XEje.append(float(row[i]))
return [idEje,XEje]
def getLote(Objetos,row,idLote):
LoteX=[]
LoteY=[]
for i in range(len(row)): # Por cada sushi
v=int(row[i])
if v>=0:
LoteX.append(Objetos[i])
LoteY.append([v,idLote])
return [LoteX,LoteY]
#%% Leer los datos y generar los lotes
# https://preference-learning.org/index.html#Datasets
# https://www.kamishima.net/sushi/
import csv
# Leer las descripciones de los objetos (descripciones de sushi)
Objetos={} # Diccionario de key:idSushi, value:atributos de sushi
with open('datasets/sushi/sushi3-2016/sushi3.idata', newline='') as icsv:
csvreader = csv.reader(icsv, delimiter='\t')
for row in csvreader:
[idEje,XEje]=getObjeto(row)
print(row)
print(XEje)
Objetos[idEje]=XEje
print('Leídas {} descripciones de sushi'.format(len(Objetos)))
#%% Leer las puntuaciones y crear el conjunto de datos de ranking
Xsushi=[]
Ysushi=[]
with open('datasets/sushi/sushi3-2016/sushi3b.5000.10.score', newline='') as icsv:
csvreader = csv.reader(icsv, delimiter=' ')
idLote=0
for row in csvreader:
[LoteX,LoteY]=getLote(Objetos,row,idLote)
Xsushi=Xsushi+LoteX
Ysushi=Ysushi+LoteY
idLote=idLote+1
# if idLote>=1000:
# break
print('Creados {} ejemplos valorados a partir de {} lotes'.
format(len(Ysushi),idLote))
#%% Actividad presencial 1: Estimar la calidad
# Estima el CIndex del conjunto de datos (Xsushi,Ysushi) utilizando como ranker
#linearRank con valores por defecto.
# Imprime el resultado.
#<TODO BEGIN Actividad 1>
from Rank import linearRank,multiBatchCIndex
from sklearn.model_selection import cross_validate
from sklearn.metrics import make_scorer
from sklearn.model_selection import GroupKFold
# Los grupos son los lotes
groups=[]
for [value,idLote] in Ysushi:
groups.append(idLote)
ranker=linearRank()
CVscores=cross_validate(ranker,Xsushi,Ysushi,scoring=make_scorer(multiBatchCIndex),
groups=groups,cv=GroupKFold())
print('CIndex (proporción de pares correctamente ordenados en cada lote): {:6.4f}'
.format(CVscores['test_score'].mean()))
#<TODO END Actividad 1>
#%% Actividad presencial 2: compara dos sushis
# Utiliza el modelo del que se ha estimado el CIndex para evaluar estos 2 sushis:
# Objetos según se leen del CSV
s1Ori=['7','tamago','1','1','9','2.36807095343681','1.86622320768662','1.03246753246753','0.84']
s2Ori=['8','toro','1','0','1','0.551854655563967','2.05753217259652','4.48545454545455','0.8']
# Objetos con valores numéricos
[ids1,s1]=getObjeto(s1Ori)
[ids2,s2]=getObjeto(s2Ori)
# 2.1 : Crea el modelo
# 2.2 : Evalua ambos sushis, imprime sus evaluaciones
# 2.3 : Implementa el código que indique cual es el mejor
#<TODO BEGIN Actividad 2>
# Paso a paso
# 2.1 : Crea el modelo
ranker.fit(Xsushi,Ysushi)
# 2.2 : Evalua ambos sushis, imprime sus evaluaciones
ev1=ranker.predict([s1])[0]
ev2=ranker.predict([s2])[0]
print('Sushi {} evaluado con {:+6.4f}'.format(s1Ori[0],ev1))
print('Sushi {} evaluado con {:+6.4f}'.format(s2Ori[0],ev2))
# 2.3 : Implementa el código que indique cual es el mejor
if ev1>ev2:
mejor=s1Ori[0]
peor= s2Ori[0]
else:
mejor=s2Ori[0]
peor= s1Ori[0]
print('Sushi {} mejor que sushi {}'.format(mejor,peor))
#<TODO END Actividad 2>
# -*- coding: utf-8 -*-
"""
Tema 1 : Problemas Complejos de Aprendizaje Supervisado
Sesión 3 : Ranking
Actividad 1
"""
#%% Actividad Alumno 1
def getObjeto(row):
idEje=int(row[0])
XEje=[]
for i in range(2,len(row)):
if i==4: # Minor grouop, hay 12 diferentes del 0 al 11 (ver README-en.txt)
v=int(row[i])
for iv in range(12):
if iv==v:
XEje.append(1)
else:
XEje.append(0)
XEje.append(float(row[i]))
return [idEje,XEje]
def getLote(Objetos,row,idLote):
LoteX=[]
LoteY=[]
for i in range(len(row)): # Por cada sushi
v=int(row[i])
if v>=0:
LoteX.append(Objetos[i])
LoteY.append([v,idLote])
return [LoteX,LoteY]
#%% Leer los datos y generar los lotes
# https://preference-learning.org/index.html#Datasets
# https://www.kamishima.net/sushi/
import csv
# Leer las descripciones de los objetos (descripciones de sushi)
Objetos={} # Diccionario de key:idSushi, value:atributos de sushi
with open('datasets/sushi/sushi3-2016/sushi3.idata', newline='') as icsv:
csvreader = csv.reader(icsv, delimiter='\t')
for row in csvreader:
[idEje,XEje]=getObjeto(row)
print(row)
print(XEje)
Objetos[idEje]=XEje
print('Leídas {} descripciones de sushi'.format(len(Objetos)))
#%% Leer las puntuaciones y crear el conjunto de datos de ranking
Xsushi=[]
Ysushi=[]
with open('datasets/sushi/sushi3-2016/sushi3b.5000.10.score', newline='') as icsv:
csvreader = csv.reader(icsv, delimiter=' ')
idLote=0
for row in csvreader:
[LoteX,LoteY]=getLote(Objetos,row,idLote)
Xsushi=Xsushi+LoteX
Ysushi=Ysushi+LoteY
idLote=idLote+1
# if idLote>=1000:
# break
print('Creados {} ejemplos valorados a partir de {} lotes'.
format(len(Ysushi),idLote))
#%% Actividad presencial 1: Estimar la calidad
# Estima el CIndex del conjunto de datos (Xsushi,Ysushi) utilizando como ranker
#linearRank con valores por defecto.
# Imprime el resultado.
#<TODO BEGIN Actividad 1>
# # Estimación por validación cruzada (3 folds) del CIndex
# cv = cross_validate(ranker, Xsushi, Ysushi,
# scoring=make_scorer(CIndex),
# cv=3)
# print('CIndex (proporción de pares correctamente ordenados en cada lote): {:6.4f}'
# .format(cv['test_score'].mean()))
#<TODO END Actividad 1>
#%% Actividad presencial 2: compara dos sushis
# Utiliza el modelo del que se ha estimado el CIndex para evaluar estos 2 sushis:
# Objetos según se leen del CSV
s1Ori=['7','tamago','1','1','9','2.36807095343681','1.86622320768662','1.03246753246753','0.84']
s2Ori=['8','toro','1','0','1','0.551854655563967','2.05753217259652','4.48545454545455','0.8']
# Objetos con valores numéricos
[ids1,s1]=getObjeto(s1Ori)
[ids2,s2]=getObjeto(s2Ori)
# 2.1 : Crea el modelo
# 2.2 : Evalua ambos sushis, imprime sus evaluaciones
# 2.3 : Implementa el código que indique cual es el mejor
#<TODO BEGIN Actividad 2>
# Paso a paso
# 2.1 : Crea el modelo
import numpy as np
from xgboost import XGBRanker
# === 1. Convertir Xsushi y Ysushi a arrays ===
X = np.array(Xsushi, dtype=np.float32)
y = np.array([v for (v, lote) in Ysushi], dtype=np.float32)
qids = np.array([lote for (v, lote) in Ysushi], dtype=np.int32)
# === 2. Ordenar por ID de lote (requerido por XGBoost) ===
order = np.argsort(qids, kind="stable")
X = X[order]
y = y[order]
qids = qids[order]
# === 3. Obtener tamaños de grupo ===
_, counts = np.unique(qids, return_counts=True)
group_sizes = counts.tolist()
# === 4. Crear modelo con valores por defecto ===
model = XGBRanker(
objective="rank:ndcg", # valor por defecto para ranking
eval_metric="ndcg", # métrica estándar de ranking
)
# === 5. Entrenar el modelo ===
model.fit(X, y, group=group_sizes)
print("Modelo XGBRanker entrenado correctamente.")
# 2.2 : Evalua ambos sushis, imprime sus evaluaciones
p=model.predict([s1,s2])
# 2.3 : Implementa el código que indique cual es el mejor
if p[0]>p[1]:
mejor=s1Ori[1]
peor= s2Ori[1]
else:
mejor=s2Ori[1]
peor= s1Ori[1]
print('Sushi {} mejor que sushi {}'.format(mejor,peor))
#<TODO END Actividad 2>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 1 : Cuantificación
Bloque A : Problemas de cuantificación
"""
from genToy import genToy
from quantificationlib.baselines.cc import CC
from quantificationlib.baselines.ac import AC,PAC
from quantificationlib.multiclass.df import DFX
import numpy as np
import random
from sklearn.svm import SVC
import matplotlib.pyplot as plt
# Generar conjuntos con diferente prevalencia en entrenamiento que en test
preTr=0.5 # Prevalencia de entrenamiento
preTe=0.05 # Prevalencia de test
print('Prevalencia de generación: entrenamiento:{:4} test : {}'.format(preTr,preTe))
RG=random.Random()
RG.seed(1)
[XTr,YTr]=genToy(RG,100,prevalencia=preTr) # Mirar genToy.py
[XTe,YTe]=genToy(RG,100,prevalencia=preTe)
#%% Representación gráfica
def pintaXY(X,Y,ax,tit,color0,color1):
iPos=np.nonzero(Y)[0] # índices positivos
iNeg=np.nonzero(Y-1)[0] # índices negativos
ax.set_title(tit)
ax.set_xlim([-4,+4])
ax.set_ylim([-4,+4])
ax.set_xlabel('Atributo 0')
ax.set_ylabel('Atributo 1')
ax.plot(X[iPos,0],X[iPos,1],'o',color=color1,label='Pos')
ax.plot(X[iNeg,0],X[iNeg,1],'o',color=color0,label='Neg')
if color0!=color1:
ax.legend()
fig,axs=plt.subplots(1,2,figsize=[6,3],layout='tight')
pintaXY(XTr,YTr,axs[0],'Entrenamiento\nPrevalencia={}'.format(preTr),color0='blue',color1='green')
pintaXY(XTe,YTe,axs[1],'Test (Sin categoría)\nPrevalencia=¿?',color0='grey',color1='grey')
plt.savefig('CuantiEntrenamiento.pdf')
fig=plt.figure(figsize=[3,3],layout='tight')
pintaXY(XTe,YTe,plt.axes(),'Test\nPrevalencia={}'.format(preTe),color0='blue',color1='green')
plt.savefig('CuantiTest.pdf')
#%% Proceso de cuantificación
# Clasificador
Class=SVC(kernel='linear',C=1,probability=True)
Class.fit(XTr,YTr) # Modelo
# Clasificación del test
ClaTe=Class.predict(XTe)
# Estimación de probabilidad del entrenamiento y del test
ProTr=Class.predict_proba(XTr)
ProTe=Class.predict_proba(XTe)
Nombres=['CC','AC','PAC','HFX']
PE=[None]*4 # Prevalencia estimada de cada algoritmo
# Estimar la prevalencia con los diferentes algoritmos de cuantificación
#%% CC
Q=CC(estimator_test=Class)
Q.fit(XTr,YTr)
pres=Q.predict(XTe)
pre=pres[np.argwhere(Q.classes_==1)[0][0]]
PE[0]=pre
#%% AC
Q=AC(estimator_train=Class, estimator_test=Class)
Q.fit(XTr,YTr)
pres=Q.predict(XTe)
pre=pres[np.argwhere(Q.classes_==1)[0][0]]
PE[1]=pre
#%% PAC
Q=PAC(estimator_train=Class, estimator_test=Class)
Q.fit(XTr,YTr)
pres=Q.predict(XTe)
pre=pres[np.argwhere(Q.classes_==1)[0][0]]
PE[2]=pre
#%% HDX
Q=DFX()
Q.fit(XTr,YTr)
pres=Q.predict(XTe)
pre=pres[np.argwhere(Q.classes_==1)[0][0]]
PE[3]=pre
#%% Calculo del valor de pérdida
# Cálculo de P. Te. (Prevalencia del test)
PTe=len(np.nonzero(YTe)[0])/len(YTe)
print('Prevalencia obtenida del test : {:6.4f}'.format(PTe))
# Imprimir la PE y el valor de pérdida para algoritmo de cuantificación
# l1 : distancia lineal, norma 1 (diferencia en valor absoluto)
# l2 : distancia cuadrática, norma 2 (diferencia al cuadrado)
for iac in range(len(PE)):
pre=PE[iac]
print('Prevalencia del test estimada con {:3}: {:6.4f} l1={:6.4f} l2={:6.4f}'
.format(Nombres[iac],pre,abs(pre-PTe),(pre-PTe)**2))
#%% Ejercicio 1
# Para el experimento anterior variar la prevalencia del conjunto de test de
#0,05 a 0,95 con paso 0,05.
# Calcular la pérdida l1 y l2 para los 4 sistemas de cuantificación e imprimirla
# Optativo:
# Almacenar los valores l1 y hacer una gráfica
# SALIDA:
"""
Prevalencia obtenida del test : 0.0500
Prevalencia del test estimada con CC : 0.1600 l1=0.1100 l2=0.0121
Prevalencia del test estimada con AC : 0.0476 l1=0.0024 l2=0.0000
Prevalencia del test estimada con PAC: 0.0557 l1=0.0057 l2=0.0000
Prevalencia del test estimada con HDy: 0.0911 l1=0.0411 l2=0.0017
Prevalencia obtenida del test : 0.1000
Prevalencia del test estimada con CC : 0.1400 l1=0.0400 l2=0.0016
Prevalencia del test estimada con AC : 0.0238 l1=0.0762 l2=0.0058
Prevalencia del test estimada con PAC: 0.0689 l1=0.0311 l2=0.0010
Prevalencia del test estimada con HDy: 0.1059 l1=0.0059 l2=0.0000
Prevalencia obtenida del test : 0.1500
Prevalencia del test estimada con CC : 0.2500 l1=0.1000 l2=0.0100
Prevalencia del test estimada con AC : 0.1548 l1=0.0048 l2=0.0000
Prevalencia del test estimada con PAC: 0.1610 l1=0.0110 l2=0.0001
Prevalencia del test estimada con HDy: 0.1705 l1=0.0205 l2=0.0004
Prevalencia obtenida del test : 0.2000
Prevalencia del test estimada con CC : 0.2800 l1=0.0800 l2=0.0064
Prevalencia del test estimada con AC : 0.1905 l1=0.0095 l2=0.0001
Prevalencia del test estimada con PAC: 0.1997 l1=0.0003 l2=0.0000
Prevalencia del test estimada con HDy: 0.1892 l1=0.0108 l2=0.0001
Prevalencia obtenida del test : 0.2500
Prevalencia del test estimada con CC : 0.3400 l1=0.0900 l2=0.0081
Prevalencia del test estimada con AC : 0.2619 l1=0.0119 l2=0.0001
Prevalencia del test estimada con PAC: 0.2755 l1=0.0255 l2=0.0006
Prevalencia del test estimada con HDy: 0.3138 l1=0.0638 l2=0.0041
Prevalencia obtenida del test : 0.3000
Prevalencia del test estimada con CC : 0.3600 l1=0.0600 l2=0.0036
Prevalencia del test estimada con AC : 0.2857 l1=0.0143 l2=0.0002
Prevalencia del test estimada con PAC: 0.2995 l1=0.0005 l2=0.0000
Prevalencia del test estimada con HDy: 0.3360 l1=0.0360 l2=0.0013
Prevalencia obtenida del test : 0.3500
Prevalencia del test estimada con CC : 0.3800 l1=0.0300 l2=0.0009
Prevalencia del test estimada con AC : 0.3095 l1=0.0405 l2=0.0016
Prevalencia del test estimada con PAC: 0.3267 l1=0.0233 l2=0.0005
Prevalencia del test estimada con HDy: 0.3233 l1=0.0267 l2=0.0007
Prevalencia obtenida del test : 0.4000
Prevalencia del test estimada con CC : 0.4300 l1=0.0300 l2=0.0009
Prevalencia del test estimada con AC : 0.3690 l1=0.0310 l2=0.0010
Prevalencia del test estimada con PAC: 0.3893 l1=0.0107 l2=0.0001
Prevalencia del test estimada con HDy: 0.3701 l1=0.0299 l2=0.0009
Prevalencia obtenida del test : 0.4500
Prevalencia del test estimada con CC : 0.4900 l1=0.0400 l2=0.0016
Prevalencia del test estimada con AC : 0.4405 l1=0.0095 l2=0.0001
Prevalencia del test estimada con PAC: 0.4659 l1=0.0159 l2=0.0003
Prevalencia del test estimada con HDy: 0.4863 l1=0.0363 l2=0.0013
Prevalencia obtenida del test : 0.5000
Prevalencia del test estimada con CC : 0.5200 l1=0.0200 l2=0.0004
Prevalencia del test estimada con AC : 0.4762 l1=0.0238 l2=0.0006
Prevalencia del test estimada con PAC: 0.4926 l1=0.0074 l2=0.0001
Prevalencia del test estimada con HDy: 0.5060 l1=0.0060 l2=0.0000
Prevalencia obtenida del test : 0.5500
Prevalencia del test estimada con CC : 0.5600 l1=0.0100 l2=0.0001
Prevalencia del test estimada con AC : 0.5238 l1=0.0262 l2=0.0007
Prevalencia del test estimada con PAC: 0.5049 l1=0.0451 l2=0.0020
Prevalencia del test estimada con HDy: 0.5371 l1=0.0129 l2=0.0002
Prevalencia obtenida del test : 0.6000
Prevalencia del test estimada con CC : 0.6100 l1=0.0100 l2=0.0001
Prevalencia del test estimada con AC : 0.5833 l1=0.0167 l2=0.0003
Prevalencia del test estimada con PAC: 0.5858 l1=0.0142 l2=0.0002
Prevalencia del test estimada con HDy: 0.5723 l1=0.0277 l2=0.0008
Prevalencia obtenida del test : 0.6500
Prevalencia del test estimada con CC : 0.6300 l1=0.0200 l2=0.0004
Prevalencia del test estimada con AC : 0.6071 l1=0.0429 l2=0.0018
Prevalencia del test estimada con PAC: 0.5958 l1=0.0542 l2=0.0029
Prevalencia del test estimada con HDy: 0.6056 l1=0.0444 l2=0.0020
Prevalencia obtenida del test : 0.7000
Prevalencia del test estimada con CC : 0.6800 l1=0.0200 l2=0.0004
Prevalencia del test estimada con AC : 0.6667 l1=0.0333 l2=0.0011
Prevalencia del test estimada con PAC: 0.6736 l1=0.0264 l2=0.0007
Prevalencia del test estimada con HDy: 0.6793 l1=0.0207 l2=0.0004
Prevalencia obtenida del test : 0.7500
Prevalencia del test estimada con CC : 0.7500 l1=0.0000 l2=0.0000
Prevalencia del test estimada con AC : 0.7500 l1=0.0000 l2=0.0000
Prevalencia del test estimada con PAC: 0.7620 l1=0.0120 l2=0.0001
Prevalencia del test estimada con HDy: 0.7285 l1=0.0215 l2=0.0005
Prevalencia obtenida del test : 0.8000
Prevalencia del test estimada con CC : 0.8100 l1=0.0100 l2=0.0001
Prevalencia del test estimada con AC : 0.8214 l1=0.0214 l2=0.0005
Prevalencia del test estimada con PAC: 0.7959 l1=0.0041 l2=0.0000
Prevalencia del test estimada con HDy: 0.8141 l1=0.0141 l2=0.0002
Prevalencia obtenida del test : 0.8500
Prevalencia del test estimada con CC : 0.7900 l1=0.0600 l2=0.0036
Prevalencia del test estimada con AC : 0.7976 l1=0.0524 l2=0.0027
Prevalencia del test estimada con PAC: 0.7973 l1=0.0527 l2=0.0028
Prevalencia del test estimada con HDy: 0.8137 l1=0.0363 l2=0.0013
Prevalencia obtenida del test : 0.9000
Prevalencia del test estimada con CC : 0.8500 l1=0.0500 l2=0.0025
Prevalencia del test estimada con AC : 0.8690 l1=0.0310 l2=0.0010
Prevalencia del test estimada con PAC: 0.8860 l1=0.0140 l2=0.0002
Prevalencia del test estimada con HDy: 0.9082 l1=0.0082 l2=0.0001
Prevalencia obtenida del test : 0.9500
Prevalencia del test estimada con CC : 0.8600 l1=0.0900 l2=0.0081
Prevalencia del test estimada con AC : 0.8810 l1=0.0690 l2=0.0048
Prevalencia del test estimada con PAC: 0.8882 l1=0.0618 l2=0.0038
Prevalencia del test estimada con HDy: 0.8994 l1=0.0506 l2=0.0026
"""
#<TODO BEGIN Ejercicio 1>
from quantificationlib.baselines.cc import CC
from quantificationlib.baselines.ac import AC,PAC
from quantificationlib.multiclass.df import DFX
l1s=[] # Almacena todas las pérdidas l1
PorPreTes=list(range(5,100,5))
for PorPreTe in PorPreTes: # Porcentaje de prevalencia de test
preTe=PorPreTe/100 # Prevalencia en proporcion
[XTe,YTe]=genToy(RG,100,prevalencia=preTe)
# Estimación de clasificación del test
ClaTe=Class.predict(XTe) # Genera las predicciones de clase del clasificador entrenado (Class) para el test.
# Estimación de probabilidad del test
ProTe=Class.predict_proba(XTe) # Genera las predicciones probabilísticas del clasificador (probabilidad de cada clase).
# Estimar la prevalencia con los diferentes algoritmos de cuantificación
# CC
Q=CC(estimator_test=Class) #Crea un cuantificador CC usando el clasificador Class para estimar las clases del test.
Q.fit(XTr,YTr)
pres=Q.predict(XTe) #Obtiene el vector de prevalencias estimadas para cada clase.
pre=pres[np.argwhere(Q.classes_==1)[0][0]] #Extrae la prevalencia estimada solo para la clase positiva (1).
PE[0]=pre
# AC
Q=AC(estimator_train=Class, estimator_test=Class) #6.1 Crea cuantificador AC usando el clasificador para entrenamiento y test.
Q.fit(XTr,YTr)
pres=Q.predict(XTe) #Calcula las prevalencias ajustadas.
pre=pres[np.argwhere(Q.classes_==1)[0][0]] #Extrae prevalencia estimada de la clase positiva.
PE[1]=pre
# PAC
Q=PAC(estimator_train=Class, estimator_test=Class) # Inicializa el cuantificador PAC.
Q.fit(XTr,YTr)
pres=Q.predict(XTe,predictions_test=ProTe) #Predice la prevalencia usando las probabilidades estimadas del test.
pre=pres[np.argwhere(Q.classes_==1)[0][0]] #Toma la prevalencia estimada para la clase positiva.
PE[2]=pre
# HDy
Q=DFX() #Crea el cuantificador basado en distancia de Hellinger.
Q.fit(XTr,YTr)
pres=Q.predict(XTe) #Extrae la prevalencia de la clase positiva.
pre=pres[np.argwhere(Q.classes_==1)[0][0]]
PE[3]=pre
# Cálculo de P. Te. (Prevalencia del test)
PTe=len(np.nonzero(YTe)[0])/len(YTe)
#Calcula la prevalencia real de la clase 1 en el test:
#np.nonzero(YTe) obtiene índices donde YTe==1.
#Divide entre el total de YTe.
print('\nPrevalencia obtenida del test : {:6.4f}'.format(PTe))
# Imprimir la PE y el valor de pérdida para algoritmo de cuantificación
# l1 : distancia lineal, norma 1 (diferencia en valor absoluto)
# l2 : distancia cuadrática, norma 2 (diferencia al cuadrado)
l1sc=[None]*4 # l1 para sistema de cuentificación
for iac in range(len(PE)):
pre=PE[iac]
print('Prevalencia del test estimada con {:3}: {:6.4f} l1={:6.4f} l2={:6.4f}'
.format(Nombres[iac],pre,abs(pre-PTe),(pre-PTe)**2))
l1sc[iac]=abs(pre-PTe)
l1s.append(l1sc) # Almacenar las l1 de cada sistema
# Gráfica
l1s=np.array(l1s) # Lo convierto a numpy.ndarray para poder seleccionar columnas
PreTes=list(map(lambda x:x/100,PorPreTes)) # Dividir entre 100 cada Porcentaje de Prevalencia
plt.figure()
for iac in range(len(PE)):
plt.plot(PreTes,l1s[:,iac],label=Nombres[iac])
plt.legend()
plt.xticks([0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9])
plt.xlabel('Prevalencia')
plt.ylabel('l1')
plt.savefig('Prevalencia_l1_4Algoritmos.pdf')
#<TODO END Ejercicio 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 1 : Cuantificación
Bloque B : Estimar pérdida en cuantificación
"""
from genToy import genToy
from quantificationlib.baselines.cc import CC
from quantificationlib.baselines.ac import AC,PAC
from quantificationlib.multiclass.df import DFX
from quantificationlib.bag_generator import PriorShift_BagGenerator
from QUtils import dataSetFromQBags
# from cuanti.utils import create_bags_with_multiple_prevalence
import numpy as np
import random
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
# La estimación de funcioners de calidad en
preTr=0.5 # Prevalencia de entrenamiento
print('Prevalencia de generación: entrenamiento:{:4}'.format(preTr))
RG=random.Random()
RG.seed(1)
# Generar un conjunto de datos
[X,Y]=genToy(RG,1000,prevalencia=preTr)
# Dividirlo en entrenamiento y test
[XTr,XTe,YTr,YTe]=train_test_split(X,Y,test_size=1/3,random_state=1,stratify=Y)
# Crear el modelo de entrenamiento
# Clasificador
Class=SVC(kernel='linear',C=1,probability=True)
# AC
Q=AC(estimator_train=Class, estimator_test=Class)
Q.fit(XTr,YTr)
l1s=[]
# Crear diferentes conjuntos de test con distinta prevalencia
n_bags=100
BG=PriorShift_BagGenerator(n_bags=n_bags,random_state=1)
bags=BG.generate_bags(XTe,YTe)
for i in range(n_bags):
[Xb,Yb]=dataSetFromQBags(XTe,YTe,bags,i)
p=np.mean(Yb)
pres=Q.predict(Xb)
pe=pres[np.argwhere(Q.classes_==1)[0][0]] # Predicted prevalence
l1=abs(pe-p)
print('Prevalencia test:{:6.4f} AC:{:6.4f} l1={:6.4f}'.format(p,pe,l1))
l1s.append(l1)
print('AC , l1 medio:{:6.4f}\n'.format(np.mean(l1s)))
#%% Ejericio 1
# Para el sistema DFX para cada uno de los valores de bins [4,8,12] estimar l1 y l2
# SALIDA:
"""
DFX n_bins: 4 , l1 medio=0.019924 l2 medio=0.000614
DFX n_bins: 8 , l1 medio=0.021082 l2 medio=0.000673
DFX n_bins:12 , l1 medio=0.023663 l2 medio=0.000822
"""
# #<TODO BEGIN Ejercicio 1>
from genToy import genToy
from quantificationlib.baselines.cc import CC
from quantificationlib.baselines.ac import AC,PAC
from quantificationlib.multiclass.df import DFX
from quantificationlib.bag_generator import PriorShift_BagGenerator
from QUtils import dataSetFromQBags
bins=[4,8,12]
l1bins=[]
l2bins=[]
for n_bins in bins:
Q=DFX(n_bins=n_bins) #Crea un cuantificador DFX con el número de bins actual.
Q.fit(XTr,YTr)
l1s=[]
l2s=[]
# Crear diferentes conjuntos de test con distinta prevalencia
n_bags=100
BG=PriorShift_BagGenerator(n_bags=n_bags,random_state=1) #Crea el generador de bolsas:
bags=BG.generate_bags(XTe,YTe) #Genera n_bags bolsas a partir del dataset de test original XTe,YTe.
for i in range(n_bags):
[Xb,Yb]=dataSetFromQBags(XTe,YTe,bags,i) #Construye el dataset correspondiente a la bolsa i:
p=np.mean(Yb) #Calcula la prevalencia real de la bolsa como la media de Yb (0/1).
pres=Q.predict(Xb) #Obtiene las prevalencias estimadas por DFX para cada clase.
pe=pres[np.argwhere(Q.classes_==1)[0][0]] # Predicted prevalence
l1=abs(pe-p) #Extrae la prevalencia estimada de la clase positiva (1).
l2=l1**2
# print('Prevalencia test:{:6.4f} PAC:{:6.4f} l1={:6.4f} l2={:6.4f}'.format(prev,pp,l1,l2))
l1s.append(l1)
l2s.append(l2)
l1bin=np.mean(l1s)
l2bin=np.mean(l2s)
l1bins.append(l1bin)
l2bins.append(l2bin)
print('DFX n_bins:{:2} , l1 medio={:8.6f} l2 medio={:8.6f}'.
format(n_bins,l1bin,l2bin))
# #<TODO END Ejercicio 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
author: quevedo@uniovi.es
"""
from genToy import genToy
import numpy as np
import random
from sklearn.svm import SVC
import matplotlib.pyplot as plt
# Generar conjuntos con diferente prevalencia en entrenamiento que en test
nEjemplos=100
preTrDe=0.5 # Prevalencia de entrenamiento deseada
RG=random.Random()
RG.seed(1)
[XTr,YTr]=genToy(RG,nEjemplos,prevalencia=preTrDe) # Mirar genToy.py
pTr=np.mean(YTr)
print('Prevalencia generada: entrenamiento :{:4.2f}'.format(pTr))
# Entrenar el clasificador
Class=SVC(kernel='linear',C=1,probability=True)
Class.fit(XTr,YTr)
# Test
[XTe,YTe]=genToy(RG,nEjemplos,prevalencia=0.95) # Generar ejemplo de test
pTe=np.mean(YTe)
print('Prevalencia generada: test :{:4.2f}'.format(pTe))
P=Class.predict(XTe) # Evaluar
pCC=np.mean(P) # Prevalencia de la evaluación (Classify and Count)
print('Prevalencia Clasificar y Contar (CC):{:4.2f}'.format(pCC))
PTes=[]
PCCs=[]
pTeDes=[por/100 for por in range(0,100,5)]
for pTeDe in pTeDes:
[XTe,YTe]=genToy(RG,nEjemplos,prevalencia=pTeDe) # Generar ejemplo de test
pTe=np.mean(YTe) # Calcular prevalencia del test
P=Class.predict(XTe) # Evaluar
pCC=np.mean(P) # Prevalencia de la evaluación (Classify and Count)
# Guardar los valores
PTes.append(pTe)
PCCs.append(pCC)
# Gráfica
plt.figure()
plt.plot(PTes,PCCs,label='Prevalencia CC')
plt.plot(PTes,PTes,label='Prevalencia Test')
plt.plot([pTr],[pTr],label='Prevalencia entrenamiento',marker='+',markersize=20)
plt.xlabel('Prevalencia de test')
plt.ylabel('Prevalencia Clasificar y Contar')
plt.xticks(pTeDes,rotation=90)
plt.legend()
plt.grid()
plt.savefig('PreSesion.pdf',bbox_inches='tight')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo@uniovi.es
"""
import numpy as np
def dataSetFromQBags(X,Y,bags,n):
"""
Creates the dataset of the bag n from the dataset (X|Y)
Params:
X,Y : Data Set Attributess/Category
bags : Bags returned by BagGenerator.generate_bags
n : index of the bag in [0, n_bag] , where n_bag is the parameter of
BagGenerator
Returns [Xb,Yb:
Xb|Yb : bag dataset
"""
# p=bags[0][0][n]
inds=bags[1][:,n]
Xb=[]
Yb=[]
for i in inds:
Xb.append(X[i])
Yb.append(Y[i])
return [np.array(Xb),np.array(Yb)]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 2 : Importancia
Bloque A : Algoritmos de Estimación de la Importancia
"""
from IEM.IEM import CI,PIE,DIE,KLIEP
import random
import matplotlib.pyplot as plt
import numpy as np
from gen1DimNormal import gen1DimNormal
#%% Ejercicio 1
# Modifica la generación del entrenamiento para que tenga una desviación típica de 10
# Ejecuta el bloque A y obtén las gráficas.
# El test está distribuído en una zona más pequeña que el entrenamiento.
# Las importancias para KLIEP y KDE se ajustan a esa zona, pero para PE no.
# El problam para PE es que usa como clasficador probabilístico LogisticRegression
#que es lineal. Prueba con otro clasificador probabilístico no lineal y comprueba
#que se ajusta.
# NOTA: Este código es una copia del bloque A, modificar lo que sea necesario
# PISTA: SVC puede ser usado como clasificador probabilístico.
from sklearn.svm import SVC
#%% Creación de conjuntos con diferente distribución en X
# Se crean dos conjuntos de ejemplos sin categoría (solo X) con un solo atributo.
# XTrain tiene valores siguiendo una distribución normal(media=-1,sd=1)
# XTest tiene valores siguiendo una distribución normal(media=+1,sd=1)
RG=random.Random()
RG.seed(1)
nEje=100
dt=1
XTrain=gen1DimNormal(RG,nEje,-1,dt)
XTest=gen1DimNormal(RG,nEje,+1,dt)
#%% Dibujar la distribución del entrenamiento y test
bins=10
fig=plt.figure()
plt.title('Distribución de entrenamiento y test')
plt.hist(XTrain[:,0],bins=bins,color='blue',label='XTrain',histtype='step')
# fig=plt.figure()
plt.hist(XTest[:,0],bins=bins,color='green',label='XTest',histtype='step')
plt.legend()
plt.xlabel('X')
plt.ylabel('Número de ejemplos en cada bin')
# plt.savefig('DistriEntre10Test1.pdf')
#%% Cálculo de la importancia utilizando estimadores de importancia
SVCProb=SVC(kernel='rbf',C=1,probability=True)
IEs=[CI(),PIE(ProbClass=SVCProb),DIE(),KLIEP()]
Importancias=[None]*4 # Importancias de cada estimador
for iie in range(len(IEs)):
IE=IEs[iie] # Obtener el Estimador de Importancia
IE.fit(XTrain) # Utilizar el train (si fuera necesario)
imp=IE.importance(XTest) # Calcular la importancia
Importancias[iie]=imp # Almacenar la importancia del estimador
#%% Mostrar en una gráfica las importancias calculadas
fig,[[ax1,ax2],[ax3,ax4]]=plt.subplots(2,2,figsize=[8,5],layout='tight')
axes=[ax1,ax2,ax3,ax4]
for iie in range(len(IEs)):
# Realizar la gráfica
axe=axes[iie] # Obtener el eje donde pintar
X=XTrain[:,0] # Tomar el primer atributo (solo hay uno)
axe.plot(X,Importancias[iie],'x',color='blue') # Pintar la gráfica
axe.set_title(IEs[iie].getName()) # Nombre del Estimador de importancia
axe.set_xlabel('X')
axe.set_ylabel('Importancia')
# plt.savefig('ImportanciasLREntre10Test1.pdf')
# plt.savefig('ImportanciasSVCProbaEntre10Test1.pdf')
#<TODO END Ejercicio 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 2 : Importancia
Bloque B : Estimación del error con Importancia
"""
from IEM.IEM import CI,PIE,DIE,KLIEP
import random
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import cross_val_predict
from sklearn.model_selection import train_test_split
from QUtils import dataSetFromQBags
from quantificationlib.bag_generator import PriorShift_BagGenerator
from sklearn.svm import SVC
from sklearn.metrics import zero_one_loss
from gen1DimNormal import gen1DimNormal
def genNormalMasMenos1(RG,nMas,nMenos):
"""
Se crea un conjunto de ejemplos con un atributo donde los de clase +1 tienen
una distribución normal(+1,+1) y los de clase negativa normal(-1,+1)
Params:
RG : random.Random()
nMas : Nº de ejemplos positivos
nMenos : Nº de ejemplos negativos
"""
X=[]
Y=[]
for i in range(nMas):
x=RG.normalvariate(+1,2)
X.append([x])
Y.append(+1)
for i in range(nMenos):
x=RG.normalvariate(-1,2)
X.append([x])
Y.append(-1)
return [X,Y]
#%% Información Inicial
# Creación de conjuntos con diferente distribución en X
# Se crea un conjunto de ejemplos con un atributo donde los de clase +1 tienen
# una distribución normal(+1,+1) y los de clase negativa normal(-1,+1)
RG=random.Random()
RG.seed(1)
[XTr,YTr]=genNormalMasMenos1(RG,nMas=20,nMenos= 5)
[XTe,YTe]=genNormalMasMenos1(RG,nMas= 5,nMenos=20) # Fíjate en la diferencia con el Train (cambian los Mas por los Menos)
# Crear un clasifiador
Class=SVC(kernel='linear',C=1)
# Generadores de importancia con los que estimar el error
IEs=[CI(),PIE(),DIE()]
#%% Aplicar la importancia para estimar el error del test XTe,YTe
# Errores en entrenamiento
CVPreds=cross_val_predict(Class,XTr,YTr,cv=3) # Fíjate que es con todo el conjunto de entrenamiento (y no con una parte Tr2)
Errors=[zero_one_loss([YTr[i]],[CVPreds[i]]) for i in range(len(YTr))]
print('Estimación del error para el test(Se conoce solo X) usando diferentes importancias')
# Para cada Importancia
ZOLImp=[] # Los Zero One Loss estimado según cada Importancia
for iIE in range(len(IEs)):
IE=IEs[iIE]
IE.fit(XTr)
imp=IE.importance(XTe) # Generar la importancia para el test
# Estimar el error según esta importancia
ErrsImp=[None]*len(imp)
for j in range(len(imp)):
ErrsImp[j]=Errors[j]*imp[j] # Se multiplica cada error del CV por esta importancia
ErrImp=np.mean(ErrsImp) # La media será la importancia estimada por esta importancia
print(' Importancia {:3} zero_one_loss={:6.4f}'.format(IEs[iIE].getName(),np.mean(ErrImp)))
ZOLImp.append(ErrImp)
#Ejercicio 1
"""
Se obtiene la siguiente salida del código anterior:
Estimación del error para el test(Se conoce solo X) usando diferentes importancias
Importancia CI zero_one_loss=0.1200
Importancia PIE zero_one_loss=0.1470
Importancia DIE zero_one_loss=0.1459
Pregunta 1: ¿Cuál es la mejor importancia?
Respuesta : No se puede saber por esta experimentación, habría que conocer el
valor de zero_one_loss sobre el test y ver cuál se aproxima más.
"""
#Ejercicio 2
"""
Obtén el error en test.
Calcula la l1 de cada estimación de Importancia.
"""
# Valor verdadero de error en test
print()
print('Utilizando el test entero(Atributos y Categoría):')
Class.fit(XTr,YTr)
P=Class.predict(XTe)
E=zero_one_loss(YTe,P)
print('Error Verdadero en Test : {:6.4f}'.format(E))
l1s=[]
for iIE in range(len(IEs)):
l1=abs(E-ZOLImp[iIE])
print(' Importancia {:3} l1={:6.4f}'.format(IEs[iIE].getName(),l1))
l1s.append(l1)
# Mejor Importancia
posMejor=np.argmin(l1s)
print('Mejor Importancia: {}'.format(IEs[posMejor].getName()))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 2 : Importancia
Bloque C : Estimación de la calidad de la Importancia
"""
from IEM.IEM import CI,PIE,DIE,KLIEP
import random
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import cross_val_predict
from sklearn.model_selection import train_test_split
from QUtils import dataSetFromQBags
from quantificationlib.bag_generator import PriorShift_BagGenerator
from sklearn.svm import SVC
from sklearn.metrics import zero_one_loss
from gen1DimNormal import gen1DimNormal
def genNormalMasMenos1(RG,nMas,nMenos):
"""
Se crea un conjunto de ejemplos con un atributo donde los de clase +1 tienen
una distribución normal(+1,+1) y los de clase negativa normal(-1,+1)
Params:
RG : random.Random()
nMas : Nº de ejemplos positivos
nMenos : Nº de ejemplos negativos
"""
X=[]
Y=[]
for i in range(nMas):
x=RG.normalvariate(+1,2)
X.append([x])
Y.append(+1)
for i in range(nMenos):
x=RG.normalvariate(-1,2)
X.append([x])
Y.append(-1)
return [X,Y]
#%% Información Inicial
# Creación de conjuntos con diferente distribución en X
# Se crea un conjunto de ejemplos con un atributo donde los de clase +1 tienen
# una distribución normal(+1,+1) y los de clase negativa normal(-1,+1)
RG=random.Random()
RG.seed(1)
[XTr,YTr]=genNormalMasMenos1(RG,nMas=20,nMenos= 5)
# Crear un clasifiador
Class=SVC(kernel='linear',C=1)
# Generadores de importancia a evaluar
IEs=[CI(),PIE(),DIE()]
#%% Estimación de la calidad de la importancia
# Hay varias importancias, pero no hay ningún test. Se pretende estimar qué importancia
# es la mejor
# No se hará validación cruzada, en su lugar se realizará una partición train/test.
# Para el test se generarán varios bags con varias distribuciones y se medirá
# la calidad del acierto del error
# Dividir el entrenamiento en entrenamiento2(Tr2) y test2(Te2)
[XTr2,XTe2,YTr2,YTe2]=train_test_split(XTr,YTr,test_size=1/3,random_state=1,stratify=YTr)
# Calcular las predicciones y los errores de Tr2 en CV
CVPreds=cross_val_predict(Class,XTr2,YTr2,cv=3)
Errors=[zero_one_loss([YTr2[i]],[CVPreds[i]]) for i in range(len(YTr2))]
# Crear el modelo de clasificación para Tr2
Class.fit(XTr2,YTr2)
# Entrenar cada Importancia a evaluar con Tr2
for i in range(len(IEs)):
IEs[i].fit(XTr2)
# Crear bags a partir del Test 2
n_bags=100
BG=PriorShift_BagGenerator(n_bags=n_bags,random_state=1)
bags=BG.generate_bags(XTe2,YTe2)
# L1: distancia entre el error estimado por cada importancia y el verdadero de test
l1s=[[] for k in range(len(IEs))]
for i in range(n_bags):
# Crear la bag
[Xb,Yb]=dataSetFromQBags(XTe2,YTe2,bags,i)
# El error de esta bag
Pb=Class.predict(Xb)
Eb=zero_one_loss(Yb,Pb)
# Error estimado según cada Importancia
for iIE in range(len(IEs)):
IE=IEs[iIE]
imp=IE.importance(Xb) # Generar la importancia para este test
# Estimar el error según esta importancia
# Será la media del producto del error de cada ejemplo por su importancia
ErrImp=np.mean([Errors[j]*imp[j] for j in range(len(imp))])
# Se calcula l1 como la distancia entre el error del modelo y el estimado
# por esta importancia
l1=abs(Eb-ErrImp)
l1s[iIE].append(l1) # Se almacenan los l1 para cada importancia
print('Calidad de la estimación del error usando solo el entrenamiento:')
for iIE in range(len(IEs)):
print(' Estimación calidad usando {:3} l1={:6.4f}'.format(IEs[iIE].getName(),np.mean(l1s[iIE])))
#%% Ejercicio 1
"""
Se obtiene la siguiente salida del código anterior:
Calidad de la estimación del error usando solo el entrenamiento:
Importancia CI l1=0.3076
Importancia PIE l1=0.2226
Importancia DIE l1=0.2564
Pregunta 1: Utilizando solo el entrenamiento, ¿Cuál es la mejor Importancia?
Respuesta: La mejor es PIE que tiene el menor l1
"""
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 2 : Importancia
Bloque D : Mejora del aprendizaje con importancia
"""
from IEM.IEM import CI,PIE,DIE,KLIEP
import random
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import cross_val_predict
from sklearn.svm import SVC
from sklearn.metrics import zero_one_loss
from gen1DimNormal import gen1DimNormal
#%% Creación de conjuntos con diferente distribución en X
# Se crean dos conjuntos de ejemplos sin categoría (solo X) con un solo atributo.
# XTrain tiene valores siguiendo una distribución normal(media=-1,sd=1)
# XTest1 tiene valores siguiendo una distribución normal(media=-1,sd=1)
# XTest2 tiene valores siguiendo una distribución normal(media=+1,sd=1)
# NOTA: El test 1 tiene la misma distribución que el entrenamiento, mientras que
# que el test2 la tiene diferente
RG=random.Random()
RG.seed(1)
nEje=100
dt=1
XTrain=gen1DimNormal(RG,nEje,-1,dt)
XTest1=gen1DimNormal(RG,nEje,-1,dt)
XTest2=gen1DimNormal(RG,nEje,+1,dt)
# La categoría será el signo de la X
YTrain=list(map(lambda x:np.sign(x[0]),XTrain))
YTest1=list(map(lambda x:np.sign(x[0]),XTest1))
YTest2=list(map(lambda x:np.sign(x[0]),XTest2))
#%% Error en test sin importancia
Class=SVC(kernel='linear',C=0.01)
Class.fit(XTrain,YTrain) # El modelo es el mismo y se aplica a los 2 test
# Test 1
Preds1=Class.predict(XTest1)
print('Error sin importancia en test1: {:6.4f}'.format(zero_one_loss(YTest1,Preds1)))
# Test 2
Preds2=Class.predict(XTest2)
print('Error sin importancia en test2: {:6.4f}'.format(zero_one_loss(YTest2,Preds2)))
#%% Utilizar importancia para mejorar el aprendizaje
# En el proceso de aprendizaje ponderar cada ejemplo por la importancia permite
#adecuar el modelo a la distribución del test.
# Tiene como desventaja que hay que hacer un entrenamiento(fit) por cada evaluación
#de test con una distribución diferente.
# En SVC el método fit tiene el parámetro sample_weight que permite dar un peso
#a cada ejemplo. Asignado a ese parámetro la importancia se permite ajustar el
#modelo según la importancia
# Estimador y cálculo de importancias para XTrain con respecto XTest1 y XTest2
IE=PIE()
IE.fit(XTrain)
imp1PIE=IE.importance(XTest1)
imp2PIE=IE.importance(XTest2)
# Test 1
Class.fit(XTrain,YTrain,sample_weight=imp1PIE) # El modelo es particular para este test
Preds1=Class.predict(XTest1)
print('Error con importancia en test1: {:6.4f}'.format(zero_one_loss(YTest1,Preds1)))
# Test 2
Class.fit(XTrain,YTrain,sample_weight=imp2PIE) # El modelo es particular para este test
Preds2=Class.predict(XTest2)
print('Error con importancia en test2: {:6.4f}'.format(zero_one_loss(YTest2,Preds2)))
#%% Ejercicio 1
# Variar la media del test desde -3 hasta +3 de uno en uno.
# Calcular e imprimir el error en test sin y con importancia PIE
# Opcional hacer una gráfica
#SALIDA:
# Ejercicio 1
# Entrenamiento media:-1
# Test media:-3. Error: sin importancia=0.0100 con importancia=0.0100
# Test media:-2. Error: sin importancia=0.0400 con importancia=0.0400
# Test media:-1. Error: sin importancia=0.1500 con importancia=0.1500
# Test media:+0. Error: sin importancia=0.4300 con importancia=0.0600
# Test media:+1. Error: sin importancia=0.8600 con importancia=0.1400
# Test media:+2. Error: sin importancia=0.9300 con importancia=0.0700
# Test media:+3. Error: sin importancia=1.0000 con importancia=0.0000
from IEM.IEM import CI,PIE,DIE,KLIEP
import random
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import cross_val_predict
from sklearn.svm import SVC
from sklearn.metrics import zero_one_loss
from gen1DimNormal import gen1DimNormal
print('\nEjercicio 1')
ErroresSin=[] # Almacenar errores y m para gráfica
ErroresCon=[]
ms=[]
print('Entrenamiento media:-1')
for m in range(-3,+3+1):
ms.append(m) # Almaceno las m
print('Test media:{:+d}. Error: '.format(m),end='')
XTest=gen1DimNormal(RG,nEje,m,dt) # Generar el test
YTest=list(map(lambda x:np.sign(x[0]),XTest))
# Sin importancia
#<TODO BEGIN Ejercicio 1.1>
Class.fit(XTrain,YTrain) #Entrena el clasificador base (Class) normalmente, sin pesos.
PredsSin=Class.predict(XTest) # Evaluación del modelo
ErrorSin=zero_one_loss(YTest,PredsSin)
print('sin importancia={:6.4f} '.format(ErrorSin),end='')
ErroresSin.append(ErrorSin) # Almaceno los errores sin importancia
#<TODO END Ejercicio 1.1>
# Con importancia
#<TODO BEGIN Ejercicio 1.2>
impPIE=IE.importance(XTest) #Calcula los pesos de importancia (importance weights) para cada ejemplo del test, usando el método de Importance Estimation (IE):
# El peso mide cuánto cambia la distribución del test respecto al training.
Class.fit(XTrain,YTrain,sample_weight=impPIE) #Entrena el clasificador, pero ahora cada ejemplo del training se pondera según los pesos de importancia. Esto ajusta el modelo a la distribución del test.
PredsCon=Class.predict(XTest) # Evaluación del modelo
ErrorCon=zero_one_loss(YTest,PredsCon)
print('con importancia={:6.4f}'.format(ErrorCon))
ErroresCon.append(ErrorCon) # Almaceno los errores con importancia
#<TODO END Ejercicio 1.2>
# Gráfica
plt.figure(figsize=[5,3],layout='tight')
plt.title('Media de X en entrenamiento:-1. Y=sign(X)')
plt.plot(ms,ErroresSin,label='Sin importancia')
plt.plot(ms,ErroresCon,label='Con importancia')
plt.xlabel('Media de X en test')
plt.ylabel('Error en test')
plt.legend()
plt.savefig('ErrorTestSinConImportanciaVariandoDistTest.pdf')
#<TODO END Ejercicio 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 2 : Importancia
Bloque A : Algoritmos de Estimación de la Importancia
"""
import random
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import cross_val_predict
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
def genEjemplosNegPos(RG,nEje,nEjeNeg):
X=[]
Y=[]
for n in range(nEje):
x=[RG.random() for i in range(dim)] # Valor en uniforme(0,1) en dim dimensiones
y=1 if sum(x)>dim/2 else -1 # Categoría
if n<nEjeNeg and y==1 or n>=nEjeNeg and y==-1: # Hay que hacer un cambio
x=[1-n for n in x]
y=-y
X.append(x)
Y.append(y)
X=np.array(X)
Y=np.array(Y)
return [X,Y]
#%% Creación de conjuntos con diferente distribución en X
# Se crean dos conjuntos de ejemplos sin categoría (solo X) con un solo atributo.
# Tanto X
# XTrain tiene valores siguiendo una distribución normal(media=-1,sd=1)
# XTest1 tiene valores siguiendo una distribución normal(media=+1,sd=1)
RG=random.Random()
RG.seed(1)
dim=2
# Entrenamiento
nEje=100
nEjeNeg=90
# Test1
nEjeTest1=100
nEjeNegTest1=10
# Test2
nEjeTest2=100
nEjeNegTest2=90
# Crear entrenamiento y tests
[X,Y] =genEjemplosNegPos(RG,nEje,nEjeNeg)
[XTe1,YTe1]=genEjemplosNegPos(RG,nEjeTest1,nEjeNegTest1)
[XTe2,YTe2]=genEjemplosNegPos(RG,nEjeTest1,nEjeNegTest2)
# Entrenamiento
Class=SVC(kernel='linear',C=1)
# Class=SVC(kernel='rbf',C=1)
# Class=RandomForestClassifier()
Class.fit(X,Y)
# Cross Validation
PCV=cross_val_predict(Class,X,Y)
FCV=np.where(PCV-Y)[0] # FCVallos
# Test1
P1=Class.predict(XTe1)
F1=np.where(P1-YTe1)[0] # FCVallos
# Test2
P2=Class.predict(XTe2)
F2=np.where(P2-YTe2)[0] # FCVallos
# Suma de los valores X
S=np.array([sum(x) for x in X])
# Pintar entrenamiento
fig=plt.figure()
plt.plot(S,Y,marker='x',linestyle='none',label='Entrenamiento',color='blue')
plt.xlabel('Suma de x')
plt.ylabel('Signo de la categoria real')
plt.title('Entrenamiento')
plt.savefig('T2_S2_Pre_Entrenamiento.pdf')
# Pintar CV y Tests
fig=plt.figure()
# CV
plt.plot(S,Y,marker='o',linestyle='none',label='Entre. CV Aciertos',fillstyle='none',color='orange')
plt.plot(S[FCV],Y[FCV],marker='o',linestyle='none',label='Entre. CV Fallos',fillstyle='none',color='red')
# Test1
STe1=np.array([sum(x) for x in XTe1])
plt.plot(STe1,YTe1*0.5,marker='+',linestyle='none',label='Test1 Aciertos',fillstyle='none',color='green')
plt.plot(STe1[F1],YTe1[F1]*0.5,marker='+',linestyle='none',label='Test1 Fallos',fillstyle='none',color='red')
# Test2
STe2=np.array([sum(x) for x in XTe2])
plt.plot(STe2,YTe2*0.25,marker='x',linestyle='none',label='Test2 Aciertos',fillstyle='none',color='green')
plt.plot(STe2[F2],YTe2[F2]*0.25,marker='x',linestyle='none',label='Test2 Fallos',fillstyle='none',color='red')
plt.ylim(-1.5,+1.5)
plt.title('Dimensiones={}\n{}'.format(dim,Class))
plt.xlabel('Suma de x')
plt.ylabel('Signo de la categoria real')
plt.legend()
plt.savefig('T2_S2_Pre_CV_Tests.pdf')
# plt.savefig('T2_S2_Pre_CV_Test.pdf')
# plt.savefig('T2_S2_Pre_CV.pdf')
print('Error en CV : {:4.2f}'.format(len(FCV)/len(PCV)))
print('Error en test1: {:4.2f}'.format(len(F1)/len(P1)))
print('Error en test2: {:4.2f}'.format(len(F2)/len(P2)))
# Error y multiplicarlo
ErrCV=abs(PCV-Y)
ErrCV=[0 if e==0 else 1 for e in ErrCV]
# Pintar Error CV
fig=plt.figure()
plt.title('Error de la CV. Media={}'.format(np.mean(ErrCV)))
plt.xlabel('Suma de x')
plt.plot(S,ErrCV,marker='|',linestyle='none',label='Error CV',fillstyle='none',color='orange')
plt.savefig('T2_S2_Pre_ErrorCV.pdf')
# Calcular la importancia
# Importancia sin normalizar de cada ejemplo
def importanciaNN(x):
return x**2
def importancia(X):
iNN=np.array([importanciaNN(x) for x in X]) # Cálculo de la importanciaNN de cada ejemplos
Media=np.mean(iNN) # Se obtiene la media
impor=iNN/Media # Se divide entre la media pra obtener la importancia
return impor
imporX=importancia(S)
# Pintar La importancia
fig=plt.figure()
plt.title('Importancia. Media={:6.4f}'.format(np.mean(imporX)))
plt.xlabel('Suma de x')
plt.plot(S,imporX,marker='_',linestyle='none',label='Error CV',fillstyle='none',color='blue')
plt.savefig('T2_S2_Pre_Importance.pdf')
# Error * importancia
ErrCVImp=imporX*ErrCV
# Pintar La importancia * error
fig=plt.figure()
plt.title('Error*Importancia. Media={:6.4f}'.format(np.mean(ErrCVImp)))
plt.xlabel('Suma de x')
plt.plot(S,ErrCVImp,marker='+',linestyle='none',label='Error CV',fillstyle='none',color='black')
plt.savefig('T2_S2_Pre_ErrorCVxImportance.pdf')
print('Error*Importancia: {:6.4f}'.format(np.mean(ErrCVImp)))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 2 : Cambios en la distribución
Sesión 2 : Importancia
Actividad 1
"""
#%% Actividad 1
# DATOS:
# Los datos de origen son tipos de hormigón:
# https://archive.ics.uci.edu/ml/datasets/Concrete+Compressive+Strength
# Se trata de prededir la fuerza del hormigon en función de varias características
#del mismo.
# Es un problema de REGRESIÓN
# Todo lo visto de la importancia se puede aplicar igual regresión que a clasificación
# Los datos se cargan y se dividen en entrenamiento y test: [XTr,YTr,XTe,YTe]
# Se proporciona la media del entrenamiento y del test (es un dato equivalente
#a la prevalencia)
# ENUNCIADO
# Se proporcionan dos regresores Reg1 y Reg2
# Se proporciona un estimador de Importancia IE
# A Se quiere estimar cual es el mejor según MAE y utilizando IE si fuera conveniente,
# en las siguientes SITUACIONES:
# A.1 : No se tiene el test, solo se utiliza el entrenamiento (XTr,YTr)
# A.2 : Además del entrenamiento se tienen los atributos del test, pero no su cagegoría
# B ¿Cuál es el mejor estimador A.1 o A.2? ¿Cómo lo puedes saber?
# C. Trata de mejor la predicción del mejor regresor según IE usando la propia IE
import csv
import numpy as np
import random
from sklearn.svm import SVR,SVC
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_validate,cross_val_predict
from sklearn.metrics import make_scorer,mean_absolute_error
from IEM.IEM import PIE,DIE,KLIEP
def concreteReader():
def _CategoryGaussNoisyOrder(RG,X,Y):
orderVals=[]
for v in Y:
nv=RG.normalvariate(v,1)
orderVals.append(nv)
order=np.argsort(orderVals)
return [np.array(X)[order],np.array(Y)[order]]
X=[]
Y=[]
FName='imporDataset/Concrete_Data.csv'
with open(FName) as Fcsv:
csvreader = csv.reader(Fcsv, delimiter=',')
header=True
for row in csvreader:
if header:
header=False
else:
rowNum=list(map(float,row))
X.append(rowNum[:(len(row)-1)])
Y.append(rowNum[len(row)-1])
nEje=len(X)
print('Leídos {} ejemplos de cemento media(Y)={:6.4f}'.format(nEje,np.mean(Y)))
RG=random.Random()
RG.seed(1)
[X,Y]=_CategoryGaussNoisyOrder(RG,X,Y)
nTr=int(nEje*(9/10))
nTe=nEje-nTr
XTr=X[:nTr]
YTr=Y[:nTr]
XTe=X[nTr:]
YTe=Y[nTr:]
print(' Entrenamiento: ejemplos:{} media(Y)={:6.4f}'.format(nTr,np.mean(YTr)))
print(' Test : ejemplos:{} media(Y)={:6.4f}'.format(nTe,np.mean(YTe)))
return [XTr,YTr,XTe,YTe]
# Obtener conjunto de datos
[XTr,YTr,XTe,YTe]=concreteReader()
# ¿Qué regresor es mejor?
Reg1=SVR(kernel='linear',C=1)
Reg2=RandomForestRegressor()
# Estimador de Importancia a utilizar
from sklearn.linear_model import LogisticRegression
# IE=PIE(LogisticRegression(C=0.01,random_state=1,max_iter=10000))
IE=PIE(SVC(C=100,random_state=1,probability=True,max_iter=10000)) # Para C
print('Reg1:',Reg1)
print('Reg2:',Reg2)
#%% Estimación del MAE sin tener en cuenta el TEST
print('\nEstimando mediante CV (Sin utilizar el test)')
# BEGIN A.1
CV1=cross_validate(Reg1,XTr,YTr,scoring={'MAE':make_scorer(mean_absolute_error)},cv=3)
MAE1_CV=CV1['test_MAE'].mean()
CV2=cross_validate(Reg2,XTr,YTr,scoring={'MAE':make_scorer(mean_absolute_error)},cv=3)
MAE2_CV=CV2['test_MAE'].mean()
# END A.1
print(' REG1 CV MAE={:6.4f}'.format(MAE1_CV))
print(' REG2 CV MAE={:6.4f}'.format(MAE2_CV))
print(' Mejor: ',end='')
if MAE1_CV<MAE2_CV:
print('Reg1')
else:
print('Reg2')
#%% Estimación del MAE teniendo en cuenta los atributos del test (NO la categoría)
# Se usará la importancia para estimar
print('\nEstimando mediante CV con atributos de test junto con importancia PIE')
# BEGIN A.2
# Errores en CV de cada ejemplo para cada regresor
CVPreds1=cross_val_predict(Reg1,XTr,YTr,cv=3)
CVErrors1=[mean_absolute_error([YTr[i]],[CVPreds1[i]]) for i in range(len(YTr))]
CVPreds2=cross_val_predict(Reg2,XTr,YTr,cv=3)
CVErrors2=[mean_absolute_error([YTr[i]],[CVPreds2[i]]) for i in range(len(YTr))]
# Importancia PIE
IE.fit(XTr)
impPIE=IE.importance(XTe)
MAE1_PIE=np.mean(np.array(CVErrors1)*np.array(impPIE))
MAE2_PIE=np.mean(np.array(CVErrors2)*np.array(impPIE))
# END A.2
print(' REG1 MAE PIE={:6.4f}'.format(MAE1_PIE))
print(' REG2 MAE PIE={:6.4f}'.format(MAE2_PIE))
print(' Mejor ',end='')
if MAE1_PIE<MAE2_PIE:
print('Reg1')
else:
print('Reg2')
#%% Aprendizaje, evaluación y medida de error (MAE) del test (Valor verdadero)
print('\nCalculando el error en test:')
# BEGIN B
# Regresor 1
Reg1.fit(XTr,YTr)
pred=Reg1.predict(XTe)
MAE1_Test=mean_absolute_error(YTe,pred)
# Regresor 2
Reg2.fit(XTr,YTr)
pred=Reg2.predict(XTe)
MAE2_Test=mean_absolute_error(YTe,pred)
print(' Reg1: MAE en test={:7.4f}'.format(MAE1_Test))
print(' Reg2: MAE en test={:7.4f}'.format(MAE2_Test))
print(' Mejor ',end='')
if MAE1_Test<MAE2_Test:
print('Reg1')
else:
print('Reg2')
# END B
# BEGIN C
#%% Mejora del aprendizaje con importancia (Situación 4)
print('\nAprendizaje mejorado con pesos de importancia (sample_weight):')
# 1. Calculamos la importancia del entrenamiento respecto al test
IE.fit(XTr)
imp_train = IE.importance(XTe) # Pesos para cada ejemplo de XTr basados en su relevancia para XTe
# 2. Reentrenamos los modelos usando sample_weight
# Nota: Reg1 (SVR) y Reg2 (RandomForest) soportan sample_weight en sklearn
Reg1.fit(XTr, YTr, sample_weight=imp_train)
Reg2.fit(XTr, YTr, sample_weight=imp_train)
# 3. Predecimos de nuevo sobre el test real
preds1_imp = Reg1.predict(XTe)
preds2_imp = Reg2.predict(XTe)
# 4. Calculamos el nuevo MAE real
MAE1_Test_Imp = mean_absolute_error(YTe, preds1_imp)
MAE2_Test_Imp = mean_absolute_error(YTe, preds2_imp)
print(' Reg1 con importancia: MAE en test={:7.4f}'.format(MAE1_Test_Imp))
print(' Reg2 con importancia: MAE en test={:7.4f}'.format(MAE2_Test_Imp))
# END C
import numpy as np
import warnings
import random
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KernelDensity
from IEM.implementations.DRE import DensityRatioEstimator
import pdb
class ImportanceEstimator:
"""
Base class for all Importance Estimator Methods
Children classes must implement:
_fit(self,XTrain) : previous calculous that involve only train
_importance(self,XTrain,XTest): train’s importance estimation for this test
"""
def __init__(self,name=None):
"""
Params:
- name : string , name of the importance estimator
"""
self.name=name
self.XTrain=None
def getName(self):
return self.name
def fit(self,XTrain):
self.XTrain=XTrain
return self._fit(XTrain)
def _fit(self,XTrain):
pass
def importance(self,XTest):
ie=self._importance(self.XTrain,XTest)
# Check
if min(ie)<0:
raise Exception('ImportanceEstimator: {}._imporatance(...) outputs negative importances'.format(self.__class__.__name__))
if len(ie)!=len(self.XTrain):
raise Exception('ImportanceEstimator: {}._imporatance(...) outputs {} values and the train dataset len is {}'.
format(self.__class__.__name__,len(ie),len(self.XTrain)))
# Adjust for mean(ie)=1
meanIE=sum(ie)/len(ie)
if meanIE==0: # All train examples are equally important
return [1]*len(ie)
ie=list(map(lambda x:x/meanIE,ie))
return ie
def _importance(self,XTrain,XTest):
pass
class CI(ImportanceEstimator):
"""
Constant Importance, method that allways returns constant importance for all
train examples (returns 1 for all values)
"""
def __init__(self):
super().__init__('CI')
self.Ones=[]
def _fit(self,XTrain):
self.Ones=[1]*len(XTrain)
return None
def _importance(self,XTrain,XTest):
return self.Ones
class PIE(ImportanceEstimator):
"""
Probabilistic Importance Estimator.
Calculates the importance as P_test(Train)/P_train(Train)
"""
def __init__(self,ProbClass=None):
"""
Params:
- ProbClass : Classfier that outputs probabilistic predictions
Must have methods: fit , predict_proba
If None: linear_model.LogisticRegression(random_state=1)
"""
super().__init__('PIE')
if ProbClass==None:
self.ProbClass=LogisticRegression(random_state=1)
else:
self.ProbClass=ProbClass
def _fit(self,XTrain):
return None
def _importance(self,XTrain,XTest):
X = np.concatenate((XTrain, XTest))
Y = np.zeros(len(X))
Y[:len(XTrain)] = 1
self.ProbClass.fit(X, Y)
probs=self.ProbClass.predict_proba(XTrain)
pTrBeTrain=probs[:,1]
pTrBeTest =probs[:,0]
importance = pTrBeTest / pTrBeTrain
return importance
class DIE(ImportanceEstimator):
"""
Density Importance Estimator.
Calculates the importance using the density D of train and test.
"""
def __init__(self,de=None):
"""
Params:
- de : Density Estimator.
Must have methods: fit, score_samples
If None: sklearn.neighbors.KernelDensity(bandwidth=1, kernel='gaussian')
"""
super().__init__('DIE')
if de==None:
self.de=KernelDensity(bandwidth=1, kernel='gaussian')
else:
self.de=de
self.DF_trainV=None
def _fit(self,XTrain):
self.de.fit(XTrain)
self.DF_trainV = self.de.score_samples(XTrain)
return self
def _importance(self,XTrain,XTest):
self.de.fit(XTest)
DF_testV = self.de.score_samples(self.XTrain)
importance = np.zeros(len(self.XTrain))
for i in range(len(self.XTrain)):
importance[i] =np.exp(DF_testV[i]-self.DF_trainV[i])
return importance
class KLIEP (ImportanceEstimator):
def __init__(self):
super().__init__('KLIEP')
def _fit(self,XTrain):
# Do nothing for fitting
return None
def _importance(self,XTrain,XTest):
kliep = DensityRatioEstimator()
XTrain=np.array(XTrain)
XTest=np.array(XTest)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
kliep.fit(XTrain, XTest)
importance = kliep.predict(XTrain)
return importance
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
"""
import numpy as np
def gen1DimNormal(RG,nEje,m,dt):
"""
Crea un conjunto de ejenplos de una dimension con una distribución normal
Parámetros:
- RG : objeto random.Random()
- nEje : número de ejemplos
- m : media
- dt : desviación típica
Retorna: numpy.ndarray nEje x 1 (Matriz con una columna, no lista lineal)
donde los valores siguen una distribución normal(m,dt)
"""
X=[]
for c in range(nEje):
x=RG.normalvariate(m,dt)
X.append([x])
return np.array(X)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo@uniovi.es
"""
import numpy as np
def dataSetFromQBags(X,Y,bags,n):
"""
Creates the dataset of the bag n from the dataset (X|Y)
Params:
X,Y : Data Set Attributess/Category
bags : Bags returned by BagGenerator.generate_bags
n : index of the bag in [0, n_bag] , where n_bag is the parameter of
BagGenerator
Returns [Xb,Yb:
Xb|Yb : bag dataset
"""
# p=bags[0][0][n]
inds=bags[1][:,n]
Xb=[]
Yb=[]
for i in inds:
Xb.append(X[i])
Yb.append(Y[i])
return [np.array(Xb),np.array(Yb)]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 1 : Definición del problema y algoritmos de ASS
Bloque A : Problemas semi-supervisados
"""
# En los conjuntos de datos que tienen una gran cantidad de ejemplos con categoría
#desconocida se puede aprovechar ese conjunto de datos sin categoría para mejorar
#el aprendizaje. Esto sería Aprendizaje Semisupervisado
# En este tema solo se tratarán problemas de clasificación y se marcará con -1
#la categoría de los ejemplos de los que se desconoce el valor de la misma
# Utilizaremos el conjuntos de datos letter-recognition:
# https://archive.ics.uci.edu/ml/datasets/Letter+Recognition
# La categoría es una letra y los atributos información sobre características
#gráficas de esa letra manuscrita
# Se tratará de distinguir dos letras con cierto parecido: M y N
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.metrics import accuracy_score
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
import matplotlib.pyplot as plt
import numpy as np
import warnings
# Atributos usados en la representación (Cambiar al gusto. Valores en [0;15])
ix=6
iy=12
# Letras usadas como categoría (Cambiar al gusto. Valores letras mayúsculas en inglés)
L1='M'
L2='N'
# Se lee el dataset, dividido en entrenamiento y test
# El entranamiento se divide en supervisado y no supervisado
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=1,propTest=1/3.0,propEti=1/100.0)
# Convertir letras en números de categorías
n1=ord(L1)-ord('A')
n2=ord(L2)-ord('A')
plt.figure()
plt.title('Entrenamiento: Letter M - N')
plt.xlabel('Atributo {}'.format(ix))
plt.ylabel('Atributo {}'.format(iy))
plt.plot(np.array(XTrNoSuper)[:,ix],np.array(XTrNoSuper)[:,iy],'x',label='NS',markersize=12,color='tab:gray')
plt.plot(np.array(XTrSuper)[np.array(YTrSuper)==n1,ix],np.array(XTrSuper)[np.array(YTrSuper)==n1,iy],marker='${}$'.format(L1),markersize=12,linestyle='None',color='tab:orange',label='M')
plt.plot(np.array(XTrSuper)[np.array(YTrSuper)==n2,ix],np.array(XTrSuper)[np.array(YTrSuper)==n2,iy],marker='${}$'.format(L2),markersize=12,linestyle='None',color='tab:blue',label='N')
plt.xticks([0,2,4,6,8,10,12,14])
plt.yticks([0,2,4,6,8,10,12,14,16])
# plt.legend()
plt.savefig('EntreMNX.pdf')
plt.figure()
plt.title('Test: Letter M - N')
plt.xlabel('Atributo {}'.format(ix))
plt.xticks([2,4,6,8,10,12,14])
plt.ylabel('Atributo {}'.format(iy))
plt.plot(np.array(XTe)[np.array(YTe)==n1,ix],np.array(XTe)[np.array(YTe)==n1,iy],marker='$|^{}|$'.format(L1),markersize=12,linestyle='None',color='tab:orange',label='M')
plt.plot(np.array(XTe)[np.array(YTe)==n2,ix],np.array(XTe)[np.array(YTe)==n2,iy],marker='$|_{}|$'.format(L2),markersize=12,linestyle='None',color='tab:blue',label='N')
plt.xticks([0,2,4,6,8,10,12,14])
plt.yticks([0,2,4,6,8,10,12,14,16])
# plt.legend()
plt.savefig('TestMN.pdf')
#%% Variando NS
# Aprenderemos con diferentes proporciones de no etiquetado en el conjunto de
#entrenamiento para estudiar como afecta al error en test
# Evaluar el aprendizaje según aumenta el número de ejemplos no etiquetados
#usando accuracy_score
ntrSuper =len(XTrSuper) # Nº de ejemplos etiquetados
ntrNoSuper=len(XTrNoSuper) # Nº de ejemplos no etiquetados
# Algoritmos de aprendizaje semisupervisado
LP=LabelPropagation()
LS=LabelSpreading()
# Proporciones de ejemplos no etiquetados
PENS=[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]
# Almacenes de aciertos para cada sistema no supervisado
LPAcs =[]
LSAcs =[]
nNoSupers=[] # Almacén de Nº de ejemplos no etiquetados en cada iteración
# NOTA: Los sistemas LabelPropagation y LabelSpreading dan algunos warnings
# que con las siguientes instrucciones evitan que se muestren
with warnings.catch_warnings():
warnings.simplefilter("ignore")
for PNS in PENS:
# El aprendizaje semi supervisado utiliza los ejemplos con y sin categoría
# Crear el dataset con la proporción apropiada de no etiquetados
nNoSuper=int(ntrNoSuper*PNS) # Nº de ejemplos no etiquetados
nNoSupers.append(nNoSuper) # Almacenamos el Nº de no etiquetados
XtrSuNoSu=XTrSuper+XTrNoSuper[:nNoSuper] # Entrenamiento con etiquetados y los primeros nNoSuper no etiquetados
YtrSuNoSu=YTrSuper+YTrNoSuper[:nNoSuper] # Test con etiquetados y los primeros nNoSuper no etiquetados
# LabelPropagation
LP.fit(XtrSuNoSu,YtrSuNoSu)
P=LP.predict(XTe)
LPAc=accuracy_score(YTe,P)
LPAcs.append(LPAc)
# LabelSpreading
LS.fit(XtrSuNoSu,YtrSuNoSu)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
LSAcs.append(LSAc)
print('Ejemplos etiquetados:{:3}, no etiquetados:{:5}.'.format(ntrSuper,nNoSuper),end=' ')
print(' Acierto: LP={:6.4f} LS={:6.4f}'.format(LPAc,LSAc))
#%% Gráfica de acierto - Ejemplos no etiquetado
plt.figure()
plt.title('Ejemplos etiquetados:{}'.format(ntrSuper))
plt.xlabel('Ejemplos no etiquetados')
plt.ylabel('Acierto')
plt.plot(nNoSupers,LPAcs,label='LabelPropagation')
plt.plot(nNoSupers,LSAcs,label='LabelSpreading')
plt.legend()
plt.grid(axis='y')
plt.savefig('LP_LS_Letter_MN.pdf')
#%% Ejercicio 1
"""
Se quiere mejorar la clasificación entre M y N añadiendo ejemplos no etiquetados
de letras que se parezcan. En este caso se utilizarán las letras W y Z que
rotándolas tienen similitudes con M y N.
Se proporciona:
Entrenamiento:
Ejemplos etiquetados de M y N : XTrSuperWZ,YTrSuperWZ
Ejemplos no etiquetados de M y N : XTrMNNoSuper,YTrMNNoSuper
Ejemplos no etiquetados de W y Z : XTrNoSuperWZ,YTrNoSuperWZ
Test:
Ejemplos etiquetados de M y N : XTeMN,YTeMN
Calcular la accuracy para LabelSpreading (LS) en las siguientes situaciones:
A) Solo se utilizan ejemplos etiquetados de M y N
Esto sería aprendizaje sueprvisado clásico.
B) Se utilizan ejemplos etiquetados y no etiquetados de M y N
Aprendizaje no supervisado clásico.
C) Se utilizan ejemplos etiquetados de M y N junto con los no etiquetados de W y Z
No tenemos ejemplos no etiquetados de M y N, ¿Podría mejorar con otros no etiquetados?
D) Se utilizan ejemplos etiquetados y no etiquetados de M y N y no etiquetados de W y Z
Utilizamos todos los ejemplos disponibles
D) ¿Como se calcula la accuracy?
E) ¿Cuál es la mejor opción?
SALIDA:
A) Accuracy LS=0.5905
B) Accuracy LS=0.7638
C) Accuracy LS=0.6248
D) Accuracy LS=0.7657
"""
# Datos
# Ejemplos de las letras N y M
[XTrMNSuper,YTrMNSuper,XTrMNNoSuper,YTrMNNoSuper,XTeMN,YTeMN]=leeSemiSuperLetter(letras=['M','N'],semilla=1,propTest=1/3.0,propEti=1/100.0)
# Ejemplos de las letras W y Z
[XTrWZSuper,YTrWZSuper,XTrWZNoSuper,YTrWZNoSuper,XTeWZ,YTeWZ]=leeSemiSuperLetter(letras=['W','Z'],semilla=1,propTest=0,propEti=0)
# NOTA: Los datos son listas python : type(XTrMNSuper) --> list
# Para concatenar listas utilizar el operador '+'
# Ejemplo:
A=[[1,2],[3,4]]
B=[[5,6],[7,8],[9,10]]
AB=A+B
print('A:',A)
print('B:',B)
print('A+B:',AB)
# NOTA: implementar la solución dentro de este bloque para evitar mostrar warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore')
#TODO BEGIN Ejercicio 1
# A) Solo se utilizan ejemplos etiquetados de M y N
XTr=XTrMNSuper
YTr=YTrMNSuper
# LabelSpreading
LS.fit(XTr,YTr)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
print('A) Accuracy LS={:6.4f}'.format(LSAc))
# B) Se utilizan ejemplos etiquetados y no etiquetados de M y N
XTr=XTrMNSuper+XTrMNNoSuper
YTr=YTrMNSuper+YTrMNNoSuper
# LabelSpreading
LS.fit(XTr,YTr)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
print('B) Accuracy LS={:6.4f}'.format(LSAc))
# C) Se utilizan ejemplos etiquetados de M y N junto con los no etiquetados de W y Z
XTr=XTrMNSuper+XTrWZNoSuper
YTr=YTrMNSuper+YTrWZNoSuper
# LabelSpreading
LS.fit(XTr,YTr)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
print('C) Accuracy LS={:6.4f}'.format(LSAc))
# D) Se utilizan ejemplos etiquetados de M y N junto con los no etiquetados de W y Z
XTr=XTrMNSuper+XTrMNNoSuper+XTrWZNoSuper
YTr=YTrMNSuper+YTrMNNoSuper+YTrWZNoSuper
# LabelSpreading
LS.fit(XTr,YTr)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
print('D) Accuracy LS={:6.4f}'.format(LSAc))
#END Ejercicio 1
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 1 : Parámetros y su optimización
Blqoue B : Parámetros
"""
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.metrics import accuracy_score
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
import matplotlib.pyplot as plt
import numpy as np
import warnings
# Los sistemas de aprendizaje semisupervisado LabelPropagation (LP) y
#LabelSpreading (LS) tienen varios parámetros:
# https://scikit-learn.org/stable/modules/generated/sklearn.semi_supervised.LabelPropagation.html
# https://scikit-learn.org/stable/modules/generated/sklearn.semi_supervised.LabelSpreading.html
# Los que vamos a manejar en esta práctica son (mirar descripción en la web):
# - kernel : {‘knn’, ‘rbf’}
# - gamma : float (para el kernel rbf)
# - n_neighbors : int (para el kernel rbf)
# Para LS también hay el parámetro:
# - alpha : float en [0,1]
# Partiendo del código de la sesión 1, modificarlo para que implemente estas variantes:
# LP1 rbf gamma=1
# LP2 knn n_neighbors=3
# LS1 rbf gamma=1
# LS2 knn n_neighbors=3 alpha=0.1
# Solución:
# Atributos usados en la representación (Cambiar al gusto. Valores en [0;15])
ix=6
iy=12
# Letras usadas como categoría (Cambiar al gusto. Valores letras mayúsculas en inglés)
L1='M'
L2='N'
# Se lee el dataset, dividido en entrenamiento y test
# El entranamiento se divide en supervisado y no supervisado
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=1,propTest=1/3.0,propEti=1/100.0)
#%% Variando NS
# Aprenderemos con diferentes proporciones de no etiquetado en el conjunto de
#entrenamiento para estudiar como afecta al error en test
# Evaluar el aprendizaje según aumenta el número de ejemplos no etiquetados
#usando accuracy_score
ntrSuper =len(XTrSuper) # Nº de ejemplos etiquetados
ntrNoSuper=len(XTrNoSuper) # Nº de ejemplos no etiquetados
# Algoritmos de aprendizaje semisupervisado
LP1=LabelPropagation(kernel='rbf',gamma=1,max_iter=1000)
LP2=LabelPropagation(kernel='knn',n_neighbors=3,max_iter=1000)
LS1=LabelSpreading(kernel='rbf',gamma=1,alpha=0.8,max_iter=1000)
LS2=LabelSpreading(kernel='knn',n_neighbors=3,alpha=0.1,max_iter=1000)
# Proporciones de ejemplos no etiquetados
PENS=[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]
# Almacenes de aciertos para cada sistema
LP1Acs =[]
LS1Acs =[]
LP2Acs =[]
LS2Acs =[]
nNoSupers=[] # Almacén de Nº de ejemplos no etiquetados en cada iteración
# NOTA: Los sistemas LabelPropagation y LabelSpreading dan algunos warnings
# que con las siguientes instrucciones evitan que se muestren
with warnings.catch_warnings():
warnings.simplefilter("ignore")
for PNS in PENS:
# El aprendizaje semi supervisado utiliza los ejemplos con y sin categoría
# Crear el dataset con la proporción apropiada de no etiquetados
nNoSuper=int(ntrNoSuper*PNS) # Nº de ejemplos no etiquetados
nNoSupers.append(nNoSuper) # Almacenamos el Nº de no etiquetados
XTrSuNoSu=XTrSuper+XTrNoSuper[:nNoSuper] # Entrenamiento con etiquetados y los primeros nNoSuper no etiquetados
YTrSuNoSu=YTrSuper+YTrNoSuper[:nNoSuper] # Test con etiquetados y los primeros nNoSuper no etiquetados
# LabelPropagation 1
LP1.fit(XTrSuNoSu,YTrSuNoSu)
P=LP1.predict(XTe)
LP1Ac=accuracy_score(YTe,P)
LP1Acs.append(LP1Ac)
# LabelSpreading 1
LS1.fit(XTrSuNoSu,YTrSuNoSu)
P=LS1.predict(XTe)
LS1Ac=accuracy_score(YTe,P)
LS1Acs.append(LS1Ac)
# LabelPropagation 2
LP2.fit(XTrSuNoSu,YTrSuNoSu)
P=LP2.predict(XTe)
LP2Ac=accuracy_score(YTe,P)
LP2Acs.append(LP2Ac)
# LabelSpreading 2
LS2.fit(XTrSuNoSu,YTrSuNoSu)
P=LS2.predict(XTe)
LS2Ac=accuracy_score(YTe,P)
LS2Acs.append(LS2Ac)
print('Ejemplos etiquetados:{:3}, no etiquetados:{:5}.'.format(ntrSuper,nNoSuper),end=' ')
print(' Acierto: LP1={:6.4f} LP2={:6.4f} LS1={:6.4f} LS2={:6.4f}'.
format(LP1Ac,LP2Ac,LS1Ac,LS2Ac))
#%% Gráfica de acierto - Ejemplos no etiquetado
plt.figure()
plt.title('Ejemplos etiquetados:{}'.format(ntrSuper))
plt.xlabel('Ejemplos no etiquetados')
plt.ylabel('Acierto')
plt.plot(nNoSupers,LP1Acs,label='LP 1')
plt.plot(nNoSupers,LS1Acs,label='LS 1')
plt.plot(nNoSupers,LP2Acs,label='LP 2')
plt.plot(nNoSupers,LS2Acs,label='LS 2')
plt.legend()
plt.grid(axis='y')
plt.savefig('LP_LS_Letter_MN.pdf')
#%% Ejercicio 1
"""
Para el total de ejemplos no supervisados y con el algoritmo
LS con kernel='knn' alpha=0.1
varía el número de vecinos desde 1 hasta el doble del número de atributos.
Calcula e imprime el acierto en cada iteración
SALIDA:
knn= 1 acierto: 0.4971
knn= 2 acierto: 0.4990
knn= 3 acierto: 0.5371
knn= 4 acierto: 0.5486
knn= 5 acierto: 0.5905
knn= 6 acierto: 0.6286
knn= 7 acierto: 0.6286
knn= 8 acierto: 0.6514
knn= 9 acierto: 0.6552
knn=10 acierto: 0.6610
knn=11 acierto: 0.6781
knn=12 acierto: 0.6914
knn=13 acierto: 0.7010
knn=14 acierto: 0.7162
knn=15 acierto: 0.7181
knn=16 acierto: 0.7124
knn=17 acierto: 0.7295
knn=18 acierto: 0.7143
knn=19 acierto: 0.7257
knn=20 acierto: 0.7295
knn=21 acierto: 0.7295
knn=22 acierto: 0.7295
knn=23 acierto: 0.7257
knn=24 acierto: 0.7219
knn=25 acierto: 0.7238
knn=26 acierto: 0.7219
knn=27 acierto: 0.7829
knn=28 acierto: 0.7943
knn=29 acierto: 0.8038
knn=30 acierto: 0.8057
knn=31 acierto: 0.8019
knn=32 acierto: 0.7905
"""
vecinos=list(range(1,len(XTe[0])*2+1)) # Vecinos sobre los que iterar
LSAcs=[] # Si se guardan aquí los aciertos, se genera una gráfica con los mismos
#<TODO BEGIN Ejercicio 1>
# Creamos conjunto de entrenamiento
XTrSuNoSu=XTrSuper+XTrNoSuper
YTrSuNoSu=YTrSuper+YTrNoSuper
with warnings.catch_warnings():
warnings.simplefilter("ignore")
for nn in vecinos:
# Se crea el objeto de aprendizaje semisupervisado
LS=LabelSpreading(kernel='knn',n_neighbors=nn,alpha=0.1,max_iter=1000)
# Proceso de entrenamiento, test y cálculo de la accuracy
LS.fit(XTrSuNoSu,YTrSuNoSu)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
# Guardar e imprimir la accuracy
LSAcs.append(LSAc)
print('knn={:2} acierto: {:6.4f}'.format(nn,LSAc))
#<TODO END Ejercicio 1>
#%% Gráfica de acierto - Ejemplos no etiquetado
plt.figure()
plt.title('Variando vecinos'.format(ntrSuper))
plt.xlabel('Número de vecinos (nn)')
plt.ylabel('Acierto')
plt.plot(vecinos,LSAcs,label='LS(knn)')
plt.legend()
plt.grid(axis='y')
plt.savefig('LS_knn_varia_nn.pdf')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 1 : Definición del problema y algoritmos de ASS
Actividad del Campus Virtual
"""
import matplotlib.pyplot as plt
import numpy as np
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
from sklearn.metrics import accuracy_score
import random
def genTest(puntosLado=25):
X=[]
Y=[]
def i2v(pl,i):
mpl=pl/2
return (i-mpl)/mpl
for f in range(puntosLado):
y=i2v(puntosLado,f)
for c in range(puntosLado):
x=i2v(puntosLado,c)
mod=(y**2+x**2)**0.5
if mod<0.5:
X.append([x,y])
Y.append(0)
elif mod>0.75:
X.append([x,y])
Y.append(1)
return [X,Y]
def pinta(X,Y,titulo,fileName=None):
X=np.array(X)
Y=np.array(Y)
plt.figure()
plt.ylim([-1.1,1.75])
plt.xlim([-1.1,1.75])
plt.title(titulo)
colors=['grey' ,'red' ,'blue' ]
labels=['No etiquetado','Dentro','Fuera']
for cate in [-1,0,1]:
inds=np.where(Y==cate)[0]
X0=X[inds,0]
X1=X[inds,1]
plt.plot(X0,X1,color=colors[cate+1],linestyle='None',markersize=8,marker='o',
label=labels[cate+1])
plt.legend(loc='upper left')
if fileName!=None:
plt.savefig(fileName)
#%% EJERCICIO 1
"""
Se tienen que clasificar los puntos en una zona donde hay un FOSO con el sistema
LabelSpreading
El foso está centrado en la posición (0,0) y empieza a una distancia 0.5 del
centro y termina a una distancia 0.75.
Solo se saben dos puntos:
el punto (0,0) es de clase 0
el punto (1,1) es de clase 1.
1: Comprobar qué accuracy se obtiene sin ejemplos no supervisados
2: Añadir los ejemplos no supervisados
3: Comprobar qué accuracy se obtiene con ejemplos no supervisados
4: Modificar los hyper-parámetros (manualmente) de LabelSpreading
para mejor esta última accuracy.
Se Proporciona un conjunto de test: XTe , YTe
Hay que crear el conjunto de entrenamiento y evaluarlo
NOTA: en cda paso hay una llamada a la función pinta que genera un .pdf
observa los .pdf como ayuda
"""
# Test
[XTe,YTe]=genTest()
pinta(XTe,YTe,'FOSO: Test','FOSO_Test.pdf')
# Ejemplos supervisados de entrenamiento
#TODO BEGIN 1.1
XTr=[[0,0],[1,1]]
YTr=[ 0 , 1 ]
#END 1.1
pinta(XTr,YTr,'FOSO: Entrenamiento solo etiquetados','FOSO_EntreSoloEti.pdf')
# Sistema de aprendizaje no supervisado LabelSpreading
max_iter=100000
LS=LabelSpreading(max_iter=max_iter)
# Cambia manualmente los hiperparámetros para conseguir mejor accuracy
#TODO BEGIN 1.2
LS=LabelSpreading(kernel='rbf',gamma=100,alpha=0.9999,max_iter=max_iter)
#END 1.2
# Realiza un entrenamiento / test y guarda la accuracy en Acc
#TODO 1.3
LS.fit(XTr,YTr)
P=LS.predict(XTe)
Acc=accuracy_score(YTe,P)
#END 1.3
print('Accuracy sin ejemplos no supervisados: {:6.4f}'.format(Acc))
pinta(XTe,P,'FOSO: Predicción del test con:\nEntrenamiento solo etiquetados','FOSO_PredcSoloEti.pdf')
# Añadir al entrenamiento (XTr,YTr) ejemplos no etiquetados
RG=random.Random()
RG.seed(1)
for r in range(1000):
# Punto en (x in [-1,+1],y in [-1,+1]) con distribución uniforme
x1=RG.random()*2-1
x2=RG.random()*2-1
dis=(x1**2+x2**2)**0.5 # Distancia al centro (al (0,0))
# Insertar en XTr,YTr el punto (x1,x2) como no supervisado si fuera el caso
#TODO BEGIN 1.4:
if dis<0.5 or dis>0.75:
XTr.append([x1,x2])
YTr.append(-1)
#END 1.4
pinta(XTr,YTr,'FOSO: Entrenamiento etiquetados y no etiquetados','FOSO_EntreEtiNoEti.pdf')
# Realiza un entrenamiento / test con los ejemplos supervisados y no supervisados
# Guarda la accuracy en Acc
#TODO 1.5:
LS.fit(XTr,YTr)
P=LS.predict(XTe)
Acc=accuracy_score(YTe,P)
# END TODO
print('Accuracy con ejemplos no supervisados: {:6.4f}'.format(Acc))
pinta(XTe,P,'FOSO: Predicción del test con:\nEntrenamiento etiquetados y no etiquetados','FOSO_PredcEtiNoEti.pdf')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 1 : Definición del problema y algoritmos de ASS
Actividad 1
"""
import random
import warnings
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer
from sklearn.preprocessing import OneHotEncoder
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
import matplotlib.pyplot as plt
import numpy as np
from SemiSuperDatasets.leeIris import divideTrainTest
def dibujaIris(XTr0,XTr1,XTr2,titulo,etiquetados=None):
nombresEachCat=['Setosa(0)','Virginica(1)','Versicolor(2)']
coloresEachCat=['tab:orange','g','b']
if etiquetados==None:
nombresCat=nombresEachCat
coloresCat=coloresEachCat
else:
nombresCat=[None,None,None]
coloresCat=['tab:gray','tab:gray','tab:gray']
marca='o'
attX=2
attY=3
attXNombre='Longitud del pétalo'
attYNombre='Anchura del pétalo'
X=[None]*3
X[0]=np.array(XTr0)[:,attX]
X[1]=np.array(XTr1)[:,attX]
X[2]=np.array(XTr2)[:,attX]
Y=[None]*3
Y[0]=np.array(XTr0)[:,attY]
Y[1]=np.array(XTr1)[:,attY]
Y[2]=np.array(XTr2)[:,attY]
plt.figure()
plt.title(titulo)
plt.xlabel(attXNombre)
plt.ylabel(attYNombre)
for p in range(len(X)):
plt.plot(X[p],Y[p],color=coloresCat[p],marker=marca,linestyle='None',label=nombresCat[p])
if etiquetados!=None:
e0=etiquetados[0]
plt.plot(X[0][e0],Y[0][e0],color=coloresEachCat[0],marker=marca,linestyle='None',label=nombresEachCat[0])
e1=etiquetados[1]
plt.plot(X[1][e1],Y[1][e1],color=coloresEachCat[1],marker=marca,linestyle='None',label=nombresEachCat[1])
e2=etiquetados[2]
plt.plot(X[2][e2],Y[2][e2],color=coloresEachCat[2],marker=marca,linestyle='None',label=nombresEachCat[2])
plt.legend()
plt.legend()
plt.savefig('{}.pdf'.format(titulo))
def unEtiquetadoPorCategoria(XTr0,XTr1,XTr2,semilla):
"""
Crea unas categorías donde solo hay un ejemplo etiquetado por cada una de
las tres categorías.
Parámetros:
- semilla : semilla del proceso aleatorio
- nCate0|1|2 : número de ejemplos por cada categoría
Retorna:
[XTr0NoEti,XTr0Eti,XTr1NoEti,XTr1Eti,XTr2NoEti,XTr2Eti,e0,e1,e2]:
De la categoría 0
XTr0NoEti: Conjunto de ejemplos no etiquetados (Todos menos 1)
XTr0Eti : Conjunto de ejemplos etiquetados (Solo 1)
(Análogo para el resto de categorías)
e0,e1,e2 : índice (posición) del ejemplo etiquetado de cada categoría
"""
RG=random.Random()
RG.seed(semilla)
# Ejemplo etiquetado por cada categoría
e0=RG.randrange(len(XTr0))
e1=RG.randrange(len(XTr1))
e2=RG.randrange(len(XTr2))
# Dividir en conjuntos etiquetados y no etiquetados
XTr0NoEti=XTr0[:e0]+XTr0[(e0+1):]
XTr0Eti=[XTr0[e0]]
XTr1NoEti=XTr1[:e1]+XTr1[(e1+1):]
XTr1Eti=[XTr1[e1]]
XTr2NoEti=XTr2[:e2]+XTr2[(e2+1):]
XTr2Eti=[XTr2[e2]]
return [XTr0NoEti,XTr0Eti,XTr1NoEti,XTr1Eti,XTr2NoEti,XTr2Eti,e0,e1,e2]
#%% EJERCICIO 1
"""
Utilizaremos el conjuntos de datos iris:
https://archive.ics.uci.edu/ml/machine-learning-databases/iris/
La categoría es un dígito que corresponde:
0 -> setosa
1 -> virginica
2 -> versicolor
Se da la función divideTrainTest que divide el conjunto en train y test, dividiendo
el train, a su vez, en tres conjuntos, uno de cada categoría.
El objetivo es comprobar si al usar solo un ejemplo de cada categoría el resto
de ejemplos usados como no etiquetados mejoran el aprendizaje.
Se pide repetir 10 veces variando la semilla del generador de números aleatorios:
- Seleccionar aleatoriamente 1 ejemplo de cada categoría, será el train supervisado.
- El resto de ejemplos serán serán el entrenamiento no supervisado.
Realizar 2 conjuntos de ejemplos:
A) Solamente usar ejemplos etiquetados (3, 1 por cada categoría)
B) Usar los ejemplos etiquetados y los no etiquetados
Realizar un proceso de aprendizaje/evaluación y calcular el acierto para los
sistemas: LabelPropagation(LP), y LabelSpreading(LS) tanto para el conjuto A)
como el B)
Calcular la mejora por utilizar ejemplos no etiquetados para LP y para LS
Almacenar los aciertos de LP y LS
Imprimir para cada sistema el acierto en A), en B) y la mejora
Al final de las 10 repeticiones imprimir la media de las mejoras de LP y LS
NOTA: Parte del código viene hecho. Rellenar en los bloques <TODO> </TODO>
NOTA: Mirar creaDataset.pdf
SALIDA: (Observar los .pdf)
Iteración 0 Etiquetados: Se:24 Vi:26 Ve: 2 Acierto y mejora:
- LP A)=0.8600 B)=0.9000 Mejora=0.0400
- LS A)=0.8600 B)=0.9200 Mejora=0.0600
Iteración 1 Etiquetados: Se: 8 Vi: 4 Ve:16 Acierto y mejora:
- LP A)=0.8400 B)=0.9200 Mejora=0.0800
- LS A)=0.8400 B)=0.8800 Mejora=0.0400
Iteración 2 Etiquetados: Se: 3 Vi: 5 Ve: 5 Acierto y mejora:
- LP A)=0.9200 B)=0.8800 Mejora=-0.0400
- LS A)=0.9200 B)=0.9000 Mejora=-0.0200
Iteración 3 Etiquetados: Se:15 Vi: 8 Ve:23 Acierto y mejora:
- LP A)=0.8800 B)=0.8800 Mejora=0.0000
- LS A)=0.8800 B)=0.9000 Mejora=0.0200
Iteración 4 Etiquetados: Se:15 Vi:19 Ve: 6 Acierto y mejora:
- LP A)=0.8000 B)=0.6600 Mejora=-0.1400
- LS A)=0.8000 B)=0.7800 Mejora=-0.0200
Iteración 5 Etiquetados: Se:16 Vi:22 Ve:33 Acierto y mejora:
- LP A)=0.9000 B)=0.9200 Mejora=0.0200
- LS A)=0.9000 B)=0.9200 Mejora=0.0200
Iteración 6 Etiquetados: Se: 5 Vi:31 Ve:16 Acierto y mejora:
- LP A)=0.8600 B)=0.9200 Mejora=0.0600
- LS A)=0.8600 B)=0.8600 Mejora=0.0000
Iteración 7 Etiquetados: Se:20 Vi: 9 Ve:25 Acierto y mejora:
- LP A)=0.9200 B)=0.8800 Mejora=-0.0400
- LS A)=0.9200 B)=0.9200 Mejora=0.0000
Iteración 8 Etiquetados: Se:14 Vi:23 Ve:24 Acierto y mejora:
- LP A)=0.8000 B)=0.7000 Mejora=-0.1000
- LS A)=0.8000 B)=0.8800 Mejora=0.0800
Iteración 9 Etiquetados: Se:29 Vi:23 Ve:17 Acierto y mejora:
- LP A)=0.8800 B)=0.7000 Mejora=-0.1800
- LS A)=0.8800 B)=0.9600 Mejora=0.0800
Mejora media LP=-0.0300 LS=0.0260
"""
# Lectura de datos iris
# Ejemplo de llamada a divideTrainTest
[XTr0,XTr1,XTr2,XTe,YTe]=divideTrainTest(semilla=1)
# Dibuja este entrenamiento
dibujaIris(XTr0,XTr1,XTr2,'Entrenamiento iris')
#%% Solución
# Algoritmos de aprendizaje semisupervisado
#TODO 1.1
# Crear los objetos LP y LS de las clases LabelPropagation y LabelSpreading respectivamente
LP=LabelPropagation()
LS=LabelSpreading()
#END 1.1
# Para almacenar las mejoras de LP y LS
mejorasLP=[]
mejorasLS=[]
warnings.simplefilter("ignore")
for rep in range(10): # Para 10 repeticiones
# Se obtiene un ejemplo etiquetado aleatorio de cada categoria:
# XTr0Eti, XTr1Eti, XTr2NoEti
# Y los no etiquetados
# XTr0NoEti, XTr1NoEti, XTr2NoEti
[XTr0NoEti,XTr0Eti,XTr1NoEti,XTr1Eti,XTr2NoEti,XTr2Eti,e0,e1,e2]=unEtiquetadoPorCategoria(XTr0,XTr1,XTr2,semilla=rep)
dibujaIris(XTr0,XTr1,XTr2,'Entrenamiento iris Iteración {}'.format(rep),etiquetados=[e0,e1,e2])
# Caso A) solo ejemplos etiquetados
#TODO 1.2
# Crear el conjunto de ejemplos [XTr,YTr] con solo los ejemplos etiquetados.
XTr=XTr0Eti+XTr1Eti+XTr2Eti
YTr=[0,1,2]
#END 1.2
# LabelPropagation
#TODO 1.3
# Realiza un proceso de aprendizaje evaluación para LP
# Calcula la accuracy con entrenamiento (XTr,YTr) y test (XTe,YTe)
# Almacénala en la variable LPAc_A
LP.fit(XTr,YTr)
P=LP.predict(XTe)
LPAc_A=accuracy_score(YTe,P)
#END 1.3
# LabelSpreading
#TODO 1.4
# Realiza un proceso de aprendizaje evaluación para LS
# Calcula la accuracy con entrenamiento (XTr,YTr) y test (XTe,YTe)
# Almacénala en la variable LSAc_A
LS.fit(XTr,YTr)
P=LS.predict(XTe)
LSAc_A=accuracy_score(YTe,P)
#END 1.4
# Caso B) ejemplos etiquetados y no etiquetados
#TODO 1.5
# Crear el conjunto de ejemplos [XTr,YTr] con los ejemplos etiquetados y los no etiquetados
XTrNoEti=XTr0NoEti+XTr1NoEti+XTr2NoEti
XTr=XTr+XTrNoEti
YTr=YTr+[-1]*len(XTrNoEti)
#END 1.5
# LabelPropagation
#TODO 1.6
# Realiza un proceso de aprendizaje evaluación para LP
# Calcula la accuracy con entrenamiento (XTr,YTr) y test (XTe,YTe)
# Almacénala en la variable LPAc_B
LP.fit(XTr,YTr)
P=LP.predict(XTe)
LPAc_B=accuracy_score(YTe,P)
#END 1.6
# LabelSpreading
#TODO 1.7
# Realiza un proceso de aprendizaje evaluación para LS
# Calcula la accuracy con entrenamiento (XTr,YTr) y test (XTe,YTe)
# Almacénala en la variable LSAc_-b
LS.fit(XTr,YTr)
P=LS.predict(XTe)
LSAc_B=accuracy_score(YTe,P)
#END 1.7
#TODO 1.8
# Calcular las mejoras de LP y LS
mejoraLP=LPAc_B-LPAc_A
mejoraLS=LSAc_B-LSAc_A
#END 1.8
# Almacenar las mejoras
mejorasLP.append(mejoraLP)
mejorasLS.append(mejoraLS)
# Imprimir el índice de los 3 ejemplos etiquetados
# Imprimir par LP y LS el acierto en la opción A), en B) y la mejora
print('Iteración {} Etiquetados: Se:{:2} Vi:{:2} Ve:{:2} Acierto y mejora:'.format(rep,e0,e1,e2))
print(' - LP A)={:6.4f} B)={:6.4f} Mejora={:6.4f}'.format(LPAc_A,LPAc_B,mejoraLP))
print(' - LS A)={:6.4f} B)={:6.4f} Mejora={:6.4f}'.format(LSAc_A,LSAc_B,mejoraLS))
print('Mejora media LP={:6.4f} LS={:6.4f}'.format(np.mean(mejorasLP),np.mean(mejorasLS)))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 1 : Definición del problema y algoritmos de ASS
Bloque Pre : Problemas semi-supervisados
"""
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.metrics import accuracy_score
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
import matplotlib.pyplot as plt
import numpy as np
import warnings
def getArribaAbajo(soloEtiquetaVertices=False):
X=[]
Y=[]
nPuntos=20
for ix1 in range(nPuntos):
x1=ix1/nPuntos
for ix2 in range(nPuntos):
x2=ix2/nPuntos
if x2>0.4 and x2<0.6:
continue
exa=[x1,x2]
if soloEtiquetaVertices:
cate=-1
else:
cate=1 if x2>0.5 else 0
X.append(exa)
Y.append(cate)
if soloEtiquetaVertices:
Y[0] =0
Y[len(Y)-1]=+1
return [np.array(X),np.array(Y)]
[XTr,YTr]=getArribaAbajo(soloEtiquetaVertices=True)
[XTe,YTe]=getArribaAbajo()
def pinta(X,Y,titulo,fichero=None):
plt.figure()
plt.title(titulo)
for iExa in range(len(X)):
colors=['tab:gray','tab:red','tab:blue']
exa=XTr[iExa]
color=colors[Y[iExa]+1]
plt.plot([exa[0]],[exa[1]],color=color,linestyle='None',markersize=12,marker='o')
if(fichero!=None):
plt.savefig(fichero)
pinta(XTr,YTr,'Arriba/Abajo Entrenamiento','AA_Train.pdf')
pinta(XTe,YTe,'Arriba/Abajo Test','AA_Test.pdf')
XTrEti=[XTr[0,:],XTr[len(XTr)-1,:]]
YTrEti=[YTr[0],YTr[len(YTr)-1]]
LP=LabelPropagation()
LP.fit(XTrEti,YTrEti)
Pse=LP.predict(XTe)
pinta(XTe,Pse,'Arriba/Abajo Predicción con solo etiquetados','AA_PredOnlyLabeled.pdf')
LP.fit(XTr,YTr)
P=LP.predict(XTe)
pinta(XTe,P,'Arriba/Abajo Predicción con etiquetados y NO etiquetados','AA_PredLabeledAndUnlabeled.pdf')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo
"""
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
import numpy as np
class concatenaNE_LP(LabelPropagation):
"""
Crea una versión de LabelPropagation que al aprender (fit) concatena un
conjunto fijo de ejemplos no etiquetados.
Funciona como LabelPropagation pero antes de aprender (fit) hay que utilizar
setNE para indicar el conjunto de ejemplos no etiquetados
"""
def __init__(self,XNe=None,kernel='rbf', *, gamma=20, n_neighbors=7, max_iter=1000, tol=0.001, n_jobs=None):
"""
Params:
XNe : Cadena que representa el conjunto de ejemplos no etiquetados.
Tiene que ser de tal manera que mediante eval(XNe) se obtenga
el conjunto de ejemplos original
Ejemplo:
X = [[1,2,3],[4,5,6]]
Xstr=X.__str__()
Conjunto de ejemplos no etiquetados. Se incorporarán con
categoría -1
Si XNe==None no se concatenan ejemplos
Para el resto ver el resto de parámetros ver: LabelPropagation
"""
super().__init__(kernel, gamma=gamma, n_neighbors=n_neighbors, max_iter=max_iter, tol=tol, n_jobs=n_jobs)
self.XNe=XNe
def fit(self,X,Y):
if self.XNe==None:
return self.fit(X,Y)
X=np.array(X)
XNe=eval(self.XNe)
XA=np.concatenate([X,XNe])
Y=np.array(Y)
YNe=[-1]*len(XNe)
YA=np.concatenate([Y,YNe])
return LabelPropagation.fit(self,XA,YA)
class concatenaNE_LS(LabelSpreading):
"""
Crea una versión de LabelPropagation que al aprender (fit) concatena un
conjunto fijo de ejemplos no etiquetados.
Funciona como LabelPropagation pero antes de aprender (fit) hay que utilizar
setNE para indicar el conjunto de ejemplos no etiquetados
"""
def __init__(self,XNe=None,kernel='rbf', *, gamma=20, n_neighbors=7, alpha=0.2, max_iter=30, tol=0.001, n_jobs=None):
"""
Params:
XNe : Cadena que representa el conjunto de ejemplos no etiquetados.
Tiene que ser de tal manera que mediante eval(XNe) se obtenga
el conjunto de ejemplos original
Ejemplo:
X = [[1,2,3],[4,5,6]]
Xstr=X.__str__()
Conjunto de ejemplos no etiquetados. Se incorporarán con
categoría -1
Si XNe==None no se concatenan ejemplos
Para el resto ver el resto de parámetros ver: LabelPropagation
"""
super().__init__(kernel, gamma=gamma, n_neighbors=n_neighbors, alpha=alpha, max_iter=max_iter, tol=tol, n_jobs=n_jobs)
self.XNe=XNe
def fit(self,X,Y):
if self.XNe==None:
return self.fit(X,Y)
X=np.array(X)
XNe=eval(self.XNe)
XA=np.concatenate([X,XNe])
Y=np.array(Y)
YNe=[-1]*len(XNe)
YA=np.concatenate([Y,YNe])
return LabelSpreading.fit(self,XA,YA)
"""
@author: quevedo
"""
from sklearn.metrics import accuracy_score
def ssAccuracy(Y,P):
"""
Calcula accuracy_score sobre los ejemplos cuya Y no vale -1
"""
Yf=[]
Pf=[]
for r in range(len(Y)):
if Y[r]!=-1:
Yf.append(Y[r])
Pf.append(P[r])
return accuracy_score(Yf,Pf)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 2 : Parámetros y su optimización
Blqoue A : Estimación de error (CV)
"""
# En este bloque se mostará cómo estimar el error usando validación cruzada
#en problemas semisupervisados
import numpy as np
import csv
import random
import warnings
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.metrics import accuracy_score
from sklearn.model_selection import cross_val_score
from sklearn.metrics import make_scorer
from sklearn.semi_supervised import SelfTrainingClassifier,LabelPropagation,LabelSpreading
from semisuperAA2.ssAccuracy import ssAccuracy
from semisuperAA2.concatenaNE import concatenaNE_LP,concatenaNE_LS
# Atributos usados en la representación (Cambiar al gusto. Valores en [0;15])
ix=6
iy=12
# Letras usadas como categoría (Cambiar al gusto. Valores letras mayúsculas en inglés)
L1='O'
L2='D'
# Se lee el dataset, dividido en entrenamiento y test
# El entranamiento se divide en supervisado y no supervisado
# en este caso se toman 3/100 de ejemplos de entrenamiento para que haya algunos
#más y así tener más ejemplos para los entrenamientos/test de la validación cruazada
#del GridSerch
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=1,propTest=1/3.0,propEti=3/100.0)
warnings.simplefilter("ignore")
#%% A) Estimar el error repartiendo los no etiquetados en los folds:
print('\n** LabelPropagation:')
# A.1 Se crea el conjunto de entrenamiento con los ejemplos etiquetados y no etiquetados
X=XTrSuper+XTrNoSuper
Y=YTrSuper+YTrNoSuper
# A.2 Se utiliza un algoritmo semisupervisado original
LP=LabelPropagation()
# A.3 Se utiliza una validación cruzada con una medida de calidad que ignore
# los ejemplos no etiquetados, en este caso ssAccuracy
# Ver semisuperAA2/ssAccuracy.py
PCVA=cross_val_score(LP,X,Y,scoring=make_scorer(ssAccuracy),cv=3)
print(' Estimar el acierto repartiendo no etiquetados entre los folds:')
print(' - Accuracy: {:6.4f}'.format(np.mean(PCVA)))
#%% B) Estimar el error usando siempre todos los no etiquetados
# B.1 El conjunto de entrenamiento son solo los ejemplos etiquetados
X=XTrSuper
Y=YTrSuper
# B.2 Se utiliza un algoritmo semisupervisado que permite concatenar un conjunto
# de ejemplos no etiquetados al hacer el entrenamiento.
ConLP=concatenaNE_LP(XTrNoSuper)
# B.3 Se utiliza una validación cruzada con una medida de calidad clásica
PCVB=cross_val_score(ConLP,X,Y,scoring=make_scorer(accuracy_score),cv=3)
print(' Estimar el acierto usando siempre el mismo conjunto de no etiquetados')
print(' - Accuracy: {:6.4f}'.format(np.mean(PCVB)))
#%% Ejercicio 1
"""
Repetir las estimaciones anteriores de las 2 maneras para LabelSpreading
SALIDA:
** LabelSpreading:
Estimar el acierto repartiendo no etiquetados entre los folds:
- Accuracy: 0.9061
Estimar el acierto usando siempre el mismo conjunto de no etiquetados
- Accuracy: 0.9697
"""
#TODO Ejercicio 1
# A) Estimar el error repartiendo los no etiquetados en los folds:
print('\n** LabelSpreading:')
# A.1 Se crea el conjunto de entrenamiento con los ejemplos etiquetados y no etiquetados
X=XTrSuper+XTrNoSuper
Y=YTrSuper+YTrNoSuper
# A.2 Se utiliza un algoritmo semisupervisado original
LS=LabelSpreading()
# A.3 Se utiliza una validación cruzada con una medida de calidad que ignore
# los ejemplos no etiquetados, en este caso ssAccuracy
# Ver semisuperAA2/ssAccuracy.py
PCVA=cross_val_score(LS,X,Y,scoring=make_scorer(ssAccuracy),cv=3)
print(' Estimar el acierto repartiendo no etiquetados entre los folds:')
print(' - Accuracy: {:6.4f}'.format(np.mean(PCVA)))
# B) Estimar el error usando siempre todos los no etiquetados
# B.1 El conjunto de entrenamiento son solo los ejemplos etiquetados
X=XTrSuper
Y=YTrSuper
# B.2 Se utiliza un algoritmo semisupervisado que permite concatenar un conjunto
# de ejemplos no etiquetados.
ConLS=concatenaNE_LS(XTrNoSuper)
# B.3 Se utiliza una validación cruzada con una medida de calidad clásica
PCVB=cross_val_score(ConLS,X,Y,scoring=make_scorer(accuracy_score),cv=3)
print(' Estimar el acierto usando siempre el mismo conjunto de no etiquetados')
print(' - Accuracy: {:6.4f}'.format(np.mean(PCVB)))
#END Ejercicio 1
#%% Ejercicio 2
"""
Para LabelSpreading, haz un código que imprima cuál de las dos opciones de
estimar el error es mejor
"""
#TODO Ejercicio 2
# Se utilizan todos los ejemplos etiquetados o no
XTr=XTrSuper+XTrNoSuper
YTr=YTrSuper+YTrNoSuper
LS.fit(XTr,YTr)
P=LS.predict(XTe)
AccTest=accuracy_score(YTe,P)
print('Accuracy en test: {:6.4f}'.format(AccTest))
AccA=np.mean(PCVA)
AccB=np.mean(PCVB)
DisA=abs(AccTest-AccA)
DisB=abs(AccTest-AccB)
if DisA<DisB:
print('Mejor modo A) repartiendo no etiquetados entre los folds')
else:
print('Mejor modo B) usando siempre el mismo conjunto de no etiquetados')
#END Ejercicio 2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 2 : Parámetros y su optimización
Blqoue B : Optimización de parámetros
"""
# Como se ha visto en el bloque anterior, los parámetros tienen una gran influencia
#en la calidad de los sistemas.
# Una manera adecuada para estimar los parámetros es utilizar GridSearchCV
# Partiendo del código del Bloque A, añadir un sistema para LP y otro para LS
#que optimice:
# - gamma en [2**-2,2**-1,2**0,2**1,2**2]
# - n_neighbors en [3,5,7,9,11]
# Problema:
# Los ejemplos que hay en el conjunto de datos son supervisados y no supervisados
#y estos últimos también serán tratados por el CV del Grid Search, lo que
#provocará que aparezcan en el los folds y por tanto en los tests.
# Cuando se vaya a calcular la medida para un par Predicción(P) Valor en test(Y)
#ocurrirá que habrá valores en Y que serán -1, esto es, no supervisados, y éstos
#no tendrán que participar en el cálculo de la medida.
# Solución:
# Se crea una función de pérdida que solo tiene en cuenta los valores de la Y
#que no son -1. Función ssAccuracy, línea 63.
import csv
import random
import warnings
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV,train_test_split,StratifiedKFold
from sklearn.metrics import make_scorer
from semisuperAA2.ssAccuracy import ssAccuracy
from semisuperAA2.concatenaNE import concatenaNE_LS
from sklearn.preprocessing import OneHotEncoder
from sklearn.semi_supervised import SelfTrainingClassifier,LabelPropagation,LabelSpreading
import matplotlib.pyplot as plt
# Atributos usados en la representación (Cambiar al gusto. Valores en [0;15])
ix=6
iy=12
# Letras usadas como categoría (Cambiar al gusto. Valores letras mayúsculas en inglés)
L1='M'
L2='N'
# Se lee el dataset, dividido en entrenamiento y test
# El entranamiento se divide en supervisado y no supervisado
# en este caso se toman 3/100 de ejemplos de entrenamiento para que haya algunos
#más y así tener más ejemplos para los entrenamientos/test de la validación cruazada
#del GridSerch
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=1,propTest=1/3.0,propEti=3/100.0)
#%% Descomposición del entrenamiento en validación cruzada
# Vamos a simular una descomposición entrenamiento/test de una validación cruzada
#donde el conjunto de entrenamiento esté compuesto por los ejemplos etiquetados
#y los no etiquetados.
# Para ello usaremos train_test_split:
# https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
# Concatenamos los ejemplos etiquetados y no etiquetados para obtener el conjunto
#de entrenamiento completo
X=XTrSuper+XTrNoSuper
Y=YTrSuper+YTrNoSuper
# [XFoldTr,XFoldTe,YFoldTr,YFoldTe]=train_test_split(X,Y,test_size=1.0/3,random_state=1)
import numpy as np
def dibujaSS(X,Y,titulo,Atx=6,Aty=12):
colors=['tab:gray','tab:orange','tab:blue']
markers=['x','s','s']
markerssize=[12,8,8]
npY=np.array(Y)
npX=np.array(X)
tit=titulo
plt.figure(figsize=[6,6])
plt.axis([0,14,0,16])
cates=np.unique(Y)
for icate in range(len(cates)):
cate=cates[icate]
indexes=np.where(npY==cate)[0]
xp=npX[indexes,Atx]
yp=npX[indexes,Aty]
plt.plot(xp,yp,color=colors[icate],linestyle='',marker=markers[icate],
markersize=markerssize[icate],label='Clase:{}. Ejemplos:{}'.format(cate,len(indexes)))
plt.title(titulo)
plt.legend(loc='upper right')
plt.savefig('Letter_{}.pdf'.format(titulo))
SplitsFolds=StratifiedKFold(n_splits=3,shuffle=True, random_state=1)
folds=SplitsFolds.split(X,Y)
X=np.array(X)
Y=np.array(Y)
dibujaSS(X,Y,'Entrenamiento')
for i, (train_index, test_index) in enumerate(folds):
fX=X[test_index,:]
fY=Y[test_index]
dibujaSS(fX,fY,'Fold_{}'.format(i))
#%% Variando NS
# Aprenderemos con diferentes proporciones de no etiquetado en el conjunto de
#entrenamiento para estudiar como afecta al error en test
ntrSuper =len(XTrSuper) # Nº de ejemplos etiquetador
ntrNoSuper=len(XTrNoSuper) # Nº de ejemplos no etiquetados
# Algoritmos de aprendizaje semisupervisado
LP=LabelPropagation(kernel='knn',n_neighbors=7,max_iter=1000)
LPGS=GridSearchCV(LP,{'n_neighbors':[8,16,32]},
scoring=make_scorer(ssAccuracy,greater_is_better=True),cv=3)
LS=LabelSpreading(kernel='knn',n_neighbors=7,max_iter=1000)
LSGS=GridSearchCV(LS,{'n_neighbors':[8,16,32]},
scoring=make_scorer(ssAccuracy,greater_is_better=True),cv=3)
# Proporciones de ejemplos no etiquetados
PENS=[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]
# Almacenes de aciertos para cada sistema
LPAcs =[]
LSAcs =[]
LPGSAcs =[]
LSGSAcs =[]
LPMejoraGS=[]
LSMejoraGS=[]
nNoSupers=[] # Nº de ejemplos no etiquetados en cada iteración
warnings.simplefilter("ignore")
for PNS in PENS:
# Crear el dataset con la proporción apropiada de no supervisados
nNoSuper=int(ntrNoSuper*PNS)
nNoSupers.append(nNoSuper)
XTrSuNoSu=XTrSuper+XTrNoSuper[:nNoSuper]
YTrSuNoSu=YTrSuper+YTrNoSuper[:nNoSuper]
print('-LabelPropagation')
LP.fit(XTrSuNoSu,YTrSuNoSu)
P=LP.predict(XTe)
LPAc=accuracy_score(YTe,P)
LPAcs.append(LPAc)
LPGS.fit(XTrSuNoSu,YTrSuNoSu)
P=LPGS.predict(XTe)
LPGSAc=accuracy_score(YTe,P)
LPGSAcs.append(LPGSAc)
LPMejoraGS.append(LPGSAc-LPAc)
print('LabelSpreading')
LS.fit(XTrSuNoSu,YTrSuNoSu)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
LSAcs.append(LSAc)
print('len(LSAcs)={}'.format(len(LSAcs)))
LSGS.fit(XTrSuNoSu,YTrSuNoSu)
P=LSGS.predict(XTe)
LSGSAc=accuracy_score(YTe,P)
LSGSAcs.append(LSGSAc)
LSMejoraGS.append(LSGSAc-LSAc)
print('Ejemplos etiquetados:{:5}, no etiquetados:{:5}.'
.format(ntrSuper,nNoSuper))
print(' Acierto: LP={:6.4f} LPGS={:6.4f} LS={:6.4f} LSGS={:6.4f} '.
format(LPAc,LPGSAc,LSAc,LSGSAc))
#%% Gráfica de acierto - Ejemplos no etiquetados
plt.figure()
plt.title('Ejemplos etiquetados:{}'.format(ntrSuper))
plt.xlabel('Ejemplos no etiquetados')
plt.ylabel('Acierto')
plt.plot(nNoSupers,LPAcs,label='LP')
plt.plot(nNoSupers,LPGSAcs,label='LP GS')
plt.plot(nNoSupers,LSAcs,label='LS')
plt.plot(nNoSupers,LSGSAcs,label='LS GS')
plt.legend()
plt.savefig('Letter_Acierto_ConSin_GS.pdf')
#%% Gráfica de mejora del Grid Search - Ejemplos no etiquetados
plt.figure()
plt.tight_layout(pad=1.5)
plt.title('Ejemplos etiquetados:{}'.format(ntrSuper))
plt.xlabel('Ejemplos no etiquetados')
plt.ylabel('Mejora en acierto del GS')
plt.plot(nNoSupers,LPMejoraGS,label='LP Mejora GS')
plt.plot(nNoSupers,LSMejoraGS,label='LS Mejora GS')
plt.grid(axis='y')
plt.legend()
plt.savefig('Letter_Mejora_GS.pdf')
#%% Ejercicio 1
"""
Con el algoritmo LS con kernel='knn' calcular cuanto mejora al utilizar Grid Search
donde se busca en n_neighbors entre los valores 8, 16 y 32.
Pero en este caso en cada entrenamiento de LS (incluído en el GridSearch) se
han de utilizar TODOS los ejemplos no supervisados
Repite la llamanda a leeSemiSuperLetter variando la semilla 20 veces (de 1 a 20)
Imprimir la mejora media y el porcentaje de veces que gana utilizar Grid Search
Salida:
Semilla= 1 Acierto LS=0.6476 LS con GS=0.8648 mejora=+0.2171
Semilla= 2 Acierto LS=0.6952 LS con GS=0.8533 mejora=+0.1581
Semilla= 3 Acierto LS=0.7105 LS con GS=0.8629 mejora=+0.1524
Semilla= 4 Acierto LS=0.6705 LS con GS=0.8648 mejora=+0.1943
Semilla= 5 Acierto LS=0.6438 LS con GS=0.8571 mejora=+0.2133
Semilla= 6 Acierto LS=0.6667 LS con GS=0.8667 mejora=+0.2000
Semilla= 7 Acierto LS=0.6857 LS con GS=0.8552 mejora=+0.1695
Semilla= 8 Acierto LS=0.7010 LS con GS=0.8667 mejora=+0.1657
Semilla= 9 Acierto LS=0.7048 LS con GS=0.8800 mejora=+0.1752
Semilla=10 Acierto LS=0.6800 LS con GS=0.8571 mejora=+0.1771
Semilla=11 Acierto LS=0.7200 LS con GS=0.8838 mejora=+0.1638
Semilla=12 Acierto LS=0.7029 LS con GS=0.8610 mejora=+0.1581
Semilla=13 Acierto LS=0.6533 LS con GS=0.8514 mejora=+0.1981
Semilla=14 Acierto LS=0.6400 LS con GS=0.8838 mejora=+0.2438
Semilla=15 Acierto LS=0.6876 LS con GS=0.8610 mejora=+0.1733
Semilla=16 Acierto LS=0.6610 LS con GS=0.8514 mejora=+0.1905
Semilla=17 Acierto LS=0.6952 LS con GS=0.8610 mejora=+0.1657
Semilla=18 Acierto LS=0.6724 LS con GS=0.8667 mejora=+0.1943
Semilla=19 Acierto LS=0.7105 LS con GS=0.8724 mejora=+0.1619
Semilla=20 Acierto LS=0.6514 LS con GS=0.8495 mejora=+0.1981
Mejora media=0.1835
Hay mejora en el 100.0% de los experimentos
"""
semillas=list(range(1,20+1))
LSMejoraGS=[]
VecesMejoraGS=0
for semilla in semillas:
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=semilla,propTest=1/3.0,propEti=3/100.0)
# Ejemplos etiquetados y no etiquetados
XTrSuNoSu=XTrSuper+XTrNoSuper
YTrSuNoSu=YTrSuper+YTrNoSuper
#TODO 1.1
GSLS=GridSearchCV(LS,{'n_neighbors':[8,16,32]},
scoring=make_scorer(accuracy_score,greater_is_better=True),cv=3)
#END 1.1
with warnings.catch_warnings():
warnings.simplefilter("ignore")
# Sin GridSearch
#TODO 1.2
LS.fit(XTrSuNoSu,YTrSuNoSu)
P=LS.predict(XTe)
LSAc=accuracy_score(YTe,P)
LSAcs.append(LSAc)
#END 1.2
# Con Grid Search
#TODO 1.3
LSGS.fit(XTrSuNoSu,YTrSuNoSu)
P=LSGS.predict(XTe)
LSGSAc=accuracy_score(YTe,P)
LSGSAcs.append(LSGSAc)
#END 1.3
#TODO 1.4
MejoraGS=LSGSAc-LSAc # Cáclulo de la mejora
#END 1.4
LSMejoraGS.append(MejoraGS) # Almacenar la mejora
if MejoraGS>0:
VecesMejoraGS=VecesMejoraGS+1 # Contar cuando el GS mejora a no GS
print('Semilla={:2} Acierto LS={:6.4f} LS con GS={:6.4f} mejora={:+6.4f}'.
format(semilla,LSAc,LSGSAc,LSGSAc-LSAc))
print('Mejora media={:6.4f}'.format(sum(LSMejoraGS)/len(semillas)))
print('Hay mejora en el {:3.1f}% de los experimentos'.format(100*VecesMejoraGS/len(semillas)))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 2 : Parámetros y su optimización
Blqoue B : Optimización de parámetros
"""
# Como se ha visto en el bloque anterior, los parámetros tienen una gran influencia
#en la calidad de los sistemas.
# Una manera adecuada para estimar los parámetros es utilizar GridSearchCV
# Partiendo del código del Bloque A, añadir un sistema para LP y otro para LS
#que optimice:
# - gamma en [2**-2,2**-1,2**0,2**1,2**2]
# - n_neighbors en [3,5,7,9,11]
# Problema:
# Los ejemplos que hay en el conjunto de datos son supervisados y no supervisados
#y estos últimos también serán tratados por el CV del Grid Search, lo que
#provocará que aparezcan en el los folds y por tanto en los tests.
# Cuando se vaya a calcular la medida para un par Predicción(P) Valor en test(Y)
#ocurrirá que habrá valores en Y que serán -1, esto es, no supervisados, y éstos
#no tendrán que participar en el cálculo de la medida.
# Solución:
# Se crea una función de pérdida que solo tiene en cuenta los valores de la Y
#que no son -1. Función ssAccuracy, línea 63.
import csv
import random
import warnings
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV,train_test_split,StratifiedKFold
from sklearn.metrics import make_scorer
from semisuperAA2.ssAccuracy import ssAccuracy
from sklearn.preprocessing import OneHotEncoder
from sklearn.semi_supervised import SelfTrainingClassifier,LabelPropagation,LabelSpreading
import matplotlib.pyplot as plt
# Atributos usados en la representación (Cambiar al gusto. Valores en [0;15])
ix=6
iy=12
# Letras usadas como categoría (Cambiar al gusto. Valores letras mayúsculas en inglés)
L1='M'
L2='N'
# Se lee el dataset, dividido en entrenamiento y test
# El entranamiento se divide en supervisado y no supervisado
# en este caso se toman 3/100 de ejemplos de entrenamiento para que haya algunos
#más y así tener más ejemplos para los entrenamientos/test de la validación cruazada
#del GridSerch
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=1,propTest=1/3.0,propEti=3/100.0)
#%% Descomposición del entrenamiento en validación cruzada
# Vamos a simular una descomposición entrenamiento/test de una validación cruzada
#donde el conjunto de entrenamiento esté compuesto por los ejemplos etiquetados
#y los no etiquetados.
# Para ello usaremos train_test_split:
# https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
# Concatenamos los ejemplos etiquetados y no etiquetados para obtener el conjunto
#de entrenamiento completo
X=XTrSuper+XTrNoSuper
Y=YTrSuper+YTrNoSuper
# [XFoldTr,XFoldTe,YFoldTr,YFoldTe]=train_test_split(X,Y,test_size=1.0/3,random_state=1)
import numpy as np
def dibujaSS(X,Y,titulo,Atx=6,Aty=12):
colors=['tab:gray','tab:orange','tab:blue']
markers=['x','s','s']
markerssize=[12,8,8]
npY=np.array(Y)
npX=np.array(X)
tit=titulo
plt.figure(figsize=[6,6])
plt.axis([0,14,0,16])
cates=np.unique(Y).tolist()
if -1 not in cates:
cates=[-1]+cates
for icate in range(len(cates)):
cate=cates[icate]
indexes=np.where(npY==cate)[0]
if len(indexes)>0:
xp=npX[indexes,Atx]
yp=npX[indexes,Aty]
plt.plot(xp,yp,color=colors[icate],linestyle='',marker=markers[icate],
markersize=markerssize[icate],label='Clase:{}. Ejemplos:{}'.format(cate,len(indexes)))
plt.title(titulo)
plt.legend(loc='upper right')
plt.savefig('Letter_{}.pdf'.format(titulo))
X=np.array(X)
Y=np.array(Y)
dibujaSS(X,Y,'Entrenamiento')
dibujaSS(XTrSuper,YTrSuper,'Entrenamiento etiquetado')
dibujaSS(XTrNoSuper,YTrNoSuper,'Entrenamiento NO etiquetado')
# Los no etiquetados se distribuyen por los folds
SplitsFolds=StratifiedKFold(n_splits=3,shuffle=True, random_state=1)
folds=SplitsFolds.split(X,Y)
for i, (train_index, test_index) in enumerate(folds):
fX=X[test_index,:]
fY=Y[test_index]
dibujaSS(fX,fY,'Fold_{}'.format(i))
# Solo los ejemplos etiquetados se distribuyen por los folds
SplitsFolds=StratifiedKFold(n_splits=3,shuffle=True, random_state=1)
folds=SplitsFolds.split(XTrSuper,YTrSuper)
for i, (train_index, test_index) in enumerate(folds):
fX=X[test_index,:]
fY=Y[test_index]
dibujaSS(fX,fY,'Fold_{}'.format(i))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 2 : Parámetros y su optimización
Actividad 1
"""
from sklearn.semi_supervised import LabelSpreading
from sklearn.metrics.pairwise import rbf_kernel,sigmoid_kernel,chi2_kernel,laplacian_kernel
from SemiSuperDatasets.leeSemiSuperLetter import leeSemiSuperLetter
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score,make_scorer
import warnings
import math
import numpy as np
# Letras usadas como categoría (Cambiar al gusto. Valores letras mayúsculas en inglés)
L1='M'
L2='N'
[XTrSuper,YTrSuper,XTrNoSuper,YTrNoSuper,XTe,YTe]=leeSemiSuperLetter(letras=[L1,L2],semilla=1,propTest=1/3.0,propEti=3/100.0)
XTr=XTrSuper+XTrNoSuper
YTr=YTrSuper+YTrNoSuper
#%% Ejericicio 0
"""
Partiendo del problema de la sesión 2:
Se quiere probar con varios kernels.
A continuación se muestra un uso de entrenamiento/test con 4 opciones:
A) predefinido (constante cadena)
B) pairwise kernel (con valores por defecto)
C) función que llama a pairwise kernel y que fija parámetros
D) objeto que implementa el método __call__ y que se da valor a gamma en el
constructor (__init__)
En este ejercicio no hay que programar, solo entender lo hecho
"""
# A) Kernel 'rbf'
# Se pasa como constante cadena. Gamma=20
LSrbf=LabelSpreading(kernel='rbf')
LSrbf.fit(XTr,YTr)
Prbf=LSrbf.predict(XTe)
# B) Kernel rbf pairwise:
# https://scikit-learn.org/stable/modules/metrics.html#metrics
# gamma= 1 / atributos
LSKrbf=LabelSpreading(kernel=rbf_kernel)
LSKrbf.fit(XTr,YTr)
PKrbf=LSKrbf.predict(XTe)
# C) Kernel rbf donde se especifica el valor de gamma
# Es necesario crear una función nueva, ya que el parámetro gamma no se puede
#pasar explícitamente en la llamada al kernel
# En este caso gamma=1
def miKernelRBFGammaFijo(X1,X2):
return rbf_kernel(X1,X2,gamma=1)
LSMKrbfGF=LabelSpreading(kernel=miKernelRBFGammaFijo)
LSMKrbfGF.fit(XTr,YTr)
PMKrbfGF=LSMKrbfGF.predict(XTe)
# D) Kernel como llamada al método __call__ de un objeto donde el valor gamma
#se para como parámetro al constructor. De esta manera se puede indicar el valor
#de gamma sin que sea fijo como en el caso C)
class MiKernelRBFGammaVariable:
def __init__(self,gamma):
self.gamma=gamma
def __call__(self,X1,X2):
return rbf_kernel(X1,X2,self.gamma)
def __str__(self):
return 'MiKernelRBFGammaVariable(gamma={})'.format(self.gamma)
miKrbfGV=MiKernelRBFGammaVariable(gamma=2)
LSMKrbfGV=LabelSpreading(kernel=miKrbfGV)
LSMKrbfGV.fit(XTr,YTr)
PMKrbfGV=LSMKrbfGV.predict(XTe)
print('LS(rbf) : accuracy_score={:6.4f}'.format(accuracy_score(YTe,Prbf)))
print('LS(Krbf) : accuracy_score={:6.4f}'.format(accuracy_score(YTe,PKrbf)))
print('LS(MKrbfGF): accuracy_score={:6.4f}'.format(accuracy_score(YTe,PMKrbfGF)))
print('LS(MKrbfGV): accuracy_score={:6.4f}'.format(accuracy_score(YTe,PMKrbfGV)))
#%% Ejercicio 1
"""
Repite los pasos B), C) y D) con el kernel Laplacian : laplacian_kernel
Puedes utilizar las siglas: lpc
NOTA: puedes copiar y pegar todo el código anterior y luego
1 seleccionas el texto pegado
2 reemplazar (Ctrl+r) "rbf" por "lpc" solo en la selección
3 cambiar rbf_kernel (ahora será lpc_kernel) por laplacian_kernel
SALIDA:
LS(Klpc) : accuracy_score=0.8762
LS(MKlpcGF): accuracy_score=0.9067
LS(MKlpcGV): accuracy_score=0.9048
"""
#TODO 1
# B) Kernel lpc pairwise:
# https://scikit-learn.org/stable/modules/metrics.html#metrics
# gamma= 1 / atributos
LSKlpc=LabelSpreading(kernel=laplacian_kernel)
LSKlpc.fit(XTr,YTr)
PKlpc=LSKlpc.predict(XTe)
# C) Kernel lpc donde se especifica el valor de gamma
# Es necesario crear una función nueva, ya que el parámetro gamma no se puede
#pasar explícitamente en la llamada al kernel
# En este caso gamma=1
def miKernellpcGammaFijo(X1,X2):
return laplacian_kernel(X1,X2,gamma=1)
LSMKlpcGF=LabelSpreading(kernel=miKernellpcGammaFijo)
LSMKlpcGF.fit(XTr,YTr)
PMKlpcGF=LSMKlpcGF.predict(XTe)
# D) Kernel como llamada al método __call__ de un objeto donde el valor gamma
#se para como parámetro al constructor. De esta manera se puede indicar el valor
#de gamma sin que sea fijo como en el caso C)
class MiKernelLPCGammaVariable:
def __init__(self,gamma):
self.gamma=gamma
def __call__(self,X1,X2):
return laplacian_kernel(X1,X2,self.gamma)
def __str__(self):
return 'MiKernellpcGammaVariable(gamma={})'.format(self.gamma)
miKlpcGV=MiKernelLPCGammaVariable(gamma=2)
LSMKlpcGV=LabelSpreading(kernel=miKlpcGV)
LSMKlpcGV.fit(XTr,YTr)
PMKlpcGV=LSMKlpcGV.predict(XTe)
print('LS(Klpc) : accuracy_score={:6.4f}'.format(accuracy_score(YTe,PKlpc)))
print('LS(MKlpcGF): accuracy_score={:6.4f}'.format(accuracy_score(YTe,PMKlpcGF)))
print('LS(MKlpcGV): accuracy_score={:6.4f}'.format(accuracy_score(YTe,PMKlpcGV)))
#END 1
#%% Ejercicio 2
"""
Se quiere buscar el mejor conjunto de hiperparámetros para LS entre los kernels:
rfb con gamma [0.1,1,10]
lpc con gamma [0.1,1,10]
Crear un modelo con ese mejor kernel y aplicarlo al test
PISTA:
Se puede utilizar un grid search sobre LS para determinar el kernel, pero
no se puede utilizar para buscar el hiperparámetro gamma de lpc porque no está
como parámetro del objeto LS.
La solución que se propone es crear 3 kernels rfb y 3 kernels lpc mediante
objetos kernel (opción D) y utilizarlos en un grid search donde solo varíe el
parámetro kernel.
PISTA: utiliza la función ssAccuracy (los test pueden tener ejemplos no etiquetados)
"""
def ssAccuracy(Y,P):
"""
Calcula accuracy_score sobre los ejemplos cuya Y no vale -1
"""
Yf=[]
Pf=[]
for r in range(len(Y)):
if Y[r]!=-1:
Yf.append(Y[r])
Pf.append(P[r])
return accuracy_score(Yf,Pf)
#TODO 2
KernelsGS=[MiKernelRBFGammaVariable(gamma=0.1),
MiKernelRBFGammaVariable(gamma=1),
MiKernelRBFGammaVariable(gamma=10),
MiKernelLPCGammaVariable(gamma=0.1),
MiKernelLPCGammaVariable(gamma=1),
MiKernelLPCGammaVariable(gamma=10)]
LS=LabelSpreading(max_iter=1000)
LSGS=GridSearchCV(LS,{'kernel':KernelsGS},
scoring=make_scorer(ssAccuracy,greater_is_better=True),cv=3)
#END 2
LSGS.fit(XTr,YTr)
P=LSGS.predict(XTe)
print('GridSearch(LS) : best kernel={}'.format(LSGS.best_estimator_.kernel))
print('GridSearch(LS): accuracy_score={:6.4f}'.format(accuracy_score(YTe,P)))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 3 : Aprendizaje SemiSupervisado (ASS)
Sesión 2 : Definición del problema y algoritmos de ASS
Actividad del Campus Virtual
"""
import warnings
import matplotlib.pyplot as plt
import numpy as np
from sklearn.semi_supervised import LabelPropagation,LabelSpreading
from sklearn.metrics import accuracy_score
import random
def genTest(puntosLado=25):
X=[]
Y=[]
def i2v(pl,i):
mpl=pl/2
return (i-mpl)/mpl
for f in range(puntosLado):
y=i2v(puntosLado,f)
for c in range(puntosLado):
x=i2v(puntosLado,c)
mod=(y**2+x**2)**0.5
if mod<0.5:
X.append([x,y])
Y.append(0)
elif mod>0.75:
X.append([x,y])
Y.append(1)
return [X,Y]
def pinta(X,titulo):
X=np.array(X)
plt.figure()
plt.title(titulo)
X0=X[:,0]
X1=X[:,1]
plt.plot(X0,X1,color='grey',linestyle='None',markersize=12,marker='o')
#%% Ejercicio 1
"""
Se tienen que clasificar los puntos en una zona donde hay un FOSO.
El foso está centrado en la posición (0,0) y empieza a una distancia 0.5 del
centro y termina a una distancia 0.75. Solo se saben dos puntos: el punto (0,0)
es de clase 0 y el punto (1,1) es de clase 1.
Pero sí se conocen los ejemplos no supervisados: XNs
Crear un sistema de aprendizaje no supervisasdo untilizando LabelSpreading de
manera que ajuste los hyperparámetros gamma y alpha (Se indican valores) y que
al ajustarlos tenga en cuenta TODOS los no supervisados en cada aprendizaje
que haga
Realizar un entrenamiento / test , calcular la accuracy e imprimirla
Se piden 3 situaciones:
1) No se utilizan no supervisados en ningún caso
2) Se utilizan PARTE de los no supervisados en el GridSearchCV
y TODOS al entrenar el modelo final
3) Se utilizan TODOS los no supervisados, tanto en el GridSearch como en el
modelo final
NOTA: Hiperparámetro recomendado a usar en LabelSpreading
max_iter=100000
Ejemplo de salida:
1) Accuracy SIN ejemplos no etiquetados: 0.2559
Hyperparámetros elegidos:{'alpha': 0.9, 'gamma': 1}
2) Accuracy CON PARTE de ejemplos no etiquetados en en cada fit del GS
y CON TODOS los ejemplos no etiquetados en train: 0.2559
Hyperparámetros elegidos:{'alpha': 0.9, 'gamma': 1}
3) Accuracy CON TODOS los ejemplos no etiquetados en cada fit: 1.0000
Hyperparámetros elegidos:{'alpha': 0.9, 'gamma': 100}
"""
# Test
[XTe,YTe]=genTest()
pinta(XTe,'FOSO')
# Ejemplos con categoría
XTr=[[0,0],[1,1],[0.1,0],[-0.92,1],[0,0.1],[1,-0.89],[-0.11,-0.1],[-0.91,-0.9]]
YTr=[ 0 , 1 , 0 , 1 , 0 , 1 , 0 , 1 ]
# No etiquetados
XNs=XTe
# Valores para ajustar
valoresGamma=[10**+i for i in range(0,+3+1)]
valoresAlpha=[1-(10**-i) for i in range(1,5)]
# Sistema de aprendizaje no supervisado
from sklearn.metrics import accuracy_score
from semisuperAA2.ssAccuracy import ssAccuracy
from sklearn.metrics import make_scorer
from sklearn.semi_supervised import LabelSpreading
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer
# NOTA: Hiperparámetro recomendado a usar en LabelSpreading
max_iter=100000
with warnings.catch_warnings():
warnings.simplefilter("ignore")
#%% SIN NO etiquetados
#TODO 1.1
LS=LabelSpreading(max_iter=max_iter)
LSGS=GridSearchCV(LS,{'gamma':valoresGamma,'alpha':valoresAlpha},
scoring=make_scorer(accuracy_score,greater_is_better=True),cv=2)
#END 1.1
# Train Test
#TODO 1.2
LSGS.fit(XTr,YTr)
P=LSGS.predict(XTe)
Acc=accuracy_score(YTe,P)
#END 1.2
print('1) Accuracy SIN ejemplos no etiquetados: {:6.4f}'.format(Acc))
print(' Hyperparámetros elegidos:{}\n'.format(LSGS.best_params_))
#%% CON PARTE de ejemplos no etiquetados en en cada fit del GridSearch y CON TODOS los ejemplos no etiquetados en train
#TODO 1.3
LS=LabelSpreading(max_iter=max_iter)
LSGS=GridSearchCV(LS,{'gamma':valoresGamma,'alpha':valoresAlpha},
scoring=make_scorer(ssAccuracy,greater_is_better=True),cv=2)
#END 1.3
# Train Test
#TODO 1.4
XTrNS=XTr+XNs
YTrNS=YTr+[-1]*len(XNs)
LSGS.fit(XTrNS,YTrNS)
P=LSGS.predict(XTe)
Acc=accuracy_score(YTe,P)
#END 1.4
print('2) Accuracy CON PARTE de ejemplos no etiquetados en en cada fit del GS\n y CON TODOS los ejemplos no etiquetados en train: {:6.4f}'.format(Acc))
print(' Hyperparámetros elegidos:{}\n'.format(LSGS.best_params_))
#%% CON TODOS los NO etiquetados tanto es la estimación de los hyperparámetros como en train
#TODO 1.5
from semisuperAA2.concatenaNE import concatenaNE_LS
LS=concatenaNE_LS(XNs,max_iter=max_iter)
LSGS=GridSearchCV(LS,{'gamma':valoresGamma,'alpha':valoresAlpha},
scoring=make_scorer(accuracy_score,greater_is_better=True),cv=2)
#END 1.5
# Train Test
#TODO 1.6
LSGS.fit(XTr,YTr)
P=LSGS.predict(XTe)
Acc=accuracy_score(YTe,P)
#END 1.6
print('3) Accuracy CON TODOS los ejemplos no etiquetados en cada fit: {:6.4f}'.format(Acc))
print(' Hyperparámetros elegidos:{}\n'.format(LSGS.best_params_))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 1 : Definición del problema y algoritmos de ANS
Bloque A : Sistemas de Clustering transductivos
"""
# Para conjuntos sin categoría puede ser útil hacer grupos (clusters)
# Utilizaremos los algoritmos de clústering de sklearn
# https://scikit-learn.org/stable/modules/clustering.html
# Los sistemas de clustering transductivos solo asignan clusters a los ejemplos
#de entrenamiento.
# Desde el punto de vista de la implementación no tienen método predict, si no,
#fit_predict, que realiza una aprendizaje y predicción del cluster de los ejemplos
#de entrenamiento
# En este bloque se muestra los clusters creados por el algoritmo
#AgglomerativeClustering
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.cluster import AgglomerativeClustering,DBSCAN
import matplotlib.pyplot as plt
import numpy as np
import random
nejemplos=50 # Número de ejemplos no etiqeutados por cada distrubución
RG=random.Random() # Generador de números aleatorios
RG.seed(1) # Semilla del generador de números aleatorios
for sd in [0.1, 0.5, 1]: # Para diferentes valores de desviación típica (sd)
simple=simpleGen(sd=sd)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
CSAC=AgglomerativeClustering(n_clusters=2) # Crear el objeto de Clustering
clusters = CSAC.fit_predict(X)
pintaSimpleClusters(clusters,X,'sd={} AgglomerativeClustering'.format(sd),'AgglomerativeClustering_sd_{}'.format(sd))
#%% Ejercicio 1
"""
Para cada conjunto de datos simple con sd en [0.1; 0.5; 1.0]
Crea 2 clústers con AgglomerativeClustering
Imprime para cada clúster cuántos ejemplos tiene.
NOTAS:
El número de clusters viene en el campo n_clusters del objeto (CSAC).
Estos se numeran de 0 a (n_clusters-1)
El objeto retornado por AgglomerativeClustering.fit_predict (clusters)
es una lista con el identificador del cluster que el algoritmo asigna a
cada ejemplo
El código: indices=np.where(clusters==label)[0] retorna los índices
que están asignados al clúster label.
PISTA: Utiliza el código del bucle anterior y cambia 'pintaClusters' por un
código que imprima el número de ejemplos de cada clúster
SALIDA
simple sd=0.1
Clúster 0 Ejemplos:50
Clúster 1 Ejemplos:50
simple sd=0.5
Clúster 0 Ejemplos:49
Clúster 1 Ejemplos:51
simple sd=1
Clúster 0 Ejemplos:59
Clúster 1 Ejemplos:41
"""
for sd in [0.1, 0.5, 1]: # Para diferentes valores de desviación típica (sd)
simple=simpleGen(sd=sd)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
#TODO Ejercicio 1
CSAC=AgglomerativeClustering(n_clusters=2)
clusters = CSAC.fit_predict(X)
print('simple sd={}'.format(sd))
for label in range(CSAC.n_clusters):
indices=np.where(clusters==label)[0]
print(' Clúster {} Ejemplos:{}'.format(label,len(indices)))
#END Ejercicio 1
#%% Ejercicio 2
"""
Basado en el ejercicio 1 realizar otro que utilice DBSCAN
https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html
Este algoritmo no tiene como hiperparámetro el número de clústers (n_clusters) sino
eps, que es la distancia máxima para indicar vecindad.
En vez de iterar por el valor de sd para simple, fijar sd=1 e iterar por
eps en [0.1,0.5, 1]
NOTA: Este algoritmo descarta algunos ejemplos de agregarlos a clústers, los
etiqueta con valor -1
PISTA: Este algoritmo no tiene el campo n_clusters. En su lugar se puede utilizar
el siguiente bucle para iterar por las etiquetas:
for label in np.unique(clusters):
SALIDA: ver figuras: DBSCAN_eps=*.pdf
DBSCAN(eps=0.5)
Clúster -1 Ejemplos:30
Clúster 0 Ejemplos:6
Clúster 1 Ejemplos:5
Clúster 2 Ejemplos:12
Clúster 3 Ejemplos:11
Clúster 4 Ejemplos:20
Clúster 5 Ejemplos:6
Clúster 6 Ejemplos:6
Clúster 7 Ejemplos:4
DBSCAN(eps=0.75)
Clúster -1 Ejemplos:7
Clúster 0 Ejemplos:84
Clúster 1 Ejemplos:9
DBSCAN(eps=1)
Clúster -1 Ejemplos:2
Clúster 0 Ejemplos:98
"""
simple=simpleGen(sd=1)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
for eps in [0.5, 0.75, 1]: # Para diferentes valores de desviación típica (sd)
#TODO Ejercicio 2
CSDBS=DBSCAN(eps=eps)
clusters = CSDBS.fit_predict(X)
print('DBSCAN(eps={})'.format(eps))
for label in np.unique(clusters):
indices=np.where(clusters==label)[0] # Índices del clúster label
print(' Clúster {} Ejemplos:{}'.format(label,len(indices)))
#END Ejercicio 2
pintaSimpleClusters(clusters,X,'DBSCAN(eps={})'.format(eps),'DBSCAN_eps={}'.format(eps))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Aprendizaje No Supervisado (ANS)
Sesión 1 : Definición del problema y algoritmos de ANS
Bloque B : Sistemas de Clustering inductivos
"""
# Los Sistemas de ANS inductivos permiten asignar clusters a ejemplos que
#no son de entrenamiento.
# Desde el punto de vista de la implementación tienen los métodos fit y predict.
# En este ejemplo se utilizará el algoritmo KMeans
# https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html
# Una vez entrenado, el objeto KMeans tiene el campo
# cluster_centers_ : lista con el centroide de cada cluster
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.cluster import KMeans,MeanShift
import numpy as np
import random
nejemplos=50 # Número de ejemplos no etiqeutados por cada distrubución
RG=random.Random() # Generador de números aleatorios
RG.seed(1) # Semilla del generador de números aleatorios
for sd in [0.1, 0.5, 1]: # Para diferentes valores de desviación típica (sd)
simple=simpleGen(sd=sd)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
CSKMeans=KMeans(n_clusters=2,random_state=1) # Crear el objeto de Clustering
CSKMeans.fit(X)
clusters=CSKMeans.predict(X)
pintaSimpleClusters(clusters,X,'KMeans(n_clusters={}) sd={}'.format(CSKMeans.n_clusters,sd),'KMeans_sd_{}_'.format(sd),CSKMeans.cluster_centers_)
#%% Ejercicio 1
"""
Para cada conjunto de datos simple con sd en [0.1; 0.5; 1.0]
Crea 2 clústers con KMeans
Imprime para cada clúster cuántos ejemplos tiene.
NOTAS:
CSKMeans.cluster_centers_ almacena los centroides de los clústers creados.
El número de centroides es el número de clusters
clusters es una lista con el identificador del cluster que el algoritmo
asigna a cada ejemplo
El código: indices=np.where(clusters==label)[0] retorna los índices
que están asignados al clúster label.
SALIDA
simple sd=0.1
Clúster 0 Ejemplos:50
Clúster 1 Ejemplos:50
simple sd=0.5
Clúster 0 Ejemplos:49
Clúster 1 Ejemplos:51
simple sd=1
Clúster 0 Ejemplos:55
Clúster 1 Ejemplos:45
"""
#<TODO BEGIN Ejercicio 1>
for sd in [0.1, 0.5, 1]:
simple=simpleGen(sd=sd)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
#TODO 1
CSKMeans=KMeans(n_clusters=2,random_state=1) #Crea un modelo de K-Means configurado para:
CSKMeans.fit(X) #Entrena el modelo K-Means sobre los datos generados X.
clusters=CSKMeans.predict(X) #Clasifica cada punto en uno de los 2 clústeres según los centros aprendidos.
print('simple sd={}'.format(sd))
for label in range(len(CSKMeans.cluster_centers_)): #Itera sobre los índices de los clústeres. Como n_clusters=2, esto equivale a: for label in [0,1]:
indices=np.where(clusters==label)[0] #Obtiene los índices de los ejemplos asignados a ese clúster.
print(' Clúster {} Ejemplos:{}'.format(label,len(indices)))
#END 1
#%% Ejercicio 2
"""
Basado en el ejercicio 1 realizar otro que utilice MeanShift
https://scikit-learn.org/stable/modules/generated/sklearn.cluster.MeanShift.html
Este algoritmo no tiene como hiperparámetro el número de clústers (n_clusters) sino
el ancho de banda (bandwidth). El número de clusters se calcula automáticamente
para los datos dados y el bandwidth
En vez de iterar por el valor de sd para simple, fijar sd=1 e iterar
bandwidth en [1,1.25,1.5,1.75,2,None] (Cuando se utiliza None se calcula
automáticament un valor)
SALIDA: Ficheros: simple_MeanShift_bandwidth=*.pdf
MeanShift bandwidth=1
Clúster 0 Ejemplos:22
Clúster 1 Ejemplos:15
Clúster 2 Ejemplos:16
Clúster 3 Ejemplos:11
Clúster 4 Ejemplos:8
Clúster 5 Ejemplos:10
Clúster 6 Ejemplos:9
Clúster 7 Ejemplos:7
Clúster 8 Ejemplos:1
Clúster 9 Ejemplos:1
MeanShift bandwidth=1.25
Clúster 0 Ejemplos:54
Clúster 1 Ejemplos:20
Clúster 2 Ejemplos:26
MeanShift bandwidth=1.5
Clúster 0 Ejemplos:56
Clúster 1 Ejemplos:44
MeanShift bandwidth=1.75
Clúster 0 Ejemplos:53
Clúster 1 Ejemplos:47
MeanShift bandwidth=2
Clúster 0 Ejemplos:100
"""
simple=simpleGen(sd=1)
Xs=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
for bandwidth in [1,1.25,1.5,1.75,2,None]: # Para diferentes valores de bandwidth
#TODO Ejercicio 2
CSMeanShift=MeanShift(bandwidth=bandwidth) #Crea un objeto del algoritmo de clustering MeanShift usando el bandwidth actual.
CSMeanShift.fit(Xs) #Entrena el modelo MeanShift sobre los datos X.
clusters=CSMeanShift.predict(X) #Asigna cada punto a uno de los clústeres obtenidos durante el entrenamiento.
print('MeanShift bandwidth={}'.format(bandwidth))
for label in range(len(CSMeanShift.cluster_centers_)): #Recorre cada clúster encontrado por MeanShift. Su longitud indica cuántos clústeres hay.
indices=np.where(clusters==label)[0]
print(' Clúster {} Ejemplos:{}'.format(label,len(indices)))
#END Ejercicio 2
pintaSimpleClusters(clusters,X,'MeanShift(bandwidth={})'.format(bandwidth),'MeanShift_bandwidth={}'.format(bandwidth),CSMeanShift.cluster_centers_)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 1 : Definición del problema y algoritmos de ANS
Bloque C : Definir distancia
"""
# Un parámetro crítico para realizar clústers es la distancia, entendida como
#función que dados 2 ejemplos retorna un valor numérico que pueda ser entendida
#como distancia
# En este ejemplo se utilizará el algoritmo AgglomerativeClustering
# https://scikit-learn.org/stable/modules/generated/sklearn.cluster.AgglomerativeClustering.html
# Tiene el parámetro metric que permite definir la distancia
from simpleGen import concentricoGen,pintaSimpleClusters
from sklearn.cluster import AgglomerativeClustering
import numpy as np
import random
# Crear conjunto concéntrico
nejemplos=200 # Número de ejemplos no etiquetados por cada distrubución
RG=random.Random() # Generador de números aleatorios
RG.seed(1)
conGen=concentricoGen()
X=conGen(RG,nejemplos,nejemplos)
pintaSimpleClusters(None,X,'datos concéntricos','datosConcen.pdf')
# Clústers con distancia euclídea
cluGen=AgglomerativeClustering(n_clusters=2,linkage='average',metric='euclidean')
C=cluGen.fit_predict(X)
pintaSimpleClusters(C,X,'datos concéntricos. AgglomerativeClustering(euclidean) ','datosConcenAgloEucli.pdf')
# Clústers con distancia determinada solo por el radio
class distancia:
"""
Clase padre de distancias. Calcula la matriz de distancias (simétrica con diagonal 0).
Clase abstracta. Las clases hijas necesitan definir
_distanciaPar(self,E1,E2)
Que dados dos ejemplos retorna su distancia
"""
def __call__(self,X):
"""
Retorna la matriz de distancias de la lista de ejemplos
Parámetros:
-X : lista de ejemplos (solo atributos, sin categoría)
"""
M=np.zeros([len(X),len(X)])
for i in range(len(X)-1):
for j in range(i+1,len(X)):
M[i,j]=self._distanciaPar(X[i],X[j])
M[j,i]=M[i,j]
return M
def _distanciaPar(self,E1,E2):
"""
Método a ser redefinido en la clase hija.
Dados dos ejemplos retorna su distancia.
"""
pass
class distanciaRadio(distancia):
"""
Clase que define la distancia radio.
"""
def _distanciaPar(self,E1,E2):
"""
Distancia Radio entre dos ejemplos.
Se calcula la distancia del punto 0 a E1 y a E2, serán los radios r1 y r2
La distancia entre los radios será la distancia entre los ejemplos.
"""
r1=0
r2=0
for i in range(len(E1)):
r1=r1+E1[i]**2
r2=r2+E2[i]**2
r1=r1**0.5
r2=r2**0.5
return abs(r1-r2)
cluGen=AgglomerativeClustering(n_clusters=2,linkage='average',metric=distanciaRadio())
C=cluGen.fit_predict(X)
pintaSimpleClusters(C,X,'datos concéntricos. AgglomerativeClustering(disRadio) ','datosConcenAglodisRadio.pdf')
#%% Ejercicio 1
"""
Crea una distancia entre lo parecidos que son los valores de los ejemplos.
Paso 1: Piensa una medida de similitud (MS) entre los valores(atributos) de un ejemplo.
Si un ejemplo tiene todos los valores iguales valdrá 0, si no, aumentará.
Paso 2: La distancia entre dos ejemplos será la distancia entre sus MS.
Esa distancia puede ser en valor absoluto, cuadrática, ...
NOTA: Se puede calcular la igualdad de un ejemplo E como la desviación típica
de los valores del ejemplo:
np.std(E)
SALIDA:
ver plots/datosConcenAglodistanciaIgualdad.pdf
"""
class distanciaIgualdad(distancia):
def _distanciaPar(self,E1,E2):
#TODO 1.1
# Igualdad de cada uno de los ejemplos
I1=np.std(E1) # Calculamos la igualdad del ejemplo 1
I2=np.std(E2) # Calculamos la igualdad del ejemplo 2
#END 1.1
#TODO 1.2
# Retornar la distancia entre esas igualdades
return abs(I1-I2) # La distancia entre las igualdades nos da la
# distancia de este par de ejemplos
#END 1.2
cluGen=AgglomerativeClustering(n_clusters=2,linkage='average',metric=distanciaIgualdad())
C=cluGen.fit_predict(X)
pintaSimpleClusters(C,X,'datos concéntricos. AgglomerativeClustering(distanciaIgualdad) ','datosConcenAglodistanciaIgualdad.pdf')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 1 : Definición del problema y algoritmos de ANS
Bloque Pre : Problemas no supervisados
"""
# Hay conjuntos de datos que no tienen una categoría, solo atributos y sus valores.
# En este bloque se muestra un conjunto de ejemplos llamado 'simple' que tiene
#dos atributos y ejemplos que siguen dos distribuciones normales en media:
# (+1,+1) y (-1,-1).
# Se gereran para desviaciones dípicas (sd) 0.1, 0.5 y 1
from simpleGen import simpleGen
import matplotlib.pyplot as plt
import numpy as np
import random
from sklearn.metrics.pairwise import euclidean_distances
def cluAleaSimple(RG,X):
CluTra=[]
for x in X: # Para cada ejemplo
# Calcular un ejemplo de la distribución negativa y otro de la positiva
x1NegAlea=RG.normalvariate(-1,1)
x2NegAlea=RG.normalvariate(-1,1)
x1PosAlea=RG.normalvariate(+1,1)
x2PosAlea=RG.normalvariate(+1,1)
# Secalculan las distancias a cada ejemplo generado
dNeg=euclidean_distances([x,[x1NegAlea,x2NegAlea]])[0][1]
dPos=euclidean_distances([x,[x1PosAlea,x2PosAlea]])[0][1]
# El cluster lo determinará el más cercano
CluTra.append(-1 if dNeg<dPos else +1)
return np.array(CluTra)
nejemplos=50 # Número de ejemplos no etiqeutados por cada distrubución
RG=random.Random() # Generador de números aleatorios
for sd in [0.1, 0.5, 1]: # Para diferentes valores de desviación típica (sd)
simple=simpleGen(sd=sd) # Generador de ejemplos no etiquetados
RG.seed(1) # Semilla del generador de números aleatorios
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
# Pintar los ejemplos
plt.figure()
plt.xlim([-3,+3])
plt.ylim([-3,+3])
plt.plot(X[:,0],X[:,1],linestyle='None',marker='o',color='tab:gray')
plt.xlabel('Atributo 0')
plt.ylabel('Atributo 1')
plt.title('simple {0}+{0} ejemplos. sd={1}'.format(nejemplos,simple.sd))
plt.savefig('plots/simple_sd_{}.pdf'.format(simple.sd))
# Hay dos tipos de clústers:
# Transductivo: define una partición del conjunto de ejemplos,
# no se puede aplicar a ejemplos no vistos
# Inductivo : existe un modelo que permite crear una partición sobre el conjunto
# de entrenamiento y sobre ejempos no vistos
# Usando el ejemplo de SD=1
simple=simpleGen(sd=1)
RG.seed(1) # Semilla del generador de números aleatorios
X=simple(RG,nejemplos)
# Se crea un Transductivo aleatorio, utilizando las mismas distribuciones con
#que fueron creados los ejemplos
CluTra=cluAleaSimple(RG,X)
# Pintar el cluster
plt.figure()
plt.xlim([-3,+3])
plt.ylim([-3,+3])
indNeg=np.where(CluTra==-1)
plt.plot(X[indNeg,0],X[indNeg,1],linestyle='None',marker='o',color='red')
indPos=np.where(CluTra==+1)
plt.plot(X[indPos,0],X[indPos,1],linestyle='None',marker='o',color='blue')
plt.xlabel('Atributo 0')
plt.ylabel('Atributo 1')
plt.title('Cluster Transductivo aleatorio'.format(nejemplos,simple.sd))
plt.savefig('plots/ClusTrans.pdf'.format(simple.sd))
# Se crea un cluster inductivo con el modelo atributo0>atributo1
CluInd=np.array([+1 if x[0]>x[1] else -1 for x in X])
# Pintar el cluster
plt.figure()
plt.xlim([-3,+3])
plt.ylim([-3,+3])
indNeg=np.where(CluInd==-1)
plt.plot(X[indNeg,0],X[indNeg,1],linestyle='None',marker='o',color='red')
indPos=np.where(CluInd==+1)
plt.plot(X[indPos,0],X[indPos,1],linestyle='None',marker='o',color='blue')
plt.xlabel('Atributo 0')
plt.ylabel('Atributo 1')
plt.title('Cluster Inductivo atr0>atr1'.format(nejemplos,simple.sd))
plt.savefig('plots/ClusInduc.pdf'.format(simple.sd))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 1 : Definición del problema y algoritmos de ANS
Actividad 1
"""
from simpleGen import pintaSimpleClusters
from NoSuperDatasets.leeIris import leeIris
import numpy as np
import random
def grid(xmin=1,xmax=7,ymin=0,ymax=3,step=0.1):
"""
Crea un conjunto de datos sin etiquetar de dos atributos.
Los valores están en los intervalos [xmin,xmax] para el primer atributo e
[ymin,ymax] para el segundo.
Los valores se obtienen consecutivamente cada step (paso)
"""
# Valores verticales
Y=[]
y=ymin
while y<=ymax:
Y.append(y)
y=y+step
# Valores horizontales
X=[]
x=xmin
while x<=xmax:
X.append(x)
x=x+step
M=[]
for y in Y:
for x in X:
M.append([x,y])
return np.array(M)
XIris=leeIris()
XGrid=grid(xmin=1,xmax=7,ymin=0,ymax=3,step=0.1)
#%% Ejercicio 1
"""
DATOS:
XIris es un conjunto no etiquetado del dataset iris donde solo hay 2 atributos (los dos más relevantes)
XGrid es un conjunto no etiquetado en el mismo dominio que XIris pero con
ejemplos secuenciales cada 'step' que en este caso es 0.1 (una rejilla de ejemplos)
ENUNCIADO:
Se quieren hacer tres clusters sobre XIris y luego evaluar con esos clusters
los ejemplos de XGrid.
¿Cuántos ejemplos de cada clase hay en XIris? ¿Y en XGrid?
PISTA:
Fíjate en el enunciado para elegir el sistema de cluster adecuado
SALIDA: ver figuras iris_KMeans.pdf y iris_KMeans_Grid.pdf
Iris:
Clúster 0 ejemplos:50
Clúster 1 ejemplos:48
Clúster 2 ejemplos:52
XGrid:
Clúster 0 ejemplos:497
Clúster 1 ejemplos:597
Clúster 2 ejemplos:736
"""
print('\nEjercicio 1')
#TODO 1.1
# Elegir el sistema de clustering : CS
# Se elige KMeans porque se puede indicar el número de clústers y es inductivo
#(se puede aplicar el modelo a otros ejemplos)
from sklearn.cluster import KMeans
CS=KMeans(n_clusters=3,random_state=1)
#END 1
# Obtener clústers con XIris y aplicarlos tanbién a XGrid
#TODO 1.2
CS.fit(XIris) # Creo el modelo de clúster con el Iris
clustersIris=CS.predict(XIris)# Clústers de XIris
clustersGrid=CS.predict(XGrid)# Clústers de XGrid
#END 1.2
# Contar ejemplos en cada clúster
print('Iris:')
#TODO 1.3
for label in range(CS.n_clusters):
indices=np.where(clustersIris==label)[0]
print(' Clúster {} ejemplos:{}'.format(label,len(indices)))
#END 1.3
print('XGrid:')
#TODO 1.4
for label in range(CS.n_clusters):
indices=np.where(clustersGrid==label)[0]
print(' Clúster {} ejemplos:{}'.format(label,len(indices)))
#END 1.4
# Pinta clústers
pintaSimpleClusters(clustersIris,XIris,'XIris (n_clusters={})'.format(CS.n_clusters),'XIris',CS.cluster_centers_,xlim=[1,7],ylim=[0,3])
pintaSimpleClusters(clustersGrid,XGrid,'XGrid (n_clusters={})'.format(CS.n_clusters),'XGrid',CS.cluster_centers_,xlim=[1,7],ylim=[0,3])
#%% Ejercicio 2
"""
Se quieren agrupar los lirios por normales y especiales.
Los normales son los que están cerca del punto medio.
Los especiales son los que están alejados del punto medio
Utiliza un sistema de clustering que genere dos clústeres: normales y especiales
Para ello crea la distanciaAlMedio
NOTAS:
El punto medio de los lirios se puede conseguir como
IrisMean=np.mean(XIris,0)
Utiliza el constructor (__init__) para pasar el punto medio de iris a la distancia
Luego utiliza ese punto medio en el método _distanciaPar
PISTA:
Utiliza una distancia que mida la diferencia entre las distancias al
punto medio
SALIDA:
ver Iris_Aglomerative_DisMean.pdf
"""
IrisMean=np.mean(XIris,0)
from sklearn.cluster import AgglomerativeClustering
class distancia:
"""
Clase padre de distancias. Calcula la matriz de distancias (simétrica con diagonal 0).
Clase abstracta. Las clases hijas necsitan definir
_distanciaPar(self,E1,E2)
Que dados dos ejemplos retorna su distancia
"""
def __call__(self,X):
"""
Retorna la matriz de distancias de la lista de ejemplos
Parámetros:
-X : lista de ejemplos (solo atributos, sin categoría)
"""
M=np.zeros([len(X),len(X)])
for i in range(len(X)-1):
for j in range(i+1,len(X)):
M[i,j]=self._distanciaPar(X[i],X[j])
M[j,i]=M[i,j]
return M
def _distanciaPar(self,E1,E2):
"""
Método a ser redefinido en la clase hija.
Dados dos ejemplos retorna su distancia.
"""
pass
class distanciaAlMedio(distancia):
# Crea un constructor que almacene el Punto Medio (IrisMean)
#TODO 2.1
def __init__(self,IrisMean):
self.IrisMean=IrisMean
#END 2.1
def _distanciaPar(self,E1,E2):
# PISTA: Utilizar un código similar al de distanciaRadio pero que la
# Distancia en vez de cada ejemplo al (0,0) sea al IrisMean
#TODO 2.2
r1=0
r2=0
for i in range(len(E1)):
r1=r1+(E1[i]-self.IrisMean[i])**2
r2=r2+(E2[i]-self.IrisMean[i])**2
r1=r1**0.5
r2=r2**0.5
return abs(r1-r2)
#END 2.2
cluGen=AgglomerativeClustering(n_clusters=2,linkage='average',
# Asigna a metric un objeto distanciaAlMedio que
# tenga el punto medio de XIris
#TODO 2.3
metric=distanciaAlMedio(IrisMean)
#END 2.3
)
C=cluGen.fit_predict(XIris)
pintaSimpleClusters(C,XIris,'Iris AgglomerativeClustering(disMedio) ','Iris_Aglomerative_DisMean.pdf',xlim=[1,7],ylim=[0,3])
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
"""
import matplotlib.pyplot as plt
import numpy as np
import random
import math
class simpleGen:
"""
Crea un conjunto no etiquetado donde los ejemplos se obtienen de una distribución
normal centrada en (-1,-1) y (+1,+1).
"""
def __init__(self,sd=0.1):
self.sd=sd # Desviación estándar de la distribución normal
def __call__(self,RG,nejemplos):
"""
Parámetros:
- RG : random.Random().
- nejemplos : entero, número de ejemplos generados para cada una de las
distribuciones (-1,-1) y (+1,+1)
Retorna X : matriz de tipo numpy.ndarray con 2 atributos y
(nejemplos+nejemplos) ejemplos
"""
X=[]
for e in range(nejemplos):
X.append(self._genExample(RG,-1,-1))
X.append(self._genExample(RG,+1,+1))
return np.array(X)
def _genExample(self,RG,mu1,mu2):
return [RG.normalvariate(mu1,self.sd),RG.normalvariate(mu2,self.sd)]
class concentricoGen:
"""
Crea un conjunto no etiquetado donde los ejemplos se obtienen de una distribución
uniforme sobre un círculo de radio r1 y un anillo de radios r21 y r22
"""
def __init__(self,r1=0.5,r21=1.5,r22=2):
self.r1=r1
self.r21=r21
self.r22=r22
def __call__(self,RG,eje1=100,eje2=100):
X=[]
# Circunferencia interior
for i in range(eje1):
radio =RG.random()*self.r1
angulo=RG.random()*math.pi*2
x=math.cos(angulo)*radio
y=math.sin(angulo)*radio
X.append([x,y])
# Anillo exterior
for i in range(eje2):
radio =RG.random()*(self.r22-self.r21)+self.r21
angulo=RG.random()*math.pi*2
x=math.cos(angulo)*radio
y=math.sin(angulo)*radio
X.append([x,y])
return X
def pintaSimpleClusters(clusters,X,titulo,fileName,centroides=[],xlim=[-3,+3],ylim=[-3,+3]):
"""
Función que pinta clusters hechos sobre conjuntos de datos 'simple'
Pinta sobre los 2 primeros atributos en el intervalo [-3,+3]
Parámetros:
- clusters : lista con el número de cluster de cada ejemplo
(-1 no pertenece a ningún clúster)
Si None todos los valores serán -1
- X : Valores de los ejemplos (generados por simpleGen)
- titulo : String. Título de la figura
- fileName : String. nombre del fichero sin extensión (la extensión será .pdf)
Si vale None no se guarad en fichero
Se guardará en la carpeta plots/
- centroides: Lista de centroides. Si es vacía no se pinta nada.
"""
colores=['tab:orange','tab:blue']
centroCols=['tab:green','tab:red']
plt.figure()
if type(clusters)==type(None):
clusters=[-1]*len(X)
labels=np.unique(clusters)
RG=None
X=np.array(X)
for ilabel in range(len(labels)):
label=labels[ilabel]
if label==-1:
color='tab:gray' # No asignados a ningún cluster
ccolor=color
elif ilabel<len(colores):
color=colores[ilabel]
ccolor=centroCols[ilabel]
else:
if RG==None:
RG=random.Random()
RG.seed(1)
color=(RG.random(),RG.random(),RG.random())
ccolor=color
indices=np.where(clusters==label)[0]
plt.xlim(xlim)
plt.ylim(ylim)
plt.plot(X[indices,0],X[indices,1],linestyle='None',marker='o',color=color,label='Ejemplos {}'.format(label))
if len(centroides)>0:
centroide=centroides[ilabel]
plt.plot([centroide[0]],[centroide[1]],linestyle='None',marker='x',markersize=18,color=ccolor,label='Centroide {}'.format(label))
plt.title(titulo)
plt.xlabel('Atributo 0')
plt.ylabel('Atributo 1')
plt.legend()
if fileName!=None:
plt.savefig('plots/{}.pdf'.format(fileName))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 2 : Calidad
Bloque A : Medidas de calidad
"""
# Aunque no se tenga categoría se pueden calcular medidas de calida de partición
#en clústers. En esta práctica se utilizarán las de sklearn:
# https://scikit-learn.org/stable/modules/model_evaluation.html
# Algunas de estas medidas necesitan saber el valor real (ground truth) como:
# rand_score
# adjusted_mutual_info_score
# homogeneity_score
# completeness_score
# v_measure_score
# fowlkes_mallows_score
# Otras no necesitan conocer el valor real de la partición
# silhouette_score (better as higher)
# calinski_harabasz_score (better as higher)
# davies_bouldin_score (better as lower)
# En esta práctica utilizaremos estas últimas
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.cluster import MeanShift
from sklearn.metrics import silhouette_score ,calinski_harabasz_score,davies_bouldin_score
import numpy as np
import random
import matplotlib.pyplot as plt
# Crear el conjunto de ejemplos
nejemplos=50 # Número de ejemplos no etiqeutados por cada distrubución
RG=random.Random() # Generador de números aleatorios
RG.seed(1) # Semilla del generador de números aleatorios
simple=simpleGen(sd=1)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetadossd=1
# bandwidth de 1 a 2 con paso 0.1
bandwidths=[bd/10 for bd in range(10,20+1)]
# Almacenar los valores de calidad para cada métrica
si=[] # silhouette_score
ca=[] # calinski_harabasz_score
da=[] # davies_bouldin_score
for bandwidth in bandwidths: # Para diferentes valores de bandwidth
# Proceso de aprendizaje no supervisado
CSMeanShift=MeanShift(bandwidth=bandwidth) # Crear el objeto de Clustering
CSMeanShift.fit(X)
clusters = CSMeanShift.predict(X)
# Cálculo de valores para las funciones de pédida
n_clusters=len(np.unique(clusters))
if n_clusters>1:
si.append(silhouette_score(X,clusters))
ca.append(calinski_harabasz_score(X,clusters))
da.append(davies_bouldin_score(X,clusters))
else: # si solo hay un clúster no se aplican
si.append(None)
ca.append(None)
da.append(None)
metricValues=[si,ca,da]
metricNames=['silhouette','calinski_harabasz','davies_bouldin']
for iMetric in range(len(metricValues)):
plt.figure()
plt.title('MeanShift')
plt.plot(bandwidths,metricValues[iMetric],marker='x')
plt.ylabel(metricNames[iMetric])
plt.xlabel('bandwidth')
plt.savefig('plots/A_MeanShift_{}'.format(metricNames[iMetric]))
#%% Ejercicio 1
"""
DATOS:
Las listas si, ca y da tienen los valores de las métricas:
silhouette_score, calinski_harabasz_score y davies_bouldin_score
respectivamente
La lista bandwidths tiene los valores de bandwidth
ENUNCIADO:
Haz un código que calcule el mejor valor de bandwidth según cada métrica
Imprimir el valor para cada métrica
NOTA:
Usa np.argmax o np.argmin para obtener el índice del mayor o menor valor en
un array
SALIDA:
silhouette mejor bandwidth=1.7
calinski_harabasz mejor bandwidth=1.7
davies_bouldin mejor bandwidth=1.6
"""
# Calcula los índices del mejor valor de bandwith para cada medida
#TODO 1
ibest_si=np.argmax(si) # silhouette (si)
ibest_ca=np.argmax(ca) # calinski_harabasz (ca)
ibest_da=np.argmin(da) # davies_bouldin (da)
#END 1
print('silhouette mejor bandwidth={}'.format(bandwidths[ibest_si]))
print('calinski_harabasz mejor bandwidth={}'.format(bandwidths[ibest_ca]))
print('davies_bouldin mejor bandwidth={}'.format(bandwidths[ibest_da]))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 2 : Calidad
Bloque B : Influencia de atributos
"""
# En general, los algoritmos de ANS son muy sensibles al número de atributos.
# Pero no es lo mismo si son atributos relevantes o irrelevantes.
# En este bloque se estudia el caso en que se fija el número de clústers y se
#varían tanto los atributos relevantes como irrelevantes.
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.cluster import KMeans,AgglomerativeClustering
from sklearn.metrics import silhouette_score ,calinski_harabasz_score,davies_bouldin_score
import numpy as np
import random
import matplotlib.pyplot as plt
#%% Ejercicio 1
"""
Añade el código necesario para en cada iteración calcular silhouette_score
tanto del algoritmo KMeans como del AgglomerativeClustering
SALIDA:
atr rel= 2 no rel= 0 silhouette_score KMeans=0.4792 AgglomerativeClustering=0.4524
atr rel= 50 no rel= 0 silhouette_score KMeans=0.4297 AgglomerativeClustering=0.4297
atr rel=100 no rel= 0 silhouette_score KMeans=0.4190 AgglomerativeClustering=0.4190
atr rel= 2 no rel= 50 silhouette_score KMeans=0.0307 AgglomerativeClustering=0.0262
atr rel= 2 no rel=100 silhouette_score KMeans=0.0124 AgglomerativeClustering=0.0144
"""
# Parámetros del conjunto de ejemplos
nejemplos=50 # Número de ejemplos no etiquetados por cada distrubución
RG=random.Random() # Generador de números aleatorios
RG.seed(1) # Semilla del generador de números aleatorios
sd=1
# Sistemas ANS
CSKMeans=KMeans(n_clusters=2,random_state=1)
CSAggclu =AgglomerativeClustering(n_clusters=2)
# Variando el número de atributos relevantes
for atrRel in [2,50,100]:
# Crear el conjunto de ejemplos
simple=simpleGen(sd=sd,atrRel=atrRel,atrNoRel=0)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados sd=1
# Proceso de aprendizaje con KMeans
CSKMeans.fit(X)
clustersKM=CSKMeans.predict(X)
pintaSimpleClusters(clustersKM,X,'simple(atrRel={}) KMeans'.format(atrRel),'B_KMeans_Rel={}'.format(atrRel))
# Proceso de aprendizaje con AgglomerativeClustering
clustersAC=CSAggclu.fit_predict(X)
pintaSimpleClusters(clustersAC,X,'simple(atrRel={}) AgglomerativeClustering'.format(atrRel),'B_AggClu_Rel={}'.format(atrRel))
#TODO 1.1
siKM=silhouette_score(X,clustersKM) # silhouette para los clusters de KMeans
siAC=silhouette_score(X,clustersAC) # silhouette para los clusters de AgglomerativeClustering
#END 1.1
print('atr rel={:3} no rel= 0 silhouette_score KMeans={:6.4f} AgglomerativeClustering={:6.4f}'.format(atrRel,siKM,siAC))
# Variando el número de atributos NO relevantes
for atrNoRel in [50,100]:
# Crear el conjunto de ejemplos
simple=simpleGen(sd=1,atrRel=2,atrNoRel=atrNoRel)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetadossd=1
# Proceso de aprendizaje con KMeans
CSKMeans.fit(X)
clustersKM=CSKMeans.predict(X)
pintaSimpleClusters(clustersKM,X,'simple(atrNoRel={}) KMeans'.format(atrNoRel),'B_KMeans_noRel={}'.format(atrNoRel))
# Proceso de aprendizaje con AgglomerativeClustering
clustersAC=CSAggclu.fit_predict(X)
pintaSimpleClusters(clustersAC,X,'simple(atrNoRel={}) AgglomerativeClustering'.format(atrNoRel),'B_AggClu_noRel={}'.format(atrNoRel))
#TODO 1.2
siKM=silhouette_score(X,clustersKM) # silhouette para los clusters de KMeans
siAC=silhouette_score(X,clustersAC) # silhouette para los clusters de AgglomerativeClustering
#END 1.2
print('atr rel= 2 no rel={:3} silhouette_score KMeans={:6.4f} AgglomerativeClustering={:6.4f}'.format(atrNoRel,siKM,siAC))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 2 : Calidad
Bloque C : Influencia de atributos 2 (Outliers)
"""
# Los sistemas que no tienen un parámetro con el número de clústers requieren
#ser ajustados según el número de atributos.
# Observa el ejemplo de MeanShift variando el parámetro bandwidth para un
#conjunto tipo simple con 50 atributos relevantes.
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.cluster import MeanShift, DBSCAN
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
from sklearn.metrics import silhouette_score
import numpy as np
import random
import matplotlib.pyplot as plt
# Parámetros del conjunto de ejemplos
nejemplos=50 # Número de ejemplos no etiquetados por cada distrubución
RG=random.Random() # Generador de números aleatorios
sd=1 # Desviación típica
atrRel=50 # Atributos relevantes
# Crear el conjunto de ejemplos
RG.seed(1) # Semilla del generador de números aleatorios
simple=simpleGen(sd=sd,atrRel=atrRel)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetadossd=1
# Variando bandwidth
print('simple(attRel={}) MeanShift:'.format(atrRel))
for bandwidth in [1,2,4,6,8,10,12,14]:
# Sistemas ANS
CSMeanSh=MeanShift(bandwidth=bandwidth)
# Proceso de aprendizaje con MeanShift
CSMeanSh.fit(X)
clustersMS=CSMeanSh.predict(X)
# Dibujar los clusters
pintaSimpleClusters(clustersMS,X,'simple(atrRel={}) MeanShift(bandwidth={})'
.format(atrRel,bandwidth),'C_MeanShift(bandwidth={})'.format(bandwidth))
# Imprimir información y cálculo de calinski_harabasz_score
n_clusters=len(np.unique(clustersMS))
if n_clusters>1 and n_clusters<len(X):
ca=calinski_harabasz_score(X,clustersMS)
else:
ca='None'
print(' bandwidith={:2} clusters={:3} calinski_harabasz={}'
.format(bandwidth,n_clusters,ca))
# Cuando hay ejemplos que no están asignados a ningún clúster (outliers) no hay
#que tenerlos en cuenta a la hora de calcular la medida.
# La función clusterMetric(X,clusters,metric) realiza este proceso
def clusterMetric(X,clusters,metric,worstValue=-np.inf):
"""
Calcula la métrica metric tomando como clústers de un solo ejemplo los ejemplos
que no estén en ningún clúster
Parámetros:
- X : conjunto de datos
- clusters : lista con el número de clúster de cada ejemplo (puede ser -1)
- metric : métrica de clústers que no necesita el valor verdadero
- worstValue: valor que retorna si solo hay un cluster o hay un cluster
por cada ejemplo
Retorna: [metric_value,n_clusters,n_samples]
- metric_value : valor de la métrica o None si el número de clústers
es 1 o el número de ejemplos
- n_clusters : número de clusters (sin contar el -1)
- n_samples : número de ejemplos que están en algún clúster (sin contar el -1)
"""
clusterSet=np.unique(clusters)
# Si solo hay un clúster o hay tantos como ejemplos
if len(clusterSet)==1 or len(clusterSet)==len(X):
value=worstValue # Se retorna el peor valor
elif min(clusterSet)>=0: # No hay -1
value= metric(X,clusters) # Lo que retorne la métrica
else:
# Cambiar todos los -1 por un clúster nuevo
cluNuevo=max(clusterSet)+1
clustersNuevo=[]
for c in clusters:
if c==-1:
clustersNuevo.append(cluNuevo)
cluNuevo=cluNuevo+1
else:
clustersNuevo.append(c)
value=metric(X,clustersNuevo)
# Cálculo del Nº de clústers sin -1
n_clusters=len(clusterSet)
if min(clusterSet)==-1:
n_clusters=n_clusters-1
# Cálculo del nº de ejemplos que están en algún clúster
indNoNeg=np.where(np.array(clusters)>=0)[0]
n_samples=len(indNoNeg)
return [value,n_clusters,n_samples]
# Ejercicio 1
# Para el conjunto de ejemplos de este bloque realizar clusters con el algoritmo
#DBSCAN variando eps en [2,4,6,8,10,12,14]
# Imprimir el número de clústers y el valor de la métrica calinski_harabasz para
#los ejemplos que están en algún clúster.
# Opcional: realiza el gráfico de los clústers generados
# NOTA: Utiliza clusterMetric
# SALIDA: ver figuras C_DBSCAN(eps=*).pdf
# simple(attRel=50) DBSCAN:
# eps= 2 clusters= 0 calinski_harabasz=None
# eps= 4 clusters= 0 calinski_harabasz=None
# eps= 6 clusters= 0 calinski_harabasz=None
# eps= 8 clusters= 2 calinski_harabasz=43.172984366909176
# eps=10 clusters= 2 calinski_harabasz=104.39818911983511
# eps=12 clusters= 2 calinski_harabasz=104.39818911983511
# eps=14 clusters= 1 calinski_harabasz=None
#<TODO BEGIN Ejercicio 1>
# Variando eps
print('simple(attRel={}) DBSCAN:'.format(atrRel))
for eps in [2,4,6,8,10,12,14]:
# Sistemas ANS
CSDBSCAN=DBSCAN(eps=eps)
# Proceso de clustering
clustersDS=CSDBSCAN.fit_predict(X)
# Calcular calinski_harabasz_score cuando hay clústers que pueden valer -1
[ca,n_clusters,n_samples]=clusterMetric(X,clustersDS,calinski_harabasz_score)
# Imprimir información y cálculo de calinski_harabasz_score
print(' eps={:2} en {} clusters hay {} ejemplos, calinski_harabasz={}'
.format(eps,n_clusters,n_samples,ca))
# Opcional: Dibujar los clusters
pintaSimpleClusters(clustersDS,X,'simple(atrRel={}) DBSCAN(eps={})'
.format(atrRel,eps),'C_DBSCAN(eps={})'.format(eps))
#<TODO END Ejercicio 1>
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 2 : Calidad
Actividad del Campus Virtual
"""
import matplotlib.pyplot as plt
import numpy as np
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
from cluster_distance import cluster_distance # intra_cluster_distance,inter_cluster_distance
from sklearn.cluster import MeanShift, DBSCAN, KMeans, AgglomerativeClustering
import random
def getRandomCluster(RG,nExa,nClus):
"""
Genera un cluster aleatorio para nExa ejemplos utilizando nClus cluster
Parametros:
RG : objeto random.Random()
nExa : entero, número de ejemplos
nClus: entero, número de clusters
"""
clus=[0]*nExa
# Al menos, un ejemplo de cada clúster
for i in range(nClus):
clus[i]=i
# El resto aleatorio con distribución uniforme
for i in range(nClus,nExa):
clus[i]=RG.randrange(nClus)
# Ahora se reordena al aleatoriamente
RG.shuffle(clus)
return clus
#%% Generar n puntos en 2D en cada una de las 4 zonas
# Cada zona está determianda por su punto medio : [-1,-2],[-1,+2],[+1,-2],[+1,+2]
n=5
CentroZonas=[[-1,-2],[-1,+2],[+1,-2],[+1,+2]]
sd=0.25
RG=random.Random()
RG.seed(1)
X=[] # Puntos
for iCZ in range(len(CentroZonas)):
centro=CentroZonas[iCZ]
centrox=centro[0]
centroy=centro[1]
for i in range(n):
x=RG.normalvariate(0,sd)+centrox
y=RG.normalvariate(0,sd)+centroy
X.append([x,y])
X=np.array(X)
pintaSimpleClusters([-1]*len(X),X,'Ejemplos sin agrupar')
#%% Crear nParticiones al azar de nClus cada una
# Cada partición es una asignación a clústers de los ejemplos X
RG=random.Random()
seed=1
RG.seed(seed)
nParticiones=10000
nClus=4
nExa=len(X)
particiones=[None]*nParticiones
for p in range(nParticiones):
particiones[p]=getRandomCluster(RG,nExa,nClus)
# Pinta, a modo de ejemplo, una partición
pintaSimpleClusters(particiones[0],X,'Partición 0')
#%% Multi objetivo lineal
def multiObtetivoLinear(paresMedidasCalidadPesos,clusters,X):
"""
Crea una función lineal usando una lista de pares:
medidas de calidad de clústers
pesos
Evalúa los clústers en X usando esta función lineal
Params:
paresMedidasCalidadPesos : lista [medida de calidad de clústers, peso]
clusters : lista con los índices de los clústers para cada ejemplo
X : ejemplos no etiquetados (atributos)
Returns: la suma del producto de cada valoración de la medida por su peso
"""
return sum([medida(X,clusters)*peso for [medida,peso] in paresMedidasCalidadPesos])
#Ejemplo de uso
# Se quiere evaluar la partición 0 usando una medida de calidad que sea:
# calinski_harabasz_score*2+silhouette_score*1
paresMedidasCalidadPesos=[[calinski_harabasz_score,2],[silhouette_score,1]]
calidadMOLin=multiObtetivoLinear(paresMedidasCalidadPesos,particiones[0],X)
print('Partición 0: medida de calidad multiobjetivo lineal: {:6.4f}'.format(calidadMOLin))
#%% Ejercicio 1
"""
DATOS:
X : Ejemplos no etiquetados (Atributos)
particiones: Lista con todas las particiones (cada partición es una división
en clústers).
ENUNCIADO:
Crea unos pares calidad pesos que utilicen
- silhouette_score
- calinski_harabasz_score
- davies_bouldin_score
- intra_cluster_distance
- inter_cluster_distance
Y que de un valor más grande cuanto mejor
Obtén el índice de la partición de particiones que tiene el mejor valor de
esta función multi objetivo
"""
CD=cluster_distance()
intra_cluster_distance=CD.intra_cluster_distance
inter_cluster_distance=CD.inter_cluster_distance
bestP =None # Guarda el índice de la mejor particion
bestScore=None # Guarda el mejor valor
#TODO 1.1
# El signo determina si es mejor cuando más grande (+1) o más pequeño(-1)
paresMedidasCalidadPesos=[[calinski_harabasz_score,+1],
[silhouette_score ,+1],
[davies_bouldin_score ,-1],
[intra_cluster_distance ,-1],
[inter_cluster_distance ,+1]]
#END 1.1
for p in range(nParticiones):
#TODO 1.2
MOScore=multiObtetivoLinear(paresMedidasCalidadPesos,particiones[p],X) # Score de la función multi objetivo lineal
#END 1.2
#TODO 1.3
if bestScore==None or bestScore<MOScore: # Si el nuevo es mejor que el mejor actual
#END 1.3
# Se almacena el nuevo como el mejor actual
bestScore=MOScore
bestP =p
print('Mejor partición {} MOScore={:6.4f}'.format(bestP,bestScore))
pintaSimpleClusters(particiones[bestP],X,'Partición {} MOScore={:6.4f}'.format(bestP,bestScore))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 4 : Aprendizaje No Supervisado (ANS)
Sesión 2 : Calidad
Actividad 1
"""
from simpleGen import simpleGen,pintaSimpleClusters
from sklearn.metrics import silhouette_score, calinski_harabasz_score
from sklearn.cluster import MeanShift, DBSCAN, KMeans, AgglomerativeClustering
import numpy as np
import random
from sklearn.decomposition import PCA,TruncatedSVD
import matplotlib.pyplot as plt
# Cuando hay attributos no relvantes se pueden utilizar estrategias de reducción
#de atributos no supervisadas, por ejmeplo Principal component analysis (PCA):
# https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html
# Estos sistemas suelen tener un parámetro que determina cuantos atributos generan
#(el número de componentes).
# En esta práctica se observa como varía una medida de calidad cuando se hacen
#clústers sobre una reducción de atributos variando el número de componentes
# Parámetros del conjunto de ejemplos
nejemplos=50 # Número de ejemplos no etiquetados por cada distrubución
RG=random.Random() # Generador de números aleatorios
sd=1
atrNoRel=100
# Crear el conjunto de ejemplos
RG.seed(1) # Semilla del generador de números aleatorios
simple=simpleGen(sd=sd,atrNoRel=atrNoRel)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
# Sistemas ANS
CSKMeans=KMeans(n_clusters=2)#(n_clusters=2,random_state=1)
# Proceso de aprendizaje con KMeans
clustersKM=CSKMeans.fit_predict(X)
# Calcular la medida de calidad
v =silhouette_score(X,clustersKM)
# Variando el número de atributos NO relevantes
vs=[] # Valores de la medida sin reducción
vReds=[] # Valores de la medida con reducción
ncs=list(range(1,min([len(X),len(X[0])]))) # número de componentes desde 1 a atributos-1
for n_components in ncs:
# Sistema reductor de atributos
Red=PCA()
Red.n_components=n_components
XRed=Red.fit_transform(X)
# Proceso de aprendizaje con KMeans sobre el conjunto reducido
clustersKMRed=CSKMeans.fit_predict(XRed)
# Calcular la medida de calidad con reducción
vRed=silhouette_score(X,clustersKMRed)
# Almacenar las medidas de calidad
vs.append(v) # Almacena siempre el mismo valor
vReds.append(vRed)
# Imprimir las medidas
print('atr no rel={:3} silhouette_score KMeans={:6.4f} KMeans(Red(n_components={:2})={:6.4f}'.
format(atrNoRel,v,n_components,vRed))
plt.figure()
plt.plot(ncs,vs,label='Sin reducción')
plt.plot(ncs,vReds,label='Reducción PCA')
plt.ylabel('silhouette_score')
plt.xlabel('n_components')
plt.legend()
plt.title('KMeans(n_clusters=2)')
plt.savefig('plots/Presencial_PCA_KMeans.pdf')
#%% ENUNCIADO actividad 1
# Variando el número de atributos no relevantes (atrNoRel) de 10 a 100 de 10 en 10,
#se crea un conjunto de ejemplos 'simple' (código propuesto)
# Calcular la medida silhouette_score para
# 1- el sistema de clustering SClus
# 2- el sistema que reduce atributos y luego aplica SClus
# La función redCluster eligen el mejor número de componentes según una medida
# Mirar el código, leer los comentarios y completar el código marcado como:
# BEGIN 1.<x>
# END 1.<x>
# para x en {a,b,c,d,e,f}
# SALIDA: plots/RedYClus_AgglomerativeClustering.pdf
# Atr no rel= 10 silhouette_score cluster=0.0712 red+cluster=0.0781
# Atr no rel= 20 silhouette_score cluster=0.0498 red+cluster=0.0325
# Atr no rel= 30 silhouette_score cluster=0.0511 red+cluster=0.0316
# Atr no rel= 40 silhouette_score cluster=0.0274 red+cluster=0.0157
# Atr no rel= 50 silhouette_score cluster=0.0231 red+cluster=0.0201
# Atr no rel= 60 silhouette_score cluster=0.0215 red+cluster=0.0170
# Atr no rel= 70 silhouette_score cluster=0.0160 red+cluster=0.0130
# Atr no rel= 80 silhouette_score cluster=0.0142 red+cluster=0.0092
# Atr no rel= 90 silhouette_score cluster=0.0155 red+cluster=0.0089
# Atr no rel=100 silhouette_score cluster=0.0126 red+cluster=0.0079
def redCluster(SRed,SClus,X,metrica,best=+1):
"""
Función que hace un proceso de reducción y luego otro de clustering
Para elegir el número de componentes itera desde 1 al número de atributos y
Toma el mejor según la métrica.
Parámetros:
SRed : Sistema de reducción de parámetros. Tiene que tener:
Hyperparámetro: n_components
Método : fit_transform
Por ejemplo PCA()
SClus : Sistema de clustering. Tiene que tener:
Método : fit_predict
X : Matriz. Conjunto de datos sin categoría
metrica : Métrica de clustering.
best : Si +1 la métrica es mejor cuanto mayor
Si -1 la métrica es mejor cuanto menor
Por defecto: +1
"""
bestMetrica=None
bestCluster=None
ncs=list(range(1,min([len(X),len(X[0])]))) # número de componentes desde 1 a atributos-1
for n_components in ncs:
# Sistema reductor de atributos: utilizar n_components y luego fit_transform
# BEGIN 1.a
SRed.n_components=n_components
XRed=SRed.fit_transform(X)
# END 1.a
# Proceso de clustering sobre el conjunto reducido
# BEGIN 1.b
clustersRed=SClus.fit_predict(XRed)
# END 1.b
# Calcular la medida de calidad
# BEGIN 1.c
vRed=metrica(X,clustersRed) # IMPORTANTE utilizar X, no XRed
# END 1.c
# Quedarse con la mejor medida y clúster hasta el momento
if bestMetrica==None or vRed!=None and bestMetrica*best<vRed*best:
bestMetrica=vRed
bestCluster=clustersRed
# Retornar el cluster de la mejor metrica
return bestCluster
# BEGIN ACTIVIDAD 1
SRed=PCA()
SClus=AgglomerativeClustering(n_clusters=2)
# Experimentar para diferentes números de atributos no relevantes
vs=[]
vReds=[]
nejemplos=50 # Número de ejemplos no etiquetados por cada distrubución
RG=random.Random() # Generador de números aleatorios
sd=2
anr=list(range(10,100+1,10)) # Atributos no relevantes
for atrNoRel in anr:
# Crear el conjunto de ejemplos
RG.seed(1) # Semilla del generador de números aleatorios
simple=simpleGen(sd=sd,atrNoRel=atrNoRel)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
# Proceso de clustering co el conjunto original
# BEGIN 1.d
clusters=SClus.fit_predict(X)
# END 1.d
# Cáculo de la métrica silhouette_score
# BEGIN 1.e
v=silhouette_score(X,clusters)
# END 1.e
vs.append(v)
# Proceso de reducción + clustering
clustersRed=redCluster(SRed,SClus,X,silhouette_score)
# Cáculo de la métrica silhouette_score
# BEGIN 1.f
vRed=silhouette_score(X,clustersRed)
# END 1.f
vReds.append(vRed)
print('Atr no rel={:3} silhouette_score cluster={:6.4f} red+cluster={:6.4f}'
.format(atrNoRel,v,vRed))
plt.figure()
plt.plot(anr,vs,label='Clúster')
plt.plot(anr,vReds,label='Reducción+clúster')
plt.ylabel('silhouette_score')
plt.xlabel('Atributos no relevantes')
plt.legend()
plt.title('AgglomerativeClustering(n_clusters=2)')
plt.savefig('plots/RedYClus_AgglomerativeClustering.pdf')
# END ACTIVIDAD 1
#%% ENUNCIADO actividad 2
# Repite el ejercicio anterior para el sistema DBSCAN(eps=4)
# PISTA: DBSCAN puede generar predicciones como outliers. Hay que tratarlas
# adecuamete
# PISTA: redCluster toma como tercer parámetro una función métrica, esto es una
# función de la forma f(X,clusters) -> valor
# PISTA: de clusterMetric solo nos es útil para este ejercicio el primer elemento
# que retorna (metric_value)
# SALIDA: plots/RedYClus_DBSCAN.pdf
# Atr no rel= 10 silhouette_score cluster=None red+cluster=0.1956345243580752
# Atr no rel= 20 silhouette_score cluster=None red+cluster=0.06628188881909468
# Atr no rel= 30 silhouette_score cluster=None red+cluster=0.04110672152494071
# Atr no rel= 40 silhouette_score cluster=None red+cluster=0.002860862358982693
# Atr no rel= 50 silhouette_score cluster=None red+cluster=0.03241486726938161
# Atr no rel= 60 silhouette_score cluster=None red+cluster=0.023773086495309844
# Atr no rel= 70 silhouette_score cluster=None red+cluster=0.033602048790723614
# Atr no rel= 80 silhouette_score cluster=None red+cluster=0.022763502354880352
# Atr no rel= 90 silhouette_score cluster=None red+cluster=0.026082035594245
# Atr no rel=100 silhouette_score cluster=None red+cluster=0.013064686052516329
SRed=PCA()
SClus=DBSCAN(eps=5)
# BEGIN ACTIVIDAD 2
def clusterMetric(X,clusters,metric,worstValue=-np.inf):
"""
Calcula la métrica metric tomando como clústers de un solo ejemplo los ejemplos
que no estén en ningún clúster
Parámetros:
- X : conjunto de datos
- clusters : lista con el número de clúster de cada ejemplo (puede ser -1)
- metric : métrica de clústers que no necesita el valor verdadero
- worstValue: valor que retorna si solo hay un cluster o hay un cluster
por cada ejemplo
Retorna: [metric_value,n_clusters,n_samples]
- metric_value : valor de la métrica o None si el número de clústers
es 1 o el número de ejemplos
- n_clusters : número de clusters (sin contar el -1)
- n_samples : número de ejemplos que están en algún clúster (sin contar el -1)
"""
clusterSet=np.unique(clusters)
# Si solo hay un clúster o hay tantos como ejemplos
if len(clusterSet)==1 or len(clusterSet)==len(X):
value=worstValue # Se retorna el peor valor
elif min(clusterSet)>=0: # No hay -1
value= metric(X,clusters) # Lo que retorne la métrica
else:
# Cambiar todos los -1 por un clúster nuevo
cluNuevo=max(clusterSet)+1
clustersNuevo=[]
for c in clusters:
if c==-1:
clustersNuevo.append(cluNuevo)
cluNuevo=cluNuevo+1
else:
clustersNuevo.append(c)
value=metric(X,clustersNuevo)
# Cálculo del Nº de clústers sin -1
n_clusters=len(clusterSet)
if min(clusterSet)==-1:
n_clusters=n_clusters-1
# Cálculo del nº de ejemplos que están en algún clúster
indNoNeg=np.where(np.array(clusters)>=0)[0]
n_samples=len(indNoNeg)
return [value,n_clusters,n_samples]
def clusterMetric_silhouette_score(X,clusters):
return clusterMetric(X,clusters,silhouette_score)[0]
# Experimentar para diferentes números de atributos no relevantes
vs=[]
vReds=[]
nejemplos=50 # Número de ejemplos no etiquetados por cada distrubución
RG=random.Random() # Generador de números aleatorios
sd=2
anr=list(range(10,100+1,10)) # Atributos no relevantes
for atrNoRel in anr:
# Crear el conjunto de ejemplos
RG.seed(1) # Semilla del generador de números aleatorios
simple=simpleGen(sd=sd,atrNoRel=atrNoRel)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
# Proceso de clustering
clusters=SClus.fit_predict(X)
v=clusterMetric(X,clusters,silhouette_score)[0]
vs.append(v)
# Proceso de reducción + clustering
clustersRed=redCluster(SRed,SClus,X,clusterMetric_silhouette_score)
vRed=clusterMetric(X,clustersRed,silhouette_score)[0]
vReds.append(vRed)
print('Atr no rel={:3} silhouette_score cluster={} red+cluster={}'
.format(atrNoRel,v,vRed))
plt.figure()
plt.plot(anr,vs,label='Clúster')
plt.plot(anr,vReds,label='Reducción+clúster')
plt.ylabel('silhouette_score')
plt.xlabel('Atributos no relevantes')
plt.legend()
plt.title('DBSCAN(eps=4)')
plt.savefig('plots/RedYClus_DBSCAN.pdf')
# END ACTIVIDAD 2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Bloque A : Detección de outliers
"""
import numpy as np
import random
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
from sklearn.covariance import EllipticEnvelope
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
def plotOutliers(X,Y,titulo,fileName=None):
plt.figure()
ax=plt.axes()
ax.add_patch(Rectangle([0,0],2,2,edgecolor='black' ,facecolor='None'))
ax.add_patch(Rectangle([0,0],1,1,edgecolor='tab:blue' ,facecolor='None'))
ax.add_patch(Rectangle([1,1],1,1,edgecolor='tab:orange',facecolor='None'))
plt.title(titulo)
plt.xlabel('x0')
plt.ylabel('x1')
plt.xlim([-1,3])
plt.ylim([-1,3])
colIO=['tab:grey','tab:red']
col01=['tab:blue','tab:orange']
classes=np.unique(Y)
if min(classes)==-1: # Hay outliers
col=colIO
labels=['Outliers','Inliers']
else:
col=col01
labels=['Clase {}'.format(cla) for cla in classes]
cc=0
for cla in classes:
indCla=np.where(Y==cla)[0]
plt.plot(X[indCla,0],X[indCla,1],linestyle='None',marker='o',color=col[cc],
label=labels[cc])
cc=cc+1
plt.legend()
if type(fileName)!=type(None):
plt.savefig('plots/{}.pdf'.format(fileName))
def genCuadros(RG,nIrre=0,nEjeClass=10):
X=[]
Y=[]
for cla in [0,1]:
for n in range(nEjeClass):
x1=RG.random()+cla
x2=RG.random()+cla
row=[x1,x2]
for i in range(nIrre):
row.append(RG.random())
X.append(row)
Y.append(cla)
return [np.array(X),np.array(Y)]
def generaOutFuera02(nOut,nIrre=0):
X=[]
Y=[]
for i in range(nOut):
cla=RG.randint(0,1)
x0=RG.random()+RG.choice([-1,2])
x1=RG.random()+RG.choice([-1,2])
row=[x0,x1]
for i in range(nIrre):
row.append(RG.random())
X.append(row)
Y.append(cla)
return [np.array(X),np.array(Y)]
def generaOutEn02(nOut,nIrre=0):
X=[]
Y=[]
for i in range(nOut):
cla=RG.randint(0,1)
x0=RG.random()
x1=RG.random()
if RG.random()>0.5:
x0=x0+1
else:
x1=x1+1
row=[x0,x1]
for i in range(nIrre):
row.append(RG.random())
X.append(row)
Y.append(cla)
return [np.array(X),np.array(Y)]
"""
Ejercicio 1: Observa y cambia
Esta actividad muestra como se eliminan outliers y el efecto que tiene en el
aprendizaje.
Ejecuta bloque a bloque este script. Fíjate en el código y en los dibujos que se
muestran.
Cambia el sistema de detección de outliers (hay varios comentados en la sección de
parámetros)
¿Cuál es mejor?
"""
#%% Parámetros y datos: Cuadros: entrenamiento y test
# Parámetros
RG=random.Random()
RG.seed(1) # Semilla
nIrre=4 # Nº de atributos irrelevantes
nEjeEntre= 10 # Ejemplos entrenamiento por cada clase
nEjeTest =100 # Ejemplos test por cada clase
nEjeOut = 10 # Nº de Outliers
# Algoritmo de detección de outliers
OutDet=IsolationForest(random_state=1)
# OutDet=LocalOutlierFactor()
# OutDet=EllipticEnvelope(random_state=1)
# Clasificador
Class=SVC(kernel='linear',C=1)
[XTr,YTr]=genCuadros(RG,nIrre=nIrre,nEjeClass=nEjeEntre) # Entrenamiento
[XTe,YTe]=genCuadros(RG,nIrre=nIrre,nEjeClass=nEjeTest ) # Test
print('Cuadros Atributos:{} (2 relevantes). Ejemplos entrenamiento:{} test:{}'
.format(len(XTr[0]),len(XTr),len(XTe)))
plotOutliers(XTe,YTe,'Test: Cuadros','Cuadros_01_Test')
plotOutliers(XTr,YTr,'Entrenamiento: Cuadros original','Cuadros_02_EntreOriginal')
#%% Clasificar y evaluar sin outliers
Class.fit(XTr,YTr)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print('Evaluación sin outliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación SIN outliers: accuracy={:6.4f}'.format(Acc),
'Cuadros_03_EvalOriginal')
#%% Inserción de outliers FUERA del rango de los atributos
print('Outliers FUERA del rango de atributos:')
[XOut,YOut]=generaOutFuera02(nEjeOut,nIrre=nIrre)
XTrOut=np.concatenate([XTr,XOut])
YTrOut=np.concatenate([YTr,YOut])
plotOutliers(XTrOut,YTrOut,'Entrenamiento con {} outliers fuera de [0;2]'.format(nEjeOut),
'Cuadros_04_ConOutliersFuera02')
Class.fit(XTrOut,YTrOut)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON outliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON outliers: accuracy={:6.4f}'.format(Acc),
'Cuadros_05_EvaluacionConOutliersFuera02')
#%% Calcular outliers
outValueTr=OutDet.fit_predict(XTrOut)
indOut=np.where(outValueTr==-1)[0]
indIn =np.where(outValueTr==+1)[0]
print(' Entrenamiento Inliers={:4} Outliers={:3}'.format(len(indIn),len(indOut)))
plotOutliers(XTrOut,outValueTr,'Outliers detectados (clase -1)'.format(OutDet),
'Cuadros_06_OutliersFuera02Detectados')
#%% Eliminar outliers, clasificar y evaluar
XTrIn=XTrOut[indIn]
YTrIn=YTrOut[indIn]
plotOutliers(XTrIn,YTrIn,'Entranamiento CON inliers',
'Cuadros_07_QuitandoOutliersFuera02Detectados')
Class.fit(XTrIn,YTrIn)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON inliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON inliers: accuracy={:6.4f}'.format(Acc),
'Cuadros_08_EvalQuitandoOutliersFuera02Detectados')
#%% Inserción de outliers DENTRO del rango de los atributos
print('Outliers DENTRO del rango de atributos:')
[XOut,YOut]=generaOutEn02(nEjeOut,nIrre=nIrre)
XTrOut=np.concatenate([XTr,XOut])
YTrOut=np.concatenate([YTr,YOut])
plotOutliers(XTrOut,YTrOut,'Entrenamiento con {} outliers DENTRO de [0;2]'.format(nEjeOut),
'Cuadros_09_ConOutliersDentro02')
Class.fit(XTrOut,YTrOut)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON outliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON outliers: accuracy={:6.4f}'.format(Acc),
'Cuadros_10_EvaluacionConOutliersDentro02')
#%% Calcular outliers
outValueTr=OutDet.fit_predict(XTrOut)
indOut=np.where(outValueTr==-1)[0]
indIn =np.where(outValueTr==+1)[0]
print(' Entrenamiento Inliers={:4} Outliers={:3}'.format(len(indIn),len(indOut)))
plotOutliers(XTrOut,outValueTr,'Outliers detectados (clase -1)'.format(OutDet),
'Cuadros_11_OutliersDentro02Detectados')
#%% Eliminar outliers, clasificar y evaluar
XTrIn=XTrOut[indIn]
YTrIn=YTrOut[indIn]
plotOutliers(XTrIn,YTrIn,'Entranamiento CON inliers',
'Cuadros_12_QuitandoOutliersDentro02Detectados')
Class.fit(XTrIn,YTrIn)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON inliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON inliers: accuracy={:6.4f}'.format(Acc),
'Cuadros_13_EvalQuitandoOutliersDentro02Detectados')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Bloque A : Detección de outliers
"""
import numpy as np
import random
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
from sklearn.covariance import EllipticEnvelope
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
def plotOutliers(X,Y,titulo,fileName=None):
plt.figure()
ax=plt.axes()
ax.add_patch(Rectangle([0,0],2,2,edgecolor='black' ,facecolor='None'))
ax.add_patch(Rectangle([0,0],1,1,edgecolor='tab:blue' ,facecolor='None'))
ax.add_patch(Rectangle([1,1],1,1,edgecolor='tab:orange',facecolor='None'))
plt.title(titulo)
plt.xlabel('x0')
plt.ylabel('x1')
plt.xlim([-1,3])
plt.ylim([-1,3])
colIO=['tab:grey','tab:red']
col01=['tab:blue','tab:orange']
classes=np.unique(Y)
if min(classes)==-1: # Hay outliers
col=colIO
labels=['Outliers','Inliers']
else:
col=col01
labels=['Clase {}'.format(cla) for cla in classes]
cc=0
for cla in classes:
indCla=np.where(Y==cla)[0]
plt.plot(X[indCla,0],X[indCla,1],linestyle='None',marker='o',color=col[cc],
label=labels[cc])
cc=cc+1
plt.legend()
if type(fileName)!=type(None):
plt.savefig('plots/{}.pdf'.format(fileName))
def genCuadros(RG,nIrre=0,nEjeClass=10):
X=[]
Y=[]
for cla in [0,1]:
for n in range(nEjeClass):
x1=RG.random()+cla
x2=RG.random()+cla
row=[x1,x2]
for i in range(nIrre):
row.append(RG.random())
X.append(row)
Y.append(cla)
return [np.array(X),np.array(Y)]
def generaOutEnClase(nOut,nIrre=0):
X=[]
Y=[]
for i in range(nOut):
cla=RG.randint(0,1)
x0=RG.random()
x1=RG.random()
if RG.random()>0.5:
cla=1
else:
x0=x0+1
x1=x1+1
cla=0
row=[x0,x1]
for i in range(nIrre):
row.append(RG.random())
X.append(row)
Y.append(cla)
return [np.array(X),np.array(Y)]
"""
Ejercicio 1: Observa y cambia
Esta actividad muestra como se eliminan outliers y el efecto que tiene en el
aprendizaje.
Ejecuta bloque a bloque este script. Fíjate en el código y en los dibujos que se
muestran.
Cambia el sistema de detección de outliers (hay varios comentados en la sección de
parámetros)
¿Cuál es mejor?
"""
#%% Parámetros y datos: Cuadros: entrenamiento y test
# Parámetros
RG=random.Random()
RG.seed(1) # Semilla
nIrre=4 # Nº de atributos irrelevantes
nEjeEntre= 10 # Ejemplos entrenamiento por cada clase
nEjeTest =100 # Ejemplos test por cada clase
nEjeOut = 10 # Nº de Outliers
# Algoritmo de detección de outliers
OutDet=IsolationForest(random_state=1)
# OutDet=LocalOutlierFactor()
# OutDet=EllipticEnvelope(random_state=1)
# Clasificador
Class=SVC(kernel='linear',C=1)
[XTr,YTr]=genCuadros(RG,nIrre=nIrre,nEjeClass=nEjeEntre) # Entrenamiento
[XTe,YTe]=genCuadros(RG,nIrre=nIrre,nEjeClass=nEjeTest ) # Test
print('Cuadros Atributos:{} (2 relevantes). Ejemplos entrenamiento:{} test:{}'
.format(len(XTr[0]),len(XTr),len(XTe)))
plotOutliers(XTe,YTe,'Test: Cuadros','CuadrosB_01_Test')
plotOutliers(XTr,YTr,'Entrenamiento: Cuadros original','CuadrosB_02_EntreOriginal')
#%% Clasificar y evaluar sin outliers
Class.fit(XTr,YTr)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print('Evaluación sin outliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación SIN outliers: accuracy={:6.4f}'.format(Acc),
'CuadrosB_03_EvalOriginal')
#%% Inserción de outliers dentro de los dominios de las clases
print('Outliers en el dominio de las clases:')
[XOut,YOut]=generaOutEnClase(nEjeOut,nIrre=nIrre)
XTrOut=np.concatenate([XTr,XOut])
YTrOut=np.concatenate([YTr,YOut])
plotOutliers(XTrOut,YTrOut,'Entrenamiento con {} outliers en las clases'.format(nEjeOut),
'CuadrosB_04_ConOutliersDentroClases')
Class.fit(XTrOut,YTrOut)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON outliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON outliers: accuracy={:6.4f}'.format(Acc),
'CuadrosB_05_EvaluacionConOutliersDentro')
#%% Calcular outliers
outValueTr=OutDet.fit_predict(XTrOut)
indOut=np.where(outValueTr==-1)[0]
indIn =np.where(outValueTr==+1)[0]
print(' Entrenamiento Inliers={:4} Outliers={:3}'.format(len(indIn),len(indOut)))
plotOutliers(XTrOut,outValueTr,'Outliers detectados (clase -1)'.format(OutDet),
'CuadrosB_06_OutliersDentroClasesDetectados')
#%% Eliminar outliers, clasificar y evaluar
XTrIn=XTrOut[indIn]
YTrIn=YTrOut[indIn]
plotOutliers(XTrIn,YTrIn,'Entranamiento CON inliers',
'CuadrosB_07_QuitandoOutliersDentroClasesDetectados')
Class.fit(XTrIn,YTrIn)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON inliers: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON inliers: accuracy={:6.4f}'.format(Acc),
'CuadrosB_08_EvalQuitandoOutliersDentroClasesDetectados')
#%% Quedarse con inliers clase a clase
XTrClaIn=[] # Ejemplos inliers detectados clase a clase
YTrClaIn=[] # La categoría es redundante, pero por simplificar el código (opción docente)
classes=np.unique(YTrOut)
for cla in classes:
indCla=np.where(YTrOut==cla)[0]
XCla=XTrOut[indCla] # Ejemplos de la clase cla
YCla=YTrOut[indCla] # Es algo redundante
outValueTr=OutDet.fit_predict(XCla)
indIn =np.where(outValueTr==+1)[0]
XClaIn=XCla[indIn]
YClaIn=YCla[indIn]
XTrClaIn.append(XClaIn)
YTrClaIn.append(YClaIn)
print(' Inliers en clase {}:{:2} de un total de {:2}'.format(cla,len(XClaIn),len(XCla)))
plotOutliers(XCla,outValueTr,'Outliers detectados (clase -1) de la clase {}'.format(cla),
'CuadrosB_09_OutliersDentroClase_{}_Detectados'.format(cla))
XTrClaIn=np.concatenate(XTrClaIn)
YTrClaIn=np.concatenate(YTrClaIn)
plotOutliers(XTrClaIn,YTrClaIn,'Entranamiento CON inliers clase a clase',
'CuadrosB_10_OutliersClaseAClase')
#%% Clasificar y evaluar
Class.fit(XTrClaIn,YTrClaIn)
P=Class.predict(XTe)
Acc=accuracy_score(YTe,P)
print(' Evaluación CON inliers clase a clase: accuracy={:6.4f}'.format(Acc))
plotOutliers(XTe,P,'Evaluación CON inliers clase a clase: accuracy={:6.4f}'.format(Acc),
'CuadrosB_11_EvalQuitandoOutliersClaseAClaseDetectados')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Bloque C : Detección de ejemplos novedosos (novelty)
"""
import numpy as np
import random
import matplotlib.pyplot as plt
from sklearn.svm import OneClassSVM
from sklearn.ensemble import IsolationForest
def genNovelty(RG,nNormales,nNovedosos):
X=[]
sd=0.25
mediaX1=0
mediaX2=0
# Ejemplos normales
for i in range(nNormales):
x1=RG.normalvariate(mediaX1,sd)
x2=RG.normalvariate(mediaX2,sd)
X.append([x1,x2])
# Ejemplos cada vez más novedosos
for i in range(nNovedosos):
mediaX1=1#mediaX1+0.1
mediaX2=1#mediaX2+0.1
x1=RG.normalvariate(mediaX1,sd)
x2=RG.normalvariate(mediaX2,sd)
X.append([x1,x2])
return np.array(X)
def plotOneData(X,label,marker,color):
plt.plot(X[:,0],X[:,1],label=label,linestyle='None',marker=marker,color=color)
def plot2D(XTr,XTe,XTrNo,XTeNo,title,filename):
plt.figure()
plt.title(title)
plt.xlabel('X0')
plt.ylabel('X1')
plotOneData(XTr,'Entrenamiento','o','blue')
plotOneData(XTe,'Test','o','green')
plotOneData(XTrNo,'Novedad en Entre.','x','magenta')
plotOneData(XTeNo,'Novedad en Test','x','orange')
plt.legend()
plt.savefig(filename)
#%% Generación de datos normales y novedosos
RG=random.Random()
RG.seed(1)
XTr=genNovelty(RG,nNormales=25,nNovedosos= 0) # Entrenamiento todos normales
XTe=genNovelty(RG,nNormales=25,nNovedosos=25) # Test 15 normalesy 15 novedosos
X=np.concatenate([XTr,XTe]) # Todos
#%% Aplicar sistema de detección de anomalías: OneClassSVM
nu=0.1
AD=OneClassSVM(nu=nu)
AD.fit(XTr)
# En test
PTe=AD.predict(XTe)
ind=np.where(PTe==-1)[0] # Índices a ejemplos considerados novedad
XTeNo=XTe[ind,:] # Ejemplos novedosos
# En entrenamiento
PTr=AD.predict(XTr)
ind=np.where(PTr==-1)[0] # Índices a ejemplos consideredos novedad
XTrNo=XTr[ind,:] # Ejemplos novedosos
plot2D(XTr,XTe,XTrNo,XTeNo,'OneClassSVM(nu={})'.format(nu),'plots/OneClassSVM_CorteOriginal.pdf')
print('OneClassSVM(nu={})'.format(nu))
print(' Ejemplos de entrenamiento:{} novedad:{:2}'.format(len(XTr),len(XTrNo)))
print(' Ejemplos de Test :{} novedad:{:2}'.format(len(XTe),len(XTeNo)))
#%% No todas las novedades lo son con la misma 'intensidad'.
# Obtenemos la función de decisión.
# El signo es la predicción, pero cuanto más negativa más novedad.
DFTe=AD.decision_function(XTe)
plt.figure()
plt.title('OneClassSVM(nu={})'.format(nu))
plt.xlabel('X0')
plt.ylabel('Función de decisión')
plt.plot(XTe[:,0],DFTe,linestyle='None',marker='.',color='navy',label='Función de decisión')
plt.plot(XTe[:,0],[0] *len(XTe),linestyle='solid',marker=None,color='black',label='Punto de corte original(0)')
plt.plot(XTe[:,0],[-0.3]*len(XTe),linestyle='solid',marker=None,color='red',label='Punto de corte=-0.3')
plt.legend()
plt.savefig('plots/OneClassSVM_DF.pdf')
#%% Actividad 1
# Vista la gráfica anterior nos parece mejor que la separación entre novedosas
# y no novedosas esté en -0.3 en vez de 0
# Modifica PTe de mamera que tenga el valor del signo de la función de decisión (DF)
# a la que se le resta el valor de separacion, en este caso -0.3
# Imprime el número de ejemplos novedosos en este caso
# NOTA: puedes usar la función np.sign que retorna el signo de un numero
#TODO Actividad 1
# Modificar la predicción en test
PTe=np.sign(DFTe-(-0.3)) #justa las predicciones de test (PTe) usando un nuevo umbral.DFTe contiene los valores de la función de decisión (la distancia al hiperplano o frontera) para el conjunto de test.Al restar -0.3 (que equivale a hacer DFTe + 0.3), estamos desplazando la frontera de decisión.np.sign() calcula el signo del resultado: si el valor es positivo devuelve +1 (normal) y si es negativo devuelve -1 (novedad).
# Calcular la DF de train
DFTr=AD.decision_function(XTr) #Obtiene las puntuaciones de la función de decisión para los datos de entrenamiento (XTr) usando el modelo entrenado (AD). Valores más altos significan datos más "normales" o internos, y valores más bajos o negativos indican datos más lejanos o anómalos.
# Modificar la predicción en train
PTr=np.sign(DFTr-(-0.3)) # Aplica exactamente la misma lógica del punto anterior pero sobre los datos de entrenamiento. Reasigna las etiquetas en PTr (+1 o -1) basándose en el nuevo punto de corte de -0.3.
#Calcular cuantos ejemplos novedosos hay ahora en train y test
ind=np.where(PTe==-1)[0] # Busca y almacena en la variable ind las posiciones (índices) de todos los elementos del conjunto de test que fueron clasificados como novedad (-1).
XTeNo=XTe[ind,:] # : Filtra la matriz XTe para quedarse únicamente con las filas (características) de los ejemplos que han sido identificados como novedades en test.
ind=np.where(PTr==-1)[0] #Reutiliza la variable ind para buscar ahora los índices de los elementos del conjunto de entrenamiento que fueron clasificados como novedad (-1).
XTrNo=XTr[ind,:] # Ejemplos novedosos
print('OneClassSVM(nu={}) Punto de corte=-0.3'.format(nu))
print(' Ejemplos de entrenamiento:{} novedad:{:2}'.format(len(XTr),len(XTrNo)))
print(' Ejemplos de Test :{} novedad:{:2}'.format(len(XTe),len(XTeNo)))
#END TODO Actividad 1
plot2D(XTr,XTe,XTrNo,XTeNo,'OneClassSVM(nu={}) Punto de corte=-0.3'.format(nu),'plots/OneClassSVM_CorteCambiado.pdf')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Bloque D : Detección de ejemplos novedosos (novelty) con autoencoders
"""
"""
NOTA: Instalar Torch:
conda install pytorch cpu-only -c pytorch
"""
from AutoencoderNovelty import AutoencoderNovelty
import numpy as np
import random
import matplotlib.pyplot as plt
def genNovelty(RG,nNormales,nNovedosos):
X=[]
sd=0.2
mediaX1=0
mediaX2=0
# Ejemplos normales
for i in range(nNormales):
x1=RG.normalvariate(mediaX1,sd)
x2=RG.normalvariate(mediaX2,sd)
X.append([x1,x2])
# Ejemplos cada vez más novedosos
for i in range(nNovedosos):
mediaX1=1
mediaX2=1
x1=RG.normalvariate(mediaX1,sd)
x2=RG.normalvariate(mediaX2,sd)
X.append([x1,x2])
return np.array(X)
def plotOneData(X,label,marker,color):
plt.plot(X[:,0],X[:,1],label=label,linestyle='None',marker=marker,color=color)
def plot2D(XTr,XTe,XTrNo,XTeNo,title,filename):
plt.figure()
plt.title(title)
plt.xlabel('X0')
plt.ylabel('X1')
plotOneData(XTr,'Entrenamiento','o','blue')
plotOneData(XTe,'Test','o','green')
plotOneData(XTrNo,'Novedad en Entre.','x','magenta')
plotOneData(XTeNo,'Novedad en Test','x','orange')
plt.legend()
plt.savefig(filename)
#%% Generación de datos normales y novedosos
RG=random.Random()
RG.seed(2)
XTr=genNovelty(RG,nNormales=25,nNovedosos= 0) # Entrenamiento todos normales
XTe=genNovelty(RG,nNormales=175,nNovedosos=25) # Test 15 normalesy 15 novedosos
X=np.concatenate([XTr,XTe]) # Todos
#%% Aplicar sistema de detección de anomalías: AutoencoderNovelty
AD=AutoencoderNovelty()
AD.fit(XTr)
# En test
PTe=AD.predict(XTe)
ind=np.where(PTe==-1)[0] # Índices a ejemplos considerados novedad
XTeNo=XTe[ind,:] # Ejemplos novedosos
# En entrenamiento
PTr=AD.predict(XTr)
ind=np.where(PTr==-1)[0] # Índices a ejemplos consideredos novedad
XTrNo=XTr[ind,:] # Ejemplos novedosos
plot2D(XTr,XTe,XTrNo,XTeNo,'AutoencoderNovelty','plots/Autoencoder.pdf')
print('AutoencoderNovelty')
print(' Ejemplos de entrenamiento:{} novedad:{:2}'.format(len(XTr),len(XTrNo)))
print(' Ejemplos de Test :{} novedad:{:2}'.format(len(XTe),len(XTeNo)))
#%% Ejercicio 1
"""
Observa la gráfica AutoencoderNovelty.
No hay ningún ejemplo de entrenamiento marcado como novedad. ¿Porqué es así?
Se quiere que solo los ejemplos de test que están en la parte superior derecha
se traten como novedad. ¿Qué se podría cambiar en el AutoencoderNovelty?
Modifica la versión 2 (v2) para lograrlo. (Está copiada a continuación)
PISTA: Mira los parámetros del constructor
"""
#%% Aplicar sistema de detección de anomalías: AutoencoderNovelty v2
# Solución
# Ningún ejemplo de entrenamiento está tratado como novedad porque el valor tope
# de novedad es el máximo del error de reconstrucción en entrenamiento y por tanto
# ningún ejemplo de entrenamiento supera ese límite
# Para conseguir que haya menos novedad se puede subir el valor tope de novedad
# Se usará el parámetro calculoTh que calcula el valor tope de novedad
# Propuesta: sumar al máximo la desviación típica de los errores de reconstrucción
# Versión con creación de función
def miCalculoTh(ers):
return max(ers)+np.std(ers)
AD=AutoencoderNovelty(calculoTh=miCalculoTh)
# Versión con programación funcional
# AD=AutoencoderNovelty(calculoTh=lambda ers : max(ers)+np.std(ers))
AD.fit(XTr)
# En test
PTe=AD.predict(XTe)
ind=np.where(PTe==-1)[0] # Índices a ejemplos considerados novedad
XTeNo=XTe[ind,:] # Ejemplos novedosos
# En entrenamiento
PTr=AD.predict(XTr)
ind=np.where(PTr==-1)[0] # Índices a ejemplos consideredos novedad
XTrNo=XTr[ind,:] # Ejemplos novedosos
plot2D(XTr,XTe,XTrNo,XTeNo,'AutoencoderNovelty v2','plots/Autoencoder_v2.pdf')
print('AutoencoderNovelty v2')
print(' Ejemplos de entrenamiento:{} novedad:{:2}'.format(len(XTr),len(XTrNo)))
print(' Ejemplos de Test :{} novedad:{:2}'.format(len(XTe),len(XTeNo)))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Bloque Pre : Detección de anomalías
"""
import numpy as np
import random
from sklearn.svm import SVC
import matplotlib.pyplot as plt
def genPosNeg(RG,nExa=10):
X=[]
Y=[]
for cate in [-1,+1]:
for n in range(nExa):
x=RG.random()*cate
X.append([x])
Y.append(cate)
return [X,Y]
def dibuja(X,Y,titulo,filename):
Y=np.array(Y)
X=np.array(X)
plt.figure(figsize=[6,2])
plt.title(titulo)
plt.xlabel('X')
plt.ylabel('Categoría')
plt.ylim([-1.15,+1.15])
for cate in [-1,+1]:
ind=np.where(Y==cate)
XPos=X[ind,:].flatten()
plt.plot(XPos,[cate]*len(XPos),linestyle='None',marker='o')
plt.savefig(filename)
#%% Programa principal
Cla=SVC(kernel='linear',gamma=1,C=1)
# Con un outlier
RG=random.Random()
RG.seed(1)
[XTr,YTr]=genPosNeg(RG)
[XTe,YTe]=genPosNeg(RG)
# Se añade un outlier
XTr.append([-10])
YTr.append(+1)
dibuja(XTr,YTr,'Entrenamiento con outlier','plots/preTrainWithOutlier.pdf')
Cla.fit(XTr,YTr)
P=Cla.predict(XTe)
dibuja(XTe,P,'Predicción de test','plots/preEvalTestModelWithOutlier.pdf')
# Quitamos el outlier
XTr=XTr[:(len(XTr)-1)]
YTr=YTr[:(len(YTr)-1)]
dibuja(XTr,YTr,'Entrenamiento sin outlier','plots/preTrainNoOutlier.pdf')
Cla.fit(XTr,YTr)
P=Cla.predict(XTe)
dibuja(XTe,P,'Predicción de test','plots/preEvalTestModelNoOutlier.pdf')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Actividad 1
"""
from datasets.leeIris import leeIris
import matplotlib.pyplot as plt
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
from sklearn.covariance import EllipticEnvelope
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import random
def pintaClassOutliers(outValue,X,Y,titulo,fileName,xlim=None,ylim=None):
"""
Función que pinta clusters hechos sobre conjuntos de datos 'simple'
Pinta sobre los 2 primeros atributos en el intervalo [-3,+3]
Parámetros:
- outValue : lista donde -1 indica outlier y -1 inlier
- [X,Y] : Valores de los ejemplos
- titulo : String. Título de la figura
- fileName : String. nombre del fichero sin extensión (la extensión será .pdf)
Si vale None no se guarad en fichero
Se guardará en la carpeta plots/
"""
colores=['tab:orange','tab:blue','tab:red']
YOut=list(map(lambda o,y:y if o==+1 else -1,outValue,Y))
plt.figure()
labels=np.unique(YOut)
RG=None
for ilabel in range(len(labels)):
label=labels[ilabel]
if label==-1:
color='tab:gray' # No asignados a ninguna clase
elif label<len(colores):
color=colores[label]
else:
if RG==None:
RG=random.Random()
RG.seed(1)
color=(RG.random(),RG.random(),RG.random())
ccolor=color
indices=np.where(YOut==label)[0]
if xlim!=None:
plt.xlim(xlim)
if ylim!=None:
plt.ylim(ylim)
plt.plot(X[indices,0],X[indices,1],linestyle='None',marker='o',color=color,label='clase {}'.format(label) if label>=0 else 'outliers')
plt.title(titulo)
plt.xlabel('Atributo 0')
plt.ylabel('Atributo 1')
plt.legend(loc='upper left')
if fileName!=None:
plt.savefig('plots/{}.pdf'.format(fileName))
def randomMove(RG,YTr,CatSource,CatTarget,proportion):
indSou=np.where(YTr==CatSource)[0]
nMove=int(len(indSou)*proportion)
RG.shuffle(indSou)
for i in indSou[:nMove]:
YTr[i]=CatTarget
# ACTIVIDAD 1
# Se lee el conjunto iris con los 2 atributos relevantes.
# Se divide en entrenamien to y test
# En el entrenamietno se intercambian algunos valores de la categoría (Y).
# La gráfica plots/IrisSwapped.pdf muestra como queda.
# Se realiza un aprendizaje/evaluación con Random Forest.
# Se lee el conjunto de ejemplos iris (solo 2 atributos)
[X,Y]=leeIris()
[XTr,XTe,YTr,YTe]=train_test_split(X,Y,test_size=1/3, stratify=Y,random_state=5)
xlim=[0,7]
ylim=[0,3]
# Se intercambian al azar algunas categorías
RG=random.Random()
RG.seed(1)
randomMove(RG,YTr,CatSource=1,CatTarget=2,proportion=0.25)
randomMove(RG,YTr,CatSource=0,CatTarget=1,proportion=0.25)
outValue=[1]*len(YTr)
pintaClassOutliers(outValue,XTr,YTr,'iris swapped','IrisSwapped',xlim=xlim,ylim=ylim)
Class=RandomForestClassifier(random_state=1)
# Aprender con todos los ejemplos
Class.fit(XTr,YTr)
P=Class.predict(XTe)
Acc=accuracy_score(P,YTe)
print('Accuracy con todos({}) los ejemplos : {:6.4f}'.format(len(XTr),Acc))
# ENUNCIADO ACTIVIDAD 1
"""
Utilizar dos estrategias de eliminación de outliers: todos los ejemplos y clase a clase
Realizar con los inliers otro aprendizaje/evaluación y calcular la accuracy
¿Se ha conseguido mejorar con alguna estrategia?
"""
#TODO ACTIVIDAD 1
# sobre todo el conjunto: Calcular outliers y eliminarlos
OutDet=IsolationForest(random_state=1) #Instancia el detector de outliers. En este caso utiliza Isolation Forest, un algoritmo basado en árboles de decisión que aisla las anomalías de forma # eficiente. El random_state=1 asegura que el resultado sea siempre el mismo si vuelves a ejecutar el código.
# ALTERNATIVAS A USAR EL IsolationForest
# OutDet=LocalOutlierFactor(novelty=True)
# OutDet=EllipticEnvelope(random_state=1)
outValueTr=OutDet.fit_predict(XTr) # Entrena el detector con los datos de entrenamiento (XTr) y predice si cada dato es normal o un outlier. Guarda el resultado en outValueTr.
pintaClassOutliers(outValueTr,XTr,YTr,'iris swapped(junto)','IrisSwapped_junto',xlim=xlim,ylim=ylim)
indexInlayer=np.where(outValueTr==+1)[0] # Busca las posiciones (índices) de todos los elementos cuyo valor es +1 (los datos normales o inliers).
print('Entrenamiento Inliers (Junto): {} ejemplos'.format(len(indexInlayer)))
XTrIn=XTr[indexInlayer]
YTrIn=YTr[indexInlayer] #Crea un nuevo conjunto de entrenamiento filtrado (XTrIn e YTrIn) que contiene únicamente los datos que pasaron el filtro de inliers.
# Apreder sin los outlayers
Class.fit(XTrIn,YTrIn)
P=Class.predict(XTe)
Acc=accuracy_score(P,YTe)
print('Accuracy con inlayers (junto) : {:6.4f}'.format(Acc))
# Clase a clase: Calcular outliers y eliminarlos
XTrClaIn=[] # Ejemplos inliers detectados clase a clase
YTrClaIn=[] # La categoría es redundante, pero por simplificar el código (opción docente)
classes=np.unique(YTr) #Extrae las clases o categorías únicas que existen en las etiquetas (por ejemplo: [0, 1, 2]).
for cla in classes:
indCla=np.where(YTr==cla)[0] #: Encuentra los índices o posiciones de los datos que pertenecen únicamente a la clase actual
XCla=XTr[indCla] # Ejemplos de la clase cla
YCla=YTr[indCla] # Es algo redundante
outValueTr=OutDet.fit_predict(XCla) #Ajusta el detector y predice los outliers únicamente con los datos de esta clase. Esto permite identificar qué puntos son "raros" respecto a sus compañeros de clase.
indIn =np.where(outValueTr==+1)[0] #Encuentra los índices locales de los datos clasificados como normales (+1) dentro de esta clase.
XClaIn=XCla[indIn]
YClaIn=YCla[indIn]
XTrClaIn.append(XClaIn) #Como las listas contenían bloques separados por clase, np.concatenate une todos los fragmentos en una sola matriz de datos continua (un solo bloque para X y otro para Y).
YTrClaIn.append(YClaIn)
print(' Inliers en clase {}:{:2} de un total de {:2}'.format(cla,len(XClaIn),len(XCla)))
XTrClaIn=np.concatenate(XTrClaIn)
YTrClaIn=np.concatenate(YTrClaIn)
print('Entrenamiento total Inliers={:4} Outliers={:4}'
.format(len(XTrClaIn),len(XTr)-len(XTrClaIn)))
# Apreder sin los outlayers
Class.fit(XTrClaIn,YTrClaIn)
P=Class.predict(XTe)
Acc=accuracy_score(P,YTe)
print('Accuracy con inlayers (Clase a Clase): {:6.4f}'.format(Acc))
# END ACTIVIDAD 1
#%% ENUNCIADO ACTIVIDAD 2
"""
Se quieren combinar varios sistemas de detección de outliers de manera
que solo se tome como outlier un ejemplo si para todos sistemas se indica que es
outlier.
Utilizar la clase outlierCombina
Aplicarlo al caso de detección de outliers por clase
"""
class outlierCombina:
"""
Combina detectores de outliers indicando como outliers los ejemplos
que todos los detectores indican que son outliers
"""
def __init__(self,ODList):
"""
Parámetros:
- ODList : lista de detectores de outliers
"""
self.ODList=ODList
def fit_predict(self,X):
Preds=[] # Todas las predicciones
for OD in self.ODList:
POD=OD.fit_predict(X)
Preds.append(POD)
# Preds tiene tanatas filas como detectores de outliers hay en self.ODList
# Cada columna son las predicciones que se han hecho para un ejemplo
# Se queire que la predicción P sea oulier solamente si todas las predicciones
# para cada ejemplo indican que es outliar
# TODO 2.1
P=np.max(Preds,axis=0) # Si hay un +1 para algún OD (Detector de Outliers),
# ese ejemplo ya es no outlier
# END
return np.array(P)
# Calcular outliers y eliminarlos clase a clase
print('\nCombinando dos sistemas de detección de outliers')
XTrIn=[]
YTrIn=[]
OutDet1=IsolationForest(random_state=1)
OutDet2=LocalOutlierFactor()
OutDet3=EllipticEnvelope(random_state=1)
#TODO 2.2 Crea una lista con varios OD (Detector de Outliers)
ODList=[OutDet1,OutDet2]
#END
#TODO 2.3 Crea objeto outlierCombina que utiliza lista anterior
OutDetCom=outlierCombina(ODList)
#END
for c in np.unique(YTr):
indexc=np.where(YTr==c)[0]
XTrc=XTr[indexc,:]
YTrc=YTr[indexc]
#TODO 2.4 Utiliza el detector de outliers combinado
outValueTr=OutDetCom.fit_predict(XTrc)
#END
print('Entrenamiento clase {} Inliers={:4} Outliers={:4}'.format(c,len(np.where(outValueTr==+1)[0]),len(np.where(outValueTr==-1)[0])))
pintaClassOutliers(outValueTr,XTrc,YTrc,'iris swapped','IrisSwapped_class_{}'.format(c),xlim=xlim,ylim=ylim)
# Insertar los inliers en el conjunto [XTrIn,YTrIn]
indexInlayer=np.where(outValueTr==+1)[0]
XTrIn=XTrIn+XTrc[indexInlayer,:].tolist()
YTrIn=YTrIn+YTrc[indexInlayer].tolist()
print('Entrenamiento total Inliers={:4} Outliers={:4}'
.format(len(XTrIn),len(XTr)-len(XTrIn)))
# Apreder sin los outlayers
Class.fit(XTrIn,YTrIn)
P=Class.predict(XTe)
Acc=accuracy_score(P,YTe)
print('Accuracy con inlayers (por clases): {:6.4f}'.format(Acc))
#%% ENUNCIADO ACTIVIDAD 3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 5 : Detección de anomalías
Sesión 1 : Detección de anomalías
Actividad 2
"""
from datasets.leeIris import leeIris
import matplotlib.pyplot as plt
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from random import random
from sklearn.model_selection import train_test_split
def pintaClass(X,Y,titulo,fileName=None,xlim=None,ylim=None):
"""
Función que pinta datsets de clasificación
Parámetros:
- [X,Y] : Valores de los ejemplos
- titulo : String. Título de la figura
- fileName : String. nombre del fichero sin extensión (la extensión será .pdf)
Si vale None no se guarad en fichero
Se guardará en la carpeta plots/
"""
if type(Y)==type(None):
Y=np.array([-1]*len(X))
colores=['tab:orange','tab:blue','tab:red']
plt.figure()
labels=np.unique(Y)
RG=None
for ilabel in range(len(labels)):
label=int(labels[ilabel])
if label==-1:
color='tab:gray' # No asignados a ninguna clase
elif label<len(colores):
color=colores[label]
else:
if RG==None:
RG=random.Random()
RG.seed(1)
color=(RG.random(),RG.random(),RG.random())
ccolor=color
indices=np.where(Y==label)[0]
if xlim!=None:
plt.xlim(xlim)
if ylim!=None:
plt.ylim(ylim)
plt.plot(X[indices,0],X[indices,1],linestyle='None',marker='o',color=color,label='clase {}'.format(label))
plt.title(titulo)
plt.xlabel('Atributo 0')
plt.ylabel('Atributo 1')
plt.legend(loc='upper left')
if fileName!=None:
plt.savefig('plots/{}.pdf'.format(fileName))
def leeIris12(seed=1):
[X,Y]=leeIris()
# Iris with categories 1 or 2
indIris01=np.where(Y>=1)[0]
X12=X[indIris01,:]
Y12=Y[indIris01]
# Iris with categories 2
indIris0=np.where(Y==0)[0]
X0=X[indIris0,:]
Y0=Y[indIris0]
# Train test over Iris 01
[XTr,XTe,YTr,YTe]=train_test_split(X12,Y12,test_size=1/3, stratify=Y12,random_state=seed)
# Iris 2 is added to test
XTe=np.concatenate([XTe,X0])
YTe=np.concatenate([YTe,Y0])
return [XTr,YTr,XTe,YTe]
# ENUNCIADO ACTIVIDAD 1
"""
Se han recogido varios lirios (iris) y se ha creado un conjunto etiquetado con
algunos lirios de los tipos 1 y 2
Se ha realizado un proceso de aprendizaje que crea un modelo que diferencia
entre los tipos 1 y 2 (Class)
Se quiere evaluarlo sobre el resto de lirios que se tienen que no están etiquetados,
y que no solo tienen que ser de tipo 1 o 2.
Ha de predecirse 1 o 2 para los lirios que se cree que son de clase 1 o 2 y
si no se cree que es de esa clase se ha de predecir -1
"""
# Se lee el conjunto de ejemplos iris (solo 2 atributos)
[XTr,YTr,XTe,_]=leeIris12()
xlim=[0,7]
ylim=[0,3]
outValue=[]
pintaClass(XTr,YTr ,'iris 12 : train',xlim=xlim,ylim=ylim)
pintaClass(XTe,None,'iris 12 : test' ,xlim=xlim,ylim=ylim)
Class=RandomForestClassifier(random_state=1)
Class.fit(XTr,YTr)
# SOLUCIÓN
# TODO 1.1
# 1 Se realiza una predicción
P=Class.predict(XTe)
#END
# 2 Se detecta la novedad en el test
# TODO 1.2
from AutoencoderNovelty import AutoencoderNovelty
AD=AutoencoderNovelty(calculoTh=lambda ers:max(ers)+np.std(ers)) # El umbral se calcula sumando el error de reconstrucción máximo (max(ers)) más la desviación estándar de los errores (np.std(ers)). Cualquier dato de test con un error de reconstrucción mayor a este umbral será considerado una "novedad".
AD.fit(XTr)
# En test
PNovelty=AD.predict(XTe)
#END
# 3 Se combina como indica el enunciado: si hay novedad -> novedad, si no la predicción de Class
# Guarda la predicción en P (Para luego pintarla)
# TODO 1.3
for i in range(len(XTe)):
if PNovelty[i]==-1:
P[i]=-1
# END
pintaClass(XTe,P,'iris 12 : predicción' ,xlim=xlim,ylim=ylim)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 6 : Procesos de decisión de Markov
Sesión 1 : Procesos de decisión de Markov
Bloque A : Expresión del problema en función de P (Probabilidades de transición) y R (Reconpensas)
Optimización de la Política usando un MDPSolver
"""
#https://pymdptoolbox.readthedocs.io/en/latest/api/mdp.html#mdptoolbox.mdp.MDP
# Se quiere representar según un Proceso de Decisión de Markov (DPM) la siguiente situación:
# Se tiene una nave que puede estar a la (I)zquierda o a la (D)erecha.
#Se puede controlar este elemento móvil mediante las acciones (I)zquierda y
#(D)erecha.
# Hay un ET que puede estar en las dos posiciones de la nave.
#Este elemento no se puede controlar.
# La nave puede disparar.
# El objetivo es que la nave dispare cuando esté en la misma posición que el ET.
# Cuando se mueve la nave, el extraterrestre (ET) puede ir a I o D con igual probabilidad.
# Cuando dispara la nave y no está en la misma posición que el ET
#no ocurre nada. En este caso la recompensa es de -1
# Cuando dispara la nave y está en la misma posición que el ET, el
#ET cambia de posición con probabilidad 0.75 y se queda con
#probabilidad 0.25. En este caso la recompensa es de +1
# Solución: Definición de estados y acciones
# Hay 4 estados correspondientes a las 4 combinaciones de la nave y el ET.
# <Posición de la nave>: I,D (Izquierda o derecha)
# <Posición del ET> : I,D (Izquierda o derecha)
# Estados <Posición de la nave><Posición del ET>
# - II
# - ID
# - DI
# - DD
# Hay 3 acciones,
# - I: mover a la izquierda
# - D: mover a la derecha
# - S: disparar
# Solución: Uso de mdptoolbox
import numpy as np
import mdptoolbox
# El estado XY indica que la nave está en X y el ET en Y
# Los nombres solo son útiles para imprimir la política y el valor de una manera
#humanamente inteligible
# Estados: II(0), ID(1), DI(2), DD(3)
SNombres=['II','ID','DI','DD']
# Acciones: I(0) ,D(1) ,S(2)
ANombres=['I','D','S']
#%% Probabilidades de cambio de estado según la acción
# Se crea una matriz de probabilidad por cada acción
# Estas matrices son cuadradas: NºEstados x NºEstados
# El valor de la fila f y la columna c es la probabilidad de que estando en el
#estado f y ejecutando la acción asociada a la matriz (hay una por cada acción)
#se pase al estado c
# P de la acción I(zquierda)
# Estado destino: II ID DI DD
PI=[ [0.5,0.5,0 ,0], # Estado origen II
[0.5,0.5,0 ,0], # Estado origen ID
[0.5,0.5,0 ,0], # Estado origen DI
[0.5,0.5,0 ,0]] # Estado origen DD
# Explicación de un caso:
# Si se está en el estado DI (nave a la Derecha y ET a la Izquierda)
#y se ejecuta I (Mover a la Izquierda) las probabilidades de II son 0.5, las de
#ID son 0.5 y el resto 0. Esto significa que la nave irá a la izquierda pero
# el extaterrestre se moverá a la derecha o quedará a la izquierda con igual
# probabilidad. Que es lo indicado en el enunciado.
# P de la acción D(erecha)
# Estado destino: II ID DI DD
PD=[ [0 ,0 ,0.5,0.5], # Estado origen II
[0 ,0 ,0.5,0.5], # Estado origen ID
[0 ,0 ,0.5,0.5], # Estado origen DI
[0 ,0 ,0.5,0.5]] # Estado origen DD
# P de la acción disparo (S)hoot
# Estado destino: II ID DI DD
PS= [[0.25,0.75,0 ,0 ], # Estado origen II
[0 ,1 ,0 ,0 ], # Estado origen ID
[0 ,0 ,1 ,0 ], # Estado origen DI
[0 ,0 ,0.75,0.25]] # Estado origen DD
# Explicación:
# Para el orígenes ID solo puede ir ID, lo mismo para DI. Esto implementa la
#parte del enunciado en la que indica que si se dispara y no está el ET en el
#mismo sitio no pasa nada.
# Para los orígenes donde coinciden, se queda en ese estado con probabilidad 0.25
#y cambia el ET con probabilidad 0.75
# mdptoolbox.mdp no acepta que P y R sean de tipo list
# Admite que P y R sean numpy.Array
P=np.array([PI,PD,PS])
#%% Reconpensas
# El orden de las acciones de R (reconpensas) ha de ser el mismo que el de P
# Por cada acción se crea una matriz de recompensas cuadradas: NºEstados x NºEstados
# El valor de la fila f y la columna c es la reconpensa por cambiar del estado
#f al estado c al aplicar la acción de la matriz correspondiente.
# Los valores de P que sean 0 definen situaciones imposibles.
# Aunque no es obligatorio se recomienda marcar esas posiciones al definir R
#para evitar posibles confusiones. El valor que se ponga no tendrá efecto.
# En este caso se define la variable i que será la que se use para marcar las
#situaciones imposibles en las recompensas. El valor que se de a i no tiene efecto
i=0
# R de la acción I(zquierda)
# Estado destino: II ID DI DD
RI= [[ 0, 0, i, i], # Estado origen II
[ 0, 0, i, i], # Estado origen ID
[ 0, 0, i, i], # Estado origen DI
[ 0, 0, i, i]] # Estado origen DD
# R de la acción D(erecha)
# Estado destino: II ID DI DD
RD= [[ i, i, 0, 0], # Estado origen II
[ i, i, 0, 0], # Estado origen ID
[ i, i, 0, 0], # Estado origen DI
[ i, i, 0, 0]] # Estado origen DD
# R de la acción disparo S(hot)
# Estado destino: II ID DI DD
RS= [[+1,+1, i, i], # Estado origen II
[ i,-1, i, i], # Estado origen ID
[ i, i,-1, i], # Estado origen DI
[ i, i,+1,+1]] # Estado origen DD
R=np.array([RI,RD,RS])
MDPSolver = mdptoolbox.mdp.PolicyIteration(P, R, discount=0.9)
MDPSolver.run()
print('mdptoolbox.mdp.PolicyIteration')
print('Política de acciones:')
for s in range(len(MDPSolver.policy)):
print(' Si estás en el estado {} haz {}'.format(SNombres[s],ANombres[MDPSolver.policy[s]]))
print('\nValor esperado según empieces en el estado:')
for s in range(len(MDPSolver.V)):
print(' {} valor esperado: {:6.4f}'.format(SNombres[s],MDPSolver.V[s]))
print('Valor esperado medio: {:6.4f}'.format(np.mean(MDPSolver.V)))
#%%Ejercicio 1
"""
Resuelve el proceso anterior usando mdptoolbox.mdp.QLearning
Salida
mdptoolbox.mdp.QLearning
Política de acciones:
Si estás en el estado II haz S
Si estás en el estado ID haz I
Si estás en el estado DI haz I
Si estás en el estado DD haz I
Valor esperado según empieces en el estado:
II valor esperado : 4.3428
ID valor esperado : 3.4548
DI valor esperado : 3.5670
DD valor esperado : 3.5427
Valor esperado medio: 3.7268
"""
#TODO Ejerccio 1
MDPSolver = mdptoolbox.mdp.QLearning(P, R, discount=0.9)
MDPSolver.run()
print('\n\nmdptoolbox.mdp.QLearning')
print('Política de acciones:')
for s in range(len(MDPSolver.policy)):
print(' Si estás en el estado {} haz {}'.format(SNombres[s],ANombres[MDPSolver.policy[s]]))
print('\nValor esperado según empieces en el estado:')
for s in range(len(MDPSolver.V)):
print(' {} valor esperado : {:6.4f}'.format(SNombres[s],MDPSolver.V[s]))
print('Valor esperado medio: {:6.4f}'.format(np.mean(MDPSolver.V)))
#<TODO END Ejerccio 1>
#%%Ejercicio 2
"""
Se quiere que haya un coste de 1 (recompensa de -1) cada vez que haya un movimiento
Se considera un movimiento cuando:
a) se ejecuta la acción izquierda y se pasa de la derecha a la izquierda
b) se ejecuta la acción derecha y se pasa de la izquierda a la derecha
Modifica solo los elementos necesarios del MDP (usa PolicyIteration)
Repite el proceso anterior con esta representación
P1 ¿Cambia la política? ¿Qué significa?
P2 ¿Cambian los valores? ¿Porqué?
SALIDA
Hay coste (recompensa negativa) al moverse
Política de acciones:
Si estás en el estado II haz S
Si estás en el estado ID haz I
Si estás en el estado DI haz D
Si estás en el estado DD haz S
Valor esperado según empieces en el estado:
II valor esperado : 4.4898
ID valor esperado : 3.6735
DI valor esperado : 3.6735
DD valor esperado : 4.4898
Valor esperado medio: 4.0816
"""
#TODO Ejercicio 2
# R de la acción I(zquierda)
# Estado destino: II ID DI DD
RI= [[ 0, 0, i, i], # Estado origen II
[ 0, 0, i, i], # Estado origen ID
[-1,-1, i, i], # Estado origen DI
[-1,-1, i, i]] # Estado origen DD
# R de la acción D(erecha)
# Estado destino: II ID DI DD
RD= [[ i, i,-1,-1], # Estado origen II
[ i, i,-1,-1], # Estado origen ID
[ i, i, 0, 0], # Estado origen DI
[ i, i, 0, 0]] # Estado origen DD
# RI= [[ 0, 0, i, i], # Estado origen II
# [ 0, 0, i, i], # Estado origen ID
# [ 0, 0, i, i], # Estado origen DI
# [ 0, 0, i, i]] # Estado origen DD
# # R de la acción D(erecha)
# # Estado destino: II ID DI DD
# RD= [[ i, i, 0, 0], # Estado origen II
# [ i, i, 0, 0], # Estado origen ID
# [ i, i, 0, 0], # Estado origen DI
# [ i, i, 0, 0]] # Estado origen DD
R=np.array([RI,RD,RS])
MDPSolver = mdptoolbox.mdp.PolicyIteration(P, R, discount=0.9)
# MDPSolver = mdptoolbox.mdp.QLearning(P, R, discount=0.9)
MDPSolver.run()
print('\n\nHay coste (recompensa negativa) al moverse')
print('Política de acciones:')
for s in range(len(MDPSolver.policy)):
print(' Si estás en el estado {} haz {}'.format(SNombres[s],ANombres[MDPSolver.policy[s]]))
print('\nValor esperado según empieces en el estado:')
for s in range(len(MDPSolver.V)):
print(' {} valor esperado : {:6.4f}'.format(SNombres[s],MDPSolver.V[s]))
print('Valor esperado medio: {:6.4f}'.format(np.mean(MDPSolver.V)))
# P1 ¿Cambia la política? ¿Qué significa?
# Sí, cambia. Sin coste de moMDPSolvermiento en el estado DI se mueve a la izquierda
# (cambia a estados II o ID). Con coste de moMDPSolvermiento si está en DI propone
# la acción D (se queda a la derecha)
# P2 ¿Cambian los valores? ¿Porqué?
# Los valores no cambian ya que cuando hay un coste este nunca se aplica ya que
#no se ejecuta ningún moMDPSolvermiento (si está a la izqueirda queda a la izquierda y
#si está a la derecha queda a la derecha)
#END Ejerccio 2
# -*- coding: utf-8 -*-
"""
Tema 6 : Procesos de decisión de Markov
Sesión 1 : Procesos de decisión de Markov
Actividad 1
"""
#%% Ejercicio 1
"""
En una competición de puntería hay 2 objetivos uno grande y otro pequeño.
En cada instante hay solo un objetivo disponible. La aparición de uno u otro
en cada instante es equiprobable.
Se puede disparar o esperar. Si se espera hay una penalización de un punto.
Si se dispara se puede acertar o fallar.
Si está el objetivo grande, la probabilidad de acertar es de 0.75 y la de fallar
de 0.25. Si se acierta se tiene una recompensa de 5 y si se falla una penalización
de 10.
Si está el objetivo pequeño, la probabilidad de acertar es de 0.25 y la de fallar
de 0.75. Si se acierta se tiene una recompensa de 10 y si se falla una penalización
de 5.
Tras fallar o acertar, independientemente de la acción realizada, se haría
disponible un objetivo (vuelve al inicio).
Plantear este problema como un proceso de decisión de Markov y calcular una
política utilizando el resolvedor PolicyIteration con un descuento de 0,99.
Calcular el valor esperado para cada estado y el valor medio.
PISTA: Se puede realizar con 4 estados:
G - Aparece un objetivo Grande
P - Aparece un objetivo Pequeño
F - Hay un Fallo
A - Hay un Acierto
"""
#%% Solución
import numpy as np
import mdptoolbox
#TODO Actividad 1
# Los objetivos pueden estar en dos estados: objetivo (G)rande, objetivo (P)equeño.
#Pero hay que conocer si se ha acertado o no, así que necesitamos otros dos estados:
# (A)cierto y (F)allo.
# Acciones hay 2 (Di)sparar o (Es)perar.
SNombres=['G','P','F','A']
ANombres= ['Es','Di']
i=0 # usado para marcar las probabilidades y reconpensas que no pueden realizarse
# Después de un (A)cierto i (F)allo, independienbtemente de la acción, se pasa
#al estado G o P con probabilidad 0.5
# Si se (Es)spera: se pasa al estado G o P con probabilidad 0.5
#Estado destino: G ,P F, A
PEs= [[0.5,0.5, i, i ],# Origen G
[0.5,0.5, i, i ],# Origen P
[0.5,0.5, i, i ],# Origen F
[0.5,0.5, i, i ]]# Origen A
# Si se (Di)spara:
#Estado destino: G ,P ,F ,A
PDi= [[i ,i ,0.25,0.75],# Origen G
[i ,i ,0.75,0.25],# Origen P
[0.5,0.5,i ,i ],# Origen F
[0.5,0.5,i ,i ]]# Origen A
P=np.array([PEs,PDi])
#%% Recompensas
# Desde los estados F o A nunca hay penalización ni reconpensa para ir al estado
#G o P inicial
# Si se (Es)spera tanto desde el estado G o P hay una penalización de 1
#Estado destino: G ,P , F, A
REs= [[-1,-1, i, i ],# Origen G
[-1,-1, i, i ],# Origen P
[ 0, 0, i, i ],# Origen F
[ 0, 0, i, i ]]# Origen A
# Si se (Di)spara:
#Estado destino: G ,P ,F ,A
RDi= [[i ,i ,-10, +5],# Origen G
[i ,i , -5,+10],# Origen P
[0 ,0 ,i ,i ],# Origen F
[0 ,0 ,i ,i ]]# Origen A
R=np.array([REs,RDi])
#%% Resolver
MDPSolver = mdptoolbox.mdp.PolicyIteration(P, R, discount=0.99)
MDPSolver.run()
print('Política de acciones:')
for s in range(len(MDPSolver.policy)):
print(' Si estás en el estado {} haz {}'.format(SNombres[s],ANombres[MDPSolver.policy[s]]))
print('\nValor esperado según empieces en el estado:')
for s in range(len(MDPSolver.V)):
print(' {} valor esperado: {:6.4f}'.format(SNombres[s],MDPSolver.V[s]))
print('Valor medio: {:6.4f}'.format(np.mean(MDPSolver.V)))
#END Actividad 1
# -*- coding: utf-8 -*-
"""
Tema 6 : Procesos de decisión de Markov
Sesión 1 : Procesos de decisión de Markov
Actividad 1.1 (Continuación de la 1)
"""
#%% Ejercicio 2: Racha
"""
En una competición de puntería hay 2 objetivos uno grande y otro pequeño.
En cada instante hay solo un objetivo disponible. La aparición de uno u otro
en cada instante es equiprobable.
Se puede disparar o esperar. Si se espera hay una penalización de un punto.
Si se dispara se puede acertar o fallar.
Si está el objetivo grande, la probabilidad de acertar es de 0.75 y la de fallar
de 0.25. Si se acierta se tiene una reconpensa de 5 y si se falla una penalización
de 10.
Si está el objetivo pequeño, la probabilidad de acertar es de 0.25 y la de fallar
de 0.75. Si se acierta se tiene una reconpensa de 10 y si se falla una penalización
de 5.
Tras fallar se haría disponible un objetivo (vuelve al inicio).
NUEVO:
Tras acertar se está en RACHA lo que significa que si se acertó un objetivo grande
vuelve a aparecer un objetivo grande y se acertó un objetivo pequeño vuelve a
aparecer el objetivo pequeño.
Plantear este problema como un proceso de decisión de Markov y calcular una
política utilizando el resolvedor PolicyIteration con un descuento de 0,99.
Calcular el valor esperado para cada estado y el valor medio.
PISTA: Se puede realizar con 5 estados:
G - Aparece un objetivo Grande
P - Aparece un objetivo Pequeño
F - Hay un Fallo
AG- Hay un Acierto del objetivo Grande
AP- Hay un Acierto del objetivo Pequeño
"""
#%% Solución
import numpy as np
import mdptoolbox
SNombres=['G','P','F','AP','AG']
ANombres= ['Es','Di']
#TODO Actividad 1
#%% Estados
# Los objetivos pueden estar en dos estados: objetivo (G)rande, objetivo (P)equeño.
#Pero hay que conocer si se ha acertado o no, así que necesitamos otros dos estados:
# (A)cierto y (F)allo.
# Acciones hay 2 (Di)sparar o (Es)perar.
i=0 # usado para marcar las probabilidades y reconpensas que no pueden realizarse
# Después de un (A)cierto i (F)allo, independienbtemente de la acción, se pasa
#al estado G o P con probabilidad 0.5
# Si se (Es)spera: se pasa al estado G o P con probabilidad 0.5
#Estado destino: G ,P F AP AG
PEs= [[0.5,0.5, i, i, i],# Origen G
[0.5,0.5, i, i, i ],# Origen P
[0.5,0.5, i, i, i ],# Origen F
[0.5,0.5, i, i, i ],# Origen AP
[0.5,0.5, i, i, i ]]# Origen AG
# Si se (Di)spara:
#Estado destino: G ,P ,F ,AP AG
PDi= [[i ,i ,0.25,i ,0.75],# Origen G
[i ,i ,0.75,0.25,i ],# Origen P
[0.5,0.5,i ,i ,i ],# Origen F
[i ,1 ,i ,i ,i ],# Origen AP
[1 ,i ,i ,i ,i ]]# Origen AG
P=np.array([PEs,PDi])
#%% Reconpensas
# Desde los estados F o A nunca hay penalización ni reconpensa para ir al estado
#G o P inicial
# Si se (Es)spera tanto desde el estado G o P hay una penalización de 1
#Estado destino: G ,P , F, AP AG
REs= [[-1,-1, i, i, i ],# Origen G
[-1,-1, i, i, i ],# Origen P
[ 0, 0, i, i, i ],# Origen F
[ 0, 0, i, i, i ],# Origen AP
[ 0, 0, i, i, i ]]# Origen AG
# Si se (Di)spara:
#Estado destino: G ,P ,F ,AP AG
RDi= [[i ,i ,-10,i , +5],# Origen G
[i ,i , -5,+10,i ],# Origen P
[0 ,0 ,i ,i ,i ],# Origen F
[i ,0 ,i ,i ,i ],# Origen AP
[0 ,i ,i ,i ,i ]]# Origen AG
R=np.array([REs,RDi])
#%% Resolver
MDPSolver = mdptoolbox.mdp.PolicyIteration(P, R, discount=0.99)
MDPSolver.run()
print('Política de acciones:')
for s in range(len(MDPSolver.policy)):
print(' Si estás en el estado {} haz {}'.format(SNombres[s],ANombres[MDPSolver.policy[s]]))
print('\nValor esperado según empieces en el estado:')
for s in range(len(MDPSolver.V)):
print(' {} valor esperado: {:6.4f}'.format(SNombres[s],MDPSolver.V[s]))
print('Valor medio: {:6.4f}'.format(np.mean(MDPSolver.V)))
#END Actividad 1
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo
"""
from RL_MDP.Estado import Estado
class EstadoVehiculo(Estado):
def __init__(self,nombre,cuartosGas,pinchado=True): # EjerTaller
Estado.__init__(self,nombre)
self.cuartosGas=cuartosGas
self.pinchado=pinchado # EjerTaller
def getCuartosGas(self):
"""
Retorna los cuartos de Gas que tiene un vehículo en este estado.
"""
return self.cuartosGas
def getPinchado(self): # EjerTaller
"""
Retorna si el aceite está usado o no
"""
return self.pinchado
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo
"""
from RL_MDP.Accion import Accion
class Ruta(Accion):
"""
Define la ruta (acción) que realiza un vehículo.
Se ha de difinir los cuartos de Gas que consume esta ruta.
Calcula la recompensa en función del estado del vehículo.
Clase padre abstracta para crear hijas redefiniendo getRecompensa
"""
def __init__(self,nombre,cuartosGas,penaNoGas,probPinchar=0,esTaller=False): # EjerTaller
"""
Parámetros:
- nombre : nombre de la ruta
- cuartosGas : Entero en [0;4] 0 cadena que será una expresión a evaluar.
La expresión puede utilizar RG objeto random.Random();
La expresión ha de retorna un entero en [0;4]
Cuartos de Gas que se consumen en esta ruta
- penaNoGas : Penalización (valor negativo) por intentar hacer esta ruta
y no tener combustible suficiente
- probPinchar: probabilida de pinchar. Un valor flotante en [0;1]. # EjerTaller
- esTaller : Indica si esta ruta es un Taller. Si es un taller cambia # EjerTaller
el estado de Pinchado a False y los cuartos a 0
"""
Accion.__init__(self,nombre)
self.cGas=cuartosGas
self.penaNoGas=penaNoGas
self.probPinchar=probPinchar # EjerTaller
self.Taller=esTaller # EjerTaller
def getCuartosGas(self,RG):
cGas=self.cGas
if type(cGas)==int: # Constant
return self.cGas
if type(cGas)==str: # Evaluation
return eval(cGas)
def esTaller(self): # EjerTaller
return self.Taller
def getEstadoNuevoRecompensa(self,estadoActual,listaEstados,RG):
"""
La acción cambia el estadoActual por el nuevoEstado
Parámetros:
- estadoActual : Estado actual. objeto hijo de Estado
- listaEstados : Lista con todos los estados del entorno
- RG : Generador de aleatoriedad. Objeto de clase random.Random()
Retorna: [estadoNuevo,recompensa]
- estadoNuevo : elemento de listaEstados
- recompensa : valor numérico con la recompensa tras haber ejecutado
la acción actual y pasar del estado estadoActual al estadoNuevo
"""
gasTiene =estadoActual.getCuartosGas() # Gas que tiene
pinchado =estadoActual.getPinchado() # Si hay una rueda pinchada o no # EjerTaller
gasNecesita =self.getCuartosGas(RG) # Gas que necesita para completar la ruta
cuartosTrasRuta=min([4,gasTiene-gasNecesita]) # Cálculo de los cuartos de Gas que quedan tras la ruta
esTaller =self.esTaller() # Si es una ruta a taller # EjerTaller
# Se queda sin Gas
if cuartosTrasRuta<0:
# Retorna el estado correspondiente a 0 cuartos de Gas junto con la penalización por qeudarse sin Gas
return [self._estadoCuartosPinchado(listaEstados,0,pinchado),self.penaNoGas] # entonces penalización
# Si es un taller # EjerTaller
if esTaller:
estadoTrasAccion =self._estadoCuartosPinchado(listaEstados,0,False)
else: # EjerTaller
# Comprobar si pincha o no en esta ruta
if self.probPinchar>=RG.random(): # Hay pinchazo
pinchado=True
estadoTrasAccion=self._estadoCuartosPinchado(listaEstados,cuartosTrasRuta,pinchado) # Calcula el estado tras la acción
recompensa =self.getRecompensa(RG,estadoActual,estadoTrasAccion) # Calcula Recompensa de esta acción
# Si se pincha se divide la recompensa entre 2
if pinchado: # EjerTaller
recompensa=recompensa/2
# Retorna el estado correspondiente a cuartosTrasRuta cuartos de Gas(estadoTrasAcccion)
#junto con la recompensa
return [estadoTrasAccion,recompensa]
def _estadoCuartosPinchado(self,listaEstados,cuartos,pinchado): # EjerTaller
"""
Retorna el estado de listaEstados que tiene los cuartos de Gas indicados
en cuartos y el valor de esPinchado() indicado por Pinchado
"""
for e in listaEstados:
if e.getCuartosGas()==cuartos and e.getPinchado()==pinchado:
return e
print('ERROR: No hay estados con "{}" cuartos de GAS y que pinchado sea {}'.formnat(cuartos,pinchado))
exit()
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
pass
class RutaUniforme(Ruta):
"""
Crea una ruta con una recompensa con distribución uniforme en el intervalo [a,b]
Es independiente del estado (solo un estado)
"""
def __init__(self,nombre,cuartosGas,penaNoGas,probPinchar,a,b):# EjerTaller
Ruta.__init__(self,nombre,cuartosGas,penaNoGas,probPinchar)# EjerTaller
self.a=a
self.b=b
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return RG.uniform(self.a,self.b)
class RutaNormal(Ruta):
"""
Crea una ruta con una recompensa con distribución normal de media m y distribución típica d
Es independiente del estado (solo un estado)
"""
def __init__(self,nombre,cuartosGas,penaNoGas,probPinchar,m,d):# EjerTaller
Ruta.__init__(self,nombre,cuartosGas,penaNoGas,probPinchar)# EjerTaller
self.m=m
self.d=d
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return RG.normalvariate(self.m,self.d)
class RutaGas(Ruta):
"""
Ruta a la Gasolimera
"""
def __init__(self,nombre):
Ruta.__init__(self,nombre,cuartosGas=-4,penaNoGas=None,probPinchar=0)# EjerTaller
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return 0
class RutaTaller(Ruta): # EjerTaller
"""
Ruta al Taller
"""
def __init__(self,nombre):
Ruta.__init__(self,nombre,cuartosGas=0,penaNoGas=None,probPinchar=0,esTaller=True)# EjerTaller
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return 0
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Tema 7 : Aprendizaje por refuerzo
Sesión 1 : Definición y ejecución de un RL
Bloque A : Definición y ejecución de un RL
"""
from Ruta import RutaUniforme,RutaNormal,RutaGas,RutaTaller # EjerTaller
from EstadoVehiculo import EstadoVehiculo
from RL_MDP.Entorno import Entorno
from RL_MDP.Agente import Agente
import matplotlib.pyplot as plt
import numpy as np
# Ejercicio 1
# Crear en un proyecto nuevo copia de este donde:
# El estado del vehículo, además, de los cuartos de Gas que tiene indicará si ha pinchado (Sí o No)
# Si hay pinchazo las recompensas por realizar rutas serán la mitad
# Crear una nueva ruta Taller que repare el pinchazo (pase pinchado de True a False) y deje a vehículo sin Gas
# Tendrá que haber un nuevo parámetro de Ruta que indique si es Taller o no
# Las rutas tendrán una probabilidad de que haya un pinchazo
#%% Crea una lista con 5 rutas más la (Gas)olinera y el taller # EjerTaller
penaNoGas=-1
Ru1=RutaUniforme('U0_10',cuartosGas=1 ,penaNoGas=penaNoGas,probPinchar=0.0,a=0,b=10)
Ru2=RutaUniforme('U5_9' ,cuartosGas='max([1,int(RG.normalvariate(1,1))])' ,penaNoGas=penaNoGas,probPinchar=0.1,a=5,b= 9)
Ru3=RutaNormal ('N5_2' ,cuartosGas=1 ,penaNoGas=penaNoGas,probPinchar=0.0,m=5,d= 2)
Ru4=RutaNormal ('N9_1' ,cuartosGas=2 ,penaNoGas=penaNoGas,probPinchar=0.2,m=9,d= 1)
Ru5=RutaNormal ('N6_2' ,cuartosGas='0 if RG.random()<0.1 else 1' ,penaNoGas=penaNoGas,probPinchar=0.1,m=6,d= 2)
RuG=RutaGas ('Gas')
RuT=RutaTaller ('Taller')
lRutas=[Ru1,Ru2,Ru3,Ru4,Ru5,RuG,RuT]
# Crea una lista con los estados un vehículo # EjerTaller
lEstadosVehiculo=[]
for c in range(4+1):
for p in [False,True]:
ev=EstadoVehiculo('C{}P{}'.format(c,0 if p==False else 1),cuartosGas=c,pinchado=p)
lEstadosVehiculo.append(ev)
# Crea el entorno
seedEntorno=1
Transporte=Entorno(listaAcciones=lRutas,listaEstados=lEstadosVehiculo,seed=seedEntorno)
#%% Crea un agente
seedAgente=1
Conductor=Agente(listaAcciones=Transporte.getListaAcciones(),
listaEstados =Transporte.getListaEstados() ,
seed=seedAgente,eps=0.1,epsFactor=1)
#%% Ejecuta el RL
Rs=[] # Recompensas de cada iteración
As=[] # Acciones de cada iteración
Iters=50000 # Número de iteraciones
s=Transporte.getEstadoInicial()
for i in range(Iters):
a=Conductor.decide(s) # Decide que acción hacer ante el estado s
[s,r]=Transporte.make(s,a) # El entorno responde ante la acción a en el esatado s
#y retorna el nuevo estado y la recompensa
Conductor.setRecompensa(r,s) #indicando al agente la recompensa obtenida y el
#estado al que llega(feedback) el agente se actualiza
# Almacenar la acción y la recompensa en cada iteración
As.append(a)
Rs.append(r)
# Prompt
if i%1000==0:
print('.',end='')
print('Resultados del RL:')
Conductor.printP() # Imprime cubo de probabilidades
RMedia=np.mean(Rs) # Media de recompensas
print('- Iteración {} recompensa media: {:6.4f}'.format(Iters,RMedia))
# Política obtenida
print('- Política:')
MDP=Conductor.getMDP()
lAcciones=Transporte.getListaAcciones() # Lista de acciones (para poder nombrarlas)
for s in range(len(MDP.policy)):
print(' * Si estás en el estado {} haz {}'.format(lEstadosVehiculo[s].getNombre(),lAcciones[MDP.policy[s]].getNombre()))
#%% Recompensa media en cada iteración
RMediaItera=[]
RSumaItera=0
for i in range(len(Rs)):
RSumaItera=RSumaItera+Rs[i]
RMediaItera.append(RSumaItera/(i+1))
Iter=list(range(len(Rs))) # Lista de iteraciones
plt.figure()
plt.plot(Iter,RMediaItera)
# plt.ylim((0,9))
plt.grid()
plt.title('RL_MDP')
plt.xlabel('Iteración')
plt.ylabel('Recompensa media')
#%% Proporción de acciones ejecutadas en cada iteración
Iter=list(range(len(Rs))) # Lista de iteraciones
plt.figure()
for a in range(Transporte.getNAcciones()):
aSuma=0
aMediaItera=[]
for i in range(len(As)):
if a==As[i].getIndex():
aSuma=aSuma+1
aMediaItera.append(aSuma/(i+1))
plt.plot(Iter,aMediaItera,label=lAcciones[a].getNombre())
plt.legend()
plt.grid()
plt.title('RL_MDP')
plt.xlabel('Iteración')
plt.ylabel('Proporción de la acción en cada iteración')
# Ejercicio 2
# Añade el cansancio a RL_MDP_RutaTaller
# Cada vez que se realiza una ruta se incrementa el cansancio.
# Cada Ruta permite definir cuanto aumenta el cansancio con un valor entero en [0,3]
# El valor máximo es 3
# Hay una nueva Ruta Descanso que pone el cansancio a 0
# Al hacer una ruta (excepto Gasolinera, Taller y Descanso) la recompensa se
# multiplica por 3/(3+cansancio)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Ejercicio 1
"""
#%% Datos
from simpleGen import simpleGen
import random
import warnings
warnings.simplefilter("ignore")
# Parámetros del conjunto de ejemplos
nejemplos=50 # Número de ejemplos no etiquetados
atrNoRel=2 # Nº de atributos no relevantes
atrRel=2 # Nº de atributos relevantes
nAtr=atrRel+atrNoRel # Nº de atributos total
# Crear el conjunto de ejemplos
RG=random.Random() # Generador de números aleatorios
RG.seed(1) # Semilla del generador de números aleatorios
sd=1
simple=simpleGen(sd=sd,atrRel=atrRel,atrNoRel=atrNoRel)
X=simple(RG,nejemplos) # Generar los ejemplos no etiquetados
#%% Enunciado
""" Ejercicio 1 (Este ejercicio es algo más corto que uno típico de examen)
Se quieren crear clústers del conjunto X, luego calcular el cluster para el ejemplo zero
e imprimir el índice (número) del mismo
Utilizar dos algoritmos diferentes e indicar cual es mejor.
"""
zero=[0,0,0,0]
import numpy as np
from sklearn.cluster import MeanShift, KMeans
from sklearn.metrics import silhouette_score
# ************
# Utilizo KMeans
SC=KMeans()
SC.fit(X)
clusters=SC.predict(X)
ic=SC.predict([zero])
print('KMeans: índice de zero',ic[0])
KMss=silhouette_score(X,clusters)
# # ************
# # Utilizo MeanShift
SC=MeanShift()
SC.fit(X)
clusters=SC.predict(X)
ic=SC.predict([zero])
print('MeanShift: índice de zero',ic[0])
n_clusters=len(np.unique(clusters))
if n_clusters>1 and n_clusters<len(X):
MSss=silhouette_score(X,clusters)
else:
MSss=-np.inf
if KMss>MSss:
print('Mejor KMeans')
else:
print('Mejor MeanShift')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Ejercicio 2
"""
#%% Datos
from PaquetesInversion import PaqueteInversionPasado,PaquetesInversionFuturos
[X,Y]=PaqueteInversionPasado()
[X1,X2,X3,X4,X5]=PaquetesInversionFuturos()
#%% Enunciado
""" Ejercicio 2: inversión
Se tiene información de un paquete de inversiones(X,Y) donde cada inversión se
califica como buena(valor 1 en Y) o mala(valor 0 en Y).
Tiene 4 atributos correspondientes a información financiera de las mismas:
Nivel de riesgo
Liquidez
Valor liquidativo
Volatilidad histórica
Las inversiones buenas dan un beneficio de +5 y las malas de -3.
See tienen 5 paquetes de inversión de futuro: [X1,X2,X3,X4,X5].
Se han de contratar los paquetes enteros, y el objetivo es elegir cual es el mejor paquete
y cuánto beneficio se obtiene.
Hay que comparar dos métodos de elegir el mejor paquete.
El software elegirá el mejor
Utilizando el mejor se decidirá cuál es el mejor paquete de inversión.
Se imprimirá el nombre del mejor paquete (X1,...) y el beneficio estimado
"""
#%% EJER 2
import numpy as np
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from quantificationlib.baselines.cc import CC
from quantificationlib.baselines.ac import AC
from quantificationlib.bag_generator import PriorShift_BagGenerator
from QUtils import dataSetFromQBags
# Dividirlo en entrenamiento y test
[XTr,XTe,YTr,YTe]=train_test_split(X,Y,test_size=1/3,random_state=1,stratify=Y)
p=np.mean(YTr) #Qué hace: Calcula la media de las etiquetas de entrenamiento (YTr). Como las etiquetas son binarias ($0$ y $1$), la media representa exactamente la proporción de casos positivos (prevalencia) originales. Luego la imprime en pantalla
print('Prevalencia entrenamiento: {:6.4f}'.format(p))
# Clasificador (Común para ámbos métodos)
Class=SVC(kernel='linear',C=1,probability=True) # Instancia un clasificador SVM con kernel lineal. El parámetro probability=True es indispensable porque el método de cuantificación AC requerirá las probabilidades predichas de pertenencia a la clase, no solo la etiqueta final.
# *******************
# Método AC
Q=AC(estimator_train=Class, estimator_test=Class)
Q.fit(XTr,YTr)
l1s=[]
# Crear diferentes conjuntos de test con distinta prevalencia
n_bags=100
BG=PriorShift_BagGenerator(n_bags=n_bags,random_state=1)
bags=BG.generate_bags(XTe,YTe) #Inicializa una lista vacía l1s para guardar los errores. Define que creará 100 muestras de prueba ficticias (n_bags)
#generadas mediante Prior Shift (desplazamiento de la probabilidad a priori). Esto significa que el generador tomará los datos de test originales (XTe, YTe) y construirá 100 subconjuntos ("bolsas"), cada uno forzado a tener una mezcla/prevalencia de positivos radicalmente diferente (desde $0\%$ hasta $100\%$).
for i in range(n_bags):
[Xb,Yb]=dataSetFromQBags(XTe,YTe,bags,i)
p=np.mean(Yb)
pres=Q.predict(Xb)
pe=pres[np.argwhere(Q.classes_==1)[0][0]] # Predicted prevalence
l1=abs(pe-p)
#print('Prevalencia test:{:6.4f} AC:{:6.4f} l1={:6.4f}'.format(p,pe,l1))
l1s.append(l1)
ACl1=np.mean(l1s)
ACQ=Q
print('AC , l1 medio:{:6.4f}'.format(ACl1))
# *******************
# Método CC
Q=CC( estimator_test=Class)
Q.fit(XTr,YTr)
l1s=[]
# Crear diferentes conjuntos de test con distinta prevalencia
n_bags=100
BG=PriorShift_BagGenerator(n_bags=n_bags,random_state=1)
bags=BG.generate_bags(XTe,YTe)
for i in range(n_bags):
[Xb,Yb]=dataSetFromQBags(XTe,YTe,bags,i)
p=np.mean(Yb)
pres=Q.predict(Xb)
pe=pres[np.argwhere(Q.classes_==1)[0][0]] # Predicted prevalence
l1=abs(pe-p)
#print('Prevalencia test:{:6.4f} AC:{:6.4f} l1={:6.4f}'.format(p,pe,l1))
l1s.append(l1)
CCl1=np.mean(l1s)
CCQ=Q
print('CC , l1 medio:{:6.4f}'.format(CCl1))
# *******************
# Elegir el mejor Modelo
if ACl1<CCl1:
Q=ACQ
print('Mejor modelo AC')
else:
Q=CCQ
print('Mejor modelo CC')
#Compara los errores medios de ambos métodos (ACl1 y CCl1).
# Aquel método que tenga el error medio más bajo (más cercano a 0) es asignado a la variable genérica Q como el modelo definitivo de producción, e imprime cuál fue el ganador.
# *******************
# El mejor paquete será el que tena más prevalencia tenga (más inversiones buenas)
# Buscar en [X1,X2,X3,X4,X5] el que tenga más prevalencia
mejorBene=-np.inf
mejorIndex=None
listX=[X1,X2,X3,X4,X5] #Prepara la optimización. Inicializa el mejor beneficio en menos infinito (-np.inf), el índice ganador en None, y agrupa en una lista listX los 5 lotes de datos sin etiquetar.
for i in range(5):
# Calcular la prevalencia de X_i
XTe=listX[i]
p=Q.predict(XTe)
p1=p[np.argwhere(Q.classes_==1)[0][0]]
#Toma el paquete actual listX[i], usa el cuantificador ganador Q para estimar la prevalencia en este lote desconocido y almacena la proporción estimada de inversiones buenas en la variable p1
# Calcular cuantos ejemplos son positivos y cuántos negativos
nEje=len(XTe)
nPos=nEje*p1
nNeg=nEje*(1-p1)
# Calcular el beneficio
bene=5*nPos-3*nNeg
# Quedarse con el mejor
if bene>mejorBene:
mejorBene=bene
mejorIndex=i
# Imporimir el mejor
print('Mejor paquete: X{}, beneficio={:6.2f}'.format(mejorIndex+1,mejorBene))
#END EJER 2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: quevedo@uniovi.es
"""
import numpy as np
from RL_MDP.Accion import Accion
class Ruta(Accion):
"""
Define la ruta (acción) que realiza un vehículo.
Se ha de difinir los cuartos de Gas que consume esta ruta.
Calcula la recompensa en función del estado del vehículo.
Clase padre abstracta para crear hijas redefiniendo getRecompensa
"""
def __init__(self,nombre,cuartosGas,penaNoGas,probPinchar=0,esTaller=False,modificaAnimo=0): # EjerTaller
"""
Parámetros:
- nombre : nombre de la ruta
- cuartosGas : Entero en [0;4] 0 cadena que será una expresión a evaluar.
La expresión puede utilizar RG objeto random.Random();
La expresión ha de retorna un entero en [0;4]
Cuartos de Gas que se consumen en esta ruta
- penaNoGas : Penalización (valor negativo) por intentar hacer esta ruta
y no tener combustible suficiente
- probPinchar: probabilida de pinchar. Un valor flotante en [0;1]. # EjerTaller
- esTaller : Indica si esta ruta es un Taller. Si es un taller cambia # EjerTaller
el estado de Pinchado a False y los cuartos a 0
- modificaAnimo : valor numérico, negativo(tiende a emperar el ánimo)
o positivo (tiende a mejorar el ánimo)
"""
Accion.__init__(self,nombre)
self.cGas=cuartosGas
self.penaNoGas=penaNoGas
self.probPinchar=probPinchar # EjerTaller
self.Taller=esTaller # EjerTaller
self.modificaAnimo=modificaAnimo
def getCuartosGas(self,RG):
cGas=self.cGas
if type(cGas)==int: # Constant
return self.cGas
if type(cGas)==str: # Evaluation
return eval(cGas)
def esTaller(self): # EjerTaller
return self.Taller
def getEstadoNuevoRecompensa(self,estadoActual,listaEstados,RG):
"""
La acción cambia el estadoActual por el nuevoEstado
Parámetros:
- estadoActual : Estado actual. objeto hijo de Estado
- listaEstados : Lista con todos los estados del entorno
- RG : Generador de aleatoriedad. Objeto de clase random.Random()
Retorna: [estadoNuevo,recompensa]
- estadoNuevo : elemento de listaEstados
- recompensa : valor numérico con la recompensa tras haber ejecutado
la acción actual y pasar del estado estadoActual al estadoNuevo
"""
gasTiene =estadoActual.getCuartosGas() # Gas que tiene
pinchado =estadoActual.getPinchado() # Si hay una rueda pinchada o no # EjerTaller
gasNecesita =self.getCuartosGas(RG) # Gas que necesita para completar la ruta
cuartosTrasRuta=min([4,gasTiene-gasNecesita]) # Cálculo de los cuartos de Gas que quedan tras la ruta
esTaller =self.esTaller() # Si es una ruta a taller # EjerTaller
animo =estadoActual.getAnimo()
modificaAnimo =self.modificaAnimo
animoTrasRuta =int(np.sign(animo+modificaAnimo))
# Se queda sin Gas
if cuartosTrasRuta<0:
# Retorna el estado correspondiente a 0 cuartos de Gas junto con la penalización por qeudarse sin Gas
return [self._estadoCuartosPinchadoAnimo(listaEstados,0,pinchado,animoTrasRuta),self.penaNoGas] # entonces penalización
# Si es un taller # EjerTaller
if esTaller:
estadoTrasAccion =self._estadoCuartosPinchadoAnimo(listaEstados,0,False,animoTrasRuta)
else: # EjerTaller
# Comprobar si pincha o no en esta ruta
if self.probPinchar>=RG.random(): # Hay pinchazo
pinchado=True
estadoTrasAccion=self._estadoCuartosPinchadoAnimo(listaEstados,cuartosTrasRuta,pinchado,animoTrasRuta) # Calcula el estado tras la acción
recompensa =self.getRecompensa(RG,estadoActual,estadoTrasAccion) # Calcula Recompensa de esta acción
# Si se pincha se divide la recompensa entre 2
if pinchado: # EjerTaller
recompensa=recompensa/2
if animoTrasRuta==-1:
recompensa=recompensa/2
elif animoTrasRuta==+1:
recompensa=recompensa*2
# Retorna el estado correspondiente a cuartosTrasRuta cuartos de Gas(estadoTrasAcccion)
#junto con la recompensa
return [estadoTrasAccion,recompensa]
def _estadoCuartosPinchadoAnimo(self,listaEstados,cuartos,pinchado,animo): # EjerTaller
"""
Retorna el estado de listaEstados que tiene los cuartos de Gas indicados
en cuartos y el valor de esPinchado() indicado por Pinchado
"""
for e in listaEstados:
if e.getCuartosGas()==cuartos and e.getPinchado()==pinchado and e.getAnimo()==animo:
return e
print('ERROR: No hay estados con "{}" cuartos de GAS y que pinchado sea {}'.formnat(cuartos,pinchado))
exit()
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
pass
class RutaUniforme(Ruta):
"""
Crea una ruta con una recompensa con distribución uniforme en el intervalo [a,b]
Es independiente del estado (solo un estado)
"""
def __init__(self,nombre,cuartosGas,penaNoGas,probPinchar,modificaAnimo,a,b):# EjerTaller
Ruta.__init__(self,nombre,cuartosGas,penaNoGas,probPinchar,modificaAnimo=modificaAnimo)# EjerTaller
self.a=a
self.b=b
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return RG.uniform(self.a,self.b)
class RutaNormal(Ruta):
"""
Crea una ruta con una recompensa con distribución normal de media m y distribución típica d
Es independiente del estado (solo un estado)
"""
def __init__(self,nombre,cuartosGas,penaNoGas,probPinchar,modificaAnimo,m,d):# EjerTaller
Ruta.__init__(self,nombre,cuartosGas,penaNoGas,probPinchar,modificaAnimo=modificaAnimo)# EjerTaller
self.m=m
self.d=d
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return RG.normalvariate(self.m,self.d)
class RutaGas(Ruta):
"""
Ruta a la Gasolimera
"""
def __init__(self,nombre,modificaAnimo):
Ruta.__init__(self,nombre,cuartosGas=-4,penaNoGas=None,probPinchar=0,modificaAnimo=modificaAnimo)# EjerTaller
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return 0
class RutaTaller(Ruta): # EjerTaller
"""
Ruta al Taller
"""
def __init__(self,nombre,modificaAnimo):
Ruta.__init__(self,nombre,cuartosGas=0,penaNoGas=None,probPinchar=0,esTaller=True,modificaAnimo=modificaAnimo)# EjerTaller
def getRecompensa(self,RG,estadoActual,estadoTrasAcccion):
return 0