diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/src/configuration.py b/src/configuration.py index c4bea68..659522c 100644 --- a/src/configuration.py +++ b/src/configuration.py @@ -77,17 +77,17 @@ class Configuration: character = "".join([f"- {selected_persona[i]}\n" for i in range(len(selected_persona))]) example1 = "{'passage': 'چیپست اسنپدراگون نام خانواده\\u200cای از SoCها یا «سیستم بر روی یک تراشه» است که توسط شرکت تراشه\\u200cساز آمریکایی کوالکام Qualcomm ساخته می شود. هدف از ساخت این تراشه\\u200cها استفاده از آنها در گوشی\\u200cهای همراه هوشمند و تبلت\\u200cهاست. هر تراشه\\u200cی اسنپ\\u200cدراگون شامل واحدهای پردازنده\\u200cی مرکزی، پردازنده\\u200cی گرافیکی Adreno، کنترلر دوربین، سیستم ناوبری جهانی GPS، مودم داخلی برای اتصال به شبکه\\u200cهای 5G و اتصال Wi-Fi است. در واقع در این تراشه\\u200cها تمام ابزارهای لازم برای توانمندسازی یک دستگاه هوشمند یکجا جمع شده است. البته تمام پردازنده های اسنپدراگون توانایی\\u200cهای برابری ندارد.', 'Character': 'A widely acclaimed microprocessor scientist passionate about creating next generation mobile chipsets', 'Question_Type': 'keywords', 'Difficulty': 'university'}" - example2 = '{\'passage\': \'امام علی(ع) پس از خلیفه سوم، به اصرار مسلمانان خلافت و حکومت را پذیرفت. او در دوران حکومتش اهمیت ویژه\\u200cای برای عدالت قائل بود و در برابر روش خلفا که بیت\\u200cالمال را بر اساس سوابق افراد تقسیم می\\u200cکردند، ایستاد. وی دستور داد که عرب و عجم و هر مسلمانی از هر تیره و تبار که باشد، در سهم بیت\\u200cالمال یکسان\\u200cاند و تمام زمین\\u200cهایی که عثمان به افراد مختلف واگذار کرده بود، به بیت\\u200cالمال بازگرداند. در دوره کوتاه حکومت امام علی(ع) سه جنگ سنگین داخلی جمل، صفین و نهروان درگرفت.\', \'Character\': "a devout Shi\'a Muslim scholar who has spent years studying Islamic scriptures and the life of Imam Ali.", \'Question_Type\': \'keywords\', \'Difficulty\': \'university\'}' + example2 = str( + { + "passage": "مدل‌های معادلات ساختاری مبتنی بر بیزی (Bayesian SEM) در دهه‌های اخیر به‌عنوان جایگزینی برای روش‌های کلاسیک بیشینه‌سازی درست‌نمایی مطرح شده‌اند. در این رویکرد، به‌جای برآورد نقطه‌ای پارامترها، توزیع پسین آن‌ها بر اساس پیشین‌های انتخاب‌شده محاسبه می‌شود. یکی از چالش‌های بنیادی در Bayesian SEM، تعیین پیشین‌های ضعیفاً آگاهانه برای ماتریس کوواریانس خطاهاست؛ زیرا انتخاب نامناسب پیشین می‌تواند همگرایی زنجیره‌های MCMC را مختل کرده و منجر به posterior shape distortion شود. افزون بر این، ارزیابی مدل در این چارچوب دیگر محدود به شاخص‌های کلاسیک برازش نیست، بلکه معیارهایی مانند WAIC، LOO-CV و Bayes Factor نقش تعیین‌کننده‌ای دارند. با وجود مزایای متعدد، همچون امکان مدل‌سازی ساختارهای سلسله‌مراتبی پیچیده و داده‌های گمشده، همچنان بحث‌های مهمی درباره حساسیت مدل به specification priors و پایداری استنباط در نمونه‌های کوچک باقی مانده است.", + "Character": "a rigorous quantitative methodologist specializing in Bayesian hierarchical modeling and structural equation theory", + "Question_Type": "acquire_knowledge", + "Difficulty": "phd" + }) example3 = "{'passage': 'همه موجودات زنده بر اساس ویژگی های بسیار اساسی و مشترک طبقه بندی می شوند. سپس موجودات درون هر گروه به گروههای کوچکتر تقسیم می شوند. این گروه های کوچکتر بر اساس شباهت های دقیق تر در هر گروه بزرگتر هستند. این سیستم گروه بندی ، مطالعه گروه های خاصی از موجودات را برای دانشمندان آسان می کند. ویژگی هایی مانند ظاهر ، تولید مثل ، تحرک و عملکرد تنها چند روش برای گروه بندی موجودات زنده است. این گروه های تخصصی را طبقه بندی موجودات زنده می نامند. طبقه بندی موجودات زنده شامل ۷ سطح است: فرمانروا، شاخه، رده، راسته، خانواده، جنس و گونه', 'Character': 'a classification biologists who is slightly impatient with imprecise information.', 'Question_Type': 'acquire_knowledge', 'Difficulty': 'high_school'}" - config_prompt = f"""Given a **Passage** and **Character**, select the appropriate option from -three fields: Character, Question_Type, Difficulty, and return the output -in JSON format. -First, select the Character who are likely to be interested in the Passage -from the candidates. Then select the Question_Type that the Character -might ask about the Passage; Finally, choose the Difficulty of the -possible question based on the Passage, the Character, and the -Question_Type. + config_prompt = f"""Given a **Passage** and **Character**, select the appropriate option from three fields: Character, Question_Type, Difficulty, and return the output in JSON format. +First, select the Character who are likely to be interested in the Passage from the candidates. Then select the Question_Type that the Character might ask about the Passage; Finally, choose the Difficulty of the possible question based on the Passage, the Character, and the Question_Type. Character: Given by input **Character** Question_Type: - keywords: ... @@ -104,9 +104,7 @@ Example1: {example1} Example2: {example2} Example3: {example3} -Now, generate the **output** based on the **Passage** and **Character** from -user, the **Passage** will be in {language} language and the **Character** -will be in English. +Now, generate the **output** based on the **Passage** and **Character** from user, the **Passage** will be in {language} language and the **Character** will be in English. Ensure to generate only the JSON output with content in English. @@ -162,7 +160,7 @@ Ensure to generate only the JSON output with content in English. # for key in data: # example[key] = data[key] - config["length"] = random.choice([10, 20, 40, 80]) + config["length"] = random.choice([10, 20, 40, 80, 150]) return config diff --git a/src/parallel_requester.py b/src/parallel_requester.py new file mode 100644 index 0000000..68a180c --- /dev/null +++ b/src/parallel_requester.py @@ -0,0 +1,53 @@ +import threading +from typing import Callable, Any +from tqdm import tqdm + +class ParallelRequester: + def __init__(self): + self.lock = threading.Lock() + + + def get_a_data(self): + with self.lock: + if self.data_idx < len(self.data): + data = self.data[self.data_idx] + data_idx = self.data_idx + self.data_idx += 1 + else: + data = None + data_idx = None + + + return data, data_idx + + + def thread_function(self, exec_function: Callable[[Any], Any]): + while True: + data, data_idx = self.get_a_data() + if data == None: + return + self.all_res[data_idx] = exec_function(data) + self.pbar.update(1) + + + def run(self, data, exec_function, num_threads): + self.data_idx = 0 + self.all_res = {} + self.data = data + + self.pbar = tqdm(total=len(data), desc="Processing", unit="item") + + allthreads = [] + for thread_idx in range(num_threads): + allthreads += [threading.Thread(target=self.thread_function, args=(exec_function,))] + for thread_idx in range(num_threads): + allthreads[thread_idx].start() + for thread_idx in range(num_threads): + allthreads[thread_idx].join() + + all_res = [self.all_res[i] for i in range(len(self.all_res))] + + del self.all_res + del self.data + + return all_res diff --git a/src/pipline.py.py b/src/pipline.py.py index 9149841..cc0932b 100644 --- a/src/pipline.py.py +++ b/src/pipline.py.py @@ -1,18 +1,12 @@ import json import os -import requests -import tqdm -import faiss -import numpy import importlib -from openai import OpenAI -from dotenv import load_dotenv import re import random +import tqdm import pandas as pd - def import_lib(path, file_name, package_name): file_path = path + "/" + file_name + ".py" spec = importlib.util.spec_from_file_location(file_name, file_path) @@ -23,7 +17,7 @@ def import_lib(path, file_name, package_name): Configuration = import_lib(os.path.dirname(__file__) , "configuration", "Configuration") QueryGenerator = import_lib(os.path.dirname(__file__) , "query_generator", "QueryGenerator") - +ParallelRequester = import_lib(os.path.dirname(__file__) , "parallel_requester", "ParallelRequester") class Pipline: def __init__(self): @@ -32,6 +26,7 @@ class Pipline: self.configuration.init_persona() self.query_generator = QueryGenerator() + def load_data(self): df = pd.read_csv(self.file_path + "/../data/persian_blog/blogs.csv") rows = df.values.tolist() @@ -62,32 +57,91 @@ class Pipline: with open(path + "/v" + str(number) + "_dataset.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) + + + def get_a_data(self): + with self.lock: + if self.data_idx < len(self.data): + data = self.data[self.data_idx] + data_idx = self.data_idx + else: + data = None + data_idx = None + self.data_idx += 1 + return data, data_idx + + + def exec_function(self, passage): + config = self.configuration.run(passage) + generated_data = self.query_generator.run(passage, config) + one_data = config.copy() + one_data["document"] = passage + one_data["query"] = generated_data["query"] + return one_data + + + def make_a_passage(self, selected_lenth, sentences, start_idx): + one_passage = "" + for i in range(start_idx, len(sentences)): + if len(one_passage) + len(sentences[i]) > selected_lenth and len(one_passage) > 0: + return one_passage, i + one_passage += sentences[i] + return one_passage, len(sentences) + + + def chunk_data(self, passage): + max_length = 3000 + min_length = 30 + + if len(passage) < max_length: + return [passage] + + sentences = passage.split(".") + + all_passages = [] + start_idx = 0 + stop_idx = 0 + while True: + selected_lenth = random.choice([50, 100, 200, 300, 500, 800, 1300, 2000, 3000]) + start_idx = stop_idx + one_passage, stop_idx = self.make_a_passage(selected_lenth, sentences, start_idx) + + if len(one_passage) > min_length: + all_passages += [one_passage] + + if stop_idx == len(sentences): + break + + return all_passages + + def pre_process(self, data): + chunk_data = [] + for i in tqdm.trange(len(data)): + chunk_data += self.chunk_data(data[i]) + random.shuffle(chunk_data) + return chunk_data + + def run(self): data = self.load_data() + chunk_data = self.pre_process(data) - num_data = 10 + num_data = 20 + num_threads = 5 + + parallel_requester = ParallelRequester() + dataset = parallel_requester.run(chunk_data[0:num_data], self.exec_function, num_threads) - dataset = [] - for i in range(num_data): - config = self.configuration.run(data[i]) - generated_data = self.query_generator.run(data[i], config) - one_data = config.copy() - one_data["document"] = data[i] - one_data["query"] = generated_data - dataset += [one_data] - self.save_dataset(dataset) - - def main(): + random.seed(42) pipline = Pipline() - pipline.run() if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/query_generator.py b/src/query_generator.py index 1969398..1a50080 100644 --- a/src/query_generator.py +++ b/src/query_generator.py @@ -27,17 +27,23 @@ class QueryGenerator: self.openai_responder = OpenAIResponder(client=client, model=os.environ["OPENAI_MODEL"], price_per_1m_input_tokens=0, price_per_1m_output_tokens=0) def get_prompt(self, passage, character, corpus_language, queries_language, difficulty, length, language, question_type): - - example = "{'passage': 'چیپست اسنپدراگون نام خانواده\\u200cای از SoCها یا «سیستم بر روی یک تراشه» است که توسط شرکت تراشه\\u200cساز آمریکایی کوالکام Qualcomm ساخته می شود. هدف از ساخت این تراشه\\u200cها استفاده از آنها در گوشی\\u200cهای همراه هوشمند و تبلت\\u200cهاست. هر تراشه\\u200cی اسنپ\\u200cدراگون شامل واحدهای پردازنده\\u200cی مرکزی، پردازنده\\u200cی گرافیکی Adreno، کنترلر دوربین، سیستم ناوبری جهانی GPS، مودم داخلی برای اتصال به شبکه\\u200cهای 5G و اتصال Wi-Fi است. در واقع در این تراشه\\u200cها تمام ابزارهای لازم برای توانمندسازی یک دستگاه هوشمند یکجا جمع شده است. البته تمام پردازنده های اسنپدراگون توانایی\\u200cهای برابری ندارد.', 'Character': 'A widely acclaimed microprocessor scientist passionate about creating next generation mobile chipsets', 'Question_Type': 'keywords', 'Difficulty': 'university'}" - example2 = '{\'passage\': \'امام علی(ع) پس از خلیفه سوم، به اصرار مسلمانان خلافت و حکومت را پذیرفت. او در دوران حکومتش اهمیت ویژه\\u200cای برای عدالت قائل بود و در برابر روش خلفا که بیت\\u200cالمال را بر اساس سوابق افراد تقسیم می\\u200cکردند، ایستاد. وی دستور داد که عرب و عجم و هر مسلمانی از هر تیره و تبار که باشد، در سهم بیت\\u200cالمال یکسان\\u200cاند و تمام زمین\\u200cهایی که عثمان به افراد مختلف واگذار کرده بود، به بیت\\u200cالمال بازگرداند. در دوره کوتاه حکومت امام علی(ع) سه جنگ سنگین داخلی جمل، صفین و نهروان درگرفت.\', \'Character\': "a devout Shi\'a Muslim scholar who has spent years studying Islamic scriptures and the life of Imam Ali.", \'Question_Type\': \'keywords\', \'Difficulty\': \'university\'}' - example3 = "{'passage': 'همه موجودات زنده بر اساس ویژگی های بسیار اساسی و مشترک طبقه بندی می شوند. سپس موجودات درون هر گروه به گروههای کوچکتر تقسیم می شوند. این گروه های کوچکتر بر اساس شباهت های دقیق تر در هر گروه بزرگتر هستند. این سیستم گروه بندی ، مطالعه گروه های خاصی از موجودات را برای دانشمندان آسان می کند. ویژگی هایی مانند ظاهر ، تولید مثل ، تحرک و عملکرد تنها چند روش برای گروه بندی موجودات زنده است. این گروه های تخصصی را طبقه بندی موجودات زنده می نامند. طبقه بندی موجودات زنده شامل ۷ سطح است: فرمانروا، شاخه، رده، راسته، خانواده، جنس و گونه', 'Character': 'a classification biologists who is slightly impatient with imprecise information.', 'Question_Type': 'acquire_knowledge', 'Difficulty': 'high_school'}" + example = { + "input" : { + "passage": "مدل‌های معادلات ساختاری مبتنی بر بیزی (Bayesian SEM) در دهه‌های اخیر به‌عنوان جایگزینی برای روش‌های کلاسیک بیشینه‌سازی درست‌نمایی مطرح شده‌اند. در این رویکرد، به‌جای برآورد نقطه‌ای پارامترها، توزیع پسین آن‌ها بر اساس پیشین‌های انتخاب‌شده محاسبه می‌شود. یکی از چالش‌های بنیادی در Bayesian SEM، تعیین پیشین‌های ضعیفاً آگاهانه برای ماتریس کوواریانس خطاهاست؛ زیرا انتخاب نامناسب پیشین می‌تواند همگرایی زنجیره‌های MCMC را مختل کرده و منجر به posterior shape distortion شود. افزون بر این، ارزیابی مدل در این چارچوب دیگر محدود به شاخص‌های کلاسیک برازش نیست، بلکه معیارهایی مانند WAIC، LOO-CV و Bayes Factor نقش تعیین‌کننده‌ای دارند. با وجود مزایای متعدد، همچون امکان مدل‌سازی ساختارهای سلسله‌مراتبی پیچیده و داده‌های گمشده، همچنان بحث‌های مهمی درباره حساسیت مدل به specification priors و پایداری استنباط در نمونه‌های کوچک باقی مانده است.", + "Character": "a rigorous quantitative methodologist specializing in Bayesian hierarchical modeling and structural equation theory", + "Question_Type": "acquire_knowledge", + "Difficulty": "phd" + }, + "output": { + "query": "در مدل‌های معادلات ساختاری مبتنی بر رویکرد بیزی، چرا انتخاب پیشین‌های ضعیفاً آگاهانه برای ماتریس کوواریانس خطاها اهمیت دارد و چه پیامدی در صورت انتخاب نامناسب آن رخ می‌دهد؟" + } + } prompt = f"""Given a **Character**, **Passage**, and **Requirement**, generate a query from the **Character**'s perspective that satisfies the **Requirement** and can be used to retrieve the **Passage**. Please return the result in JSON format. - - +The query must NOT mention, reference, or imply the existence Passage. Instead, generate a natural, self-contained query whose answer can be found within the **Passage**. Here is an example: - +{example} Now, generate the **output** based on the **Character**, **Passage** and **Requirement** from user, the **Passage** will be in {corpus_language} language, the **Character** and **Requirement** will be in English.