diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 23b921309f..5501c295eb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,19 +1,22 @@ -name: Deploy to GitHub Pages +name: Deploy to gh-pages on: - # Trigger the workflow every time you push to the `main` branch - # Using a different branch name? Replace `main` with your branch’s name + # Trigger the workflow if any web/** files are modified push: - branches: [ main ] - # Allows you to run this workflow manually from the Actions tab on GitHub. + branches: + - "main" + - "2.5" + paths: + - 'web/**' workflow_dispatch: env: site_path: ./web + version_path: / # Allow this job to clone the repo and create a page deployment permissions: - contents: read + contents: write pages: write id-token: write @@ -21,7 +24,11 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Checkout your repository using git + - name: Update version_path for non-main branches + if: ${{ github.ref_type == 'branch' && github.ref_name != 'main'}} + run: echo version_path=/version/${{ github.ref_name }}/ >> $GITHUB_ENV + + - name: Checkout your repository uses: actions/checkout@v4 - name: Setup Node @@ -31,28 +38,20 @@ jobs: cache: npm cache-dependency-path: "${{ env.site_path }}/package-lock.json" - - name: Install - shell: "bash" - working-directory: ${{ env.site_path }} + - name: Install dependencies run: npm install - - - name: Build - shell: "bash" working-directory: ${{ env.site_path }} + + - name: Build project run: npm run build + env: + PUBLIC_GH_BRANCH: ${{ github.ref_name }} + working-directory: ${{ env.site_path }} - - name: Upload Pages Artifact - uses: actions/upload-pages-artifact@v3 + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4.6.4 with: - path: "${{ env.site_path }}/dist/" - - deploy: - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + branch: gh-pages + folder: ${{ env.site_path }}/dist + target-folder: ${{ env.version_path }} + clean-exclude: version diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fabed9dfbf..32843e03e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,10 +47,11 @@ To collaborate efficiently, please read through this section and follow them. #### Checking the coding style We check code style using flake8 and isort. A bash script (`runtest.sh`) is provided to run all tests locally. +You can use `runtest.sh -f` to use black to fix your code style automatically as well. License information: all source code files should start with this paragraph: ``` -# Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/examples/advanced/psi/user_email_match/README.md b/examples/advanced/psi/user_email_match/README.md index 2b01108e8e..324bc8483e 100644 --- a/examples/advanced/psi/user_email_match/README.md +++ b/examples/advanced/psi/user_email_match/README.md @@ -113,18 +113,22 @@ copy NVFlare/examples/advanced/psi/user_email_match/data to /tmp/nvflare/psi dir **run job** ``` -nvflare simulator -w /tmp/nvflare/psi -n 3 -t 3 user_email_match/jobs/user_email_match +nvflare simulator -w /tmp/nvflare/psi/job -n 3 -t 3 user_email_match/jobs/user_email_match ``` Once job completed and succeed, you should be able to find the intersection for different sites at ``` -/tmp/nvflare/psi/simulate_job/site-1/psi/intersection.txt -/tmp/nvflare/psi/simulate_job/site-2/psi/intersection.txt -/tmp/nvflare/psi/simulate_job/site-3/psi/intersection.txt +/tmp/nvflare/psi/job/simulate_job/site-1/psi/intersection.txt +/tmp/nvflare/psi/job/simulate_job/site-2/psi/intersection.txt +/tmp/nvflare/psi/job/simulate_job/site-3/psi/intersection.txt ``` to compare these intersections, you can check with the followings: ```bash -diff <(sort /tmp/nvflare/psi/simulate_job/site-1/psi/intersection.txt) <(sort /tmp/nvflare/psi/simulate_job/site-2/psi/intersection.txt) -diff <(sort /tmp/nvflare/psi/simulate_job/site-2/psi/intersection.txt) <(sort /tmp/nvflare/psi/simulate_job/site-3/psi/intersection.txt) +diff <(sort /tmp/nvflare/psi/job/simulate_job/site-1/psi/intersection.txt) <(sort /tmp/nvflare/psi/job/simulate_job/site-2/psi/intersection.txt) +diff <(sort /tmp/nvflare/psi/job/simulate_job/site-2/psi/intersection.txt) <(sort /tmp/nvflare/psi/job/simulate_job/site-3/psi/intersection.txt) ``` + +**NOTE** +>>The PSI operator depends on openmind-psi. It now supports up-to-python 3.11 +python 3.12 is still working in progress \ No newline at end of file diff --git a/examples/advanced/psi/user_email_match/requirements.txt b/examples/advanced/psi/user_email_match/requirements.txt index b6e4a897ad..e4ffc72b58 100644 --- a/examples/advanced/psi/user_email_match/requirements.txt +++ b/examples/advanced/psi/user_email_match/requirements.txt @@ -1,3 +1,2 @@ -nvflare~=2.5.0rc -openmined.psi==1.1.1 +openmined-psi==2.0.5 pandas diff --git a/examples/advanced/rag/README.md b/examples/advanced/rag/README.md new file mode 100644 index 0000000000..a03387d29d --- /dev/null +++ b/examples/advanced/rag/README.md @@ -0,0 +1,3 @@ +# Federated Retrieval-Augmented Generation (RAG) +The examples in this directory illustrate how to use [NVIDIA FLARE](https://nvidia.github.io/NVFlare) for RAG tasks, including: +- federated embedding model training \ No newline at end of file diff --git a/examples/advanced/rag/embedding/README.md b/examples/advanced/rag/embedding/README.md new file mode 100644 index 0000000000..2dcbd97b60 --- /dev/null +++ b/examples/advanced/rag/embedding/README.md @@ -0,0 +1,79 @@ +# Embedding Model Tuning via SentenceTransformers Trainer +This example shows how to use [NVIDIA FLARE](https://nvidia.github.io/NVFlare) for embedding tuning tasks, a critical component of Retrieval-Augmented Generation (RAG). + +It illustrates how to adapt a local training script with [SentenceTransformers](https://github.com/UKPLab/sentence-transformers) trainer to NVFlare. + +## Introduction +[SentenceTransformers](https://sbert.net/) is a widely used framework for computing dense vector representations for texts. +The models are based on transformer, achieving state-of-the-art performance in various tasks. + +One major application is to embed the text in vector space for later clustering and/or retrieval using similarity metrics. + +This example illustrates a supervised fine-tuning (SFT) scheme for an embedding model with various training datasets. + +## Setup +Please make sure you set up virtual environment following [example root readme](../../../README.md). +Install additional requirements (if you already have a specific version of nvflare installed in your environment, you may want to remove nvflare in the requirements to avoid reinstalling nvflare): +``` +python3 -m pip install -r requirements.txt +``` +Models and data will be loaded directly from Huggingface, so no need to download them manually. + +## Centralized Training +### Single-session training +Centralized trainings, as the baseline for comparison with FL results, are done with the following command: +``` +bash train_single_session.sh +``` + +### Adaptation Step 1: iterative training +To adapt the centralized training script to federated application, under `launch_once = true` setting, we first need to "break" the single call to `trainer.train()` into iterative calls, one for each round of training. +For this purpose, we provided `utils/train_iterative.py` as an example, which is a modified version of `utils/train_single_session.py`. + +In the iterative training script, the `trainer.train()` call is replaced by a `for` loop, and the training epochs are split into six rounds, `unit_train_epochs = 0.25` epoch per round, in total `0.25 * 6 = 1.5` epochs, same as single session setting. + +The first round is trained with `trainer.train()`, then from the second round, +we call `trainer.train(resume_from_checkpoint=True)` with `args.num_train_epochs` incremented by `unit_train_epochs` to continue training from the last checkpoint. + +To run iterative training, we use the following command: +``` +bash train_iterative.sh +``` + +The training loss curves are shown below, single session and iterative scripts align with each other. + +![iter_single](./figs/iter_single.png) + +### Adaptation Step 2: federated with NVFlare +Once we have the iterative training script ready with "starting model" loading capability, it can be easily adapted to a NVFlare trainer by using [Client API](../../../hello-world/ml-to-fl/pt/README.md). + +The major code modifications are for receiving the global model, set it as the starting point for each round's training, and returning the trained model after each local training round. + +## Federated Training +We can use the Python JobAPI to create and run the federated training job. +``` +python3 train_fed.py +``` + +## Results +Below are the evaluation results on two test datasets - [stsb](https://huggingface.co/datasets/sentence-transformers/stsb) with embedding similarity evaluation, and [NLI](https://huggingface.co/datasets/sentence-transformers/all-nli) with triplet accuracy evaluation. The candidate models are: +- NLI: single site training using NLI data +- Squad: single site training using Squad data +- Quora: single site training using Quora data +- All: centralized training using the combined data (see `utils/train_single_session.py`) +- Federated: three sites federated learning, each site contains its own data of NLI, Squad or Quora + +We listed two similarity metrics for each of the two testing datasets: +```commandline +bash eval_all.sh +``` + + TrainData | STSB_pearson_cos | STSB_spearman_euc | NLI_cos_acc | NLI_euc_acc +--- |------------------|-------------------|-------------| --- +NLI | 0.7586 | 0.7895 | 0.8033 | 0.8045 +Squad | 0.8206 | 0.8154 | 0.8051 | 0.8042 +Quora | 0.8161 | 0.8121 | 0.7891 | 0.7854 +All | 0.8497 | 0.8523 | 0.8426 | 0.8384 +Federated | 0.8443 | 0.8367 | 0.8261 | 0.8249 + +As shown, the federated training results are better than individual site's, and can be close to the centralized training results, demonstrating the effectiveness of NVFlare in embedding model tuning tasks. \ No newline at end of file diff --git a/examples/advanced/rag/embedding/eval_all.sh b/examples/advanced/rag/embedding/eval_all.sh new file mode 100644 index 0000000000..b7afe7b600 --- /dev/null +++ b/examples/advanced/rag/embedding/eval_all.sh @@ -0,0 +1,8 @@ +for dataset_name in nli squad quora all +do + echo "Evaluation on model ${dataset_name}" + python utils/eval_model.py --model_path /tmp/embed/cen/models_single/mpnet-base-${dataset_name}/final +done + +echo "Evaluation on model federated" +python utils/eval_model.py --model_path /tmp/embed/nvflare/workspace_api/site-1/models/mpnet-base-nli/global \ No newline at end of file diff --git a/examples/advanced/rag/embedding/figs/iter_single.png b/examples/advanced/rag/embedding/figs/iter_single.png new file mode 100644 index 0000000000..ff6e6bce9d Binary files /dev/null and b/examples/advanced/rag/embedding/figs/iter_single.png differ diff --git a/examples/advanced/rag/embedding/requirements.txt b/examples/advanced/rag/embedding/requirements.txt new file mode 100644 index 0000000000..dd1cf6cf27 --- /dev/null +++ b/examples/advanced/rag/embedding/requirements.txt @@ -0,0 +1,7 @@ +nvflare~=2.5.0 +torch +datasets +scikit-learn +tensorboard +transformers +sentence-transformers \ No newline at end of file diff --git a/examples/advanced/rag/embedding/src/st_model.py b/examples/advanced/rag/embedding/src/st_model.py new file mode 100755 index 0000000000..19c0ffd10c --- /dev/null +++ b/examples/advanced/rag/embedding/src/st_model.py @@ -0,0 +1,26 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +from sentence_transformers import SentenceTransformer + + +class SenTransModel(torch.nn.Module): + def __init__(self, model_name): + super(SenTransModel, self).__init__() + self.model = SentenceTransformer(model_name) + + def forward(self, input_id): + output = self.model(input_ids=input_id, return_dict=False) + return output diff --git a/examples/advanced/rag/embedding/src/train_fl.py b/examples/advanced/rag/embedding/src/train_fl.py new file mode 100644 index 0000000000..b501f69e01 --- /dev/null +++ b/examples/advanced/rag/embedding/src/train_fl.py @@ -0,0 +1,158 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import copy + +from datasets import load_dataset +from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, SentenceTransformerTrainingArguments +from sentence_transformers.losses import MultipleNegativesRankingLoss +from sentence_transformers.training_args import BatchSamplers +from transformers import trainer_utils + +import nvflare.client as flare + + +def main(): + # argparse + parser = argparse.ArgumentParser(description="Train a model on a dataset") + parser.add_argument( + "--model_name", + type=str, + default="microsoft/mpnet-base", + ) + parser.add_argument( + "--dataset_name", + type=str, + default="nli", + ) + args = parser.parse_args() + model_name = args.model_name + dataset_name = args.dataset_name + + # Load a model to finetune with + model = SentenceTransformer(model_name) + + # Load training datasets + if dataset_name == "nli": + # (anchor, positive, negative) + dataset_train = load_dataset("sentence-transformers/all-nli", "triplet", split="train[:16000]") + dataset_val = load_dataset("sentence-transformers/all-nli", "triplet", split="dev") + elif dataset_name == "squad": + # (question, answer) + dataset_train = load_dataset("sentence-transformers/squad", split="train[:16000]") + dataset_val = load_dataset("sentence-transformers/squad", split="train[16000:18000]") + elif dataset_name == "quora": + # (anchor, positive) + dataset_train = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train[:16000]") + dataset_val = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train[16000:18000]") + else: + raise ValueError(f"Unknown dataset name: {dataset_name}") + + # Load loss function + loss = MultipleNegativesRankingLoss(model) + + base_model_name = model_name.split("/")[-1] + output_dir = f"./models/{base_model_name}-{dataset_name}" + unit_train_epochs = 0.25 + # Specify training arguments + args = SentenceTransformerTrainingArguments( + # Required parameter: + output_dir=output_dir, + # Optional training parameters: + num_train_epochs=unit_train_epochs, + per_device_train_batch_size=16, + per_device_eval_batch_size=16, + learning_rate=1e-6, + lr_scheduler_type="constant", + bf16=True, + batch_sampler=BatchSamplers.NO_DUPLICATES, + # Optional tracking/debugging parameters: + eval_strategy="steps", + eval_steps=50, + save_strategy="steps", + save_steps=50, + save_total_limit=1, + # logging parameters: + logging_dir=f"{output_dir}/logs", + logging_strategy="steps", + logging_steps=50, + report_to="tensorboard", + ) + + # Define trainer + trainer = SentenceTransformerTrainer( + model=model, + args=args, + train_dataset=dataset_train, + eval_dataset=dataset_val, + loss=loss, + ) + + # initializes NVFlare client API + flare.init() + + while flare.is_running(): + # receives FLModel from NVFlare + input_model = flare.receive() + curr_round = input_model.current_round + print(f"current_round={curr_round}") + + # Update the key name received from global model if using model def file + global_model = copy.deepcopy(input_model.params) + for key in list(global_model.keys()): + global_model[key.replace("model.", "", 1)] = global_model.pop(key) + + # evaluate on received global model + trainer.model.load_state_dict(global_model) + eval_loss_dict = trainer.evaluate() + eval_loss = float(eval_loss_dict["eval_loss"]) + print(f"Evaluation loss: {eval_loss}") + # Save the global model + model.save_pretrained(f"{output_dir}/global") + + # Train the model + if curr_round == 0: + # First round: start from scratch + trainer.train() + else: + # Subsequent rounds: start from the previous model + # Since we perform iterative training by using "resume" functionality + # we need to replace the resume weights with global weights every round + resume_from_checkpoint_folder = trainer_utils.get_last_checkpoint(trainer.args.output_dir) + # update local record with global model weights + trainer.model.save_pretrained(resume_from_checkpoint_folder) + # increment the number of training epochs so that the trainer will continue training + args.num_train_epochs += unit_train_epochs + # continue training + trainer.train(resume_from_checkpoint=True) + + # update the key name sent to global model + out_param = trainer.model.state_dict() + for key in list(out_param.keys()): + out_param["model." + key] = out_param.pop(key).cpu() + num_steps = trainer.train_dataset.num_rows * unit_train_epochs + + # construct trained FL model + output_model = flare.FLModel( + params=out_param, + metrics={"eval_loss": eval_loss}, + meta={"NUM_STEPS_CURRENT_ROUND": num_steps}, + ) + # send model back to NVFlare + flare.send(output_model) + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/rag/embedding/train_fed.py b/examples/advanced/rag/embedding/train_fed.py new file mode 100644 index 0000000000..428e28f8bb --- /dev/null +++ b/examples/advanced/rag/embedding/train_fed.py @@ -0,0 +1,56 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from nvflare import FedJob +from nvflare.app_common.widgets.intime_model_selector import IntimeModelSelector +from nvflare.app_common.workflows.fedavg import FedAvg +from nvflare.app_opt.pt.file_model_persistor import PTFileModelPersistor +from nvflare.job_config.script_runner import ScriptRunner + +if __name__ == "__main__": + n_clients = 3 + num_rounds = 7 + train_script = "src/train_fl.py" + + # Create the FedJob + job = FedJob(name="embed_fedavg", min_clients=3, mandatory_clients=["site-1", "site-2", "site-3"]) + + # Define the FedAvg controller workflow and send to server + controller = FedAvg( + num_clients=n_clients, + num_rounds=num_rounds, + ) + job.to(controller, "server") + + # Define the model persistor and send to server + # First send the model to the server + job.to("src/st_model.py", "server") + # Then send the model persistor to the server + model_args = {"path": "src.st_model.SenTransModel", "args": {"model_name": "microsoft/mpnet-base"}} + job.to(PTFileModelPersistor(model=model_args), "server", id="persistor") + + # Add model selection widget and send to server + job.to(IntimeModelSelector(key_metric="eval_loss", negate_key_metric=True), "server", id="model_selector") + + # Send ScriptRunner to all clients + runner = ScriptRunner(script=train_script, script_args="--dataset_name nli") + job.to(runner, "site-1") + runner = ScriptRunner(script=train_script, script_args="--dataset_name squad") + job.to(runner, "site-2") + runner = ScriptRunner(script=train_script, script_args="--dataset_name quora") + job.to(runner, "site-3") + + job.export_job("/tmp/embed/nvflare/job_api") + job.simulator_run("/tmp/embed/nvflare/workspace_api") diff --git a/examples/advanced/rag/embedding/train_iterative.sh b/examples/advanced/rag/embedding/train_iterative.sh new file mode 100644 index 0000000000..1ac5c89ff3 --- /dev/null +++ b/examples/advanced/rag/embedding/train_iterative.sh @@ -0,0 +1,5 @@ +for dataset_name in nli squad quora +do + echo "Training on ${dataset_name}" + python utils/train_iterative.py --dataset_name ${dataset_name} +done diff --git a/examples/advanced/rag/embedding/train_single_session.sh b/examples/advanced/rag/embedding/train_single_session.sh new file mode 100644 index 0000000000..248c7abb84 --- /dev/null +++ b/examples/advanced/rag/embedding/train_single_session.sh @@ -0,0 +1,5 @@ +for dataset_name in nli squad quora all +do + echo "Training on ${dataset_name}" + python utils/train_single_session.py --dataset_name ${dataset_name} +done diff --git a/examples/advanced/rag/embedding/utils/eval_model.py b/examples/advanced/rag/embedding/utils/eval_model.py new file mode 100644 index 0000000000..757819fbe8 --- /dev/null +++ b/examples/advanced/rag/embedding/utils/eval_model.py @@ -0,0 +1,58 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse + +from datasets import load_dataset +from sentence_transformers import SentenceTransformer +from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator, SimilarityFunction, TripletEvaluator + + +def main(): + # argparse + parser = argparse.ArgumentParser(description="Train a model on a dataset") + parser.add_argument( + "--model_path", + type=str, + ) + args = parser.parse_args() + model_path = args.model_path + + # Load a model to finetune with + model = SentenceTransformer(model_path) + + # Evaluate the trained model on the test set with embedding similarity + dataset_test = load_dataset("sentence-transformers/stsb", split="validation") + test_evaluator = EmbeddingSimilarityEvaluator( + sentences1=dataset_test["sentence1"], + sentences2=dataset_test["sentence2"], + scores=dataset_test["score"], + main_similarity=SimilarityFunction.COSINE, + ) + metric_score = test_evaluator(model) + print(f"Test set evaluation on STSB: {metric_score}") + + # Evaluate the trained model on the test set with triplet loss + dataset_test = load_dataset("sentence-transformers/all-nli", "triplet", split="test") + test_evaluator = TripletEvaluator( + anchors=dataset_test["anchor"], + positives=dataset_test["positive"], + negatives=dataset_test["negative"], + ) + metric_score = test_evaluator(model) + print(f"Test set evaluation on NLI: {metric_score}") + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/rag/embedding/utils/train_iterative.py b/examples/advanced/rag/embedding/utils/train_iterative.py new file mode 100644 index 0000000000..9dcd02fba5 --- /dev/null +++ b/examples/advanced/rag/embedding/utils/train_iterative.py @@ -0,0 +1,152 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse + +from datasets import load_dataset +from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, SentenceTransformerTrainingArguments +from sentence_transformers.losses import MultipleNegativesRankingLoss +from sentence_transformers.training_args import BatchSamplers + + +def main(): + # argparse + parser = argparse.ArgumentParser(description="Train a model on a dataset") + parser.add_argument( + "--model_name", + type=str, + default="microsoft/mpnet-base", + ) + parser.add_argument( + "--dataset_name", + type=str, + default="nli", + ) + args = parser.parse_args() + model_name = args.model_name + dataset_name = args.dataset_name + + # Load a model to finetune with + model = SentenceTransformer(model_name) + + # Load training datasets + # (anchor, positive, negative) + dataset_nli_train = load_dataset("sentence-transformers/all-nli", "triplet", split="train[:16000]") + # (question, answer) + dataset_squad_train = load_dataset("sentence-transformers/squad", split="train[:16000]") + # (anchor, positive) + dataset_quora_train = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train[:16000]") + + # Combine all datasets into a dictionary with dataset names to datasets + dataset_all_train = { + "all-nli-triplet": dataset_nli_train, + "squad": dataset_squad_train, + "quora": dataset_quora_train, + } + + # Load validation datasets + # (anchor, positive, negative) + dataset_nli_val = load_dataset("sentence-transformers/all-nli", "triplet", split="dev") + # (question, answer) + dataset_squad_val = load_dataset("sentence-transformers/squad", split="train[16000:18000]") + # (anchor, positive) + dataset_quora_val = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train[16000:18000]") + # Combine all datasets into a dictionary with dataset names to datasets + dataset_all_val = { + "all-nli-triplet": dataset_nli_val, + "squad": dataset_squad_val, + "quora": dataset_quora_val, + } + + # Load loss function + loss_mnrl = MultipleNegativesRankingLoss(model) + # Create a mapping with dataset names to loss functions + loss_all = { + "all-nli-triplet": loss_mnrl, + "squad": loss_mnrl, + "quora": loss_mnrl, + } + + if dataset_name == "all": + dataset_train = dataset_all_train + dataset_val = dataset_all_val + loss_func = loss_all + elif dataset_name == "nli": + dataset_train = dataset_nli_train + dataset_val = dataset_nli_val + loss_func = loss_mnrl + elif dataset_name == "squad": + dataset_train = dataset_squad_train + dataset_val = dataset_squad_val + loss_func = loss_mnrl + elif dataset_name == "quora": + dataset_train = dataset_quora_train + dataset_val = dataset_quora_val + loss_func = loss_mnrl + else: + raise ValueError(f"Unknown dataset name: {dataset_name}") + + base_model_name = model_name.split("/")[-1] + output_dir = f"/tmp/embed/cen/models_iter/{base_model_name}-{dataset_name}" + unit_train_epochs = 0.25 + # Specify training arguments + args = SentenceTransformerTrainingArguments( + # Required parameter: + output_dir=output_dir, + # Optional training parameters: + num_train_epochs=unit_train_epochs, + per_device_train_batch_size=16, + per_device_eval_batch_size=16, + learning_rate=1e-6, + lr_scheduler_type="constant", + bf16=True, + batch_sampler=BatchSamplers.NO_DUPLICATES, + # Optional tracking/debugging parameters: + eval_strategy="steps", + eval_steps=50, + save_strategy="steps", + save_steps=50, + save_total_limit=1, + # logging parameters: + logging_dir=f"{output_dir}/logs", + logging_strategy="steps", + logging_steps=50, + report_to="tensorboard", + ) + + # Define trainer + trainer = SentenceTransformerTrainer( + model=model, + args=args, + train_dataset=dataset_train, + eval_dataset=dataset_val, + loss=loss_func, + ) + + for round in range(6): + # Train the model + # First round: start from scratch + if round == 0: + trainer.train() + # Subsequent rounds: start from the previous model + else: + args.num_train_epochs += unit_train_epochs + trainer.train(resume_from_checkpoint=True) + + # Save the trained model + model.save_pretrained(f"{output_dir}/final") + + +if __name__ == "__main__": + main() diff --git a/examples/advanced/rag/embedding/utils/train_single_session.py b/examples/advanced/rag/embedding/utils/train_single_session.py new file mode 100644 index 0000000000..5c66976de9 --- /dev/null +++ b/examples/advanced/rag/embedding/utils/train_single_session.py @@ -0,0 +1,142 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse + +from datasets import load_dataset +from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, SentenceTransformerTrainingArguments +from sentence_transformers.losses import MultipleNegativesRankingLoss +from sentence_transformers.training_args import BatchSamplers + + +def main(): + # argparse + parser = argparse.ArgumentParser(description="Train a model on a dataset") + parser.add_argument( + "--model_name", + type=str, + default="microsoft/mpnet-base", + ) + parser.add_argument( + "--dataset_name", + type=str, + default="nli", + ) + args = parser.parse_args() + model_name = args.model_name + dataset_name = args.dataset_name + + # Load a model to finetune with + model = SentenceTransformer(model_name) + + # Load training datasets + # (anchor, positive, negative) + dataset_nli_train = load_dataset("sentence-transformers/all-nli", "triplet", split="train[:16000]") + # (question, answer) + dataset_squad_train = load_dataset("sentence-transformers/squad", split="train[:16000]") + # (anchor, positive) + dataset_quora_train = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train[:16000]") + + # Combine all datasets into a dictionary with dataset names to datasets + dataset_all_train = { + "all-nli-triplet": dataset_nli_train, + "squad": dataset_squad_train, + "quora": dataset_quora_train, + } + + # Load validation datasets + # (anchor, positive, negative) + dataset_nli_val = load_dataset("sentence-transformers/all-nli", "triplet", split="dev") + # (question, answer) + dataset_squad_val = load_dataset("sentence-transformers/squad", split="train[16000:18000]") + # (anchor, positive) + dataset_quora_val = load_dataset("sentence-transformers/quora-duplicates", "pair", split="train[16000:18000]") + # Combine all datasets into a dictionary with dataset names to datasets + dataset_all_val = { + "all-nli-triplet": dataset_nli_val, + "squad": dataset_squad_val, + "quora": dataset_quora_val, + } + + # Load loss function + loss_mnrl = MultipleNegativesRankingLoss(model) + # Create a mapping with dataset names to loss functions + loss_all = { + "all-nli-triplet": loss_mnrl, + "squad": loss_mnrl, + "quora": loss_mnrl, + } + + if dataset_name == "all": + dataset_train = dataset_all_train + dataset_val = dataset_all_val + loss_func = loss_all + elif dataset_name == "nli": + dataset_train = dataset_nli_train + dataset_val = dataset_nli_val + loss_func = loss_mnrl + elif dataset_name == "squad": + dataset_train = dataset_squad_train + dataset_val = dataset_squad_val + loss_func = loss_mnrl + elif dataset_name == "quora": + dataset_train = dataset_quora_train + dataset_val = dataset_quora_val + loss_func = loss_mnrl + else: + raise ValueError(f"Unknown dataset name: {dataset_name}") + + base_model_name = model_name.split("/")[-1] + output_dir = f"/tmp/embed/cen/models_single/{base_model_name}-{dataset_name}" + # Specify training arguments + args = SentenceTransformerTrainingArguments( + # Required parameter: + output_dir=output_dir, + # Optional training parameters: + num_train_epochs=1.5, + per_device_train_batch_size=16, + per_device_eval_batch_size=16, + learning_rate=1e-6, + lr_scheduler_type="constant", + bf16=True, + batch_sampler=BatchSamplers.NO_DUPLICATES, + # Optional tracking/debugging parameters: + eval_strategy="steps", + eval_steps=50, + save_strategy="steps", + save_steps=50, + save_total_limit=1, + # logging parameters: + logging_dir=f"{output_dir}/logs", + logging_strategy="steps", + logging_steps=50, + report_to="tensorboard", + ) + + # Define trainer + trainer = SentenceTransformerTrainer( + model=model, + args=args, + train_dataset=dataset_train, + eval_dataset=dataset_val, + loss=loss_func, + ) + trainer.train() + + # Save the trained model + model.save_pretrained(f"{output_dir}/final") + + +if __name__ == "__main__": + main() diff --git a/examples/hello-world/hello-pt-resnet/README.md b/examples/hello-world/hello-pt-resnet/README.md new file mode 100644 index 0000000000..4b1a0f51a6 --- /dev/null +++ b/examples/hello-world/hello-pt-resnet/README.md @@ -0,0 +1,20 @@ +# Hello PyTorch ResNet + +Example of using [NVIDIA FLARE](https://nvflare.readthedocs.io/en/main/index.html) to train an image classifier +using federated averaging ([FedAvg](https://arxiv.org/abs/1602.05629)) +and [PyTorch](https://pytorch.org/) as the deep learning training framework. Comparing with the Hello PyTorch example, it uses the torchvision ResNet, +instead of the SimpleNetwork. + +> **_NOTE:_** This example uses the [CIFAR-10](https://www.cs.toronto.edu/~kriz/cifar.html) dataset and will load its data within the client train code. + +The Job API only supports the object instance created directly out of the Python Class. It does not support +the object instance created through using the Python function. Comparing with the hello-pt example, +if we replace the SimpleNetwork() object with the resnet18(num_classes=10), +the "resnet18(num_classes=10)" creates an torchvision "ResNet" object instance out of the "resnet18" function. +As shown in the [torchvision reset](https://github.com/pytorch/vision/blob/main/torchvision/models/resnet.py#L684-L705), +the resnet18 is a Python function, which creates and returns a ResNet object. The job API can +only use the "ResNet" object instance for generating the job config. It can not detect the object creating function logic in the "resnet18". + +This example demonstrates how to wrap up the resnet18 Python function into a Resnet18 Python class. Then uses the Resnet18(num_classes=10) +object instance in the job API. After replacing the SimpleNetwork() with the Resnet18(num_classes=10), +you can follow the exact same steps in the hello-pt example to run the fedavg_script_runner_pt.py. diff --git a/examples/hello-world/hello-pt-resnet/fedavg_script_runner_pt.py b/examples/hello-world/hello-pt-resnet/fedavg_script_runner_pt.py new file mode 100644 index 0000000000..ece630dcdb --- /dev/null +++ b/examples/hello-world/hello-pt-resnet/fedavg_script_runner_pt.py @@ -0,0 +1,41 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from src.resnet_18 import Resnet18 + +from nvflare.app_opt.pt.job_config.fed_avg import FedAvgJob +from nvflare.job_config.script_runner import ScriptRunner + +if __name__ == "__main__": + n_clients = 2 + num_rounds = 2 + train_script = "src/hello-pt_cifar10_fl.py" + + job = FedAvgJob( + name="hello-pt_cifar10_fedavg", + n_clients=n_clients, + num_rounds=num_rounds, + initial_model=Resnet18(num_classes=10), + ) + + # Add clients + for i in range(n_clients): + executor = ScriptRunner( + script=train_script, + script_args="", # f"--batch_size 32 --data_path /tmp/data/site-{i}" + ) + job.to(executor, f"site-{i + 1}") + + # job.export_job("/tmp/nvflare/jobs/job_config") + job.simulator_run("/tmp/nvflare/jobs/workdir", gpu="0") diff --git a/examples/hello-world/hello-pt-resnet/requirements.txt b/examples/hello-world/hello-pt-resnet/requirements.txt new file mode 100644 index 0000000000..919cc32ba2 --- /dev/null +++ b/examples/hello-world/hello-pt-resnet/requirements.txt @@ -0,0 +1,3 @@ +nvflare~=2.5.0rc +torch +torchvision diff --git a/examples/hello-world/hello-pt-resnet/src/hello-pt_cifar10_fl.py b/examples/hello-world/hello-pt-resnet/src/hello-pt_cifar10_fl.py new file mode 100644 index 0000000000..860e6f0cac --- /dev/null +++ b/examples/hello-world/hello-pt-resnet/src/hello-pt_cifar10_fl.py @@ -0,0 +1,103 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import torch +from resnet_18 import Resnet18 +from torch import nn +from torch.optim import SGD +from torch.utils.data.dataloader import DataLoader +from torchvision.datasets import CIFAR10 +from torchvision.transforms import Compose, Normalize, ToTensor + +import nvflare.client as flare +from nvflare.client.tracking import SummaryWriter + +DATASET_PATH = "/tmp/nvflare/data" + + +def main(): + batch_size = 4 + epochs = 5 + lr = 0.01 + model = Resnet18(num_classes=10) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + loss = nn.CrossEntropyLoss() + optimizer = SGD(model.parameters(), lr=lr, momentum=0.9) + transforms = Compose( + [ + ToTensor(), + Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), + ] + ) + + flare.init() + sys_info = flare.system_info() + client_name = sys_info["site_name"] + + train_dataset = CIFAR10( + root=os.path.join(DATASET_PATH, client_name), + transform=transforms, + download=True, + train=True, + ) + train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) + + summary_writer = SummaryWriter() + while flare.is_running(): + input_model = flare.receive() + print(f"current_round={input_model.current_round}") + + model.load_state_dict(input_model.params) + model.to(device) + + steps = epochs * len(train_loader) + for epoch in range(epochs): + running_loss = 0.0 + for i, batch in enumerate(train_loader): + images, labels = batch[0].to(device), batch[1].to(device) + optimizer.zero_grad() + + predictions = model(images) + cost = loss(predictions, labels) + cost.backward() + optimizer.step() + + running_loss += cost.cpu().detach().numpy() / images.size()[0] + if i % 3000 == 0: + print(f"Epoch: {epoch}/{epochs}, Iteration: {i}, Loss: {running_loss / 3000}") + global_step = input_model.current_round * steps + epoch * len(train_loader) + 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(model.state_dict(), PATH) + + output_model = flare.FLModel( + params=model.cpu().state_dict(), + meta={"NUM_STEPS_CURRENT_ROUND": steps}, + ) + + flare.send(output_model) + + +if __name__ == "__main__": + main() diff --git a/examples/hello-world/hello-pt-resnet/src/resnet_18.py b/examples/hello-world/hello-pt-resnet/src/resnet_18.py new file mode 100644 index 0000000000..3420fdd741 --- /dev/null +++ b/examples/hello-world/hello-pt-resnet/src/resnet_18.py @@ -0,0 +1,33 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from typing import Any, Optional + +from torchvision.models import ResNet +from torchvision.models._utils import _ovewrite_named_param +from torchvision.models.resnet import BasicBlock, ResNet18_Weights + + +class Resnet18(ResNet): + def __init__(self, num_classes, weights: Optional[ResNet18_Weights] = None, progress: bool = True, **kwargs: Any): + self.num_classes = num_classes + + weights = ResNet18_Weights.verify(weights) + + if weights is not None: + _ovewrite_named_param(kwargs, "num_classes", len(weights.meta["categories"])) + + super().__init__(BasicBlock, [2, 2, 2, 2], num_classes=num_classes, **kwargs) + + if weights is not None: + super().load_state_dict(weights.get_state_dict(progress=progress)) diff --git a/nvflare/app_common/ccwf/ccwf_job.py b/nvflare/app_common/ccwf/ccwf_job.py index 13631ab1d3..36ef6a1300 100644 --- a/nvflare/app_common/ccwf/ccwf_job.py +++ b/nvflare/app_common/ccwf/ccwf_job.py @@ -20,8 +20,7 @@ from nvflare.app_common.abstract.shareable_generator import ShareableGenerator from nvflare.app_common.app_constant import AppConstants from nvflare.app_common.ccwf.common import Constant, CyclicOrder -from nvflare.fuel.utils.validation_utils import check_object_type -from nvflare.job_config.api import FedJob, has_add_to_job_method +from nvflare.job_config.api import FedJob, validate_object_for_job from nvflare.widgets.widget import Widget from .cse_client_ctl import CrossSiteEvalClientController @@ -31,6 +30,8 @@ from .swarm_client_ctl import SwarmClientController from .swarm_server_ctl import SwarmServerController +_EXECUTOR_TASKS = ["train", "validate", "submit_model"] + class SwarmServerConfig: def __init__( @@ -190,6 +191,7 @@ def __init__( name: str = "fed_job", min_clients: int = 1, mandatory_clients: Optional[List[str]] = None, + executor_tasks: Optional[List[str]] = None, external_resources: Optional[str] = None, ): """Client-Controlled Workflow Job. @@ -200,9 +202,19 @@ def __init__( name (name, optional): name of the job. Defaults to "fed_job" min_clients (int, optional): the minimum number of clients for the job. Defaults to 1. mandatory_clients (List[str], optional): mandatory clients to run the job. Default None. + executor_tasks (List[str], optional): tasks for the executor external_resources (str, optional): External resources directory or filename. Defaults to None. """ super().__init__(name, min_clients, mandatory_clients) + + # A CCWF job can have multiple workflows (swarm, cyclic, etc.), but can only have one executor for training! + # This executor can be added by any workflow. + self.executor = None + + self.executor_tasks = executor_tasks + if not executor_tasks: + self.executor_tasks = _EXECUTOR_TASKS + if external_resources: self.to_server(external_resources) self.to_clients(external_resources) @@ -250,7 +262,10 @@ def add_swarm( wait_time_after_min_resps_received=client_config.wait_time_after_min_resps_received, ) self.to_clients(client_controller, tasks=["swarm_*"]) - self.to_clients(client_config.executor, tasks=["train", "validate", "submit_model"]) + if not self.executor: + # We add the executor only if it's not added yet. + self.to_clients(client_config.executor, tasks=self.executor_tasks) + self.executor = client_config.executor if client_config.model_selector: self.to_clients(client_config.model_selector, id="model_selector") @@ -288,7 +303,11 @@ def add_cyclic( final_result_ack_timeout=client_config.final_result_ack_timeout, ) self.to_clients(client_controller, tasks=["cyclic_*"]) - self.to_clients(client_config.executor, tasks=["train", "validate", "submit_model"]) + + if not self.executor: + # We add the executor only if it's not added yet. + self.to_clients(client_config.executor, tasks=self.executor_tasks) + self.executor = client_config.executor if cse_config: self.add_cross_site_eval(cse_config, persistor_id) @@ -318,21 +337,3 @@ def add_cross_site_eval( get_model_timeout=cse_config.get_model_timeout, ) self.to_clients(client_controller, tasks=["cse_*"]) - - -def validate_object_for_job(name, obj, obj_type): - """Check whether the specified object is valid for job. - The object must either have the add_to_fed_job method or is valid object type. - - Args: - name: name of the object - obj: the object to be checked - obj_type: the object type that the object should be, if it doesn't have the add_to_fed_job method. - - Returns: None - - """ - if has_add_to_job_method(obj): - return - - check_object_type(name, obj, obj_type) diff --git a/nvflare/app_common/psi/dh_psi/dh_psi_workflow.py b/nvflare/app_common/psi/dh_psi/dh_psi_workflow.py index 95212ff73f..a69a372394 100644 --- a/nvflare/app_common/psi/dh_psi/dh_psi_workflow.py +++ b/nvflare/app_common/psi/dh_psi/dh_psi_workflow.py @@ -261,6 +261,9 @@ def parallel_backward_pass(self, ordered_clients: list, intersect_site: SiteSize other_sites = [site for site in ordered_clients if site.name != intersect_site.name] other_sites = self.get_updated_site_sizes(other_sites) + # todo: we might be able to skip these steps, simply broadcast the final intersection result directly + # todo: to all other sites, this avoid the backward pass of the intersection calculation + s = intersect_site other_site_sizes = set([site.size for site in other_sites]) setup_msgs: Dict[str, str] = self.prepare_setup_messages(s, other_site_sizes) diff --git a/nvflare/job_config/api.py b/nvflare/job_config/api.py index 3fd0c888bd..495b808f94 100644 --- a/nvflare/job_config/api.py +++ b/nvflare/job_config/api.py @@ -14,14 +14,14 @@ import os.path import re import uuid -from typing import Any, List, Optional +from typing import Any, List, Optional, Union from nvflare.apis.executor import Executor from nvflare.apis.filter import Filter from nvflare.apis.impl.controller import Controller from nvflare.apis.job_def import ALL_SITES, SERVER_SITE_NAME from nvflare.fuel.utils.class_utils import get_component_init_parameters -from nvflare.fuel.utils.validation_utils import check_positive_int +from nvflare.fuel.utils.validation_utils import check_object_type, check_positive_int from nvflare.job_config.fed_app_config import ClientAppConfig, FedAppConfig, ServerAppConfig from nvflare.job_config.fed_job_config import FedJobConfig @@ -33,25 +33,39 @@ class FedApp: - def __init__(self): + def __init__(self, app_config: Union[ClientAppConfig, ServerAppConfig]): """FedApp handles `ClientAppConfig` and `ServerAppConfig` and allows setting task result or task data filters.""" - self.app = None # Union[ClientAppConfig, ServerAppConfig] + self.app_config = app_config self._used_ids = [] + # obj_id => comp_id + # obj_id is the Python's object ID; comp_id is the component ID for job config + # _oid_to_cid keeps the mapping between obj_id and comp_id. + # this is to make sure that when the same object is used, it is configured only once in the job. + self._oid_to_cid = {} + def get_app_config(self): - return self.app + return self.app_config def add_task_result_filter(self, tasks: List[str], task_filter: Filter): - self.app.add_task_result_filter(tasks, task_filter) + self.app_config.add_task_result_filter(tasks, task_filter) def add_task_data_filter(self, tasks: List[str], task_filter: Filter): - self.app.add_task_data_filter(tasks, task_filter) - - def add_component(self, component, id=None): - if id is None: - id = "component" - final_id = self.generate_tracked_id(id) - self.app.add_component(final_id, component) + self.app_config.add_task_data_filter(tasks, task_filter) + + def add_component(self, component, comp_id=None): + # is the component already configured? + oid = id(component) + cid = self._oid_to_cid.get(oid) + if cid: + # the component is already configured + return cid + + if comp_id is None: + comp_id = "component" + final_id = self.generate_tracked_id(comp_id) + self.app_config.add_component(final_id, component) + self._oid_to_cid[oid] = final_id return final_id def _generate_id(self, id: str = "") -> str: @@ -79,7 +93,7 @@ def add_external_script(self, ext_script: str): Args: ext_script: List of external scripts that need to be deployed to the client/server. """ - self.app.add_ext_script(ext_script) + self.app_config.add_ext_script(ext_script) def add_external_dir(self, ext_dir: str): """Register external folder to include them in custom directory. @@ -87,7 +101,7 @@ def add_external_dir(self, ext_dir: str): Args: ext_dir: external folder that need to be deployed to the client/server. """ - self.app.add_ext_dir(ext_dir) + self.app_config.add_ext_dir(ext_dir) def _add_resource(self, resource: str): if not isinstance(resource, str): @@ -122,26 +136,24 @@ def __init__(self, obj: Any, target: str, comp_id: str): class ClientApp(FedApp): def __init__(self): """Wrapper around `ClientAppConfig`.""" - super().__init__() - self.app = ClientAppConfig() + super().__init__(ClientAppConfig()) def add_executor(self, executor: Executor, tasks=None): if not tasks: tasks = ["*"] # Add executor for any task by default - self.app.add_executor(tasks, executor) + self.app_config.add_executor(tasks, executor) class ServerApp(FedApp): """Wrapper around `ServerAppConfig`.""" def __init__(self): - super().__init__() - self.app: ServerAppConfig = ServerAppConfig() + super().__init__(ServerAppConfig()) def add_controller(self, controller: Controller, id=None): if not id: id = "controller" - self.app.add_workflow(self.generate_tracked_id(id), controller) + self.app_config.add_workflow(self.generate_tracked_id(id), controller) class FedJob: @@ -571,3 +583,21 @@ def check_kwargs(args_to_check: dict, args_expected: dict): def has_add_to_job_method(obj: Any) -> bool: add_to_job_method = getattr(obj, _ADD_TO_JOB_METHOD_NAME, None) return add_to_job_method is not None and callable(add_to_job_method) + + +def validate_object_for_job(name, obj, obj_type): + """Check whether the specified object is valid for job. + The object must either have the add_to_fed_job method or is valid object type. + + Args: + name: name of the object + obj: the object to be checked + obj_type: the object type that the object should be, if it doesn't have the add_to_fed_job method. + + Returns: None + + """ + if has_add_to_job_method(obj): + return + + check_object_type(name, obj, obj_type) diff --git a/nvflare/tool/job/job_cli.py b/nvflare/tool/job/job_cli.py index a5688e7195..e458e9b96f 100644 --- a/nvflare/tool/job/job_cli.py +++ b/nvflare/tool/job/job_cli.py @@ -15,7 +15,6 @@ import os import shutil import traceback -from distutils.dir_util import copy_tree from tempfile import mkdtemp from typing import List, Optional, Tuple @@ -157,7 +156,7 @@ def create_job(cmd_args): for app_name in template_srcs: src = template_srcs[app_name] app_config_dir = get_config_dir(job_folder, app_name) - copy_tree(src=src, dst=app_config_dir) + shutil.copytree(src=src, dst=app_config_dir, dirs_exist_ok=True) remove_extra_files(app_config_dir) prepare_meta_config(cmd_args, template_src, app_names) app_variable_values = prepare_job_config(cmd_args, app_names) @@ -352,7 +351,7 @@ def submit_job(cmd_args): raise ValueError(f"invalid job folder: {cmd_args.job_folder}") temp_job_dir = mkdtemp() - copy_tree(cmd_args.job_folder, temp_job_dir) + shutil.copytree(cmd_args.job_folder, temp_job_dir, dirs_exist_ok=True) app_dirs = get_app_dirs_from_job_folder(cmd_args.job_folder) app_names = [os.path.basename(f) for f in app_dirs] @@ -704,7 +703,7 @@ def prepare_app_scripts(job_folder, app_custom_dirs, cmd_args): if os.path.exists(script_dir): if script_dir == job_folder or is_subdir(job_folder, script_dir): raise ValueError("job_folder must not be the same or sub directory of script_dir") - copy_tree(cmd_args.script_dir, app_custom_dir) + shutil.copytree(cmd_args.script_dir, app_custom_dir, dirs_exist_ok=True) remove_pycache_files(app_custom_dir) else: raise ValueError(f"{cmd_args.script_dir} doesn't exists") diff --git a/web/astro.config.mjs b/web/astro.config.mjs index 32414d4b29..1efb442010 100644 --- a/web/astro.config.mjs +++ b/web/astro.config.mjs @@ -1,8 +1,10 @@ import { defineConfig } from "astro/config"; import tailwind from "@astrojs/tailwind"; +const branch = process.env.PUBLIC_GH_BRANCH; + export default defineConfig({ site: "https://nvidia.github.io", - base: "/NVFlare", + base: branch === 'main' ? '/NVFlare' : `/NVFlare/version/${branch}`, integrations: [tailwind()], }); diff --git a/web/package-lock.json b/web/package-lock.json index 53a75a9611..c259b44ca5 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -5760,9 +5760,9 @@ } }, "node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" }, "node_modules/periscopic": { "version": "3.1.0", @@ -6606,9 +6606,9 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "bin": { "rollup": "dist/bin/rollup" }, @@ -8039,9 +8039,9 @@ } }, "node_modules/vite": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", - "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz", + "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", diff --git a/web/src/components/code.astro b/web/src/components/code.astro index 58bc6bb585..36c92fdad0 100644 --- a/web/src/components/code.astro +++ b/web/src/components/code.astro @@ -15,6 +15,8 @@ import 'prismjs/plugins/line-numbers/prism-line-numbers.css' import GoogleColab from '../images/google_colab.svg.png' +const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; + // PyTorch Code Sections -------------------------------------------------- const installCode_pt = ` @@ -625,8 +627,8 @@ python3 fedavg_cifar10_tf_job.py const frameworks = [ { id: "pytorch", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/getting_started/pt/nvflare_pt_getting_started.ipynb", - github_link: "https://github.com/NVIDIA/NVFlare/blob/main/examples/getting_started/pt/nvflare_pt_getting_started.ipynb", + 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`, sections: [ { id: "install-pytorch", @@ -679,8 +681,8 @@ const frameworks = [ }, { id: "lightning", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/getting_started/pt/nvflare_lightning_getting_started.ipynb", - github_link: "https://github.com/NVIDIA/NVFlare/blob/main/examples/getting_started/pt/nvflare_lightning_getting_started.ipynb", + 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`, sections: [ { id: "install-lightning", @@ -733,8 +735,8 @@ const frameworks = [ }, { id: "tensorflow", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/getting_started/tf/nvflare_tf_getting_started.ipynb", - github_link: "https://github.com/NVIDIA/NVFlare/blob/main/examples/getting_started/tf/nvflare_tf_getting_started.ipynb", + 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`, sections: [ { id: "install-tensorflow", @@ -838,7 +840,7 @@ const frameworks = [ - + NVIDIA logo @@ -848,7 +850,7 @@ const frameworks = [ - +
- + NVIDIA logo NVIDIA FLARE diff --git a/web/src/components/gettingStarted.astro b/web/src/components/gettingStarted.astro index f9101ca406..1738f9a651 100644 --- a/web/src/components/gettingStarted.astro +++ b/web/src/components/gettingStarted.astro @@ -1,4 +1,6 @@ --- +const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; + const walkthrough = [ { id: "step1", @@ -17,7 +19,7 @@ const walkthrough = [ description: "Use the ModelController API to write a federated control flow for FedAvg.", button_text: "View Source", - link: "https://github.com/NVIDIA/NVFlare/blob/main/nvflare/app_common/workflows/fedavg.py", + link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/nvflare/app_common/workflows/fedavg.py`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%202%20-%20Server.mp4", }, { @@ -27,7 +29,7 @@ const walkthrough = [ description: "Use the Client API to write local training code for a PyTorch CIFAR-10 trainer.", button_text: "View Source", - link: "https://github.com/NVIDIA/NVFlare/blob/main/examples/getting_started/pt/src/cifar10_fl.py", + link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/src/cifar10_fl.py`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%203%20-%20Client.mp4", }, { @@ -37,7 +39,7 @@ const walkthrough = [ 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/main/examples/getting_started/pt/nvflare_pt_getting_started.ipynb", + link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/getting_started/pt/nvflare_pt_getting_started.ipynb`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%204%20-%20Job.mp4", }, { @@ -47,7 +49,7 @@ const walkthrough = [ description: "Learn more about NVIDIA FLARE and taking federated learning from simulation to the real world.", button_text: "Tutorial Catalog", - link: "/NVFlare/catalog", + link: "catalog", video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20Getting%20Started%20-%20Part%205%20-%20Next%20Steps.mp4", }, ]; @@ -70,7 +72,7 @@ const km = [ description: "What is Kaplan-Meier Analysis and how can we adapt it to a federated setting?", button_text: "View Source", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/kaplan-meier-he", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/kaplan-meier-he`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20KM%20-%20Part%202%20-%20Kaplan%20Meier.mp4", }, { @@ -80,7 +82,7 @@ const km = [ description: "Dive into an end-to-end implementation of federated Kaplan-Meier.", button_text: "View Source", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/kaplan-meier-he/src", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/kaplan-meier-he/src`, video: "https://developer.download.nvidia.com/assets/Clara/flare/Flare%202.5.0%20KM%20-%20Part%203%20-%20Implementation.mp4", }, ]; diff --git a/web/src/components/hero.astro b/web/src/components/hero.astro index f03a253016..e55fd687cd 100644 --- a/web/src/components/hero.astro +++ b/web/src/components/hero.astro @@ -1,6 +1,9 @@ --- import NvidiaLogo from '../images/nvidia_eye.png' import NvflareAnimation from '../images/nvflare_graphic_animation.mp4' + +const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; +const base_url = import.meta.env.BASE_URL; ---
@@ -9,6 +12,29 @@ import NvflareAnimation from '../images/nvflare_graphic_animation.mp4'
+ + + + + + + @@ -22,7 +48,7 @@ import NvflareAnimation from '../images/nvflare_graphic_animation.mp4' class="inline-flex space-x-6"> - Just shipped v2.5.0 + Just Released Documentation Tutorial Catalog diff --git a/web/src/components/overview.astro b/web/src/components/overview.astro index 164d60a43b..582791c3c3 100644 --- a/web/src/components/overview.astro +++ b/web/src/components/overview.astro @@ -9,7 +9,7 @@ const features = [ `, description: "Provides user-friendly APIs for client and server programming. Use the Simulator and POC modes to quickly simulate a federated learning application.", - link:"/NVFlare/research", + link:"research", }, { id: "framework-agnostic", @@ -20,7 +20,7 @@ const features = [ `, description: "Designed as a federated computing platform agnostic to frameworks, workloads, datasets, and domains. Federated learning apps are built on this foundation.", - link: "/NVFlare/agnostic", + link: "agnostic", }, { id: "open-arch", @@ -42,7 +42,7 @@ const features = [ `, description: "Prioritizes security with secure provisioning, event-based security plugins, authorization control, data filtering, audit logs, and advanced privacy-preserving algorithms.", - link: "/NVFlare/security", + link: "security", }, { id: "integration", diff --git a/web/src/components/series.astro b/web/src/components/series.astro index eb1b0d3fc7..2e83dd5a00 100644 --- a/web/src/components/series.astro +++ b/web/src/components/series.astro @@ -1,4 +1,6 @@ --- +const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; + const series_100 = { id: "100", title: "FLARE 100: Core", @@ -12,7 +14,7 @@ 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/main/examples/getting_started" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/getting_started` }, { title: "Client API", @@ -36,19 +38,19 @@ const series_100 = { title: "ML/DL to FL", tags: ["beg.", "numpy", "pytorch", "lightning", "tensorflow"], description: "Example for converting Deep Learning (DL) code to Federated Learning (FL) using the Client API. Configurations for numpy, pytorch, lighting, and tensorflow.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/ml-to-fl" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/ml-to-fl` }, { title: "Hello FedAvg", 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/main/examples/hello-world/hello-fedavg/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-fedavg/README.md` }, { title: "Job API Examples", tags: ["int.", "pytorch", "lightning", "sklearn", "tensorflow"], description: "Various examples using the Job API for different workflows and frameworks.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/job_api" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/job_api` }, ] }, @@ -61,21 +63,21 @@ const series_100 = { title: "Intro to the FL Simulator", tags: ["beg.", "tools"], description: "Use the NVIDIA FLARE Simulator to run a local simulation with multi-process settings within a single computer, offering quick response and debugging.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/flare_simulator.ipynb", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/tutorials/flare_simulator.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/flare_simulator.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/tutorials/flare_simulator.ipynb` }, { title: "POC Mode", tags: ["beg.", "tools"], description: "Proof-of-concept mode to simulate real-world deployment on a local host with different processes represent server, clients, and an admin console.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/setup_poc.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/setup_poc.ipynb` }, { title: "FLARE API", tags: ["beg.", "tools"], description: "Manage system and jobs programmatically with the python FLARE API.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/flare_api.ipynb", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/tutorials/flare_api.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/flare_api.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/tutorials/flare_api.ipynb` }, { title: "Operating NVIDIA FLARE: Admin Client, Commands, FLARE API", @@ -151,55 +153,55 @@ const series_200 = { title: "Step-by-Step CIFAR10 Examples", tags: ["beg.", "algorithm", "pytorch", "dl"], description: "Step-by-step examples series with CIFAR-10 (image data) to showcase different NVIDIA FLARE features, workflows, and APIs.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/step-by-step/cifar10" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/step-by-step/cifar10` }, { title: "Step-by-Step HIGGS Examples", tags: ["beg.", "algorithm", "pandas", "sklearn", "xgboost"], description: "Step-by-step examples series with HIGGS (tabular data) to showcase different NVIDIA FLARE features, workflows, and APIs.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/step-by-step/higgs" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/step-by-step/higgs` }, { title: "Hello FedAvg", tags: ["beg.", "algorithm", "pytorch", "dl"], description: "Example using the FedAvg workflow to implement Federated Averaging.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-fedavg/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-fedavg/README.md` }, { title: "Hello Numpy Cross-Site Validation", tags: ["beg.", "algorithm", "numpy"], description: "Example using the CrossSiteEval workflow for cross site evaluation. Demonstrate option to use previous results without training workflow.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-numpy-cross-val/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-numpy-cross-val/README.md` }, { title: "Hello Cyclic Weight Transfer", tags: ["beg.", "algorithm", "tensorflow", "dl"], description: "Example using the CyclicController workflow to implement Cyclic Weight Transfer.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-cyclic/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-cyclic/README.md` }, { title: "Simulated Federated Learning with CIFAR-10", tags: ["int.", "algorithm", "simulator", "pytorch", "dl"], description: "Running FedAvg, FedProx, FedOpt, and SCAFFOLD algorithms using the FL simulator.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/cifar10/cifar10-sim/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/cifar10/cifar10-sim/README.md` }, { title: "TensorFlow Algorithms and Examples", tags: ["adv.", "algorithm", "tensorflow"], description: "FedOpt, FedProx, Scaffold implementations for Tensorflow.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/job_api/tf" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/job_api/tf` }, { title: "Financial Application with Federated XGBoost Methods", tags: ["adv.", "algorithm", "xgboost", "finance"], description: "Example using XGBoost in vertical and horizontal approaches to train a federated model to perform fraud detection with a finance dataset.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/finance" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/finance` }, { title: "Federated Learning Research", tags: ["adv.", "algorithm"], description: "Collection of research work for Federated Learning algorithms implemented with FLARE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research` }, ] }, @@ -212,43 +214,43 @@ const series_200 = { title: "BioNemo example for Drug Discovery", tags: ["adv.", "algorithm", "nemo", "healthcare"], description: "Running BioNeMo (NVIDIA's generative AI platform for drug discovery) in a federated learning environment using NVIDIA FLARE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/bionemo" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/bionemo` }, { title: "Federated Learning for Prostate Segmentation from Multi-source Data", tags: ["adv.", "algorithm", "healthcare", "monai", "dl"], description: "Example of training a multi-institutional prostate segmentation model using FedAvg, FedProx, and Ditto.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/prostate/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/prostate/README.md` }, { title: "NVFlare + MONAI integration", tags: ["adv.", "algorithm", "healthcare", "monai", "dl"], description: "Using NVIDIA FLARE to train a 3D medical image analysis model using federated averaging (FedAvg) and MONAI Bundle.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/monai/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/monai/README.md` }, { title: "Federated Learning with Differential Privacy for BraTS18 segmentation", tags: ["adv.", "algorithm", "healthcare", "dp", "monai", "dl"], description: "Illustrates the use of differential privacy for training brain tumor segmentation models using federated learning.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/brats18/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/brats18/README.md` }, { title: "Federated GNN: Protein Classification", tags: ["adv.", "algorithm", "healthcare", "pytorch", "dl"], description: "Example using GNNs for Protein Classification using PPI dataset using GraphSAGE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning` }, { title: "End-to-End Federated XGBoost for Financial Credit Card Detection", tags: ["adv.", "algorithm", "xgboost", "finance"], description: "Show the end-to-end process of feature engineering, pre-processing and training in federated settings. You can use FLARE to perform federated ETL and then training.", - link: "https://github.com/NVIDIA/NVFlare/tree/5fc5ff31f35be63330dec38e1c4e80a6f84586ed/examples/advanced/finance-end-to-end" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/finance-end-to-end` }, { title: "Federated GNN: Financial Transaction Classification", tags: ["adv.", "algorithm", "finance", "pytorch", "dl"], description: "Example using GNNs for Financial Transaction Classification with Elliptic++ dataset using GraphSAGE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning` }, ] }, @@ -269,26 +271,26 @@ const series_200 = { tags: ["int.", "algorithm", "healthcare", "he", "analytics"], description: "Kaplan-Meier survival analysis in federated setting without and with secure features via time-binning and Homomorphic Encryption (HE).", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/kaplan-meier-he", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/kaplan-meier-he`, }, { title: "Federated Learning with Differential Privacy for BraTS18 segmentation", tags: ["adv.", "algorithm", "healthcare", "dp", "monai", "dl"], description: "Illustrates the use of differential privacy for training brain tumor segmentation models using federated learning.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/brats18/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/brats18/README.md` }, { title: "Real-world Federated Learning with CIFAR-10", tags: ["int.", "algorithm", "poc", "pytorch", "learner", "dl", "he"], description: "Provisioning secure workspace and running FedAvg with streaming of TensorBoard metrics to the server during training and homomorphic encryption.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/cifar10/cifar10-real-world/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/cifar10/cifar10-real-world/README.md` }, { title: "Secure Federated XGBoost with Homomorphic Encryption", tags: ["adv.", "algorithm", "xgboost", "he"], description: "Federated secure training with XGBoost using homomorphic encryption.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/xgboost_secure", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/xgboost_secure`, }, ] } @@ -412,7 +414,7 @@ const series_list = [ To dive deeper into what NVIDIA FLARE has to offer, explore our full catalog of various tutorials and examples showcasing everything from the core features to advanced applications.

-->
Tutorial Catalog -> diff --git a/web/src/components/tutorials.astro b/web/src/components/tutorials.astro index d763e831c4..b3a0ea51f3 100644 --- a/web/src/components/tutorials.astro +++ b/web/src/components/tutorials.astro @@ -1,38 +1,40 @@ --- import GoogleColab from '../images/google_colab.svg.png' +const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; +const base_url = import.meta.env.BASE_URL; const highlights = [ { title: "LLM Tuning via HuggingFace SFT Trainer", tags: ["adv.", "algorithm", "huggingface", "llm"], description: "Example for using NVIDIA FLARE with a HuggingFace trainer for LLM tuning tasks.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/llm_hf" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/llm_hf` }, { title: "Survival Analysis with Federated Kaplan-Meier", tags: ["int.", "algorithm", "healthcare", "client-api", "model-controller", "he", "analytics"], description: "Kaplan-Meier survival analysis in federated setting without and with secure features via time-binning and Homomorphic Encryption (HE).", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/kaplan-meier-he", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/kaplan-meier-he`, }, { title: "Real-world Federated Learning with CIFAR-10", tags: ["int.", "algorithm", "poc", "pytorch", "learner", "dl", "he"], description: "Provisioning secure workspace and running FedAvg with streaming of TensorBoard metrics to the server during training and homomorphic encryption.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/cifar10/cifar10-real-world/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/cifar10/cifar10-real-world/README.md` }, { title: "Financial Application with Federated XGBoost Methods", tags: ["adv.", "algorithm", "xgboost", "finance"], description: "Example using XGBoost in vertical and horizontal approaches to train a federated model to perform fraud detection with a finance dataset.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/finance" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/finance` }, { title: "Parameter Efficient Fine Turning", tags: ["adv.", "algorithm", "nemo", "llm"], description: "Example utilizing NeMo's PEFT methods to adapt a LLM to a downstream task.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/nemo/examples/peft" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/nemo/examples/peft` }, ] @@ -41,28 +43,28 @@ const tutorials = [ title: "Intro to the FL Simulator", tags: ["beg.", "tools"], description: "Use the NVIDIA FLARE Simulator to run a local simulation with multi-process settings within a single computer, offering quick response and debugging.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/flare_simulator.ipynb", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/tutorials/flare_simulator.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/flare_simulator.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/tutorials/flare_simulator.ipynb` }, { title: "POC Mode", tags: ["beg.", "tools"], description: "Proof-of-concept mode to simulate real-world deployment on a local host with different processes represent server, clients, and an admin console.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/setup_poc.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/setup_poc.ipynb` }, { title: "FLARE API", tags: ["beg.", "tools"], description: "Manage system and jobs programmatically with the python FLARE API.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/flare_api.ipynb", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/tutorials/flare_api.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/flare_api.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/tutorials/flare_api.ipynb` }, { title: "Job CLI", tags: ["beg.", "tools"], description: "Job CLI for options to create and submit jobs from a command line interface in POC or production environments.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/tutorials/job_cli.ipynb", - colab_link: "https://colab.research.google.com/github/NVIDIA/NVFlare/blob/main/examples/tutorials/job_cli.ipynb" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/tutorials/job_cli.ipynb`, + colab_link: `https://colab.research.google.com/github/NVIDIA/NVFlare/blob/${gh_branch}/examples/tutorials/job_cli.ipynb` }, { title: "Operating NVIDIA FLARE: Admin Client, Commands, FLARE API", @@ -75,19 +77,19 @@ const tutorials = [ title: "FL Experiment Tracking with TensorBoard Streaming", tags: ["int.", "tools", "pytorch"], description: "Example integrating NVIDIA FLARE with TensorBoard streaming capability from clients to the server.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/experiment-tracking/tensorboard/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/experiment-tracking/tensorboard/README.md` }, { title: "FL Experiment Tracking with MLflow", tags: ["int.", "tools", "pytorch"], description: "Example integrating NVIDIA FLARE with MLflow streaming capability from clients to the server.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/experiment-tracking/mlflow/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/experiment-tracking/mlflow/README.md` }, { title: "FL Experiment Tracking with Weights and Biases", tags: ["int.", "tools", "pytorch"], description: "Example integrating NVIDIA FLARE with Weights and Biases streaming capability from clients to the server.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/experiment-tracking/wandb/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/experiment-tracking/wandb/README.md` }, { title: "Containerized Deployment with Docker", @@ -142,328 +144,328 @@ const tutorials = [ title: "Federated Policies", tags: ["adv.", "deployment"], description: "Example to demonstrate the federated site policies for authorization, resource and data privacy management.", - link: "https://github.com/NVIDIA/NVFlare/blob/main/examples/advanced/federated-policies/README.rst" + link: `https://github.com/NVIDIA/NVFlare/blob/${gh_branch}/examples/advanced/federated-policies/README.rst` }, { title: "Custom Authentication", tags: ["adv.", "deployment"], description: "Example to demonstrate the custom authentication policy and secure mode.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/custom_authentication/README.rst" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/custom_authentication/README.rst` }, { title: "Job-Level Authorization", tags: ["adv.", "deployment"], description: "Example to demonstrate the job-level authorization policy and secure mode.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/job-level-authorization/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/job-level-authorization/README.md` }, { title: "Federated Learning Hub", tags: ["adv.", "deployment"], description: "Allow hierarchical interaction between several levels of NVIDIA FLARE FL systems, e.g. Tier-1 (hub) and Tier-2 (sub-systems).", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/fl_hub/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/fl_hub/README.md` }, { title: "Getting Started", tags: ["beg.", "algorithm", "client-api", "model-controller", "job-api", "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/main/examples/getting_started" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/getting_started` }, { title: "Step-by-Step CIFAR10 Examples", tags: ["beg.", "algorithm", "pytorch", "dl"], description: "Step-by-step examples series with CIFAR-10 (image data) to showcase different NVIDIA FLARE features, workflows, and APIs.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/step-by-step/cifar10" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/step-by-step/cifar10` }, { title: "Step-by-Step HIGGS Examples", tags: ["beg.", "algorithm", "pandas", "sklearn", "xgboost"], description: "Step-by-step examples series with HIGGS (tabular data) to showcase different NVIDIA FLARE features, workflows, and APIs.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/step-by-step/higgs" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/step-by-step/higgs` }, { title: "ML/DL to FL", tags: ["beg.", "algorithm", "client-api", "numpy", "pytorch", "lightning", "tensorflow"], description: "Example for converting Deep Learning (DL) code to Federated Learning (FL) using the Client API. Configurations for numpy, pytorch, lighting, and tensorflow.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/ml-to-fl" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/ml-to-fl` }, { title: "Hello FedAvg", tags: ["beg.", "algorithm", "pytorch", "model-controller"], description: "Example using the FedAvg workflow to implement Federated Averaging.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-fedavg/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-fedavg/README.md` }, { title: "Hello Numpy Cross-Site Validation", tags: ["beg.", "algorithm", "numpy", "model-controller"], description: "Example using the CrossSiteEval workflow for cross site evaluation. Demonstrate option to use previous results without training workflow.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-numpy-cross-val/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-numpy-cross-val/README.md` }, { title: "Hello Cyclic Weight Transfer", tags: ["beg.", "algorithm", "tensorflow", "model-controller", "dl"], description: "Example using the CyclicController workflow to implement Cyclic Weight Transfer.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-cyclic/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-cyclic/README.md` }, { title: "Hello PyTorch", tags: ["beg.", "algorithm", "pytorch", "model-controller", "dl"], description: "Example of an image classifier with FedAvg using PyTorch as the deep learning training framework.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-pt/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-pt/README.md` }, { title: "Hello TensorFlow", tags: ["beg.", "algorithm", "tensorflow", "model-controller", "dl"], description: "Example of an image classifier with FedAvg using TensorFlow as the deep learning training framework.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/hello-world/hello-tf/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-tf/README.md` }, { title: "Job API Examples", tags: ["int.", "algorithm", "job-api", "pytorch", "lightning", "sklearn", "tensorflow"], description: "Various examples using the Job API for different workflows and frameworks.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/job_api" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/job_api` }, { title: "Simulated Federated Learning with CIFAR-10", tags: ["int.", "algorithm", "simulator", "pytorch", "learner", "model-controller", "dl"], description: "Running FedAvg, FedProx, FedOpt, and SCAFFOLD algorithms using the FL simulator.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/cifar10/cifar10-sim/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/cifar10/cifar10-sim/README.md` }, { title: "Real-world Federated Learning with CIFAR-10", tags: ["int.", "algorithm", "poc", "pytorch", "learner", "dl", "he"], description: "Provisioning secure workspace and running FedAvg with streaming of TensorBoard metrics to the server during training and homomorphic encryption.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/cifar10/cifar10-real-world/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/cifar10/cifar10-real-world/README.md` }, { title: "Logistic Regression with Newton-Raphton", tags: ["int.", "algorithm", "client-api", "model-controller", "ml"], description: "Federated binary classification via logistic regression with second-order Newton-Raphson optimization.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/lr-newton-raphson", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/lr-newton-raphson`, }, { title: "Survival Analysis with Federated Kaplan-Meier", tags: ["int.", "algorithm", "healthcare", "client-api", "model-controller", "he", "analytics"], description: "Kaplan-Meier survival analysis in federated setting without and with secure features via time-binning and Homomorphic Encryption (HE).", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/kaplan-meier-he", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/kaplan-meier-he`, }, { title: "Swarm Learning", tags: ["int.", "algorithm", "pytorch", "learner", "dl"], description: "Example using Swarm Learning and Client-Controlled Cross-site Evaluation workflows.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/swarm_learning", + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/swarm_learning`, }, { title: "Federated Linear Model with Scikit-learn", tags: ["int.", "algorithm", "sklearn", "ml"], description: "Using scikit-learn for federated linear model learning on tabular data.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/sklearn-linear/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/sklearn-linear/README.md` }, { title: "Federated K-Means Clustering with Scikit-learn", tags: ["int.", "algorithm", "sklearn", "ml"], description: "Using scikit-learn and k-Means for federated clustering on tabular data.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/sklearn-kmeans/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/sklearn-kmeans/README.md` }, { title: "Federated SVM with Scikit-learn", tags: ["int.", "algorithm", "sklearn", "ml"], description: "Using scikit-learn and SVM for federated model learning on tabular data.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/sklearn-svm/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/sklearn-svm/README.md` }, { title: "Histogram-based FL for XGBoost", tags: ["adv.", "algorithm", "xgboost", "ml"], description: "Histogram-based algorithm for XGBoost.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/xgboost/histogram-based/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/xgboost/histogram-based/README.md` }, { title: "Tree-based Federated Learning for XGBoost", tags: ["adv.", "algorithm", "xgboost", "ml"], description: "Tree-based algorithms including bagging and cyclic approaches for XGBoost.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/xgboost/tree-based/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/xgboost/tree-based/README.md` }, { title: "Federated Learning for Random Forest based on XGBoost", tags: ["adv.", "algorithm", "xgboost", "ml"], description: "Example of using NVIDIA FLARE with scikit-learn and Random Forest.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/random_forest/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/random_forest/README.md` }, { title: "Federated Vertical XGBoost", tags: ["adv.", "algorithm", "xgboost", "ml"], description: "Example using Private Set Intersection and XGBoost on vertically split HIGGS data.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/vertical_xgboost/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/vertical_xgboost/README.md` }, { title: "NVFlare + MONAI integration", tags: ["adv.", "algorithm", "healthcare", "monai", "dl"], description: "Using NVIDIA FLARE to train a 3D medical image analysis model using federated averaging (FedAvg) and MONAI Bundle.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/monai/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/monai/README.md` }, { title: "Federated Learning with Differential Privacy for BraTS18 segmentation", tags: ["adv.", "algorithm", "healthcare", "dp", "monai", "dl"], description: "Illustrates the use of differential privacy for training brain tumor segmentation models using federated learning.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/brats18/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/brats18/README.md` }, { title: "Federated Learning for Prostate Segmentation from Multi-source Data", tags: ["adv.", "algorithm", "healthcare", "monai", "dl"], description: "Example of training a multi-institutional prostate segmentation model using FedAvg, FedProx, and Ditto.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/prostate/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/prostate/README.md` }, { title: "Federated Statistics for Images", tags: ["adv.", "algorithm", "pandas", "analytics"], description: "Example of gathering local image histogram to compute the global dataset histograms.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/federated-statistics/image_stats/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/federated-statistics/image_stats/README.md` }, { title: "Federated Statistics for DataFrame", tags: ["adv.", "algorithm", "pandas", "analytics"], description: "Example of gathering local statistics summary from Pandas DataFrame to compute the global dataset statistics.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/federated-statistics/df_stats/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/federated-statistics/df_stats/README.md` }, { title: "MONAI & NVIDIA FLARE Integration with Experiment Tracking", tags: ["adv.", "algorithm", "experiment-tracking", "monai"], description: "Example using NVIDIA FLARE and MONAI integration with experiment tracking streaming from clients to server.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/monai/examples/spleen_ct_segmentation_local/README.md#51-experiment-tracking-with-mlflow" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/monai/examples/spleen_ct_segmentation_local/README.md#51-experiment-tracking-with-mlflow` }, { title: "KeyCloak Site Authentication Integration", tags: ["adv.", "algorithm", "security"], description: "Demonstrate KeyCloak integration for supporting site-specific authentication.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/keycloak-site-authentication/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/keycloak-site-authentication/README.md` }, { title: "NLP-NER", tags: ["adv.", "algorithm", "huggingface", "dl"], description: "Illustrates both BERT and GPT-2 models from Hugging Face on a Named Entity Recognition (NER) task using the NCBI disease dataset.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/nlp-ner/README.md" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/nlp-ner/README.md` }, { title: "Parameter Efficient Fine Turning", tags: ["adv.", "algorithm", "nemo", "llm"], description: "Example utilizing NeMo's PEFT methods to adapt a LLM to a downstream task.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/nemo/examples/peft" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/nemo/examples/peft` }, { title: "Prompt-Tuning Example", tags: ["adv.", "algorithm", "nemo", "llm"], description: "Example for using NVIDIA FLARE with NeMo for LLM prompt learning.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/nemo/examples/prompt_learning" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/nemo/examples/prompt_learning` }, { title: "Supervised Fine Tuning (SFT)", tags: ["adv.", "algorithm", "nemo", "llm"], description: "Example to fine-tune all parameters of a LLM on supervised data.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/integration/nemo/examples/supervised_fine_tuning" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/integration/nemo/examples/supervised_fine_tuning` }, { title: "LLM Tuning via HuggingFace SFT Trainer", tags: ["adv.", "algorithm", "huggingface", "llm"], description: "Example for using NVIDIA FLARE with a HuggingFace trainer for LLM tuning tasks.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/llm_hf" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/llm_hf` }, { title: "Federated GNN: Protein Classification", tags: ["adv.", "algorithm", "healthcare", "pytorch", "dl"], description: "Example using GNNs for Protein Classification using PPI dataset using GraphSAGE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning` }, { title: "Federated GNN: Financial Transaction Classification", tags: ["adv.", "algorithm", "finance", "pytorch", "dl"], description: "Example using GNNs for Financial Transaction Classification with Elliptic++ dataset using GraphSAGE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/gnn#federated-gnn-on-graph-dataset-using-inductive-learning` }, { title: "Financial Application with Federated XGBoost Methods", tags: ["adv.", "algorithm", "xgboost", "finance"], description: "Example using XGBoost in vertical and horizontal approaches to train a federated model to perform fraud detection with a finance dataset.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/finance" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/finance` }, { title: "BioNemo example for Drug Discovery", tags: ["adv.", "algorithm", "nemo", "healthcare"], description: "Running BioNeMo (NVIDIA's generative AI platform for drug discovery) in a federated learning environment using NVIDIA FLARE.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/bionemo" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/bionemo` }, { title: "Hierarchical Federated Statistics", tags: ["adv.", "algorithm", "analytics"], description: "Show to generate hierarchical statistics for data that can be represented as Pandas Data Frame.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/federated-statistics/hierarchical_stats" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/federated-statistics/hierarchical_stats` }, { title: "FedAvg Early Stopping", tags: ["int.", "algorithm", "model-controller", "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/main/examples/hello-world/hello-fedavg" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/hello-world/hello-fedavg` }, { title: "TensorFlow Algorithms and Examples", tags: ["adv.", "algorithm", "model-controller", "tensorflow"], description: "FedOpt, FedProx, Scaffold implementations for Tensorflow.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/examples/advanced/job_api/tf" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/job_api/tf` }, { title: "End-to-End Federated XGBoost for Financial Credit Card Detection", tags: ["adv.", "algorithm", "xgboost", "finance"], description: "Show the end-to-end process of feature engineering, pre-processing and training in federated settings. You can use FLARE to perform federated ETL and then training.", - link: "https://github.com/NVIDIA/NVFlare/tree/5fc5ff31f35be63330dec38e1c4e80a6f84586ed/examples/advanced/finance-end-to-end" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/examples/advanced/finance-end-to-end` }, { title: "Auto-FedRL", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Efficient reinforcement learning (RL)-based federated hyperparameter optimization algorithm.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/auto-fed-rl" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/auto-fed-rl` }, { title: "ConDistFL", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Conditional Distillation for Federated Learning from Partially Annotated Data.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/condist-fl" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/condist-fl` }, { title: "FedBN", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Federated Learning on Non-IID Features via Local Batch Normalization designed to address the feature shift problem when aggregating models across different data distributions.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/fed-bn" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/fed-bn` }, { title: "FedBPT", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Efficient Federated Black-box Prompt Tuning for Large Language Models.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/fed-bpt" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/fed-bpt` }, { title: "FedCE", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Fair Federated Medical Image Segmentation via Client Contribution Estimation.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/fed-ce" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/fed-ce` }, { title: "FedSM", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Personalized Federated Learning with FedSM Alogithrm. Closing the Generalization Gap of Cross-silo Federated Medical Image Segmentation.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/fed-sm" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/fed-sm` }, { title: "One-shot Vertical Federated Learning", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Solve the communication bottleneck and the problem of limited overlapping samples simultaneously based on semi-supervised learning.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/one-shot-vfl" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/one-shot-vfl` }, { title: "Quantifying Data Leakage in Federated Learning", tags: ["adv.", "algorithm", "research", "pytorch"], description: "Present new ways to measure and visualize potential data leakage in FL.", - link: "https://github.com/NVIDIA/NVFlare/tree/main/research/quantifying-data-leakage" + link: `https://github.com/NVIDIA/NVFlare/tree/${gh_branch}/research/quantifying-data-leakage` }, ] @@ -518,7 +520,7 @@ const tag_list = [
- + Back to home @@ -535,7 +537,7 @@ const tag_list = [ To read more, visit our documentation and - GitHub. + GitHub.

diff --git a/web/src/pages/404.astro b/web/src/pages/404.astro index 7cd6ab2cdc..be1263507c 100644 --- a/web/src/pages/404.astro +++ b/web/src/pages/404.astro @@ -1,5 +1,7 @@ --- import Layout from "@layouts/Layout.astro"; + +const base_url = import.meta.env.BASE_URL; --- @@ -53,7 +55,7 @@ import Layout from "@layouts/Layout.astro";
- + Back to home diff --git a/web/src/pages/agnostic.astro b/web/src/pages/agnostic.astro index 62e9f739a8..2e3abca5fb 100644 --- a/web/src/pages/agnostic.astro +++ b/web/src/pages/agnostic.astro @@ -2,6 +2,7 @@ import Layout from "@layouts/Layout.astro"; import FrameworkAgnostic from '../images/framework_agnostic.png' +const base_url = import.meta.env.BASE_URL; --- @@ -11,7 +12,7 @@ import FrameworkAgnostic from '../images/framework_agnostic.png'
- + Back to home @@ -22,7 +23,7 @@ import FrameworkAgnostic from '../images/framework_agnostic.png'

NVIDIA FLARE is designed as a federated computing platform that is agnostic to frameworks, workloads, datasets, and domains. - View the Tutorial Catalog to see different examples in these categories. + View the Tutorial Catalog to see different examples in these categories.

framework agnostic diff --git a/web/src/pages/research.astro b/web/src/pages/research.astro index 795a92f8da..b67860330a 100644 --- a/web/src/pages/research.astro +++ b/web/src/pages/research.astro @@ -7,6 +7,8 @@ import MammographyGraphs from '../images/research/mammography_graphs.jpg' import Pancreas from '../images/research/pancreas.jpg' import Sun from '../images/research/sun.jpg' +const gh_branch = import.meta.env.PUBLIC_GH_BRANCH; +const base_url = import.meta.env.BASE_URL; const case_studies = [ { @@ -72,7 +74,7 @@ const case_studies = [
- + Back to home @@ -149,7 +151,7 @@ const case_studies = [

NVIDIA FLARE offers a lot of state of art research work, here is a quick view of the recent work. - Learn more in the research directory in our GitHub and view our list of Publications. + Learn more in the research directory in our GitHub and view our list of Publications.

NVFLARE research diff --git a/web/src/pages/security.astro b/web/src/pages/security.astro index fac52bac78..4152e620fc 100644 --- a/web/src/pages/security.astro +++ b/web/src/pages/security.astro @@ -3,6 +3,8 @@ import Layout from "@layouts/Layout.astro"; import DataPrivacy from '../images/data_privacy_arch.png' import FedAuth from '../images/fed_auth_arch.png' +const base_url = import.meta.env.BASE_URL; + --- @@ -10,7 +12,7 @@ import FedAuth from '../images/fed_auth_arch.png'