From 096b0576f246afd89a27d660c89a49189e403d0e Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 10 Mar 2026 16:59:53 -0400 Subject: [PATCH 1/3] update astro web page example --- web/src/components/code.astro | 163 ++++++++++++------------ web/src/components/gettingStarted.astro | 20 +-- web/src/components/series.astro | 16 +-- 3 files changed, 97 insertions(+), 102 deletions(-) diff --git a/web/src/components/code.astro b/web/src/components/code.astro index 220242d584..e2d49d286b 100644 --- a/web/src/components/code.astro +++ b/web/src/components/code.astro @@ -125,7 +125,7 @@ def main(): summary_writer.add_scalar( tag="loss_for_each_batch", - scalar=running_loss, + scalar=running_loss, global_step=global_step ) running_loss = 0.0 @@ -175,70 +175,65 @@ def main(): if __name__ == "__main__": main()`; -const serverCode_pt = ` -from nvflare.app_common.workflows.base_fedavg import BaseFedAvg - -class FedAvg(BaseFedAvg): - def run(self) -> None: - self.info("Start FedAvg.") - - model = self.load_model() - model.start_round = self.start_round - model.total_rounds = self.num_rounds - - for self.current_round in range(self.start_round, self.start_round + self.num_rounds): - self.info(f"Round {self.current_round} started.") - model.current_round = self.current_round - - clients = self.sample_clients(self.num_clients) - - results = self.send_model_and_wait(targets=clients, data=model) - - aggregate_results = self.aggregate(results) +const modelCode_pt = ` +import torch +import torch.nn as nn +import torch.nn.functional as F - model = self.update_model(model, aggregate_results) - self.save_model(model) +class SimpleNetwork(nn.Module): + def __init__(self): + super(SimpleNetwork, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) - self.info("Finished FedAvg.") + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = torch.flatten(x, 1) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x `; const jobCode_pt = ` -from cifar10_pt_fl import Net +from model import SimpleNetwork -from nvflare.app_common.workflows.fedavg import FedAvg -from nvflare.app_opt.pt.job_config.base_fed_job import BaseFedJob -from nvflare.job_config.script_runner import ScriptRunner +from nvflare.app_opt.pt.recipes.fedavg import FedAvgRecipe +from nvflare.recipe import SimEnv, add_experiment_tracking -if __name__ == "__main__": + +def main(): n_clients = 2 num_rounds = 2 - train_script = "cifar10_pt_fl.py" - - # Create BaseFedJob with model - job = BaseFedJob( - name="cifar10_pt_fedavg", - model=Net(), - ) - # Define the controller and send to server - controller = FedAvg( - num_clients=n_clients, + recipe = FedAvgRecipe( + name="hello-pt", + min_clients=n_clients, num_rounds=num_rounds, + model=SimpleNetwork(), + train_script="client.py", + train_args="--batch_size 16", ) - job.to_server(controller) + add_experiment_tracking(recipe, tracking_type="tensorboard") - # Add clients - for i in range(n_clients): - runner = ScriptRunner(script=train_script) - job.to(runner, f"site-{i}") + env = SimEnv(num_clients=n_clients) + run = recipe.execute(env) + print("Job Status is:", run.get_status()) + print("Result can be found in:", run.get_result()) - # job.export_job("/tmp/nvflare/jobs/job_config") - job.simulator_run("/tmp/nvflare/jobs/workdir", gpu="0") + +if __name__ == "__main__": + main() `; const runCode_pt = ` -python3 fedavg_cifar10_pt_job.py +python job.py `; // Lightning Code Sections -------------------------------------------------- @@ -530,7 +525,7 @@ def main(): # (5) evaluate aggregated/received model _, test_global_acc = model.evaluate(test_images, test_labels, verbose=2) print( - f"Accuracy of the received model on round {input_model.current_round} on the 10000 test images: + f"Accuracy of the received model on round {input_model.current_round} on the 10000 test images: {test_global_acc * 100} %" ) @@ -628,8 +623,8 @@ python3 fedavg_cifar10_tf_job.py const frameworks = [ { id: "pytorch", - colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/nvflare_pt_getting_started.ipynb`, - github_link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/nvflare_pt_getting_started.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/hello-pt.ipynb`, + github_link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/hello-pt.ipynb`, sections: [ { id: "install-pytorch", @@ -644,29 +639,28 @@ const frameworks = [ id: "client-pytorch", type: "client", framework: "pytorch", - title: "Client Code (cifar10_pt_fl.py)", + title: "Client Code (client.py)", description: - "We use the Client API to convert the centralized training PyTorch code into federated learning code with only a few lines of changes highlighted below. Essentially the client will receive a model from NVIDIA FLARE, perform local training and validation, and then send the model back.", + "Use the Client API to convert your training script into federated learning code. The client receives the global model from FLARE, performs local training and validation, and sends the updated model back.", code: clientCode_pt, highlighted_lines: "29,58,61,63,139-143,145", }, { - id: "server-pytorch", - type: "server", + id: "model-pytorch", + type: "model", framework: "pytorch", - title: "Server Code (fedavg.py)", + title: "Model (model.py)", description: - "The ModelController API is used to write a federated routine with mechanisms for distributing and receiving models from clients. Here we implement the basic FedAvg algorithm using some helper functions from BaseFedAvg.", - code: serverCode_pt, - highlighted_lines: "7,17,23", + "Model definition lives in model.py and is referenced by both the client and the job recipe.", + code: modelCode_pt, }, { id: "job-pytorch", type: "job", framework: "pytorch", - title: "Job Code (fedavg_cifar10_pt_job.py)", + title: "Job (job.py)", description: - "Lastly we construct the job with our 'cifar10_pt_fl.py' client script and 'FedAvg' server controller. The BaseFedJob automatically configures components for model persistence, model selection, and TensorBoard streaming. We then run the job with the FL simulator.", + "The Recipe API defines the FL job in Python: FedAvgRecipe with model, client script, and options. No separate server file — run with the simulator via recipe.execute(SimEnv(...)).", code: jobCode_pt, }, { @@ -675,15 +669,15 @@ const frameworks = [ framework: "pytorch", title: "Run the Job", description: - "To run the job with the simulator, copy the code and execute the job script, or run in Google Colab. Alternatively, export the job to a configuration and run the job in other modes.", + "From the example directory, run: python job.py. Or open the notebook in Google Colab.", code: runCode_pt, }, ], }, { id: "lightning", - colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/nvflare_lightning_getting_started.ipynb`, - github_link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/nvflare_lightning_getting_started.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-lightning/hello_lightning.ipynb`, + github_link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-lightning/hello_lightning.ipynb`, sections: [ { id: "install-lightning", @@ -736,8 +730,8 @@ const frameworks = [ }, { id: "tensorflow", - colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/tf/nvflare_tf_getting_started.ipynb`, - github_link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/tf/nvflare_tf_getting_started.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/hello-pt.ipynb`, + github_link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-tf`, sections: [ { id: "install-tensorflow", @@ -842,7 +836,7 @@ const frameworks = [ - + NVIDIA logo @@ -852,7 +846,7 @@ const frameworks = [ - +
- +
    @@ -899,11 +893,11 @@ const frameworks = [
- +
- - + + @@ -927,33 +921,35 @@ const frameworks = [ const googleColab = document.getElementById('colab-button'); const githubButton = document.getElementById('github-button'); - const codeTabs = document.getElementById("code-tabs"); + const codeTabs = document.getElementById("code-tabs"); var sectionMap = { "install": { - wrapper: document.getElementById('install-wrapper'), + wrapper: document.getElementById('install-wrapper'), elements: [], }, "client": { - wrapper: document.getElementById('client-wrapper'), + wrapper: document.getElementById('client-wrapper'), + elements: [], + }, + "model": { + wrapper: document.getElementById('model-wrapper'), elements: [], }, "server": { - wrapper: document.getElementById('server-wrapper'), + wrapper: document.getElementById('model-wrapper'), elements: [], }, "job": { - wrapper: document.getElementById('job-wrapper'), + wrapper: document.getElementById('job-wrapper'), elements: [], }, "run": { - wrapper: document.getElementById('run-wrapper'), + wrapper: document.getElementById('run-wrapper'), elements: [], } }; - console.log(sectionMap); - // Loop over the code sections and create the code elements frameworks.forEach((framework) => { framework.sections.forEach((code_section) => { @@ -962,7 +958,7 @@ const frameworks = [ sectionMap[code_section.type].elements.push( { section: code_section, - code: codeElement, + code: codeElement, framework: code_section.framework } ); @@ -986,7 +982,6 @@ const frameworks = [ code_height = "h-[700px]"; if (code_section.hasOwnProperty("highlighted_lines")) { highlighted_lines = "data-line=\"" + code_section.highlighted_lines + "\" " - console.log(code_section.title, code_section.type, code_section.hasOwnProperty("highlighted_lines"), highlighted_lines) } } @@ -1043,7 +1038,7 @@ const frameworks = [ googleColab?.setAttribute("href", framework.colab_link); githubButton?.setAttribute("href", framework.github_link); } - }); + }); } diff --git a/web/src/components/gettingStarted.astro b/web/src/components/gettingStarted.astro index f265932743..96b7da8a5a 100644 --- a/web/src/components/gettingStarted.astro +++ b/web/src/components/gettingStarted.astro @@ -17,11 +17,11 @@ const walkthrough = [ { id: "step2", step: "Step 2", - title: "Server Code", + title: "Job (Recipe)", description: - "Use the ModelController API to write a federated control flow for FedAvg.", + "Use the Recipe API to define the FL job in Python: job.py with FedAvgRecipe, model, and client script — no separate server file.", button_text: "View Source", - link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/nvflare/app_common/workflows/fedavg.py`, + link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/job.py`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%202%20-%20Server.mp4", }, { @@ -29,19 +29,19 @@ const walkthrough = [ step: "Step 3", title: "Client Code", description: - "Use the Client API to write local training code for a PyTorch CIFAR-10 trainer.", + "Use the Client API to convert your training script into federated learning code (client.py).", button_text: "View Source", - link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/src/cifar10_fl.py`, + link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/client.py`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%203%20-%20Client.mp4", }, { id: "step4", step: "Step 4", - title: "FedJob and Simulator", + title: "Model & Run", description: - "Formulate the NVIDIA FLARE job and simulate a federated run with the multi-process simulator.", - button_text: "View Notebook", - link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/nvflare_pt_getting_started.ipynb`, + "Model lives in model.py. Run the job with the simulator: python job.py, or open the hello-pt notebook in Colab.", + button_text: "View Example", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-pt`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%204%20-%20Job.mp4", }, { @@ -197,7 +197,7 @@ const series = [
)) } - + diff --git a/web/src/components/series.astro b/web/src/components/series.astro index d4aa7db231..6f5a0601d8 100644 --- a/web/src/components/series.astro +++ b/web/src/components/series.astro @@ -157,8 +157,8 @@ const series_100 = { { title: "Getting Started", tags: ["beg.", "pytorch", "lightning", "sklearn", "tensorflow"], - description: "Getting started examples using the Client API, Model Controller API, and Job API for different frameworks.", - link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/getting_started` + description: "Hello-world examples using the Recipe API (job.py, client.py, model.py) for PyTorch, Lightning, TensorFlow, and more.", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world` }, { title: "Client API", @@ -185,10 +185,10 @@ const series_100 = { link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world#examples-by-framework` }, { - title: "Hello FedAvg", + title: "Hello FedAvg (Recipe)", tags: ["beg.", "pytorch"], - description: "Demonstrate flexibility of the ModelController API, and show how to write a Federated Averaging workflow with early stopping, model selection, and saving and loading.", - link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-fedavg/README.md` + description: "FedAvg using the Recipe API: job.py, client.py, and model.py. Run with the simulator (python job.py).", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-pt` }, { title: "Job API Examples", @@ -308,10 +308,10 @@ const series_200 = { description: "See how FL algorithms can be implemented in a variety of frameworks.", cards: [ { - title: "Hello FedAvg", + title: "Hello FedAvg (Recipe)", tags: ["beg.", "algorithm", "pytorch", "dl"], - description: "Example using the FedAvg workflow to implement Federated Averaging.", - link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-fedavg/README.md` + description: "FedAvg workflow with the Recipe API: job.py, client.py, model.py. Run with python job.py.", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-pt` }, { title: "Hello Numpy Cross-Site Validation", From 81ef359aba4a77e16a0c7525bf8644323b912c2f Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 10 Mar 2026 17:12:16 -0400 Subject: [PATCH 2/3] hide outdated videos for now --- web/src/components/gettingStarted.astro | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/src/components/gettingStarted.astro b/web/src/components/gettingStarted.astro index 96b7da8a5a..10585e0863 100644 --- a/web/src/components/gettingStarted.astro +++ b/web/src/components/gettingStarted.astro @@ -105,7 +105,7 @@ const series = [

Begin your NVIDIA FLARE journey with these guides designed to help you quickly grasp essential concepts. - Follow along with the videos below, and try it out yourself. + Follow the steps below and try it out yourself.

@@ -182,7 +182,8 @@ const series = [ - + + )) } From b92beb0ec7fab3d28b248201dd41addb87881562 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 11 Mar 2026 16:11:28 -0400 Subject: [PATCH 3/3] address PR comments --- web/src/components/code.astro | 203 +++++++++--------------- web/src/components/gettingStarted.astro | 3 +- 2 files changed, 76 insertions(+), 130 deletions(-) diff --git a/web/src/components/code.astro b/web/src/components/code.astro index e2d49d286b..29ad5c5531 100644 --- a/web/src/components/code.astro +++ b/web/src/components/code.astro @@ -20,66 +20,56 @@ const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; // PyTorch Code Sections -------------------------------------------------- const installCode_pt = ` -pip install nvflare torch torchvision +pip install nvflare torch torchvision tensorboard `; const clientCode_pt = ` +import argparse + import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.optim as optim import torchvision -import torchvision.transforms as transforms - - -class Net(nn.Module): - def __init__(self): - super().__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = torch.flatten(x, 1) # flatten all dimensions except batch - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - +from model import SimpleNetwork +from torch import nn +from torch.optim import SGD +from torchvision.transforms import Compose, Normalize, ToTensor # (1) import nvflare client API import nvflare.client as flare - -# (optional) metrics from nvflare.client.tracking import SummaryWriter -# (optional) set a fix place so we don't need to download everytime DATASET_PATH = "/tmp/nvflare/data" -# If available, we use GPU to speed things up. -DEVICE = "cuda" if torch.cuda.is_available() else "cpu" - - -def main(): - transform = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) - ]) - batch_size = 4 - epochs = 2 - trainset = torchvision.datasets.CIFAR10(root=DATASET_PATH, train=True, download=True, transform=transform) - trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=2) +def evaluate(net, data_loader, device): + correct = 0 + total = 0 + with torch.no_grad(): + for data in data_loader: + images, labels = data[0].to(device), data[1].to(device) + outputs = net(images) + _, predicted = torch.max(outputs.data, 1) + total += labels.size(0) + correct += (predicted == labels).sum().item() + print(f"Accuracy of the network on the 10000 test images: {100 * correct // total} %") + return 100 * correct // total - testset = torchvision.datasets.CIFAR10(root=DATASET_PATH, train=False, download=True, transform=transform) - testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=2) - net = Net() +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--epochs", type=int, default=2) + parser.add_argument("--batch_size", type=int, default=16) + args = parser.parse_args() + + model = SimpleNetwork() + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + loss = nn.CrossEntropyLoss() + optimizer = SGD(model.parameters(), lr=0.01, momentum=0.9) + + transform = Compose([ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) + train_set = torchvision.datasets.CIFAR10(root=DATASET_PATH, train=True, download=True, transform=transform) + train_loader = torch.utils.data.DataLoader(train_set, batch_size=args.batch_size, shuffle=True, num_workers=2) + test_set = torchvision.datasets.CIFAR10(root=DATASET_PATH, train=False, download=True, transform=transform) + test_loader = torch.utils.data.DataLoader(test_set, batch_size=args.batch_size, shuffle=False, num_workers=2) # (2) initializes NVFlare client API flare.init() @@ -91,84 +81,34 @@ def main(): print(f"current_round={input_model.current_round}") # (4) loads model from NVFlare - net.load_state_dict(input_model.params) - - criterion = nn.CrossEntropyLoss() - optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + model.load_state_dict(input_model.params) + model.to(device) - # (optional) use GPU to speed things up - net.to(DEVICE) - # (optional) calculate total steps - steps = epochs * len(trainloader) - for epoch in range(epochs): # loop over the dataset multiple times + # (5) evaluate on received model for model selection + accuracy = evaluate(model, test_loader, device) - running_loss = 0.0 - for i, data in enumerate(trainloader, 0): - # get the inputs; data is a list of [inputs, labels] - # (optional) use GPU to speed things up - inputs, labels = data[0].to(DEVICE), data[1].to(DEVICE) + if flare.is_evaluate(): + output_model = flare.FLModel(metrics={"accuracy": accuracy}) + flare.send(output_model) + continue - # zero the parameter gradients + steps = args.epochs * len(train_loader) + for epoch in range(args.epochs): + for i, batch in enumerate(train_loader): + images, labels = batch[0].to(device), batch[1].to(device) optimizer.zero_grad() - - # forward + backward + optimize - outputs = net(inputs) - loss = criterion(outputs, labels) - loss.backward() + predictions = model(images) + cost = loss(predictions, labels) + cost.backward() optimizer.step() + # ... optional logging via summary_writer.add_scalar(...) - # print statistics - running_loss += loss.item() - if i % 2000 == 1999: # print every 2000 mini-batches - print(f"[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}") - global_step = input_model.current_round * steps + epoch * len(trainloader) + i - - summary_writer.add_scalar( - tag="loss_for_each_batch", - scalar=running_loss, - global_step=global_step - ) - running_loss = 0.0 - - print("Finished Training") - - PATH = "./cifar_net.pth" - torch.save(net.state_dict(), PATH) - - # (5) wraps evaluation logic into a method to re-use for - # evaluation on both trained and received model - def evaluate(input_weights): - net = Net() - net.load_state_dict(input_weights) - # (optional) use GPU to speed things up - net.to(DEVICE) - - correct = 0 - total = 0 - # since we're not training, we don't need to calculate the gradients for our outputs - with torch.no_grad(): - for data in testloader: - # (optional) use GPU to speed things up - images, labels = data[0].to(DEVICE), data[1].to(DEVICE) - # calculate outputs by running images through the network - outputs = net(images) - # the class with the highest energy is what we choose as prediction - _, predicted = torch.max(outputs.data, 1) - total += labels.size(0) - correct += (predicted == labels).sum().item() - - print(f"Accuracy of the network on the 10000 test images: {100 * correct // total} %") - return 100 * correct // total - - # (6) evaluate on received model for model selection - accuracy = evaluate(input_model.params) - # (7) construct trained FL model + # (6) construct trained FL model and (7) send back to NVFlare output_model = flare.FLModel( - params=net.cpu().state_dict(), + params=model.cpu().state_dict(), metrics={"accuracy": accuracy}, meta={"NUM_STEPS_CURRENT_ROUND": steps}, ) - # (8) send model back to NVFlare flare.send(output_model) @@ -524,10 +464,7 @@ def main(): # (5) evaluate aggregated/received model _, test_global_acc = model.evaluate(test_images, test_labels, verbose=2) - print( - f"Accuracy of the received model on round {input_model.current_round} on the 10000 test images: - {test_global_acc * 100} %" - ) + print(f"Accuracy of the received model on round {input_model.current_round} on the 10000 test images: {test_global_acc * 100} %") model.fit(train_images, train_labels, epochs=1, validation_data=(test_images, test_labels)) @@ -643,7 +580,7 @@ const frameworks = [ description: "Use the Client API to convert your training script into federated learning code. The client receives the global model from FLARE, performs local training and validation, and sends the updated model back.", code: clientCode_pt, - highlighted_lines: "29,58,61,63,139-143,145", + highlighted_lines: "12,54,58,62,90", }, { id: "model-pytorch", @@ -730,7 +667,7 @@ const frameworks = [ }, { id: "tensorflow", - colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/hello-pt.ipynb`, + colab_link: "", github_link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-tf`, sections: [ { @@ -835,15 +772,17 @@ const frameworks = [ - -
- - NVIDIA logo - - Run in Google Colab - - + + + + + NVIDIA logo + + Run in Google Colab + + + @@ -919,6 +858,7 @@ const frameworks = [ const frameworkLabels = document.querySelectorAll("#framework-labels > span"); const frameworkDropdown = document.getElementById('framework-dropdown'); const googleColab = document.getElementById('colab-button'); + const colabButtonWrapper = document.getElementById('colab-button-wrapper'); const githubButton = document.getElementById('github-button'); const codeTabs = document.getElementById("code-tabs"); @@ -1035,7 +975,12 @@ const frameworks = [ frameworks.forEach((framework) => { if (active_framework == framework.id) { - googleColab?.setAttribute("href", framework.colab_link); + if (framework.colab_link) { + googleColab?.setAttribute("href", framework.colab_link); + colabButtonWrapper?.classList.remove("hidden"); + } else { + colabButtonWrapper?.classList.add("hidden"); + } githubButton?.setAttribute("href", framework.github_link); } }); diff --git a/web/src/components/gettingStarted.astro b/web/src/components/gettingStarted.astro index 10585e0863..0a7b65b8a1 100644 --- a/web/src/components/gettingStarted.astro +++ b/web/src/components/gettingStarted.astro @@ -22,7 +22,8 @@ const walkthrough = [ "Use the Recipe API to define the FL job in Python: job.py with FedAvgRecipe, model, and client script — no separate server file.", button_text: "View Source", link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/hello-world/hello-pt/job.py`, - video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%202%20-%20Server.mp4", + // Legacy "Part 2 - Server" video; when re-enabling videos, replace with Recipe/job-focused asset + video: "", }, { id: "step3",