{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "#!/usr/bin/env python\n", "# -*- coding: utf-8 -*-\n", "# Python version: 3.6\n", "\n", "\n", "import os\n", "import copy\n", "import time\n", "import pickle\n", "import numpy as np\n", "from tqdm import tqdm\n", "\n", "import torch\n", "from tensorboardX import SummaryWriter\n", "\n", "from options import args_parser\n", "from update import LocalUpdate, test_inference\n", "from models import MLP, CNNMnist, CNNFashion_Mnist, CNNCifar\n", "from utils import get_dataset, average_weights, exp_details" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Namespace(dataset='mnist', epochs=5, frac=0.1, gpu=None, iid=1, local_bs=10, local_ep=3, lr=0.01, model='mlp', num_classes=10, num_clusters=2, num_users=100, optimizer='sgd', unequal=0, verbose=1)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import argparse\n", "\n", "parser = argparse.ArgumentParser()\n", "# parser.add_argument('--name', '-n', default='foo', help='foo')\n", "parser.add_argument('--model', type=str, default='mlp', help='model name(mlp or cnn)')\n", "parser.add_argument('--dataset', type=str, default='mnist', help=\"name of dataset(mnist or cifar)\")\n", "parser.add_argument('--epochs', type=int, default=5, help=\"number of rounds of training(10)\")\n", "parser.add_argument('--num_users', type=int, default=100, help=\"number of users: K\")\n", "parser.add_argument('--num_classes', type=int, default=10, help=\"number of classes\")\n", "parser.add_argument('--gpu', default=None, help=\"To use cuda, set to a specific GPU ID. Default set to use CPU.\")\n", "parser.add_argument('--frac', type=float, default=0.1, help='the fraction of clients: C')\n", "parser.add_argument('--local_ep', type=int, default=3, help=\"the number of local epochs: E\")\n", "parser.add_argument('--local_bs', type=int, default=10, help=\"local batch size: B\")\n", "parser.add_argument('--lr', type=float, default=0.01, help='learning rate')\n", "parser.add_argument('--optimizer', type=str, default='sgd', help=\"type of optimizer\")\n", "parser.add_argument('--verbose', type=int, default=1, help='verbose')\n", "parser.add_argument('--iid', type=int, default=1, help='Default set to IID. Set to 0 for non-IID.')\n", "parser.add_argument('--unequal', type=int, default=0, help='whether to use unequal data splits for \\\n", " non-i.i.d setting (use 0 for equal splits)')\n", "# parser.add_argument('--seed', type=int, default=1, help='random seed')\n", "\n", "parser.add_argument('--num_clusters', type=int, default=2, help='verbose')\n", "\n", "\n", "# args = parser.parse_args([])\n", "args, _ = parser.parse_known_args()\n", "\n", "args\n", "# --dataset=mnist --gpu=0 --iid=0 --epochs=10" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "start time: 1571373590.742761\n" ] } ], "source": [ "# if __name__ == '__main__':\n", "start_time = time.time()\n", "print(\"start time: \", start_time)\n", "\n", "# define paths\n", "path_project = os.path.abspath('..')\n", "logger = SummaryWriter('../logs')\n", "\n", "# args = args_parser()\n", "# exp_details(args)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "if args.gpu:\n", " torch.cuda.set_device(args.gpu)\n", "device = 'cuda' if args.gpu else 'cpu'" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# load dataset and user groups\n", "train_dataset, test_dataset, user_groups = get_dataset(args)" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "keys = user_groups.keys()\n", "keys" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "raw", "metadata": {}, "source": [ "l = np.arange(5,6)\n", "l\n", "{k:user_groups[k] for k in l if k in user_groups}" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "np.arange(cluster_size, dtype=int)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Each cluster size: 50.0\n", "Size of cluster 1: 50\n", "Size of cluster 2: 50\n" ] } ], "source": [ "# Splitting into clusters. FL groups\n", "cluster_size = args.num_users / args.num_clusters\n", "print(\"Each cluster size: \", cluster_size)\n", "\n", "# Cluster 1\n", "A1 = np.arange(cluster_size, dtype=int)\n", "user_groupsA = {k:user_groups[k] for k in A1 if k in user_groups}\n", "print(\"Size of cluster 1: \", len(user_groupsA))\n", "# Cluster 2\n", "B1 = np.arange(cluster_size, cluster_size+cluster_size, dtype=int)\n", "user_groupsB = {k:user_groups[k] for k in B1 if k in user_groups}\n", "print(\"Size of cluster 2: \", len(user_groupsB))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "dict_keys([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49])\n", "dict_keys([50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99])\n" ] } ], "source": [ "# Check that clusters are all different\n", "print(user_groupsA.keys())\n", "print(user_groupsB.keys())" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# BUILD MODEL\n", "def build_model(args, train_dataset):\n", " if args.model == 'cnn':\n", " # Convolutional neural network\n", " if args.dataset == 'mnist':\n", " global_model = CNNMnist(args=args)\n", " elif args.dataset == 'fmnist':\n", " global_model = CNNFashion_Mnist(args=args)\n", " elif args.dataset == 'cifar':\n", " global_model = CNNCifar(args=args)\n", "\n", " elif args.model == 'mlp':\n", " # Multi-layer preceptron\n", " img_size = train_dataset[0][0].shape\n", " len_in = 1\n", " for x in img_size:\n", " len_in *= x\n", " global_model = MLP(dim_in=len_in, dim_hidden=200,\n", " dim_out=args.num_classes)\n", " else:\n", " exit('Error: unrecognized model')\n", " \n", " return global_model" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "199210\n", "----------------------------------------------------------------\n", " Layer (type) Output Shape Param #\n", "================================================================\n", " Linear-1 [-1, 200] 157,000\n", " Dropout-2 [-1, 200] 0\n", " ReLU-3 [-1, 200] 0\n", " Linear-4 [-1, 200] 40,200\n", " Dropout-5 [-1, 200] 0\n", " ReLU-6 [-1, 200] 0\n", " Linear-7 [-1, 10] 2,010\n", " Softmax-8 [-1, 10] 0\n", "================================================================\n", "Total params: 199,210\n", "Trainable params: 199,210\n", "Non-trainable params: 0\n", "----------------------------------------------------------------\n", "Input size (MB): 0.00\n", "Forward/backward pass size (MB): 0.01\n", "Params size (MB): 0.76\n", "Estimated Total Size (MB): 0.77\n", "----------------------------------------------------------------\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# MODEL PARAM SUMMARY\n", "global_model = build_model(args, train_dataset)\n", "pytorch_total_params = sum(p.numel() for p in global_model.parameters())\n", "print(pytorch_total_params)\n", "\n", "from torchsummary import summary\n", "\n", "summary(global_model, (1, 28, 28))\n", "global_model.parameters()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "MLP(\n", " (layer_input): Linear(in_features=784, out_features=200, bias=True)\n", " (relu): ReLU()\n", " (dropout): Dropout(p=0.5, inplace=False)\n", " (layer_hidden1): Linear(in_features=200, out_features=200, bias=True)\n", " (layer_hidden2): Linear(in_features=200, out_features=10, bias=True)\n", " (softmax): Softmax(dim=1)\n", ")\n" ] } ], "source": [ "# Set the model to train and send it to device.\n", "global_model.to(device)\n", "global_model.train()\n", "print(global_model)\n", "\n", "# copy weights\n", "global_weights = global_model.state_dict()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Set the cluster models to train and send it to device.\n", "cluster_modelA = build_model(args, train_dataset)\n", "cluster_modelA.to(device)\n", "cluster_modelA.train()\n", "# copy weights\n", "cluster_modelA_weights = cluster_modelA.state_dict()\n", "\n", "# Set the cluster models to train and send it to device.\n", "cluster_modelB = build_model(args, train_dataset)\n", "cluster_modelB.to(device)\n", "cluster_modelB.train()\n", "# copy weights\n", "cluster_modelB_weights = cluster_modelA.state_dict()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "raw", "metadata": {}, "source": [ "print(np.random.choice(range(args.num_users), m, replace=False))\n", "np.random.choice(A1, m, replace=False)" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "len(A1)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# Defining the training function\n", "def fl_train(args, train_dataset, cluster_global_model, cluster, usergrp, epochs):\n", "# print(args)\n", " \n", "# # Set the model to train and send it to device.\n", "# cluster_global_model = build_model(args, train_dataset)\n", "# cluster_global_model.to(device)\n", "# cluster_global_model.train()\n", "\n", "# # copy weights\n", "# cluster_global_weights = cluster_global_model.state_dict()\n", " \n", " cluster_train_loss, cluster_train_accuracy = [], []\n", " cluster_val_acc_list, cluster_net_list = [], []\n", " cluster_cv_loss, cluster_cv_acc = [], []\n", " print_every = 2\n", " cluster_val_loss_pre, counter = 0, 0\n", "\n", " for epoch in tqdm(range(epochs)):\n", " cluster_local_weights, cluster_local_losses = [], []\n", " print(f'\\n | Cluster Training Round : {epoch+1} |\\n')\n", "\n", " cluster_global_model.train()\n", " m = max(int(args.frac * len(cluster)), 1)\n", " idxs_users = np.random.choice(cluster, m, replace=False)\n", "\n", "\n", " for idx in idxs_users:\n", " cluster_local_model = LocalUpdate(args=args, dataset=train_dataset, idxs=usergrp[idx], logger=logger)\n", " cluster_w, cluster_loss = cluster_local_model.update_weights(model=copy.deepcopy(cluster_global_model), global_round=epoch)\n", " cluster_local_weights.append(copy.deepcopy(cluster_w))\n", " cluster_local_losses.append(copy.deepcopy(cluster_loss))\n", "\n", " # averaging global weights\n", " cluster_global_weights = average_weights(cluster_local_weights)\n", "\n", " # update global weights\n", " cluster_global_model.load_state_dict(cluster_global_weights)\n", "\n", " cluster_loss_avg = sum(cluster_local_losses) / len(cluster_local_losses)\n", " cluster_train_loss.append(cluster_loss_avg)\n", "\n", "# # Calculate avg training accuracy over all users at every epoch\n", "# cluster_list_acc, cluster_list_loss = [], []\n", "# cluster_global_model.eval()\n", " \n", "# for c in range(len(cluster)):\n", "# cluster_local_model = LocalUpdate(args=args, dataset=train_dataset,\n", "# idxs=usergrp[idx], logger=logger)\n", "# cluster_acc, cluster_loss = cluster_local_model.inference(model=cluster_global_model)\n", "# cluster_list_acc.append(cluster_acc)\n", "# cluster_list_loss.append(cluster_loss)\n", "# cluster_train_accuracy.append(sum(cluster_list_acc)/len(cluster_list_acc))\n", " \n", "# return cluster_global_model, cluster_train_loss #cluster_global_weights, cluster_loss_avg, cluster_train_accuracy \n", " return cluster_global_weights, cluster_loss_avg\n", " \n", "# A_model, A_weights, A_losses, A_trainacc = fl_train(args, train_dataset, cluster_modelA, A1, user_groupsA, 2) \n", "# A_model, A_trainloss = fl_train(args, train_dataset, cluster_modelA, A1, user_groupsA, 2)\n", "# A_weights, A_losses = fl_train(args, train_dataset, cluster_modelA, A1, user_groupsA, 2)" ] }, { "cell_type": "raw", "metadata": {}, "source": [ "# Defining the evaluation function\n", "def fl_eval(train_dataset, global_model, cluster, usergrp, idxs_users):\n", " cluster_list_acc, cluster_list_loss = [], []\n", "# for c in range(len(cluster)):\n", " for c in idxs_users:\n", " cluster_local_model = LocalUpdate(args=args, dataset=train_dataset,\n", " idxs=usergrp[c], logger=logger)\n", " acc, loss = cluster_local_model.inference(model=global_model)\n", " cluster_list_acc.append(acc)\n", " cluster_list_loss.append(loss)\n", "# train_accuracy.append(sum(list_acc)/len(list_acc))\n", "\n", " return cluster_list_acc, cluster_list_loss\n", "\n", "\n", "clusterA_list_acc, clusterA_list_loss = fl_eval(train_dataset, global_model, A1, user_groupsA, A_idxs_users)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Main training" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 0%| | 0/5 [00:00" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# PLOTTING (optional)\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "matplotlib.use('Agg')\n", "\n", "# Plot Loss curve\n", "plt.figure()\n", "plt.title('Training Loss vs Communication rounds')\n", "plt.plot(range(len(train_loss)), train_loss, color='r')\n", "plt.ylabel('Training loss')\n", "plt.xlabel('Communication Rounds')\n", "# plt.savefig('../save/fed_{}_{}_{}_C[{}]_iid[{}]_E[{}]_B[{}]_loss.png'.\n", "# format(args.dataset, args.model, args.epochs, args.frac,\n", "# args.iid, args.local_ep, args.local_bs))\n", "plt.show" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# Plot Average Accuracy vs Communication rounds\n", "plt.figure()\n", "plt.title('Average Accuracy vs Communication rounds')\n", "plt.plot(range(len(train_accuracy)), train_accuracy, color='k')\n", "plt.ylabel('Average Accuracy')\n", "plt.xlabel('Communication Rounds')\n", "# plt.savefig('../save/fed_{}_{}_{}_C[{}]_iid[{}]_E[{}]_B[{}]_acc.png'.\n", "# format(args.dataset, args.model, args.epochs, args.frac,\n", "# args.iid, args.local_ep, args.local_bs))\n", "plt.show" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "fl_pytorch", "language": "python", "name": "fl_pytorch" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 2 }