Asosiy qismga o'tish

Round-trip check (qaytish tekshiruvi): matn planni bildiradimi

14-bob 6 kod bloki 1852 so'z

Muammo

12-bobda o'qituvchi planni so'rov matniga aylantirdi. 13-bobda plan to'g'ri ekanini ko'rdik: uni kod yasaydi va DuckDB tekshiradi. Lekin matn-chi? O'qituvchi planni noto'g'ri tushunishi, biror filtrni tashlab ketishi yoki o'zidan qo'shishi mumkin. Unda training ma'lumotida "so'rov A → plan B" juftligi paydo bo'ladi — va model A ni ko'rganda B ni yozishni o'rganadi. Bu eng xavfli xato: modelga yolg'on o'rgatiladi.

Uni ushlash uchun Tezda round-trip check (qaytish tekshiruvi) bor. G'oyasi oddiy:

  1. O'qituvchi plandan matn yozdi.
  2. Endi matnni boshqa "o'quvchi"ga beramiz — u planni ko'rmaydi, faqat matnni va jadvalni. U matndan o'z planini yozadi.
  3. Ikkala plan DuckDB da bajariladi. Natijalar bir xil bo'lsa — matn planni to'g'ri ifodalaydi. Farq qilsa — juftlik tashlanadi.

Matnni tekshiruvchi — yana Gemma, lekin endi boshqa rolda: Tez modeli o'rnida, Tez promptini olib, foydalanuvchi so'rovini planga aylantiradi.

Tekshiruv kodi

training/roundtrip.py dagi verify funksiyasi:

python4 qator
import inspect
from training import roundtrip

print(inspect.getsource(roundtrip.verify))
Chiqish
def verify(con, table, plan, answer, settings, history=()):
    if not isinstance(answer, dict) or not ({"ask", "steps"} & answer.keys()):
        return False, "invalid answer"
    if "ask" in answer:
        return False, "asked instead of acting"
    if plan.get("on") == "table":
        answer = {**answer, "on": "table"}
    try:
        result = run_plan(con, table, effective_steps(history, answer), settings)
    except (ToolError, KeyError, TypeError) as error:
        return False, f"plan failed: {error}"
    expected_ops, answer_ops = operators(plan["steps"]), operators(answer["steps"])
    if any(key in answer_ops and answer_ops[key] != op for key, op in expected_ops.items()):
        return False, "different operator"
    expected = run_plan(con, table, effective_steps(history, plan), settings)
    return (True, "same result") if same_result(result, expected, settings) else (False, "different result")

To'rtta rad etish sababi bor:

  • invalid answer / asked instead of acting — tekshiruvchi plan o'rniga savol berdi. Demak matn noaniq: aniq so'rovdan plan chiqishi kerak edi.
  • plan failed — tekshiruvchining plani bajarilmadi.
  • different operator — natija tasodifan bir xil bo'lsa ham, bitta ustun va bitta qiymatga boshqa amal qo'llangan (masalan, = o'rniga contains). Bu ataylab qat'iy qoida: "Sergeli" bilan contains va = bu jadvalda bir xil natija berishi mumkin, lekin ular boshqa ma'no.
  • different result — planlar boshqa natija berdi.

Haqiqiy natijalar

Saqlangan partiyalardan sakkizta qismni olamiz — ularning hammasida jarayon bir xil: o'qituvchi matnlari, birinchi tekshiruv (Gemma), rad etilganlar uchun ikkinchi tekshiruv. Fayllar o'zgartirilmasdan o'qiladi:

python25 qator
import json
import pandas as pd

QISMLAR = [("batch10", "short45"), ("batch12", "conditions"), ("batch12", "newtables"), ("batch12", "ranges"),
           ("batch12", "totals"), ("batch13", "newshort"), ("batch13", "newshort2"), ("batch13", "toolshort")]

def oqi(fayl):
    return [json.loads(q) for q in open(fayl, encoding="utf-8")]

jadval = []
for partiya, qism in QISMLAR:
    papka = f"/mnt/d/Tez_spikes/{partiya}"
    jadval.append({
        "qism": f"{partiya}/{qism}",
        "matnlar": len(oqi(f"{papka}/phrases_{qism}.jsonl")),
        "birinchida_otdi": len(oqi(f"{papka}/kept_{qism}.jsonl")),
        "qutqarildi": len(oqi(f"{papka}/rescued_{qism}.jsonl")),
        "tashlandi": len(oqi(f"{papka}/final_rejected_{qism}.jsonl")),
    })
jadval = pd.DataFrame(jadval)
show(jadval)
jami = jadval.sum(numeric_only=True)
print(f"jami matn: {jami['matnlar']:,}")
print(f"birinchi tekshiruvdan o'tdi: {jami['birinchida_otdi'] / jami['matnlar']:.0%}")
print(f"oxirida tashlandi: {jami['tashlandi'] / jami['matnlar']:.0%}")
Chiqish
jami matn: 6,040
birinchi tekshiruvdan o'tdi: 77%
oxirida tashlandi: 13%
qismmatnlarbirinchida_otdiqutqarilditashlandi
batch10/short451 358909142307
batch12/conditions4493604247
batch12/newtables1 5111 216179116
batch12/ranges4003781111
batch12/totals5334414151
batch13/newshort1 018762136120
batch13/newshort23232403845
batch13/toolshort4483272596

Har to'rt-besh matndan biri birinchi tekshiruvdan o'tmadi. Eng ko'p rad etilgan qismlar — "short45" va "toolshort", ya'ni ataylab qisqa yozilgan so'rovlar: qisqa matnda ma'no oson yo'qoladi.

Rad etish sabablari:

python9 qator
from collections import Counter

birinchi, oxirgi = Counter(), Counter()
for partiya, qism in QISMLAR:
    papka = f"/mnt/d/Tez_spikes/{partiya}"
    birinchi.update(y["check"].split(":")[0] for y in oqi(f"{papka}/rejected_{qism}.jsonl"))
    oxirgi.update(y["check"].split(":")[0] for y in oqi(f"{papka}/final_rejected_{qism}.jsonl"))
print("birinchi tekshiruvda:", dict(birinchi))
print("oxirida tashlanganlar:", dict(oxirgi))
Chiqish
birinchi tekshiruvda: {'different result': 1148, 'plan failed': 87, 'different operator': 167, 'asked instead of acting': 5}
oxirida tashlanganlar: {'different result': 697, 'asked instead of acting': 12, 'plan failed': 17, 'different operator': 67}

Ko'pchilik — "different result": matn boshqa planga olib keldi.

Tashlangan matnlarni o'qiymiz

Raqam yetarli emas — tashlangan matnlarning o'zini ko'rish kerak:

python6 qator
for y in oqi("/mnt/d/Tez_spikes/batch12/final_rejected_conditions.jsonl")[:3]:
    print(f"[{y['language']}] {y['text']}")
    print(f"   reja:        {json.dumps(y['plan']['steps'], ensure_ascii=False)[:150]}")
    print(f"   tekshiruvchi: {y['checker_output'][:150]}")
    print(f"   sabab: {y['check']}")
    print()
Chiqish
[en] keep rows where amount is greater than 0
   reja:        [{"args": {"column": "summa_uzs", "op": ">", "value": 0}, "tool": "filter"}]
   tekshiruvchi: {"steps":[{"tool":"filter","args":{"column":"miqdor","op":">","value":0}}]}
   sabab: different result

[uz_latin] Faqat 'Sergeli' bor do'konlarni qoldir. Har bir do'kon bo'yicha miqdorning o'rtachasini hisobla va eng yuqori 3 tasini ko'rsat.
   reja:        [{"args": {"column": "dokon", "op": "contains", "value": "Sergeli"}, "tool": "filter"}, {"args": {"by": "miqdor", "descending": true, "group": {"agg":
   tekshiruvchi: {"steps":[{"tool":"filter","args":{"column":"dokon","op":"=","value":"Sergeli"}},{"tool":"top_n","args":{"by":"miqdor","n":3,"descending":true,"group"
   sabab: different operator

[uz_cyrillic] Фақат 'Sergeli' бор дўконларни қолдир. Ҳар бир дўкон бўйича миқдорнинг ўртачасини ҳисобла ва энг юқори 3 тасини кўрсат.
   reja:        [{"args": {"column": "dokon", "op": "contains", "value": "Sergeli"}, "tool": "filter"}, {"args": {"by": "miqdor", "descending": true, "group": {"agg":
   tekshiruvchi: {"steps":[{"tool":"filter","args":{"column":"dokon","op":"=","value":"Sergeli"}},{"tool":"top_n","args":{"by":"miqdor","n":3,"descending":true,"group"
   sabab: different operator

Birinchi misol juda ibratli: "keep rows where amount is greater than 0". Planda — summa_uzs. Tekshiruvchi esa "amount" ni "miqdor" (dona soni) deb tushundi. Inglizcha "amount" ikkala ma'noni beradi — matn haqiqatan noaniq, va tekshiruv uni to'g'ri tashladi.

Ikkinchi va uchinchi: matn va plan ma'nosi bir xil ko'rinadi, lekin planda contains, tekshiruvchida =. "different operator" qoidasi ularni tashladi. Bu qoida bu yerda ortiqcha qattiq bo'lishi mumkin — lekin biz bir nechta yaxshi juftlikni yo'qotishni yomon juftlikni o'tkazib yuborishdan afzal ko'rdik.

Endi 12-bobdagi misolni eslang: o'sha planning o'zbekcha matni — "amount dan katta 0" — o'tgan edi. Bir xil so'z, bir xil plan: inglizcha matn tashlandi, o'zbekcha matn o'tdi. Tekshiruvchi ikkinchi holatda to'g'ri ustunni tanlagan. Bu tekshiruvning tabiati: u har bir matnni alohida, bitta tekshiruvchining bitta javobi bilan baholaydi. U ma'noni tekshiradi — til sifatini emas, va bitta urinish bilan.

Qutqarish

Ikkinchi tekshiruv — rescue (qutqarish). Birinchi tekshiruvda rad etilgan matnlar boshqa tekshiruvchiga beriladi, va u ham shu planni tiklasa — matn qaytariladi. Ikkinchi tekshiruvchi — o'sha paytdagi eng yaxshi Tez modeli, tez8.

Nega? Chunki Gemma ham xato qiladi. Ba'zi matnlar to'g'ri, lekin Gemma ularni noto'g'ri o'qigan. Qutqarilgan misollar:

python7 qator
for y in oqi("/mnt/d/Tez_spikes/batch12/rescued_conditions.jsonl")[:3]:
    print(f"[{y['language']}] {y['text']}")
    print(f"   reja: {json.dumps(y['plan']['steps'], ensure_ascii=False)[:140]}")
    print()
qutqarilgan = jami["qutqarildi"]
qabul = jami["birinchida_otdi"] + qutqarilgan
print(f"qabul qilingan juftliklarning qutqarilgan ulushi: {qutqarilgan / qabul:.0%}")
Chiqish
[uz_cyrillic] Фойда бўйича 0 дан катта бўлган қаторларни олиб ташла.
   reja: [{"args": {"column": "foyda_uzs", "op": ">", "value": 0}, "tool": "remove_rows"}]

[uz_latin] Iltimos, 'Sardor Tursunov' so'zini o'z ichiga oluvchi sotuvchi qatorlarini olib tashlang, qolgan barcha qatorlar qolsin.
   reja: [{"args": {"column": "sotuvchi", "op": "contains", "value": "Tursunov"}, "tool": "remove_rows"}]

[uz_latin] Faqat miqdor 0 dan kichik bo'lgan qatorlarni va mahsulot hamda do'kon ustunlariga ega natijani jadval ko'rinishida bera olasizmi?
   reja: [{"args": {"column": "miqdor", "op": "<", "value": 0}, "tool": "filter"}, {"args": {"columns": ["mahsulot", "dokon"]}, "tool": "select_colum

qabul qilingan juftliklarning qutqarilgan ulushi: 12%

Qutqarishning yashirin narxi

Bu yerda bitta nozik muammo bor, va uni ochiq aytish kerak. Qutqaruvchi — tez8, ya'ni o'quvchining o'zi. U faqat o'zi allaqachon to'g'ri tushunadigan matnlarni qutqaradi. Demak qutqarilgan juftliklar modelga yangi narsa o'rgatmaydi — ular uning hozirgi bilimini takrorlaydi. Model noto'g'ri tushunadigan, lekin to'g'ri matnlar esa — aynan eng foydali o'quv materiali — ikkala tekshiruvdan ham o'tmaydi va tashlanadi.

Bu xato emas, lekin cheklov: tekshiruv tizimi training ma'lumotini modelning hozirgi imkoniyatlariga qarab biroz "siljitadi". Bu ulushni o'lchab bordik (yuqorida), lekin uning modelga ta'sirini alohida o'lchamaganmiz.

Tillar bo'yicha

Oxirgi savol: tekshiruv biror tilni boshqasidan ko'proq tashlaydimi? Har bir til uchun qabul qilingan ulush:

python10 qator
tillar = {}
for partiya, qism in QISMLAR:
    papka = f"/mnt/d/Tez_spikes/{partiya}"
    for y in oqi(f"{papka}/phrases_{qism}.jsonl"):
        tillar.setdefault(y["language"], [0, 0])[0] += 1
    for fayl in (f"{papka}/kept_{qism}.jsonl", f"{papka}/rescued_{qism}.jsonl"):
        for y in oqi(fayl):
            tillar[y["language"]][1] += 1
for til, (hammasi, qabul_) in tillar.items():
    print(f"{til:12s} {qabul_:,} / {hammasi:,} = {qabul_ / hammasi:.0%}")
Chiqish
uz_latin     990 / 1,145 = 86%
uz_cyrillic  982 / 1,145 = 86%
ru           1,126 / 1,325 = 85%
en           1,156 / 1,274 = 91%
mixed        993 / 1,151 = 86%

Ulushlar yaqin. Inglizcha biroz yuqori. Ehtimol sababi — o'qituvchi ham, tekshiruvchi ham ingliz tilida eng ko'p o'rgatilgan modellar; buni alohida tekshirmaganmiz. O'zbek kirill lotin bilan deyarli bir xil — ajablanarli emas: kirill matni lotin matnidan kod bilan o'girilgan (12-bob), ma'nosi bir xil.

Xulosa

  • Qaytish tekshiruvi: boshqa model matndan planni qayta tiklaydi; natija bir xil bo'lmasa, juftlik tashlanadi. Bu trainingga yolg'on juftlik kirishining asosiy to'sig'i.
  • Saqlangan sakkizta qismda har to'rt-besh matndan biri birinchi tekshiruvdan o'tmagan; asosiy sabab — boshqa natija.
  • Tekshiruv ma'noni tekshiradi, til sifatini emas, va har matnga bitta urinish beradi.
  • Qutqarish ba'zi to'g'ri matnlarni qaytaradi, lekin qutqaruvchi — o'quvchining o'zi; bu ma'lumotni model allaqachon biladigan narsaga siljitadi.
  • Tillar bo'yicha qabul ulushi yaqin; ingliz tili biroz yuqori.

Keyingi bobda tekshirilgan juftliklar training fayliga qanday aylanishini ko'ramiz: eksport, sarlavhalarni qayta nomlash va shovqin.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar