Skip to content

fix(core/lifelong): correct rounds condition to fix HAS_COMPLETED_INITIAL_TRAINING in standard mode - #573

Open
g-k-s-03 wants to merge 2 commits into
kubeedge:mainfrom
g-k-s-03:fix/lifelong-has-completed-initial-training
Open

g-k-s-03 wants to merge 2 commits into
kubeedge:mainfrom
g-k-s-03:fix/lifelong-has-completed-initial-training

Conversation

@g-k-s-03

@g-k-s-03 g-k-s-03 commented Jul 2, 2026 •

Copy link
Copy Markdown

Description

In standard benchmark mode (elif mode != 'multi-inference'), the
training loop starts at r=1. The _train() method uses rounds < 1
to determine whether initial training has completed. Since 1 < 1 is
always False, HAS_COMPLETED_INITIAL_TRAINING is set to 'True'
on the very first call — before any training has occurred.

This is a silent correctness bug. Every lifelong learning algorithm
receives an incorrect signal on the first training round, causing it
to either attempt loading a non-existent checkpoint or skip
knowledge-base initialization. Benchmark metrics (BWT, FWT, Matrix)
are silently corrupted as a result.

Root Cause

The condition rounds < 1 only evaluates to True when r=0. The
standard mode loop starts at r=1, so the condition is never True
on the first call.

Fix (final, mode-aware)

An initial one-line change (rounds <= 1 unconditionally) was shown in
review to regress the zero-based no-inference and hard-example-mining
modes, whose first round starts at r=0 rather than r=1.

The final implementation determines the correct predicate per mode:

mode = (self.model_eval_config or {}).get('model_metric', {}).get('mode')
is_initial = rounds < 1 if mode in ('no-inference', 'hard-example-mining') else rounds <= 1
if is_initial:
    os.environ['HAS_COMPLETED_INITIAL_TRAINING'] = 'False'
else:
    os.environ['HAS_COMPLETED_INITIAL_TRAINING'] = 'True'

This uses rounds < 1 (original, preserved behavior) for the two
zero-based modes, and rounds <= 1 (the actual fix) for standard mode,
which starts at r=1.

Files Changed

  • core/testcasecontroller/algorithm/paradigm/lifelong_learning/lifelong_learning.py

Testing

Verified by reviewer zjr060424-lab against the full mode matrix
(standard, no-inference, hard-example-mining), confirming the fix
resolves the standard-mode defect without regressing the zero-based
modes. See review discussion below for details.

Fixes #572

@kubeedge-bot
kubeedge-bot requested review from MooreZheng and hsj576 July 2, 2026 07:21
@kubeedge-bot

Copy link
Copy Markdown
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: g-k-s-03
To complete the pull request process, please assign jaypume after the PR has been reviewed.
You can assign the PR to them by writing /assign @jaypume in a comment when ready.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubeedge-bot kubeedge-bot added the size/S Denotes a PR that changes 10-29 lines, ignoring generated files. label Jul 2, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds validation to ensure the aggregation module is present and initialized in federated learning, raising a ValueError if not. It also modifies the condition for checking if initial training has completed in lifelong learning from rounds < 1 to rounds <= 1. However, this change to rounds <= 1 introduces a regression in no-inference and hard-example-mining modes where the first round starts at r=0. A suggestion is provided to conditionally determine the initial training round based on the active mode.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 359 to 362
if rounds <= 1:
os.environ["HAS_COMPLETED_INITIAL_TRAINING"] = 'False'
else:
os.environ["HAS_COMPLETED_INITIAL_TRAINING"] = 'True'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Changing the condition to rounds <= 1 fixes the issue in standard mode (where the first round starts at r=1), but it introduces a regression in no-inference and hard-example-mining modes. In those modes, the first round starts at r=0 and the second round is r=1. With rounds <= 1, the second round (r=1) will be incorrectly treated as the initial training round (HAS_COMPLETED_INITIAL_TRAINING set to 'False').

To support both loop starting points correctly, we should determine whether it is the initial training round based on the active mode.

Suggested change
if rounds <= 1:
os.environ["HAS_COMPLETED_INITIAL_TRAINING"] = 'False'
else:
os.environ["HAS_COMPLETED_INITIAL_TRAINING"] = 'True'
mode = (self.model_eval_config or {}).get('model_metric', {}).get('mode')
is_initial = rounds < 1 if mode in ('no-inference', 'hard-example-mining') else rounds <= 1
if is_initial:
os.environ['HAS_COMPLETED_INITIAL_TRAINING'] = 'False'
else:
os.environ['HAS_COMPLETED_INITIAL_TRAINING'] = 'True'

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid point — updated the fix to be mode-aware. The condition now uses rounds < 1 for no-inference and hard-example-mining modes (preserving original behavior) and rounds <= 1 for standard mode (the actual fix). This avoids the regression you identified

@g-k-s-03
g-k-s-03 force-pushed the fix/lifelong-has-completed-initial-training branch from 9adcc5f to f042092 Compare July 2, 2026 07:23
@kubeedge-bot kubeedge-bot added size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Jul 2, 2026
@g-k-s-03

g-k-s-03 commented Jul 2, 2026

Copy link
Copy Markdown
Author

@jaypume The PR description above reflects the initial approach. The actual fix is mode-aware it uses rounds < 1 for no-inference and hard-example-mining modes (preserving original behaviour) and rounds <= 1 for standard mode. See the latest commit for the final implementation

@g-k-s-03

g-k-s-03 commented Jul 2, 2026

Copy link
Copy Markdown
Author

/assign

@zjr060424-lab zjr060424-lab left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review — #573 Lifelong Initial-Training State

Target: kubeedge/ianvs#573
Reviewed head: 96522f6b14fcfd408f2f9d3c7e94fc1bbde829ce
Base comparison: main@37a9c60a9747af0cfe3170f84249bc349c56e8d5

Recommendation

Accepted as it is.

The latest head correctly distinguishes the zero-based training loops used by no-inference and hard-example-mining from the one-based standard loop. I have two non-blocking follow-up suggestions: add a parameterized regression test and update the PR description so that it reflects the final mode-aware implementation.

Existing Review Boundary

The original one-line rounds <= 1 change was already shown to regress the second round of the two zero-based modes. That problem was identified in the existing Gemini inline review, and the author incorporated the suggested mode-aware implementation in the latest commit.

I am not presenting that existing finding as a new contribution. This review verifies the corrected latest head against the actual mode matrix.

Executed Verification

I executed the real LifelongLearning._train() method from both locked source revisions with a CPU-only fake job. The fake job recorded the value returned by Sedna Context.get_parameters("HAS_COMPLETED_INITIAL_TRAINING") immediately inside job.train().

The test covered the first and next training calls for all three relevant mode conventions:

Mode Round Desired value main@37a9c60 PR head 96522f6
no-inference 0 False False False
no-inference 1 True True True
hard-example-mining 0 False False False
hard-example-mining 1 True True True
standard 1 False True False
standard 2 True True True

Both verification scripts exited with code 0:

  • the main-branch run reproduced the incorrect standard-mode first-round state;
  • the PR-head run matched the desired matrix in every tested case.

This confirms that the latest head fixes the reported standard-mode defect without reintroducing the already-reviewed regression in the two zero-based modes.

Source-Level Assessment

The current standard branch calls _train() first with r=1, while no-inference and hard-example-mining begin their initial training with r=0. The latest implementation maps those existing loop conventions correctly:

mode = (self.model_eval_config or {}).get('model_metric', {}).get('mode')
is_initial = rounds < 1 if mode in ('no-inference', 'hard-example-mining') else rounds <= 1

The state is established before the Sedna job is built and before job.train() consumes it, so the timing is correct for this target.

Non-Blocking Follow-Ups

1. Add an executable mode matrix

The current diff does not add a regression test. A parameterized Core test should lock in at least these cases:

standard:                r=1 -> False, r=2 -> True
no-inference:            r=0 -> False, r=1 -> True
hard-example-mining:     r=0 -> False, r=1 -> True

The assertion should observe the value at job.train(), rather than only testing a duplicated helper expression.

2. Update the PR description

The PR description still explains the earlier unconditional rounds <= 1 approach and states that the zero-based modes are unaffected. The latest head no longer uses that implementation. Updating the description and test section would make the final review history unambiguous.

Verification Boundary

Executed:

  • real Ianvs Core _train() from locked main and PR checkouts;
  • Sedna environment lookup;
  • CPU-only fake job and data object;
  • six mode/round cases;
  • both scripts completed with exit code 0.

Not Executed: full GPU/model training, external datasets, complete end-to-end Lifelong Example benchmarks, pushed CI, or production deployment.

Evidence Attachment

01-pr573-mode-matrix 01-pr573-mode-matrix-replay

The GIF is an animated replay generated from the saved execution logs; the raw text logs and exit codes remain the source of truth.

The screenshot was visually verified for legibility and contains the saved main-versus-PR mode matrix. The underlying text logs and source trace were also preserved for reproducibility.

@suhaan-24

Copy link
Copy Markdown
Contributor

Reviewed against sedna 0.6.0.1 / Python 3.9.6 / macOS Apple M4, CPU+MPS.

Evidence that this fix is required, from running a different PR

While reviewing #645 I checked it out into an isolated worktree and ran
examples/cityscapes-synthia/lifelong_learning_bench/curb-detection against it, with the
Example-local layer repaired (stale config paths, train_url→train_index, dataset path) and no
other change. #645's own target defect cleared — zero occurrences of
'TaskDefinitionByOrigin' object has no attribute 'get' — and execution reached
Model loaded successfully and entered _train. It then stopped here:

[INFO] lifelong_learning.py(316) - Download last task index from /tmp/cloud_task/index.pkl to index.pkl
  File ".../paradigm/lifelong_learning/lifelong_learning.py", line 369, in _train
Exception: index.pkl not exists!
RuntimeError: (paradigm=lifelonglearning) pipeline runs failed, error: index.pkl not exists!

Sedna took the update() branch on the first training call and demanded a knowledge-base index
that nothing had created yet — exactly the condition this PR fixes.

So this is the blocker that remains once the module-representation fix is in place. That is a
useful sequencing fact: #645 and this PR are both necessary and neither is sufficient, and
this one should not be deferred behind the larger Core PRs.

On the mode-aware revision

The no-inference / hard-example-mining regression raised by gemini-code-assist and the
mode-aware revision that followed both look right to me. range(rounds + 1) in those branches
does start at r = 0, so rounds < 1 is the correct predicate there, and rounds <= 1 is
correct for the standard branch that starts at r = 1. No further concern on that point.

Two small notes

  1. The mode comparison is against two hardcoded literals.

    is_initial = rounds < 1 if mode in ('no-inference', 'hard-example-mining') else rounds <= 1

    A future third mode inherits the rounds <= 1 branch by default. If that default is
    deliberate, a brief comment saying so would help; if the invariant is really "which value does
    this branch's loop start at", deriving it from the loop start rather than from the mode name
    would be more robust.

  2. Overlap with [LFX Term : 01 ]Restoration: CItyscape-Sythia Curb detection #441. [LFX Term : 01 ]Restoration: CItyscape-Sythia Curb detection #441 fixes the same flag semantics at the call site instead
    (_train(..., 0) on the first round). Both are correct in isolation, but if both merge the
    invariant is encoded in two places and a later change to either silently masks a regression in
    the other. Worth an explicit decision on which layer owns it. A difference worth knowing: [LFX Term : 01 ]Restoration: CItyscape-Sythia Curb detection #441
    also moves round-1 training output from output/train/1/ to output/train/0/; this PR does
    not change the directory.

Verification boundary

Executed: the #645 run described above, on
cityscapes-synthia/lifelong_learning_bench/curb-detection, CPU/MPS, no GPU, no gated dataset.
This branch itself was not checked out and run — the evidence here establishes that the defect
is reachable and blocking after other fixes are applied, not that this specific diff clears it.

@barakahabimbola

Copy link
Copy Markdown

Reviewing this as part of LFX 2026 Term 3 pretest bonus coverage (full review in Discussion #964). The fix correctly distinguishes standard mode's one-based loop from the zero-based no-inference/hard-example-mining modes — I know an earlier naive version of this (rounds <= 1) would've regressed the zero-based modes, and the mode-aware version here avoids that. Worth double-checking this holds across all three modes in testing, not just standard mode, since that's the case that originally motivated the fix. (Static/traced review — haven't executed the mode matrix myself.)

@g-k-s-03

Copy link
Copy Markdown
Author

@jaypume this is ready for your approval when you get a chance.

@g-k-s-03

Copy link
Copy Markdown
Author

/assign @jaypume

Description updated to lead with the final mode-aware implementation, per
@zjr060424-lab's follow-up. The mode matrix in his review confirms all six
cases (standard r=1/r=2, no-inference r=0/r=1, hard-example-mining r=0/r=1).

Re @suhaan-24's note on overlap with #441: that PR fixes the same flag at the
call site. I think the core-level fix belongs here, since it holds for every
example rather than one call site, but happy to defer if maintainers prefer
the other layer.

@g-k-s-03

g-k-s-03 commented Oct 5, 2026

Copy link
Copy Markdown
Author

@jaypume, any update on whether this pr is ok or do I need to change anything?

suhaan-24 added a commit to suhaan-24/ianvs that referenced this pull request Oct 21, 2026
_train told Sedna that initial training had already happened unless the
round number was below 1. The default mode trains first in round 1, so
Sedna took the update path on the very first call and failed with
"index.pkl not exists!".

The condition is now mode-aware: the default mode treats round 1 as
initial training, and no-inference and hard-example-mining keep treating
round 0 as initial training. The mode-aware condition follows kubeedge#573 by
g-k-s-03.

Part of stage S3 of the Phase IV proposal (kubeedge#1000). Adds tests/core/
test_lifelong_paradigm.py: 7 preservation tests and 1 change test.

Refs: kubeedge#572, kubeedge#573
Signed-off-by: Suhaan <suhaanrk73@gmail.com>
…TIAL_TRAINING in standard mode, Issue 572

Signed-off-by: g-k-s-03 <govindsingh97704@gmail.com>
…L_TRAINING

Signed-off-by: g-k-s-03 <govindsingh97704@gmail.com>
@g-k-s-03
g-k-s-03 force-pushed the fix/lifelong-has-completed-initial-training branch from 96522f6 to 3f0d7d9 Compare October 22, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XS Denotes a PR that changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(core/lifelong): HAS_COMPLETED_INITIAL_TRAINING set to True on first training call in standard mode

6 participants