| … | … | @@ -5,6 +5,7 @@ |
| 5 | 5 | // [--v2 word|dj] PREREG-v2 arms (implies the v1 pipeline); adds JT-<arm>-COLL rows |
| 6 | 6 | // [--v3] PREREG-v3: V2-DJ + sense units split by disjuncts; adds S rows (eligible, split, split∩poly, poly∩eligible) |
| 7 | 7 | // [--splitdiag] PREREG-v3diag: D rows (split test on gold/JT parse x gold/JT classes); skips audit and baselines |
| 8 | // [--splitdiag2] PREREG-v3pool: D rows for GG, JG1, JG6, PG1, PG6 (pooled-parse connectors, gold classes) |
| 8 | 9 | // [--dump prefix] write <prefix>-<tier>.bin (final labels + attention parse) for ../level2/l2 --jt |
| 9 | 10 | // --v1 runs the PREREG-v1 learner (layer 0 from word-pair PMI, classes by exchange over attended pairs). |
| 10 | 11 | // C <seed> <tier> <K> <method> <checkpoint> <vmeasure> <m1acc> <senseacc> v1 continuous checkpoints |
| … | … | @@ -708,6 +709,31 @@ static void train_v3 (const corpus_t *tr, params_t *p, int **cls_out, double per |
| 708 | 709 | free(tcon); free(tnc); free(start); free(occ); free(fill); free(minor); free(tru.w); |
| 709 | 710 | } |
| 710 | 711 | |
| 712 | // Split test over all eligible words with the bags in tcon/tnc; prints one D row. |
| 713 | static void split_stats (const corpus_t *tr, const int *start, const int *occ, const int *tcon, const int *tnc, int *minor, |
| 714 | int seed, int tier, const char *cell) { |
| 715 | long elig = 0, split = 0, split_poly = 0, poly_elig = 0, nmp = 0; |
| 716 | double mp = 0, mr = 0; |
| 717 | for (int w = 0; w < V; w++) { |
| 718 | int m = start[w + 1] - start[w]; |
| 719 | const int *ow = occ + start[w]; |
| 720 | if (m < SPLIT_MIN) continue; |
| 721 | elig++; poly_elig += poly[w]; |
| 722 | if (!split_word(ow, m, tcon, tnc, minor)) continue; |
| 723 | split++; |
| 724 | if (!poly[w]) continue; |
| 725 | split_poly++; |
| 726 | int cnt[64] = { 0 }, c1 = -1, c2 = -1; |
| 727 | for (int i = 0; i < m; i++) cnt[tr->c[ow[i]]]++; |
| 728 | for (int g = 0; g < NCLS; g++) { if (c1 < 0 || cnt[g] > cnt[c1]) { c2 = c1; c1 = g; } else if (c2 < 0 || cnt[g] > cnt[c2]) c2 = g; } |
| 729 | long inm = 0, ins = 0, both = 0; |
| 730 | for (int i = 0; i < m; i++) { int a = minor[i], b = tr->c[ow[i]] == c2; inm += a; ins += b; both += a && b; } |
| 731 | mp += inm ? (double) both / inm : 0; mr += ins ? (double) both / ins : 0; nmp++; |
| 732 | } |
| 733 | printf("D\t%d\t%d\t%d\t%s\t%ld\t%ld\t%ld\t%ld\t%.5f\t%.5f\t%ld\n", seed, tier, KJ, cell, elig, split, split_poly, poly_elig, |
| 734 | nmp ? mp / nmp : 0, nmp ? mr / nmp : 0, nmp); |
| 735 | } |
| 736 | |
| 711 | 737 | // PREREG-v3diag.md: the v3 split test on links from parse {gold tree, JT attention parse} x connector classes |
| 712 | 738 | // {gold, JT label}. D rows: cell, eligible, split, split&poly, poly&eligible, mean minor-component precision and |
| 713 | 739 | // recall over split polysemous words, number of such words. |
| … | … | @@ -748,30 +774,88 @@ static void split_diag (const corpus_t *tr, const params_t *p, int seed, int tie |
| 748 | 774 | tnc[o + t] = nc; |
| 749 | 775 | } |
| 750 | 776 | } |
| 751 | | long elig = 0, split = 0, split_poly = 0, poly_elig = 0, nmp = 0; |
| 752 | | double mp = 0, mr = 0; |
| 753 | | for (int w = 0; w < V; w++) { |
| 754 | | int m = start[w + 1] - start[w]; |
| 755 | | const int *ow = occ + start[w]; |
| 756 | | if (m < SPLIT_MIN) continue; |
| 757 | | elig++; poly_elig += poly[w]; |
| 758 | | if (!split_word(ow, m, tcon, tnc, minor)) continue; |
| 759 | | split++; |
| 760 | | if (!poly[w]) continue; |
| 761 | | split_poly++; |
| 762 | | int cnt[64] = { 0 }, c1 = -1, c2 = -1; |
| 763 | | for (int i = 0; i < m; i++) cnt[tr->c[ow[i]]]++; |
| 764 | | for (int g = 0; g < NCLS; g++) { if (c1 < 0 || cnt[g] > cnt[c1]) { c2 = c1; c1 = g; } else if (c2 < 0 || cnt[g] > cnt[c2]) c2 = g; } |
| 765 | | long inm = 0, ins = 0, both = 0; |
| 766 | | for (int i = 0; i < m; i++) { int a = minor[i], b = tr->c[ow[i]] == c2; inm += a; ins += b; both += a && b; } |
| 767 | | mp += inm ? (double) both / inm : 0; mr += ins ? (double) both / ins : 0; nmp++; |
| 768 | | } |
| 769 | | printf("D\t%d\t%d\t%d\t%s\t%ld\t%ld\t%ld\t%ld\t%.5f\t%.5f\t%ld\n", seed, tier, KJ, cell[c], elig, split, split_poly, poly_elig, |
| 770 | | nmp ? mp / nmp : 0, nmp ? mr / nmp : 0, nmp); |
| 777 | split_stats(tr, start, occ, tcon, tnc, minor, seed, tier, cell[c]); |
| 771 | 778 | } |
| 772 | 779 | free(jl); free(jh); free(start); free(occ); free(fill); free(tcon); free(tnc); free(minor); |
| 773 | 780 | } |
| 774 | 781 | |
| 782 | // PREREG-v3pool.md: gold connector classes; links from the gold tree, the JT attention parse or the window-PMI |
| 783 | // parse over JT labels, each as the max parse or the consensus (>= 4 of 6) of Gumbel-perturbed parses. |
| 784 | #define NPOOL 6 |
| 785 | static void split_diag2 (const corpus_t *tr, const params_t *p, int seed, int tier) { |
| 786 | int N = tr->ntok, hd[MAXN + 1]; |
| 787 | int *jl = malloc(sizeof(int) * N); |
| 788 | double *ae = malloc(sizeof(double) * N * W); // ae[t*W + d-1] = α_{t,t+d} + α_{t+d,t} |
| 789 | static sent_state_t S; |
| 790 | for (int s = 0; s < tr->ns; s++) { |
| 791 | int o = tr->off[s], n = tr->off[s + 1] - o; |
| 792 | estep(tr, o, n, p, &S); |
| 793 | for (int t = 0; t < n; t++) { |
| 794 | int b = 0; for (int z = 1; z < KJ; z++) if (S.q[t][z] > S.q[t][b]) b = z; jl[o + t] = b; |
| 795 | for (int d = 1; d <= W; d++) ae[(size_t) (o + t) * W + d - 1] = t + d < n ? S.alpha[t][t + d] + S.alpha[t + d][t] : 0; |
| 796 | } |
| 797 | } |
| 798 | corpus_t Lc = *tr; Lc.c = jl; |
| 799 | arm_t a = { "l1", KJ, NULL, 1 }; |
| 800 | pairs_t P = count_pairs(&a, &Lc); |
| 801 | long ev, acc; |
| 802 | double *Sc = hard_scores(&P, &ev, &acc); |
| 803 | free_pairs(&P); |
| 804 | double mJ = 0, mP = 0; long nJ = 0, nP = 0; |
| 805 | for (int s = 0; s < tr->ns; s++) { |
| 806 | int o = tr->off[s], n = tr->off[s + 1] - o; |
| 807 | for (int i = 0; i < n; i++) for (int j = i + 1; j < n && j - i <= W; j++) { |
| 808 | double v = ae[(size_t) (o + i) * W + j - i - 1], u = Sc[(size_t) jl[o + i] * KJ + jl[o + j]]; |
| 809 | if (v > 0) { mJ += v; nJ++; } |
| 810 | if (u > NEG && u > 0) { mP += u; nP++; } |
| 811 | } |
| 812 | } |
| 813 | mJ = nJ ? mJ / nJ : 0; mP = nP ? mP / nP : 0; |
| 814 | printf("#\tsplitdiag2\tmean_pos_edge_JT=%.5f\tmean_pos_edge_PMI=%.5f\n", mJ, mP); |
| 815 | int *start = calloc(V + 1, sizeof(int)), *occ = malloc(sizeof(int) * N), *fill = malloc(sizeof(int) * V); |
| 816 | for (int t = 0; t < N; t++) start[tr->w[t] + 1]++; |
| 817 | for (int w = 0; w < V; w++) start[w + 1] += start[w]; |
| 818 | memcpy(fill, start, sizeof(int) * V); |
| 819 | for (int t = 0; t < N; t++) occ[fill[tr->w[t]]++] = t; |
| 820 | int *tcon = malloc(sizeof(int) * N * MAXC), *tnc = malloc(sizeof(int) * N), *minor = malloc(sizeof(int) * N); |
| 821 | static const char *cell[5] = { "GG", "JG1", "JG6", "PG1", "PG6" }; |
| 822 | static int lc[MAXN][MAXN]; |
| 823 | for (int c = 0; c < 5; c++) { |
| 824 | int pmi = c >= 3, KP = c == 2 || c == 4 ? NPOOL : 1, need = KP == 1 ? 1 : 4; |
| 825 | double scale = 0.3 * (pmi ? mP : mJ); |
| 826 | rng_t r = { 7777ULL * seed + 31ULL * tier + c }; |
| 827 | for (int s = 0; s < tr->ns; s++) { |
| 828 | int o = tr->off[s], n = tr->off[s + 1] - o, m = n + 1; |
| 829 | for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) lc[i][j] = 0; |
| 830 | if (c == 0) { |
| 831 | for (int t = 0; t < n; t++) { int h = tr->h[o + t]; if (h >= 0) { lc[t][h]++; lc[h][t]++; } } |
| 832 | } else for (int k = 0; k < KP; k++) { |
| 833 | for (int h = 0; h < m; h++) for (int d = 0; d < m; d++) sc[h][d] = NEG; |
| 834 | for (int d = 1; d < m; d++) sc[0][d] = 0; |
| 835 | for (int i = 0; i < n; i++) for (int j = i + 1; j < n && j - i <= W; j++) { |
| 836 | double v; |
| 837 | if (pmi) v = Sc[(size_t) jl[o + i] * KJ + jl[o + j]]; |
| 838 | else { v = ae[(size_t) (o + i) * W + j - i - 1]; if (!(v > 0)) v = NEG; } |
| 839 | if (KP > 1 && v > NEG) { double u = runif(&r); if (u <= 0) u = 1e-300; v += scale * -log(-log(u)); } |
| 840 | sc[i + 1][j + 1] = sc[j + 1][i + 1] = v; |
| 841 | } |
| 842 | eisner(m, hd); |
| 843 | for (int d = 1; d < m; d++) { int h = hd[d] - 1; if (h >= 0) { lc[d - 1][h]++; lc[h][d - 1]++; } } |
| 844 | } |
| 845 | for (int t = 0; t < n; t++) { |
| 846 | int nc = 0; |
| 847 | for (int j = 0; j < n; j++) { |
| 848 | if (j == t || lc[t][j] < need) continue; |
| 849 | if (nc < MAXC) tcon[(size_t) (o + t) * MAXC + nc++] = (j < t ? 0 : 64) + tr->c[o + j]; |
| 850 | } |
| 851 | tnc[o + t] = nc; |
| 852 | } |
| 853 | } |
| 854 | split_stats(tr, start, occ, tcon, tnc, minor, seed, tier, cell[c]); |
| 855 | } |
| 856 | free(jl); free(ae); free(Sc); free(start); free(occ); free(fill); free(tcon); free(tnc); free(minor); |
| 857 | } |
| 858 | |
| 775 | 859 | static void train_v1 (const corpus_t *tr, params_t *p, int **cls_out, double perturb) { |
| 776 | 860 | if (V3) train_v3(tr, p, cls_out, perturb); |
| 777 | 861 | else train_core(tr, tr, V, p, cls_out, perturb); |
| … | … | @@ -844,7 +928,7 @@ static corpus_t shuffle_within (corpus_t C, uint64_t seed) { |
| 844 | 928 | |
| 845 | 929 | int main (int argc, char **argv) { |
| 846 | 930 | if (argc < 2) { fprintf(stderr, "usage: jt <seed> [--tiers a,b] [--heldout N] [--K k] [--shuffle] [--explain f] [--log f]\n"); return 1; } |
| 847 | | int seed = atoi(argv[1]), tiers[16] = { 3000, 10000, 30000 }, ntier = 3, nho = 2000, shuffle = 0, v1 = 0, splitdiag = 0; |
| 931 | int seed = atoi(argv[1]), tiers[16] = { 3000, 10000, 30000 }, ntier = 3, nho = 2000, shuffle = 0, v1 = 0, splitdiag = 0, splitdiag2 = 0; |
| 848 | 932 | const char *explain_path = NULL, *log_path = NULL, *dump_prefix = NULL; |
| 849 | 933 | for (int i = 2; i < argc; i++) { |
| 850 | 934 | if (!strcmp(argv[i], "--heldout") && i + 1 < argc) nho = atoi(argv[++i]); |
| … | … | @@ -852,6 +936,7 @@ int main (int argc, char **argv) { |
| 852 | 936 | else if (!strcmp(argv[i], "--shuffle")) shuffle = 1; |
| 853 | 937 | else if (!strcmp(argv[i], "--v1")) v1 = 1; |
| 854 | 938 | else if (!strcmp(argv[i], "--splitdiag")) splitdiag = 1; |
| 939 | else if (!strcmp(argv[i], "--splitdiag2")) splitdiag2 = 1; |
| 855 | 940 | else if (!strcmp(argv[i], "--v3")) { v1 = 1; MODE = 2; V3 = 1; } |
| 856 | 941 | 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; } } |
| 857 | 942 | else if (!strcmp(argv[i], "--explain") && i + 1 < argc) explain_path = argv[++i]; |
| … | … | @@ -907,6 +992,7 @@ int main (int argc, char **argv) { |
| 907 | 992 | 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); |
| 908 | 993 | printf("L\t%d\t%d\t%d\tATTN-%s%s\t%.5f\n", seed, tier, KJ, nm, tag, attention_parse_f1(&ho, &p)); |
| 909 | 994 | if (splitdiag) { split_diag(&tr, &p, seed, tier); free(cls); continue; } |
| 995 | if (splitdiag2) { split_diag2(&tr, &p, seed, tier); free(cls); continue; } |
| 910 | 996 | 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); |
| 911 | 997 | if (dump_prefix) { char dp[512]; snprintf(dp, sizeof dp, "%s-%d.bin", dump_prefix, tier); dump_layer1(dp, &tr, &ho, &p); } |
| 912 | 998 | for (int t = 0; t < ho.ntok; t++) lab[t] = cls[ho.w[t]]; |