representation source
This commit is contained in:
parent
afe4f0b87e
commit
6b84ad4031
|
@ -4,7 +4,7 @@
|
||||||
"""
|
"""
|
||||||
from p1_classifier import do_classify
|
from p1_classifier import do_classify
|
||||||
from p2_ner_recognizer import do_ner_recognize
|
from p2_ner_recognizer import do_ner_recognize
|
||||||
# from p3_words_embedder import do_word_embedder
|
from p3_words_embedder import do_word_embedder
|
||||||
# from p4_keyword_extractor import do_keyword_extract
|
# from p4_keyword_extractor import do_keyword_extract
|
||||||
# from p5_simplifier import do_simplify
|
# from p5_simplifier import do_simplify
|
||||||
|
|
||||||
|
@ -14,8 +14,18 @@ def get_sections():
|
||||||
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
||||||
eh_obj = ElasticHelper()
|
eh_obj = ElasticHelper()
|
||||||
sections = eh_obj.iterateJsonFile(sections_path, True)
|
sections = eh_obj.iterateJsonFile(sections_path, True)
|
||||||
|
sections = convert_to_dict(sections)
|
||||||
return sections
|
return sections
|
||||||
|
|
||||||
|
def convert_to_dict(sections):
|
||||||
|
sections_dict = {}
|
||||||
|
for item in sections:
|
||||||
|
id = item['id']
|
||||||
|
source = item['source']
|
||||||
|
sections_dict[id] = source
|
||||||
|
|
||||||
|
return sections_dict
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
# get sections to do nlp processes
|
# get sections to do nlp processes
|
||||||
|
|
102
p1_classifier.py
102
p1_classifier.py
|
@ -107,36 +107,31 @@ def full_path_text_maker(full_path):
|
||||||
full_path_text = full_path_text.strip()
|
full_path_text = full_path_text.strip()
|
||||||
return full_path_text
|
return full_path_text
|
||||||
|
|
||||||
def do_classify(sections):
|
def single_section_classification(id, section_source):
|
||||||
print(f'start classification: {datetime.datetime.now()}')
|
"""
|
||||||
|
این متد، متن ورودی را کلاسبندی می کند
|
||||||
|
|
||||||
test_counter = 1
|
**Args:
|
||||||
# all = تعداد سکشن های فایل قبلی 282671
|
id (str): شناسه سکشن
|
||||||
all = 285839
|
section_source (obj): سورس یک سکشن که شامل متن قانون و متادیتاهای مربوط به آن می شود
|
||||||
# لیستی جهت ذخیره عناوین قانون ها
|
|
||||||
qanon_title_list = []
|
|
||||||
# دیکشنری برای ذخیره نتایج که شامل شناسه سکشن ها و 4 کلاس به ترتیب اولویت است
|
|
||||||
new_sections_dict = {}
|
|
||||||
|
|
||||||
for index, item in enumerate(sections):
|
**Returns:
|
||||||
|
classification_result(obj): چند کلاس پیشنهادی به ترتیب اولویت
|
||||||
id = item
|
classification_status(bool): بیان می کند که عملیات کلاس بندی موفق بوده یا خیر
|
||||||
|
desc(str): توضیحی در مورد موفقیت یا خطای عملیات ارائه می دهد
|
||||||
source = sections[id]
|
"""
|
||||||
# اگر نوع سکشن، عنوان یا موخره یا امضاء باشد، نیازی به فرایند کلاسبندی نیست و لیست کلاس های مربوط به این سکشن را خالی قرار می دهیم
|
classification_result ={
|
||||||
if source['other_info']['full_path'] == 'عنوان' or source['other_info']['full_path'] == 'موخره' or source['other_info']['full_path'] == 'امضاء':
|
|
||||||
new_sections_dict[id] ={
|
|
||||||
"best-class":{},
|
"best-class":{},
|
||||||
"other-classes": []}
|
"other-classes": []}
|
||||||
|
content = section_source['content']
|
||||||
|
# اگر نوع سکشن، عنوان یا موخره یا امضاء باشد، نیازی به فرایند کلاسبندی نیست و لیست کلاس های مربوط به این سکشن را خالی قرار می دهیم
|
||||||
|
if content =='' or section_source['other_info']['full_path'] == 'عنوان' or section_source['other_info']['full_path'] == 'موخره' or section_source['other_info']['full_path'] == 'امضاء':
|
||||||
|
return classification_result, True, 'Classification was successful'
|
||||||
|
|
||||||
print(f'section: {all}/{index+1}/{id}', flush=True)
|
qanon_title = section_source['qanon_title']
|
||||||
continue
|
|
||||||
|
|
||||||
content = source['content']
|
|
||||||
qanon_title = source['qanon_title']
|
|
||||||
# این متغیر ریشه سکشن اخیر تا رسیدن به قانون را مشخص می کند
|
# این متغیر ریشه سکشن اخیر تا رسیدن به قانون را مشخص می کند
|
||||||
# مثلا: ماده5>تبصره یک>بند الف
|
# مثلا: ماده5>تبصره یک>بند الف
|
||||||
full_path = source['other_info']['full_path'].split(">")
|
full_path = section_source['other_info']['full_path'].split(">")
|
||||||
# بازسازی متن مسیر سکشن از جزء به کل
|
# بازسازی متن مسیر سکشن از جزء به کل
|
||||||
full_path_text = full_path_text_maker(full_path)
|
full_path_text = full_path_text_maker(full_path)
|
||||||
|
|
||||||
|
@ -146,41 +141,54 @@ def do_classify(sections):
|
||||||
pre_content = f"محتوای {full_path_text} {cleaning(qanon_title)} متن زیر است."
|
pre_content = f"محتوای {full_path_text} {cleaning(qanon_title)} متن زیر است."
|
||||||
try:
|
try:
|
||||||
content = cleaning(content)
|
content = cleaning(content)
|
||||||
except Exception as e:
|
except Exception as error:
|
||||||
# شناسه ماده هایی که محتوای آنها خالی است در این مسیر ذخیره می شوند
|
# شناسه ماده هایی که محتوای آنها خالی است در این مسیر ذخیره می شوند
|
||||||
with open('./data/empty_content_log.txt', 'a', encoding='utf-8') as output_file:
|
with open('./data/cleaning_content_log.txt', 'a', encoding='utf-8') as output_file:
|
||||||
output_file.write(id + " >> " + str(e) + "\n")
|
output_file.write(id + " >> " + str(error) + "\n")
|
||||||
continue
|
print('cleaning content error!')
|
||||||
|
return classification_result, False, str(error)
|
||||||
try:
|
try:
|
||||||
# دریافت کلاس های مربوط به یک سکشن
|
# دریافت کلاس های مربوط به یک سکشن
|
||||||
section_classes = get_window_classes(f"{pre_content} {content}")
|
section_classes = get_window_classes(f"{pre_content} {content}")
|
||||||
|
|
||||||
#region collect data for evaluation
|
except Exception as error:
|
||||||
""" این قسمت تا 7 خط بعدی، صرفا جهت جمع آوری و ذخیره تعدادی از سکشن ها و کلاس های پیش بینی شده برای آنها، به منظور ارزیابی عملکرد مدل توسط کاربر انسانی است و دخیل در فرایند کلاسبندی نیست"""
|
error_content = f"{id} -- Classification Error Content:{error}\n"
|
||||||
if (len(tokenizer(f"{pre_content} {content}")['input_ids'][1:-1]) < 1500) and not qanon_title in qanon_title_list:
|
|
||||||
with open('./data/classification/test_log_60e_hoosh_fp3.txt', 'a', encoding='utf-8') as output_file:
|
|
||||||
message = f"\n{test_counter}\n{id} : {pre_content} {content}\nclasses:\n"
|
|
||||||
for cls in section_classes:
|
|
||||||
message += f"{cls['label']} >> {cls['score']}\n"
|
|
||||||
output_file.write(message + "\n")
|
|
||||||
test_counter+=1
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
error = e
|
|
||||||
with open('./data/classification/errors.txt', 'a', encoding='utf-8') as output_file:
|
with open('./data/classification/errors.txt', 'a', encoding='utf-8') as output_file:
|
||||||
output_file.write(f"{id} -- Error Content:{error}\n")
|
output_file.write(error_content)
|
||||||
continue
|
print(error_content)
|
||||||
# item['classes'] = section_classes
|
return classification_result, False, error_content
|
||||||
# ساماندهی کلاس های پیش بینی شده در عنوان بهترین کلاس و دیگر کلاسها بر اساس امتیاز تخمین مدل و ذخیره در دیکشنری
|
|
||||||
new_sections_dict[id] ={
|
classification_result ={
|
||||||
"content" : content,
|
|
||||||
"best-class" : section_classes[0],
|
"best-class" : section_classes[0],
|
||||||
"other-classes" : section_classes[1:]
|
"other-classes" : section_classes[1:]
|
||||||
}
|
}
|
||||||
|
return classification_result, True, 'Classification was successful'
|
||||||
|
|
||||||
|
def do_classify(sections):
|
||||||
|
print(f'start classification: {datetime.datetime.now()}')
|
||||||
|
|
||||||
|
test_counter = 1
|
||||||
|
# all = تعداد سکشن های فایل قبلی 282671
|
||||||
|
all = len(sections)
|
||||||
|
# لیستی جهت ذخیره عناوین قانون ها
|
||||||
|
qanon_title_list = []
|
||||||
|
# دیکشنری برای ذخیره نتایج که شامل شناسه سکشن ها و 4 کلاس به ترتیب اولویت است
|
||||||
|
new_sections_dict = {}
|
||||||
|
|
||||||
|
for index, id in enumerate(sections):
|
||||||
|
|
||||||
|
source = sections[id]['source']
|
||||||
|
classification_result, classification_status, desc = single_section_classification(id, source)
|
||||||
|
|
||||||
|
if not classification_status:
|
||||||
|
print(f'id: {id} classification error. error description: {desc}')
|
||||||
|
|
||||||
|
# ساماندهی کلاس های پیش بینی شده در عنوان بهترین کلاس و دیگر کلاسها بر اساس امتیاز تخمین مدل و ذخیره در دیکشنری
|
||||||
|
new_sections_dict[id] = classification_result
|
||||||
|
|
||||||
""" برای حالت تست که می خواهیم عملکرد مدل کلاسیفایر را ارزیابی کنیم، بدین جهت که تنوعی از قوانین مختلف را بررسی کنیم، عنوان قوانین را ذخیره می کنیم تا از تکرار بررسی سکشن های متعدد از یک قانون پرهیز شود"""
|
""" برای حالت تست که می خواهیم عملکرد مدل کلاسیفایر را ارزیابی کنیم، بدین جهت که تنوعی از قوانین مختلف را بررسی کنیم، عنوان قوانین را ذخیره می کنیم تا از تکرار بررسی سکشن های متعدد از یک قانون پرهیز شود"""
|
||||||
qanon_title_list.append(qanon_title)
|
# qanon_title = source['qanon_title']
|
||||||
|
# qanon_title_list.append(qanon_title)
|
||||||
print(f'section: {all}/{index+1}/{id}', flush=True)
|
print(f'section: {all}/{index+1}/{id}', flush=True)
|
||||||
# ذخیره دیکشنری شناسه های قانون و کلاس های تخمین زده شده در فایل جیسون
|
# ذخیره دیکشنری شناسه های قانون و کلاس های تخمین زده شده در فایل جیسون
|
||||||
with open('./data/classification/all_sections_classes_new_140405.json', 'w', encoding='utf-8') as output_file:
|
with open('./data/classification/all_sections_classes_new_140405.json', 'w', encoding='utf-8') as output_file:
|
||||||
|
|
|
@ -15,6 +15,7 @@ model = "./models/ner/2025-07-22--20-44-37--HooshvareLab--bert-fa-base-uncased-n
|
||||||
tagger = SequenceTagger.load(model)
|
tagger = SequenceTagger.load(model)
|
||||||
print('model read and tagger initialized')
|
print('model read and tagger initialized')
|
||||||
|
|
||||||
|
today = f'{datetime.datetime.year}-{datetime.datetime.month}-{datetime.datetime.day}-{datetime.datetime.hour}'
|
||||||
|
|
||||||
def prepare_data(ner_obj_list):
|
def prepare_data(ner_obj_list):
|
||||||
ner_data_list = []
|
ner_data_list = []
|
||||||
|
@ -97,7 +98,17 @@ def find_ner_values_in_text(text, ner_values):
|
||||||
return ner_obj
|
return ner_obj
|
||||||
|
|
||||||
|
|
||||||
def inference_main(input_sentence):
|
def single_ner_recognizer(input_sentence):
|
||||||
|
"""
|
||||||
|
این متد، موجودیت های نامدار را از متن ورودی استخراج می کند
|
||||||
|
|
||||||
|
**Args:
|
||||||
|
input_sentence (str): متن سکشن
|
||||||
|
|
||||||
|
**Returns:
|
||||||
|
ner_obj_list, (obj): آبجکتی از موجودیت های نامدار تشخیص داده شده
|
||||||
|
proccess_result(obj): بخش اول شامل یک کلید از نوع بولین است که موفقیت عملیات را بیان می کند. بخش دوم نیز توضیحی در مورد وضعیت نتیجه عملیات را در بر دارد
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
proccess_result = True, ''
|
proccess_result = True, ''
|
||||||
|
|
||||||
|
@ -128,10 +139,10 @@ def inference_main(input_sentence):
|
||||||
|
|
||||||
ner_obj_list = find_ner_values_in_text(input_sentence, ner_values)
|
ner_obj_list = find_ner_values_in_text(input_sentence, ner_values)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
proccess_result = False , error.args[0]
|
proccess_result = False , str(error.args[0])
|
||||||
ner_obj_list = []
|
ner_obj_list = []
|
||||||
|
|
||||||
return ner_obj_list, input_sentence, proccess_result
|
return ner_obj_list, proccess_result
|
||||||
|
|
||||||
# تابع بازگشتی برای تقسیم متن به تکه های کوچکتر از 512 کاراکتر
|
# تابع بازگشتی برای تقسیم متن به تکه های کوچکتر از 512 کاراکتر
|
||||||
def split_sentence(input_sentence):
|
def split_sentence(input_sentence):
|
||||||
|
@ -180,6 +191,27 @@ def split_sentence(input_sentence):
|
||||||
|
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
def do_ner_recognize(sections):
|
||||||
|
len_sections = len(sections)
|
||||||
|
for index, id in enumerate(sections):
|
||||||
|
content = sections[id]['content']
|
||||||
|
if not content == '':
|
||||||
|
content = cleaning(content)
|
||||||
|
ner_obj_list, ner_result = single_ner_recognizer(content)
|
||||||
|
if not ner_result[0]:
|
||||||
|
print(f'ner recognization error. id: {id}')
|
||||||
|
error_content = f'id: {id} - error: {ner_result[1]}\n'
|
||||||
|
with open(f'./data/ner/ner_errors_{today}.txt', 'a+', encoding='utf-8') as file:
|
||||||
|
file.write(error_content)
|
||||||
|
continue
|
||||||
|
|
||||||
|
ner_data_list = prepare_data(ner_obj_list)
|
||||||
|
sections[id]['ners_v2'] = ner_data_list
|
||||||
|
print(f'ner process: {index+1}/{len_sections}')
|
||||||
|
print(f'len_sections ner recognization finished!')
|
||||||
|
|
||||||
|
return sections
|
||||||
|
|
||||||
def get_sections():
|
def get_sections():
|
||||||
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
||||||
eh_obj = ElasticHelper()
|
eh_obj = ElasticHelper()
|
||||||
|
@ -187,20 +219,6 @@ def get_sections():
|
||||||
sections = convert_to_dict(sections)
|
sections = convert_to_dict(sections)
|
||||||
return sections
|
return sections
|
||||||
|
|
||||||
def do_ner_recognize(sections):
|
|
||||||
len_sections = len(sections)
|
|
||||||
for index, item in enumerate(sections):
|
|
||||||
content = sections[item]['content']
|
|
||||||
if not content == '':
|
|
||||||
content = cleaning(content)
|
|
||||||
ner_obj_list, content_ai, ner_result = inference_main(content)
|
|
||||||
ner_data_list = prepare_data(ner_obj_list)
|
|
||||||
sections[item]['ners_v2'] = ner_data_list
|
|
||||||
print(f'ner process: {index+1}/{len_sections}')
|
|
||||||
print(f'len_sections ner recognization finished!')
|
|
||||||
|
|
||||||
return sections
|
|
||||||
|
|
||||||
def convert_to_dict(sections):
|
def convert_to_dict(sections):
|
||||||
sections_dict = {}
|
sections_dict = {}
|
||||||
for item in sections:
|
for item in sections:
|
||||||
|
@ -225,7 +243,7 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
sections = do_ner_recognize(sections)
|
sections = do_ner_recognize(sections)
|
||||||
|
|
||||||
with open('./data/ner/sections_ner.json', 'w', encoding='utf-8') as output:
|
with open(f'./data/ner/sections_ner_{today}.json', 'w', encoding='utf-8') as output:
|
||||||
data = json.dumps(sections, ensure_ascii=False, indent=4)
|
data = json.dumps(sections, ensure_ascii=False, indent=4)
|
||||||
output.write(data)
|
output.write(data)
|
||||||
|
|
||||||
|
|
|
@ -3,28 +3,52 @@
|
||||||
"""
|
"""
|
||||||
from sentence_transformers import SentenceTransformer
|
from sentence_transformers import SentenceTransformer
|
||||||
import json
|
import json
|
||||||
|
import datetime
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
def load_embedder_model(model_name):
|
today = f'{datetime.datetime.year}-{datetime.datetime.month}-{datetime.datetime.day}-{datetime.datetime.hour}'
|
||||||
|
|
||||||
|
model_name = 'sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2'#89-25
|
||||||
|
# model_name = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'#87-30
|
||||||
|
# model_name = 'sharif-dal/dal-bert'#90-41
|
||||||
|
# model_name = 'lifeweb-ai/shiraz'#97-67
|
||||||
|
# model_name = 'BAAI/bge-m3'#90-35
|
||||||
|
# model_name = 'jinaai/jina-embeddings-v3'#??
|
||||||
|
# model_name = 'jinaai/jina-embeddings-v4'#??
|
||||||
|
# model_name = 'HooshvareLab/bert-base-parsbert-uncased'#90-54
|
||||||
model = SentenceTransformer(model_name)
|
model = SentenceTransformer(model_name)
|
||||||
return model
|
|
||||||
|
|
||||||
def do_word_embedder(sections):
|
def do_word_embedder(sections):
|
||||||
model_name = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
|
|
||||||
model = load_embedder_model(model_name)
|
|
||||||
for index, item in enumerate(sections):
|
for index, item in enumerate(sections):
|
||||||
embeddings = get_sentence_embeddings(sections[item]['content'])
|
embeddings = single_section_embedder(sections[item]['content'])
|
||||||
sections[item]['embeddings'] = embeddings
|
sections[item]['embeddings'] = embeddings
|
||||||
|
|
||||||
with open('./data/embeddings/sections_embeddings.json', 'w', encoding='utf-8') as outpu_file:
|
with open(f'./data/embeddings/sections_embeddings_{today}.json', 'w', encoding='utf-8') as output_file:
|
||||||
data = json.dumps(sections, ensure_ascii=False)
|
data = json.dumps(sections, ensure_ascii=False)
|
||||||
outpu_file.write(data)
|
output_file.write(data)
|
||||||
|
|
||||||
def get_sentence_embeddings(sentence):
|
def single_section_embedder(sentence):
|
||||||
model_name = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
|
"""
|
||||||
model = load_embedder_model(model_name)
|
این متد، متن ورودی را تبدیل به بردار متناظر آن می کند
|
||||||
|
|
||||||
|
**Args:
|
||||||
|
sentence (str): متن یک سکشن
|
||||||
|
|
||||||
|
**Returns:
|
||||||
|
embeddings: لیست بردار متناظر با متن ورودی
|
||||||
|
"""
|
||||||
embeddings = model.encode(sentence)
|
embeddings = model.encode(sentence)
|
||||||
return embeddings
|
return embeddings
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def cosine_similarity(vec1, vec2):
|
||||||
pass
|
dot_product = np.dot(vec1, vec2) # ضرب داخلی دو بردار
|
||||||
|
norm_vec1 = np.linalg.norm(vec1) # نُرم بردار اول
|
||||||
|
norm_vec2 = np.linalg.norm(vec2) # نُرم بردار دوم
|
||||||
|
return dot_product / (norm_vec1 * norm_vec2)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
embd1 = single_section_embedder("۲ – درصد مشارکت و سهم پرداختی کارفرما نسبت به مأخذ کسر حق بیمه به صندوقهای فعال در سطح همگانی بیمههای اجتماعی و درمانی یکسان خواهد بود.")
|
||||||
|
embd2 = single_section_embedder("۳ – درصد مشارکت و سهم پرداختی بیمهشده نسبت به مأخذ کسر حق بیمه به صندوقهای فعال در سطح همگانی بیمههای اجتماعی و درمانی یکسان خواهد بود.")
|
||||||
|
# embd2 = get_sentence_embeddings("تو کم گذاشتی وگرنه شکست نمی خوردی")
|
||||||
|
similarity = cosine_similarity(embd1, embd2)
|
||||||
|
print(f'similarity: {similarity}')
|
|
@ -15,6 +15,7 @@ os.environ['HF_HOME'] = "/home/admin/HFHOME"
|
||||||
model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
||||||
#model_id = "meta-llama/Llama-3.1-70B-Instruct"
|
#model_id = "meta-llama/Llama-3.1-70B-Instruct"
|
||||||
|
|
||||||
|
today = f'{datetime.datetime.year}-{datetime.datetime.month}-{datetime.datetime.day}-{datetime.datetime.hour}'
|
||||||
|
|
||||||
bnb_config = BitsAndBytesConfig(
|
bnb_config = BitsAndBytesConfig(
|
||||||
load_in_8bit=True, bnb_8bit_use_double_quant=True, bnb_8bit_quant_type="nf8", bnb_8bit_compute_dtype=torch.bfloat16
|
load_in_8bit=True, bnb_8bit_use_double_quant=True, bnb_8bit_quant_type="nf8", bnb_8bit_compute_dtype=torch.bfloat16
|
||||||
|
@ -61,7 +62,7 @@ def generate(formatted_prompt):
|
||||||
USER_PROMPT = f"""از متن ارائه شده، حداقل {keywords_count} عبارت کلیدی مهم و معنادار را استخراج کنید. خروجی باید به صورت زیر باشد:
|
USER_PROMPT = f"""از متن ارائه شده، حداقل {keywords_count} عبارت کلیدی مهم و معنادار را استخراج کنید. خروجی باید به صورت زیر باشد:
|
||||||
• یک لیست فارسی
|
• یک لیست فارسی
|
||||||
• شماره ترتیبی در ابتدای هر عبارت
|
• شماره ترتیبی در ابتدای هر عبارت
|
||||||
• هر عبارت در یک خط جداگانه *
|
• هر عبارت در یک خط جداگانه
|
||||||
• بدون هیچ توضیح اضافی در ابتدا یا انتهای پاسخ
|
• بدون هیچ توضیح اضافی در ابتدا یا انتهای پاسخ
|
||||||
موارد زیر در استخراج عبارات کلیدی الزامی است:
|
موارد زیر در استخراج عبارات کلیدی الزامی است:
|
||||||
• بسیار مهم و حیاتی است که عبارات کلیدی باید دقیقاً در متن موجود باشند، بنابراین هرگز از خلاقیت برای ساختن کلیدواژهای که دقیقا در متن وجود ندارد، استفاده نکن.
|
• بسیار مهم و حیاتی است که عبارات کلیدی باید دقیقاً در متن موجود باشند، بنابراین هرگز از خلاقیت برای ساختن کلیدواژهای که دقیقا در متن وجود ندارد، استفاده نکن.
|
||||||
|
@ -100,12 +101,21 @@ def generate(formatted_prompt):
|
||||||
return tokenizer.decode(response, skip_special_tokens=True)
|
return tokenizer.decode(response, skip_special_tokens=True)
|
||||||
|
|
||||||
|
|
||||||
def get_rules(sentence):
|
def single_section_get_keyword(sentence):
|
||||||
formatted_prompt = format_prompt(sentence)
|
"""
|
||||||
rules = generate(formatted_prompt).split('\n')
|
این متد، کلیدواژه های قانونی موجود در متن ورودی را استخراج می کند
|
||||||
result = [r.strip() for r in rules if r.strip()]
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
**Args:
|
||||||
|
sentence(str): متن یک سکشن قانونی
|
||||||
|
**Returns:
|
||||||
|
kws(list): لیستی از کلیدواژه های تشخیص داده شده
|
||||||
|
"""
|
||||||
|
formatted_prompt = format_prompt(sentence)
|
||||||
|
keywords = generate(formatted_prompt).split('\n')
|
||||||
|
kws = [kw.strip() for kw in keywords if kw.strip()]
|
||||||
|
# حذف کلیدواژه های تکراری
|
||||||
|
kws = list(set(kws))
|
||||||
|
return kws
|
||||||
|
|
||||||
def get_sections():
|
def get_sections():
|
||||||
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
||||||
|
@ -138,17 +148,21 @@ def do_keyword_extract(sections):
|
||||||
# if item['id'] in prev_ids:
|
# if item['id'] in prev_ids:
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
if counter > 10:
|
# # برای تست خروجی
|
||||||
with open('./data/keyword/prev_kw_ids.txt', 'a+', encoding='utf-8') as file:
|
# if counter > 10:
|
||||||
file.write(period_ids_text)
|
# with open('./data/keyword/prev_kw_ids.txt', 'a+', encoding='utf-8') as file:
|
||||||
break
|
# file.write(period_ids_text)
|
||||||
|
# break
|
||||||
|
|
||||||
id = item
|
id = item
|
||||||
content = sections[id]['content']
|
content = sections[id]['content']
|
||||||
try:
|
try:
|
||||||
sections[id]['keywords'] = get_rules(content)
|
sections[id]['keywords'] = single_section_get_keyword(content)
|
||||||
except:
|
except Exception as error:
|
||||||
print(f"section kw error : {id}\n")
|
print(f"section kw error : {id}\n")
|
||||||
|
error_content = f'id: {id} - error: {str(error)}\n'
|
||||||
|
with open(f'./data/keyword/keyword_errors_{today}.txt', 'a+', encoding='utf-8') as file:
|
||||||
|
file.write(error_content)
|
||||||
counter += 1
|
counter += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -157,7 +171,7 @@ def do_keyword_extract(sections):
|
||||||
print(f"section: {counter}-id: {id}")
|
print(f"section: {counter}-id: {id}")
|
||||||
# temp_dict.append(item)
|
# temp_dict.append(item)
|
||||||
if counter % 1000 == 0:
|
if counter % 1000 == 0:
|
||||||
outputfile = open(f'./data/keyword/sections_kw_llama8b_{str(file_counter)}.json', "a+", encoding='utf-8')
|
outputfile = open(f'./data/keyword/sections_kw_llama8b_{str(file_counter)}_{today}.json', "a+", encoding='utf-8')
|
||||||
outputfile.write(json.dumps(period_sections, ensure_ascii=False, indent=2))
|
outputfile.write(json.dumps(period_sections, ensure_ascii=False, indent=2))
|
||||||
outputfile.close()
|
outputfile.close()
|
||||||
print(f"file {str(file_counter)} created in {str(datetime.datetime.now())} +++++++++++++++++++++++++\n ")
|
print(f"file {str(file_counter)} created in {str(datetime.datetime.now())} +++++++++++++++++++++++++\n ")
|
||||||
|
@ -165,13 +179,13 @@ def do_keyword_extract(sections):
|
||||||
file_counter += 1
|
file_counter += 1
|
||||||
period_sections = []
|
period_sections = []
|
||||||
# save proccessed sections id for next executions of this code
|
# save proccessed sections id for next executions of this code
|
||||||
with open('./data/keyword/prev_kw_ids.txt', 'a+', encoding='utf-8') as file:
|
with open(f'./data/keyword/prev_kw_ids_{today}.txt', 'a+', encoding='utf-8') as file:
|
||||||
file.write(period_ids_text)
|
file.write(period_ids_text)
|
||||||
counter += 1
|
counter += 1
|
||||||
period_ids_text = f'***** file number: {file_counter} *****\n'
|
period_ids_text = f'***** file number: {file_counter} *****\n'
|
||||||
|
|
||||||
|
|
||||||
outputfile = open(f'./data/keyword/sections_kw_llama8b_{str(file_counter)}.json', "w", encoding='utf-8')
|
outputfile = open(f'./data/keyword/sections_kw_llama8b_{str(file_counter)}_{today}.json', "w", encoding='utf-8')
|
||||||
outputfile.write(json.dumps(period_sections, ensure_ascii=False, indent = 4))
|
outputfile.write(json.dumps(period_sections, ensure_ascii=False, indent = 4))
|
||||||
outputfile.close()
|
outputfile.close()
|
||||||
print(f"file {str(file_counter)} created in {str(datetime.datetime.now())} +++++++++++++++++++++++++ ")
|
print(f"file {str(file_counter)} created in {str(datetime.datetime.now())} +++++++++++++++++++++++++ ")
|
||||||
|
|
|
@ -1,4 +1,85 @@
|
||||||
|
"""
|
||||||
|
ساده سازی (بازنمایی) جملات قانونی به تعدادی جمله ساده تر و روان تر
|
||||||
|
"""
|
||||||
|
import datetime
|
||||||
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||||
|
import torch
|
||||||
|
import json
|
||||||
|
|
||||||
|
today = f'{datetime.datetime.year}-{datetime.datetime.month}-{datetime.datetime.day}-{datetime.datetime.hour}'
|
||||||
|
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
||||||
|
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", torch_dtype=torch.bfloat16)
|
||||||
|
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
total = 0
|
||||||
|
remained = 0
|
||||||
|
id = ''
|
||||||
|
keywords_count = 15
|
||||||
|
|
||||||
|
def single_section_representation(text):
|
||||||
|
global remained
|
||||||
|
try:
|
||||||
|
sen_count = (len(text) / 1000) * 15
|
||||||
|
sen_count = int(sen_count)
|
||||||
|
if sen_count == 0:
|
||||||
|
sen_count = 1
|
||||||
|
messages = [{"role": "system", "content": "تو یک وکیل حقوق دان هستی و باید بتوانی متن های قانونی و حقوقی را بدون تغییر اصطلاحات فنی، به صورتی توضیح دهی که افراد غیر حقوق دان، معنای متن را درک کنند. " },
|
||||||
|
{"role": "user", "content":
|
||||||
|
f"متن زیر را در قالب {sen_count} جمله جداگانه، ساده و روان به زبان فارسی، برای کسی که حقوق دان نیست، بازنویسی کن و بین دو * قرار بده و هیچ گونه توضیحی در ابتدا یا انتهای پاسخ، اضافه نکن. جملاتی که تولید می کنی، از نظر معنایی تکراری نباشند و از مجموع جملات بتوان منظور و معنای دقیق متن داده شده را فهم کرد. در پایان هر جمله، علامت نقطه قرار بده و به هیچ وجه جمله آخر را به صورت ناقص رها نکن.\n متن:{text}"
|
||||||
|
}]
|
||||||
|
|
||||||
|
input_ids = tokenizer.apply_chat_template(
|
||||||
|
messages,
|
||||||
|
add_generation_prompt=True,
|
||||||
|
return_tensors="pt"
|
||||||
|
).to(model.device)
|
||||||
|
|
||||||
|
terminators = [
|
||||||
|
tokenizer.eos_token_id,
|
||||||
|
tokenizer.convert_tokens_to_ids("<|eot_id|>")
|
||||||
|
]
|
||||||
|
model.generation_config.pad_token_id = tokenizer.pad_token_id
|
||||||
|
|
||||||
|
|
||||||
def do_simplify(sections):
|
outputs = model.generate(
|
||||||
|
input_ids,
|
||||||
|
max_new_tokens=500,
|
||||||
|
eos_token_id=terminators,
|
||||||
|
do_sample=True,
|
||||||
|
temperature=0.7,
|
||||||
|
top_p=0.85,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = outputs[0][input_ids.shape[-1]:]
|
||||||
|
sentences = tokenizer.decode(response, skip_special_tokens=True)
|
||||||
|
result = True
|
||||||
|
desc = 'operation successful'
|
||||||
|
return result, desc, sentences
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
result = False
|
||||||
|
desc = str(error)
|
||||||
|
return result, desc, []
|
||||||
|
|
||||||
|
def do_representation(sections):
|
||||||
|
print(f"start time: {datetime.datetime.now()}")
|
||||||
|
|
||||||
|
for index, id in sections:
|
||||||
|
result, desc, sentences = single_section_representation(sections[id]['content'])
|
||||||
|
if not result:
|
||||||
|
error_content = f'id: {id} - error: {desc}\n'
|
||||||
|
with open(f'./data/represent/represent_errors_{today}.txt', 'a+', encoding='utf-8') as file:
|
||||||
|
file.write(error_content)
|
||||||
|
|
||||||
|
sections[id]['represented_sentences'] = sentences
|
||||||
|
|
||||||
|
|
||||||
|
print(f"end time: {datetime.datetime.now()}")
|
||||||
|
print(" *** finished! *** ")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
18
test.py
Normal file
18
test.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from p3_words_embedder import get_sentence_embeddings
|
||||||
|
|
||||||
|
sentences = [
|
||||||
|
'جمله یک',
|
||||||
|
'جمله 2',
|
||||||
|
'جمله 3',
|
||||||
|
'جمله 4',
|
||||||
|
'جمله 5',
|
||||||
|
'جمله 6',
|
||||||
|
'جمله 7',
|
||||||
|
]
|
||||||
|
|
||||||
|
emds = {}
|
||||||
|
for item in sentences:
|
||||||
|
edm = get_sentence_embeddings(item)
|
||||||
|
emds[item] = edm
|
||||||
|
|
||||||
|
print()
|
61
test/test__p3_words_embedder.py
Normal file
61
test/test__p3_words_embedder.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
sys.path.append(parent_dir)
|
||||||
|
from p3_words_embedder import do_word_embedder, get_sentence_embeddings, cosine_similarity
|
||||||
|
import unittest
|
||||||
|
import numpy as np
|
||||||
|
import json
|
||||||
|
from sentence_transformers import SentenceTransformer
|
||||||
|
|
||||||
|
class TestSentenceEmbedder(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
# مدل را یکبار بارگذاری میکنیم تا در همه تستها استفاده شود
|
||||||
|
cls.model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
|
||||||
|
|
||||||
|
def test_get_sentence_embeddings(self):
|
||||||
|
# بررسی خروجی تابع get_sentence_embeddings
|
||||||
|
sentence = "This is a test sentence."
|
||||||
|
embeddings = get_sentence_embeddings(sentence)
|
||||||
|
self.assertEqual(len(embeddings.shape), 1, "Embedding should be a 1D vector.")
|
||||||
|
self.assertGreater(embeddings.shape[0], 0, "Embedding vector should not be empty.")
|
||||||
|
|
||||||
|
def test_cosine_similarity(self):
|
||||||
|
# تست تابع cosine_similarity برای دو بردار
|
||||||
|
vec1 = np.array([1, 0, 0])
|
||||||
|
vec2 = np.array([0, 1, 0])
|
||||||
|
vec3 = np.array([1, 1, 0])
|
||||||
|
|
||||||
|
sim1 = cosine_similarity(vec1, vec2)
|
||||||
|
sim2 = cosine_similarity(vec1, vec3)
|
||||||
|
|
||||||
|
self.assertAlmostEqual(sim1, 0.0, places=5, msg="Cosine similarity between orthogonal vectors should be 0.")
|
||||||
|
self.assertAlmostEqual(sim2, 0.7071, places=4, msg="Cosine similarity between vec1 and vec3 should be approximately 0.7071.")
|
||||||
|
|
||||||
|
def test_do_word_embedder(self):
|
||||||
|
# تست تابع do_word_embedder
|
||||||
|
sections = {
|
||||||
|
"sec1": {"content": "This is the first section."},
|
||||||
|
"sec2": {"content": "This is the second section."}
|
||||||
|
}
|
||||||
|
do_word_embedder(sections)
|
||||||
|
|
||||||
|
# فایل خروجی را بررسی میکنیم
|
||||||
|
with open('sect_embeddings.json', 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
for key in sections.keys():
|
||||||
|
self.assertIn('embeddings', data[key], f"Section {key} should contain 'embeddings'.")
|
||||||
|
self.assertEqual(len(data[key]['embeddings']), 384, f"Embedding size for section {key} should be 384.")
|
||||||
|
|
||||||
|
def test_cosine_similarity_edge_cases(self):
|
||||||
|
# تست موارد خاص برای cosine_similarity
|
||||||
|
vec1 = np.array([0, 0, 0])
|
||||||
|
vec2 = np.array([1, 1, 1])
|
||||||
|
|
||||||
|
with self.assertRaises(ZeroDivisionError, msg="Cosine similarity should handle zero vectors."):
|
||||||
|
cosine_similarity(vec1, vec2)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user