We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Meaning of Recognition Scores
I am using ABINet to do some text recognition on TorchServe, and my question is very similar to https://github.com/open-mmlab/mmocr/issues/1092
However, in my case I modified https://github.com/open-mmlab/mmocr/blob/main/tools/deployment/mmocr_handler.py#L46 to be `results = model_inference(self.model, data, batch_mode=True)` so the result I got has multiple scores instead of 1:
```
(open-mmlab) root@d40b21f4becb:/mmocr# curl -X POST http://localhost:8080/predictions/ABINet -T "{images435/forms_f1120_0_1628795799/0.png,images435/forms_f1120_0_1628795799/1.png}"
{
  "text": "us",
  "score": [
    18.370647430419922,
    15.632901191711426
  ]
}{
  "text": "corporation",
  "score": [
    23.138269424438477,
    21.309354782104492,
    21.27399444580078,
    25.794034957885742,
    22.265600204467773,
    22.63265037536621,
    22.026500701904297,
    20.361919403076172,
    19.674522399902344,
    19.15252113342285,
    20.794090270996094
  ]
```
My questions are:
1. What exactly do the scores mean here? 
2. What is the range of score? 
3. Why there are multiple scores for a single image?
4. Why sometimes I have 2 scores but sometimes I have many more scores? (I noticed that the number of scores is not fixed)

I have been trying to looking at source codes but I couldn't find exactly where it's leading this to happen. Any help is appreciated. 
Meaning of Recognition Scores
I am using ABINet to do some text recognition on TorchServe, and my question is very similar to https://github.com/open-mmlab/mmocr/issues/1092
However, in my case I modified https://github.com/open-mmlab/mmocr/blob/main/tools/deployment/mmocr_handler.py#L46 to be `results = model_inference(self.model, data, batch_mode=True)` so the result I got has multiple scores instead of 1:
```
(open-mmlab) root@d40b21f4becb:/mmocr# curl -X POST http://localhost:8080/predictions/ABINet -T "{images435/forms_f1120_0_1628795799/0.png,images435/forms_f1120_0_1628795799/1.png}"
{
  "text": "us",
  "score": [
    18.370647430419922,
    15.632901191711426
  ]
}{
  "text": "corporation",
  "score": [
    23.138269424438477,
    21.309354782104492,
    21.27399444580078,
    25.794034957885742,
    22.265600204467773,
    22.63265037536621,
    22.026500701904297,
    20.361919403076172,
    19.674522399902344,
    19.15252113342285,
    20.794090270996094
  ]
```
My questions are:
1. What exactly do the scores mean here? 
2. What is the range of score? 
3. Why there are multiple scores for a single image?
4. Why sometimes I have 2 scores but sometimes I have many more scores? (I noticed that the number of scores is not fixed)

I have been trying to looking at source codes but I couldn't find exactly where it's leading this to happen. Any help is appreciated. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mmocr/models/textrecog/convertors/attn.py`
Content:
```
1 # Copyright (c) OpenMMLab. All rights reserved.
2 import torch
3 
4 import mmocr.utils as utils
5 from mmocr.models.builder import CONVERTORS
6 from .base import BaseConvertor
7 
8 
9 @CONVERTORS.register_module()
10 class AttnConvertor(BaseConvertor):
11     """Convert between text, index and tensor for encoder-decoder based
12     pipeline.
13 
14     Args:
15         dict_type (str): Type of dict, should be one of {'DICT36', 'DICT90'}.
16         dict_file (None|str): Character dict file path. If not none,
17             higher priority than dict_type.
18         dict_list (None|list[str]): Character list. If not none, higher
19             priority than dict_type, but lower than dict_file.
20         with_unknown (bool): If True, add `UKN` token to class.
21         max_seq_len (int): Maximum sequence length of label.
22         lower (bool): If True, convert original string to lower case.
23         start_end_same (bool): Whether use the same index for
24             start and end token or not. Default: True.
25     """
26 
27     def __init__(self,
28                  dict_type='DICT90',
29                  dict_file=None,
30                  dict_list=None,
31                  with_unknown=True,
32                  max_seq_len=40,
33                  lower=False,
34                  start_end_same=True,
35                  **kwargs):
36         super().__init__(dict_type, dict_file, dict_list)
37         assert isinstance(with_unknown, bool)
38         assert isinstance(max_seq_len, int)
39         assert isinstance(lower, bool)
40 
41         self.with_unknown = with_unknown
42         self.max_seq_len = max_seq_len
43         self.lower = lower
44         self.start_end_same = start_end_same
45 
46         self.update_dict()
47 
48     def update_dict(self):
49         start_end_token = '<BOS/EOS>'
50         unknown_token = '<UKN>'
51         padding_token = '<PAD>'
52 
53         # unknown
54         self.unknown_idx = None
55         if self.with_unknown:
56             self.idx2char.append(unknown_token)
57             self.unknown_idx = len(self.idx2char) - 1
58 
59         # BOS/EOS
60         self.idx2char.append(start_end_token)
61         self.start_idx = len(self.idx2char) - 1
62         if not self.start_end_same:
63             self.idx2char.append(start_end_token)
64         self.end_idx = len(self.idx2char) - 1
65 
66         # padding
67         self.idx2char.append(padding_token)
68         self.padding_idx = len(self.idx2char) - 1
69 
70         # update char2idx
71         self.char2idx = {}
72         for idx, char in enumerate(self.idx2char):
73             self.char2idx[char] = idx
74 
75     def str2tensor(self, strings):
76         """
77         Convert text-string into tensor.
78         Args:
79             strings (list[str]): ['hello', 'world']
80         Returns:
81             dict (str: Tensor | list[tensor]):
82                 tensors (list[Tensor]): [torch.Tensor([1,2,3,3,4]),
83                                                     torch.Tensor([5,4,6,3,7])]
84                 padded_targets (Tensor(bsz * max_seq_len))
85         """
86         assert utils.is_type_list(strings, str)
87 
88         tensors, padded_targets = [], []
89         indexes = self.str2idx(strings)
90         for index in indexes:
91             tensor = torch.LongTensor(index)
92             tensors.append(tensor)
93             # target tensor for loss
94             src_target = torch.LongTensor(tensor.size(0) + 2).fill_(0)
95             src_target[-1] = self.end_idx
96             src_target[0] = self.start_idx
97             src_target[1:-1] = tensor
98             padded_target = (torch.ones(self.max_seq_len) *
99                              self.padding_idx).long()
100             char_num = src_target.size(0)
101             if char_num > self.max_seq_len:
102                 padded_target = src_target[:self.max_seq_len]
103             else:
104                 padded_target[:char_num] = src_target
105             padded_targets.append(padded_target)
106         padded_targets = torch.stack(padded_targets, 0).long()
107 
108         return {'targets': tensors, 'padded_targets': padded_targets}
109 
110     def tensor2idx(self, outputs, img_metas=None):
111         """
112         Convert output tensor to text-index
113         Args:
114             outputs (tensor): model outputs with size: N * T * C
115             img_metas (list[dict]): Each dict contains one image info.
116         Returns:
117             indexes (list[list[int]]): [[1,2,3,3,4], [5,4,6,3,7]]
118             scores (list[list[float]]): [[0.9,0.8,0.95,0.97,0.94],
119                                          [0.9,0.9,0.98,0.97,0.96]]
120         """
121         batch_size = outputs.size(0)
122         ignore_indexes = [self.padding_idx]
123         indexes, scores = [], []
124         for idx in range(batch_size):
125             seq = outputs[idx, :, :]
126             max_value, max_idx = torch.max(seq, -1)
127             str_index, str_score = [], []
128             output_index = max_idx.cpu().detach().numpy().tolist()
129             output_score = max_value.cpu().detach().numpy().tolist()
130             for char_index, char_score in zip(output_index, output_score):
131                 if char_index in ignore_indexes:
132                     continue
133                 if char_index == self.end_idx:
134                     break
135                 str_index.append(char_index)
136                 str_score.append(char_score)
137 
138             indexes.append(str_index)
139             scores.append(str_score)
140 
141         return indexes, scores
142
```
--- 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/mmocr/models/textrecog/convertors/attn.py b/mmocr/models/textrecog/convertors/attn.py
--- a/mmocr/models/textrecog/convertors/attn.py
+++ b/mmocr/models/textrecog/convertors/attn.py
@@ -123,6 +123,7 @@
         indexes, scores = [], []
         for idx in range(batch_size):
             seq = outputs[idx, :, :]
+            seq = seq.softmax(dim=-1)
             max_value, max_idx = torch.max(seq, -1)
             str_index, str_score = [], []
             output_index = max_idx.cpu().detach().numpy().tolist()

{"golden_diff": "diff --git a/mmocr/models/textrecog/convertors/attn.py b/mmocr/models/textrecog/convertors/attn.py\n--- a/mmocr/models/textrecog/convertors/attn.py\n+++ b/mmocr/models/textrecog/convertors/attn.py\n@@ -123,6 +123,7 @@\n         indexes, scores = [], []\n         for idx in range(batch_size):\n             seq = outputs[idx, :, :]\n+            seq = seq.softmax(dim=-1)\n             max_value, max_idx = torch.max(seq, -1)\n             str_index, str_score = [], []\n             output_index = max_idx.cpu().detach().numpy().tolist()\n", "issue": "Meaning of Recognition Scores\nI am using ABINet to do some text recognition on TorchServe, and my question is very similar to https://github.com/open-mmlab/mmocr/issues/1092\r\nHowever, in my case I modified https://github.com/open-mmlab/mmocr/blob/main/tools/deployment/mmocr_handler.py#L46 to be `results = model_inference(self.model, data, batch_mode=True)` so the result I got has multiple scores instead of 1:\r\n```\r\n(open-mmlab) root@d40b21f4becb:/mmocr# curl -X POST http://localhost:8080/predictions/ABINet -T \"{images435/forms_f1120_0_1628795799/0.png,images435/forms_f1120_0_1628795799/1.png}\"\r\n{\r\n  \"text\": \"us\",\r\n  \"score\": [\r\n    18.370647430419922,\r\n    15.632901191711426\r\n  ]\r\n}{\r\n  \"text\": \"corporation\",\r\n  \"score\": [\r\n    23.138269424438477,\r\n    21.309354782104492,\r\n    21.27399444580078,\r\n    25.794034957885742,\r\n    22.265600204467773,\r\n    22.63265037536621,\r\n    22.026500701904297,\r\n    20.361919403076172,\r\n    19.674522399902344,\r\n    19.15252113342285,\r\n    20.794090270996094\r\n  ]\r\n```\r\nMy questions are:\r\n1. What exactly do the scores mean here? \r\n2. What is the range of score? \r\n3. Why there are multiple scores for a single image?\r\n4. Why sometimes I have 2 scores but sometimes I have many more scores? (I noticed that the number of scores is not fixed)\r\n\r\nI have been trying to looking at source codes but I couldn't find exactly where it's leading this to happen. Any help is appreciated. \nMeaning of Recognition Scores\nI am using ABINet to do some text recognition on TorchServe, and my question is very similar to https://github.com/open-mmlab/mmocr/issues/1092\r\nHowever, in my case I modified https://github.com/open-mmlab/mmocr/blob/main/tools/deployment/mmocr_handler.py#L46 to be `results = model_inference(self.model, data, batch_mode=True)` so the result I got has multiple scores instead of 1:\r\n```\r\n(open-mmlab) root@d40b21f4becb:/mmocr# curl -X POST http://localhost:8080/predictions/ABINet -T \"{images435/forms_f1120_0_1628795799/0.png,images435/forms_f1120_0_1628795799/1.png}\"\r\n{\r\n  \"text\": \"us\",\r\n  \"score\": [\r\n    18.370647430419922,\r\n    15.632901191711426\r\n  ]\r\n}{\r\n  \"text\": \"corporation\",\r\n  \"score\": [\r\n    23.138269424438477,\r\n    21.309354782104492,\r\n    21.27399444580078,\r\n    25.794034957885742,\r\n    22.265600204467773,\r\n    22.63265037536621,\r\n    22.026500701904297,\r\n    20.361919403076172,\r\n    19.674522399902344,\r\n    19.15252113342285,\r\n    20.794090270996094\r\n  ]\r\n```\r\nMy questions are:\r\n1. What exactly do the scores mean here? \r\n2. What is the range of score? \r\n3. Why there are multiple scores for a single image?\r\n4. Why sometimes I have 2 scores but sometimes I have many more scores? (I noticed that the number of scores is not fixed)\r\n\r\nI have been trying to looking at source codes but I couldn't find exactly where it's leading this to happen. Any help is appreciated. \n", "before_files": [{"content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\nimport mmocr.utils as utils\nfrom mmocr.models.builder import CONVERTORS\nfrom .base import BaseConvertor\n\n\n@CONVERTORS.register_module()\nclass AttnConvertor(BaseConvertor):\n    \"\"\"Convert between text, index and tensor for encoder-decoder based\n    pipeline.\n\n    Args:\n        dict_type (str): Type of dict, should be one of {'DICT36', 'DICT90'}.\n        dict_file (None|str): Character dict file path. If not none,\n            higher priority than dict_type.\n        dict_list (None|list[str]): Character list. If not none, higher\n            priority than dict_type, but lower than dict_file.\n        with_unknown (bool): If True, add `UKN` token to class.\n        max_seq_len (int): Maximum sequence length of label.\n        lower (bool): If True, convert original string to lower case.\n        start_end_same (bool): Whether use the same index for\n            start and end token or not. Default: True.\n    \"\"\"\n\n    def __init__(self,\n                 dict_type='DICT90',\n                 dict_file=None,\n                 dict_list=None,\n                 with_unknown=True,\n                 max_seq_len=40,\n                 lower=False,\n                 start_end_same=True,\n                 **kwargs):\n        super().__init__(dict_type, dict_file, dict_list)\n        assert isinstance(with_unknown, bool)\n        assert isinstance(max_seq_len, int)\n        assert isinstance(lower, bool)\n\n        self.with_unknown = with_unknown\n        self.max_seq_len = max_seq_len\n        self.lower = lower\n        self.start_end_same = start_end_same\n\n        self.update_dict()\n\n    def update_dict(self):\n        start_end_token = '<BOS/EOS>'\n        unknown_token = '<UKN>'\n        padding_token = '<PAD>'\n\n        # unknown\n        self.unknown_idx = None\n        if self.with_unknown:\n            self.idx2char.append(unknown_token)\n            self.unknown_idx = len(self.idx2char) - 1\n\n        # BOS/EOS\n        self.idx2char.append(start_end_token)\n        self.start_idx = len(self.idx2char) - 1\n        if not self.start_end_same:\n            self.idx2char.append(start_end_token)\n        self.end_idx = len(self.idx2char) - 1\n\n        # padding\n        self.idx2char.append(padding_token)\n        self.padding_idx = len(self.idx2char) - 1\n\n        # update char2idx\n        self.char2idx = {}\n        for idx, char in enumerate(self.idx2char):\n            self.char2idx[char] = idx\n\n    def str2tensor(self, strings):\n        \"\"\"\n        Convert text-string into tensor.\n        Args:\n            strings (list[str]): ['hello', 'world']\n        Returns:\n            dict (str: Tensor | list[tensor]):\n                tensors (list[Tensor]): [torch.Tensor([1,2,3,3,4]),\n                                                    torch.Tensor([5,4,6,3,7])]\n                padded_targets (Tensor(bsz * max_seq_len))\n        \"\"\"\n        assert utils.is_type_list(strings, str)\n\n        tensors, padded_targets = [], []\n        indexes = self.str2idx(strings)\n        for index in indexes:\n            tensor = torch.LongTensor(index)\n            tensors.append(tensor)\n            # target tensor for loss\n            src_target = torch.LongTensor(tensor.size(0) + 2).fill_(0)\n            src_target[-1] = self.end_idx\n            src_target[0] = self.start_idx\n            src_target[1:-1] = tensor\n            padded_target = (torch.ones(self.max_seq_len) *\n                             self.padding_idx).long()\n            char_num = src_target.size(0)\n            if char_num > self.max_seq_len:\n                padded_target = src_target[:self.max_seq_len]\n            else:\n                padded_target[:char_num] = src_target\n            padded_targets.append(padded_target)\n        padded_targets = torch.stack(padded_targets, 0).long()\n\n        return {'targets': tensors, 'padded_targets': padded_targets}\n\n    def tensor2idx(self, outputs, img_metas=None):\n        \"\"\"\n        Convert output tensor to text-index\n        Args:\n            outputs (tensor): model outputs with size: N * T * C\n            img_metas (list[dict]): Each dict contains one image info.\n        Returns:\n            indexes (list[list[int]]): [[1,2,3,3,4], [5,4,6,3,7]]\n            scores (list[list[float]]): [[0.9,0.8,0.95,0.97,0.94],\n                                         [0.9,0.9,0.98,0.97,0.96]]\n        \"\"\"\n        batch_size = outputs.size(0)\n        ignore_indexes = [self.padding_idx]\n        indexes, scores = [], []\n        for idx in range(batch_size):\n            seq = outputs[idx, :, :]\n            max_value, max_idx = torch.max(seq, -1)\n            str_index, str_score = [], []\n            output_index = max_idx.cpu().detach().numpy().tolist()\n            output_score = max_value.cpu().detach().numpy().tolist()\n            for char_index, char_score in zip(output_index, output_score):\n                if char_index in ignore_indexes:\n                    continue\n                if char_index == self.end_idx:\n                    break\n                str_index.append(char_index)\n                str_score.append(char_score)\n\n            indexes.append(str_index)\n            scores.append(str_score)\n\n        return indexes, scores\n", "path": "mmocr/models/textrecog/convertors/attn.py"}], "after_files": [{"content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport torch\n\nimport mmocr.utils as utils\nfrom mmocr.models.builder import CONVERTORS\nfrom .base import BaseConvertor\n\n\n@CONVERTORS.register_module()\nclass AttnConvertor(BaseConvertor):\n    \"\"\"Convert between text, index and tensor for encoder-decoder based\n    pipeline.\n\n    Args:\n        dict_type (str): Type of dict, should be one of {'DICT36', 'DICT90'}.\n        dict_file (None|str): Character dict file path. If not none,\n            higher priority than dict_type.\n        dict_list (None|list[str]): Character list. If not none, higher\n            priority than dict_type, but lower than dict_file.\n        with_unknown (bool): If True, add `UKN` token to class.\n        max_seq_len (int): Maximum sequence length of label.\n        lower (bool): If True, convert original string to lower case.\n        start_end_same (bool): Whether use the same index for\n            start and end token or not. Default: True.\n    \"\"\"\n\n    def __init__(self,\n                 dict_type='DICT90',\n                 dict_file=None,\n                 dict_list=None,\n                 with_unknown=True,\n                 max_seq_len=40,\n                 lower=False,\n                 start_end_same=True,\n                 **kwargs):\n        super().__init__(dict_type, dict_file, dict_list)\n        assert isinstance(with_unknown, bool)\n        assert isinstance(max_seq_len, int)\n        assert isinstance(lower, bool)\n\n        self.with_unknown = with_unknown\n        self.max_seq_len = max_seq_len\n        self.lower = lower\n        self.start_end_same = start_end_same\n\n        self.update_dict()\n\n    def update_dict(self):\n        start_end_token = '<BOS/EOS>'\n        unknown_token = '<UKN>'\n        padding_token = '<PAD>'\n\n        # unknown\n        self.unknown_idx = None\n        if self.with_unknown:\n            self.idx2char.append(unknown_token)\n            self.unknown_idx = len(self.idx2char) - 1\n\n        # BOS/EOS\n        self.idx2char.append(start_end_token)\n        self.start_idx = len(self.idx2char) - 1\n        if not self.start_end_same:\n            self.idx2char.append(start_end_token)\n        self.end_idx = len(self.idx2char) - 1\n\n        # padding\n        self.idx2char.append(padding_token)\n        self.padding_idx = len(self.idx2char) - 1\n\n        # update char2idx\n        self.char2idx = {}\n        for idx, char in enumerate(self.idx2char):\n            self.char2idx[char] = idx\n\n    def str2tensor(self, strings):\n        \"\"\"\n        Convert text-string into tensor.\n        Args:\n            strings (list[str]): ['hello', 'world']\n        Returns:\n            dict (str: Tensor | list[tensor]):\n                tensors (list[Tensor]): [torch.Tensor([1,2,3,3,4]),\n                                                    torch.Tensor([5,4,6,3,7])]\n                padded_targets (Tensor(bsz * max_seq_len))\n        \"\"\"\n        assert utils.is_type_list(strings, str)\n\n        tensors, padded_targets = [], []\n        indexes = self.str2idx(strings)\n        for index in indexes:\n            tensor = torch.LongTensor(index)\n            tensors.append(tensor)\n            # target tensor for loss\n            src_target = torch.LongTensor(tensor.size(0) + 2).fill_(0)\n            src_target[-1] = self.end_idx\n            src_target[0] = self.start_idx\n            src_target[1:-1] = tensor\n            padded_target = (torch.ones(self.max_seq_len) *\n                             self.padding_idx).long()\n            char_num = src_target.size(0)\n            if char_num > self.max_seq_len:\n                padded_target = src_target[:self.max_seq_len]\n            else:\n                padded_target[:char_num] = src_target\n            padded_targets.append(padded_target)\n        padded_targets = torch.stack(padded_targets, 0).long()\n\n        return {'targets': tensors, 'padded_targets': padded_targets}\n\n    def tensor2idx(self, outputs, img_metas=None):\n        \"\"\"\n        Convert output tensor to text-index\n        Args:\n            outputs (tensor): model outputs with size: N * T * C\n            img_metas (list[dict]): Each dict contains one image info.\n        Returns:\n            indexes (list[list[int]]): [[1,2,3,3,4], [5,4,6,3,7]]\n            scores (list[list[float]]): [[0.9,0.8,0.95,0.97,0.94],\n                                         [0.9,0.9,0.98,0.97,0.96]]\n        \"\"\"\n        batch_size = outputs.size(0)\n        ignore_indexes = [self.padding_idx]\n        indexes, scores = [], []\n        for idx in range(batch_size):\n            seq = outputs[idx, :, :]\n            seq = seq.softmax(dim=-1)\n            max_value, max_idx = torch.max(seq, -1)\n            str_index, str_score = [], []\n            output_index = max_idx.cpu().detach().numpy().tolist()\n            output_score = max_value.cpu().detach().numpy().tolist()\n            for char_index, char_score in zip(output_index, output_score):\n                if char_index in ignore_indexes:\n                    continue\n                if char_index == self.end_idx:\n                    break\n                str_index.append(char_index)\n                str_score.append(char_score)\n\n            indexes.append(str_index)\n            scores.append(str_score)\n\n        return indexes, scores\n", "path": "mmocr/models/textrecog/convertors/attn.py"}]}