-
Notifications
You must be signed in to change notification settings - Fork 64
Expand file tree
/
Copy pathdata.py
More file actions
132 lines (104 loc) · 4.21 KB
/
Copy pathdata.py
File metadata and controls
132 lines (104 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
from dlhub.data.splits import train_val_split_indices
from tracks.nlp.toy_text import Vocab, simple_tokenize
@dataclass(frozen=True)
class DataConfig:
num_samples: int = 320
batch_size: int = 16
max_length: int = 28
val_fraction: float = 0.2
seed: int = 0
num_workers: int = 0
CALLBACK_LEVELS = ("no_callback", "callback")
CALLBACK_TO_ID = {name: idx for idx, name in enumerate(CALLBACK_LEVELS)}
ISSUES = ("billing", "refund", "delivery", "reservation")
CHANNELS = ("dialog", "chat", "call")
MOODS = ("calm", "frustrated", "urgent")
FOLLOWUP_WINDOWS = ("today", "tomorrow", "nextday")
OUTCOMES = ("resolved", "pending", "waiting")
def _build_vocab(texts: list[str]) -> Vocab:
token_to_id = {"<pad>": 0, "<unk>": 1}
id_to_token = ["<pad>", "<unk>"]
for text in texts:
for token in simple_tokenize(text):
if token in token_to_id:
continue
token_to_id[token] = len(id_to_token)
id_to_token.append(token)
return Vocab(token_to_id=token_to_id, id_to_token=id_to_token, pad_id=0, unk_id=1)
def _build_example(callback_level: str, rng: np.random.Generator) -> tuple[str, int]:
issue = str(rng.choice(ISSUES))
channel = str(rng.choice(CHANNELS))
mood = str(rng.choice(MOODS))
followup_window = str(rng.choice(FOLLOWUP_WINDOWS))
outcome = str(rng.choice(OUTCOMES))
if callback_level == "no_callback":
text = (
f"customer {channel} issue {issue} mood calm outcome resolved "
f"callback no followup none status {outcome}"
)
else:
text = (
f"customer {channel} issue {issue} mood {mood} outcome pending "
f"callback required followup {followup_window} specialist callback"
)
return text, int(CALLBACK_TO_ID[callback_level])
def _make_examples(config: DataConfig) -> list[tuple[str, int]]:
rng = np.random.default_rng(int(config.seed))
examples: list[tuple[str, int]] = []
for _ in range(int(config.num_samples)):
callback_level = str(rng.choice(CALLBACK_LEVELS))
examples.append(_build_example(callback_level, rng))
rng.shuffle(examples)
return examples
class DialogCallbackDataset:
def __init__(self, *, examples: list[tuple[str, int]], vocab: Vocab, max_length: int) -> None:
self.examples = list(examples)
self.vocab = vocab
self.max_length = int(max_length)
def __len__(self) -> int:
return len(self.examples)
def __getitem__(self, idx: int):
import torch
text, label = self.examples[int(idx)]
ids, attn = self.vocab.encode(text, max_length=self.max_length)
return {
"input_ids": torch.tensor(ids, dtype=torch.long),
"attention_mask": torch.tensor(attn, dtype=torch.float32),
"labels": torch.tensor(label, dtype=torch.long),
}
def get_dataloaders(config: DataConfig):
import torch
from torch.utils.data import DataLoader, Subset
examples = _make_examples(config)
vocab = _build_vocab([text for text, _ in examples])
dataset = DialogCallbackDataset(examples=examples, vocab=vocab, max_length=int(config.max_length))
train_idx, val_idx = train_val_split_indices(
n=len(dataset),
val_fraction=float(config.val_fraction),
seed=int(config.seed),
)
def _collate(batch):
return {
"input_ids": torch.stack([item["input_ids"] for item in batch], dim=0),
"attention_mask": torch.stack([item["attention_mask"] for item in batch], dim=0),
"labels": torch.stack([item["labels"] for item in batch], dim=0),
}
train_loader = DataLoader(
Subset(dataset, train_idx),
batch_size=int(config.batch_size),
shuffle=True,
num_workers=int(config.num_workers),
collate_fn=_collate,
)
val_loader = DataLoader(
Subset(dataset, val_idx),
batch_size=int(config.batch_size),
shuffle=False,
num_workers=int(config.num_workers),
collate_fn=_collate,
)
return train_loader, val_loader, vocab
__all__ = ["DataConfig", "DialogCallbackDataset", "get_dataloaders"]