[RELNOTES] Add `target_tensors` to `compile`.
diff --git a/keras/engine/training.py b/keras/engine/training.py
index 80ade166499..3b517810e08 100644
--- a/keras/engine/training.py
+++ b/keras/engine/training.py
@@ -52,6 +52,10 @@ def _standardize_input_data(data, names, shapes=None,
         ValueError: in case of improperly formatted user-provided data.
     """
     if not names:
+        if data:
+            raise ValueError('Error when checking model ' +
+                             exception_prefix + ': '
+                             'expected no data, but got:', data)
         return []
     if data is None:
         return [None for _ in range(len(names))]
@@ -564,7 +568,8 @@ class Model(Container):
     """
 
     def compile(self, optimizer, loss, metrics=None, loss_weights=None,
-                sample_weight_mode=None, weighted_metrics=None, **kwargs):
+                sample_weight_mode=None, weighted_metrics=None,
+                target_tensors=None, **kwargs):
         """Configures the model for training.
 
         # Arguments
@@ -597,6 +602,14 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
                 If the model has multiple outputs, you can use a different
                 `sample_weight_mode` on each output by passing a
                 dictionary or a list of modes.
+            target_tensors: by default, Keras will create placeholders for the
+                model's target, which will be fed with the target data during
+                training. If instead you would like to use your own
+                target tensors (in turn, Keras will not expect external
+                Numpy data for these targets at training time), you
+                can specify them via the `target_tensors` argument. It can be
+                a single tensor (for a single-output model), a list of tensors,
+                or a dict mapping output names to target tensors.
             weighted_metrics: list of metrics to be evaluated and weighted
                 by sample_weight or class_weight during training and testing
             **kwargs: when using the Theano/CNTK backends, these arguments
@@ -644,19 +657,16 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
             loss_functions = [loss_function for _ in range(len(self.outputs))]
         self.loss_functions = loss_functions
         weighted_losses = [_weighted_masked_objective(fn) for fn in loss_functions]
-        skip_indices = []
+        skip_target_indices = []
+        skip_target_weighing_indices = []
         self._feed_outputs = []
         self._feed_output_names = []
         self._feed_output_shapes = []
         self._feed_loss_fns = []
         for i in range(len(weighted_losses)):
             if weighted_losses[i] is None:
-                skip_indices.append(i)
-            else:
-                self._feed_outputs.append(self.outputs[i])
-                self._feed_output_names.append(self.output_names[i])
-                self._feed_output_shapes.append(self.internal_output_shapes[i])
-                self._feed_loss_fns.append(self.loss_functions[i])
+                skip_target_indices.append(i)
+                skip_target_weighing_indices.append(i)
 
         # Prepare output masks.
         masks = self.compute_mask(self.inputs, mask=None)
@@ -691,6 +701,56 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
                             str(loss_weights) +
                             ' - expected a list of dicts.')
 
+        # Prepare targets of model.
+        self.targets = []
+        self._feed_targets = []
+        if target_tensors is not None:
+            if isinstance(target_tensors, list):
+                if len(target_tensors) != len(self.outputs):
+                    raise ValueError(
+                        'When passing a list as `target_tensors`, '
+                        'it should have one entry per model outputs. '
+                        'The model has ' + str(len(self.outputs)) +
+                        ' outputs, but you passed target_tensors=' +
+                        str(target_tensors))
+            elif isinstance(target_tensors, dict):
+                for name in target_tensors:
+                    if name not in self.output_names:
+                        raise ValueError('Unknown entry in `target_tensors` '
+                                         'dictionary: "' + name + '". '
+                                         'Only expected the following keys: ' +
+                                         str(self.output_names))
+                _target_tensors = []
+                for name in self.output_names:
+                    _target_tensors.append(target_tensors.get(name, None))
+                target_tensors = _target_tensors
+            else:
+                raise TypeError('Expected `target_tensors` to be '
+                                'a list or dict, but got:', target_tensors)
+        for i in range(len(self.outputs)):
+            if i in skip_target_indices:
+                self.targets.append(None)
+            else:
+                shape = self.internal_output_shapes[i]
+                name = self.output_names[i]
+                if target_tensors is not None:
+                    target = target_tensors[i]
+                else:
+                    target = None
+                if target is None:
+                    target = K.placeholder(ndim=len(shape),
+                                           name=name + '_target',
+                                           sparse=K.is_sparse(self.outputs[i]),
+                                           dtype=K.dtype(self.outputs[i]))
+                    self._feed_targets.append(target)
+                    self._feed_outputs.append(self.outputs[i])
+                    self._feed_output_names.append(name)
+                    self._feed_output_shapes.append(shape)
+                    self._feed_loss_fns.append(self.loss_functions[i])
+                else:
+                    skip_target_weighing_indices.append(i)
+                self.targets.append(target)
+
         # Prepare sample weights.
         sample_weights = []
         sample_weight_modes = []
@@ -703,7 +763,7 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
                                      'Only expected the following keys: ' +
                                      str(self.output_names))
             for i, name in enumerate(self.output_names):
-                if i in skip_indices:
+                if i in skip_target_weighing_indices:
                     weight = None
                     sample_weight_modes.append(None)
                 else:
@@ -729,7 +789,7 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
                                  'sample_weight_mode=' +
                                  str(sample_weight_mode))
             for i in range(len(self.output_names)):
-                if i in skip_indices:
+                if i in skip_target_weighing_indices:
                     weight = None
                     sample_weight_modes.append(None)
                 else:
@@ -746,7 +806,7 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
                 sample_weights.append(weight)
         else:
             for i, name in enumerate(self.output_names):
-                if i in skip_indices:
+                if i in skip_target_weighing_indices:
                     sample_weight_modes.append(None)
                     sample_weights.append(None)
                 else:
@@ -763,25 +823,9 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
         self.sample_weight_modes = sample_weight_modes
         self._feed_sample_weight_modes = []
         for i in range(len(self.outputs)):
-            if i not in skip_indices:
+            if i not in skip_target_weighing_indices:
                 self._feed_sample_weight_modes.append(self.sample_weight_modes[i])
 
-        # Prepare targets of model.
-        self.targets = []
-        self._feed_targets = []
-        for i in range(len(self.outputs)):
-            if i in skip_indices:
-                self.targets.append(None)
-            else:
-                shape = self.internal_output_shapes[i]
-                name = self.output_names[i]
-                target = K.placeholder(ndim=len(shape),
-                                       name=name + '_target',
-                                       sparse=K.is_sparse(self.outputs[i]),
-                                       dtype=K.dtype(self.outputs[i]))
-                self.targets.append(target)
-                self._feed_targets.append(target)
-
         # Prepare metrics.
         self.metrics = metrics
         self.weighted_metrics = weighted_metrics
@@ -792,7 +836,7 @@ def compile(self, optimizer, loss, metrics=None, loss_weights=None,
         total_loss = None
         with K.name_scope('loss'):
             for i in range(len(self.outputs)):
-                if i in skip_indices:
+                if i in skip_target_indices:
                     continue
                 y_true = self.targets[i]
                 y_pred = self.outputs[i]
@@ -836,7 +880,7 @@ def append_metric(layer_index, metric_name, metric_tensor):
 
         with K.name_scope('metrics'):
             for i in range(len(self.outputs)):
-                if i in skip_indices:
+                if i in skip_target_indices:
                     continue
 
                 y_true = self.targets[i]
@@ -884,7 +928,7 @@ def handle_metrics(metrics, weights=None):
         self.sample_weights = sample_weights
         self._feed_sample_weights = []
         for i in range(len(self.sample_weights)):
-            if i not in skip_indices:
+            if i not in skip_target_weighing_indices:
                 self._feed_sample_weights.append(sample_weights[i])
 
         # Functions for train, test and predict will
diff --git a/tests/keras/engine/test_training.py b/tests/keras/engine/test_training.py
index a17187f5a31..f0986978421 100644
--- a/tests/keras/engine/test_training.py
+++ b/tests/keras/engine/test_training.py
@@ -4,10 +4,10 @@
 import sys
 import scipy.sparse as sparse
 
+import keras
 from keras.layers import Dense, Dropout
 from keras.engine.topology import Input
 from keras.engine.training import Model
-from keras.engine.training import Model
 from keras.engine.training import _check_loss_and_target_compatibility
 from keras.engine.training import _weighted_masked_objective
 from keras.engine.training import _check_array_lengths
@@ -673,7 +673,7 @@ def test_model_with_partial_loss():
 
 @keras_test
 @pytest.mark.skipif((K.backend() == 'cntk'),
-                    reason="cntk does not support external loss yet")
+                    reason='cntk does not support external loss yet')
 def test_model_with_external_loss():
     # None loss, only regularization loss.
     a = Input(shape=(3,), name='input_a')
@@ -749,5 +749,64 @@ def test_model_with_external_loss():
         assert out.shape == (10, 4)
 
 
+@keras_test
+def test_target_tensors():
+    # single-output, as list
+    model = keras.models.Sequential()
+    model.add(keras.layers.Dense(4, input_shape=(4,), name='dense'))
+    input_val = np.random.random((10, 4))
+    target_val = np.random.random((10, 4))
+    target = keras.backend.variable(target_val)
+    model.compile(optimizer='rmsprop', loss='mse', target_tensors=[target])
+    model.train_on_batch(input_val, None)
+
+    # single-output, as dict
+    model.compile(optimizer='rmsprop', loss='mse',
+                  target_tensors={'dense': target})
+    model.train_on_batch(input_val, None)
+
+    # test invalid arguments
+    with pytest.raises(TypeError):
+        model.compile(optimizer='rmsprop', loss='mse',
+                      target_tensors=set())
+    with pytest.raises(ValueError):
+        model.compile(optimizer='rmsprop', loss='mse',
+                      target_tensors=[target, target])
+    with pytest.raises(ValueError):
+        model.compile(optimizer='rmsprop', loss='mse',
+                      target_tensors={'dense2': None})
+    with pytest.raises(ValueError):
+        model.compile(optimizer='rmsprop', loss='mse',
+                      target_tensors=[target])
+        model.train_on_batch(input_val, target_val)
+
+    # multi-output, as list
+    input_val = np.random.random((10, 4))
+    target_val_a = np.random.random((10, 4))
+    target_val_b = np.random.random((10, 4))
+    target_a = keras.backend.variable(target_val_a)
+    target_b = keras.backend.variable(target_val_b)
+
+    inputs = keras.layers.Input(shape=(4,))
+    output_a = keras.layers.Dense(4, name='dense_a')(inputs)
+    output_b = keras.layers.Dense(4, name='dense_b')(inputs)
+    model = keras.models.Model(inputs, [output_a, output_b])
+    model.compile(optimizer='rmsprop', loss='mse',
+                  target_tensors=[target_a, target_b])
+    model.train_on_batch(input_val, None)
+
+    # multi-output, as dict
+    model.compile(optimizer='rmsprop', loss='mse',
+                  target_tensors={'dense_a': target_a,
+                                  'dense_b': target_b})
+    model.train_on_batch(input_val, None)
+
+    # test with sample weights
+    model.compile(optimizer='rmsprop', loss='mse',
+                  target_tensors=[target_a, target_b])
+    model.train_on_batch(input_val, None,
+                         sample_weight={'dense_a': np.random.random((10,))})
+
+
 if __name__ == '__main__':
     pytest.main([__file__])
