Download app.py from cihatyldz/pusula: direct link, hf CLI and curl.
- Browser
- Download file 19.6 kB
-
https://huggingface.co/spaces/cihatyldz/pusula/resolve/main/app.py
- Command line
-
hf download hf://spaces/cihatyldz/pusula/app.py
-
curl -L -o app.py https://huggingface.co/spaces/cihatyldz/pusula/resolve/main/app.py
19.6 kB
| """ | |
| Pusula — Turkce arama modellerinin gercek kullanici sorgularina dayanikliligi. | |
| Veri: leaderboard.json (yalnizca skorlar; benchmark metni icermez). Bozulmalar: pusula_perturb.py. | |
| """ | |
| import difflib | |
| import html | |
| import json | |
| import gradio as gr | |
| from pusula_perturb import PERTURBATIONS, perturb | |
| with open("leaderboard.json", encoding="utf-8") as f: | |
| DATA = json.load(f) | |
| META, MODELS, FIND = DATA["meta"], DATA["models"], DATA.get("findings", {}) | |
| PERTS = [p["name"] for p in META["perturbations"]] | |
| PLABEL = {p["name"]: p["label"] for p in META["perturbations"]} | |
| PDESC = {p["name"]: p["description"] for p in META["perturbations"]} | |
| DATASETS = META["datasets"] | |
| RANKED = [d for d in DATASETS if d.get("in_ranking", True)] | |
| HELD = [d for d in DATASETS if not d.get("in_ranking", True)] # gosterilir, siralamaya girmez | |
| HELD_NAMES = {d["name"] for d in HELD} | |
| METRIC = META["metric"] | |
| METRIC_LABEL = {"ndcg@10": "nDCG@10", "recall@10": "Recall@10", "mrr@10": "MRR@10"}.get(METRIC, METRIC) | |
| BY_SHORT = {m["short"]: m for m in MODELS} | |
| # Kahraman cumlesi icin bozulmaya gore ifade (sayiya ek gelmeyecek bicimde kurulur) | |
| PERT_PHRASE = { | |
| "upper": "BÜYÜK HARFLE yazılmış sorgularda", | |
| "ascii_full": "Türkçe karakter kullanılmadan yazılmış sorgularda", | |
| "ascii_partial": "Türkçe karakterlerin bir kısmı atlanmış sorgularda", | |
| "typo": "yazım hatalı sorgularda", | |
| "colloquial": "gündelik dille yazılmış sorgularda", | |
| "mobile": "telefondan aceleyle yazılmış sorgularda", | |
| } | |
| VIEWS = {"Mobil sorguda başarı": "mobile", "Temiz sorguda başarı": "clean", "Ortalama koruma": "avg"} | |
| ALL = f"Sıralamadaki veri setleri ({len(RANKED)})" if HELD else "Tüm veri setleri" | |
| DS_CHOICES = [ALL] + [(f"{d['name']} (sıralama dışı)" if d["name"] in HELD_NAMES else d["name"], d["name"]) for d in DATASETS] | |
| def pct(x): | |
| return "—" if x is None else f"%{100 * x:.0f}" | |
| def fmt_params(p): | |
| if not p: | |
| return "" | |
| return f"{p / 1000:.1f}B" if p >= 1000 else f"{p}M" | |
| # ═══ Kahraman alani: veriden turetilen kapak ═══ | |
| TR_MTEB_URL = "https://aclanthology.org/2025.findings-emnlp.471/" | |
| def _stat(value, label, kind=""): | |
| return (f'<div class="pu-stat {kind}"><div class="pu-stat-value">{value}</div>' | |
| f'<div class="pu-stat-label">{label}</div></div>') | |
| def hero_html(): | |
| n_models, n_ds = len(MODELS), len(RANKED) | |
| n_q = f"{sum(d['n_queries'] for d in RANKED):,}".replace(",", ".") | |
| held_note = (" " + ", ".join(html.escape(d["name"]) for d in HELD) + | |
| " tabloda ayrıca gösteriliyor ama eğitim verisiyle örtüştüğü için sıralamaya girmiyor.") if HELD else "" | |
| stats = [] | |
| wd = FIND.get("worst_drop") | |
| if wd: | |
| stats.append(_stat(pct(wd["retention"]), | |
| f'{html.escape(PLABEL.get(wd["pert"], wd["pert"]))} sorgularda <b>{html.escape(wd["model"])}</b> ' | |
| f'modelinin koruma oranı', "pu-stat-neg")) | |
| cw = FIND.get("clean_winner") | |
| if cw and cw["mobile_rank"] != 1: | |
| stats.append(_stat(f'{cw["clean_rank"]}. → {cw["mobile_rank"]}.', | |
| f'Temiz sorgularda lider olan <b>{html.escape(cw["model"])}</b>, mobil sorgulardaki sırası')) | |
| mw = FIND.get("mobile_winner") | |
| if mw: | |
| score = BY_SHORT[mw["model"]]["by_pert"]["mobile"]["score"] | |
| tied = f', {", ".join(map(html.escape, mw["tied_with"]))} ile aynı grupta' if mw["tied_with"] else "" | |
| stats.append(_stat(f'<span class="pu-stat-name">{html.escape(mw["model"])}</span>', | |
| f'Mobil sorgularda en iyi model ({METRIC_LABEL} {score:.3f}{tied})')) | |
| fe = FIND.get("fold_effect") | |
| if fe and fe["before"] is not None: | |
| stats.append(_stat(f'{pct(fe["before"])} → {pct(fe["after"])}', | |
| "BM25'e tek satırlık ASCII katlama eklenince ASCII sorgulardaki koruma oranı")) | |
| return f""" | |
| <header class="pu-hero"> | |
| <div class="pu-top"> | |
| <div class="pu-brand"> | |
| <svg class="pu-compass" viewBox="0 0 48 48" aria-hidden="true"> | |
| <circle cx="24" cy="24" r="21" fill="none" stroke="currentColor" stroke-width="1.6"/> | |
| <path d="M24 6 L29 24 L24 24 Z" fill="#C8102E"/><path d="M24 6 L19 24 L24 24 Z" fill="#9E0B23"/> | |
| <path d="M24 42 L19 24 L24 24 Z" fill="currentColor" opacity=".85"/><path d="M24 42 L29 24 L24 24 Z" fill="currentColor" opacity=".55"/> | |
| <circle cx="24" cy="24" r="2.2" fill="currentColor"/> | |
| </svg> | |
| <div> | |
| <h1>Pusula</h1> | |
| <p class="pu-kicker">Türkçe retrieval dayanıklılık benchmark'ı</p> | |
| </div> | |
| </div> | |
| <nav class="pu-links"> | |
| <a href="https://huggingface.co/datasets/{html.escape(META['dataset_repo'])}" target="_blank" rel="noopener">Veri ve kod ↗</a> | |
| <a href="{TR_MTEB_URL}" target="_blank" rel="noopener">TR-MTEB ↗</a> | |
| </nav> | |
| </div> | |
| <p class="pu-lede">Türkçe arama modelleri, kullanıcıların sorguları gerçekte yazdığı gibi yazıldığında ne kadar başarı kaybediyor? | |
| {n_models} model, {n_ds} veri setinden {n_q} sorgu ve {len(PERTS)} tür sorgu bozulmasıyla ölçüldü.{held_note}</p> | |
| <div class="pu-stats">{"".join(stats)}</div> | |
| </header>""" | |
| # ═══ Siralama tablosu ═══ | |
| def _values(m, dataset): | |
| if dataset == ALL: | |
| return m["clean"], {p: m["by_pert"][p] for p in PERTS} | |
| d = m["by_dataset"][dataset] | |
| return d["clean"], {p: d[p] for p in PERTS} | |
| def _heat(r): | |
| if r is None: | |
| return '<td class="pu-num pu-na">—</td>' | |
| alpha = min(0.8, max(0.0, (1 - r) * 0.9)) # %100 renksiz; %90→.09, %75→.23, %50→.45, %15→.77 | |
| return (f'<td class="pu-num pu-heat" style="--a:{alpha:.3f}" ' | |
| f'title="Değişen sorgularda koruma: %{100 * r:.1f} (100 üstü: bozulma tesadüfen işe yaradı)">{round(100 * r)}</td>') | |
| def board_html(view_label, dataset): | |
| view = VIEWS[view_label] | |
| rows = [] | |
| for m in MODELS: | |
| clean, perts = _values(m, dataset) | |
| rets = [perts[p]["retention"] for p in PERTS if perts[p]["retention"] is not None] | |
| rows.append((m, clean, perts, sum(rets) / len(rets) if rets else None)) | |
| key = {"mobile": lambda r: -r[2]["mobile"]["score"], "clean": lambda r: -r[1], | |
| "avg": lambda r: -(r[3] if r[3] is not None else -1)}[view] | |
| rows.sort(key=key) | |
| show_groups = view == "mobile" and dataset == ALL | |
| head = "".join(f'<th class="pu-num" title="{html.escape(PDESC[p])}">{html.escape(PLABEL[p])}</th>' for p in PERTS) | |
| body = [] | |
| for i, (m, clean, perts, avg) in enumerate(rows): | |
| g = m["group"] if show_groups else i + 1 | |
| band = "pu-band" if show_groups and m["group"] % 2 == 0 else "" | |
| same = show_groups and i > 0 and rows[i - 1][0]["group"] == m["group"] | |
| rank_cell = f'<td class="pu-rank">{"" if same else g}</td>' | |
| fam = html.escape(m["family"]) | |
| body.append( | |
| f'<tr class="{band}">{rank_cell}' | |
| f'<td class="pu-model"><a href="https://huggingface.co/{html.escape(m["model_id"])}" target="_blank" rel="noopener">' | |
| f'{html.escape(m["short"])}</a><div class="pu-meta"><span class="pu-fam pu-fam-{fam[:3].lower()}">{fam}</span>' | |
| f'<span class="pu-params">{fmt_params(m.get("params_m"))}</span></div></td>' | |
| f'<td class="pu-num">{clean:.3f}</td><td class="pu-num pu-strong">{perts["mobile"]["score"]:.3f}</td>' | |
| + "".join(_heat(perts[p]["retention"]) for p in PERTS) | |
| + f'<td class="pu-num pu-strong">{"—" if avg is None else round(100 * avg)}</td></tr>') | |
| note = ("Aynı numaralı modeller arasındaki fark istatistiksel olarak ayırt edilemez " | |
| "(veri seti dengeli eşleştirilmiş bootstrap, %95)." if show_groups | |
| else "Gruplama yalnızca sıralamadaki veri setleri ve mobil sıralamada gösterilir.") | |
| if dataset in HELD_NAMES: | |
| reason = next(d.get("holdout_reason") or "" for d in HELD if d["name"] == dataset) | |
| warn = (f'<p class="pu-warn"><b>Sıralama dışı veri seti.</b> {html.escape(reason)} ' | |
| f'Bu tablodaki skorlar bazı modeller için şişik olabilir; modelleri karşılaştırmak için kullanmayın.</p>') | |
| else: | |
| warn = "" | |
| return f"""{warn} | |
| <div class="pu-table-wrap"><table class="pu-table"> | |
| <thead><tr><th>{"Grup" if show_groups else "#"}</th><th>Model</th><th class="pu-num">Temiz<br>{METRIC_LABEL}</th> | |
| <th class="pu-num">Mobil<br>{METRIC_LABEL}</th>{head}<th class="pu-num">Ort.</th></tr> | |
| <tr class="pu-subhead"><th></th><th></th><th></th><th></th><th class="pu-num" colspan="{len(PERTS)}">Koruma oranı (%), yalnızca bozulmanın değiştirdiği sorgularda</th><th></th></tr></thead> | |
| <tbody>{"".join(body)}</tbody></table></div> | |
| <p class="pu-note">{note} Mutlak skorlar alt-örneklenmiş korpus nedeniyle TR-MTEB ile karşılaştırılamaz; benchmark'ın iddiası koruma oranlarıdır.</p>""" | |
| # ═══ Sorgunu boz ═══ | |
| def _diff(a, b): | |
| out = [] | |
| for op, i1, i2, j1, j2 in difflib.SequenceMatcher(None, a, b, autojunk=False).get_opcodes(): | |
| seg = html.escape(b[j1:j2]) | |
| if op == "equal": | |
| out.append(seg) | |
| elif seg: | |
| out.append(f"<mark>{seg}</mark>") | |
| else: | |
| out.append(f'<mark class="pu-del" title="silinen: {html.escape(a[i1:i2])}"></mark>') | |
| return "".join(out) | |
| def perturb_html(text, seed): | |
| text = (text or "").strip() | |
| if not text: | |
| return '<p class="pu-note">Bir sorgu yazın.</p>' | |
| rows = [] | |
| for p in PERTS: | |
| out = perturb(text, f"demo-{text}", p, seed=int(seed)) | |
| changed = out != text | |
| fragile, robust = FIND.get("most_fragile", {}).get(p), FIND.get("most_robust", {}).get(p) | |
| hint = "" | |
| if fragile and robust: | |
| fr, rb = BY_SHORT[fragile]["by_pert"][p]["retention"], BY_SHORT[robust]["by_pert"][p]["retention"] | |
| hint = (f'<span class="pu-hint">En kırılgan: {html.escape(fragile)} {pct(fr)} ' | |
| f'En dayanıklı: {html.escape(robust)} {pct(rb)}</span>') | |
| rows.append(f'<div class="pu-pert"><div class="pu-pert-head"><b>{html.escape(PLABEL[p])}</b>' | |
| f'<span>{html.escape(PDESC[p])}</span></div>' | |
| f'<p class="pu-pert-text">{_diff(text, out) if changed else html.escape(out)}' | |
| f'{"" if changed else " <em>(bu sorguyu değiştirmedi)</em>"}</p>{hint}</div>') | |
| return "".join(rows) | |
| def next_seed(seed): | |
| return int(seed) + 1 | |
| # ═══ Yontem ═══ | |
| def method_md(): | |
| ds_rows = "\n".join( | |
| f"| {d['name']} | {d['domain']} | {'doğal Türkçe' if d.get('native') else 'çeviri'} | {d['n_queries']} | {d['corpus_size']:,} | " | |
| .replace(",", ".") + ("Evet" if d.get("in_ranking", True) else "**Hayır**") + " |" | |
| for d in DATASETS) | |
| held_md = "".join(f"\n- **{d['name']}**: {d.get('holdout_reason') or ''}" for d in HELD) | |
| held_md = (f"\n\n**Sıralama dışı veri setleri.** Bu setler tabloda gösterilir ama ortalamaya, sıralamaya, gruplara ve " | |
| f"bulgulara girmez:{held_md}") if HELD else "" | |
| pinned = "\n".join(f"- **{m['short']}**: `{m['model_id']}` @ `{m['revision'][:7]}`" for m in MODELS if m.get("revision")) | |
| pinned_md = f"\n\n### Sabitlenmiş model sürümleri\n{pinned}" if pinned else "" | |
| p_rows = "\n".join(f"| {PLABEL[p]} | {PDESC[p]} | {META['example']['outputs'][p]} |" for p in PERTS) | |
| failed = "\n".join(f"- **{f['short']}** (`{f['model_id']}`): {f['reason']}" for f in META.get("failed", [])) or "- Yok" | |
| return f""" | |
| ### Veri setleri | |
| | Veri seti | Alan | Kaynak | Sorgu | Korpus | Sıralamada | | |
| |---|---|---|---|---|---| | |
| {ds_rows}{held_md} | |
| Korpus, her sorgunun ilgili dokümanlarını koruyacak şekilde alt-örneklendi. TR-MTEB veri setleri | |
| ([Baysan & Güngör, Findings of EMNLP 2025](https://aclanthology.org/2025.findings-emnlp.471/)) ve | |
| İletişim Başkanlığı DMM bültenlerinden türetilen doğal Türkçe başlık → analiz görevi kullanıldı. | |
| ### Bozulmalar | |
| Yalnızca **sorgu** bozulur, dokümanlar temiz kalır. Örnek sorgu: *{META['example']['text']}* | |
| | Bozulma | Açıklama | Örnek | | |
| |---|---|---| | |
| {p_rows} | |
| ### Metrik | |
| **{METRIC_LABEL}** ve **koruma oranı** = bozuk sorgudaki {METRIC_LABEL} / temiz sorgudaki {METRIC_LABEL}; yalnızca bozulmanın | |
| gerçekten değiştirdiği sorgular üzerinden, sıralamadaki veri setleri eşit ağırlıkla. Sıralama mobil sorgulardaki mutlak başarıya göredir. | |
| ### Değerlendirilemeyen modeller | |
| {failed}{pinned_md} | |
| ### Sınırlamalar | |
| - Üç veri seti makine çevirisi; sorgular resmî dil kullandığı için gündelik yazım bozulması az sorguyu etkiler. | |
| - Mutlak skorlar alt-örneklenmiş korpus nedeniyle TR-MTEB ile karşılaştırılamaz. | |
| - Bozulmalar kural tabanlıdır; gerçek kullanıcı hatalarının tüm çeşitliliğini kapsamaz. | |
| ### Yeniden üretim | |
| Benchmark parmak izi: `{META['fingerprint']}`. Kod ve sorgu bazında skorlar: | |
| [{META['dataset_repo']}](https://huggingface.co/datasets/{META['dataset_repo']}). | |
| Benchmark metinleri lisans nedeniyle dağıtılmaz; kod, kaynak veri setlerinden birebir aynısını üretir. | |
| """ | |
| HEAD = ('<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>' | |
| '<link href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600&family=Source+Serif+4:opsz,wght@8..60,500;8..60,600&display=swap" rel="stylesheet">') | |
| CSS = """ | |
| :root { --pu-ink:#1B2430; --pu-muted:#5F6B7A; --pu-line:rgba(95,107,122,.25); --pu-red:#C8102E; --pu-good:#2F7D6D; --pu-bad:#C8102E; } | |
| .gradio-container { max-width: 1280px !important; margin: 0 auto !important; | |
| font-family: 'Public Sans', system-ui, -apple-system, 'Segoe UI', sans-serif !important; } | |
| footer { display:none !important; } | |
| .pu-hero { padding: 30px 4px 10px; } | |
| .pu-top { display:flex; justify-content:space-between; align-items:flex-start; gap:20px; flex-wrap:wrap; } | |
| .pu-brand { display:flex; gap:14px; align-items:center; color:var(--pu-ink); } | |
| .dark .pu-brand { color:#E8ECF1; } | |
| .pu-compass { width:50px; height:50px; flex:none; } | |
| .pu-brand h1 { margin:0 !important; font:600 2.3rem/1.05 'Source Serif 4', Georgia, serif !important; letter-spacing:-.015em; color:inherit !important; } | |
| .pu-kicker { margin:4px 0 0 !important; font-size:.95rem; color:var(--pu-muted) !important; } | |
| .pu-links { display:flex; gap:8px; padding-top:6px; } | |
| .pu-links a { border:1px solid var(--pu-line); border-radius:6px; padding:6px 12px; font-size:.86rem; font-weight:500; | |
| text-decoration:none !important; color:inherit !important; white-space:nowrap; } | |
| .pu-links a:hover { border-color: var(--pu-muted); } | |
| .pu-lede { max-width:64ch; margin:18px 0 0 !important; font-size:1.02rem; line-height:1.6; color:var(--pu-muted) !important; } | |
| .pu-stats { display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:22px; margin-top:28px; } | |
| .pu-stat { border-top:2px solid var(--pu-ink); padding-top:14px; } | |
| .dark .pu-stat { border-top-color:#E8ECF1; } | |
| .pu-stat.pu-stat-neg { border-top-color: var(--pu-red); } | |
| .pu-stat-value { font:600 2.25rem/1.1 'Source Serif 4', Georgia, serif; color:var(--pu-ink) !important; font-variant-numeric: tabular-nums; } | |
| .dark .pu-stat-value { color:#E8ECF1 !important; } | |
| .pu-stat-neg .pu-stat-value { color: var(--pu-red) !important; } | |
| .pu-stat-name { font-size:1.45rem; letter-spacing:-.01em; } | |
| .pu-stat-label { margin-top:8px; font-size:.88rem; line-height:1.5; color:var(--pu-muted) !important; } | |
| .pu-stat-label b { color:inherit !important; font-weight:600; white-space:nowrap; } | |
| /* contain:inline-size → kutunun genisligi icerikten bagimsiz; tablo atalari genisletemez, kendi icinde kayar */ | |
| .pu-table-wrap { overflow-x:auto; width:100%; contain:inline-size; } | |
| .pu-table { width:100%; border-collapse:collapse; font-size:.9rem; border:0 !important; } | |
| .pu-table tr { border:0 !important; } /* Gradio her satira 4 yonlu kenarlik veriyor */ | |
| .pu-table th, .pu-table td { border-left:0 !important; border-right:0 !important; border-top:0 !important; } | |
| .pu-table th { text-align:left; font-weight:600; padding:8px 10px; border-bottom:1px solid var(--pu-line); white-space:nowrap; } | |
| .pu-table .pu-subhead th { font-weight:400; font-size:.78rem; color:var(--pu-muted); padding-top:0; text-align:center; } | |
| .pu-table td { padding:7px 9px; border-bottom:1px solid var(--pu-line) !important; vertical-align:middle; } | |
| .pu-num { text-align:right; font-variant-numeric: tabular-nums; } | |
| .pu-strong { font-weight:600; } | |
| .pu-rank { width:3em; color:var(--pu-muted); font-variant-numeric: tabular-nums; } | |
| .pu-band td:not(.pu-heat) { background: rgba(95,107,122,.06); } | |
| .pu-model { white-space:nowrap; } | |
| .pu-model a { color:inherit; text-decoration:none; font-weight:500; } | |
| .pu-meta { margin-top:2px; display:flex; gap:8px; align-items:center; } | |
| .pu-model a:hover { text-decoration:underline; } | |
| .pu-fam { font-size:.72rem; padding:1px 6px; border-radius:4px; border:1px solid var(--pu-line); color:var(--pu-muted); } | |
| .pu-fam-tür { border-color: rgba(200,16,46,.45); color: var(--pu-red); } | |
| .pu-params { font-size:.78rem; color:var(--pu-muted); } | |
| .pu-heat { background: rgba(200,16,46,var(--a)) !important; } | |
| .pu-na { color:var(--pu-muted); } | |
| .pu-note { color:var(--pu-muted); font-size:.85rem; margin-top:10px; } | |
| .pu-warn { border-left:3px solid var(--pu-red); padding:8px 12px; margin:0 0 14px !important; font-size:.9rem; background:rgba(200,16,46,.06); } | |
| .pu-pert { padding:12px 0; border-bottom:1px solid var(--pu-line); } | |
| .pu-pert-head { display:flex; gap:12px; align-items:baseline; flex-wrap:wrap; } | |
| .pu-pert-head span { color:var(--pu-muted); font-size:.85rem; } | |
| .pu-pert-text { margin:6px 0 4px; font-size:1.05rem; } | |
| .pu-pert-text mark { background: rgba(200,16,46,.16); color:inherit; border-radius:2px; padding:0 1px; } | |
| .pu-pert-text mark.pu-del { display:inline-block; width:2px; height:1.05em; padding:0; margin:0 1px; vertical-align:text-bottom; background: var(--pu-red); } | |
| .pu-pert-text em { color:var(--pu-muted); font-size:.85rem; } | |
| .pu-hint { font-size:.82rem; color:var(--pu-muted); } | |
| """ | |
| EXAMPLES = [["İstanbul'daki kargo şubesi nerede, ne zaman açılıyor?"], ["Kredi kartı borcumu nasıl yapılandırabilirim?"], | |
| ["Çocuğum ateşleniyor, ne yapacağım?"], ["Emeklilik fonu getirisi vergiye tabi mi?"], | |
| ["Şirketimin hisseleri neden düşüyor?"]] | |
| with gr.Blocks(title="Pusula — Türkçe retrieval dayanıklılık benchmark'ı", fill_width=True) as demo: | |
| gr.HTML(hero_html()) | |
| with gr.Tabs(): | |
| with gr.Tab("Sıralama"): | |
| with gr.Row(): | |
| view = gr.Radio(list(VIEWS), value="Mobil sorguda başarı", label="Sıralama ölçütü") | |
| ds = gr.Dropdown(DS_CHOICES, value=ALL, label="Veri seti") | |
| board = gr.HTML(board_html("Mobil sorguda başarı", ALL)) | |
| view.change(board_html, [view, ds], board) | |
| ds.change(board_html, [view, ds], board) | |
| with gr.Tab("Sorgunu boz"): | |
| seed = gr.State(0) | |
| with gr.Row(): | |
| q = gr.Textbox(label="Sorgu", value=EXAMPLES[0][0], lines=1, scale=5) | |
| again = gr.Button("Yeni rastgele sürüm", scale=1) | |
| gr.Examples(EXAMPLES, inputs=q, label="Örnekler") | |
| out = gr.HTML(perturb_html(EXAMPLES[0][0], 0)) | |
| q.change(perturb_html, [q, seed], out) | |
| again.click(next_seed, seed, seed).then(perturb_html, [q, seed], out) | |
| with gr.Tab("Yöntem"): | |
| gr.Markdown(method_md()) | |
| demo.launch(css=CSS, head=HEAD) | |