Skip to content

Commit df9718b

Browse files
committed
Robert model
1 parent b1a2a2f commit df9718b

4 files changed

+269
-0
lines changed

RobertaRun.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from sklearn.model_selection import StratifiedKFold
2+
import pandas as pd
3+
from RobertaTweetModel import RobertaTweetModel
4+
import torch.optim as optim
5+
from utils.loss import loss
6+
from utils.roberta_get_train_val_loaders import roberta_get_train_val_loaders
7+
from RobertaTrainModel import roberta_train_model
8+
import torch
9+
10+
num_epochs = 3
11+
batch_size = 1
12+
seed_value = 28091997
13+
14+
torch.cuda.manual_seed(seed_value)
15+
torch.cuda.manual_seed_all(seed_value)
16+
torch.backends.cudnn.deterministic = True
17+
torch.backends.cudnn.benchmark = True
18+
skl = StratifiedKFold(n_splits=5, shuffle=True, random_state=seed_value)
19+
20+
train_df = pd.read_csv('./data/train.csv')
21+
train_df['text'] = train_df['text'].astype(str)
22+
train_df['selected_text'] = train_df['selected_text'].astype(str)
23+
24+
for fold, (train_idx, val_idx) in enumerate(skl.split(train_df, train_df.sentiment), start=1):
25+
print("========== Fold {} ========== ".format(fold))
26+
model = RobertaTweetModel()
27+
optimizer = optim.AdamW(model.parameters(), lr=3e-5, betas=(0.9, 0.999))
28+
loss = loss
29+
dataloader_dict = roberta_get_train_val_loaders(
30+
train_df, train_idx, val_idx, batch_size)
31+
roberta_train_model(
32+
model,
33+
dataloader_dict,
34+
loss,
35+
optimizer,
36+
num_epochs,
37+
f'./weights/roberta/roberta_fold_{fold}.bin'
38+
)

RobertaTrainModel.py

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import torch
2+
from utils.compute_jaccard_score import compute_jaccard_score
3+
import tqdm
4+
5+
6+
def roberta_train_model(model, dataloaders_dict, loss, optimizer, num_epochs, filename):
7+
model.cuda()
8+
9+
for epoch in range(num_epochs):
10+
# Mỗi epoch sẽ thực hiện 2 phase
11+
for phase in ['train', 'val']:
12+
# Nếu phase train thì huấn luyện, phase val thì tính loss và jaccard
13+
if phase == 'train':
14+
model.train()
15+
else:
16+
model.eval()
17+
18+
# Khởi tạo loss và jaccard
19+
epoch_loss = 0.0
20+
epoch_jaccard = 0.0
21+
22+
for data in tqdm.tqdm((dataloaders_dict[phase])):
23+
# Lấy thông tin dữ liệu
24+
ids = data['ids'].cuda()
25+
masks = data['masks'].cuda()
26+
tweet = data['tweet']
27+
offsets = data['offsets'].numpy()
28+
start_idx = data['start_idx'].cuda()
29+
end_idx = data['end_idx'].cuda()
30+
31+
# Reset tích lũy đạo hàm
32+
optimizer.zero_grad()
33+
34+
with torch.set_grad_enabled(phase == 'train'):
35+
start_logits, end_logits = model(ids, masks)
36+
loss_value = loss(
37+
start_logits, end_logits, start_idx, end_idx)
38+
39+
# nếu là phase train thì thực hiện lan truyền ngược
40+
# và cập nhật tham số
41+
if phase == 'train':
42+
loss_value.backward()
43+
optimizer.step()
44+
45+
epoch_loss += loss_value.item() * len(ids)
46+
47+
start_idx = start_idx.cpu().detach().numpy()
48+
end_idx = end_idx.cpu().detach().numpy()
49+
50+
start_logits = torch.softmax(
51+
start_logits, dim=1).cpu().detach().numpy()
52+
end_logits = torch.softmax(
53+
end_logits, dim=1).cpu().detach().numpy()
54+
55+
# Tính toán jaccard cho tất cả các câu
56+
for i in range(len(ids)):
57+
jaccard_score = compute_jaccard_score(
58+
tweet[i],
59+
start_idx[i],
60+
end_idx[i],
61+
start_logits[i],
62+
end_logits[i],
63+
offsets[i]
64+
)
65+
epoch_jaccard += jaccard_score
66+
67+
# Trung bình loss và jaccard
68+
epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)
69+
epoch_jaccard = epoch_jaccard / \
70+
len(dataloaders_dict[phase].dataset)
71+
72+
print("Epoch {}/{} | {:^5} | Loss: {:.4f} | Jaccard: {:.4f}".format(epoch +
73+
1, num_epochs, phase, epoch_loss, epoch_jaccard))
74+
torch.save(model.state_dict(), filename)

RobertaTweetDataset.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import torch
2+
import tokenizers
3+
import pandas as pd
4+
5+
class RobertaTweetDataset(torch.utils.data.Dataset):
6+
def __init__(self, df, max_len=128):
7+
# Dataframe dữ liệu
8+
self.df = df
9+
# độ dài tối đa của câu
10+
self.max_len = max_len
11+
# Nhãn
12+
self.labeled = 'selected_text' in df
13+
# Khởi tạo mã hóa BPE
14+
self.tokenizer = tokenizers.ByteLevelBPETokenizer(
15+
vocab_file='./roberta.base.torch/vocab.json',
16+
merges_file='./roberta.base.torch/merges.txt',
17+
lowercase=True,
18+
add_prefix_space=True
19+
)
20+
21+
def __len__(self):
22+
""" Trả về độ dài của DataFrame """
23+
return len(self.df)
24+
25+
def get_input_data(self, row):
26+
"""
27+
Tạo sample input cho 1 dòng dữ liệu
28+
- Input: <s><sentiment></s></s>token11 token12 ... </s><pad><pad>
29+
30+
"""
31+
# Thêm khoảng trắng vào đầu câu đầu vào
32+
tweet = " " + " ".join(row.text.lower().split())
33+
# Mã hóa BPE cho câu đầu vào
34+
encoding = self.tokenizer.encode(tweet)
35+
# {'positive': 1313, 'negative': 2430, 'neutral': 7974}
36+
sentiment_id = self.tokenizer.encode(row.sentiment).ids
37+
# 0 là đại diện cho token <s> và 2 là token </s>, 1 là token <pad>
38+
# Mã hóa câu đầu vào sang số
39+
ids = [0] + sentiment_id + [2, 2] + encoding.ids + [2]
40+
# offset là vị trí các token của câu ban đầu
41+
# Ví dụ (0,2) (2,3) (3,4) (4,9) ...
42+
offsets = [(0, 0)] * 4 + encoding.offsets + [(0, 0)]
43+
44+
# Thêm các token pad cho viền câu
45+
pad_len = self.max_len - len(ids)
46+
if pad_len > 0:
47+
ids += [1] * pad_len
48+
offsets += [(0,0)] * pad_len
49+
50+
ids = torch.tensor(ids)
51+
# Tạo mặt nạ, đánh dấu 1 cho toàn bộ câu đầu vào
52+
# Trừ các phần là <pad>
53+
masks = torch.where(ids != 1, torch.tensor(1), torch.tensor(0))
54+
offsets = torch.tensor(offsets)
55+
56+
return ids, masks, tweet, offsets
57+
58+
def get_target_idx(self, row, tweet, offsets):
59+
selected_text = " " + " ".join(row.selected_text.lower().split())
60+
61+
len_st = len(selected_text) - 1
62+
# Vị trí bắt đầu và kết thúc của selectec_text trong tweet
63+
idx0, idx1 = None, None
64+
65+
for ind in (i for i, e in enumerate(tweet) if e == selected_text[1]):
66+
if " " + tweet[ind:ind+len_st] == selected_text:
67+
idx0 = ind
68+
idx1 = ind + len_st - 1
69+
70+
# Đánh dấu những vị trí mà có ký tự của selected_text là 1
71+
char_targets = [0] * len(tweet)
72+
if idx0 != None and idx1 != None:
73+
for ct in range(idx0, idx1 + 1):
74+
char_targets[ct] = 1
75+
76+
# Đánh dấu những token chứa selected_text
77+
target_idx = []
78+
for j, (offset1, offset2) in enumerate(offsets):
79+
if sum(char_targets[offset1:offset2]) > 0:
80+
target_idx.append(j)
81+
82+
# Token bắt đầu và token kết thúc của selected_text
83+
start_idx = target_idx[0]
84+
end_idx = target_idx[-1]
85+
86+
return start_idx, end_idx
87+
88+
def __getitem__(self, index):
89+
"""
90+
Chuyển đổi hàng dữ liệu thứ index trong dataFrame
91+
sang dữ liệu đầu vào của mô hình
92+
Các thuộc tính cho dữ liệu đầu vafo:
93+
- ids
94+
- masks
95+
- tweet
96+
- offsets
97+
- start_idx
98+
- end_idx
99+
"""
100+
data = {}
101+
row = self.df.iloc[index]
102+
103+
ids, masks, tweet, offsets = self.get_input_data(row)
104+
data['ids'] = ids
105+
data['masks'] = masks
106+
data['tweet'] = tweet
107+
data['offsets'] = offsets
108+
109+
if self.labeled:
110+
start_idx, end_idx = self.get_target_idx(row, tweet, offsets)
111+
data['start_idx'] = start_idx
112+
data['end_idx'] = end_idx
113+
114+
return data

RobertaTweetModel.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from torch import nn
2+
import torch
3+
from transformers import RobertaConfig, RobertaModel
4+
5+
6+
class RobertaTweetModel(nn.Module):
7+
def __init__(self):
8+
super(RobertaTweetModel, self).__init__()
9+
config = RobertaConfig.from_pretrained(
10+
'./roberta.base.torch/config.json',
11+
output_hidden_states=True
12+
)
13+
self.roberta = RobertaModel.from_pretrained(
14+
'./roberta.base.torch/pytorch_model.bin',
15+
config=config
16+
)
17+
18+
self.dropout = nn.Dropout(0.5)
19+
self.fc = nn.Linear(config.hidden_size, 2)
20+
nn.init.normal_(self.fc.weight, std=0.2)
21+
nn.init.normal_(self.fc.bias, 0)
22+
23+
def forward(self, input_ids, attention_mask):
24+
# Đầu vào Roberta cần chỉ số các token (input_ids)
25+
# Và attention_mask (Mặt nạ biểu diễn câu 0 = pad, 1 = otherwise)
26+
_, _, hs = self.roberta(input_ids, attention_mask)
27+
28+
# len(hs) = 13 tensor, mỗi tensor shape là (1, 128, 768)
29+
x = torch.stack([hs[-1], hs[-2], hs[-3], hs[-4]])
30+
# x shape (4,1,128,768)
31+
x = torch.mean(x, 0)
32+
# x shape (1,128,768)
33+
x = self.dropout(x)
34+
x = self.fc(x)
35+
# x shape (1,128,2)
36+
start_logits, end_logits = x.split(1, dim=-1)
37+
38+
# Nếu số chiều cuối là 1 thì bỏ đi (1,128,1) -> (1,128)
39+
# Ví dụ (AxBxCX1) --> size (AxBxC)
40+
start_logits = start_logits.squeeze(-1)
41+
end_logits = end_logits.squeeze(-1)
42+
43+
return start_logits, end_logits

0 commit comments

Comments
 (0)