Text Classification
Transformers
Safetensors
English
Korean
Japanese
qev_decision_merged
feature-extraction
qev
dlp
typed-decision
japanese
bf16
custom_code
Instructions to use querypieai/QEV1.1-9B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use querypieai/QEV1.1-9B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="querypieai/QEV1.1-9B", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("querypieai/QEV1.1-9B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 12,204 Bytes
0127113 | 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | #!/usr/bin/env python3
"""Build an isolated QEV2 DLP multitask dataset.
The source DLP corpus has document-level findings. This converter keeps that
ground truth and adds a small, deterministic bundle of type questions to every
record: is_dlp plus up to three type decisions. Japanese vulnerability rows
are converted from their existing explicit annotations and are never used to
overwrite the official corpus.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import random
from collections import Counter
from pathlib import Path
TYPE_MAP = {
"PERSON_NAME": "is_person_name",
"GOVERNMENT_ID": "is_government_id",
"CREDIT_CARD_NUMBER": "is_credit_card",
"FINANCIAL_ACCOUNT_NUMBER": "is_financial_account",
"PASSWORD": "is_password",
"MEDICAL_RECORD_NUMBER": "is_medical_record",
"EMAIL_ADDRESS": "is_email",
"PHONE_NUMBER": "is_phone",
"API_KEY": "is_api_key",
"AUTH_TOKEN": "is_auth_token",
"DEVICE_ID": "is_device_id",
"MAC_ADDRESS": "is_mac_address",
"DATE_OF_BIRTH": "is_birth_date",
"IP_ADDRESS": "is_ip_address",
"DRIVERS_LICENSE_NUMBER": "is_drivers_license",
"STREET_ADDRESS": "is_address",
"BLOOD_TYPE": "is_blood_type",
"PASSPORT": "is_passport",
"AGE": "is_age",
"VEHICLE_IDENTIFICATION_NUMBER": "is_vehicle_id",
"IBAN_CODE": "is_iban",
}
TYPE_ORDER = list(dict.fromkeys(TYPE_MAP.values()))
JP_KEY_MAP = {
"phone": "is_phone",
"date_of_birth": "is_birth_date",
"drivers_license": "is_drivers_license",
"passport": "is_passport",
"government_id": "is_government_id",
"medical": "is_medical_record",
"biometric": "is_biometric",
"email": "is_email",
"address": "is_address",
"credit_card": "is_credit_card",
"password": "is_password",
"api_key": "is_api_key",
"auth_token": "is_auth_token",
}
LABEL_TEXT = {
"en": {
"is_dlp": ("Does this source text contain one or more actual privacy-sensitive values?", "No actual privacy-sensitive value is present.", "At least one actual privacy-sensitive value is present."),
"is_email": "an actual email address",
"is_phone": "an actual telephone number",
"is_birth_date": "an actual date of birth",
"is_drivers_license": "an actual driver's license number",
"is_passport": "an actual passport or travel document number",
"is_government_id": "an actual government-issued identifier",
"is_medical_record": "an actual medical record or patient identifier",
"is_person_name": "an actual person's name",
"is_address": "an actual street address",
},
"jp": {
"is_dlp": ("ใใฎใใญในใใซๅฎ้ใฎใใฉใคใใทใผใปๆฉๅฏๆ
ๅ ฑใฎๅคใ1ใคไปฅไธๅซใพใใฆใใพใใ๏ผ", "ๅฎ้ใฎๅคใฏๅซใพใใฆใใชใใ", "ๅฎ้ใฎๅคใๅซใพใใฆใใใ"),
"is_email": "ๅฎ้ใฎใกใผใซใขใใฌใน",
"is_phone": "ๅฎ้ใฎ้ป่ฉฑ็ชๅท",
"is_birth_date": "ๅฎ้ใฎ็ๅนดๆๆฅ๏ผ่ฅฟๆฆใปๅๆฆใปๅ
จ่งๆฐๅญใๅซใ๏ผ",
"is_drivers_license": "ๅฎ้ใฎ้่ปขๅ
่จฑ่จผ็ชๅทใปๅ
่จฑ็ชๅท",
"is_passport": "ๅฎ้ใฎใในใใผใ็ชๅทใปๆ
ๅธ็ชๅท",
"is_government_id": "ๅฎ้ใฎๅฝๆฐIDใปๅไบบ็ชๅทใชใฉๆฟๅบ็บ่กID",
"is_medical_record": "ๅฎ้ใฎๆฃ่
IDใป่จบ็้ฒ็ชๅทใชใฉๅป็่ญๅฅๅญ",
"is_person_name": "ๅฎ้ใฎๅไบบๅ",
"is_address": "ๅฎ้ใฎไฝๆ",
},
"ko": {
"is_dlp": ("์ด ํ
์คํธ์ ์ค์ ๊ฐ์ธ์ ๋ณดยท๋ฏผ๊ฐ์ ๋ณด ๊ฐ์ด ํ๋ ์ด์ ํฌํจ๋์ด ์์ต๋๊น?", "์ค์ ๊ฐ์ด ํฌํจ๋์ด ์์ง ์์ต๋๋ค.", "์ค์ ๊ฐ์ด ํ๋ ์ด์ ํฌํจ๋์ด ์์ต๋๋ค."),
"is_email": "์ค์ ์ด๋ฉ์ผ ์ฃผ์",
"is_phone": "์ค์ ์ ํ๋ฒํธ",
"is_birth_date": "์ค์ ์๋
์์ผ",
"is_drivers_license": "์ค์ ์ด์ ๋ฉดํ๋ฒํธ",
"is_passport": "์ค์ ์ฌ๊ถ๋ฒํธ",
"is_government_id": "์ค์ ์ ๋ถ ๋ฐํ ์๋ณ๋ฒํธ",
"is_medical_record": "์ค์ ํ์ยท์ง๋ฃ ๊ธฐ๋ก ์๋ณ์",
"is_person_name": "์ค์ ์ฌ๋ ์ด๋ฆ",
"is_address": "์ค์ ์ฃผ์",
},
}
def lang_of(row: dict, default="en") -> str:
value = row.get("language") or row.get("_meta", {}).get("language") or default
value = {"ja": "jp", "japanese": "jp", "en-us": "en", "ko-kr": "ko"}.get(str(value).lower(), str(value).lower())
return value if value in {"en", "jp", "ko"} else default
def question(qid: str, lang: str, value: bool, *, is_dlp=False) -> dict:
if is_dlp:
instruction, no_text, yes_text = LABEL_TEXT[lang]["is_dlp"]
else:
phrase = LABEL_TEXT[lang].get(qid, qid)
if lang == "jp":
instruction = f"ใใฎใใญในใใซ{phrase}ใๅซใพใใฆใใพใใ๏ผๅ
จ่งใปๅ่งใๅบๅใ่จๅทใ่กจๅฝขๅผใๆน่กใๅซใ่กจ่จใๅคๅฎใใพใใ"
elif lang == "ko":
instruction = f"์ด ํ
์คํธ์ {phrase}๊ฐ ํฌํจ๋์ด ์์ต๋๊น? ์ ๊ฐยท๋ฐ๊ฐ, ๊ตฌ๋ถ๊ธฐํธ, ํ ํ์, ์ค๋ฐ๊ฟ ํ๊ธฐ๋ฅผ ํฌํจํด ํ์ ํฉ๋๋ค."
else:
instruction = f"Does this source text contain {phrase}? Include full-width/half-width, separators, tables, and line-wrapped forms."
no_text = "ๅฎ้ใฎๅคใฏๅซใพใใฆใใชใใ" if lang == "jp" else ("์ค์ ๊ฐ์ด ํฌํจ๋์ด ์์ง ์์ต๋๋ค." if lang == "ko" else "No actual value is present.")
yes_text = "ๅฎ้ใฎๅคใๅซใพใใฆใใใ" if lang == "jp" else ("์ค์ ๊ฐ์ด ํฌํจ๋์ด ์์ต๋๋ค." if lang == "ko" else "An actual value is present.")
return {
"type": "noul",
"instructions": instruction,
"criteria": {"false": no_text, "true": yes_text},
"label": "true" if value else "false",
}
def stable_choice(seed: str, candidates: list[str], n: int) -> list[str]:
rng = random.Random(int(hashlib.sha256(seed.encode()).hexdigest()[:16], 16))
shuffled = list(candidates)
rng.shuffle(shuffled)
return shuffled[:n]
def official_record(row: dict, split: str, ordinal: int) -> dict:
lang = lang_of(row)
positive = {TYPE_MAP[x.get("info_type")] for x in row.get("findings", []) if x.get("info_type") in TYPE_MAP}
# Three type slots per record keeps the demo-compatible four-question limit.
chosen = list(sorted(positive))[:3]
if len(chosen) < 3:
pool = [x for x in TYPE_ORDER if x not in positive]
chosen += stable_choice(row.get("id", str(ordinal)), pool, 3 - len(chosen))
qs = {"is_dlp": question("is_dlp", lang, bool(row.get("findings")), is_dlp=True)}
for qid in chosen:
qs[qid] = question(qid, lang, qid in positive)
return {
"id": f"mt-{split}-{row.get('id', ordinal)}",
"state": row.get("text", ""),
"questions": qs,
"_meta": {"origin": "official-dlp-findings", "source_id": row.get("id"), "source_split": split,
"language": lang, "info_types": sorted(x for x in positive), "binary_label": int(bool(row.get("findings")))},
}
def ja_record(row: dict, split: str, ordinal: int) -> list[dict]:
lang = "jp"
state = row.get("state", "")
src_q = row.get("questions", {})
labels = {JP_KEY_MAP[k]: (q.get("label") == "true") for k, q in src_q.items() if k in JP_KEY_MAP}
pos = {k for k, v in labels.items() if v}
# retain explicitly annotated types; fill with negatives to create bundles.
keys = list(labels)
bundles = [keys[i:i + 3] for i in range(0, len(keys), 3)] or [[]]
out = []
for bi, bundle in enumerate(bundles):
qs = {"is_dlp": question("is_dlp", lang, bool(row.get("_meta", {}).get("info_types") or row.get("questions", {}).get("is_dlp", {}).get("label") == "true"), is_dlp=True)}
for qid in bundle:
qs[qid] = question(qid, lang, labels[qid])
out.append({"id": f"mt-ja-{split}-{row.get('id', ordinal)}-b{bi}", "state": state, "questions": qs,
"_meta": {"origin": "ja-vulnerability-annotations", "source_id": row.get("id"), "source_split": split,
"language": "jp", "info_types": sorted(pos), "binary_label": int(bool(pos))}})
return out
def general_record(row: dict) -> dict:
# Keep replay records unchanged except for a provenance marker.
row = dict(row)
meta = dict(row.get("_meta", {})); meta["origin"] = "general-replay"; row["_meta"] = meta
# Make the multitask contract explicit for replay too. The replay corpus
# is the non-DLP general set, so this additional target is always false.
if isinstance(row.get("questions"), dict) and "is_dlp" not in row["questions"]:
lang = lang_of(row, "en")
row["questions"] = {"is_dlp": question("is_dlp", lang, False, is_dlp=True), **row["questions"]}
return row
def read_jsonl(path: Path):
with path.open(encoding="utf-8") as f:
for line in f:
if line.strip():
yield json.loads(line)
def write_jsonl(path: Path, rows):
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as f:
for row in rows:
f.write(json.dumps(row, ensure_ascii=False, separators=(",", ":")) + "\n")
def build(args):
out = Path(args.out); out.mkdir(parents=True, exist_ok=True)
source_root, dlp_root, ja_root = map(Path, (args.source_root, args.dlp_root, args.ja_root))
split_map = {"train": "train", "validation": "development", "test": "historical"}
stats = Counter(); generated = {}
for source_split, target_split in split_map.items():
rows = []
for i, row in enumerate(read_jsonl(source_root / f"{source_split}.jsonl")):
rows.append(official_record(row, source_split, i)); stats[f"official_{target_split}"] += 1
if target_split == "train":
# Existing official QEV2 replay is already a carefully selected 30% general mix.
for row in read_jsonl(dlp_root / "train-r0-replay30.jsonl"):
if row.get("_meta", {}).get("origin") == "general-replay" or row.get("_meta", {}).get("group_id"):
rows.append(general_record(row)); stats["replay"] += 1
ja_path = ja_root / "train-r4-focused.jsonl"
elif target_split == "development":
ja_path = ja_root / "development-r3.jsonl"
else:
ja_path = ja_root / "historical-test-multitask.jsonl"
# JA source is explicitly annotated; include only Japanese records to avoid duplicating EN/KO.
if ja_path.exists():
for i, row in enumerate(read_jsonl(ja_path)):
raw_lang = row.get("language") or row.get("_meta", {}).get("language")
if str(raw_lang).lower() in {"jp", "ja", "japanese"} and row.get("_meta", {}).get("origin") != "official-dlp-findings":
converted = ja_record(row, target_split, i); rows.extend(converted); stats[f"ja_{target_split}"] += len(converted)
write_jsonl(out / f"{target_split}-multitask.jsonl", rows)
generated[target_split] = len(rows)
manifest = {"schema": "qev2-train-multitask-v1", "question_limit": 4, "type_map": TYPE_MAP,
"logical_split": {"train": "train-multitask.jsonl", "development": "development-multitask.jsonl", "historical": "historical-multitask.jsonl"},
"official_source": str(source_root), "replay_source": str(dlp_root / "train-r0-replay30.jsonl"),
"ja_source": str(ja_root), "counts": generated, "stats": dict(stats),
"label_rule": "is_dlp=true iff source findings/info_types is non-empty; type labels come only from findings or explicit JA annotations"}
(out / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
print(json.dumps(manifest, ensure_ascii=False, indent=2))
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--out", required=True)
ap.add_argument("--source-root", required=True)
ap.add_argument("--dlp-root", required=True)
ap.add_argument("--ja-root", required=True)
build(ap.parse_args())
|