class Tester: """ 测试 """ def __init__(self, _hparams): self.test_loader = get_test_loader(_hparams) self.encoder = CNN().to(DEVICE) self.decoder = RNN(fea_dim=_hparams.fea_dim, embed_dim=_hparams.embed_dim, hid_dim=_hparams.hid_dim, max_sen_len=_hparams.max_sen_len, vocab_pkl=_hparams.vocab_pkl).to(DEVICE) self.test_cap = _hparams.test_cap def testing(self, save_path, test_path): """ 测试 :param save_path: 模型的保存地址 :param test_path: 保存测试过程生成句子的路径 :return: """ print('*' * 20, 'test', '*' * 20) self.load_models(save_path) self.set_eval() sen_json = [] with torch.no_grad(): for val_step, (img, img_id) in tqdm(enumerate(self.test_loader)): img = img.to(DEVICE) features = self.encoder.forward(img) sens, _ = self.decoder.sample(features) sen_json.append({'image_id': int(img_id), 'caption': sens[0]}) with open(test_path, 'w') as f: json.dump(sen_json, f) result = coco_eval(self.test_cap, test_path) for metric, score in result: print(metric, score) def load_models(self, save_path): ckpt = torch.load(save_path, map_location={'cuda:2': 'cuda:0' }) # 映射是因为解决保存模型的卡与加载模型的卡不一致的问题 encoder_state_dict = ckpt['encoder_state_dict'] self.encoder.load_state_dict(encoder_state_dict) decoder_state_dict = ckpt['decoder_state_dict'] self.decoder.load_state_dict(decoder_state_dict) def set_eval(self): self.encoder.eval() self.decoder.eval()
total_step = 0 # 총 몇 번의 학습이 일어났나 저장하기 위한 객체 list_loss = list() # loss 저장용 리스트 for epoch in trange(EPOCHS): # EPOCHS 만큼 실행할 for 문 for i, data in enumerate( data_loader): # data_loader 에서 선언한 data 와 그 인덱스 i를 선언 total_step += 1 # step을 1 늘림 input, label = data[0], data[ 1] # data_loader에서 읽은 data 객체의 인풋 데이터와 라벨 읽어옴 # input shape = [32,1,28,28] [batch size, channel, height, width] input = input.view( input.shape[0], -1) if model == 'MLP' else input # 인풋 데이터를 1차원 행렬로 만듬 # [batch size, channel*height*weight] classification_results = model.forward(input) # [batch size, 10] # MLP 클래스의 forward 부함수들 실행. 입력 데이터는 위에서 만든 input l = loss(classification_results, label) # loss 객체(Cross entropy) 에 forward 결과값과 라벨 입력 list_loss.append(l.detach().item()) # loss_list 에 loss 저장. # l.detach(): 로스 계산을 하지 않겠다는 선언. 이미 l = loss 에서 로스를 계산했으니 필요 없음. # item(): tensor 형식으로 돌려줄 값을 float 형식으로 돌려줌 optim.zero_grad( ) # make gradients zero. 각 웨이트가 부여받은 gradient를 0으로 만듬 l.backward( ) # giving weight's gradient to each weights. l 객체에서 구한 loss 값을 가중치들에 부여 optim.step() # Weights adjust themself using given gradient. # 각 객체가 부여받은 loss 값을 선언한 옵티마이저 설정에 맞게 수정하여 가중치에 반영
loss = nn.CrossEntropyLoss() optim = torch.optim.Adam(model.parameters(), lr=2e-4, betas=(0.5,0.99), eps=1e-8) # parameters : wexight , optim : 그래디언트 전달 , lr: 계산되는 그래디언트의 값이 너무 크기 때문에 그래디언트에 붙이는 계수 EPOCHS = 1 # 전체 데이터의 학습을 몇번 시킬 것인가? total_step = 0 list_loss = list() for epoch in range(EPOCHS): for i, data in enumerate(data_loader): # enumerate : 반복문에서 index를 입력할 때 사용 total_step = total_step+1 input, label = data[0], data[1] # input shape [32, 1, 28, 28] 첫번째 배치사이즈, channel, height, width input = input.view(input.shape[0], -1) if MODEL == 'MLP' else input # # batchsize, channel * height * width 왜 이렇게 하는거지 ? → 1차원으로 바꾸기 위해서 # view? reshape? 메모리주소? classification_results = model.forward(input) # [bstch size, 10] #nn.module을 상속한 클래스는 forward를 생략해도된다. l = loss(classification_results, label) list_loss.append(l.detach().item()) # detach 그래디언트를 없애는 item : torch tensor를 파이썬의 float이나 int로 바꿔줌 optim.zero_grad() # 그래디언트를 초기화( l.backward() # l을 출력하기 까지의 해당하는 각각의 그래디언트를 돌려줌 optim.step() # 이건 뭐냐 그래디언트를 각레이어로 전달해주는 거 맞나,, print(i) torch.save(model, '{}.pt'.format(MODEL)) # 'MLP.pt', 'CNN.pt' plt.figure() #figure : 도화지 plt.plot(range(len(list_loss)), list_loss, linestyle='--') plt.show()
class TextClassifier: def __init__(self, paths, batch_size=6, iterations=50, initial_lr=0.003, hidden_size=256, dropout=0.2, kernel_sz=3): self.use_cuda = torch.cuda.is_available() self.device = torch.device('cuda:0' if self.use_cuda else 'cpu') self.data = DataReader(paths) self.data.set_training_data(batch_size, ('cuda:0' if self.use_cuda else 'cpu')) self.train_batch_loader = BatchGenerator(self.data.train_data, 'Sentence', 'Label') self.val_batch_loader = BatchGenerator(self.data.val_data, 'Sentence', 'Label') self.test_batch_loader = BatchGenerator(self.data.test_data, 'Sentence', 'Label') # Store hyperparameters self.batch_size = batch_size self.iterations = iterations self.initial_lr = initial_lr self.kernel_sz = kernel_sz # Create Model emb_size, emb_dim = self.data.TEXT.vocab.vectors.size() self.cnn_model = CNN(emb_size=emb_size, emb_dimension=emb_dim, n_out=len(self.data.LABEL.vocab), dropout=dropout, kernel_sz=kernel_sz, stride=1, padding=0, out_filters=hidden_size, pretrained_emb=self.data.TEXT.vocab.vectors) if self.use_cuda: self.cnn_model.cuda() def train(self): train_loss_hist = [] val_loss_hist = [] train_acc_hist = [] val_acc_hist = [] test_acc_hist = [] loss = 0.0 best_model = 0.0 for itr in range(self.iterations): print("\nIteration: " + str(itr + 1)) optimizer = optim.SGD(self.cnn_model.parameters(), lr=self.initial_lr) self.cnn_model.train() total_loss = 0.0 total_acc = 0.0 steps = 0 data_iter = iter(self.train_batch_loader) # For some reason using for loop on iterator (next) is missing the target variable (y) # Have to loop over the length and retrieve the batch_data inside the loop for i in range(len(self.train_batch_loader)): ((x_batch, x_len_batch), y_batch) = next(data_iter) # if torch.min(x_len_batch) > self.kernel_sz: optimizer.zero_grad() loss, logits = self.cnn_model.forward(x_batch, y_batch) acc = torch.sum(torch.argmax(logits, dim=1) == y_batch) total_loss += loss.item() total_acc += acc.item() steps += 1 loss.backward() optimizer.step() train_loss_hist.append(total_loss / steps) train_acc_hist.append(total_acc / len(self.data.trainds)) val_loss, val_acc = self.eval_model(self.val_batch_loader, len(self.data.valds)) val_loss_hist.append(val_loss) val_acc_hist.append(val_acc) if best_model < val_acc: best_model = val_acc test_loss, test_acc = self.eval_model(self.test_batch_loader, len(self.data.testds)) print("Train: {Loss: " + str(total_loss / steps) + ", Acc: " + str(total_acc / len(self.data.trainds)) + " }") print("Val: {Loss: " + str(val_loss) + ", Acc: " + str(val_acc) + " }") # test_loss, test_acc = self.eval_model(self.test_batch_loader, len(self.data.testds) ) test_acc_hist.append(test_acc) return train_loss_hist, train_acc_hist, val_loss_hist, val_acc_hist, test_acc def eval_model(self, batch_loader, N): self.cnn_model.eval() total_loss = 0.0 total_acc = 0.0 steps = 0 batch_iter = iter(batch_loader) with torch.no_grad(): for i in range(len(batch_loader)): ((x_batch, x_len_batch), y_batch) = next(batch_iter) loss, logits = self.cnn_model(x_batch, y_batch) acc = torch.sum(torch.argmax(logits, dim=1) == y_batch) total_loss += loss.item() total_acc += acc.item() steps += 1 return (total_loss / steps), (total_acc / N)
class Trainer: """ 训练 """ def __init__(self, _hparams): utils.set_seed(_hparams.fixed_seed) self.train_loader = get_train_loader(_hparams) self.val_loader = get_val_loader(_hparams) self.encoder = CNN().to(DEVICE) self.decoder = RNN(fea_dim=_hparams.fea_dim, embed_dim=_hparams.embed_dim, hid_dim=_hparams.hid_dim, max_sen_len=_hparams.max_sen_len, vocab_pkl=_hparams.vocab_pkl).to(DEVICE) self.loss_fn = nn.CrossEntropyLoss() self.optimizer = torch.optim.Adam(self.get_params(), lr=_hparams.lr) self.writer = SummaryWriter() self.max_sen_len = _hparams.max_sen_len self.val_cap = _hparams.val_cap self.ft_encoder_lr = _hparams.ft_encoder_lr self.ft_decoder_lr = _hparams.ft_decoder_lr self.best_CIDEr = 0 def fine_tune_encoder(self, fine_tune_epochs, val_interval, save_path, val_path): print('*' * 20, 'fine tune encoder for', fine_tune_epochs, 'epochs', '*' * 20) self.encoder.fine_tune() self.optimizer = torch.optim.Adam([ { 'params': self.encoder.parameters(), 'lr': self.ft_encoder_lr }, { 'params': self.decoder.parameters(), 'lr': self.ft_decoder_lr }, ]) self.training(fine_tune_epochs, val_interval, save_path, val_path) self.encoder.froze() print('*' * 20, 'fine tune encoder complete', '*' * 20) def get_params(self): """ 模型需要优化的全部参数,此处encoder暂时设计不用训练,故不加参数 :return: """ return list(self.decoder.parameters()) def training(self, max_epochs, val_interval, save_path, val_path): """ 训练 :param val_path: 保存验证过程生成句子的路径 :param save_path: 保存模型的地址 :param val_interval: 验证的间隔 :param max_epochs: 最大训练的轮次 :return: """ print('*' * 20, 'train', '*' * 20) for epoch in range(max_epochs): self.set_train() epoch_loss = 0 epoch_steps = len(self.train_loader) for step, (img, cap, cap_len) in tqdm(enumerate(self.train_loader)): # batch_size * 3 * 224 * 224 img = img.to(DEVICE) cap = cap.to(DEVICE) self.optimizer.zero_grad() features = self.encoder.forward(img) outputs = self.decoder.forward(features, cap) outputs = pack_padded_sequence(outputs, cap_len - 1, batch_first=True)[0] targets = pack_padded_sequence(cap[:, 1:], cap_len - 1, batch_first=True)[0] train_loss = self.loss_fn(outputs, targets) epoch_loss += train_loss.item() train_loss.backward() self.optimizer.step() epoch_loss /= epoch_steps self.writer.add_scalar('epoch_loss', epoch_loss, epoch) print('epoch_loss: {}, epoch: {}'.format(epoch_loss, epoch)) if (epoch + 1) % val_interval == 0: CIDEr = self.validating(epoch, val_path) if self.best_CIDEr <= CIDEr: self.best_CIDEr = CIDEr self.save_model(save_path, epoch) def save_model(self, save_path, train_epoch): """ 保存最好的模型 :param save_path: 保存模型文件的地址 :param train_epoch: 当前训练的轮次 :return: """ model_state_dict = { 'encoder_state_dict': self.encoder.state_dict(), 'decoder_state_dict': self.decoder.state_dict(), 'tran_epoch': train_epoch, } print('*' * 20, 'save model to: ', save_path, '*' * 20) torch.save(model_state_dict, save_path) def validating(self, train_epoch, val_path): """ 验证 :param val_path: 保存验证过程生成句子的路径 :param train_epoch: 当前训练的epoch :return: """ print('*' * 20, 'validate', '*' * 20) self.set_eval() sen_json = [] with torch.no_grad(): for val_step, (img, img_id) in tqdm(enumerate(self.val_loader)): img = img.to(DEVICE) features = self.encoder.forward(img) sens, _ = self.decoder.sample(features) sen_json.append({'image_id': int(img_id), 'caption': sens[0]}) with open(val_path, 'w') as f: json.dump(sen_json, f) result = coco_eval(self.val_cap, val_path) scores = {} for metric, score in result: scores[metric] = score self.writer.add_scalar(metric, score, train_epoch) return scores['CIDEr'] def set_train(self): self.encoder.train() self.decoder.train() def set_eval(self): self.encoder.eval() self.decoder.eval()
shuffle=True) OUTPUT_DIM = 10 EPOCH = 10 loss_memory = [] loss_history = [] lr = 0.3 conv_net = CNN(OUTPUT_DIM, lr) # Training for e in range(EPOCH): correct = 0 for batch_idx, (data, targets) in enumerate(test_loader): print(batch_idx) input_data = data.numpy() out = conv_net.forward(input_data) pred = np.argmax(out, axis=1) labels = np.zeros((len(targets), OUTPUT_DIM)) for i in range(len(targets)): if pred[i] == targets[i]: correct += 1 labels[i][targets[i]] = 1 loss = F.compute_loss(out, labels) loss_memory.append(np.sum(loss, axis=0)) conv_net.backward(labels) conv_net.sgd() loss_history.append(loss_memory) av_loss = np.sum(loss_memory) / len(test_loader.dataset) loss_memory = [] print( '\n Training Epoch : {}, Accuracy: ({:.0f}%), Averaged loss : {:.4f} '.