Spaces:
Running
Running
Update dialogues.py
Browse files- dialogues.py +31 -93
dialogues.py
CHANGED
|
@@ -1,18 +1,4 @@
|
|
| 1 |
# coding=utf-8
|
| 2 |
-
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
| 3 |
-
#
|
| 4 |
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
-
# you may not use this file except in compliance with the License.
|
| 6 |
-
# You may obtain a copy of the License at
|
| 7 |
-
#
|
| 8 |
-
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
-
#
|
| 10 |
-
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
-
# See the License for the specific language governing permissions and
|
| 14 |
-
# limitations under the License.
|
| 15 |
-
|
| 16 |
import json
|
| 17 |
import os
|
| 18 |
from dataclasses import asdict, dataclass
|
|
@@ -30,10 +16,7 @@ IGNORE_INDEX = -100
|
|
| 30 |
|
| 31 |
@dataclass
|
| 32 |
class DialogueTemplate(ModelHubMixin):
|
| 33 |
-
"""Converts all turns of a dialogue between a user and assistant to a standardized format.
|
| 34 |
-
|
| 35 |
-
Adapted from OpenAI's ChatML (https://github.com/openai/openai-python/blob/main/chatml.md) and Vicuna (https://github.com/lm-sys/FastChat/blob/main/fastchat/conversation.py)
|
| 36 |
-
"""
|
| 37 |
|
| 38 |
system: str
|
| 39 |
messages: List[Dict[str, str]] = None
|
|
@@ -42,10 +25,15 @@ class DialogueTemplate(ModelHubMixin):
|
|
| 42 |
assistant_token: str = "<|assistant|>"
|
| 43 |
end_token: str = "<|end|>"
|
| 44 |
|
| 45 |
-
def
|
| 46 |
-
|
| 47 |
if self.messages is None:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
raise ValueError("Dialogue template must have at least one message.")
|
|
|
|
| 49 |
for message in self.messages:
|
| 50 |
if message["role"] == "user":
|
| 51 |
prompt += self.user_token + "\n" + message["content"] + self.end_token + "\n"
|
|
@@ -54,9 +42,9 @@ class DialogueTemplate(ModelHubMixin):
|
|
| 54 |
return prompt
|
| 55 |
|
| 56 |
def get_inference_prompt(self) -> str:
|
| 57 |
-
|
| 58 |
-
if self.messages is None:
|
| 59 |
raise ValueError("Dialogue template must have at least one message.")
|
|
|
|
| 60 |
for message in self.messages:
|
| 61 |
if message["role"] == "user":
|
| 62 |
prompt += self.user_token + "\n" + message["content"] + self.end_token + "\n"
|
|
@@ -66,10 +54,9 @@ class DialogueTemplate(ModelHubMixin):
|
|
| 66 |
return prompt
|
| 67 |
|
| 68 |
def get_dialogue(self):
|
| 69 |
-
|
| 70 |
-
prompt = ""
|
| 71 |
-
if self.messages is None:
|
| 72 |
raise ValueError("Dialogue template must have at least one message.")
|
|
|
|
| 73 |
for message in self.messages:
|
| 74 |
if message["role"] == "user":
|
| 75 |
prompt += "\n\nHuman: " + message["content"]
|
|
@@ -96,12 +83,12 @@ class DialogueTemplate(ModelHubMixin):
|
|
| 96 |
@classmethod
|
| 97 |
def from_dict(cls, data):
|
| 98 |
return DialogueTemplate(
|
| 99 |
-
system=data
|
| 100 |
-
messages=data
|
| 101 |
-
system_token=data
|
| 102 |
-
user_token=data
|
| 103 |
-
assistant_token=data
|
| 104 |
-
end_token=data
|
| 105 |
)
|
| 106 |
|
| 107 |
def _save_pretrained(self, save_directory: Union[str, Path]) -> None:
|
|
@@ -124,40 +111,15 @@ class DialogueTemplate(ModelHubMixin):
|
|
| 124 |
token: Optional[Union[str, bool]],
|
| 125 |
**model_kwargs,
|
| 126 |
) -> T:
|
| 127 |
-
"""Loads the dialogue template from a local directory or the Huggingface Hub.
|
| 128 |
-
|
| 129 |
-
Args:
|
| 130 |
-
model_id (`str`):
|
| 131 |
-
ID of the model to load from the Huggingface Hub (e.g. `bigscience/bloom`).
|
| 132 |
-
revision (`str`, *optional*):
|
| 133 |
-
Revision of the model on the Hub. Can be a branch name, a git tag or any commit id. Defaults to the
|
| 134 |
-
latest commit on `main` branch.
|
| 135 |
-
force_download (`bool`, *optional*, defaults to `False`):
|
| 136 |
-
Whether to force (re-)downloading the model weights and configuration files from the Hub, overriding
|
| 137 |
-
the existing cache.
|
| 138 |
-
resume_download (`bool`, *optional*, defaults to `False`):
|
| 139 |
-
Whether to delete incompletely received files. Will attempt to resume the download if such a file exists.
|
| 140 |
-
proxies (`Dict[str, str]`, *optional*):
|
| 141 |
-
A dictionary of proxy servers to use by protocol or endpoint (e.g., `{'http': 'foo.bar:3128',
|
| 142 |
-
'http://hostname': 'foo.bar:4012'}`).
|
| 143 |
-
token (`str` or `bool`, *optional*):
|
| 144 |
-
The token to use as HTTP bearer authorization for remote files. By default, it will use the token
|
| 145 |
-
cached when running `huggingface-cli login`.
|
| 146 |
-
cache_dir (`str`, `Path`, *optional*):
|
| 147 |
-
Path to the folder where cached files are stored.
|
| 148 |
-
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 149 |
-
If `True`, avoid downloading the file and return the path to the local cached file if it exists.
|
| 150 |
-
model_kwargs:
|
| 151 |
-
Additional keyword arguments passed along to the [`~ModelHubMixin._from_pretrained`] method.
|
| 152 |
-
"""
|
| 153 |
-
if os.path.isdir(model_id): # Can either be a local directory
|
| 154 |
print("Loading dialogue template from local directory")
|
| 155 |
template_file = os.path.join(model_id, TEMPLATE_FILENAME)
|
| 156 |
-
else:
|
| 157 |
-
template_file = hf_hub_download(
|
| 158 |
repo_id=model_id,
|
| 159 |
filename=TEMPLATE_FILENAME,
|
| 160 |
-
revision=revision,
|
| 161 |
cache_dir=cache_dir,
|
| 162 |
force_download=force_download,
|
| 163 |
proxies=proxies,
|
|
@@ -165,24 +127,18 @@ class DialogueTemplate(ModelHubMixin):
|
|
| 165 |
token=token,
|
| 166 |
local_files_only=local_files_only,
|
| 167 |
)
|
| 168 |
-
|
| 169 |
-
# Load template
|
| 170 |
with open(template_file, "r") as f:
|
| 171 |
data = json.load(f)
|
| 172 |
return cls.from_dict(data=data)
|
| 173 |
|
| 174 |
|
| 175 |
-
#
|
| 176 |
default_template = DialogueTemplate(
|
| 177 |
system="Below is a dialogue between a human user and an AI assistant. The assistant is happy to help with almost anything, and will do its best to understand exactly what is needed.",
|
| 178 |
)
|
| 179 |
|
| 180 |
-
#
|
| 181 |
-
|
| 182 |
-
no_system_template = DialogueTemplate(
|
| 183 |
-
system="",
|
| 184 |
-
)
|
| 185 |
-
|
| 186 |
alpaca_template = DialogueTemplate(
|
| 187 |
system="Below is an instruction that describes a task. Write a response that appropriately completes the request.",
|
| 188 |
user_token="### Instruction:",
|
|
@@ -197,27 +153,21 @@ SUPPORTED_DIALOGUE_TEMPLATES = {
|
|
| 197 |
|
| 198 |
|
| 199 |
def get_dialogue_template(template: str) -> DialogueTemplate:
|
| 200 |
-
if template not in SUPPORTED_DIALOGUE_TEMPLATES
|
| 201 |
raise ValueError(f"Template {template} is not supported!")
|
| 202 |
return SUPPORTED_DIALOGUE_TEMPLATES[template].copy()
|
| 203 |
|
| 204 |
|
| 205 |
def prepare_dialogue(example, dialogue_template, is_train=True):
|
| 206 |
-
""
|
| 207 |
-
# TODO: make this simpler by just ensuring every dataset has a messages column
|
| 208 |
-
if "messages" in example.keys() and example["messages"] is not None:
|
| 209 |
dialogue_template.messages = example["messages"]
|
| 210 |
-
elif
|
| 211 |
-
# Construct single-turn dialogue from prompt and completion
|
| 212 |
dialogue_template.messages = [
|
| 213 |
{"role": "user", "content": example["prompt"]},
|
| 214 |
{"role": "assistant", "content": example["completion"]},
|
| 215 |
]
|
| 216 |
-
elif "prompt" in example
|
| 217 |
-
|
| 218 |
-
dialogue_template.messages = [
|
| 219 |
-
{"role": "user", "content": example["prompt"]},
|
| 220 |
-
]
|
| 221 |
else:
|
| 222 |
raise ValueError(
|
| 223 |
f"Could not format example as dialogue! Require either `messages` or `[prompt, completion]` or `[prompt]` keys but found {list(example.keys())}"
|
|
@@ -227,15 +177,3 @@ def prepare_dialogue(example, dialogue_template, is_train=True):
|
|
| 227 |
else:
|
| 228 |
example["text"] = dialogue_template.get_inference_prompt()
|
| 229 |
return example
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
def mask_user_labels(tokenizer, dialogue_template, labels):
|
| 233 |
-
"""Masks the user turns of a dialogue from the loss"""
|
| 234 |
-
user_token_id = tokenizer.convert_tokens_to_ids(dialogue_template.user_token)
|
| 235 |
-
assistant_token_id = tokenizer.convert_tokens_to_ids(dialogue_template.assistant_token)
|
| 236 |
-
for idx, label_id in enumerate(labels):
|
| 237 |
-
if label_id == user_token_id:
|
| 238 |
-
current_idx = idx
|
| 239 |
-
while labels[current_idx] != assistant_token_id and current_idx < len(labels):
|
| 240 |
-
labels[current_idx] = IGNORE_INDEX
|
| 241 |
-
current_idx += 1
|
|
|
|
| 1 |
# coding=utf-8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import json
|
| 3 |
import os
|
| 4 |
from dataclasses import asdict, dataclass
|
|
|
|
| 16 |
|
| 17 |
@dataclass
|
| 18 |
class DialogueTemplate(ModelHubMixin):
|
| 19 |
+
"""Converts all turns of a dialogue between a user and assistant to a standardized format."""
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
system: str
|
| 22 |
messages: List[Dict[str, str]] = None
|
|
|
|
| 25 |
assistant_token: str = "<|assistant|>"
|
| 26 |
end_token: str = "<|end|>"
|
| 27 |
|
| 28 |
+
def __post_init__(self):
|
| 29 |
+
"""Ensure that messages is never None."""
|
| 30 |
if self.messages is None:
|
| 31 |
+
self.messages = []
|
| 32 |
+
|
| 33 |
+
def get_training_prompt(self) -> str:
|
| 34 |
+
if len(self.messages) == 0:
|
| 35 |
raise ValueError("Dialogue template must have at least one message.")
|
| 36 |
+
prompt = self.system_token + "\n" + self.system + self.end_token + "\n"
|
| 37 |
for message in self.messages:
|
| 38 |
if message["role"] == "user":
|
| 39 |
prompt += self.user_token + "\n" + message["content"] + self.end_token + "\n"
|
|
|
|
| 42 |
return prompt
|
| 43 |
|
| 44 |
def get_inference_prompt(self) -> str:
|
| 45 |
+
if len(self.messages) == 0:
|
|
|
|
| 46 |
raise ValueError("Dialogue template must have at least one message.")
|
| 47 |
+
prompt = self.system_token + "\n" + self.system + self.end_token + "\n"
|
| 48 |
for message in self.messages:
|
| 49 |
if message["role"] == "user":
|
| 50 |
prompt += self.user_token + "\n" + message["content"] + self.end_token + "\n"
|
|
|
|
| 54 |
return prompt
|
| 55 |
|
| 56 |
def get_dialogue(self):
|
| 57 |
+
if len(self.messages) == 0:
|
|
|
|
|
|
|
| 58 |
raise ValueError("Dialogue template must have at least one message.")
|
| 59 |
+
prompt = ""
|
| 60 |
for message in self.messages:
|
| 61 |
if message["role"] == "user":
|
| 62 |
prompt += "\n\nHuman: " + message["content"]
|
|
|
|
| 83 |
@classmethod
|
| 84 |
def from_dict(cls, data):
|
| 85 |
return DialogueTemplate(
|
| 86 |
+
system=data.get("system", ""),
|
| 87 |
+
messages=data.get("messages", None),
|
| 88 |
+
system_token=data.get("system_token", "<|system|>"),
|
| 89 |
+
user_token=data.get("user_token", "<|user|>"),
|
| 90 |
+
assistant_token=data.get("assistant_token", "<|assistant|>"),
|
| 91 |
+
end_token=data.get("end_token", "<|end|>"),
|
| 92 |
)
|
| 93 |
|
| 94 |
def _save_pretrained(self, save_directory: Union[str, Path]) -> None:
|
|
|
|
| 111 |
token: Optional[Union[str, bool]],
|
| 112 |
**model_kwargs,
|
| 113 |
) -> T:
|
| 114 |
+
"""Loads the dialogue template from a local directory or the Huggingface Hub."""
|
| 115 |
+
if os.path.isdir(model_id):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
print("Loading dialogue template from local directory")
|
| 117 |
template_file = os.path.join(model_id, TEMPLATE_FILENAME)
|
| 118 |
+
else:
|
| 119 |
+
template_file = hf_hub_download(
|
| 120 |
repo_id=model_id,
|
| 121 |
filename=TEMPLATE_FILENAME,
|
| 122 |
+
revision=revision or "main",
|
| 123 |
cache_dir=cache_dir,
|
| 124 |
force_download=force_download,
|
| 125 |
proxies=proxies,
|
|
|
|
| 127 |
token=token,
|
| 128 |
local_files_only=local_files_only,
|
| 129 |
)
|
|
|
|
|
|
|
| 130 |
with open(template_file, "r") as f:
|
| 131 |
data = json.load(f)
|
| 132 |
return cls.from_dict(data=data)
|
| 133 |
|
| 134 |
|
| 135 |
+
# Default template
|
| 136 |
default_template = DialogueTemplate(
|
| 137 |
system="Below is a dialogue between a human user and an AI assistant. The assistant is happy to help with almost anything, and will do its best to understand exactly what is needed.",
|
| 138 |
)
|
| 139 |
|
| 140 |
+
# Supporting other templates
|
| 141 |
+
no_system_template = DialogueTemplate(system="")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
alpaca_template = DialogueTemplate(
|
| 143 |
system="Below is an instruction that describes a task. Write a response that appropriately completes the request.",
|
| 144 |
user_token="### Instruction:",
|
|
|
|
| 153 |
|
| 154 |
|
| 155 |
def get_dialogue_template(template: str) -> DialogueTemplate:
|
| 156 |
+
if template not in SUPPORTED_DIALOGUE_TEMPLATES:
|
| 157 |
raise ValueError(f"Template {template} is not supported!")
|
| 158 |
return SUPPORTED_DIALOGUE_TEMPLATES[template].copy()
|
| 159 |
|
| 160 |
|
| 161 |
def prepare_dialogue(example, dialogue_template, is_train=True):
|
| 162 |
+
if "messages" in example and example["messages"] is not None:
|
|
|
|
|
|
|
| 163 |
dialogue_template.messages = example["messages"]
|
| 164 |
+
elif "prompt" in example and "completion" in example:
|
|
|
|
| 165 |
dialogue_template.messages = [
|
| 166 |
{"role": "user", "content": example["prompt"]},
|
| 167 |
{"role": "assistant", "content": example["completion"]},
|
| 168 |
]
|
| 169 |
+
elif "prompt" in example:
|
| 170 |
+
dialogue_template.messages = [{"role": "user", "content": example["prompt"]}]
|
|
|
|
|
|
|
|
|
|
| 171 |
else:
|
| 172 |
raise ValueError(
|
| 173 |
f"Could not format example as dialogue! Require either `messages` or `[prompt, completion]` or `[prompt]` keys but found {list(example.keys())}"
|
|
|
|
| 177 |
else:
|
| 178 |
example["text"] = dialogue_template.get_inference_prompt()
|
| 179 |
return example
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|