Repository navigation
Conversation
|
After 547 merges, this is a small ticket that piggy backs on it. Just needs a rebase when it merges. |
|
@krickert Please go for rebase and harmonization of this branch, pls. Just want to make sure, things are consistent for ppl to inspect the PR draft against main. |
|
On it |
6e889ec to
1519c75
Compare
rzo1
left a comment
There was a problem hiding this comment.
Hi @krickert - as usual here is the first round of an AI review with our usual review bar:
The findings are inline (5 blocking, 32 minor). Process points:
- The PR is marked ready, but the remaining work listed in its description (model/factory/CLI integration, shared scoring, parser-owned caches, deterministic vocabulary, fresh evaluations) changes public API and the model format, both of which freeze at 3.0.0. Please move it back to draft until that has landed.
- The pinned feedforward scores (
UniversalDependencyParserEval.java:246/259) depend on the vocabulary order and cover greedy decoding only, while the manual uses beam 4. After the ordering fix we need an eval build to re-pin them, with a beam row added. - Squash message: please drop the "Rebased onto main after OPENNLP-547…" paragraph and the
Co-authored-by/Reviewed-bytrailers, replace theAssisted-by/Co-authored-by: Claudelines with at most one ASF-conformantGenerated-by:trailer, and keep the body to 2-3 sentences.
| * @since 3.0.0 | ||
| */ | ||
| @ThreadSafe | ||
| public class FeedforwardDependencyModel { |
There was a problem hiding this comment.
Blocking: A public *Model that does not extend BaseModel adds a second model contract (no manifest, language, version check or serialVersionUID, own MAGIC format), while the sibling DependencyModel extends BaseModel. This freezes at 3.0.0. Please extend BaseModel and store the weights as an artifact entry with its own serializer, or keep the type package-private until the model/factory integration lands.
| * not be negative. | ||
| * @param seed The random seed making a run reproducible. | ||
| */ | ||
| public record Settings(int embeddingSize, int hiddenSize, int epochs, int batchSize, |
There was a problem hiding this comment.
Blocking: The public Settings record (nine components, seed included) and the static train/refine methods add a hyperparameter API that bypasses TrainingParameters. Existing pattern: DependencyParserME.train(languageCode, samples, TrainingParameters). Please accept TrainingParameters and keep Settings package-private as the parsed form, or keep the trainer non-public until the standard training integration is done.
| * same values. Training and refinement use uncached copies; {@link #copy()} | ||
| * does not copy the cache.</p> | ||
| */ | ||
| synchronized void enableScoringCache() { |
There was a problem hiding this comment.
Blocking: The scoring cache is bounded by entry count, not bytes, and every parser constructor enables it, also for models loaded from untrusted files (FeedforwardDependencyParser.java:82). With CACHE_ENTRY_LIMIT = 32_768 entries of double[hiddenSize] and MAX_HIDDEN_SIZE = 65_536, a ~10 MB model can grow the cache to ~17 GB. Line 252 also allocates an AtomicReferenceArray(rows) per position (36) up front. Please budget the cache in bytes, size each position's table to the vocabulary it reads, and make the cache parser-owned and opt-in.
Also: the Javadoc gives rationale, not behaviour, e.g. "Enables the scoring cache; subsequent calls have no effect."
| Arrays.fill(outputBiasGradient, 0.0); | ||
| embeddingGradients.clear(); | ||
|
|
||
| for (int b = batchStart; b < batchEnd; b++) { |
There was a problem hiding this comment.
Blocking: The network math exists in several copies: optimize() inlines a full forward/backward pass that GlobalOptimizer.forward/backward (555/595) already implement apart from dropout; FeedforwardDependencyModel.score is a third forward pass; log-softmax is written four times although ArrayMath.logSumOfExps exists; FeedforwardDependencyScoringTest:254-275 tests two copies by reflection. Please use one package-private forward/backward helper with an optional dropout mask and one log-softmax, tested directly. Until then the class Javadoc at FeedforwardDependencyModel.java:49 ("the same class scores configurations during training and decoding") is incorrect.
| * @throws IllegalArgumentException Thrown if a parameter is {@code null} or no | ||
| * trainable example can be derived from the samples. | ||
| */ | ||
| public static FeedforwardDependencyModel train(ObjectStream<DependencySample> samples, |
There was a problem hiding this comment.
Blocking: @throws is incomplete on train and refine: missing IllegalStateException from checkFinite after a diverging epoch (1049/1129, 329); for train also the IllegalArgumentException for exceeding MAX_MODEL_FLOAT_VALUES (791) and for non-finite pretrained values (712).
| parser = new FeedforwardDependencyParser(model); | ||
| } | ||
|
|
||
| @Test |
There was a problem hiding this comment.
Minor: The eight tests lack Javadoc (the comment at 102-103 belongs there) and repeat the DependencyParserEdgeCaseTest contract (empty input, null token/tag, single token, round trip, thread sharing, same THREADS/ITERATIONS_PER_THREAD); the thread test covers greedy only. Please run the shared contract as a @ParameterizedTest over both parsers and include beam size 4.
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| /** Checks numerical precision and concurrent cache use during dependency parsing. */ | ||
| class FeedforwardDependencyScoringTest { |
There was a problem hiding this comment.
Minor: Package-private while siblings are public; constants 52-57 lack Javadoc; repeats via an IntStream.range(0, 8) round argument instead of @RepeatedTest; reads private fields (cache, remaining, byPosition) by reflection. Please test through package-private accessors once the cache is parser-owned.
| * @param times How often to repeat it. | ||
| * @return The repeated content. Never {@code null}. | ||
| */ | ||
| private static String repeat(String conllu, int times) { |
There was a problem hiding this comment.
Minor: repeat re-implements String.repeat: (CONLLU + "\n\n").repeat(REPETITIONS). The local model (235) shadows the static DependencyModel model (106), and the test checks only self-consistency and the root, not ALICE_TREE. Please rename the local and assert the gold tree, or say why the toy model cannot reach it.
| * <p>The parser is trained with a feature cutoff of {@value #FEATURE_CUTOFF} and | ||
| * evaluated on English, German, Spanish, and French. The English cross validation uses | ||
| * {@value #ENGLISH_FOLDS} folds of the training split.</p> | ||
| * <p>The classical parser is trained with a feature cutoff of {@value #FEATURE_CUTOFF} |
There was a problem hiding this comment.
Minor: "the classical parser" is informal, please use {@link DependencyParserME}. Lines 60-61 read as if the English treebank were AnCora: "English EWT and Spanish AnCora development splits".
| FeedforwardDependencyModel.load(Path.of("en-depparse-ff.bin")), 4);]]> | ||
| </programlisting> | ||
| </para> | ||
| <para> |
There was a problem hiding this comment.
Minor: This paragraph documents an internal failure mode. The example (108) uses serialize(OutputStream) unlike DependencyModel.serialize(Path) in the section above, and the evaluation paragraph (153-160) still describes a single parser. Please keep one or two sentences on greedy vs beam decoding, Settings.defaults() and thread safety, and state which parser is evaluated on which split.
1519c75 to
c1e5fa1
Compare
|
Eval failed - small patch to fix it. On it now |
c1e5fa1 to
f0d8072
Compare
|
Patch applied, eval build 101 is running. Will flip to ready for review once that is done. |
Adds FeedforwardDependencyParser, an arc-standard neural parser after Chen and Manning (2014) that implements the same DependencyParser API as DependencyParserME, with greedy and beam decoding. FeedforwardDependencyTrainer trains the FeedforwardDependencyModel from TrainingParameters and refines it with beam search and early updates; the model is a BaseModel with the network as an artifact. StringUtil.toLowerCase applies the Unicode final sigma rule, and the manual, the CoNLL-U usage test and the UD evaluation cover the new parser. Generated-by: Claude Code Signed-off-by: Kristian Rickert <kristian@apache.org> Co-authored-by: Claude Code 2.1.296 (Claude Opus 5.5)
f0d8072 to
ff09c3f
Compare
|
https://ci-builds.apache.org/job/OpenNLP/job/eval-tests-configurable/102/ 101 revealed a change that was needed. Re-running locally (should be about 2x faster) while 102 runs. |
Adds
FeedforwardDependencyParser, an arc-standard neural dependency parser after Chen and Manning (2014). It implements the sameDependencyParserAPI asDependencyParserMEand decodes greedily or with a beam.FeedforwardDependencyTrainertrains the model fromTrainingParametersand can refine it with beam search and early updates.FeedforwardDependencyModelextendsBaseModeland stores the network as an artifact with its own serializer. The scoring cache is parser-owned, opt-in and bounded in bytes.StringUtil.toLowerCasenow applies the Unicode final sigma rule, which the parser uses to key its word vocabulary.The manual, the CoNLL-U usage test and
UniversalDependencyParserEvalcover the new parser. The evaluation has greedy and beam 4 cases for English EWT and Spanish AnCora; the pinned scores will be refreshed from an eval build on this branch before the PR leaves draft.Remaining before 3.0.0: factory and CLI integration, which will be a follow-up PR.