ChatBot_Yte / src /NLU /train_intent.py
giangpvg's picture
Upload ChatBot Project
80a6b9a
Raw
History Blame Contribute Delete
5.83 kB
import os
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torch.optim import AdamW
from transformers import get_linear_schedule_with_warmup
from preprocess import load_data_and_encoders
from model_intent import JointPhoBERTModel
from sklearn.metrics import accuracy_score
def train():
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(current_dir))
data_dir = os.path.join(project_root, 'data')
train_path = os.path.join(data_dir, 'train.json')
dev_path = os.path.join(data_dir, 'dev.json')
test_path = os.path.join(data_dir, 'test.json')
model_name = "vinai/phobert-base-v2"
batch_size = 16
epochs = 10
max_len = 128
learning_rate = 2e-5
print("1. Loading Data and Tokenizer...")
train_dataset, dev_dataset, test_dataset, label_encoder, tokenizer = load_data_and_encoders(
train_path, dev_path, test_path, model_name, max_len
)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = DataLoader(dev_dataset, batch_size=batch_size)
test_loader = DataLoader(test_dataset, batch_size=batch_size)
num_intents = label_encoder.get_num_intents()
num_ner_tags = label_encoder.get_num_ner_tags()
print(f" -> Num Intents: {num_intents}, Num NER Tags: {num_ner_tags}")
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"2. Using device: {device}")
print("3. Initializing Model...")
model = JointPhoBERTModel(model_name, num_intents, num_ner_tags)
model.to(device)
intent_criterion = nn.CrossEntropyLoss()
# Bỏ qua index -100 (padding và các subwords phụ) khi tính loss cho NER
ner_criterion = nn.CrossEntropyLoss(ignore_index=-100)
optimizer = AdamW(model.parameters(), lr=learning_rate)
total_steps = len(train_loader) * epochs
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=total_steps)
best_val_loss = float('inf')
print("4. Starting Training Loop...")
for epoch in range(epochs):
print(f"\\n========== Epoch {epoch+1}/{epochs} ==========")
model.train()
total_train_loss = 0
total_intent_loss = 0
total_ner_loss = 0
for step, batch in enumerate(train_loader):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
intent_labels = batch['intent_label'].to(device)
ner_labels = batch['ner_labels'].to(device)
optimizer.zero_grad()
intent_logits, ner_logits = model(input_ids, attention_mask)
loss_intent = intent_criterion(intent_logits, intent_labels)
# Tính Loss NER (reshape lại logits và labels)
active_logits = ner_logits.view(-1, num_ner_tags)
active_labels = ner_labels.view(-1)
loss_ner = ner_criterion(active_logits, active_labels)
# Tổng hợp Loss
loss = loss_intent + loss_ner
total_train_loss += loss.item()
total_intent_loss += loss_intent.item()
total_ner_loss += loss_ner.item()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
scheduler.step()
if (step + 1) % 50 == 0:
print(f" Step {step+1}/{len(train_loader)} | Loss: {loss.item():.4f} (Intent: {loss_intent.item():.4f}, NER: {loss_ner.item():.4f})")
avg_train_loss = total_train_loss / len(train_loader)
print(f"-> Average Training Loss: {avg_train_loss:.4f} (Intent: {total_intent_loss/len(train_loader):.4f}, NER: {total_ner_loss/len(train_loader):.4f})")
# Validation
model.eval()
total_val_loss = 0
all_intent_preds = []
all_intent_labels = []
with torch.no_grad():
for batch in dev_loader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
intent_labels = batch['intent_label'].to(device)
ner_labels = batch['ner_labels'].to(device)
intent_logits, ner_logits = model(input_ids, attention_mask)
loss_intent = intent_criterion(intent_logits, intent_labels)
active_logits = ner_logits.view(-1, num_ner_tags)
active_labels = ner_labels.view(-1)
loss_ner = ner_criterion(active_logits, active_labels)
loss = loss_intent + loss_ner
total_val_loss += loss.item()
intent_preds = torch.argmax(intent_logits, dim=1).cpu().numpy()
all_intent_preds.extend(intent_preds)
all_intent_labels.extend(intent_labels.cpu().numpy())
avg_val_loss = total_val_loss / len(dev_loader)
val_intent_acc = accuracy_score(all_intent_labels, all_intent_preds)
print(f"-> Validation Loss: {avg_val_loss:.4f} | Intent Accuracy: {val_intent_acc:.4f}")
if avg_val_loss < best_val_loss:
best_val_loss = avg_val_loss
ckpt_dir = os.path.join(project_root, 'src', 'checkpoints')
os.makedirs(ckpt_dir, exist_ok=True)
ckpt_path = os.path.join(ckpt_dir, "best_joint_model.pth")
torch.save(model.state_dict(), ckpt_path)
print(f"=> Saved new best model to '{ckpt_path}'")
if __name__ == "__main__":
train()