blob: bfc4fb8882c9439d82f705ab42dc1955e8950030 [file] [log] [blame]
# %%
from singa import singa_wrap as singa
from singa import device
from singa import tensor
from singa import opt
from singa import layer
from singa.layer import Layer
from singa import model
import numpy as np
import time
import argparse
from PIL import Image
import os
from glob import glob
from sklearn.metrics import accuracy_score
import json
from tqdm import tqdm
from transforms import Compose, ToTensor, Normalize
np_dtype = {"float16": np.float16, "float32": np.float32}
singa_dtype = {"float16": tensor.float16, "float32": tensor.float32}
# %%
transforms = Compose([
ToTensor(),
Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# %%
class ClassDataset:
def __init__(self, img_folder, transforms):
super(ClassDataset, self).__init__()
self.img_list = list()
self.transforms = transforms
classes = os.listdir(img_folder)
for i in classes:
images = glob(os.path.join(img_folder, i, "*"))
for img in images:
self.img_list.append((img, i))
def __len__(self) -> int:
return len(self.img_list)
def __getitem__(self, index: int):
img_path, label_str = self.img_list[index]
img = Image.open(img_path)
img = self.transforms.forward(img)
label = np.array(label_str, dtype=np.int32)
return img, label
def batchgenerator(self, indexes, batch_size, data_size):
batch_x = np.zeros((batch_size,) + data_size)
batch_y = np.zeros((batch_size,) + (1,), dtype=np.int32)
for idx, i in enumerate(indexes):
sample_x, sample_y = self.__getitem__(i)
batch_x[idx, :, :, :] = sample_x
batch_y[idx, :] = sample_y
return batch_x, batch_y
# %%
class Sequential:
def __init__(self, *args):
# super(Sequential, self).__init__()
self.mod_list = []
for i in args:
self.mod_list.append(i)
def __call__(self, input):
for module in self.mod_list:
input = module(input)
return input
class CNNModel(model.Model):
def __init__(self, num_classes):
super(CNNModel, self).__init__()
self.input_size = 28
self.dimension = 4
self.num_classes = num_classes
# self.layer1 = Sequential(
# layer.Conv2d(16, kernel_size=3),
# layer.BatchNorm2d(),
# layer.ReLU())
self.layer1 = layer.Conv2d(16, kernel_size=3, activation="RELU")
self.bn1 = layer.BatchNorm2d()
# self.layer2 = Sequential(
# layer.Conv2d(16, kernel_size=3),
# layer.BatchNorm2d(),
# layer.ReLU(),
# layer.MaxPool2d(kernel_size=2, stride=2))
self.layer2 = layer.Conv2d(16, kernel_size=3, activation="RELU")
self.bn2 = layer.BatchNorm2d()
self.pooling2 = layer.MaxPool2d(kernel_size=2, stride=2)
# self.layer3 = Sequential(
# layer.Conv2d(64, kernel_size=3),
# layer.BatchNorm2d(),
# layer.ReLU())
self.layer3 = layer.Conv2d(64, kernel_size=3, activation="RELU")
self.bn3 = layer.BatchNorm2d()
# self.layer4 = Sequential(
# layer.Conv2d(64, kernel_size=3),
# layer.BatchNorm2d(),
# layer.ReLU())
self.layer4 = layer.Conv2d(64, kernel_size=3, activation="RELU")
self.bn4 = layer.BatchNorm2d()
# self.layer5 = Sequential(
# layer.Conv2d(64, kernel_size=3, padding=1),
# layer.BatchNorm2d(),
# layer.ReLU(),
# layer.MaxPool2d(kernel_size=2, stride=2))
self.layer5 = layer.Conv2d(64, kernel_size=3, padding=1, activation="RELU")
self.bn5 = layer.BatchNorm2d()
self.pooling5 = layer.MaxPool2d(kernel_size=2, stride=2)
self.flatten = layer.Flatten()
# self.fc = Sequential(
# layer.Linear(128),
# layer.ReLU(),
# layer.Linear(128),
# layer.ReLU(),
# layer.Linear(num_classes))
self.linear1 = layer.Linear(128)
self.linear2 = layer.Linear(128)
self.linear3 = layer.Linear(self.num_classes)
self.relu = layer.ReLU()
self.softmax_cross_entropy = layer.SoftMaxCrossEntropy()
self.dropout = layer.Dropout(ratio=0.3)
def forward(self, x):
x = self.layer1(x)
x = self.bn1(x)
x = self.layer2(x)
x = self.bn2(x)
x = self.pooling2(x)
x = self.layer3(x)
x = self.bn3(x)
x = self.layer4(x)
x = self.bn4(x)
x = self.layer5(x)
x = self.bn5(x)
x = self.pooling5(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.relu(x)
x = self.linear2(x)
x = self.relu(x)
x = self.linear3(x)
# x = self.fc(x)
return x
def set_optimizer(self, optimizer):
self.optimizer = optimizer
def train_one_batch(self, x, y, dist_option, spars):
out = self.forward(x)
loss = self.softmax_cross_entropy(out, y)
if dist_option == 'plain':
self.optimizer(loss)
elif dist_option == 'half':
self.optimizer.backward_and_update_half(loss)
elif dist_option == 'partialUpdate':
self.optimizer.backward_and_partial_update(loss)
elif dist_option == 'sparseTopK':
self.optimizer.backward_and_sparse_update(loss,
topK=True,
spars=spars)
elif dist_option == 'sparseThreshold':
self.optimizer.backward_and_sparse_update(loss,
topK=False,
spars=spars)
return out, loss
# %%
def getACC(y_true, y_score):
'''Accuracy metric.
:param y_true: the ground truth labels, shape: (n_samples, n_labels) or (n_samples,) if n_labels==1
:param y_score: the predicted score of each class,
shape: (n_samples, n_labels) or (n_samples, n_classes) or (n_samples,) if n_labels==1 or n_classes==1
:param task: the task of current dataset
:param threshold: the threshold for multilabel and binary-class tasks
'''
y_true = y_true.squeeze()
y_score = y_score.squeeze()
ret = accuracy_score(y_true, np.argmax(y_score, axis=-1))
return ret
def accuracy(pred, target):
# y is network output to be compared with ground truth (int)
y = np.argmax(pred, axis=1)
a = (y[:,None]==target).sum()
correct = np.array(a, "int").sum()
return correct
# %% dataset configuration
dataset_path = "./bloodmnist"
train_path = os.path.join(dataset_path, "train")
val_path = os.path.join(dataset_path, "val")
cfg_path = os.path.join(dataset_path, "param.json")
with open(cfg_path,'r') as load_f:
num_class = json.load(load_f)["num_classes"]
train_dataset = ClassDataset(train_path, transforms)
val_dataset = ClassDataset(val_path, transforms)
batch_size = 256
# %% model configuration
model = CNNModel(num_classes=num_class)
criterion = layer.SoftMaxCrossEntropy()
# optimizer_ft = opt.SGD(lr=0.005, momentum=0.9, weight_decay=1e-5, dtype=singa_dtype["float32"])
optimizer_ft = opt.Adam(lr=1e-3)
# optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=lr)
# lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_ft, mode='max', patience=5, threshold=1e-3)
# %% start training
dev = device.create_cpu_device()
dev.SetRandSeed(0)
np.random.seed(0)
tx = tensor.Tensor(
(batch_size, 3, model.input_size, model.input_size), dev,
singa_dtype['float32'])
ty = tensor.Tensor((batch_size,), dev, tensor.int32)
num_train_batch = train_dataset.__len__() // batch_size
num_val_batch = val_dataset.__len__() // batch_size
idx = np.arange(train_dataset.__len__(), dtype=np.int32)
model.set_optimizer(optimizer_ft)
model.compile([tx], is_train=True, use_graph=False, sequential=False)
dev.SetVerbosity(0)
max_epoch = 100
for epoch in range(max_epoch):
print('start**************************************************')
start_time = time.time()
# np.random.shuffle(idx)
train_correct = np.zeros(shape=[1], dtype=np.float32)
test_correct = np.zeros(shape=[1], dtype=np.float32)
train_loss = np.zeros(shape=[1], dtype=np.float32)
model.train()
for b in tqdm(range(num_train_batch)):
# forward + backward + optimize
x, y = train_dataset.batchgenerator(idx[b * batch_size:(b + 1) * batch_size],
batch_size=batch_size, data_size=(3, model.input_size, model.input_size))
x = x.astype(np_dtype['float32'])
# print(tx.size())
# print(y.dtype)
tx.copy_from_numpy(x)
ty.copy_from_numpy(y)
out, loss = model(tx, ty, dist_option="plain", spars=None)
# print(out)
# print(loss)
# train_correct += getACC(y, tensor.to_numpy(out))
train_correct += accuracy(tensor.to_numpy(out), y)
train_loss += tensor.to_numpy(loss)[0]
print('Training loss = %f, training accuracy = %f' %
(train_loss, train_correct /
(num_train_batch * batch_size)))
model.eval()
for b in tqdm(range(num_val_batch)):
# forward + backward + optimize
x, y = train_dataset.batchgenerator(idx[b * batch_size:(b + 1) * batch_size],
batch_size=batch_size, data_size=(3, model.input_size, model.input_size))
x = x.astype(np_dtype['float32'])
tx.copy_from_numpy(x)
ty.copy_from_numpy(y)
out = model(tx)
# test_correct += getACC(y, tensor.to_numpy(out))
test_correct += accuracy(tensor.to_numpy(out), y)
print('Evaluation accuracy = %f, Elapsed Time = %fs' %
(test_correct / (num_val_batch * batch_size),
time.time() - start_time))