# Copyright 2020 The HuggingFace Evaluate Authors. # Modified in 2026 by SyntaxSpirits: scores with rouge-score-rs instead of rouge-score. # # 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. """ ROUGE metric computed with rouge-score-rs, a Rust implementation of Google's rouge-score. """ import datasets import numpy # Here to have a nice missing dependency error message early on import rouge_score_rs from packaging import version from rouge_score_rs import rouge_scorer, scoring import evaluate if version.parse(rouge_score_rs.__version__) < version.parse("0.1.1"): raise ImportError("This metric requires rouge-score-rs>=0.1.1: pip install -U 'rouge-score-rs[aggregate]>=0.1.1'") _CITATION = """\ @inproceedings{lin-2004-rouge, title = "{ROUGE}: A Package for Automatic Evaluation of Summaries", author = "Lin, Chin-Yew", booktitle = "Text Summarization Branches Out", month = jul, year = "2004", address = "Barcelona, Spain", publisher = "Association for Computational Linguistics", url = "https://www.aclweb.org/anthology/W04-1013", pages = "74--81", } """ _DESCRIPTION = """\ ROUGE, or Recall-Oriented Understudy for Gisting Evaluation, is a set of metrics and a software package used for evaluating automatic summarization and machine translation software in natural language processing. The metrics compare an automatically produced summary or translation against a reference or a set of references (human-produced) summary or translation. With the default tokenizer, ROUGE is case insensitive, meaning that upper case letters are treated the same way as lower case letters. This metric uses rouge-score-rs (https://github.com/SyntaxSpirits/rougers), a Rust implementation of Google Research's reimplementation of ROUGE (https://github.com/google-research/google-research/tree/master/rouge). It gives the same scores as the `rouge` metric for string inputs and tokenizers that return lists of strings, and computes them faster. """ _KWARGS_DESCRIPTION = """ Calculates ROUGE F1 scores for a list of predictions and references. Args: predictions: list of predictions to score, as strings. references: list with one reference string per prediction, or a list of reference strings per prediction. With several references, the one with the highest F1 is used for each ROUGE type. rouge_types: A list of rouge types to calculate. Valid names: `"rouge{n}"` for n from 1 to 9 (e.g. `"rouge1"`, `"rouge2"`): n-gram based scoring, `"rougeL"`: Longest common subsequence based scoring. `"rougeLsum"`: rougeLsum splits text using `"\n"`. See details in https://github.com/huggingface/datasets/issues/617 use_stemmer: Bool indicating whether the default tokenizer should apply the Porter stemmer. use_aggregator: If True, return for each ROUGE type the median of 1,000 bootstrap means of the per-prediction scores (pass `seed` to `evaluate.load` for reproducible values); if False, return one score per prediction. tokenizer: Optional callable splitting a string into a list of string tokens. Lowercasing and stemming are only done by the default tokenizer. Returns: rouge1: rouge_1 (f1), rouge2: rouge_2 (f1), rougeL: rouge_l (f1), rougeLsum: rouge_lsum (f1) Examples: >>> rouge = evaluate.load('SyntaxSpirits/rouge') >>> predictions = ["hello there", "general kenobi"] >>> references = ["hello there", "general kenobi"] >>> results = rouge.compute(predictions=predictions, references=references) >>> print(results) {'rouge1': 1.0, 'rouge2': 1.0, 'rougeL': 1.0, 'rougeLsum': 1.0} """ class Tokenizer: """Helper class to wrap a callable into a class with a `tokenize` method as used by rouge-score.""" def __init__(self, tokenizer_func): self.tokenizer_func = tokenizer_func def tokenize(self, text): return self.tokenizer_func(text) @evaluate.utils.file_utils.add_start_docstrings(_DESCRIPTION, _KWARGS_DESCRIPTION) class Rouge(evaluate.Metric): def _info(self): return evaluate.MetricInfo( description=_DESCRIPTION, citation=_CITATION, inputs_description=_KWARGS_DESCRIPTION, features=[ datasets.Features( { "predictions": datasets.Value("string", id="sequence"), "references": datasets.Sequence(datasets.Value("string", id="sequence")), } ), datasets.Features( { "predictions": datasets.Value("string", id="sequence"), "references": datasets.Value("string", id="sequence"), } ), ], codebase_urls=[ "https://huggingface.co/spaces/SyntaxSpirits/rouge", "https://github.com/SyntaxSpirits/rougers", "https://github.com/google-research/google-research/tree/master/rouge", ], reference_urls=[ "https://en.wikipedia.org/wiki/ROUGE_(metric)", "https://github.com/google-research/google-research/tree/master/rouge", ], ) def _compute( self, predictions, references, rouge_types=None, use_aggregator=True, use_stemmer=False, tokenizer=None ): if rouge_types is None: rouge_types = ["rouge1", "rouge2", "rougeL", "rougeLsum"] multi_ref = isinstance(references[0], list) if tokenizer is not None: tokenizer = Tokenizer(tokenizer) scorer = rouge_scorer.RougeScorer(rouge_types=rouge_types, use_stemmer=use_stemmer, tokenizer=tokenizer) if use_aggregator: aggregator = scoring.BootstrapAggregator() else: scores = [] if multi_ref: all_scores = (scorer.score_multi(ref, pred) for ref, pred in zip(references, predictions)) else: all_scores = scorer.score_batch(references, predictions) for score in all_scores: if use_aggregator: aggregator.add_scores(score) else: scores.append(score) if use_aggregator: result = aggregator.aggregate() for key in result: result[key] = result[key].mid.fmeasure else: result = {} for key in scores[0]: result[key] = list(score[key].fmeasure for score in scores) return result