Two-phase preprocessing¶
lmsy_w2v_rfs can construct multi-word expressions (MWEs) in two phases before Word2Vec sees a single token. Each phase catches a different class of MWE. Phase 2 (statistical) always runs; Phase 1a (parser-based) runs only when you select a parser backend — the default preprocessor="none" skips it.
The flow¶
flowchart LR
RAW[Raw documents] --> P1A["Phase 1a: parser-based\nlemmatize, NER mask,\nUD MWE join"]
P1A --> P1B["Phase 1b (optional):\nstatic MWE list\npost-pass"]
P1B --> CLEAN["Clean:\nlowercase, drop punctuation,\ndrop SRAF stopwords"]
CLEAN --> P2["Phase 2: gensim Phrases\nbigram pass, trigram pass\n(statistical)"]
P2 --> W2V[Word2Vec input]
style P1A fill:#e8f4f8,stroke:#2c7a96
style P2 fill:#fef3e8,stroke:#c16d19
Phase 1a: parser-based, syntactic¶
The configured parser tokenizes, lemmatizes, tags named entities, and joins tokens linked by Universal Dependencies v2 labels fixed, flat, compound, and compound:prt. Five backends are available through Config.preprocessor:
| value | Needs | Strength |
|---|---|---|
"none" (default) |
nothing | Whitespace tokenize + lowercase; zero dependencies, runs out of the box |
"static" |
nltk only |
Deterministic curated-list pass; no parser |
"spacy" |
[spacy] extra and a model |
Fastest parser; best NER; 0% fixed or compound:prt recall |
"corenlp" |
[corenlp] extra and Java 8+ |
Paper-exact; 76% syntactic MWE recall; best JVM thread scaling |
"stanza" |
[stanza] extra |
Python-native; 57% syntactic MWE recall; slowest on CPU |
The default, "none", does no parsing — it just splits on whitespace and lowercases. It is the zero-friction starting point and is the right choice when your input is already tokenized, or when you simply want to try the package; Phase 2 and Word2Vec still produce useful dictionaries. The parser backends add value when you want their Phase 1a signals: lemmatization (so the seed integrity matches integrities/integrated) and NER masking (so firm names like Apple are replaced with [NER:TYPE] placeholders and cannot enter a dictionary). For paper-faithful Phase 1a, choose "corenlp"; for a fast Java-free parser, choose "spacy".
Phase 1b: optional static MWE list¶
After the main preprocessor runs, a curated MWE list (Config.mwe_list) can join anything the parser missed. The packaged "finance" list is a hand-curated ~246-entry file of UD fixed prepositional phrases and earnings-call jargon. Pass mwe_list="finance" to opt in.
Phase 2: statistical, gensim Phrases¶
After cleaning, gensim's Phrases runs one bigram pass and (by default) one trigram pass on the corpus itself. It learns high-frequency co-occurrences that no parser will flag, because they are collocations rather than grammatical units.
What each phase catches¶
The two phases are complementary because they rely on different signals:
| MWE | Caught by | Why |
|---|---|---|
customer_commitment |
Phase 1a | UD compound between two nouns |
with_respect_to |
Phase 1a | UD fixed prepositional phrase |
roll_out |
Phase 1a | UD compound:prt phrasal verb |
forward_looking_statement |
Phase 2 | High-frequency collocation, no UD label |
fourth_quarter |
Phase 2 | Domain collocation, no UD label |
Phase 1a is grammar-driven and catches syntactic patterns that appear once or twice in the corpus. Phase 2 is frequency-driven and catches idiomatic phrasings that occur often enough to dominate their constituent words' co-occurrence statistics. Phase 2 runs by default; Phase 1a is enabled by choosing a parser backend (spacy, corenlp, or stanza). The full benchmark behind this design, including NER quality and throughput numbers, lives in Preprocessor comparison.