Text Classification
Transformers
Safetensors
English
roberta
data-classification
sensitivity
data-governance
Synthetic
text-embeddings-inference
Instructions to use aitrainingdatabroker/document-sensitivity-tier-classifier with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use aitrainingdatabroker/document-sensitivity-tier-classifier with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="aitrainingdatabroker/document-sensitivity-tier-classifier")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("aitrainingdatabroker/document-sensitivity-tier-classifier") model = AutoModelForSequenceClassification.from_pretrained("aitrainingdatabroker/document-sensitivity-tier-classifier", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Add source/gen_synth.py
Browse files- source/gen_synth.py +379 -0
source/gen_synth.py
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Datasets 5 to 10 and the training files of models 4 to 10: invented text, no real person, company or secret (2026-10-30).
|
| 2 |
+
|
| 3 |
+
python hf-work/gen_synth.py
|
| 4 |
+
Each dataset is filled from templates. The TEST templates are never used for training, so a score measures new sentence
|
| 5 |
+
shapes and not only new names. Names, companies and numbers are invented; phone numbers use the fictional 555-01xx range,
|
| 6 |
+
emails use example domains, and keys carry a "demo" prefix.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import json
|
| 10 |
+
import random
|
| 11 |
+
import string
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
|
| 14 |
+
ROOT = Path(__file__).resolve().parent.parent
|
| 15 |
+
rng = random.Random(20261030)
|
| 16 |
+
|
| 17 |
+
FIRST = ["Marisol", "Dennick", "Aurelio", "Tamsin", "Liesel", "Bram", "Yusra", "Corwin", "Ilse", "Nadim", "Petra", "Ravindra",
|
| 18 |
+
"Sunniva", "Teodor", "Wren", "Zaida", "Hollis", "Imogen", "Kasimir", "Delphine", "Ezio", "Odalys", "Fenwick", "Galen"]
|
| 19 |
+
LAST = ["Quarrington", "Abernethy", "Okafor", "Lindqvist", "Marchetti", "Pemberton", "Szabo", "Thistlewood", "Hakimi",
|
| 20 |
+
"Brannigan", "Castellanos", "Duchesne", "Fairweather", "Gruber", "Ishikawa", "Valdespino", "Nwosu", "Ostrander"]
|
| 21 |
+
ORGS = ["Larkspur Logistics", "Copperfield Supply", "Harborview Dental", "Northgate Analytics", "Juniper Freight", "Ridgeway Builders",
|
| 22 |
+
"Millbrook Software", "Ashford Insurance Group", "Kingsport Marine", "Willow Creek Foods", "Eastlake Clinics",
|
| 23 |
+
"Brightwater Staffing", "Granite Peak Capital", "Fernhill Media", "Stonebridge Legal", "Tidewater Robotics"]
|
| 24 |
+
STREETS = ["Larkspur Lane", "Copperfield Road", "Harbor View Drive", "Old Mill Court", "Juniper Street", "Ridgeway Avenue",
|
| 25 |
+
"Elmhurst Boulevard", "Quarry Road", "Saltmarsh Way", "Pine Terrace"]
|
| 26 |
+
CITIES = ["Brookhaven", "Millbrook", "Fairhaven", "Ashford", "Kingsport", "Willow Creek", "Eastlake", "Stonebridge", "Tidewater"]
|
| 27 |
+
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
| 28 |
+
DOMAINS = ["example.com", "example.org", "mail.example", "corp.example", "sample.test"]
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def person():
|
| 32 |
+
return f"{rng.choice(FIRST)} {rng.choice(LAST)}"
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def org():
|
| 36 |
+
return rng.choice(ORGS)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def email(name=None):
|
| 40 |
+
n = (name or person()).lower().replace(" ", ".")
|
| 41 |
+
return f"{n}@{rng.choice(DOMAINS)}"
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def phone():
|
| 45 |
+
return f"(555) 01{rng.randint(0, 9)}-{rng.randint(1000, 9999)}"
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def address():
|
| 49 |
+
return f"{rng.randint(12, 980)} {rng.choice(STREETS)}, {rng.choice(CITIES)}"
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def date():
|
| 53 |
+
return f"{rng.choice(MONTHS)} {rng.randint(1, 28)}, {rng.randint(2024, 2026)}"
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def money():
|
| 57 |
+
return f"${rng.randint(120, 48000):,}.{rng.randint(0, 99):02d}"
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def code(prefix, n=6):
|
| 61 |
+
return prefix + "".join(rng.choice(string.digits) for _ in range(n))
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def key():
|
| 65 |
+
return "demo_key_" + "".join(rng.choice(string.ascii_lowercase + string.digits) for _ in range(20))
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def ip():
|
| 69 |
+
return f"10.{rng.randint(0, 250)}.{rng.randint(0, 250)}.{rng.randint(2, 250)}"
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def write(kind, name, split, rows):
|
| 73 |
+
out = ROOT / kind / name / "data"
|
| 74 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 75 |
+
with open(out / f"{split}.jsonl", "w", encoding="utf-8", newline="\n") as f:
|
| 76 |
+
for r in rows:
|
| 77 |
+
f.write(json.dumps(r, ensure_ascii=False) + "\n")
|
| 78 |
+
print(f"{kind}/{name}/{split}: {len(rows)} rows")
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
PRE = ["", "", "Note: ", "FYI, ", "Quick one: ", "Following up. ", "Per our earlier message, ", "Hello. ", "To whom it may concern: ", "Sorry for the delay. "]
|
| 82 |
+
SUF = ["", "", " Thanks.", " Please advise.", " Let me know.", " More detail on request.", " Regards.", " Appreciated."]
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def classification(name, label_field, train_t, test_t, fill, per_template, extra=None):
|
| 86 |
+
"""train_t / test_t: {label: [template, ...]}. Returns (train_rows, test_rows) and writes the dataset."""
|
| 87 |
+
out = {}
|
| 88 |
+
for split, templates in (("train", train_t), ("test", test_t)):
|
| 89 |
+
rows, seen = [], set()
|
| 90 |
+
for label, tpls in templates.items():
|
| 91 |
+
for t in tpls:
|
| 92 |
+
for _ in range(per_template):
|
| 93 |
+
text = rng.choice(PRE) + t.format(**fill()) + rng.choice(SUF)
|
| 94 |
+
if text in seen:
|
| 95 |
+
continue
|
| 96 |
+
seen.add(text)
|
| 97 |
+
rows.append({"text": text, label_field: label, **({k: v(label) for k, v in extra.items()} if extra else {})})
|
| 98 |
+
rng.shuffle(rows)
|
| 99 |
+
out[split] = rows
|
| 100 |
+
return out
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def with_ids(prefix, rows):
|
| 104 |
+
return [{"id": f"{prefix}-{i:04d}", **r} for i, r in enumerate(rows, 1)]
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def spans_from(segments):
|
| 108 |
+
text, ents = "", []
|
| 109 |
+
for seg, lab in segments:
|
| 110 |
+
if lab:
|
| 111 |
+
ents.append({"label": lab, "start": len(text), "end": len(text) + len(seg)})
|
| 112 |
+
text += seg
|
| 113 |
+
return {"text": text, "entities": ents}
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
# ======================= dataset 5 and model 9: document sensitivity tier =======================
|
| 117 |
+
THING = ["the Q3 roadmap", "the onboarding guide", "the vendor list", "the pricing sheet", "the incident review", "the hiring plan",
|
| 118 |
+
"the support playbook", "the launch plan", "the audit notes", "the budget draft", "the partner terms", "the training deck"]
|
| 119 |
+
TEAM = ["support", "finance", "engineering", "sales", "operations", "legal", "people team", "marketing"]
|
| 120 |
+
TIER_FILL = lambda: {"thing": rng.choice(THING), "team": rng.choice(TEAM), "org": org(), "n": rng.randint(3, 40), "month": rng.choice(MONTHS)}
|
| 121 |
+
TIER_TRAIN = {
|
| 122 |
+
"public": ["{org} announces {thing} in a press release for all media.", "Blog post for our website readers: how {team} works with customers.",
|
| 123 |
+
"Published job listing for a {team} role, visible on our careers page.", "Public FAQ answer about opening hours and how to contact {team}.",
|
| 124 |
+
"Product brochure PDF for download, approved by marketing for anyone.", "Social media post celebrating {n} years in business.",
|
| 125 |
+
"Open-source release notes for version {n}, posted on our public repository.", "Conference talk slides cleared for the public audience."],
|
| 126 |
+
"internal": ["Team update from {team}: {thing} moves to next sprint, share inside the company only.", "All-hands notes from {month}, for employees, not for customers.",
|
| 127 |
+
"Internal wiki page on how {team} files expense reports.", "Slack thread where {team} compares tools; staff only.",
|
| 128 |
+
"Meeting agenda for the weekly {team} sync and action items.", "Draft of {thing} circulating among managers for comment.",
|
| 129 |
+
"Staff newsletter with office closures and training dates.", "Org chart and desk map for employees."],
|
| 130 |
+
"confidential": ["Customer contract with {org} that names pricing, renewal terms and discounts.", "Revenue forecast for the board, not for release outside leadership.",
|
| 131 |
+
"Unreleased {thing}, under embargo until the announcement.", "Supplier agreement with negotiated rates for {team}.",
|
| 132 |
+
"Investor update with cash position and runway for {n} months.", "Source code of the billing service, access limited to engineers.",
|
| 133 |
+
"Acquisition discussion notes with {org}, known to a handful of executives.", "Salary bands and bonus plan for {team}, shared with HR only."],
|
| 134 |
+
"restricted": ["Patient records with names, diagnoses and insurance numbers, covered by health privacy law.", "Export of customer payment card numbers and billing addresses.",
|
| 135 |
+
"Employee Social Security numbers and bank details held by payroll.", "Production database credentials and private signing keys for {thing}.",
|
| 136 |
+
"Legal hold material under attorney-client privilege for a pending case.", "Unredacted identity documents scanned for {n} customers.",
|
| 137 |
+
"Full customer database with emails, phone numbers and home addresses, un-de-identified.", "Security incident forensics naming affected accounts and exploited weaknesses."],
|
| 138 |
+
}
|
| 139 |
+
TIER_TEST = {
|
| 140 |
+
"public": ["Newsletter issue for subscribers about {thing}, published openly.", "Open house invitation from {org}, posted for the neighbourhood."],
|
| 141 |
+
"internal": ["Reminder to {team} staff about the holiday schedule and who covers the desk.", "Notes from {team}'s retrospective on {thing}, inside the company."],
|
| 142 |
+
"confidential": ["Term sheet from {org}, only for the deal team.", "Customer churn analysis with account names and contract values."],
|
| 143 |
+
"restricted": ["Spreadsheet of staff medical leave reasons and doctors' notes.", "Raw customer list with government ID numbers attached to {n} rows."],
|
| 144 |
+
}
|
| 145 |
+
r = classification("document-sensitivity-tiers", "label", TIER_TRAIN, TIER_TEST, TIER_FILL, 10)
|
| 146 |
+
write("datasets", "synthetic-document-sensitivity-tiers", "train", with_ids("tier", [{"text": x["text"], "tier": x["label"]} for x in r["train"]]))
|
| 147 |
+
write("datasets", "synthetic-document-sensitivity-tiers", "test", with_ids("tier-t", [{"text": x["text"], "tier": x["label"]} for x in r["test"]]))
|
| 148 |
+
write("models", "document-sensitivity-tier-classifier", "train", r["train"])
|
| 149 |
+
write("models", "document-sensitivity-tier-classifier", "test", r["test"])
|
| 150 |
+
|
| 151 |
+
# ======================= dataset 6, models 4 and 10: contract clauses (type and risk) =======================
|
| 152 |
+
CL_FILL = lambda: {"a": rng.choice(["the Seller", "the Company", "Licensor"]), "b": rng.choice(["the Buyer", "the Lab", "Licensee"]),
|
| 153 |
+
"n": rng.choice([30, 45, 60, 90, 180]), "y": rng.choice([1, 2, 3, 5]), "p": rng.choice(["thirty", "forty-five", "sixty", "ninety"])}
|
| 154 |
+
CLAUSES = { # type -> (standard train, watch train, standard test, watch test)
|
| 155 |
+
"exclusivity": (["{b} receives exclusive rights to the Dataset for {n} days, after which {a} may license it to others.",
|
| 156 |
+
"During the exclusivity period of {n} days, {a} will not license the Dataset to any other party; the period ends on a fixed date."],
|
| 157 |
+
["{b} receives exclusive rights to the Dataset for all purposes, with no end date.",
|
| 158 |
+
"{a} may never license the Dataset or anything derived from it to any other party, in any field."],
|
| 159 |
+
["Exclusivity applies for {y} year(s) and only to the named use, and {a} keeps all other rights."],
|
| 160 |
+
["The exclusive grant covers every future version of the Dataset and renews automatically without notice."]),
|
| 161 |
+
"term_and_renewal": (["This Agreement lasts {y} year(s) and renews only if both parties agree in writing.",
|
| 162 |
+
"The term is {y} year(s) from the effective date, and either party may decline renewal with {n} days' notice."],
|
| 163 |
+
["This Agreement renews automatically for successive {y}-year terms and {a} cannot end it.",
|
| 164 |
+
"The term continues until {b} decides to stop, at its sole discretion."],
|
| 165 |
+
["The initial term is {y} year(s); renewal needs a signed amendment."],
|
| 166 |
+
["The license is perpetual and irrevocable and survives any breach by {b}."]),
|
| 167 |
+
"payment": (["{b} pays the fee within {n} days of an invoice, by bank transfer, and late amounts accrue modest interest.",
|
| 168 |
+
"The fee is due in stages: a deposit on signing and the balance on acceptance of the delivered data."],
|
| 169 |
+
["{b} may withhold payment for any reason until it is fully satisfied, with no deadline.",
|
| 170 |
+
"Payment is due only after {b} has earned revenue from a model trained on the Dataset, and {a} cannot see those figures."],
|
| 171 |
+
["Invoices are payable within {n} days and disputes must be raised in writing within ten days."],
|
| 172 |
+
["Fees may be reduced by {b} at any time by notice, and refunds are due on request after delivery."]),
|
| 173 |
+
"confidentiality": (["Each party keeps the other's confidential information private for {y} year(s) after the Agreement ends, with the usual exceptions.",
|
| 174 |
+
"{b} will protect the Dataset with the care it gives its own secrets and limit access to those who need it."],
|
| 175 |
+
["{a} must keep every detail of this Agreement secret forever, even from its own advisers and auditors.",
|
| 176 |
+
"{b} may disclose the Dataset to any third party without telling {a}."],
|
| 177 |
+
["Confidential information excludes what is public, already known or independently developed."],
|
| 178 |
+
["Confidentiality binds {a} only; {b} owes no duty of secrecy about anything it learns."]),
|
| 179 |
+
"ip_and_ownership": (["{a} keeps ownership of the Dataset and grants {b} a limited license for the stated purpose.",
|
| 180 |
+
"Models {b} trains remain {b}'s, and {a} keeps its rights in the source records."],
|
| 181 |
+
["Ownership of the Dataset passes to {b} on delivery, including all future records {a} creates.",
|
| 182 |
+
"{b} owns everything {a} produces during the term, whether or not it relates to the Dataset."],
|
| 183 |
+
["The license is non-transferable and covers only the field of use set out in the schedule."],
|
| 184 |
+
["{a} assigns all present and future rights in its systems and records to {b} for no extra fee."]),
|
| 185 |
+
"indemnity": (["Each party defends the other against claims caused by its own breach, up to a stated cap.",
|
| 186 |
+
"{a} stands behind its statement that it has the right to license the Dataset, with liability capped at the fees paid."],
|
| 187 |
+
["{a} indemnifies {b} without any cap for every claim of any kind, including claims {b} causes.",
|
| 188 |
+
"{a} pays {b}'s legal costs on any dispute, even where {a} wins."],
|
| 189 |
+
["Liability under this clause is limited to the fees paid in the {n} days before the claim."],
|
| 190 |
+
["{a} bears all liability for how {b} uses the Dataset after delivery, with no limit."]),
|
| 191 |
+
"audit_and_deletion": (["{a} may ask once a year for a written confirmation of how the Dataset is used, and {b} deletes it at the end of the term.",
|
| 192 |
+
"On request {b} confirms in writing that copies were deleted within {n} days."],
|
| 193 |
+
["{b} may keep the Dataset forever and {a} has no way to check how it is used.",
|
| 194 |
+
"{b} may share the Dataset with its affiliates and partners and need not delete it."],
|
| 195 |
+
["At the end of the term {b} deletes the Dataset and certifies it, and {a} may audit once with notice."],
|
| 196 |
+
["There is no audit right and no duty to delete, even if {a} ends the Agreement for breach."]),
|
| 197 |
+
"termination": (["Either party may end the Agreement for uncured breach after {n} days' written notice.",
|
| 198 |
+
"{a} may end the Agreement if {b} misses a payment and does not fix it within {n} days."],
|
| 199 |
+
["Only {b} may end the Agreement, at any time, and keeps everything delivered.",
|
| 200 |
+
"{a} cannot end the Agreement for any reason, including non-payment."],
|
| 201 |
+
["Termination for convenience needs {n} days' notice, and fees already earned stay payable."],
|
| 202 |
+
["On termination by {b}, all data already delivered stays with {b} and no fees are refunded or owed."]),
|
| 203 |
+
}
|
| 204 |
+
tr_t_type, te_t_type, tr_t_risk, te_t_risk = {}, {}, {"standard": [], "watch": []}, {"standard": [], "watch": []}
|
| 205 |
+
rows_train, rows_test = [], []
|
| 206 |
+
for ctype, (s_tr, w_tr, s_te, w_te) in CLAUSES.items():
|
| 207 |
+
for tpls, risk, rows in ((s_tr, "standard", rows_train), (w_tr, "watch", rows_train), (s_te, "standard", rows_test), (w_te, "watch", rows_test)):
|
| 208 |
+
seen = set()
|
| 209 |
+
for t in tpls:
|
| 210 |
+
for _ in range(12):
|
| 211 |
+
text = rng.choice(PRE) + t.format(**CL_FILL()) + rng.choice(SUF)
|
| 212 |
+
if text not in seen:
|
| 213 |
+
seen.add(text)
|
| 214 |
+
rows.append({"text": text, "clause_type": ctype, "risk": risk})
|
| 215 |
+
rng.shuffle(rows_train)
|
| 216 |
+
rng.shuffle(rows_test)
|
| 217 |
+
write("datasets", "synthetic-contract-clauses-by-type-and-risk", "train", with_ids("cl", rows_train))
|
| 218 |
+
write("datasets", "synthetic-contract-clauses-by-type-and-risk", "test", with_ids("cl-t", rows_test))
|
| 219 |
+
write("models", "contract-clause-type-classifier", "train", [{"text": x["text"], "label": x["clause_type"]} for x in rows_train])
|
| 220 |
+
write("models", "contract-clause-type-classifier", "test", [{"text": x["text"], "label": x["clause_type"]} for x in rows_test])
|
| 221 |
+
write("models", "contract-clause-risk-classifier", "train", [{"text": x["text"], "label": x["risk"]} for x in rows_train])
|
| 222 |
+
write("models", "contract-clause-risk-classifier", "test", [{"text": x["text"], "label": x["risk"]} for x in rows_test])
|
| 223 |
+
|
| 224 |
+
# ======================= dataset 7 and model 5: inbound inquiries by intent =======================
|
| 225 |
+
IQ_FILL = lambda: {"name": person(), "org": org(), "n": rng.choice([30, 45, 60, 120, 400]), "thing": rng.choice(
|
| 226 |
+
["support tickets", "CRM history", "design files", "call recordings", "team chat", "project records", "code reviews", "invoices"])}
|
| 227 |
+
INTENT_TRAIN = {
|
| 228 |
+
"seller_interest": ["Hi, I run {org} with about {n} staff. We have years of {thing}. Could we sell or license it to an AI lab?",
|
| 229 |
+
"My company has a lot of {thing} sitting unused. How does licensing it work and what would you need from us?",
|
| 230 |
+
"We are closing down {org} and want to know whether our {thing} has any value to AI companies.",
|
| 231 |
+
"I saw your video. Does a firm of {n} people qualify, and who pays your fee?",
|
| 232 |
+
"Our owners are open to licensing {thing}. What is the first step?", "Interested in learning how to monetise our archive of {thing}. Please call me."],
|
| 233 |
+
"buyer_request": ["We are an AI lab looking for licensed {thing} from operating companies. Can you share what you have?",
|
| 234 |
+
"Our model team needs a dataset of {thing}, de-identified, with clear rights. Do you source this?",
|
| 235 |
+
"Looking to buy business records for training an agent. Which industries can you cover?",
|
| 236 |
+
"As a buyer we want sample data before committing. How do you handle diligence?",
|
| 237 |
+
"Please send what you can offer in {thing}; we have a budget this quarter.", "Our research group wants to license real-world {thing} and needs the rights confirmed."],
|
| 238 |
+
"referral_offer": ["I know several owners with {thing}. How does your referral arrangement work?",
|
| 239 |
+
"As a consultant I can introduce companies of {n} people. What do referrers receive?",
|
| 240 |
+
"My accounting clients keep asking about selling their data. Can we partner on introductions?",
|
| 241 |
+
"I run an M&A advisory and can bring wind-down companies. Do you work with referral partners?",
|
| 242 |
+
"Happy to introduce owners if the terms are fair. Please send your partner details.", "We have a list of companies that may want to license {thing}; can we share it with you?"],
|
| 243 |
+
"press_media": ["I am a reporter writing about AI training data. Could you give a comment on how licensing works?",
|
| 244 |
+
"Our podcast would like to interview you about selling company data to AI labs.",
|
| 245 |
+
"Writing a feature on data brokers for our newsletter; can we schedule a short call?",
|
| 246 |
+
"Journalist here. Do you have figures on how many companies license their data?",
|
| 247 |
+
"We would like to quote you in an article about {thing} and AI.", "An editor asked me to request an interview about your work."],
|
| 248 |
+
"vendor_pitch": ["We offer lead generation and can fill your calendar with {n} meetings a month.", "Our agency does SEO and link building for sites like yours. Want a free audit?",
|
| 249 |
+
"I can build you a website redesign at a discount this week.", "Our software helps companies like {org} automate outreach. Book a demo?",
|
| 250 |
+
"We sell guest posts on high authority blogs; interested?", "Special offer on email lists for your campaigns, limited time."],
|
| 251 |
+
"support_question": ["I filled in your form but did not get a reply. Can you check on it?", "How do I update the details I sent you last week?",
|
| 252 |
+
"I cannot open the calculator result page. Could you help?", "Where do I find the checklist you mentioned in the video?",
|
| 253 |
+
"I made an account, but I forgot which email I used. Can you help me sign in?", "The page says error when I submit my company information."],
|
| 254 |
+
"opt_out": ["Please remove me from your list.", "Stop emailing me. I am not interested.", "Unsubscribe me and do not contact this address again.",
|
| 255 |
+
"I did not ask for these messages. Take me off your list.", "Not interested, please delete my details.", "Remove {org} from your mailing list."],
|
| 256 |
+
"spam": ["Congratulations, you have won a prize, click here to claim.", "Buy cheap watches and pills now, best prices guaranteed.",
|
| 257 |
+
"Earn money fast from home, no skills needed, send your bank login.", "Your account is suspended, verify your password at the link.",
|
| 258 |
+
"Hot singles near you want to meet tonight.", "Crypto giveaway, send one coin and get ten back."],
|
| 259 |
+
}
|
| 260 |
+
INTENT_TEST = {
|
| 261 |
+
"seller_interest": ["We own a small agency, {n} employees, with {thing} going back years. Is there a market for this?", "Can {org} get paid for its {thing}? We would like an honest answer."],
|
| 262 |
+
"buyer_request": ["Procurement at our lab is exploring licensed {thing}; what is your sourcing process?", "We are building an evaluation set and need real {thing} with permission."],
|
| 263 |
+
"referral_offer": ["Do you pay a finder's fee if I connect you with a company of {n} staff?", "I advise owners on exits and would like to discuss working together on introductions."],
|
| 264 |
+
"press_media": ["I cover AI for a trade magazine and need an expert on data licensing for a quote.", "Would you join our webinar to explain how companies sell {thing} to AI labs?"],
|
| 265 |
+
"vendor_pitch": ["We help companies like {org} get more traffic with our paid promotion package.", "Our team can create videos for your channel at a fraction of the usual cost."],
|
| 266 |
+
"support_question": ["The link in your email does not work for me. Can you resend it?", "I need to correct a number I entered on the form. How do I do that?"],
|
| 267 |
+
"opt_out": ["Kindly take {org} off all further messages.", "I would like no further contact. Please confirm you removed me."],
|
| 268 |
+
"spam": ["Winner! Claim your free gift card by entering your card details here.", "Invest now and double your money in a week, guaranteed returns."],
|
| 269 |
+
}
|
| 270 |
+
r = classification("inquiries", "label", INTENT_TRAIN, INTENT_TEST, IQ_FILL, 10)
|
| 271 |
+
write("datasets", "synthetic-inbound-inquiries-by-intent", "train", with_ids("inq", [{"text": x["text"], "intent": x["label"]} for x in r["train"]]))
|
| 272 |
+
write("datasets", "synthetic-inbound-inquiries-by-intent", "test", with_ids("inq-t", [{"text": x["text"], "intent": x["label"]} for x in r["test"]]))
|
| 273 |
+
write("models", "inbound-inquiry-intent-classifier", "train", r["train"])
|
| 274 |
+
write("models", "inbound-inquiry-intent-classifier", "test", r["test"])
|
| 275 |
+
|
| 276 |
+
# ======================= dataset 8 and model 6: invoice and purchase order text with personal and account details =======================
|
| 277 |
+
def inv_fill():
|
| 278 |
+
return {"p": person(), "o": org(), "e": email(), "ph": phone(), "ad": address(), "d": date(), "m": money(), "i": code("INV-"), "ac": code("", 10), "t": code("", 9)}
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def spans_template(parts):
|
| 282 |
+
"""parts: list of (text-or-slot, label). Slot names in braces fill from `fill`."""
|
| 283 |
+
return parts
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
INV_TRAIN = [
|
| 287 |
+
[("Invoice ", None), ("{i}", "INVOICE_ID"), (" issued ", None), ("{d}", "DATE"), (" to ", None), ("{o}", "ORG"), (", attn ", None), ("{p}", "PERSON"), (". Amount due ", None), ("{m}", "MONEY"), (".", None)],
|
| 288 |
+
[("Bill to: ", None), ("{p}", "PERSON"), (", ", None), ("{ad}", "ADDRESS"), (". Contact ", None), ("{e}", "EMAIL"), (" or ", None), ("{ph}", "PHONE"), (". Total ", None), ("{m}", "MONEY"), (".", None)],
|
| 289 |
+
[("Please remit ", None), ("{m}", "MONEY"), (" to account ", None), ("{ac}", "BANK_ACCOUNT"), (" by ", None), ("{d}", "DATE"), (" quoting ", None), ("{i}", "INVOICE_ID"), (".", None)],
|
| 290 |
+
[("Vendor: ", None), ("{o}", "ORG"), (" (tax ID ", None), ("{t}", "TAX_ID"), ("). Remit to ", None), ("{ad}", "ADDRESS"), (".", None)],
|
| 291 |
+
[("Purchase order ", None), ("{i}", "INVOICE_ID"), (" approved by ", None), ("{p}", "PERSON"), (" on ", None), ("{d}", "DATE"), (" for ", None), ("{m}", "MONEY"), (".", None)],
|
| 292 |
+
[("Questions about this invoice? Email ", None), ("{e}", "EMAIL"), (" or call ", None), ("{ph}", "PHONE"), (". Ref ", None), ("{i}", "INVOICE_ID"), (".", None)],
|
| 293 |
+
[("Payment received from ", None), ("{o}", "ORG"), (" on ", None), ("{d}", "DATE"), (", ", None), ("{m}", "MONEY"), (", from account ending ", None), ("{ac}", "BANK_ACCOUNT"), (".", None)],
|
| 294 |
+
]
|
| 295 |
+
INV_TEST = [
|
| 296 |
+
[("Statement for ", None), ("{p}", "PERSON"), (" at ", None), ("{o}", "ORG"), (": balance ", None), ("{m}", "MONEY"), (", due ", None), ("{d}", "DATE"), (".", None)],
|
| 297 |
+
[("Wire details: account ", None), ("{ac}", "BANK_ACCOUNT"), (", beneficiary ", None), ("{o}", "ORG"), (", tax number ", None), ("{t}", "TAX_ID"), (".", None)],
|
| 298 |
+
[("Ship to ", None), ("{ad}", "ADDRESS"), (" for ", None), ("{p}", "PERSON"), (", phone ", None), ("{ph}", "PHONE"), (", order ", None), ("{i}", "INVOICE_ID"), (".", None)],
|
| 299 |
+
[("Credit note ", None), ("{i}", "INVOICE_ID"), (" of ", None), ("{m}", "MONEY"), (" sent to ", None), ("{e}", "EMAIL"), (" on ", None), ("{d}", "DATE"), (".", None)],
|
| 300 |
+
]
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
def render_spans(templates, per, fill):
|
| 304 |
+
rows, seen = [], set()
|
| 305 |
+
for tpl in templates:
|
| 306 |
+
for _ in range(per):
|
| 307 |
+
f = fill()
|
| 308 |
+
seg = []
|
| 309 |
+
for text, lab in tpl:
|
| 310 |
+
seg.append((text.format(**f) if "{" in text else text, lab))
|
| 311 |
+
row = spans_from(seg)
|
| 312 |
+
if row["text"] not in seen:
|
| 313 |
+
seen.add(row["text"])
|
| 314 |
+
rows.append(row)
|
| 315 |
+
rng.shuffle(rows)
|
| 316 |
+
return rows
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
tr, te = render_spans(INV_TRAIN, 40, inv_fill), render_spans(INV_TEST, 30, inv_fill)
|
| 320 |
+
write("datasets", "synthetic-invoice-pii-spans", "train", with_ids("inv", tr))
|
| 321 |
+
write("datasets", "synthetic-invoice-pii-spans", "test", with_ids("inv-t", te))
|
| 322 |
+
write("models", "invoice-pii-detector", "train", tr)
|
| 323 |
+
write("models", "invoice-pii-detector", "test", te)
|
| 324 |
+
|
| 325 |
+
# ======================= dataset 9 and model 7: chat logs with personal and technical identifiers =======================
|
| 326 |
+
def chat_fill():
|
| 327 |
+
return {"p": person(), "p2": person(), "e": email(), "ph": phone(), "k": key(), "ip": ip(), "t": code("TCK-", 5)}
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
CHAT_TRAIN = [
|
| 331 |
+
[("[10:42] ", None), ("{p}", "PERSON"), (": can you email the file to ", None), ("{e}", "EMAIL"), (" before lunch?", None)],
|
| 332 |
+
[("[14:03] ", None), ("{p}", "PERSON"), (": customer rang from ", None), ("{ph}", "PHONE"), (", ticket is ", None), ("{t}", "ID_NUMBER"), (".", None)],
|
| 333 |
+
[("oops I pasted the key in here: ", None), ("{k}", "API_KEY"), (" please rotate it", None)],
|
| 334 |
+
[("{p}", "PERSON"), (" > the server at ", None), ("{ip}", "IP_ADDRESS"), (" keeps timing out again", None)],
|
| 335 |
+
[("@", None), ("{p2}", "PERSON"), (" can you take ", None), ("{t}", "ID_NUMBER"), ("? ", None), ("{p}", "PERSON"), (" is out today", None)],
|
| 336 |
+
[("Reach me on ", None), ("{ph}", "PHONE"), (" if the deploy fails, or ", None), ("{e}", "EMAIL"), (".", None)],
|
| 337 |
+
[("config line: token=", None), ("{k}", "API_KEY"), (" host=", None), ("{ip}", "IP_ADDRESS")],
|
| 338 |
+
[("thanks ", None), ("{p2}", "PERSON"), (", closing ", None), ("{t}", "ID_NUMBER"), (" now", None)],
|
| 339 |
+
]
|
| 340 |
+
CHAT_TEST = [
|
| 341 |
+
[("DM from ", None), ("{p}", "PERSON"), (": my login is ", None), ("{e}", "EMAIL"), (", the old box is ", None), ("{ip}", "IP_ADDRESS")],
|
| 342 |
+
[("Heads up ", None), ("{p2}", "PERSON"), (": api secret ", None), ("{k}", "API_KEY"), (" is in the wiki page, remove it", None)],
|
| 343 |
+
[("call ", None), ("{p}", "PERSON"), (" on ", None), ("{ph}", "PHONE"), (" about ", None), ("{t}", "ID_NUMBER")],
|
| 344 |
+
[("{p}", "PERSON"), (" and ", None), ("{p2}", "PERSON"), (" are both cc'd at ", None), ("{e}", "EMAIL")],
|
| 345 |
+
]
|
| 346 |
+
tr, te = render_spans(CHAT_TRAIN, 45, chat_fill), render_spans(CHAT_TEST, 35, chat_fill)
|
| 347 |
+
write("datasets", "synthetic-chat-logs-pii-spans", "train", with_ids("chat", tr))
|
| 348 |
+
write("datasets", "synthetic-chat-logs-pii-spans", "test", with_ids("chat-t", te))
|
| 349 |
+
write("models", "chat-log-pii-detector", "train", tr)
|
| 350 |
+
write("models", "chat-log-pii-detector", "test", te)
|
| 351 |
+
|
| 352 |
+
# ======================= dataset 10 and model 8: sales call notes by outcome =======================
|
| 353 |
+
CALL_FILL = lambda: {"name": person(), "org": org(), "n": rng.choice([2, 3, 4, 6]), "w": rng.choice(["Friday", "next Tuesday", "the 14th", "end of month"]),
|
| 354 |
+
"thing": rng.choice(["the pilot", "the proposal", "the quote", "the demo", "the sample"])}
|
| 355 |
+
OUT_TRAIN = {
|
| 356 |
+
"advanced": ["Spoke with {name} at {org}. Liked {thing}, wants a second call with the team on {w}.", "{name} asked for a written proposal and named two colleagues to join the next meeting.",
|
| 357 |
+
"Good fit confirmed. {org} will send access for {thing} and we booked a review on {w}.", "Budget approved in principle by {name}; next step is the security questionnaire.",
|
| 358 |
+
"{name} introduced us to legal at {org}. Drafting terms this week.", "Strong interest. They want {thing} reworked for their data and will reply by {w}."],
|
| 359 |
+
"stalled": ["{name} said the team is busy and to check back after {w}. No date set.", "Waiting on {org} to reply to the last {n} emails. Nothing since the call.",
|
| 360 |
+
"{name} likes it but needs sign-off from a person they cannot name yet.", "Pushed back to next quarter by {org}; no owner for {thing}.",
|
| 361 |
+
"Left a voicemail twice for {name}. Still no answer on {thing}.", "Interest is real but their priorities changed and the project is on hold."],
|
| 362 |
+
"lost": ["{name} told us {org} chose another provider and will not continue.", "Price was too high for {org}; {name} closed the file.",
|
| 363 |
+
"{org} decided to build it in house, so no further calls are needed.", "{name} said it is not a fit for their size and asked us to stop following up.",
|
| 364 |
+
"Legal at {org} refused the data terms and the deal ended.", "{name} replied that they have no interest in {thing} and no budget now or later."],
|
| 365 |
+
"won": ["{name} signed the agreement for {org} and we start on {w}.", "Contract returned countersigned by {org}; invoice sent for {thing}.",
|
| 366 |
+
"{org} confirmed the order after {thing} and payment is scheduled for {w}.", "{name} approved the final terms; kickoff booked with {n} people from their side.",
|
| 367 |
+
"Deal closed. {org} signed, and first delivery is planned for {w}.", "Signed today. {name} thanked the team and asked for the onboarding pack."],
|
| 368 |
+
}
|
| 369 |
+
OUT_TEST = {
|
| 370 |
+
"advanced": ["{name} wants {thing} sent to their technical lead before the next call with {org}.", "They asked for references and set a follow-up for {w}."],
|
| 371 |
+
"stalled": ["No response from {org} after the proposal; last contact was {n} weeks ago.", "{name} is travelling and said to try again later without giving a date."],
|
| 372 |
+
"lost": ["{org} went with a cheaper alternative, according to {name}.", "{name} said the project was cancelled and {org} no longer needs this."],
|
| 373 |
+
"won": ["{org} has accepted the quote and {name} sent the purchase order.", "We got the yes from {name}; paperwork is complete."],
|
| 374 |
+
}
|
| 375 |
+
r = classification("calls", "label", OUT_TRAIN, OUT_TEST, CALL_FILL, 10)
|
| 376 |
+
write("datasets", "synthetic-sales-call-notes-by-outcome", "train", with_ids("call", [{"text": x["text"], "outcome": x["label"]} for x in r["train"]]))
|
| 377 |
+
write("datasets", "synthetic-sales-call-notes-by-outcome", "test", with_ids("call-t", [{"text": x["text"], "outcome": x["label"]} for x in r["test"]]))
|
| 378 |
+
write("models", "sales-call-outcome-classifier", "train", r["train"])
|
| 379 |
+
write("models", "sales-call-outcome-classifier", "test", r["test"])
|