Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lm-evaluation-harness/Qwen2.5-7B_eval.log +0 -0
- lm-evaluation-harness/eval_base_qwen2_5_32b.log +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/__init__.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/group.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/instance.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/metrics.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/model.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/registry.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/samplers.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/samplers.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/__pycache__/task.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/api/model.py +493 -0
- lm-evaluation-harness/lm_eval/caching/__pycache__/cache.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/caching/cache.py +59 -0
- lm-evaluation-harness/lm_eval/decontamination/archiver.py +174 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/__init__.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/__init__.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/custom.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/custom.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/extraction.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/extraction.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/selection.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/__pycache__/transformation.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/filters/custom.py +17 -0
- lm-evaluation-harness/lm_eval/filters/decontamination.py +25 -0
- lm-evaluation-harness/lm_eval/filters/selection.py +61 -0
- lm-evaluation-harness/lm_eval/filters/transformation.py +122 -0
- lm-evaluation-harness/lm_eval/loggers/__init__.py +2 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/__init__.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/__init__.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/evaluation_tracker.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/evaluation_tracker.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/utils.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/utils.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/wandb_logger.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/__pycache__/wandb_logger.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/loggers/evaluation_tracker.py +537 -0
- lm-evaluation-harness/lm_eval/loggers/utils.py +149 -0
- lm-evaluation-harness/lm_eval/loggers/wandb_logger.py +358 -0
- lm-evaluation-harness/lm_eval/models/__init__.py +36 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/__init__.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/__init__.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/anthropic_llms.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/anthropic_llms.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/api_models.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/api_models.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/dummy.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/dummy.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-311.pyc +0 -0
lm-evaluation-harness/Qwen2.5-7B_eval.log
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-evaluation-harness/eval_base_qwen2_5_32b.log
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (195 Bytes). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/group.cpython-310.pyc
ADDED
|
Binary file (4.62 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/instance.cpython-310.pyc
ADDED
|
Binary file (1.53 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/metrics.cpython-310.pyc
ADDED
|
Binary file (13.4 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/model.cpython-310.pyc
ADDED
|
Binary file (18 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/registry.cpython-311.pyc
ADDED
|
Binary file (8.62 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/samplers.cpython-310.pyc
ADDED
|
Binary file (5.67 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/samplers.cpython-311.pyc
ADDED
|
Binary file (10.4 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/__pycache__/task.cpython-310.pyc
ADDED
|
Binary file (47.4 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/api/model.py
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
import logging
|
| 5 |
+
import os
|
| 6 |
+
from typing import Dict, List, Optional, Tuple, Type, TypeVar, Union
|
| 7 |
+
|
| 8 |
+
import transformers
|
| 9 |
+
from sqlitedict import SqliteDict
|
| 10 |
+
from tqdm import tqdm
|
| 11 |
+
|
| 12 |
+
from lm_eval import utils
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
eval_logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
T = TypeVar("T", bound="LM")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class LM(abc.ABC):
|
| 21 |
+
def __init__(self) -> None:
|
| 22 |
+
"""Defines the interface that should be implemented by all LM subclasses.
|
| 23 |
+
LMs are assumed to take text (strings) as input and yield strings as output
|
| 24 |
+
(inputs/outputs should be tokenization-agnostic.)
|
| 25 |
+
|
| 26 |
+
"""
|
| 27 |
+
# set rank and world size to a single process, by default.
|
| 28 |
+
self._rank = 0
|
| 29 |
+
self._world_size = 1
|
| 30 |
+
self.cache_hook = CacheHook(None)
|
| 31 |
+
|
| 32 |
+
@abc.abstractmethod
|
| 33 |
+
def loglikelihood(self, requests) -> List[Tuple[float, bool]]:
|
| 34 |
+
"""Compute log-likelihood of generating a continuation from a context.
|
| 35 |
+
Downstream tasks should attempt to use loglikelihood instead of other
|
| 36 |
+
LM calls whenever possible.
|
| 37 |
+
|
| 38 |
+
:param requests: list[Instance]
|
| 39 |
+
A list of Instance objects, with property `args` which returns a tuple (context, continuation).
|
| 40 |
+
`context: str`
|
| 41 |
+
Context string. Implementations of LM must be able to handle an
|
| 42 |
+
empty context string.
|
| 43 |
+
`continuation: str`
|
| 44 |
+
The continuation over which log likelihood will be calculated. If
|
| 45 |
+
there is a word boundary, the space should be in the continuation.
|
| 46 |
+
For example, context="hello" continuation=" world" is correct.
|
| 47 |
+
|
| 48 |
+
:return: list[tuple[float, bool]]
|
| 49 |
+
A list of pairs (logprob, isgreedy)
|
| 50 |
+
`logprob: float`
|
| 51 |
+
The log probability of `continuation`.
|
| 52 |
+
`isgreedy`:
|
| 53 |
+
Whether `continuation` would be generated by greedy sampling from `context`.
|
| 54 |
+
"""
|
| 55 |
+
pass
|
| 56 |
+
|
| 57 |
+
@abc.abstractmethod
|
| 58 |
+
def loglikelihood_rolling(self, requests) -> List[float]:
|
| 59 |
+
"""Compute full log-likelihood of a string, with no truncation, for perplexity computation
|
| 60 |
+
- We will use the full max context length of the model.
|
| 61 |
+
- For inputs that exceed the max context length, we divide the tokenized string into chunks of up to
|
| 62 |
+
the max context length.
|
| 63 |
+
- IMPORTANT: Each document's loglikelihood/perplexity is computed *separately*, unlike other implementations
|
| 64 |
+
which may simply concatenate multiple documents together.
|
| 65 |
+
- IMPORTANT: We maximize the amount of context for each prediction. Specifically, for inputs that we break into
|
| 66 |
+
multiple chunks, the last input will still a full-sized context.
|
| 67 |
+
Example:
|
| 68 |
+
Input tokens: [ 0 1 2 3 4 5 6 7 8 9 ]
|
| 69 |
+
Prefix: BOS/EOS
|
| 70 |
+
Max context length: 4
|
| 71 |
+
Resulting input/prediction pairs:
|
| 72 |
+
|
| 73 |
+
INPUT: BOS 0 1 2
|
| 74 |
+
PRED: 0 1 2 3
|
| 75 |
+
|
| 76 |
+
INPUT: 3 4 5 6
|
| 77 |
+
PRED: 4 5 6 7
|
| 78 |
+
|
| 79 |
+
INPUT: 5 6 7 8
|
| 80 |
+
PRED: 8 9
|
| 81 |
+
|
| 82 |
+
Observe that:
|
| 83 |
+
1. Each token is predicted exactly once
|
| 84 |
+
2. For the last pair, we provide the full context, but only score the last two tokens
|
| 85 |
+
|
| 86 |
+
:param requests: list[Instance]
|
| 87 |
+
A list of Instance objects with property `args` which returns a tuple (context,).
|
| 88 |
+
string: str
|
| 89 |
+
String for which we are computing overall loglikelihood
|
| 90 |
+
:return: list[tuple[float]]
|
| 91 |
+
A list of tuples (logprob,)
|
| 92 |
+
logprob: float
|
| 93 |
+
The log probability of `context` conditioned on the BOS/EOS token.
|
| 94 |
+
Can also be overridden for custom cases by `prefix_token_id`.
|
| 95 |
+
"""
|
| 96 |
+
pass
|
| 97 |
+
|
| 98 |
+
# TODO: Add an optional max length
|
| 99 |
+
@abc.abstractmethod
|
| 100 |
+
def generate_until(self, requests) -> List[str]:
|
| 101 |
+
"""Generate greedily until a stopping sequence
|
| 102 |
+
|
| 103 |
+
:param requests: list[Instance]
|
| 104 |
+
A list of Instance objects with property `args` which returns a tuple (context, gen_kwargs).
|
| 105 |
+
context: str
|
| 106 |
+
Context string
|
| 107 |
+
gen_kwargs: dict
|
| 108 |
+
A dictionary of keyword arguments to pass to the generation function e.g. top_k, until, etc.
|
| 109 |
+
:return: list[str]
|
| 110 |
+
A list of model generated continuations.
|
| 111 |
+
continuation: str
|
| 112 |
+
The generated continuation.
|
| 113 |
+
"""
|
| 114 |
+
pass
|
| 115 |
+
|
| 116 |
+
def apply_chat_template(
|
| 117 |
+
self, chat_history: List[Dict[str, str]], add_generation_prompt=True
|
| 118 |
+
) -> str:
|
| 119 |
+
"""
|
| 120 |
+
Defines how to transform few-shot examples provided as chat history into a format that can be used as input to the LM.
|
| 121 |
+
|
| 122 |
+
:param chat_history: list[dict[str, str]]
|
| 123 |
+
A list of dictionaries with keys 'role' and 'content'.
|
| 124 |
+
Values are strings representing the role name and the content of the message, respectively.
|
| 125 |
+
:param add_generation_prompt: bool
|
| 126 |
+
Whether to append an assistant gen prefix (for e.g. <|assistant|>) to the assistant messages in the chat history. False if prefilling an assistant message.
|
| 127 |
+
:return: str
|
| 128 |
+
A string representing the chat history in a format that can be used as input to the LM.
|
| 129 |
+
"""
|
| 130 |
+
raise NotImplementedError(
|
| 131 |
+
"To use this model with chat templates, please implement the 'apply_chat_template' method for your model type."
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
@classmethod
|
| 135 |
+
def create_from_arg_string(
|
| 136 |
+
cls: Type[T], arg_string: str, additional_config: Optional[dict] = None
|
| 137 |
+
) -> T:
|
| 138 |
+
"""
|
| 139 |
+
Creates an instance of the LM class using the given argument string and additional config.
|
| 140 |
+
|
| 141 |
+
Parameters:
|
| 142 |
+
- arg_string: A string containing arguments in the format key1=value1,key2=value2.
|
| 143 |
+
- additional_config: Optional dictionary containing additional configuration parameters.
|
| 144 |
+
|
| 145 |
+
Returns:
|
| 146 |
+
- Instance of the LM class.
|
| 147 |
+
"""
|
| 148 |
+
additional_config = {} if additional_config is None else additional_config
|
| 149 |
+
args = utils.simple_parse_args_string(arg_string)
|
| 150 |
+
args2 = {k: v for k, v in additional_config.items() if v is not None}
|
| 151 |
+
return cls(**args, **args2)
|
| 152 |
+
|
| 153 |
+
@classmethod
|
| 154 |
+
def create_from_arg_obj(
|
| 155 |
+
cls: Type[T], arg_dict: dict, additional_config: Optional[dict] = None
|
| 156 |
+
) -> T:
|
| 157 |
+
"""
|
| 158 |
+
Creates an instance of the LM class using the given arg_obj
|
| 159 |
+
|
| 160 |
+
Parameters:
|
| 161 |
+
- arg_obj: A dict containing arguments in the format key1=value1,key2=value2.
|
| 162 |
+
- additional_config: Optional dictionary containing additional configuration parameters.
|
| 163 |
+
|
| 164 |
+
Returns:
|
| 165 |
+
- Instance of the LM class.
|
| 166 |
+
"""
|
| 167 |
+
|
| 168 |
+
additional_config = {} if additional_config is None else additional_config
|
| 169 |
+
additional_config = {
|
| 170 |
+
k: v for k, v in additional_config.items() if v is not None
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
return cls(**arg_dict, **additional_config)
|
| 174 |
+
|
| 175 |
+
@property
|
| 176 |
+
def rank(self):
|
| 177 |
+
# used in the case of parallelism. Hardcoded to
|
| 178 |
+
# ensure no errors arise using API models which do
|
| 179 |
+
# not support multi-device parallelism nor expect it.
|
| 180 |
+
return self._rank
|
| 181 |
+
|
| 182 |
+
@property
|
| 183 |
+
def world_size(self):
|
| 184 |
+
# used in the case of parallelism. Hardcoded to
|
| 185 |
+
# ensure no errors arise using API models which do
|
| 186 |
+
# not support multi-device parallelism nor expect it.
|
| 187 |
+
return self._world_size
|
| 188 |
+
|
| 189 |
+
@property
|
| 190 |
+
def tokenizer_name(self) -> str:
|
| 191 |
+
"""Must be defined for LM subclasses which implement Chat Templating.
|
| 192 |
+
Should return the name of the tokenizer or chat template used.
|
| 193 |
+
Used only to properly fingerprint caches when requests are being cached with `--cache_requests`, otherwise not used.
|
| 194 |
+
"""
|
| 195 |
+
raise NotImplementedError(
|
| 196 |
+
"To use this model with chat templates, please implement the 'tokenizer_name' property."
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
def chat_template(self, chat_template: Union[bool, str] = False) -> Optional[str]:
|
| 200 |
+
"""Returns the chat template structure for user/assistant messages if a template is provided.
|
| 201 |
+
This method is intended to be overridden in a subclass to define a specific chat template format.
|
| 202 |
+
For models that do not support chat templates, this method returns None by default.
|
| 203 |
+
"""
|
| 204 |
+
|
| 205 |
+
return ""
|
| 206 |
+
|
| 207 |
+
def set_cache_hook(self, cache_hook) -> None:
|
| 208 |
+
self.cache_hook = cache_hook
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
### SQLite-based caching of LM responses
|
| 212 |
+
def hash_args(attr, args):
|
| 213 |
+
dat = json.dumps([attr] + list(args))
|
| 214 |
+
return hashlib.sha256(dat.encode("utf-8")).hexdigest()
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
class CacheHook:
|
| 218 |
+
def __init__(self, cachinglm) -> None:
|
| 219 |
+
if cachinglm is None:
|
| 220 |
+
self.dbdict = None
|
| 221 |
+
return
|
| 222 |
+
|
| 223 |
+
self.dbdict = cachinglm.dbdict
|
| 224 |
+
|
| 225 |
+
def add_partial(self, attr, req, res) -> None:
|
| 226 |
+
if self.dbdict is None:
|
| 227 |
+
return
|
| 228 |
+
hsh = hash_args(attr, req)
|
| 229 |
+
self.dbdict[hsh] = res
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
class CachingLM:
|
| 233 |
+
def __init__(self, lm, cache_db) -> None:
|
| 234 |
+
"""LM wrapper that returns cached results if they exist, and uses the underlying LM if not.
|
| 235 |
+
|
| 236 |
+
:param lm: LM
|
| 237 |
+
Underlying LM
|
| 238 |
+
:param cache_db: str
|
| 239 |
+
Path to cache db
|
| 240 |
+
"""
|
| 241 |
+
self.lm = lm
|
| 242 |
+
self.cache_db = cache_db
|
| 243 |
+
if os.path.dirname(cache_db):
|
| 244 |
+
os.makedirs(os.path.dirname(cache_db), exist_ok=True)
|
| 245 |
+
self.dbdict = SqliteDict(cache_db, autocommit=True)
|
| 246 |
+
|
| 247 |
+
# add hook to lm
|
| 248 |
+
lm.set_cache_hook(self.get_cache_hook())
|
| 249 |
+
|
| 250 |
+
def __getattr__(self, attr: str):
|
| 251 |
+
lm_attr = getattr(self.lm, attr)
|
| 252 |
+
if attr not in ["loglikelihood", "loglikelihood_rolling", "generate_until"]:
|
| 253 |
+
eval_logger.debug(f"Passing through attribute '{attr}' to underlying LM")
|
| 254 |
+
return lm_attr
|
| 255 |
+
|
| 256 |
+
def fn(requests):
|
| 257 |
+
res = []
|
| 258 |
+
remaining_reqs = []
|
| 259 |
+
warned = False
|
| 260 |
+
# figure out which ones are cached and which ones are new
|
| 261 |
+
eval_logger.info(
|
| 262 |
+
f"Loading '{attr}' responses from cache '{self.cache_db}' where possible..."
|
| 263 |
+
)
|
| 264 |
+
for req in tqdm(requests, desc="Checking cached requests"):
|
| 265 |
+
hsh = hash_args(attr, req.args)
|
| 266 |
+
if attr == "generate_until" and req.args[1].get("do_sample", False):
|
| 267 |
+
# when we are doing non-greedy generation, don't use the cache
|
| 268 |
+
# (else every "randomly sampled" generation would be identical for repeats > 1).
|
| 269 |
+
if not warned:
|
| 270 |
+
eval_logger.warning(
|
| 271 |
+
f"Arguments to lm.generate_until() '{req.args[1]}' include non-deterministic sampling. Caching will not be performed for such requests."
|
| 272 |
+
)
|
| 273 |
+
warned = True
|
| 274 |
+
res.append(None)
|
| 275 |
+
remaining_reqs.append(req)
|
| 276 |
+
elif hsh in self.dbdict:
|
| 277 |
+
ob = self.dbdict[hsh]
|
| 278 |
+
|
| 279 |
+
assert ob is not None
|
| 280 |
+
|
| 281 |
+
res.append(ob)
|
| 282 |
+
else:
|
| 283 |
+
res.append(None)
|
| 284 |
+
remaining_reqs.append(req)
|
| 285 |
+
eval_logger.info(
|
| 286 |
+
f"Cached requests: {len(requests) - len(remaining_reqs)}, Requests remaining: {len(remaining_reqs)}"
|
| 287 |
+
)
|
| 288 |
+
if remaining_reqs:
|
| 289 |
+
# actually run the LM on the requests that do not have cached results
|
| 290 |
+
rem_res = getattr(self.lm, attr)(remaining_reqs)
|
| 291 |
+
else:
|
| 292 |
+
rem_res = []
|
| 293 |
+
|
| 294 |
+
# stick the new ones back into the list and also cache any of the new ones
|
| 295 |
+
resptr = 0
|
| 296 |
+
for req, r in zip(remaining_reqs, rem_res):
|
| 297 |
+
while res[resptr] is not None:
|
| 298 |
+
resptr += 1
|
| 299 |
+
|
| 300 |
+
res[resptr] = r
|
| 301 |
+
|
| 302 |
+
# caching
|
| 303 |
+
hsh = hash_args(attr, req.args)
|
| 304 |
+
self.dbdict[hsh] = r
|
| 305 |
+
self.dbdict.commit()
|
| 306 |
+
|
| 307 |
+
return res
|
| 308 |
+
|
| 309 |
+
return fn
|
| 310 |
+
|
| 311 |
+
def get_cache_hook(self):
|
| 312 |
+
return CacheHook(self)
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
class TemplateLM(LM):
|
| 316 |
+
"""
|
| 317 |
+
A class acting as intermediary between the LM base class
|
| 318 |
+
and boilerplate often included in other LM subclasses.
|
| 319 |
+
"""
|
| 320 |
+
|
| 321 |
+
tokenizer = None
|
| 322 |
+
|
| 323 |
+
@property
|
| 324 |
+
@abc.abstractmethod
|
| 325 |
+
def eot_token_id(self):
|
| 326 |
+
pass
|
| 327 |
+
|
| 328 |
+
@property
|
| 329 |
+
def prefix_token_id(self):
|
| 330 |
+
# it is used as prefix for loglikelihood
|
| 331 |
+
return self.eot_token_id
|
| 332 |
+
|
| 333 |
+
@abc.abstractmethod
|
| 334 |
+
def tok_encode(self, string: str, **kwargs) -> List[int]:
|
| 335 |
+
"""
|
| 336 |
+
Tokenize a string using the model's tokenizer and return a list of token IDs.
|
| 337 |
+
"""
|
| 338 |
+
pass
|
| 339 |
+
|
| 340 |
+
@abc.abstractmethod
|
| 341 |
+
def _loglikelihood_tokens(self, requests, **kwargs) -> List[Tuple[float, bool]]:
|
| 342 |
+
pass
|
| 343 |
+
|
| 344 |
+
def _encode_pair(
|
| 345 |
+
self, context: str, continuation: str
|
| 346 |
+
) -> Tuple[List[int], List[int]]:
|
| 347 |
+
n_spaces = len(context) - len(context.rstrip())
|
| 348 |
+
if n_spaces > 0:
|
| 349 |
+
continuation = context[-n_spaces:] + continuation
|
| 350 |
+
context = context[:-n_spaces]
|
| 351 |
+
|
| 352 |
+
model_class = getattr(self, "AUTO_MODEL_CLASS", None)
|
| 353 |
+
|
| 354 |
+
if model_class == transformers.AutoModelForSeq2SeqLM:
|
| 355 |
+
context_enc = self.tok_encode(context)
|
| 356 |
+
continuation_enc = self.tok_encode(continuation, add_special_tokens=False)
|
| 357 |
+
else:
|
| 358 |
+
whole_enc = self.tok_encode(context + continuation)
|
| 359 |
+
context_enc = self.tok_encode(context)
|
| 360 |
+
|
| 361 |
+
context_enc_len = len(context_enc)
|
| 362 |
+
continuation_enc = whole_enc[context_enc_len:]
|
| 363 |
+
|
| 364 |
+
return context_enc, continuation_enc
|
| 365 |
+
|
| 366 |
+
def loglikelihood(
|
| 367 |
+
self, requests, disable_tqdm: bool = False
|
| 368 |
+
) -> List[Tuple[float, bool]]:
|
| 369 |
+
new_reqs = []
|
| 370 |
+
for context, continuation in [req.args for req in requests]:
|
| 371 |
+
if context == "":
|
| 372 |
+
# BOS or EOS as context
|
| 373 |
+
context_enc, continuation_enc = (
|
| 374 |
+
[self.prefix_token_id],
|
| 375 |
+
self.tok_encode(continuation),
|
| 376 |
+
)
|
| 377 |
+
else:
|
| 378 |
+
context_enc, continuation_enc = self._encode_pair(context, continuation)
|
| 379 |
+
|
| 380 |
+
new_reqs.append(((context, continuation), context_enc, continuation_enc))
|
| 381 |
+
|
| 382 |
+
return self._loglikelihood_tokens(new_reqs, disable_tqdm=disable_tqdm)
|
| 383 |
+
|
| 384 |
+
@abc.abstractmethod
|
| 385 |
+
def loglikelihood_rolling(
|
| 386 |
+
self, requests, disable_tqdm: bool = False
|
| 387 |
+
) -> List[float]:
|
| 388 |
+
pass
|
| 389 |
+
|
| 390 |
+
@abc.abstractmethod
|
| 391 |
+
def generate_until(self, requests, disable_tqdm: bool = False) -> List[str]:
|
| 392 |
+
pass
|
| 393 |
+
|
| 394 |
+
def chat_template(self, chat_template: Union[bool, str] = False) -> Optional[str]:
|
| 395 |
+
"""
|
| 396 |
+
Set and get the appropriate chat template for the model.
|
| 397 |
+
This method sets the tokenizer's chat_template and returns the template string for reproducibility.
|
| 398 |
+
|
| 399 |
+
The template selection logic is adapted from the Transformers library's `apply_chat_template`
|
| 400 |
+
method in the Tokenizer class. The original implementation can be found at:
|
| 401 |
+
https://github.com/huggingface/transformers/blob/fc35907f95459d7a6c5281dfadd680b6f7b620e3/src/transformers/tokenization_utils_base.py#L1687
|
| 402 |
+
|
| 403 |
+
This method ensures that the right template is chosen based on the following:
|
| 404 |
+
0. If the model has no 'tokenizer' attribute: assumes that there is only a single possible chat template, handled on the model provider side internally. Returns the empty string.
|
| 405 |
+
1. If the model's tokenizer has multiple templates:
|
| 406 |
+
a. Use the specified template if it exists in the dictionary.
|
| 407 |
+
b. Use the default template from the list if no specific template is provided.
|
| 408 |
+
c. Raise an error if no default template exists and no specific template is provided.
|
| 409 |
+
2. If the model's tokenizer has a single template or no template:
|
| 410 |
+
a. Use the tokenizer's chat template if available.
|
| 411 |
+
b. Fall back to the default chat template if no tokenizer chat template exists.
|
| 412 |
+
|
| 413 |
+
Args:
|
| 414 |
+
chat_template (Union[bool, str]): Specifies the chat template to use.
|
| 415 |
+
- If False or None, no template is applied.
|
| 416 |
+
- If True, the default or only available template is used.
|
| 417 |
+
- If a string, the template with the matching name is used.
|
| 418 |
+
|
| 419 |
+
Returns:
|
| 420 |
+
Optional[str]: The selected chat template, or None if no template is applied.
|
| 421 |
+
"""
|
| 422 |
+
if self.tokenizer is None:
|
| 423 |
+
return ""
|
| 424 |
+
|
| 425 |
+
if chat_template is False or chat_template is None:
|
| 426 |
+
eval_logger.warning(
|
| 427 |
+
"model.chat_template was called with the chat_template set to False or None. "
|
| 428 |
+
"Therefore no chat template will be applied. Make sure this is an intended behavior."
|
| 429 |
+
)
|
| 430 |
+
return None
|
| 431 |
+
|
| 432 |
+
# Convert boolean chat_template to None to ensure compatibility with the adapted logic
|
| 433 |
+
if isinstance(chat_template, bool):
|
| 434 |
+
chat_template = None
|
| 435 |
+
using_default_template = False
|
| 436 |
+
|
| 437 |
+
# First, handle the cases when the model has a dict of multiple templates
|
| 438 |
+
try:
|
| 439 |
+
template = (
|
| 440 |
+
self.tokenizer.chat_template or self.tokenizer.default_chat_template
|
| 441 |
+
)
|
| 442 |
+
except AttributeError:
|
| 443 |
+
return None
|
| 444 |
+
|
| 445 |
+
if isinstance(template, dict):
|
| 446 |
+
using_default_dict = self.tokenizer.chat_template is None
|
| 447 |
+
|
| 448 |
+
if chat_template is not None:
|
| 449 |
+
if chat_template in template:
|
| 450 |
+
selected_template = template[chat_template]
|
| 451 |
+
if using_default_dict:
|
| 452 |
+
using_default_template = True
|
| 453 |
+
else:
|
| 454 |
+
raise ValueError(
|
| 455 |
+
f"The specified chat template '{chat_template}' is not available. "
|
| 456 |
+
f"Available template names are {sorted(template.keys())}."
|
| 457 |
+
)
|
| 458 |
+
else:
|
| 459 |
+
# If user didn't pass a chat template, use the default template from the dict
|
| 460 |
+
if "default" in template:
|
| 461 |
+
selected_template = template["default"]
|
| 462 |
+
using_default_template = True
|
| 463 |
+
else:
|
| 464 |
+
raise ValueError(
|
| 465 |
+
"This model has multiple chat templates with no default specified! Please either pass a chat "
|
| 466 |
+
"template or the name of the template you wish to use to the `chat_template` argument. Available "
|
| 467 |
+
f"template names are {sorted(template.keys())}."
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
# Cases when the model has a single template or no template
|
| 471 |
+
else:
|
| 472 |
+
# priority: `chat_template` argument > `tokenizer.chat_template` > `tokenizer.default_chat_template
|
| 473 |
+
if isinstance(chat_template, str):
|
| 474 |
+
eval_logger.warning(
|
| 475 |
+
"Chat template name provided, but the tokenizer's chat template is not a dictionary. "
|
| 476 |
+
"Using the tokenizer's chat template or the default template instead."
|
| 477 |
+
)
|
| 478 |
+
if self.tokenizer.chat_template is not None:
|
| 479 |
+
selected_template = self.tokenizer.chat_template
|
| 480 |
+
else:
|
| 481 |
+
selected_template = self.tokenizer.default_chat_template
|
| 482 |
+
using_default_template = True
|
| 483 |
+
|
| 484 |
+
if using_default_template:
|
| 485 |
+
eval_logger.warning(
|
| 486 |
+
"No chat template is set for this tokenizer, falling back to a default class-level template. This is "
|
| 487 |
+
"very error-prone, because models are often trained with templates different from the class default! "
|
| 488 |
+
"Default chat templates are a legacy feature and will be removed in Transformers v4.43, at which "
|
| 489 |
+
"point any code depending on them will stop working. We recommend setting a valid chat template before "
|
| 490 |
+
"then to ensure that this model continues working without issues."
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
return selected_template
|
lm-evaluation-harness/lm_eval/caching/__pycache__/cache.cpython-310.pyc
ADDED
|
Binary file (1.69 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/caching/cache.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
import dill
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
eval_logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
MODULE_DIR = os.path.dirname(os.path.realpath(__file__))
|
| 12 |
+
|
| 13 |
+
OVERRIDE_PATH = os.getenv("LM_HARNESS_CACHE_PATH")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
PATH = OVERRIDE_PATH if OVERRIDE_PATH else f"{MODULE_DIR}/.cache"
|
| 17 |
+
|
| 18 |
+
# This should be sufficient for uniqueness
|
| 19 |
+
HASH_INPUT = "EleutherAI-lm-evaluation-harness"
|
| 20 |
+
|
| 21 |
+
HASH_PREFIX = hashlib.sha256(HASH_INPUT.encode("utf-8")).hexdigest()
|
| 22 |
+
|
| 23 |
+
FILE_SUFFIX = f".{HASH_PREFIX}.pickle"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def load_from_cache(file_name: str, cache: bool = False):
|
| 27 |
+
if not cache:
|
| 28 |
+
return
|
| 29 |
+
try:
|
| 30 |
+
path = f"{PATH}/{file_name}{FILE_SUFFIX}"
|
| 31 |
+
|
| 32 |
+
with open(path, "rb") as file:
|
| 33 |
+
cached_task_dict = dill.loads(file.read())
|
| 34 |
+
return cached_task_dict
|
| 35 |
+
|
| 36 |
+
except Exception:
|
| 37 |
+
eval_logger.debug(f"{file_name} is not cached, generating...")
|
| 38 |
+
pass
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def save_to_cache(file_name, obj):
|
| 42 |
+
if not os.path.exists(PATH):
|
| 43 |
+
os.mkdir(PATH)
|
| 44 |
+
|
| 45 |
+
file_path = f"{PATH}/{file_name}{FILE_SUFFIX}"
|
| 46 |
+
|
| 47 |
+
eval_logger.debug(f"Saving {file_path} to cache...")
|
| 48 |
+
with open(file_path, "wb") as file:
|
| 49 |
+
file.write(dill.dumps(obj))
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
# NOTE the "key" param is to allow for flexibility
|
| 53 |
+
def delete_cache(key: str = ""):
|
| 54 |
+
files = os.listdir(PATH)
|
| 55 |
+
|
| 56 |
+
for file in files:
|
| 57 |
+
if file.startswith(key) and file.endswith(FILE_SUFFIX):
|
| 58 |
+
file_path = f"{PATH}/{file}"
|
| 59 |
+
os.unlink(file_path)
|
lm-evaluation-harness/lm_eval/decontamination/archiver.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
import io
|
| 3 |
+
import json
|
| 4 |
+
import mmap
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Any
|
| 8 |
+
|
| 9 |
+
import jsonlines
|
| 10 |
+
import tqdm
|
| 11 |
+
import zstandard
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def json_serial(obj: Any) -> str:
|
| 15 |
+
"""JSON serializer for objects not serializable by default json code"""
|
| 16 |
+
|
| 17 |
+
if isinstance(obj, (datetime.datetime,)):
|
| 18 |
+
return obj.isoformat()
|
| 19 |
+
raise TypeError("Type %s not serializable" % type(obj))
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# Modified version of lm_dataformat Archive for single file.
|
| 23 |
+
class Archive:
|
| 24 |
+
def __init__(self, file_path: str, compression_level: int = 3) -> None:
|
| 25 |
+
self.file_path = file_path
|
| 26 |
+
dir_name = os.path.dirname(file_path)
|
| 27 |
+
if dir_name:
|
| 28 |
+
os.makedirs(dir_name, exist_ok=True)
|
| 29 |
+
self.fh = open(self.file_path, "wb")
|
| 30 |
+
self.cctx = zstandard.ZstdCompressor(level=compression_level)
|
| 31 |
+
self.compressor = self.cctx.stream_writer(self.fh)
|
| 32 |
+
|
| 33 |
+
def add_data(self, data, meta=None) -> None:
|
| 34 |
+
if meta is None:
|
| 35 |
+
meta = {}
|
| 36 |
+
self.compressor.write(
|
| 37 |
+
json.dumps({"text": data, "meta": meta}, default=json_serial).encode(
|
| 38 |
+
"UTF-8"
|
| 39 |
+
)
|
| 40 |
+
+ b"\n"
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
def commit(self) -> None:
|
| 44 |
+
self.compressor.flush(zstandard.FLUSH_FRAME)
|
| 45 |
+
self.fh.flush()
|
| 46 |
+
self.fh.close()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# Modified version of lm_dataformat Reader with self.fh set, allowing peeking for tqdm.
|
| 50 |
+
class Reader:
|
| 51 |
+
def __init__(self) -> None:
|
| 52 |
+
pass
|
| 53 |
+
|
| 54 |
+
def read(
|
| 55 |
+
self,
|
| 56 |
+
file,
|
| 57 |
+
get_meta: bool = False,
|
| 58 |
+
autojoin_paragraphs: bool = True,
|
| 59 |
+
para_joiner: str = "\n\n",
|
| 60 |
+
):
|
| 61 |
+
with open(file, "rb") as fh:
|
| 62 |
+
self.fh = fh
|
| 63 |
+
cctx = zstandard.ZstdDecompressor()
|
| 64 |
+
reader = io.BufferedReader(cctx.stream_reader(fh))
|
| 65 |
+
rdr = jsonlines.Reader(reader)
|
| 66 |
+
for ob in rdr:
|
| 67 |
+
# naive jsonl where each object is just the string itself, with no meta. For legacy compatibility.
|
| 68 |
+
if isinstance(ob, str):
|
| 69 |
+
assert not get_meta
|
| 70 |
+
yield ob
|
| 71 |
+
continue
|
| 72 |
+
|
| 73 |
+
text = ob["text"]
|
| 74 |
+
|
| 75 |
+
if autojoin_paragraphs and isinstance(text, list):
|
| 76 |
+
text = para_joiner.join(text)
|
| 77 |
+
|
| 78 |
+
if get_meta:
|
| 79 |
+
yield text, (ob["meta"] if "meta" in ob else {})
|
| 80 |
+
else:
|
| 81 |
+
yield text
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class TextArchive:
|
| 85 |
+
def __init__(self, file_path, mode: str = "rb+") -> None:
|
| 86 |
+
self.file_path = file_path
|
| 87 |
+
dir_name = os.path.dirname(file_path)
|
| 88 |
+
if dir_name:
|
| 89 |
+
os.makedirs(dir_name, exist_ok=True)
|
| 90 |
+
|
| 91 |
+
if not os.path.exists(file_path):
|
| 92 |
+
Path(file_path).touch()
|
| 93 |
+
|
| 94 |
+
self.fh = open(self.file_path, mode)
|
| 95 |
+
|
| 96 |
+
def add_data(self, data) -> None:
|
| 97 |
+
self.fh.write(data.encode("UTF-8") + b"\n")
|
| 98 |
+
|
| 99 |
+
def commit(self) -> None:
|
| 100 |
+
self.fh.flush()
|
| 101 |
+
self.fh.close()
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class TextReader:
|
| 105 |
+
def __init__(self, file_path) -> None:
|
| 106 |
+
self.file_path = file_path
|
| 107 |
+
|
| 108 |
+
# Optimized mmap read with infrequent tqdm updates to maintain speed
|
| 109 |
+
# Tested up to 250MB/s.
|
| 110 |
+
def read_tqdm(self, update_frequency: int = 10000):
|
| 111 |
+
current_file_position = 0
|
| 112 |
+
line_counter = 0
|
| 113 |
+
with (
|
| 114 |
+
open(self.file_path, "r", encoding="utf-8") as fh,
|
| 115 |
+
tqdm.tqdm(
|
| 116 |
+
total=os.path.getsize(self.file_path),
|
| 117 |
+
dynamic_ncols=True,
|
| 118 |
+
unit="byte",
|
| 119 |
+
unit_scale=1,
|
| 120 |
+
) as progress,
|
| 121 |
+
):
|
| 122 |
+
with mmap.mmap(fh.fileno(), length=0, access=mmap.ACCESS_READ) as mmap_obj:
|
| 123 |
+
for line in iter(mmap_obj.readline, b""):
|
| 124 |
+
line = line.decode("utf-8")
|
| 125 |
+
line_counter += 1
|
| 126 |
+
if line_counter == update_frequency:
|
| 127 |
+
new_file_pos = mmap_obj.tell()
|
| 128 |
+
bytes_read = new_file_pos - current_file_position
|
| 129 |
+
current_file_position = new_file_pos
|
| 130 |
+
progress.update(bytes_read)
|
| 131 |
+
line_counter = 0
|
| 132 |
+
yield line[:-1]
|
| 133 |
+
|
| 134 |
+
def read_and_tell(self):
|
| 135 |
+
current_file_position = 0
|
| 136 |
+
with open(self.file_path, "r", encoding="utf8") as fh:
|
| 137 |
+
with mmap.mmap(fh.fileno(), length=0, access=mmap.ACCESS_READ) as mmap_obj:
|
| 138 |
+
for line in iter(mmap_obj.readline, b""):
|
| 139 |
+
line = line.decode("utf-8")
|
| 140 |
+
new_file_pos = mmap_obj.tell()
|
| 141 |
+
raw_bytes_read = new_file_pos - current_file_position
|
| 142 |
+
current_file_position = new_file_pos
|
| 143 |
+
yield line[:-1], raw_bytes_read
|
| 144 |
+
|
| 145 |
+
def read(self):
|
| 146 |
+
with open(self.file_path, "r", encoding="utf8") as fh:
|
| 147 |
+
with mmap.mmap(fh.fileno(), length=0, access=mmap.ACCESS_READ) as mmap_obj:
|
| 148 |
+
for line in iter(mmap_obj.readline, b""):
|
| 149 |
+
line = line.decode("utf-8")
|
| 150 |
+
yield line[:-1]
|
| 151 |
+
|
| 152 |
+
def read_slow(self):
|
| 153 |
+
with open(self.file_path, "r", encoding="utf8") as fh:
|
| 154 |
+
while True:
|
| 155 |
+
line = fh.readline()
|
| 156 |
+
if line == -1 or line == "":
|
| 157 |
+
break
|
| 158 |
+
else:
|
| 159 |
+
yield line[:-1]
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
# Optimized for speed. Decompresses the archive in shell before
|
| 163 |
+
# using the mmap'd TextReader.
|
| 164 |
+
class ZStdTextReader:
|
| 165 |
+
def __init__(self, file) -> None:
|
| 166 |
+
self.file = file
|
| 167 |
+
|
| 168 |
+
def read_tqdm(self):
|
| 169 |
+
decompressed_file = self.file[:-4]
|
| 170 |
+
print("Decompressing file, please wait...")
|
| 171 |
+
os.system(f"zstd -d {self.file}") # linux decompress is faster
|
| 172 |
+
reader = TextReader(decompressed_file)
|
| 173 |
+
yield from reader.read_tqdm()
|
| 174 |
+
os.remove(decompressed_file)
|
lm-evaluation-harness/lm_eval/filters/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (851 Bytes). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.2 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/custom.cpython-310.pyc
ADDED
|
Binary file (966 Bytes). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/custom.cpython-311.pyc
ADDED
|
Binary file (1.38 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/extraction.cpython-310.pyc
ADDED
|
Binary file (8.14 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/extraction.cpython-311.pyc
ADDED
|
Binary file (12.8 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/selection.cpython-310.pyc
ADDED
|
Binary file (2.92 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/__pycache__/transformation.cpython-310.pyc
ADDED
|
Binary file (5.77 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/filters/custom.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from lm_eval.api.filter import Filter
|
| 2 |
+
from lm_eval.api.registry import register_filter
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@register_filter("custom")
|
| 6 |
+
class CustomFilter(Filter):
|
| 7 |
+
"""
|
| 8 |
+
Custom filter that applies a custom, user-defined function to the model responses.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
def __init__(self, **kwargs) -> None:
|
| 12 |
+
self.filter_fn = kwargs.pop("filter_fn")
|
| 13 |
+
|
| 14 |
+
super().__init__(**kwargs)
|
| 15 |
+
|
| 16 |
+
def apply(self, resps, docs):
|
| 17 |
+
return self.filter_fn(resps, docs)
|
lm-evaluation-harness/lm_eval/filters/decontamination.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from lm_eval.api.filter import Filter
|
| 2 |
+
from lm_eval.api.registry import register_filter
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@register_filter("decontaminate")
|
| 6 |
+
class DecontaminationFilter(Filter):
|
| 7 |
+
"""
|
| 8 |
+
A filter which evaluates
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
name = "track_decontamination"
|
| 12 |
+
|
| 13 |
+
def __init__(self, path) -> None:
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
TODO: make sure only ever run one time on the train set (should this be cached as a class var? keyed by value for "path").
|
| 17 |
+
should further cache result on a given (task_name, doc_id)
|
| 18 |
+
"""
|
| 19 |
+
self._decontam_results = None
|
| 20 |
+
|
| 21 |
+
def apply(self, resps, docs) -> None:
|
| 22 |
+
"""
|
| 23 |
+
Return {"no_contamination", "only_contamination"} keys for the 2 different subsets
|
| 24 |
+
"""
|
| 25 |
+
pass
|
lm-evaluation-harness/lm_eval/filters/selection.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import Counter
|
| 2 |
+
|
| 3 |
+
from lm_eval.api.filter import Filter
|
| 4 |
+
from lm_eval.api.registry import register_filter
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
# TODO: implement "arg_max" filter. either it should take in an arbitrary "scoring"/reward function
|
| 8 |
+
# that takes an input and returns a scalar and then should select the max reward,
|
| 9 |
+
# or should implement different filters for different ways of handling a reward model's inference.
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@register_filter("take_first")
|
| 13 |
+
class TakeFirstFilter(Filter):
|
| 14 |
+
def __init__(self) -> None:
|
| 15 |
+
"""
|
| 16 |
+
Can define custom behavior here, if an individual instantiation of a Filter class should have state.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def apply(self, resps, docs):
|
| 20 |
+
"""
|
| 21 |
+
Assuming each entry of `resps` is a list of model responses, we discard all but the first response.
|
| 22 |
+
"""
|
| 23 |
+
return map(lambda r: r[0], resps)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@register_filter("take_first_k")
|
| 27 |
+
class TakeKFilter(Filter):
|
| 28 |
+
def __init__(self, **kwargs) -> None:
|
| 29 |
+
self.k = kwargs.pop("k")
|
| 30 |
+
|
| 31 |
+
super().__init__(**kwargs)
|
| 32 |
+
|
| 33 |
+
def apply(self, resps, docs):
|
| 34 |
+
# need resp to be subscriptable to check below
|
| 35 |
+
resps = list(resps)
|
| 36 |
+
# check we have at least k responses per doc, else we can't take the first k
|
| 37 |
+
assert len(resps[0]) >= self.k, (
|
| 38 |
+
f"Need at least {self.k} responses per doc to take first {self.k}, but got {len(resps[0])} only! Please increase TaskConfig.repeats ."
|
| 39 |
+
)
|
| 40 |
+
return map(lambda r: r[: self.k], resps)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@register_filter("majority_vote")
|
| 44 |
+
class MajorityVoteFilter(Filter):
|
| 45 |
+
def __init__(self) -> None:
|
| 46 |
+
"""
|
| 47 |
+
Can define custom behavior here, if an individual instantiation of a Filter class should have state.
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
def apply(self, resps, docs):
|
| 51 |
+
"""
|
| 52 |
+
Each entry of `resps` is a list of model responses.
|
| 53 |
+
We select the response that occurs most frequently in each entry of `resps`.
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
def select_majority(resp):
|
| 57 |
+
counts = Counter(resp)
|
| 58 |
+
vote = counts.most_common(1)[0][0]
|
| 59 |
+
return vote
|
| 60 |
+
|
| 61 |
+
return map(lambda r: [select_majority(r)], resps)
|
lm-evaluation-harness/lm_eval/filters/transformation.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
|
| 3 |
+
from lm_eval.api.filter import Filter
|
| 4 |
+
from lm_eval.api.registry import register_filter
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@register_filter("lowercase")
|
| 8 |
+
class LowercaseFilter(Filter):
|
| 9 |
+
def __init__(self) -> None:
|
| 10 |
+
pass
|
| 11 |
+
|
| 12 |
+
def apply(self, resps, docs):
|
| 13 |
+
def filter_set(inst):
|
| 14 |
+
return [resp.lower() for resp in inst]
|
| 15 |
+
|
| 16 |
+
return [filter_set(resp) for resp in resps]
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@register_filter("uppercase")
|
| 20 |
+
class UppercaseFilter(Filter):
|
| 21 |
+
def __init__(self) -> None:
|
| 22 |
+
pass
|
| 23 |
+
|
| 24 |
+
def apply(self, resps, docs):
|
| 25 |
+
def filter_set(inst):
|
| 26 |
+
return [resp.upper() for resp in inst]
|
| 27 |
+
|
| 28 |
+
return [filter_set(resp) for resp in resps]
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@register_filter("map")
|
| 32 |
+
class MapFilter(Filter):
|
| 33 |
+
def __init__(self, mapping_dict: dict = None, default_value=None) -> None:
|
| 34 |
+
"""
|
| 35 |
+
Initializes the MapFilter with a given mapping dictionary and default value.
|
| 36 |
+
|
| 37 |
+
Args:
|
| 38 |
+
- mapping_dict (dict): A dictionary containing the key-value mappings.
|
| 39 |
+
Default is an empty dictionary.
|
| 40 |
+
- default_value (Any): The value to be returned when a key is not found in the mapping_dict.
|
| 41 |
+
Default is None.
|
| 42 |
+
|
| 43 |
+
Example:
|
| 44 |
+
mapper = MapFilter({'A': 1, 'B': 2}, default_value=0)
|
| 45 |
+
"""
|
| 46 |
+
if mapping_dict is None:
|
| 47 |
+
mapping_dict = {}
|
| 48 |
+
assert isinstance(mapping_dict, dict), (
|
| 49 |
+
"Provided mapping_dict is not a dictionary"
|
| 50 |
+
)
|
| 51 |
+
self.mapping_dict = mapping_dict
|
| 52 |
+
self.default_value = default_value
|
| 53 |
+
|
| 54 |
+
def apply(self, resps, docs):
|
| 55 |
+
def filter_set(inst):
|
| 56 |
+
return [self.mapping_dict.get(resp, self.default_value) for resp in inst]
|
| 57 |
+
|
| 58 |
+
return [filter_set(resp) for resp in resps]
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
@register_filter("format_span")
|
| 62 |
+
class SPANFilter(Filter):
|
| 63 |
+
def __init__(self) -> None:
|
| 64 |
+
pass
|
| 65 |
+
|
| 66 |
+
def apply(self, resps, docs):
|
| 67 |
+
def format_ner_text(text):
|
| 68 |
+
label_dict = {
|
| 69 |
+
"person": "PER",
|
| 70 |
+
"location": "LOC",
|
| 71 |
+
"organization": "ORG",
|
| 72 |
+
"counties": "LOC",
|
| 73 |
+
"places": "LOC",
|
| 74 |
+
"people": "PER",
|
| 75 |
+
"persons": "PER",
|
| 76 |
+
"company": "ORG",
|
| 77 |
+
"country": "LOC",
|
| 78 |
+
"continent": "LOC",
|
| 79 |
+
"time": "DATE",
|
| 80 |
+
"date": "DATE",
|
| 81 |
+
"per": "PER",
|
| 82 |
+
"loc": "LOC",
|
| 83 |
+
"org": "ORG",
|
| 84 |
+
}
|
| 85 |
+
text = text.lower()
|
| 86 |
+
for key, value in label_dict.items():
|
| 87 |
+
text = text.replace(key, value)
|
| 88 |
+
|
| 89 |
+
text = "$".join(i for i in text.split("$$"))
|
| 90 |
+
return text.rstrip("$$")
|
| 91 |
+
|
| 92 |
+
def format_named_entities(text):
|
| 93 |
+
"""
|
| 94 |
+
Extract named entities from text and format them as 'label: value $$ label: value'.
|
| 95 |
+
Handles grouped entities (e.g., LOC: kenya, uganda) and excludes 'none' values.
|
| 96 |
+
"""
|
| 97 |
+
# Regular expression to match label: entities pattern
|
| 98 |
+
pattern = r"\b(PER|LOC|ORG|DATE):\s*([^$]+)"
|
| 99 |
+
# Normalize newline characters
|
| 100 |
+
text = text.replace("\n", "$").strip()
|
| 101 |
+
matches = re.findall(pattern, text)
|
| 102 |
+
|
| 103 |
+
formatted_entities = []
|
| 104 |
+
|
| 105 |
+
for label, values in matches:
|
| 106 |
+
# Split multiple entities separated by commas and strip whitespace
|
| 107 |
+
entities = [value.strip() for value in values.split(",")]
|
| 108 |
+
|
| 109 |
+
# Exclude 'none' entities
|
| 110 |
+
for entity in entities:
|
| 111 |
+
if entity.lower() != "none":
|
| 112 |
+
formatted_entities.append(f"{label.lower()}: {entity}")
|
| 113 |
+
|
| 114 |
+
# Join entities with the desired separator
|
| 115 |
+
return " $ ".join(formatted_entities)
|
| 116 |
+
|
| 117 |
+
def filter_set(inst):
|
| 118 |
+
return [
|
| 119 |
+
format_named_entities(format_ner_text(resp.lower())) for resp in inst
|
| 120 |
+
]
|
| 121 |
+
|
| 122 |
+
return [filter_set(resp) for resp in resps]
|
lm-evaluation-harness/lm_eval/loggers/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .evaluation_tracker import EvaluationTracker
|
| 2 |
+
from .wandb_logger import WandbLogger
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (291 Bytes). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (337 Bytes). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/evaluation_tracker.cpython-310.pyc
ADDED
|
Binary file (15.8 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/evaluation_tracker.cpython-311.pyc
ADDED
|
Binary file (26.4 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/utils.cpython-310.pyc
ADDED
|
Binary file (4.43 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (7.57 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/wandb_logger.cpython-310.pyc
ADDED
|
Binary file (11.9 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/__pycache__/wandb_logger.cpython-311.pyc
ADDED
|
Binary file (23.4 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/loggers/evaluation_tracker.py
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
import re
|
| 5 |
+
import time
|
| 6 |
+
from collections import defaultdict
|
| 7 |
+
from dataclasses import asdict, dataclass
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
from datasets import load_dataset
|
| 12 |
+
from datasets.utils.metadata import MetadataConfigs
|
| 13 |
+
from huggingface_hub import (
|
| 14 |
+
DatasetCard,
|
| 15 |
+
DatasetCardData,
|
| 16 |
+
HfApi,
|
| 17 |
+
hf_hub_url,
|
| 18 |
+
)
|
| 19 |
+
from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
|
| 20 |
+
|
| 21 |
+
from lm_eval.utils import (
|
| 22 |
+
get_file_datetime,
|
| 23 |
+
get_file_task_name,
|
| 24 |
+
get_results_filenames,
|
| 25 |
+
get_sample_results_filenames,
|
| 26 |
+
handle_non_serializable,
|
| 27 |
+
hash_string,
|
| 28 |
+
sanitize_list,
|
| 29 |
+
sanitize_model_name,
|
| 30 |
+
sanitize_task_name,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
eval_logger = logging.getLogger(__name__)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@dataclass(init=False)
|
| 38 |
+
class GeneralConfigTracker:
|
| 39 |
+
"""
|
| 40 |
+
Tracker for the evaluation parameters.
|
| 41 |
+
|
| 42 |
+
Attributes:
|
| 43 |
+
model_source (str): Source of the model (e.g. Hugging Face, GGUF, etc.)
|
| 44 |
+
model_name (str): Name of the model.
|
| 45 |
+
model_name_sanitized (str): Sanitized model name for directory creation.
|
| 46 |
+
start_time (float): Start time of the experiment. Logged at class init.
|
| 47 |
+
end_time (float): Start time of the experiment. Logged when calling [`GeneralConfigTracker.log_end_time`]
|
| 48 |
+
total_evaluation_time_seconds (str): Inferred total evaluation time in seconds (from the start and end times).
|
| 49 |
+
"""
|
| 50 |
+
|
| 51 |
+
model_source: str = None
|
| 52 |
+
model_name: str = None
|
| 53 |
+
model_name_sanitized: str = None
|
| 54 |
+
system_instruction: str = None
|
| 55 |
+
system_instruction_sha: str = None
|
| 56 |
+
fewshot_as_multiturn: bool = None
|
| 57 |
+
chat_template: str = None
|
| 58 |
+
chat_template_sha: str = None
|
| 59 |
+
start_time: float = None
|
| 60 |
+
end_time: float = None
|
| 61 |
+
total_evaluation_time_seconds: str = None
|
| 62 |
+
|
| 63 |
+
def __init__(self) -> None:
|
| 64 |
+
"""Starts the evaluation timer."""
|
| 65 |
+
self.start_time = time.perf_counter()
|
| 66 |
+
|
| 67 |
+
@staticmethod
|
| 68 |
+
def _get_model_name(model_args: str) -> str:
|
| 69 |
+
"""Extracts the model name from the model arguments."""
|
| 70 |
+
|
| 71 |
+
def extract_model_name(model_args: str, key: str) -> str:
|
| 72 |
+
"""Extracts the model name from the model arguments using a key."""
|
| 73 |
+
args_after_key = model_args.split(key)[1]
|
| 74 |
+
return args_after_key.split(",")[0]
|
| 75 |
+
|
| 76 |
+
# order does matter, e.g. peft and delta are provided together with pretrained
|
| 77 |
+
prefixes = ["peft=", "delta=", "pretrained=", "model=", "path=", "engine="]
|
| 78 |
+
for prefix in prefixes:
|
| 79 |
+
if prefix in model_args:
|
| 80 |
+
return extract_model_name(model_args, prefix)
|
| 81 |
+
return ""
|
| 82 |
+
|
| 83 |
+
def log_experiment_args(
|
| 84 |
+
self,
|
| 85 |
+
model_source: str,
|
| 86 |
+
model_args: str,
|
| 87 |
+
system_instruction: str,
|
| 88 |
+
chat_template: str,
|
| 89 |
+
fewshot_as_multiturn: bool,
|
| 90 |
+
) -> None:
|
| 91 |
+
"""Logs model parameters and job ID."""
|
| 92 |
+
self.model_source = model_source
|
| 93 |
+
self.model_name = GeneralConfigTracker._get_model_name(model_args)
|
| 94 |
+
self.model_name_sanitized = sanitize_model_name(self.model_name)
|
| 95 |
+
self.system_instruction = system_instruction
|
| 96 |
+
self.system_instruction_sha = (
|
| 97 |
+
hash_string(system_instruction) if system_instruction else None
|
| 98 |
+
)
|
| 99 |
+
self.chat_template = chat_template
|
| 100 |
+
self.chat_template_sha = hash_string(chat_template) if chat_template else None
|
| 101 |
+
self.fewshot_as_multiturn = fewshot_as_multiturn
|
| 102 |
+
|
| 103 |
+
def log_end_time(self) -> None:
|
| 104 |
+
"""Logs the end time of the evaluation and calculates the total evaluation time."""
|
| 105 |
+
self.end_time = time.perf_counter()
|
| 106 |
+
self.total_evaluation_time_seconds = str(self.end_time - self.start_time)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class EvaluationTracker:
|
| 110 |
+
"""
|
| 111 |
+
Keeps track and saves relevant information of the evaluation process.
|
| 112 |
+
Compiles the data from trackers and writes it to files, which can be published to the Hugging Face hub if requested.
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
def __init__(
|
| 116 |
+
self,
|
| 117 |
+
output_path: str = None,
|
| 118 |
+
hub_results_org: str = "",
|
| 119 |
+
hub_repo_name: str = "",
|
| 120 |
+
details_repo_name: str = "",
|
| 121 |
+
results_repo_name: str = "",
|
| 122 |
+
push_results_to_hub: bool = False,
|
| 123 |
+
push_samples_to_hub: bool = False,
|
| 124 |
+
public_repo: bool = False,
|
| 125 |
+
token: str = "",
|
| 126 |
+
leaderboard_url: str = "",
|
| 127 |
+
point_of_contact: str = "",
|
| 128 |
+
gated: bool = False,
|
| 129 |
+
) -> None:
|
| 130 |
+
"""
|
| 131 |
+
Creates all the necessary loggers for evaluation tracking.
|
| 132 |
+
|
| 133 |
+
Args:
|
| 134 |
+
output_path (str): Path to save the results. If not provided, the results won't be saved.
|
| 135 |
+
hub_results_org (str): The Hugging Face organization to push the results to. If not provided, the results will be pushed to the owner of the Hugging Face token.
|
| 136 |
+
hub_repo_name (str): The name of the Hugging Face repository to push the results to. If not provided, the results will be pushed to `lm-eval-results`.
|
| 137 |
+
details_repo_name (str): The name of the Hugging Face repository to push the details to. If not provided, the results will be pushed to `lm-eval-results`.
|
| 138 |
+
result_repo_name (str): The name of the Hugging Face repository to push the results to. If not provided, the results will not be pushed and will be found in the details_hub_repo.
|
| 139 |
+
push_results_to_hub (bool): Whether to push the results to the Hugging Face hub.
|
| 140 |
+
push_samples_to_hub (bool): Whether to push the samples to the Hugging Face hub.
|
| 141 |
+
public_repo (bool): Whether to push the results to a public or private repository.
|
| 142 |
+
token (str): Token to use when pushing to the Hugging Face hub. This token should have write access to `hub_results_org`.
|
| 143 |
+
leaderboard_url (str): URL to the leaderboard on the Hugging Face hub on the dataset card.
|
| 144 |
+
point_of_contact (str): Contact information on the Hugging Face hub dataset card.
|
| 145 |
+
gated (bool): Whether to gate the repository.
|
| 146 |
+
"""
|
| 147 |
+
self.general_config_tracker = GeneralConfigTracker()
|
| 148 |
+
|
| 149 |
+
self.output_path = output_path
|
| 150 |
+
self.push_results_to_hub = push_results_to_hub
|
| 151 |
+
self.push_samples_to_hub = push_samples_to_hub
|
| 152 |
+
self.public_repo = public_repo
|
| 153 |
+
self.leaderboard_url = leaderboard_url
|
| 154 |
+
self.point_of_contact = point_of_contact
|
| 155 |
+
self.api = HfApi(token=token) if token else None
|
| 156 |
+
self.gated_repo = gated
|
| 157 |
+
|
| 158 |
+
if not self.api and (push_results_to_hub or push_samples_to_hub):
|
| 159 |
+
raise ValueError(
|
| 160 |
+
"Hugging Face token is not defined, but 'push_results_to_hub' or 'push_samples_to_hub' is set to True. "
|
| 161 |
+
"Please provide a valid Hugging Face token by setting the HF_TOKEN environment variable."
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
if (
|
| 165 |
+
self.api
|
| 166 |
+
and hub_results_org == ""
|
| 167 |
+
and (push_results_to_hub or push_samples_to_hub)
|
| 168 |
+
):
|
| 169 |
+
hub_results_org = self.api.whoami()["name"]
|
| 170 |
+
eval_logger.warning(
|
| 171 |
+
f"hub_results_org was not specified. Results will be pushed to '{hub_results_org}'."
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
if hub_repo_name == "":
|
| 175 |
+
details_repo_name = (
|
| 176 |
+
details_repo_name if details_repo_name != "" else "lm-eval-results"
|
| 177 |
+
)
|
| 178 |
+
results_repo_name = (
|
| 179 |
+
results_repo_name if results_repo_name != "" else details_repo_name
|
| 180 |
+
)
|
| 181 |
+
else:
|
| 182 |
+
details_repo_name = hub_repo_name
|
| 183 |
+
results_repo_name = hub_repo_name
|
| 184 |
+
eval_logger.warning(
|
| 185 |
+
"hub_repo_name was specified. Both details and results will be pushed to the same repository. Using hub_repo_name is no longer recommended, details_repo_name and results_repo_name should be used instead."
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
self.details_repo = f"{hub_results_org}/{details_repo_name}"
|
| 189 |
+
self.details_repo_private = f"{hub_results_org}/{details_repo_name}-private"
|
| 190 |
+
self.results_repo = f"{hub_results_org}/{results_repo_name}"
|
| 191 |
+
self.results_repo_private = f"{hub_results_org}/{results_repo_name}-private"
|
| 192 |
+
|
| 193 |
+
def save_results_aggregated(
|
| 194 |
+
self,
|
| 195 |
+
results: dict,
|
| 196 |
+
samples: dict,
|
| 197 |
+
) -> None:
|
| 198 |
+
"""
|
| 199 |
+
Saves the aggregated results and samples to the output path and pushes them to the Hugging Face hub if requested.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
results (dict): The aggregated results to save.
|
| 203 |
+
samples (dict): The samples results to save.
|
| 204 |
+
"""
|
| 205 |
+
self.general_config_tracker.log_end_time()
|
| 206 |
+
|
| 207 |
+
if self.output_path:
|
| 208 |
+
try:
|
| 209 |
+
eval_logger.info("Saving results aggregated")
|
| 210 |
+
|
| 211 |
+
# calculate cumulative hash for each task - only if samples are provided
|
| 212 |
+
task_hashes = {}
|
| 213 |
+
if samples:
|
| 214 |
+
for task_name, task_samples in samples.items():
|
| 215 |
+
sample_hashes = [
|
| 216 |
+
s["doc_hash"] + s["prompt_hash"] + s["target_hash"]
|
| 217 |
+
for s in task_samples
|
| 218 |
+
]
|
| 219 |
+
task_hashes[task_name] = hash_string("".join(sample_hashes))
|
| 220 |
+
|
| 221 |
+
# update initial results dict
|
| 222 |
+
results.update({"task_hashes": task_hashes})
|
| 223 |
+
results.update(asdict(self.general_config_tracker))
|
| 224 |
+
dumped = json.dumps(
|
| 225 |
+
results,
|
| 226 |
+
indent=2,
|
| 227 |
+
default=handle_non_serializable,
|
| 228 |
+
ensure_ascii=False,
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
path = Path(self.output_path if self.output_path else Path.cwd())
|
| 232 |
+
self.date_id = datetime.now().isoformat().replace(":", "-")
|
| 233 |
+
if path.suffix == ".json":
|
| 234 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 235 |
+
file_results_aggregated = path.with_name(
|
| 236 |
+
f"{path.stem}_{self.date_id}.json"
|
| 237 |
+
)
|
| 238 |
+
else:
|
| 239 |
+
path = path.joinpath(
|
| 240 |
+
self.general_config_tracker.model_name_sanitized
|
| 241 |
+
)
|
| 242 |
+
path.mkdir(parents=True, exist_ok=True)
|
| 243 |
+
file_results_aggregated = path.joinpath(
|
| 244 |
+
f"results_{self.date_id}.json"
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
file_results_aggregated.open("w", encoding="utf-8").write(dumped)
|
| 248 |
+
|
| 249 |
+
if self.api and self.push_results_to_hub:
|
| 250 |
+
repo_id = (
|
| 251 |
+
self.results_repo
|
| 252 |
+
if self.public_repo
|
| 253 |
+
else self.results_repo_private
|
| 254 |
+
)
|
| 255 |
+
self.api.create_repo(
|
| 256 |
+
repo_id=repo_id,
|
| 257 |
+
repo_type="dataset",
|
| 258 |
+
private=not self.public_repo,
|
| 259 |
+
exist_ok=True,
|
| 260 |
+
)
|
| 261 |
+
self.api.upload_file(
|
| 262 |
+
repo_id=repo_id,
|
| 263 |
+
path_or_fileobj=str(file_results_aggregated),
|
| 264 |
+
path_in_repo=os.path.join(
|
| 265 |
+
self.general_config_tracker.model_name,
|
| 266 |
+
file_results_aggregated.name,
|
| 267 |
+
),
|
| 268 |
+
repo_type="dataset",
|
| 269 |
+
commit_message=f"Adding aggregated results for {self.general_config_tracker.model_name}",
|
| 270 |
+
)
|
| 271 |
+
eval_logger.info(
|
| 272 |
+
"Successfully pushed aggregated results to the Hugging Face Hub. "
|
| 273 |
+
f"You can find them at: {repo_id}"
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
except Exception as e:
|
| 277 |
+
eval_logger.warning("Could not save results aggregated")
|
| 278 |
+
eval_logger.info(repr(e))
|
| 279 |
+
else:
|
| 280 |
+
eval_logger.info(
|
| 281 |
+
"Output path not provided, skipping saving results aggregated"
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
def save_results_samples(
|
| 285 |
+
self,
|
| 286 |
+
task_name: str,
|
| 287 |
+
samples: dict,
|
| 288 |
+
) -> None:
|
| 289 |
+
"""
|
| 290 |
+
Saves the samples results to the output path and pushes them to the Hugging Face hub if requested.
|
| 291 |
+
|
| 292 |
+
Args:
|
| 293 |
+
task_name (str): The task name to save the samples for.
|
| 294 |
+
samples (dict): The samples results to save.
|
| 295 |
+
"""
|
| 296 |
+
if self.output_path:
|
| 297 |
+
try:
|
| 298 |
+
eval_logger.info(f"Saving per-sample results for: {task_name}")
|
| 299 |
+
|
| 300 |
+
path = Path(self.output_path if self.output_path else Path.cwd())
|
| 301 |
+
if path.suffix == ".json":
|
| 302 |
+
path = path.parent
|
| 303 |
+
else:
|
| 304 |
+
path = path.joinpath(
|
| 305 |
+
self.general_config_tracker.model_name_sanitized
|
| 306 |
+
)
|
| 307 |
+
path.mkdir(parents=True, exist_ok=True)
|
| 308 |
+
|
| 309 |
+
file_results_samples = path.joinpath(
|
| 310 |
+
f"samples_{task_name}_{self.date_id}.jsonl"
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
for sample in samples:
|
| 314 |
+
# we first need to sanitize arguments and resps
|
| 315 |
+
# otherwise we won't be able to load the dataset
|
| 316 |
+
# using the datasets library
|
| 317 |
+
arguments = {}
|
| 318 |
+
for i, arg in enumerate(sample["arguments"]):
|
| 319 |
+
arguments[f"gen_args_{i}"] = {}
|
| 320 |
+
for j, tmp in enumerate(arg):
|
| 321 |
+
arguments[f"gen_args_{i}"][f"arg_{j}"] = tmp
|
| 322 |
+
|
| 323 |
+
sample["resps"] = sanitize_list(sample["resps"])
|
| 324 |
+
sample["filtered_resps"] = sanitize_list(sample["filtered_resps"])
|
| 325 |
+
sample["arguments"] = arguments
|
| 326 |
+
sample["target"] = str(sample["target"])
|
| 327 |
+
|
| 328 |
+
sample_dump = (
|
| 329 |
+
json.dumps(
|
| 330 |
+
sample,
|
| 331 |
+
default=handle_non_serializable,
|
| 332 |
+
ensure_ascii=False,
|
| 333 |
+
)
|
| 334 |
+
+ "\n"
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
with open(file_results_samples, "a", encoding="utf-8") as f:
|
| 338 |
+
f.write(sample_dump)
|
| 339 |
+
|
| 340 |
+
if self.api and self.push_samples_to_hub:
|
| 341 |
+
repo_id = (
|
| 342 |
+
self.details_repo
|
| 343 |
+
if self.public_repo
|
| 344 |
+
else self.details_repo_private
|
| 345 |
+
)
|
| 346 |
+
self.api.create_repo(
|
| 347 |
+
repo_id=repo_id,
|
| 348 |
+
repo_type="dataset",
|
| 349 |
+
private=not self.public_repo,
|
| 350 |
+
exist_ok=True,
|
| 351 |
+
)
|
| 352 |
+
try:
|
| 353 |
+
if self.gated_repo:
|
| 354 |
+
headers = build_hf_headers()
|
| 355 |
+
r = get_session().put(
|
| 356 |
+
url=f"https://huggingface.co/api/datasets/{repo_id}/settings",
|
| 357 |
+
headers=headers,
|
| 358 |
+
json={"gated": "auto"},
|
| 359 |
+
)
|
| 360 |
+
hf_raise_for_status(r)
|
| 361 |
+
except Exception as e:
|
| 362 |
+
eval_logger.warning("Could not gate the repository")
|
| 363 |
+
eval_logger.info(repr(e))
|
| 364 |
+
self.api.upload_folder(
|
| 365 |
+
repo_id=repo_id,
|
| 366 |
+
folder_path=str(path),
|
| 367 |
+
path_in_repo=self.general_config_tracker.model_name_sanitized,
|
| 368 |
+
repo_type="dataset",
|
| 369 |
+
commit_message=f"Adding samples results for {task_name} to {self.general_config_tracker.model_name}",
|
| 370 |
+
)
|
| 371 |
+
eval_logger.info(
|
| 372 |
+
f"Successfully pushed sample results for task: {task_name} to the Hugging Face Hub. "
|
| 373 |
+
f"You can find them at: {repo_id}"
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
except Exception as e:
|
| 377 |
+
eval_logger.warning("Could not save sample results")
|
| 378 |
+
eval_logger.info(repr(e))
|
| 379 |
+
else:
|
| 380 |
+
eval_logger.info("Output path not provided, skipping saving sample results")
|
| 381 |
+
|
| 382 |
+
def recreate_metadata_card(self) -> None:
|
| 383 |
+
"""
|
| 384 |
+
Creates a metadata card for the evaluation results dataset and pushes it to the Hugging Face hub.
|
| 385 |
+
"""
|
| 386 |
+
|
| 387 |
+
eval_logger.info("Recreating metadata card")
|
| 388 |
+
repo_id = self.details_repo if self.public_repo else self.details_repo_private
|
| 389 |
+
|
| 390 |
+
files_in_repo = self.api.list_repo_files(repo_id=repo_id, repo_type="dataset")
|
| 391 |
+
results_files = get_results_filenames(files_in_repo)
|
| 392 |
+
sample_files = get_sample_results_filenames(files_in_repo)
|
| 393 |
+
|
| 394 |
+
# Build a dictionary to store the latest evaluation datetime for:
|
| 395 |
+
# - Each tested model and its aggregated results
|
| 396 |
+
# - Each task and sample results, if existing
|
| 397 |
+
# i.e. {
|
| 398 |
+
# "org__model_name__gsm8k": "2021-09-01T12:00:00",
|
| 399 |
+
# "org__model_name__ifeval": "2021-09-01T12:00:00",
|
| 400 |
+
# "org__model_name__results": "2021-09-01T12:00:00"
|
| 401 |
+
# }
|
| 402 |
+
latest_task_results_datetime = defaultdict(lambda: datetime.min.isoformat())
|
| 403 |
+
|
| 404 |
+
for file_path in sample_files:
|
| 405 |
+
file_path = Path(file_path)
|
| 406 |
+
filename = file_path.name
|
| 407 |
+
model_name = file_path.parent
|
| 408 |
+
task_name = get_file_task_name(filename)
|
| 409 |
+
results_datetime = get_file_datetime(filename)
|
| 410 |
+
task_name_sanitized = sanitize_task_name(task_name)
|
| 411 |
+
# Results and sample results for the same model and task will have the same datetime
|
| 412 |
+
samples_key = f"{model_name}__{task_name_sanitized}"
|
| 413 |
+
results_key = f"{model_name}__results"
|
| 414 |
+
latest_datetime = max(
|
| 415 |
+
latest_task_results_datetime[samples_key],
|
| 416 |
+
results_datetime,
|
| 417 |
+
)
|
| 418 |
+
latest_task_results_datetime[samples_key] = latest_datetime
|
| 419 |
+
latest_task_results_datetime[results_key] = max(
|
| 420 |
+
latest_task_results_datetime[results_key],
|
| 421 |
+
latest_datetime,
|
| 422 |
+
)
|
| 423 |
+
|
| 424 |
+
# Create metadata card
|
| 425 |
+
card_metadata = MetadataConfigs()
|
| 426 |
+
|
| 427 |
+
# Add the latest aggregated results to the metadata card for easy access
|
| 428 |
+
for file_path in results_files:
|
| 429 |
+
file_path = Path(file_path)
|
| 430 |
+
results_filename = file_path.name
|
| 431 |
+
model_name = file_path.parent
|
| 432 |
+
eval_date = get_file_datetime(results_filename)
|
| 433 |
+
eval_date_sanitized = re.sub(r"[^\w\.]", "_", eval_date)
|
| 434 |
+
results_filename = Path("**") / Path(results_filename).name
|
| 435 |
+
config_name = f"{model_name}__results"
|
| 436 |
+
sanitized_last_eval_date_results = re.sub(
|
| 437 |
+
r"[^\w\.]", "_", latest_task_results_datetime[config_name]
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
if eval_date_sanitized == sanitized_last_eval_date_results:
|
| 441 |
+
# Ensure that all results files are listed in the metadata card
|
| 442 |
+
current_results = card_metadata.get(config_name, {"data_files": []})
|
| 443 |
+
current_results["data_files"].append(
|
| 444 |
+
{"split": eval_date_sanitized, "path": [str(results_filename)]}
|
| 445 |
+
)
|
| 446 |
+
card_metadata[config_name] = current_results
|
| 447 |
+
# If the results file is the newest, update the "latest" field in the metadata card
|
| 448 |
+
card_metadata[config_name]["data_files"].append(
|
| 449 |
+
{"split": "latest", "path": [str(results_filename)]}
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
# Add the tasks details configs
|
| 453 |
+
for file_path in sample_files:
|
| 454 |
+
file_path = Path(file_path)
|
| 455 |
+
filename = file_path.name
|
| 456 |
+
model_name = file_path.parent
|
| 457 |
+
task_name = get_file_task_name(filename)
|
| 458 |
+
eval_date = get_file_datetime(filename)
|
| 459 |
+
task_name_sanitized = sanitize_task_name(task_name)
|
| 460 |
+
eval_date_sanitized = re.sub(r"[^\w\.]", "_", eval_date)
|
| 461 |
+
results_filename = Path("**") / Path(filename).name
|
| 462 |
+
config_name = f"{model_name}__{task_name_sanitized}"
|
| 463 |
+
sanitized_last_eval_date_results = re.sub(
|
| 464 |
+
r"[^\w\.]", "_", latest_task_results_datetime[config_name]
|
| 465 |
+
)
|
| 466 |
+
if eval_date_sanitized == sanitized_last_eval_date_results:
|
| 467 |
+
# Ensure that all sample results files are listed in the metadata card
|
| 468 |
+
current_details_for_task = card_metadata.get(
|
| 469 |
+
config_name, {"data_files": []}
|
| 470 |
+
)
|
| 471 |
+
current_details_for_task["data_files"].append(
|
| 472 |
+
{"split": eval_date_sanitized, "path": [str(results_filename)]}
|
| 473 |
+
)
|
| 474 |
+
card_metadata[config_name] = current_details_for_task
|
| 475 |
+
# If the samples results file is the newest, update the "latest" field in the metadata card
|
| 476 |
+
card_metadata[config_name]["data_files"].append(
|
| 477 |
+
{"split": "latest", "path": [str(results_filename)]}
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
# Get latest results and extract info to update metadata card examples
|
| 481 |
+
latest_datetime = max(latest_task_results_datetime.values())
|
| 482 |
+
latest_model_name = max(
|
| 483 |
+
latest_task_results_datetime, key=lambda k: latest_task_results_datetime[k]
|
| 484 |
+
)
|
| 485 |
+
last_results_file = [
|
| 486 |
+
f for f in results_files if latest_datetime.replace(":", "-") in f
|
| 487 |
+
][0]
|
| 488 |
+
last_results_file_path = hf_hub_url(
|
| 489 |
+
repo_id=repo_id, filename=last_results_file, repo_type="dataset"
|
| 490 |
+
)
|
| 491 |
+
latest_results_file = load_dataset(
|
| 492 |
+
"json", data_files=last_results_file_path, split="train"
|
| 493 |
+
)
|
| 494 |
+
results_dict = latest_results_file["results"][0]
|
| 495 |
+
new_dictionary = {"all": results_dict}
|
| 496 |
+
new_dictionary.update(results_dict)
|
| 497 |
+
results_string = json.dumps(new_dictionary, indent=4)
|
| 498 |
+
|
| 499 |
+
dataset_summary = (
|
| 500 |
+
"Dataset automatically created during the evaluation run of model "
|
| 501 |
+
)
|
| 502 |
+
if self.general_config_tracker.model_source == "hf":
|
| 503 |
+
dataset_summary += f"[{self.general_config_tracker.model_name}](https://huggingface.co/{self.general_config_tracker.model_name})\n"
|
| 504 |
+
else:
|
| 505 |
+
dataset_summary += f"{self.general_config_tracker.model_name}\n"
|
| 506 |
+
dataset_summary += (
|
| 507 |
+
f"The dataset is composed of {len(card_metadata) - 1} configuration(s), each one corresponding to one of the evaluated task.\n\n"
|
| 508 |
+
f"The dataset has been created from {len(results_files)} run(s). Each run can be found as a specific split in each "
|
| 509 |
+
'configuration, the split being named using the timestamp of the run.The "train" split is always pointing to the latest results.\n\n'
|
| 510 |
+
'An additional configuration "results" store all the aggregated results of the run.\n\n'
|
| 511 |
+
"To load the details from a run, you can for instance do the following:\n"
|
| 512 |
+
)
|
| 513 |
+
if self.general_config_tracker.model_source == "hf":
|
| 514 |
+
dataset_summary += (
|
| 515 |
+
"```python\nfrom datasets import load_dataset\n"
|
| 516 |
+
f'data = load_dataset(\n\t"{repo_id}",\n\tname="{latest_model_name}",\n\tsplit="latest"\n)\n```\n\n'
|
| 517 |
+
)
|
| 518 |
+
dataset_summary += (
|
| 519 |
+
"## Latest results\n\n"
|
| 520 |
+
f"These are the [latest results from run {latest_datetime}]({last_results_file_path.replace('/resolve/', '/blob/')}) "
|
| 521 |
+
"(note that there might be results for other tasks in the repos if successive evals didn't cover the same tasks. "
|
| 522 |
+
'You find each in the results and the "latest" split for each eval):\n\n'
|
| 523 |
+
f"```python\n{results_string}\n```"
|
| 524 |
+
)
|
| 525 |
+
card_data = DatasetCardData(
|
| 526 |
+
dataset_summary=dataset_summary,
|
| 527 |
+
repo_url=f"https://huggingface.co/{self.general_config_tracker.model_name}",
|
| 528 |
+
pretty_name=f"Evaluation run of {self.general_config_tracker.model_name}",
|
| 529 |
+
leaderboard_url=self.leaderboard_url,
|
| 530 |
+
point_of_contact=self.point_of_contact,
|
| 531 |
+
)
|
| 532 |
+
card_metadata.to_dataset_card_data(card_data)
|
| 533 |
+
card = DatasetCard.from_template(
|
| 534 |
+
card_data,
|
| 535 |
+
pretty_name=card_data.pretty_name,
|
| 536 |
+
)
|
| 537 |
+
card.push_to_hub(repo_id, repo_type="dataset")
|
lm-evaluation-harness/lm_eval/loggers/utils.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import os
|
| 3 |
+
import re
|
| 4 |
+
import subprocess
|
| 5 |
+
from importlib.metadata import version
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Any, Dict, Optional, Tuple, Union
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
from torch.utils.collect_env import get_pretty_env_info
|
| 11 |
+
from transformers import __version__ as trans_version
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def remove_none_pattern(input_string: str) -> Tuple[str, bool]:
|
| 18 |
+
"""Remove the ',none' substring from the input_string if it exists at the end.
|
| 19 |
+
|
| 20 |
+
Args:
|
| 21 |
+
input_string (str): The input string from which to remove the ',none' substring.
|
| 22 |
+
|
| 23 |
+
Returns:
|
| 24 |
+
Tuple[str, bool]: A tuple containing the modified input_string with the ',none' substring removed
|
| 25 |
+
and a boolean indicating whether the modification was made (True) or not (False).
|
| 26 |
+
"""
|
| 27 |
+
# Define the pattern to match ',none' at the end of the string
|
| 28 |
+
pattern = re.compile(r",none$")
|
| 29 |
+
|
| 30 |
+
# Use sub() to replace ',none' with an empty string
|
| 31 |
+
result = re.sub(pattern, "", input_string)
|
| 32 |
+
|
| 33 |
+
# check if the input_string changed
|
| 34 |
+
removed = result != input_string
|
| 35 |
+
|
| 36 |
+
return result, removed
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _handle_non_serializable(o: Any) -> Union[int, str, list]:
|
| 40 |
+
"""Handle non-serializable objects by converting them to serializable types.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
o (Any): The object to be handled.
|
| 44 |
+
|
| 45 |
+
Returns:
|
| 46 |
+
Union[int, str, list]: The converted object. If the object is of type np.int64 or np.int32,
|
| 47 |
+
it will be converted to int. If the object is of type set, it will be converted
|
| 48 |
+
to a list. Otherwise, it will be converted to str.
|
| 49 |
+
"""
|
| 50 |
+
if isinstance(o, np.int64) or isinstance(o, np.int32):
|
| 51 |
+
return int(o)
|
| 52 |
+
elif isinstance(o, set):
|
| 53 |
+
return list(o)
|
| 54 |
+
else:
|
| 55 |
+
return str(o)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def get_commit_from_path(repo_path: Union[Path, str]) -> Optional[str]:
|
| 59 |
+
try:
|
| 60 |
+
git_folder = Path(repo_path, ".git")
|
| 61 |
+
if git_folder.is_file():
|
| 62 |
+
git_folder = Path(
|
| 63 |
+
git_folder.parent,
|
| 64 |
+
git_folder.read_text(encoding="utf-8").split("\n")[0].split(" ")[-1],
|
| 65 |
+
)
|
| 66 |
+
if Path(git_folder, "HEAD").exists():
|
| 67 |
+
head_name = (
|
| 68 |
+
Path(git_folder, "HEAD")
|
| 69 |
+
.read_text(encoding="utf-8")
|
| 70 |
+
.split("\n")[0]
|
| 71 |
+
.split(" ")[-1]
|
| 72 |
+
)
|
| 73 |
+
head_ref = Path(git_folder, head_name)
|
| 74 |
+
git_hash = head_ref.read_text(encoding="utf-8").replace("\n", "")
|
| 75 |
+
else:
|
| 76 |
+
git_hash = None
|
| 77 |
+
except Exception as err:
|
| 78 |
+
logger.debug(
|
| 79 |
+
f"Failed to retrieve a Git commit hash from path: {str(repo_path)}. Error: {err}"
|
| 80 |
+
)
|
| 81 |
+
return None
|
| 82 |
+
return git_hash
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def get_git_commit_hash():
|
| 86 |
+
"""
|
| 87 |
+
Gets the git commit hash of your current repo (if it exists).
|
| 88 |
+
Source: https://github.com/EleutherAI/gpt-neox/blob/b608043be541602170bfcfb8ec9bf85e8a0799e0/megatron/neox_arguments/neox_args.py#L42
|
| 89 |
+
"""
|
| 90 |
+
try:
|
| 91 |
+
git_hash = subprocess.check_output(["git", "describe", "--always"]).strip()
|
| 92 |
+
git_hash = git_hash.decode()
|
| 93 |
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
| 94 |
+
# FileNotFoundError occurs when git not installed on system
|
| 95 |
+
git_hash = get_commit_from_path(os.getcwd()) # git hash of repo if exists
|
| 96 |
+
return git_hash
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def add_env_info(storage: Dict[str, Any]):
|
| 100 |
+
try:
|
| 101 |
+
pretty_env_info = get_pretty_env_info()
|
| 102 |
+
except Exception as err:
|
| 103 |
+
pretty_env_info = str(err)
|
| 104 |
+
try:
|
| 105 |
+
lm_eval_version = version("lm_eval")
|
| 106 |
+
except Exception as err:
|
| 107 |
+
lm_eval_version = str(err)
|
| 108 |
+
transformers_version = trans_version
|
| 109 |
+
upper_dir_commit = get_commit_from_path(
|
| 110 |
+
Path(os.getcwd(), "..")
|
| 111 |
+
) # git hash of upper repo if exists
|
| 112 |
+
added_info = {
|
| 113 |
+
"pretty_env_info": pretty_env_info,
|
| 114 |
+
"transformers_version": transformers_version,
|
| 115 |
+
"lm_eval_version": lm_eval_version,
|
| 116 |
+
"upper_git_hash": upper_dir_commit, # in case this repo is submodule
|
| 117 |
+
}
|
| 118 |
+
storage.update(added_info)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def add_tokenizer_info(storage: Dict[str, Any], lm):
|
| 122 |
+
if getattr(lm, "tokenizer", False):
|
| 123 |
+
try:
|
| 124 |
+
tokenizer_info = {
|
| 125 |
+
"tokenizer_pad_token": [
|
| 126 |
+
lm.tokenizer.pad_token,
|
| 127 |
+
str(lm.tokenizer.pad_token_id),
|
| 128 |
+
],
|
| 129 |
+
"tokenizer_eos_token": [
|
| 130 |
+
lm.tokenizer.eos_token,
|
| 131 |
+
str(lm.tokenizer.eos_token_id),
|
| 132 |
+
],
|
| 133 |
+
"tokenizer_bos_token": [
|
| 134 |
+
lm.tokenizer.bos_token,
|
| 135 |
+
str(lm.tokenizer.bos_token_id),
|
| 136 |
+
],
|
| 137 |
+
"eot_token_id": getattr(lm, "eot_token_id", None),
|
| 138 |
+
"max_length": getattr(lm, "max_length", None),
|
| 139 |
+
}
|
| 140 |
+
storage.update(tokenizer_info)
|
| 141 |
+
except Exception as err:
|
| 142 |
+
logger.debug(
|
| 143 |
+
f"Logging detailed tokenizer info failed with {err}, skipping..."
|
| 144 |
+
)
|
| 145 |
+
# seems gguf and textsynth do not have tokenizer
|
| 146 |
+
else:
|
| 147 |
+
logger.debug(
|
| 148 |
+
"LM does not have a 'tokenizer' attribute, not logging tokenizer metadata to results."
|
| 149 |
+
)
|
lm-evaluation-harness/lm_eval/loggers/wandb_logger.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import copy
|
| 2 |
+
import json
|
| 3 |
+
import logging
|
| 4 |
+
from typing import Any, Dict, List, Literal, Tuple
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
import pandas as pd
|
| 8 |
+
from packaging.version import Version
|
| 9 |
+
|
| 10 |
+
from lm_eval.loggers.utils import _handle_non_serializable, remove_none_pattern
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def get_wandb_printer() -> Literal["Printer"]:
|
| 17 |
+
"""Returns a wandb printer instance for pretty stdout."""
|
| 18 |
+
from wandb.sdk.lib.printer import new_printer
|
| 19 |
+
|
| 20 |
+
printer = new_printer()
|
| 21 |
+
return printer
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class WandbLogger:
|
| 25 |
+
def __init__(self, init_args=None, config_args=None) -> None:
|
| 26 |
+
"""Attaches to wandb logger if already initialized. Otherwise, passes init_args to wandb.init() and config_args to wandb.config.update()
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
init_args Optional[Dict]: Arguments for init configuration.
|
| 30 |
+
config_args Optional[Dict]: Arguments for config
|
| 31 |
+
|
| 32 |
+
Parse and log the results returned from evaluator.simple_evaluate() with:
|
| 33 |
+
wandb_logger.post_init(results)
|
| 34 |
+
wandb_logger.log_eval_result()
|
| 35 |
+
wandb_logger.log_eval_samples(results["samples"])
|
| 36 |
+
"""
|
| 37 |
+
try:
|
| 38 |
+
import wandb
|
| 39 |
+
|
| 40 |
+
assert Version(wandb.__version__) >= Version("0.13.6")
|
| 41 |
+
if Version(wandb.__version__) < Version("0.13.6"):
|
| 42 |
+
wandb.require("report-editing:v0")
|
| 43 |
+
except Exception as e:
|
| 44 |
+
logger.warning(
|
| 45 |
+
"To use the wandb reporting functionality please install wandb>=0.13.6.\n"
|
| 46 |
+
"To install the latest version of wandb run `pip install wandb --upgrade`\n"
|
| 47 |
+
f"{e}"
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
self.wandb_args: Dict[str, Any] = init_args or {}
|
| 51 |
+
self.wandb_config_args: Dict[str, Any] = config_args or {}
|
| 52 |
+
|
| 53 |
+
# pop the step key from the args to save for all logging calls
|
| 54 |
+
self.step = self.wandb_args.pop("step", None)
|
| 55 |
+
|
| 56 |
+
# initialize a W&B run
|
| 57 |
+
if wandb.run is None:
|
| 58 |
+
self.run = wandb.init(**self.wandb_args)
|
| 59 |
+
if self.wandb_config_args:
|
| 60 |
+
self.run.config.update(self.wandb_config_args)
|
| 61 |
+
else:
|
| 62 |
+
self.run = wandb.run
|
| 63 |
+
|
| 64 |
+
self.printer = get_wandb_printer()
|
| 65 |
+
|
| 66 |
+
def post_init(self, results: Dict[str, Any]) -> None:
|
| 67 |
+
self.results: Dict[str, Any] = copy.deepcopy(results)
|
| 68 |
+
self.task_names: List[str] = list(results.get("results", {}).keys())
|
| 69 |
+
self.group_names: List[str] = list(results.get("groups", {}).keys())
|
| 70 |
+
|
| 71 |
+
def _get_config(self) -> Dict[str, Any]:
|
| 72 |
+
"""Get configuration parameters."""
|
| 73 |
+
self.task_configs = self.results.get("configs", {})
|
| 74 |
+
cli_configs = self.results.get("config", {})
|
| 75 |
+
configs = {
|
| 76 |
+
"task_configs": self.task_configs,
|
| 77 |
+
"cli_configs": cli_configs,
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
return configs
|
| 81 |
+
|
| 82 |
+
def _sanitize_results_dict(self) -> Tuple[Dict[str, str], Dict[str, Any]]:
|
| 83 |
+
"""Sanitize the results dictionary."""
|
| 84 |
+
_results = copy.deepcopy(self.results.get("results", dict()))
|
| 85 |
+
|
| 86 |
+
# Remove None from the metric string name
|
| 87 |
+
tmp_results = copy.deepcopy(_results)
|
| 88 |
+
for task_name in self.task_names:
|
| 89 |
+
task_result = tmp_results.get(task_name, dict())
|
| 90 |
+
for metric_name, metric_value in task_result.items():
|
| 91 |
+
_metric_name, removed = remove_none_pattern(metric_name)
|
| 92 |
+
if removed:
|
| 93 |
+
_results[task_name][_metric_name] = metric_value
|
| 94 |
+
_results[task_name].pop(metric_name)
|
| 95 |
+
|
| 96 |
+
# remove string valued keys from the results dict
|
| 97 |
+
wandb_summary = {}
|
| 98 |
+
for task in self.task_names:
|
| 99 |
+
task_result = _results.get(task, dict())
|
| 100 |
+
for metric_name, metric_value in task_result.items():
|
| 101 |
+
if isinstance(metric_value, str):
|
| 102 |
+
wandb_summary[f"{task}/{metric_name}"] = metric_value
|
| 103 |
+
|
| 104 |
+
for summary_metric, summary_value in wandb_summary.items():
|
| 105 |
+
_task, _summary_metric = summary_metric.split("/")
|
| 106 |
+
_results[_task].pop(_summary_metric)
|
| 107 |
+
|
| 108 |
+
tmp_results = copy.deepcopy(_results)
|
| 109 |
+
for task_name, task_results in tmp_results.items():
|
| 110 |
+
for metric_name, metric_value in task_results.items():
|
| 111 |
+
_results[f"{task_name}/{metric_name}"] = metric_value
|
| 112 |
+
_results[task_name].pop(metric_name)
|
| 113 |
+
for task in self.task_names:
|
| 114 |
+
_results.pop(task)
|
| 115 |
+
|
| 116 |
+
return wandb_summary, _results
|
| 117 |
+
|
| 118 |
+
def _log_results_as_table(self) -> None:
|
| 119 |
+
"""Generate and log evaluation results as a table to W&B."""
|
| 120 |
+
columns = [
|
| 121 |
+
"Version",
|
| 122 |
+
"Filter",
|
| 123 |
+
"num_fewshot",
|
| 124 |
+
"Metric",
|
| 125 |
+
"Value",
|
| 126 |
+
"Stderr",
|
| 127 |
+
]
|
| 128 |
+
|
| 129 |
+
def make_table(columns: List[str], key: str = "results"):
|
| 130 |
+
import wandb
|
| 131 |
+
|
| 132 |
+
table = wandb.Table(columns=columns)
|
| 133 |
+
results = copy.deepcopy(self.results)
|
| 134 |
+
|
| 135 |
+
for k, dic in results.get(key).items():
|
| 136 |
+
if k in self.group_names and not key == "groups":
|
| 137 |
+
continue
|
| 138 |
+
version = results.get("versions").get(k)
|
| 139 |
+
if version == "N/A":
|
| 140 |
+
version = None
|
| 141 |
+
n = results.get("n-shot").get(k)
|
| 142 |
+
|
| 143 |
+
for (mf), v in dic.items():
|
| 144 |
+
m, _, f = mf.partition(",")
|
| 145 |
+
if m.endswith("_stderr"):
|
| 146 |
+
continue
|
| 147 |
+
if m == "alias":
|
| 148 |
+
continue
|
| 149 |
+
|
| 150 |
+
if m + "_stderr" + "," + f in dic:
|
| 151 |
+
se = dic[m + "_stderr" + "," + f]
|
| 152 |
+
if se != "N/A":
|
| 153 |
+
se = "%.4f" % se
|
| 154 |
+
table.add_data(*[k, version, f, n, m, str(v), str(se)])
|
| 155 |
+
else:
|
| 156 |
+
table.add_data(*[k, version, f, n, m, str(v), ""])
|
| 157 |
+
|
| 158 |
+
return table
|
| 159 |
+
|
| 160 |
+
# log the complete eval result to W&B Table
|
| 161 |
+
table = make_table(["Tasks"] + columns, "results")
|
| 162 |
+
self.run.log({"evaluation/eval_results": table}, step=self.step)
|
| 163 |
+
|
| 164 |
+
if "groups" in self.results.keys():
|
| 165 |
+
table = make_table(["Groups"] + columns, "groups")
|
| 166 |
+
self.run.log({"evaluation/group_eval_results": table}, step=self.step)
|
| 167 |
+
|
| 168 |
+
def _log_results_as_artifact(self) -> None:
|
| 169 |
+
"""Log results as JSON artifact to W&B."""
|
| 170 |
+
import wandb
|
| 171 |
+
|
| 172 |
+
dumped = json.dumps(
|
| 173 |
+
self.results, indent=2, default=_handle_non_serializable, ensure_ascii=False
|
| 174 |
+
)
|
| 175 |
+
artifact = wandb.Artifact("results", type="eval_results")
|
| 176 |
+
with artifact.new_file("results.json", mode="w", encoding="utf-8") as f:
|
| 177 |
+
f.write(dumped)
|
| 178 |
+
self.run.log_artifact(artifact)
|
| 179 |
+
|
| 180 |
+
def log_eval_result(self) -> None:
|
| 181 |
+
"""Log evaluation results to W&B."""
|
| 182 |
+
# Log configs to wandb
|
| 183 |
+
configs = self._get_config()
|
| 184 |
+
self.run.config.update(configs, allow_val_change=self.step is not None)
|
| 185 |
+
|
| 186 |
+
wandb_summary, self.wandb_results = self._sanitize_results_dict()
|
| 187 |
+
# update wandb.run.summary with items that were removed
|
| 188 |
+
self.run.summary.update(wandb_summary)
|
| 189 |
+
# Log the evaluation metrics to wandb
|
| 190 |
+
self.run.log(self.wandb_results, step=self.step)
|
| 191 |
+
# Log the evaluation metrics as W&B Table
|
| 192 |
+
self._log_results_as_table()
|
| 193 |
+
# Log the results dict as json to W&B Artifacts
|
| 194 |
+
self._log_results_as_artifact()
|
| 195 |
+
|
| 196 |
+
def _generate_dataset(
|
| 197 |
+
self, data: List[Dict[str, Any]], config: Dict[str, Any]
|
| 198 |
+
) -> pd.DataFrame:
|
| 199 |
+
"""Generate a dataset from evaluation data.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
data (List[Dict[str, Any]]): The data to generate a dataset for.
|
| 203 |
+
config (Dict[str, Any]): The configuration of the task.
|
| 204 |
+
|
| 205 |
+
Returns:
|
| 206 |
+
pd.DataFrame: A dataframe that is ready to be uploaded to W&B.
|
| 207 |
+
"""
|
| 208 |
+
ids = [x["doc_id"] for x in data]
|
| 209 |
+
labels = [x["target"] for x in data]
|
| 210 |
+
instance = [""] * len(ids)
|
| 211 |
+
resps = [""] * len(ids)
|
| 212 |
+
filtered_resps = [""] * len(ids)
|
| 213 |
+
model_outputs = {}
|
| 214 |
+
|
| 215 |
+
metrics_list = config["metric_list"]
|
| 216 |
+
metrics = {}
|
| 217 |
+
for metric in metrics_list:
|
| 218 |
+
metric = metric.get("metric")
|
| 219 |
+
if metric in ["word_perplexity", "byte_perplexity", "bits_per_byte"]:
|
| 220 |
+
metrics[f"{metric}_loglikelihood"] = [x[metric][0] for x in data]
|
| 221 |
+
if metric in ["byte_perplexity", "bits_per_byte"]:
|
| 222 |
+
metrics[f"{metric}_bytes"] = [x[metric][1] for x in data]
|
| 223 |
+
else:
|
| 224 |
+
metrics[f"{metric}_words"] = [x[metric][1] for x in data]
|
| 225 |
+
else:
|
| 226 |
+
metrics[metric] = [x[metric] for x in data]
|
| 227 |
+
|
| 228 |
+
if config["output_type"] == "loglikelihood":
|
| 229 |
+
instance = [x["arguments"][0][0] for x in data]
|
| 230 |
+
labels = [x["arguments"][0][1] for x in data]
|
| 231 |
+
resps = [
|
| 232 |
+
f"log probability of continuation is {x['resps'][0][0][0]} "
|
| 233 |
+
+ "\n\n"
|
| 234 |
+
+ "continuation will {} generated with greedy sampling".format(
|
| 235 |
+
"not be" if not x["resps"][0][0][1] else "be"
|
| 236 |
+
)
|
| 237 |
+
for x in data
|
| 238 |
+
]
|
| 239 |
+
filtered_resps = [
|
| 240 |
+
f"log probability of continuation is {x['filtered_resps'][0][0]} "
|
| 241 |
+
+ "\n\n"
|
| 242 |
+
+ "continuation will {} generated with greedy sampling".format(
|
| 243 |
+
"not be" if not x["filtered_resps"][0][1] else "be"
|
| 244 |
+
)
|
| 245 |
+
for x in data
|
| 246 |
+
]
|
| 247 |
+
elif config["output_type"] == "multiple_choice":
|
| 248 |
+
instance = [x["arguments"][0][0] for x in data]
|
| 249 |
+
choices = [
|
| 250 |
+
"\n".join([f"{idx}. {y[1]}" for idx, y in enumerate(x["arguments"])])
|
| 251 |
+
for x in data
|
| 252 |
+
]
|
| 253 |
+
resps = [np.argmax([n[0][0] for n in x["resps"]]) for x in data]
|
| 254 |
+
filtered_resps = [
|
| 255 |
+
np.argmax([n[0] for n in x["filtered_resps"]]) for x in data
|
| 256 |
+
]
|
| 257 |
+
elif config["output_type"] == "loglikelihood_rolling":
|
| 258 |
+
instance = [x["arguments"][0][0] for x in data]
|
| 259 |
+
resps = [x["resps"][0][0] for x in data]
|
| 260 |
+
filtered_resps = [x["filtered_resps"][0] for x in data]
|
| 261 |
+
elif config["output_type"] == "generate_until":
|
| 262 |
+
instance = [x["arguments"][0][0] for x in data]
|
| 263 |
+
resps = [x["resps"][0][0] for x in data]
|
| 264 |
+
filtered_resps = [x["filtered_resps"][0] for x in data]
|
| 265 |
+
|
| 266 |
+
model_outputs["raw_predictions"] = resps
|
| 267 |
+
model_outputs["filtered_predictions"] = filtered_resps
|
| 268 |
+
|
| 269 |
+
df_data = {
|
| 270 |
+
"id": ids,
|
| 271 |
+
"data": instance,
|
| 272 |
+
}
|
| 273 |
+
if config["output_type"] == "multiple_choice":
|
| 274 |
+
df_data["choices"] = choices
|
| 275 |
+
|
| 276 |
+
tmp_data = {
|
| 277 |
+
"input_len": [len(x) for x in instance],
|
| 278 |
+
"labels": labels,
|
| 279 |
+
"output_type": config["output_type"],
|
| 280 |
+
}
|
| 281 |
+
df_data.update(tmp_data)
|
| 282 |
+
df_data.update(model_outputs)
|
| 283 |
+
df_data.update(metrics)
|
| 284 |
+
|
| 285 |
+
return pd.DataFrame(df_data)
|
| 286 |
+
|
| 287 |
+
def _log_samples_as_artifact(
|
| 288 |
+
self, data: List[Dict[str, Any]], task_name: str
|
| 289 |
+
) -> None:
|
| 290 |
+
import wandb
|
| 291 |
+
|
| 292 |
+
# log the samples as an artifact
|
| 293 |
+
dumped = json.dumps(
|
| 294 |
+
data,
|
| 295 |
+
indent=2,
|
| 296 |
+
default=_handle_non_serializable,
|
| 297 |
+
ensure_ascii=False,
|
| 298 |
+
)
|
| 299 |
+
artifact = wandb.Artifact(f"{task_name}", type="samples_by_task")
|
| 300 |
+
with artifact.new_file(
|
| 301 |
+
f"{task_name}_eval_samples.json", mode="w", encoding="utf-8"
|
| 302 |
+
) as f:
|
| 303 |
+
f.write(dumped)
|
| 304 |
+
self.run.log_artifact(artifact)
|
| 305 |
+
# artifact.wait()
|
| 306 |
+
|
| 307 |
+
def log_eval_samples(self, samples: Dict[str, List[Dict[str, Any]]]) -> None:
|
| 308 |
+
"""Log evaluation samples to W&B.
|
| 309 |
+
|
| 310 |
+
Args:
|
| 311 |
+
samples (Dict[str, List[Dict[str, Any]]]): Evaluation samples for each task.
|
| 312 |
+
"""
|
| 313 |
+
task_names: List[str] = [
|
| 314 |
+
x for x in self.task_names if x not in self.group_names
|
| 315 |
+
]
|
| 316 |
+
|
| 317 |
+
ungrouped_tasks = []
|
| 318 |
+
tasks_by_groups = {}
|
| 319 |
+
|
| 320 |
+
for task_name in task_names:
|
| 321 |
+
group_names = self.task_configs[task_name].get("group", None)
|
| 322 |
+
if group_names:
|
| 323 |
+
if isinstance(group_names, str):
|
| 324 |
+
group_names = [group_names]
|
| 325 |
+
|
| 326 |
+
for group_name in group_names:
|
| 327 |
+
if not tasks_by_groups.get(group_name):
|
| 328 |
+
tasks_by_groups[group_name] = [task_name]
|
| 329 |
+
else:
|
| 330 |
+
tasks_by_groups[group_name].append(task_name)
|
| 331 |
+
else:
|
| 332 |
+
ungrouped_tasks.append(task_name)
|
| 333 |
+
|
| 334 |
+
for task_name in ungrouped_tasks:
|
| 335 |
+
eval_preds = samples[task_name]
|
| 336 |
+
|
| 337 |
+
# log the samples as a W&B Table
|
| 338 |
+
df = self._generate_dataset(eval_preds, self.task_configs.get(task_name))
|
| 339 |
+
self.run.log({f"{task_name}_eval_results": df}, step=self.step)
|
| 340 |
+
|
| 341 |
+
# log the samples as a json file as W&B Artifact
|
| 342 |
+
self._log_samples_as_artifact(eval_preds, task_name)
|
| 343 |
+
|
| 344 |
+
for group, grouped_tasks in tasks_by_groups.items():
|
| 345 |
+
grouped_df = pd.DataFrame()
|
| 346 |
+
for task_name in grouped_tasks:
|
| 347 |
+
eval_preds = samples[task_name]
|
| 348 |
+
df = self._generate_dataset(
|
| 349 |
+
eval_preds, self.task_configs.get(task_name)
|
| 350 |
+
)
|
| 351 |
+
df["group"] = group
|
| 352 |
+
df["task"] = task_name
|
| 353 |
+
grouped_df = pd.concat([grouped_df, df], ignore_index=True)
|
| 354 |
+
|
| 355 |
+
# log the samples as a json file as W&B Artifact
|
| 356 |
+
self._log_samples_as_artifact(eval_preds, task_name)
|
| 357 |
+
|
| 358 |
+
self.run.log({f"{group}_eval_results": grouped_df}, step=self.step)
|
lm-evaluation-harness/lm_eval/models/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import (
|
| 2 |
+
anthropic_llms,
|
| 3 |
+
api_models,
|
| 4 |
+
dummy,
|
| 5 |
+
gguf,
|
| 6 |
+
hf_audiolm,
|
| 7 |
+
hf_steered,
|
| 8 |
+
hf_vlms,
|
| 9 |
+
huggingface,
|
| 10 |
+
ibm_watsonx_ai,
|
| 11 |
+
mamba_lm,
|
| 12 |
+
nemo_lm,
|
| 13 |
+
neuralmagic,
|
| 14 |
+
neuron_optimum,
|
| 15 |
+
openai_completions,
|
| 16 |
+
optimum_ipex,
|
| 17 |
+
optimum_lm,
|
| 18 |
+
sglang_causallms,
|
| 19 |
+
sglang_generate_API,
|
| 20 |
+
textsynth,
|
| 21 |
+
vllm_causallms,
|
| 22 |
+
vllm_vlms,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# TODO: implement __all__
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
# enable hf hub transfer if available
|
| 31 |
+
import hf_transfer # type: ignore # noqa
|
| 32 |
+
import huggingface_hub.constants # type: ignore
|
| 33 |
+
|
| 34 |
+
huggingface_hub.constants.HF_HUB_ENABLE_HF_TRANSFER = True
|
| 35 |
+
except ImportError:
|
| 36 |
+
pass
|
lm-evaluation-harness/lm_eval/models/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (835 Bytes). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.17 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/anthropic_llms.cpython-310.pyc
ADDED
|
Binary file (11.2 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/anthropic_llms.cpython-311.pyc
ADDED
|
Binary file (16.8 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/api_models.cpython-310.pyc
ADDED
|
Binary file (20.3 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/api_models.cpython-311.pyc
ADDED
|
Binary file (37.8 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/dummy.cpython-310.pyc
ADDED
|
Binary file (1.62 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/dummy.cpython-311.pyc
ADDED
|
Binary file (2.59 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-310.pyc
ADDED
|
Binary file (4.13 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-311.pyc
ADDED
|
Binary file (7.13 kB). View file
|
|
|