PEFT
Safetensors
English
web-agents
gui-agents
mind2web
lora
pointer-network
selective-prediction
calibration
decision-making
fast-decision-model
system-1
model-routing
gui-grounding
Instructions to use senpaicq/m2w-gemma4-e2b-pointer with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use senpaicq/m2w-gemma4-e2b-pointer with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
File size: 9,252 Bytes
a882958 | 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 | #!/usr/bin/env python3
"""Multimodal-Mind2Web hazırlığı: her adım için ilk-K aday listesi (görüntüsüz).
Çıktı (her split için bir jsonl.gz): adım başına görev, geçmiş eylemler, işlem türü,
adaylar (tag, kısa metin, kutu; K=0 ise hepsi, TF-IDF sırasıyla), doğru adayın sırası. Ekran görüntüleri BURADA
kaydedilmez; öznitelik çıkarırken akıştan tekrar okunur.
Buradaki sıralama yalnızca ön sıralama (TF-IDF). Gerçek ilk-50 süzgeci m2w_ranker.py'deki DeBERTa.
Gerçek bir satırda TF-IDF doğru öğeyi 554 adayın 349. sırasına koydu: tek başına yetmez.
Kullanım:
python m2w_prep.py --splits train --max_rows 300 --out m2w_smoke
python m2w_prep.py --splits train test_task test_website test_domain --out m2w_all
"""
import argparse
import gzip
import itertools
import json
import os
import re
import zlib
from collections import Counter
import numpy as np
from lxml import etree
from sklearn.feature_extraction.text import TfidfVectorizer
NAME = "osunlp/Multimodal-Mind2Web"
COLS = ["action_uid", "raw_html", "cleaned_html", "operation", "pos_candidates", "neg_candidates",
"website", "domain", "subdomain", "annotation_id", "confirmed_task",
"action_reprs", "target_action_index", "target_action_reprs"]
LABEL_ATTRS = ("aria_label", "placeholder", "title", "name", "alt", "value", "id")
# Yapısal alanlar (aday biçimi denemeleri için): metin öznitelikleri boş değilse, mantıksal öznitelikler varsa (boş değerle bile) saklanır
TEXT_AT = ("role", "type", "aria_label", "aria-label", "placeholder", "title", "name", "alt", "value", "id",
"aria_checked", "aria-checked", "aria_expanded", "aria-expanded", "aria_disabled", "aria-disabled",
"aria_selected", "aria-selected", "aria_haspopup", "aria-haspopup")
BOOL_AT = ("checked", "disabled", "readonly", "required", "selected")
ATTR_KEYS = Counter() # doğru adaylarda hangi öznitelik anahtarları görülüyor (özet için)
def small_attrs(a):
out = {}
for k in TEXT_AT:
v = a.get(k)
if v is not None and str(v).strip():
out[k] = str(v).strip()[:120]
for k in BOOL_AT:
if k in a:
out[k] = str(a[k])[:10]
return out
def _load(c):
return json.loads(c) if isinstance(c, str) else c
def parse_cand(c):
c = _load(c)
a = _load(c["attributes"]) if c.get("attributes") else {}
bbox = None
r = a.get("bounding_box_rect")
if r:
try:
v = [float(x) for x in (r.split(",") if isinstance(r, str) else r)]
if len(v) == 4:
bbox = v
except ValueError:
pass
return {"tag": c.get("tag", ""), "bid": str(c.get("backend_node_id", a.get("backend_node_id", ""))),
"attrs": a, "bbox": bbox, "orig": bool(c.get("is_original_target", False))}
def node_texts(cleaned_html, bids):
"""backend_node_id -> kısa iç metin (alt ağaçtan, ilk birkaç parça)."""
out = {}
if not cleaned_html or not bids:
return out
try:
root = etree.fromstring(cleaned_html.encode("utf-8", "ignore"),
etree.HTMLParser(recover=True, huge_tree=True))
except Exception:
return out
if root is None:
return out
for el in root.iter():
b = el.get("backend_node_id")
if b in bids:
t = " ".join("".join(itertools.islice(el.itertext(), 40)).split())
out[b] = t[:200]
return out
def cand_text(cd, inner):
a = cd["attrs"]
parts = [cd["tag"]]
for k in ("role", "type"):
if a.get(k):
parts.append(str(a[k]))
for k in LABEL_ATTRS:
v = a.get(k)
if v and str(v).strip():
parts.append(str(v).strip()[:60])
if inner:
parts.append(inner)
return " | ".join(parts)[:160]
def page_size(raw_html):
m = re.search(r'bounding_box_rect="([^"]+)"', raw_html[:4000])
if m:
v = m.group(1).split(",")
if len(v) == 4:
return float(v[2]), float(v[3])
return None, None
def rank(query, texts, bboxes, drop_zero_area):
"""Benzerlik puanı ile sıralı indeksler. Alanı sıfır olan öğeler en sona."""
n = len(texts)
if n == 0:
return []
try:
vec = TfidfVectorizer(token_pattern=r"[A-Za-z0-9]{2,}", lowercase=True, sublinear_tf=True)
M = vec.fit_transform(texts + [query])
sc = (M[:n] @ M[n].T).toarray().ravel()
except ValueError:
sc = np.zeros(n)
sc = sc.astype(float)
for i, b in enumerate(bboxes):
if b is None or b[2] <= 0 or b[3] <= 0:
sc[i] = -1.0 if drop_zero_area else sc[i] - 0.05
return list(np.argsort(-sc, kind="stable"))
def process_row(r, K, drop_zero_area):
pos = [parse_cand(c) for c in r["pos_candidates"]]
neg = [parse_cand(c) for c in r["neg_candidates"]]
allc = pos + neg
bids = {c["bid"] for c in allc}
texts_in = node_texts(r["cleaned_html"], bids)
texts = [cand_text(c, texts_in.get(c["bid"], "")[:100]) for c in allc] # eski metin: iç metin 100 karakter
idx = int(r["target_action_index"])
hist = list(r["action_reprs"][:idx])
query = r["confirmed_task"] + " " + " ".join(re.sub(r"->.*", "", h) for h in hist[-3:])
order = rank(query, texts, [c["bbox"] for c in allc], drop_zero_area)
if K:
order = order[:K]
pos_b = {c["bid"] for c in pos}
for c in pos:
ATTR_KEYS.update(c["attrs"].keys())
cands, pos_idx = [], []
for j, i in enumerate(order):
c = allc[i]
cands.append({"bid": c["bid"], "tag": c["tag"], "text": texts[i], "bbox": c["bbox"],
"inner": texts_in.get(c["bid"], ""), "at": small_attrs(c["attrs"])})
if c["bid"] in pos_b:
pos_idx.append(j)
pw, ph = page_size(r["raw_html"])
op = _load(r["operation"]) # gerçek veride JSON metni
return {
"action_uid": r["action_uid"], "ann": r["annotation_id"], "step": idx,
"website": r["website"], "domain": r["domain"], "task": r["confirmed_task"],
"history": hist, "target": r["target_action_reprs"],
"op": op["op"], "value": op.get("value", ""),
"page_w": pw, "page_h": ph, "n_total": len(allc), "n_pos": len(pos),
"cands": cands, "pos_idx": pos_idx,
}
def open_stream(split):
from datasets import load_dataset
try:
return load_dataset(NAME, split=split, streaming=True, columns=COLS)
except Exception as e: # columns desteklenmiyorsa
print("columns= çalışmadı, tüm kolonlarla akış:", type(e).__name__)
ds = load_dataset(NAME, split=split, streaming=True)
return ds.remove_columns([c for c in ("screenshot",) if c])
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--splits", nargs="+", default=["train"])
ap.add_argument("--out", default="m2w")
ap.add_argument("--K", type=int, default=0, help="0 = tüm adaylar")
ap.add_argument("--max_rows", type=int, default=0)
ap.add_argument("--val_mod", type=int, default=8, help="eğitim sitelerinin 1/val_mod kadarı doğrulamaya")
ap.add_argument("--keep_zero_area", action="store_true")
args = ap.parse_args()
os.makedirs(args.out, exist_ok=True)
for split in args.splits:
ds = open_stream(split)
if args.max_rows:
ds = ds.take(args.max_rows)
outs = {}
st = Counter()
n_cand = []
for k, r in enumerate(ds):
ex = process_row(r, args.K, not args.keep_zero_area)
name = split
if split == "train":
name = "val" if zlib.crc32(ex["website"].encode()) % args.val_mod == 0 else "train"
if name not in outs:
outs[name] = gzip.open(os.path.join(args.out, f"{name}.jsonl.gz"), "wt", encoding="utf-8")
ex["split"] = name
outs[name].write(json.dumps(ex, ensure_ascii=False) + "\n")
st[f"{name}/n"] += 1
st[f"{name}/op_{ex['op']}"] += 1
st[f"{name}/pos_yok"] += ex["n_pos"] == 0
st[f"{name}/pos_ilkK_icinde"] += len(ex["pos_idx"]) > 0
st[f"{name}/pos_ilk1"] += 0 in ex["pos_idx"]
n_cand.append(ex["n_total"])
if (k + 1) % 500 == 0:
print(f" {split}: {k + 1} adım", flush=True)
for f in outs.values():
f.close()
print(f"\n== {split} == adaylar/adım: ortanca {int(np.median(n_cand))}, en çok {max(n_cand)}")
for name in sorted({k.split('/')[0] for k in st}):
n = st[f"{name}/n"]
print(f" [{name}] adım={n} | doğru aday listede yok={st[name+'/pos_yok']} "
f"| recall@{args.K or 'hepsi'}={st[name+'/pos_ilkK_icinde']/n:.3f} | top1={st[name+'/pos_ilk1']/n:.3f} "
f"| CLICK={st[name+'/op_CLICK']} TYPE={st[name+'/op_TYPE']} SELECT={st[name+'/op_SELECT']}")
print("\ndoğru adaylarda görülen öznitelik anahtarları (ilk 40):", ATTR_KEYS.most_common(40))
json.dump(dict(ATTR_KEYS.most_common(200)), open(os.path.join(args.out, "oznitelik_anahtarlari.json"), "w"), indent=1)
print("\nyazıldı:", args.out)
if __name__ == "__main__":
main()
|