Repository: pytorch/ignite

Issue:
Improve MetricLambda implementation
User can get the following output in the `state.metrics`:
```
{'accuracy': 0.37127896829244644,
 'error': 0.6287210317075536,
 'error[0]': -0.6287210317075536,
 'error[0][0]': 0.37127896829244644,
```
when launch the below code:
```python
accuracy_metric = Accuracy()
error_metric = (accuracy_metric - 1.0) * (-1.0)

metrics = {
    "accuracy": accuracy_metric,
    "error": error_metric,
}

validator = create_supervised_evaluator(model, metrics=metrics)
validator.run(val_loader, max_epochs=1)
print(validator.state.metrics)
```

This is due to 

https://github.com/pytorch/ignite/blob/d9820451da779e0d0c393804db381e5483240b1c/ignite/metrics/metrics_lambda.py#L50-L54

and 

https://github.com/pytorch/ignite/blob/d9820451da779e0d0c393804db381e5483240b1c/ignite/metrics/metric.py#L68

IMO, user is not interested of this internal info used to compute `error_metric`. We can add some special characters to ignore the result of compute when insert to `engine.state.metrics[name]`.

cc @zasdfgbnm 

Discussion:
vfdev-5: @anmolsjoshi thanks for the PR, however I thought about something like this : 
```python
class Metric(with_metaclass(ABCMeta, object)):

    name_to_ignore = "~MetricNameToIgnore~"
    
    def completed(self, engine, name):
        if self.name_to_ignore not in name:
            engine.state.metrics[name] = self.compute()


class MetricsLambda(Metric):

    def attach(self, engine, name):
        # recursively attach all its dependencies
        for index, metric in enumerate(self.args):
            if isinstance(metric, Metric):
                metric.attach(engine, name + '{}[{}]'.format(self.name_to_ignore, index))
        super(MetricsLambda, self).attach(engine, name)
```


anmolsjoshi: @vfdev-5 your changes have been incorporated. 
vfdev-5: @anmolsjoshi could you please write a test checking that dictionary contains only what we need. Take a test example with a metric composed of several internal metrics, e.g F1
anmolsjoshi: @vfdev-5 Added a test!

Patch:
diff --git a/README.rst b/README.rst
index 5f0253bf..bdedca5f 100644
--- a/README.rst
+++ b/README.rst
@@ -10,6 +10,9 @@ Ignite
 .. image:: https://pepy.tech/badge/pytorch-ignite
     :target: https://pepy.tech/project/pytorch-ignite
 
+.. image:: https://img.shields.io/badge/dynamic/json.svg?label=docs&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fpytorch-ignite%2Fjson&query=%24.info.version&colorB=brightgreen&prefix=v
+    :target: https://pytorch.org/ignite/index.html
+    
 Ignite is a high-level library to help with training neural networks in PyTorch.
 
 - ignite helps you write compact but full-featured training loops in a few lines of code
diff --git a/ignite/metrics/metrics_lambda.py b/ignite/metrics/metrics_lambda.py
index a3e27dcd..22827521 100644
--- a/ignite/metrics/metrics_lambda.py
+++ b/ignite/metrics/metrics_lambda.py
@@ -1,4 +1,5 @@
 from ignite.metrics.metric import Metric
+from ignite.engine import Events
 
 
 class MetricsLambda(Metric):
@@ -12,20 +13,25 @@ class MetricsLambda(Metric):
     resetted. When attach, all its dependencies would be automatically
     attached.
 
-    Arguments:
+    Args:
         f (callable): the function that defines the computation
         args (sequence): Sequence of other metrics or something
             else that will be fed to ``f`` as arguments.
 
-    Examples:
-        >>> precision = Precision(average=False)
-        >>> recall = Recall(average=False)
-        >>> def Fbeta(r, p, beta):
-        >>>     return torch.mean((1 + beta ** 2) * p * r / (beta ** 2 * p + r)).item()
-        >>> F1 = MetricsLambda(Fbeta, recall, precision, 1)
-        >>> F2 = MetricsLambda(Fbeta, recall, precision, 2)
-        >>> F3 = MetricsLambda(Fbeta, recall, precision, 3)
-        >>> F4 = MetricsLambda(Fbeta, recall, precision, 4)
+    Example:
+
+    .. code-block:: python
+
+        precision = Precision(average=False)
+        recall = Recall(average=False)
+
+        def Fbeta(r, p, beta):
+            return torch.mean((1 + beta ** 2) * p * r / (beta ** 2 * p + r + 1e-20)).item()
+
+        F1 = MetricsLambda(Fbeta, recall, precision, 1)
+        F2 = MetricsLambda(Fbeta, recall, precision, 2)
+        F3 = MetricsLambda(Fbeta, recall, precision, 3)
+        F4 = MetricsLambda(Fbeta, recall, precision, 4)
     """
     def __init__(self, f, *args):
         self.function = f
@@ -51,5 +57,8 @@ class MetricsLambda(Metric):
         # recursively attach all its dependencies
         for index, metric in enumerate(self.args):
             if isinstance(metric, Metric):
-                metric.attach(engine, name + '[{}]'.format(index))
+                if not engine.has_event_handler(metric.started, Events.EPOCH_STARTED):
+                    engine.add_event_handler(Events.EPOCH_STARTED, metric.started)
+                if not engine.has_event_handler(metric.iteration_completed, Events.ITERATION_COMPLETED):
+                    engine.add_event_handler(Events.ITERATION_COMPLETED, metric.iteration_completed)
         super(MetricsLambda, self).attach(engine, name)


Test patch:
diff --git a/tests/ignite/metrics/test_metrics_lambda.py b/tests/ignite/metrics/test_metrics_lambda.py
index aa7de4e9..32b660b8 100644
--- a/tests/ignite/metrics/test_metrics_lambda.py
+++ b/tests/ignite/metrics/test_metrics_lambda.py
@@ -121,3 +121,33 @@ def test_integration():
     assert precision_true == approx(precision), "{} vs {}".format(precision_true, precision)
     assert recall_true == approx(recall), "{} vs {}".format(recall_true, recall)
     assert f1_true == approx(state.metrics['f1']), "{} vs {}".format(f1_true, state.metrics['f1'])
+
+
+def test_state_metrics():
+
+    y_pred = torch.randint(0, 2, size=(15, 10, 4)).float()
+    y = torch.randint(0, 2, size=(15, 10, 4)).long()
+
+    def update_fn(engine, batch):
+        y_pred, y = batch
+        return y_pred, y
+
+    evaluator = Engine(update_fn)
+
+    precision = Precision(average=False)
+    recall = Recall(average=False)
+    F1 = precision * recall * 2 / (precision + recall + 1e-20)
+    F1 = MetricsLambda(lambda t: torch.mean(t).item(), F1)
+
+    precision.attach(evaluator, "precision")
+    recall.attach(evaluator, "recall")
+    F1.attach(evaluator, "f1")
+
+    def data(y_pred, y):
+        for i in range(y_pred.shape[0]):
+            yield (y_pred[i], y[i])
+
+    d = data(y_pred, y)
+    state = evaluator.run(d, max_epochs=1)
+
+    assert set(state.metrics.keys()) == set(["precision", "recall", "f1"])