| import torch
|
| import torch.nn as nn
|
| import torch.optim as optim
|
| from torch.utils.data import Dataset, DataLoader
|
| from transformers import BertTokenizer, BertForSequenceClassification
|
| import pandas as pd
|
| from tqdm import tqdm
|
| import os
|
| import time
|
|
|
|
|
| EPOCHS = 3
|
| BATCH_SIZE = 16
|
|
|
|
|
| device = "cuda" if torch.cuda.is_available() else "cpu"
|
| if torch.cuda.is_available():
|
| print(f"β
Using GPU: {torch.cuda.get_device_name(0)}")
|
| else:
|
| print("β No GPU found. Using CPU instead.")
|
| print(f"Using device: {device}")
|
|
|
|
|
| class YelpResponseDataset(Dataset):
|
| def __init__(self, file_path, tokenizer, max_length=256):
|
| self.data = pd.read_csv(file_path)
|
| self.tokenizer = tokenizer
|
| self.max_length = max_length
|
|
|
| def __len__(self):
|
| return len(self.data)
|
|
|
| def __getitem__(self, idx):
|
| text = self.data.iloc[idx]["text"]
|
| response = self.data.iloc[idx]["response"]
|
|
|
| tokens = self.tokenizer(
|
| text,
|
| truncation=True,
|
| padding="max_length",
|
| max_length=self.max_length,
|
| return_tensors="pt",
|
| )
|
|
|
| return tokens["input_ids"].squeeze(0), tokens["attention_mask"].squeeze(0), response
|
|
|
|
|
| class ResponseModel(nn.Module):
|
| def __init__(self):
|
| super(ResponseModel, self).__init__()
|
| self.bert = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=768)
|
|
|
| def forward(self, input_ids, attention_mask):
|
| outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
|
| return outputs.logits
|
|
|
|
|
| def train_response():
|
| tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
|
| train_dataset = YelpResponseDataset("../data/train.csv", tokenizer)
|
| train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
|
|
|
| model = ResponseModel().to(device)
|
| optimizer = optim.AdamW(model.parameters(), lr=3e-5)
|
| criterion = nn.MSELoss()
|
|
|
| os.makedirs("models", exist_ok=True)
|
| model_path = "models/response_model.pth"
|
|
|
|
|
| if os.path.exists(model_path):
|
| print(f"π Loading previous model weights from {model_path}...")
|
| model.load_state_dict(torch.load(model_path, map_location=device))
|
| else:
|
| print("π Starting training from scratch.")
|
|
|
| model.train()
|
|
|
|
|
| sample_batch = next(iter(train_loader))
|
| start_time_test = time.time()
|
| with torch.no_grad():
|
| _ = model(sample_batch[0].to(device), sample_batch[1].to(device))
|
| estimated_batch_time = time.time() - start_time_test
|
| estimated_time_per_epoch = estimated_batch_time * len(train_loader)
|
| total_estimated_time = estimated_time_per_epoch * EPOCHS
|
| print(f"β³ Estimated total training time: {total_estimated_time / 60:.2f} min")
|
|
|
| total_start_time = time.time()
|
|
|
| for epoch in range(EPOCHS):
|
| epoch_start_time = time.time()
|
| total_loss = 0
|
|
|
| for input_ids, attention_mask, response in tqdm(train_loader, desc=f"Training Epoch {epoch+1}/{EPOCHS}"):
|
| input_ids, attention_mask = input_ids.to(device), attention_mask.to(device)
|
|
|
| optimizer.zero_grad()
|
| response_pred = model(input_ids, attention_mask)
|
|
|
|
|
| loss = criterion(response_pred, torch.zeros_like(response_pred))
|
| loss.backward()
|
| optimizer.step()
|
|
|
| total_loss += loss.item()
|
|
|
| epoch_time = time.time() - epoch_start_time
|
| avg_epoch_time = (time.time() - total_start_time) / (epoch + 1)
|
| remaining_time = avg_epoch_time * (EPOCHS - (epoch + 1))
|
|
|
| print(f"π Epoch {epoch+1} Time: {epoch_time:.2f} sec | Remaining Time: {remaining_time/60:.2f} min")
|
| print(f"π― Epoch {epoch+1}, Response Model Loss: {total_loss:.4f}")
|
|
|
|
|
| torch.save(model.state_dict(), model_path)
|
| print(f"β
Model saved to '{model_path}' (Epoch {epoch+1})")
|
|
|
| total_training_time = time.time() - total_start_time
|
| print(f"π Training complete in {total_training_time/60:.2f} min!")
|
|
|
| if __name__ == "__main__":
|
| train_response()
|
|
|