使用Pytorch进行机器学习,必定要包含的内容
以下是一个demo,使用pytorch解决Regression类型任务
# PyTorch import torch import torch.nn as nn from torch.utils.data import Dataset, DataLoader # For data preprocess import numpy as np import csv import os # For plotting import matplotlib.pyplot as plt from matplotlib.pyplot import figure def get_device(): ''' Get device (if GPU is available, use GPU) ''' return 'cuda' if torch.cuda.is_available() else 'cpu' def plot_learning_curve(loss_record, title=''): ''' Plot learning curve of your DNN (train & dev loss) ''' total_steps = len(loss_record['train']) x_1 = range(total_steps) x_2 = x_1[::len(loss_record['train']) // len(loss_record['dev'])] figure(figsize=(6, 4)) plt.plot(x_1, loss_record['train'], c='tab:red', label='train') plt.plot(x_2, loss_record['dev'], c='tab:cyan', label='dev') plt.ylim(0.0, 5.) plt.xlabel('Training steps') plt.ylabel('MSE loss') plt.title('Learning curve of {}'.format(title)) plt.legend() plt.show() def plot_pred(dv_set, model, device, lim=35., preds=None, targets=None): ''' Plot prediction of your DNN ''' if preds is None or targets is None: model.eval() preds, targets = [], [] for x, y in dv_set: x, y = x.to(device), y.to(device) with torch.no_grad(): pred = model(x) preds.append(pred.detach().cpu()) targets.append(y.detach().cpu()) preds = torch.cat(preds, dim=0).numpy() targets = torch.cat(targets, dim=0).numpy() figure(figsize=(5, 5)) plt.scatter(targets, preds, c='r', alpha=0.5) plt.plot([-0.2, lim], [-0.2, lim], c='b') plt.xlim(-0.2, lim) plt.ylim(-0.2, lim) plt.xlabel('ground truth value') plt.ylabel('predicted value') plt.title('Ground Truth v.s. Prediction') plt.show() class COVID19Dataset(Dataset): ''' Dataset for loading and preprocessing the COVID19 dataset ''' def __init__(self, path, mode='train', target_only=False): self.mode = mode # Read data into numpy arrays with open(path, 'r') as fp: data = list(csv.reader(fp)) data = np.array(data[1:])[:, 1:].astype(float) if not target_only: feats = list(range(93)) else: # TODO: Using 40 states & 2 tested_positive features (indices = 57 & 75) feats = list(range(40))+[57,75] pass if mode == 'test': # Testing data # data: 893 x 93 (40 states + day 1 (18) + day 2 (18) + day 3 (17)) data = data[:, feats] self.data = torch.FloatTensor(data) else: # Training data (train/dev sets) # data: 2700 x 94 (40 states + day 1 (18) + day 2 (18) + day 3 (18)) target = data[:, -1] data = data[:, feats] # Splitting training data into train & dev sets if mode == 'train': indices = [i for i in range(len(data)) if i % 10 != 0] elif mode == 'dev': indices = [i for i in range(len(data)) if i % 10 == 0] # Convert data into PyTorch tensors self.data = torch.FloatTensor(data[indices]) self.target = torch.FloatTensor(target[indices]) # Normalize features (you may remove this part to see what will happen) self.data[:, 40:] = \ (self.data[:, 40:] - self.data[:, 40:].mean(dim=0, keepdim=True)) \ / self.data[:, 40:].std(dim=0, keepdim=True) self.dim = self.data.shape[1] print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})' .format(mode, len(self.data), self.dim)) def __getitem__(self, index): # Returns one sample at a time if self.mode in ['train', 'dev']: # For training return self.data[index], self.target[index] else: # For testing (no target) return self.data[index] def __len__(self): # Returns the size of the dataset return len(self.data) def prep_dataloader(path, mode, batch_size, n_jobs=0, target_only=False): ''' Generates a dataset, then is put into a dataloader. ''' dataset = COVID19Dataset(path, mode=mode, target_only=target_only) # Construct dataset dataloader = DataLoader( dataset, batch_size, shuffle=(mode == 'train'), drop_last=False, num_workers=n_jobs, pin_memory=True) # Construct dataloader return dataloader class NeuralNet(nn.Module): ''' A simple fully-connected deep neural network ''' def __init__(self, input_dim): super(NeuralNet, self).__init__() # Define your neural network here # TODO: How to modify this model to achieve better performance? self.net = nn.Sequential( nn.Linear(input_dim, 64), nn.ReLU(), # nn.Sigmoid(), nn.Linear(64, 1) ) # Mean squared error loss self.criterion = nn.MSELoss(reduction='mean') self.myloss = torch.tensor([0.0], requires_grad = True) def forward(self, x): ''' Given input of size (batch_size x input_dim), compute output of the network ''' return self.net(x).squeeze(1) def cal_loss(self, pred, target): ''' Calculate loss ''' # TODO: you may implement L1/L2 regularization here # L1 mean absolute error (MAE) # error = 0 # if pred.shape[0] == target.shape[0]: # for i in range(pred.shape[0]): # x = pred[i].item() # y = target[i].item() # e = abs(x-y) # error += e # meanError = error*1.0/pred.shape[0] # return torch.tensor(meanError, requires_grad = True) # import math # # L2 function mean squaril error(MAE) # error = 0 # if pred.shape[0] == target.shape[0]: # for i in range(pred.shape[0]): # x = pred[i].item() # y = target[i].item() # e = math.pow((x-y),2) # error += e # # meanError = error*1.0/pred.shape[0] # # print(meanError) # return torch.tensor(meanError, requires_grad = True) #要用tensor的方式计算出 mse # torch.mean((pred - target)**2) return torch.mean(abs(pred - target)) # 要用tensor的方式计算出 mse # return torch.mean((pred - target)**2) # return self.criterion(pred, target) def dev(dv_set, model, device): model.eval() # set model to evalutation mode total_loss = 0 for x, y in dv_set: # iterate through the dataloader x, y = x.to(device), y.to(device) # move data to device (cpu/cuda) with torch.no_grad(): # disable gradient calculation pred = model(x) # forward pass (compute output) mse_loss = model.cal_loss(pred, y) # compute loss total_loss += mse_loss.detach().cpu().item() * len(x) # accumulate loss total_loss = total_loss / len(dv_set.dataset) # compute averaged loss return total_loss def train(tr_set, dv_set, model, config, device): r''' DNN training ''' n_epochs = config['n_epochs'] # Maximum number of epochs # Setup optimizer 等价于 torch.optim.SGD(model.parameters(), lr, momentum = 0) optimizer = getattr(torch.optim, config['optimizer'])( model.parameters(), **config['optim_hparas']) min_mse = 1000. loss_record = {'train': [], 'dev': []} # for recording training loss early_stop_cnt = 0 epoch = 0 while epoch < n_epochs: model.train() # set model to training mode for x, y in tr_set: # iterate through the dataloader optimizer.zero_grad() # set gradient to zero x, y = x.to(device), y.to(device) # move data to device (cpu/cuda) pred = model(x) # forward pass (compute output) mse_loss = model.cal_loss(pred, y) # compute loss mse_loss.backward() # compute gradient (backpropagation) optimizer.step() # update model with optimizer loss_record['train'].append(mse_loss.detach().cpu().item()) # After each epoch, test your model on the validation (development) set. dev_mse = dev(dv_set, model, device) if dev_mse < min_mse: # Save model if your model improved min_mse = dev_mse print('Saving model (epoch = {:4d}, loss = {:.4f})' .format(epoch + 1, min_mse)) torch.save(model.state_dict(), config['save_path']) # Save model to specified path early_stop_cnt = 0 else: early_stop_cnt += 1 epoch += 1 loss_record['dev'].append(dev_mse) if early_stop_cnt > config['early_stop']: # Stop training if your model stops improving for "config['early_stop']" epochs. print("Stop training if your model stops improving for epochs.") break print('Finished training after {} epochs'.format(epoch)) return min_mse, loss_record def test(tt_set, model, device): model.eval() # set model to evalutation mode preds = [] for x in tt_set: # iterate through the dataloader x = x.to(device) # move data to device (cpu/cuda) with torch.no_grad(): # disable gradient calculation pred = model(x) # forward pass (compute output) preds.append(pred.detach().cpu()) # collect prediction preds = torch.cat(preds, dim=0).numpy() # concatenate all predictions and convert to a numpy array return preds def save_pred(preds, file): ''' Save predictions to specified file ''' print('Saving results to {}'.format(file)) with open(file, 'w') as fp: writer = csv.writer(fp) writer.writerow(['id', 'tested_positive']) for i, p in enumerate(preds): writer.writerow([i, p]) if __name__ == "__main__": # TODO: How to tune these hyper-parameters to improve your model's performance? config = { 'n_epochs': 5000, # maximum number of epochs 'batch_size': 200, # mini-batch size for dataloader 'optimizer': 'Adadelta', # optimization algorithm (optimizer in torch.optim) 'optim_hparas': { # hyper-parameters for the optimizer (depends on which optimizer you are using) 'lr': 0.2, # learning rate of SGD # 'momentum': 0.8 # momentum for SGD }, 'early_stop': 500, # early stopping epochs (the number epochs since your model's last improvement) 'save_path': 'models/model.pth' # your model will be saved here } tr_path = 'covid.train.csv' # path to training data tt_path = 'covid.test.csv' # path to testing data device = get_device() # get the current available device ('cpu' or 'cuda') os.makedirs('models', exist_ok=True) # The trained model will be saved to ./models/ target_only = False # TODO: Using 40 states & 2 tested_positive features myseed = 42069 # set a random seed for reproducibility torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False np.random.seed(myseed) torch.manual_seed(myseed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(myseed) print("this is main function") tr_set = prep_dataloader(tr_path, 'train', config['batch_size'], target_only=target_only) dv_set = prep_dataloader(tr_path, 'dev', config['batch_size'], target_only=target_only) tt_set = prep_dataloader(tt_path, 'test', config['batch_size'], target_only=target_only) print("data load finished!") model = NeuralNet(tr_set.dataset.dim).to(device) # Construct model and move to device model_loss, model_loss_record = train(tr_set, dv_set, model, config, device) plot_learning_curve(model_loss_record, title='deep model') del model model = NeuralNet(tr_set.dataset.dim).to(device) ckpt = torch.load(config['save_path'], map_location='cpu') # Load your best model model.load_state_dict(ckpt) plot_pred(dv_set, model, device) # Show prediction on the validation set preds = test(tt_set, model, device) # predict COVID-19 cases with your model save_pred(preds, 'pred.csv') # save prediction file to pred.csv