| @@ -41,3 +41,4 @@ cache/ | |||||
| tmp/ | tmp/ | ||||
| learnware_pool/ | learnware_pool/ | ||||
| PFS/ | PFS/ | ||||
| data/ | |||||
| @@ -0,0 +1,24 @@ | |||||
| import os | |||||
| import joblib | |||||
| import numpy as np | |||||
| from learnware.model import BaseModel | |||||
| from model import ConvModel | |||||
| import torch | |||||
| class Model(BaseModel): | |||||
| def __init__(self): | |||||
| dir_path = os.path.dirname(os.path.abspath(__file__)) | |||||
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |||||
| self.model = ConvModel(channel=3, n_random_features=10).to(device) | |||||
| self.model.load_state_dict(torch.load(os.path.join(dir_path, "conv_model.pth"))) | |||||
| self.model.eval() | |||||
| def fit(self, X: np.ndarray, y: np.ndarray): | |||||
| pass | |||||
| def predict(self, X: np.ndarray) -> np.ndarray: | |||||
| return self.model(X) | |||||
| def finetune(self, X: np.ndarray, y: np.ndarray): | |||||
| pass | |||||
| @@ -0,0 +1,283 @@ | |||||
| import torch | |||||
| from torchvision import datasets, transforms | |||||
| import torch.nn.functional as F | |||||
| from scipy.ndimage.interpolation import rotate as scipyrotate | |||||
| import numpy as np | |||||
| def get_fashion_mnist(data_root="./data", output_channels=1, image_size=28): | |||||
| ds_train = datasets.FashionMNIST( | |||||
| data_root, | |||||
| train=True, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_train = ds_train.data | |||||
| y_train = ds_train.targets | |||||
| ds_test = datasets.FashionMNIST( | |||||
| data_root, | |||||
| train=False, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_test = ds_test.data | |||||
| y_test = ds_test.targets | |||||
| X_train = X_train[:, None, :, :].float() | |||||
| X_test = X_test[:, None, :, :].float() | |||||
| if output_channels > 1: | |||||
| X_train = torch.cat([X_train for i in range(output_channels)], 1) | |||||
| X_test = torch.cat([X_test for i in range(output_channels)], 1) | |||||
| X_test = (X_test - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| X_train = (X_train - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| return X_train, y_train, X_test, y_test | |||||
| def get_mnist(data_root="./data/", output_channels=1, image_size=28): | |||||
| ds_train = datasets.MNIST( | |||||
| data_root, | |||||
| train=True, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_train = [] | |||||
| for x, _ in ds_train: | |||||
| X_train.append(x) | |||||
| X_train = torch.stack(X_train) | |||||
| y_train = ds_train.targets | |||||
| ds_test = datasets.MNIST( | |||||
| data_root, | |||||
| train=False, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_test = [] | |||||
| for x, _ in ds_test: | |||||
| X_test.append(x) | |||||
| X_test = torch.stack(X_test) | |||||
| y_test = ds_test.targets | |||||
| if output_channels > 1: | |||||
| X_train = torch.cat([X_train for i in range(output_channels)], 1) | |||||
| X_test = torch.cat([X_test for i in range(output_channels)], 1) | |||||
| X_test = (X_test - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| X_train = (X_train - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| return X_train, y_train, X_test, y_test | |||||
| def get_cifar10(data_root="./data/", output_channels=3, image_size=32): | |||||
| ds_train = datasets.CIFAR10( | |||||
| data_root, | |||||
| train=True, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_train = ds_train.data | |||||
| y_train = ds_train.targets | |||||
| ds_test = datasets.CIFAR10( | |||||
| data_root, | |||||
| train=False, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_test = ds_test.data | |||||
| y_test = ds_test.targets | |||||
| X_train = torch.Tensor(np.moveaxis(X_train, 3, 1)) | |||||
| y_train = torch.Tensor(y_train).long() | |||||
| X_test = torch.Tensor(np.moveaxis(X_test, 3, 1)) | |||||
| y_test = torch.Tensor(y_test).long() | |||||
| if output_channels == 1: | |||||
| X_train = torch.mean(X_train, 1, keepdim=True) | |||||
| X_test = torch.mean(X_test, 1, keepdim=True) | |||||
| X_test = (X_test - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| X_train = (X_train - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| return X_train, y_train, X_test, y_test | |||||
| def get_svhn(output_channels=1, image_size=32): | |||||
| ds_train = datasets.SVHN( | |||||
| "./data/", | |||||
| split="train", | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_train = ds_train.data | |||||
| y_train = ds_train.labels | |||||
| ds_test = datasets.SVHN( | |||||
| "./data/", | |||||
| split="test", | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_test = ds_test.data | |||||
| y_test = ds_test.labels | |||||
| X_train = torch.Tensor(X_train) | |||||
| y_train = torch.Tensor(y_train).long() | |||||
| X_test = torch.Tensor(X_test) | |||||
| y_test = torch.Tensor(y_test).long() | |||||
| if output_channels == 1: | |||||
| X_train = torch.mean(X_train, 1, keepdim=True) | |||||
| X_test = torch.mean(X_test, 1, keepdim=True) | |||||
| X_test = (X_test - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| X_train = (X_train - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| return X_train, y_train, X_test, y_test | |||||
| def get_cifar100(data_root="./data/", output_channels=3, image_size=32): | |||||
| ds_train = datasets.CIFAR100( | |||||
| data_root, | |||||
| train=True, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_train = ds_train.data | |||||
| y_train = ds_train.targets | |||||
| ds_test = datasets.CIFAR100( | |||||
| data_root, | |||||
| train=False, | |||||
| download=True, | |||||
| transform=transforms.Compose([transforms.ToTensor(), transforms.Resize([image_size, image_size])]), | |||||
| ) | |||||
| X_test = ds_test.data | |||||
| y_test = ds_test.targets | |||||
| X_train = torch.Tensor(np.moveaxis(X_train, 3, 1)) | |||||
| y_train = torch.Tensor(y_train).long() | |||||
| X_test = torch.Tensor(np.moveaxis(X_test, 3, 1)) | |||||
| y_test = torch.Tensor(y_test).long() | |||||
| if output_channels == 1: | |||||
| X_train = torch.mean(X_train, 1, keepdim=True) | |||||
| X_test = torch.mean(X_test, 1, keepdim=True) | |||||
| X_test = (X_test - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| X_train = (X_train - torch.mean(X_train, [0, 2, 3], keepdim=True)) / (torch.std(X_train, [0, 2, 3], keepdim=True)) | |||||
| return X_train, y_train, X_test, y_test | |||||
| def get_zca_matrix(X, reg_coef=0.1): | |||||
| X_flat = X.reshape(X.shape[0], -1) | |||||
| cov = (X_flat.T @ X_flat) / X_flat.shape[0] | |||||
| reg_amount = reg_coef * torch.trace(cov) / cov.shape[0] | |||||
| u, s, _ = torch.svd(cov.cuda() + reg_amount * torch.eye(cov.shape[0]).cuda()) | |||||
| inv_sqrt_zca_eigs = s ** (-0.5) | |||||
| whitening_transform = torch.einsum("ij,j,kj->ik", u, inv_sqrt_zca_eigs, u) | |||||
| return whitening_transform.cpu() | |||||
| def layernorm_data(X): | |||||
| X_processed = X - torch.mean(X, [1, 2, 3], keepdim=True) | |||||
| X_processed = X_processed / torch.sqrt(torch.sum(X_processed**2, [1, 2, 3], keepdim=True)) | |||||
| return X_processed | |||||
| def transform_data(X, whitening_transform): | |||||
| if len(whitening_transform.shape) == 2: | |||||
| X_flat = X.reshape(X.shape[0], -1) | |||||
| X_flat = X_flat @ whitening_transform | |||||
| return X_flat.view(*X.shape) | |||||
| else: | |||||
| X_flat = X.reshape(X.shape[0], -1) | |||||
| X_flat = torch.einsum("nd, ndi->ni", X_flat, whitening_transform) | |||||
| return X_flat.view(*X.shape) | |||||
| def scale_to_zero_one(X): | |||||
| mins = torch.min(X.view(X.shape[0], -1), 1)[0].view(-1, 1, 1, 1) | |||||
| maxes = torch.max(X.view(X.shape[0], -1), 1)[0].view(-1, 1, 1, 1) | |||||
| return (X - mins) / (maxes - mins) | |||||
| def augment(images, dc_aug_param, device): | |||||
| # This can be sped up in the future. | |||||
| if dc_aug_param != None and dc_aug_param["strategy"] != "none": | |||||
| scale = dc_aug_param["scale"] | |||||
| crop = dc_aug_param["crop"] | |||||
| rotate = dc_aug_param["rotate"] | |||||
| noise = dc_aug_param["noise"] | |||||
| strategy = dc_aug_param["strategy"] | |||||
| shape = images.shape | |||||
| mean = [] | |||||
| for c in range(shape[1]): | |||||
| mean.append(float(torch.mean(images[:, c]))) | |||||
| def cropfun(i): | |||||
| im_ = torch.zeros(shape[1], shape[2] + crop * 2, shape[3] + crop * 2, dtype=torch.float, device=device) | |||||
| for c in range(shape[1]): | |||||
| im_[c] = mean[c] | |||||
| im_[:, crop : crop + shape[2], crop : crop + shape[3]] = images[i] | |||||
| r, c = np.random.permutation(crop * 2)[0], np.random.permutation(crop * 2)[0] | |||||
| images[i] = im_[:, r : r + shape[2], c : c + shape[3]] | |||||
| def scalefun(i): | |||||
| h = int((np.random.uniform(1 - scale, 1 + scale)) * shape[2]) | |||||
| w = int((np.random.uniform(1 - scale, 1 + scale)) * shape[2]) | |||||
| tmp = F.interpolate( | |||||
| images[i : i + 1], | |||||
| [h, w], | |||||
| )[0] | |||||
| mhw = max(h, w, shape[2], shape[3]) | |||||
| im_ = torch.zeros(shape[1], mhw, mhw, dtype=torch.float, device=device) | |||||
| r = int((mhw - h) / 2) | |||||
| c = int((mhw - w) / 2) | |||||
| im_[:, r : r + h, c : c + w] = tmp | |||||
| r = int((mhw - shape[2]) / 2) | |||||
| c = int((mhw - shape[3]) / 2) | |||||
| images[i] = im_[:, r : r + shape[2], c : c + shape[3]] | |||||
| def rotatefun(i): | |||||
| im_ = scipyrotate( | |||||
| images[i].cpu().data.numpy(), | |||||
| angle=np.random.randint(-rotate, rotate), | |||||
| axes=(-2, -1), | |||||
| cval=np.mean(mean), | |||||
| ) | |||||
| r = int((im_.shape[-2] - shape[-2]) / 2) | |||||
| c = int((im_.shape[-1] - shape[-1]) / 2) | |||||
| images[i] = torch.tensor(im_[:, r : r + shape[-2], c : c + shape[-1]], dtype=torch.float, device=device) | |||||
| def noisefun(i): | |||||
| images[i] = images[i] + noise * torch.randn(shape[1:], dtype=torch.float, device=device) | |||||
| augs = strategy.split("_") | |||||
| for i in range(shape[0]): | |||||
| choice = np.random.permutation(augs)[0] # randomly implement one augmentation | |||||
| if choice == "crop": | |||||
| cropfun(i) | |||||
| elif choice == "scale": | |||||
| scalefun(i) | |||||
| elif choice == "rotate": | |||||
| rotatefun(i) | |||||
| elif choice == "noise": | |||||
| noisefun(i) | |||||
| return images | |||||
| @@ -0,0 +1,114 @@ | |||||
| import numpy as np | |||||
| import torch | |||||
| import get_data | |||||
| import os | |||||
| import random | |||||
| from utils import generate_uploader, generate_user, ImageDataLoader, train | |||||
| from learnware.market import EasyMarket, BaseUserInfo | |||||
| from learnware.market import database_ops | |||||
| from learnware.learnware import Learnware | |||||
| import learnware.specification as specification | |||||
| origin_data_root = "./data/origin_data" | |||||
| processed_data_root = "./data/processed_data" | |||||
| dataset = "cifar10" | |||||
| n_uploaders = 50 | |||||
| n_users = 10 | |||||
| n_classes = 10 | |||||
| data_root = os.path.join(origin_data_root, dataset) | |||||
| data_save_root = os.path.join(processed_data_root, dataset) | |||||
| user_save_root = os.path.join(data_save_root, "user") | |||||
| uploader_save_root = os.path.join(data_save_root, "uploader") | |||||
| model_save_root = os.path.join(data_save_root, "uploader_model") | |||||
| os.makedirs(data_root, exist_ok=True) | |||||
| os.makedirs(user_save_root, exist_ok=True) | |||||
| os.makedirs(uploader_save_root, exist_ok=True) | |||||
| os.makedirs(model_save_root, exist_ok=True) | |||||
| semantic_specs = [ | |||||
| { | |||||
| "Data": {"Values": ["Tabular"], "Type": "Class"}, | |||||
| "Task": { | |||||
| "Values": ["Classification"], | |||||
| "Type": "Class", | |||||
| }, | |||||
| "Device": {"Values": ["GPU"], "Type": "Tag"}, | |||||
| "Scenario": {"Values": ["Nature"], "Type": "Tag"}, | |||||
| "Description": {"Values": "", "Type": "Description"}, | |||||
| "Name": {"Values": "learnware_1", "Type": "Name"}, | |||||
| }, | |||||
| { | |||||
| "Data": {"Values": ["Tabular"], "Type": "Class"}, | |||||
| "Task": { | |||||
| "Values": ["Classification"], | |||||
| "Type": "Class", | |||||
| }, | |||||
| "Device": {"Values": ["GPU"], "Type": "Tag"}, | |||||
| "Scenario": {"Values": ["Business", "Nature"], "Type": "Tag"}, | |||||
| "Description": {"Values": "", "Type": "Description"}, | |||||
| "Name": {"Values": "learnware_2", "Type": "Name"}, | |||||
| }, | |||||
| { | |||||
| "Data": {"Values": ["Tabular"], "Type": "Class"}, | |||||
| "Task": { | |||||
| "Values": ["Classification"], | |||||
| "Type": "Class", | |||||
| }, | |||||
| "Device": {"Values": ["GPU"], "Type": "Tag"}, | |||||
| "Scenario": {"Values": ["Business"], "Type": "Tag"}, | |||||
| "Description": {"Values": "", "Type": "Description"}, | |||||
| "Name": {"Values": "learnware_3", "Type": "Name"}, | |||||
| }, | |||||
| ] | |||||
| user_senmantic = { | |||||
| "Data": {"Values": ["Tabular"], "Type": "Class"}, | |||||
| "Task": { | |||||
| "Values": ["Classification"], | |||||
| "Type": "Class", | |||||
| }, | |||||
| "Device": {"Values": ["GPU"], "Type": "Tag"}, | |||||
| "Scenario": {"Values": ["Business"], "Type": "Tag"}, | |||||
| "Description": {"Values": "", "Type": "Description"}, | |||||
| "Name": {"Values": "", "Type": "Name"}, | |||||
| } | |||||
| def prepare_data(): | |||||
| if dataset == "cifar10": | |||||
| X_train, y_train, X_test, y_test = get_data.get_cifar10(data_root) | |||||
| elif dataset == "mnist": | |||||
| X_train, y_train, X_test, y_test = get_data.get_mnist(data_root) | |||||
| else: | |||||
| return | |||||
| generate_uploader(X_train, y_train, n_uploaders=n_uploaders, data_save_root=uploader_save_root) | |||||
| generate_user(X_test, y_test, n_users=n_users, data_save_root=user_save_root) | |||||
| def prepare_model(): | |||||
| dataloader = ImageDataLoader(data_save_root, train=True) | |||||
| for i in range(n_uploaders): | |||||
| print("Train on uploader: %d" % (i)) | |||||
| X, y = dataloader.get_idx_data(i) | |||||
| model = train(X, y, out_classes=n_classes) | |||||
| model_save_path = os.path.join(model_save_root, "uploader_%d.pth" % (i)) | |||||
| torch.save(model.state_dict(), model_save_path) | |||||
| print("Model saved to '%s'" % (model_save_path)) | |||||
| def prepare_learnware(): | |||||
| pass | |||||
| def prepare_market(): | |||||
| for i in range(n_uploaders): | |||||
| data_path = os.path.join(uploader_save_root, "uploader_%d_X.npy" % (i)) | |||||
| model_path = os.path.join(model_save_root, "uploader_%d.pth" % (i)) | |||||
| if __name__ == "__main__": | |||||
| prepare_data() | |||||
| prepare_model() | |||||
| prepare_market() | |||||
| @@ -0,0 +1,183 @@ | |||||
| import torch | |||||
| import torch.nn as nn | |||||
| import torch.nn.functional as F | |||||
| import numpy as np | |||||
| class Linear(nn.Module): | |||||
| def __init__(self, input_feature=256, num_classes=10): | |||||
| super().__init__() | |||||
| self.linear_1 = nn.Linear(input_feature, 128) | |||||
| self.dropout_1 = nn.Dropout(p=0.5) | |||||
| self.linear_2 = nn.Linear(128, 128) | |||||
| self.dropout_2 = nn.Dropout(p=0.5) | |||||
| self.linear_3 = nn.Linear(128, num_classes) | |||||
| def forward(self, x): | |||||
| out1 = F.relu(self.dropout_1(self.linear_1(x))) | |||||
| out2 = F.relu(self.dropout_2(self.linear_2(out1))) | |||||
| out = self.linear_3(out2) | |||||
| return out | |||||
| class OriginModel(nn.Module): | |||||
| def __init__(self, last_layer_feature=256): | |||||
| super().__init__() | |||||
| self.linear_1 = nn.Linear(last_layer_feature, 128) | |||||
| self.linear_2 = nn.Linear(128, 128) | |||||
| self.linear_3 = nn.Linear(128, 10) | |||||
| def forward(self, x): | |||||
| out = F.relu(self.linear_1(x)) | |||||
| out = F.relu(self.linear_2(out)) | |||||
| out = self.linear_3(out) | |||||
| return out | |||||
| class ConvModel(nn.Module): | |||||
| def __init__( | |||||
| self, | |||||
| channel, | |||||
| n_random_features, | |||||
| net_width=64, | |||||
| net_depth=3, | |||||
| net_act="relu", | |||||
| net_norm="batchnorm", | |||||
| net_pooling="avgpooling", | |||||
| im_size=(32, 32), | |||||
| ): | |||||
| super().__init__() | |||||
| # print('Building Conv Model') | |||||
| self.features, shape_feat = self._make_layers( | |||||
| channel, net_width, net_depth, net_norm, net_act, net_pooling, im_size | |||||
| ) | |||||
| num_feat = shape_feat[0] * shape_feat[1] * shape_feat[2] | |||||
| self.classifier = GaussianLinear(num_feat, n_random_features) | |||||
| def forward(self, x): | |||||
| out = self.features(x) | |||||
| out = out.reshape(out.size(0), -1) | |||||
| out = self.classifier(out) | |||||
| return out | |||||
| def _get_activation(self, net_act): | |||||
| if net_act == "sigmoid": | |||||
| return nn.Sigmoid() | |||||
| elif net_act == "relu": | |||||
| return nn.ReLU(inplace=True) | |||||
| elif net_act == "leakyrelu": | |||||
| return nn.LeakyReLU(negative_slope=0.01) | |||||
| elif net_act == "gelu": | |||||
| return nn.SiLU() | |||||
| else: | |||||
| exit("unknown activation function: %s" % net_act) | |||||
| def _get_pooling(self, net_pooling): | |||||
| if net_pooling == "maxpooling": | |||||
| return nn.MaxPool2d(kernel_size=2, stride=2) | |||||
| elif net_pooling == "avgpooling": | |||||
| return nn.AvgPool2d(kernel_size=2, stride=2) | |||||
| elif net_pooling == "none": | |||||
| return None | |||||
| else: | |||||
| exit("unknown net_pooling: %s" % net_pooling) | |||||
| def _get_normlayer(self, net_norm, shape_feat): | |||||
| # shape_feat = (c*h*w) | |||||
| if net_norm == "batchnorm": | |||||
| return nn.BatchNorm2d(shape_feat[0], affine=True) | |||||
| elif net_norm == "layernorm": | |||||
| return nn.LayerNorm(shape_feat, elementwise_affine=True) | |||||
| elif net_norm == "instancenorm": | |||||
| return nn.GroupNorm(shape_feat[0], shape_feat[0], affine=True) | |||||
| elif net_norm == "groupnorm": | |||||
| return nn.GroupNorm(4, shape_feat[0], affine=True) | |||||
| elif net_norm == "none": | |||||
| return None | |||||
| else: | |||||
| exit("unknown net_norm: %s" % net_norm) | |||||
| def _make_layers(self, channel, net_width, net_depth, net_norm, net_act, net_pooling, im_size): | |||||
| layers = [] | |||||
| in_channels = channel | |||||
| # if im_size[0] == 28: | |||||
| # im_size = (32, 32) | |||||
| shape_feat = [in_channels, im_size[0], im_size[1]] | |||||
| for d in range(net_depth): | |||||
| # print(shape_feat) | |||||
| layers += [Conv2d_gaussian(in_channels, net_width, kernel_size=3, padding=1)] | |||||
| # layers += [nn.Conv2d(in_channels, net_width, kernel_size=3, padding='same')] | |||||
| shape_feat[0] = net_width | |||||
| if net_norm != "none": | |||||
| layers += [self._get_normlayer(net_norm, shape_feat)] | |||||
| layers += [self._get_activation(net_act)] | |||||
| in_channels = net_width | |||||
| if net_pooling != "none": | |||||
| layers += [self._get_pooling(net_pooling)] | |||||
| shape_feat[1] //= 2 | |||||
| shape_feat[2] //= 2 | |||||
| return nn.Sequential(*layers), shape_feat | |||||
| class Conv2d_gaussian(torch.nn.Conv2d): | |||||
| def reset_parameters(self) -> None: | |||||
| # Setting a=sqrt(5) in kaiming_uniform is the same as initializing with | |||||
| # uniform(-1/sqrt(k), 1/sqrt(k)), where k = weight.size(1) * prod(*kernel_size) | |||||
| # For more details see: https://github.com/pytorch/pytorch/issues/15314#issuecomment-477448573 | |||||
| # torch.nn.init.kaiming_normal_(self.weight, a= math.sqrt(5)) | |||||
| # W has shape out, in, h, w | |||||
| torch.nn.init.normal_( | |||||
| self.weight, 0, np.sqrt(2) / np.sqrt(self.weight.shape[1] * self.weight.shape[2] * self.weight.shape[3]) | |||||
| ) | |||||
| if self.bias is not None: | |||||
| fan_in, _ = torch.nn.init._calculate_fan_in_and_fan_out(self.weight) | |||||
| # print(fan_in) | |||||
| if fan_in != 0: | |||||
| # bound = 0 * 1 / math.sqrt(fan_in) | |||||
| # torch.nn.init.uniform_(self.bias, -bound, bound) | |||||
| # torch.nn.init.uniform_(self.bias, -bound, bound) | |||||
| torch.nn.init.normal_(self.bias, 0, 0.1) | |||||
| class GaussianLinear(torch.nn.Module): | |||||
| __constants__ = ["in_features", "out_features"] | |||||
| in_features: int | |||||
| out_features: int | |||||
| weight: torch.Tensor | |||||
| def __init__( | |||||
| self, in_features: int, out_features: int, bias: bool = True, device=None, dtype=None, funny=False | |||||
| ) -> None: | |||||
| factory_kwargs = {"device": device, "dtype": dtype} | |||||
| super(GaussianLinear, self).__init__() | |||||
| self.funny = funny | |||||
| self.in_features = in_features | |||||
| self.out_features = out_features | |||||
| self.weight = torch.nn.Parameter(torch.empty((out_features, in_features), **factory_kwargs)) | |||||
| if bias: | |||||
| self.bias = torch.nn.Parameter(torch.empty(out_features, **factory_kwargs)) | |||||
| else: | |||||
| self.register_parameter("bias", None) | |||||
| self.reset_parameters() | |||||
| def reset_parameters(self) -> None: | |||||
| # Setting a=sqrt(5) in kaiming_uniform is the same as initializing with | |||||
| # uniform(-1/sqrt(in_features), 1/sqrt(in_features)). For details, see | |||||
| # https://github.com/pytorch/pytorch/issues/57109 | |||||
| # torch.nn.init.kaiming_normal_(self.weight, a=1 * np.sqrt(5)) | |||||
| torch.nn.init.normal_(self.weight, 0, np.sqrt(2) / np.sqrt(self.in_features)) | |||||
| # torch.nn.init.normal_(self.weight, 0, 3/np.sqrt(self.in_features)) | |||||
| if self.bias is not None: | |||||
| fan_in, _ = torch.nn.init._calculate_fan_in_and_fan_out(self.weight) | |||||
| bound = 1 / np.sqrt(fan_in) if fan_in > 0 else 0 | |||||
| # torch.nn.init.uniform_(self.bias, -bound, bound) | |||||
| torch.nn.init.normal_(self.bias, 0, 0.1) | |||||
| def forward(self, input: torch.Tensor) -> torch.Tensor: | |||||
| return torch.nn.functional.linear(input, self.weight, self.bias) | |||||
| def extra_repr(self) -> str: | |||||
| return "in_features={}, out_features={}, bias={}".format( | |||||
| self.in_features, self.out_features, self.bias is not None | |||||
| ) | |||||
| @@ -0,0 +1,160 @@ | |||||
| import os | |||||
| import numpy as np | |||||
| import random | |||||
| import math | |||||
| import torch | |||||
| import torch.nn as nn | |||||
| import torch.optim as optim | |||||
| from model import ConvModel | |||||
| class ImageDataLoader: | |||||
| def __init__(self, data_root, train: bool = True): | |||||
| self.data_root = data_root | |||||
| self.train = train | |||||
| def get_idx_data(self, idx=0): | |||||
| if self.train: | |||||
| X_path = os.path.join(self.data_root, "uploader", "uploader_%d_X.npy" % (idx)) | |||||
| y_path = os.path.join(self.data_root, "uploader", "uploader_%d_y.npy" % (idx)) | |||||
| if not (os.path.exists(X_path) and os.path.exists(y_path)): | |||||
| raise Exception("Index Error") | |||||
| X = np.load(X_path) | |||||
| y = np.load(y_path) | |||||
| else: | |||||
| X_path = os.path.join(self.data_root, "user", "user_%d_X.npy" % (idx)) | |||||
| y_path = os.path.join(self.data_root, "user", "user_%d_y.npy" % (idx)) | |||||
| if not (os.path.exists(X_path) and os.path.exists(y_path)): | |||||
| raise Exception("Index Error") | |||||
| X = np.load(X_path) | |||||
| y = np.load(y_path) | |||||
| return X, y | |||||
| def generate_uploader(data_x, data_y, n_uploaders=50, data_save_root=None): | |||||
| if data_save_root is None: | |||||
| return | |||||
| os.makedirs(data_save_root, exist_ok=True) | |||||
| for i in range(n_uploaders): | |||||
| random_class_num = random.randint(6, 10) | |||||
| cls_indx = list(range(10)) | |||||
| random.shuffle(cls_indx) | |||||
| selected_cls_indx = cls_indx[:random_class_num] | |||||
| rest_cls_indx = cls_indx[random_class_num:] | |||||
| selected_data_indx = [] | |||||
| for cls in selected_cls_indx: | |||||
| data_indx = list(torch.where(data_y == cls)[0]) | |||||
| # print(type(data_indx)) | |||||
| random.shuffle(data_indx) | |||||
| data_num = random.randint(800, 2000) | |||||
| selected_indx = data_indx[:data_num] | |||||
| selected_data_indx = selected_data_indx + selected_indx | |||||
| for cls in rest_cls_indx: | |||||
| flag = random.randint(0, 1) | |||||
| if flag == 0: | |||||
| continue | |||||
| data_indx = list(torch.where(data_y == cls)[0]) | |||||
| random.shuffle(data_indx) | |||||
| data_num = random.randint(20, 80) | |||||
| selected_indx = data_indx[:data_num] | |||||
| selected_data_indx = selected_data_indx + selected_indx | |||||
| selected_X = data_x[selected_data_indx].numpy() | |||||
| selected_y = data_y[selected_data_indx].numpy() | |||||
| print(selected_X.dtype, selected_y.dtype) | |||||
| print(selected_X.shape, selected_y.shape) | |||||
| X_save_dir = os.path.join(data_save_root, "uploader_%d_X.npy" % (i)) | |||||
| y_save_dir = os.path.join(data_save_root, "uploader_%d_y.npy" % (i)) | |||||
| np.save(X_save_dir, selected_X) | |||||
| np.save(y_save_dir, selected_y) | |||||
| print("Saving to %s" % (X_save_dir)) | |||||
| def generate_user(data_x, data_y, n_users=50, data_save_root=None): | |||||
| if data_save_root is None: | |||||
| return | |||||
| os.makedirs(data_save_root, exist_ok=True) | |||||
| for i in range(n_users): | |||||
| random_class_num = random.randint(3, 6) | |||||
| cls_indx = list(range(10)) | |||||
| random.shuffle(cls_indx) | |||||
| selected_cls_indx = cls_indx[:random_class_num] | |||||
| selected_data_indx = [] | |||||
| for cls in selected_cls_indx: | |||||
| data_indx = list(torch.where(data_y == cls)[0]) | |||||
| # print(type(data_indx)) | |||||
| random.shuffle(data_indx) | |||||
| data_num = random.randint(150, 350) | |||||
| selected_indx = data_indx[:data_num] | |||||
| selected_data_indx = selected_data_indx + selected_indx | |||||
| # print('Total Index:', len(selected_data_indx)) | |||||
| selected_X = data_x[selected_data_indx].numpy() | |||||
| selected_y = data_y[selected_data_indx].numpy() | |||||
| print(selected_X.shape, selected_y.shape) | |||||
| X_save_dir = os.path.join(data_save_root, "user_%d_X.npy" % (i)) | |||||
| y_save_dir = os.path.join(data_save_root, "user_%d_y.npy" % (i)) | |||||
| np.save(X_save_dir, selected_X) | |||||
| np.save(y_save_dir, selected_y) | |||||
| print("Saving to %s" % (X_save_dir)) | |||||
| # Train Uploaders' models | |||||
| def train(X, y, out_classes, epochs=35, batch_size=128): | |||||
| print(X.shape, y.shape) | |||||
| input_feature = X.shape[1] | |||||
| data_size = X.shape[0] | |||||
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |||||
| model = ConvModel(channel=input_feature, n_random_features=out_classes).to(device) | |||||
| model.train() | |||||
| # Adam optimizer with learning rate 1e-3 | |||||
| # optimizer = optim.Adam(model.parameters(), lr=1e-3) | |||||
| # SGD optimizer with learning rate 1e-2 | |||||
| optimizer = optim.SGD(model.parameters(), lr=1e-2, momentum=0.9) | |||||
| # mean-squared error loss | |||||
| criterion = nn.CrossEntropyLoss() | |||||
| for epoch in range(epochs): | |||||
| running_loss = [] | |||||
| indx = list(range(data_size)) | |||||
| random.shuffle(indx) | |||||
| curr_X = X[indx] | |||||
| curr_y = y[indx] | |||||
| for i in range(math.floor(data_size / batch_size)): | |||||
| inputs, annos = curr_X[i * batch_size : (i + 1) * batch_size], curr_y[i * batch_size : (i + 1) * batch_size] | |||||
| inputs = torch.from_numpy(inputs).to(device) | |||||
| annos = torch.from_numpy(annos).to(device) | |||||
| # print(inputs.dtype, annos.dtype) | |||||
| out = model(inputs) | |||||
| optimizer.zero_grad() | |||||
| loss = criterion(out, annos) | |||||
| loss.backward() | |||||
| optimizer.step() | |||||
| running_loss.append(loss.item()) | |||||
| # print('Epoch: %d, Average Loss: %.3f'%(epoch+1, np.mean(running_loss))) | |||||
| # Train Accuracy | |||||
| acc = test(X, y, model) | |||||
| model.train() | |||||
| return model | |||||
| def test(test_X, test_y, model, batch_size=128): | |||||
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |||||
| model.eval() | |||||
| total, correct = 0, 0 | |||||
| data_size = test_X.shape[0] | |||||
| for i in range(math.ceil(data_size / batch_size)): | |||||
| inputs, annos = test_X[i * batch_size : (i + 1) * batch_size], test_y[i * batch_size : (i + 1) * batch_size] | |||||
| inputs = torch.Tensor(inputs).to(device) | |||||
| annos = torch.Tensor(annos).to(device) | |||||
| out = model(inputs) | |||||
| _, predicted = torch.max(out.data, 1) | |||||
| total += annos.size(0) | |||||
| correct += (predicted == annos).sum().item() | |||||
| acc = correct / total * 100 | |||||
| print("Accuracy: %.2f" % (acc)) | |||||
| return acc | |||||
| @@ -32,7 +32,7 @@ if os.path.exists("MANIFEST"): | |||||
| # What packages are required for this module to be executed? | # What packages are required for this module to be executed? | ||||
| # `estimator` may depend on other packages. In order to reduce dependencies, it is not written here. | # `estimator` may depend on other packages. In order to reduce dependencies, it is not written here. | ||||
| REQUIRED = [ | REQUIRED = [ | ||||
| "numpy>=1.12.0, <1.24", | |||||
| "numpy>=1.20.0", | |||||
| "pandas>=0.25.1", | "pandas>=0.25.1", | ||||
| "scipy>=1.0.0", | "scipy>=1.0.0", | ||||
| "matplotlib>=3.1.3", | "matplotlib>=3.1.3", | ||||