Commit 0e1c7d2b authored by Bruno López Trigo's avatar Bruno López Trigo

Engadida documentación actualizada

parent 7ab07ed7
......@@ -31,6 +31,11 @@ import weka.core.OptionHandler;
import weka.core.Utils;
import weka.estimators.EstimatorUtils;
/*
*
* Implementación del módulo constructor
*
*/
public class BuilderManagerImpl implements BuilderManager {
private final MapperJSON mapper;
......@@ -41,6 +46,12 @@ public class BuilderManagerImpl implements BuilderManager {
this.fmanager = new FileManager();
}
/*
*
* Construye el modelo de clasificación de WEKA, adicionalmente genera los
* archivos de configuración del dataset.
*
*/
@Override
public DatasetConfig buildModels(String token, String datasetName, String model, String[] options)
throws ConflictEx, FormatEx, NotFoundEx {
......@@ -56,6 +67,7 @@ public class BuilderManagerImpl implements BuilderManager {
}
try {
// Leer el dataset de entrenamiento y construir instancias
readerLRN = new FileReader(dataset.getAbsolutePath());
Instances instancesLRN = new Instances(readerLRN);
instancesLRN.setClassIndex(instancesLRN.numAttributes() - 1);
......@@ -68,6 +80,12 @@ public class BuilderManagerImpl implements BuilderManager {
PrintStream log;
PrintStream predictions;
/*
*
* Construír el modelo de clasificación con WEKA con las opciones indicadas por
* el usuario
*
*/
switch (model) {
case "J48":
c45model = new J48();
......@@ -137,40 +155,54 @@ public class BuilderManagerImpl implements BuilderManager {
double[] minMax = new double[2];
double distance;
if (instancesLRN.numAttributes() < 100) {
for (int i = 0; i < instancesLRN.numAttributes(); i++) {
log.println(" " + instancesLRN.attribute(i).name());
if (i != (instancesLRN.numAttributes() - 1)) {
if (instancesLRN.attribute(i).isNumeric()) {
config.getAttributes().add(new NumericAttribute(instancesLRN.attribute(i).name(),
instancesLRN.attribute(i).name()));
EstimatorUtils.getMinMax(instancesLRN, i, minMax);
distance = (minMax[1] - minMax[0]) / 3;
((NumericAttribute) config.getAttributes().get(i))
.setInterval(new Interval(minMax[0], minMax[1]));
config.getAttributes().get(i).getProperties()
.add(new NumericProperty("Low", new Interval(minMax[0], minMax[0] + distance), 0));
config.getAttributes().get(i).getProperties().add(new NumericProperty("Medium",
new Interval(minMax[0] + distance, minMax[0] + 2 * distance), 1));
config.getAttributes().get(i).getProperties().add(
new NumericProperty("High", new Interval(minMax[0] + 2 * distance, minMax[1]), 2));
} else {
config.getAttributes().add(new CategoricAttribute(instancesLRN.attribute(i).name(),
instancesLRN.attribute(i).name()));
for (int j = 0; j < instancesLRN.attribute(i).numValues(); j++)
config.getAttributes().get(i).getProperties().add(new CategoricProperty(
instancesLRN.attribute(i).value(j), instancesLRN.attribute(i).value(j)));
}
// Genera la configuración del experto
for (int i = 0; i < instancesLRN.numAttributes(); i++) {
log.println(" " + instancesLRN.attribute(i).name());
if (i != (instancesLRN.numAttributes() - 1)) {
/*
*
* Para el caso de los atributos numéricos dividir el atributo en 3 intervalos
* idénticos con etiquetas (Low, Medium, High)
*
*/
if (instancesLRN.attribute(i).isNumeric()) {
config.getAttributes().add(new NumericAttribute(instancesLRN.attribute(i).name(),
instancesLRN.attribute(i).name()));
EstimatorUtils.getMinMax(instancesLRN, i, minMax);
distance = (minMax[1] - minMax[0]) / 3;
((NumericAttribute) config.getAttributes().get(i))
.setInterval(new Interval(minMax[0], minMax[1]));
config.getAttributes().get(i).getProperties()
.add(new NumericProperty("Low", new Interval(minMax[0], minMax[0] + distance), 0));
config.getAttributes().get(i).getProperties().add(new NumericProperty("Medium",
new Interval(minMax[0] + distance, minMax[0] + 2 * distance), 1));
config.getAttributes().get(i).getProperties()
.add(new NumericProperty("High", new Interval(minMax[0] + 2 * distance, minMax[1]), 2));
}
/*
*
* Para el caso de los atributos categóricos las etiquetas son los posibles valores
* del atributo
*
*/
else {
config.getAttributes().add(new CategoricAttribute(instancesLRN.attribute(i).name(),
instancesLRN.attribute(i).name()));
for (int j = 0; j < instancesLRN.attribute(i).numValues(); j++)
config.getAttributes().get(i).getProperties().add(new CategoricProperty(
instancesLRN.attribute(i).value(j), instancesLRN.attribute(i).value(j)));
}
}
} else {
log.println(" [list of attributes omitted]");
}
// Añadir a la configuración los consecuentes o clases del problema
for (int i = 0; i < instancesLRN.classAttribute().numValues(); i++) {
config.getConsequents().add(new Consequent((i + 1), instancesLRN.classAttribute().value(i),
......@@ -201,6 +233,12 @@ public class BuilderManagerImpl implements BuilderManager {
Evaluation evalLRN = new Evaluation(instancesLRN, null);
/*
*
* Evaluar el modelo usando un 10 cross-validation por defecto
*
*/
switch (model) {
case "J48":
evalLRN.crossValidateModel(c45model, instancesLRN, 10, new Random(1));
......@@ -221,6 +259,7 @@ public class BuilderManagerImpl implements BuilderManager {
File configLocation;
// Si la configuración no existe se lanza la excepción y se crea la nueva
try {
configLocation = this.fmanager.getConfig(token, datasetName, "en");
config = this.mapper.readConfigJSON(configLocation);
......@@ -232,6 +271,11 @@ public class BuilderManagerImpl implements BuilderManager {
evalLRN = new Evaluation(instancesLRN);
/*
*
* Evaluar el modelo con las instancias de entrenamiento
*
*/
switch (model) {
case "J48":
evalLRN.evaluateModel(c45model, instancesLRN, (Object[]) new String[1]);
......@@ -247,6 +291,7 @@ public class BuilderManagerImpl implements BuilderManager {
break;
}
// Escribir las predicciones en el archivo de predicciones
for (Prediction p : evalLRN.predictions()) {
predictions.println(p.actual() + " " + p.predicted());
}
......@@ -264,44 +309,61 @@ public class BuilderManagerImpl implements BuilderManager {
return config;
}
/*
*
* Construye el modelo de clasificación de WEKA para FURIA para clasificar una
* instancia concreta. Este método se utiliza para clasificar cuando se produce
* stretching, vote for the most frequent class o abstain.
*
*/
@Override
public double classifyFURIA(String token, String datasetName, String[] options, brunolopez.expliclas.models.Instance instance)
throws FormatEx, NotFoundEx {
public double classifyFURIA(String token, String datasetName, String[] options,
brunolopez.expliclas.models.Instance instance) throws FormatEx, NotFoundEx {
FileReader readerLRN;
File dataset = this.fmanager.getDataset(token, datasetName);
double value = -1;
try {
// Leemos el dataset de entrenamiento para generar las instancias
readerLRN = new FileReader(dataset.getAbsolutePath());
Instances instancesLRN = new Instances(readerLRN);
instancesLRN.setClassIndex(instancesLRN.numAttributes() - 1);
FURIA furia = null;
// Construimos el modelo de WEKA para FURIA
furia = new FURIA();
furia.setOptions(options);
furia.buildClassifier(instancesLRN);
// Generamos una nueva instancia
Instance wekaInstance = new DenseInstance(instancesLRN.instance(0).numAttributes());
for(int i=0; i<(instancesLRN.instance(0).numAttributes() - 1); i++) {
if(instancesLRN.instance(0).attribute(i).isString() || instancesLRN.instance(0).attribute(i).isNominal()) {
wekaInstance.setValue(instancesLRN.instance(0).attribute(i), instance.getCategoricValue(instancesLRN.instance(0).attribute(i).name()));
// Añadimos a la nueva instancia los valores que queremos clasificar
for (int i = 0; i < (instancesLRN.instance(0).numAttributes() - 1); i++) {
if (instancesLRN.instance(0).attribute(i).isString()
|| instancesLRN.instance(0).attribute(i).isNominal()) {
wekaInstance.setValue(instancesLRN.instance(0).attribute(i),
instance.getCategoricValue(instancesLRN.instance(0).attribute(i).name()));
} else {
wekaInstance.setValue(instancesLRN.instance(0).attribute(i), instance.getNumericValue(instancesLRN.instance(0).attribute(i).name()));
wekaInstance.setValue(instancesLRN.instance(0).attribute(i),
instance.getNumericValue(instancesLRN.instance(0).attribute(i).name()));
}
}
// Especificamos que la instancia es del mismo tipo que las instancias de entrenamiento
wekaInstance.setDataset(instancesLRN);
// Especificamos una clase cualquiera de salida (en este caso la primera)
wekaInstance.setClassValue(instancesLRN.instance(0).classValue());
// Realizamos la clasificación
value = furia.classifyInstance(wekaInstance);
} catch (Exception ex) {
throw new FormatEx("Error building log, check format");
}
return value;
}
}
......@@ -7,8 +7,6 @@ import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import brunolopez.expliclas.builder.BuilderManager;
import brunolopez.expliclas.builder.BuilderManagerImpl;
import brunolopez.expliclas.exceptions.FormatEx;
import brunolopez.expliclas.exceptions.NotFoundEx;
import brunolopez.expliclas.models.DatasetConfig;
......@@ -17,18 +15,27 @@ import brunolopez.expliclas.models.Position;
import brunolopez.expliclas.utils.FileManager;
import brunolopez.expliclas.utils.MapperJSON;
/*
*
* Clase constructora de matrices de confusión
*
*/
public class MatrixBuilder {
private BufferedReader reader;
private final FileManager fmanager;
private final BuilderManager builder;
public MatrixBuilder(File input) throws FileNotFoundException {
this.reader = new BufferedReader(new FileReader(input));
this.fmanager = new FileManager();
this.builder = new BuilderManagerImpl();
}
/*
*
* Construcción de la matriz de cross-validation, se limita a la lectura
* del modelo de WEKA
*
*/
public Matrix readMatrix() throws IOException {
String line = "";
......@@ -70,6 +77,12 @@ public class MatrixBuilder {
}
/*
*
* Construcción de la matriz de confusión de entrenamiento y test, requiere de la lectura
* de los archivos de predicciones
*
*/
public Matrix buildMatrixInstances(String token, String name, String algorithm, String type, boolean abstain) throws NotFoundEx, IOException, FormatEx {
File predictions;
......@@ -82,26 +95,32 @@ public class MatrixBuilder {
configFile = this.fmanager.getConfig(token, name, "en");
DatasetConfig config = mapper.readConfigJSON(configFile);
// Se obtienen las predicciones
if (type.equals("train")) {
predictions = this.fmanager.getPredictions(token, name, algorithm);
} else {
predictions = this.fmanager.getPredictionsTest(token, name, algorithm);
}
// Se crea una matriz, indicando se tiene columna de abstenciones o no
m = new Matrix(config.getConsequents().size(), abstain);
this.reader = new BufferedReader(new FileReader(predictions));
String line = reader.readLine();
int instance = 1;
while (line != null) {
// Si la predicción es "NaN" entonces hay una abstención
if(line.split(" ")[1].equals("NaN")) {
p = new Position((int) Float.parseFloat(line.split(" ")[0]), config.getConsequents().size());
} else {
p = new Position((int) Float.parseFloat(line.split(" ")[0]), (int) Float.parseFloat(line.split(" ")[1]));
}
// Si la predicción es incorrecta se añade a la lista de instancias confundidas la instancia
if (p.getRow() != p.getColumn()) {
m.addConfused(p, instance);
}
// Se incrementa en la matriz la posición correspondiente a la predicción
m.increment(p.getRow(), p.getColumn());
line = reader.readLine();
instance++;
......
......@@ -25,19 +25,22 @@ import brunolopez.expliclas.models.trees.NumericAttribute;
import brunolopez.expliclas.utils.FileManager;
import brunolopez.expliclas.utils.MapperJSON;
/*
*
* Clase constructora de reglas borrosas
*
*/
public class RuleBuilder {
private Scanner sc;
private File log;
private File config;
private File output;
private MapperJSON mapper;
private FileManager fmanager;
public RuleBuilder(File log, File config, File output) throws NotFoundEx {
this.log = log;
this.config = config;
this.output = output;
this.mapper = new MapperJSON();
this.fmanager = new FileManager();
......@@ -51,6 +54,7 @@ public class RuleBuilder {
public RuleBuilder() {
}
// Construcción de reglas
public VisualRules buildRules(String token, String dataset, String algorithm) throws IOException {
String line = this.sc.nextLine();
......@@ -64,6 +68,14 @@ public class RuleBuilder {
Rules rules = new Rules();
rules.setOptions(options);
/*
*
* Detección de opciones importantes:
*
* -p: para determinar si la TNorm es producto o mínimo
* -s: para determinar la acción cuando no se activan reglas.
*
*/
for (int i = 0; i < options.length; i++) {
if (options[i].equals("-p")) {
rules.setProduct(options[i + 1].equals("0"));
......@@ -93,8 +105,29 @@ public class RuleBuilder {
DatasetConfig datasetConfig = this.mapper.readConfigJSON(this.config);
/*
*
* Expresión regular de intervalo numérico
*
* Grupo 1: atributo - Grupo 2: valores
*
*/
Pattern numericInterval = Pattern.compile("^\\((.*)\\sin\\s\\[(.*)\\]\\)$");
/*
*
* Expresión regular de intervalo categórico
*
* Grupo 1: atributo - Grupo 2: valor
*
*/
Pattern categoricInterval = Pattern.compile("^\\((.*)\\s=\\s(.*)\\)$");
/*
*
* Expresión regular de intervalo categórico
*
* Grupo 1: consecuente - Grupo 2: CF
*
*/
Pattern outputClass = Pattern.compile("^.*=(.*)\\s\\(CF\\s=\\s(.*)\\)$");
Matcher numericMatch, categoricMatch, outputMatch;
......@@ -102,7 +135,7 @@ public class RuleBuilder {
String[] intervals;
String interval = "", output = "";
ArrayList<Rule> list = new ArrayList();
ArrayList<Rule> list = new ArrayList<Rule>();
String[] values;
Rule rule;
RuleComponent component, aux;
......@@ -111,6 +144,10 @@ public class RuleBuilder {
rule = new Rule();
intervals = line.split(" and ");
for (int i = 0; i < intervals.length; i++) {
/*
* Si el intervalo contiene " => " estamos en el último
* por lo tanto separamos el intervalo de la clase de salida
*/
if (intervals[i].contains(" => ")) {
interval = intervals[i].split(" => ")[0];
output = intervals[i].split(" => ")[1];
......@@ -121,10 +158,19 @@ public class RuleBuilder {
categoricMatch = categoricInterval.matcher(interval);
outputMatch = outputClass.matcher(output);
// Para el intervalo numérico
if (numericMatch.matches()) {
// ¿Ya existe un componente para este atributo en la regla?