Jromi / app.py
renpley2's picture
Create app.py
1c80eed verified
Raw
History Blame Contribute Delete
22.3 kB
import subprocess
import sys
import os
import json
# --- تنظیمات کلی پروژه ---
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
MODEL_DIR = os.path.join(PROJECT_DIR, "rayzh_model")
DATA_DIR = os.path.join(PROJECT_DIR, "data")
API_PORT = 5000
GRADIO_PORT = 7860
# --- لیست وابستگی‌ها ---
# این لیست باید شامل تمام کتابخانه‌هایی باشد که نیاز دارید.
# کتابخانه‌هایی که برای آموزش استفاده می‌شوند (مانند datasets, accelerate)
# و کتابخانه‌هایی که برای API و رابط کاربری استفاده می‌شوند (مانند flask, gradio).
DEPENDENCIES = [
"transformers",
"datasets",
"torch",
"tensorboard", # برای لاگ های احتمالی
"accelerate",
"evaluate",
"pandas",
"flask",
"gradio",
"sentencepiece", # معمولا برای مدل های چند زبانه لازم است
"protobuf" # گاهی اوقات برای serializing/deserializing مدل ها لازم است
]
# --- توابع کمکی ---
def run_command(command, cwd=None, env=None):
"""
یک دستور را اجرا می کند و خروجی آن را نمایش می دهد.
"""
print(f"Executing command: {' '.join(command)}")
try:
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
cwd=cwd,
env=env
)
for line in process.stdout:
print(line, end='')
process.wait()
if process.returncode != 0:
print(f"Error executing command: {' '.join(command)}")
return False
return True
except FileNotFoundError:
print(f"Error: Command '{command[0]}' not found. Is it installed and in your PATH?")
return False
except Exception as e:
print(f"An error occurred: {e}")
return False
def install_dependencies():
"""
وابستگی های لازم را نصب می کند.
"""
print("--- نصب وابستگی ها ---")
for dep in DEPENDENCIES:
if not run_command([sys.executable, "-m", "pip", "install", dep]):
print(f"Failed to install {dep}. Please check your internet connection and pip setup.")
return False
print("--- وابستگی ها با موفقیت نصب شدند ---")
return True
def create_dummy_data():
"""
اگر داده های آموزشی وجود نداشته باشند، داده های نمونه ایجاد می کند.
در سناریوی واقعی، شما باید داده های واقعی خود را اینجا قرار دهید یا دانلود کنید.
"""
os.makedirs(DATA_DIR, exist_ok=True)
dummy_csv_path = os.path.join(DATA_DIR, "sentiment_data.csv")
if not os.path.exists(dummy_csv_path):
print("Creating dummy sentiment data...")
data = {
"text": [
"چه روز خوبی بود! خیلی خوشحالم. 😊",
"امروز خیلی ناراحتم و خسته ام. 😞",
"هوا امروز عالیه!",
"This is a fantastic product! I love it. 👍",
"I am very disappointed with the service. 😠",
"How are you doing today?",
"سلام، خوبی؟",
"صبح بخیر!",
"Good morning!",
"I'm not sure how to feel about this.",
"کارم راه افتاد! ممنون. 🙏",
"واقعا؟ باورم نمیشه! 🤯",
"I can't believe this happened. 😥"
],
"label": [
1, # مثبت
0, # منفی
1, # مثبت
1, # مثبت
0, # منفی
2, # خنثی/احوالپرسی (برای ساده سازی)
2, # خنثی/احوالپرسی
2, # خنثی/احوالپرسی
2, # خنثی/احوالپرسی
0, # منفی (با توجه به متن)
1, # مثبت
1, # مثبت (با توجه به ایموجی)
0 # منفی
]
}
import pandas as pd
df = pd.DataFrame(data)
df.to_csv(dummy_csv_path, index=False)
print(f"Dummy data created at: {dummy_csv_path}")
else:
print("Sentiment data already exists.")
def train_model():
"""
مدل را فاین‌تیون می کند.
این بخش ساده شده است و فرض می کند داده های لازم وجود دارند.
در واقعیت، این بخش می تواند بسیار پیچیده تر باشد.
"""
print("--- شروع آموزش مدل ---")
os.makedirs(MODEL_DIR, exist_ok=True)
# ما از یک اسکریپت جداگانه برای آموزش استفاده می کنیم تا فایل اصلی شلوغ نشود.
# این اسکریپت در زمان اجرا توسط اسکریپت اصلی فراخوانی می شود.
train_script_content = f"""
import os
import pandas as pd
from datasets import Dataset
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments
import numpy as np
import evaluate
import torch
# --- تنظیمات ---
MODEL_NAME = "bert-base-multilingual-cased" # مدل پایه چند زبانه
NUM_LABELS = 3 # 0: منفی, 1: مثبت, 2: خنثی/احوالپرسی
TRAIN_DATA_PATH = os.path.join("{DATA_DIR}", "sentiment_data.csv")
OUTPUT_MODEL_PATH = "{MODEL_DIR}"
LOGGING_DIR = os.path.join("{PROJECT_DIR}", "logs")
# --- بارگذاری داده ها ---
print("Loading data from:", TRAIN_DATA_PATH)
df = pd.read_csv(TRAIN_DATA_PATH)
dataset = Dataset.from_pandas(df)
# تقسیم به مجموعه آموزش و تست
dataset = dataset.train_test_split(test_size=0.2, seed=42)
train_dataset = dataset['train']
eval_dataset = dataset['test']
# --- توکنایزر و مدل ---
print("Loading tokenizer and model...")
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME, num_labels=NUM_LABELS)
# تابع توکنایز کردن
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=128)
print("Tokenizing datasets...")
tokenized_train_dataset = train_dataset.map(tokenize_function, batched=True)
tokenized_eval_dataset = eval_dataset.map(tokenize_function, batched=True)
# حذف ستون های غیر ضروری و تغییر نام ها
tokenized_train_dataset = tokenized_train_dataset.remove_columns(["text"])
tokenized_eval_dataset = tokenized_eval_dataset.remove_columns(["text"])
tokenized_train_dataset = tokenized_train_dataset.rename_column("label", "labels")
tokenized_eval_dataset = tokenized_eval_dataset.rename_column("label", "labels")
# تنظیم فرمت دیتاست برای PyTorch
tokenized_train_dataset.set_format("torch")
tokenized_eval_dataset.set_format("torch")
# --- معیارهای ارزیابی ---
metric = evaluate.load("accuracy") # می توانید precision, recall, f1 را هم اضافه کنید
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
# --- پارامترهای آموزش ---
training_args = TrainingArguments(
output_dir=LOGGING_DIR, # لاگ ها اینجا ذخیره می شوند
evaluation_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=4, # کاهش سایز بچ برای حافظه کمتر
per_device_eval_batch_size=4,
num_train_epochs=3,
weight_decay=0.01,
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="accuracy",
push_to_hub=False, # مستقیم آپلود نمی کنیم
report_to="tensorboard" # فعال کردن TensorBoard
)
# --- ساخت Trainer ---
print("Initializing Trainer...")
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_train_dataset,
eval_dataset=tokenized_eval_dataset,
compute_metrics=compute_metrics,
tokenizer=tokenizer,
)
# --- شروع آموزش ---
print("Starting training...")
trainer.train()
# --- ذخیره مدل نهایی ---
print(f"Saving final model to {OUTPUT_MODEL_PATH}...")
trainer.save_model(OUTPUT_MODEL_PATH)
tokenizer.save_pretrained(OUTPUT_MODEL_PATH)
print("Model training and saving complete.")
"""
# ایجاد فایل آموزش در پوشه موقت یا جای دیگر
train_script_path = os.path.join(PROJECT_DIR, "train_script.py")
with open(train_script_path, "w", encoding="utf-8") as f:
f.write(train_script_content)
# اجرای اسکریپت آموزش
if not run_command([sys.executable, train_script_path]):
print("Model training failed.")
return False
# پاک کردن فایل موقت آموزش
os.remove(train_script_path)
print("--- آموزش مدل با موفقیت انجام شد ---")
return True
def start_api():
"""
یک API ساده با Flask راه‌اندازی می کند.
"""
print(f"--- شروع API در پورت {API_PORT} ---")
# ما از یک اسکریپت جداگانه برای API استفاده می کنیم
api_script_content = f"""
import os
from flask import Flask, request, jsonify
from transformers import pipeline
import torch
# --- تنظیمات ---
MODEL_PATH = "{MODEL_DIR}"
# NUM_LABELS = 3 # اگر نیاز به تنظیم دقیق تر باشد
# --- بررسی وجود مدل ---
if not os.path.exists(MODEL_PATH):
print(f"Error: Model not found at {{MODEL_PATH}}. Please train the model first.")
exit(1)
# --- بارگذاری مدل ---
print(f"Loading model from {{MODEL_PATH}}...")
try:
# تعیین دستگاه (CPU یا GPU)
device = 0 if torch.cuda.is_available() else -1
print(f"Using device: {{'cuda' if device == 0 else 'cpu'}}")
sentiment_analyzer = pipeline(
"sentiment-analysis",
model=MODEL_PATH,
tokenizer=MODEL_PATH,
device=device # استفاده از GPU در صورت امکان
)
print("Model loaded successfully.")
# --- تابع تحلیل ---
def analyze_text_sentiment(text):
if not text:
return {
"error": "Input text is required.",
"input_text": text,
"sentiment": None,
"sentiment_score": None
}
try:
# اگر مدل شما NUM_LABELS=3 باشد، ممکن است خروجی متفاوت باشد
# مثلا {'label': 'LABEL_1', 'score': 0.99}
# ما باید آن را به 'positive', 'negative', 'neutral' تبدیل کنیم
result = sentiment_analyzer(text)[0]
label = result['label'].lower()
if 'positive' in label or label.endswith('_1'): # فرض بر این است که 1 مثبت است
sentiment = "positive"
elif 'negative' in label or label.endswith('_0'): # فرض بر این است که 0 منفی است
sentiment = "negative"
else: # فرض بر این است که 2 یا چیز دیگر خنثی است
sentiment = "neutral"
return {
"input_text": text,
"sentiment": sentiment,
"sentiment_score": round(result['score'], 4)
}
except Exception as e:
print(f"Error during sentiment analysis: {{e}}")
return {
"error": f"An error occurred during analysis: {{e}}",
"input_text": text,
"sentiment": None,
"sentiment_score": None
}
# --- Flask App ---
app = Flask(__name__)
@app.route('/analyze', methods=['POST'])
def analyze_endpoint():
data = request.get_json()
text = data.get('text')
result = analyze_text_sentiment(text)
return jsonify(result)
@app.route('/', methods=['GET'])
def health_check():
return jsonify({"status": "API is running", "model_loaded": True})
# --- اجرای App ---
if __name__ == '__main__':
print(f"Starting Flask API on port {API_PORT}...")
# برای production از gunicorn یا uwsgi استفاده کنید
app.run(host='0.0.0.0', port=API_PORT, debug=False) # debug=False برای production
except Exception as e:
print(f"Error loading model or setting up API: {{e}}")
exit(1)
"""
api_script_path = os.path.join(PROJECT_DIR, "api_server.py")
with open(api_script_path, "w", encoding="utf-8") as f:
f.write(api_script_content)
print("API server script created.")
# اجرای API در پس زمینه
# توجه: در یک محیط واقعی، شما این کار را با ابزارهایی مانند Gunicorn یا Supervisord انجام می دهید.
# اینجا برای سادگی، آن را در یک فرآیند جداگانه اجرا می کنیم.
# اما اجرای آن به صورت "در پس زمینه" از یک فایل واحد پیچیده است.
# ما به کاربر اطلاع می دهیم که چگونه آن را اجرا کند.
print(f"\n--- برای اجرای API: ---")
print(f"1. فایل '{os.path.basename(api_script_path)}' در همین پوشه ایجاد شده است.")
print(f"2. ترمینال جدیدی باز کنید و به پوشه پروژه بروید.")
print(f"3. دستور زیر را اجرا کنید: python {os.path.basename(api_script_path)}")
print(f"API در پورت {API_PORT} در دسترس خواهد بود.")
print("----------------------\n")
return True
def start_gradio_interface():
"""
یک رابط کاربری ساده با Gradio راه‌اندازی می کند.
"""
print(f"--- راه اندازی رابط کاربری Gradio در پورت {GRADIO_PORT} ---")
os.makedirs(MODEL_DIR, exist_ok=True)
if not os.path.exists(os.path.join(MODEL_DIR, "config.json")):
print(f"Error: Model directory '{MODEL_DIR}' is empty or does not contain a trained model.")
print("Please train the model first.")
return False
gradio_script_content = f"""
import gradio as gr
from transformers import pipeline
import os
import torch
# --- تنظیمات ---
MODEL_PATH = "{MODEL_DIR}"
# NUM_LABELS = 3 # اگر نیاز به تنظیم دقیق تر باشد
# --- بارگذاری مدل ---
print(f"Loading model from {{MODEL_PATH}} for Gradio interface...")
try:
# تعیین دستگاه (CPU یا GPU)
device = 0 if torch.cuda.is_available() else -1
print(f"Gradio using device: {{'cuda' if device == 0 else 'cpu'}}")
sentiment_analyzer = pipeline(
"sentiment-analysis",
model=MODEL_PATH,
tokenizer=MODEL_PATH,
device=device
)
print("Model loaded successfully for Gradio.")
# --- تابع تحلیل ---
def analyze_text_for_gradio(text):
if not text:
return "لطفا متنی وارد کنید."
try:
result = sentiment_analyzer(text)[0]
label = result['label'].lower()
if 'positive' in label or label.endswith('_1'):
sentiment = "مثبت"
elif 'negative' in label or label.endswith('_0'):
sentiment = "منفی"
else:
sentiment = "خنثی"
return f"احساس تشخیص داده شده: **{{sentiment}}** (امتیاز: {{round(result['score'], 4)}})"
except Exception as e:
print(f"Error during Gradio analysis: {{e}}")
return f"خطایی رخ داد: {{e}}"
# --- رابط کاربری Gradio ---
interface = gr.Interface(
fn=analyze_text_for_gradio,
inputs=gr.Textbox(lines=3, placeholder="متن فارسی یا انگلیسی خود را اینجا وارد کنید...", label="متن ورودی"),
outputs=gr.Textbox(label="نتیجه تحلیل"),
title="مدل هوش مصنوعی رایژ (Rayzh)",
description="این مدل احساسات (مثبت، منفی، خنثی) متن شما را به زبان فارسی و انگلیسی تشخیص می دهد.",
allow_flagging="never"
)
# --- اجرای App ---
if __name__ == '__main__':
print(f"Starting Gradio interface on port {GRADIO_PORT}...")
# share=True باعث می شود یک لینک عمومی برای تست ایجاد شود (مناسب برای دمو)
interface.launch(server_port=GRADIO_PORT, share=False)
except Exception as e:
print(f"Error loading model or setting up Gradio interface: {{e}}")
exit(1)
"""
gradio_script_path = os.path.join(PROJECT_DIR, "gradio_app.py")
with open(gradio_script_path, "w", encoding="utf-8") as f:
f.write(gradio_script_content)
print("Gradio interface script created.")
print(f"\n--- برای اجرای رابط کاربری Gradio: ---")
print(f"1. فایل '{os.path.basename(gradio_script_path)}' در همین پوشه ایجاد شده است.")
print(f"2. ترمینال جدیدی باز کنید و به پوشه پروژه بروید.")
print(f"3. دستور زیر را اجرا کنید: python {os.path.basename(gradio_script_path)}")
print(f"رابط کاربری Gradio در پورت {GRADIO_PORT} در دسترس خواهد بود.")
print("---------------------------------------\n")
return True
def main():
"""
تابع اصلی برای اجرای مراحل پروژه.
"""
print("--- شروع فرآیند ساخت مدل رایژ ---")
# بررسی و نصب وابستگی ها
if not install_dependencies():
print("Could not install all dependencies. Exiting.")
return
# ایجاد داده های نمونه اگر وجود نداشته باشند
create_dummy_data()
# --- مراحل ساخت مدل ---
# شما می توانید این مراحل را با آرگومان های خط فرمان کنترل کنید.
# مثلا: python rayzh_ai.py --mode train --mode api
# فعلا همه را به ترتیب اجرا می کنیم.
# مرحله 1: آموزش مدل
# برای اجرای سریع تر، اگر مدل از قبل آموزش دیده باشد، می توانید این بخش را رد کنید.
# اما چون هدف "از صفر تا صد" است، آن را اجرا می کنیم.
# فرض می کنیم مرحله آموزش موفقیت آمیز است.
print("\n--- مرحله 1: آموزش مدل ---")
if not train_model():
print("Model training failed. Cannot proceed to API/Interface.")
return
print("--- مرحله 1: آموزش مدل تکمیل شد ---")
# مرحله 2: آماده سازی اسکریپت API
print("\n--- مرحله 2: آماده سازی اسکریپت API ---")
if not start_api():
print("Failed to prepare API script.")
# ما همچنان سعی می کنیم رابط کاربری را اجرا کنیم
print("--- مرحله 2: آماده سازی اسکریپت API تکمیل شد ---")
# مرحله 3: آماده سازی اسکریپت رابط کاربری Gradio
print("\n--- مرحله 3: آماده سازی اسکریپت رابط کاربری Gradio ---")
if not start_gradio_interface():
print("Failed to prepare Gradio interface script.")
print("--- مرحله 3: آماده سازی اسکریپت رابط کاربری Gradio تکمیل شد ---")
print("\n--- فرآیند ساخت مدل رایژ به پایان رسید ---")
print("اکنون می توانید اسکریپت های API و Gradio را در ترمینال های جداگانه اجرا کنید.")
print(f"برای API: python {os.path.basename(os.path.join(PROJECT_DIR, 'api_server.py'))}")
print(f"برای Gradio: python {os.path.basename(os.path.join(PROJECT_DIR, 'gradio_app.py'))}")
print("----------------------------------------------")
if __name__ == "__main__":
# این بخش به کاربر اجازه می دهد تا بخش های خاصی را اجرا کند
# مثلا: python rayzh_ai.py --mode install
# یا: python rayzh_ai.py --mode train
# یا: python rayzh_ai.py --mode api
# یا: python rayzh_ai.py --mode gradio
import argparse
parser = argparse.ArgumentParser(description="Rayzh AI Model Setup and Run Script")
parser.add_argument("--mode", type=str, default="all", choices=["install", "data", "train", "api", "gradio", "all"],
help="Mode to run: install, data, train, api, gradio, or all.")
args = parser.parse_args()
if args.mode == "install" or args.mode == "all":
if not install_dependencies():
sys.exit(1)
if args.mode == "data" or args.mode == "all":
create_dummy_data()
if args.mode == "train" or args.mode == "all":
if not train_model():
sys.exit(1)
if args.mode == "api" or args.mode == "all":
if not start_api():
sys.exit(1)
if args.mode == "gradio" or args.mode == "all":
if not start_gradio_interface():
sys.exit(1)
if args.mode == "all":
# اگر all انتخاب شده باشد، main() را فراخوانی می کنیم که همه چیز را انجام می دهد
# اما چون بخش های بالا به صورت جداگانه اجرا می شوند، اینجا کاری نمی کنیم.
pass
elif args.mode != "install" and args.mode != "data" and args.mode != "train" and args.mode != "api" and args.mode != "gradio":
print(f"Unknown mode: {args.mode}")
sys.exit(1)
print("Script finished.")