| … | … | @@ -3,6 +3,8 @@ |
| 3 | 3 | // |
| 4 | 4 | // jt <seed> [--tiers 3000,10000,30000] [--heldout N] [--K 30] [--shuffle] [--explain file] [--log file] [--v1] |
| 5 | 5 | // [--v2 word|dj] PREREG-v2 arms (implies the v1 pipeline); adds JT-<arm>-COLL rows |
| 6 | // [--v3] PREREG-v3: V2-DJ + sense units split by disjuncts; adds S rows (eligible, split, split∩poly, poly∩eligible) |
| 7 | // [--dump prefix] write <prefix>-<tier>.bin (final labels + attention parse) for ../level2/l2 --jt |
| 6 | 8 | // --v1 runs the PREREG-v1 learner (layer 0 from word-pair PMI, classes by exchange over attended pairs). |
| 7 | 9 | // C <seed> <tier> <K> <method> <checkpoint> <vmeasure> <m1acc> <senseacc> v1 continuous checkpoints |
| 8 | 10 | // Output rows (stdout): |
| … | … | @@ -23,6 +25,10 @@ |
| 23 | 25 | |
| 24 | 26 | static int KJ = 30; |
| 25 | 27 | static int MODE = 0; // 0 v1 (class profiles), 1 V2-WORD (neighbour words), 2 V2-DJ (disjuncts); PREREG-v2.md |
| 28 | static int V3 = 0; // PREREG-v3.md: V2-DJ, then split words into sense units by their disjuncts, re-learn |
| 29 | static int VU; // size of the layer-0 alphabet (words, or sense units in v3) |
| 30 | static uint64_t unit_hash; // v3: hash of the training unit assignment (audit) |
| 31 | static long st_elig, st_split, st_split_poly, st_poly_elig; // v3 split diagnostics, last training call |
| 26 | 32 | |
| 27 | 33 | static uint64_t fnv_bytes (uint64_t h, const void *p, size_t n); |
| 28 | 34 | |
| … | … | @@ -466,13 +472,13 @@ static double *word_scores (const corpus_t *tr) { |
| 466 | 472 | } |
| 467 | 473 | |
| 468 | 474 | // Layer-0 attention of occurrence t over its window, from word-pair PMI. Returns 0 if no neighbour passes. |
| 469 | | static int word_attention (const corpus_t *C, int o, int n, int t, const double *SW, double *alpha) { |
| 475 | static int word_attention (const corpus_t *C, int o, int n, int t, const double *SW, int A, double *alpha) { |
| 470 | 476 | double tot = 0; |
| 471 | 477 | for (int j = 0; j < n; j++) { |
| 472 | 478 | alpha[j] = 0; |
| 473 | 479 | if (j == t || abs(j - t) > W) continue; |
| 474 | 480 | int a = t < j ? C->w[o + t] : C->w[o + j], b = t < j ? C->w[o + j] : C->w[o + t]; |
| 475 | | double v = SW[(size_t) a * V + b]; |
| 481 | double v = SW[(size_t) a * A + b]; |
| 476 | 482 | if (v > NEG) { alpha[j] = v; tot += v; } |
| 477 | 483 | } |
| 478 | 484 | if (tot <= 0) return 0; |
| … | … | @@ -534,35 +540,39 @@ static int *exchange_weighted (const double *Wp, const corpus_t *tr) { |
| 534 | 540 | } |
| 535 | 541 | |
| 536 | 542 | // Full v1 learner on a training prefix. Leaves params in p and the layer-0 classes in *cls_out. |
| 537 | | static void train_v1 (const corpus_t *tr, params_t *p, int **cls_out, double perturb) { |
| 538 | | double *SW = word_scores(tr); |
| 539 | | double *Wp = calloc((size_t) V * V, sizeof(double)), alpha[MAXN]; |
| 540 | | for (int s = 0; s < tr->ns; s++) { |
| 541 | | int o = tr->off[s], n = tr->off[s + 1] - o; |
| 543 | // tru: the same text with layer-0 symbols (words, or v3 sense units, alphabet vu); EM always runs over words (tr). |
| 544 | static void train_core (const corpus_t *tr, const corpus_t *tru, int vu, params_t *p, int **cls_out, double perturb) { |
| 545 | int V0 = V; |
| 546 | V = VU = vu; // layer 0 and exchange over the unit alphabet |
| 547 | double *SW = word_scores(tru); |
| 548 | double *Wp = calloc((size_t) vu * vu, sizeof(double)), alpha[MAXN]; |
| 549 | for (int s = 0; s < tru->ns; s++) { |
| 550 | int o = tru->off[s], n = tru->off[s + 1] - o; |
| 542 | 551 | for (int t = 0; t < n; t++) { |
| 543 | | if (!word_attention(tr, o, n, t, SW, alpha)) continue; |
| 552 | if (!word_attention(tru, o, n, t, SW, vu, alpha)) continue; |
| 544 | 553 | for (int j = 0; j < n; j++) if (alpha[j] > 0) { |
| 545 | | int a = t < j ? tr->w[o + t] : tr->w[o + j], b = t < j ? tr->w[o + j] : tr->w[o + t]; |
| 546 | | Wp[(size_t) a * V + b] += alpha[j]; |
| 554 | int a = t < j ? tru->w[o + t] : tru->w[o + j], b = t < j ? tru->w[o + j] : tru->w[o + t]; |
| 555 | Wp[(size_t) a * vu + b] += alpha[j]; |
| 547 | 556 | } |
| 548 | 557 | } |
| 549 | 558 | } |
| 550 | | int *cls = exchange_weighted(Wp, tr); |
| 559 | int *cls = exchange_weighted(Wp, tru); |
| 560 | V = V0; |
| 551 | 561 | // Initial counts: one-hot classes, word-level attention as messages. |
| 552 | 562 | counts_t c; counts_alloc(&c); |
| 553 | 563 | static sent_state_t S; |
| 554 | 564 | for (int s = 0; s < tr->ns; s++) { |
| 555 | 565 | int o = tr->off[s], n = tr->off[s + 1] - o; |
| 556 | 566 | S.n = n; |
| 557 | | for (int t = 0; t < n; t++) { for (int z = 0; z < KJ; z++) S.q[t][z] = 0; S.q[t][cls[tr->w[o + t]]] = 1; } |
| 567 | for (int t = 0; t < n; t++) { for (int z = 0; z < KJ; z++) S.q[t][z] = 0; S.q[t][cls[tru->w[o + t]]] = 1; } |
| 558 | 568 | for (int t = 0; t < n; t++) { |
| 559 | 569 | for (int y = 0; y < KJ; y++) { S.mL[t][y] = 0; S.mR[t][y] = 0; } |
| 560 | | int ok = word_attention(tr, o, n, t, SW, alpha); |
| 570 | int ok = word_attention(tru, o, n, t, SW, vu, alpha); |
| 561 | 571 | memcpy(S.alpha[t], alpha, sizeof(double) * n); |
| 562 | 572 | if (!ok) continue; |
| 563 | | for (int j = 0; j < n; j++) if (alpha[j] > 0) (j < t ? S.mL[t] : S.mR[t])[cls[tr->w[o + j]]] += alpha[j]; |
| 573 | for (int j = 0; j < n; j++) if (alpha[j] > 0) (j < t ? S.mL[t] : S.mR[t])[cls[tru->w[o + j]]] += alpha[j]; |
| 564 | 574 | } |
| 565 | | if (MODE == 2) { int cl[MAXN]; for (int j = 0; j < n; j++) cl[j] = cls[tr->w[o + j]]; disjuncts(&S, cl); } |
| 575 | if (MODE == 2) { int cl[MAXN]; for (int j = 0; j < n; j++) cl[j] = cls[tru->w[o + j]]; disjuncts(&S, cl); } |
| 566 | 576 | accumulate(tr, o, &S, &c); |
| 567 | 577 | } |
| 568 | 578 | c.N[0] += perturb; // audit mutation test only |
| … | … | @@ -576,6 +586,132 @@ static void train_v1 (const corpus_t *tr, params_t *p, int **cls_out, double per |
| 576 | 586 | *cls_out = cls; |
| 577 | 587 | } |
| 578 | 588 | |
| 589 | // ---------------------------------------------------------------- v3 (PREREG-v3.md): sense units from disjuncts |
| 590 | |
| 591 | #define NCODE 128 // connector code = side * 64 + class |
| 592 | #define SPLIT_MIN 20 |
| 593 | #define SPLIT_ADD 0.1 |
| 594 | |
| 595 | // Log-likelihood of bags b[0..m) under multinomial counts c (add-SPLIT_ADD over NCODE codes). |
| 596 | static double bag_ll (const int *codes, int nc, const double *lth) { double s = 0; for (int k = 0; k < nc; k++) s += lth[codes[k]]; return s; } |
| 597 | static void mult_log (const double *c, double *lth) { |
| 598 | double tot = 0; |
| 599 | for (int k = 0; k < NCODE; k++) tot += c[k]; |
| 600 | for (int k = 0; k < NCODE; k++) lth[k] = log((c[k] + SPLIT_ADD) / (tot + SPLIT_ADD * NCODE)); |
| 601 | } |
| 602 | static double chi2_999 (int df) { double a = 2.0 / (9 * df), x = 1 - a + 3.0902 * sqrt(a); return df * x * x * x; } // Wilson–Hilferty |
| 603 | |
| 604 | // Split test for one word over its occurrences occ[0..m) (indices into tcon/tnc). Sets minor[i] = 1 for the |
| 605 | // smaller component's occurrences. Returns 1 if the split is accepted. |
| 606 | static int split_word (const int *occ, int m, const int *tcon, const int *tnc, int *minor) { |
| 607 | int *O = malloc(sizeof(int) * m), no = 0, used[NCODE] = { 0 }, kc = 0; |
| 608 | for (int i = 0; i < m; i++) if (tnc[occ[i]] > 0) O[no++] = i; |
| 609 | double c1[NCODE] = { 0 }, l1[NCODE], cA[NCODE], cB[NCODE], lA[NCODE], lB[NCODE]; |
| 610 | for (int i = 0; i < no; i++) for (int k = 0; k < tnc[occ[O[i]]]; k++) c1[tcon[occ[O[i]] * MAXC + k]]++; |
| 611 | for (int k = 0; k < NCODE; k++) if (c1[k] > 0) { used[k] = 1; kc++; } |
| 612 | mult_log(c1, l1); |
| 613 | double LL1 = 0; |
| 614 | for (int i = 0; i < no; i++) LL1 += bag_ll(tcon + occ[O[i]] * MAXC, tnc[occ[O[i]]], l1); |
| 615 | // Initial split: the connector whose presence/absence gives the largest G (hard two-multinomial fit). |
| 616 | int best = -1; double bestG = -1; |
| 617 | for (int code = 0; code < NCODE; code++) { |
| 618 | if (!used[code]) continue; |
| 619 | memset(cA, 0, sizeof cA); memset(cB, 0, sizeof cB); |
| 620 | int nB = 0; |
| 621 | for (int i = 0; i < no; i++) { |
| 622 | const int *b = tcon + occ[O[i]] * MAXC; int nc = tnc[occ[O[i]]], has = 0; |
| 623 | for (int k = 0; k < nc; k++) has |= b[k] == code; |
| 624 | nB += has; |
| 625 | for (int k = 0; k < nc; k++) (has ? cB : cA)[b[k]]++; |
| 626 | } |
| 627 | if (nB == 0 || nB == no) continue; |
| 628 | mult_log(cA, lA); mult_log(cB, lB); |
| 629 | double ll = 0; |
| 630 | for (int i = 0; i < no; i++) { |
| 631 | const int *b = tcon + occ[O[i]] * MAXC; int nc = tnc[occ[O[i]]], has = 0; |
| 632 | for (int k = 0; k < nc; k++) has |= b[k] == code; |
| 633 | ll += bag_ll(b, nc, has ? lB : lA); |
| 634 | } |
| 635 | if (2 * (ll - LL1) > bestG) { bestG = 2 * (ll - LL1); best = code; } |
| 636 | } |
| 637 | int accept = 0; |
| 638 | if (best >= 0) { |
| 639 | double *r = malloc(sizeof(double) * no), wB = 0, LL2 = 0; |
| 640 | for (int i = 0; i < no; i++) { |
| 641 | const int *b = tcon + occ[O[i]] * MAXC; int nc = tnc[occ[O[i]]], has = 0; |
| 642 | for (int k = 0; k < nc; k++) has |= b[k] == best; |
| 643 | r[i] = has; |
| 644 | } |
| 645 | for (int it = 0; it <= 20; it++) { // 20 EM passes, then one more E-step for LL2 and the posteriors |
| 646 | memset(cA, 0, sizeof cA); memset(cB, 0, sizeof cB); wB = 0; |
| 647 | for (int i = 0; i < no; i++) { |
| 648 | const int *b = tcon + occ[O[i]] * MAXC; |
| 649 | for (int k = 0; k < tnc[occ[O[i]]]; k++) { cA[b[k]] += 1 - r[i]; cB[b[k]] += r[i]; } |
| 650 | wB += r[i]; |
| 651 | } |
| 652 | wB /= no; |
| 653 | if (wB <= 0 || wB >= 1) break; |
| 654 | mult_log(cA, lA); mult_log(cB, lB); |
| 655 | LL2 = 0; |
| 656 | for (int i = 0; i < no; i++) { |
| 657 | const int *b = tcon + occ[O[i]] * MAXC; int nc = tnc[occ[O[i]]]; |
| 658 | double a = log(1 - wB) + bag_ll(b, nc, lA), bb = log(wB) + bag_ll(b, nc, lB), mx = a > bb ? a : bb; |
| 659 | double lse = mx + log(exp(a - mx) + exp(bb - mx)); |
| 660 | LL2 += lse; r[i] = exp(bb - lse); |
| 661 | } |
| 662 | } |
| 663 | double G = 2 * (LL2 - LL1), wmin = wB < 0.5 ? wB : 1 - wB; |
| 664 | accept = wB > 0 && wB < 1 && G > chi2_999(kc) && wmin >= 0.05; |
| 665 | if (getenv("JT_SPLIT_DEBUG")) fprintf(stderr, "split m=%d no=%d kc=%d initG=%.1f G=%.1f thr=%.1f wB=%.3f LL1=%.1f LL2=%.1f accept=%d\n", m, no, kc, bestG, G, chi2_999(kc), wB, LL1, LL2, accept); |
| 666 | for (int i = 0; i < m; i++) minor[i] = 0; |
| 667 | if (accept) for (int i = 0; i < no; i++) minor[O[i]] = wB < 0.5 ? r[i] > 0.5 : r[i] <= 0.5; |
| 668 | free(r); |
| 669 | } |
| 670 | free(O); |
| 671 | return accept; |
| 672 | } |
| 673 | |
| 674 | static void train_v3 (const corpus_t *tr, params_t *p, int **cls_out, double perturb) { |
| 675 | int *cls0; |
| 676 | train_core(tr, tr, V, p, &cls0, 0); |
| 677 | free(cls0); |
| 678 | int *tcon = malloc(sizeof(int) * tr->ntok * MAXC), *tnc = malloc(sizeof(int) * tr->ntok); |
| 679 | static sent_state_t S; |
| 680 | for (int s = 0; s < tr->ns; s++) { |
| 681 | int o = tr->off[s]; |
| 682 | estep(tr, o, tr->off[s + 1] - o, p, &S); |
| 683 | for (int t = 0; t < S.n; t++) { tnc[o + t] = S.nc[t]; memcpy(tcon + (size_t) (o + t) * MAXC, S.con[t], sizeof(int) * MAXC); } |
| 684 | } |
| 685 | // Occurrences grouped by word (counting sort). |
| 686 | int *start = calloc(V + 1, sizeof(int)), *occ = malloc(sizeof(int) * tr->ntok), *fill = malloc(sizeof(int) * V); |
| 687 | for (int t = 0; t < tr->ntok; t++) start[tr->w[t] + 1]++; |
| 688 | for (int w = 0; w < V; w++) start[w + 1] += start[w]; |
| 689 | memcpy(fill, start, sizeof(int) * V); |
| 690 | for (int t = 0; t < tr->ntok; t++) occ[fill[tr->w[t]]++] = t; |
| 691 | corpus_t tru = *tr; |
| 692 | tru.w = malloc(sizeof(int) * tr->ntok); |
| 693 | memcpy(tru.w, tr->w, sizeof(int) * tr->ntok); |
| 694 | int vu = V, *minor = malloc(sizeof(int) * tr->ntok); |
| 695 | st_elig = st_split = st_split_poly = st_poly_elig = 0; |
| 696 | for (int w = 0; w < V; w++) { |
| 697 | int m = start[w + 1] - start[w]; |
| 698 | if (m < SPLIT_MIN) continue; |
| 699 | st_elig++; st_poly_elig += poly[w]; |
| 700 | if (!split_word(occ + start[w], m, tcon, tnc, minor)) continue; |
| 701 | st_split++; st_split_poly += poly[w]; |
| 702 | for (int i = 0; i < m; i++) if (minor[i]) tru.w[occ[start[w] + i]] = vu; |
| 703 | vu++; |
| 704 | } |
| 705 | unit_hash = fnv_bytes(1469598103934665603ULL, tru.w, sizeof(int) * tr->ntok); |
| 706 | train_core(tr, &tru, vu, p, cls_out, perturb); |
| 707 | free(tcon); free(tnc); free(start); free(occ); free(fill); free(minor); free(tru.w); |
| 708 | } |
| 709 | |
| 710 | static void train_v1 (const corpus_t *tr, params_t *p, int **cls_out, double perturb) { |
| 711 | if (V3) train_v3(tr, p, cls_out, perturb); |
| 712 | else train_core(tr, tr, V, p, cls_out, perturb); |
| 713 | } |
| 714 | |
| 579 | 715 | static uint64_t params_hash (const params_t *p, const int *cls) { |
| 580 | 716 | uint64_t h = 1469598103934665603ULL; |
| 581 | 717 | h = fnv_bytes(h, p->lpi, sizeof(double) * V * KJ); |
| … | … | @@ -583,7 +719,35 @@ static uint64_t params_hash (const params_t *p, const int *cls) { |
| 583 | 719 | h = fnv_bytes(h, p->pmi, sizeof(double) * KJ * KJ); |
| 584 | 720 | if (MODE == 1) { h = fnv_bytes(h, p->lpsiL, sizeof(double) * KJ * V); h = fnv_bytes(h, p->lpsiR, sizeof(double) * KJ * V); } |
| 585 | 721 | if (MODE == 2) { h = fnv_bytes(h, p->dj.key, sizeof(uint64_t) * p->dj.cap); h = fnv_bytes(h, p->dj.cnt, sizeof(double) * p->dj.cap * KJ); } |
| 586 | | return fnv_bytes(h, cls, sizeof(int) * V); |
| 722 | if (V3) h = fnv_bytes(h, &unit_hash, sizeof unit_hash); |
| 723 | return fnv_bytes(h, cls, sizeof(int) * VU); |
| 724 | } |
| 725 | |
| 726 | // Layer-1 dump for ../level2 (PREREG-JT.md): per corpus ntok, labels[ntok], heads[ntok] (in-sentence offset, -1 root). |
| 727 | static void dump_layer1 (const char *path, const corpus_t *tr, const corpus_t *ho, const params_t *p) { |
| 728 | FILE *f = fopen(path, "wb"); |
| 729 | if (!f) { fprintf(stderr, "cannot write %s\n", path); exit(1); } |
| 730 | static sent_state_t S; |
| 731 | for (int k = 0; k < 2; k++) { |
| 732 | const corpus_t *C = k ? ho : tr; |
| 733 | int *lab = malloc(sizeof(int) * C->ntok), *head = malloc(sizeof(int) * C->ntok), hd[MAXN + 1]; |
| 734 | for (int s = 0; s < C->ns; s++) { |
| 735 | int o = C->off[s], n = C->off[s + 1] - o, m = n + 1; |
| 736 | estep(C, o, n, p, &S); |
| 737 | for (int t = 0; t < n; t++) { int b = 0; for (int z = 1; z < KJ; z++) if (S.q[t][z] > S.q[t][b]) b = z; lab[o + t] = b; } |
| 738 | for (int h = 0; h < m; h++) for (int d = 0; d < m; d++) sc[h][d] = NEG; |
| 739 | for (int d = 1; d < m; d++) sc[0][d] = 0; |
| 740 | for (int i = 0; i < n; i++) for (int j = i + 1; j < n && j - i <= W; j++) { |
| 741 | double v = S.alpha[i][j] + S.alpha[j][i]; |
| 742 | sc[i + 1][j + 1] = sc[j + 1][i + 1] = v > 0 ? v : NEG; |
| 743 | } |
| 744 | eisner(m, hd); |
| 745 | for (int d = 1; d < m; d++) head[o + d - 1] = hd[d] - 1; |
| 746 | } |
| 747 | fwrite(&C->ntok, sizeof(int), 1, f); fwrite(lab, sizeof(int), C->ntok, f); fwrite(head, sizeof(int), C->ntok, f); |
| 748 | free(lab); free(head); |
| 749 | } |
| 750 | fclose(f); |
| 587 | 751 | } |
| 588 | 752 | |
| 589 | 753 | // X-COLL (PREREG-v2): every occurrence of a word gets the word's most frequent label in lab. |
| … | … | @@ -616,15 +780,17 @@ static corpus_t shuffle_within (corpus_t C, uint64_t seed) { |
| 616 | 780 | int main (int argc, char **argv) { |
| 617 | 781 | if (argc < 2) { fprintf(stderr, "usage: jt <seed> [--tiers a,b] [--heldout N] [--K k] [--shuffle] [--explain f] [--log f]\n"); return 1; } |
| 618 | 782 | int seed = atoi(argv[1]), tiers[16] = { 3000, 10000, 30000 }, ntier = 3, nho = 2000, shuffle = 0, v1 = 0; |
| 619 | | const char *explain_path = NULL, *log_path = NULL; |
| 783 | const char *explain_path = NULL, *log_path = NULL, *dump_prefix = NULL; |
| 620 | 784 | for (int i = 2; i < argc; i++) { |
| 621 | 785 | if (!strcmp(argv[i], "--heldout") && i + 1 < argc) nho = atoi(argv[++i]); |
| 622 | 786 | else if (!strcmp(argv[i], "--K") && i + 1 < argc) KJ = atoi(argv[++i]); |
| 623 | 787 | else if (!strcmp(argv[i], "--shuffle")) shuffle = 1; |
| 624 | 788 | else if (!strcmp(argv[i], "--v1")) v1 = 1; |
| 789 | else if (!strcmp(argv[i], "--v3")) { v1 = 1; MODE = 2; V3 = 1; } |
| 625 | 790 | else if (!strcmp(argv[i], "--v2") && i + 1 < argc) { v1 = 1; i++; MODE = !strcmp(argv[i], "word") ? 1 : !strcmp(argv[i], "dj") ? 2 : -1; if (MODE < 0) { fprintf(stderr, "--v2 word|dj\n"); return 1; } } |
| 626 | 791 | else if (!strcmp(argv[i], "--explain") && i + 1 < argc) explain_path = argv[++i]; |
| 627 | 792 | else if (!strcmp(argv[i], "--log") && i + 1 < argc) log_path = argv[++i]; |
| 793 | else if (!strcmp(argv[i], "--dump") && i + 1 < argc) dump_prefix = argv[++i]; |
| 628 | 794 | else if (!strcmp(argv[i], "--tiers") && i + 1 < argc) { |
| 629 | 795 | ntier = 0; |
| 630 | 796 | for (char *p = strtok(argv[++i], ","); p && ntier < 16; p = strtok(NULL, ",")) tiers[ntier++] = atoi(p); |
| … | … | @@ -638,7 +804,7 @@ int main (int argc, char **argv) { |
| 638 | 804 | for (int i = 0; i < ntier; i++) if (tiers[i] > maxt) maxt = tiers[i]; |
| 639 | 805 | corpus_t full = gen_corpus(1000003ULL * seed + 1, maxt), ho = gen_corpus(1000003ULL * seed + 2, nho); |
| 640 | 806 | if (shuffle) { full = shuffle_within(full, 99ULL * seed + 1); ho = shuffle_within(ho, 99ULL * seed + 2); } |
| 641 | | const char *tag = shuffle ? "-SHUF" : "", *nm = MODE == 0 ? "V1" : MODE == 1 ? "V2W" : "V2D"; |
| 807 | const char *tag = shuffle ? "-SHUF" : "", *nm = V3 ? "V3" : MODE == 0 ? "V1" : MODE == 1 ? "V2W" : "V2D"; |
| 642 | 808 | int *lab = malloc(sizeof(int) * ho.ntok); |
| 643 | 809 | params_t p; params_alloc(&p); |
| 644 | 810 | |
| … | … | @@ -674,6 +840,8 @@ int main (int argc, char **argv) { |
| 674 | 840 | collapse(&ho, lab, KJ, lc); score_labels(&ho, lc, KJ, &vm, &m1, &se); free(lc); |
| 675 | 841 | printf("V\t%d\t%d\t%d\tJT-%s-COLL%s\t%.5f\t%.5f\t%.5f\n", seed, tier, KJ, nm, tag, vm, m1, se); |
| 676 | 842 | printf("L\t%d\t%d\t%d\tATTN-%s%s\t%.5f\n", seed, tier, KJ, nm, tag, attention_parse_f1(&ho, &p)); |
| 843 | if (V3) printf("S\t%d\t%d\t%d\tJT-%s%s\t%ld\t%ld\t%ld\t%ld\n", seed, tier, KJ, nm, tag, st_elig, st_split, st_split_poly, st_poly_elig); |
| 844 | if (dump_prefix) { char dp[512]; snprintf(dp, sizeof dp, "%s-%d.bin", dump_prefix, tier); dump_layer1(dp, &tr, &ho, &p); } |
| 677 | 845 | for (int t = 0; t < ho.ntok; t++) lab[t] = cls[ho.w[t]]; |
| 678 | 846 | score_labels(&ho, lab, KJ, &vm, &m1, &se); |
| 679 | 847 | printf("V\t%d\t%d\t%d\tTYPE-A%s\t%.5f\t%.5f\t%.5f\n", seed, tier, KJ, tag, vm, m1, se); |