Compare commits

..

No commits in common. "main" and "qwen_train" have entirely different histories.

29 changed files with 67 additions and 8024 deletions

View File

@ -8,12 +8,12 @@ from data_preprocess.text_embedder import TextEmbedder
THRESHOLD_MULTIPLY = 0.95
RANDOM_NEGATIVE_COUNT = 6
batch_size = 1000
batch_size = 100
text_embedder = TextEmbedder()
def generate_random_negative_sample(all_dataset, corpus_list=[]):
def generate_random_negative_sample(all_dataset):
"""
generate random negative sample from dataset
Args:
@ -35,7 +35,7 @@ def generate_random_negative_sample(all_dataset, corpus_list=[]):
for id in range(i, min(i + batch_size, len_dataset)):
question_list.append(all_dataset[id]['question'])
question_embeddings = text_embedder.embed_texts(question_list, do_preprocess=False, convert_to_numpy=False)
question_embeddings = text_embedder.embed_texts(question_list)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
@ -52,7 +52,7 @@ def generate_random_negative_sample(all_dataset, corpus_list=[]):
for passage in all_dataset[id]['passage_positive']:
passage_positive_list.append(passage)
passage_positive_embeddings = text_embedder.embed_texts(passage_positive_list, do_preprocess=False, convert_to_numpy=False)
passage_positive_embeddings = text_embedder.embed_texts(passage_positive_list)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
@ -71,7 +71,7 @@ def generate_random_negative_sample(all_dataset, corpus_list=[]):
for passage in all_dataset[id]['passage_negative']:
passage_negative_list.append(passage)
passage_negative_embeddings = text_embedder.embed_texts(passage_negative_list, do_preprocess=False, convert_to_numpy=False)
passage_negative_embeddings = text_embedder.embed_texts(passage_negative_list)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
@ -80,18 +80,10 @@ def generate_random_negative_sample(all_dataset, corpus_list=[]):
all_texts.append(all_dataset[id]['passage_negative'][passage_id])
count += 1
print("calculate corpus embeddings")
# calculate corpus embeddings
for i in tqdm(range(0, len(corpus_list), batch_size)):
corpus_embeddings = text_embedder.embed_texts(corpus_list[i:i+batch_size], do_preprocess=False, convert_to_numpy=False)
all_embeddings.extend(corpus_embeddings)
all_texts.extend(corpus_list[i:i+batch_size])
############ Create FAISS index ############
all_embeddings = np.array(all_embeddings, dtype=np.float32)
dim = all_embeddings.shape[1]
# index = faiss.IndexFlatIP(dim)
index = faiss.IndexHNSWFlat(dim, 32, faiss.METRIC_INNER_PRODUCT)
index = faiss.IndexFlatIP(dim)
faiss.normalize_L2(all_embeddings)
index.add(all_embeddings)
@ -102,16 +94,15 @@ def generate_random_negative_sample(all_dataset, corpus_list=[]):
question_embeddings = all_dataset_embeddings[id]['question_embedding']
question_embeddings_normalized = np.array([question_embeddings], dtype=np.float32)
faiss.normalize_L2(question_embeddings_normalized)
# passage_positive_embeddings = all_dataset_embeddings[id]['passage_positive_embedding'][0]
passage_positive_embeddings = all_dataset_embeddings[id]['passage_positive_embedding'][0]
# score_question_passage_positive = np.dot(question_embeddings, passage_positive_embeddings)
score_question_passage_positive = np.dot(question_embeddings, passage_positive_embeddings)
num_retrieved = 15
num_retrieved = 30
vector_scores, vector_ids = index.search(question_embeddings_normalized, num_retrieved)
for vector_score, vector_id in zip(vector_scores[0], vector_ids[0]):
if (all_texts[vector_id] not in not_valid_passages):# and (vector_score < THRESHOLD_MULTIPLY * score_question_passage_positive):
if (all_texts[vector_id] not in not_valid_passages) and (vector_score < THRESHOLD_MULTIPLY * score_question_passage_positive):
all_dataset[id]['passage_negative_random'].append(all_texts[vector_id])
not_valid_passages.append(all_texts[vector_id])
if len(all_dataset[id]['passage_negative_random']) >= RANDOM_NEGATIVE_COUNT:
break

View File

@ -1,103 +0,0 @@
from typing import List, Dict, Any
import json
import asyncio
import aiohttp
import time
import re
model_url = "http://192.168.130.206:4001/v1"
model = "google/gemma-3-27b-it"
class LLMModel:
def __init__(self):
self.instruction = """
You are a helpful assistant that help to me to modify and change the input question.
I will give you a question and its text and you must replace the words of question with synonyms or similar words.
## Important:
- replace the words of question with synonyms or similar words.
-the question must be in persian language.
return the question nothing else.
"""
async def run_llm(self, session, question, text):
"""
Run the llm model.
Args:
session: The session to use for the request.
question: The question to evaluate the text.
text: The text to evaluate.
Returns:
The result of the text.
"""
headers = {"Content-Type": "application/json"}
input_message = f"""{{"question": "{question}", "text": "{text}"}}"""
messages = [{"role": "system", "content": self.instruction}, {"role": "user", "content": input_message}]
payload = {
"model": model,
"messages": messages,
"max_tokens": 100
}
try:
async with session.post(model_url + "/chat/completions", headers=headers, json=payload) as resp:
resp.raise_for_status()
response = await resp.json()
result = response['choices'][0]['message']['content']
print(f"question: {question}")
print(f"result: {result}")
print("--------------------------------")
return result
except Exception as e:
try:
print(f"Error in llm model {response}: {e}")
except:
print(f"Error in llm model: {e}")
return ""
async def run_llm_async(self, question_list, text_list):
"""
Send all chunk requests concurrently.
Args:
question_list: The list of questions.
text_list: The list of texts.
Returns:
The list of results.
"""
async with aiohttp.ClientSession() as session:
tasks = [self.run_llm(session, question, text) for question, text in zip(question_list, text_list)]
results = await asyncio.gather(*tasks)
return results
def modify_question_llm(self, query_list: List[str], text_list: List[str]) -> List[Dict[str, Any]]:
"""
Modify question of the documents based on the query using the LLM model.
Args:
query_list: The list of queries.
text_list: The list of texts.
Returns:
The list of modified questions.
"""
if not text_list:
return []
start_time = time.time()
results = asyncio.run(self.run_llm_async(query_list, text_list))
end_time = time.time()
# print(f"Time taken for llm model: {end_time - start_time} seconds")
return results

View File

@ -153,9 +153,9 @@ def main(output_path):
#load synthetic dataset
print("--------------------------------")
print("loading synthetic dataset")
synthetic_train_path = "/home/firouzi/embedding_model/research_notebook/data/synthetic-persian-qa-retrieval/train.jsonl"
synthetic_corpus_path = "/home/firouzi/embedding_model/research_notebook/data/synthetic-persian-qa-retrieval/corpus.jsonl"
synthetic_queries_path = "/home/firouzi/embedding_model/research_notebook/data/synthetic-persian-qa-retrieval/queries.jsonl"
synthetic_train_path = "/home/firouzi/embedding_model/data_preprocess_notebook/data/synthetic-persian-qa-retrieval/train.jsonl"
synthetic_corpus_path = "/home/firouzi/embedding_model/data_preprocess_notebook/data/synthetic-persian-qa-retrieval/corpus.jsonl"
synthetic_queries_path = "/home/firouzi/embedding_model/data_preprocess_notebook/data/synthetic-persian-qa-retrieval/queries.jsonl"
synthetic_dataset = load_synthetic_dataset(synthetic_train_path, synthetic_queries_path, synthetic_corpus_path)
print(f"synthetic dataset loaded : {len(synthetic_dataset)} samples")
@ -173,11 +173,11 @@ def main(output_path):
print(f"successfully merged synthetic and pquad dataset")
print("--------------------------------")
# # removing false negative samples from all dataset
# print("start to remove false negative samples from all dataset")
# all_dataset = remove_false_negative(all_dataset, random_negative_sample=False)
# print(f"successfully removed false negative samples from all dataset")
# print("--------------------------------")
# removing false negative samples from all dataset
print("start to remove false negative samples from all dataset")
all_dataset = remove_false_negative(all_dataset, random_negative_sample=False)
print(f"successfully removed false negative samples from all dataset")
print("--------------------------------")
# with open("/home/firouzi/embedding_model/data/train.json", "r", encoding="utf-8") as f:
# all_dataset = json.load(f)

View File

@ -1,160 +0,0 @@
import argparse
from datasets import load_dataset
import json
from tqdm import tqdm
from data_preprocess.remove_false_negative_model import LLMModel
from data_preprocess.generate_random_negative_sample import generate_random_negative_sample
llm_model = LLMModel()
def load_msmarco_dataset():
"""
load pquad dataset from huggingface
output:
[{
"question": "",
"passage_positive": [],
"passage_negative": [],
"passage_negative_random": []
}]
"""
print("start loading msmarco dataset")
name = "MCINext/msmarco-fa"
dataset_qrel = load_dataset(name)["train"]
print("start loading corpus")
dataset_corpus_list = load_dataset(name,data_files="corpus.jsonl")["train"]
dataset_corpus = {}
for data in dataset_corpus_list:
dataset_corpus[str(data["_id"])] = data["text"]
print("start loading queries")
dataset_queries_list = load_dataset(name,data_files="queries.jsonl")["train"]
dataset_queries = {}
for data in dataset_queries_list:
dataset_queries[str(data["_id"])] = data["text"]
dataset = []
print("start creating dataset")
for data in tqdm(dataset_qrel):
if data["query-id"] in dataset_queries and data["corpus-id"] in dataset_corpus:
dataset.append({
"question": dataset_queries[data["query-id"]],
"passage_positive": [dataset_corpus[data["corpus-id"]]],
"passage_negative": [],
"passage_negative_random": [],
})
print(f"length of dataset: {len(dataset)}")
print("--------------------------------")
return dataset, list(dataset_corpus.values())
def remove_false_negative(dataset, random_negative_sample=False):
"""
remove false negative samples from synthetic dataset
Args:
dataset: list of dicts
Returns:
dataset: list of dicts
"""
if random_negative_sample:
negative_name = "passage_negative_random"
else:
negative_name = "passage_negative"
# calculate passage negative embeddings
negative_count_all = 0
negative_count_removed = 0
len_dataset = len(dataset)
batch_size = 50
for i in tqdm(range(0, len_dataset, batch_size)):
question_list = []
passage_negative_list = []
for id in range(i, min(i + batch_size, len_dataset)):
for passage in dataset[id][negative_name]:
question_list.append(dataset[id]['question'])
passage_negative_list.append(passage)
results = llm_model.remove_false_negative_llm(question_list, passage_negative_list)
negative_count_removed += len([_ for _ in results if _ == "1"])
negative_count_all += len(results)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
new_negative_list = []
for passage_id in range(len(dataset[id][negative_name])):
if results[count] == "0":
new_negative_list.append(dataset[id][negative_name][passage_id])
count += 1
dataset[id][negative_name] = new_negative_list
print(f"removed {negative_count_removed} false negative samples from {negative_count_all} samples")
print("--------------------------------")
return dataset
def save_dataset(dataset, output_path):
"""
save dataset to json file
Args:
dataset: list of dicts
output_path: path to save dataset
"""
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(dataset, f, ensure_ascii=False, indent=4)
def main(output_path):
#load msmarco dataset
print("--------------------------------")
all_dataset, corpus_list = load_msmarco_dataset()
print(f"msmarco dataset loaded : {len(all_dataset)} samples")
print("--------------------------------")
#generate random negative samples
print("start to generate random negative samples")
all_dataset = generate_random_negative_sample(all_dataset, corpus_list)
print(f"successfully generated random negative samples")
print("--------------------------------")
# removing random false negative samples from all dataset
print("start to remove random false negative samples from all dataset")
all_dataset = remove_false_negative(all_dataset, random_negative_sample=True)
print(f"successfully removed random false negative samples from all dataset")
print("--------------------------------")
# save dataset
print("start to save dataset")
save_dataset(all_dataset, output_path)
print(f"successfully saved dataset")
print("--------------------------------")
if __name__ == "__main__":
"""
preprocess dataset for training
pipelines:
load msmarco dataset from huggingface
generate random negative samples
save dataset to json file
python preprocess_v2.py --output_path /home/firouzi/embedding_model/data/v2/msmarco_train.json
"""
parser = argparse.ArgumentParser()
parser.add_argument("--output_path", type=str, required=True)
args = parser.parse_args()
output_path = args.output_path
main(output_path)

View File

@ -1,142 +0,0 @@
import argparse
from datasets import load_dataset
import json
from tqdm import tqdm
import time
from data_preprocess.modify_question_model import LLMModel
llm_model = LLMModel()
def load_msmarco_dataset():
"""
load pquad dataset from huggingface
output:
[{
"question": "",
"passage_positive": [],
"passage_negative": [],
"passage_negative_random": []
}]
"""
print("start loading msmarco dataset")
name = "MCINext/msmarco-fa"
dataset_qrel = load_dataset(name)["train"]
print("start loading corpus")
dataset_corpus_list = load_dataset(name,data_files="corpus.jsonl")["train"]
dataset_corpus = {}
for data in dataset_corpus_list:
dataset_corpus[str(data["_id"])] = data["text"]
print("start loading queries")
dataset_queries_list = load_dataset(name,data_files="queries.jsonl")["train"]
dataset_queries = {}
for data in dataset_queries_list:
dataset_queries[str(data["_id"])] = data["text"]
dataset = []
print("start creating dataset")
for data in tqdm(dataset_qrel):
if data["query-id"] in dataset_queries and data["corpus-id"] in dataset_corpus:
dataset.append({
"question": dataset_queries[data["query-id"]],
"passage_positive": [dataset_corpus[data["corpus-id"]]],
"new_question": "",
"passage_negative": [],
"passage_negative_random": [],
})
print(f"length of dataset: {len(dataset)}")
print("--------------------------------")
return dataset
def modify_question(dataset):
"""
modify question of dataset
Args:
dataset: list of dicts
Returns:
dataset: list of dicts
"""
len_dataset = len(dataset)
batch_size = 50
for i in tqdm(range(0, len_dataset, batch_size)):
question_list = []
passage_positive_list = []
for id in range(i, min(i + batch_size, len_dataset)):
question_list.append(dataset[id]['question'])
passage_positive_list.append(dataset[id]['passage_positive'][0])
results = llm_model.modify_question_llm(question_list, passage_positive_list)
time.sleep(2)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
dataset[id]["new_question"] = results[count]
count += 1
print(f"successfully modified question")
print("--------------------------------")
return dataset
def save_dataset(dataset, output_path):
"""
save dataset to json file
Args:
dataset: list of dicts
output_path: path to save dataset
"""
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(dataset, f, ensure_ascii=False, indent=4)
def main(output_path):
#load msmarco dataset
print("--------------------------------")
all_dataset = load_msmarco_dataset()
print(f"msmarco dataset loaded : {len(all_dataset)} samples")
print("--------------------------------")
# removing random false negative samples from all dataset
print("start to modify question")
all_dataset = modify_question(all_dataset[:270000])
print(f"successfully modified question")
print("--------------------------------")
# save dataset
print("start to save dataset")
save_dataset(all_dataset, output_path)
print(f"successfully saved dataset")
print("--------------------------------")
if __name__ == "__main__":
"""
preprocess dataset for training
pipelines:
load msmarco dataset from huggingface
generate random negative samples
save dataset to json file
python preprocess_v2.py --output_path /home/firouzi/embedding_model/data/v2/msmarco_train.json
"""
parser = argparse.ArgumentParser()
parser.add_argument("--output_path", type=str, required=True)
args = parser.parse_args()
output_path = args.output_path
main(output_path)

View File

@ -102,7 +102,7 @@ class LLMModel:
start_time = time.time()
results = asyncio.run(self.run_llm_async(query_list, text_list))
end_time = time.time()
# print(f"Time taken for llm model: {end_time - start_time} seconds")
print(f"Time taken for llm model: {end_time - start_time} seconds")
return results

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ import requests
import numpy as np
from dotenv import load_dotenv
import os
import time
load_dotenv()
@ -21,26 +20,21 @@ class TextEmbedder:
return text
def embed_texts(self, texts:list[str], do_preprocess=True, convert_to_numpy=True)->list[list[float]]:
def embed_texts(self, texts:list[str])->list[list[float]]:
"""
Embed texts using the model.
"""
if texts == []:
return []
if do_preprocess:
texts = [self.preprocess_embedder(text) for text in texts]
texts = [self.preprocess_embedder(text) for text in texts]
payload = {
"model": self.model_name,
"input": texts
}
responses = requests.post("http://78.38.161.78:3094/v1/embeddings", headers=self.headers, json=payload)
if convert_to_numpy:
embeddings = [np.array(response["embedding"], dtype=np.float32) for response in responses.json()["data"]]
else:
embeddings = [response["embedding"] for response in responses.json()["data"]]
embeddings = [np.array(response["embedding"], dtype=np.float32) for response in responses.json()["data"]]
return embeddings

View File

@ -1,225 +0,0 @@
import argparse
import json
import math
import importlib
from tqdm import tqdm
from hazm import Normalizer
import random
import numpy as np
import faiss
normalizer = Normalizer()
def load_dataset(input_file):
with open(input_file, "r", encoding="utf-8") as f:
dataset = json.load(f)[:1000]
return dataset
def calculate_ndcg(scores, n):
def calculate_dcg(scores, n):
idcg = 0
for i in range(n):
a = (2 ** scores[i]) - 1
b = math.log2(i + 2)
idcg += (a/b)
return idcg
def calculate_idcg(scores, n):
new_scores = scores.copy()
new_scores.sort(reverse=True)
idcg = calculate_dcg(new_scores, n)
return idcg
dcg = calculate_dcg(scores, n)
idcg = 1 #calculate_idcg(scores, n)
ndcg = dcg/idcg
return ndcg
def calculate_recall(scores):
try:
num_ground_truth = scores.count(1)
recall_7 = scores[:7].count(1) / num_ground_truth
recall_12 = scores[:12].count(1) / num_ground_truth
recall_20 = scores[:20].count(1) / num_ground_truth
recall_variant = scores[:scores.count(1)].count(1) / scores.count(1)
return recall_7, recall_12, recall_20, recall_variant
except:
return 0, 0, 0, 0
def calculate_precision(scores):
precision_7 = scores[:7].count(1) / 7
precision_12 = scores[:12].count(1) / 12
precision_20 = scores[:20].count(1) / 20
return precision_7, precision_12, precision_20
def preprocess_reranker(text:str, preprocess:bool=True, add_extra_word:bool=False):
if preprocess:
text = text.replace("\n", ".")
text = normalizer.normalize(text)
if add_extra_word:
text += " رهبر انقلاب اسلامی حضرت امام خامنه ای "
return text
def run(input_file, model):
module = importlib.import_module("evaluation.models." + model)
model = module.model()
ndcg_scores = []
recall_7_scores = []
recall_12_scores = []
recall_20_scores = []
recall_variant_scores = []
precision_7_scores = []
precision_12_scores = []
precision_20_scores = []
all_dataset = load_dataset(input_file)[:1000]
batch_size = 100
len_dataset = len(all_dataset)
all_dataset_embeddings = [{'question_embedding': "", 'passage_positive_embedding': []} for _ in range(len_dataset)]
all_embeddings = []
all_texts = []
print("calculate question embeddings")
# calculate question embeddings
for i in tqdm(range(0, len_dataset, batch_size)):
question_list = []
for id in range(i, min(i + batch_size, len_dataset)):
question_list.append(all_dataset[id]['question'])
question_embeddings = model.embed_texts(question_list, query_is=True)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
all_dataset_embeddings[id]['question_embedding'] = question_embeddings[count]
count += 1
print("calculate passage positive embeddings")
# calculate passage positive embeddings
for i in tqdm(range(0, len_dataset, batch_size)):
passage_positive_list = []
for id in range(i, min(i + batch_size, len_dataset)):
for passage in all_dataset[id]['passage_positive']:
passage_positive_list.append(passage)
passage_positive_embeddings = model.embed_texts(passage_positive_list)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
for passage_id in range(len(all_dataset[id]['passage_positive'])):
all_dataset_embeddings[id]['passage_positive_embedding'].append(passage_positive_embeddings[count])
all_embeddings.append(passage_positive_embeddings[count])
all_texts.append(all_dataset[id]['passage_positive'][passage_id])
count += 1
print("calculate passage negative embeddings")
# calculate passage negative embeddings
for i in tqdm(range(0, len_dataset, batch_size)):
passage_negative_list = []
for id in range(i, min(i + batch_size, len_dataset)):
for passage in all_dataset[id]['passage_negative']:
passage_negative_list.append(passage)
passage_negative_embeddings = model.embed_texts(passage_negative_list)
count = 0
for id in range(i, min(i + batch_size, len_dataset)):
for passage_id in range(len(all_dataset[id]['passage_negative'])):
all_embeddings.append(passage_negative_embeddings[count])
all_texts.append(all_dataset[id]['passage_negative'][passage_id])
count += 1
#create faiss index
all_embeddings = np.array(all_embeddings, dtype=np.float32)
print(f"all_embeddings shape: {all_embeddings.shape}")
dim = all_embeddings.shape[1]
index = faiss.IndexFlatIP(dim)
faiss.normalize_L2(all_embeddings)
index.add(all_embeddings)
for count, data in enumerate(tqdm(all_dataset)):
#get top 10 chunks
question_embeddings = all_dataset_embeddings[count]['question_embedding']
question_embeddings_normalized = np.array([question_embeddings], dtype=np.float32)
faiss.normalize_L2(question_embeddings_normalized)
scores_embed, ids_embed = index.search(question_embeddings_normalized, 10)
chunks = [all_texts[id] for id in ids_embed[0]]
scores_llm = []
for chunk in chunks:
if chunk in data["passage_positive"]:
scores_llm.append(1)
else:
scores_llm.append(0)
# print(f"question {count}: {question}")
# for i in range(len(scores_embed)):
# print(f"chunk {i}: scores_embed {scores_embed[i]}, scores_llm {scores_llm[i]}")
# print("--------------------------------\n")
sorted_pairs = sorted(zip(scores_embed, scores_llm), reverse=True)
scores = [rel for _, rel in sorted_pairs]
#calculate ndcg
ndcg = calculate_ndcg(scores, len(scores))
ndcg_scores.append(ndcg)
#calculate recall
recall_7, recall_12, recall_20, recall_variant = calculate_recall(scores)
recall_7_scores.append(recall_7)
recall_12_scores.append(recall_12)
recall_20_scores.append(recall_20)
recall_variant_scores.append(recall_variant)
#calculate precision
precision_7, precision_12, precision_20 = calculate_precision(scores)
precision_7_scores.append(precision_7)
precision_12_scores.append(precision_12)
precision_20_scores.append(precision_20)
print(f"NDCG: {sum(ndcg_scores)/len(ndcg_scores)}")
print(f"Recall 7: {sum(recall_7_scores)/len(recall_7_scores)}")
print(f"Recall 12: {sum(recall_12_scores)/len(recall_12_scores)}")
print(f"Recall 20: {sum(recall_20_scores)/len(recall_20_scores)}")
print(f"Recall Variant: {sum(recall_variant_scores)/len(recall_variant_scores)}")
print(f"Precision 7: {sum(precision_7_scores)/len(precision_7_scores)}")
print(f"Precision 12: {sum(precision_12_scores)/len(precision_12_scores)}")
print(f"Precision 20: {sum(precision_20_scores)/len(precision_20_scores)}")
def main():
"""
-First give your questions to generate_dataset.py and generate a json file and give the path as input_file.
-Second create your model class in ./models folder similar to sample_model.py
-Third run the script with the following command:
python evaluate.py --input_file <path_to_your_json_file> --model <path_to_your_model_class>
"""
parser = argparse.ArgumentParser()
parser.add_argument('--input_file', help='json input file path')
parser.add_argument('--model', help='the path of model class')
args = parser.parse_args()
print(f"Start to evaluate the model {args.model} with normalizer and extra words input file {args.input_file}")
run(args.input_file, args.model)
if __name__ == "__main__":
exit(main())

View File

@ -1,148 +0,0 @@
import argparse
import json
import math
import importlib
import tqdm
from hazm import Normalizer
normalizer = Normalizer()
def load_dataset(input_file):
with open(input_file, "r", encoding="utf-8") as f:
dataset = json.load(f)
return dataset
def calculate_ndcg(scores, n):
def calculate_dcg(scores, n):
idcg = 0
for i in range(n):
a = (2 ** scores[i]) - 1
b = math.log2(i + 2)
idcg += (a/b)
return idcg
def calculate_idcg(scores, n):
new_scores = scores.copy()
new_scores.sort(reverse=True)
idcg = calculate_dcg(new_scores, n)
return idcg
dcg = calculate_dcg(scores, n)
idcg = calculate_idcg(scores, n)
ndcg = dcg/idcg
return ndcg
def calculate_recall(scores):
try:
num_ground_truth = scores.count(4)
if num_ground_truth == 0:
num_ground_truth = scores.count(3)
recall_7 = scores[:7].count(4) / num_ground_truth
recall_12 = scores[:12].count(4) / num_ground_truth
recall_20 = scores[:20].count(4) / num_ground_truth
recall_variant = scores[:scores.count(4)].count(4) / scores.count(4)
return recall_7, recall_12, recall_20, recall_variant
except:
return 0, 0, 0, 0
def calculate_precision(scores):
precision_7 = scores[:7].count(4) / 7
precision_12 = scores[:12].count(4) / 12
precision_20 = scores[:20].count(4) / 20
return precision_7, precision_12, precision_20
def preprocess_reranker(text:str, preprocess:bool=True, add_extra_word:bool=False):
if preprocess:
text = text.replace("\n", ".")
text = normalizer.normalize(text)
if add_extra_word:
text += " رهبر انقلاب اسلامی حضرت امام خامنه ای "
return text
def run(input_file, model):
module = importlib.import_module("evaluation.models." + model)
model = module.model()
ndcg_scores = []
recall_7_scores = []
recall_12_scores = []
recall_20_scores = []
recall_variant_scores = []
precision_7_scores = []
precision_12_scores = []
precision_20_scores = []
dataset = load_dataset(input_file)
for count, data in enumerate(tqdm.tqdm(dataset)):
question = data["question"]
chunks = [data["chunks"][str(id)] for id in range(len(data["chunks"].keys()))]
scores_llm = [data["scores"][str(id)] for id in range(len(data["chunks"].keys()))]
scores_embed = []
for chunk in chunks:
scores_embed.append(model.run(preprocess_reranker(question, preprocess=True), preprocess_reranker(chunk, preprocess=True, add_extra_word=False)))
# print(f"question {count}: {question}")
# for i in range(len(scores_embed)):
# print(f"chunk {i}: scores_embed {scores_embed[i]}, scores_llm {scores_llm[i]}")
# print("--------------------------------\n")
sorted_pairs = sorted(zip(scores_embed, scores_llm), reverse=True)
scores = [rel for _, rel in sorted_pairs]
#calculate ndcg
ndcg = calculate_ndcg(scores, len(scores))
ndcg_scores.append(ndcg)
#calculate recall
recall_7, recall_12, recall_20, recall_variant = calculate_recall(scores)
recall_7_scores.append(recall_7)
recall_12_scores.append(recall_12)
recall_20_scores.append(recall_20)
recall_variant_scores.append(recall_variant)
#calculate precision
precision_7, precision_12, precision_20 = calculate_precision(scores)
precision_7_scores.append(precision_7)
precision_12_scores.append(precision_12)
precision_20_scores.append(precision_20)
print(f"NDCG: {sum(ndcg_scores)/len(ndcg_scores)}")
print(f"Recall 7: {sum(recall_7_scores)/len(recall_7_scores)}")
print(f"Recall 12: {sum(recall_12_scores)/len(recall_12_scores)}")
print(f"Recall 20: {sum(recall_20_scores)/len(recall_20_scores)}")
print(f"Recall Variant: {sum(recall_variant_scores)/len(recall_variant_scores)}")
print(f"Precision 7: {sum(precision_7_scores)/len(precision_7_scores)}")
print(f"Precision 12: {sum(precision_12_scores)/len(precision_12_scores)}")
print(f"Precision 20: {sum(precision_20_scores)/len(precision_20_scores)}")
def main():
"""
-First give your questions to generate_dataset.py and generate a json file and give the path as input_file.
-Second create your model class in ./models folder similar to sample_model.py
-Third run the script with the following command:
python evaluate.py --input_file <path_to_your_json_file> --model <path_to_your_model_class>
"""
parser = argparse.ArgumentParser()
parser.add_argument('--input_file', help='json input file path')
parser.add_argument('--model', help='the path of model class')
args = parser.parse_args()
print(f"Start to evaluate the model {args.model} with normalizer and extra words input file {args.input_file}")
run(args.input_file, args.model)
if __name__ == "__main__":
exit(main())

View File

@ -1,146 +0,0 @@
import mteb
import numpy as np
import requests
import tqdm
from torch.utils.data import DataLoader
from mteb.encoder_interface import PromptType
from typing import Any
# from mteb.abstasks.task_metadata import TaskMetadata
# from mteb.models.models_protocols import EncoderProtocol
import json
import os
from sentence_transformers import SentenceTransformer
from datasets import load_dataset
from datasets.config import HF_DATASETS_CACHE
from huggingface_hub.utils import get_session
import numpy
class CustomModel:
def __init__(self, model):
self.session = requests.Session()
self.model = model
def get_simplexity_query2vec_results(self, sentences, embedding_url, model, template):
params = {}
params["model"] = model
params["template"] = template
headers = {"accept": "application/json"}
data = {}
if len(sentences) < 2000:
my_range = range
else:
my_range = tqdm.trange
batch_size = 1024
vec = []
for i in my_range(0, len(sentences), batch_size):
start_idx = i
stop_idx = min(i+batch_size, len(sentences))
data["queries"] = sentences[start_idx:stop_idx]
response = self.session.post(embedding_url, headers=headers, params=params, data=json.dumps(data), timeout=600)
new_vec = response.json()
vec += new_vec
return vec
def encode(
self,
sentences: list[str],
task_name: str,
prompt_type: PromptType | None = None,
**kwargs,
) -> np.ndarray:
embedding_url = "http://127.0.0.1:5000/embedding"
if prompt_type == None:
template = "document"
elif prompt_type == PromptType.query:
template = "query"
elif prompt_type == PromptType.document:
template = "document"
else:
raise Exception("Error: prompt_type")
all_embeddings = []
# all_texts = []
# for batch in inputs:
# all_texts += batch["text"]
# embeddings = self.get_simplexity_query2vec_results(batch["text"], embedding_url, model, template)
# all_embeddings += embeddings
all_embeddings = self.get_simplexity_query2vec_results(sentences, embedding_url, self.model, template)
return numpy.array(all_embeddings)
def is_dataset_cached(dataset_name):
dataset_dir_prefix = dataset_name.replace("/", "__")
return any(dataset_dir_prefix in folder for folder in os.listdir(HF_DATASETS_CACHE))
def evaluate():
model_name = "Qwen3-Embedding-0.6B"
# model_name = "llama-embed-nemotron-8b"
# model_name = "embeddinggemma-300m"
model = CustomModel(model_name)
file_path = os.path.dirname(__file__)
# model = mteb.get_model(model_name)
# model = SentenceTransformer(model_name)
# model.model_card_data.model_name = model_name
# model.mteb_model_meta.name = model_name
# tasks = mteb.get_tasks(tasks=["Banking77Classification"])
fas_benchmark = mteb.get_benchmark("MTEB(fas, v2)")
# benchmark = mteb.get_benchmark("MTEB(eng, v2)")
# benchmark[0].metadata.task_list
# tasks = mteb.get_tasks(tasks=["Banking77Classification"])
# tasks[0].metadata.task_list
# cache = mteb.cache.ResultCache(cache_path=file_path + "/.cache")
# for i in range(len(benchmark)):
# dataset_conf = benchmark[i].metadata_dict["dataset"]
# # if is_dataset_cached(dataset_conf["path"]) == True:
# # continue
# dataset = load_dataset(
# dataset_conf["path"],
# revision=dataset_conf["revision"]
# )
# benchmarks = [fas_benchmark[i] for i in range(len(fas_benchmark)) if fas_benchmark[i].metadata_dict["name"] not in ["DigikalamagClassification", "DigikalamagClustering",
# "MIRACLReranking", "PersianWebDocumentRetrieval"]]
# benchmarks = [fas_benchmark[i] for i in range(len(fas_benchmark)) if fas_benchmark[i].metadata_dict["name"] in ["ArguAna-Fa.v2"]]
benchmarks = [fas_benchmark[i] for i in range(len(fas_benchmark)) if fas_benchmark[i].metadata_dict["name"] in ["ArguAna-Fa.v2", "SCIDOCS-Fa.v2"]]
evaluation = mteb.MTEB(tasks=benchmarks)
results = evaluation.run(model, output_folder=file_path + "/results/" + model_name)
# for benchmark in benchmarks:
# try:
# evaluation = mteb.MTEB(tasks=[benchmark])
# # results = evaluation.run(model, output_folder=file_path + "/results/Qwen3-Embedding-4B", proxies=proxies)
# results = evaluation.run(model, output_folder=file_path + "/results/Qwen3-Embedding-0.6B")
# except:
# print("________________________")
# print("Error : " + str(benchmark.metadata_dict["name"]))
# results = mteb.evaluate(model, tasks=benchmark, cache=cache)
print("results = " + str(results))
def main():
# get_results()
evaluate()
if __name__ == "__main__":
main()

View File

@ -1,15 +0,0 @@
from sentence_transformers import SentenceTransformer
class model():
def __init__(self):
from sentence_transformers import SentenceTransformer
self.model = SentenceTransformer("google/embeddinggemma-300m")
def run(self, question:str, chunk:str)->int:
query_embeddings = self.model.encode_query(question)
document_embeddings = self.model.encode_document(chunk)
similarities = self.model.similarity(query_embeddings, document_embeddings)
return similarities

View File

@ -1,17 +0,0 @@
from sentence_transformers import SentenceTransformer
class model():
def __init__(self):
from sentence_transformers import SentenceTransformer
# self.model = SentenceTransformer("./models/gemma/checkpoint-33246")
self.model = SentenceTransformer("google/embeddinggemma-300m")
self.model.load_adapter("./models/gemma/checkpoint-33246")
def run(self, question:str, chunk:str)->int:
query_embeddings = self.model.encode_query(question)
document_embeddings = self.model.encode_document(chunk)
similarities = self.model.similarity(query_embeddings, document_embeddings)
return similarities

View File

@ -18,44 +18,4 @@
9-longragfa dataset: it is long doc and query and for evaluation : question = 250, passage = 1500 : not using
10-Synthetic-persian-qa-retrieval dataset : question = 223423, passage = 250000 : negetaive passage are not exactly different : needs preprocessing
evaluation : 50 question of rahbar
no train
NDCG: 0.8452119768348717
Recall 7: 0.3373666606161222
Recall 12: 0.48390155482482855
Recall 20: 0.6340810809380268
Recall Variant: 0.44313617731261423
Precision 7: 0.4714285714285715
Precision 12: 0.41999999999999993
Precision 20: 0.358
train with 100 with lora
NDCG: 0.8432282495018343
Recall 7: 0.33695911259587386
Recall 12: 0.4729916144600827
Recall 20: 0.6212526155736547
Recall Variant: 0.43208929205133273
Precision 7: 0.4685714285714285
Precision 12: 0.4099999999999999
Precision 20: 0.35200000000000004
train with 33000 steps on all dataset
NDCG: 0.8414338101165514
Recall 7: 0.3118752420460591
Recall 12: 0.4692991653842038
Recall 20: 0.6261433602218365
Recall Variant: 0.43146001721540145
Precision 7: 0.4514285714285714
Precision 12: 0.4049999999999999
Precision 20: 0.348
evaluation dataset_test : 1000 sample
no train :
NDCG: 0.991
train with 33000 steps on all dataset :
NDCG: 0.9975
10-Synthetic-persian-qa-retrieval dataset : question = 223423, passage = 250000 : negetaive passage are not exactly different : needs preprocessing

View File

@ -1,39 +0,0 @@
import json
from datasets import load_dataset
from tqdm import tqdm
names = ["MCINext/FEVER_FA_test_top_250_only_w_correct-v2", "MCINext/fiqa-fa-v2", "MCINext/HotpotQA_FA_test_top_250_only_w_correct-v2",
"MCINext/MSMARCO_FA_test_top_250_only_w_correct-v2", "MCINext/NQ_FA_test_top_250_only_w_correct-v2", "MCINext/quora-fa-v2", "MCINext/scifact-fa-v2",
"MCINext/synthetic-persian-chatbot-rag-faq-retrieval", "MCINext/synthetic-persian-qa-retrieval", "MCINext/trec-covid-fa-v2"]
names = names[3:4]
for name in tqdm(names):
print(f"loading {name}")
dataset_qrel = load_dataset(name)["test"]
dataset_corpus_list = load_dataset(name,data_files="corpus/corpus.jsonl")["train"]
dataset_corpus = {}
for data in dataset_corpus_list:
dataset_corpus[data["_id"]] = data["text"]
dataset_queries_list = load_dataset(name,data_files="queries/queries.jsonl")["train"]
dataset_queries = {}
for data in dataset_queries_list:
dataset_queries[data["_id"]] = data["text"]
dataset = []
print("start creating dataset")
for data in dataset_qrel:
if data["query-id"] in dataset_queries and data["corpus-id"] in dataset_corpus:
dataset.append({
"question": dataset_queries[data["query-id"]],
"passage_positive": [dataset_corpus[data["corpus-id"]]],
"passage_negative": [],
"passage_negative_random": [],
})
print(f"length of dataset: {len(dataset)}")
with open(f"./research_notebook/data/mci/{name.split('/')[-1]}_v2.json", "w") as f:
json.dump(dataset, f, indent=4, ensure_ascii=False)

View File

@ -11,7 +11,10 @@
"output_type": "stream",
"text": [
"/home/firouzi/embedding_model/.venv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
" from .autonotebook import tqdm as notebook_tqdm\n",
"Downloading readme: 100%|██████████| 419/419 [00:00<00:00, 1.18MB/s]\n",
"Downloading data: 100%|██████████| 1.59M/1.59M [00:01<00:00, 1.03MB/s]\n",
"Generating train split: 100%|██████████| 7000/7000 [00:00<00:00, 175360.77 examples/s]\n"
]
}
],
@ -53,7 +56,15 @@
"execution_count": 3,
"id": "5ba361dd",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Map: 100%|██████████| 7000/7000 [00:00<00:00, 19176.72 examples/s]\n"
]
}
],
"source": [
"import numpy as np\n",
"\n",
@ -91,78 +102,48 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 5,
"id": "a35c1466",
"metadata": {},
"outputs": [],
"source": [
"split = ds.train_test_split(test_size=0.02, shuffle=True, seed=520)\n",
"split = ds.train_test_split(test_size=0.1, shuffle=True, seed=520)\n",
"train = split[\"train\"]\n",
"test = split[\"test\"]"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "aec6787d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"140"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(test)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "c5cc42ed",
"execution_count": 6,
"id": "24f3f7fb",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Creating json from Arrow format: 0%| | 0/7 [00:00<?, ?ba/s]"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Creating json from Arrow format: 100%|██████████| 7/7 [00:00<00:00, 21.58ba/s]\n",
"Creating json from Arrow format: 100%|██████████| 1/1 [00:00<00:00, 148.87ba/s]\n"
"Creating json from Arrow format: 100%|██████████| 7/7 [00:00<00:00, 26.22ba/s]\n"
]
},
{
"data": {
"text/plain": [
"364936"
"16583481"
]
},
"execution_count": 14,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train.to_json(\"training.json\")\n",
"test.to_json(\"test.json\")"
"train.to_json(\"training.json\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "536227f7",
"id": "c5cc42ed",
"metadata": {},
"outputs": [],
"source": []

View File

@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"id": "a78759c8",
"metadata": {},
"outputs": [
@ -11,7 +11,11 @@
"output_type": "stream",
"text": [
"/home/firouzi/embedding_model/.venv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
" from .autonotebook import tqdm as notebook_tqdm\n",
"/home/firouzi/embedding_model/.venv/lib/python3.10/site-packages/datasets/load.py:1461: FutureWarning: The repository for Gholamreza/pquad contains custom code which must be executed to correctly load the dataset. You can inspect the repository content at https://hf.co/datasets/Gholamreza/pquad\n",
"You can avoid this message in future by passing the argument `trust_remote_code=True`.\n",
"Passing `trust_remote_code=True` will be mandatory to load this dataset from the next major release of `datasets`.\n",
" warnings.warn(\n"
]
}
],
@ -23,7 +27,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"id": "c91f659a",
"metadata": {},
"outputs": [
@ -50,7 +54,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 6,
"id": "d66809ce",
"metadata": {},
"outputs": [
@ -58,12 +62,12 @@
"name": "stdout",
"output_type": "stream",
"text": [
"{'question': 'سام میرزا در چه تاریخی توسط نادرشاه دستگیر شد؟', 'passage_positive': ['این ضربت سخت خیال نادر را پریشان کرد و رضا قلی میرزا را که در رکاب بود در طهران گذاشت و خود به داغستان رفت در این سفر اگرچه بعضی از رؤسای طوایف لزکی از در اطاعت درآمدند لیکن غالب سکنه داغستان به قلل جبال پرارتفاع پناه گرفتند و از هر طرف به تعرّض اردوی نادر دست زدند و لطمات بسیار به ایشان وارد آوردند حتّی موقعی به خیمه خود نادر نیز تعرّض رساندند. در رمضان ۱۱۵۴ موقعیکه نادر هنوز در داغستان بود غلامی را که مرتکب انداختن تیر در جنگل سوادکوه شده بود بخدمت او آوردند. نادر او را کور کرد. شخصی بنام سام میرزا که به ادّعای فرزندی شاه سلطان حسین در آذربایجان به سلطنت طلبی برخاسته و محمّد خان پسر سرخای خان لزگی و خوانین دربند و داغستان را با خود همدست نموده بود. نادر توسّط نصر اللّه میرزا و چند تن از سرداران خود انقلاب این حدود را بالاخره خواباند و سام میرزا در ذی\\u200cالقعده ۱۱۵۶ دستگیر گردید.'], 'passage_negative': [], 'passage_negative_random': []}\n"
"{'question': 'جنگ جهانی اول در چه تاریخی پایان یافت؟', 'passgae_positive': [], 'passgae_negative': ['در سال ۱۸۷۱ امپراتوری آلمان با اتحاد پروس و کنفدراسیون جرمن شمالی توسط اتو ون بیسمارک به وجود آمد. این کشور قدرتمند تا سال ۱۹۱۸ ادامه یافت و با عنوان رایش دوم مشهور شد. بیسمارک توانست استان\\u200cهای جدید زیادی را طی جنگ\\u200cهای مبتکرانهٔ کوتاه و دیپلماتیک به دست آورد. او با اتریش هم پیمان شد تا دانمارک را شکست دهد و ناحیهٔ شلزویگ-هولشتاین را تصرف کند. او جنگ اتریش و پروس (آسترو-پروسیان) را آغاز کرد و پیروز شد اما اینکار فقط برای این بود که ایتالیا طرف آلمان را بگیرد. سپس پروس وارد جنگ فرانسه و پروس (فرانکو-پروسین) (۷۱-۱۸۷۰) شد و توانست شکست کاملی به فرانسه وارد سازد. ویلهلم اول به عنوان آخرین توهین به فرانسوی\\u200cها در کاخ ورسای در قلب فرانسه به عنوان امپراتور آلمان سوگند خورد. امپراتوری آلمان تا پایان جنگ جهانی اول یعنی زمانی که فرانسه توانست در پیمان ورسای تلافی بکند در اوج خود بود.']}\n"
]
}
],
"source": [
"print(all_dataset[1241])"
"print(all_dataset[1240])"
]
},
{

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
import json
from datasets import load_dataset
dataset_qrel = load_dataset("MCINext/scidocs-fa-v2")["test"]
dataset_corpus_list = load_dataset("MCINext/scidocs-fa-v2",data_files="corpus.jsonl")["train"]
dataset_corpus = {}
for data in dataset_corpus_list:
dataset_corpus[data["_id"]] = data["text"]
dataset_queries_list = load_dataset("MCINext/scidocs-fa-v2",data_files="queries.jsonl")["train"]
dataset_queries = {}
for data in dataset_queries_list:
dataset_queries[data["_id"]] = data["text"]
dataset = []
print("start creating dataset")
for data in dataset_qrel:
if data["query-id"] in dataset_queries and data["corpus-id"] in dataset_corpus:
dataset.append({
"question": dataset_queries[data["query-id"]],
"passage_positive": [dataset_corpus[data["corpus-id"]]],
"passage_negative": [],
"passage_negative_random": [],
})
print(f"length of dataset: {len(dataset)}")
with open("./research_notebook/data/scidocs/scidocs_v2.json", "w") as f:
json.dump(dataset, f, indent=4, ensure_ascii=False)

View File

@ -149,441 +149,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "1fabd9d8",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"import random\n",
"\n",
"with open(\"/home/firouzi/embedding_model/data/v2/msmarco.json\", \"r\", encoding=\"utf-8\") as f:\n",
" all_dataset = json.load(f)\n",
"\n",
"test_ratio = 0.001 # 10% test data\n",
"\n",
"# Shuffle and split\n",
"random.shuffle(all_dataset)\n",
"split_index = int(len(all_dataset) * (1 - test_ratio))\n",
"train_data = all_dataset[:split_index]\n",
"test_data = all_dataset[split_index:]\n",
"\n",
"\n",
"with open(\"/home/firouzi/embedding_model/data/v2/msmarco_test.json\", \"w\", encoding=\"utf-8\") as f:\n",
" json.dump(test_data, f, ensure_ascii=False, indent=4)\n",
"\n",
"# with open(\"/home/firouzi/embedding_model/data/v2/train_train.json\", \"w\", encoding=\"utf-8\") as f:\n",
"# json.dump(train_data, f, ensure_ascii=False, indent=4)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "c4b498cc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"loading MCINext/FEVER_FA_test_top_250_only_w_correct-v2\n",
"start creating dataset\n",
"length of dataset: 1171\n"
]
}
],
"source": [
"import json\n",
"from datasets import load_dataset\n",
"from tqdm import tqdm\n",
"\n",
"\n",
"name = \"MCINext/FEVER_FA_test_top_250_only_w_correct-v2\"\n",
"print(f\"loading {name}\")\n",
"dataset_qrel = load_dataset(name)[\"test\"]\n",
"dataset_corpus_list = load_dataset(name,data_files=\"corpus/corpus.jsonl\")[\"train\"]\n",
"dataset_corpus = {}\n",
"for data in dataset_corpus_list:\n",
" dataset_corpus[data[\"_id\"]] = data[\"text\"]\n",
"\n",
"dataset_queries_list = load_dataset(name,data_files=\"queries/queries.jsonl\")[\"train\"]\n",
"dataset_queries = {}\n",
"for data in dataset_queries_list:\n",
" dataset_queries[data[\"_id\"]] = data[\"text\"]\n",
"\n",
"\n",
"dataset = []\n",
"print(\"start creating dataset\")\n",
"for data in dataset_qrel:\n",
"\n",
" if int(data[\"query-id\"]) in dataset_queries and data[\"corpus-id\"] in dataset_corpus:\n",
" dataset.append({\n",
" \"question\": dataset_queries[int(data[\"query-id\"])],\n",
" \"passage_positive\": [dataset_corpus[data[\"corpus-id\"]]],\n",
" \"passage_negative\": [],\n",
" \"passage_negative_random\": [],\n",
" })\n",
"\n",
"print(f\"length of dataset: {len(dataset)}\")\n",
"with open(f\"../data/mci/{name.split('/')[-1]}_v2.json\", \"w\") as f:\n",
" json.dump(dataset, f, indent=4, ensure_ascii=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fadf910d",
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"from dotenv import load_dotenv\n",
"import os\n",
"import re\n",
"\n",
"load_dotenv()\n",
"\n",
"qwen = False\n",
"if qwen:\n",
" url = \"https://qwen3.chatllm.aiengines.ir/v1/chat/completions\"\n",
" model = \"Qwen/Qwen3-4B-Instruct-2507\"\n",
" headers = {\"Content-Type\": \"application/json\", \"Authorization\": f\"Bearer {os.getenv('LLM_AS_RERANKER_PASS')}\"}\n",
"else:\n",
" url = \"http://192.168.130.206:4001/v1/chat/completions\"\n",
" model = \"google/gemma-3-27b-it\"\n",
" headers = {\"Content-Type\": \"application/json\"}\n",
"\n",
"instruction = \"\"\"\n",
"You are a helpful assistant that help me to find that the text is relevant to the question or not.\n",
"You are given a question and a text.\n",
"You must evaluate the text based on the question and return \"1\" if the text is relevant to the question and \"0\" if the text is not relevant to the question.\n",
" \n",
"be carefull, I have chosen the text randomly from my dataset so the text must answer the question independently.\n",
"You must return the result in the following format:\n",
"{{\"result\": \"1\" or \"0\"}}\n",
"\"\"\"\n",
"\n",
"question = \"چرا خورشید اینقدر داغ است؟\"\n",
"texts = [\n",
" \"سیاراتی که در مدار نزدیک به ستاره های کوتوله سرخ قرار دارند، به دلیل نزدیکی به ستاره مادر، تحت تابش تشعشعات شدید قرار می گیرند. این تابش می تواند شرایط زیست محیطی را به شدت تحت تأثیر قرار دهد و ممکن است تنها مکان هایی که حیات در آنجا امکان وجود دارد، زیر لایه های ضخیم یخ باشد. این شرایط می تواند مانع از شکل گیری و تکامل حیات در سطح سیاره شود.\",\n",
" \"کتاب تابستان به دلیل داستان جذاب و توصیف‌های زیبا از طبیعت و زندگی، توجه تمام علاقه‌مندان به ادبیات داستانی را به خود جلب می‌کند. این کتاب نه تنها احساسات عمیق انسانی را به تصویر می‌کشد بلکه خواننده را به دنیای خاطرات شیرین تابستان‌های کودکی می‌برد.\",\n",
" \"حضرت ادریس علیه السلام به عنوان منشاء و معلم بسیاری از علوم شناخته می‌شود. او به عنوان یکی از قدیمی‌ترین پیشوایان علم، افکار بشر را به سمت استدلال و دقت در بحث سوق داده است. همچنین، او اولین کسی است که علم نجوم را به الهام الهی استخراج کرده و زمین را به چهار ربع تقسیم نموده است. در دعای روز اول ماه رجب نیز به علم ادریس اشاره شده است.\",\n",
" \"ستاره‌های کوتوله سرخ معمولاً به صورت دوره‌ای شعله‌هایی از پرتوهای ماورا بنفش و اشعه X ساطع می‌کنند که می‌تواند اتمسفر سیارات اطراف را از بین ببرد و احتمال تشکیل حیات را کاهش دهد. اما ستاره Ross 128 به عنوان یک ستاره آرام شناخته می‌شود و این ویژگی باعث می‌شود که شرایط بهتری برای تشکیل حیات در سیاره Ross 128b فراهم شود.\",\n",
" \"پوست کودکان به دلیل عدم تکامل کامل ملانوسیت‌ها، سیستم عروقی پوستی و توانایی تولید عرق حساس است. ملانوسیت‌ها که مسئول تولید ملانین هستند، از پوست در برابر اشعه ماورای بنفش محافظت می‌کنند. همچنین، سیستم عروقی پوستی در کودکان هنوز به طور کامل توسعه نیافته و این امر می‌تواند منجر به آسیب‌پذیری بیشتر پوست شود. علاوه بر این، پوست کودکان نمی‌تواند به اندازه کافی عرق تولید کند، که باعث می‌شود رطوبت پوست کاهش یابد. به همین دلیل، پوست کودکان به ویژه نوزادان به نسبت وزن بدن در معرض آسیب بیشتری قرار دارد.\",\n",
" \"نزول این آیه به واکنش‌های معاصرین پیامبر اسلام، به ویژه ابوجهل و دیگر مخالفان، اشاره دارد که به دلیل عدم درک صحیح از معانی عمیق آیات و قدرت خداوند، به تمسخر و انکار آن می‌پرداختند. این آیه به نوعی به آنها پاسخ می‌دهد و نشان می‌دهد که حتی در آتش، درختی وجود دارد که فراتر از تصور آنهاست. این واکنش‌ها نشان‌دهنده چالش‌های اعتقادی و فکری در آن زمان بود.\",\n",
" \"شرایط محیطی می‌تواند به عنوان فیلتر عمل کند زیرا عوامل متعددی مانند مقدار و نوع کالری، دما، اتمسفر، آب، خشکی، آفتاب، و بلایای طبیعی مانند آتشفشان و زلزله بر روی موجودات زنده تأثیر می‌گذارد. این عوامل می‌توانند موجودات را به چالش بکشند و تنها آن‌هایی که توانایی سازگاری با این شرایط را دارند، زنده می‌مانند و به نسل‌های بعدی منتقل می‌شوند.\",\n",
" \"خشکی پوست ممکن است موقتی یا فصلی باشد. به عنوان مثال، در فصل زمستان به دلیل هوای سرد و خشک، بسیاری از افراد دچار خشکی پوست می‌شوند. در مقابل، در فصل‌های گرم و مرطوب، ممکن است این مشکل کاهش یابد. با این حال، برخی افراد ممکن است به طور دائمی با خشکی پوست مواجه باشند که نیاز به مراقبت و درمان مداوم دارد.\",\n",
" \"سحابی خرچنگ دریایی، که با نام NGC 6357 شناخته می‌شود، به دلیل وجود ستاره‌های غیرمعمول روشن و عظیم در آن، به عنوان یک مکان مهم در کهکشان معرفی می‌شود. این سحابی همچنین خوشۀ ستاره‌ای باز Pismis 24 را در نزدیکی مرکز خود دارد که نشان‌دهنده فعالیت‌های شدید ستاره‌زایی در این ناحیه است. درخشش آبی در نزدیکی منطقه تشکیل ستاره داخلی ناشی از انتشار گاز هیدروژن یونیزه است که به جذابیت این سحابی افزوده است.\",\n",
" \"سحابی خرچنگ ده سال نوری وسعت دارد و در مرکز آن یک تپ اختر قرار دارد. این تپ اختر به سنگینی خورشید اما به اندازه یک شهر کوچک است و ثانیه‌ای سی بار به دور خود می‌چرخد. همچنین، سحابی خرچنگ با سرعتی حدود 10000 کیلومتر بر ثانیه منبسط می‌شود و انتظار می‌رود که در چند هزار سال آینده به تدریج کم‌فروغ‌تر شده و سرانجام ناپدید گردد.\",\n",
" \"وجود یک اقیانوس زیر سطحی در قمر اروپا می تواند شرایط لازم برای زندگی موجودات میکروسکوپی را فراهم کند. این اقیانوس می تواند شامل مواد مغذی و حرارت لازم برای حیات باشد. با توجه به اینکه بخار آب فوران شده از سطح اروپا ممکن است به این اقیانوس مرتبط باشد، بررسی های آینده می تواند به دانشمندان کمک کند تا پتانسیل اروپا برای تبدیل شدن به یک محل قابل سکونت را بدون نیاز به حفاری لایه یخ بررسی کنند.\",\n",
" \"چگالش بوز-اینشتین حالتی از ماده است که در دماهای بسیار پایین ایجاد می‌شود و در آن اتم‌ها رفتار موجی خود را نشان می‌دهند. در این حالت، قوانین فیزیک کلاسیک به کنار می‌روند و فیزیک کوانتومی حاکم می‌شود. اتم‌ها در این حالت به گونه‌ای حرکت می‌کنند که انگار بر روی یکدیگر سوار هستند و این رفتار تنها در دماهای بسیار پایین قابل مشاهده است.\",\n",
" \"در سال 2012، انجمن سلطنتی شیمی مسابقه‌ای را برای یافتن بهترین توضیح برای اثر امپمبا برگزار کرد. برنده این مسابقه نظریه‌ای را ارائه داد که علت این پدیده را 'ابرسرد شدن' می‌دانست. این نشان می‌دهد که این موضوع همچنان مورد توجه محققان و دانشمندان است و تلاش‌ها برای درک بهتر آن ادامه دارد.\",\n",
" \"داستان کتاب عاشق آتشفشان در قرن هجدهم و در میان آشفتگی‌های دربار ناپلی اتفاق می‌افتد. این رمان به بررسی موضوعاتی چون عشق، تاریخ و روابط اجتماعی می‌پردازد و داستان زندگی سر ویلیام همیلتون و همسرش اِما را روایت می‌کند. همچنین، این داستان به تحولات سیاسی و اجتماعی آن زمان نیز اشاره دارد.\",\n",
" \"انفجارهای هوایی به دلیل قدرت تخریب بالای خود و نادر بودن برخورد سیارک‌ها، خطرناک‌تر محسوب می‌شوند. در حالی که برخورد سیارک‌های بزرگ به ندرت اتفاق می‌افتد، انفجارهای هوایی می‌توانند به طور ناگهانی و بدون هشدار پیشین رخ دهند. به عنوان مثال، انفجار سیارکی به اندازه یک خانه در سال ۱۳۹۲ در چلیابینسک روسیه منجر به آسیب دیدن بیش از ۱۶۰۰ نفر شد. این نشان می‌دهد که انفجارهای هوایی می‌توانند تأثیرات جدی بر روی جمعیت‌های انسانی داشته باشند.\",\n",
" \"پرتقال به دلیل خواص ضدعفونی‌کننده و ضد میکروبی که دارد، می‌تواند به درمان مشکلات پوستی کمک کند. به عنوان مثال، شما می‌توانید از پوست پرتقال له شده به عنوان مرهم برای اگزما و ناراحتی‌های پوستی استفاده کنید. این خاصیت به دلیل وجود ترکیبات طبیعی در پوست پرتقال است که به تسکین و بهبود وضعیت پوست کمک می‌کند.\",\n",
" \"نوسانات درخشندگی ستاره KIC 8462852 به‌طور نامنظم و شدید اتفاق می‌افتند و این الگو با آنچه معمولاً در ستاره‌های دارای سیارات فراخورشیدی مشاهده می‌شود، متفاوت است. در حالی که وجود یک سیاره می‌تواند باعث نوسانات درخشندگی شود، در این مورد، نوسانات به‌قدری پیچیده و غیرقابل پیش‌بینی هستند که فرضیه‌های دیگر مانند وجود ابرهای میان ستاره‌ای، دنباله‌دارها یا حتی اَبَرسازه‌های بیگانگان فضایی نیز مطرح شده‌اند.\",\n",
" \"پاریس به عنوان یک مقصد گردشگری محبوب به دلیل جاذبه‌های تاریخی، فرهنگی و هنری خود شناخته می‌شود. این شهر با برج ایفل، موزه لوور، کاتدرال نوتردام و خیابان‌های زیبا و پر از زندگی، هر ساله میلیون‌ها گردشگر را به خود جذب می‌کند. همچنین، فرهنگ غنی، غذاهای لذیذ و فضاهای رمانتیک پاریس، تجربه‌ای منحصر به فرد را برای بازدیدکنندگان فراهم می‌آورد. به همین دلیل، پاریس به عنوان 'شهر نور' و 'شهر عشق' در قلب بسیاری از مردم جهان جای دارد.\"\n",
" ]\n",
" \n",
"for text in texts:\n",
" input_message = f\"\"\"{{\"question\": \"{question}\", \"text\": \"{text}\"}}\"\"\"\n",
" messages = [{\"role\": \"system\", \"content\": instruction}, {\"role\": \"user\", \"content\": input_message}]\n",
"\n",
" payload = {\n",
" \"model\": model,\n",
" \"messages\": messages,\n",
" \"max_tokens\": 100\n",
" }\n",
"\n",
" req = requests.post(url, headers=headers, json=payload)\n",
" print(text)\n",
" print(req.json()['choices'][0]['message']['content'])\n",
" print(\"--------------------------------\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8062bb30",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Generating train split: 100%|██████████| 48729/48729 [00:05<00:00, 9203.01 examples/s] \n"
]
}
],
"source": [
"from datasets import load_dataset\n",
"\n",
"dataset = load_dataset(\"castorini/mr-tydi\", \"combined\")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "b857e2f7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"query is : দেবশ্রী রায়ের ডাক নাম কি ?\n",
"passage positive is : দেবশ্রী রায় এর জন্ম, বেড়ে ওঠা কলকাতায়। তার পিতার নাম বীরেন্দ্র কিশোর রায় এবং মায়ের নাম আরতি রায়। ১৯৬৯ সনে তিনি প্রথম \"বালক গদাধর\" নামে একটি চলচিত্রে শিশুশিল্পী হিসেবে অভিনয় করেন, যার পরিচালক ছিলেন হরিময় সেন। তিনি কলকাতার এ পি জে ,পার্ক স্ট্রীট শাখার স্কুলের ছাত্রী ছিলেন, যদিও নাচের প্রতি অত্যধিক ঝোঁক থাকার কারণে বেশিদূর এগোতে পারেন নি। তার ডাক নাম চুমকি।\n",
"passage negative is : নবী (সা.) এর মৃত্যুর পর জনগণ আবু বকর (রা.) খলিফা নির্বাচন করেন। তখন আয়িশা(রা.) নবী (সা.)এর স্ত্রী এবং খলিফার কন্যা হিসেবে সমধিক সম্মান পেতেন। এমনকি জনগণ তাকে নবীজি (সা.) এর কাছ থেকে \"সিদ্দিক\"(সত্যবাদী) উপাধি প্রাপ্ত আবু বকর (রা.) এর কন্যা হিসেবে \"সিদ্দিকা বিনতু সিদ্দিক\"(সত্যবাদীর কন্যা সত্যবাদিনী) বলে ডাকতে শুরু করে।[34] আবু বকর (রা.) তাঁর মৃত্যুর পূর্বকালে উমর (রা.) কে খলিফা নিযুক্ত করে যান।[34] খলিফা উমর (রা.)-এর শাসনামলেও তিনি দাপটের সাথে রাজনৈতিক সিদ্ধান্তসমূহে মতামত প্রদান করার স্বাধীনতা লাভ করেছিলেন।[34]\n",
"passage negative is : বঙ্গীয় কৃষক সমাজের কিংবদন্তি অনুযায়ী, দূর অতীতে বাংলায় এক বলবীর্যবান ও মন্ত্রৌষধি জ্ঞানী মহাপুরুষ জন্মেছিলেন। তিনি প্রতিবছর আশ্বিন মাসের সংক্রান্তির আগের রাতে নির্দিষ্ট শুভলগ্নে লাঠি ও জ্বলন্ত মশাল হাতে বীরত্বব্যঞ্জক ভঙ্গিতে ভয়ঙ্কর শব্দ করতে করতে বিধাতাপুরুষের কাছে যেতেন এবং তাঁর কাছে পার্থিব মানুষদের শারীরিক ও সাংসারিক দুঃখের বিষয়গুলি ব্যক্ত করে সেসব দুঃখ থেকে পরিত্রাণের উপদেশ ও আশীর্বাদ মর্ত্যধামে নিয়ে আসতেন। পৃথিবীর মানুষের সংবাদ নিয়ে স্বর্গধামে গমন করতেন বলেই তার নাম 'ডাক পুরুষ'। আবার অন্যমতে, আশ্বিন-সংক্রান্তির পূর্বরাত্রে নির্দিষ্ট সময়ে পল্লীবাসীকে ডেকে উন্মুক্ত প্রান্তরে নিয়ে গিয়ে শারীরিক কসরত বা ব্যায়াম এবং মন্ত্রৌষধির শিক্ষা দিতেন। অনেক পণ্ডিতের মতে, এই ডাকপুরুষ পূর্ববঙ্গের মানুষ ছিলেন।\n",
"passage negative is : ১৯৫২ সালে রানী দ্বিতীয় এলিজাবেথ কর্তৃক গৃহীত রাজ্যাঙ্কের সংখ্যা বেশি বিরোধের মধ্যে স্কটল্যান্ডের কিছু সংখ্যক ডাক বাক্স অভিগ্রস্ত হয়, যা EIIR গোল্লা বাক্স হিসাবে প্রদর্শিত ছিল।\n",
"passage negative is : শহরটি বিভিন্ন নামে পরিচিতি পেয়েছে। কিছু গবেষক মনে করেন, বর্তমান ওতাপুলুয়ার কাছাকাছি কাতুবুলু নুয়ারা এ শহরের প্রকৃত নাম। কিন্তু ঐতিহাসিকভাবে জনপ্রিয় নাম হচ্ছে সেনকাদাগালা বা সেনকাদাগালাপুরা যা প্রাতিষ্ঠানিকভাবে সেনকাদাগালা শ্রীবর্ধনা মহা নুয়ারা। এটি সংক্ষেপে মহা নুয়ারা নামে পরিচিত। লোকউপাখ্যানে কয়েকটি সম্ভাব্য উৎস থেকে এসেছে। গুহায় অবস্থানকারী সেনকান্দা নামীয় ব্রাহ্মণের নাম থেকে এ শহরের নাম উদ্ভূত। অন্য উৎসে জানা যায়, তৃতীয় বিক্রমাবাহু’র রাণী সেনকান্দা পাথরে রঙ করে সেনকাদাগালা রেখেছিলেন। ক্যান্ডি রাজ্যও অনেক নামে পরিচিতি পেয়েছে। ঔপনিবেশিক আমলে সিংহলীজ কান্দা উদা রাতা বা কান্দা উদা পাস রাতা থেকে ইংরেজি নাম ক্যান্ডি হয়েছে। যার অর্থ দাঁড়ায় পর্বতের উপর ভূমি। পর্তুগীজরা সংক্ষেপে ক্যান্ডিয়া রেখেছিল যা রাজ্য ও এর রাজধানী উভয় ক্ষেত্রেই ব্যবহৃত হতো। সিংহলী ভাষায় ক্যান্ডিকে মহা নুয়ারা নামে ডাকা হয় যার অর্থ মহান শহর বা রাজধানী। তা স্বত্ত্বেও প্রায়শই শহরটিকে নুয়ারা নামে ডাকা হয়।\n",
"passage negative is : উম্মে আইমান বারাকাহ (রা.) ছিলেন মুহাম্মদ (সা.) এর একজন অত্যন্ত মর্যাদামান সাহাবী। ইসলামের প্রথমদিকে , যখন মুসলমানদের প্রচণ্ড দুর্সময় চলছিল, তখন রাসুলুল্লাহকে বিপদ-আপদ থেকে আগলে রেখেছিলেন এই উম্মে আইমান বারাকাহ। তিনি মুহাম্মদ (সা.) এর পিতা আবদুল্লাহ ইবনে আবদুল মুত্তালিবের অধীন একজন ইথিওপিয়ান দাসী ছিলেন। আবদুল্লাহর মৃত্যুর পর তিনি মুহাম্মদ (সা.) এর অধীনে কাজ করতেন। পরবর্তীতে তিনি তাকে মুক্ত করে দেন। এরপর তিনি বনু খাজরাজ গোত্রের উবাইদ ইবনে জায়িদ নামক এক ব্যক্তিকে বিয়ে করেন। আইমান নামে তাদের এক সন্তান হয়। একারণে তাকে উম্মে আইমান (আইমানের মা) ডাকা হয়। স্বামীর মৃত্যুর পর তিনি জায়িদ ইবনে হারিসকে (রা.)বিয়ে করেন। একদিন মসজিদে আসরের নামাজের পর রাসুলুল্লাহ বললেন, তোমাদের মধ্যে কে একজন জান্নাতি মহিলাকে বিয়ে করতে চাও। জমায়েত নিশ্চুপ। হঠাৎ উঠে দাঁড়ালেন রাসুলের পালক পুত্র জায়িদ। বললেন, ইয়া রাসুলুল্লাহ আমি। রাসুল এই কথা বলেন তিনবার। তিনবারই কেবল জায়িদ তার সম্মতির কথা জানান। বিয়েতে এই পবিত্র দম্পতির উসামা ইবনে জায়িদ নামে এক পুত্র জন্মগ্রহণ করেন। হযরত উসামা (রা.)অনেক যুদ্ধে মুসলমানদের সেনাপতিত্ব করেন। ক্রীতদাসী মায়ের সন্তানকে এভাবে সম্মান দিয়ে রাসুল কুরাইশদের আভিজাত্যকে ধুলোয় মিশিয়ে দেন। উম্মে আইমান বারাকাহ ও বেলাল (রা.) দুইজনই দুনিয়াতে বেহেস্তের সুসংবাদপ্রাপ্ত। কিন্তু কোনো এক অজ্ঞাত কারণে এরা আশারায়ে মুবাশশিরার সদস্য নন। অথচ সহিহ হাদিস দ্বারা দুজনের জান্নাতপ্রাপ্তি প্রমাণিত। উসমান ইবনে আফফানের শাসনামলে উম্মে আইমান বারাকার (রা.) মহান সন্তান উসামা মৃত্যুবরণ করেন।\n",
"passage negative is : বাংলা গোয়েন্দাকাহিনী কেন্দ্রিক উপন্যাসের মধ্যে প্রিয়নাথ মুখোপাধ্যায়ের 'দারোগার দপ্তর', পঞ্চানন ঘোষালের পুলিশ কাহিনীর নাম উল্লেখ্য, বাংলা মৌলিক গোয়েন্দাকাহিনীর প্রণেতা ধরা হয় পাঁচকড়ি দে'কে। আধুনিক বাংলা গোয়েন্দা কাহিনীর জনক ধরা হয় শরদিন্দু বন্দ্যোপাধ্যায়কে। তার গোয়েন্দা ব্যোমকেশ ও সহকারী অজিত বাংলার পাঠকমহলে গভীর ছাপ ফেলেছিল। ব্যোমকেশ গোয়েন্দা কাহিনী চলচ্চিত্রায়িতও হয়েছে একাধিকবার। এছাড়া বাংলা সাহিত্যের প্রথিতযশা সাহিত্যিক ক্ষেত্রমোহন ঘোষ, দীনেন্দ্রকুমার রায়, নীহাররঞ্জন গুপ্ত, হেমেন্দ্রকুমার রায়, প্রেমেন্দ্র মিত্র, সৈয়দ মুস্তাফা সিরাজ, নারায়ণ সান্যাল,সত্যজিৎ রায়, কৃশানু বন্দ্যোপাধ্যায়, অর্দ্রীশ বর্ধন, তপন বন্দ্যোপাধ্যায়, প্রভাবতী দেবী সরস্বতী, লীলা মজুমদার, সমরেশ বসু, সুনীল গঙ্গোপাধ্যায়, সমরেশ মজুমদার, সুচিত্রা ভট্টাচার্য গোয়েন্দাকাহিনী লিখেছেন। এদের মধ্যে সত্যজিৎ রায়ের ফেলুদা জনপ্রিয়। যার প্রধান চরিত্র প্রদোষ চন্দ্র মিত্র। ডাক নাম ফেলু। এছাড়া আছে ফেলুদা খুড়তুতো ভাই তপেস রঞ্জন মিত্র। ডাক নাম তপসে। গল্পের অন্য চরিত্র লালমোহন গাঙ্গুলি। যিনি জটায়ু ছদ্মনামে রহস্য উপন্যাস লেখেন।\n",
"passage negative is : মেটে তিতির সাধারণত শুকনো তৃণভূমি, ক্ষেত-খামার, ক্ষুদ্র ঝোপ ও বালিয়াড়িতে বিচরণ করে। কালো তিতিরের মত আর্দ্র এলাকা এদের পছন্দ নয়। সচরাচর জোড়ায় জোড়ায় বা -৮টি পাখির পারিবারিক দলে ঘুরে বেড়ায়। এরা ঠোঁট ও পা দিয়ে মাটি আঁচড়ে খাবার খোঁজে। খাদ্যতালিকায় রয়েছে পোকামাকড়, আগাছার বীজ, শস্যদানা, ঘাসের ডগা ও রসালো ফল। ডানা দ্রুত ঝাপটে কিছুক্ষণ ওড়ার পর কিছু সময় বাতাসে ভেসে থাকে, তারপর আবার ডানা চালায়। তবে ওড়ার চেয়ে হেঁটে বেড়ানো পছন্দ করে বেশি। রাতে ছোট কাঁটাগাছ অথবা ঘন ঝোপের নিচে থাকে। এরা মাঝে মাঝে ডাকে। ডাক অনেকটা \"খাতি-তার...খাতি...তার\"। মূলত ডাক থেকেই এদের নাম হয়েছে \"তিতির\"। ভয় পেলে ঘর্ষণের মত শব্দ করে ডাকে: \"ক্ষিরর-ক্ষিরর\"।\n",
"passage negative is : কুমিরের 'রা' (ডাক শব্দ যেমন সব শিয়ালের একরা) থেকে 'কুমিরা' নামের উৎপত্তি হয় বলে অনেকের বিশ্বাস। তখনকার দিনে শিশুদের কান্না থামাতে মায়ের মুখে কুমিরের ‍রা' বা ডাকের অনুকরণ করে ওদের ভয় দেখাত।\n",
"passage negative is : ক্যাথরিন, ডাচেস অফ কেমব্রিজ (; জন্ম: ৯ জানুয়ারি, ১৯৮২) হলেন চার্লস, প্রিন্স অব ওয়েলস এবং ডায়ানা, প্রিন্সেস অব ওয়েলসের জ্যেষ্ঠ পুত্র এবং রাণী দ্বিতীয় এলিজাবেথ ও প্রিন্স ফিলিপ, ডিউক অব এডিনবরার তৃতীয় জ্যেষ্ঠ পৌত্র প্রিন্স উইলিয়ামের পত্নী। ২০১১ সালের ২৯ এপ্রিল ওয়েস্টমিনস্টার অ্যাবেতে দীর্ঘদিনের বন্ধু প্রিন্স উইলিয়ামের সাথে তাঁর বিবাহ-পর্ব সম্পন্ন হয়। তাঁর বিবাহ-পূর্ব নাম ছিল ক্যাথরিন এলিজাবেথ মিডলটন; ডাক নাম \"কেট\"।\n",
"passage negative is : ডেনমার্কের দ্বিতীয় মার্গারেথ (; জন্ম: ১৬ এপ্রিল, ১৯৪০) রাণী হিসেবে ডেনমার্কের বর্তমান রাষ্ট্রপ্রধানের দায়িত্ব পালন করছেন। তবে তাঁর নামকে খুব কমই ইংরেজি বাগধারামাফিক \"দ্বিতীয় মার্গারেট\" নামে ডাকা হয়। তিনি রাজা নবম ফ্রেদেরিক ও সুইডেনের ইনগ্রিদের জ্যেষ্ঠা কন্যা। ১৪ জানুয়ারি, ১৯৭২ খ্রিস্টাব্দে পিতার মৃত্যুর পর তিনি ক্ষমতায় আরোহণ করেন। এরফলে তিনি ১৩৭৫-১৪১২ সময়কালে স্ক্যান্ডিনেভিয়ার দেশের কালমার ইউনিয়নের দায়িত্বে নিয়োজিত প্রথম মার্গারেথের পর ডেনমার্ক সাম্রাজ্যের প্রথম নারী হিসেবে পরিচিত পান।\n",
"passage negative is : দেবশ্রী রায় একজন প্রখ্যাত ভারতীয় বাঙালি অভিনেত্রী ও নৃত্যশিল্পী।[1] তিনি নটরাজ দলের কর্ণধার। তিনি ভারতীয় লোকনৃত্যকে পাশ্চাত্য মঞ্চে উপস্থাপনা করেন এবং ধ্রুপদী ও লোকনৃত্যের সংমিশ্রণে এক অপূর্ব নৃত্যকৌশলী রচনা করেন। তিনি বাংলা ছায়াছবির অন্যতম সফল ও জনপ্রিয় একজন অভিনেত্রী। তিন দশকের বেশি সময় ধরে দেবশ্রী রায় বাংলা ছবিতে অভিনয় করেছেন এবং দুই দশকব্যাপী তার বাণিজ্যিক সাফল্য ধরে রাখেন।[2] তিনি তামিল চলচ্চিত্রে \"চিন্তামণি\" নামে অভিনয় করেছেন। তিনি একশোরও বেশি চলচ্চিত্রে অভিনয় করেছেন এবং পেয়েছেন চল্লিশের বেশি পুরস্কার। তিনি একজন পশুপ্রেমী।[3][4] তিনি ২০১১ সাল থেকে পশ্চিমবঙ্গের একজন মাননীয় বিধায়ক।\n",
"passage negative is : তিনি তাঁর পিতা রাম মুখোপাধ্যায় একজন অবসরপ্রাপ্ত পরিচালক। তাঁর মা কৃষ্ণা চলচ্চিত্রে গান গাইতেন। তাঁর ভাই রাজা মুখোপাধ্যায় একজন চিত্র প্রযোজক। তাঁর মাসি হলেন প্রখ্যাত চিত্রনায়িকা দেবশ্রী রায়। বলিউড তারকা অভিনেত্রী কাজল তাঁর সম্পর্কিত বোন।তিনি বিখ্যাত পরিচালক প্রযোজক যশ চোপড়া এর বড় ছেলে পরিচালক ও প্রযোজক আদিত্য চোপড়াকে বিয়ে করেন।\n",
"passage negative is : ১৯৮২ সালে দেবশ্রী, গৌতম মুখোপাধ্যায়ের ত্রয়ী ছবিতে অভিনয় করেন। এই ছবির বিপুল বাণিজ্যিক সাফল্যের হাত ধরে দেবশ্রী টালিগঞ্জের প্রথম সারিতে উঠে আসেন।[9] ১৯৮৫ সালে তিনি বিজয় সিং প্রযোজিত ও পরিচালিত কভী আজনবী থে ছবিতে অভিনয় করেন। এই ছবিতে দেবশ্রী অভিনীত চরিত্রটি আশা নাম্নী এক নেপথ্য গায়িকার যিনি এক ক্রিকেটারের প্রেমে পরেন। এই ছবিতে তিনি তৎকালীন ক্রিকেটার সন্দীপ পাতিলের বিপরীতে অভিনয় করেন। মুম্বায়ের মিডিয়া মহলে সেই সময় এই ছবিটিকে ঘিরে তুমুল চর্চা শুরু হয়, বিশেষত গীত মেরে হোঠোকো দে গয়া কোই গানের দৃশ্যে দেবশ্রীর অপূর্ব আবেদনশীল অভিব্যক্তি প্রচার মাধ্যমে উত্তেজনা সৃষ্টি করে। স্বাভাবিকভাবেই জনমানসেও প্রত্যাশার তুমুল পারদ চড়তে থাকে। দুর্ভাগ্যবশত মুক্তির পর ছবিটি প্রত্যাশা পূরন করতে ব্যর্থ হয়। তিনি ওম পুরীর বিপরীতে পরিচালক আকাশ জৈন পরিচালিত ছবি সিপীয়াঁ (১৯৮৬) তে অভিনয় করেন।[10][9]\n",
"passage negative is : নরকাসুর কামাখ্যা দেবীকে বিয়ে করার প্রস্তাব দেন। দেবীকে বলাতে দেবী শর্ত রাখেন যে, মোরগ বা কুক্কুট রাত পেরোনোর জানান দেওয়ার আগে যদি নরক নীলাচল পাহাড়ের তলা থেকে মন্দির পর্যন্ত এক রাতের মধ্যে সিঁড়ি নির্মাণ করতে পারেন তবে তিনি বিয়ে করতে রাজী হবেন। নরকাসুর সেইমত সিড়ি নির্মাণ করে রাত পেরোনোর আগে শেষ করার উপক্রম করলেন। কামাখ্যা দেবী তখন ভয় পেয়ে একটি মোরগকে চেপে ধরাতে সে ডাক দেয়। নরকও রাত পেরোলো বলে ভেবে কাজ অর্ধেক রাখলেন। পরে আসল কথা জানতে পেরে নরক কুক্কুটটিকে ক্রোধবশত ধাওয়া করে হত্যা করেন। এজন্য দরং জেলায় \"কুকুরাকটা\" নামে একটি স্থান আছে। অন্যদিকে, অসমাপ্ত সিঁড়িটিকে \"মেখেলা-উজোয়া পথ\" বলে অভিহিত করা হয়। এরপর বশিষ্ঠ মুনিকে কামাখ্যা মন্দিরে উপাসনা করতে অনুমতি না দেওয়ায় মুনি নরক এবং দেবীকে অভিশাপ দেন যে এই মন্দিরে পূজা করা কারো মনোবাঞ্ছা পূর্ণ হবে না। শিবের হস্তক্ষেপে এই অভিশাপ সেই বছর পর্যন্ত সীমিত হয়। এতে নরকাসুর বিষ্ণু এবং কামাখ্যার অপ্রিয়ভাজন হয়ে ওঠেন।\n",
"passage negative is : ১৯৮৬ সালে তপন সিংহর \"আতঙ্ক\" ছবিতে শতাব্দী রায়ের অভিনয় জীবনের অভিষেক হয়। এর পূর্বে তিনি দিনেন দাশগুপ্তর পরিচালানায় \"টিনা\" নামক একটি ছবিতে অভিনয় করেন কিন্তু ছবিটি মুক্তি পায় নি। এরপর তিনি তপন সাহার পরিচালনায় \"অমর বন্ধন\" ছবিতে তাপস পালের বিপরীতে অভিনয় করেন। টালিগঞ্জে সেই সময় দেবশ্রী রায়ের প্রবল প্রতাপ। তার মধ্যেও নিজের জায়গা করে নেন শতাব্দী। ১৯৮৭ সালে অঞ্জন চৌধুরীর পরিচালনায় \"গুরু দক্ষিনা\" ছবিতে তিনি তাপস পালের বিপরীতে অভিনয় করেন। এই ছবির বিপুল বাণিজ্যিক সাফল্য তার অভিনয় জীবনের ভাগ্য বদলে দেয়।\n",
"passage negative is : রাণী হামিদ (জন্ম:২৩ ফেব্রুয়ারি,১৯৪৪) একজন বাংলাদেশী দাবাড়ু। তিনি বাংলাদেশের প্রথম মহিলা আন্তর্জাতিক দাবা মাস্টার। রাণী হামিদের পুরো নাম সৈয়দা জসিমুন্নেসা খাতুন ডাক নাম রাণী। বিয়ের পর তিনি স্বামীর নাম যুক্ত করে রাণী হামিদ হন। ক্রীড়াজগতে তিনি রাণী হামিদ নামেই পরিচিত। ১৯৮৫ সালে তিনি ফিদে আন্তর্জাতিক মহিলা মাস্টার খেতাব পান। তিনি ৩ বার ব্রিটিশ মহিলা দাবা প্রতিযোগিতায় চ্যাম্পিয়ন হন। । রাণী হামিদের সন্তান কায়সার হামিদ ১৯৮০-এর দশকে বাংলাদেশের ক্রীড়া জগতের খ্যাতনামা ফুটবল খেলোয়াড় ছিলেন।\n",
"passage negative is : দেবশ্রী রায় সম্প্রতি রাজনীতিতে এসেছেন। তিনি বর্তমানের পশ্চিমবঙ্গের শাসক তৃণমূল কংগ্রেস দলের বিধায়ক (এমএলএ)। ২০১১ সালে তিনি রায়দিঘি থেকে প্রতিদ্বন্দ্বীতা করেন সিপিএম এর শক্তিশালী প্রার্থী প্রাক্তন মন্ত্রী কান্তি গাঙ্গুলির সাথে এবং জয়ী হন।\n",
"passage negative is : কভী আজনবী থে ছবিতে অভিনয়ের সূত্রে দেবশ্রী সন্দীপ পাতিলের সাথে প্রণয় বন্ধনে জড়িয়ে পরেন।[12] দীর্ঘদিন সম্পর্কের পর ১৯৯২ সালে তিনি বিয়ে করেন বাংলা ছবির খ্যাতিমান অভিনেতা প্রসেনজিৎ চট্টোপাধ্যায় কে।[13] ১৯৯৫ সালে তারা আলাদা হয়ে যান। ভারতের জনপ্রিয় অভিনেত্রী রাণী মুখার্জী সম্পর্কে তার ভাগ্নি হন।[14][15]\n",
"passage negative is : ১৯৮৩ সালে বিজয় সিং পরিচালিত \"কভী আজনবী থে\" ছবিতে অভিনয়ের সূত্রে দেবশ্রী রায়ের সাথে সন্দীপ পাতিলের আলাপ হয়। সন্দীপ তখন বিবাহিত, তা সত্ত্বেও তিনি দেবশ্রীর সাথে সম্পর্কে জড়িয়ে পরেন। আশির দশকের মধ্যভাগে সংবাদ মাধ্যমে তাদের সম্পর্ক নিয়ে তুমুল চর্চা শুরু হয় যা তার দাম্পত্য জীবনে অশান্তির সৃষ্টি করে। ১৯৮৫ সালে, \"কভী আজনবী থে\" মুক্তি পাওয়ার পরে সন্দীপ ও দেবশ্রীর সম্পর্ক ভেঙে যায়। পরবর্তীকালে দীপা নাম্নী এক রমণীকে বিয়ে করেন পাতিল। তাঁর সন্তান চিরাগ মারাঠী চলচ্চিত্র রাদা রক্সে অভিনয় করে। ‘ইকাচ শাটকর’ নামের মারাঠী ক্রীড়াবিষয়ক সাময়িকীর সম্পাদকের দায়িত্বে রয়েছেন। ১৯৮৪ সালে ‘স্যান্ডি স্টর্ম’ নামের একটি আত্মজীবনীমূলক গ্রন্থ প্রকাশ করেন।\n",
"passage negative is : ১৯৮৫ সালে মহুয়া রায়চৌধুরীর অকালত্যুত্যবাংলা ছতে এক শূণ্যতার জন্ম দেয়ছে। সেই বছরেই দেবশ্রী তরুণ মজুমদারের ভালোবাসা ভালোবাসা ছবিতে অভিনয় করেন। এই ছবির বিপুল বাণিজ্যিক সাফল্য তাকে মহুয়া রায়চৌধুরীর ছেড়ে যাওয়া সিংহাসন অধিকার করতে সাহায্য করে।[9] এই ছবি তাপস-দেবশ্রী জুটির ভিত কে শক্তিশালী করে। পরবর্তীকালে অর্পণ (১৯৮৭), শঙ্খচূড় (১৯৮৮), সুরের সাথী (১৯৮৮), সুরের আকাশে (১৯৮৮), নয়নমণি (১৯৮৯), চোখের আলোয় (১৯৮৯), শুভ কামনা (১৯৯১), মায়াবিনী (১৯৯২), ফিরে পাওয়া (১৯৯৩), তবু মনে রেখো (১৯৯৩), পুত্রবধূ (১৯৯৮)— এর মতো বাণিজ্যিক সফল ছবিতে দেবশ্রী তাপস পালের বিপরীতে অভিনয় করেন।[7][11]\n",
"passage negative is : মার্কিন যুক্তরাষ্ট্রের উচ্চ আদালতের রায়ের (রয়ান বনাম পোস্ট অফিস ডিপার্টমেন্ট, ৩৯৭ ইউ.এস ৭২৮ (১৯৭৯)) প্রতিক্রিয়া দেখিয়ে মার্কিন যুক্তরাষ্ট্র ডাক সেবা নিষেধক আদেশ লাভের আবেদন করার ক্ষমতা প্রদান করে, যেটি লোকজনদের বেসরকারী সংস্থাদের হতে পাঠানো ডাক বন্ধ করার ক্ষমতা দেয় এবং এ ধরণের প্রতিষ্ঠানের ডাক তালিকা হতে ভোক্তাদের তথ্য মুছে ফেলার জন্য দাবি করার সুযোগ দেয়া হয়।\n",
"passage negative is : মার্কিন যুক্তরাষ্ট্রের শুরুর দিকে রাষ্ট্রপতির সহধর্মনীকে বিশেস কোন নামে ডাকা হত না। তবে \"লেডি\" বা \"মিসেস প্রেসিডেন্ট\" বা \"মিসেস প্রেসিডেন্ট্রেস\" নামে ডাকা হত। প্রথম রাষ্ট্রপতি জর্জ ওয়াশিংটনের সহধর্মণীকে \"লেডি ওয়াশিংটন\" নামে ডাকা হত।\n",
"passage negative is : ‘বাঁকুড়া’ শব্দটির বুৎপত্তি নিয়ে বিশেষজ্ঞদের মধ্যে মতবিরোধ আছে। কোল-মুণ্ডাদের ভাষায় ওড়া বা ড়া শব্দের অর্থ বসতি। বাঁকু শব্দের অর্থ এবড়ো খেবড়ো।ছোটোনাগপুর মালভূমির অংশ বিশেষ। ‘বাঁকুড়া’ নামটি 'বাঁকা' শব্দ থেকেও উৎপন্ন হওয়ার সম্ভাবনা আছে। জেলার সবচেয়ে প্রভাবশালী লৌকিক দেবতাদের একজন হলেন ধর্মঠাকুর। তাঁকে স্থানীয়রা 'বাঁকুড়া রায়' নামে ডাকেন।[3] স্থানীয় কিংবদন্তি অনুসারে, জেলা সদর বাঁকুড়া শহরের নামকরণ হয়েছে এই শহরের প্রতিষ্ঠাতা তথা স্থানীয় গোষ্ঠীপতি নেতা বাঁকু রায়ের নামানুসারে। অন্য একটি কিংবদন্তি অনুসারে, বিষ্ণুপুরের রাজা বীর হাম্বিরের ২২ পুত্রের অন্যতম বীর বাঁকুড়ার নামে এই শহরের নামকরণ করা হয়েছে। বীর হাম্বির তাঁর রাজ্যকে ২২টি তরফে ভাগ করে দেন। প্রতিটি তরফ তাঁর এক এক পুত্রের অধীনে আসে। জয়বেলিয়া তরফটি বীর বাঁকুড়ার ভাগে পড়ে। তিনিই বাঁকুড়া শহরটি গড়ে তোলেন। অন্য একটি মতে, বাঁকুড়া নামটি বানকুন্ডা নামের অপভ্রংশ। 'বানকুন্ডা' শব্দের অর্থ পাঁচটি দিঘি। পুরনো সরকারি নথিপত্রে ইংরেজি Bacoonda নামটি পাওয়া যায়।[1]\n",
"passage negative is : সা (ষড়্‌জ বা খরজ) ----- ময়ূরের ধ্বনি হতেঋা (কোমল ঋষভ) রা (শুদ্ধ ঋষভ বা রেখাব)----- বৃষভের ডাক হতে জ্ঞা (কোমল গান্ধার), গা (শুদ্ধ গান্ধার) ----- ছাগলের ডাক হতেমা (শুদ্ধ মধ্যম) ----- শৃগালের ডাক হতে হ্মা (তীব্র বা কড়ি মধ্যম), পা (পঞ্চম) ----- কোকিলের ধ্বনি হতেদা (কোমল ধৈবত), ধা (শুদ্ধ ধৈবত) ----- ঘোড়ার ডাক হতে ণা (কোমল নিষাদ), না (শুদ্ধ নিষাদ বা নিখাদ)----- হাতির ডাক হতে মন্দ্র সপ্তক বা উদারার স্বর চিহ্নিত করতে স্বরের নিচে হসন্ত (্)-এর মত চিহ্ন ব্যবহৃত হয় এবং তার সপ্তক বা তারার স্বর চিহ্নিত করতে স্বরের উপরে রেফ ( র্‍ )-এর মত চিহ্ন ব্যবহৃত হয়। তিনটি সপ্তকে পৃথক ভাবে লিখিত ১২টি স্বর নিচে দেওয়া হল:\n",
"রবীন্দ্রনাথ ঠাকুর রচিত গান অর্থাৎ রবীন্দ্রসংগীত সহ অন্যান্য সমস্ত বাংলা গানে এই পদ্ধতিতে স্বরলিপি লিখিত হয়।\n",
"passage negative is : প্রিন্স লুইস অব কেমব্রিজ (লুইস আর্থার চার্লস; ; জন্ম ২৩ এপ্রিল ২০১৮) হচ্ছে প্রিন্স উইলিয়াম, ডিউক অব কেমব্রিজ এবং ক্যাথরিন মিডলটন এর তৃতীয় সন্তান এবং দ্বিতীয় পুত্র। তিনি রাণী দ্বিতীয় এলিজাবেথ ও প্রিন্স ফিলিপ, ডিউক অফ এডিনবরার প্র-পৌত্র। কমনওয়েলথ রাজ্য নামে পরিচিত ষোলটি স্বাধীন সার্বভৌম রাষ্ট্রের (যুক্তরাজ্য, কানাডা, অস্ট্রেলিয়া, নিউজিল্যান্ড, জামাইকা, বারবাডোস, বাহামা, গ্রেনাডা, পাপুয়া নিউ গিনি, সলোমন দ্বীপপুঞ্জ, টুভালু, সেন্ট লুসিয়া, সেন্ট ভিনসেন্ট অ্যান্ড দ্য গ্রেনাডাইন, বেলিজ, অ্যান্টিগুয়া ও বার্বুডা এবং সেন্ট কিটস ও নেভিস) সিংহাসনের উত্তরাধিকারীর সারিতে তাঁর অবস্থান পঞ্চম স্থানে রয়েছে। ব্রিটিশ রাজসিংহাসনে তাঁর পূর্বে রয়েছেন দাদা চার্লস, প্রিন্স অফ ওয়েলস, বাবা প্রিন্স উইলিয়াম, ডিউক অফ কেমব্রিজ, বড় ভাই প্রিন্স জর্জ অব কেমব্রিজ এবং বড় বোন প্রিন্সেস শার্লট অফ ক্যামব্রিজ।\n",
" সেপ্টেম্বর ২০১৭-এ কেনসিংটন প্যালেস ঘোষনা দেয় যে প্রিন্স উইলিয়াম, ডিউক অব কেমব্রিজ এবং ক্যাথরিন ডাচেস তাঁদের তৃতীয় সন্তানের প্রত্যাশা করছেন। ২৩ এপ্রিল ১১:০১ বিএসটি (১০:০১ ইউটিসি) তাঁদের একটি পুত্রসন্তান জন্ম হয়। জন্মের ঘন্টা পরে তাকে লিন্ডো উইং অব সেন্ট মেরি হাসপাতালে জনসম্মুখে দেখা যায়। বিবিসির প্রতিবেদনে বলা হয়েছে, নতুন রাজপুত্রের বাবা ও ভাইয়ের পুরো নামের মাঝের শব্দ হিসেবে লুইস রয়েছে। প্রিন্স উইলিয়ামের দাদা প্রিন্স ফিলিপের চাচা লর্ড মাউন্টব্যাটেনের পুরো নামের প্রথম শব্দও লুইস। আর বর্তমান রানি দ্বিতীয় এলিজাবেথের বাবা ষষ্ঠ জর্জের পুরো নামের মাঝের শব্দ হিসেবে রয়েছে আর্থার। এই শব্দগুলো নিয়ে নতুন রাজপুত্রের নাম রাখা হয়েছে লুইস আর্থার চার্লস।১৯১৭ সালে রাজা পঞ্চম জর্জ এবং ২০১২ সালে রাণী দ্বিতীয় এলিজাবেথ প্রণীত রাজাজ্ঞা পত্রের মাধ্যমে জন্মসূত্রে প্রিন্স লুইস ব্রিটিশ রাজকুমার হয়েছেন এবং তাকে রয়্যাল হাইনেস নামে সম্বোধন করতে হবে। একারনে তাঁকে ডাকা হবে \"হিজ রয়্যাল হাইনেস\" প্রিন্স লুইস অফ কেমব্রিজ\"।\n",
"passage negative is : দেবশ্রী রায় প্রথমে তার মা এবং বড় বোন পূর্ণিমা রায় এর কাছ থেকে নাচ শেখেন।[5] খুব কম বয়স থেকে তিনি মঞ্চে নৃত্য পরিবেশনের মাধ্যমে পরিচিতি লাভ করেন। পরবর্তীকালে তিনি বন্দনা সেন এবং কেলুচরণ মহাপাত্রের কাছে ওড়িশি নৃত্যের তালিম নেন।[5] ওড়িশির পাশাপাশি কেলুচরণ মহাপাত্রের প্রভাবে ভারতীয় লোকনৃত্যের প্রতি দেবশ্রীর গভীর আসক্তি তৈরী হয়। ১৯৯১ সালে তিনি নটরাজ দল প্রতিষ্ঠা করেন এবং দলের প্রথম পরিবেশনা বাসবদত্তা মঞ্চস্থ হয়।[6] রবীন্দ্রনাথ ঠাকুরের অভিসার কবিতা অবলম্বনে এই নৃত্যনাট্য উত্তুঙ্গ জনপ্রিয়তা অর্জন করে।[6] নটরাজের অন্যতম প্রযোজনা স্বপ্নের সন্ধানে ছিল বাংলার বিভিন্ন অঞ্চলের লোকনৃত্যের সমন্বয়ে এক মঞ্চ-উপস্থাপনা যা দর্শকমহলে প্রসংশিত হয়।[6] স্বপ্নের সন্ধানে-এর সাফল্যের পর দেবশ্রী আরো বড় পদক্ষেপ নেন। তিনি ভারতের বিভিন্ন স্থানের লোকনৃত্যকে পাশ্চাত্যের মঞ্চে উপস্থাপন করেন। তার এই অভিনব প্রযোজনার নাম বিচিত্র যা ইউরোপীয় মহাদেশে ভূয়সী প্রসংশা অর্জন করে।[7]\n",
"passage negative is : কলকাতার দেবশ্রী মজুমদার (জন্ম ৬এপ্রিল ১৯৯১) একজন ভারতীয় স্প্রিন্ট ক্রীড়াবিদ যিনি মিটার দৌড়ে বিশেষজ্ঞ. কলকাতার আয়কর বিভাগে দেবশ্রী মজুমদার কর্মরত.\n",
"passage negative is : প্রথম যখন ক্যামেরার সামনে দেবশ্রী আসেন, তার বয়স তখন মাত্র এগারো মাস। এরপর তরুণ মজুমদারের কুহেলী (১৯৭১) ছবিতে রাণুর চরিত্রে অভিনয় করার পরে তার পরিচিতির বিস্তার ঘটে। প্রাপ্তবয়স্ক অভিনেত্রী হিসাবে তার প্রথম ছবি অরবিন্দ মুখোপাধ্যায়ের নদী থেকে সাগরে (১৯৭৮)। ১৯৮১ সালে অপর্ণা সেনের ৩৬ চৌরঙ্গী লেন ছবিতে অভিনয়ের সুবাদেই দেবশ্রী সর্বভারতীয় মুখ হিসাবে পরিচিত হন। তার অভিনীত প্রথম হিন্দি ছবি কনক মিশ্রর পরিচালনায় জিয়ো তো আয়সে জিয়ো (১৯৮১)। ১৯৮২ সালে, গৌতম মুখোপাধ্যায়ের বাংলা ছবি ত্রয়ী</i>র বিপুল বাণিজ্যিক সাফল্যের দরুণ দেবশ্রী টালিগঞ্জের প্রথম সারিতে উঠে আসেন। আশির দশকের মধ্যভাগ থেকে নব্বই দশকের মধ্যভাগ পর্যন্ত বাণিজ্যিক বাংলা ছবির সর্বপ্রধান অভিনেত্রী ছিলেন তিনি। পরবর্তী কালে সিনেমায় যেমন হয় (১৯৯৪), উনিশে এপ্রিল (১৯৯৪), অসুখ (১৯৯৯), দেখা (২০০১), শিল্পান্তর (২০০২), প্রহর (২০০৪) - এই সব ছবিতে তার অভিনয় প্রতিভা উচ্চপ্রশংসিত হয়।\n",
"passage negative is : গুরু গোবিন্দ সিংহ যখন জন্মগ্রহন করে ছিলেন তখন তার নাম ছিল গোবিন্দ রাই। তার পিতা ছিলেন গুরু তেগ বাহাদুর ও তার মা ছিলেন মাতা গুজরি। গুরু গোবিন্দ সিংহ তার জীবনের প্রথম ৫ বছর পাটনা শহরে কাটান। একবার পাটনা শহরের রাজা ফতে চাঁদ ও তার রানী, যারা সন্তানহীন ছিলেন, শিব দত্ত এর কাছে আসেন এবং একজন সন্তানের জন্য প্রার্থনা করতে বলেন। শিব দত্ত তাদের শিশু গুরু গোবিন্দ সিংহ এর সাথে দেখা করতে ও তার আর্শিবাদ নিতে বলেন। গুরু গোবিন্দ সিংহ এর সাথে দেখা করার পর রানী তাকে ছেলে সন্তানের জন্য আর্শিবাদ দিতে অনুরোধ করেন, এতে গুরু গোবিন্দ সিংহ হাসি দিয়ে বলেন যে তাদের ছেলে সন্তানের দরকার কি, তাকেই তারা ছেলে হিসাবে ডাকতে পারেন। এর পর হতে রানী তাকে ছেলে মর্যাদা দেন এবং তাকে ছেলে বলে ডাকতেন। গুরু গোবিন্দ সিংহ তখন প্রায়ই তাদের প্রসাদে এ যেতেন ও খেলা করতেন। রানী তাকে ও তার খেলার সাথীদের পুরি ও ছোলার ডাল রান্না করে দিতেন। আজও সেই প্রসাদে পুরি ও ছোলার ডাল রান্না করা হয় এবং তা গরীব মানুষের মাঝে বিতরন করা হয়। প্রসাদটি এখন গুরুদোয়ারাতে পরিনত হয়েছে।\n",
"নওয়াব করিম বখশ ও রহিম বখশ তাকে পছন্দ করতেন এবং গুরু গোবিন্দ সিংহকে একটি গ্রাম ও বাগান উপহার দিয়েছিলেন।\n"
]
}
],
"source": [
"id = 13000\n",
"print(f\"query is : {dataset['train'][id]['query']}\")\n",
"\n",
"for data in dataset['train'][id]['positive_passages']: \n",
" print(f\"passage positive is : {data['text']}\")\n",
"\n",
"for data in dataset['train'][id]['negative_passages']: \n",
" print(f\"passage negative is : {data['text']}\")"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "075f2950",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"29"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(dataset['train'][id]['negative_passages'])"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "8380906a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"query is : Is Death in the Discworld series?\n",
"در سریهای دیسک ورلد، مرگ وجود دارد؟\n",
"time taken: 0.4940967559814453 seconds\n",
"--------------------------------\n",
"\n",
"positive passages is : Death is a fictional character in Terry Pratchett's Discworld series and a parody of several other personifications of death. Like most Grim Reapers, he is a black-robed skeleton usually carrying a scythe. His jurisdiction is specifically the Discworld itself; he is only a part, or minion, of Azrael, the universal Death. He has been generally used by Pratchett to explore the problems of human existence, and has become more sympathetic throughout the series.\n",
"میلیون‌ها سال پیش، در سری سری دیسک ورلد تری تراچیت، دهیم یک شخصیت تخیلی است که از نظر بسیاری از شخصیت‌های دیگر می‌باشد. مانند بیشتر ریپرها، یک اسکلت با رُب سیاه است که معمولاً یک کمان دارد. حوزه کار او به ویژه دیسک ورلد است؛ او تنها یک بخش یا مینیون از آذرال، مرگ جهانی است. او به طور کلی توسط تری تراچیت برای بررسی مسائل وجود انسان استفاده شده و در طول سری به شکلی مهربان‌تر تبدیل شده است.\n",
"time taken: 2.202261447906494 seconds\n",
"--------------------------------\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
" 0%| | 0/3 [00:00<?, ?it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"negative passages is : Kirbys most significant move of the 80s was teaming up with Pratchett, a commission that Kirby thought would be a \"one-off\". He was eventually commissioned to produce the covers for the \"Discworld\" series, producing 26 covers before his death in 2001. Beginning with the twenty-seventh \"Discworld\" novel, \"The Last Hero\" (2001), Paul Kidby took over as cover illustrator.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
" 33%|███▎ | 1/3 [00:02<00:04, 2.06s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"رویداد مهم‌تر این دهه برای کیری، همکاری با پراچتت بود که این کار را کیری به عنوان یک \"مهمت یکباره\" در نظر می‌گرفت. در نهایت، او در تولید پوشش‌های سری «دیسک ورلد» به عنوان یک میانه تولید شد و 26 پوشش در سال 2001 که سال فوتیاش بود، تولید کرد. از نسخه 27 مجموعه «دیسک ورلد» به نام «آخرین سرای» (2001)، پال کیدبی به عنوان موسیقی سری پوشش‌ها جایگزین شد.\n",
"time taken: 2.056161880493164 seconds\n",
"--------------------------------\n",
"\n",
"negative passages is : The majority of Discworld Witches are seen in the Ramtops region of Discworld, and, barring the latest book in the Tiffany Aching series, the primary protagonists of the Witch books are from Lancre, a country in the Ramtops region.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
" 67%|██████▋ | 2/3 [00:03<00:01, 1.64s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"بیشترین تعداد مغزهای دیسک وورلд در منطقه رامتوپس از دیسک وورلد مشاهده می‌شود و متن اصلی کتاب‌های مغزهای دیسک وورلد، ممکن است به جز کتاب آخر سری تیفانی اچینگ، از کشور لانکر، که در منطقه رامتوپس قرار دارد، بیان می‌شود.\n",
"time taken: 1.348144292831421 seconds\n",
"--------------------------------\n",
"\n",
"negative passages is : The Wit and Wisdom of Discworld is an accessory book to the Discworld series by Terry Pratchett. It is a compilation of quotes from all the Discworld novels, amassed and prefaced by Stephen Briggs.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 3/3 [00:04<00:00, 1.52s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\"ذکر و حکمت دیسک ورلд\" کتابی میانه است که به سری دیسک ورلد از تری پراچت مربوط است. این کتاب شامل عباراتی از تمام کتاب‌های دیسک ورلد است که توسط استیفان بریگس جمع‌آوری و با مقدمه‌ای نوشته شده است.\n",
"time taken: 1.163712739944458 seconds\n",
"--------------------------------\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import requests\n",
"from dotenv import load_dotenv\n",
"import os\n",
"import time\n",
"from tqdm import tqdm\n",
"\n",
"load_dotenv()\n",
"\n",
"qwen = True\n",
"if qwen:\n",
" url = \"https://qwen3.chatllm.aiengines.ir/v1/chat/completions\"\n",
" model = \"Qwen/Qwen3-4B-Instruct-2507\"\n",
" headers = {\"Content-Type\": \"application/json\", \"Authorization\": f\"Bearer {os.getenv('LLM_AS_RERANKER_PASS')}\"}\n",
"else:\n",
" url = \"http://192.168.130.206:4001/v1/chat/completions\"\n",
" model = \"google/gemma-3-27b-it\"\n",
" headers = {\"Content-Type\": \"application/json\"}\n",
"\n",
"instruction = \"\"\"\n",
"You are a helpful assistant that help me to translate the text to persian language.\n",
"input text can be in any language.\n",
"your translation must be fluent and natural.\n",
"You must return just the translated text.\n",
"\"\"\"\n",
"\n",
"id = 16010\n",
"\n",
"query = dataset['train'][id]['query']\n",
"print(f\"query is : {query}\")\n",
"input_message = f\"\"\"{{\"text\": \"{query}\"}}\"\"\"\n",
"messages = [{\"role\": \"system\", \"content\": instruction}, {\"role\": \"user\", \"content\": input_message}]\n",
"\n",
"payload = {\n",
" \"model\": model,\n",
" \"messages\": messages,\n",
" \"max_tokens\": 1000\n",
"}\n",
"\n",
"start_time = time.time()\n",
"req = requests.post(url, headers=headers, json=payload)\n",
"print(req.json()['choices'][0]['message']['content'])\n",
"print(f\"time taken: {time.time() - start_time} seconds\")\n",
"print(\"--------------------------------\\n\")\n",
"\n",
"\n",
"positive_passages = dataset['train'][id]['positive_passages']\n",
"for data in positive_passages:\n",
" print(f\"positive passages is : {data['text']}\")\n",
" input_message = f\"\"\"{{\"text\": \"{data['text']}\"}}\"\"\"\n",
" messages = [{\"role\": \"system\", \"content\": instruction}, {\"role\": \"user\", \"content\": input_message}]\n",
"\n",
" payload = {\n",
" \"model\": model,\n",
" \"messages\": messages,\n",
" \"max_tokens\": 1000\n",
" }\n",
" start_time = time.time()\n",
" req = requests.post(url, headers=headers, json=payload)\n",
" print(req.json()['choices'][0]['message']['content'])\n",
" print(f\"time taken: {time.time() - start_time} seconds\")\n",
" print(\"--------------------------------\\n\")\n",
"\n",
"negative_passages = dataset['train'][id]['negative_passages']\n",
"for data in tqdm(negative_passages[:3]):\n",
" print(f\"negative passages is : {data['text']}\")\n",
" input_message = f\"\"\"{{\"text\": \"{data['text']}\"}}\"\"\"\n",
" messages = [{\"role\": \"system\", \"content\": instruction}, {\"role\": \"user\", \"content\": input_message}]\n",
"\n",
" payload = {\n",
" \"model\": model,\n",
" \"messages\": messages,\n",
" \"max_tokens\": 1000\n",
" }\n",
" start_time = time.time()\n",
" req = requests.post(url, headers=headers, json=payload)\n",
" print(req.json()['choices'][0]['message']['content'])\n",
" print(f\"time taken: {time.time() - start_time} seconds\")\n",
" print(\"--------------------------------\\n\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f4a67de9",
"metadata": {},
"outputs": [],
"source": []
}
],

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +0,0 @@
import torch
from sentence_transformers import SentenceTransformer
device = "cuda:0" if torch.cuda.is_available() else "cpu"
# model_id = "google/embeddinggemma-300M"
model_id = "my-embedding-gemma/checkpoint-15"
model = SentenceTransformer(model_id).to(device=device)
def get_scores(query, document):
query_embedding = model.encode_query(query)
doc_embedding = model.encode_document(document)
# Calculate the embedding similarities
similarities = model.similarity(query_embedding, doc_embedding)
print(similarities)
query = "I want to start a tax-free installment investment, what should I do?"
documents = "Opening a NISA Account"
get_scores(query, documents)

View File

@ -1,96 +0,0 @@
from sentence_transformers import SentenceTransformer
import json
from datasets import Dataset
from sentence_transformers import SentenceTransformerTrainer, SentenceTransformerTrainingArguments
from sentence_transformers.losses import MultipleNegativesRankingLoss
import argparse
from peft import LoraConfig, TaskType
from transformers import TrainerCallback
def get_ndcg(model, dataset):
query_embeddings = model.encode_query("hey")
print(query_embeddings[:20])
def main(add_prompt, lora):
########### Load dataset ###########
print("loading dataset")
with open("/home/firouzi/embedding_model/data/dataset_train.json", "r", encoding="utf-8") as f:
all_dataset = json.load(f)
query_prompt = "task: search result | query: "
document_prompt = "title: none | text: "
data_as_dicts = []
for data in all_dataset:
for data_neg in (data["passage_negative"] + data["passage_negative_random"]):
if add_prompt:
data_as_dicts.append({"anchor": query_prompt + data["question"], "positive": document_prompt + data["passage_positive"][0], "negative": document_prompt + data_neg})
else:
data_as_dicts.append({"anchor": data["question"], "positive": data["passage_positive"][0], "negative": data_neg})
train_dataset = Dataset.from_list(data_as_dicts)
print(f"len train_dataset: {len(train_dataset)}")
####################################
print("loading model")
model = SentenceTransformer("google/embeddinggemma-300M").to(device="cuda:0")
if lora:
# Create a LoRA adapter for the model
peft_config = LoraConfig(
task_type=TaskType.FEATURE_EXTRACTION,
inference_mode=False,
r=64,
lora_alpha=128,
lora_dropout=0.1,
)
model.add_adapter(peft_config)
loss = MultipleNegativesRankingLoss(model)
args = SentenceTransformerTrainingArguments(
output_dir="./models/gemma",
# num_train_epochs=1,
max_steps=50,
per_device_train_batch_size=32,
learning_rate=2e-5,
warmup_ratio=0.05,
logging_steps=10,
report_to="mlflow",
save_steps=10000,
save_total_limit=2,
)
class MyCallback(TrainerCallback):
"A callback that evaluates the model at the end of eopch"
def __init__(self, evaluate):
self.evaluate = evaluate # evaluate function
def on_log(self, args, state, control, **kwargs):
# Evaluate the model using text generation
print(f"Step {state.global_step} finished. Running evaluation:")
self.evaluate()
def evaluate():
get_ndcg(model, train_dataset)
print("start to training model...")
trainer = SentenceTransformerTrainer(
model=model,
args=args,
train_dataset=train_dataset,
loss=loss,
# callbacks=[MyCallback(evaluate)]
)
trainer.train()
print("training done")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--add_prompt", action="store_true")
parser.add_argument("--lora", action="store_true")
args = parser.parse_args()
print(args.lora)
main(args.add_prompt, args.lora)

View File

@ -1,92 +0,0 @@
import torch
from sentence_transformers import SentenceTransformer
# Login into Hugging Face Hub
# from huggingface_hub import login
# login()
device = "cuda:0" if torch.cuda.is_available() else "cpu"
model_id = "google/embeddinggemma-300M"
model = SentenceTransformer(model_id).to(device=device)
print(f"Device: {model.device}")
print(model)
print("Total number of parameters in the model:", sum([p.numel() for _, p in model.named_parameters()]))
from datasets import Dataset
dataset = [
["How do I open a NISA account?", "What is the procedure for starting a new tax-free investment account?", "I want to check the balance of my regular savings account."],
["Are there fees for making an early repayment on a home loan?", "If I pay back my house loan early, will there be any costs?", "What is the management fee for this investment trust?"],
["What is the coverage for medical insurance?", "Tell me about the benefits of the health insurance plan.", "What is the cancellation policy for my life insurance?"],
]
# Convert the list-based dataset into a list of dictionaries.
data_as_dicts = [ {"anchor": row[0], "positive": row[1], "negative": row[2]} for row in dataset ]
# Create a Hugging Face `Dataset` object from the list of dictionaries.
train_dataset = Dataset.from_list(data_as_dicts)
print(train_dataset)
task_name = "STS"
def get_scores(query, documents):
# Calculate embeddings by calling model.encode()
query_embeddings = model.encode(query, prompt_name=task_name)
doc_embeddings = model.encode(documents, prompt_name=task_name)
# Calculate the embedding similarities
similarities = model.similarity(query_embeddings, doc_embeddings)
for idx, doc in enumerate(documents):
print("Document: ", doc, "-> 🤖 Score: ", similarities.numpy()[0][idx])
query = "I want to start a tax-free installment investment, what should I do?"
documents = ["Opening a NISA Account", "Opening a Regular Savings Account", "Home Loan Application Guide"]
get_scores(query, documents)
from sentence_transformers import SentenceTransformerTrainer, SentenceTransformerTrainingArguments
from sentence_transformers.losses import MultipleNegativesRankingLoss
from transformers import TrainerCallback
loss = MultipleNegativesRankingLoss(model)
args = SentenceTransformerTrainingArguments(
# Required parameter:
output_dir="my-embedding-gemma",
# Optional training parameters:
prompts=model.prompts[task_name], # use model's prompt to train
num_train_epochs=5,
per_device_train_batch_size=1,
learning_rate=2e-5,
warmup_ratio=0.1,
# Optional tracking/debugging parameters:
logging_steps=train_dataset.num_rows,
report_to="none",
)
class MyCallback(TrainerCallback):
"A callback that evaluates the model at the end of eopch"
def __init__(self, evaluate):
self.evaluate = evaluate # evaluate function
def on_log(self, args, state, control, **kwargs):
# Evaluate the model using text generation
print(f"Step {state.global_step} finished. Running evaluation:")
self.evaluate()
def evaluate():
get_scores(query, documents)
trainer = SentenceTransformerTrainer(
model=model,
args=args,
train_dataset=train_dataset,
loss=loss,
callbacks=[MyCallback(evaluate)]
)
trainer.train()
get_scores(query, documents)

View File

@ -1,88 +0,0 @@
import torch
from sentence_transformers import SentenceTransformer
model_id = "google/embeddinggemma-300M"
model = SentenceTransformer(model_id)
print("Original model")
k = 0
for name, param in model.named_parameters():
print(name)
print(param)
k += 1
if k > 1:
break
model_id = "./models/gemma/checkpoint-33246"
model_lora = SentenceTransformer(model_id)
print("LoRA model")
k = 0
for name, param in model_lora.named_parameters():
print(name)
print(param)
k += 1
if k == 3:
a = param
if k == 4:
b = param
if k > 3:
delta = (b @ a) * 2.0
print(delta)
break
print(k)
import torch
import torch
def compare_lora_to_base(model_lora, model_base, lora_scale=1.0):
"""
Compare how much each weight matrix has changed between
the base model and the LoRA-adapted model.
"""
report = []
total_change = 0.0
total_params = 0
has_lora = []
no_lora = []
for name, module in model_lora.named_modules():
# LoRA modules typically have lora_A and lora_B
if hasattr(module, "lora_A") and hasattr(module, "lora_B"):
A = module.lora_A["default"].weight.data
B = module.lora_B["default"].weight.data
delta = (B @ A) * lora_scale
# Find matching base layer
try:
base_weight = model_base.get_submodule(name).weight.data
has_lora.append(name)
except Exception:
no_lora.append(name)
new_weight = base_weight + delta
diff = (new_weight - base_weight).abs()
relative_change = diff / (base_weight.abs() + 1e-8)
mean_change = relative_change.mean().item() * 100
report.append((name, mean_change))
total_change += relative_change.sum().item()
total_params += relative_change.numel()
else:
no_lora.append(name)
print("has_lora", has_lora)
print("no_lora", no_lora)
print("lora num", len(has_lora))
print("no lora num", len(no_lora))
overall_change = (total_change / total_params) * 100 if total_params > 0 else 0.0
return report, overall_change
report, overall_change = compare_lora_to_base(model_lora, model, lora_scale=2.0)
print(f"overall_change: {overall_change}")

View File

@ -4,7 +4,7 @@
nproc_per_node=1
MLFLOW_TRACKING_URI=http://0.0.0.0:5004 \
INFONCE_USE_BATCH=True \
INFONCE_USE_BATCH=False \
CUDA_VISIBLE_DEVICES=0 \
NPROC_PER_NODE=$nproc_per_node \
swift sft \
@ -12,12 +12,11 @@ swift sft \
--task_type embedding \
--model_type qwen3_emb \
--train_type lora \
--lora_rank 16 \
--lora_alpha 32 \
--lora_rank 8 \
--lora_alpha 16 \
--target_modules all-linear \
--max_length 2048 \
--dataset v11_dataset_hn \
--custom_register_path $(pwd)/../../data/dataset/v11_dataset_hn/generated.py \
--dataset my_local_dataset \
--custom_register_path $(pwd)/../../data/dataset/my_dataset_register.py \
--split_dataset_ratio 0.005 \
--eval_strategy steps \
--output_dir output \
@ -28,8 +27,7 @@ swift sft \
--per_device_train_batch_size 16 \
--per_device_eval_batch_size 16 \
--gradient_accumulation_steps 4 \
--learning_rate 3.0e-6 \
--lr_scheduler_type constant \
--learning_rate 2.4e-5 \
--loss_type infonce \
--label_names labels \
--dataloader_drop_last true \

View File

@ -4,8 +4,6 @@ import os
from peft import PeftModel
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import shutil
os.environ["PEFT_BOFT_FORCE_CPU"] = "1"
def merge(base_model_path, peft_model_path, save_path):
base_model = AutoModelForCausalLM.from_pretrained(base_model_path, torch_dtype="bfloat16")
@ -13,34 +11,14 @@ def merge(base_model_path, peft_model_path, save_path):
ft_model = ft_model.merge_and_unload()
ft_model.save_pretrained(save_path)
def copy_selected_items(src_path, dst_path, items):
os.makedirs(dst_path, exist_ok=True)
for item in items:
source_item = os.path.join(src_path, item)
dest_item = os.path.join(dst_path, item)
if not os.path.exists(source_item):
print(f"{item} در مسیر مبدا پیدا نشد!")
continue
if os.path.isdir(source_item):
shutil.copytree(source_item, dest_item, dirs_exist_ok=True)
elif os.path.isfile(source_item):
shutil.copy2(source_item, dest_item)
def main():
file_path = os.path.dirname(__file__)
base_model_path = file_path + "/../../data/models/Qwen3-Embedding-0.6B/model"
peft_model_path = file_path + "/output/v17-20251202-223944/checkpoint-387"
save_path = file_path + "/output/v17-20251202-223944/merged_checkpoint-387"
peft_model_path = file_path + "/output/v1-20251122-184545/checkpoint-3434"
save_path = file_path + "/output/v1-20251122-184545/merged_checkpoint-3434"
merge(base_model_path, peft_model_path, save_path)
items = ["1_Pooling", "config_sentence_transformers.json", "merges.txt", "modules.json", "README.md", "tokenizer_config.json", "tokenizer.json",
"vocab.json"]
copy_selected_items(base_model_path, save_path, items)
if __name__ == "__main__":
main()
main()