complete embedder and kw
This commit is contained in:
parent
b2b1be0a21
commit
afe4f0b87e
|
@ -1,12 +1,12 @@
|
|||
"""
|
||||
سورس اجرای پردازش های مختلف روی اجزای قانونی
|
||||
شامل: کلاسیفیکیشن، تشخیص موجودیت های نامدار، استخراج بردار کلمات، استخراج کلیدواژه ها و سادهسازی متن
|
||||
شامل: کلاسیفیکیشن، تشخیص موجودیت های نامدار، استخراج بردار کلمات، استخراج کلیدواژه ها و سادهسازی(بازنمایی) متن
|
||||
"""
|
||||
from p1_classifier import do_classify
|
||||
from p2_ner_recognizer import do_ner_recognize
|
||||
from p3_words_embedder import do_word_embedder
|
||||
from p4_keyword_extractor import do_keyword_extract
|
||||
from p5_simplifier import do_simplify
|
||||
# from p3_words_embedder import do_word_embedder
|
||||
# from p4_keyword_extractor import do_keyword_extract
|
||||
# from p5_simplifier import do_simplify
|
||||
|
||||
from elastic_helper import ElasticHelper
|
||||
|
||||
|
@ -21,6 +21,14 @@ def main():
|
|||
# get sections to do nlp processes
|
||||
sections = get_sections()
|
||||
|
||||
# dictsections = {}
|
||||
# for item in sections:
|
||||
# if not item['id'] == 'qs2180272':
|
||||
# continue
|
||||
# dictsections[item['id']] = item['source']
|
||||
# break
|
||||
# sections = dictsections
|
||||
|
||||
# 1. classify
|
||||
sections = do_classify(sections)
|
||||
|
||||
|
@ -28,15 +36,15 @@ def main():
|
|||
sections = do_ner_recognize(sections)
|
||||
|
||||
# 3. word embedder
|
||||
sections = do_word_embedder(sections)
|
||||
# sections = do_word_embedder(sections)
|
||||
|
||||
# 4. keyword extract
|
||||
sections = do_keyword_extract(sections)
|
||||
# result_kw = do_keyword_extract(sections)
|
||||
|
||||
# 5. simpify
|
||||
sections = do_simplify(sections)
|
||||
# result_simp = do_simplify(sections)
|
||||
|
||||
print('all nlp processes finished successfully!')
|
||||
|
||||
|
||||
|
||||
main()
|
||||
|
|
|
@ -120,9 +120,9 @@ def do_classify(sections):
|
|||
|
||||
for index, item in enumerate(sections):
|
||||
|
||||
id = item['id']
|
||||
id = item
|
||||
|
||||
source = item['source']
|
||||
source = sections[id]
|
||||
# اگر نوع سکشن، عنوان یا موخره یا امضاء باشد، نیازی به فرایند کلاسبندی نیست و لیست کلاس های مربوط به این سکشن را خالی قرار می دهیم
|
||||
if source['other_info']['full_path'] == 'عنوان' or source['other_info']['full_path'] == 'موخره' or source['other_info']['full_path'] == 'امضاء':
|
||||
new_sections_dict[id] ={
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
"""
|
||||
سورس تشخیص موجودیت های نامدار برای هر جزء قانون
|
||||
این فرایند برای 285 هزار ماده کمتر از دو ساعت زمان می برد
|
||||
"""
|
||||
|
||||
from flair.data import Sentence
|
||||
from flair.models import SequenceTagger
|
||||
import re
|
||||
import json
|
||||
import datetime
|
||||
from elastic_helper import ElasticHelper
|
||||
from normalizer import cleaning
|
||||
|
||||
model = "./models/ner/2025-07-22--20-44-37--HooshvareLab--bert-fa-base-uncased-ner-peyma/final-model.pt"
|
||||
tagger = SequenceTagger.load(model)
|
||||
|
@ -175,14 +180,56 @@ def split_sentence(input_sentence):
|
|||
|
||||
return parts
|
||||
|
||||
def get_sections():
|
||||
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
||||
eh_obj = ElasticHelper()
|
||||
sections = eh_obj.iterateJsonFile(sections_path, True)
|
||||
sections = convert_to_dict(sections)
|
||||
return sections
|
||||
|
||||
def do_ner_recognize(sections):
|
||||
len_sections = len(sections)
|
||||
for index, section in enumerate(sections):
|
||||
content = section['content']
|
||||
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)
|
||||
section['ners_v2'] = ner_data_list
|
||||
print(f'ner process: {section}/{len_sections}')
|
||||
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):
|
||||
sections_dict = {}
|
||||
for item in sections:
|
||||
id = item['id']
|
||||
source = item['source']
|
||||
sections_dict[id] = source
|
||||
|
||||
return sections_dict
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
print(f'start: {datetime.datetime.now()}')
|
||||
sections = get_sections()
|
||||
|
||||
# dictsections = {}
|
||||
# for item in sections:
|
||||
# if not item['id'] == 'qs2180272':
|
||||
# continue
|
||||
# dictsections[item['id']] = item['source']
|
||||
# break
|
||||
# sections = dictsections
|
||||
|
||||
sections = do_ner_recognize(sections)
|
||||
|
||||
with open('./data/ner/sections_ner.json', 'w', encoding='utf-8') as output:
|
||||
data = json.dumps(sections, ensure_ascii=False, indent=4)
|
||||
output.write(data)
|
||||
|
||||
print(f'end: {datetime.datetime.now()}')
|
||||
|
||||
print('finished ner recognization!')
|
||||
|
|
@ -1,4 +1,30 @@
|
|||
"""
|
||||
ایجاد بردار جملات - امبدینگ
|
||||
"""
|
||||
from sentence_transformers import SentenceTransformer
|
||||
import json
|
||||
|
||||
def load_embedder_model(model_name):
|
||||
model = SentenceTransformer(model_name)
|
||||
return model
|
||||
|
||||
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):
|
||||
embeddings = get_sentence_embeddings(sections[item]['content'])
|
||||
sections[item]['embeddings'] = embeddings
|
||||
|
||||
with open('./data/embeddings/sections_embeddings.json', 'w', encoding='utf-8') as outpu_file:
|
||||
data = json.dumps(sections, ensure_ascii=False)
|
||||
outpu_file.write(data)
|
||||
|
||||
def get_sentence_embeddings(sentence):
|
||||
model_name = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
|
||||
model = load_embedder_model(model_name)
|
||||
embeddings = model.encode(sentence)
|
||||
return embeddings
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
|
|
@ -1,135 +1,193 @@
|
|||
|
||||
|
||||
"""
|
||||
استخراج واژگان کلیدی از اجزاء قانونی
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
import json
|
||||
import datetime
|
||||
import torch
|
||||
import time
|
||||
|
||||
import os
|
||||
from elastic_helper import ElasticHelper
|
||||
from transformers import AutoTokenizer, AutoModel
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
||||
|
||||
from normalizer import Normalizer
|
||||
from tokenizer import *
|
||||
os.environ['HF_HOME'] = "/home/admin/HFHOME"
|
||||
|
||||
address = os.getcwd()
|
||||
model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
||||
#model_id = "meta-llama/Llama-3.1-70B-Instruct"
|
||||
|
||||
not_have_two_token_kw_list = []
|
||||
|
||||
if torch.cuda.is_available():
|
||||
model_id = "PartAI/Dorna-Llama3-8B-Instruct"
|
||||
#model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
||||
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", torch_dtype=torch.bfloat16)
|
||||
bnb_config = BitsAndBytesConfig(
|
||||
load_in_8bit=True, bnb_8bit_use_double_quant=True, bnb_8bit_quant_type="nf8", bnb_8bit_compute_dtype=torch.bfloat16
|
||||
)
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
model_id,
|
||||
torch_dtype=torch.bfloat16,
|
||||
device_map="auto",
|
||||
quantization_config=bnb_config
|
||||
)
|
||||
terminators = [
|
||||
tokenizer.eos_token_id,
|
||||
tokenizer.convert_tokens_to_ids("<|eot_id|>")
|
||||
]
|
||||
model.generation_config.pad_token_id = tokenizer.eos_token_id #tokenizer.pad_token_id
|
||||
|
||||
index_name_i = 'semantic_search-v10'
|
||||
# SYS_PROMPT = """You receive a Persian legal text and extract from it the keywords that are most important.
|
||||
# And you don't need to provide explanations or additional text.
|
||||
# Put each keyword on a single line."
|
||||
# """# Explain your answer step by step.
|
||||
|
||||
SYS_PROMPT = """You are a highly accurate and detail-oriented assistant specialized in analyzing Persian legal texts."""# Explain your answer step by step.
|
||||
SYS_PROMPT = """شما یک دستیار متخصص در استخراج عبارات کلیدی از متون حقوقی فارسی هستید. وظیفه شما این است که از متن ارائه شده توسط کاربر، عبارات اسمی کلیدی مهم و معنادار را استخراج کنید."""# gemini prompt
|
||||
|
||||
|
||||
counter = 0
|
||||
total = 0
|
||||
remained = 0
|
||||
id = ''
|
||||
keywords_count = 15
|
||||
def format_prompt(SENTENCE):
|
||||
# PROMPT = f"Persian legal text: {SENTENCE}."
|
||||
PROMPT = f"متن: {SENTENCE}."
|
||||
return PROMPT
|
||||
|
||||
def generateKeywords(text):
|
||||
global remained
|
||||
try:
|
||||
def kw_count_calculator(text):
|
||||
keywords_count = (len(text) / 1000) * 15
|
||||
keywords_count = int(keywords_count)
|
||||
if keywords_count == 0:
|
||||
keywords_count = 1
|
||||
return keywords_count
|
||||
|
||||
def generate(formatted_prompt):
|
||||
keywords_count = kw_count_calculator(formatted_prompt)
|
||||
|
||||
messages = [{"role": "system", "content": "تو یک وکیل حقوق دان هستی و باید بتوانی متن های قانونی و حقوقی را بدون تغییر اصطلاحات فنی، به صورتی توضیح دهی که افراد غیر حقوق دان، معنای متن را درک کنند. " },
|
||||
{"role": "user", "content":
|
||||
'''از "متن" حداقل {} عبارت های کلیدی مهم و پراهمیت را استخراج کن و عبارت های کلیدی را در قالب لیست به زبان فارسی چاپ کن و هر کلید عبارت کلیدی را در یک خط جدید قرار بده و هیچ گونه توضیحی در ابتدا یا انتهای پاسخ، اضافه نکن.
|
||||
هر عبارت کلیدی دارای یک شماره ترتیبی در ابتدای آن باشد. عبارت های کلیدی، دقیقا در متن موجود باشد. بسیار مهم و ضروری است که طول هر عبارت کلیدی حداقل دو توکن داشته باشد و عبارت کلیدی یک توکنی قابل قبول نیست. تاکید می کنم که هیچ عبارت کلیدی نباید فقط یک توکن داشته باشد. نام سازمان ها و نهادها و اشخاص حقوقی، حتما به عنوان عبارت کلیدی درنظر گرفته شود. هیچ عبارت کلیدی، فعل یا حرف اضافه نباشد و فقط شامل اسم هایی باشد که به هم اضافه شده اند. هیچ عبارت کلیدی نباید با حرف اضافه یا حرف «و» تمام شود. ضروری است که عبارت های کلیدی شامل ماده، بند، تبصره یا تاریخ ها نباشند.
|
||||
"متن": {}
|
||||
'''.format(keywords_count, text)
|
||||
}]
|
||||
|
||||
|
||||
# Gemini Prompt
|
||||
USER_PROMPT = f"""از متن ارائه شده، حداقل {keywords_count} عبارت کلیدی مهم و معنادار را استخراج کنید. خروجی باید به صورت زیر باشد:
|
||||
• یک لیست فارسی
|
||||
• شماره ترتیبی در ابتدای هر عبارت
|
||||
• هر عبارت در یک خط جداگانه *
|
||||
• بدون هیچ توضیح اضافی در ابتدا یا انتهای پاسخ
|
||||
موارد زیر در استخراج عبارات کلیدی الزامی است:
|
||||
• بسیار مهم و حیاتی است که عبارات کلیدی باید دقیقاً در متن موجود باشند، بنابراین هرگز از خلاقیت برای ساختن کلیدواژهای که دقیقا در متن وجود ندارد، استفاده نکن.
|
||||
• طول هر عبارت کلیدی حداقل دو کلمه باشد. عبارات تک-کلمهای قابل قبول نیستند.
|
||||
• نام سازمانها، مؤسسات و اشخاص حقوقی باید به عنوان عبارات کلیدی در نظر گرفته شوند.
|
||||
• عبارات کلیدی نباید فعل یا حرف اضافه باشند و فقط باید شامل اسمهایی باشند که به هم اضافه شدهاند (عبارت اسمی).
|
||||
• عبارات کلیدی نباید به حرف اضافه یا حرف "و" ختم شوند.
|
||||
• عبارات کلیدی نباید شامل کلمات "ماده"، "تبصره" و "بند" یا تاریخها باشند.
|
||||
به عنوان مثال، اگر متن "قانون مدنی جمهوری اسلامی ایران در ماده ۲۲۲ در خصوص مسئولیت مدنی اشخاص حقوقی در تبصره ۱ به موضوع خسارت ناشی از تقصیر کارکنان اشاره دارد." باشد، خروجی باید به شکل زیر باشد:
|
||||
1. قانون مدنی جمهوری اسلامی ایران
|
||||
2. مسئولیت مدنی اشخاص حقوقی
|
||||
3. خسارت ناشی از تقصیر کارکنان
|
||||
اکنون متن مورد نظر را دریافت خواهید کرد.
|
||||
"""
|
||||
formatted_prompt = formatted_prompt[:50000] # to avoid GPU OOM
|
||||
messages = [
|
||||
{"role":"system","content":SYS_PROMPT},
|
||||
{"role":"user","content":USER_PROMPT},
|
||||
{"role":"user","content":formatted_prompt}
|
||||
]
|
||||
# tell the model to generate
|
||||
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
|
||||
|
||||
|
||||
outputs = model.generate(
|
||||
input_ids,
|
||||
max_new_tokens=256,
|
||||
max_new_tokens=2048,
|
||||
eos_token_id=terminators,
|
||||
do_sample=True,
|
||||
temperature=0.6,
|
||||
top_p=0.85,
|
||||
top_p=0.9,
|
||||
)
|
||||
#lock0.release()
|
||||
response = outputs[0][input_ids.shape[-1]:]
|
||||
keywords = tokenizer.decode(response, skip_special_tokens=True)
|
||||
#lock1.acquire()
|
||||
# resp = es.update(index=index_name_i, id=id, doc={"content_keywords-llama3-str": str(keywords)})
|
||||
return tokenizer.decode(response, skip_special_tokens=True)
|
||||
|
||||
|
||||
return keywords
|
||||
|
||||
except Exception as inst:
|
||||
print(type(inst)) # the exception type
|
||||
print(inst.args) # arguments stored in .args
|
||||
print("Exception: " + str(inst))
|
||||
def get_rules(sentence):
|
||||
formatted_prompt = format_prompt(sentence)
|
||||
rules = generate(formatted_prompt).split('\n')
|
||||
result = [r.strip() for r in rules if r.strip()]
|
||||
return result
|
||||
|
||||
|
||||
def do_keyword_extract(sections):
|
||||
start_time = time.time()
|
||||
print("start_time: "+str(datetime.now()))
|
||||
|
||||
try:
|
||||
keywords_dict = []
|
||||
|
||||
count = 1
|
||||
for index, content_item in enumerate(sections):
|
||||
id = content_item
|
||||
# if not id in not_have_two_token_kw_list:
|
||||
# continue
|
||||
content = content_item['content']
|
||||
content_len = len(content.split())
|
||||
# کنارگذاشتن محتواهای با حجم زیاد
|
||||
if content_len > 2000:
|
||||
print("too long content " + str(id))
|
||||
continue
|
||||
|
||||
keywords = generateKeywords(content)
|
||||
print("section " + str(count) + "/" + str(len(not_have_two_token_kw_list)) + " keyword extracting ... ")
|
||||
content_item['keywords'] = keywords
|
||||
keywords_dict.append({
|
||||
'id':id,
|
||||
'keywords':keywords
|
||||
})
|
||||
count+=1
|
||||
|
||||
# write_to_json(keywords_dict, "../data/sections_kw_110_llama_main.json")
|
||||
|
||||
except Exception as inst:
|
||||
print(type(inst)) # the exception type
|
||||
print(inst.args) # arguments stored in .args
|
||||
|
||||
end_time = time.time()
|
||||
print("end_time: "+ str(datetime.now()))
|
||||
operation_time = (int(end_time-start_time)/60)/60
|
||||
print(f"elapsed time: {operation_time} hours")
|
||||
print(f"Finished!!!")
|
||||
|
||||
def get_sections():
|
||||
sections_path = "/home/gpu/data_11/14040423/mj_qa_section.zip"
|
||||
eh_obj = ElasticHelper()
|
||||
sections = eh_obj.iterateJsonFile(sections_path, True)
|
||||
sections = convert_to_dict(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 do_keyword_extract(sections):
|
||||
start_time = datetime.datetime.now()
|
||||
print(f'start time: {start_time}')
|
||||
|
||||
# prev_ids = read_file_by_address('./data/prev_kw_ids_170k.txt').splitlines()
|
||||
counter = 1
|
||||
file_counter = 1
|
||||
temp_dict = []
|
||||
temp_data_text = ''
|
||||
period_sections = []
|
||||
period_ids_text = f'***** file number: {file_counter} *****\n'
|
||||
for index, item in enumerate(sections):
|
||||
# if item['id'] in prev_ids:
|
||||
# continue
|
||||
|
||||
if counter > 10:
|
||||
with open('./data/keyword/prev_kw_ids.txt', 'a+', encoding='utf-8') as file:
|
||||
file.write(period_ids_text)
|
||||
break
|
||||
|
||||
id = item
|
||||
content = sections[id]['content']
|
||||
try:
|
||||
sections[id]['keywords'] = get_rules(content)
|
||||
except:
|
||||
print(f"section kw error : {id}\n")
|
||||
counter += 1
|
||||
continue
|
||||
|
||||
period_ids_text += f"{id} \n"
|
||||
|
||||
print(f"section: {counter}-id: {id}")
|
||||
# temp_dict.append(item)
|
||||
if counter % 1000 == 0:
|
||||
outputfile = open(f'./data/keyword/sections_kw_llama8b_{str(file_counter)}.json', "a+", encoding='utf-8')
|
||||
outputfile.write(json.dumps(period_sections, ensure_ascii=False, indent=2))
|
||||
outputfile.close()
|
||||
print(f"file {str(file_counter)} created in {str(datetime.datetime.now())} +++++++++++++++++++++++++\n ")
|
||||
|
||||
file_counter += 1
|
||||
period_sections = []
|
||||
# save proccessed sections id for next executions of this code
|
||||
with open('./data/keyword/prev_kw_ids.txt', 'a+', encoding='utf-8') as file:
|
||||
file.write(period_ids_text)
|
||||
counter += 1
|
||||
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.write(json.dumps(period_sections, ensure_ascii=False, indent = 4))
|
||||
outputfile.close()
|
||||
print(f"file {str(file_counter)} created in {str(datetime.datetime.now())} +++++++++++++++++++++++++ ")
|
||||
|
||||
end_time = datetime.datetime.now()
|
||||
print(f"end_time: {end_time}")
|
||||
print(f"elapsed time: {(end_time-start_time)} Hours!!! ")
|
||||
print(f"elapsed time: {(end_time-start_time)/86400} Days!!! ")
|
||||
print("end")
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
print(f'start: {datetime.datetime.now()}')
|
||||
sections = get_sections()
|
||||
|
||||
sections = do_keyword_extract(sections)
|
||||
|
||||
print(f'end: {datetime.datetime.now()}')
|
Loading…
Reference in New Issue
Block a user