From 78b9d1674736a2bb3e3a5ba74ede542a9b9df550 Mon Sep 17 00:00:00 2001 From: Dan Runt Date: Mon, 23 Mar 2026 16:03:08 -0400 Subject: [PATCH] Restructure validation directory --- .../1_data_extraction.ipynb | 320 ----------- .../2_data_engineering.ipynb | 225 -------- .../3_statistical_analyses.ipynb | 540 ------------------ .../4_model_training.ipynb | 276 --------- .../5_model_evaluation.ipynb | 203 ------- sandbox/pneumonia-prediction/README.md | 25 - .../containers/data-prep/Dockerfile | 34 -- .../containers/data-prep/dataprep_gc.py | 49 -- .../containers/data-prep/requirements.txt | 7 - .../containers/data-prep/run_dataprep.sh | 17 - .../containers/merge-cohorts/Dockerfile | 36 -- .../containers/merge-cohorts/README.md | 24 - .../merge-cohorts/merge_cohorts_data.py | 8 - .../containers/merge-cohorts/requirements.in | 1 - .../containers/merge-cohorts/requirements.txt | 65 --- .../containers/prediction-model/Dockerfile | 51 -- .../containers/prediction-model/README.md | 27 - .../config/config_fed_client.json | 22 - .../config/config_fed_server.json | 63 -- .../prediction-model/custom/network.py | 47 -- .../custom/pneumonia_trainer.py | 189 ------ .../prediction-model/custom/pt_constants.py | 21 - .../custom/pt_model_locator.py | 69 --- .../custom/validation_json_generator.py | 83 --- .../containers/prediction-model/infer.py | 50 -- .../prediction-model/requirements.txt | 4 - .../source_centralized_model/README.md | 38 -- .../pneumonia_classification.py | 149 ----- .../containers/train-test-split/Dockerfile | 34 -- .../containers/train-test-split/README.md | 24 - .../train-test-split/requirements.txt | 4 - .../containers/train-test-split/run_code.sh | 8 - .../train-test-split/train_test_split.py | 15 - .../site-testing-financial/README.md | 0 .../data/credit_risk_dataset.csv | 0 .../model/app/config/config_fed_client.conf | 0 .../model/app/config/config_fed_server.conf | 0 .../model/app/custom/fl_config.py | 0 .../model/app/custom/xgboost_fl.py | 0 .../site-testing-financial/model/meta.conf | 0 .../site-testing-financial/site-testing.ipynb | 0 .../site-testing-life-science/README.md | 0 .../data/cyp3a4_all_no_test.csv | 0 .../model/app/config/config_fed_client.json | 0 .../model/app/config/config_fed_server.json | 0 .../app/custom/chemprop_fl_regression.py | 0 .../site-testing-life-science/model/infer.py | 0 .../site-testing-life-science/model/meta.json | 0 .../site-testing.ipynb | 0 49 files changed, 2728 deletions(-) delete mode 100644 sandbox/pneumonia-prediction/1_data_extraction.ipynb delete mode 100644 sandbox/pneumonia-prediction/2_data_engineering.ipynb delete mode 100644 sandbox/pneumonia-prediction/3_statistical_analyses.ipynb delete mode 100644 sandbox/pneumonia-prediction/4_model_training.ipynb delete mode 100644 sandbox/pneumonia-prediction/5_model_evaluation.ipynb delete mode 100644 sandbox/pneumonia-prediction/README.md delete mode 100644 sandbox/pneumonia-prediction/containers/data-prep/Dockerfile delete mode 100644 sandbox/pneumonia-prediction/containers/data-prep/dataprep_gc.py delete mode 100644 sandbox/pneumonia-prediction/containers/data-prep/requirements.txt delete mode 100755 sandbox/pneumonia-prediction/containers/data-prep/run_dataprep.sh delete mode 100644 sandbox/pneumonia-prediction/containers/merge-cohorts/Dockerfile delete mode 100644 sandbox/pneumonia-prediction/containers/merge-cohorts/README.md delete mode 100755 sandbox/pneumonia-prediction/containers/merge-cohorts/merge_cohorts_data.py delete mode 100644 sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.in delete mode 100644 sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.txt delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/Dockerfile delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/README.md delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_client.json delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_server.json delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/custom/network.py delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/custom/pneumonia_trainer.py delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_constants.py delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_model_locator.py delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/custom/validation_json_generator.py delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/infer.py delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/requirements.txt delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/README.md delete mode 100644 sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/pneumonia_classification.py delete mode 100644 sandbox/pneumonia-prediction/containers/train-test-split/Dockerfile delete mode 100644 sandbox/pneumonia-prediction/containers/train-test-split/README.md delete mode 100644 sandbox/pneumonia-prediction/containers/train-test-split/requirements.txt delete mode 100755 sandbox/pneumonia-prediction/containers/train-test-split/run_code.sh delete mode 100644 sandbox/pneumonia-prediction/containers/train-test-split/train_test_split.py rename {sandbox => validation}/site-testing-financial/README.md (100%) rename {sandbox => validation}/site-testing-financial/data/credit_risk_dataset.csv (100%) rename {sandbox => validation}/site-testing-financial/model/app/config/config_fed_client.conf (100%) rename {sandbox => validation}/site-testing-financial/model/app/config/config_fed_server.conf (100%) rename {sandbox => validation}/site-testing-financial/model/app/custom/fl_config.py (100%) rename {sandbox => validation}/site-testing-financial/model/app/custom/xgboost_fl.py (100%) rename {sandbox => validation}/site-testing-financial/model/meta.conf (100%) rename {sandbox => validation}/site-testing-financial/site-testing.ipynb (100%) rename {sandbox => validation}/site-testing-life-science/README.md (100%) rename {sandbox => validation}/site-testing-life-science/data/cyp3a4_all_no_test.csv (100%) rename {sandbox => validation}/site-testing-life-science/model/app/config/config_fed_client.json (100%) rename {sandbox => validation}/site-testing-life-science/model/app/config/config_fed_server.json (100%) rename {sandbox => validation}/site-testing-life-science/model/app/custom/chemprop_fl_regression.py (100%) rename {sandbox => validation}/site-testing-life-science/model/infer.py (100%) rename {sandbox => validation}/site-testing-life-science/model/meta.json (100%) rename {sandbox => validation}/site-testing-life-science/site-testing.ipynb (100%) diff --git a/sandbox/pneumonia-prediction/1_data_extraction.ipynb b/sandbox/pneumonia-prediction/1_data_extraction.ipynb deleted file mode 100644 index 9a80a779..00000000 --- a/sandbox/pneumonia-prediction/1_data_extraction.ipynb +++ /dev/null @@ -1,320 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "8cec8749", - "metadata": {}, - "source": [ - "# Notebook #1: Data Extraction\n", - "## Importing tabular data onto Rhino with SQL queries\n", - "In this notebook, you'll use SQL to query from an external database (such as a health system's clinical data warehouse) and import the results of those queries onto the Rhino Federated Computing Platform.\n", - "\n", - "### Import the Rhino Health Python library\n", - "The code below imports various classes and functions from the `rhino_health` library, which is a custom library designed to interact with the Rhino Federated Computing Platform. More information about the SDK can be found on our [Official SDK Documentation](https://rhinohealth.github.io/rhino_sdk_docs/html/autoapi/index.html) and on our [PyPI Repository Page](https://pypi.org/project/rhino-health/) " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bb085bd3-3cb2-49d6-b628-7f357fe1a1c7", - "metadata": {}, - "outputs": [], - "source": [ - "pip install --upgrade rhino_health" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9e3e349", - "metadata": {}, - "outputs": [], - "source": [ - "import getpass\n", - "from pprint import pprint\n", - "import rhino_health as rh\n", - "from rhino_health.lib.endpoints.cohort.cohort_dataclass import CohortCreateInput\n", - "from rhino_health.lib.endpoints.data_schema.data_schema_dataclass import DataschemaCreateInput\n", - "from rhino_health.lib.endpoints.project.project_dataclass import ProjectCreateInput\n", - "from rhino_health.lib.endpoints.sql_query.sql_query_dataclass import (SQLQueryImportInput,SQLQueryInput,SQLServerTypes,ConnectionDetails)\n", - "from rhino_health.lib.endpoints.aimodel.aimodel_dataclass import (AIModel,AIModelCreateInput,AIModelRunInput,ModelTypes)" - ] - }, - { - "cell_type": "markdown", - "id": "3e04b454", - "metadata": {}, - "source": [ - "### Authenticate to the Rhino FCP\n", - "The `RhinoSession` class in the `rhino_health` library is a comprehensive interface for interacting with various endpoints in the Rhino Health ecosystem. It offers direct access to multiple specialized endpoints, including AI models, cohorts, data schemas, model results, projects, and workgroups, facilitating a wide range of operations in healthcare data management and analysis. The class also supports features like two-factor authentication and user switching, enhancing security and flexibility in handling different user sessions and workflows within the Rhino Health platform." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3600a62c-8df7-4f1a-aa28-bf7528caa3a4", - "metadata": {}, - "outputs": [], - "source": [ - "my_username = \"FCP_LOGIN_EMAIL\" # Replace this with the email you use to log into Rhino Health\n", - "session = rh.login(username=my_username, password=getpass.getpass())" - ] - }, - { - "cell_type": "markdown", - "id": "101d31d9-b79f-4fa9-a9dd-1c8fe31a4cff", - "metadata": {}, - "source": [ - "### Identify the desired project in the Rhino UI.\n", - "Before completing this step using the Python SDK, create a project on the Rhino web platform. Once the project has been created, copy the UID from the project you just created in the UI by navigating to the homepage, pressing on the three-vertical dot button in your project's square, and then selecting the button Copy UID.\n", - "\n", - "\n", - "![image info](https://drive.google.com/uc?export=view&id=1QjVkMmHTCdCpQF3qysScJr8tiSXtecOz)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae904603-854b-457b-9203-294a1abbb1d9", - "metadata": {}, - "outputs": [], - "source": [ - "project_uid = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' # Replace with your Project's UID\n", - "workgroup_uid = session.project.get_collaborating_workgroups(project_uid)[0].uid" - ] - }, - { - "cell_type": "markdown", - "id": "7324ef97", - "metadata": {}, - "source": [ - "### Connection Setup\n", - "The `rhino_health.lib.endpoints.sql_query.sql_query_dataclass` module in the Rhino Health library provides classes to handle SQL queries against external databases and import data into the Rhino Federated Computing Platform. It includes `SQLQueryInput` for specifying parameters of a SQL query, `SQLQueryImportInput` for importing a cohort from an external SQL database query, and `SQLQuery`, a class representing an executed SQL query. Additional classes like `QueryResultStatus` and `SQLServerTypes` define the status of query results and supported SQL server types, respectively, while the `ConnectionDetails` class specifies connection details for an external SQL database.\n", - "\n", - "More information about Rhino's SQL classes can be found by reviewing our SDK documentation [here](https://rhinohealth.github.io/rhino_sdk_docs/html/autoapi/rhino_health/lib/endpoints/sql_query/index.html).\n", - "\n", - "When specifying the connection details, ensure that you provide the server_type using the approved SQLServerTypes enum. This step ensures that your server is supported and compatible with the querying process." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0da2aa6e", - "metadata": {}, - "outputs": [], - "source": [ - "sql_db_user = \"rhino\" # Replace this with your DB username (make sure the user has read-only permissions to the DB).\n", - "external_server_url = \"ext-hospital-data.covi47dnmpiy.us-east-1.rds.amazonaws.com:5432\" # Replace this with url + port of the SQL DB you want to query (ie \"{url}:{port}\").\n", - "db_name = \"hospital_data\" # Replace this with your DB name.\n", - "\n", - "connection_details = ConnectionDetails(\n", - " server_user=sql_db_user,\n", - " password=getpass.getpass(), \n", - " server_type=SQLServerTypes.POSTGRESQL, # Replace POSTGRESQL with the relevant type of your sql server (See docs for all supported types).\n", - " server_url=external_server_url,\n", - " db_name=db_name\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "1136895c", - "metadata": {}, - "source": [ - "### Writing SQL queries against the DB\n", - "Using the `SQLQueryImportInput` function will allow us to query an external relational database and import the results of the query as a cohort. A Cohort is a central concept on the Rhino platform; to learn more, please navigate to this [link](https://docs.rhinohealth.com/hc/en-us/articles/12384748397213-What-is-a-Cohort-)\n", - "\n", - "Executing the `SQLQueryImportInput` function requires a few arguments:\n", - "- cohort_name (str): Name for the cohort you are creating.\n", - "- is_data_deidentified (bool): Indicates if the data in the query is deidentified for privacy reasons.\n", - "- connection_details (ConnectionDetails): Details like URL, user, and password to connect to the SQL server.\n", - "- data_schema_uid (Optional[str]): The unique identifier for the data schema in the context of the query.\n", - "- timeout_seconds (int): Time limit in seconds for the query execution.\n", - "- project_uid (str): Unique identifier for the project context of the query.\n", - "- workgroup_uid (str): Unique identifier for the workgroup context of the query.\n", - "- sql_query (str): The actual SQL query to be run.\n", - "\n", - "#### Table 1: Patient Admission Data\n", - "Our first query will retrieve patient demographics and associated clinical codes from inpatient admissions for patients with chest x-rays (see the WHERE clause, where we identify a selection of chest x-rays in the MIMIC v4 database)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "66339119-56f3-41ba-b022-2e4380076f61", - "metadata": {}, - "outputs": [], - "source": [ - "query_demo=\"\"\"\n", - "SELECT distinct\n", - " pat.subject_id\n", - " , adm.hadm_id\n", - " , pat.anchor_age + (EXTRACT(YEAR FROM adm.admittime) - pat.anchor_year) AS age\n", - " , pat.gender\n", - " , adm.insurance\n", - " , adm.admission_type\n", - " ,adm.admission_location\n", - " ,adm.discharge_location\n", - " ,adm.language\n", - " ,adm.marital_status\n", - " , adm.race\n", - " , icd.icd_code as diagnosis_code\n", - " ,proc.icd_code as procedure_code\n", - "FROM mimiciv_hosp.admissions adm\n", - "LEFT JOIN mimiciv_hosp.patients pat\n", - "ON pat.subject_id = adm.subject_id\n", - "LEFT JOIN mimiciv_hosp.diagnoses_icd icd\n", - "ON adm.subject_id = icd.subject_id\n", - "AND adm.hadm_id = icd.hadm_id\n", - "LEFT JOIN mimiciv_hosp.procedures_icd proc\n", - "ON adm.subject_id = proc.subject_id\n", - "AND adm.hadm_id = proc.hadm_id\n", - "LEFT JOIN mimiciv_cxr.study_list study\n", - "ON adm.subject_id =study.subject_id\n", - "WHERE study.study_id in(57375967,50771383,54205396,53186264,54684191,50531538,59965534,51029426,50336039,51967845,53970869,54898709,57084339,58117097,58509443,\n", - "58555910,58733084,59289932,51449744,55590752,56617354,57652741,50230446,50252971,50702026,50789010,51737583,54855307,56421164,58996402,59565087,52358194,\n", - "51732447,53447201,54136122,58882809,54076811,53022275,58261299,57661212,52821744,53831730,56167317,57754443,50548939,55758528,58974095,51613820,58898689,55328702,\n", - "52654671,53468612,55928380,53461983,58400857,57874958,58971884,59558528,51497652,53161617,54277770,59484629,51212589,58414548,53818182,58812027,59585309,53534710,\n", - "58748017,53445324,58890389,53977911,57107380,58184428,53522120,55014265)\n", - "\"\"\"\n", - "import_run_params = SQLQueryImportInput(\n", - " session = session,\n", - " project = project_uid, # The project/workgroup will be used to validate permissions (including and k_anonymization value)\n", - " workgroup = workgroup_uid,\n", - " connection_details = connection_details,\n", - " cohort_name = 'mimic_ehr_demo_dev',\n", - " data_schema_uid = None, # Auto-Generating the Output Data Schema for the Cohort\n", - " timeout_seconds = 1200,\n", - " is_data_deidentified = True,\n", - " sql_query = query_demo\n", - ")\n", - "\n", - "response = session.sql_query.import_cohort_from_sql_query(import_run_params)" - ] - }, - { - "cell_type": "markdown", - "id": "d80ae68c", - "metadata": {}, - "source": [ - "#### Table 2: EHR Observations\n", - "Our second query will retrieve observations from our clinical information system, including patient BMI, height, weight, and diastolic and systolic blood pressure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "21d3f4eb-3ef9-4302-8bf3-34c5f7ece97a", - "metadata": {}, - "outputs": [], - "source": [ - "query_obs = \"\"\"\n", - "SELECT\n", - " omr.subject_id,\n", - " omr.chartdate,\n", - " omr.result_name,\n", - " max(omr.result_value) as result\n", - "FROM mimiciv_hosp.omr omr\n", - "LEFT JOIN mimiciv_cxr.study_list study\n", - "ON omr.subject_id =study.subject_id\n", - "WHERE study.study_id in (57375967,50771383,54205396,53186264,54684191,50531538,59965534,51029426,50336039,51967845,53970869,54898709,57084339,58117097,58509443,58555910,58733084,59289932,51449744,55590752,56617354,57652741,50230446,50252971,50702026,50789010,51737583,54855307,56421164,58996402,59565087,52358194,51732447,53447201,54136122,58882809,54076811,53022275,58261299,57661212,52821744,53831730,56167317,57754443,50548939,55758528,58974095,51613820,58898689,55328702,52654671,53468612,55928380,53461983,58400857,57874958,58971884,59558528,51497652,53161617,54277770,59484629,51212589,58414548,53818182,58812027,59585309,53534710,58748017,53445324,58890389,53977911,57107380,58184428,53522120,55014265)\n", - "GROUP BY omr.subject_id, omr.chartdate, omr.result_name\n", - "\"\"\"\n", - "\n", - "import_run_params = SQLQueryImportInput(\n", - " session = session,\n", - " project = project_uid, # The project/workgroup will be used to validate permissions (including and k_anonymization value)\n", - " workgroup = workgroup_uid,\n", - " connection_details = connection_details,\n", - " cohort_name = 'mimic_ehr_obs_dev',\n", - " data_schema_uid = None, # Auto-Generating the Output Data Schema for the Cohort\n", - " timeout_seconds = 1200,\n", - " is_data_deidentified = True,\n", - " sql_query = query_obs\n", - ")\n", - "\n", - "response = session.sql_query.import_cohort_from_sql_query(import_run_params)" - ] - }, - { - "cell_type": "markdown", - "id": "98e754e6", - "metadata": {}, - "source": [ - "### Importing chest x-rays from a PACS system into my Rhino client\n", - "Next, we'll import chest x-rays onto my Rhino client so that we can conduct a computer vision experiment in the following steps:.\n", - "\n", - "**To enable a friction-less guided sandbox experience, Rhino staff have uploaded DICOM data into the project for you.** If you are interested in learning more about how data can be imported from your local computing environment into the Rhino Federated Computing Platform, please refer to this section of our documentation [here](https://docs.rhinohealth.com/hc/en-us/articles/12385912890653-Adding-Data-to-your-Rhino-Federated-Computing-Platform-Client).\n", - "\n", - "The data has been loaded in the `/rhino_data/image/dicom` path in the Rhino client. In addition, a file that provides metadata to associate the DICOM studies with the EHR data has been imported ('/rhino_data/image/metadata/aidev_cohort.csv')." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f652fdc", - "metadata": {}, - "outputs": [], - "source": [ - "dicom_path = \"/rhino_data/image/dicom\"\n", - "metadata_file = \"/rhino_data/image/metadata/aidev_cohort.csv\"\n", - "\n", - "cohort_creation_params = CohortCreateInput(\n", - " name=\"mimic_cxr_dev\",\n", - " description=\"mimic_cxr_dev\",\n", - " project_uid=project_uid, \n", - " workgroup_uid=workgroup_uid,\n", - " image_filesystem_location=dicom_path,\n", - " csv_filesystem_location = metadata_file,\n", - " is_data_deidentified=True,\n", - " method=\"filesystem\",\n", - ")\n", - "\n", - "ai_developer_image_cohort = session.cohort.add_cohort(cohort_creation_params)\n", - "print(f\"Created new cohort '{ai_developer_image_cohort.name}' with uid '{ai_developer_image_cohort.uid}'\")" - ] - }, - { - "cell_type": "markdown", - "id": "1a47f554", - "metadata": {}, - "source": [ - "### What you'll see in the Rhino UI:\n", - "Once all three queries have been executed, you should see three cohorts in the user interface:\n", - "![Mimic Cohorts in the FCP](https://drive.google.com/uc?export=view&id=1f6BaoyG3-4kmw4vUSr_WGNPq5_ZD3awC)" - ] - }, - { - "cell_type": "markdown", - "id": "ce69a362", - "metadata": {}, - "source": [ - "### Where is my data in the Rhino client? \n", - "Once data is uploaded, it'll reside in your designated Rhino client. While the Rhino Federated Computing Platform eliminates the need for the user to know the path of the data (enabling users just to refer to 'cohorts' it'll reside in the `/rhino_data/image/dicom` folder. \n", - "![image info](https://drive.google.com/uc?export=view&id=1oHvJhcU_My3cNWg8-amjStj8sA2t_iwQ)\n", - "\n", - "To learn more about working with DICOM data on the Rhino Federated Computing Platform, please refer to our documentation [here](https://docs.rhinohealth.com/hc/en-us/articles/13136536913693-Example-1-Defining-a-Cohort-with-DICOM-Data)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/sandbox/pneumonia-prediction/2_data_engineering.ipynb b/sandbox/pneumonia-prediction/2_data_engineering.ipynb deleted file mode 100644 index 9515cffe..00000000 --- a/sandbox/pneumonia-prediction/2_data_engineering.ipynb +++ /dev/null @@ -1,225 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "c69d61a4", - "metadata": {}, - "source": [ - "# Notebook #2: Federated Data Engineering\n", - "In this notebook, we'll convert CXR DICOM to JPG files and apply the conversion code to multiple sites." - ] - }, - { - "cell_type": "markdown", - "id": "5224695d-b02b-41ea-a5d6-ad71c4799ac8", - "metadata": {}, - "source": [ - "### Install the Rhino Health Python SDK, Load All Necessary Libraries and Login to the Rhino FCP" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52c80562-63c0-4d01-a650-34094fd2b333", - "metadata": {}, - "outputs": [], - "source": [ - "pip install --upgrade rhino_health" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1114292d", - "metadata": {}, - "outputs": [], - "source": [ - "import getpass\n", - "import rhino_health as rh\n", - "from rhino_health.lib.endpoints.aimodel.aimodel_dataclass import (\n", - " AIModel,\n", - " AIModelCreateInput,\n", - " AIModelRunInput,\n", - " ModelTypes,\n", - " CodeRunType\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7084c9f3-cac9-4b22-8da3-1415d51a7d16", - "metadata": {}, - "outputs": [], - "source": [ - "my_username = \"FCP_LOGIN_EMAIL\" # Replace this with the email you use to log into Rhino Health\n", - "session = rh.login(username=my_username, password=getpass.getpass())" - ] - }, - { - "cell_type": "markdown", - "id": "c22aa633", - "metadata": {}, - "source": [ - "### Retrieve Project and Cohort Information" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "00cffc9f-2a49-464a-8380-02a6548df34d", - "metadata": {}, - "outputs": [], - "source": [ - "project = session.project.get_project_by_name(\"YOUR_PROJECT_NAME\") # Replace with your project name\n", - "dataschema = project.data_schemas[0]\n", - "print(f\"Loaded dataschema '{dataschema.name}' with uid '{dataschema.uid}'\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b756f753-7b98-40fd-9f2b-0a505fe64b6b", - "metadata": {}, - "outputs": [], - "source": [ - "cxr_schema = project.get_data_schema_by_name('Auto-generated schema for mimic_cxr_dev', project_uid=project.uid)\n", - "cxr_schema_uid =cxr_schema.uid" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7153dd89-65cb-4234-9995-7bf72c29c5f4", - "metadata": {}, - "outputs": [], - "source": [ - "collaborators = project.collaborating_workgroups\n", - "workgroups_by_name = {x.name: x for x in collaborators}\n", - "workgroups_by_uid = {x.uid: x for x in collaborators}\n", - "hco_workgroup = workgroups_by_name[\"Health System - Sandbox\"]\n", - "aidev_workgroup = workgroups_by_name[\"Decode Health - Sandbox\"]\n", - "\n", - "print(f\"Found workgroups '{aidev_workgroup.name}' and collaborators '{hco_workgroup.name}'\")" - ] - }, - { - "cell_type": "markdown", - "id": "303b52e4-ca0c-4542-859e-3d1f3159b2bc", - "metadata": {}, - "source": [ - "### Get the CXR Cohorts From Both Sites" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3cf0ee9-cc66-48a9-a6c3-d38d461cede9", - "metadata": {}, - "outputs": [], - "source": [ - "cohorts = project.cohorts\n", - "cohorts_by_workgroup = {workgroups_by_uid[x.workgroup_uid].name: x for x in cohorts}\n", - "hco_cxr_cohort = project.get_cohort_by_name(\"mimic_cxr_hco\")\n", - "aidev_cxr_cohort = project.get_cohort_by_name(\"mimic_cxr_dev\")\n", - "hco_cxr_cohort_uid = hco_cxr_cohort.uid\n", - "aidev_cxr_cohort_uid = aidev_cxr_cohort.uid\n", - "print(f\"Loaded CXR cohorts '{hco_cxr_cohort.uid}', '{aidev_cxr_cohort.uid}'\")" - ] - }, - { - "cell_type": "markdown", - "id": "11f28226-17a6-4f80-9d9e-bf18ebaa6ef5", - "metadata": {}, - "source": [ - "### We will use a Pre-defined Container Image with our Model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "451d6751-e4aa-46b3-aa72-aff8471bc09e", - "metadata": {}, - "outputs": [], - "source": [ - "cxr_image_uri= \"865551847959.dkr.ecr.us-east-1.amazonaws.com/workgroup-rhino-sandbox-decode-health:data-prep-sb-1\"" - ] - }, - { - "cell_type": "markdown", - "id": "62b431d5-e830-4ac4-8c4b-201499e11d88", - "metadata": {}, - "source": [ - "### Define the Generalized Compute Model that will Convert DICOM Images to JPG Files" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32a37efb-4f5f-4043-8b2d-ab976e6bc08a", - "metadata": {}, - "outputs": [], - "source": [ - "compute_params = AIModelCreateInput(\n", - " name=\"DICOM to JPG Transformation Code\",\n", - " description=\"CXR JPG transformation the AI dev and Health System datasets\",\n", - " input_data_schema_uids = [cxr_schema_uid],\n", - " output_data_schema_uids = [None], # Auto-Generating the Output Data Schema for the Model\n", - " project_uid = project.uid,\n", - " model_type = ModelTypes.GENERALIZED_COMPUTE, \n", - " config={\"container_image_uri\": cxr_image_uri}\n", - ")\n", - "\n", - "compute_model = session.aimodel.create_aimodel(compute_params)\n", - "print(f\"Got aimodel '{compute_model.name}' with uid {compute_model.uid}\")" - ] - }, - { - "cell_type": "markdown", - "id": "c4f36033-e597-4c9b-916b-89235af73ebd", - "metadata": {}, - "source": [ - "### Run the Model Defined in the Previous Cell" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6626dd0a-228b-4c62-adff-12852c7a9276", - "metadata": {}, - "outputs": [], - "source": [ - "run_params = AIModelRunInput(\n", - " aimodel_uid = compute_model.uid,\n", - " input_cohort_uids = [aidev_cxr_cohort_uid,hco_cxr_cohort_uid], \n", - " output_cohort_names_suffix = \"_conv\",\n", - " timeout_seconds = 600\n", - ")\n", - "model_run = session.aimodel.run_aimodel(run_params)\n", - "run_result = model_run.wait_for_completion()\n", - "print(f\"Finished running {compute_model.name}\")\n", - "print(f\"Result status is '{run_result.status.value}', errors={run_result.result_info.get('errors') if run_result.result_info else None}\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/sandbox/pneumonia-prediction/3_statistical_analyses.ipynb b/sandbox/pneumonia-prediction/3_statistical_analyses.ipynb deleted file mode 100644 index 0edf8d79..00000000 --- a/sandbox/pneumonia-prediction/3_statistical_analyses.ipynb +++ /dev/null @@ -1,540 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "cb0d6d79", - "metadata": {}, - "source": [ - "# Notebook #3: Exploratory Data Analyses, Statistical Testing, and Time Series Analysis\n", - "The `rhino_health` Python library is a robust tool designed to support a wide array of statistical and epidemiological analyses over federated data sets, crucial in the realm of healthcare and medical research. In this notebook, we demonstrate the ability to perform analyses over multiple distributed datasets. More specifically, we'll perform an analysis of data related to pneumonia outcomes and perform both exploratory data analyses to support our machine learning project as well as hypothesis testing for a traditional biostatistics analysis.\n", - "\n", - "#### Import Rhino's Metrics Module\n", - "\n", - "The `rhino_health.lib.metrics` module in the Rhino Health library is a comprehensive suite designed for diverse statistical and epidemiological analyses of healthcare data. This module is divided into several submodules, each targeting specific types of metrics and analyses.\n", - "\n", - "In the following code block, we'll import some basic functions like Mean() and Count() and authenticate to the Rhino cloud using your specific credentials." - ] - }, - { - "cell_type": "markdown", - "id": "dcddb792-e7c4-4eba-9cbd-9a40c4f520db", - "metadata": {}, - "source": [ - "### Install the Rhino Health Python SDK, Load All Necessary Libraries and Login to the Rhino FCP" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8f71a19", - "metadata": {}, - "outputs": [], - "source": [ - "import getpass\n", - "from pprint import pprint\n", - "import rhino_health as rh\n", - "\n", - "from rhino_health.lib.metrics.basic import Sum, Count, Mean,StandardDeviation\n", - "from rhino_health.lib.metrics.aggregate_metrics.aggregation_service import get_cloud_aggregated_metric_data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08d793a0-a816-4648-9e38-e05aa0bfec8a", - "metadata": {}, - "outputs": [], - "source": [ - "my_username = \"FCP_LOGIN_EMAIL\" # Replace this with the email you use to log into Rhino Health\n", - "session = rh.login(username=my_username, password=getpass.getpass())" - ] - }, - { - "cell_type": "markdown", - "id": "f11ba5c0", - "metadata": {}, - "source": [ - "### Load the Cohorts\n", - "We'll use our SDK to identify the relevant cohorts that we'd like to perform exploratory analyses on. It is **critical to understand that the cohorts must have the same data schema in order to generate statistics on multiple cohorts simultaneously.**\n", - "\n", - "" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12c7a83c", - "metadata": {}, - "outputs": [], - "source": [ - "#Replace with your project and cohort names. Raw data and harmonized data\n", - "project = session.project.get_project_by_name(\"YOUR_PROJECT_NAME\")\n", - "\n", - "cxr_cohorts = [\n", - " project.get_cohort_by_name(\"mimic_cxr_dev\"), # Replace Me\n", - " project.get_cohort_by_name(\"mimic_cxr_hco\"), # Replace Me\n", - "] " - ] - }, - { - "cell_type": "markdown", - "id": "35a679bf", - "metadata": {}, - "source": [ - "### An Introduction to Federated Metrics\n", - "\n", - "The Rhino Federated Computing Platform allows you to quickly and securely calculate metrics using federated computing across multiple sites. Each metric on the Rhino platform has two components:\n", - "\n", - "#### The Metric Object\n", - "\n", - "The metric configuration object is a crucial component, serving as a blueprint for metric retrieval. It allows you to specify the metric variables, grouping preferences, and data filters. For example, let's define two metrics:\n", - "\n", - "1. Count of total cases across both cohorts\n", - "2. Count of positive pneumonia cases across both cohorts\n", - "\n", - "#### The Response Object\n", - "\n", - "When retrieving a metric, *all results are returned in a MetricResponse object*. The MetricResponse object is a Python class that includes the specific outcome values in the 'output' attribute, such as statistical measures, and details about the metric configuration ('metric_configuration_dict').\n", - "\n", - "The metric results will always be under the output attribute, under the metric name key (in this case, \"chi_square\"). The metric response values are then stored under the value name (e.g., \"p_value\" in the example above). The initial metric configuration used to generate this output can be found under the \"metric_configuration_dict\" attribute.\n", - "\n", - "\n", - "### Exploratory Data Analysis\n", - "\n", - "In the rest of this notebook, we'll analyze the federated data that we've prepared in the preceding notebooks. The data will be aggregated across two sites.\n", - "\n", - "### Defining a simple metric without a filter:\n", - "\n", - "We'll define the simplest metric possible -  a simple count of the number of rows across both of our cohorts: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bdcf4e68", - "metadata": {}, - "outputs": [], - "source": [ - "# Count the number of entries in the dataset\n", - "pneumonia_count_response = session.project.aggregate_cohort_metric( \n", - " cohort_uids=[str(cohort.uid) for cohort in cxr_cohorts], # list containing relevant cohorts\n", - " metric_configuration=Count(variable=\"Pneumonia\") # Metric configuration\n", - ") \n", - "\n", - "pneumonia_count = pneumonia_count_response.output\n", - "print(f\"Entries in Dataset: {pneumonia_count}\")" - ] - }, - { - "cell_type": "markdown", - "id": "f76ff3d9", - "metadata": {}, - "source": [ - "### Adding a filter to our metric\n", - "The `data_filters` parameter enables you to refine your analysis by setting conditions and filter the output by certain criteria. We'll now filter our `Count()` variable by a value; in this case, pneumona cases are identified by the `pneumonia` value of 1, and thus we'll add `pneumonia` as a `filter_column` and `1` as a `filter_value`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b0748fc7", - "metadata": {}, - "outputs": [], - "source": [ - "# Count the number of people with pneumonia and the number without pneumonia\n", - "pneumonia_count_configuration = Count(variable={\"data_column\": \"Pneumonia\", \n", - " \"filter_column\": \"Pneumonia\", \n", - " \"filter_value\": 1})\n", - "\n", - "pneumonia_count_response = session.project.aggregate_cohort_metric(\n", - " cohort_uids=[str(cohort.uid) for cohort in cxr_cohorts],\n", - " metric_configuration=pneumonia_count_configuration)\n", - " \n", - "pneumonia_count = pneumonia_count_response.output\n", - "\n", - "print(f\"Pneumonia Cases in Dataset: {pneumonia_count}\")" - ] - }, - { - "cell_type": "markdown", - "id": "b37e0041-0aea-4729-a954-d75212f93de6", - "metadata": {}, - "source": [ - "#### Adding a grouping mechanism to our metric\n", - "In addition to the `data_filter parameter`, we can also add a `group_by` parameter allows you to organize metrics based on specific categorical variables. In this example, we'll calculate the mean age across our two cohorts using the gender column in our data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dfd1b195-8e18-4ae1-8cc7-3c25c0f55a34", - "metadata": {}, - "outputs": [], - "source": [ - "# Get median age of the aggregated cohort\n", - "median_age_response = session.project.aggregate_cohort_metric(\n", - " cohort_uids=[str(cohort.uid) for cohort in cohorts],\n", - " metric_configuration=Median(variable=\"age\",\n", - " group_by=\"gender\"),\n", - ")\n", - "\n", - "median_age = median_age_response.output[\"age\"]\n", - "print(f\"Median Age: {median_age}\")" - ] - }, - { - "cell_type": "markdown", - "id": "c9e20814-1300-4600-889e-e65281d5b409", - "metadata": {}, - "source": [ - "#### Contingency Tables (aka two-by-two tables)\n", - "The `TwoByTwoTable` metric facilitates the creation of a two-by-two contingency table, enabling you to analyze the relationships between variables. Here is an example for generating a two by two table metric for the columns \"exposed\" and \"detected\" in the data:\n", - "\n", - "Lets examine the count of men and women with and without our outcome of interest (pneumonia). We can use Rhino's two-by-two table functions to generate simple contingency tables. \n", - "\n", - "The table results are also stored in a response object, that can be parsed into a pandas data frame in order to view the results as a table:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ac5d0938-2884-4099-9291-32e0b63294a6", - "metadata": {}, - "outputs": [], - "source": [ - "from rhino_health.lib.metrics import Count, FilterType, Mean, StandardDeviation, RocAuc\n", - "from rhino_health.lib.metrics.epidemiology.two_by_two_table_based_metrics import *\n", - "\n", - "# Calculate TBTT\n", - "tbtt = TwoByTwoTable(\n", - " variable=\"subject_id\",\n", - " detected_column_name=\"gender\",\n", - " exposed_column_name=\"pneumonia\",\n", - ")\n", - "\n", - "\n", - "table_result = project.aggregate_cohort_metric([str(cohort.uid) for cohort in cohorts], # cohort uids\n", - " tbtt).output # metric configuration\n", - "pd.DataFrame(table_result.as_table())" - ] - }, - { - "cell_type": "markdown", - "id": "7bbc7a1a-59be-44d4-be99-3c0c1e2b91cc", - "metadata": {}, - "source": [ - "Interestingly, we can see that our cohort is extremely skewed towards women with pneumonia. \n", - "\n", - "#### Odds Ratio:\n", - "We can configure an odds ratio metric using the same configuration and execution pattern that we defined above for the median statistic. The Odds metric calculates the odds of an event occurring rather than not occuring, and can be generated like so: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ed70f10e-6000-4ac4-931d-2862e6191c24", - "metadata": {}, - "outputs": [], - "source": [ - "odds_ratio_config = OddsRatio(\n", - " variable=\"subject_id\",\n", - " exposed_column_name=\"gender\",\n", - " detected_column_name=\"pneumonia\",\n", - ")\n", - "\n", - "session.project.aggregate_cohort_metric([str(cohort.uid) for cohort in cohorts], # cohort_uids\n", - " odds_ratio_config).output # metric configuration" - ] - }, - { - "cell_type": "markdown", - "id": "95823efa-563d-4e28-88d3-6f8ebaab1c8a", - "metadata": {}, - "source": [ - "#### Prevalence & Incidence\n", - "\n", - "The Prevalence metric calculates the proportion of individuals who have or develop a specific condition over a specified time range, whereas the Incidence describes the occurrence of new cases over a period of time. In this example, the prevalence and incidence of pneumonia is calculated between the given specific time range. Note that the column representing the time data should contain time in UTC format." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e4b7cb4-3936-4676-8050-3e34d66db07f", - "metadata": {}, - "outputs": [], - "source": [ - "from rhino_health.lib.metrics import Prevalence, Incidence \n", - "\n", - "prevalence_config = Prevalence(variable=\"id\", \n", - " time_column_name=\"Time Pneumonia\", \n", - " detected_column_name=\"Pneumonia\", \n", - " start_time=\"2023-02-02T07:07:48Z\", \n", - " end_time=\"2023-06-10T11:24:43Z\", ) \n", - "prevalence_results = session.project.aggregate_cohort_metric(cohort_uids, prevalence_config)\n", - "\n", - "\n", - "incidence_config = Incidence( variable=\"id\", \n", - " time_column_name=\"Time Pneumonia\", \n", - " detected_column_name=\"Pneumonia\", \n", - " start_time=\"2023-02-02T07:07:48Z\", \n", - " end_time=\"2023-06-10T11:24:43Z\", ) \n", - "incidence_results = session.project.aggregate_cohort_metric(cohort_uids, incidence_config)" - ] - }, - { - "cell_type": "markdown", - "id": "43d57a45-8996-46dc-a70b-022b4740f221", - "metadata": {}, - "source": [ - "## Statistical Testing\n", - "\n", - "#### Chi-Square Test\n", - "\n", - "The Chi-Square test is employed to assess the independence between two categorical variables. In this example, we examine the association between the occurrence of pneumonia and gender across different cohorts. The result includes the Chi-Square statistic, p-value, and degrees of freedom." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5a63c490-3a89-44ff-8699-37171c5693d0", - "metadata": {}, - "outputs": [], - "source": [ - "from rhino_health.lib.metrics.statistics_tests import ChiSquare\n", - "\n", - "chi_square_config = ChiSquare(variable=\"id\", variable_1=\"Pneumonia\", variable_2=\"Gender\")\n", - "\n", - "result = project.aggregate_cohort_metric(cohort_uids, chi_square_config)" - ] - }, - { - "cell_type": "markdown", - "id": "fb4e101a-4d0b-4ab9-8606-425e63f540f0", - "metadata": {}, - "source": [ - "#### T-Test\n", - "\n", - "The T-Test is utilized to determine if there is a significant difference between the means of two groups. The implemented method is the Welch test, that does not assume equality of variance. The result includes the T-Test statistic, p-value, and degrees of freedom." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae832273-b8e8-435c-89f1-d59e0e878131", - "metadata": {}, - "outputs": [], - "source": [ - "from rhino_health.lib.metrics.statistics_tests import TTest\n", - "\n", - "t_test_config = TTest(numeric_variable=\"Height\", categorical_variable=\"Gender\")\n", - "\n", - "t_test_result = project.aggregate_cohort_metric(cohort_uids, t_test_config)" - ] - }, - { - "cell_type": "markdown", - "id": "3315574c-c379-434f-8bb7-14abc1a8393f", - "metadata": {}, - "source": [ - "#### One-Way ANOVA\n", - "\n", - "The One-Way ANOVA (Analysis of Variance) is applied to assess whether there are any statistically significant differences between the means of three or more independent groups. In this example, we examine the relationship between inflammation level and height. The result will contain the following calculated values: anova statistic value, p value, dfc, dfe, dft, MSC, MSE, SSC, SSE, SST. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa25e2dd-1f91-4c8c-a5bf-fc496b6d346a", - "metadata": {}, - "outputs": [], - "source": [ - "from rhino_health.lib.metrics.statistics_tests import OneWayANOVA\n", - "\n", - "anova_config = OneWayANOVA(variable=\"id\", numeric_variable=\"Height\", categorical_variable=\"Inflammation Level\")\n", - "\n", - "anova_result = project.aggregate_cohort_metric(cohort_uids, anova_config)" - ] - }, - { - "cell_type": "markdown", - "id": "88747707-095f-448a-a0d2-7e0bdf40eb22", - "metadata": {}, - "source": [ - "## Time Series Analysis\n", - "The Rhino SDK also has the ability to perform Kaplan Meier analyses. The Kaplan-Meier Metric is a powerful tool for analyzing time-to-event data, such as patient survival rates.  The KaplanMeier() function returns the k-percentile of entries for a specified variable. \n", - "\n", - "For configuring the basic Kaplan-Meier metric, you can set up the metric and retrieve results as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a461e37-03d9-4d66-a1df-3cd1577ca8be", - "metadata": {}, - "outputs": [], - "source": [ - "from rhino_health.lib.metrics import KaplanMeier\n", - "\n", - "# Set the time and event variables\n", - "time_variable = \"time_column_name\"\n", - "event_variable = \"event_column_name\"\n", - "\n", - "# Create a KaplanMeier instance\n", - "metric_configuration = KaplanMeier(time_variable=time_variable, event_variable=event_variable)\n", - "\n", - "# Retrieve results for your project and cohorts\n", - "results = project.aggregate_cohort_metric(cohort_uids=[str(cohort.uid) for cohort in km_cohorts], metric_configuration=metric_configuration)" - ] - }, - { - "cell_type": "markdown", - "id": "905e3a4c-cca5-46b9-962b-3b386837c7ec", - "metadata": {}, - "source": [ - "#### Working with Kaplan-Meier Metric Results\n", - "Extracting Time and Events Vector\n", - "\n", - "The Kaplan-Meier Metric in the Rhino Health Platform provides results that allow you to analyze time-to-event data, create survival models, and visualize Kaplan-Meier curves. \n", - "\n", - "The results of the Kaplan-Meier Metric are stored in a KaplanMeierModelResults object with an \"output\" attribute that contains time and event vectors. Access these vectors as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "02c91ed7-f359-4e88-a899-ae87f0d5c891", - "metadata": {}, - "outputs": [], - "source": [ - "# Accessing the vectors using the names of the original time and event data columns\n", - "# For non grouped results\n", - "time_vector = results.output[\"time_column_name\"]\n", - "event_vector = results.output[\"event_column_name\"]\n", - "\n", - "# For grouped results, where the group of interest is \"1\"\n", - "time_vector_group_1 = results.output[\"1\"][\"time_column_name\"]\n", - "event_vector_group_1 = results.output[\"1\"][\"event_column_name\"]" - ] - }, - { - "cell_type": "markdown", - "id": "379aaecb-31a5-42a7-85df-b7bad1fb23fb", - "metadata": {}, - "source": [ - "By obtaining these vectors, you can proceed to create a survival model and gain more insights from your Kaplan-Meier data in any way desired. \n", - "\n", - "\n", - "### Creating a Survival Model\n", - "\n", - "The Rhino Health Platform SDK provides a convenient way to obtain the survival model object, which allows you to explore detailed Kaplan-Meier analysis. The object is a SurvFuncRight object from the statsmodels library:m" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "75186d13-fd19-4936-b6f5-87f0ababdd81", - "metadata": {}, - "outputs": [], - "source": [ - "# For non grouped results\n", - "survival_model = results.surv_func_right_model()\n", - "\n", - "# For grouped results, get the survival model where the group of interest is \"1\"\n", - "group = \"1\"\n", - "survival_model = results.surv_func_right_model(group=group)\n", - "\n", - "# Access various properties of the survival model\n", - "median_time = survival_model.quantile(0.5)  # Median survival time\n", - "cumulative_hazard = survival_model.cumulative_hazard_at_times([100, 200, 300])  # Cumulative hazard at specific times\n", - "print(survival_model.summary())" - ] - }, - { - "cell_type": "markdown", - "id": "e7db251d-8cc8-4462-906d-a09bc56e6331", - "metadata": {}, - "source": [ - "Note that to use this feature, you need to have the statsmodels library installed in your Python environment. If you haven't installed it yet, you can do so using `pip`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a3da0efe-5435-4bfd-8868-fc0165bbdc6a", - "metadata": {}, - "outputs": [], - "source": [ - "pip install statsmodels" - ] - }, - { - "cell_type": "markdown", - "id": "9a6a6494-5af9-419e-b2c1-f0412e35e00a", - "metadata": {}, - "source": [ - "### Plotting Kaplan-Meier Curves\n", - "\n", - "Visualizing Kaplan-Meier curves is a way to gain insights into your survival data. The Rhino Health Platform SDK KaplanMeierMetricResults object can be used to plot these curves. Using matplotlib.pyplot library is a convenient way for that:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a22df95-6149-499d-8cc4-51da38f2e82d", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "# Accessing time and event vectors\n", - "time_vector = results.output[\"time_column_name\"]\n", - "event_vector = results.output[\"event_column_name\"]\n", - "\n", - "# Plot Kaplan-Meier survival curve\n", - "plt.figure(figsize=(10, 6))\n", - "plt.step(time_vector, event_vector, where='post', label=\"model 1\")\n", - "plt.legend(loc=\"upper left\")\n", - "plt.title(\"Kaplan-Meier Survival Curve\")\n", - "plt.xlabel(\"Time\")\n", - "plt.ylabel(\"Survival Probability\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "b8ec95c1-56d8-438f-a519-1d6954bd0a5f", - "metadata": {}, - "source": [ - "#### Differential Privacy for Kaplan-Meier Metric\n", - "\n", - "Differential privacy is a technique used in the FCP to protect patient data by adding noise to query results. Like all FCP metrics, the Kaplan-Meier metric supports differential privacy, and you can configure the privacy enforcement level in your project settings. The default privacy level is \"Medium,\" but you can select from \"None,\" \"Low,\" \"Medium,\" or \"High\" according to your project's privacy requirements, whereas:\n", - "\n", - "None - No noise is added to any of the data.\n", - "Low, Medium - Noise is partially added to the data. Times that less than k (anonymity threshold) events occur are aggregated and averaged across events occurring in adjacent times, and noise is then added to them.\n", - "High - Noise is added to all of the time data.  \n", - "\n", - "To learn more about configuring differential privacy settings, please refer to the project settings documentation." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/sandbox/pneumonia-prediction/4_model_training.ipynb b/sandbox/pneumonia-prediction/4_model_training.ipynb deleted file mode 100644 index da0781ae..00000000 --- a/sandbox/pneumonia-prediction/4_model_training.ipynb +++ /dev/null @@ -1,276 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "9d5ace37-8ae8-4bd8-a905-6713750d8129", - "metadata": {}, - "source": [ - "# Notebook #4: Running Federated Training of the Pneumonia Model" - ] - }, - { - "cell_type": "markdown", - "id": "b9ef6e7a-63a6-4bbc-b134-ebbd60dfd550", - "metadata": {}, - "source": [ - "### Install the Rhino Health Python SDK, Load All Necessary Libraries and Login to the Rhino FCP" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f3890be-ff13-472e-901a-594fa99e9ddb", - "metadata": {}, - "outputs": [], - "source": [ - "import getpass\n", - "import rhino_health as rh\n", - "from rhino_health.lib.endpoints.aimodel.aimodel_dataclass import (\n", - " AIModelCreateInput,\n", - " ModelTypes,\n", - " AIModelRunInput,\n", - " AIModelMultiCohortInput,\n", - " AIModelTrainInput \n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "863b04e3-ea1f-4edf-8e06-6721a591bcb2", - "metadata": {}, - "outputs": [], - "source": [ - "my_username = \"FCP_LOGIN_EMAIL\" # Replace this with the email you use to log into Rhino Health\n", - "session = rh.login(username=my_username, password=getpass.getpass())" - ] - }, - { - "cell_type": "markdown", - "id": "f1b96218-22e0-40c0-98c9-b1a9b0e3a84a", - "metadata": {}, - "source": [ - "### Retrieve Project and Cohort Information" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7fe89328-be4c-4b6d-8a20-d166d98b828c", - "metadata": {}, - "outputs": [], - "source": [ - "project = session.project.get_project_by_name(\"YOUR_PROJECT_NAME\") # Replace with your project name" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cdf16109-f711-4ae8-82b8-a0140aa68aeb", - "metadata": {}, - "outputs": [], - "source": [ - "# Get the schema that was created after JPG conversion\n", - "cxr_schema = project.get_data_schema_by_name('Auto-generated schema for mimic_cxr_hco_conv', project_uid=project.uid)\n", - "cxr_schema_uid =cxr_schema.uid\n", - "print(cxr_schema_uid)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d32168a-c05d-4e69-b199-1e13e2805067", - "metadata": {}, - "outputs": [], - "source": [ - "cohorts = project.cohorts\n", - "hco_cxr_cohort = project.get_cohort_by_name(\"mimic_cxr_hco_conv\")\n", - "aidev_cxr_cohort = project.get_cohort_by_name(\"mimic_cxr_dev_conv\")\n", - "cxr_cohorts = [aidev_cxr_cohort.uid, hco_cxr_cohort.uid]\n", - "print(f\"Loaded CXR cohorts '{hco_cxr_cohort.uid}', '{aidev_cxr_cohort.uid}'\")" - ] - }, - { - "cell_type": "markdown", - "id": "b01cdbbf-ef6a-4260-80ea-2cf93eedccc6", - "metadata": {}, - "source": [ - "## Create the Train Test Split Model and then Run it Over both CXR Cohorts\n", - "We will split both CXR Data Cohorts into two Cohorts one for training and the other testing\n", - "### We will use a Pre-defined Container Image with our Model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce46343c-0f66-4fcb-83fe-95fda628b1ca", - "metadata": {}, - "outputs": [], - "source": [ - "train_split_image_uri = \"865551847959.dkr.ecr.us-east-1.amazonaws.com/workgroup-rhino-sandbox-decode-health:train-test-split-sb\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd94c56e-a079-42a4-aff2-29617489539c", - "metadata": {}, - "outputs": [], - "source": [ - "aimodel = AIModelCreateInput(\n", - " name=\"Train Test Split\",\n", - " description=\"Splitting data into train and test datasets per site\",\n", - " input_data_schema_uids=[cxr_schema_uid],\n", - " output_data_schema_uids=[None], # Auto-Generating the Output Data Schema for the Model\n", - " model_type=ModelTypes.GENERALIZED_COMPUTE,\n", - " project_uid = project.uid,\n", - " config={\"container_image_uri\": train_split_image_uri}\n", - ")\n", - "aimodel = session.aimodel.create_aimodel(aimodel)\n", - "print(f\"Got aimodel '{aimodel.name}' with uid {aimodel.uid}\")\n", - "\n", - "run_params = AIModelMultiCohortInput(\n", - " aimodel_uid= aimodel.uid,\n", - " input_cohort_uids=[aidev_cxr_cohort.uid, hco_cxr_cohort.uid],\n", - " output_cohort_naming_templates= ['{{ input_cohort_names.0 }} - Train', '{{ input_cohort_names.0 }} - Test'],\n", - " timeout_seconds=600,\n", - " sync=False,\n", - ")\n", - "\n", - "print(f\"Starting to run {aimodel.name}\")\n", - "model_run = session.aimodel.run_aimodel(run_params)\n", - "run_result = model_run.wait_for_completion()\n", - "print(f\"Finished running {aimodel.name}\")\n", - "print(f\"Result status is '{train_result.status.value}', errors={train_result.result_info.get('errors') if train_result.result_info else None}\")" - ] - }, - { - "cell_type": "markdown", - "id": "6252401a-b01a-4a31-aa9c-23a835f25dc7", - "metadata": {}, - "source": [ - "## Create and Run the Federated Model Training and Validation Across Both of Our Two Sites\n", - "We will utilize NVFlare to train our pneumonia predicition model using our local training Cohort and the remote Health System training Cohort. The model will then be validated again the local testing Cohort and the remote Health System testing Cohort.\n", - "### We will use a Pre-defined Container Image with our Model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7484e178-ef9d-4f3b-a68f-184d55c05940", - "metadata": {}, - "outputs": [], - "source": [ - "model_train_image_uri = \"865551847959.dkr.ecr.us-east-1.amazonaws.com/workgroup-rhino-sandbox-decode-health:prediction-model-sb-22\"" - ] - }, - { - "cell_type": "markdown", - "id": "4edcbf4b-6f97-4720-bd3e-e6c9b8025680", - "metadata": {}, - "source": [ - "### Search for our Newly Split Local and Remote Cohorts " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c0b5ee5f-0e1d-4c80-ba46-f362672f81a1", - "metadata": {}, - "outputs": [], - "source": [ - "input_training_cohorts = session.cohort.search_for_cohorts_by_name('Train')\n", - "input_validation_cohorts = session.cohort.search_for_cohorts_by_name('Test')\n", - "print(\"Found training cohorts:\")\n", - "print([x.name for x in input_training_cohorts])\n", - "print(\"Found validation cohorts:\")\n", - "print([x.name for x in input_validation_cohorts])" - ] - }, - { - "cell_type": "markdown", - "id": "e8ca1627-7d0b-4ffa-bb6c-6698dcc3a960", - "metadata": {}, - "source": [ - "### Create the Pneumonia Prediciton Model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1efeda7-cb4c-446f-ac95-19c5de6ed5fc", - "metadata": {}, - "outputs": [], - "source": [ - "aimodel = AIModelCreateInput(\n", - " name=\"Pneumonia Prediction Model Training\",\n", - " description=\"Pneumonia Prediction Model Training\",\n", - " input_data_schema_uids=[cxr_schema_uid],\n", - " output_data_schema_uids=[None], # Auto-Generating the Output Data Schema for the Model\n", - " project_uid= project.uid,\n", - " model_type=ModelTypes.NVIDIA_FLARE_V2_2,\n", - " config={\"container_image_uri\": model_train_image_uri}\n", - ")\n", - "\n", - "aimodel = session.aimodel.create_aimodel(aimodel)\n", - "print(f\"Got aimodel '{aimodel.name}' with uid {aimodel.uid}\")" - ] - }, - { - "cell_type": "markdown", - "id": "5d1b79e0-84bb-42b5-9729-1adc3d124ea8", - "metadata": {}, - "source": [ - "### Run the Pneumonia Prediciton Model Training with Validation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c92c2b1d-e98d-4b48-8c2c-07852b5e1c6d", - "metadata": {}, - "outputs": [], - "source": [ - "run_params = AIModelTrainInput(\n", - " aimodel_uid=aimodel.uid,\n", - " input_cohort_uids=[x.uid for x in input_training_cohorts], \n", - " one_fl_client_per_cohort=True , \n", - " validation_cohort_uids=[x.uid for x in input_validation_cohorts], \n", - " validation_cohorts_inference_suffix=\" - Pneumonia training results\",\n", - " timeout_seconds=600,\n", - " config_fed_server=\"\",\n", - " config_fed_client=\"\",\n", - " secrets_fed_client=\"\",\n", - " secrets_fed_server=\"\",\n", - " sync=False,\n", - ")\n", - "\n", - "print(f\"Starting to run federated training of {aimodel.name}\")\n", - "model_train = session.aimodel.train_aimodel(run_params)\n", - "train_result = model_train.wait_for_completion()\n", - "print(f\"Finished running {aimodel.name}\")\n", - "print(f\"Result status is '{train_result.status.value}', errors={train_result.result_info.get('errors') if train_result.result_info else None}\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/sandbox/pneumonia-prediction/5_model_evaluation.ipynb b/sandbox/pneumonia-prediction/5_model_evaluation.ipynb deleted file mode 100644 index f15f17f4..00000000 --- a/sandbox/pneumonia-prediction/5_model_evaluation.ipynb +++ /dev/null @@ -1,203 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "041a98c7-316d-4a31-9641-14fff4aaf1de", - "metadata": {}, - "source": [ - "# Notebook #5: Federated Evaluations" - ] - }, - { - "cell_type": "markdown", - "id": "e1711135-c0c6-47cd-9511-76ee8bdf35f2", - "metadata": {}, - "source": [ - "### Install the Rhino Health Python SDK, Load All Necessary Libraries and Login to the Rhino FCP" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d7fd74d-416e-41b1-8a08-2383beb05b34", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.axes\n", - "import matplotlib.figure\n", - "import matplotlib.pyplot as plt\n", - "from PIL import Image\n", - "import os\n", - "import sys\n", - "import getpass\n", - "import json\n", - "import io\n", - "import base64\n", - "\n", - "import rhino_health as rh\n", - "from rhino_health.lib.metrics import RocAuc, RocAucWithCI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b015539-3224-40dc-9b22-2d7d8caa2fd2", - "metadata": {}, - "outputs": [], - "source": [ - "my_username = \"FCP_LOGIN_EMAIL\" # Replace this with the email you use to log into Rhino Health\n", - "session = rh.login(username=my_username, password=getpass.getpass())" - ] - }, - { - "cell_type": "markdown", - "id": "f455e007-81e4-4421-ae3d-126d147fca75", - "metadata": {}, - "source": [ - "### Load the Results Cohorts from the Pneumonia Training & Validation Process" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8cb6d9e7-a0a7-4935-9920-a6ad217b7c4b", - "metadata": {}, - "outputs": [], - "source": [ - "project = session.project.get_project_by_name(\"YOUR_PROJECT_NAME\") # Replace with your project name\n", - "results_cohorts = session.cohort.search_for_cohorts_by_name('COHORT_SUFFIX') # Change it with your suffix\n", - "[cohort.name for cohort in results_cohorts]" - ] - }, - { - "cell_type": "markdown", - "id": "c9d92da8-5bec-4b4d-97bf-75596e71940e", - "metadata": {}, - "source": [ - "### Calculate ROC (Underlying Results Data Stays On-prem)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "43f733fc-bf90-4945-ade5-63a9932f4a4d", - "metadata": {}, - "outputs": [], - "source": [ - "# function to plot ROC\n", - "\n", - "def plot_roc(results,cohorts):\n", - " colors = plt.rcParams['axes.prop_cycle'].by_key()['color']\n", - " linestyle_cycle = ['-', '--']\n", - " fig, ax = plt.subplots(figsize=[6, 4], dpi=200)\n", - " linestyle = linestyle_cycle[0]\n", - " \n", - " for i,result in enumerate (results):\n", - " roc_metrics = result.output\n", - " color = colors[0]\n", - " ax.plot(roc_metrics['fpr'], roc_metrics['tpr'], color=colors[i], \n", - " linestyle=linestyle, label=cohorts[i])\n", - " ax.legend(loc='lower right')\n", - "\n", - " ax.title.set_text('ROC per Site')\n", - " ax.set_xlabel('1 - Specificity')\n", - " ax.set_ylabel('Sensitivity')\n", - " ax.grid(True)\n", - " ax.set_xlim([0, 1])\n", - " ax.set_ylim([0, 1])\n", - " fig.canvas.draw()\n", - " return fig" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b5fe7df-cff1-4f4b-8d0f-e55c992ec4a2", - "metadata": {}, - "outputs": [], - "source": [ - "results = []\n", - "cohorts = []\n", - "report_data = []\n", - "report_data.append({\"type\": \"Title\", \"data\": \"ROC Analysis\"})\n", - "\n", - "for result in results_cohorts:\n", - " cohort = session.cohort.get_cohort(result.uid)\n", - " cohorts.append(cohort.name.split('-')[0])\n", - " metric_configuration = RocAuc(y_true_variable=\"Pneumonia\",\n", - " y_pred_variable=\"Model_Score\")\n", - " results.append(cohort.get_metric(metric_configuration))\n", - "fig = plot_roc(results, cohorts)\n", - "image_to_store = Image.frombytes('RGB', fig.canvas.get_width_height(), fig.canvas.tostring_rgb())\n", - "image_to_store.save(\"ROC_per_site.png\", format='png', optimize=True, quality=100)\n", - "\n", - "with open(\"ROC_per_site.png\", \"rb\") as temp_image:\n", - " base_64_image = base64.b64encode(temp_image.read()).decode(\"utf-8\")\n", - " report_data.append(\n", - " {\n", - " \"type\": \"Image\",\n", - " \"data\": {\n", - " \"image_filename\": \"ROC per site\",\n", - " \"image_base64\": base_64_image,\n", - " },\n", - " \"width\": 100\n", - " }\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "b45b0e2e-7e55-4c53-b471-8b9c16a9d5d7", - "metadata": {}, - "source": [ - "### Upload the visualizations to the Rhino Health Platform" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a6889e5-2c44-454d-9b43-7facb8508189", - "metadata": {}, - "outputs": [], - "source": [ - "model_result_uid = \"XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\" # Paste the UID of the model results object for your NVF" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f13b1b6c-58bd-454a-8a3d-efcadbdfd309", - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Sending visualizations to the Cloud\")\n", - "\n", - "result = session.post(f\"federatedmodelactions/{model_result_uid}/set_report/\", \n", - " data={\"report_data\": json.dumps(report_data)})\n", - "print('Done')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/sandbox/pneumonia-prediction/README.md b/sandbox/pneumonia-prediction/README.md deleted file mode 100644 index 00f5d51b..00000000 --- a/sandbox/pneumonia-prediction/README.md +++ /dev/null @@ -1,25 +0,0 @@ -## Conducting an end-to-end ML project using multimodal data from multiple hospitals - -![image info](https://drive.google.com/uc?export=view&id=1SkAZdWns-Xi8lxw7xQzRhc8jKsnOCfIL) - -**Follow the Tutorial: [Here](https://docs.rhinohealth.com/hc/en-us/articles/15586509051549-Pneumonia-Prediction-Step-1-Scenario-FCP-Overview)** - -## Notebook 1: Import EHR and CXR Datasets -In this step, we gain experience in importing multimodal datasets onto the Rhino FCP. We will use MIMIC-IV as the source to create datasets from a database that hosts EHR data. In addition, we will load CXR DICOM images, which are linked to the EHR tables using a mapping lookup table. - -## Notebook 2: Data Engineering - In this step, the the DICOM files (chest x-rays) are converted to JPEG. - -## Notebook 3: Exploratory data analysis & biostatistical methods -In this step, the user will gain experience using privacy preserving exploratory data analytics capabilities of Rhino. We show few privacy preserving query with metrics. - - -## Notebook 4: Prepare Models for FL Training and execute model training -In this step, you will split data into training and testing and use the datasets for model training. You will gain experience converting an existing model to NVFlare and execute the FL model training across two sites. - -## Notebook 5: Federated Evaluations -Visualize evaluation results - - -Need Help? -[Rhino Health Documenation Center](https://docs.rhinohealth.com/) or [support@rhinohealth.com](mailto:support@rhinohealth.com) \ No newline at end of file diff --git a/sandbox/pneumonia-prediction/containers/data-prep/Dockerfile b/sandbox/pneumonia-prediction/containers/data-prep/Dockerfile deleted file mode 100644 index f7b5258c..00000000 --- a/sandbox/pneumonia-prediction/containers/data-prep/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM python:3.10.8-bullseye as wheelbuilder - -COPY requirements.txt ./ - -RUN --mount=type=cache,target=/.cache/pip \ - python -m pip install -U pip setuptools wheel \ - && pip wheel --no-deps --cache-dir=/.cache/pip --wheel-dir /wheels -r requirements.txt - - -FROM python:3.10.8-slim-bullseye - -COPY --from=wheelbuilder /wheels /wheels -RUN python -m venv /venv \ - && . /venv/bin/activate \ - && python -m pip install --no-cache -U pip setuptools wheel \ - && pip install --no-cache /wheels/* - -ARG UID=5642 -ARG GID=5642 - -RUN ( getent group $GID >/dev/null || groupadd -r -g $GID localgroup ) \ - && useradd -m -l -s /bin/bash -g $GID -N -u $UID localuser \ - && chown -R $UID:$GID /venv - -WORKDIR /home/localuser -USER localuser - -COPY --chown=$UID:$GID dataprep_gc.py run_dataprep.sh ./ - -# This is basically what venv/bin/activate does -ENV PATH="/venv/bin:$PATH" -ENV VIRTUAL_ENV="/venv" - -CMD ["./run_dataprep.sh", "/input", "/output", "/input/cohort_data.csv"] diff --git a/sandbox/pneumonia-prediction/containers/data-prep/dataprep_gc.py b/sandbox/pneumonia-prediction/containers/data-prep/dataprep_gc.py deleted file mode 100644 index 3b9cd628..00000000 --- a/sandbox/pneumonia-prediction/containers/data-prep/dataprep_gc.py +++ /dev/null @@ -1,49 +0,0 @@ -import pandas as pd -import os -import pydicom -import numpy as np -from PIL import Image -from sklearn.impute import SimpleImputer -import glob - - -def convert_dcm_image_to_jpg(name): - dcm = pydicom.dcmread(name) - img = dcm.pixel_array.astype(float) - rescaled_image = (np.maximum(img, 0) / img.max()) * 255 # float pixels - final_image = np.uint8(rescaled_image) # integers pixels - final_image = Image.fromarray(final_image) - return final_image - - -def cohort_dcm_to_jpg(df_cohort): - input_dir = '/input/dicom_data/' - output_dir = '/output/file_data/' - dcm_list = glob.glob(input_dir + '/*/*.dcm') - - df_cohort['JPG_file'] = 'Nan' - for dcm_file in dcm_list: - image = convert_dcm_image_to_jpg(dcm_file) - jpg_file_name = dcm_file.split('/')[-1].split('.dcm')[0] + '.jpg' - ds = pydicom.dcmread(dcm_file) - idx = df_cohort['Pneumonia'][df_cohort.SeriesUID == ds.SeriesInstanceUID].index[0] - ground_truth = '1' if df_cohort.loc[idx, 'Pneumonia'] else '0' - class_folder = output_dir + ground_truth - if not os.path.exists(class_folder): - os.makedirs(class_folder) - image.save('/'.join([class_folder, jpg_file_name])) - df_cohort.loc[idx, 'JPG file'] = '/'.join([ground_truth, jpg_file_name]) - - return df_cohort - - -if __name__ == '__main__': - # Read cohort from /input - df_cohort = pd.read_csv('/input/cohort_data.csv') - - # Convert DICOM to JPG - df_cohort = cohort_dcm_to_jpg(df_cohort) - - # Write cohort to /output - df_cohort.to_csv('/output/cohort_data.csv', index=False) - diff --git a/sandbox/pneumonia-prediction/containers/data-prep/requirements.txt b/sandbox/pneumonia-prediction/containers/data-prep/requirements.txt deleted file mode 100644 index 8d3914ad..00000000 --- a/sandbox/pneumonia-prediction/containers/data-prep/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -pandas==1.3.4 -numpy==1.21.3 -sklearn==0.0 -sklearn-pandas==1.8.0 -scikit-learn==1.0.2 -pydicom==2.2.0 -Pillow==8.4.0 \ No newline at end of file diff --git a/sandbox/pneumonia-prediction/containers/data-prep/run_dataprep.sh b/sandbox/pneumonia-prediction/containers/data-prep/run_dataprep.sh deleted file mode 100755 index bfca65ab..00000000 --- a/sandbox/pneumonia-prediction/containers/data-prep/run_dataprep.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -if [ $# -ne 3 ]; then - >&2 echo "Usage: $0 input_dir output_dir csv_file" - exit 1 -fi - -INPUT_DIR=$1 -OUTPUT_DIR=$2 -CSV_FILE=$3 - -DIR=$(dirname "$(readlink -f "$BASH_SOURCE")") - -set -x -set -e - -python $DIR/dataprep_gc.py --input_csv ${CSV_FILE} --input_dir ${INPUT_DIR} --output_dir ${OUTPUT_DIR} diff --git a/sandbox/pneumonia-prediction/containers/merge-cohorts/Dockerfile b/sandbox/pneumonia-prediction/containers/merge-cohorts/Dockerfile deleted file mode 100644 index 3647ba52..00000000 --- a/sandbox/pneumonia-prediction/containers/merge-cohorts/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM python:3.10.8-slim-bullseye - -# Set up non-root user and group. -ARG UID=5642 -ARG GID=5642 -RUN ( getent group $GID >/dev/null || groupadd -r -g $GID localgroup ) \ - && useradd -m -l -s /bin/bash -g $GID -N -u $UID localuser - -# Create and "activate" venv. -ENV VIRTUAL_ENV="/venv" -RUN mkdir $VIRTUAL_ENV \ - && chmod g+s $VIRTUAL_ENV \ - && chown $UID:$GID $VIRTUAL_ENV \ - && python -m venv $VIRTUAL_ENV -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -# Install dependencies. -COPY requirements.txt ./ -RUN --mount=type=cache,target=/root/.cache/pip \ - python -m pip install --upgrade pip setuptools wheel \ - && pip install -r requirements.txt \ - && rm requirements.txt - -WORKDIR /home/localuser -USER localuser - -# Copy code. -COPY --chown=$UID:$GID merge_cohorts_data.py ./ - -ENV PYTHONUNBUFFERED=1 - -CMD [ "python", "./merge_cohorts_data.py", \ - "--input_dir", "/input", \ - "--output_dir", "/output", \ - "--cohort_csv_file", "/input/cohort_data.csv" \ -] diff --git a/sandbox/pneumonia-prediction/containers/merge-cohorts/README.md b/sandbox/pneumonia-prediction/containers/merge-cohorts/README.md deleted file mode 100644 index c32878eb..00000000 --- a/sandbox/pneumonia-prediction/containers/merge-cohorts/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Generalized Compute Example - Merge Cohorts Data -
- -### **Description** - -This example provides files that can be used with the Rhino Health Generalized Compute capability to remotely merge two input cohorts into a single output cohort on a Rhino Client. - -It shows how to: -* Process multiple input CSV files -* Merge the inputs into a single output CSV file -* Use a single-step Dockerfile to build the container image (without using a separate step for installing requirements) - -Please reference the User Documentation and/or Tutorials for in depth explanations on how to use the Generalized Compute capability. -

- -### **Resources** -- `Dockerfile` - This is the Dockerfile to be used for building the container image -- `merge_cohorts_data.py` - This file contains the python code for merging the input cohorts -- `requirements.in` - The input python requirements for this project -- `requirements.txt` - The compiled python requirements for this project (using `pip-compile` on the requirements.in file) -

- -# Getting Help -For additional support, please reach out to [support@rhinohealth.com](mailto:support@rhinohealth.com). diff --git a/sandbox/pneumonia-prediction/containers/merge-cohorts/merge_cohorts_data.py b/sandbox/pneumonia-prediction/containers/merge-cohorts/merge_cohorts_data.py deleted file mode 100755 index 5ea9a31f..00000000 --- a/sandbox/pneumonia-prediction/containers/merge-cohorts/merge_cohorts_data.py +++ /dev/null @@ -1,8 +0,0 @@ -import pandas as pd - -if __name__ == "__main__": - first = pd.read_csv("/input/0/cohort_data.csv") - second = pd.read_csv("/input/1/cohort_data.csv") - third = pd.read_csv("/input/2/cohort_data.csv") - merged = pd.concat([first, second, third], ignore_index=True, axis=0) - merged.to_csv("/output/0/cohort_data.csv", index=False) diff --git a/sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.in b/sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.in deleted file mode 100644 index fac8cba9..00000000 --- a/sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.in +++ /dev/null @@ -1 +0,0 @@ -pandas ~= 1.4.3 diff --git a/sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.txt b/sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.txt deleted file mode 100644 index 5e8709e8..00000000 --- a/sandbox/pneumonia-prediction/containers/merge-cohorts/requirements.txt +++ /dev/null @@ -1,65 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: -# -# pip-compile --generate-hashes --output-file=requirements.txt requirements.in -# -numpy==1.23.0 \ - --hash=sha256:092f5e6025813e64ad6d1b52b519165d08c730d099c114a9247c9bb635a2a450 \ - --hash=sha256:196cd074c3f97c4121601790955f915187736f9cf458d3ee1f1b46aff2b1ade0 \ - --hash=sha256:1c29b44905af288b3919803aceb6ec7fec77406d8b08aaa2e8b9e63d0fe2f160 \ - --hash=sha256:2b2da66582f3a69c8ce25ed7921dcd8010d05e59ac8d89d126a299be60421171 \ - --hash=sha256:5043bcd71fcc458dfb8a0fc5509bbc979da0131b9d08e3d5f50fb0bbb36f169a \ - --hash=sha256:58bfd40eb478f54ff7a5710dd61c8097e169bc36cc68333d00a9bcd8def53b38 \ - --hash=sha256:79a506cacf2be3a74ead5467aee97b81fca00c9c4c8b3ba16dbab488cd99ba10 \ - --hash=sha256:94b170b4fa0168cd6be4becf37cb5b127bd12a795123984385b8cd4aca9857e5 \ - --hash=sha256:97a76604d9b0e79f59baeca16593c711fddb44936e40310f78bfef79ee9a835f \ - --hash=sha256:98e8e0d8d69ff4d3fa63e6c61e8cfe2d03c29b16b58dbef1f9baa175bbed7860 \ - --hash=sha256:ac86f407873b952679f5f9e6c0612687e51547af0e14ddea1eedfcb22466babd \ - --hash=sha256:ae8adff4172692ce56233db04b7ce5792186f179c415c37d539c25de7298d25d \ - --hash=sha256:bd3fa4fe2e38533d5336e1272fc4e765cabbbde144309ccee8675509d5cd7b05 \ - --hash=sha256:d0d2094e8f4d760500394d77b383a1b06d3663e8892cdf5df3c592f55f3bff66 \ - --hash=sha256:d54b3b828d618a19779a84c3ad952e96e2c2311b16384e973e671aa5be1f6187 \ - --hash=sha256:d6ca8dabe696c2785d0c8c9b0d8a9b6e5fdbe4f922bde70d57fa1a2848134f95 \ - --hash=sha256:d8cc87bed09de55477dba9da370c1679bd534df9baa171dd01accbb09687dac3 \ - --hash=sha256:f0f18804df7370571fb65db9b98bf1378172bd4e962482b857e612d1fec0f53e \ - --hash=sha256:f1d88ef79e0a7fa631bb2c3dda1ea46b32b1fe614e10fedd611d3d5398447f2f \ - --hash=sha256:f9c3fc2adf67762c9fe1849c859942d23f8d3e0bee7b5ed3d4a9c3eeb50a2f07 \ - --hash=sha256:fc431493df245f3c627c0c05c2bd134535e7929dbe2e602b80e42bf52ff760bc \ - --hash=sha256:fe8b9683eb26d2c4d5db32cd29b38fdcf8381324ab48313b5b69088e0e355379 - # via pandas -pandas==1.4.3 \ - --hash=sha256:07238a58d7cbc8a004855ade7b75bbd22c0db4b0ffccc721556bab8a095515f6 \ - --hash=sha256:0daf876dba6c622154b2e6741f29e87161f844e64f84801554f879d27ba63c0d \ - --hash=sha256:16ad23db55efcc93fa878f7837267973b61ea85d244fc5ff0ccbcfa5638706c5 \ - --hash=sha256:1d9382f72a4f0e93909feece6fef5500e838ce1c355a581b3d8f259839f2ea76 \ - --hash=sha256:24ea75f47bbd5574675dae21d51779a4948715416413b30614c1e8b480909f81 \ - --hash=sha256:2893e923472a5e090c2d5e8db83e8f907364ec048572084c7d10ef93546be6d1 \ - --hash=sha256:2ff7788468e75917574f080cd4681b27e1a7bf36461fe968b49a87b5a54d007c \ - --hash=sha256:41fc406e374590a3d492325b889a2686b31e7a7780bec83db2512988550dadbf \ - --hash=sha256:48350592665ea3cbcd07efc8c12ff12d89be09cd47231c7925e3b8afada9d50d \ - --hash=sha256:605d572126eb4ab2eadf5c59d5d69f0608df2bf7bcad5c5880a47a20a0699e3e \ - --hash=sha256:6dfbf16b1ea4f4d0ee11084d9c026340514d1d30270eaa82a9f1297b6c8ecbf0 \ - --hash=sha256:6f803320c9da732cc79210d7e8cc5c8019aad512589c910c66529eb1b1818230 \ - --hash=sha256:721a3dd2f06ef942f83a819c0f3f6a648b2830b191a72bbe9451bcd49c3bd42e \ - --hash=sha256:755679c49460bd0d2f837ab99f0a26948e68fa0718b7e42afbabd074d945bf84 \ - --hash=sha256:78b00429161ccb0da252229bcda8010b445c4bf924e721265bec5a6e96a92e92 \ - --hash=sha256:958a0588149190c22cdebbc0797e01972950c927a11a900fe6c2296f207b1d6f \ - --hash=sha256:a3924692160e3d847e18702bb048dc38e0e13411d2b503fecb1adf0fcf950ba4 \ - --hash=sha256:d51674ed8e2551ef7773820ef5dab9322be0828629f2cbf8d1fc31a0c4fed640 \ - --hash=sha256:d5ebc990bd34f4ac3c73a2724c2dcc9ee7bf1ce6cf08e87bb25c6ad33507e318 \ - --hash=sha256:d6c0106415ff1a10c326c49bc5dd9ea8b9897a6ca0c8688eb9c30ddec49535ef \ - --hash=sha256:e48fbb64165cda451c06a0f9e4c7a16b534fcabd32546d531b3c240ce2844112 - # via -r requirements.in -python-dateutil==2.8.2 \ - --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ - --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 - # via pandas -pytz==2022.1 \ - --hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \ - --hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c - # via pandas -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via python-dateutil diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/Dockerfile b/sandbox/pneumonia-prediction/containers/prediction-model/Dockerfile deleted file mode 100644 index 48ca5461..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/Dockerfile +++ /dev/null @@ -1,51 +0,0 @@ -# !! EDIT THIS: Set base Docker image to be used. -FROM nvidia/cuda:11.3.1-cudnn8-runtime-ubuntu20.04 - -# Set env vars to be able to run apt-get commands without issues. -ARG LC_ALL="C.UTF-8" -ARG TZ=Etc/UTC -ENV DEBIAN_FRONTEND=noninteractive - -# Install Python 3.8 -RUN --mount=type=cache,id=apt,target=/var/cache/apt \ - rm -f /etc/apt/apt.conf.d/docker-clean \ - && apt-get update \ - && apt-get install -y -q software-properties-common \ - && add-apt-repository ppa:deadsnakes/ppa \ - && apt-get update \ - && apt-get install -y -q python3.8 python3.8-dev python3.8-venv \ - && apt-get remove -y --autoremove software-properties-common \ - && rm -rf /var/lib/apt/lists/* - -# Set up non-root user and group. -ARG UID=5642 -ARG GID=5642 -RUN ( getent group $GID >/dev/null || groupadd -r -g $GID localgroup ) \ - && useradd -m -l -s /bin/bash -g $GID -N -u $UID localuser - -# Create and "activate" venv. -ENV VIRTUAL_ENV="/venv" -RUN mkdir $VIRTUAL_ENV \ - && chmod g+s $VIRTUAL_ENV \ - && chown $UID:$GID $VIRTUAL_ENV \ - && python3.8 -m venv $VIRTUAL_ENV -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -# Install dependencies. -COPY requirements.txt ./ -RUN --mount=type=cache,target=/root/.cache/pip \ - python -m pip install --upgrade pip setuptools wheel \ - && pip install -r requirements.txt \ - && rm requirements.txt - -WORKDIR /home/localuser -USER localuser - -# !! EDIT THIS: Copy the needed local files (code and otherwise) into the container work directory. -# Note: For directories, have a separate COPY command for each top-level directory. -COPY --chown=$UID:$GID ./config ./config -COPY --chown=$UID:$GID ./custom ./custom -COPY --chown=$UID:$GID ./infer.py ./infer.py - -ENV PYTHONPATH="/home/localuser/custom" -ENV PYTHONUNBUFFERED=1 diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/README.md b/sandbox/pneumonia-prediction/containers/prediction-model/README.md deleted file mode 100644 index 7a0ac030..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# NVIDIA FLARE Example - MIMIC CXR for NVIDIA FLARE 2.2 -
- -### **Description** - -This example contains files to train a pneumonia detection model from Chest XRays using Rhino Health's Federated Computing Platform (FCP) and NVIDIA FLARE v2.2 - -It shows how to: -* Use PyTorch model code adapted to NVIDIA FLARE (NVFlare) v2.2, and apply the necessary changes for it to run on FCP -* Add an `infer.py` script to perform inference on the trained model -* Package the code in a Docker container that can be used with FCP - -Please reference the User Documentation and/or Tutorials for in depth explanations on how to use NVFlare on FCP -

- -### **Resources** -- `config` - This is the standard NVFlare directory for config files - - `config_fed_client.json` - The standard NVFlare federated client config, setting to 1 epoch for the example - - `config_fed_server.json` - The standard NVFlare federated server config, setting the output model weights file to be stored in `/output/model_parameters.pt` -- `custom` - This is the standard NVFlare directory for custom model code, containing the code for the pneumonia model (reading the input data from the `/input` folder in order to work with FCP) -- `infer.py` - A script for running inference on the trained model -- `Dockerfile` - This is the Dockerfile to be used for building the container image -- `requirements.txt` - The python requirements for this project -

- -# Getting Help -For additional support, please reach out to [support@rhinohealth.com](mailto:support@rhinohealth.com). diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_client.json b/sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_client.json deleted file mode 100644 index 9bd8a121..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_client.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "format_version": 2, - - "executors": [ - { - "tasks": ["train", "submit_model"], - "executor": { - "path": "pneumonia_trainer.PneumoniaTrainer", - "args": { - "lr": 0.01, - "epochs": 1 - } - } - } - ], - "task_result_filters": [ - ], - "task_data_filters": [ - ], - "components": [ - ] -} diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_server.json b/sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_server.json deleted file mode 100644 index 9539f4ea..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/config/config_fed_server.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "format_version": 2, - - "server": { - "heart_beat_timeout": 600 - }, - "task_data_filters": [], - "task_result_filters": [], - "components": [ - { - "id": "persistor", - "name": "PTFileModelPersistor", - "args": { - "model": { - "path": "network.PneumoniaModel" - }, - "global_model_file_name": "/output/model_parameters.pt" - } - }, - { - "id": "shareable_generator", - "path": "nvflare.app_common.shareablegenerators.full_model_shareable_generator.FullModelShareableGenerator", - "args": {} - }, - { - "id": "aggregator", - "path": "nvflare.app_common.aggregators.intime_accumulate_model_aggregator.InTimeAccumulateWeightedAggregator", - "args": { - "expected_data_kind": "WEIGHTS" - } - }, - { - "id": "model_locator", - "path": "pt_model_locator.PTModelLocator", - "args": { - - } - }, - { - "id": "json_generator", - "path": "validation_json_generator.ValidationJsonGenerator", - "args": { - } - } - ], - "workflows": [ - { - "id": "scatter_and_gather", - "name": "ScatterAndGather", - "args": { - "min_clients": 1, - "num_rounds": 1, - "start_round": 0, - "wait_time_after_min_received": 10, - "aggregator_id": "aggregator", - "persistor_id": "persistor", - "shareable_generator_id": "shareable_generator", - "train_task_name": "train", - "train_timeout": 0 - } - } - ] -} diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/custom/network.py b/sandbox/pneumonia-prediction/containers/prediction-model/custom/network.py deleted file mode 100644 index a1fbf15a..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/custom/network.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. -# -# 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.nn as nn - - -class PneumoniaModel(nn.Module): - def __init__(self, num_classes=2): - super().__init__() - - self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=3, stride=1, padding=1) - - self.bn1 = nn.BatchNorm2d(num_features=12) - self.relu1 = nn.ReLU() - self.pool = nn.MaxPool2d(kernel_size=2) - self.conv2 = nn.Conv2d(in_channels=12, out_channels=20, kernel_size=3, stride=1, padding=1) - self.relu2 = nn.ReLU() - self.conv3 = nn.Conv2d(in_channels=20, out_channels=32, kernel_size=3, stride=1, padding=1) - self.bn3 = nn.BatchNorm2d(num_features=32) - self.relu3 = nn.ReLU() - self.fc = nn.Linear(in_features=32 * 112 * 112, out_features=num_classes) - - def forward(self, input): - output = self.conv1(input) - output = self.bn1(output) - output = self.relu1(output) - output = self.pool(output) - output = self.conv2(output) - output = self.relu2(output) - output = self.conv3(output) - output = self.bn3(output) - output = self.relu3(output) - output = output.view(-1, 32 * 112 * 112) - output = self.fc(output) - - return output diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/custom/pneumonia_trainer.py b/sandbox/pneumonia-prediction/containers/prediction-model/custom/pneumonia_trainer.py deleted file mode 100644 index 9f4a73a9..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/custom/pneumonia_trainer.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (c) 2023, Rhino HealthTech, Inc. -# Original file modified by Rhino Health to adapt it to the Rhino Health Federated Computing Platform. - -# Copyright (c) 2021, NVIDIA CORPORATION. -# -# 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.path -import os - -import torch -from torch import nn -from torch.optim import Adam -from torch.utils.data.dataloader import DataLoader -from torchvision.transforms import ToTensor, Normalize, Compose, Resize, RandomRotation, CenterCrop -import torchvision - -from nvflare.apis.dxo import from_shareable, DXO, DataKind, MetaKey -from nvflare.apis.executor import Executor -from nvflare.apis.fl_constant import ReturnCode, ReservedKey -from nvflare.apis.fl_context import FLContext -from nvflare.apis.shareable import Shareable, make_reply -from nvflare.apis.signal import Signal -from nvflare.app_common.abstract.model import make_model_learnable, model_learnable_to_dxo -from nvflare.app_common.app_constant import AppConstants -from nvflare.app_common.pt.pt_fed_utils import PTModelPersistenceFormatManager -from pt_constants import PTConstants -from network import PneumoniaModel - - -class PneumoniaTrainer(Executor): - - def __init__(self, lr=0.01, epochs=5, train_task_name=AppConstants.TASK_TRAIN, - submit_model_task_name=AppConstants.TASK_SUBMIT_MODEL, exclude_vars=None): - - """ - Args: - lr (float, optional): Learning rate. Defaults to 0.01 - epochs (int, optional): Epochs. Defaults to 5 - train_task_name (str, optional): Task name for train task. Defaults to "train". - submit_model_task_name (str, optional): Task name for submit model. Defaults to "submit_model". - exclude_vars (list): List of variables to exclude during model loading. - """ - super().__init__() - - self._lr = lr - self._epochs = epochs - self._train_task_name = train_task_name - self._submit_model_task_name = submit_model_task_name - self._exclude_vars = exclude_vars - - # Training setup - self.model = PneumoniaModel() - self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - self.model.to(self.device) - self.loss = nn.CrossEntropyLoss() - self.optimizer = Adam(self.model.parameters(), lr=lr) - - # Create mimic-cxr dataset for training. - transforms = Compose([ - Resize(size=(256, 256)), - RandomRotation(degrees=(-20, +20)), - CenterCrop(size=224), - ToTensor(), - Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) - ]) - - cohort_uid = next(os.walk('/input/cohorts'))[1][0] - self._train_dataset = torchvision.datasets.ImageFolder(root='/input/cohorts/'+cohort_uid+'/file_data', - transform=transforms) - - self._train_loader = DataLoader(self._train_dataset, batch_size=4, shuffle=True) - - self._n_iterations = len(self._train_loader) - - # Setup the persistence manager to save PT model. - # The default training configuration is used by persistence manager - # in case no initial model is found. - self._default_train_conf = {"train": {"model": type(self.model).__name__}} - self.persistence_manager = PTModelPersistenceFormatManager( - data=self.model.state_dict(), default_train_conf=self._default_train_conf) - - def local_train(self, fl_ctx, weights, abort_signal): - # Set the model weights - self.model.load_state_dict(state_dict=weights) - - # Basic training - self.model.train() - for epoch in range(self._epochs): - running_loss = 0.0 - for i, batch in enumerate(self._train_loader): - if abort_signal.triggered: - # If abort_signal is triggered, we simply return. - # The outside function will check it again and decide steps to take. - return - - images, labels = batch[0].to(self.device), batch[1].to(self.device) - self.optimizer.zero_grad() - - predictions = self.model(images) - cost = self.loss(predictions, labels) - cost.backward() - self.optimizer.step() - - running_loss += (cost.cpu().detach().numpy()/images.size()[0]) - if i % 3000 == 0: - self.log_info(fl_ctx, f"Epoch: {epoch}/{self._epochs}, Iteration: {i}, " - f"Loss: {running_loss/3000}") - running_loss = 0.0 - - def execute(self, task_name: str, shareable: Shareable, fl_ctx: FLContext, abort_signal: Signal) -> Shareable: - try: - if task_name == self._train_task_name: - # Get model weights - try: - dxo = from_shareable(shareable) - except Exception: - self.log_error(fl_ctx, "Unable to extract dxo from shareable.") - return make_reply(ReturnCode.BAD_TASK_DATA) - - # Ensure data_files kind is weights. - if not dxo.data_kind == DataKind.WEIGHTS: - self.log_error(fl_ctx, f"data_kind expected WEIGHTS but got {dxo.data_kind} instead.") - return make_reply(ReturnCode.BAD_TASK_DATA) - - # Convert weights to tensor. Run training - torch_weights = {k: torch.as_tensor(v) for k, v in dxo.data.items()} - self.local_train(fl_ctx, torch_weights, abort_signal) - - # Check the abort_signal after training. - # local_train returns early if abort_signal is triggered. - if abort_signal.triggered: - return make_reply(ReturnCode.TASK_ABORTED) - - # Save the local model after training. - self.save_local_model(fl_ctx) - - # Get the new state dict and send as weights - new_weights = self.model.state_dict() - new_weights = {k: v.cpu().numpy() for k, v in new_weights.items()} - - outgoing_dxo = DXO(data_kind=DataKind.WEIGHTS, data=new_weights, - meta={MetaKey.NUM_STEPS_CURRENT_ROUND: self._n_iterations}) - return outgoing_dxo.to_shareable() - elif task_name == self._submit_model_task_name: - # Load local model - ml = self.load_local_model(fl_ctx) - - # Get the model parameters and create dxo from it - dxo = model_learnable_to_dxo(ml) - return dxo.to_shareable() - else: - return make_reply(ReturnCode.TASK_UNKNOWN) - except Exception: - self.log_exception(fl_ctx, f"Exception in simple trainer.") - return make_reply(ReturnCode.EXECUTION_EXCEPTION) - - def save_local_model(self, fl_ctx: FLContext): - run_dir = fl_ctx.get_engine().get_workspace().get_run_dir(fl_ctx.get_prop(ReservedKey.RUN_NUM)) - models_dir = os.path.join(run_dir, PTConstants.PTModelsDir) - if not os.path.exists(models_dir): - os.makedirs(models_dir) - model_path = os.path.join(models_dir, PTConstants.PTLocalModelName) - - ml = make_model_learnable(self.model.state_dict(), {}) - self.persistence_manager.update(ml) - torch.save(self.persistence_manager.to_persistence_dict(), model_path) - - def load_local_model(self, fl_ctx: FLContext): - run_dir = fl_ctx.get_engine().get_workspace().get_run_dir(fl_ctx.get_prop(ReservedKey.RUN_NUM)) - models_dir = os.path.join(run_dir, PTConstants.PTModelsDir) - if not os.path.exists(models_dir): - return None - model_path = os.path.join(models_dir, PTConstants.PTLocalModelName) - - self.persistence_manager = PTModelPersistenceFormatManager(data=torch.load(model_path), - default_train_conf=self._default_train_conf) - ml = self.persistence_manager.to_model_learnable(exclude_vars=self._exclude_vars) - return ml diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_constants.py b/sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_constants.py deleted file mode 100644 index 0b1c46fa..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_constants.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. -# -# 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. - -class PTConstants: - PTServerName = "server" - PTFileModelName = "model_parameters.pt" - PTLocalModelName = "local_model.pt" - - PTModelsDir = "models" - CrossValResultsJsonFilename = "cross_val_results.json" diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_model_locator.py b/sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_model_locator.py deleted file mode 100644 index 54610cc6..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/custom/pt_model_locator.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. -# -# 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 -from typing import List, Union - -import torch.cuda - -from nvflare.apis.dxo import DXO -from nvflare.apis.fl_context import FLContext -from nvflare.app_common.abstract.model import model_learnable_to_dxo -from nvflare.app_common.abstract.model_locator import ModelLocator -from nvflare.app_common.pt.pt_fed_utils import PTModelPersistenceFormatManager -from pt_constants import PTConstants -from network import PneumoniaModel - - -class PTModelLocator(ModelLocator): - - def __init__(self, exclude_vars=None, model=None): - super(PTModelLocator, self).__init__() - - self.model = PneumoniaModel() - self.exclude_vars = exclude_vars - - def get_model_names(self, fl_ctx: FLContext) -> List[str]: - return [PTConstants.PTServerName] - - def locate_model(self, model_name, fl_ctx: FLContext) -> Union[DXO, None]: - if model_name == PTConstants.PTServerName: - try: - server_run_dir = fl_ctx.get_engine().get_workspace().get_app_dir(fl_ctx.get_run_number()) - model_path = os.path.join(server_run_dir, PTConstants.PTFileModelName) - if not os.path.exists(model_path): - return None - - # Load the torch model - device = "cuda" if torch.cuda.is_available() else "cpu" - data = torch.load(model_path, map_location=device) - - # Setup the persistence manager. - if self.model: - default_train_conf = {"train": {"model": type(self.model).__name__}} - else: - default_train_conf = None - - # Use persistence manager to get learnable - persistence_manager = PTModelPersistenceFormatManager(data, default_train_conf=default_train_conf) - ml = persistence_manager.to_model_learnable(exclude_vars=None) - - # Create dxo and return - return model_learnable_to_dxo(ml) - except: - self.log_error(fl_ctx, "Error in retrieving {model_name}.", fire_event=False) - return None - else: - self.log_error(fl_ctx, f"PTModelLocator doesn't recognize name: {model_name}", fire_event=False) - return None diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/custom/validation_json_generator.py b/sandbox/pneumonia-prediction/containers/prediction-model/custom/validation_json_generator.py deleted file mode 100644 index cfd5aebe..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/custom/validation_json_generator.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. -# -# 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 json -import os.path - -from nvflare.apis.dxo import DataKind, from_shareable -from nvflare.apis.event_type import EventType -from nvflare.apis.fl_context import FLContext -from nvflare.app_common.app_constant import AppConstants -from nvflare.app_common.app_event_type import AppEventType -from nvflare.widgets.widget import Widget - - -class ValidationJsonGenerator(Widget): - def __init__(self, results_dir=AppConstants.CROSS_VAL_DIR, json_file_name="cross_val_results.json"): - """Catches VALIDATION_RESULT_RECEIVED event and generates a results.json containing accuracy of each - validated model. - - Args: - results_dir (str, optional): Name of the results directory. Defaults to cross_site_val - json_file_name (str, optional): Name of the json file. Defaults to cross_val_results.json - """ - super(ValidationJsonGenerator, self).__init__() - - self._results_dir = results_dir - self._val_results = {} - self._json_file_name = json_file_name - - def handle_event(self, event_type: str, fl_ctx: FLContext): - if event_type == EventType.START_RUN: - self._val_results.clear() - elif event_type == AppEventType.VALIDATION_RESULT_RECEIVED: - model_owner = fl_ctx.get_prop(AppConstants.MODEL_OWNER, None) - data_client = fl_ctx.get_prop(AppConstants.DATA_CLIENT, None) - val_results = fl_ctx.get_prop(AppConstants.VALIDATION_RESULT, None) - - if not model_owner: - self.log_error( - fl_ctx, "model_owner unknown. Validation result will not be saved to json", fire_event=False - ) - if not data_client: - self.log_error( - fl_ctx, "data_client unknown. Validation result will not be saved to json", fire_event=False - ) - - if val_results: - try: - dxo = from_shareable(val_results) - dxo.validate() - - if dxo.data_kind == DataKind.METRICS: - if data_client not in self._val_results: - self._val_results[data_client] = {} - self._val_results[data_client][model_owner] = dxo.data - else: - self.log_error( - fl_ctx, f"Expected dxo of kind METRICS but got {dxo.data_kind} instead.", fire_event=False - ) - except Exception: - self.log_exception(fl_ctx, "Exception in handling validation result.", fire_event=False) - else: - self.log_error(fl_ctx, "Validation result not found.", fire_event=False) - elif event_type == EventType.END_RUN: - run_dir = fl_ctx.get_engine().get_workspace().get_run_dir(fl_ctx.get_job_id()) - cross_val_res_dir = os.path.join(run_dir, self._results_dir) - if not os.path.exists(cross_val_res_dir): - os.makedirs(cross_val_res_dir) - - res_file_path = os.path.join(cross_val_res_dir, self._json_file_name) - with open(res_file_path, "w") as f: - json.dump(self._val_results, f) diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/infer.py b/sandbox/pneumonia-prediction/containers/prediction-model/infer.py deleted file mode 100644 index 440e0e06..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/infer.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -import sys - -import pandas as pd -import torch -import torchvision -from torch.utils.data.dataloader import DataLoader -from torchvision.transforms import ToTensor, Normalize, Compose, Resize, RandomRotation, CenterCrop - -from network import PneumoniaModel - - -def infer(model_weights_file_path): - # Setup the model - model = PneumoniaModel() - model.load_state_dict(torch.load(model_weights_file_path)["model"]) - model.eval() - device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") - model.to(device) - - # Preparing the dataset for testing. - transforms = Compose([ - Resize(size=(256, 256)), - RandomRotation(degrees=(-20, +20)), - CenterCrop(size=224), - ToTensor(), - Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) - ]) - tabular_data = pd.read_csv("/input/cohort_data.csv") - dataset = torchvision.datasets.ImageFolder(root="/input/file_data", transform=transforms) - loader = DataLoader(dataset, batch_size=4, shuffle=False) - - # Inference: Apply model and add scores column. - scores = [] - with torch.no_grad(): - for i, (images, labels) in enumerate(loader): - images = images.to(device) - output = model(images) - batch_scores = torch.select(output, 1, 1) - scores.extend([score.item() for score in batch_scores]) - tabular_data['Model_Score'] = scores - - tabular_data.to_csv("/output/cohort_data.csv", index=False) - - -if __name__ == "__main__": - args = sys.argv[1:] - (model_weights_file_path,) = args - infer(model_weights_file_path) - sys.exit(0) diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/requirements.txt b/sandbox/pneumonia-prediction/containers/prediction-model/requirements.txt deleted file mode 100644 index f11fd31b..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -nvflare==2.2.3 -pandas==1.4.1 -torch>=1.10,<1.11 -torchvision>=0.11.1,<0.12 diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/README.md b/sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/README.md deleted file mode 100644 index e333f28a..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/README.md +++ /dev/null @@ -1,38 +0,0 @@ -**How to Transform a Centralized Training Model to NVFLARE** - -* Begin with a centralized model, for example: https://www.kaggle.com/code/fahadmehfoooz/pneumonia-classification-using-pytorch/notebook -* Follow these steps: - 1. Custom folder: - Create your network.py, trainer.py, and copy relevant functions to your trainer.py - 2. Config folder: - Create config_fed_client.json and config_fed_server.json - 3. infer.py (optional) - Create your model validation code (to be run using Generalized Compute) - 4. Try everything out with the docker_run.sh utility - 5. Push your container to your ECR repo with the docker_push.sh utility - 6. Run training and validation via the Rhino Health Platform in the UI or SDK -* Example: Pneumonia Classification Model: - * Source: source_centralized_model/pneumonia_classification.py - * Converted to: custom/[network.py + pneumonia_trainer.py (+pt_constants.py)] + infer.py - - **Training** - - |Step| Source | NVFLARE| - |---|:---:|---:| - |data transform|lines 9-30|pneumonia_trainer.py: lines 66-73| - |load data|lines 33-52|pneumonia_trainer.py: lines 75-82| - |define model|lines 54-85|network.py| - |model params|lines 91-99|pneumonia_trainer.py: lines 59-64| - |training|lines 101-128|pneumonia_trainer.py: lines 90-116| - |NVFLARE wrapper|___|pneumonia_trainer.py: lines 118-186| - - **Inference** - - |Step| Source | NVFLARE| - |---|:---:|---:| - |data transform|lines 9-30|infer.py: lines 24-30| - |load data|lines 33-52|infer.py: lines 31-33| - |define model|lines 54-85|network.py| - |infer|lines 130-146|infer.py: lines 35-43| - |write cohort|___|infer.py: lines 47-55| - diff --git a/sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/pneumonia_classification.py b/sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/pneumonia_classification.py deleted file mode 100644 index 11b3ea94..00000000 --- a/sandbox/pneumonia-prediction/containers/prediction-model/source_centralized_model/pneumonia_classification.py +++ /dev/null @@ -1,149 +0,0 @@ -import torch -import os -from torchvision import transforms as T, datasets, models -from torch.utils.data import DataLoader -from torch import nn, optim -from torch.autograd import Variable - - -def data_transforms(phase=None): - if phase == TRAIN: - - data_T = T.Compose([ - - T.Resize(size=(256, 256)), - T.RandomRotation(degrees=(-20, +20)), - T.CenterCrop(size=224), - T.ToTensor(), - T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) - ]) - - elif phase == TEST: - - data_T = T.Compose([ - - T.Resize(size=(224, 224)), - T.ToTensor(), - T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) - ]) - - return data_T - - -data_dir = "../input/chest-xray-pneumonia/chest_xray/chest_xray" -TEST = 'test' -TRAIN = 'train' - -trainset = datasets.ImageFolder(os.path.join(data_dir, TRAIN), transform=data_transforms(TRAIN)) -testset = datasets.ImageFolder(os.path.join(data_dir, TEST), transform=data_transforms(TEST)) - -class_names = trainset.classes -print(class_names) -print(trainset.class_to_idx) - -trainloader = DataLoader(trainset, batch_size=64, shuffle=True) -testloader = DataLoader(testset, batch_size=64, shuffle=True) - -images, labels = iter(trainloader).next() - -for i, (images, labels) in enumerate(trainloader): - if torch.cuda.is_available(): - images = Variable(images.cuda()) - labels = Variable(labels.cuda()) - -class classify(nn.Module): - def __init__(self, num_classes=2): - super(classify, self).__init__() - - self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=3, stride=1, padding=1) - - self.bn1 = nn.BatchNorm2d(num_features=12) - self.relu1 = nn.ReLU() - self.pool = nn.MaxPool2d(kernel_size=2) - self.conv2 = nn.Conv2d(in_channels=12, out_channels=20, kernel_size=3, stride=1, padding=1) - self.relu2 = nn.ReLU() - self.conv3 = nn.Conv2d(in_channels=20, out_channels=32, kernel_size=3, stride=1, padding=1) - self.bn3 = nn.BatchNorm2d(num_features=32) - self.relu3 = nn.ReLU() - self.fc = nn.Linear(in_features=32 * 112 * 112, out_features=num_classes) - - # Feed forward function - - def forward(self, input): - output = self.conv1(input) - output = self.bn1(output) - output = self.relu1(output) - output = self.pool(output) - output = self.conv2(output) - output = self.relu2(output) - output = self.conv3(output) - output = self.bn3(output) - output = self.relu3(output) - output = output.view(-1, 32 * 112 * 112) - output = self.fc(output) - - return output - - -device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') - - -model = classify() -# defining the optimizer -optimizer = optim.Adam(model.parameters(), lr=0.01) -# defining the loss function -criterion = nn.CrossEntropyLoss() -# checking if GPU is available -if torch.cuda.is_available(): - model = model.cuda() - criterion = criterion.cuda() - -Losses = [] -for i in range(4): - running_loss = 0 - for images, labels in trainloader: - - # Changing images to cuda for gpu - if torch.cuda.is_available(): - images = images.cuda() - labels = labels.cuda() - - # Training pass - # Sets the gradient to zero - optimizer.zero_grad() - - output = model(images) - loss = criterion(output, labels) - - # This is where the model learns by backpropagating - # accumulates the loss for mini batch - loss.backward() - - # And optimizes its weights here - optimizer.step() - Losses.append(loss) - - running_loss += loss.item() - else: - print("Epoch {} - Training loss: {}".format(i + 1, running_loss / len(trainloader))) - -correct_count, all_count = 0, 0 -for images, labels in testloader: - for i in range(len(labels)): - if torch.cuda.is_available(): - images = images.cuda() - labels = labels.cuda() - img = images[i].view(1, 3, 224, 224) - with torch.no_grad(): - logps = model(img) - - ps = torch.exp(logps) - probab = list(ps.cpu()[0]) - pred_label = probab.index(max(probab)) - true_label = labels.cpu()[i] - if (true_label == pred_label): - correct_count += 1 - all_count += 1 - -print("Number Of Images Tested =", all_count) -print("\nModel Accuracy =", (correct_count / all_count)) diff --git a/sandbox/pneumonia-prediction/containers/train-test-split/Dockerfile b/sandbox/pneumonia-prediction/containers/train-test-split/Dockerfile deleted file mode 100644 index cf6f03e0..00000000 --- a/sandbox/pneumonia-prediction/containers/train-test-split/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM python:3.10.8-bullseye as wheelbuilder - -COPY requirements.txt ./ - -RUN --mount=type=cache,target=/.cache/pip \ - python -m pip install -U pip setuptools wheel \ - && pip wheel --no-deps --cache-dir=/.cache/pip --wheel-dir /wheels -r requirements.txt - - -FROM python:3.10.8-slim-bullseye - -COPY --from=wheelbuilder /wheels /wheels -RUN python -m venv /venv \ - && . /venv/bin/activate \ - && python -m pip install --no-cache -U pip setuptools wheel \ - && pip install --no-cache /wheels/* - -ARG UID=5642 -ARG GID=5642 - -RUN ( getent group $GID >/dev/null || groupadd -r -g $GID localgroup ) \ - && useradd -m -l -s /bin/bash -g $GID -N -u $UID localuser \ - && chown -R $UID:$GID /venv - -WORKDIR /home/localuser -USER localuser - -COPY --chown=$UID:$GID train_test_split.py run_code.sh ./ - -# This is basically what venv/bin/activate does -ENV PATH="/venv/bin:$PATH" -ENV VIRTUAL_ENV="/venv" - -CMD ["./run_code.sh"] diff --git a/sandbox/pneumonia-prediction/containers/train-test-split/README.md b/sandbox/pneumonia-prediction/containers/train-test-split/README.md deleted file mode 100644 index 37888a60..00000000 --- a/sandbox/pneumonia-prediction/containers/train-test-split/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Generalized Compute Example - Train Test Split -
- -### **Description** - -This example provides files that can be used with the Rhino Health Generalized Compute capability to remotely split an input cohort into two output cohorts on a Rhino Client. - -It shows how to: -* Process an input CSV file as a dataframe -* Create multiple output CSV files from this input -* Use a multi-step Dockerfile to build the container image (using a separate step for installing requirements) - -Please reference the User Documentation and/or Tutorials for in depth explanations on how to use the Generalized Compute capability. -

- -### **Resources** -- `Dockerfile` - This is the Dockerfile to be used for building the container image -- `train_test_split.py` - This file contains the python code for splitting input cohort (using sklearn.model_selection.train_test_split) -- `run_code.sh` - The entrypoint shell script for the docker container, which runs train_test_split.py -- `requirements.txt` - The python requirements for this project -

- -# Getting Help -For additional support, please reach out to [support@rhinohealth.com](mailto:support@rhinohealth.com). diff --git a/sandbox/pneumonia-prediction/containers/train-test-split/requirements.txt b/sandbox/pneumonia-prediction/containers/train-test-split/requirements.txt deleted file mode 100644 index 11586ace..00000000 --- a/sandbox/pneumonia-prediction/containers/train-test-split/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pandas==1.3.4 -sklearn==0.0 -sklearn-pandas==1.8.0 -scikit-learn==1.0.2 diff --git a/sandbox/pneumonia-prediction/containers/train-test-split/run_code.sh b/sandbox/pneumonia-prediction/containers/train-test-split/run_code.sh deleted file mode 100755 index 42d46d9f..00000000 --- a/sandbox/pneumonia-prediction/containers/train-test-split/run_code.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -DIR=$(dirname "$(readlink -f "$BASH_SOURCE")") - -set -x -set -e - -python $DIR/train_test_split_gc.py diff --git a/sandbox/pneumonia-prediction/containers/train-test-split/train_test_split.py b/sandbox/pneumonia-prediction/containers/train-test-split/train_test_split.py deleted file mode 100644 index 3f641a15..00000000 --- a/sandbox/pneumonia-prediction/containers/train-test-split/train_test_split.py +++ /dev/null @@ -1,15 +0,0 @@ -import pandas as pd -from sklearn.model_selection import train_test_split - - -if __name__ == '__main__': - # Read cohort from /input - df_cohort = pd.read_csv('/input/0/cohort_data.csv') - - # Split to train and test sets - df_train, df_test = train_test_split(df_cohort, test_size=0.33) - - # Write cohorts to /output - df_train.to_csv('/output/0/cohort_data.csv', index=False) - df_test.to_csv('/output/1/cohort_data.csv', index=False) - diff --git a/sandbox/site-testing-financial/README.md b/validation/site-testing-financial/README.md similarity index 100% rename from sandbox/site-testing-financial/README.md rename to validation/site-testing-financial/README.md diff --git a/sandbox/site-testing-financial/data/credit_risk_dataset.csv b/validation/site-testing-financial/data/credit_risk_dataset.csv similarity index 100% rename from sandbox/site-testing-financial/data/credit_risk_dataset.csv rename to validation/site-testing-financial/data/credit_risk_dataset.csv diff --git a/sandbox/site-testing-financial/model/app/config/config_fed_client.conf b/validation/site-testing-financial/model/app/config/config_fed_client.conf similarity index 100% rename from sandbox/site-testing-financial/model/app/config/config_fed_client.conf rename to validation/site-testing-financial/model/app/config/config_fed_client.conf diff --git a/sandbox/site-testing-financial/model/app/config/config_fed_server.conf b/validation/site-testing-financial/model/app/config/config_fed_server.conf similarity index 100% rename from sandbox/site-testing-financial/model/app/config/config_fed_server.conf rename to validation/site-testing-financial/model/app/config/config_fed_server.conf diff --git a/sandbox/site-testing-financial/model/app/custom/fl_config.py b/validation/site-testing-financial/model/app/custom/fl_config.py similarity index 100% rename from sandbox/site-testing-financial/model/app/custom/fl_config.py rename to validation/site-testing-financial/model/app/custom/fl_config.py diff --git a/sandbox/site-testing-financial/model/app/custom/xgboost_fl.py b/validation/site-testing-financial/model/app/custom/xgboost_fl.py similarity index 100% rename from sandbox/site-testing-financial/model/app/custom/xgboost_fl.py rename to validation/site-testing-financial/model/app/custom/xgboost_fl.py diff --git a/sandbox/site-testing-financial/model/meta.conf b/validation/site-testing-financial/model/meta.conf similarity index 100% rename from sandbox/site-testing-financial/model/meta.conf rename to validation/site-testing-financial/model/meta.conf diff --git a/sandbox/site-testing-financial/site-testing.ipynb b/validation/site-testing-financial/site-testing.ipynb similarity index 100% rename from sandbox/site-testing-financial/site-testing.ipynb rename to validation/site-testing-financial/site-testing.ipynb diff --git a/sandbox/site-testing-life-science/README.md b/validation/site-testing-life-science/README.md similarity index 100% rename from sandbox/site-testing-life-science/README.md rename to validation/site-testing-life-science/README.md diff --git a/sandbox/site-testing-life-science/data/cyp3a4_all_no_test.csv b/validation/site-testing-life-science/data/cyp3a4_all_no_test.csv similarity index 100% rename from sandbox/site-testing-life-science/data/cyp3a4_all_no_test.csv rename to validation/site-testing-life-science/data/cyp3a4_all_no_test.csv diff --git a/sandbox/site-testing-life-science/model/app/config/config_fed_client.json b/validation/site-testing-life-science/model/app/config/config_fed_client.json similarity index 100% rename from sandbox/site-testing-life-science/model/app/config/config_fed_client.json rename to validation/site-testing-life-science/model/app/config/config_fed_client.json diff --git a/sandbox/site-testing-life-science/model/app/config/config_fed_server.json b/validation/site-testing-life-science/model/app/config/config_fed_server.json similarity index 100% rename from sandbox/site-testing-life-science/model/app/config/config_fed_server.json rename to validation/site-testing-life-science/model/app/config/config_fed_server.json diff --git a/sandbox/site-testing-life-science/model/app/custom/chemprop_fl_regression.py b/validation/site-testing-life-science/model/app/custom/chemprop_fl_regression.py similarity index 100% rename from sandbox/site-testing-life-science/model/app/custom/chemprop_fl_regression.py rename to validation/site-testing-life-science/model/app/custom/chemprop_fl_regression.py diff --git a/sandbox/site-testing-life-science/model/infer.py b/validation/site-testing-life-science/model/infer.py similarity index 100% rename from sandbox/site-testing-life-science/model/infer.py rename to validation/site-testing-life-science/model/infer.py diff --git a/sandbox/site-testing-life-science/model/meta.json b/validation/site-testing-life-science/model/meta.json similarity index 100% rename from sandbox/site-testing-life-science/model/meta.json rename to validation/site-testing-life-science/model/meta.json diff --git a/sandbox/site-testing-life-science/site-testing.ipynb b/validation/site-testing-life-science/site-testing.ipynb similarity index 100% rename from sandbox/site-testing-life-science/site-testing.ipynb rename to validation/site-testing-life-science/site-testing.ipynb