Commit b91df293 authored by Bruno López Trigo's avatar Bruno López Trigo

Versión preliminar de demostrador casi lista para desplegar

parent b2729a30
......@@ -135,6 +135,7 @@ public class TreeInterpreter {
double splitValue, supMargin, infMargin;
Classification classification = new Classification();
HashMap<String, Boolean> modified = new HashMap();
HashMap<String, Double> splitValues = new HashMap();
ArrayList<HashMap<String, Boolean>> modifiedList = new ArrayList();
ArrayList<String> solutions = new ArrayList();
......@@ -151,6 +152,11 @@ public class TreeInterpreter {
ArrayList<Classification> classifications = new ArrayList();
while (!alternatives.isEmpty()) {
for(Map.Entry<String, Double> entry: splitValues.entrySet()){
if((double) entry.getValue() != alternatives.get(0).getNumericValue(entry.getKey())){
classification.addSplitValue(entry.getKey(), entry.getValue());
}
}
while (!(path.get(path.size() - 1) instanceof ConsequentNode)) {
if (path.get(path.size() - 1) instanceof NumericNode) {
att = ((NumericNode) path.get(path.size() - 1)).getAttribute();
......@@ -170,7 +176,8 @@ public class TreeInterpreter {
}
supMargin = alternatives.get(0).getNumericValue(att.getId()) + alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && supMargin > splitValue) {
auxInstance = new Instance(instance);
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), supMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
......@@ -191,7 +198,8 @@ public class TreeInterpreter {
}
infMargin = alternatives.get(0).getNumericValue(att.getId()) - alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && infMargin <= splitValue) {
auxInstance = new Instance(instance);
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), infMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
......@@ -214,7 +222,8 @@ public class TreeInterpreter {
}
supMargin = alternatives.get(0).getNumericValue(att.getId()) + alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && supMargin >= splitValue) {
auxInstance = new Instance(instance);
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), supMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
......@@ -235,7 +244,8 @@ public class TreeInterpreter {
}
infMargin = alternatives.get(0).getNumericValue(att.getId()) - alternatives.get(0).getNumericValue(att.getId()) * percentageMargin;
if (!modifiedList.get(0).get(att.getId()) && infMargin < splitValue) {
auxInstance = new Instance(instance);
splitValues.put(att.getId(), splitValue);
auxInstance = new Instance(alternatives.get(0));
auxInstance.putValue(att.getId(), infMargin);
modified = new HashMap(modified);
modified.put(att.getId(), true);
......
......@@ -32,6 +32,10 @@ public abstract class ClauseGenerator {
return this.getNlgFactory().createSentence(clause);
}
public NPPhraseSpec generateNPPhrase(String determiner, String subject){
return this.getNlgFactory().createNounPhrase(determiner, subject);
}
public SPhraseSpec generateClause(ArrayList<String> subject, String verb, ArrayList<String> object) {
SPhraseSpec phrase = this.getNlgFactory().createClause();
VPPhraseSpec verbPhrase = this.getNlgFactory().createVerbPhrase(verb);
......@@ -184,6 +188,32 @@ public abstract class ClauseGenerator {
return phrase;
}
public SPhraseSpec generateClauseObjectElement(String subjectModifier, String subject, String verb, boolean plural, String objectModifier, NLGElement object, String objectPostModifier) {
SPhraseSpec phrase = this.getNlgFactory().createClause();
VPPhraseSpec verbPhrase = this.getNlgFactory().createVerbPhrase(verb);
verbPhrase.setPlural(plural);
NPPhraseSpec subjectPhrase = this.getNlgFactory().createNounPhrase(subject);
subjectPhrase.setPreModifier(subjectModifier);
NPPhraseSpec objectPhrase = this.getNlgFactory().createNounPhrase(object);
objectPhrase.setPreModifier(objectModifier);
objectPhrase.setPostModifier(objectPostModifier);
if(subject!=null){
phrase.setSubject(subjectPhrase);
phrase.setVerbPhrase(verbPhrase);
phrase.setObject(objectPhrase);
}
else{
verbPhrase.addPreModifier(subjectModifier);
phrase = this.getNlgFactory().createClause(null, verbPhrase, objectPhrase);
}
return phrase;
}
public SPhraseSpec generateClauseObject(String subjectModifier, String subject, String verb, boolean plural, String objectModifier, String object) {
......@@ -277,7 +307,7 @@ public abstract class ClauseGenerator {
CoordinatedPhraseElement phrase = this.getNlgFactory().createCoordinatedPhrase();
for(String n: noums){
phrase.addCoordinate(n.toLowerCase());
phrase.addCoordinate(n);
}
return phrase;
......@@ -293,4 +323,15 @@ public abstract class ClauseGenerator {
return phrase;
}
public CoordinatedPhraseElement generateElementsCoordinate(ArrayList<NLGElement> element){
CoordinatedPhraseElement phrase = this.getNlgFactory().createCoordinatedPhrase();
for(NLGElement e: element){
phrase.addCoordinate(e);
}
return phrase;
}
}
......@@ -16,6 +16,7 @@ import java.util.HashMap;
import java.util.Map;
import simplenlg.framework.CoordinatedPhraseElement;
import simplenlg.framework.NLGElement;
import simplenlg.phrasespec.NPPhraseSpec;
import simplenlg.phrasespec.SPhraseSpec;
public class ExplainerManagerImpl implements ExplainerManager {
......@@ -518,7 +519,9 @@ public class ExplainerManagerImpl implements ExplainerManager {
HashMap<String, ArrayList<String>> attributeLabels = this.extractor.getAttributeLabels(0);
HashMap<String, ArrayList<String>> branchLabelsHigh = new HashMap();
HashMap<String, ArrayList<String>> branchLabelsLow = new HashMap();
HashMap<String, ArrayList<String>> globalProbabilities = this.extractor.globalProbabilities();
ArrayList<String> aux;
String literal;
if (numAlternatives > 1) {
String label;
......@@ -547,7 +550,7 @@ public class ExplainerManagerImpl implements ExplainerManager {
}
}
}
switch (language) {
case "en":
//auxPhrase = this.generator.generateClause(subject, "be", false, consequent);
......@@ -610,6 +613,35 @@ public class ExplainerManagerImpl implements ExplainerManager {
explanation.addClause(((ClauseGeneratorEn) this.generator).getRealisation(phrase));
}
HashMap<String, Double> splitValues;
for (int i = 1; i < numAlternatives; i++) {
splitValues = this.extractor.getSplitValues(i);
if (splitValues.size() > 1) {
literal = "(";
ArrayList<String> attributes = new ArrayList();
for (Map.Entry<String, Double> entry : splitValues.entrySet()) {
attributes.add(entry.getKey().toLowerCase());
literal += entry.getValue() + ", ";
}
literal = literal.replaceAll(", $", ")");
phrase = this.generator.generateClauseObjectElement(null, this.extractor.getConsequent(i), "be", false,
"posible due to the proximity of", this.generator.generateNoumsCoordinate(attributes), "with the split values " + literal);
} else {
for (Map.Entry<String, Double> entry : splitValues.entrySet()) {
phrase = this.generator.generateClause(this.extractor.getConsequent(i), "be", false,
"posible due to the proximity of " + entry.getKey().toLowerCase() + " with the split value (" + entry.getValue() + ")");
}
}
explanation.addClause(((ClauseGeneratorEn) this.generator).getRealisation(phrase));
}
}
break;
......@@ -631,13 +663,13 @@ public class ExplainerManagerImpl implements ExplainerManager {
explanation.addClause(((ClauseGeneratorEs) this.generator).getRealisation(phrase));
if (numAlternatives > 1) {
boolean firstSentence = true;
for (Map.Entry<String, ArrayList<String>> entry : branchLabelsHigh.entrySet()) {
if (entry.getValue().size() > 1 && firstSentence) {
firstSentence = false;
phrase = this.generator.generateClauseObjectElement("Para estos valores concretos", null, "ser", true, entry.getKey() + " que sean",
phrase = this.generator.generateClauseObjectElement("Para estos valores concretos", null, "ser", false, entry.getKey() + " que sean",
this.generator.generateNoumsCoordinate(entry.getValue()));
} else if (entry.getValue().size() > 1 && !firstSentence) {
phrase = this.generator.generateClauseObjectElement("Además", null, "ser", true, entry.getKey() + " que sea",
......@@ -676,6 +708,37 @@ public class ExplainerManagerImpl implements ExplainerManager {
}
HashMap<String, Double> splitValues;
for (int i = 1; i < numAlternatives; i++) {
splitValues = this.extractor.getSplitValues(i);
if (splitValues.size() > 1) {
literal = "(";
ArrayList<NLGElement> attributes = new ArrayList();
for (Map.Entry<String, Double> entry : splitValues.entrySet()) {
attributes.add(this.generator.generateNPPhrase("el", entry.getKey().toLowerCase()));
literal += entry.getValue() + ", ";
}
literal = literal.replaceAll(", $", ")");
phrase = this.generator.generateClauseObjectElement(null, this.extractor.getConsequent(i), "ser", false,
"posible debido a la proximidad de", this.generator.generateElementsCoordinate(attributes), "con los valores de corte " + literal);
} else {
for (Map.Entry<String, Double> entry : splitValues.entrySet()) {
phrase = this.generator.generateClause(this.extractor.getConsequent(i), "ser", false,
"posible debido a la proximidad de " + ((ClauseGeneratorEs) this.generator).getRealisation(
this.generator.generateNPPhrase("el", entry.getKey().toLowerCase())).toLowerCase().replaceAll(".$", "") +
" con el valor de corte (" + entry.getValue() + ")");
}
}
explanation.addClause(((ClauseGeneratorEs) this.generator).getRealisation(phrase));
}
break;
case "gl":
//auxPhrase = this.generator.generateClause(datasetName, "be", false, consequent);
......@@ -693,15 +756,15 @@ public class ExplainerManagerImpl implements ExplainerManager {
phrase = this.generator.generateClause("o", datasetName, "ser", false, consequent + " porque " + ((ClauseGeneratorGl) this.generator).getRealisation(coordinate).toLowerCase());
explanation.addClause(((ClauseGeneratorGl) this.generator).getRealisation(phrase));
if (numAlternatives > 1) {
boolean firstSentence = true;
for (Map.Entry<String, ArrayList<String>> entry : branchLabelsHigh.entrySet()) {
if (entry.getValue().size() > 1 && firstSentence) {
firstSentence = false;
phrase = this.generator.generateClauseObjectElement("Para estos valores concretos", null, "ser", true, entry.getKey() + " que sexan",
phrase = this.generator.generateClauseObjectElement("Para estos valores concretos", null, "ser", false, entry.getKey() + " que sexan",
this.generator.generateNoumsCoordinate(entry.getValue()));
} else if (entry.getValue().size() > 1 && !firstSentence) {
phrase = this.generator.generateClauseObjectElement("Tamén", null, "ser", true, entry.getKey() + " que sexa",
......@@ -739,6 +802,35 @@ public class ExplainerManagerImpl implements ExplainerManager {
}
}
for (int i = 1; i < numAlternatives; i++) {
splitValues = this.extractor.getSplitValues(i);
if (splitValues.size() > 1) {
literal = "(";
ArrayList<NLGElement> attributes = new ArrayList();
for (Map.Entry<String, Double> entry : splitValues.entrySet()) {
attributes.add(this.generator.generateNPPhrase("o", entry.getKey().toLowerCase()));
literal += entry.getValue() + ", ";
}
literal = literal.replaceAll(", $", ")");
phrase = this.generator.generateClauseObjectElement(null, this.extractor.getConsequent(i), "ser", false,
"posible debido a proximidade de", this.generator.generateElementsCoordinate(attributes), "cos valores de corte " + literal);
} else {
for (Map.Entry<String, Double> entry : splitValues.entrySet()) {
phrase = this.generator.generateClause(this.extractor.getConsequent(i), "ser", false,
"posible debido a proximidade de " + ((ClauseGeneratorGl) this.generator).getRealisation(
this.generator.generateNPPhrase("el", entry.getKey().toLowerCase())).toLowerCase().replaceAll(".$", "") +
" co valor de corte (" + entry.getValue() + ")");
}
}
explanation.addClause(((ClauseGeneratorGl) this.generator).getRealisation(phrase));
}
break;
}
......
......@@ -132,20 +132,20 @@ public class InfoExtractor {
ArrayList<String> attributes;
Antecedent antecedent;
String categoricId;
for (Attribute a : this.config.getAttributes()) {
antecedent = this.classifications.get(alternative).getAntecedenById(a.getId());
if (antecedent != null) {
if (antecedent.isCategoric()) {
categoricId = antecedent.getDecision().split(" is ")[1];
attributes = list.get(((CategoricProperty)this.config.getAttributeById(a.getId()).getPropertyByName(categoricId)).getValue());
attributes = list.get(((CategoricProperty) this.config.getAttributeById(a.getId()).getPropertyByName(categoricId)).getValue());
if (attributes == null) {
attributes = new ArrayList();
attributes.add(a.getName());
} else {
attributes.add(a.getName());
}
list.put(((CategoricProperty)this.config.getAttributeById(a.getId()).getPropertyByName(categoricId)).getValue(), attributes);
list.put(((CategoricProperty) this.config.getAttributeById(a.getId()).getPropertyByName(categoricId)).getValue(), attributes);
} else {
attributes = list.get(this.config.getAttributeById(a.getId()).getPropertyById(antecedent.getProperty()).getName());
if (attributes == null) {
......@@ -193,4 +193,45 @@ public class InfoExtractor {
}
public HashMap<String, ArrayList<String>> globalProbabilities() {
HashMap<String, ArrayList<String>> globalProbs = new HashMap();
String label;
ArrayList<String> consequents;
for (int i = 1; i < this.classifications.size(); i++) {
label = this.global.getLabelGlobalFor(this.analyzer.getConfusionBetween(
this.classifications.get(0).getConsequent().getMatrixPosition() - 1, this.classifications.get(i).getConsequent().getMatrixPosition() - 1));
if (globalProbs.get(label) != null) {
consequents = globalProbs.get(label);
} else {
consequents = new ArrayList();
}
consequents.add(this.config.getConsequentById(this.classifications.get(i).getConsequent().getId()).getName());
globalProbs.put(label, consequents);
}
return globalProbs;
}
public HashMap<String, Double> getSplitValues(int alternative) {
HashMap<String, Double> values = new HashMap();
for (Antecedent a : this.classifications.get(alternative).getAntecedents()) {
if (!a.isCategoric()) {
Double split = this.classifications.get(alternative).getSplitValue(a.getDecision().split(" > | <= ")[0]);
if (split != null) {
values.put(this.config.getAttributeById(a.getDecision().split(" > | <= ")[0]).getName(), split);
}
}
}
return values;
}
}
package brunolopez.expliclas.models;
import java.util.ArrayList;
import java.util.HashMap;
public class Classification {
private ArrayList<Antecedent> antecedents;
private HashMap<String, Double> splitValues;
private Instance instance;
private Consequent consequent;
private VisualNode tree;
public Classification() {
this.antecedents = new ArrayList();
this.splitValues = new HashMap();
}
public ArrayList<Antecedent> getAntecedents() {
return antecedents;
}
public HashMap<String, Double> getSplitValues() {
return splitValues;
}
public void addSplitValue(String id, Double value){
this.splitValues.put(id, value);
}
public void addAntecedent(Antecedent antecedent){
this.antecedents.add(antecedent);
}
......@@ -29,6 +40,14 @@ public class Classification {
return tree;
}
public void setSplitValues(HashMap<String, Double> splitValues) {
this.splitValues = splitValues;
}
public Double getSplitValue(String id){
return this.splitValues.get(id);
}
public void setAntecedents(ArrayList<Antecedent> antecedents) {
this.antecedents = antecedents;
}
......
......@@ -7,6 +7,7 @@ public class GlobalConfig {
private HashMap<String, Interval> labels;
private HashMap<String, Interval> labelsBranch;
private HashMap<String, Interval> labelsGlobal;
private int enumLimit;
private int branchProbabilityCut;
private HashMap<String, Integer> confusionParams;
......@@ -15,6 +16,7 @@ public class GlobalConfig {
this.labels = new HashMap();
this.labelsBranch = new HashMap();
this.confusionParams = new HashMap();
this.labelsGlobal = new HashMap();
}
public int getBranchProbabilityCut() {
......@@ -29,6 +31,10 @@ public class GlobalConfig {
return labelsBranch;
}
public HashMap<String, Interval> getLabelsGlobal() {
return labelsGlobal;
}
public HashMap<String, Integer> getConfusionParams() {
return confusionParams;
......@@ -55,6 +61,15 @@ public class GlobalConfig {
return null;
}
public String getLabelGlobalFor(Double value) {
for(Map.Entry<String, Interval> entry: this.labelsGlobal.entrySet()){
if(entry.getValue().contains(value))
return entry.getKey();
}
return null;
}
public int getEnumLimit() {
return enumLimit;
......
......@@ -687,11 +687,11 @@ public class ClassifierService {
try {
VisualNode tree = this.manager.getTree(token, dataset, algorithm, lang);
return Response.status(Response.Status.CREATED).entity(tree).build();
return Response.status(Response.Status.OK).entity(tree).build();
} catch (NotFoundEx ex) {
return Response.status(Response.Status.NOT_FOUND).entity(new SimpleMessage(ex.getMessage())).build();
} catch (IOException ex) {
return Response.status(Response.Status.NOT_FOUND).entity(new SimpleMessage("Error retrieving tree")).build();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new SimpleMessage("Error retrieving tree")).build();
}
}
......
......@@ -27,6 +27,20 @@
"right": 100
}
},
"labelsGlobal": {
"una excepcion": {
"left": 0,
"right": 10
},
"probable": {
"left": 10,
"right": 30
},
"muy probable": {
"left": 30,
"right": 100
}
},
"enumLimit": 9,
"branchProbabilityCut": 75,
"confusionParams": {
......
......@@ -27,6 +27,20 @@
"right": 100
}
},
"labelsGlobal": {
"unha excepcion": {
"left": 0,
"right": 10
},
"probable": {
"left": 10,
"right": 30
},
"moi probable": {
"left": 30,
"right": 100
}
},
"enumLimit": 9,
"branchProbabilityCut": 75,
"confusionParams": {
......
......@@ -27,6 +27,20 @@
"right": 100
}
},
"labelsGlobal": {
"an exception": {
"left": 0,
"right": 10
},
"likely": {
"left": 10,
"right": 30
},
"very likely": {
"left": 30,
"right": 100
}
},
"enumLimit": 9,
"branchProbabilityCut": 25,
"confusionParams": {
......
=== Run information ===
Scheme: weka.classifiers.trees.J48 -C 0.25 -M 2
Scheme: trees.J48 -C 0.25 -M 2
Relation: WINE
Instances: 178
Attributes: 14
......@@ -38,29 +38,22 @@ Number of Leaves : 5
Size of the tree : 9
Time taken to build model: 0 seconds
=== Stratified cross-validation ===
=== Summary ===
Correctly Classified Instances 167 93.8202 %
Incorrectly Classified Instances 11 6.1798 %
Kappa statistic 0.9059
K&B Relative Info Score 16053.8962 %
K&B Information Score 251.8575 bits 1.4149 bits/instance
Class complexity | order 0 278.9816 bits 1.5673 bits/instance
Class complexity | scheme 8611.5047 bits 48.3792 bits/instance
Complexity improvement (Sf) -8332.523 bits -46.8119 bits/instance
Mean absolute error 0.0484
Root mean squared error 0.2021
Relative absolute error 11.0144 %
Root relative squared error 43.1431 %
Total Number of Instances 178
=== Detailed Accuracy By Class ===
TP Rate FP Rate Precision Recall F-Measure MCC ROC Area PRC Area Class
0,966 0,017 0,966 0,966 0,966 0,949 0,975 0,946 1.0
0,944 0,056 0,918 0,944 0,931 0,884 0,939 0,863 2.0
0,896 0,023 0,935 0,896 0,915 0,885 0,932 0,873 3.0
Weighted Avg. 0,938 0,034 0,938 0,938 0,938 0,906 0,949 0,893
=== Confusion Matrix ===
a b c <-- classified as
......
{
"type": "numericNode",
"attribute": {
"type": "numericAtt",
"id": "flavanoids",
"name": "flavanoids",
"name": "Flavanoids",
"properties": [{
"type": "numericProp",