nx_absa_lib.nx
buildroot/runtime/nx_absa_lib.nx
about
nx_absa_lib.nx -- the SCORING MATH for SemEval-2014 Task 4 subtask 1 (aspect term extraction), as a shared lib
so the CLI (nx_absa_bench) and the gate (nx_absa_bench_gate) share ONE definition of P, R and F1 and one gold
parser. The metric is the paper's own (Pontiki et al. 2014, eq. 1-2): P=|S and G|/|S|, R=|S and G|/|G|,
F1=2PR/(P+R), which for a set overlap is exactly 2*inter/(|S|+|G|) -- computed from counts so there is no
double rounding. Everything is permil (1000 = 1.00). Nothing here is a threshold: the lib reports numbers, the
gate says what a correct scorer must return, and the CLI prints the number beside the published winners.
THE GOLD FORMAT (ABSA-PyTorch .seg mirror of the official test set): three lines per record --
line 0 mod 3: the sentence with the aspect term replaced by the placeholder $T$
line 1 mod 3: the aspect term (single or multi word, e.g. "hard disk", "delivery times")
line 2 mod 3: the polarity (1 / 0 / -1)
The gold aspect-term SET G is the distinct normalised terms across the term lines (footnote 3 of the paper
counts occurrences; this is the distinct-term variant, DECLARED, a related but not identical metric).
THE PREDICTOR is a UNIGRAM DOCUMENT-FREQUENCY baseline over the test sentences: it composes the ONE tokenizer
(rm_next_token via rm_mine_doc) and the ONE frequency ranker (rm_mine_rank) from nx_reviewmine_lib, so no
second tokenizer or ranker exists. It is a weak proxy for Hu-Liu (2004) frequent-noun-phrase mining -- our
miner does not yet extract multi-word noun phrases (rung rm_aspects), so a multi-word gold term is UNMATCHABLE
by a unigram prediction under exact-set match. That is the honest reason this baseline sits far below the
supervised systems, and the gap IS the un-landed rung. license_tier: ORIGINAL No hw writes (Rule 26). LIB.
dependencies 2 imports · 5 importers
imports: nx_syscalls.nxnx_reviewmine_lib.nx
imported by: nx_absa_bench.nxnx_absa_bench_gate.nxnx_absa_seq.nxnx_absa_seq_gate.nxnx_absa_seqextract.nx
structs
| none |
consts
| 24 | const AB_PERMIL: i64 = 1000 |
| 25 | const AB_P: i64 = 0 |
| 26 | const AB_R: i64 = 1 |
| 27 | const AB_F1: i64 = 2 |
| 28 | const AB_PRF_N: i64 = 3 |
| 30 | const AB_O_INTER: i64 = 0 |
| 31 | const AB_O_NS: i64 = 1 |
| 32 | const AB_O_NG: i64 = 2 |
| 33 | const AB_O_P: i64 = 3 |
| 34 | const AB_O_R: i64 = 4 |
| 35 | const AB_O_F1: i64 = 5 |
| 36 | const AB_O_K: i64 = 6 |
| 37 | const AB_O_VOCAB: i64 = 7 |
| 38 | const AB_O_N: i64 = 8 |
| 40 | const AB_SET_SLOTS: i64 = 65536 // power of two; a SemEval domain has ~1-2k distinct aspect terms |
| 41 | const AB_SET_ARENA: i64 = 4194304 // AB_SET_SLOTS * avg term bytes, generous (multi-word terms allowed) |
| 42 | const AB_TERM_MAX: i64 = 256 // longest aspect term stored |
| 43 | const AB_RECORD_LINES: i64 = 3 // the .seg record is three lines |
| 44 | const AB_TERM_LINE: i64 = 1 // lineno mod 3 == 1 is the aspect-term line |
| 45 | const AB_SENT_LINE: i64 = 0 // lineno mod 3 == 0 is the sentence line |
| 46 | const AB_MIN_SUPPORT: i64 = 1 // a candidate term must appear in at least one sentence |
| 47 | const AB_ARENA_TAIL: i64 = 1 // the NUL after a stored term |
| 49 | const AB_STOPWORDS: *u8 = "knowledge/reviewmine/stopwords.conf" |
| 187 | const AB_RECON_CAP: i64 = 8192 // a reconstructed review sentence |
| 188 | const AB_DOLLAR: i64 = 36 // '$' |
| 189 | const AB_UPPER_T: i64 = 84 // 'T' -- the $T$ placeholder the .seg masks the aspect term with |
| 190 | const AB_PLACEHOLDER_LEN: i64 = 3 // the three bytes of $T$ |
| 294 | const AB_POL_POS: i64 = 0 |
| 295 | const AB_POL_NEG: i64 = 1 |
| 296 | const AB_POL_NEU: i64 = 2 |
| 297 | const AB_POL_OTHER: i64 = 3 |
| 298 | const AB_POL_N: i64 = 4 |
| 299 | const AB_Q_RECORDS: i64 = 0 |
| 300 | const AB_Q_POS: i64 = 1 |
| 301 | const AB_Q_NEG: i64 = 2 |
| 302 | const AB_Q_NEU: i64 = 3 |
| 303 | const AB_Q_OTHER: i64 = 4 |
| 304 | const AB_Q_MAJ_CLASS: i64 = 5 |
| 305 | const AB_Q_MAJ_ACC: i64 = 6 |
| 306 | const AB_Q_LEX_ACC: i64 = 7 |
| 307 | const AB_Q_LEX_PRED_POS: i64 = 8 |
| 308 | const AB_Q_LEX_PRED_NEG: i64 = 9 |
| 309 | const AB_Q_LEX_PRED_NEU: i64 = 10 |
| 310 | const AB_Q_LEX_TERMS: i64 = 11 // lexicon terms loaded; 0 means the lexicon arm had no cues and says so |
| 311 | const AB_Q_N: i64 = 12 |
| 312 | const AB_LEX_DIR_DEFAULT: *u8 = "knowledge/reviewmine/" |
| 313 | const AB_LEX_DEFECT_FILE: *u8 = "lexicon_defect.conf" |
| 314 | const AB_LEX_NOTMEET_FILE: *u8 = "lexicon_notmeet.conf" |
| 315 | const AB_LEX_EXCEED_FILE: *u8 = "lexicon_exceed.conf" |
| 316 | const AB_PATH_CAP: i64 = 512 |
| 317 | const AB_CH_ONE: i64 = 49 // '1' |
| 318 | const AB_POL_LEN_ONE: i64 = 1 // "1" and "0" are one byte |
| 319 | const AB_POL_LEN_NEG: i64 = 2 // "-1" is two bytes |
| 444 | const AB_NG_MINSUP_PERMIL: i64 = 10 // Hu and Liu 2004: a frequent feature appears in at least 1 percent of sentences |
| 445 | const AB_NG_MINSUP_FLOOR: i64 = 1 |
| 446 | const AB_NG_MIN_PSUPPORT: i64 = 3 // Hu and Liu 2004: a subset phrase with p-support below 3 is redundant |
| 447 | const AB_NG_JOIN: i64 = 32 // the space between the two words of a stored bigram |
| 448 | const AB_NG_MAX_TOKENS: i64 = 512 // tokens one record carries into the n-gram pass; more is counted truncated |
| 449 | const AB_NG_BG_CAP: i64 = 66 // RM_TOK_MAX * 2 + a space + a NUL |
| 450 | const AB_NG_STOP: i64 = 0 - 1 // a stopword in the token list: a phrase boundary |
| 451 | const AB_REC_N: i64 = 6 // s0 l0 s1 l1 s2 l2 |
| 452 | const AB_G_INTER: i64 = 0 |
| 453 | const AB_G_NS: i64 = 1 |
| 454 | const AB_G_NG: i64 = 2 |
| 455 | const AB_G_P: i64 = 3 |
| 456 | const AB_G_R: i64 = 4 |
| 457 | const AB_G_F1: i64 = 5 |
| 458 | const AB_G_K: i64 = 6 |
| 459 | const AB_G_CAND: i64 = 7 // candidates after support and pruning |
| 460 | const AB_G_BIGRAMS: i64 = 8 // frequent bigrams among the candidates |
| 461 | const AB_G_PRUNED: i64 = 9 // unigrams pruned by p-support |
| 462 | const AB_G_MINSUP: i64 = 10 |
| 463 | const AB_G_MATCHED_BIGRAMS: i64 = 11 |
| 464 | const AB_G_TRUNC: i64 = 12 // records whose token list overflowed |
| 465 | const AB_G_RECORDS: i64 = 13 |
| 466 | const AB_G_N: i64 = 14 |
| 669 | const HS_HASH: i64 = 0 |
| 670 | const HS_OFF: i64 = 1 |
| 671 | const HS_LEN: i64 = 2 |
| 672 | const HS_ARENA: i64 = 3 |
| 673 | const HS_USED: i64 = 4 |
| 674 | const HS_CNT: i64 = 5 |
| 675 | const HS_FIELDS: i64 = 6 |
| 676 | const AB_GRAM_CAP: i64 = 512 |
| 677 | const AB_D_INTER: i64 = 0 |
| 678 | const AB_D_NS: i64 = 1 |
| 679 | const AB_D_NG: i64 = 2 |
| 680 | const AB_D_P: i64 = 3 |
| 681 | const AB_D_R: i64 = 4 |
| 682 | const AB_D_F1: i64 = 5 |
| 683 | const AB_D_DICT: i64 = 6 |
| 684 | const AB_D_RECORDS: i64 = 7 |
| 685 | const AB_D_N: i64 = 8 |
functions
| 61 | func ab_norm(src: *u8, n: i64, out: *u8, cap: i64) -> i64 |
| 84 | func ab_set_reset() -> i64 |
| 101 | func ab_set_add_norm(s: *u8, n: i64) -> i64 |
| 130 | func ab_set_add_raw(src: *u8, n: i64) -> i64 |
| 136 | func ab_set_has_raw(src: *u8, n: i64) -> i64 |
| 156 | func ab_set_count() -> i64 { return ab_cnt } called by 1: ab_gold_load |
| 160 | func ab_prf(inter: i64, ns: i64, ng: i64, out: *i64) -> i64 |
| 169 | func ab_gold_load(path: *u8) -> i64 |
| 195 | func ab_reconstruct(sent: *u8, sl: i64, term: *u8, tl: i64, out: *u8, cap: i64) -> i64 |
| 227 | func ab_eval(path: *u8, k: i64, out: *i64) -> i64 called by 2: mainmain calls 14: ab_gold_loadsys_mmapsys_read_filerm_vocab_resetrm_stopwords_loadrm_line_end+8 |
| 321 | func ab_pol_class(p: *u8, n: i64) -> i64 called by 1: ab_polarity |
| 332 | func ab_pol_name(c: i64) -> *u8 called by 1: main |
| 338 | func ab_lex_path(dir: *u8, file: *u8, out: *u8) -> i64 |
| 345 | func ab_lex_cues(text: *u8, n: i64, tok: *u8, ip: *i64, cues: *i64) -> i64 |
| 362 | func ab_polarity(path: *u8, lexdir: *u8, out: *i64) -> i64 called by 2: mainmain calls 9: sys_mmapsys_read_filerm_vocab_resetab_lex_pathrm_lexicon_loadrm_line_end+3 |
| 479 | func ab_ng_reset() -> i64 |
| 498 | func ab_ng_tokens(text: *u8, n: i64, tok: *u8, ip: *i64, trunc: *i64) -> i64 |
| 515 | func ab_ng_bigram(left: i64, right: i64) -> i64 |
| 523 | func ab_ng_bump(slot: i64, doc: i64) -> i64 { if ab_last[slot] != doc { ab_last[slot] = doc; ab_df[slot] = ab_df[slot] + 1 } return 0 } called by 1: ab_ngram_eval |
| 524 | func ab_ng_psbump(slot: i64, doc: i64) -> i64 { if ab_pslast[slot] != doc { ab_pslast[slot] = doc; ab_ps[slot] = ab_ps[slot] + 1 } return 0 } called by 1: ab_ngram_eval |
| 526 | func ab_rec_next(b: *u8, n: i64, ip: *i64, r: *i64) -> i64 called by 7: ab_ngram_evalab_dict_evalsp_clust_vocabsp_dict_statssp_train_passsp_train_group+1 calls 1: rm_line_end |
| 541 | func ab_ngram_eval(path: *u8, k: i64, minsup_pin: i64, out: *i64) -> i64 called by 2: mainmain calls 17: ab_gold_loadsys_mmapsys_read_filerm_vocab_resetrm_stopwords_loadab_ng_reset+11 |
| 687 | func hs_new() -> *i64 |
| 697 | func hs_reset(h: *i64) -> i64 |
| 705 | func hs_add_norm(h: *i64, s: *u8, n: i64) -> i64 |
| 737 | func hs_add_raw(h: *i64, s: *u8, n: i64) -> i64 { let m: i64 = ab_norm(s, n, ab_nbuf, AB_TERM_MAX); return hs_add_norm(h, ab_nbuf, m) } |
| 738 | func hs_has_raw(h: *i64, s: *u8, n: i64) -> i64 |
| 761 | func hs_count(h: *i64) -> i64 { return h[HS_CNT] } |
| 763 | func hs_slot_raw(h: *i64, s: *u8, n: i64) -> i64 |
| 786 | func ab_dict_load(path: *u8, h: *i64) -> i64 called by 2: ab_dict_evalsp_eval calls 6: hs_resetsys_mmapsys_read_filerm_line_endhs_add_rawhs_count |
| 803 | func ab_dict_try(dic: *i64, pred: *i64, g: *u8, n: i64) -> i64 |
| 807 | func ab_dict_eval(train: *u8, test: *u8, out: *i64) -> i64 |