We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
EarlyStoppingCallback considers first epoch as bad
## 🐛 Bug Report
EarlyStoppingCallback considers first epoch as bad. This can lead for example to always stopping after first epoch if patience=1.


### How To Reproduce
You can train a model with early stopping and patience=1 and see that it always stops after first epoch. Or you can use the unit test below that I added to pull request.

#### Code sample
```python
from unittest.mock import MagicMock, PropertyMock

from catalyst.core import EarlyStoppingCallback


def test_patience1():
    """@TODO: Docs. Contribution is welcome."""
    early_stop = EarlyStoppingCallback(1)
    runner = MagicMock()
    type(runner).stage_name = PropertyMock(return_value="training")
    type(runner).valid_metrics = PropertyMock(return_value={"loss": 0.001})
    stop_mock = PropertyMock(return_value=False)
    type(runner).need_early_stop = stop_mock

    early_stop.on_epoch_end(runner)

    assert stop_mock.mock_calls == []
```

### Expected behavior
Training doesn't stop after first epoch. And the unit test passes.


### Environment
```bash
Catalyst version: 20.06
PyTorch version: 1.5.1
Is debug build: No
CUDA used to build PyTorch: None
TensorFlow version: N/A
TensorBoard version: 2.2.2

OS: Mac OSX 10.15.5
GCC version: Could not collect
CMake version: version 3.8.0

Python version: 3.7
Is CUDA available: No
CUDA runtime version: No CUDA
GPU models and configuration: No CUDA
Nvidia driver version: No CUDA
cuDNN version: No CUDA

Versions of relevant libraries:
[pip3] catalyst-codestyle==20.4
[pip3] catalyst-sphinx-theme==1.1.1
[pip3] efficientnet-pytorch==0.6.3
[pip3] numpy==1.18.5
[pip3] segmentation-models-pytorch==0.1.0
[pip3] tensorboard==2.2.2
[pip3] tensorboard-plugin-wit==1.6.0.post3
[pip3] tensorboardX==2.0
[pip3] torch==1.5.1
[pip3] torchvision==0.6.1
[conda] catalyst-codestyle        20.4                      <pip>
[conda] catalyst-sphinx-theme     1.1.1                     <pip>
[conda] efficientnet-pytorch      0.6.3                     <pip>
[conda] numpy                     1.18.5                    <pip>
[conda] segmentation-models-pytorch 0.1.0                     <pip>
[conda] tensorboard               2.2.2                     <pip>
[conda] tensorboard-plugin-wit    1.6.0.post3               <pip>
[conda] tensorboardX              2.0                       <pip>
[conda] torch                     1.5.1                     <pip>
[conda] torchvision               0.6.1                     <pip>
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `catalyst/core/callbacks/early_stop.py`
Content:
```
1 from catalyst.core.callback import Callback, CallbackNode, CallbackOrder
2 from catalyst.core.runner import IRunner
3 
4 
5 class CheckRunCallback(Callback):
6     """@TODO: Docs. Contribution is welcome."""
7 
8     def __init__(self, num_batch_steps: int = 3, num_epoch_steps: int = 2):
9         """@TODO: Docs. Contribution is welcome."""
10         super().__init__(order=CallbackOrder.external, node=CallbackNode.all)
11         self.num_batch_steps = num_batch_steps
12         self.num_epoch_steps = num_epoch_steps
13 
14     def on_epoch_end(self, runner: IRunner):
15         """@TODO: Docs. Contribution is welcome."""
16         if runner.epoch >= self.num_epoch_steps:
17             runner.need_early_stop = True
18 
19     def on_batch_end(self, runner: IRunner):
20         """@TODO: Docs. Contribution is welcome."""
21         if runner.loader_batch_step >= self.num_batch_steps:
22             runner.need_early_stop = True
23 
24 
25 class EarlyStoppingCallback(Callback):
26     """@TODO: Docs. Contribution is welcome."""
27 
28     def __init__(
29         self,
30         patience: int,
31         metric: str = "loss",
32         minimize: bool = True,
33         min_delta: float = 1e-6,
34     ):
35         """@TODO: Docs. Contribution is welcome."""
36         super().__init__(order=CallbackOrder.external, node=CallbackNode.all)
37         self.best_score = None
38         self.metric = metric
39         self.patience = patience
40         self.num_bad_epochs = 0
41         self.is_better = None
42 
43         if minimize:
44             self.is_better = lambda score, best: score <= (best - min_delta)
45         else:
46             self.is_better = lambda score, best: score >= (best + min_delta)
47 
48     def on_epoch_end(self, runner: IRunner) -> None:
49         """@TODO: Docs. Contribution is welcome."""
50         if runner.stage_name.startswith("infer"):
51             return
52 
53         score = runner.valid_metrics[self.metric]
54         if self.best_score is None:
55             self.best_score = score
56         if self.is_better(score, self.best_score):
57             self.num_bad_epochs = 0
58             self.best_score = score
59         else:
60             self.num_bad_epochs += 1
61 
62         if self.num_bad_epochs >= self.patience:
63             print(f"Early stop at {runner.epoch} epoch")
64             runner.need_early_stop = True
65
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/catalyst/core/callbacks/early_stop.py b/catalyst/core/callbacks/early_stop.py
--- a/catalyst/core/callbacks/early_stop.py
+++ b/catalyst/core/callbacks/early_stop.py
@@ -51,9 +51,7 @@
             return
 
         score = runner.valid_metrics[self.metric]
-        if self.best_score is None:
-            self.best_score = score
-        if self.is_better(score, self.best_score):
+        if self.best_score is None or self.is_better(score, self.best_score):
             self.num_bad_epochs = 0
             self.best_score = score
         else:

{"golden_diff": "diff --git a/catalyst/core/callbacks/early_stop.py b/catalyst/core/callbacks/early_stop.py\n--- a/catalyst/core/callbacks/early_stop.py\n+++ b/catalyst/core/callbacks/early_stop.py\n@@ -51,9 +51,7 @@\n             return\n \n         score = runner.valid_metrics[self.metric]\n-        if self.best_score is None:\n-            self.best_score = score\n-        if self.is_better(score, self.best_score):\n+        if self.best_score is None or self.is_better(score, self.best_score):\n             self.num_bad_epochs = 0\n             self.best_score = score\n         else:\n", "issue": "EarlyStoppingCallback considers first epoch as bad\n## \ud83d\udc1b Bug Report\r\nEarlyStoppingCallback considers first epoch as bad. This can lead for example to always stopping after first epoch if patience=1.\r\n\r\n\r\n### How To Reproduce\r\nYou can train a model with early stopping and patience=1 and see that it always stops after first epoch. Or you can use the unit test below that I added to pull request.\r\n\r\n#### Code sample\r\n```python\r\nfrom unittest.mock import MagicMock, PropertyMock\r\n\r\nfrom catalyst.core import EarlyStoppingCallback\r\n\r\n\r\ndef test_patience1():\r\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\r\n    early_stop = EarlyStoppingCallback(1)\r\n    runner = MagicMock()\r\n    type(runner).stage_name = PropertyMock(return_value=\"training\")\r\n    type(runner).valid_metrics = PropertyMock(return_value={\"loss\": 0.001})\r\n    stop_mock = PropertyMock(return_value=False)\r\n    type(runner).need_early_stop = stop_mock\r\n\r\n    early_stop.on_epoch_end(runner)\r\n\r\n    assert stop_mock.mock_calls == []\r\n```\r\n\r\n### Expected behavior\r\nTraining doesn't stop after first epoch. And the unit test passes.\r\n\r\n\r\n### Environment\r\n```bash\r\nCatalyst version: 20.06\r\nPyTorch version: 1.5.1\r\nIs debug build: No\r\nCUDA used to build PyTorch: None\r\nTensorFlow version: N/A\r\nTensorBoard version: 2.2.2\r\n\r\nOS: Mac OSX 10.15.5\r\nGCC version: Could not collect\r\nCMake version: version 3.8.0\r\n\r\nPython version: 3.7\r\nIs CUDA available: No\r\nCUDA runtime version: No CUDA\r\nGPU models and configuration: No CUDA\r\nNvidia driver version: No CUDA\r\ncuDNN version: No CUDA\r\n\r\nVersions of relevant libraries:\r\n[pip3] catalyst-codestyle==20.4\r\n[pip3] catalyst-sphinx-theme==1.1.1\r\n[pip3] efficientnet-pytorch==0.6.3\r\n[pip3] numpy==1.18.5\r\n[pip3] segmentation-models-pytorch==0.1.0\r\n[pip3] tensorboard==2.2.2\r\n[pip3] tensorboard-plugin-wit==1.6.0.post3\r\n[pip3] tensorboardX==2.0\r\n[pip3] torch==1.5.1\r\n[pip3] torchvision==0.6.1\r\n[conda] catalyst-codestyle        20.4                      <pip>\r\n[conda] catalyst-sphinx-theme     1.1.1                     <pip>\r\n[conda] efficientnet-pytorch      0.6.3                     <pip>\r\n[conda] numpy                     1.18.5                    <pip>\r\n[conda] segmentation-models-pytorch 0.1.0                     <pip>\r\n[conda] tensorboard               2.2.2                     <pip>\r\n[conda] tensorboard-plugin-wit    1.6.0.post3               <pip>\r\n[conda] tensorboardX              2.0                       <pip>\r\n[conda] torch                     1.5.1                     <pip>\r\n[conda] torchvision               0.6.1                     <pip>\r\n```\r\n\n", "before_files": [{"content": "from catalyst.core.callback import Callback, CallbackNode, CallbackOrder\nfrom catalyst.core.runner import IRunner\n\n\nclass CheckRunCallback(Callback):\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n\n    def __init__(self, num_batch_steps: int = 3, num_epoch_steps: int = 2):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        super().__init__(order=CallbackOrder.external, node=CallbackNode.all)\n        self.num_batch_steps = num_batch_steps\n        self.num_epoch_steps = num_epoch_steps\n\n    def on_epoch_end(self, runner: IRunner):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.epoch >= self.num_epoch_steps:\n            runner.need_early_stop = True\n\n    def on_batch_end(self, runner: IRunner):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.loader_batch_step >= self.num_batch_steps:\n            runner.need_early_stop = True\n\n\nclass EarlyStoppingCallback(Callback):\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n\n    def __init__(\n        self,\n        patience: int,\n        metric: str = \"loss\",\n        minimize: bool = True,\n        min_delta: float = 1e-6,\n    ):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        super().__init__(order=CallbackOrder.external, node=CallbackNode.all)\n        self.best_score = None\n        self.metric = metric\n        self.patience = patience\n        self.num_bad_epochs = 0\n        self.is_better = None\n\n        if minimize:\n            self.is_better = lambda score, best: score <= (best - min_delta)\n        else:\n            self.is_better = lambda score, best: score >= (best + min_delta)\n\n    def on_epoch_end(self, runner: IRunner) -> None:\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.stage_name.startswith(\"infer\"):\n            return\n\n        score = runner.valid_metrics[self.metric]\n        if self.best_score is None:\n            self.best_score = score\n        if self.is_better(score, self.best_score):\n            self.num_bad_epochs = 0\n            self.best_score = score\n        else:\n            self.num_bad_epochs += 1\n\n        if self.num_bad_epochs >= self.patience:\n            print(f\"Early stop at {runner.epoch} epoch\")\n            runner.need_early_stop = True\n", "path": "catalyst/core/callbacks/early_stop.py"}], "after_files": [{"content": "from catalyst.core.callback import Callback, CallbackNode, CallbackOrder\nfrom catalyst.core.runner import IRunner\n\n\nclass CheckRunCallback(Callback):\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n\n    def __init__(self, num_batch_steps: int = 3, num_epoch_steps: int = 2):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        super().__init__(order=CallbackOrder.external, node=CallbackNode.all)\n        self.num_batch_steps = num_batch_steps\n        self.num_epoch_steps = num_epoch_steps\n\n    def on_epoch_end(self, runner: IRunner):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.epoch >= self.num_epoch_steps:\n            runner.need_early_stop = True\n\n    def on_batch_end(self, runner: IRunner):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.loader_batch_step >= self.num_batch_steps:\n            runner.need_early_stop = True\n\n\nclass EarlyStoppingCallback(Callback):\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n\n    def __init__(\n        self,\n        patience: int,\n        metric: str = \"loss\",\n        minimize: bool = True,\n        min_delta: float = 1e-6,\n    ):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        super().__init__(order=CallbackOrder.external, node=CallbackNode.all)\n        self.best_score = None\n        self.metric = metric\n        self.patience = patience\n        self.num_bad_epochs = 0\n        self.is_better = None\n\n        if minimize:\n            self.is_better = lambda score, best: score <= (best - min_delta)\n        else:\n            self.is_better = lambda score, best: score >= (best + min_delta)\n\n    def on_epoch_end(self, runner: IRunner) -> None:\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.stage_name.startswith(\"infer\"):\n            return\n\n        score = runner.valid_metrics[self.metric]\n        if self.best_score is None or self.is_better(score, self.best_score):\n            self.num_bad_epochs = 0\n            self.best_score = score\n        else:\n            self.num_bad_epochs += 1\n\n        if self.num_bad_epochs >= self.patience:\n            print(f\"Early stop at {runner.epoch} epoch\")\n            runner.need_early_stop = True\n", "path": "catalyst/core/callbacks/early_stop.py"}]}