|
365 | 365 | " print(f\" Schwierigkeit: [{bar}] {normalized:.0%}\")\n", |
366 | 366 | " print(f\" Verteilung: {dict(counter.most_common(5))}\")\n", |
367 | 367 | "\n", |
| 368 | + "n_single = sum(1 for c in groups.values() if c == 1)\n", |
| 369 | + "n_groups = len(groups)\n", |
| 370 | + "top_cat = cats.most_common(1)[0]\n", |
368 | 371 | "print(f\"\\n💡 Entropy misst die 'Zufälligkeit' der Labels.\")\n", |
369 | | - "print(f\" Category hat nur 3 Werte, 66% davon 'Service Request' → leicht zu raten.\")\n", |
370 | | - "print(f\" Assigned Group hat 21 Werte, 12 davon mit nur 1 Beispiel → fast unmöglich.\")\n", |
| 372 | + "print(f\" Category hat nur 3 Werte, {top_cat[1]}/{sum(cats.values())} davon '{top_cat[0]}' → relativ leicht.\")\n", |
| 373 | + "print(f\" Assigned Group hat {n_groups} Werte, {n_single} davon mit nur 1 Beispiel → Schwierigkeit {entropy(groups)/max_entropy(n_groups):.0%}.\")\n", |
371 | 374 | "\n", |
372 | 375 | "# ━━━ 3. Self-Consistency: Wie stabil sind die Vorhersagen? ━━━\n", |
373 | 376 | "print(f\"\\n{'━'*60}\")\n", |
|
421 | 424 | "\n", |
422 | 425 | "| Beobachtung | Was es bedeutet |\n", |
423 | 426 | "|---|---|\n", |
424 | | - "| **12 von 21 Gruppen haben nur 1 Trainings-Beispiel** | Das Modell kann keine Zuordnungsregel lernen — es hat kein zweites Beispiel zum Vergleichen |\n", |
425 | | - "| **Category-Entropy ist niedrig** (66% = Service Request) | Einfach zu raten, aber nicht informativ |\n", |
426 | | - "| **Group-Entropy ist fast maximal** | 21 Klassen, extrem ungleich verteilt — das härteste Feld |\n", |
427 | | - "| **Self-Consistency ist niedrig bei schweren Tickets** | Das Modell gibt bei mehrfachem Fragen verschiedene Antworten — es \"rät\" |\n", |
| 427 | + "| **Die Daten sind relativ gut verteilt** (18 von 20 Gruppen haben 4 Beispiele) | Kein 1-Beispiel-Problem — aber trotzdem nicht genug für 20 Klassen |\n", |
| 428 | + "| **Category-Entropy bei 76%** (58% = Service Request, 36% = Failure) | Ungleich verteilt, aber nicht trivial — \"Event NO Customer Impact\" hat nur 4 Beispiele |\n", |
| 429 | + "| **Priority-Entropy bei 71%** (69% = Standard) | \"Standard\" dominiert — \"High\" hat nur 5 von 77 Beispielen |\n", |
| 430 | + "| **Group-Entropy bei 98%** — fast maximal | 20 Klassen, fast gleichverteilt — das härteste Feld, weil das Modell zwischen 20 ähnlichen Optionen wählen muss |\n", |
| 431 | + "| **Self-Consistency ist hoch** (3/3 Übereinstimmung) | Das Modell ist sich **sicher** — aber sicher **falsch**: es erfindet eigene Kategorien wie \"Access\" oder \"Hardware\" statt die gültigen Werte zu nutzen |\n", |
| 432 | + "\n", |
| 433 | + "**Das zentrale Problem:** Das Modell antwortet konsistent, aber mit **eigenen Kategorien** (\"Access\", \"Hardware\", \"Workplace Support / ...\") statt den vorgegebenen Werten (\"Service Request\", \"Failure\", \"SDE - Service Desk\", ...). Es hat eine klare interne Logik — aber die stimmt nicht mit den Domain-spezifischen Labels überein.\n", |
428 | 434 | "\n", |
429 | 435 | "**Techniken aus der Forschung:**\n", |
430 | 436 | "\n", |
|
433 | 439 | "3. **Calibration** — Messen ob ein Modell das 90% Confidence zuweist auch wirklich zu 90% richtig liegt. LLMs sind typischerweise *overconfident*.\n", |
434 | 440 | "4. **SelfCheckGPT** — Mehrere Antworten generieren und prüfen ob sie sich widersprechen. Widersprüche = Halluzination/Unsicherheit.\n", |
435 | 441 | "\n", |
436 | | - "> **Die Kernfrage ist immer:** Wann kann ich dem Modell vertrauen? Die Antwort: Wenn es bei mehrfachem Fragen die gleiche Antwort gibt, und wenn es genug Trainingsbeispiele für diese Klasse gibt." |
437 | | - ] |
438 | | - }, |
439 | | - { |
440 | | - "cell_type": "markdown", |
441 | | - "metadata": {}, |
442 | | - "source": [ |
443 | | - "## 🎯 Beliebige Aufgabe optimieren\n", |
444 | | - "\n", |
445 | | - "Wähl eine Aufgabe aus der Liste unten und sieh dir an, was der Optimizer daraus macht. Jede Aufgabe hat eine andere Schwierigkeit — und der Optimizer zeigt dir genau, was er am Prompt ändert.\n", |
446 | | - "\n", |
447 | | - "**So geht's:** Ändere den `TASK`-String und führe die Zelle aus. Der Prompt-Diff zeigt dir den Unterschied." |
448 | | - ] |
449 | | - }, |
450 | | - { |
451 | | - "cell_type": "code", |
452 | | - "execution_count": null, |
453 | | - "metadata": {}, |
454 | | - "outputs": [], |
455 | | - "source": [ |
456 | | - "# ━━━ Verfügbare Aufgaben ━━━\n", |
457 | | - "from dspy_tasks.tasks import list_tasks\n", |
458 | | - "from dspy_tasks.actions import run_with_prompt\n", |
459 | | - "\n", |
460 | | - "print(\"Alle verfügbaren Aufgaben:\\n\")\n", |
461 | | - "for tier in [1, 2, 3, 4]:\n", |
462 | | - " tasks_in_tier = [t for t in list_tasks() if t.tier == tier]\n", |
463 | | - " tier_labels = {1: \"Basics\", 2: \"Reasoning\", 3: \"Composition\", 4: \"Agentic\"}\n", |
464 | | - " print(f\" Tier {tier} — {tier_labels[tier]}:\")\n", |
465 | | - " for t in tasks_in_tier:\n", |
466 | | - " examples = t.load_examples()[:1]\n", |
467 | | - " sample_input = \"\"\n", |
468 | | - " if examples:\n", |
469 | | - " sample_input = \" | \".join(f\"{k}={str(v)[:40]}\" for k, v in examples[0].inputs().items())\n", |
470 | | - " print(f\" • {t.id:25s} {t.name}\")\n", |
471 | | - " print(f\" {t.description[:80]}\")\n", |
472 | | - " if sample_input:\n", |
473 | | - " print(f\" Beispiel: {sample_input[:100]}\")\n", |
474 | | - " print()\n", |
475 | | - "\n", |
476 | | - "# ━━━ Aufgabe 1: Sentiment (BootstrapFewShot) ━━━\n", |
477 | | - "SENT_PROMPT = \"Bestimme ob diese Bewertung positiv, negativ oder neutral ist.\"\n", |
478 | | - "print(\"━\" * 60)\n", |
479 | | - "print(\"📋 Sentiment Classification — BootstrapFewShot\\n\")\n", |
480 | | - "\n", |
481 | | - "manual_sent = run_with_prompt(\"sentiment\", SENT_PROMPT, max_eval=8)\n", |
482 | | - "display_score(\"Sentiment: Manueller Prompt\", manual_sent.score)\n", |
483 | | - "display_results_table(manual_sent.individual_scores)\n", |
484 | | - "\n", |
485 | | - "print(\"\\n⏳ Optimiere mit BootstrapFewShot (basierend auf manuellem Prompt)...\")\n", |
486 | | - "r_sent = run_optimization(\"sentiment\", \"BootstrapFewShot\", max_eval=8, instructions=SENT_PROMPT)\n", |
487 | | - "display_score(\"Sentiment: Nach BootstrapFewShot\", r_sent.optimized_score)\n", |
488 | | - "if r_sent.optimized_individual_scores:\n", |
489 | | - " display_results_table(r_sent.optimized_individual_scores)\n", |
490 | | - "display_improvement(manual_sent.score, r_sent.optimized_score)\n", |
491 | | - "\n", |
492 | | - "# ━━━ Aufgabe 2: Multi-Hop QA (BootstrapFewShot → MIPROv2) ━━━\n", |
493 | | - "MHQA_PROMPT = \"Beantworte die Frage basierend auf dem Kontext. Denke Schritt für Schritt.\"\n", |
494 | | - "print(f\"\\n{'━'*60}\")\n", |
495 | | - "print(\"📋 Multi-Hop QA — BootstrapFewShot → MIPROv2\\n\")\n", |
496 | | - "\n", |
497 | | - "manual_mhqa = run_with_prompt(\"multihop_qa\", MHQA_PROMPT, max_eval=8)\n", |
498 | | - "display_score(\"Multi-Hop QA: Manueller Prompt\", manual_mhqa.score)\n", |
499 | | - "display_results_table(manual_mhqa.individual_scores)\n", |
500 | | - "\n", |
501 | | - "# Erst BootstrapFewShot\n", |
502 | | - "print(\"\\n⏳ Schritt 1: BootstrapFewShot...\")\n", |
503 | | - "r_mhqa_bs = run_optimization(\"multihop_qa\", \"BootstrapFewShot\", max_eval=8, instructions=MHQA_PROMPT)\n", |
504 | | - "display_score(\"Multi-Hop QA: Nach BootstrapFewShot\", r_mhqa_bs.optimized_score)\n", |
505 | | - "\n", |
506 | | - "# Dann MIPROv2 auf dem besseren Ergebnis\n", |
507 | | - "best_mhqa_score = max(manual_mhqa.score, r_mhqa_bs.optimized_score)\n", |
508 | | - "best_mhqa_prompt = MHQA_PROMPT if manual_mhqa.score >= r_mhqa_bs.optimized_score else r_mhqa_bs.prompt_after\n", |
509 | | - "best_mhqa_label = \"Manuell\" if manual_mhqa.score >= r_mhqa_bs.optimized_score else \"BootstrapFewShot\"\n", |
510 | | - "print(f\"\\n⏳ Schritt 2: MIPROv2 (basierend auf {best_mhqa_label}: {best_mhqa_score:.0%})...\")\n", |
511 | | - "r_mhqa = run_optimization(\"multihop_qa\", \"MIPROv2\", max_eval=8, instructions=best_mhqa_prompt)\n", |
512 | | - "display_score(\"Multi-Hop QA: Nach MIPROv2\", r_mhqa.optimized_score)\n", |
513 | | - "if r_mhqa.optimized_individual_scores:\n", |
514 | | - " display_results_table(r_mhqa.optimized_individual_scores)\n", |
515 | | - "\n", |
516 | | - "print(f\"\\n📊 Manuell {manual_mhqa.score:.0%} → BootstrapFewShot {r_mhqa_bs.optimized_score:.0%} → MIPROv2 {r_mhqa.optimized_score:.0%}\")" |
| 442 | + "> **Die Kernfrage ist immer:** Wann kann ich dem Modell vertrauen? Die Antwort: Hohe Self-Consistency allein reicht nicht — das Modell kann konsistent falsch liegen. Es braucht **Trainingsdaten mit den richtigen Labels**, damit der Optimizer dem Modell beibringen kann, welche Domain-spezifischen Werte erwartet werden." |
517 | 443 | ] |
518 | 444 | }, |
519 | 445 | { |
|
541 | 467 | "source": [ |
542 | 468 | "## ⏭️ Weiter geht's!\n", |
543 | 469 | "\n", |
544 | | - "Der Optimizer hat den Prompt verbessert — automatisch, messbar, reproduzierbar. Aber was passiert, wenn du **deine eigenen, echten Daten** benutzt?\n", |
| 470 | + "Du hast gesehen: der Optimizer findet bessere Prompts als du — automatisch, messbar, reproduzierbar.\n", |
545 | 471 | "\n", |
546 | | - "Im nächsten Notebook nehmen wir die echten Ticket-Daten aus dem Projekt und zeigen: **Deine Daten sind dein Burggraben.** Ein generisches Modell + deine Domain-Daten + Tuning = etwas, das kein Konkurrent kopieren kann.\n", |
| 472 | + "Im nächsten Notebook testen wir das auf **verschiedenen Aufgaben** und mit **deinen echten Daten**. Dort zeigt sich der wahre Wert: **Domain-Tuning = Burggraben.**\n", |
547 | 473 | "\n", |
548 | 474 | "👉 **[Weiter zu Notebook: Domain-Tuning →](03_domain_tuning.ipynb)**\n" |
549 | 475 | ] |
|
0 commit comments