"vscode:/vscode.git/clone" did not exist on "213ffdb54899cd82a4ecf911f217542a1b37d2fc"
Commit 1f8a8c1d authored by jon-tow's avatar jon-tow
Browse files

Merge branch 'master' of https://github.com/EleutherAI/lm-evaluation-harness into remove-dataset

parents b4c0275d b0acb337
[flake8]
ignore = E203, E266, E501, W503, F403, F401, C901
max-line-length = 127
max-complexity = 10
select = B,C,E,F,W,T4,B9
name: Pull Request
on: [pull_request]
jobs:
pre-commit:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: pre-commit/action@v2.0.3
# Ignore test linting to avoid conflicting changes to version stability.
exclude: ^tests/testdata/
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: check-added-large-files
- id: check-ast
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: destroyed-symlinks
- id: detect-private-key
- id: end-of-file-fixer
- id: no-commit-to-branch
- id: requirements-txt-fixer
- id: trailing-whitespace
- id: fix-byte-order-marker
exclude: docs/CNAME
- id: fix-encoding-pragma
args: [--remove]
- id: mixed-line-ending
args: [--fix=lf]
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.9
hooks:
- id: flake8
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
language_version: python3.8
- repo: https://github.com/codespell-project/codespell
rev: v2.1.0
hooks:
- id: codespell
exclude: >
(?x)^(
.*\.json|ignore.txt
)$
args: [--check-filenames, --check-hidden, --ignore-words=ignore.txt]
...@@ -26,7 +26,7 @@ To evaluate a model, (e.g. GPT-2) on NLU tasks (e.g. LAMBADA, HellaSwag), you ca ...@@ -26,7 +26,7 @@ To evaluate a model, (e.g. GPT-2) on NLU tasks (e.g. LAMBADA, HellaSwag), you ca
```bash ```bash
python main.py \ python main.py \
--model gpt2 \ --model gpt2 \
--device cuda:0 \ --device 0 \
--tasks lambada,hellaswag --tasks lambada,hellaswag
``` ```
(This uses gpt2-117M by default as per HF defaults, use --model_args to specify other gpt2 sizes) (This uses gpt2-117M by default as per HF defaults, use --model_args to specify other gpt2 sizes)
...@@ -37,7 +37,7 @@ Additional arguments can be provided to the model constructor using the `--model ...@@ -37,7 +37,7 @@ Additional arguments can be provided to the model constructor using the `--model
python main.py \ python main.py \
--model gpt2 \ --model gpt2 \
--model_args pretrained=EleutherAI/gpt-neo-2.7B \ --model_args pretrained=EleutherAI/gpt-neo-2.7B \
--device cuda:0 \ --device 0 \
--tasks lambada,hellaswag --tasks lambada,hellaswag
``` ```
...@@ -375,7 +375,7 @@ Additional arguments can be provided to the model constructor using the `--model ...@@ -375,7 +375,7 @@ Additional arguments can be provided to the model constructor using the `--model
python main.py \ python main.py \
--model gpt2 \ --model gpt2 \
--model_args pretrained=EleutherAI/gpt-neo-1.3B \ --model_args pretrained=EleutherAI/gpt-neo-1.3B \
--device cuda:0 \ --device 0 \
--tasks lambada,hellaswag \ --tasks lambada,hellaswag \
--num_fewshot 2 --num_fewshot 2
``` ```
...@@ -392,6 +392,21 @@ python write_out.py \ ...@@ -392,6 +392,21 @@ python write_out.py \
This will write out one text file for each task. This will write out one text file for each task.
### Test Set Decontamination
For more details see the [decontamination guide](./docs/decontamination.md).
The directory provided with the "--decontamination_ngrams_path" argument should contain
the ngram files and info.json. See the above guide for ngram generation for the pile, this could be adapted for other training sets.
```bash
python main.py \
--model gpt2 \
--device 0 \
--tasks sciq \
--decontamination_ngrams_path path/containing/training/set/ngrams
```
### Code Structure ### Code Structure
There are two major components of the library: There are two major components of the library:
......
# Decontamination
## Usage
Simply add a "--decontamination_ngrams_path" when running main.py. The provided directory should contain
the ngram files and info.json produced in "Pile Ngram Generation" further down.
```bash
python main.py \
--model gpt2 \
--device 0 \
--tasks sciq \
--decontamination_ngrams_path path/containing/training/set/ngrams
```
## Background
Downstream evaluations test model generalization, and are less useful when test set data also exists in the training set (leakage/contamination).
As a first step this is resolved through training set filtering, however often benchmarks don't exist or weren't considered prior to model training. In this case it is useful to measure the impact of test set leakage by detecting the contaminated test examples and producing a clean version of the benchmark.
The basis for our decontamination procedure can be found in Appendix C of "Language Models are Few-Shot Learners". OpenAI defined a test document as contaminated if any N-gram overlap existed with any training document. They used a range of N values between 8 and 13 depending on dataset, while we just used 13 for simplicity.
## Implementation
Contamination detection can be found in "lm_eval/decontaminate.py" with supporting code in "lm_eval/decontamination/".
decontaminate.py does the following:
1. Build dictionaries of all ngrams and their corresponding evaluation/document ids.
2. Scan through sorted files containing training set n-grams.
3. If a match is found, the corresponding evaluation/document combinations are marked as contaminated.
"lm_eval/evaluator.py" can then produce a clean version of the benchmark by excluding the results of contaminated documents. For each metric, a clean version will be shown in the results with a "decontaminate" suffix.
This is disabled by default for new tasks, to support decontamination on a task override the "should_decontaminate" and "doc_to_decontamination_query" methods. For more details see the [task guide](task_guide.md).
## Pile Ngram Generation
The relevant scripts can be found in scripts/clean_training_data, which also import from
"lm_eval/decontamination/"
1. git clone https://github.com/EleutherAI/lm-evaluation-harness.git
2. pip install -r requirements.txt
3. Download The Pile from [The Eye](https://the-eye.eu/public/AI/pile/train/)
4. Place pile files in "pile" directory under "lm-evaluation-harness" (or create a symlink)
5. Run generate_13_grams.
```bash
export PYTHONHASHSEED=0
python -m scripts/clean_training_data/generate_13_grams \
-dir path/to/working/directory \
-n 13 \
-buckets 500
```
Took approximately 4 days for us. We had the time to wait, but this could be scaled out by doing partial pile scans on multiple instances of this script and merging the relevant buckets. We fixed PYTHONHASHSEED to ensure reproducibility of bucket hashing.
6. Sort the generated 13-grams.
```bash
python -m scripts/clean_training_data/sort_13_gram_buckets \
-dir path/to/working/directory/output
```
Took approximately 5 days for us. You could speed this up by spreading the files around to different machines and running the sort script before gathering them together.
7. Compress the sorted 13 grams files and place them together with info.json.
This step only takes a few hours.
```bash
python -m scripts/clean_training_data/compress_and_package \
-dir path/to/working/directory \
-output path/to/final/directory \
-procs 8
```
Congratulations, the final directory can now be passed to lm-evaulation-harness with the "--decontamination_ngrams_path" argument.
...@@ -151,6 +151,13 @@ def doc_to_target(self, doc): ...@@ -151,6 +151,13 @@ def doc_to_target(self, doc):
Finally, be aware that the strings from `doc_to_text` and `doc_to_target` will be concatenated together to build up labeled examples in the k-shot setting where k > 0. Design with that in mind 👍. Finally, be aware that the strings from `doc_to_text` and `doc_to_target` will be concatenated together to build up labeled examples in the k-shot setting where k > 0. Design with that in mind 👍.
### Decontamination
For background on decontamination please see [this](./decontamination.md).
If you wish to support decontamination studies for your task simply override the "should_decontaminate" method and return true.
You also need to override "doc_to_decontamination_query" and return the data you wish to compare against the training set. This doesn't necessarily need to be the full document or request, and we leave this up to the implementor. For a multi-choice evaluation you could for example just return the question.
### Registering Your Task ### Registering Your Task
Now's a good time to register your task to expose it for usage. All you'll need to do is import your task module in `lm_eval/tasks/__init__.py` and provide an entry in the `TASK_REGISTRY` dictionary with the key as the name of your benchmark task (in the form it'll be referred to in the command line) and the value as the task class. See how it's done for other tasks in the [file](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/__init__.py). Now's a good time to register your task to expose it for usage. All you'll need to do is import your task module in `lm_eval/tasks/__init__.py` and provide an entry in the `TASK_REGISTRY` dictionary with the key as the name of your benchmark task (in the form it'll be referred to in the command line) and the value as the task class. See how it's done for other tasks in the [file](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/__init__.py).
...@@ -192,7 +199,11 @@ def construct_requests(self, doc, ctx): ...@@ -192,7 +199,11 @@ def construct_requests(self, doc, ctx):
""" """
return ... return ...
``` ```
If your task requires generating text you'll need to return a `rf.greedy_until` request otherwise an `rf.loglikelihood` across all labels in a classification tasks will do. #### What's a `Request`? What's a `doc`?
To reiterate, a `doc` is just a `Dict` object that contains information about a document from your corpus. It can contain things like a prompt, question type information, answers and anything else you think will be needed in order to assess your model for a given task. Keep in mind that the fields of this can be basically whatever you want (you can sort this out in `training_docs` \ `validation_docs` \ `test_docs` if you need to customise things - see above), just remember to be consistent with them throughout the rest of the `Task` you write up.
A `Request` is an object that takes the text prompt you want to present to a model and computes one of a few different types of response. These are evaluated lazily (meaning, only when the result is actually needed). If your task requires generating text you'll need to return a `rf.greedy_until` request otherwise an `rf.loglikelihood` across all labels in a classification tasks will do.
The function `construct_requests` can return a list of `Request`s or an iterable; it's perfectly fine to `yield` them from something or other. This is particularly handy if you are creating more than one request per `doc` (usually because you're up to something like multi-task learning). The objects this function returns then get consumed one by one and turned into result objects.
```python ```python
def process_results(self, doc, results): def process_results(self, doc, results):
...@@ -207,6 +218,8 @@ def process_results(self, doc, results): ...@@ -207,6 +218,8 @@ def process_results(self, doc, results):
""" """
return {} return {}
``` ```
This is the next step in the chain after `construct_requests`. In between this function and the one above, the request is evaluated. The results of that request are returned in the `results` arg to this function. By processing results, what is meant is calculating the metric or metrics of interest for your dataset using the result and associated ground truth given to this function. It's possible to calculate and return multiple metrics in this function and the logic for it can be whatever you want - as long as you've made sure the ground truth was included in the `doc` object. The dict returned from this function should be of the format `{'metric_name': value}`. It is not necessary to have the same keys for every doc processed using `process_results`; this sort of thing can be handled in the next function, `aggregation`.
```python ```python
def aggregation(self): def aggregation(self):
...@@ -217,8 +230,10 @@ def aggregation(self): ...@@ -217,8 +230,10 @@ def aggregation(self):
""" """
return {} return {}
``` ```
In `process_results`, model outputs are converted into metrics. These metrics are per document metrics, however; the `aggregation` function is used to work out what to do with them to create a corpus-level metric. Imagine you have a bunch of documents, for each of which you have calculated an F1 score. What should that mean overall? Should they be summed, averaged, the min/max found? This function handles that problem.
See `lm_eval/metrics.py` for a few "built-in" aggregate metrics you can easily import. The contents of the function itself are pretty straightforward; it should simply return a dict that maps from each metric label that could be returned by `process_results` to a function that can be used to aggregate that metric. That is to say, if the metrics that `process_results` could return are given by `{'a', 'b', 'c'}`, then all of these keys should be present in the dict returned by `aggregation`.
__NOTE__: See `lm_eval/metrics.py` for a few "built-in" aggregate metrics you can easily import. The standard metrics available in this package are generally based on `sklearn` functions, so if you are in any doubt for how to set things up the documentation over there can be of assistance. If you need to write a custom metric for some reason, start by looking at the existing ones in `lm_eval/metrics.py` for an idea about what the function signature needs to be.
```python ```python
def higher_is_better(self): def higher_is_better(self):
...@@ -229,6 +244,7 @@ def higher_is_better(self): ...@@ -229,6 +244,7 @@ def higher_is_better(self):
""" """
return {} return {}
``` ```
Finally, this function returns a dict with the same keys as `aggregation` and as it says in the description, simply tells us whether higher scores are better.
Some tasks that are good examples of various ways evaluation can be implemented can be found here: [LAMBADA](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/lambada.py), [TriviaQA](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/triviaqa.py), [SQuAD](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/squad.py). Some tasks that are good examples of various ways evaluation can be implemented can be found here: [LAMBADA](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/lambada.py), [TriviaQA](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/triviaqa.py), [SQuAD](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/lm_eval/tasks/squad.py).
...@@ -279,6 +295,11 @@ class TaskName(...): ...@@ -279,6 +295,11 @@ class TaskName(...):
## Submitting your Task ## Submitting your Task
Although we currently do not work behind a specific style guide, we'd appreciate if you tidy up your file/s with the `black` formatter (which should've been install through the `requirements.txt`). Keep things clean…ish 🙂. You can format your changes and perform flake8 standard checks by running the following commands:
```sh
pre-commit install
pre-commit run --all-files
```
Now push your work and make a pull request! Thanks for the contribution 👍. If there are any questions, leave a message in the `#lm-thunderdome` channel on the EAI discord. Now push your work and make a pull request! Thanks for the contribution 👍. If there are any questions, leave a message in the `#lm-thunderdome` channel on the EAI discord.
ROUGE
rouge
nin
...@@ -51,7 +51,7 @@ class LM(abc.ABC): ...@@ -51,7 +51,7 @@ class LM(abc.ABC):
- We will use the full max context length of the model. - We will use the full max context length of the model.
- For inputs that exceed the max context length, we divide the tokenized string into chunks of up to - For inputs that exceed the max context length, we divide the tokenized string into chunks of up to
the max context length. the max context length.
- IMPORTANT: Each document's loglikelihood/perplexity is computed *separately*, unlike other implementaitons - IMPORTANT: Each document's loglikelihood/perplexity is computed *separately*, unlike other implementations
which may simply concatenate multiple documents together. which may simply concatenate multiple documents together.
- IMPORTANT: We maximize the amount of context for each prediction. Specifically, for inputs that we break into - IMPORTANT: We maximize the amount of context for each prediction. Specifically, for inputs that we break into
multiple chunks, the last input will still a full-sized context. multiple chunks, the last input will still a full-sized context.
...@@ -118,7 +118,6 @@ class LM(abc.ABC): ...@@ -118,7 +118,6 @@ class LM(abc.ABC):
class BaseLM(LM): class BaseLM(LM):
@property @property
@abstractmethod @abstractmethod
def eot_token_id(self): def eot_token_id(self):
...@@ -145,13 +144,16 @@ class BaseLM(LM): ...@@ -145,13 +144,16 @@ class BaseLM(LM):
pass pass
@abstractmethod @abstractmethod
def tok_encode(self, string: str): pass def tok_encode(self, string: str):
pass
@abstractmethod @abstractmethod
def tok_decode(self, tokens: Iterable[int]): pass def tok_decode(self, tokens: Iterable[int]):
pass
@abstractmethod @abstractmethod
def _model_generate(self, context, max_length, eos_token_id): pass def _model_generate(self, context, max_length, eos_token_id):
pass
@abstractmethod @abstractmethod
def _model_call(self, inps): def _model_call(self, inps):
...@@ -187,19 +189,26 @@ class BaseLM(LM): ...@@ -187,19 +189,26 @@ class BaseLM(LM):
# TODO: automatic batch size detection for vectorization # TODO: automatic batch size detection for vectorization
loglikelihoods = [] loglikelihoods = []
for string, in tqdm(requests): for (string,) in tqdm(requests):
rolling_token_windows = list(map(utils.make_disjoint_window, utils.get_rolling_token_windows( rolling_token_windows = list(
map(
utils.make_disjoint_window,
utils.get_rolling_token_windows(
token_list=self.tok_encode(string), token_list=self.tok_encode(string),
prefix_token=self.eot_token_id, prefix_token=self.eot_token_id,
max_seq_len=self.max_length, max_seq_len=self.max_length,
context_len=1, context_len=1,
))) ),
)
)
rolling_token_windows = [(None,) + x for x in rolling_token_windows] rolling_token_windows = [(None,) + x for x in rolling_token_windows]
# TODO: extract out this call so it only gets called once and also somehow figure out partial caching for # TODO: extract out this call so it only gets called once and also somehow figure out partial caching for
# that # that
string_nll = self._loglikelihood_tokens(rolling_token_windows, disable_tqdm=True) string_nll = self._loglikelihood_tokens(
rolling_token_windows, disable_tqdm=True
)
# discard is_greedy # discard is_greedy
string_nll = [x[0] for x in string_nll] string_nll = [x[0] for x in string_nll]
...@@ -225,8 +234,10 @@ class BaseLM(LM): ...@@ -225,8 +234,10 @@ class BaseLM(LM):
return -len(toks), tuple(toks) return -len(toks), tuple(toks)
# TODO: automatic (variable) batch size detection for vectorization # TODO: automatic (variable) batch size detection for vectorization
reord = utils.Reorderer(requests, _collate) re_ord = utils.Reorderer(requests, _collate)
for chunk in utils.chunks(tqdm(reord.get_reordered(), disable=disable_tqdm), self.batch_size): for chunk in utils.chunks(
tqdm(re_ord.get_reordered(), disable=disable_tqdm), self.batch_size
):
inps = [] inps = []
cont_toks_list = [] cont_toks_list = []
inplens = [] inplens = []
...@@ -252,44 +263,60 @@ class BaseLM(LM): ...@@ -252,44 +263,60 @@ class BaseLM(LM):
# when too long to fit in context, truncate from the left # when too long to fit in context, truncate from the left
inp = torch.tensor( inp = torch.tensor(
(context_enc + continuation_enc)[-(self.max_length+1):][:-1], (context_enc + continuation_enc)[-(self.max_length + 1) :][:-1],
dtype=torch.long dtype=torch.long,
).to(self.device) ).to(self.device)
inplen, = inp.shape (inplen,) = inp.shape
cont = continuation_enc cont = continuation_enc
# since in _collate we make sure length is descending, the longest is always the first one. # since in _collate we make sure length is descending, the longest is always the first one.
padding_length = padding_length if padding_length is not None else inplen padding_length = (
padding_length if padding_length is not None else inplen
)
# pad length from seq to padding_length # pad length from seq to padding_length
inp = torch.cat([ inp = torch.cat(
[
inp, # [seq] inp, # [seq]
torch.zeros(padding_length - inplen, dtype=torch.long).to(inp.device) # [padding_length - seq] torch.zeros(padding_length - inplen, dtype=torch.long).to(
], dim=0) inp.device
), # [padding_length - seq]
],
dim=0,
)
inps.append(inp.unsqueeze(0)) # [1, padding_length] inps.append(inp.unsqueeze(0)) # [1, padding_length]
cont_toks_list.append(cont) cont_toks_list.append(cont)
inplens.append(inplen) inplens.append(inplen)
batched_inps = torch.cat(inps, dim=0) # [batch, padding_length batched_inps = torch.cat(inps, dim=0) # [batch, padding_length
multi_logits = F.log_softmax(self._model_call(batched_inps), dim=-1).cpu() # [batch, padding_length, vocab] multi_logits = F.log_softmax(
self._model_call(batched_inps), dim=-1
).cpu() # [batch, padding_length, vocab]
for (cache_key, _, _), logits, inp, inplen, cont_toks \ for (cache_key, _, _), logits, inp, inplen, cont_toks in zip(
in zip(chunk, multi_logits, inps, inplens, cont_toks_list): chunk, multi_logits, inps, inplens, cont_toks_list
):
# Slice to original seq length # Slice to original seq length
contlen = len(cont_toks) contlen = len(cont_toks)
logits = logits[inplen-contlen:inplen].unsqueeze(0) # [1, seq, vocab] logits = logits[inplen - contlen : inplen].unsqueeze(
0
) # [1, seq, vocab]
# Check if per-token argmax is exactly equal to continuation # Check if per-token argmax is exactly equal to continuation
greedy_tokens = logits.argmax(dim=-1) greedy_tokens = logits.argmax(dim=-1)
cont_toks = torch.tensor(cont_toks, dtype=torch.long).unsqueeze(0) # [1, seq] cont_toks = torch.tensor(cont_toks, dtype=torch.long).unsqueeze(
0
) # [1, seq]
max_equal = (greedy_tokens == cont_toks).all() max_equal = (greedy_tokens == cont_toks).all()
# Obtain log-probs at the corresponding continuation token indices # Obtain log-probs at the corresponding continuation token indices
# last_token_slice = logits[:, -1, :].squeeze(0).tolist() # last_token_slice = logits[:, -1, :].squeeze(0).tolist()
logits = torch.gather(logits, 2, cont_toks.unsqueeze(-1)).squeeze(-1) # [1, seq] logits = torch.gather(logits, 2, cont_toks.unsqueeze(-1)).squeeze(
-1
) # [1, seq]
# Answer: (log prob, is-exact-match) # Answer: (log prob, is-exact-match)
answer = (float(logits.sum()), bool(max_equal)) answer = (float(logits.sum()), bool(max_equal))
...@@ -300,10 +327,10 @@ class BaseLM(LM): ...@@ -300,10 +327,10 @@ class BaseLM(LM):
res.append(answer) res.append(answer)
return reord.get_original(res) return re_ord.get_original(res)
def greedy_until(self, requests): def greedy_until(self, requests):
# TODO: implement fully general `until` that handles untils that are # TODO: implement fully general `until` that handles until that are
# multiple tokens or that span multiple tokens correctly # multiple tokens or that span multiple tokens correctly
# TODO: extract to TokenizedLM? # TODO: extract to TokenizedLM?
...@@ -313,19 +340,23 @@ class BaseLM(LM): ...@@ -313,19 +340,23 @@ class BaseLM(LM):
toks = self.tok_encode(x[0]) toks = self.tok_encode(x[0])
return len(toks), x[0] return len(toks), x[0]
reord = utils.Reorderer(requests, _collate) re_ord = utils.Reorderer(requests, _collate)
for context, until in tqdm(reord.get_reordered()): for context, until in tqdm(re_ord.get_reordered()):
if isinstance(until, str): if isinstance(until, str):
until = [until] until = [until]
primary_until, = self.tok_encode(until[0]) (primary_until,) = self.tok_encode(until[0])
context_enc = torch.tensor([self.tok_encode(context)[self.max_gen_toks - self.max_length:]]).to(self.device) context_enc = torch.tensor(
[self.tok_encode(context)[self.max_gen_toks - self.max_length :]]
).to(self.device)
cont = self._model_generate(context_enc, context_enc.shape[1] + self.max_gen_toks, primary_until) cont = self._model_generate(
context_enc, context_enc.shape[1] + self.max_gen_toks, primary_until
)
s = self.tok_decode(cont[0].tolist()[context_enc.shape[1]:]) s = self.tok_decode(cont[0].tolist()[context_enc.shape[1] :])
for term in until: for term in until:
s = s.split(term)[0] s = s.split(term)[0]
...@@ -335,7 +366,7 @@ class BaseLM(LM): ...@@ -335,7 +366,7 @@ class BaseLM(LM):
res.append(s) res.append(s)
return reord.get_original(res) return re_ord.get_original(res)
class Task(abc.ABC): class Task(abc.ABC):
...@@ -383,7 +414,7 @@ class Task(abc.ABC): ...@@ -383,7 +414,7 @@ class Task(abc.ABC):
self._fewshot_docs = None self._fewshot_docs = None
def download(self, data_dir=None, cache_dir=None, download_mode=None): def download(self, data_dir=None, cache_dir=None, download_mode=None):
""" Downloads and returns the task dataset. """Downloads and returns the task dataset.
Override this method to download the dataset from a custom API. Override this method to download the dataset from a custom API.
:param data_dir: str :param data_dir: str
...@@ -412,9 +443,13 @@ class Task(abc.ABC): ...@@ -412,9 +443,13 @@ class Task(abc.ABC):
name=self.DATASET_NAME, name=self.DATASET_NAME,
data_dir=data_dir, data_dir=data_dir,
cache_dir=cache_dir, cache_dir=cache_dir,
download_mode=download_mode download_mode=download_mode,
) )
def should_decontaminate(self):
"""Whether this task supports decontamination against model training set."""
return False
@abstractmethod @abstractmethod
def has_training_docs(self): def has_training_docs(self):
"""Whether the task has a training set""" """Whether the task has a training set"""
...@@ -468,6 +503,12 @@ class Task(abc.ABC): ...@@ -468,6 +503,12 @@ class Task(abc.ABC):
return rnd.sample(self._training_docs, k) return rnd.sample(self._training_docs, k)
def doc_to_decontamination_query(self, doc):
print(
"Override doc_to_decontamination_query with document specific decontamination query."
)
assert False
@abstractmethod @abstractmethod
def doc_to_text(self, doc): def doc_to_text(self, doc):
pass pass
...@@ -478,7 +519,7 @@ class Task(abc.ABC): ...@@ -478,7 +519,7 @@ class Task(abc.ABC):
@abstractmethod @abstractmethod
def construct_requests(self, doc, ctx): def construct_requests(self, doc, ctx):
""" Uses RequestFactory to construct Requests and returns an iterable of """Uses RequestFactory to construct Requests and returns an iterable of
Requests which will be sent to the LM. Requests which will be sent to the LM.
:param doc: :param doc:
...@@ -523,15 +564,19 @@ class Task(abc.ABC): ...@@ -523,15 +564,19 @@ class Task(abc.ABC):
def fewshot_description(self): def fewshot_description(self):
import warnings import warnings
warnings.warn( warnings.warn(
"`fewshot_description` will be removed in futures versions. Pass " "`fewshot_description` will be removed in futures versions. Pass "
"any custom descriptions to the `evaluate` function instead.", "any custom descriptions to the `evaluate` function instead.",
DeprecationWarning) DeprecationWarning,
)
return "" return ""
@utils.positional_deprecated @utils.positional_deprecated
def fewshot_context(self, doc, num_fewshot, provide_description=None, rnd=None, description=None): def fewshot_context(
""" Returns a fewshot context string that is made up of a prepended description self, doc, num_fewshot, provide_description=None, rnd=None, description=None
):
"""Returns a fewshot context string that is made up of a prepended description
(if provided), the `num_fewshot` number of examples, and an appended prompt example. (if provided), the `num_fewshot` number of examples, and an appended prompt example.
:param doc: str :param doc: str
...@@ -548,7 +593,9 @@ class Task(abc.ABC): ...@@ -548,7 +593,9 @@ class Task(abc.ABC):
:returns: str :returns: str
The fewshot context. The fewshot context.
""" """
assert rnd is not None, "A `random.Random` generator argument must be provided to `rnd`" assert (
rnd is not None
), "A `random.Random` generator argument must be provided to `rnd`"
assert not provide_description, ( assert not provide_description, (
"The `provide_description` arg will be removed in future versions. To prepend " "The `provide_description` arg will be removed in future versions. To prepend "
"a custom description to the context, supply the corresponding string via the " "a custom description to the context, supply the corresponding string via the "
...@@ -556,7 +603,9 @@ class Task(abc.ABC): ...@@ -556,7 +603,9 @@ class Task(abc.ABC):
) )
if provide_description is not None: if provide_description is not None:
# nudge people to not specify it at all # nudge people to not specify it at all
print("WARNING: provide_description is deprecated and will be removed in a future version in favor of description_dict") print(
"WARNING: provide_description is deprecated and will be removed in a future version in favor of description_dict"
)
description = description + "\n\n" if description else "" description = description + "\n\n" if description else ""
...@@ -569,7 +618,9 @@ class Task(abc.ABC): ...@@ -569,7 +618,9 @@ class Task(abc.ABC):
else: else:
if self._fewshot_docs is None: if self._fewshot_docs is None:
self._fewshot_docs = list( self._fewshot_docs = list(
self.validation_docs() if self.has_validation_docs() else self.test_docs() self.validation_docs()
if self.has_validation_docs()
else self.test_docs()
) )
fewshotex = rnd.sample(self._fewshot_docs, num_fewshot + 1) fewshotex = rnd.sample(self._fewshot_docs, num_fewshot + 1)
...@@ -577,23 +628,27 @@ class Task(abc.ABC): ...@@ -577,23 +628,27 @@ class Task(abc.ABC):
# get rid of the doc that's the one we're evaluating, if it's in the fewshot # get rid of the doc that's the one we're evaluating, if it's in the fewshot
fewshotex = [x for x in fewshotex if x != doc][:num_fewshot] fewshotex = [x for x in fewshotex if x != doc][:num_fewshot]
labeled_examples = "\n\n".join( labeled_examples = (
[self.doc_to_text(doc) + self.doc_to_target(doc) for doc in fewshotex] "\n\n".join(
) + "\n\n" [
self.doc_to_text(doc) + self.doc_to_target(doc)
for doc in fewshotex
]
)
+ "\n\n"
)
example = self.doc_to_text(doc) example = self.doc_to_text(doc)
return description + labeled_examples + example return description + labeled_examples + example
class MultipleChoiceTask(Task): class MultipleChoiceTask(Task):
def doc_to_target(self, doc): def doc_to_target(self, doc):
return " " + doc['choices'][doc['gold']] return " " + doc["choices"][doc["gold"]]
def construct_requests(self, doc, ctx): def construct_requests(self, doc, ctx):
lls = [ lls = [
rf.loglikelihood(ctx, " {}".format(choice))[0] rf.loglikelihood(ctx, " {}".format(choice))[0] for choice in doc["choices"]
for choice in doc['choices']
] ]
return lls return lls
...@@ -601,9 +656,9 @@ class MultipleChoiceTask(Task): ...@@ -601,9 +656,9 @@ class MultipleChoiceTask(Task):
def process_results(self, doc, results): def process_results(self, doc, results):
gold = doc["gold"] gold = doc["gold"]
acc = 1. if np.argmax(results) == gold else 0. acc = 1.0 if np.argmax(results) == gold else 0.0
completion_len = np.array([float(len(i)) for i in doc["choices"]]) completion_len = np.array([float(len(i)) for i in doc["choices"]])
acc_norm = 1. if np.argmax(results / completion_len) == gold else 0. acc_norm = 1.0 if np.argmax(results / completion_len) == gold else 0.0
return { return {
"acc": acc, "acc": acc,
...@@ -624,6 +679,9 @@ class MultipleChoiceTask(Task): ...@@ -624,6 +679,9 @@ class MultipleChoiceTask(Task):
class PerplexityTask(Task, abc.ABC): class PerplexityTask(Task, abc.ABC):
def should_decontaminate(self):
"""Whether this task supports decontamination against model training set."""
return True
def has_training_docs(self): def has_training_docs(self):
return False return False
...@@ -632,9 +690,15 @@ class PerplexityTask(Task, abc.ABC): ...@@ -632,9 +690,15 @@ class PerplexityTask(Task, abc.ABC):
assert k == 0 assert k == 0
return [] return []
def fewshot_context(self, doc, num_fewshot, provide_description=None, rnd=None, description=None): def fewshot_context(
assert num_fewshot == 0, "The number of fewshot examples must be 0 for perplexity tasks." self, doc, num_fewshot, provide_description=None, rnd=None, description=None
assert rnd is not None, "A `random.Random` generator argument must be provided to `rnd`." ):
assert (
num_fewshot == 0
), "The number of fewshot examples must be 0 for perplexity tasks."
assert (
rnd is not None
), "A `random.Random` generator argument must be provided to `rnd`."
assert not provide_description, ( assert not provide_description, (
"The `provide_description` arg will be removed in future versions. To prepend " "The `provide_description` arg will be removed in future versions. To prepend "
"a custom description to the context, supply the corresponding string via the " "a custom description to the context, supply the corresponding string via the "
...@@ -642,7 +706,9 @@ class PerplexityTask(Task, abc.ABC): ...@@ -642,7 +706,9 @@ class PerplexityTask(Task, abc.ABC):
) )
if provide_description is not None: if provide_description is not None:
# nudge people to not specify it at all # nudge people to not specify it at all
print("WARNING: provide_description is deprecated and will be removed in a future version in favor of description_dict") print(
"WARNING: provide_description is deprecated and will be removed in a future version in favor of description_dict"
)
return "" return ""
...@@ -653,6 +719,9 @@ class PerplexityTask(Task, abc.ABC): ...@@ -653,6 +719,9 @@ class PerplexityTask(Task, abc.ABC):
"bits_per_byte": False, "bits_per_byte": False,
} }
def doc_to_decontamination_query(self, doc):
return doc
def doc_to_text(self, doc): def doc_to_text(self, doc):
return "" return ""
...@@ -665,7 +734,7 @@ class PerplexityTask(Task, abc.ABC): ...@@ -665,7 +734,7 @@ class PerplexityTask(Task, abc.ABC):
return req return req
def process_results(self, doc, results): def process_results(self, doc, results):
loglikelihood, = results (loglikelihood,) = results
words = self.count_words(doc) words = self.count_words(doc)
bytes_ = self.count_bytes(doc) bytes_ = self.count_bytes(doc)
return { return {
...@@ -687,13 +756,13 @@ class PerplexityTask(Task, abc.ABC): ...@@ -687,13 +756,13 @@ class PerplexityTask(Task, abc.ABC):
@classmethod @classmethod
def count_words(cls, doc): def count_words(cls, doc):
""" Downstream tasks with custom word boundaries should override this! """ """Downstream tasks with custom word boundaries should override this!"""
return len(re.split(r"\s+", doc)) return len(re.split(r"\s+", doc))
def hash_args(attr, args): def hash_args(attr, args):
dat = json.dumps([attr] + list(args)) dat = json.dumps([attr] + list(args))
return hashlib.sha256(dat.encode('utf-8')).hexdigest() return hashlib.sha256(dat.encode("utf-8")).hexdigest()
class CacheHook: class CacheHook:
...@@ -764,6 +833,7 @@ class CachingLM: ...@@ -764,6 +833,7 @@ class CachingLM:
self.dbdict.commit() self.dbdict.commit()
return res return res
return fn return fn
def get_cache_hook(self): def get_cache_hook(self):
...@@ -771,16 +841,18 @@ class CachingLM: ...@@ -771,16 +841,18 @@ class CachingLM:
REQUEST_RETURN_LENGTHS = { REQUEST_RETURN_LENGTHS = {
'loglikelihood': 2, "loglikelihood": 2,
'greedy_until': None, "greedy_until": None,
'loglikelihood_rolling': None, "loglikelihood_rolling": None,
} }
class Request: class Request:
def __init__(self, request_type, args, index=None): def __init__(self, request_type, args, index=None):
if request_type not in REQUEST_RETURN_LENGTHS.keys(): if request_type not in REQUEST_RETURN_LENGTHS.keys():
raise NotImplementedError('The request type {} is not implemented!'.format(request_type)) raise NotImplementedError(
"The request type {} is not implemented!".format(request_type)
)
self.request_type = request_type self.request_type = request_type
self.args = args self.args = args
...@@ -788,17 +860,21 @@ class Request: ...@@ -788,17 +860,21 @@ class Request:
def __iter__(self): def __iter__(self):
if REQUEST_RETURN_LENGTHS[self.request_type] is None: if REQUEST_RETURN_LENGTHS[self.request_type] is None:
raise IndexError('This request type does not return multiple arguments!') raise IndexError("This request type does not return multiple arguments!")
for i in range(REQUEST_RETURN_LENGTHS[self.request_type]): for i in range(REQUEST_RETURN_LENGTHS[self.request_type]):
yield Request(self.request_type, self.args, i) yield Request(self.request_type, self.args, i)
def __getitem__(self, i): def __getitem__(self, i):
if REQUEST_RETURN_LENGTHS[self.request_type] is None: if REQUEST_RETURN_LENGTHS[self.request_type] is None:
raise IndexError('This request type does not return multiple arguments!') raise IndexError("This request type does not return multiple arguments!")
return Request(self.request_type, self.args, i) return Request(self.request_type, self.args, i)
def __eq__(self, other): def __eq__(self, other):
return self.request_type == other.request_type and self.args == other.args and self.index == other.index return (
self.request_type == other.request_type
and self.args == other.args
and self.index == other.index
)
def __repr__(self): def __repr__(self):
return f"Req_{self.request_type}{self.args}[{self.index}]\n" return f"Req_{self.request_type}{self.args}[{self.index}]\n"
...@@ -808,6 +884,7 @@ class RequestFactory: ...@@ -808,6 +884,7 @@ class RequestFactory:
def __getattr__(self, attr): def __getattr__(self, attr):
def fn(*args): def fn(*args):
return Request(attr, args) return Request(attr, args)
return fn return fn
......
# datasets # datasets
This directory contains custom EleutherAI datasets not available in the HuggingFace `datasets` hub. This directory contains custom HuggingFace [dataset loading scripts](https://huggingface.co/docs/datasets/dataset_script). They are provided to maintain backward compatibility with the ad-hoc data downloaders in earlier versions of the `lm-evaluation-harness` before HuggingFace [`datasets`](https://huggingface.co/docs/datasets/index) was adopted as the default downloading manager. For example, some instances in the HuggingFace `datasets` repository process features (e.g. whitespace stripping, lower-casing, etc.) in ways that the `lm-evaluation-harness` did not.
In the rare case that you need to add a custom dataset to this collection, follow the __NOTE__: We are __not__ accepting any additional loading scripts into the main branch! If you'd like to use a custom dataset, fork the repo and follow HuggingFace's loading script guide found [here](https://huggingface.co/docs/datasets/dataset_script). You can then override your `Task`'s `DATASET_PATH` attribute to point to this script's local path.
HuggingFace `datasets` guide found [here](https://huggingface.co/docs/datasets/dataset_script).
\ No newline at end of file
__WARNING__: A handful of loading scripts are included in this collection because they have not yet been pushed to the Huggingface Hub or a HuggingFace organization repo. We will remove such scripts once pushed.
...@@ -68,61 +68,111 @@ class Arithmetic(datasets.GeneratorBasedBuilder): ...@@ -68,61 +68,111 @@ class Arithmetic(datasets.GeneratorBasedBuilder):
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_2da", name="arithmetic_2da",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/two_digit_addition.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/two_digit_addition.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="2-digit addition", description="2-digit addition",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_2ds", name="arithmetic_2ds",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/two_digit_subtraction.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/two_digit_subtraction.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="2-digit subtraction", description="2-digit subtraction",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_3da", name="arithmetic_3da",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/three_digit_addition.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/three_digit_addition.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="3-digit addition", description="3-digit addition",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_3ds", name="arithmetic_3ds",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/three_digit_subtraction.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/three_digit_subtraction.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="3-digit subtraction", description="3-digit subtraction",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_4da", name="arithmetic_4da",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/four_digit_addition.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/four_digit_addition.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="4-digit addition", description="4-digit addition",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_4ds", name="arithmetic_4ds",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/four_digit_subtraction.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/four_digit_subtraction.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="4-digit subtraction", description="4-digit subtraction",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_5da", name="arithmetic_5da",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/five_digit_addition.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/five_digit_addition.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="5-digit addition", description="5-digit addition",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_5ds", name="arithmetic_5ds",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/five_digit_subtraction.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/five_digit_subtraction.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="5-digit subtraction", description="5-digit subtraction",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_2dm", name="arithmetic_2dm",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/two_digit_multiplication.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/two_digit_multiplication.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="2-digit multiplication", description="2-digit multiplication",
), ),
ArithmeticConfig( ArithmeticConfig(
name="arithmetic_1dc", name="arithmetic_1dc",
url="https://raw.githubusercontent.com/openai/gpt-3/master/data/single_digit_three_ops.jsonl", url="https://raw.githubusercontent.com/openai/gpt-3/master/data/single_digit_three_ops.jsonl",
features=datasets.Features({"context": datasets.Value("string"), "completion": datasets.Value("string")}), features=datasets.Features(
{
"context": datasets.Value("string"),
"completion": datasets.Value("string"),
}
),
description="Single digit 3 operations", description="Single digit 3 operations",
), ),
] ]
...@@ -155,9 +205,12 @@ class Arithmetic(datasets.GeneratorBasedBuilder): ...@@ -155,9 +205,12 @@ class Arithmetic(datasets.GeneratorBasedBuilder):
with open(filepath, encoding="utf-8") as f: with open(filepath, encoding="utf-8") as f:
for key, row in enumerate(f): for key, row in enumerate(f):
data = json.loads(row) data = json.loads(row)
context = data['context'].strip() \ context = (
.replace('\n\n', '\n') \ data["context"]
.replace('Q:', 'Question:') \ .strip()
.replace('A:', 'Answer:') .replace("\n\n", "\n")
completion = data['completion'] .replace("Q:", "Question:")
yield key, {'context': context, 'completion': completion} .replace("A:", "Answer:")
)
completion = data["completion"]
yield key, {"context": context, "completion": completion}
...@@ -50,13 +50,16 @@ _URLS = "https://github.com/chaochun/nlu-asdiv-dataset/archive/55790e5270bb91ccf ...@@ -50,13 +50,16 @@ _URLS = "https://github.com/chaochun/nlu-asdiv-dataset/archive/55790e5270bb91ccf
class ASDiv(datasets.GeneratorBasedBuilder): class ASDiv(datasets.GeneratorBasedBuilder):
""" ASDiv: A Diverse Corpus for Evaluating and Developing English Math Word Problem Solvers """ """ASDiv: A Diverse Corpus for Evaluating and Developing English Math Word Problem Solvers"""
VERSION = datasets.Version("0.0.1") VERSION = datasets.Version("0.0.1")
BUILDER_CONFIGS = [ BUILDER_CONFIGS = [
datasets.BuilderConfig(name="asdiv", version=VERSION, datasets.BuilderConfig(
description="A diverse corpus for evaluating and developing english math word problem solvers") name="asdiv",
version=VERSION,
description="A diverse corpus for evaluating and developing english math word problem solvers",
)
] ]
def _info(self): def _info(self):
...@@ -86,7 +89,9 @@ class ASDiv(datasets.GeneratorBasedBuilder): ...@@ -86,7 +89,9 @@ class ASDiv(datasets.GeneratorBasedBuilder):
name=datasets.Split.VALIDATION, name=datasets.Split.VALIDATION,
# These kwargs will be passed to _generate_examples # These kwargs will be passed to _generate_examples
gen_kwargs={ gen_kwargs={
"filepath": os.path.join(data_dir, base_filepath, "dataset", "ASDiv.xml"), "filepath": os.path.join(
data_dir, base_filepath, "dataset", "ASDiv.xml"
),
"split": datasets.Split.VALIDATION, "split": datasets.Split.VALIDATION,
}, },
), ),
......
...@@ -61,7 +61,7 @@ _EMPTY_ADDITIONAL_ANSWER = { ...@@ -61,7 +61,7 @@ _EMPTY_ADDITIONAL_ANSWER = {
"span_end": -1, "span_end": -1,
"span_text": "", "span_text": "",
"input_text": "", "input_text": "",
"turn_id": -1 "turn_id": -1,
} }
], ],
"1": [ "1": [
...@@ -70,7 +70,7 @@ _EMPTY_ADDITIONAL_ANSWER = { ...@@ -70,7 +70,7 @@ _EMPTY_ADDITIONAL_ANSWER = {
"span_end": -1, "span_end": -1,
"span_text": "", "span_text": "",
"input_text": "", "input_text": "",
"turn_id": -1 "turn_id": -1,
} }
], ],
"2": [ "2": [
...@@ -79,7 +79,7 @@ _EMPTY_ADDITIONAL_ANSWER = { ...@@ -79,7 +79,7 @@ _EMPTY_ADDITIONAL_ANSWER = {
"span_end": -1, "span_end": -1,
"span_text": "", "span_text": "",
"input_text": "", "input_text": "",
"turn_id": -1 "turn_id": -1,
} }
], ],
} }
...@@ -91,8 +91,9 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -91,8 +91,9 @@ class Coqa(datasets.GeneratorBasedBuilder):
VERSION = datasets.Version("0.0.1") VERSION = datasets.Version("0.0.1")
BUILDER_CONFIGS = [ BUILDER_CONFIGS = [
datasets.BuilderConfig(name="coqa", version=VERSION, datasets.BuilderConfig(
description="The CoQA dataset."), name="coqa", version=VERSION, description="The CoQA dataset."
),
] ]
def _info(self): def _info(self):
...@@ -101,41 +102,52 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -101,41 +102,52 @@ class Coqa(datasets.GeneratorBasedBuilder):
"id": datasets.Value("string"), "id": datasets.Value("string"),
"source": datasets.Value("string"), "source": datasets.Value("string"),
"story": datasets.Value("string"), "story": datasets.Value("string"),
"questions": datasets.features.Sequence({ "questions": datasets.features.Sequence(
{
"input_text": datasets.Value("string"), "input_text": datasets.Value("string"),
"turn_id": datasets.Value("int32"), "turn_id": datasets.Value("int32"),
}), }
"answers": datasets.features.Sequence({ ),
"answers": datasets.features.Sequence(
{
"span_start": datasets.Value("int32"), "span_start": datasets.Value("int32"),
"span_end": datasets.Value("int32"), "span_end": datasets.Value("int32"),
"span_text": datasets.Value("string"), "span_text": datasets.Value("string"),
"input_text": datasets.Value("string"), "input_text": datasets.Value("string"),
"turn_id": datasets.Value("int32"), "turn_id": datasets.Value("int32"),
}), }
),
"additional_answers": { "additional_answers": {
"0": datasets.features.Sequence({ "0": datasets.features.Sequence(
{
"span_start": datasets.Value("int32"), "span_start": datasets.Value("int32"),
"span_end": datasets.Value("int32"), "span_end": datasets.Value("int32"),
"span_text": datasets.Value("string"), "span_text": datasets.Value("string"),
"input_text": datasets.Value("string"), "input_text": datasets.Value("string"),
"turn_id": datasets.Value("int32"), "turn_id": datasets.Value("int32"),
}), }
"1": datasets.features.Sequence({ ),
"1": datasets.features.Sequence(
{
"span_start": datasets.Value("int32"), "span_start": datasets.Value("int32"),
"span_end": datasets.Value("int32"), "span_end": datasets.Value("int32"),
"span_text": datasets.Value("string"), "span_text": datasets.Value("string"),
"input_text": datasets.Value("string"), "input_text": datasets.Value("string"),
"turn_id": datasets.Value("int32"), "turn_id": datasets.Value("int32"),
}), }
"2": datasets.features.Sequence({ ),
"2": datasets.features.Sequence(
{
"span_start": datasets.Value("int32"), "span_start": datasets.Value("int32"),
"span_end": datasets.Value("int32"), "span_end": datasets.Value("int32"),
"span_text": datasets.Value("string"), "span_text": datasets.Value("string"),
"input_text": datasets.Value("string"), "input_text": datasets.Value("string"),
"turn_id": datasets.Value("int32"), "turn_id": datasets.Value("int32"),
}),
} }
}) ),
},
}
)
return datasets.DatasetInfo( return datasets.DatasetInfo(
description=_DESCRIPTION, description=_DESCRIPTION,
features=features, features=features,
...@@ -175,10 +187,7 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -175,10 +187,7 @@ class Coqa(datasets.GeneratorBasedBuilder):
source = row["source"] source = row["source"]
story = row["story"] story = row["story"]
questions = [ questions = [
{ {"input_text": q["input_text"], "turn_id": q["turn_id"]}
"input_text": q["input_text"],
"turn_id": q["turn_id"]
}
for q in row["questions"] for q in row["questions"]
] ]
answers = [ answers = [
...@@ -187,7 +196,7 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -187,7 +196,7 @@ class Coqa(datasets.GeneratorBasedBuilder):
"span_end": a["span_end"], "span_end": a["span_end"],
"span_text": a["span_text"], "span_text": a["span_text"],
"input_text": a["input_text"], "input_text": a["input_text"],
"turn_id": a["turn_id"] "turn_id": a["turn_id"],
} }
for a in row["answers"] for a in row["answers"]
] ]
...@@ -201,7 +210,7 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -201,7 +210,7 @@ class Coqa(datasets.GeneratorBasedBuilder):
"span_end": a0["span_end"], "span_end": a0["span_end"],
"span_text": a0["span_text"], "span_text": a0["span_text"],
"input_text": a0["input_text"], "input_text": a0["input_text"],
"turn_id": a0["turn_id"] "turn_id": a0["turn_id"],
} }
for a0 in row["additional_answers"]["0"] for a0 in row["additional_answers"]["0"]
], ],
...@@ -211,7 +220,7 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -211,7 +220,7 @@ class Coqa(datasets.GeneratorBasedBuilder):
"span_end": a1["span_end"], "span_end": a1["span_end"],
"span_text": a1["span_text"], "span_text": a1["span_text"],
"input_text": a1["input_text"], "input_text": a1["input_text"],
"turn_id": a1["turn_id"] "turn_id": a1["turn_id"],
} }
for a1 in row["additional_answers"]["1"] for a1 in row["additional_answers"]["1"]
], ],
...@@ -221,7 +230,7 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -221,7 +230,7 @@ class Coqa(datasets.GeneratorBasedBuilder):
"span_end": a2["span_end"], "span_end": a2["span_end"],
"span_text": a2["span_text"], "span_text": a2["span_text"],
"input_text": a2["input_text"], "input_text": a2["input_text"],
"turn_id": a2["turn_id"] "turn_id": a2["turn_id"],
} }
for a2 in row["additional_answers"]["2"] for a2 in row["additional_answers"]["2"]
], ],
...@@ -232,5 +241,5 @@ class Coqa(datasets.GeneratorBasedBuilder): ...@@ -232,5 +241,5 @@ class Coqa(datasets.GeneratorBasedBuilder):
"source": source, "source": source,
"questions": questions, "questions": questions,
"answers": answers, "answers": answers,
"additional_answers": additional_answers "additional_answers": additional_answers,
} }
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# Custom DROP dataet that, unlike HF, keeps all question-answer pairs # Custom DROP dataset that, unlike HF, keeps all question-answer pairs
# even if there are multiple types of answers for the same question. # even if there are multiple types of answers for the same question.
"""DROP dataset.""" """DROP dataset."""
...@@ -50,7 +50,8 @@ _URLS = { ...@@ -50,7 +50,8 @@ _URLS = {
"drop": "https://s3-us-west-2.amazonaws.com/allennlp/datasets/drop/drop_dataset.zip", "drop": "https://s3-us-west-2.amazonaws.com/allennlp/datasets/drop/drop_dataset.zip",
} }
_EMPTY_VALIDATED_ANSWER = [{ _EMPTY_VALIDATED_ANSWER = [
{
"number": "", "number": "",
"date": { "date": {
"day": "", "day": "",
...@@ -59,8 +60,9 @@ _EMPTY_VALIDATED_ANSWER = [{ ...@@ -59,8 +60,9 @@ _EMPTY_VALIDATED_ANSWER = [{
}, },
"spans": [], "spans": [],
"worker_id": "", "worker_id": "",
"hit_id": "" "hit_id": "",
}] }
]
class Drop(datasets.GeneratorBasedBuilder): class Drop(datasets.GeneratorBasedBuilder):
...@@ -69,12 +71,14 @@ class Drop(datasets.GeneratorBasedBuilder): ...@@ -69,12 +71,14 @@ class Drop(datasets.GeneratorBasedBuilder):
VERSION = datasets.Version("0.0.1") VERSION = datasets.Version("0.0.1")
BUILDER_CONFIGS = [ BUILDER_CONFIGS = [
datasets.BuilderConfig(name="drop", version=VERSION, datasets.BuilderConfig(
description="The DROP dataset."), name="drop", version=VERSION, description="The DROP dataset."
),
] ]
def _info(self): def _info(self):
features = datasets.Features({ features = datasets.Features(
{
"section_id": datasets.Value("string"), "section_id": datasets.Value("string"),
"passage": datasets.Value("string"), "passage": datasets.Value("string"),
"question": datasets.Value("string"), "question": datasets.Value("string"),
...@@ -90,7 +94,8 @@ class Drop(datasets.GeneratorBasedBuilder): ...@@ -90,7 +94,8 @@ class Drop(datasets.GeneratorBasedBuilder):
"worker_id": datasets.Value("string"), "worker_id": datasets.Value("string"),
"hit_id": datasets.Value("string"), "hit_id": datasets.Value("string"),
}, },
"validated_answers": datasets.features.Sequence({ "validated_answers": datasets.features.Sequence(
{
"number": datasets.Value("string"), "number": datasets.Value("string"),
"date": { "date": {
"day": datasets.Value("string"), "day": datasets.Value("string"),
...@@ -100,8 +105,10 @@ class Drop(datasets.GeneratorBasedBuilder): ...@@ -100,8 +105,10 @@ class Drop(datasets.GeneratorBasedBuilder):
"spans": datasets.features.Sequence(datasets.Value("string")), "spans": datasets.features.Sequence(datasets.Value("string")),
"worker_id": datasets.Value("string"), "worker_id": datasets.Value("string"),
"hit_id": datasets.Value("string"), "hit_id": datasets.Value("string"),
}), }
}) ),
}
)
return datasets.DatasetInfo( return datasets.DatasetInfo(
description=_DESCRIPTION, description=_DESCRIPTION,
features=features, features=features,
...@@ -118,7 +125,9 @@ class Drop(datasets.GeneratorBasedBuilder): ...@@ -118,7 +125,9 @@ class Drop(datasets.GeneratorBasedBuilder):
name=datasets.Split.TRAIN, name=datasets.Split.TRAIN,
# These kwargs will be passed to _generate_examples # These kwargs will be passed to _generate_examples
gen_kwargs={ gen_kwargs={
"filepath": os.path.join(data_dir, "drop_dataset", "drop_dataset_train.json"), "filepath": os.path.join(
data_dir, "drop_dataset", "drop_dataset_train.json"
),
"split": "train", "split": "train",
}, },
), ),
...@@ -126,7 +135,9 @@ class Drop(datasets.GeneratorBasedBuilder): ...@@ -126,7 +135,9 @@ class Drop(datasets.GeneratorBasedBuilder):
name=datasets.Split.VALIDATION, name=datasets.Split.VALIDATION,
# These kwargs will be passed to _generate_examples # These kwargs will be passed to _generate_examples
gen_kwargs={ gen_kwargs={
"filepath": os.path.join(data_dir, "drop_dataset", "drop_dataset_dev.json"), "filepath": os.path.join(
data_dir, "drop_dataset", "drop_dataset_dev.json"
),
"split": "validation", "split": "validation",
}, },
), ),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment