Skip to content
Snippets Groups Projects
Commit 084d02df authored by René Schöne's avatar René Schöne
Browse files

Make used machine learning configurable in starter.

- Introduced MachineLearningHandlerFactory to created Encoder and Decoder
- Class is loaded at runtime, thus has to be in classpath, i.e., in Gradle dependencies
- Added TestFactory for preference learning, which sets current activity identifier to all initially known color items
- Fixed bug in Analyze to not set lastResult
- Added relevant items to change events in REST API, and changed timestamp back to Unix timestamp
- Make error message visible for failed item creation via REST API
- Added isColorItem etc. for Item nonterminal
parent c8c4bbd4
No related branches found
No related tags found
1 merge request!19dev to master
Showing
with 248 additions and 53 deletions
......@@ -363,6 +363,18 @@ aspect ItemHandling {
public void ItemWithDoubleState.setStateToDefault() { this.setState(0.0); }
public void ItemWithStringState.setStateToDefault() { this.setState(""); }
//--- is$ItemType ---
syn boolean Item.isColorItem() = false;
syn boolean ColorItem.isColorItem() = true;
syn boolean Item.isItemWithBooleanState() = false;
syn boolean ItemWithBooleanState.isItemWithBooleanState() = true;
syn boolean Item.isItemWithStringState() = false;
syn boolean ItemWithStringState.isItemWithStringState() = true;
syn boolean Item.isItemWithDoubleState() = false;
syn boolean ItemWithDoubleState.isItemWithDoubleState() = true;
syn boolean Item.isDateTimeItem() = false;
syn boolean DateTimeItem.isDateTimeItem() = true;
//--- as$ItemType ---
// those attributes will raise a ClassCastException if called on the wrong item type. But what else can we do?
syn ColorItem Item.asColorItem() = (ColorItem) this;
......@@ -425,12 +437,4 @@ aspect ItemHandling {
syn String ItemUpdate.getNewStateAsString();
eq ItemUpdateColor.getNewStateAsString() = getNewHSB().toString();
eq ItemUpdateDouble.getNewStateAsString() = Double.toString(getNewValue());
// // override Item.init$Children from JastAdd's own ASTNode aspect
// refine ASTNode public void Item.init$Children() {
// refined();
// setDefaultShouldSendState(true);
// }
}
......@@ -8,9 +8,8 @@ abstract ChangeEvent ::= <Identifier:int> <Created:Instant> ChangedItem* ;
ChangedItem ::= <NewStateAsString:String> ;
rel ChangedItem.Item -> Item ;
RecognitionEvent : ChangeEvent ;
RecognitionEvent : ChangeEvent ::= RelevantItem:ChangedItem* ;
rel RecognitionEvent.Activity -> Activity ;
rel RecognitionEvent.RelevantItem* -> ChangedItem ;
ManualChangeEvent : ChangeEvent ;
......
package de.tudresden.inf.st.eraser.jastadd.model;
import java.net.URL;
/**
* Factory to create new handlers ({@link MachineLearningEncoder} and {@link MachineLearningDecoder})
*
* @author rschoene - Initial contribution
*/
public abstract class MachineLearningHandlerFactory implements MachineLearningSetRoot {
protected Root knowledgeBase;
public enum MachineLearningHandlerFactoryTarget {
ACTIVITY_RECOGNITION,
PREFERENCE_LEARNING
}
@Override
public void setKnowledgeBaseRoot(Root root) {
this.knowledgeBase = root;
}
public abstract void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl);
public abstract MachineLearningEncoder createEncoder();
public abstract MachineLearningDecoder createDecoder();
/**
* Creates a new model.
* To create an {@link InternalMachineLearningModel}, this method needs to be overridden.
* @return the created machine learning model
*/
public MachineLearningModel createModel() {
ExternalMachineLearningModel result = new ExternalMachineLearningModel();
result.setEncoder(createEncoder());
result.setDecoder(createDecoder());
return result;
}
}
......@@ -8,7 +8,7 @@ package de.tudresden.inf.st.eraser.jastadd.model;
public interface MachineLearningSetRoot {
/**
* Informs this handler of the knowledge base.
* Informs this instance of the knowledge base.
* This method is called before any other of the interface methods.
* @param root The root node of the knowledge base
*/
......
package de.tudresden.inf.st.eraser.spark;
import beaver.Parser;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.util.JavaUtils;
......@@ -10,6 +11,7 @@ import spark.Request;
import spark.Response;
import spark.Spark;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -110,9 +112,16 @@ public class Application {
mapper::writeValueAsString);
Spark.path("/:identifier", () -> {
//--- PUT /model/items/:identifier ---
Spark.put("", (request, response) -> {
SmartHomeEntityModel SmartHomeEntityModel = root.getSmartHomeEntityModel();
Item item = ParserUtils.parseItem(request.body());
Item item;
try {
item = ParserUtils.parseItem(request.body());
} catch (IllegalArgumentException | IOException | Parser.Exception e) {
logger.catching(e);
return makeError(response, 400, "Could not create item. Error message: " + e.getMessage());
}
if (!SmartHomeEntityModel.resolveItem(item.getID()).isPresent()) {
root.getSmartHomeEntityModel().addNewItem(item);
response.status(201);
......
......@@ -14,7 +14,7 @@ import java.util.List;
@Data
@AllArgsConstructor
public abstract class SimpleChangeEvent {
public final Instant created;
public final long timestamp;
public final int identifier;
public final List<SimpleChangedItem> changed_items;
}
......@@ -20,7 +20,7 @@ public class SimpleManualChangeEvent extends SimpleChangeEvent {
public final String type = "manual";
public SimpleManualChangeEvent(Instant created, int identifier, List<SimpleChangedItem> changedItems) {
super(created, identifier, changedItems);
super(created.getEpochSecond(), identifier, changedItems);
}
static SimpleManualChangeEvent createFrom(ManualChangeEvent event) {
......
......@@ -19,17 +19,23 @@ import java.util.stream.Collectors;
public class SimpleRecognitionEvent extends SimpleChangeEvent {
public final int activity;
public final String description;
public final List<SimpleChangedItem> relevant_items;
public final String type = "recognition";
public SimpleRecognitionEvent(Instant created, int identifier, List<SimpleChangedItem> changedItems, int activity, String description) {
super(created, identifier, changedItems);
public SimpleRecognitionEvent(Instant created, int identifier, List<SimpleChangedItem> changedItems, List<SimpleChangedItem> relevantItems, int activity, String description) {
super(created.getEpochSecond(), identifier, changedItems);
this.activity = activity;
this.description = description;
this.relevant_items = relevantItems;
}
static SimpleRecognitionEvent createFrom(RecognitionEvent event) {
return new SimpleRecognitionEvent(event.getCreated(), event.getIdentifier(),
JavaUtils.toStream(event.getChangedItems()).map(SimpleChangedItem::createFrom).collect(Collectors.toList()),
JavaUtils.toStream(event.getChangedItems())
.filter(changedItem -> !changedItem.getItem().getID().isEmpty())
.map(SimpleChangedItem::createFrom)
.collect(Collectors.toList()),
JavaUtils.toStream(event.getRelevantItems()).map(SimpleChangedItem::createFrom).collect(Collectors.toList()),
event.getActivity().getIdentifier(), event.getActivity().getLabel());
}
......
package de.tudresden.inf.st.eraser.starter;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import java.net.URL;
/**
* Factory to create dummy handlers.
*
* @author rschoene - Initial contribution
*/
public class DummyMachineLearningHandlerFactory extends MachineLearningHandlerFactory {
private DummyMachineLearningModel model;
@Override
public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) {
model = DummyMachineLearningModel.createDefault();
}
@Override
public MachineLearningEncoder createEncoder() {
// not needed
return null;
}
@Override
public MachineLearningDecoder createDecoder() {
// not needed
return null;
}
@Override
public MachineLearningModel createModel() {
return model;
}
}
......@@ -9,8 +9,6 @@ import de.tudresden.inf.st.eraser.feedbackloop.api.Analyze;
import de.tudresden.inf.st.eraser.feedbackloop.api.Execute;
import de.tudresden.inf.st.eraser.feedbackloop.api.Plan;
import de.tudresden.inf.st.eraser.feedbackloop.execute.ExecuteImpl;
import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.Learner;
import de.tudresden.inf.st.eraser.feedbackloop.learner_backup.MachineLearningImpl;
import de.tudresden.inf.st.eraser.feedbackloop.plan.PlanImpl;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.openhab2.OpenHab2Importer;
......@@ -28,13 +26,14 @@ import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.ACTIVITY_RECOGNITION;
import static de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget.PREFERENCE_LEARNING;
/**
* This Starter combines and starts all modules. This includes:
*
......@@ -112,39 +111,13 @@ public class EraserStarter {
}
}
// initialize backup learner
Learner learner = new Learner();
MachineLearningHandlerFactory activityFactory = createFactory(ACTIVITY_RECOGNITION, settings.activity, root);
MachineLearningHandlerFactory preferenceFactory = createFactory(PREFERENCE_LEARNING, settings.preference, root);
// initialize activity recognition
MachineLearningRoot machineLearningRoot = root.getMachineLearningRoot();
if (settings.activity.dummy) {
logger.info("Using dummy activity recognition, ignoring other settings for this");
machineLearningRoot.setActivityRecognition(DummyMachineLearningModel.createDefault());
} else {
MachineLearningImpl handler = new MachineLearningImpl(learner, MachineLearningImpl.GOAL_ACTIVITY_PHONE_AND_WATCH);
handler.setKnowledgeBaseRoot(root);
logger.info("Reading activity recognition from csv file {}", settings.activity.file);
handler.initActivities(settings.activity.realURL().getFile());
ExternalMachineLearningModel machineLearningModel = new ExternalMachineLearningModel();
machineLearningModel.setEncoder(handler);
machineLearningModel.setDecoder(handler);
root.getMachineLearningRoot().setActivityRecognition(machineLearningModel);
}
// initialize preference learning
if (settings.preference.dummy) {
logger.info("Using dummy preference learning, ignoring other settings for this");
machineLearningRoot.setPreferenceLearning(DummyMachineLearningModel.createDefault());
} else {
logger.info("Reading preference learning from csv file {}", settings.preference.file);
MachineLearningImpl handler = new MachineLearningImpl(learner, MachineLearningImpl.GOAL_PREFERENCE_BRIGHTNESS_IRIS);
handler.setKnowledgeBaseRoot(root);
handler.initPreferences(settings.preference.realURL().getFile());
ExternalMachineLearningModel machineLearningModel = new ExternalMachineLearningModel();
machineLearningModel.setEncoder(handler);
machineLearningModel.setDecoder(handler);
root.getMachineLearningRoot().setPreferenceLearning(machineLearningModel);
}
root.getMachineLearningRoot().setActivityRecognition(activityFactory.createModel());
root.getMachineLearningRoot().setPreferenceLearning(preferenceFactory.createModel());
// machineLearningRoot.getPreferenceLearning().connectItems(settings.preference.items);
if (!machineLearningRoot.getActivityRecognition().check()) {
......@@ -246,4 +219,25 @@ public class EraserStarter {
}
logger.info("I'm done here.");
}
private static MachineLearningHandlerFactory createFactory(MachineLearningHandlerFactory.MachineLearningHandlerFactoryTarget target, Setting.MLContainer config, Root root) {
MachineLearningHandlerFactory factory = new DummyMachineLearningHandlerFactory();
String niceTargetName = target.toString().toLowerCase().replace("_", " ");
if (config.dummy || config.factory == null) {
logger.info("Using dummy {}, ignoring other settings for this", niceTargetName);
} else {
try {
Class<? extends MachineLearningHandlerFactory> clazz = Class.forName(config.factory)
.asSubclass(MachineLearningHandlerFactory.class);
factory = clazz.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
logger.error("Could not instantiate machine learning factory for {} with class '{}'. Using dummy instead.",
niceTargetName, config);
logger.catching(e);
}
}
factory.setKnowledgeBaseRoot(root);
factory.initializeFor(target, config.realURL());
return factory;
}
}
......@@ -42,6 +42,8 @@ class Setting {
}
}
public class MLContainer extends FileContainer {
/** Factory class to instantiate machine learning handlers */
public String factory;
/** Use dummy model in which the current activity is directly editable. Default: false. */
public boolean dummy = false;
/** Model id. Default: 1.*/
......
package de.tudresden.inf.st.eraser.starter;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URL;
import java.util.Optional;
/**
* Testing preference setting
*
* @author rschoene - Initial contribution
*/
public class TestMachineLearningHandlerFactory extends MachineLearningHandlerFactory {
private static final Logger logger = LogManager.getLogger(TestMachineLearningHandlerFactory.class);
@Override
public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) {
if (target != MachineLearningHandlerFactoryTarget.PREFERENCE_LEARNING) {
logger.error("Test Factory only made for preference learning, not {}", target);
}
}
@Override
public MachineLearningEncoder createEncoder() {
return null;
}
@Override
public MachineLearningDecoder createDecoder() {
return null;
}
@Override
public MachineLearningModel createModel() {
DummyMachineLearningModel result = DummyMachineLearningModel.createDefault();
for (Item item : knowledgeBase.getSmartHomeEntityModel().items()) {
if (item.isColorItem()) {
ItemUpdateDouble update = new ItemUpdateDouble() {
// dirty hack to override value based on activity during runtime
@Override
public double getNewValue() {
Optional<Activity> activityOpt = knowledgeBase.currentActivity();
return activityOpt.orElse(knowledgeBase.getMachineLearningRoot().getActivity(0)).getIdentifier();
}
};
update.setItem(item);
result.getCurrent().addPreference(update);
}
}
return result;
}
}
Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item";
Color Item: id="go1_item" label="Go 1" state="120,80,60" topic="go1_item";
Number Item: id="datetime_month" label="Month" state="1" topic="datetime_month";
Number Item: id="datetime_day" label="Day" state="31" topic="datetime_day";
Number Item: id="datetime_hour" label="Hour" state="13" topic="datetime_hour";
......
......@@ -24,6 +24,7 @@ load:
# Model for activity recognition. If dummy is true, then the file parameter is ignored.
activity:
factory: de.tudresden.inf.st.eraser.feedbackloop.learner_backup.MachineLearningHandlerFactoryImpl
# File to read in. Expected format = csv /Users/boqi/eraser/datasets
file: ../datasets/backup/activity_data.csv
external: true
......@@ -36,6 +37,7 @@ activity:
# Model for preference learning. If dummy is true, then the file parameter is ignored.
preference:
factory: de.tudresden.inf.st.eraser.starter.TestMachineLearningHandlerFactory
# File to read in. Expected format = csv
file: ../datasets/backup/preference_data.csv
external: true
......
......@@ -44,7 +44,7 @@ public class AnalyzeImpl implements Analyze {
@Override
public void analyzeLatestChanges() {
MachineLearningResult recognitionResult = knowledgeBase.getMachineLearningRoot().getActivityRecognition().getDecoder().classify();
MachineLearningResult recognitionResult = knowledgeBase.getMachineLearningRoot().getActivityRecognition().classify();
recognitionResult.getItemUpdates().forEach(ItemUpdate::apply);
knowledgeBase.currentActivity().ifPresent(activity -> {
MachineLearningResult newMLResult = knowledgeBase.getMachineLearningRoot().getPreferenceLearning().classify();
......
package de.tudresden.inf.st.eraser.feedbackloop.learner_backup;
import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningDecoder;
import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningEncoder;
import de.tudresden.inf.st.eraser.jastadd.model.MachineLearningHandlerFactory;
import java.net.URL;
/**
* Factory to create handlers of type {@link MachineLearningHandlerFactoryImpl}.
*
* @author rschoene - Initial contribution
*/
public class MachineLearningHandlerFactoryImpl extends MachineLearningHandlerFactory {
private MachineLearningImpl handler;
private static Learner learner = new Learner();
@Override
public void initializeFor(MachineLearningHandlerFactoryTarget target, URL configUrl) {
switch (target) {
case ACTIVITY_RECOGNITION:
handler = new MachineLearningImpl(learner, MachineLearningImpl.GOAL_ACTIVITY_PHONE_AND_WATCH);
handler.setKnowledgeBaseRoot(knowledgeBase);
handler.initActivities(configUrl.getFile());
break;
case PREFERENCE_LEARNING:
handler = new MachineLearningImpl(learner, MachineLearningImpl.GOAL_PREFERENCE_BRIGHTNESS_IRIS);
handler.setKnowledgeBaseRoot(knowledgeBase);
handler.initPreferences(configUrl.getFile());
break;
default:
throw new UnsupportedOperationException("Target " + target + " is not supported");
}
}
@Override
public MachineLearningEncoder createEncoder() {
return handler;
}
@Override
public MachineLearningDecoder createDecoder() {
return handler;
}
}
......@@ -37,7 +37,6 @@ public class PlanImpl implements Plan {
logger.info("Plan got new activity [{}]: {}", activity.getIdentifier(), activity.getLabel());
MachineLearningResult mlResult = knowledgeBase.getMachineLearningRoot().getPreferenceLearning().getLastResult();
knowledgeBase.getMachineLearningRoot().addChangeEvent(createRecognitionEvent(activity, mlResult.getItemUpdates()));
knowledgeBase.getMachineLearningRoot().getChangeEventList();
informExecute(mlResult.getItemUpdates());
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment