diff --git a/eraser-base/src/main/jastadd/MachineLearning.jrag b/eraser-base/src/main/jastadd/MachineLearning.jrag
index 1a912dbd92455054b358ddfee00e94cb1c71ca43..d92c35ecaffb0b099b95db4618c7d0799d6a9b7a 100644
--- a/eraser-base/src/main/jastadd/MachineLearning.jrag
+++ b/eraser-base/src/main/jastadd/MachineLearning.jrag
@@ -85,6 +85,9 @@ aspect MachineLearning {
    * Checks the ML model for all necessary children.
    * @return true, if everything is alright. false otherwise
    */
+  public abstract boolean MachineLearningModel.check();
+
+  @Override
   public boolean InternalMachineLearningModel.check() {
     boolean good = true;
     if (getOutputApplication() == null) {
@@ -109,6 +112,11 @@ aspect MachineLearning {
     return good;
   }
 
+  @Override
+  public boolean ExternalMachineLearningModel.check() {
+    throw new UnsupportedOperationException("check not available for external ML models (yet)!");
+  }
+
   //--- mlKind ---
   inh String MachineLearningModel.mlKind();
   eq MachineLearningRoot.getActivityRecognition().mlKind() = "ActivityRecognition";
diff --git a/eraser-base/src/main/jastadd/MachineLearning.relast b/eraser-base/src/main/jastadd/MachineLearning.relast
index 0101465620191b440674f8d17933f3259c20ff42..5bfeaddda253ba37b4d730dfd3638e2c1b1b70d5 100644
--- a/eraser-base/src/main/jastadd/MachineLearning.relast
+++ b/eraser-base/src/main/jastadd/MachineLearning.relast
@@ -16,8 +16,8 @@ ManualChangeEvent : ChangeEvent ;
 abstract MachineLearningModel ::= ;
 ExternalMachineLearningModel : MachineLearningModel ;
 abstract InternalMachineLearningModel : MachineLearningModel ::= <OutputApplication:DoubleDoubleFunction> ;
-rel InternalMachineLearningModel.RelevantItem* -> Item ;
-rel InternalMachineLearningModel.TargetItem* -> Item ;
+rel InternalMachineLearningModel.RelevantItem* <-> Item.RelevantInMachineLearningModel* ;
+rel InternalMachineLearningModel.TargetItem* <-> Item.TargetInMachineLearningModel* ;
 
 abstract ItemPreference ::= ;
 rel ItemPreference.Item -> Item ;
diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java
index 829726efc57bec4f931de8c1e0e046a63a9a40eb..386e6f325dc4fde274507902843a096614e09043 100644
--- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java
+++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/openhab2/mqtt/MQTTUpdater.java
@@ -6,19 +6,10 @@ import de.tudresden.inf.st.eraser.jastadd.model.Root;
 import de.tudresden.inf.st.eraser.util.MqttReceiver;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.fusesource.hawtbuf.Buffer;
-import org.fusesource.hawtbuf.UTF8Buffer;
-import org.fusesource.mqtt.client.*;
+import org.fusesource.mqtt.client.QoS;
 
 import java.io.IOException;
-import java.net.URI;
-import java.util.Arrays;
-import java.util.Objects;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * Update an imported model by subscribing to MQTT topics.
@@ -37,22 +28,22 @@ public class MQTTUpdater implements AutoCloseable {
     this.delegatee = new MqttReceiver();
   }
 
-  public MQTTUpdater(Root model) throws IllegalArgumentException {
+  public MQTTUpdater(Root root) throws IllegalArgumentException {
     this();
-    this.setModel(model);
+    this.setRoot(root);
   }
 
   /**
-   * Sets the model to update
-   * @param model the model to update
+   * Sets the model root to update
+   * @param root the model root to update
    */
-  public void setModel(Root model) {
-    ExternalHost host = model.getMqttRoot().getHost();
+  public void setRoot(Root root) {
+    ExternalHost host = root.getMqttRoot().getHost();
     delegatee.setHost(host.getHostName(), host.getPort());
     delegatee.setOnMessage((topicString, message) ->
-        model.getMqttRoot().resolveTopic(topicString).ifPresent(topic ->
+        root.getMqttRoot().resolveTopic(topicString).ifPresent(topic ->
             itemUpdate(topic.getItem(), message)));
-    delegatee.setTopicsForSubscription(model.getMqttRoot().getIncomingPrefix() + "#");
+    delegatee.setTopicsForSubscription(root.getMqttRoot().getIncomingPrefix() + "#");
     delegatee.setQoSForSubscription(QoS.AT_LEAST_ONCE);
   }
 
diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java
index 2ede06b3fef46cacc2dc3427ddc488f891b607e2..ee41ce60dcb3c50555095fa054af50e69e25b21b 100644
--- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java
+++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/Application.java
@@ -9,7 +9,6 @@ import spark.Request;
 import spark.Response;
 import spark.Spark;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -26,12 +25,12 @@ import java.util.stream.Collectors;
 public class Application {
 
   private static final Logger logger = LogManager.getLogger(Application.class);
-  private final Root model;
+  private final Root root;
   private final Lock lock;
   private final Condition quitCondition;
 
-  private Application(Root model, Lock lock, Condition quitCondition) {
-    this.model = model;
+  private Application(Root root, Lock lock, Condition quitCondition) {
+    this.root = root;
     this.lock = lock;
     this.quitCondition = quitCondition;
   }
@@ -43,19 +42,19 @@ public class Application {
 
     Spark.path("/activity", () -> {
       Spark.get("",
-          (request, response) -> wrapActivityList(model.getMachineLearningRoot().getActivityList()),
+          (request, response) -> wrapActivityList(root.getMachineLearningRoot().getActivityList()),
           mapper::writeValueAsString);
       Spark.get("/current",
-          (request, response) -> JavaUtils.ifPresentOrElseReturn(model.currentActivity(),
+          (request, response) -> JavaUtils.ifPresentOrElseReturn(root.currentActivity(),
               this::wrapActivity,
               () -> makeError(response, 204, "No activity recognized.")),
           mapper::writeValueAsString);
       Spark.put("/current", (request, response) -> {
         logger.info("request body: '{}', params: '{}', length={}", request.body(), request.params(), request.contentLength());
-        if (!model.getMachineLearningRoot().hasActivityRecognition()) {
+        if (!root.getMachineLearningRoot().hasActivityRecognition()) {
           return makeError(response, 404, "No activity recognition model found");
         }
-        MachineLearningModel activityRecognition = model.getMachineLearningRoot().getActivityRecognition();
+        MachineLearningModel activityRecognition = root.getMachineLearningRoot().getActivityRecognition();
         if (activityRecognition.canSetActivity()) {
           activityRecognition.setActivity(Integer.valueOf(request.body()));
           return "OK";
@@ -65,7 +64,7 @@ public class Application {
       });
       Spark.get("/:identifier",
           (request, response) ->
-              JavaUtils.ifPresentOrElseReturn(model.resolveActivity(paramAsInt(request, "identifier")),
+              JavaUtils.ifPresentOrElseReturn(root.resolveActivity(paramAsInt(request, "identifier")),
                   this::wrapActivity,
                   () -> makeError(response, 404, "No activity for identifier " + request.params("identifier"))),
           mapper::writeValueAsString);
@@ -73,11 +72,11 @@ public class Application {
 
     Spark.path("/events", () -> {
       Spark.get("",
-          (request, response) -> wrapChangeEventList(model.getMachineLearningRoot().getChangeEventList()),
+          (request, response) -> wrapChangeEventList(root.getMachineLearningRoot().getChangeEventList()),
           mapper::writeValueAsString);
       Spark.get("/:identifier",
           (request, response) ->
-              JavaUtils.ifPresentOrElseReturn(model.resolveChangeEvent(paramAsInt(request, "identifier")),
+              JavaUtils.ifPresentOrElseReturn(root.resolveChangeEvent(paramAsInt(request, "identifier")),
                   this::wrapChangeEvent,
                   () -> makeError(response, 404, "No event for identifier " + request.params("identifier"))),
           mapper::writeValueAsString);
@@ -86,10 +85,10 @@ public class Application {
     Spark.path("/model", () -> {
       Spark.get("/full", (request, response) -> {
         response.type("text/plain");
-        return model.prettyPrint();
+        return root.prettyPrint();
       });
       Spark.get("/items",
-          (request, response) -> wrapItemList(model.items()),
+          (request, response) -> wrapItemList(root.getOpenHAB2Model().items()),
           mapper::writeValueAsString);
       Spark.put("/items/:identifier/state", (request, response) -> {
         logger.info("request body: '{}', params: '{}', length={}", request.body(), request.params(), request.contentLength());
@@ -124,13 +123,13 @@ public class Application {
   }
 
   private Object safeItemRoute(Request request, Response response, Function<Item, String> action) {
-    return JavaUtils.ifPresentOrElseReturn(model.resolveItem(request.params("identifier")), action,
+    return JavaUtils.ifPresentOrElseReturn(root.getOpenHAB2Model().resolveItem(request.params("identifier")), action,
         () -> makeError(response, 404, "Item '" + request.body() + "' not found"));
   }
 
   private String makeHistory(Item item, Response response) {
     response.type("text/plain");
-    InfluxAdapter influxAdapter = model.getInfluxRoot().influxAdapter();
+    InfluxAdapter influxAdapter = root.getInfluxRoot().influxAdapter();
     influxAdapter.disableAsyncQuery();
     List<? extends AbstractItemPoint> list;
     if (item.asColorItem() != null) {
diff --git a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java
index 902f15bbd31fd7480bd413a239526b9576f68015..e548a6bc95012ba72675131395b9956f4a84466f 100644
--- a/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java
+++ b/eraser.spark/src/main/java/de/tudresden/inf/st/eraser/spark/DummyDataCreator.java
@@ -10,21 +10,21 @@ import de.tudresden.inf.st.eraser.util.JavaUtils;
  */
 class DummyDataCreator {
 
-  private final Root model;
+  private final Root root;
 
-  DummyDataCreator(Root model) {
-    this.model = model;
+  DummyDataCreator(Root root) {
+    this.root = root;
   }
 
   private void addDummyActivitiesToModel() {
-    MachineLearningRoot mlRoot = model.getMachineLearningRoot();
+    MachineLearningRoot mlRoot = root.getMachineLearningRoot();
     mlRoot.addActivity(new Activity(1, "Sitting in armchair"));
     mlRoot.addActivity(new Activity(2, "Going to sleep"));
     mlRoot.addActivity(new Activity(3, "Entering house"));
   }
 
   private void addDummyChangeEventsToModel() {
-    MachineLearningRoot mlRoot = model.getMachineLearningRoot();
+    MachineLearningRoot mlRoot = root.getMachineLearningRoot();
     Item iris1 = getOrCreateColorItem("iris1", "Hue Iris 1");
     Item go1 = getOrCreateColorItem("go1", "Hue Go 1");
     Item go2 = getOrCreateColorItem("go2", "Hue Go 2");
@@ -40,7 +40,7 @@ class DummyDataCreator {
   }
 
   private Item getOrCreateColorItem(String name, String label) {
-    return model.resolveItem(name).orElseGet(() -> {
+    return root.getOpenHAB2Model().resolveItem(name).orElseGet(() -> {
       ColorItem result = new ColorItem();
       result.setID(name);
       result.setLabel(label);
@@ -53,7 +53,7 @@ class DummyDataCreator {
 
   private RecognitionEvent newRecognitionEvent(int identifier, long timestamp, int activityIdentifier, ChangedItem... changedItems) {
     RecognitionEvent result = new RecognitionEvent();
-    JavaUtils.ifPresentOrElse(model.resolveActivity(activityIdentifier), result::setActivity, () -> { throw new RuntimeException("No activity found for identifier " + activityIdentifier); });
+    JavaUtils.ifPresentOrElse(root.resolveActivity(activityIdentifier), result::setActivity, () -> { throw new RuntimeException("No activity found for identifier " + activityIdentifier); });
     initChangeEvent(result, identifier, timestamp, changedItems);
     return result;
   }
diff --git a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java
index b207dc5fb16ff63b5601b3bab92a1b33b29c1241..1bf2b06887fb97a4479c4dad1e1a6d05603ffe81 100644
--- a/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java
+++ b/eraser.starter/src/main/java/de/tudresden/inf/st/eraser/starter/EraserStarter.java
@@ -13,10 +13,7 @@ import de.tudresden.inf.st.eraser.feedbackloop.execute.ExecuteImpl;
 import de.tudresden.inf.st.eraser.feedbackloop.learner.LearnerHelper;
 import de.tudresden.inf.st.eraser.feedbackloop.learner.LearnerImpl;
 import de.tudresden.inf.st.eraser.feedbackloop.plan.PlanImpl;
-import de.tudresden.inf.st.eraser.jastadd.model.DummyMachineLearningModel;
-import de.tudresden.inf.st.eraser.jastadd.model.InfluxAdapter;
-import de.tudresden.inf.st.eraser.jastadd.model.NeuralNetworkRoot;
-import de.tudresden.inf.st.eraser.jastadd.model.Root;
+import de.tudresden.inf.st.eraser.jastadd.model.*;
 import de.tudresden.inf.st.eraser.openhab2.OpenHab2Importer;
 import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater;
 import de.tudresden.inf.st.eraser.spark.Application;
@@ -87,12 +84,14 @@ public class EraserStarter {
     logger.info("Starting ERASER");
     boolean startRest = settings.rest.use;
 
-    Root model;
+    Root root;
+    OpenHAB2Model model;
     switch (settings.initModelWith) {
       case openhab:
         OpenHab2Importer importer = new OpenHab2Importer();
         try {
           model = importer.importFrom(new URL(settings.openhab.url));
+          root = model.getRoot();
         } catch (MalformedURLException e) {
           logger.error("Could not parse URL {}", settings.openhab.url);
           logger.catching(e);
@@ -104,7 +103,8 @@ public class EraserStarter {
       case load:
       default:
         try {
-          model = ParserUtils.load(settings.load.realURL());
+          root = ParserUtils.load(settings.load.realURL());
+          model = root.getOpenHAB2Model();
         } catch (IOException | Parser.Exception e) {
           logger.error("Problems parsing the given file {}", settings.load.file);
           logger.catching(e);
@@ -114,9 +114,10 @@ public class EraserStarter {
     }
 
     // initialize activity recognition
+    MachineLearningRoot machineLearningRoot = root.getMachineLearningRoot();
     if (settings.activity.dummy) {
       logger.info("Using dummy activity recognition");
-      model.getMachineLearningRoot().setActivityRecognition(DummyMachineLearningModel.createDefault());
+      machineLearningRoot.setActivityRecognition(DummyMachineLearningModel.createDefault());
     } else {
       logger.error("Reading activity recognition from file is not supported yet!");
       // TODO
@@ -125,7 +126,7 @@ public class EraserStarter {
     // initialize preference learning
     if (settings.preference.dummy) {
       logger.info("Using dummy preference learning");
-      model.getMachineLearningRoot().setPreferenceLearning(DummyMachineLearningModel.createDefault());
+      machineLearningRoot.setPreferenceLearning(DummyMachineLearningModel.createDefault());
     } else {
       logger.info("Reading preference learning from file {}", settings.preference.file);
       Learner learner = new LearnerImpl();
@@ -146,7 +147,7 @@ public class EraserStarter {
         if (neuralNetwork == null) {
           logger.error("Could not create preference model, see possible previous errors.");
         } else {
-          model.getMachineLearningRoot().setPreferenceLearning(neuralNetwork);
+          machineLearningRoot.setPreferenceLearning(neuralNetwork);
           neuralNetwork.setOutputApplication(zeroToThree -> 33 * zeroToThree);
           JavaUtils.ifPresentOrElse(
               model.resolveItem(settings.preference.affectedItem),
@@ -156,15 +157,15 @@ public class EraserStarter {
       } else {
         // loading was not successful
         logger.warn("Falling back to dummy preference learning");
-        model.getMachineLearningRoot().setPreferenceLearning(DummyMachineLearningModel.createDefault());
+        machineLearningRoot.setPreferenceLearning(DummyMachineLearningModel.createDefault());
       }
     }
-    model.getMachineLearningRoot().getPreferenceLearning().connectItems(settings.preference.items);
-    if (!model.getMachineLearningRoot().getActivityRecognition().check()) {
+    machineLearningRoot.getPreferenceLearning().connectItems(settings.preference.items);
+    if (!machineLearningRoot.getActivityRecognition().check()) {
       logger.fatal("Invalid activity recognition!");
       System.exit(1);
     }
-    if (!model.getMachineLearningRoot().getPreferenceLearning().check()) {
+    if (!machineLearningRoot.getPreferenceLearning().check()) {
       logger.fatal("Invalid preference learning!");
       System.exit(1);
     }
@@ -183,9 +184,9 @@ public class EraserStarter {
       analyze.setPlan(plan);
       plan.setExecute(execute);
 
-      analyze.setKnowledgeBase(model);
-      plan.setKnowledgeBase(model);
-      execute.setKnowledgeBase(model);
+      analyze.setKnowledgeBase(root);
+      plan.setKnowledgeBase(root);
+      execute.setKnowledgeBase(root);
 
       analyze.startAsThread(1, TimeUnit.SECONDS);
     } else {
@@ -195,7 +196,7 @@ public class EraserStarter {
     if (settings.mqttUpdate) {
       logger.info("Starting MQTT updater");
       Thread t = new Thread(() -> {
-        try (MQTTUpdater updater = new MQTTUpdater(model)) {
+        try (MQTTUpdater updater = new MQTTUpdater(root)) {
           updater.start();
           updater.waitUntilReady(5, TimeUnit.SECONDS);
           lock.lock();
@@ -215,7 +216,7 @@ public class EraserStarter {
       // start REST-API in new thread
       logger.info("Starting REST server");
       Thread t = new Thread(
-          () -> Application.start(settings.rest.port, model, settings.rest.createDummyMLData, lock, quitCondition),
+          () -> Application.start(settings.rest.port, root, settings.rest.createDummyMLData, lock, quitCondition),
           "REST-API");
       t.setDaemon(true);
       t.start();
@@ -249,7 +250,7 @@ public class EraserStarter {
     if (analyze != null) {
       analyze.stop();
     }
-    InfluxAdapter influxAdapter = model.getInfluxRoot().influxAdapter();
+    InfluxAdapter influxAdapter = root.getInfluxRoot().influxAdapter();
     if (influxAdapter != null) {
       try {
         influxAdapter.close();
diff --git a/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java b/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java
index 1fa433ad425df55e2d9c44b66c396786cddea84e..80d2346312445914306e6c224d1f9de1b766cf6d 100644
--- a/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java
+++ b/feedbackloop.execute/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/execute/ExecuteImpl.java
@@ -61,7 +61,7 @@ public class ExecuteImpl implements Execute {
   }
 
   private void resolveOrLogError(String itemId, Consumer<? super Item> consumer) {
-    Optional<Item> optionalItem = knowledgeBase.resolveItem(itemId);
+    Optional<Item> optionalItem = knowledgeBase.getOpenHAB2Model().resolveItem(itemId);
     if (!optionalItem.isPresent()) {
       logger.warn("Could not resolve '{}' as an item.", itemId);
     }
diff --git a/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java b/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java
index 19765c1e6df76fb2e2701b3a5eebb2e302728b6e..97c90b4aca7ec11535f96d1f3c184da92a471f44 100644
--- a/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java
+++ b/feedbackloop.learner/src/main/java/de/tudresden/inf/st/eraser/feedbackloop/learner/Main.java
@@ -86,10 +86,10 @@ public class Main {
 	private static Root createModel(ArrayList<Double> normalizedInputs, int inputBiasNumber) {
 
 		//create KB Model
-		Root model = Root.createEmptyRoot();
+		Root root = Root.createEmptyRoot();
 		Group group = new Group();
 		group.setID("Group1");
-		model.addGroup(group);
+		root.getOpenHAB2Model().addGroup(group);
 
 		NumberItem monthItem = new NumberItem();
 		monthItem.setState(normalizedInputs.get(0));
@@ -122,7 +122,7 @@ public class Main {
 		group.addItem(dayItem);
 		group.addItem(hourItem);
 		group.addItem(minuteItem);
-		return model;
+		return root;
 	}
 
 	/**
@@ -130,7 +130,8 @@ public class Main {
 	 */
 	private static void createBrightnessNetwork(ArrayList<Double> all_weights, int inputBiasNumber, int hiddenlayernumber,
 												int hiddenlayerbias, ArrayList<Double> normalizedInputsandOutput) {
-		Root model = createModel(normalizedInputsandOutput, inputBiasNumber);
+		Root root = createModel(normalizedInputsandOutput, inputBiasNumber);
+		OpenHAB2Model model = root.getOpenHAB2Model();
 		Item monthItem = model.resolveItem("month").orElseThrow(
 				() -> new RuntimeException("Month not found"));
 		Item dayItem = model.resolveItem("day").orElseThrow(
@@ -220,8 +221,8 @@ public class Main {
 				hiddenNeuron.connectTo(output3, weights.get(i + hiddenSum * 3));
 			}
 		}
-		model.getMachineLearningRoot().setPreferenceLearning(nn);
-		System.out.println(model.prettyPrint());
+		root.getMachineLearningRoot().setPreferenceLearning(nn);
+		System.out.println(root.prettyPrint());
 
 		List<String> output = new ArrayList<>();
 		Function<DoubleNumber, String> leafToString = classification -> Double.toString(classification.number);