Spaces:
Running
Running
Upload 6 files
Browse files- API.csv +31 -0
- Mbert.py +64 -0
- Model/classifier.pt +3 -0
- app.py +604 -0
- config.py +70 -0
- predictor.py +73 -0
API.csv
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
STT,SUPPORTED,REFUTED,NEI
|
2 |
+
1,AIzaSyBAIFOiKcFbwTXRazRAoakZhkF6R4W3j1I,AIzaSyBrECiTuLl45qxb-NUqL_6wuUbRqPXL3lo,AIzaSyAfdRKTphVa6PJ15G3L_i02JNEP2TaCq5w
|
3 |
+
2,AIzaSyC-NtmmiZWojXAD4XsuRsTDzaUwMY_iiNQ,AIzaSyBSKVP3Y4Lx3H36YgsBrdUxKiAVBakVUFs,AIzaSyB_50Q2YUWABz8oDrbEcRgMlhryQvmWgEw
|
4 |
+
3,AIzaSyALckbqi4da-_FQJIK8P3p5gcijMC9F5PY,AIzaSyCq1uLPWoDo1Kk-JAT8AQBuEHk6QG1z2IM,AIzaSyDObWlzu1zZP_zd0FW6FYrpiXxcGOhLi5Y
|
5 |
+
4,AIzaSyBrvs8kMpDuz-kgJ3oIghDgF8s3xmT7570,AIzaSyBJEBMWtbpOYH-52pbQf2GyKx78c9_h9Kk,AIzaSyAA1EAklM-bq-5MfcI9Y2kt63M1Zebvx00
|
6 |
+
5,AIzaSyAEqolxPN1NpItPfPLBxVLEBUzKWrc0xeA,AIzaSyDvQY1JJOAAXmsx8_WlPqrLgVqqvd3lud4,AIzaSyBRWS4IyakT5Gu8__SSZADvar5v6L-hy_k
|
7 |
+
6,AIzaSyBndL2HtHxawrpI7QTiIq2EmFG-ukCYfag,AIzaSyDCRts0ztEdv6WWHhQ3ZQyDnxS6L2ILoQk,AIzaSyBMCSJjjK3rKODi1y9eJd2glusztqrDj4g
|
8 |
+
7,AIzaSyAaPAwyHZls4oqh0uWYjXazrkAPIm1QzQY,AIzaSyB6v_mH_RD0mWusok6EhvvLYQqh-vDg5Js,AIzaSyDW0NIQJJqge-Qqtey9vgQb4jMqsu0IssQ
|
9 |
+
8,AIzaSyAAbn0oXtQX-E3RSFiNFcwLZKF1Q00Ch6c,AIzaSyC5ebttT7Vhj-gXK4TPvATGCPV9AJfVF1E,AIzaSyAxwjSoGBmDY-l9DtDWTvD3xUDlWwWGOFY
|
10 |
+
9,AIzaSyB6WjKVrItKY6rumH50fXF_n43fSNQnjJQ,AIzaSyDloEA_1DukyeLS79OfbL-uNDfqLdrqb7I,AIzaSyBXM7PGRU1Tp1XugljM-xwLcnO4OEsRgGY
|
11 |
+
10,AIzaSyDvaj6DTpYoBDxoKZxB5_Z2XmjObnHqY_U,AIzaSyBi3rMG4kidCGOEugZ8nJf_j0zvhbNoHB0,AIzaSyALTPwGXKWUZcuPl5Wnug0_XHLJJJinn4Q
|
12 |
+
11,AIzaSyCsYyLWelVKeCLBOyyMQz5f-3exvJDkoHM,AIzaSyAEkM4hqzO9Fgb-Dwy9mzdN4cP-G2yZ41c,AIzaSyCuti6ldTheLMFsO3qKjtUpen1tFQEzRIg
|
13 |
+
12,AIzaSyBZAFsZ1hg1CVaGdZ-L4H0A3gj74_iwjRc,AIzaSyB_TjmCt_xX_IGL6Pl6gS1qxHlQGYQyNKo,AIzaSyD9NMHc6p1hJ-kjAokMnVLn-IRE8ZhW3Do
|
14 |
+
13,AIzaSyAmpGtMQQgJnhqjvxlSSUiXEKAKwSQN8uE,AIzaSyBeXXs3rxxwjcYwCJmVJKewoiEmjMdZVx4,AIzaSyAdgYJF8ajpxjs8WylFjba6fZIKc-9VTLE
|
15 |
+
14,AIzaSyBBA-oQZr4u1GQ-N21x04-StYbuX_z8jAg,AIzaSyCZ-HeRuZp1HO4sPqAFwR7O0VVawVyc3pk,AIzaSyAnkRp0H4MjxL1ntQikaQC-P1PAvIXcqzI
|
16 |
+
15,AIzaSyAhuapkaBUdef7qnGyuKEwhCENonfiF1aw,AIzaSyA0Bm1XF0_2GPH7H4UkgZeYeSTAq4jc5WA,AIzaSyBGguLyCllMuPIXqLFApw-If8OGLrNdeQk
|
17 |
+
16,AIzaSyDzo9LWKvQOC5Pej-kM2Kt8cywxLDpcNwQ,AIzaSyCMbsNFMeiSNi90hDrw7WKdHVi1Sh-es74,AIzaSyCseI95TfJJPJja8wXK5L1BPjY7lWQ_KvA
|
18 |
+
17,AIzaSyDWgOjUbtN4dTSHpW3aAJngpQXU-U8-CJc,AIzaSyB0XCw1oTs_aSD-3_KcIBr3StUQyFGRGYI,AIzaSyAmKRn79iRVii-5GxdotpFqTgI9NQzI6P8
|
19 |
+
18,AIzaSyAcx86oHI8h0NhsjNBNQveV8DpxnVtG7wU,AIzaSyB5olPLuqJvzuIjP10Ykm2wVYZk83lr9m4,AIzaSyAZ8dp5Ethg4aTBlkq6QwwYU_ac8rgbnEs
|
20 |
+
19,AIzaSyAdiDGoVCrdGK4sZ_bgONbNDlvctEv0h_M,AIzaSyAAEQE6mb8pmnIl0jO-_f-1XkNwpg8Pvpc,AIzaSyDUXnxdRkdAr8fu3aVSIMtKmXrAVdGURz4
|
21 |
+
20,AIzaSyBrDa7tSh5SfIshD0jZG-meFO-iCxEz70I,AIzaSyAA6esL9qhK14qGR6E7hWxqkUrouitQxEU,AIzaSyALD5rmLUvrEEzJkI6PrzdzhwuNjXP0CyY
|
22 |
+
21,AIzaSyBi0r0Bk1oHSH0FZ7_TZKSE7rnMIzk8zGA,AIzaSyAk4-cIn5Vy6C5o6opXocBksLpQ3cjQybQ,AIzaSyDjadl0cgzzbDAEEr7RA38dqYKFYNyujeY
|
23 |
+
22,AIzaSyD4-MWH63DYrZHaqKTQ5K689co1cBcArGw,AIzaSyDBTOkf5Dzf6MvACV9yAHLeUNAbB_SewKU,AIzaSyDg4FAu8OjYCa6u9NWhJBI4KgnJoB5QxhI
|
24 |
+
23,AIzaSyBDVJ2NRX3D4T3jG3fxGrO7jqJrFbsg_gc,AIzaSyClqiFZ2ImNx8wQvo90wVnUUxmAlcZm58c,AIzaSyDku5uiYfILBEOoeE_A6QyI03yUL3JIWbw
|
25 |
+
24,AIzaSyB9AES6QUUwpKDpEt3eln9R6sHUZBmUz6A,AIzaSyD6YkUFUrN9ntGtvDkMVRU0s4iojFiR7X8,AIzaSyDzK8C2QcrBPXRbMb38zVVCd2Qr25dCQrU
|
26 |
+
25,AIzaSyBaL-9z24cmbTAHXAh0zbKbcx9FLaHfepg,AIzaSyCun__LfDDNQBfUdF5lZEpKLa83gyhsR0U,AIzaSyDzyt-8tuRAqQ0Ff28-Wi9Aw-BAAxEgJEc
|
27 |
+
26,AIzaSyAyu4dbWSMeh-BSnEXSwCP505Zlbsu3oWg,AIzaSyBfZOqldIDNoEQAlUpU26xNgy_rCvdUbW0,AIzaSyCmS4AqIlxktL_VGdLNxA3f-2rzFgPU5W8
|
28 |
+
27,AIzaSyBFylav_tGcQEVUZ7HUyHUAmEHq2JmnzWQ,AIzaSyA_Cz1RH-ru74co60LOqOnLMpG_Jo5Mq5A,AIzaSyCMSP--GpHzzsLv5CeAftTeA2pdDsXv1Fk
|
29 |
+
28,AIzaSyAQRh5KhN_nn5Hzr5syMhShqLf301H0Ozg,AIzaSyDVjp8IzIGQ-hOnSiFnuPa02VcsoEwQSQs,AIzaSyAI48UGAr82YuiXU2mBIukdOms_gx-N0rU
|
30 |
+
29,AIzaSyBf3Tz6jCToK7mckfTbuKylvOeum9u25RA,AIzaSyDOhVzfL-lEHdqmFJ2wkYedbSyaa1TV7hw,AIzaSyD40fHhbjg0iGcqitqp461Mj2mnzLCqVwc
|
31 |
+
30,AIzaSyDYecw8ln5eNrIkLC7N2FxD9hXnrWSXaEI,AIzaSyAk8TMOgleWNS6UQqN4SSNV3orAreRPEeM,AIzaSyDBsGgdOvSyKPXU1QGlTA_ijCqNpAd2BP8
|
Mbert.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
from torch import nn
|
3 |
+
from torch.utils.data import Dataset
|
4 |
+
|
5 |
+
class SentencePairDataset(Dataset):
|
6 |
+
def __init__(self, sentence_pairs, labels, tokenizer, max_length):
|
7 |
+
self.sentence_pairs = sentence_pairs
|
8 |
+
self.labels = labels
|
9 |
+
self.tokenizer = tokenizer
|
10 |
+
self.max_length = max_length
|
11 |
+
|
12 |
+
def __len__(self):
|
13 |
+
return len(self.sentence_pairs)
|
14 |
+
|
15 |
+
def __getitem__(self, idx):
|
16 |
+
sentence, context = self.sentence_pairs[idx]
|
17 |
+
label = self.labels[idx]
|
18 |
+
|
19 |
+
# Calculate max lengths for each part
|
20 |
+
max_context_length = int(self.max_length * 0.8) # 80% for context
|
21 |
+
max_claim_length = int(self.max_length * 0.2) # 20% for claim
|
22 |
+
|
23 |
+
# Encode claim and context separately
|
24 |
+
encoding = self.tokenizer.encode_plus(
|
25 |
+
sentence,
|
26 |
+
text_pair=context,
|
27 |
+
add_special_tokens=True,
|
28 |
+
max_length=self.max_length,
|
29 |
+
padding="max_length",
|
30 |
+
return_attention_mask=True,
|
31 |
+
return_tensors="pt",
|
32 |
+
truncation=True,
|
33 |
+
truncation_strategy='longest_first' # Truncate the longer text first
|
34 |
+
)
|
35 |
+
|
36 |
+
return {
|
37 |
+
"input_ids": encoding["input_ids"].flatten(),
|
38 |
+
"attention_mask": encoding["attention_mask"].flatten(),
|
39 |
+
"label": torch.tensor(label, dtype=torch.long),
|
40 |
+
}
|
41 |
+
|
42 |
+
class MBERTClassifier(nn.Module):
|
43 |
+
def __init__(self, mbert, num_classes):
|
44 |
+
super(MBERTClassifier, self).__init__()
|
45 |
+
self.mbert = mbert
|
46 |
+
self.layer_norm = nn.LayerNorm(self.mbert.config.hidden_size)
|
47 |
+
self.dropout = nn.Dropout(0.2)
|
48 |
+
self.batch_norm = nn.BatchNorm1d(self.mbert.config.hidden_size)
|
49 |
+
self.linear = nn.LazyLinear(num_classes)
|
50 |
+
self.activation = nn.ELU()
|
51 |
+
|
52 |
+
def forward(self, input_ids, attention_mask):
|
53 |
+
_, pooled_output = self.mbert(input_ids=input_ids, attention_mask=attention_mask, return_dict=False)
|
54 |
+
norm_output = self.layer_norm(pooled_output)
|
55 |
+
batch_norm_output = self.batch_norm(norm_output)
|
56 |
+
logits = self.linear(batch_norm_output)
|
57 |
+
activated_output = self.activation(logits)
|
58 |
+
dropout_output = self.dropout(activated_output)
|
59 |
+
return dropout_output
|
60 |
+
|
61 |
+
def predict_proba(self, input_ids, attention_mask):
|
62 |
+
logits = self.forward(input_ids, attention_mask)
|
63 |
+
probabilities = torch.softmax(logits, dim=-1)
|
64 |
+
return probabilities
|
Model/classifier.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:72343f29aa14a07f9835f5d5e7567690b10df6783d625badf42d4c423be4454a
|
3 |
+
size 711526350
|
app.py
ADDED
@@ -0,0 +1,604 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Import custom modules for prediction
|
2 |
+
from predictor import predict
|
3 |
+
import google.generativeai as genai
|
4 |
+
import streamlit as st
|
5 |
+
import pandas as pd
|
6 |
+
import wikipedia
|
7 |
+
import json
|
8 |
+
import regex as re
|
9 |
+
from typing import List, Dict, Any
|
10 |
+
import google.generativeai as genai
|
11 |
+
import warnings
|
12 |
+
import logging
|
13 |
+
import time
|
14 |
+
|
15 |
+
# Disable warnings and logging
|
16 |
+
warnings.filterwarnings('ignore')
|
17 |
+
logging.disable(logging.WARNING)
|
18 |
+
def split_sentences_vietnamese(text):
|
19 |
+
# Thay thế dấu ba chấm tạm thời
|
20 |
+
text = text.replace("...", " @@@ ")
|
21 |
+
|
22 |
+
# Chia câu bằng các dấu chấm, chấm hỏi, chấm than
|
23 |
+
sentences = re.split(r'(?<!\w\.\w.)(?<!\w\.\w)(?<!\w\.)[.!?]\s+', text)
|
24 |
+
|
25 |
+
# Khôi phục dấu ba chấm và làm sạch dữ liệu
|
26 |
+
sentences = [s.replace(" @@@ ", "...").strip() for s in sentences if s.strip()]
|
27 |
+
|
28 |
+
return sentences
|
29 |
+
# Set Wikipedia language to Vietnamese
|
30 |
+
wikipedia.set_lang("vi")
|
31 |
+
|
32 |
+
# Character to number mapping for Vietnamese alphabet
|
33 |
+
char_to_num = {
|
34 |
+
'A': 1, 'Ă': 2, 'Â': 3, 'B': 4, 'C': 5, 'D': 6, 'Đ': 7, 'E': 8, 'Ê': 9, 'G': 10,
|
35 |
+
'H': 11, 'I': 12, 'K': 13, 'L': 14, 'M': 15, 'N': 16, 'O': 17, 'Ô': 18, 'Ơ': 19,
|
36 |
+
'P': 20, 'Q': 21, 'R': 22, 'S': 23, 'T': 24, 'U': 25, 'Ư': 26, 'V': 27, 'X': 28, 'Y': 29
|
37 |
+
}
|
38 |
+
|
39 |
+
class ClaimGenerator:
|
40 |
+
"""Class to handle advanced claim generation with different APIs."""
|
41 |
+
|
42 |
+
def __init__(self, claim_type: str, api_keys_file: str = "API.csv"):
|
43 |
+
self.claim_type = claim_type
|
44 |
+
self.api_keys = self._load_api_keys(api_keys_file)
|
45 |
+
self.current_key_index = 0
|
46 |
+
self._configure_api()
|
47 |
+
API_KEYS = {
|
48 |
+
"SUPPORTED": "AIzaSyCWoLgqVB8WLM7JPQMz4u3_jIOsHVs_SkA",
|
49 |
+
"REFUTED": "AIzaSyBe4ycn90utUlwhbD-fiyHJCitsPqVUO20",
|
50 |
+
"NEI": "AIzaSyBqcQXJlF18yItimSQRtVcKCSpxguLa360"
|
51 |
+
}
|
52 |
+
|
53 |
+
self.SCHEMAS = {
|
54 |
+
"SUPPORTED": {
|
55 |
+
"type": "object",
|
56 |
+
"properties": {
|
57 |
+
"claims": {
|
58 |
+
"type": "array",
|
59 |
+
"items": {
|
60 |
+
"type": "string"
|
61 |
+
},
|
62 |
+
"description": """
|
63 |
+
Câu nhận định đúng với các thông tin và số liệu được trích xuất trực tiếp từ context,yêu cầu phức tạp và dễ bị nhầm lẫn thành nhãn REFUTED, kết hợp nhiều kỹ thuật biến đổi:
|
64 |
+
Yêu cầu:
|
65 |
+
- Thông tin phải chính xác 100% so với context
|
66 |
+
- Không được thêm thông tin ngoài context
|
67 |
+
- Có thể kết hợp nhiều thông tin từ các phần khác nhau của context
|
68 |
+
- Phải có thể verify được từ context
|
69 |
+
Gợi ý cách đặt câu:
|
70 |
+
- Thay thế các bằng các từ đồng nghĩa
|
71 |
+
- Thay thế các lượng từ có cùng ý nghĩa
|
72 |
+
- Chuyển từ chủ động thành bị động & ngược lại
|
73 |
+
- Dùng từ trái nghĩa + phủ định
|
74 |
+
- Lược bỏ những bổ ngữ "không cần thiết" (không làm thay đổi nội dung của câu)
|
75 |
+
- Chuyển đổi danh từ, tân ngữ thành 1 mệnh đề quan hệ
|
76 |
+
- Tạo câu tiền giả định
|
77 |
+
Ví dụ:
|
78 |
+
VD1:
|
79 |
+
Claim: Quân Hán của Hàn Tín đã hủy diệt quân Triệu ở trận Tình Hình.
|
80 |
+
Context: ^ Năm 204 TCN, Trận Tỉnh Hình (trận Bối Thủy - Hàn Tín phá Triệu), quân nước Hán do danh tướng Hàn Tín chỉ huy đập tan nát quân Triệu của tướng Trần Dư.
|
81 |
+
VD2:
|
82 |
+
Claim: Trong quá trình làm việc nếu có tương tác với những vật dụng dính virus thì đều có nguy cơ nhiễm bệnh.
|
83 |
+
Context: Nhân viên Y tế: Bất kỳ nhân viên Y tế nào (Bác sĩ, Nha sỹ, Y tá, Y công.) cũng có thể bị nhiễm Viêm gan siêu vi C do tiếp xúc với bệnh phẩm chứa siêu vi trong quá trình làm việc.
|
84 |
+
VD3:
|
85 |
+
Claim: Trần Bảo dẫn quân sang cứu viện phủ thành Đại La nhưng thất bại.
|
86 |
+
Context: Năm 931, Dương Đình Nghệ là tướng cũ của Khúc Hạo đem quân đánh phủ thành Đại La, lại đánh tan quân Nam Hán do Trần Bảo dẫn sang cứu viện, giết Trần Bảo và tự xưng là Tiết độ sứ.
|
87 |
+
VD4:
|
88 |
+
Claim: Gan bị xơ sẽ không dễ dàng khôi phục lại.
|
89 |
+
Context: Khi đã xơ, gan khó hồi phục lại, cho dù tình trạng viêm có thuyên giảm. Vì vậy, các thầy thuốc khuyên nên điều trị sớm nhằm ngăn ngừa hoặc làm chậm tiến triển sang giai đoạn xơ gan.
|
90 |
+
VD5:
|
91 |
+
Claim: Nền văn minh này đóng góp một trong những văn bản lâu đời nhất thế giới.
|
92 |
+
Context: Các văn bản lịch sử văn hóa đã góp phần vào sự tồn tại của nền văn minh này. Một trong những văn bản cổ nhất trên thế giới, có tên Sử thi Gilgamesh, có nguồn gốc từ nền văn minh này.
|
93 |
+
VD6:
|
94 |
+
Claim: Sau khi thua trận trước quân Hán, Hai Bà Trưng đã tuẫn tiết.
|
95 |
+
Context: Năm 43, nhà Hán sai Phục ba tướng quân Mã Viện đem quân sang tái chiếm và đàn áp, Hai Bà Trưng chống không nổi phải rút về giữ Cấm Khê rồi tự vẫn ở sông Hát. Dân địa phương lập đền thờ ở Hát Giang.
|
96 |
+
"""
|
97 |
+
}
|
98 |
+
},
|
99 |
+
"required": ["claims"]
|
100 |
+
},
|
101 |
+
"REFUTED": {
|
102 |
+
"type": "object",
|
103 |
+
"properties": {
|
104 |
+
"claims": {
|
105 |
+
"type": "array",
|
106 |
+
"items": {
|
107 |
+
"type": "string"
|
108 |
+
},
|
109 |
+
"description": """
|
110 |
+
Câu nhận định sai, mâu thuẫn với thông tin trong context, tinh vi, khó phát hiện và dễ bị nhầm lẫn thành nhãn SUPPORTED:
|
111 |
+
Yêu cầu:
|
112 |
+
- Phải sử dụng cùng chủ đề và phạm vi với context
|
113 |
+
- Thông tin hoặc số liệu phải mâu thuẫn rõ ràng với context
|
114 |
+
- Có thể thay đổi số liệu, sự kiện, hoặc mối quan hệ
|
115 |
+
- Phải có thể verify được là sai dựa trên context
|
116 |
+
Gợi ý cách đặt câu:
|
117 |
+
- Xáo trộn các giải thích của các đối tượng với nhau
|
118 |
+
- Thay thế bằng các từ trái nghĩa
|
119 |
+
- Thay thế các từ ngữ trong cùng lĩnh vực
|
120 |
+
- Biến đổi các thông số, đơn vị của các đối tượng
|
121 |
+
Ví dụ:
|
122 |
+
VD1:
|
123 |
+
Claim: Các phụ âm của phương ngữ California của tiếng Anh Mỹ, và của chuẩn RP được mô tả qua bảng phía trên.
|
124 |
+
Context: Đa số phương ngữ tiếng Anh sở hữu cùng 24 phụ âm giống nhau. Bảng bên dưới thể hiện các phụ âm của phương ngữ California của tiếng Anh Mỹ, và của chuẩn RP.
|
125 |
+
VD2:
|
126 |
+
Claim: Mặc dù đã xảy ra sự kiện đắm tàu Amphitrite vào năm 1698 nhưng quần đảo vẫn không một ai biết đến và ngày càng bị lu mờ đi.
|
127 |
+
Context: Năm 1698: Quần đảo trở nên nổi tiếng trong các biên niên sử hàng hải với vụ đắm tàu Amphitrite dưới thời vua Louis XIV trong khi đi từ Pháp sang Trung Quốc.
|
128 |
+
VD3:
|
129 |
+
Claim: Lí do để quần đảo trở nên nổi tiếng và được nhiều người biết đến trong các biên niên sử của hàng hải là vụ đắm tàu Amphitrite vào năm 1700.
|
130 |
+
Context: Năm 1698: Quần đảo trở nên nổi tiếng trong các biên niên sử hàng hải với vụ đắm tàu Amphitrite dưới thời vua Louis XIV trong khi đi từ Pháp sang Trung Quốc.
|
131 |
+
VD4:
|
132 |
+
Claim: Trôi dạt lục địa đã đẩy toàn bộ bề mặt đất liền lại với nhau.
|
133 |
+
Context: Trôi dạt lục địa đã tái định hình thể cho các đại dương của Trái Đất, kết hợp và chia cắt các đại dương cổ để tạo ra các đại dương như hiện nay. Các đại dương cổ có:
|
134 |
+
VD5:
|
135 |
+
Claim: Các tài liệu ghi về thời kỳ bắc thuộc cuối cùng của Đại Việt đã biến mất hoàn toàn từ xưa cho tới nay.
|
136 |
+
Context: Annam Lưu trữ 2006-12-03 tại Wayback Machine, thu thập các đoạn tài liệu Minh thực lục (明實錄) của nhà Minh về Đại Việt, trong đó có phần về thời kỳ bắc thuộc lần cuối.
|
137 |
+
VD6
|
138 |
+
Claim: Hiện tượng mô não mất quyền kiểm soát trong cơ thể là vì bệnh thiếu máu cục bộ khiến máu tràn một lượng lớn vào não.
|
139 |
+
Context: Trong đột quỵ do thiếu máu cục bộ, lượng máu cung cấp cho một phần não bị giảm, dẫn đến rối loạn chức năng của mô não ở khu vực đó. Có bốn lý do tại sao điều này có thể xảy ra:
|
140 |
+
"""
|
141 |
+
}
|
142 |
+
},
|
143 |
+
"required": ["claims"]
|
144 |
+
},
|
145 |
+
"NEI": {
|
146 |
+
"type": "object",
|
147 |
+
"properties": {
|
148 |
+
"claims": {
|
149 |
+
"type": "array",
|
150 |
+
"items": {
|
151 |
+
"type": "string"
|
152 |
+
},
|
153 |
+
"description": """
|
154 |
+
Câu có thông tin không thể xác minh tính chính xác chỉ dựa vào context, có khả năng gây nhầm lẫn với SUPPORTED hoặc REFUTED:
|
155 |
+
|
156 |
+
Yêu cầu cơ bản:
|
157 |
+
- Phải liên quan chặt chẽ đến chủ đề chính của context
|
158 |
+
- Có ít nhất 1 thông tin không được có trong context
|
159 |
+
- Không được mâu thuẫn trực tiếp với context
|
160 |
+
- Không thể xác định được đúng sai chỉ dựa vào context
|
161 |
+
- Phải tạo ra sự mơ hồ và không chắc chắn cao
|
162 |
+
Gợi ý cách đặt câu:
|
163 |
+
- Tạo câu một phần thông tin liên quan đến context, nhưng đã thêm vào những chi tiết, suy luận hoặc kết luận không được hỗ trợ trực tiếp bởi context.
|
164 |
+
Ví dụ:
|
165 |
+
VD1:
|
166 |
+
Claim: Thời kỳ bắc thuộc lần cuối không được biết đến cho tới khi Annam lưu trữ xuất hiện.
|
167 |
+
Context: Annam Lưu trữ 2006-12-03 tại Wayback Machine, thu thập các đoạn tài liệu Minh thực lục (明實錄) của nhà Minh về Đại Việt, trong đó có phần về thời kỳ bắc thuộc lần cuối.
|
168 |
+
VD2:
|
169 |
+
Claim: Phát triển du lịch là mục tiêu phấn đấu của tỉnh Nghệ An.
|
170 |
+
Context: Nghệ An còn lưu giữ được nhiều di tích văn hoá lịch sử, nhiều danh lam thắng cảnh, lễ hội văn hoá truyền thống - đó là những yếu tố thuận lợi giúp cho du lịch Nghệ Antriển.
|
171 |
+
VD3:
|
172 |
+
Claim: Quân đội Ba Tư đông hơn nhưng vẫn không địch lại người La Mã.
|
173 |
+
Context: ^ Năm 627, Trận Nineveh (trong cuộc Chiến tranh La Mã-Ba Tư), quân Đông La Mã do đích thân Hoàng đế Heraclius thống suất đánh thắng quân Ba Tư do Đại tướng Rhahzadh cầm đầu.
|
174 |
+
VD4:
|
175 |
+
Claim: Bệnh phẩm chứa siêu vi viêm gan C được đề xuất nên bị loại bỏ hoàn toàn.
|
176 |
+
Context: Nhân viên Y tế: Bất kỳ nhân viên Y tế nào (Bác sĩ, Nha sỹ, Y tá, Y công.) cũng có thể bị nhiễm Viêm gan siêu vi C do tiếp xúc với bệnh phẩm chứa siêu vi trong quá trình làm việc.
|
177 |
+
VD5:
|
178 |
+
Claim: Thập Nhị Chi là tính toán thời gian theo 12 con vật.
|
179 |
+
Context: Tương truyền ngày xưa có một người tên Đại Nhiêu đã lập ra Thập Can và Thập Nhị Chi để giúp người ta tính toán thời gian. Việc tính giờ cũng có liên quan đến tập tính của 12 loài vật:
|
180 |
+
VD5:
|
181 |
+
Claim: Nghệ An là một tỉnh nằm ở phía bắc sông Lam.
|
182 |
+
Context: Năm 1831, vua Minh Mệnh chia trấn Nghệ An thành 2 tỉnh: Nghệ An (phía Bắc sông Lam); Hà Tĩnh (phía nam sông Lam). Sau đó hai tỉnh Nghệ An và Hà Tĩnh sáp nhập lại, lấy tên là tỉnh An Tĩnh.
|
183 |
+
VD6:
|
184 |
+
Claim: Được mô phỏng giống như lễ hội đường phố tại các nước Châu Âu, Carneval Hội An đã được tổ chức tại Hội An vào Giao thừa 2009
|
185 |
+
Context: Carneval Hội An là lễ hội đường phố được tổ chức lần đầu tiên tại thành phố Hội An vào Giao thừa năm 2009 (dương lịch). Lễ hội mô phỏng theo các lễ hội Carneval đường phố vốn rất nổi tiếng tại các nước châu Âu và Mỹ Latin .
|
186 |
+
Lưu ý quan trọng:
|
187 |
+
- Ưu tiên sử dụng các từ ngữ gợi ý về quan hệ nhân quả, so sánh, hoặc khái quát hóa
|
188 |
+
- Tránh tạo ra các claim quá rõ ràng là không có trong context
|
189 |
+
- Nên tận dụng các thông tin ngữ cảnh để tạo ra các suy luận hợp lý nhưng không thể kiểm chứng
|
190 |
+
- Kết hợp nhiều kỹ thuật để tăng độ khó trong việc xác định nhãn
|
191 |
+
- Đảm bảo câu claim vẫn giữ được tính logic và khả năng xảy ra trong thực tế
|
192 |
+
"""
|
193 |
+
}
|
194 |
+
},
|
195 |
+
"required": ["claims"]
|
196 |
+
}
|
197 |
+
}
|
198 |
+
|
199 |
+
def _load_api_keys(self, file_path: str ) -> List[str]:
|
200 |
+
"""Load API keys from CSV file for the specific claim type."""
|
201 |
+
try:
|
202 |
+
df = pd.read_csv(file_path)
|
203 |
+
# Get all keys for the specific claim type, excluding NaN values
|
204 |
+
keys = df[self.claim_type].dropna().tolist()
|
205 |
+
if not keys:
|
206 |
+
raise ValueError(f"No API keys found for {self.claim_type}")
|
207 |
+
return keys
|
208 |
+
except Exception as e:
|
209 |
+
raise Exception(f"Error loading API keys: {str(e)}")
|
210 |
+
|
211 |
+
def _configure_api(self):
|
212 |
+
"""Configure the API with the current key."""
|
213 |
+
genai.configure(api_key=self.api_keys[self.current_key_index])
|
214 |
+
|
215 |
+
def _rotate_api_key(self):
|
216 |
+
"""Rotate to the next available API key."""
|
217 |
+
self.current_key_index = (self.current_key_index + 1) % len(self.api_keys)
|
218 |
+
self._configure_api()
|
219 |
+
print(f"Rotating to next API key for {self.claim_type}")
|
220 |
+
|
221 |
+
|
222 |
+
def generate_claims(self, context: str, max_retries: int = 10) -> List[str]:
|
223 |
+
"""Generate claims with automatic API key rotation on errors."""
|
224 |
+
retries = 0
|
225 |
+
while retries < max_retries:
|
226 |
+
try:
|
227 |
+
model = genai.GenerativeModel(
|
228 |
+
'models/gemini-1.5-flash-latest',
|
229 |
+
generation_config={
|
230 |
+
"response_mime_type": "application/json",
|
231 |
+
"response_schema": self.SCHEMAS[self.claim_type],
|
232 |
+
"temperature": 0.9,
|
233 |
+
"top_p": 0.95,
|
234 |
+
"top_k": 40,
|
235 |
+
"max_output_tokens": 1024
|
236 |
+
}
|
237 |
+
)
|
238 |
+
|
239 |
+
prompt = f"""
|
240 |
+
Fact checking là 1 trong lĩnh vực xử lý ngôn ngữ tự nhiên (NLP), là bài toán kiểm tra và xác minh tính chính xác của 1 nhận định được đưa ra trên các thông tin, tin tức từ mạng xã hội, báo chí. Mô hình NLP sẽ xác định câu nhận định đó là chính xác hay sai sự thật hoặc không được đề cập đến trong thông tin đã được cung cấp trước đó (context).
|
241 |
+
Trong bối cảnh của Adversarial, đây là bài toán tạo ra bộ dữ liệu đối kháng với mục đích đánh lừa gây và gây khó khăn cho các mô hình NLP để dự đoán tính xác thực của các câu nhận định đó. Mục đích của bài toán này là thử thách khả năng suy luận của các mô hình NLP, từ đó để tạo nên bộ dữ liệu mới chất lượng hơn, cải thiện “khả năng học” của mô hình học máy.
|
242 |
+
Context: là nguồn thông tin, nội dung được cung cấp hiện có trong dataset mà annotator sẽ dựa vào để tiến hành đặt câu và đánh dấu.
|
243 |
+
Câu Claim: là câu nhận định, tuyên bố viết về bất kỳ chủ đề gì, được xác định tính đúng sai hoặc không liên quan dựa trên thông tin từ context đưa ra mà không sử dụng bất kỳ kiến thức hoặc thông tin ngoài lề
|
244 |
+
+ SUPPORTED là nhãn dành cho câu nhận định đúng, chính xác với các thông tin, số liệu trong câu được trích xuất từ đoạn văn gốc
|
245 |
+
+ REFUTED là nhãn dành cho câu nhận định sai, với các thông tin, số liệu trong câu bị sai so với đoạn văn gốc
|
246 |
+
+ NEI là nhãn dành cho câu có các thông tin, số liệu chưa được xác định là đúng hay sai dựa trên đoạn văn gốc
|
247 |
+
Context: "{context}"
|
248 |
+
Nhiệm vụ: Tạo một câu claim {self.claim_type} có liên quan đến context có độ khó cao, có thể gây khó khăn cho các mô hình NLP trong việc dự đoán nhãn chính xác.
|
249 |
+
Yêu cầu chi tiết:
|
250 |
+
1. Tuân thủ chặt chẽ các yêu cầu và kỹ thuật được mô tả trong schema
|
251 |
+
2. Tạo câu có cấu trúc phức tạp, kết hợp nhiều kỹ thuật biến đổi
|
252 |
+
3. Đảm bảo câu vẫn hợp lý và có thể hiểu được
|
253 |
+
4. Tránh:
|
254 |
+
- Copy nguyên văn từ context
|
255 |
+
- Sử dụng các biến đổi quá đơn giản
|
256 |
+
- Tạo câu không liên quan đến chủ đề
|
257 |
+
- Sử dụng thông tin ngoài context (trừ NEI có kiểm soát)
|
258 |
+
Trả về kết quả theo đúng format JSON với trường {self.claim_type}.
|
259 |
+
"""
|
260 |
+
|
261 |
+
# Sử dụng regex để xóa các khoảng trắng thừa
|
262 |
+
prompt = re.sub(r'\s+', ' ', prompt).strip()
|
263 |
+
# Xóa toàn bộ ký tự xuống dòng
|
264 |
+
prompt = re.sub(r'\n', ' ', prompt).strip()
|
265 |
+
response = model.generate_content(prompt)
|
266 |
+
time.sleep(2) # Prevent rate limiting
|
267 |
+
return json.loads(response.text)["claims"]
|
268 |
+
|
269 |
+
except Exception as e:
|
270 |
+
error_msg = str(e)
|
271 |
+
if "429" in error_msg or "500" in error_msg:
|
272 |
+
print(f"API error: {error_msg}")
|
273 |
+
self._rotate_api_key()
|
274 |
+
retries += 1
|
275 |
+
else:
|
276 |
+
raise Exception(f"Unexpected error: {error_msg}")
|
277 |
+
|
278 |
+
|
279 |
+
def generate_hard_claims(self, context: str, predict_func, max_attempts: int = 10) -> Dict[str, Any]:
|
280 |
+
"""Generate and validate claims with API key rotation support.
|
281 |
+
Tracks and returns the claim with lowest probability across all attempts."""
|
282 |
+
lowest_prob_claim = None
|
283 |
+
lowest_prob = float('inf') # Initialize with infinity to ensure first claim is captured
|
284 |
+
attempts = 0
|
285 |
+
|
286 |
+
while attempts < max_attempts:
|
287 |
+
try:
|
288 |
+
attempts += 1
|
289 |
+
claims = self.generate_claims(context)
|
290 |
+
|
291 |
+
for claim in claims:
|
292 |
+
prediction = predict_func(context, claim)
|
293 |
+
current_prob = prediction['probabilities'][self.claim_type]
|
294 |
+
other_labels = [label for label in ["SUPPORTED", "REFUTED", "NEI"]
|
295 |
+
if label != self.claim_type]
|
296 |
+
other_probs = [prediction['probabilities'][label] for label in other_labels]
|
297 |
+
|
298 |
+
claim_result = {
|
299 |
+
'claim': claim,
|
300 |
+
'predicted_label': prediction['verdict'],
|
301 |
+
f'{self.claim_type}_probability': current_prob,
|
302 |
+
'other_probabilities': dict(zip(other_labels, other_probs)),
|
303 |
+
'all_probabilities': prediction['probabilities']
|
304 |
+
}
|
305 |
+
|
306 |
+
# Update lowest probability claim if current is lower
|
307 |
+
if current_prob < lowest_prob:
|
308 |
+
lowest_prob = current_prob
|
309 |
+
lowest_prob_claim = claim_result
|
310 |
+
|
311 |
+
# Validation logic based on label type
|
312 |
+
if self.claim_type == "NEI":
|
313 |
+
max_other_prob = max(other_probs)
|
314 |
+
if current_prob < sum(other_probs) or (0.7 > current_prob > 0.5):
|
315 |
+
return {
|
316 |
+
'success': True,
|
317 |
+
'attempts': attempts,
|
318 |
+
'results': [claim_result],
|
319 |
+
'used_fallback': False
|
320 |
+
}
|
321 |
+
else:
|
322 |
+
if current_prob < sum(other_probs):
|
323 |
+
return {
|
324 |
+
'success': True,
|
325 |
+
'attempts': attempts,
|
326 |
+
'results': [claim_result],
|
327 |
+
'used_fallback': False
|
328 |
+
}
|
329 |
+
|
330 |
+
except Exception as e:
|
331 |
+
print(f"Error during claim generation: {str(e)}")
|
332 |
+
self._rotate_api_key()
|
333 |
+
continue
|
334 |
+
|
335 |
+
return {
|
336 |
+
'success': True,
|
337 |
+
'attempts': attempts,
|
338 |
+
'results': [lowest_prob_claim] if lowest_prob_claim else [],
|
339 |
+
'used_fallback': True
|
340 |
+
}
|
341 |
+
|
342 |
+
def generate_claims_for_context(
|
343 |
+
context: str,
|
344 |
+
claim_types: List[str],
|
345 |
+
predict_func,
|
346 |
+
progress_bar
|
347 |
+
) -> List[Dict[str, Any]]:
|
348 |
+
"""Generate claims for the context with progress tracking and fallback handling."""
|
349 |
+
claims_data = []
|
350 |
+
total_steps = len(claim_types)
|
351 |
+
|
352 |
+
for i, claim_type in enumerate(claim_types):
|
353 |
+
progress_bar.progress((i / total_steps), f"Generating {claim_type} claims...")
|
354 |
+
|
355 |
+
generator = ClaimGenerator(claim_type)
|
356 |
+
result = generator.generate_hard_claims(context, predict_func)
|
357 |
+
|
358 |
+
if result['success']:
|
359 |
+
for claim_result in result['results']:
|
360 |
+
claim_data = {
|
361 |
+
'context': context,
|
362 |
+
'claim': claim_result['claim'],
|
363 |
+
'label': claim_type,
|
364 |
+
'predicted_label': claim_result['predicted_label'],
|
365 |
+
f'{claim_type}_probability': claim_result[f'{claim_type}_probability'],
|
366 |
+
'probabilities': claim_result['all_probabilities']
|
367 |
+
}
|
368 |
+
claims_data.append(claim_data)
|
369 |
+
|
370 |
+
if result.get('used_fallback'):
|
371 |
+
st.warning(f"Used fallback claims for {claim_type} after {result['attempts']} attempts")
|
372 |
+
else:
|
373 |
+
st.error(f"Failed to generate any {claim_type} claims after {result['attempts']} attempts")
|
374 |
+
|
375 |
+
return claims_data
|
376 |
+
|
377 |
+
def process_dataframe_with_claims(df: pd.DataFrame) -> pd.DataFrame:
|
378 |
+
"""Process each summary in the DataFrame and generate claims with fallback handling."""
|
379 |
+
all_rows = []
|
380 |
+
progress_bar = st.progress(0)
|
381 |
+
|
382 |
+
for i, row in df.iterrows():
|
383 |
+
context = row['Summary']
|
384 |
+
|
385 |
+
for claim_type in ["SUPPORTED", "REFUTED", "NEI"]:
|
386 |
+
generator = ClaimGenerator(claim_type)
|
387 |
+
result = generator.generate_hard_claims(context, predict)
|
388 |
+
|
389 |
+
if result['success']:
|
390 |
+
for claim_result in result['results']:
|
391 |
+
new_row = row.copy()
|
392 |
+
new_row['claim'] = claim_result['claim']
|
393 |
+
new_row['label'] = claim_type
|
394 |
+
new_row['predicted_label'] = claim_result['predicted_label']
|
395 |
+
new_row['probability'] = claim_result[f'{claim_type}_probability']
|
396 |
+
new_row['all_probabilities'] = json.dumps(claim_result['all_probabilities'])
|
397 |
+
new_row['used_fallback'] = result.get('used_fallback', False)
|
398 |
+
all_rows.append(new_row)
|
399 |
+
|
400 |
+
if result.get('used_fallback'):
|
401 |
+
st.warning(f"Used fallback claims for article {row['ID']} {claim_type}")
|
402 |
+
|
403 |
+
progress_bar.progress((i + 1) / len(df))
|
404 |
+
|
405 |
+
new_df = pd.DataFrame(all_rows)
|
406 |
+
return new_df
|
407 |
+
|
408 |
+
def generate_claims_for_context(
|
409 |
+
context: str,
|
410 |
+
claim_types: List[str],
|
411 |
+
predict_func,
|
412 |
+
progress_bar
|
413 |
+
) -> List[Dict[str, Any]]:
|
414 |
+
"""Generate claims for the context with progress tracking."""
|
415 |
+
claims_data = []
|
416 |
+
total_steps = len(claim_types)
|
417 |
+
|
418 |
+
for i, claim_type in enumerate(claim_types):
|
419 |
+
progress_bar.progress((i / total_steps), f"Generating {claim_type} claims...")
|
420 |
+
|
421 |
+
generator = ClaimGenerator(claim_type)
|
422 |
+
result = generator.generate_hard_claims(context, predict_func)
|
423 |
+
|
424 |
+
if result['success']:
|
425 |
+
for claim_result in result['results']:
|
426 |
+
claim_data = {
|
427 |
+
'context': context,
|
428 |
+
'claim': claim_result['claim'],
|
429 |
+
'label': claim_type,
|
430 |
+
'predicted_label': claim_result['predicted_label'],
|
431 |
+
f'{claim_type}_probability': claim_result[f'{claim_type}_probability'],
|
432 |
+
'probabilities': claim_result['all_probabilities']
|
433 |
+
}
|
434 |
+
claims_data.append(claim_data)
|
435 |
+
else:
|
436 |
+
st.error(f"Error generating {claim_type} claims after {result['attempts']} attempts")
|
437 |
+
|
438 |
+
return claims_data
|
439 |
+
|
440 |
+
# Wikipedia helper functions
|
441 |
+
def format_content(content):
|
442 |
+
cleaned_content = re.sub(r'==.*?==', '', content)
|
443 |
+
cleaned_text = re.sub(r'\n\s*\n', '\n', cleaned_content)
|
444 |
+
lines = cleaned_text.split("\n")
|
445 |
+
cleaned_lines = [line.strip() for line in lines]
|
446 |
+
return "\n\n".join(cleaned_lines)
|
447 |
+
|
448 |
+
def chars_to_nums(s):
|
449 |
+
return ''.join(str((char_to_num.get(char.upper(), 0) % 4) + 1) for char in s if char.upper() in char_to_num)
|
450 |
+
|
451 |
+
def create_id(topic, stt):
|
452 |
+
topic_abbr = ''.join([word[0] for word in topic.split() if word])
|
453 |
+
topic_num = chars_to_nums(topic_abbr)
|
454 |
+
return f'uit_{topic_num}_{stt}'
|
455 |
+
|
456 |
+
def wikipedia_scrape(title_input, stt, filename):
|
457 |
+
try:
|
458 |
+
page = wikipedia.page(title_input)
|
459 |
+
title = page.title
|
460 |
+
summary = wikipedia.summary(title_input, sentences=10)
|
461 |
+
url = page.url
|
462 |
+
topic = filename.split(".")[0]
|
463 |
+
base_article_id = create_id(topic, stt)
|
464 |
+
|
465 |
+
summary = format_content(summary)
|
466 |
+
# Chia câu dựa trên dấu chấm
|
467 |
+
sentences = split_sentences_vietnamese(summary)
|
468 |
+
total_sentences = len(sentences)
|
469 |
+
|
470 |
+
# Tính toán chia đoạn động
|
471 |
+
part_lengths = [5, total_sentences - 5]
|
472 |
+
|
473 |
+
results = []
|
474 |
+
start = 0
|
475 |
+
for i, length in enumerate(part_lengths, 1):
|
476 |
+
# Đảm bảo không vượt quá tổng số câu
|
477 |
+
end = min(start + length, total_sentences)
|
478 |
+
part = '. '.join(sentences[start:end]) + '.'
|
479 |
+
|
480 |
+
if len(part)>=5:
|
481 |
+
article_id = f"{base_article_id}_{i}"
|
482 |
+
results.append({
|
483 |
+
"ID": article_id,
|
484 |
+
"Title": title,
|
485 |
+
"Topic": topic,
|
486 |
+
"Summary": part,
|
487 |
+
"URL": url
|
488 |
+
})
|
489 |
+
|
490 |
+
start = end
|
491 |
+
|
492 |
+
return results
|
493 |
+
except (wikipedia.exceptions.DisambiguationError, wikipedia.exceptions.PageError) as e:
|
494 |
+
return None
|
495 |
+
|
496 |
+
|
497 |
+
|
498 |
+
@st.cache_data
|
499 |
+
def convert_df_to_csv(df):
|
500 |
+
return df.to_csv(index=False).encode("utf-8-sig")
|
501 |
+
|
502 |
+
def process_dataframe_with_claims(df: pd.DataFrame) -> pd.DataFrame:
|
503 |
+
"""Process each summary in the DataFrame and generate claims."""
|
504 |
+
|
505 |
+
# Create lists to store the new data
|
506 |
+
all_rows = []
|
507 |
+
progress_bar = st.progress(0)
|
508 |
+
# Process each row in the original DataFrame
|
509 |
+
for i, row in df.iterrows():
|
510 |
+
context = row['Summary']
|
511 |
+
|
512 |
+
# Generate claims for each type
|
513 |
+
for claim_type in ["SUPPORTED", "REFUTED", "NEI"]:
|
514 |
+
generator = ClaimGenerator(claim_type)
|
515 |
+
result = generator.generate_hard_claims(context, predict)
|
516 |
+
|
517 |
+
if result['success']:
|
518 |
+
for claim_result in result['results']:
|
519 |
+
# Create a new row with all original columns plus new claim data
|
520 |
+
new_row = row.copy()
|
521 |
+
new_row['claim'] = claim_result['claim']
|
522 |
+
new_row['label'] = claim_type
|
523 |
+
new_row['predicted_label'] = claim_result['predicted_label']
|
524 |
+
new_row['probability'] = claim_result[f'{claim_type}_probability']
|
525 |
+
new_row['support_probability'] = claim_result['all_probabilities']["SUPPORTED"]
|
526 |
+
new_row['refuted_probability'] = claim_result['all_probabilities']["REFUTED"]
|
527 |
+
new_row['nei_probability'] = claim_result['all_probabilities']["NEI"]
|
528 |
+
all_rows.append(new_row)
|
529 |
+
progress_bar.progress((i + 1) / len(df))
|
530 |
+
|
531 |
+
|
532 |
+
# Create new DataFrame with all rows
|
533 |
+
new_df = pd.DataFrame(all_rows)
|
534 |
+
return new_df
|
535 |
+
|
536 |
+
def main():
|
537 |
+
st.title("Ứng dụng crawl data wiki tiếng việt và tạo claims")
|
538 |
+
|
539 |
+
uploaded_file = st.file_uploader("Tải lên tệp văn bản (txt)", type=["txt"])
|
540 |
+
|
541 |
+
if uploaded_file is not None:
|
542 |
+
file_progress = st.progress(0)
|
543 |
+
status_text = st.empty()
|
544 |
+
|
545 |
+
file_contents = uploaded_file.getvalue().decode("utf-8")
|
546 |
+
titles = file_contents.split("\n")
|
547 |
+
|
548 |
+
articles_info = []
|
549 |
+
for index, title in enumerate(titles):
|
550 |
+
title = title.strip()
|
551 |
+
if title:
|
552 |
+
status_text.text(f"Processing article {index + 1}/{len(titles)}: {title}")
|
553 |
+
article_infos = wikipedia_scrape(title, index + 1, uploaded_file.name)
|
554 |
+
|
555 |
+
# Safely append results, handling potential None values
|
556 |
+
if article_infos:
|
557 |
+
articles_info.extend(article_infos)
|
558 |
+
file_progress.progress((index + 1) / len(titles))
|
559 |
+
else:
|
560 |
+
st.warning(f"Dòng {index + 1}: Tiêu đề trống")
|
561 |
+
|
562 |
+
file_progress.empty()
|
563 |
+
status_text.empty()
|
564 |
+
|
565 |
+
if not articles_info:
|
566 |
+
st.error("Không có bài viết nào được thu thập từ Wikipedia.")
|
567 |
+
else:
|
568 |
+
df = pd.DataFrame(articles_info)
|
569 |
+
|
570 |
+
st.subheader("Danh sách bài viết từ Wikipedia:")
|
571 |
+
st.dataframe(df, use_container_width=True, hide_index=True)
|
572 |
+
|
573 |
+
# Add automatic claim generation for all summaries
|
574 |
+
if st.button("Tạo Claims cho tất cả bài viết"):
|
575 |
+
with st.spinner("Đang tạo claims cho tất cả bài viết..."):
|
576 |
+
|
577 |
+
# Process the DataFrame and generate claims
|
578 |
+
df_with_claims = process_dataframe_with_claims(df)
|
579 |
+
|
580 |
+
st.subheader("Bảng dữ liệu với Claims:")
|
581 |
+
st.dataframe(df_with_claims, use_container_width=True)
|
582 |
+
|
583 |
+
# Download options
|
584 |
+
csv = convert_df_to_csv(df_with_claims)
|
585 |
+
claims_filename = f"uit_{uploaded_file.name.split('.')[0]}_with_claims.csv"
|
586 |
+
st.download_button(
|
587 |
+
label="Download CSV với Claims",
|
588 |
+
data=csv,
|
589 |
+
file_name=claims_filename,
|
590 |
+
mime="text/csv",
|
591 |
+
)
|
592 |
+
|
593 |
+
# Original download option for basic DataFrame
|
594 |
+
csv = convert_df_to_csv(df)
|
595 |
+
csv_filename = "uit_" + uploaded_file.name.split(".")[0] + ".csv"
|
596 |
+
st.download_button(
|
597 |
+
label="Download CSV (không có claims)",
|
598 |
+
data=csv,
|
599 |
+
file_name=csv_filename,
|
600 |
+
mime="text/csv",
|
601 |
+
)
|
602 |
+
|
603 |
+
if __name__ == "__main__":
|
604 |
+
main()
|
config.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
hf_token = "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"
|
2 |
+
|
3 |
+
supported_schema = {
|
4 |
+
"type": "object",
|
5 |
+
"properties": {
|
6 |
+
"SUPPORTED": {
|
7 |
+
"type": "string",
|
8 |
+
"description": """
|
9 |
+
Câu khẳng định đúng, với các thông tin và số liệu được trích xuất trực tiếp từ context.
|
10 |
+
Yêu cầu:
|
11 |
+
- Thông tin phải chính xác 100% so với context
|
12 |
+
- Không được thêm thông tin ngoài context
|
13 |
+
- Có thể kết hợp nhiều thông tin từ các phần khác nhau của context
|
14 |
+
- Phải có thể verify được từ context
|
15 |
+
Gợi ý cách đặt câu:
|
16 |
+
- Thay thế các bằng các từ đồng nghĩa
|
17 |
+
- Thay thế các lượng từ có cùng ý nghĩa
|
18 |
+
- Chuyển từ chủ động thành bị động & ngược lại
|
19 |
+
- Dùng từ trái nghĩa + phủ định
|
20 |
+
- Lược bỏ những bổ ngữ “không cần thiết” (không làm thay đổi nội dung của câu)
|
21 |
+
- Chuyển đổi danh từ, tân ngữ thành 1 mệnh đề quan hệ
|
22 |
+
- Tạo câu tiền giả định
|
23 |
+
"""
|
24 |
+
}
|
25 |
+
},
|
26 |
+
"required": ["SUPPORTED"]
|
27 |
+
}
|
28 |
+
|
29 |
+
refuted_schema = {
|
30 |
+
"type": "object",
|
31 |
+
"properties": {
|
32 |
+
"REFUTED": {
|
33 |
+
"type": "string",
|
34 |
+
"description": """
|
35 |
+
Câu khẳng định sai, mâu thuẫn với thông tin trong context.
|
36 |
+
Yêu cầu:
|
37 |
+
- Phải sử dụng cùng chủ đề và phạm vi với context
|
38 |
+
- Thông tin hoặc số liệu phải mâu thuẫn rõ ràng với context
|
39 |
+
- Có thể thay đổi số liệu, sự kiện, hoặc mối quan hệ
|
40 |
+
- Phải có thể verify được là sai dựa trên context
|
41 |
+
Gợi ý cách đặt câu:
|
42 |
+
- Xáo trộn các giải thích của các đối tượng với nhau
|
43 |
+
- Thay thế bằng các từ trái nghĩa
|
44 |
+
- Thay thế các từ ngữ trong cùng lĩnh vực
|
45 |
+
- Biến đổi các thông số, đơn vị của các đối tượng
|
46 |
+
"""
|
47 |
+
},
|
48 |
+
},
|
49 |
+
"required": ["REFUTED"]
|
50 |
+
}
|
51 |
+
nei_schema = {
|
52 |
+
"type": "object",
|
53 |
+
"properties": {
|
54 |
+
"NEI": {
|
55 |
+
"type": "string",
|
56 |
+
"description": """
|
57 |
+
Câu có thông tin không thể xác minh từ context.
|
58 |
+
Yêu cầu:
|
59 |
+
- Phải liên quan đến chủ đề của context
|
60 |
+
- Thông tin không được có trong context
|
61 |
+
- Không được mâu thuẫn trực tiếp với context
|
62 |
+
- Không thể xác định đúng sai chỉ dựa vào
|
63 |
+
Gợi ý cách đặt câu:
|
64 |
+
- Viết lại câu với thông tin bị thu hẹp, mở rộng, suy diễn hoặc kết hợp với 1 thông tin khác chưa xác định
|
65 |
+
- Tạo câu giả định
|
66 |
+
"""
|
67 |
+
}
|
68 |
+
},
|
69 |
+
"required": ["NEI"]
|
70 |
+
}
|
predictor.py
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
hf_token = "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"
|
2 |
+
import warnings
|
3 |
+
warnings.filterwarnings('ignore')
|
4 |
+
import logging
|
5 |
+
logging.disable(logging.WARNING)
|
6 |
+
|
7 |
+
import torch
|
8 |
+
import numpy as np
|
9 |
+
import random
|
10 |
+
from transformers import AutoModel, AutoTokenizer
|
11 |
+
from torch.utils.data import DataLoader
|
12 |
+
from Mbert import MBERTClassifier, SentencePairDataset
|
13 |
+
import pandas as pd
|
14 |
+
|
15 |
+
# Thiết lập seed cố định
|
16 |
+
def set_seed(seed):
|
17 |
+
random.seed(seed)
|
18 |
+
np.random.seed(seed)
|
19 |
+
torch.manual_seed(seed)
|
20 |
+
if torch.cuda.is_available():
|
21 |
+
torch.cuda.manual_seed(seed)
|
22 |
+
torch.cuda.manual_seed_all(seed)
|
23 |
+
torch.backends.cudnn.deterministic = True
|
24 |
+
torch.backends.cudnn.benchmark = False
|
25 |
+
|
26 |
+
# Gọi hàm set_seed với seed cố định, ví dụ: 42
|
27 |
+
set_seed(42)
|
28 |
+
device = torch.device("cpu")
|
29 |
+
modelname = "bert-base-multilingual-cased"
|
30 |
+
tokenizer = AutoTokenizer.from_pretrained(modelname, token=hf_token)
|
31 |
+
mbert = AutoModel.from_pretrained(modelname, token=hf_token).to(device)
|
32 |
+
model = MBERTClassifier(mbert, num_classes=3).to(device)
|
33 |
+
model.load_state_dict(torch.load('Model/classifier.pt', map_location=device))
|
34 |
+
|
35 |
+
def predict(context, claim):
|
36 |
+
data = pd.DataFrame([{'context': context, 'claim': claim}])
|
37 |
+
X1_pub_test = data['claim']
|
38 |
+
X2_pub_test = data['context']
|
39 |
+
X_pub_test = [(X1_pub_test, X2_pub_test) for (X1_pub_test, X2_pub_test) in zip(X1_pub_test, X2_pub_test)]
|
40 |
+
y_pub_test = [1]
|
41 |
+
|
42 |
+
test_dataset = SentencePairDataset(X_pub_test, y_pub_test, tokenizer, 256)
|
43 |
+
test_loader_pub = DataLoader(test_dataset, batch_size=1)
|
44 |
+
|
45 |
+
model.eval()
|
46 |
+
predictions = []
|
47 |
+
probabilities = []
|
48 |
+
|
49 |
+
for batch in test_loader_pub:
|
50 |
+
input_ids = batch["input_ids"].to(device)
|
51 |
+
attention_mask = batch["attention_mask"].to(device)
|
52 |
+
with torch.no_grad():
|
53 |
+
outputs = model(input_ids, attention_mask)
|
54 |
+
probs = torch.nn.functional.softmax(outputs, dim=1)
|
55 |
+
predicted = torch.argmax(outputs, dim=1)
|
56 |
+
predictions.extend(predicted.cpu().numpy().tolist())
|
57 |
+
probabilities.extend(probs.cpu().numpy().tolist())
|
58 |
+
|
59 |
+
data['verdict'] = predictions
|
60 |
+
data['verdict'] = data['verdict'].replace(0, "SUPPORTED")
|
61 |
+
data['verdict'] = data['verdict'].replace(1, "REFUTED")
|
62 |
+
data['verdict'] = data['verdict'].replace(2, "NEI")
|
63 |
+
|
64 |
+
result = {
|
65 |
+
'verdict': data['verdict'][0],
|
66 |
+
'probabilities': {
|
67 |
+
'SUPPORTED': probabilities[0][0],
|
68 |
+
'REFUTED': probabilities[0][1],
|
69 |
+
'NEI': probabilities[0][2]
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
return result
|