| | #!/usr/bin/env bash |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | set -uo pipefail |
| |
|
| | PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" |
| |
|
| | |
| | |
| | |
| | _RED='\033[0;31m' |
| | _GREEN='\033[0;32m' |
| | _YELLOW='\033[1;33m' |
| | _BLUE='\033[0;34m' |
| | _NC='\033[0m' |
| |
|
| | log_info() { echo -e "${_BLUE}[INFO]${_NC} $*"; } |
| | log_ok() { echo -e "${_GREEN}[PASS]${_NC} $*"; } |
| | log_warn() { echo -e "${_YELLOW}[WARN]${_NC} $*"; } |
| | log_fail() { echo -e "${_RED}[FAIL]${_NC} $*"; } |
| | log_skip() { echo -e " [SKIP] $*"; } |
| |
|
| | |
| | |
| | |
| | py_eval() { |
| | python3 -c "import sys; sys.exit(0 if ($1) else 1)" |
| | } |
| |
|
| | py_value() { |
| | python3 -c "print($1)" |
| | } |
| |
|
| | |
| | |
| | |
| | json_get() { |
| | local file="$1" key="$2" |
| | python3 -c " |
| | import json, sys |
| | try: |
| | d = json.load(open('$file')) |
| | keys = '$key'.split('.') |
| | for k in keys: |
| | d = d[k] |
| | print(d) |
| | except Exception as e: |
| | print('NOT_FOUND') |
| | sys.exit(1) |
| | " |
| | } |
| |
|
| | |
| | |
| | |
| | GATE_PASS=0 |
| | GATE_FAIL=0 |
| | GATE_SKIP=0 |
| |
|
| | record_pass() { GATE_PASS=$((GATE_PASS + 1)); log_ok "$*"; } |
| | record_fail() { GATE_FAIL=$((GATE_FAIL + 1)); log_fail "$*"; } |
| | record_skip() { GATE_SKIP=$((GATE_SKIP + 1)); log_skip "$*"; } |
| |
|
| | |
| | |
| | |
| | gate_pretrain() { |
| | echo "" |
| | echo "==================================================================" |
| | echo " Gate: PRETRAIN" |
| | echo " κΈ°μ€: val_loss < 2.5 | loss λ¨μ‘° κ°μ νμΈ" |
| | echo "==================================================================" |
| |
|
| | |
| | CKPT_BASE="$PROJECT_DIR/checkpoints" |
| | METRICS_FILE="" |
| |
|
| | |
| | for candidate in \ |
| | "$CKPT_BASE/korean_3b_fp8_pretrain/metrics.json" \ |
| | "$CKPT_BASE/korean_3b_pretrain/metrics.json" \ |
| | "$PROJECT_DIR/outputs/pretrain_metrics.json" \ |
| | "$PROJECT_DIR/logs/pretrain_metrics.json" |
| | do |
| | if [[ -f "$candidate" ]]; then |
| | METRICS_FILE="$candidate" |
| | break |
| | fi |
| | done |
| |
|
| | if [[ -z "$METRICS_FILE" ]]; then |
| | log_warn "μ¬μ νμ΅ λ©νΈλ¦ νμΌμ μ°Ύμ μ μμ΅λλ€." |
| | log_warn "μ°Ύλ κ²½λ‘: $CKPT_BASE/korean_3b_*/metrics.json" |
| | log_warn "λ©νΈλ¦ νμΌμ΄ μμΌλ©΄ νμ΅ μ€ν¬λ¦½νΈμμ μλ νμμΌλ‘ μ μ₯νμΈμ:" |
| | log_warn ' {"val_loss": 2.3, "loss_history": [3.1, 2.8, 2.5, 2.3]}' |
| | record_skip "λ©νΈλ¦ νμΌ μμ β κ²μ΄νΈ 건λλ" |
| | return 0 |
| | fi |
| |
|
| | log_info "λ©νΈλ¦ νμΌ: $METRICS_FILE" |
| |
|
| | |
| | VAL_LOSS=$(json_get "$METRICS_FILE" "val_loss" 2>/dev/null || echo "NOT_FOUND") |
| | if [[ "$VAL_LOSS" == "NOT_FOUND" ]]; then |
| | record_skip "val_loss ν€ μμ β 건λλ" |
| | else |
| | log_info "val_loss = $VAL_LOSS (κΈ°μ€: < 2.5)" |
| | if py_eval "$VAL_LOSS < 2.5" 2>/dev/null; then |
| | record_pass "val_loss $VAL_LOSS < 2.5" |
| | else |
| | record_fail "val_loss $VAL_LOSS >= 2.5 (κΈ°μ€ λ―Έλ¬)" |
| | fi |
| | fi |
| |
|
| | |
| | python3 - "$METRICS_FILE" <<'PYEOF' |
| | import json, sys |
| |
|
| | metrics_file = sys.argv[1] |
| | try: |
| | d = json.load(open(metrics_file)) |
| | history = d.get("loss_history", []) |
| | except Exception as e: |
| | print(f"[SKIP] loss_history μ½κΈ° μ€ν¨: {e}") |
| | sys.exit(0) |
| |
|
| | if len(history) < 2: |
| | print(f"[SKIP] loss_history λ°μ΄ν° λΆμ‘± ({len(history)}κ°)") |
| | sys.exit(0) |
| |
|
| | |
| | n = len(history) |
| | q = max(1, n // 4) |
| | early_avg = sum(history[:q]) / q |
| | late_avg = sum(history[-q:]) / q |
| |
|
| | if late_avg < early_avg: |
| | print(f"[PASS] loss λ¨μ‘° κ°μ νμΈ: μ΄κΈ° avg={early_avg:.4f} β μ΅κ·Ό avg={late_avg:.4f}") |
| | sys.exit(0) |
| | else: |
| | print(f"[FAIL] loss κ°μ λ―ΈνμΈ: μ΄κΈ° avg={early_avg:.4f}, μ΅κ·Ό avg={late_avg:.4f}") |
| | sys.exit(1) |
| | PYEOF |
| | local mono_exit=$? |
| | if [[ $mono_exit -eq 0 ]]; then |
| | GATE_PASS=$((GATE_PASS + 1)) |
| | elif [[ $mono_exit -eq 1 ]]; then |
| | GATE_FAIL=$((GATE_FAIL + 1)) |
| | fi |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | gate_sft() { |
| | echo "" |
| | echo "==================================================================" |
| | echo " Gate: SFT" |
| | echo " κΈ°μ€: val_loss μλ ΄ | λ°λ³΅λ₯ < 15% | KoBEST > 55%" |
| | echo "==================================================================" |
| |
|
| | METRICS_FILE="" |
| | for candidate in \ |
| | "$PROJECT_DIR/outputs/sft_metrics.json" \ |
| | "$PROJECT_DIR/logs/sft_metrics.json" \ |
| | "$PROJECT_DIR/checkpoints/sft/metrics.json" |
| | do |
| | if [[ -f "$candidate" ]]; then |
| | METRICS_FILE="$candidate" |
| | break |
| | fi |
| | done |
| |
|
| | if [[ -z "$METRICS_FILE" ]]; then |
| | log_warn "SFT λ©νΈλ¦ νμΌμ μ°Ύμ μ μμ΅λλ€." |
| | log_warn ' {"val_loss": 1.8, "rep_rate": 0.08, "kobest_score": 0.62}' |
| | record_skip "SFT λ©νΈλ¦ νμΌ μμ β κ²μ΄νΈ 건λλ" |
| | return 0 |
| | fi |
| |
|
| | log_info "λ©νΈλ¦ νμΌ: $METRICS_FILE" |
| |
|
| | |
| | python3 - "$METRICS_FILE" <<'PYEOF' |
| | import json, sys |
| |
|
| | metrics_file = sys.argv[1] |
| | try: |
| | d = json.load(open(metrics_file)) |
| | history = d.get("val_loss_history", []) |
| | except Exception as e: |
| | print(f"[SKIP] val_loss_history μ½κΈ° μ€ν¨: {e}") |
| | sys.exit(0) |
| |
|
| | if len(history) < 2: |
| | |
| | val_loss = d.get("val_loss") |
| | if val_loss is not None: |
| | print(f"[INFO] val_loss = {val_loss} (μλ ΄ νμ€ν 리 μμ β λ¨μΌ κ° νμΈ κ±΄λλ)") |
| | sys.exit(0) |
| |
|
| | last = history[-1] |
| | second = history[-2] |
| | rel_change = abs(last - second) / max(abs(second), 1e-9) |
| |
|
| | if rel_change < 0.01: |
| | print(f"[PASS] val_loss μλ ΄ (μλλ³νμ¨ {rel_change*100:.3f}% < 1%): {second:.4f} β {last:.4f}") |
| | sys.exit(0) |
| | else: |
| | print(f"[FAIL] val_loss λ―Έμλ ΄ (μλλ³νμ¨ {rel_change*100:.3f}% >= 1%): {second:.4f} β {last:.4f}") |
| | sys.exit(1) |
| | PYEOF |
| | local conv_exit=$? |
| | [[ $conv_exit -eq 0 ]] && GATE_PASS=$((GATE_PASS + 1)) || GATE_FAIL=$((GATE_FAIL + 1)) |
| |
|
| | |
| | REP_RATE=$(json_get "$METRICS_FILE" "rep_rate" 2>/dev/null || echo "NOT_FOUND") |
| | if [[ "$REP_RATE" == "NOT_FOUND" ]]; then |
| | record_skip "rep_rate ν€ μμ β 건λλ" |
| | else |
| | REP_PCT=$(py_value "$REP_RATE * 100") |
| | log_info "λ°λ³΅λ₯ = ${REP_PCT}% (κΈ°μ€: < 15%)" |
| | if py_eval "$REP_RATE < 0.15" 2>/dev/null; then |
| | record_pass "λ°λ³΅λ₯ ${REP_PCT}% < 15%" |
| | else |
| | record_fail "λ°λ³΅λ₯ ${REP_PCT}% >= 15% (κΈ°μ€ λ―Έλ¬)" |
| | fi |
| | fi |
| |
|
| | |
| | KOBEST=$(json_get "$METRICS_FILE" "kobest_score" 2>/dev/null || echo "NOT_FOUND") |
| | if [[ "$KOBEST" == "NOT_FOUND" ]]; then |
| | record_skip "kobest_score ν€ μμ β 건λλ" |
| | else |
| | KOBEST_PCT=$(py_value "$KOBEST * 100") |
| | log_info "KoBEST = ${KOBEST_PCT}% (κΈ°μ€: > 55%)" |
| | if py_eval "$KOBEST > 0.55" 2>/dev/null; then |
| | record_pass "KoBEST ${KOBEST_PCT}% > 55%" |
| | else |
| | record_fail "KoBEST ${KOBEST_PCT}% <= 55% (κΈ°μ€ λ―Έλ¬)" |
| | fi |
| | fi |
| | } |
| |
|
| | |
| | |
| | |
| | gate_orpo() { |
| | echo "" |
| | echo "==================================================================" |
| | echo " Gate: ORPO" |
| | echo " κΈ°μ€: λ°λ³΅λ₯ < 5% | KoBEST > 60% | chosen > rejected 90%+" |
| | echo "==================================================================" |
| |
|
| | METRICS_FILE="" |
| | for candidate in \ |
| | "$PROJECT_DIR/outputs/orpo_metrics.json" \ |
| | "$PROJECT_DIR/logs/orpo_metrics.json" \ |
| | "$PROJECT_DIR/checkpoints/orpo/metrics.json" |
| | do |
| | if [[ -f "$candidate" ]]; then |
| | METRICS_FILE="$candidate" |
| | break |
| | fi |
| | done |
| |
|
| | if [[ -z "$METRICS_FILE" ]]; then |
| | log_warn "ORPO λ©νΈλ¦ νμΌμ μ°Ύμ μ μμ΅λλ€." |
| | log_warn ' {"rep_rate": 0.03, "kobest_score": 0.63, "chosen_win_rate": 0.92}' |
| | record_skip "ORPO λ©νΈλ¦ νμΌ μμ β κ²μ΄νΈ 건λλ" |
| | return 0 |
| | fi |
| |
|
| | log_info "λ©νΈλ¦ νμΌ: $METRICS_FILE" |
| |
|
| | |
| | REP_RATE=$(json_get "$METRICS_FILE" "rep_rate" 2>/dev/null || echo "NOT_FOUND") |
| | if [[ "$REP_RATE" == "NOT_FOUND" ]]; then |
| | record_skip "rep_rate ν€ μμ β 건λλ" |
| | else |
| | REP_PCT=$(py_value "$REP_RATE * 100") |
| | log_info "λ°λ³΅λ₯ = ${REP_PCT}% (κΈ°μ€: < 5%)" |
| | if py_eval "$REP_RATE < 0.05" 2>/dev/null; then |
| | record_pass "λ°λ³΅λ₯ ${REP_PCT}% < 5%" |
| | else |
| | record_fail "λ°λ³΅λ₯ ${REP_PCT}% >= 5% (κΈ°μ€ λ―Έλ¬)" |
| | fi |
| | fi |
| |
|
| | |
| | KOBEST=$(json_get "$METRICS_FILE" "kobest_score" 2>/dev/null || echo "NOT_FOUND") |
| | if [[ "$KOBEST" == "NOT_FOUND" ]]; then |
| | record_skip "kobest_score ν€ μμ β 건λλ" |
| | else |
| | KOBEST_PCT=$(py_value "$KOBEST * 100") |
| | log_info "KoBEST = ${KOBEST_PCT}% (κΈ°μ€: > 60%)" |
| | if py_eval "$KOBEST > 0.60" 2>/dev/null; then |
| | record_pass "KoBEST ${KOBEST_PCT}% > 60%" |
| | else |
| | record_fail "KoBEST ${KOBEST_PCT}% <= 60% (κΈ°μ€ λ―Έλ¬)" |
| | fi |
| | fi |
| |
|
| | |
| | CHOSEN_WIN=$(json_get "$METRICS_FILE" "chosen_win_rate" 2>/dev/null || echo "NOT_FOUND") |
| | if [[ "$CHOSEN_WIN" == "NOT_FOUND" ]]; then |
| | record_skip "chosen_win_rate ν€ μμ β 건λλ" |
| | else |
| | WIN_PCT=$(py_value "$CHOSEN_WIN * 100") |
| | log_info "Chosen win rate = ${WIN_PCT}% (κΈ°μ€: >= 90%)" |
| | if py_eval "$CHOSEN_WIN >= 0.90" 2>/dev/null; then |
| | record_pass "Chosen win rate ${WIN_PCT}% >= 90%" |
| | else |
| | record_fail "Chosen win rate ${WIN_PCT}% < 90% (κΈ°μ€ λ―Έλ¬)" |
| | fi |
| | fi |
| | } |
| |
|
| | |
| | |
| | |
| | gate_deploy() { |
| | echo "" |
| | echo "==================================================================" |
| | echo " Gate: DEPLOY" |
| | echo " κΈ°μ€: Q4_K_M perplexity < F16 Γ 1.05 | Ollama 5κ° ν둬ννΈ μλ΅" |
| | echo "==================================================================" |
| |
|
| | local MODEL_NAME="frankenstallm-3b" |
| | local GGUF_DIR="$PROJECT_DIR/outputs/gguf" |
| | local F16_GGUF="$GGUF_DIR/${MODEL_NAME}-f16.gguf" |
| | local Q4KM_GGUF="$GGUF_DIR/${MODEL_NAME}-Q4_K_M.gguf" |
| |
|
| | |
| | if [[ ! -f "$Q4KM_GGUF" ]]; then |
| | log_warn "Q4_K_M GGUF νμΌ μμ: $Q4KM_GGUF" |
| | log_warn "λ¨Όμ μ€ν: bash scripts/convert_3b_gguf.sh" |
| | record_skip "GGUF νμΌ μμ β perplexity κ²μ΄νΈ 건λλ" |
| | else |
| | |
| | LLAMA_PPL_BIN="$PROJECT_DIR/outputs/llama.cpp/build/bin/llama-perplexity" |
| |
|
| | if [[ ! -f "$LLAMA_PPL_BIN" ]]; then |
| | log_warn "llama-perplexity λ°μ΄λ리 μμ β λΉλ μλ μ€ ..." |
| | cmake --build "$PROJECT_DIR/outputs/llama.cpp/build" \ |
| | --target llama-perplexity -j "$(nproc)" &>/dev/null || true |
| | fi |
| |
|
| | |
| | SAMPLE_TEXT="$PROJECT_DIR/outputs/gguf/ppl_sample.txt" |
| | if [[ ! -f "$SAMPLE_TEXT" ]]; then |
| | |
| | cat > "$SAMPLE_TEXT" <<'SAMPLE' |
| | μΈκ³΅μ§λ₯μ νλ μ¬νμμ λ§€μ° μ€μν κΈ°μ λ‘ μ리μ‘κ³ μμ΅λλ€. |
| | κΈ°κ³ νμ΅κ³Ό λ₯λ¬λμ λ°μ μΌλ‘ μΈν΄ λ€μν λΆμΌμμ νμ μ΄ μ΄λ£¨μ΄μ§κ³ μμ΅λλ€. |
| | μμ°μ΄ μ²λ¦¬ κΈ°μ μ λ°μ μ μΈκ°κ³Ό μ»΄ν¨ν°μ μνΈμμ© λ°©μμ κ·Όλ³Έμ μΌλ‘ λ³νμν€κ³ μμ΅λλ€. |
| | νκ΅μ΄λ κ΅μ°©μ΄λ‘μ νΉμ μ ννλ‘ μ νΉμ±μ κ°μ§κ³ μμ΄ μμ°μ΄ μ²λ¦¬μ λ
νΉν λμ μ μ μν©λλ€. |
| | λκ·λͺ¨ μΈμ΄ λͺ¨λΈμ λ±μ₯μΌλ‘ κΈ°κ³ λ²μ, ν
μ€νΈ μμ½, μ§μμλ΅ λ±μ μ±λ₯μ΄ ν¬κ² ν₯μλμμ΅λλ€. |
| | SAMPLE |
| | fi |
| |
|
| | if [[ -f "$LLAMA_PPL_BIN" && -f "$F16_GGUF" ]]; then |
| | log_info "Perplexity μΈ‘μ μ€ (F16 vs Q4_K_M) ..." |
| |
|
| | PPL_F16=$(timeout 120 "$LLAMA_PPL_BIN" -m "$F16_GGUF" -f "$SAMPLE_TEXT" 2>&1 \ |
| | | grep -oP "Perplexity: \K[0-9.]+" | head -1 || echo "0") |
| | PPL_Q4=$(timeout 120 "$LLAMA_PPL_BIN" -m "$Q4KM_GGUF" -f "$SAMPLE_TEXT" 2>&1 \ |
| | | grep -oP "Perplexity: \K[0-9.]+" | head -1 || echo "0") |
| |
|
| | if [[ "$PPL_F16" == "0" || "$PPL_Q4" == "0" ]]; then |
| | record_skip "Perplexity μΈ‘μ μ€ν¨ β 건λλ" |
| | else |
| | THRESHOLD=$(py_value "$PPL_F16 * 1.05") |
| | log_info "F16 PPL = $PPL_F16 | Q4_K_M PPL = $PPL_Q4 | κΈ°μ€: < $THRESHOLD" |
| | if py_eval "$PPL_Q4 < $PPL_F16 * 1.05" 2>/dev/null; then |
| | record_pass "Q4_K_M PPL $PPL_Q4 < F16 PPL Γ 1.05 ($THRESHOLD)" |
| | else |
| | record_fail "Q4_K_M PPL $PPL_Q4 >= F16 PPL Γ 1.05 ($THRESHOLD)" |
| | fi |
| | fi |
| | else |
| | record_skip "llama-perplexity λλ F16 GGUF μμ β perplexity κ²μ΄νΈ 건λλ" |
| | fi |
| | fi |
| |
|
| | |
| | if ! command -v ollama &>/dev/null; then |
| | record_skip "ollama μμ β μλ΅ ν
μ€νΈ 건λλ" |
| | return 0 |
| | fi |
| |
|
| | if ! ollama list 2>/dev/null | grep -q "$MODEL_NAME"; then |
| | log_warn "Ollamaμ $MODEL_NAME λͺ¨λΈμ΄ λ±λ‘λμ§ μμμ΅λλ€." |
| | log_warn "λ¨Όμ μ€ν: bash scripts/deploy_3b_ollama.sh" |
| | record_skip "Ollama λͺ¨λΈ λ―Έλ±λ‘ β μλ΅ ν
μ€νΈ 건λλ" |
| | return 0 |
| | fi |
| |
|
| | log_info "Ollama μλ΅ ν
μ€νΈ (5κ° ν둬ννΈ) ..." |
| |
|
| | declare -a PROMPTS=( |
| | "μλ
νμΈμ." |
| | "1 λνκΈ° 1μ 무μμΈκ°μ?" |
| | "νμ΄μ¬μ΄λ 무μμΈκ°μ?" |
| | "νκ΅μ μλλ μ΄λμΈκ°μ?" |
| | "μ€λ λ μ¨κ° μ’λ€μ." |
| | ) |
| |
|
| | local PASS=0 FAIL=0 |
| | for i in "${!PROMPTS[@]}"; do |
| | local PROMPT="${PROMPTS[$i]}" |
| | local NUM=$((i + 1)) |
| | if RESP=$(timeout 45 ollama run "$MODEL_NAME" "$PROMPT" 2>&1) && [[ -n "$RESP" ]]; then |
| | log_ok " ν둬ννΈ $NUM μλ΅ OK (${#RESP}μ)" |
| | PASS=$((PASS + 1)) |
| | else |
| | log_fail " ν둬ννΈ $NUM μλ΅ μ€ν¨" |
| | FAIL=$((FAIL + 1)) |
| | fi |
| | done |
| |
|
| | log_info "Ollama μλ΅: $PASS/5 μ±κ³΅" |
| | if [[ $FAIL -eq 0 ]]; then |
| | record_pass "Ollama 5κ° ν둬ννΈ λͺ¨λ μλ΅ μ±κ³΅" |
| | else |
| | record_fail "Ollama μλ΅ μ€ν¨ $FAIL/5" |
| | fi |
| | } |
| |
|
| | |
| | |
| | |
| | print_summary() { |
| | local phase="$1" |
| | local TOTAL=$((GATE_PASS + GATE_FAIL + GATE_SKIP)) |
| | echo "" |
| | echo "==================================================================" |
| | echo " Quality Gate κ²°κ³Ό: $phase" |
| | echo " PASS: $GATE_PASS | FAIL: $GATE_FAIL | SKIP: $GATE_SKIP | TOTAL: $TOTAL" |
| | echo "==================================================================" |
| |
|
| | if [[ $GATE_FAIL -eq 0 ]]; then |
| | echo -e "${_GREEN} [GATE PASSED]${_NC} λͺ¨λ κ²μ¦ κΈ°μ€ ν΅κ³Ό" |
| | echo "" |
| | return 0 |
| | else |
| | echo -e "${_RED} [GATE FAILED]${_NC} ${GATE_FAIL}κ° κ²μ¦ κΈ°μ€ λ―Έλ¬" |
| | echo " μ€ν¨ νλͺ©μ μμ ν ν λ€μ μ€ννμΈμ." |
| | echo "" |
| | return 1 |
| | fi |
| | } |
| |
|
| | |
| | |
| | |
| | PHASE="${1:-}" |
| |
|
| | if [[ -z "$PHASE" ]]; then |
| | echo "Usage: bash scripts/quality_gate.sh <phase>" |
| | echo " phase: pretrain | sft | orpo | deploy | all" |
| | exit 2 |
| | fi |
| |
|
| | echo "" |
| | echo "==================================================================" |
| | echo " Quality Gate κ²μ¦ μμ: $PHASE" |
| | echo " νλ‘μ νΈ: $PROJECT_DIR" |
| | echo " μκ° : $(date '+%Y-%m-%d %H:%M:%S')" |
| | echo "==================================================================" |
| |
|
| | case "$PHASE" in |
| | pretrain) |
| | gate_pretrain |
| | print_summary "pretrain" |
| | ;; |
| | sft) |
| | gate_sft |
| | print_summary "sft" |
| | ;; |
| | orpo) |
| | gate_orpo |
| | print_summary "orpo" |
| | ;; |
| | deploy) |
| | gate_deploy |
| | print_summary "deploy" |
| | ;; |
| | all) |
| | gate_pretrain |
| | gate_sft |
| | gate_orpo |
| | gate_deploy |
| | print_summary "all" |
| | ;; |
| | *) |
| | echo "ERROR: μ μ μλ phase: $PHASE" |
| | echo "Usage: bash scripts/quality_gate.sh <pretrain|sft|orpo|deploy|all>" |
| | exit 2 |
| | ;; |
| | esac |
| |
|