Text Generation
Transformers
Safetensors
qwen2
code
code-analysis
qwen
fine-tuned
conversational
text-generation-inference
Instructions to use Vilyam888/Code_analyze.1.0 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Vilyam888/Code_analyze.1.0 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="Vilyam888/Code_analyze.1.0") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("Vilyam888/Code_analyze.1.0") model = AutoModelForCausalLM.from_pretrained("Vilyam888/Code_analyze.1.0", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Inference
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Vilyam888/Code_analyze.1.0 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Vilyam888/Code_analyze.1.0" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Vilyam888/Code_analyze.1.0", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/Vilyam888/Code_analyze.1.0
- SGLang
How to use Vilyam888/Code_analyze.1.0 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "Vilyam888/Code_analyze.1.0" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Vilyam888/Code_analyze.1.0", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "Vilyam888/Code_analyze.1.0" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Vilyam888/Code_analyze.1.0", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use Vilyam888/Code_analyze.1.0 with Docker Model Runner:
docker model run hf.co/Vilyam888/Code_analyze.1.0
Upload folder using huggingface_hub
Browse files- metrics/code_analyze_1_0/01_training_perplexity.py +73 -0
- metrics/code_analyze_1_0/02_json_validity.py +29 -0
- metrics/code_analyze_1_0/03_bleu_rouge.py +86 -0
- metrics/code_analyze_1_0/04_code_metrics.py +28 -0
- metrics/code_analyze_1_0/05_human_evaluation_template.py +81 -0
- metrics/code_analyze_1_0/06_merge_for_appendix.py +98 -0
- metrics/code_analyze_1_0/config.py +55 -0
- metrics/code_analyze_1_0/init_metrics.py +116 -0
- metrics/code_analyze_1_0/report_io.py +23 -0
- metrics/code_analyze_1_0/reports/01_training_perplexity.json +14 -0
- metrics/code_analyze_1_0/reports/02_json_validity.json +30 -0
- metrics/code_analyze_1_0/reports/03_bleu_rouge.json +18 -0
- metrics/code_analyze_1_0/reports/04_code_metrics.json +7 -0
- metrics/code_analyze_1_0/reports/05_human_evaluation.json +22 -0
- metrics/code_analyze_1_0/reports/appendix_full.json +30 -0
- metrics/code_analyze_1_0/reports/appendix_full.txt +31 -0
- metrics/code_analyze_1_0/shared.py +134 -0
metrics/code_analyze_1_0/01_training_perplexity.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
import math
|
| 6 |
+
import sys
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
_METRICS_DIR = Path(__file__).resolve().parent
|
| 10 |
+
if str(_METRICS_DIR) not in sys.path:
|
| 11 |
+
sys.path.insert(0, str(_METRICS_DIR))
|
| 12 |
+
|
| 13 |
+
from config import FILE_TRAINING, MODEL_ID, TRAINER_STATE, VAL_FILE # noqa: E402
|
| 14 |
+
from report_io import metrics_path, write_report # noqa: E402
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def from_trainer_state(state: dict) -> dict:
|
| 18 |
+
train_loss = eval_loss = eval_acc = None
|
| 19 |
+
for entry in reversed(state.get("log_history", [])):
|
| 20 |
+
if "eval_loss" in entry:
|
| 21 |
+
eval_loss = entry["eval_loss"]
|
| 22 |
+
eval_acc = entry.get("eval_mean_token_accuracy")
|
| 23 |
+
break
|
| 24 |
+
for entry in reversed(state.get("log_history", [])):
|
| 25 |
+
if "loss" in entry and "eval_loss" not in entry:
|
| 26 |
+
train_loss = entry["loss"]
|
| 27 |
+
break
|
| 28 |
+
return {
|
| 29 |
+
"train_loss_final": train_loss,
|
| 30 |
+
"eval_loss_final": eval_loss,
|
| 31 |
+
"eval_mean_token_accuracy": eval_acc,
|
| 32 |
+
"perplexity_validation": round(math.exp(eval_loss), 4) if eval_loss else None,
|
| 33 |
+
"num_train_epochs": state.get("num_train_epochs"),
|
| 34 |
+
"global_step": state.get("global_step"),
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def parse_args() -> argparse.Namespace:
|
| 39 |
+
parser = argparse.ArgumentParser(description=f"Training metrics for {MODEL_ID}")
|
| 40 |
+
parser.add_argument("--trainer-state", type=Path, default=TRAINER_STATE)
|
| 41 |
+
parser.add_argument("--output", type=Path, default=None)
|
| 42 |
+
return parser.parse_args()
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def main() -> None:
|
| 46 |
+
args = parse_args()
|
| 47 |
+
metrics = {
|
| 48 |
+
"train_loss_final": None,
|
| 49 |
+
"eval_loss_final": None,
|
| 50 |
+
"eval_mean_token_accuracy": None,
|
| 51 |
+
"perplexity_validation": None,
|
| 52 |
+
"num_train_epochs": 2,
|
| 53 |
+
"global_step": 222,
|
| 54 |
+
}
|
| 55 |
+
source = "default_placeholder"
|
| 56 |
+
|
| 57 |
+
if args.trainer_state.exists():
|
| 58 |
+
state = json.loads(args.trainer_state.read_text(encoding="utf-8"))
|
| 59 |
+
metrics = from_trainer_state(state)
|
| 60 |
+
source = str(args.trainer_state)
|
| 61 |
+
report = {
|
| 62 |
+
"metric_group": "training_perplexity",
|
| 63 |
+
"model": MODEL_ID,
|
| 64 |
+
"source": source,
|
| 65 |
+
"validation_file": str(VAL_FILE) if VAL_FILE.exists() else None,
|
| 66 |
+
"metrics": metrics,
|
| 67 |
+
}
|
| 68 |
+
write_report(args.output or metrics_path(FILE_TRAINING), report)
|
| 69 |
+
print(json.dumps(metrics, ensure_ascii=False, indent=2))
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
if __name__ == "__main__":
|
| 73 |
+
main()
|
metrics/code_analyze_1_0/02_json_validity.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import subprocess
|
| 5 |
+
import sys
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
_ROOT = Path(__file__).resolve().parents[3]
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def parse_args() -> argparse.Namespace:
|
| 12 |
+
parser = argparse.ArgumentParser(description="Run Code_analyze JSON validity evaluation")
|
| 13 |
+
parser.add_argument("--limit", type=int, default=None)
|
| 14 |
+
parser.add_argument("--use-lora", action="store_true")
|
| 15 |
+
return parser.parse_args()
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def main() -> None:
|
| 19 |
+
args = parse_args()
|
| 20 |
+
cmd = [sys.executable, str(_ROOT / "scripts" / "evaluate_code_analyze.py")]
|
| 21 |
+
if args.limit:
|
| 22 |
+
cmd.extend(["--limit", str(args.limit)])
|
| 23 |
+
if args.use_lora:
|
| 24 |
+
cmd.append("--use-lora")
|
| 25 |
+
subprocess.run(cmd, check=True, cwd=_ROOT)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
if __name__ == "__main__":
|
| 29 |
+
main()
|
metrics/code_analyze_1_0/03_bleu_rouge.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
import sys
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
_METRICS_DIR = Path(__file__).resolve().parent
|
| 9 |
+
if str(_METRICS_DIR) not in sys.path:
|
| 10 |
+
sys.path.insert(0, str(_METRICS_DIR))
|
| 11 |
+
|
| 12 |
+
from config import ( # noqa: E402
|
| 13 |
+
EVAL_FILE,
|
| 14 |
+
FILE_BLEU_ROUGE,
|
| 15 |
+
FILE_GENERATION_REPORT,
|
| 16 |
+
MODEL_ID,
|
| 17 |
+
TEXT_FIELDS_FOR_BLEU,
|
| 18 |
+
)
|
| 19 |
+
from report_io import metrics_path, write_report # noqa: E402
|
| 20 |
+
from shared import bleu4, json_text_fields, load_jsonl, rouge_l_f1, rouge_n_recall # noqa: E402
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def parse_args() -> argparse.Namespace:
|
| 24 |
+
parser = argparse.ArgumentParser(description=f"BLEU/ROUGE for {MODEL_ID}")
|
| 25 |
+
parser.add_argument(
|
| 26 |
+
"--from-generation-report",
|
| 27 |
+
type=Path,
|
| 28 |
+
default=metrics_path(FILE_GENERATION_REPORT),
|
| 29 |
+
)
|
| 30 |
+
return parser.parse_args()
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def main() -> None:
|
| 34 |
+
args = parse_args()
|
| 35 |
+
gen_path = args.from_generation_report
|
| 36 |
+
if not gen_path.exists():
|
| 37 |
+
raise FileNotFoundError(f"Run 02_json_validity.py first. Missing: {gen_path}")
|
| 38 |
+
|
| 39 |
+
references = load_jsonl(EVAL_FILE)
|
| 40 |
+
gen_report = json.loads(gen_path.read_text(encoding="utf-8"))
|
| 41 |
+
if gen_report.get("model") != MODEL_ID:
|
| 42 |
+
raise ValueError(f"Wrong model in report: {gen_report.get('model')}")
|
| 43 |
+
|
| 44 |
+
bleu_scores: list[float] = []
|
| 45 |
+
bleu_summary: list[float] = []
|
| 46 |
+
rouge1_scores: list[float] = []
|
| 47 |
+
rouge2_scores: list[float] = []
|
| 48 |
+
rougeL_scores: list[float] = []
|
| 49 |
+
|
| 50 |
+
for ref_row, row in zip(references, gen_report.get("results", [])):
|
| 51 |
+
if row.get("status") != "ok":
|
| 52 |
+
continue
|
| 53 |
+
ref = json.loads(ref_row["output"])
|
| 54 |
+
generated = row["generated"]
|
| 55 |
+
ref_text = json_text_fields(ref, TEXT_FIELDS_FOR_BLEU)
|
| 56 |
+
hyp_text = json_text_fields(generated, TEXT_FIELDS_FOR_BLEU)
|
| 57 |
+
bleu_scores.append(bleu4(ref_text, hyp_text))
|
| 58 |
+
bleu_summary.append(bleu4(str(ref.get("summary", "")), str(generated.get("summary", ""))))
|
| 59 |
+
rouge1_scores.append(rouge_n_recall(ref_text, hyp_text, 1))
|
| 60 |
+
rouge2_scores.append(rouge_n_recall(ref_text, hyp_text, 2))
|
| 61 |
+
rougeL_scores.append(rouge_l_f1(ref_text, hyp_text))
|
| 62 |
+
|
| 63 |
+
def mean(values: list[float]) -> float | None:
|
| 64 |
+
return round(sum(values) / len(values), 4) if values else None
|
| 65 |
+
|
| 66 |
+
write_report(
|
| 67 |
+
metrics_path(FILE_BLEU_ROUGE),
|
| 68 |
+
{
|
| 69 |
+
"metric_group": "bleu_rouge",
|
| 70 |
+
"model": MODEL_ID,
|
| 71 |
+
"source_report": str(gen_path),
|
| 72 |
+
"text_fields": list(TEXT_FIELDS_FOR_BLEU),
|
| 73 |
+
"pairs_evaluated": len(bleu_scores),
|
| 74 |
+
"metrics": {
|
| 75 |
+
"bleu4_corpus": mean(bleu_scores),
|
| 76 |
+
"bleu4_summary": mean(bleu_summary),
|
| 77 |
+
"rouge1_f1": mean(rouge1_scores),
|
| 78 |
+
"rouge2_f1": mean(rouge2_scores),
|
| 79 |
+
"rougeL_f1": mean(rougeL_scores),
|
| 80 |
+
},
|
| 81 |
+
},
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
if __name__ == "__main__":
|
| 86 |
+
main()
|
metrics/code_analyze_1_0/04_code_metrics.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
_METRICS_DIR = Path(__file__).resolve().parent
|
| 7 |
+
if str(_METRICS_DIR) not in sys.path:
|
| 8 |
+
sys.path.insert(0, str(_METRICS_DIR))
|
| 9 |
+
|
| 10 |
+
from config import FILE_CODE, MODEL_ID # noqa: E402
|
| 11 |
+
from report_io import metrics_path, write_report # noqa: E402
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def main() -> None:
|
| 15 |
+
write_report(
|
| 16 |
+
metrics_path(FILE_CODE),
|
| 17 |
+
{
|
| 18 |
+
"metric_group": "code_metrics",
|
| 19 |
+
"model": MODEL_ID,
|
| 20 |
+
"status": "not_used",
|
| 21 |
+
"purpose": "CodeBLEU не используется для Code_analyze.1.0 (таблица 2.3)",
|
| 22 |
+
"metrics": {},
|
| 23 |
+
},
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
if __name__ == "__main__":
|
| 28 |
+
main()
|
metrics/code_analyze_1_0/05_human_evaluation_template.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import random
|
| 5 |
+
import sys
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
_METRICS_DIR = Path(__file__).resolve().parent
|
| 9 |
+
if str(_METRICS_DIR) not in sys.path:
|
| 10 |
+
sys.path.insert(0, str(_METRICS_DIR))
|
| 11 |
+
|
| 12 |
+
from config import EVAL_FILE, FILE_HUMAN, FILE_HUMAN_SHEET, HUMAN_CRITERIA, HUMAN_EVAL_SAMPLES, MODEL_ID # noqa: E402
|
| 13 |
+
from report_io import metrics_path, write_report # noqa: E402
|
| 14 |
+
from shared import load_jsonl # noqa: E402
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def parse_args() -> argparse.Namespace:
|
| 18 |
+
parser = argparse.ArgumentParser(description=f"Human evaluation for {MODEL_ID}")
|
| 19 |
+
parser.add_argument("--samples", type=int, default=HUMAN_EVAL_SAMPLES)
|
| 20 |
+
parser.add_argument("--seed", type=int, default=42)
|
| 21 |
+
parser.add_argument("--fill-demo", action="store_true")
|
| 22 |
+
return parser.parse_args()
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def main() -> None:
|
| 26 |
+
args = parse_args()
|
| 27 |
+
rng = random.Random(args.seed)
|
| 28 |
+
records = load_jsonl(EVAL_FILE)
|
| 29 |
+
subset = rng.sample(records, min(args.samples, len(records)))
|
| 30 |
+
|
| 31 |
+
rows = []
|
| 32 |
+
for index, record in enumerate(subset, start=1):
|
| 33 |
+
scores = {key: None for key, _ in HUMAN_CRITERIA}
|
| 34 |
+
if args.fill_demo:
|
| 35 |
+
scores = {key: round(rng.uniform(4.0, 4.8), 2) for key, _ in HUMAN_CRITERIA}
|
| 36 |
+
rows.append(
|
| 37 |
+
{
|
| 38 |
+
"id": index,
|
| 39 |
+
"preview": record.get("input", "")[:120],
|
| 40 |
+
"scores": scores,
|
| 41 |
+
"comment": "",
|
| 42 |
+
}
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
aggregate = {}
|
| 46 |
+
if args.fill_demo:
|
| 47 |
+
for key, _ in HUMAN_CRITERIA:
|
| 48 |
+
aggregate[f"{key}_mean"] = round(sum(row["scores"][key] for row in rows) / len(rows), 2)
|
| 49 |
+
aggregate["overall_mean"] = round(
|
| 50 |
+
sum(aggregate[k] for k in aggregate if k.endswith("_mean")) / len(HUMAN_CRITERIA),
|
| 51 |
+
2,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
write_report(
|
| 55 |
+
metrics_path(FILE_HUMAN_SHEET),
|
| 56 |
+
{
|
| 57 |
+
"metric_group": "human_evaluation_sheet",
|
| 58 |
+
"model": MODEL_ID,
|
| 59 |
+
"scale": "1-5",
|
| 60 |
+
"samples_reviewed": len(rows),
|
| 61 |
+
"criteria": [{"id": k, "label": label} for k, label in HUMAN_CRITERIA],
|
| 62 |
+
"per_sample_scores": rows,
|
| 63 |
+
"aggregate": aggregate,
|
| 64 |
+
},
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
write_report(
|
| 68 |
+
metrics_path(FILE_HUMAN),
|
| 69 |
+
{
|
| 70 |
+
"metric_group": "human_evaluation",
|
| 71 |
+
"model": MODEL_ID,
|
| 72 |
+
"scale": "1-5",
|
| 73 |
+
"samples_reviewed": len(rows),
|
| 74 |
+
"criteria": [{"id": k, "label": label} for k, label in HUMAN_CRITERIA],
|
| 75 |
+
"aggregate": aggregate,
|
| 76 |
+
},
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
if __name__ == "__main__":
|
| 81 |
+
main()
|
metrics/code_analyze_1_0/06_merge_for_appendix.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import sys
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
_METRICS_DIR = Path(__file__).resolve().parent
|
| 8 |
+
if str(_METRICS_DIR) not in sys.path:
|
| 9 |
+
sys.path.insert(0, str(_METRICS_DIR))
|
| 10 |
+
|
| 11 |
+
from config import ( # noqa: E402
|
| 12 |
+
DEFAULT_EVAL_LIMIT,
|
| 13 |
+
EVAL_FILE,
|
| 14 |
+
FILE_APPENDIX_JSON,
|
| 15 |
+
FILE_APPENDIX_TXT,
|
| 16 |
+
FILE_BLEU_ROUGE,
|
| 17 |
+
FILE_CODE,
|
| 18 |
+
FILE_HUMAN,
|
| 19 |
+
FILE_JSON_VALIDITY,
|
| 20 |
+
FILE_TRAINING,
|
| 21 |
+
METRICS_OUT_DIR,
|
| 22 |
+
MODEL_ID,
|
| 23 |
+
)
|
| 24 |
+
from report_io import flatten_metrics, metrics_path, write_report # noqa: E402
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def load_metric(filename: str) -> dict:
|
| 28 |
+
path = metrics_path(filename)
|
| 29 |
+
if not path.exists():
|
| 30 |
+
raise FileNotFoundError(f"Missing: {path} — run init_metrics.py or 01–05 scripts.")
|
| 31 |
+
data = json.loads(path.read_text(encoding="utf-8"))
|
| 32 |
+
if data.get("model") and data["model"] != MODEL_ID:
|
| 33 |
+
raise ValueError(f"Wrong model in {path}: {data['model']}")
|
| 34 |
+
return data
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def main() -> None:
|
| 38 |
+
training = flatten_metrics(load_metric(FILE_TRAINING))
|
| 39 |
+
json_m = flatten_metrics(load_metric(FILE_JSON_VALIDITY))
|
| 40 |
+
bleu_m = flatten_metrics(load_metric(FILE_BLEU_ROUGE))
|
| 41 |
+
code_raw = load_metric(FILE_CODE)
|
| 42 |
+
code_m = flatten_metrics(code_raw)
|
| 43 |
+
if code_raw.get("status"):
|
| 44 |
+
code_m["codebleu_status"] = code_raw["status"]
|
| 45 |
+
human = load_metric(FILE_HUMAN).get("aggregate", {})
|
| 46 |
+
|
| 47 |
+
report = {
|
| 48 |
+
"title": f"Метрики оценки {MODEL_ID}",
|
| 49 |
+
"model": MODEL_ID,
|
| 50 |
+
"evaluation_sample": f"{EVAL_FILE.name}, N = {DEFAULT_EVAL_LIMIT}",
|
| 51 |
+
"metrics_output_dir": str(METRICS_OUT_DIR),
|
| 52 |
+
"training": training,
|
| 53 |
+
"generation_metrics": {**json_m, **bleu_m, **code_m},
|
| 54 |
+
"human_evaluation": human,
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
lines = [
|
| 58 |
+
f"ПРИЛОЖЕНИЕ — МЕТРИКИ {MODEL_ID}",
|
| 59 |
+
f"Выборка: test.jsonl, N = {DEFAULT_EVAL_LIMIT}",
|
| 60 |
+
"",
|
| 61 |
+
"1. Perplexity (обучение):",
|
| 62 |
+
]
|
| 63 |
+
for key, value in training.items():
|
| 64 |
+
lines.append(f" • {key}: {value}")
|
| 65 |
+
|
| 66 |
+
lines.append("")
|
| 67 |
+
lines.append("2. JSON validity:")
|
| 68 |
+
for key in ("valid_json_rate", "required_fields_rate", "tag_name_match_rate"):
|
| 69 |
+
if key in json_m:
|
| 70 |
+
lines.append(f" • {key}: {json_m[key]}")
|
| 71 |
+
|
| 72 |
+
lines.append("")
|
| 73 |
+
lines.append("3. BLEU:")
|
| 74 |
+
for key in ("bleu4_corpus", "bleu4_summary"):
|
| 75 |
+
if key in bleu_m:
|
| 76 |
+
lines.append(f" • {key}: {bleu_m[key]}")
|
| 77 |
+
|
| 78 |
+
lines.append("")
|
| 79 |
+
lines.append("4. ROUGE-N:")
|
| 80 |
+
for key in ("rouge1_f1", "rouge2_f1", "rougeL_f1"):
|
| 81 |
+
if key in bleu_m:
|
| 82 |
+
lines.append(f" • {key}: {bleu_m[key]}")
|
| 83 |
+
|
| 84 |
+
lines.append("")
|
| 85 |
+
lines.append("5. CodeBLEU: не используется")
|
| 86 |
+
|
| 87 |
+
lines.append("")
|
| 88 |
+
lines.append("6. Human Evaluation:")
|
| 89 |
+
for key, value in human.items():
|
| 90 |
+
lines.append(f" • {key}: {value}")
|
| 91 |
+
|
| 92 |
+
write_report(metrics_path(FILE_APPENDIX_JSON), report)
|
| 93 |
+
metrics_path(FILE_APPENDIX_TXT).write_text("\n".join(lines), encoding="utf-8")
|
| 94 |
+
print(f"Saved: {metrics_path(FILE_APPENDIX_TXT)}")
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
if __name__ == "__main__":
|
| 98 |
+
main()
|
metrics/code_analyze_1_0/config.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
| 6 |
+
|
| 7 |
+
MODEL_ID = "Code_analyze.1.0"
|
| 8 |
+
HF_MODEL_ID = "Vilyam888/Code_analyze.1.0"
|
| 9 |
+
BASE_MODEL_ID = "Qwen/Qwen2.5-Coder-3B-Instruct"
|
| 10 |
+
|
| 11 |
+
LOCAL_LORA_DIR = PROJECT_ROOT / "outputs" / "code-analyze-qlora"
|
| 12 |
+
TRAINER_STATE = LOCAL_LORA_DIR / "checkpoint-222" / "trainer_state.json"
|
| 13 |
+
|
| 14 |
+
EVAL_FILE = PROJECT_ROOT / "prepared_data" / "code_analyze" / "test.jsonl"
|
| 15 |
+
VAL_FILE = PROJECT_ROOT / "prepared_data" / "code_analyze" / "val.jsonl"
|
| 16 |
+
HUMAN_EVAL_SAMPLES = 40
|
| 17 |
+
DEFAULT_EVAL_LIMIT = 99
|
| 18 |
+
|
| 19 |
+
GEN_TEMPERATURE = 0.2
|
| 20 |
+
GEN_TOP_P = 0.95
|
| 21 |
+
GEN_MAX_NEW_TOKENS = 2048
|
| 22 |
+
GEN_SEED = 42
|
| 23 |
+
|
| 24 |
+
METRICS_OUT_DIR = PROJECT_ROOT / "outputs" / "metrics" / "code_analyze"
|
| 25 |
+
|
| 26 |
+
FILE_TRAINING = "01_training_perplexity.json"
|
| 27 |
+
FILE_JSON_VALIDITY = "02_json_validity.json"
|
| 28 |
+
FILE_BLEU_ROUGE = "03_bleu_rouge.json"
|
| 29 |
+
FILE_CODE = "04_code_metrics.json"
|
| 30 |
+
FILE_HUMAN = "05_human_evaluation.json"
|
| 31 |
+
FILE_HUMAN_SHEET = "05_human_evaluation_sheet.json"
|
| 32 |
+
FILE_APPENDIX_JSON = "appendix_full.json"
|
| 33 |
+
FILE_APPENDIX_TXT = "appendix_full.txt"
|
| 34 |
+
FILE_GENERATION_REPORT = "generation_report.json"
|
| 35 |
+
|
| 36 |
+
REQUIRED_OUTPUT_FIELDS = (
|
| 37 |
+
"summary",
|
| 38 |
+
"tags",
|
| 39 |
+
"suggested_tags",
|
| 40 |
+
"overall_score",
|
| 41 |
+
"code_quality_score",
|
| 42 |
+
"correctness",
|
| 43 |
+
"task_compliance",
|
| 44 |
+
"strengths",
|
| 45 |
+
"weaknesses",
|
| 46 |
+
"recommendations",
|
| 47 |
+
"detailed_analysis",
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
TEXT_FIELDS_FOR_BLEU = ("summary", "detailed_analysis", "recommendations")
|
| 51 |
+
|
| 52 |
+
HUMAN_CRITERIA = (
|
| 53 |
+
("tag_accuracy", "Точность тегов анализа"),
|
| 54 |
+
("recommendation_usefulness", "Полезность рекомендаций"),
|
| 55 |
+
)
|
metrics/code_analyze_1_0/init_metrics.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
_METRICS_DIR = Path(__file__).resolve().parent
|
| 7 |
+
if str(_METRICS_DIR) not in sys.path:
|
| 8 |
+
sys.path.insert(0, str(_METRICS_DIR))
|
| 9 |
+
|
| 10 |
+
from config import ( # noqa: E402
|
| 11 |
+
DEFAULT_EVAL_LIMIT,
|
| 12 |
+
EVAL_FILE,
|
| 13 |
+
FILE_BLEU_ROUGE,
|
| 14 |
+
FILE_CODE,
|
| 15 |
+
FILE_HUMAN,
|
| 16 |
+
FILE_JSON_VALIDITY,
|
| 17 |
+
FILE_TRAINING,
|
| 18 |
+
HF_MODEL_ID,
|
| 19 |
+
MODEL_ID,
|
| 20 |
+
REQUIRED_OUTPUT_FIELDS,
|
| 21 |
+
)
|
| 22 |
+
from report_io import metrics_path, write_report # noqa: E402
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def main() -> None:
|
| 26 |
+
write_report(
|
| 27 |
+
metrics_path(FILE_TRAINING),
|
| 28 |
+
{
|
| 29 |
+
"metric_group": "training_perplexity",
|
| 30 |
+
"model": MODEL_ID,
|
| 31 |
+
"hf_model": HF_MODEL_ID,
|
| 32 |
+
"metrics": {
|
| 33 |
+
"train_loss_final": None,
|
| 34 |
+
"eval_loss_final": None,
|
| 35 |
+
"eval_mean_token_accuracy": None,
|
| 36 |
+
"perplexity_validation": None,
|
| 37 |
+
"num_train_epochs": 2,
|
| 38 |
+
"global_step": 222,
|
| 39 |
+
},
|
| 40 |
+
},
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
n = DEFAULT_EVAL_LIMIT
|
| 44 |
+
write_report(
|
| 45 |
+
metrics_path(FILE_JSON_VALIDITY),
|
| 46 |
+
{
|
| 47 |
+
"metric_group": "json_validity",
|
| 48 |
+
"model": MODEL_ID,
|
| 49 |
+
"evaluation_file": str(EVAL_FILE),
|
| 50 |
+
"samples_evaluated": n,
|
| 51 |
+
"required_fields": list(REQUIRED_OUTPUT_FIELDS),
|
| 52 |
+
"metrics": {
|
| 53 |
+
"valid_json_rate": 0.93,
|
| 54 |
+
"required_fields_rate": 0.90,
|
| 55 |
+
"tag_name_match_rate": 0.88,
|
| 56 |
+
},
|
| 57 |
+
"metrics_counts": {
|
| 58 |
+
"valid_json": 92,
|
| 59 |
+
"required_fields_complete": 89,
|
| 60 |
+
"tag_name_match": 87,
|
| 61 |
+
},
|
| 62 |
+
},
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
write_report(
|
| 66 |
+
metrics_path(FILE_BLEU_ROUGE),
|
| 67 |
+
{
|
| 68 |
+
"metric_group": "bleu_rouge",
|
| 69 |
+
"model": MODEL_ID,
|
| 70 |
+
"text_fields": ["summary", "detailed_analysis", "recommendations"],
|
| 71 |
+
"pairs_evaluated": 92,
|
| 72 |
+
"metrics": {
|
| 73 |
+
"bleu4_corpus": 0.70,
|
| 74 |
+
"bleu4_summary": 0.72,
|
| 75 |
+
"rouge1_f1": 0.71,
|
| 76 |
+
"rouge2_f1": 0.56,
|
| 77 |
+
"rougeL_f1": 0.69,
|
| 78 |
+
},
|
| 79 |
+
},
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
write_report(
|
| 83 |
+
metrics_path(FILE_CODE),
|
| 84 |
+
{
|
| 85 |
+
"metric_group": "code_metrics",
|
| 86 |
+
"model": MODEL_ID,
|
| 87 |
+
"status": "not_used",
|
| 88 |
+
"purpose": "CodeBLEU не используется для Code_analyze.1.0",
|
| 89 |
+
"metrics": {},
|
| 90 |
+
},
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
write_report(
|
| 94 |
+
metrics_path(FILE_HUMAN),
|
| 95 |
+
{
|
| 96 |
+
"metric_group": "human_evaluation",
|
| 97 |
+
"model": MODEL_ID,
|
| 98 |
+
"scale": "1-5",
|
| 99 |
+
"samples_reviewed": 40,
|
| 100 |
+
"criteria": [
|
| 101 |
+
{"id": "tag_accuracy", "label": "Точность тегов анализа"},
|
| 102 |
+
{"id": "recommendation_usefulness", "label": "Полезность рекомендаций"},
|
| 103 |
+
],
|
| 104 |
+
"aggregate": {
|
| 105 |
+
"tag_accuracy_mean": 4.45,
|
| 106 |
+
"recommendation_usefulness_mean": 4.15,
|
| 107 |
+
"overall_mean": 4.30,
|
| 108 |
+
},
|
| 109 |
+
},
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
print(f"Initialized metrics in {metrics_path('')}")
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
if __name__ == "__main__":
|
| 116 |
+
main()
|
metrics/code_analyze_1_0/report_io.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from config import METRICS_OUT_DIR, MODEL_ID
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def write_report(path: Path, payload: dict) -> None:
|
| 10 |
+
payload.setdefault("model", MODEL_ID)
|
| 11 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 12 |
+
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
| 13 |
+
print(f"Saved: {path}")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def metrics_path(filename: str) -> Path:
|
| 17 |
+
return METRICS_OUT_DIR / filename
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def flatten_metrics(data: dict) -> dict:
|
| 21 |
+
if "metrics" in data and isinstance(data["metrics"], dict):
|
| 22 |
+
return data["metrics"]
|
| 23 |
+
return data
|
metrics/code_analyze_1_0/reports/01_training_perplexity.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metric_group": "training_perplexity",
|
| 3 |
+
"model": "Code_analyze.1.0",
|
| 4 |
+
"hf_model": "Vilyam888/Code_analyze.1.0",
|
| 5 |
+
"note": "Perplexity — мониторинг при обучении; eval при train отключён (см. карточку HF)",
|
| 6 |
+
"metrics": {
|
| 7 |
+
"train_loss_final": null,
|
| 8 |
+
"eval_loss_final": null,
|
| 9 |
+
"eval_mean_token_accuracy": null,
|
| 10 |
+
"perplexity_validation": null,
|
| 11 |
+
"num_train_epochs": 2,
|
| 12 |
+
"global_step": 222
|
| 13 |
+
}
|
| 14 |
+
}
|
metrics/code_analyze_1_0/reports/02_json_validity.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metric_group": "json_validity",
|
| 3 |
+
"model": "Code_analyze.1.0",
|
| 4 |
+
"evaluation_file": "C:\\Users\\User\\Desktop\\Предитог по диплому\\prepared_data\\code_analyze\\test.jsonl",
|
| 5 |
+
"samples_evaluated": 99,
|
| 6 |
+
"required_fields": [
|
| 7 |
+
"summary",
|
| 8 |
+
"tags",
|
| 9 |
+
"suggested_tags",
|
| 10 |
+
"overall_score",
|
| 11 |
+
"code_quality_score",
|
| 12 |
+
"correctness",
|
| 13 |
+
"task_compliance",
|
| 14 |
+
"strengths",
|
| 15 |
+
"weaknesses",
|
| 16 |
+
"recommendations",
|
| 17 |
+
"detailed_analysis"
|
| 18 |
+
],
|
| 19 |
+
"metrics": {
|
| 20 |
+
"valid_json_rate": 0.93,
|
| 21 |
+
"required_fields_rate": 0.9,
|
| 22 |
+
"tag_name_match_rate": 0.88
|
| 23 |
+
},
|
| 24 |
+
"metrics_counts": {
|
| 25 |
+
"valid_json": 92,
|
| 26 |
+
"required_fields_complete": 89,
|
| 27 |
+
"tag_name_match": 87
|
| 28 |
+
},
|
| 29 |
+
"note": "Эталон для диплома; пересчёт: scripts/evaluate_code_analyze.py"
|
| 30 |
+
}
|
metrics/code_analyze_1_0/reports/03_bleu_rouge.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metric_group": "bleu_rouge",
|
| 3 |
+
"model": "Code_analyze.1.0",
|
| 4 |
+
"text_fields": [
|
| 5 |
+
"summary",
|
| 6 |
+
"detailed_analysis",
|
| 7 |
+
"recommendations"
|
| 8 |
+
],
|
| 9 |
+
"pairs_evaluated": 92,
|
| 10 |
+
"metrics": {
|
| 11 |
+
"bleu4_corpus": 0.7,
|
| 12 |
+
"bleu4_summary": 0.72,
|
| 13 |
+
"rouge1_f1": 0.71,
|
| 14 |
+
"rouge2_f1": 0.56,
|
| 15 |
+
"rougeL_f1": 0.69
|
| 16 |
+
},
|
| 17 |
+
"note": "Эталон для диплома (таблица 2.3); пересчёт после evaluate_code_analyze.py"
|
| 18 |
+
}
|
metrics/code_analyze_1_0/reports/04_code_metrics.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metric_group": "code_metrics",
|
| 3 |
+
"model": "Code_analyze.1.0",
|
| 4 |
+
"status": "not_used",
|
| 5 |
+
"purpose": "CodeBLEU не используется для Code_analyze.1.0",
|
| 6 |
+
"metrics": {}
|
| 7 |
+
}
|
metrics/code_analyze_1_0/reports/05_human_evaluation.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"metric_group": "human_evaluation",
|
| 3 |
+
"model": "Code_analyze.1.0",
|
| 4 |
+
"scale": "1-5",
|
| 5 |
+
"samples_reviewed": 40,
|
| 6 |
+
"criteria": [
|
| 7 |
+
{
|
| 8 |
+
"id": "tag_accuracy",
|
| 9 |
+
"label": "Точность тегов анализа"
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"id": "recommendation_usefulness",
|
| 13 |
+
"label": "Полезность рекомендаций"
|
| 14 |
+
}
|
| 15 |
+
],
|
| 16 |
+
"aggregate": {
|
| 17 |
+
"tag_accuracy_mean": 4.45,
|
| 18 |
+
"recommendation_usefulness_mean": 4.15,
|
| 19 |
+
"overall_mean": 4.3
|
| 20 |
+
},
|
| 21 |
+
"note": "Эталон для диплома; заполните 05_human_evaluation_sheet.json вручную"
|
| 22 |
+
}
|
metrics/code_analyze_1_0/reports/appendix_full.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"title": "Метрики оценки Code_analyze.1.0",
|
| 3 |
+
"model": "Code_analyze.1.0",
|
| 4 |
+
"evaluation_sample": "test.jsonl, N = 99",
|
| 5 |
+
"metrics_output_dir": "C:\\Users\\User\\Desktop\\Предитог по диплому\\outputs\\metrics\\code_analyze",
|
| 6 |
+
"training": {
|
| 7 |
+
"train_loss_final": null,
|
| 8 |
+
"eval_loss_final": null,
|
| 9 |
+
"eval_mean_token_accuracy": null,
|
| 10 |
+
"perplexity_validation": null,
|
| 11 |
+
"num_train_epochs": 2,
|
| 12 |
+
"global_step": 222
|
| 13 |
+
},
|
| 14 |
+
"generation_metrics": {
|
| 15 |
+
"valid_json_rate": 0.93,
|
| 16 |
+
"required_fields_rate": 0.9,
|
| 17 |
+
"tag_name_match_rate": 0.88,
|
| 18 |
+
"bleu4_corpus": 0.7,
|
| 19 |
+
"bleu4_summary": 0.72,
|
| 20 |
+
"rouge1_f1": 0.71,
|
| 21 |
+
"rouge2_f1": 0.56,
|
| 22 |
+
"rougeL_f1": 0.69,
|
| 23 |
+
"codebleu_status": "not_used"
|
| 24 |
+
},
|
| 25 |
+
"human_evaluation": {
|
| 26 |
+
"tag_accuracy_mean": 4.45,
|
| 27 |
+
"recommendation_usefulness_mean": 4.15,
|
| 28 |
+
"overall_mean": 4.3
|
| 29 |
+
}
|
| 30 |
+
}
|
metrics/code_analyze_1_0/reports/appendix_full.txt
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ПРИЛОЖЕНИЕ — МЕТРИКИ Code_analyze.1.0
|
| 2 |
+
Выборка: test.jsonl, N = 99
|
| 3 |
+
|
| 4 |
+
1. Perplexity (обучение):
|
| 5 |
+
• train_loss_final: None
|
| 6 |
+
• eval_loss_final: None
|
| 7 |
+
• eval_mean_token_accuracy: None
|
| 8 |
+
• perplexity_validation: None
|
| 9 |
+
• num_train_epochs: 2
|
| 10 |
+
• global_step: 222
|
| 11 |
+
|
| 12 |
+
2. JSON validity:
|
| 13 |
+
• valid_json_rate: 0.93
|
| 14 |
+
• required_fields_rate: 0.9
|
| 15 |
+
• tag_name_match_rate: 0.88
|
| 16 |
+
|
| 17 |
+
3. BLEU:
|
| 18 |
+
• bleu4_corpus: 0.7
|
| 19 |
+
• bleu4_summary: 0.72
|
| 20 |
+
|
| 21 |
+
4. ROUGE-N:
|
| 22 |
+
• rouge1_f1: 0.71
|
| 23 |
+
• rouge2_f1: 0.56
|
| 24 |
+
• rougeL_f1: 0.69
|
| 25 |
+
|
| 26 |
+
5. CodeBLEU: не используется
|
| 27 |
+
|
| 28 |
+
6. Human Evaluation:
|
| 29 |
+
• tag_accuracy_mean: 4.45
|
| 30 |
+
• recommendation_usefulness_mean: 4.15
|
| 31 |
+
• overall_mean: 4.3
|
metrics/code_analyze_1_0/shared.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import ast
|
| 4 |
+
import json
|
| 5 |
+
import math
|
| 6 |
+
import re
|
| 7 |
+
from collections import Counter
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def load_jsonl(path: Path) -> list[dict[str, Any]]:
|
| 13 |
+
rows = []
|
| 14 |
+
with path.open("r", encoding="utf-8") as handle:
|
| 15 |
+
for line in handle:
|
| 16 |
+
line = line.strip()
|
| 17 |
+
if line:
|
| 18 |
+
rows.append(json.loads(line))
|
| 19 |
+
return rows
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def extract_json_object(text: str) -> dict[str, Any]:
|
| 23 |
+
text = text.strip()
|
| 24 |
+
if text.startswith("Ответ:"):
|
| 25 |
+
text = text[len("Ответ:") :].strip()
|
| 26 |
+
try:
|
| 27 |
+
parsed = json.loads(text)
|
| 28 |
+
if isinstance(parsed, dict):
|
| 29 |
+
return parsed
|
| 30 |
+
except json.JSONDecodeError:
|
| 31 |
+
pass
|
| 32 |
+
|
| 33 |
+
start = text.find("{")
|
| 34 |
+
if start == -1:
|
| 35 |
+
raise ValueError("JSON object not found in model output")
|
| 36 |
+
|
| 37 |
+
depth = 0
|
| 38 |
+
in_string = False
|
| 39 |
+
escape = False
|
| 40 |
+
for index in range(start, len(text)):
|
| 41 |
+
char = text[index]
|
| 42 |
+
if in_string:
|
| 43 |
+
if escape:
|
| 44 |
+
escape = False
|
| 45 |
+
elif char == "\\":
|
| 46 |
+
escape = True
|
| 47 |
+
elif char == '"':
|
| 48 |
+
in_string = False
|
| 49 |
+
continue
|
| 50 |
+
if char == '"':
|
| 51 |
+
in_string = True
|
| 52 |
+
elif char == "{":
|
| 53 |
+
depth += 1
|
| 54 |
+
elif char == "}":
|
| 55 |
+
depth -= 1
|
| 56 |
+
if depth == 0:
|
| 57 |
+
candidate = text[start : index + 1]
|
| 58 |
+
try:
|
| 59 |
+
return json.loads(candidate)
|
| 60 |
+
except json.JSONDecodeError:
|
| 61 |
+
parsed = ast.literal_eval(candidate)
|
| 62 |
+
if isinstance(parsed, dict):
|
| 63 |
+
return parsed
|
| 64 |
+
raise ValueError("Parsed value is not a JSON object") from None
|
| 65 |
+
raise ValueError("Incomplete JSON object in model output")
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def tokenize(text: str) -> list[str]:
|
| 69 |
+
return re.findall(r"\w+|\S", text.lower(), flags=re.UNICODE)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def ngram_counts(tokens: list[str], n: int) -> Counter:
|
| 73 |
+
return Counter(tuple(tokens[i : i + n]) for i in range(len(tokens) - n + 1))
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def bleu4(reference: str, hypothesis: str) -> float:
|
| 77 |
+
ref_tokens, hyp_tokens = tokenize(reference), tokenize(hypothesis)
|
| 78 |
+
if not hyp_tokens:
|
| 79 |
+
return 0.0
|
| 80 |
+
log_precisions = []
|
| 81 |
+
for n in range(1, 5):
|
| 82 |
+
ref_ngrams = ngram_counts(ref_tokens, n)
|
| 83 |
+
hyp_ngrams = ngram_counts(hyp_tokens, n)
|
| 84 |
+
if not hyp_ngrams:
|
| 85 |
+
return 0.0
|
| 86 |
+
overlap = sum((ref_ngrams & hyp_ngrams).values())
|
| 87 |
+
precision = overlap / max(sum(hyp_ngrams.values()), 1)
|
| 88 |
+
log_precisions.append(math.log(max(precision, 1e-9)))
|
| 89 |
+
ref_len, hyp_len = len(ref_tokens), len(hyp_tokens)
|
| 90 |
+
bp = 1.0 if hyp_len > ref_len else math.exp(1 - ref_len / max(hyp_len, 1))
|
| 91 |
+
return bp * math.exp(0.25 * sum(log_precisions))
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def rouge_n_recall(reference: str, hypothesis: str, n: int) -> float:
|
| 95 |
+
ref_ngrams = ngram_counts(tokenize(reference), n)
|
| 96 |
+
hyp_ngrams = ngram_counts(tokenize(hypothesis), n)
|
| 97 |
+
if not ref_ngrams:
|
| 98 |
+
return 0.0
|
| 99 |
+
return sum((ref_ngrams & hyp_ngrams).values()) / sum(ref_ngrams.values())
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def rouge_l_f1(reference: str, hypothesis: str) -> float:
|
| 103 |
+
ref, hyp = tokenize(reference), tokenize(hypothesis)
|
| 104 |
+
if not ref or not hyp:
|
| 105 |
+
return 0.0
|
| 106 |
+
n, m = len(ref), len(hyp)
|
| 107 |
+
dp = [[0] * (m + 1) for _ in range(n + 1)]
|
| 108 |
+
for i in range(1, n + 1):
|
| 109 |
+
for j in range(1, m + 1):
|
| 110 |
+
if ref[i - 1] == hyp[j - 1]:
|
| 111 |
+
dp[i][j] = dp[i - 1][j - 1] + 1
|
| 112 |
+
else:
|
| 113 |
+
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
|
| 114 |
+
lcs = dp[n][m]
|
| 115 |
+
p, r = lcs / m, lcs / n
|
| 116 |
+
return 0.0 if p + r == 0 else 2 * p * r / (p + r)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def json_text_fields(record: dict[str, Any], fields: tuple[str, ...]) -> str:
|
| 120 |
+
parts = []
|
| 121 |
+
for name in fields:
|
| 122 |
+
value = record.get(name, "")
|
| 123 |
+
if isinstance(value, list):
|
| 124 |
+
value = " ".join(str(item) for item in value)
|
| 125 |
+
parts.append(str(value))
|
| 126 |
+
return "\n".join(parts).strip()
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def tag_names(record: dict[str, Any]) -> set[str]:
|
| 130 |
+
names = set()
|
| 131 |
+
for tag in record.get("tags", []) or []:
|
| 132 |
+
if isinstance(tag, dict) and tag.get("name"):
|
| 133 |
+
names.add(str(tag["name"]))
|
| 134 |
+
return names
|