| … | … | @@ -216,6 +216,75 @@ static void link_prf (const corpus_t *C, const parse_t *P, double *pr, double *r |
| 216 | 216 | *f1 = *pr + *rc > 0 ? 2 * *pr * *rc / (*pr + *rc) : 0; |
| 217 | 217 | } |
| 218 | 218 | |
| 219 | // ---------------------------------------------------------------- re-parse loop (PREREG-reparse.md) |
| 220 | |
| 221 | #define KR 30 |
| 222 | #define ROUNDS 5 |
| 223 | |
| 224 | // Exchange algorithm over weighted ordered word pairs Wp (left a, right b); copied from |
| 225 | // ../jigsaw-transformer/jt.c exchange_weighted (K = KR). |
| 226 | static int *exchange_pairs (const double *Wp, const corpus_t *tr) { |
| 227 | double *freq = calloc(V, sizeof(double)); |
| 228 | for (int t = 0; t < tr->ntok; t++) freq[tr->w[t]] += 1; |
| 229 | int *order = malloc(sizeof(int) * V); |
| 230 | for (int x = 0; x < V; x++) order[x] = x; |
| 231 | for (int i = 1; i < V; i++) { int x = order[i], j = i; while (j > 0 && freq[order[j - 1]] < freq[x]) { order[j] = order[j - 1]; j--; } order[j] = x; } |
| 232 | int *cls = malloc(sizeof(int) * V); |
| 233 | for (int i = 0; i < V; i++) cls[order[i]] = i % KR; |
| 234 | double *Nc = calloc((size_t) KR * KR, sizeof(double)); |
| 235 | for (int a = 0; a < V; a++) for (int b = 0; b < V; b++) { double v = Wp[(size_t) a * V + b]; if (v > 0) Nc[cls[a] * KR + cls[b]] += v; } |
| 236 | double sv[64], pv[64]; |
| 237 | for (int pass = 0; pass < 30; pass++) { |
| 238 | int moves = 0; |
| 239 | for (int i = 0; i < V; i++) { |
| 240 | int x = order[i]; |
| 241 | if (freq[x] == 0) continue; |
| 242 | int a = cls[x]; |
| 243 | double self = Wp[(size_t) x * V + x]; |
| 244 | memset(sv, 0, sizeof sv); memset(pv, 0, sizeof pv); |
| 245 | for (int y = 0; y < V; y++) { |
| 246 | if (y == x) continue; |
| 247 | double r = Wp[(size_t) x * V + y], l = Wp[(size_t) y * V + x]; |
| 248 | if (r > 0) sv[cls[y]] += r; |
| 249 | if (l > 0) pv[cls[y]] += l; |
| 250 | } |
| 251 | for (int c = 0; c < KR; c++) { Nc[a * KR + c] -= sv[c]; Nc[c * KR + a] -= pv[c]; } |
| 252 | Nc[a * KR + a] -= self; |
| 253 | int best = a; double bestF = -1e300; |
| 254 | for (int b = 0; b < KR; b++) { |
| 255 | for (int c = 0; c < KR; c++) { Nc[b * KR + c] += sv[c]; Nc[c * KR + b] += pv[c]; } |
| 256 | Nc[b * KR + b] += self; |
| 257 | double f = 0; |
| 258 | for (int r = 0; r < KR; r++) { |
| 259 | double row = 0, col = 0; |
| 260 | for (int c = 0; c < KR; c++) { double v = Nc[r * KR + c]; if (v > 0) f += v * log(v); row += Nc[r * KR + c]; col += Nc[c * KR + r]; } |
| 261 | if (row > 0) f -= row * log(row); |
| 262 | if (col > 0) f -= col * log(col); |
| 263 | } |
| 264 | if (f > bestF + 1e-9 || (b == a && f >= bestF - 1e-9)) { bestF = f; best = b; } |
| 265 | for (int c = 0; c < KR; c++) { Nc[b * KR + c] -= sv[c]; Nc[c * KR + b] -= pv[c]; } |
| 266 | Nc[b * KR + b] -= self; |
| 267 | } |
| 268 | for (int c = 0; c < KR; c++) { Nc[best * KR + c] += sv[c]; Nc[c * KR + best] += pv[c]; } |
| 269 | Nc[best * KR + best] += self; |
| 270 | if (best != a) { cls[x] = best; moves++; } |
| 271 | } |
| 272 | if (!moves) break; |
| 273 | } |
| 274 | free(freq); free(order); free(Nc); |
| 275 | return cls; |
| 276 | } |
| 277 | |
| 278 | // Many-to-one accuracy of labels lab[] (< nlab) against gold token classes. |
| 279 | static double m1_acc (const corpus_t *C, const int *lab, int nlab) { |
| 280 | int *ct = calloc((size_t) nlab * NCLS, sizeof(int)); |
| 281 | for (int t = 0; t < C->ntok; t++) ct[(size_t) lab[t] * NCLS + C->c[t]]++; |
| 282 | long ok = 0; |
| 283 | for (int l = 0; l < nlab; l++) { int b = 0; for (int g = 1; g < NCLS; g++) if (ct[(size_t) l * NCLS + g] > b) b = ct[(size_t) l * NCLS + g]; ok += b; } |
| 284 | free(ct); |
| 285 | return (double) ok / C->ntok; |
| 286 | } |
| 287 | |
| 219 | 288 | // ---------------------------------------------------------------- span-type table |
| 220 | 289 | |
| 221 | 290 | typedef struct { uint16_t s[LMAX]; uint8_t len, used; uint32_t cnum, hard, first; float soft; } span_t; // hard/first/soft: Test A-soft |
| … | … | @@ -461,17 +530,18 @@ static void run_soft (int seed, int tier, const char *src, const corpus_t *tr, c |
| 461 | 530 | // ---------------------------------------------------------------- main |
| 462 | 531 | |
| 463 | 532 | int main (int argc, char **argv) { |
| 464 | | if (argc < 2) { fprintf(stderr, "usage: l2 <seed> [--tiers a,b] [--heldout N] [--mut-drop] [--jt prefix] [--soft [--soft-scale s]] [--undirected]\n"); return 1; } |
| 533 | if (argc < 2) { fprintf(stderr, "usage: l2 <seed> [--tiers a,b] [--heldout N] [--mut-drop] [--jt prefix] [--soft [--soft-scale s]] [--undirected] [--reparse]\n"); return 1; } |
| 465 | 534 | int seed = atoi(argv[1]); |
| 466 | 535 | int tiers[16] = { 3000, 10000, 30000 }, ntier = 3, nho = 2000; |
| 467 | 536 | const char *jt_prefix = NULL; |
| 468 | | int soft = 0, undirected = 0; double soft_scale = 1.0; |
| 537 | int soft = 0, undirected = 0, reparse = 0; double soft_scale = 1.0; |
| 469 | 538 | for (int i = 2; i < argc; i++) { |
| 470 | 539 | if (!strcmp(argv[i], "--mut-drop")) mut_drop = 1; |
| 471 | 540 | else if (!strcmp(argv[i], "--heldout") && i + 1 < argc) nho = atoi(argv[++i]); |
| 472 | 541 | else if (!strcmp(argv[i], "--jt") && i + 1 < argc) jt_prefix = argv[++i]; |
| 473 | 542 | else if (!strcmp(argv[i], "--soft")) soft = 1; |
| 474 | 543 | else if (!strcmp(argv[i], "--undirected")) undirected = 1; |
| 544 | else if (!strcmp(argv[i], "--reparse")) reparse = 1; |
| 475 | 545 | else if (!strcmp(argv[i], "--soft-scale") && i + 1 < argc) soft_scale = atof(argv[++i]); |
| 476 | 546 | else if (!strcmp(argv[i], "--tiers") && i + 1 < argc) { |
| 477 | 547 | ntier = 0; |
| … | … | @@ -599,6 +669,45 @@ int main (int argc, char **argv) { |
| 599 | 669 | free(MI); |
| 600 | 670 | } |
| 601 | 671 | } |
| 672 | // PREREG-reparse.md: round 0 parses words; round r clusters words over the round r-1 parse links, re-parses. |
| 673 | if (reparse) { |
| 674 | if (!Ptr.lnk) { Ptr = parse_alloc(full.ntok); Pho = parse_alloc(ho.ntok); } |
| 675 | u_tr = &Ptr; u_ho = &Pho; |
| 676 | int *cls = NULL; |
| 677 | double *Wp = malloc(sizeof(double) * V * V); |
| 678 | for (int r = 0; r <= ROUNDS; r++) { |
| 679 | int A = V; |
| 680 | if (r == 0) { memcpy(str, tr.w, sizeof(int) * tr.ntok); memcpy(sho, ho.w, sizeof(int) * ho.ntok); } |
| 681 | else { |
| 682 | memset(Wp, 0, sizeof(double) * V * V); |
| 683 | for (int s = 0; s < tr.ns; s++) { |
| 684 | int o = tr.off[s], n = tr.off[s + 1] - o; |
| 685 | for (int t = 0; t < n; t++) for (int k = 0; k < Ptr.nl[o + t]; k++) { |
| 686 | int b = Ptr.lnk[(o + t) * MAXL + k]; |
| 687 | if (b > t) Wp[(size_t) tr.w[o + t] * V + tr.w[o + b]] += 1; |
| 688 | } |
| 689 | } |
| 690 | free(cls); |
| 691 | cls = exchange_pairs(Wp, &tr); |
| 692 | for (int t = 0; t < tr.ntok; t++) str[t] = cls[tr.w[t]]; |
| 693 | for (int t = 0; t < ho.ntok; t++) sho[t] = cls[ho.w[t]]; |
| 694 | A = KR; |
| 695 | } |
| 696 | corpus_t trs = with_symbols(&tr, str), hos = with_symbols(&ho, sho); |
| 697 | arm_t a = { "l1", A, NULL, 1 }; |
| 698 | pairs_t P = count_pairs(&a, &trs); |
| 699 | double *MI = pair_mi(&P), pr, rc, f1; |
| 700 | free_pairs(&P); |
| 701 | parse_u(&trs, MI, A, 0, &Ptr); parse_u(&hos, MI, A, 0, &Pho); |
| 702 | free(MI); |
| 703 | link_prf(&hos, &Pho, &pr, &rc, &f1); |
| 704 | printf("RP\t%d\t%d\t%d\t%.5f\t%.5f\t%.5f\t%.5f\n", seed, tier, r, pr, rc, f1, m1_acc(&ho, sho, A)); |
| 705 | char nm[32]; |
| 706 | snprintf(nm, sizeof nm, "M-RP%d-ucomp", r); |
| 707 | run_arm(seed, tier, nm, RULE_UCOMP, &trs, &hos, &ho, (const int *) u_tr, (const int *) u_ho); |
| 708 | } |
| 709 | free(cls); free(Wp); |
| 710 | } |
| 602 | 711 | free(Lmap); |
| 603 | 712 | |
| 604 | 713 | // PREREG-soft.md: rank span types by assembly-ness over KPARSE perturbed parses (sources G and L). |