nx_absa_seq.nx
buildroot/runtime/nx_absa_seq.nx
about
nx_absa_seq.nx -- rung IM28: a SUPERVISED SEQUENCE model for aspect-term extraction (SemEval-2014 Task 4 SB1),
the step that closes the gap from the dictionary baseline (~46 F1) toward the CRF winners (74-84 F1). It is an
AVERAGED STRUCTURED PERCEPTRON with Viterbi BIO decoding -- integer weights only, so it runs in the nofloat
estate; the CRF winners add a probabilistic objective and POS/parse features, which we do not have a tagger for,
so this is the honest reachable model, not a claim of parity. Trained on the TRAIN split, decoded on the TEST
split, scored by the same set-overlap F1 (nx_absa_lib) the other arms use so the numbers are comparable.
FEATURES per token (the hashing trick, one big integer weight table): word, 3-char prefix and suffix, previous
and next word, a has-digit shape, dictionary membership (the supervised dictionary signal folded in as a
feature), and a bias. TRANSITIONS between BIO tags, with I only reachable from B or I. Averaging is the standard
lazy scheme (a weight's average is its running sum plus its current value times the time since it last changed);
for the Viterbi argmax the common divisor is dropped. license_tier: ORIGINAL No hw writes (Rule 26). LIB.
dependencies 8 imports · 2 importers
imports: nx_syscalls.nxnx_reviewmine_lib.nxnx_absa_lib.nxnx_postag.nxnx_depparse.nxnx_wordclust.nxnx_logadd_lib.nxnx_embfeat_lib.nx
imported by: nx_absa_seq_gate.nxnx_absa_seqextract.nx
structs
| none |
consts
| 22 | const SP_W: i64 = 1048576 // 2^20 feature-weight slots |
| 23 | const SP_WMASK: i64 = 1048575 |
| 24 | const SP_NTAGS: i64 = 3 |
| 25 | const SP_O: i64 = 0 |
| 26 | const SP_B: i64 = 1 |
| 27 | const SP_I: i64 = 2 |
| 28 | const SP_NTRANS: i64 = 9 // SP_NTAGS * SP_NTAGS |
| 29 | const SP_EPOCHS: i64 = 16 // 8 left a third of sentences still updating; doubled for convergence |
| 30 | const SP_TOK_CAP: i64 = 256 // tokens per sentence |
| 31 | const SP_TOKBUF: i64 = 16384 |
| 32 | const SP_FEATBUF: i64 = 128 |
| 33 | const SP_NFEAT: i64 = 53 // 8 base + prev2, next2, prev+cur, cur+next, 4-char suffix, dictionary reliability, 5 POS, 5 parse, 5 cluster, 24 embedding |
| 38 | const SP_PFX_EMB: i64 = 108 // 'l' embedding coordinate families (the payload carries the coordinate index) |
| 39 | const SP_EMB_BASE: i64 = 29 // first embedding feature index |
| 40 | const SP_EMB_DIMS: i64 = 24 // the table's coordinate count; a table of another width refuses the mode |
| 45 | const SP_PFX_CL0: i64 = 90 // 'Z' this token's cluster at the coarsest level |
| 46 | const SP_PFX_CL1: i64 = 97 // 'a' this token's cluster at the middle level |
| 47 | const SP_PFX_CL2: i64 = 105 // 'i' this token's cluster at the finest level |
| 48 | const SP_PFX_CLP: i64 = 106 // 'j' previous token's cluster, middle level |
| 49 | const SP_PFX_CLN: i64 = 107 // 'k' next token's cluster, middle level |
| 50 | const SP_CLUST_BASE: i64 = 24 // first cluster feature index |
| 51 | const SP_CL_MID: i64 = 1 // the middle granularity's level index |
| 60 | const SP_CRF_ETA_NUM: i64 = 1 |
| 61 | const SP_CRF_ETA_DEN: i64 = 4 |
| 62 | const SP_NPAIR: i64 = 9 // SP_NTAGS * SP_NTAGS pair marginals per position |
| 63 | const SP_CRF_NLL_SLACK: i64 = 8 // Q10 units a chain of log-adds may lose: a sentence NLL below -slack is impossible and counted |
| 68 | const SP_PFX_REL: i64 = 82 // 'R' relation to the head |
| 69 | const SP_PFX_HW: i64 = 72 // 'H' head word ('^' for the root) |
| 70 | const SP_PFX_HT: i64 = 71 // 'G' head tag ('$' for the root) |
| 71 | const SP_PFX_RELT: i64 = 74 // 'J' relation + own tag |
| 72 | const SP_PFX_CHR: i64 = 75 // 'K' relations of the leftmost and rightmost dependents |
| 73 | const SP_PARSE_BASE: i64 = 19 // first parse feature index |
| 78 | const SP_PFX_POS: i64 = 84 // 'T' tag of this token |
| 79 | const SP_PFX_POSP: i64 = 85 // 'U' previous token's tag |
| 80 | const SP_PFX_POSN: i64 = 86 // 'V' next token's tag |
| 81 | const SP_PFX_POSPC: i64 = 88 // 'X' previous+current tag bigram |
| 82 | const SP_PFX_POSCN: i64 = 89 // 'Y' current+next tag bigram |
| 83 | const SP_POS_BASE: i64 = 14 // first POS feature index |
| 89 | const SP_BUCKETS: i64 = 4 // reliability quantised to fifths: 0..4 of SP_BUCKETS |
| 90 | const SP_PFX_E: i64 = 101 // 'e' dictionary reliability bucket |
| 91 | const SP_CH_DASH: i64 = 45 // '-' the bucket of a token not in the dictionary |
| 92 | const SP_MAJ_NUM: i64 = 2 // gold * 2 >= seen <=> an aspect in at least half its occurrences |
| 93 | const SP_AFF: i64 = 3 // affix length |
| 94 | const SP_AFF4: i64 = 4 // the longer suffix (a second affix family, not a replacement) |
| 95 | const SP_NEG: i64 = 0 - 1000000000 // Viterbi -infinity |
| 96 | const SP_GRAM_CAP: i64 = 512 |
| 98 | const SP_PFX_W: i64 = 119 // 'w' |
| 99 | const SP_PFX_P: i64 = 112 // 'p' prefix |
| 100 | const SP_PFX_S: i64 = 115 // 's' suffix |
| 101 | const SP_PFX_PW: i64 = 80 // 'P' previous word |
| 102 | const SP_PFX_NW: i64 = 78 // 'N' next word |
| 103 | const SP_PFX_SH: i64 = 104 // 'h' shape |
| 104 | const SP_PFX_D: i64 = 100 // 'd' dictionary membership |
| 105 | const SP_PFX_B: i64 = 98 // 'b' bias |
| 106 | const SP_PFX_PW2: i64 = 81 // 'Q' word two back |
| 107 | const SP_PFX_NW2: i64 = 77 // 'M' word two ahead |
| 108 | const SP_PFX_PC: i64 = 99 // 'c' previous+current conjunction |
| 109 | const SP_PFX_CN: i64 = 67 // 'C' current+next conjunction |
| 110 | const SP_PFX_S4: i64 = 83 // 'S' 4-char suffix |
| 111 | const SP_CH_DIGIT0: i64 = 48 |
| 112 | const SP_CH_DIGIT9: i64 = 57 |
| 113 | const SP_CH_ONE: i64 = 49 |
| 114 | const SP_CH_ZERO: i64 = 48 |
| 115 | const SP_CH_CARET: i64 = 94 // '^' BOS |
| 116 | const SP_CH_DOLLAR2: i64 = 36 // '$' EOS |
| 118 | const SP_O_INTER: i64 = 0 |
| 119 | const SP_O_NS: i64 = 1 |
| 120 | const SP_O_NG: i64 = 2 |
| 121 | const SP_O_P: i64 = 3 |
| 122 | const SP_O_R: i64 = 4 |
| 123 | const SP_O_F1: i64 = 5 |
| 124 | const SP_O_DICT: i64 = 6 |
| 125 | const SP_O_TRAINREC: i64 = 7 // train records used (term found and labelled) |
| 126 | const SP_O_TRAINSKIP: i64 = 8 // train records skipped (term not a contiguous token run) |
| 127 | const SP_O_TESTREC: i64 = 9 |
| 128 | const SP_O_EPOCHS: i64 = 10 |
| 129 | const SP_O_UPDATES: i64 = 11 // perceptron updates in the last epoch |
| 130 | const SP_O_TRAINSENT: i64 = 12 // MERGED training sentences (consecutive same-sentence records folded into one) |
| 133 | const SP_O_U_INTER: i64 = 13 |
| 134 | const SP_O_U_NS: i64 = 14 |
| 135 | const SP_O_U_P: i64 = 15 |
| 136 | const SP_O_U_R: i64 = 16 |
| 137 | const SP_O_U_F1: i64 = 17 |
| 141 | const SP_O_OC_TP: i64 = 18 |
| 142 | const SP_O_OC_NS: i64 = 19 |
| 143 | const SP_O_OC_NG: i64 = 20 |
| 144 | const SP_O_OC_P: i64 = 21 |
| 145 | const SP_O_OC_R: i64 = 22 |
| 146 | const SP_O_OC_F1: i64 = 23 |
| 147 | const SP_O_OCU_TP: i64 = 24 |
| 148 | const SP_O_OCU_NS: i64 = 25 |
| 149 | const SP_O_OCU_P: i64 = 26 |
| 150 | const SP_O_OCU_R: i64 = 27 |
| 151 | const SP_O_OCU_F1: i64 = 28 |
| 152 | const SP_O_TESTSENT: i64 = 29 // merged test sentences |
| 153 | const SP_O_DICT_MAJ: i64 = 30 // dictionary terms passing the majority rule (an aspect in >= half their training occurrences) |
| 154 | const SP_O_POS: i64 = 31 // 1 when a treebank was set and the tagger trained, else 0 |
| 155 | const SP_O_POS_TOK: i64 = 32 // tagger training tokens (0 without a treebank) |
| 156 | const SP_O_PARSE: i64 = 33 // 1 when the parser trained and every sentence was parsed, else 0 |
| 157 | const SP_O_PARSE_SENT: i64 = 34 // parser training sentences (0 without a treebank or with the parse switch off) |
| 158 | const SP_O_GROUPS: i64 = 35 // merged-sentence groups the first pass recorded (the shuffle's unit; counts unusable ones too) |
| 159 | const SP_O_SHUF: i64 = 36 // 1 when epochs 1 and later walked the shuffled order, 0 in file order (the default) |
| 160 | const SP_O_CLUST: i64 = 37 // 1 when a PPMI model was set, loaded and the vocabulary clustered, else 0 |
| 161 | const SP_O_CLUST_VOCAB: i64 = 38 // training vocabulary words the clusterer holds |
| 162 | const SP_O_CLUST_INMODEL: i64 = 39 // of those, words the PPMI model knows |
| 163 | const SP_O_CRF: i64 = 40 // 1 when the CRF objective trained the weights, 0 for the perceptron (the default) |
| 164 | const SP_O_CRF_NLL_FIRST: i64 = 41 // epoch-0 total negative log-likelihood, Q10 (0 in perceptron mode) |
| 165 | const SP_O_CRF_NLL_LAST: i64 = 42 // last-epoch total negative log-likelihood, Q10 |
| 166 | const SP_O_CRF_MARGDEV: i64 = 43 // largest |sum of a token's marginals - S| seen, Q10 (a partition witness) |
| 167 | const SP_O_CRF_NEGNLL: i64 = 44 // sentences whose partition read below their gold path score beyond the slack (must be 0) |
| 168 | const SP_O_EMB: i64 = 45 // 1 when the embedding table was set, loaded at the declared width and its features were on |
| 169 | const SP_O_EMB_DIM: i64 = 46 // the loaded table's coordinate count (0 when none) |
| 170 | const SP_O_EMB_LOOKUPS: i64 = 47 // training-pass token lookups against the vocabulary |
| 171 | const SP_O_EMB_HITS: i64 = 48 // of those, tokens the vocabulary knows (coverage numerator) |
| 176 | const SP_O_EC_UNTOK: i64 = 49 // missed and not tokenisable in its own sentence |
| 177 | const SP_O_EC_UNSEEN1: i64 = 50 // missed, single-token, absent from the training dictionary |
| 178 | const SP_O_EC_UNSEENM: i64 = 51 // missed, multi-token, absent from the training dictionary |
| 179 | const SP_O_EC_SEEN1: i64 = 52 // missed, single-token, present in the training dictionary |
| 180 | const SP_O_EC_SEENM: i64 = 53 // missed, multi-token, present in the training dictionary |
| 181 | const SP_O_EC_UNIONMISS: i64 = 54 // gold occurrences the UNION arm missed too |
| 182 | const SP_O_N: i64 = 55 |
| 183 | const SP_CH_SPACE: i64 = 32 |
| 188 | const SP_GROUP_CAP: i64 = 65536 // groups the shuffle can hold (SemEval restaurants train is 2018) |
| 190 | const SL_BUF: i64 = 0 |
| 191 | const SL_OFFS: i64 = 1 |
| 192 | const SL_LENS: i64 = 2 |
| 193 | const SL_COUNT: i64 = 3 |
| 194 | const SL_USED: i64 = 4 |
| 195 | const SL_FIELDS: i64 = 5 |
| 196 | const SL_CAP: i64 = 64 // spans per sentence |
| 197 | const SL_BUFCAP: i64 = 8192 |
| 198 | const SP_CTR_N: i64 = 4 // first-pass flag + records used + records skipped + merged sentences |
functions
| 258 | func sp_set_shuffle(on: i64) -> i64 { sp_shuffle_want = on; return 0 } |
| 263 | func sp_set_clusters(path: *u8) -> i64 { sp_clust_model = path; return 0 } |
| 265 | func sp_clust_vocab(train: *u8) -> i64 |
| 298 | func sp_set_crf(on: i64) -> i64 { sp_crf_want = on; return 0 } |
| 307 | func sp_set_embed(ppmi: *u8, emb: *u8) -> i64 { sp_emb_ppmi = ppmi; sp_emb_path = emb; return 0 } |
| 313 | func sp_set_treebank(path: *u8) -> i64 { sp_treebank = path; sp_parse_want = 0; return 0 } |
| 315 | func sp_set_parse(on: i64) -> i64 { sp_parse_want = on; return 0 } |
| 318 | func sp_tag_sentence(cnt: i64) -> i64 |
| 326 | func sl_new() -> *i64 |
| 335 | func sl_reset(h: *i64) -> i64 { h[SL_COUNT] = 0; h[SL_USED] = 0; return 0 } |
| 336 | func sl_has(h: *i64, s: *u8, n: i64) -> i64 |
| 353 | func sl_add(h: *i64, s: *u8, n: i64) -> i64 |
| 369 | func sl_inter(a: *i64, b: *i64) -> i64 |
| 379 | func sp_reset() -> i64 |
| 434 | func sp_acc_reset() -> i64 |
| 440 | func sp_acc_or(cnt: i64) -> i64 |
| 445 | func sp_same_sent(rbuf: *u8, rl: i64) -> i64 |
| 452 | func sp_flush(cnt: i64, dic: *i64) -> i64 |
| 478 | func sp_tokenize(sent: *u8, n: i64) -> i64 called by 5: sp_clust_vocabsp_dict_statssp_train_passsp_train_groupsp_eval calls 3: sys_mmaprm_next_tokenrm_catn |
| 498 | func sp_fh(pfx: i64, s: *u8, n: i64) -> i64 |
| 504 | func sp_has_digit(s: *u8, n: i64) -> i64 called by 1: sp_feats |
| 510 | func sp_feats(cnt: i64, i: i64, dic: *i64) -> i64 |
| 660 | func sp_w_at(idx: i64, useavg: i64) -> i64 called by 1: sp_emission |
| 664 | func sp_bump(idx: i64, delta: i64) -> i64 |
| 670 | func sp_tw_at(pt: i64, t: i64, useavg: i64) -> i64 |
| 675 | func sp_tbump(pt: i64, t: i64, delta: i64) -> i64 |
| 682 | func sp_emission(i: i64, tag: i64, useavg: i64) -> i64 |
| 690 | func sp_viterbi(cnt: i64, useavg: i64) -> i64 |
| 733 | func sp_gold_bio(cnt: i64, term: *u8, tlen: i64) -> i64 |
| 774 | func sp_update(cnt: i64) -> i64 |
| 806 | func sp_crf_fb(cnt: i64) -> i64 |
| 907 | func sp_crf_gold_score(cnt: i64) -> i64 |
| 918 | func sp_crf_update(cnt: i64) -> i64 |
| 953 | func sp_extract_list(cnt: i64, lst: *i64) -> i64 |
| 973 | func sp_dict_majority(slot: i64) -> i64 |
| 980 | func sp_dict_list(cnt: i64, dic: *i64, lst: *i64, majority: i64) -> i64 |
| 1005 | func sp_dict_stats(train: *u8, dic: *i64) -> i64 called by 1: sp_eval calls 12: sys_mmapsys_read_fileab_rec_nextab_reconstructsp_same_sentsp_dict_stats_flush+6 |
| 1046 | func sp_dict_stats_flush(dic: *i64) -> i64 |
| 1068 | func sp_extract(cnt: i64, pred_set: *i64) -> i64 |
| 1089 | func sp_train_pass(train: *u8, dic: *i64, first: *i64) -> i64 |
| 1144 | func sp_train_group(b: *u8, n: i64, start: i64, dic: *i64) -> i64 called by 1: sp_eval calls 9: ab_rec_nextab_reconstructsp_tokenizesp_tag_sentencesp_acc_resetsp_gold_bio+3 |
| 1170 | func sp_eval(train: *u8, test: *u8, out: *i64) -> i64 |