evaluator.py 3.3 KB
Newer Older
H
Hui Zhang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
H
Hui Zhang 已提交
14 15 16
from typing import Dict

import paddle
H
Hui Zhang 已提交
17
from paddle import distributed as dist
H
Hui Zhang 已提交
18 19 20
from paddle.io import DataLoader
from paddle.nn import Layer

H
Hui Zhang 已提交
21
from . import extension
H
Hui Zhang 已提交
22 23 24
from ..reporter import DictSummary
from ..reporter import report
from ..reporter import scope
H
Hui Zhang 已提交
25 26 27
from ..timer import Timer
from deepspeech.utils.log import Log
logger = Log(__name__).getlog()
H
Hui Zhang 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49


class StandardEvaluator(extension.Extension):

    trigger = (1, 'epoch')
    default_name = 'validation'
    priority = extension.PRIORITY_WRITER

    name = None

    def __init__(self, model: Layer, dataloader: DataLoader):
        # it is designed to hold multiple models
        models = {"main": model}
        self.models: Dict[str, Layer] = models
        self.model = model

        # dataloaders
        self.dataloader = dataloader

    def evaluate_core(self, batch):
        # compute
        self.model(batch)  # you may report here
H
Hui Zhang 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
        return

    def evaluate_sync(self, data):
        # dist sync `evaluate_core` outputs
        if data is None:
            return

        numerator, denominator = data
        if dist.get_world_size() > 1:
            numerator = paddle.to_tensor(numerator)
            denominator = paddle.to_tensor(denominator)
            # the default operator in all_reduce function is sum.
            dist.all_reduce(numerator)
            dist.all_reduce(denominator)
            value = numerator / denominator
            value = float(value)
        else:
            value = numerator / denominator
        # used for `snapshort` to do kbest save.
        report("VALID/LOSS", value)
        logger.info(f"Valid: all-reduce loss {value}")
H
Hui Zhang 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83

    def evaluate(self):
        # switch to eval mode
        for model in self.models.values():
            model.eval()

        # to average evaluation metrics
        summary = DictSummary()
        for batch in self.dataloader:
            observation = {}
            with scope(observation):
                # main evaluation computation here.
                with paddle.no_grad():
H
Hui Zhang 已提交
84
                    self.evaluate_sync(self.evaluate_core(batch))
H
Hui Zhang 已提交
85 86
            summary.add(observation)
        summary = summary.compute_mean()
H
Hui Zhang 已提交
87 88 89 90

        # switch to train mode
        for model in self.models.values():
            model.train()
H
Hui Zhang 已提交
91 92 93 94 95 96 97
        return summary

    def __call__(self, trainer=None):
        # evaluate and report the averaged metric to current observation
        # if it is used to extend a trainer, the metrics is reported to
        # to observation of the trainer
        # or otherwise, you can use your own observation
H
Hui Zhang 已提交
98 99
        with Timer("Eval Time Cost: {}"):
            summary = self.evaluate()
H
Hui Zhang 已提交
100
        for k, v in summary.items():
H
Hui Zhang 已提交
101
            report(k, v)