This commit is contained in:
xds
2025-09-30 23:43:51 +04:00
commit 6f744b8066
7 changed files with 2327 additions and 0 deletions

44
api.py Normal file
View File

@@ -0,0 +1,44 @@
import asyncio
import json
import pickle
from fastapi import FastAPI
import deepseek_module
import nlp_processor
import nlp_teacher
app = FastAPI(title="NLP API", version="1.0")
# Загрузить сохраненную модель
@app.get('/predict')
async def predict(req, cloud:int=0, async_op: int=0):
print('here')
# Получить входные данные из запроса
if cloud==1:
if async_op==1:
response_msg = await deepseek_module.nlp_yandex(req)
else:
response_msg = deepseek_module.fast_nlp_yandex(req)
print(response_msg)
return json.loads(response_msg.encode('latin1').decode('utf-8'))
else:
# Сделать предсказание модели
prediction = nlp_processor.predict_category(req)
# Вернуть предсказание в виде JSON
return prediction
@app.get('/reteach')
async def reteach():
print(nlp_teacher.reteach())
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, port=8000)

169
deepseek_module.py Normal file
View File

@@ -0,0 +1,169 @@
import asyncio
import datetime
import json
import logging
import time
from yandex_cloud_ml_sdk import YCloudML
from deepseek import DeepSeekAPI
# Инициализация клиентов
sdk = YCloudML(
folder_id="b1g9ebhd2j6dapolmspn",
auth="t1.9euelZqOxsiNmczGlYmVjZqPnciemu3rnpWamMbLiY7Ix8zHyMvPlZGJmc3l9PcLQDFA-e93IjGb3fT3S24uQPnvdyIxm83n9euelZqSyo6cyJ2PnMjInceWmI6QnO_8xeuelZqSyo6cyJ2PnMjInceWmI6QnA.73RaOj7lFKgEkAFnhrpy1LkS3EvH-hYeBPZ1mX43hyPgSCZ4ShrVFjarra1xCHGpRA3F_Z576viaMyg6UXR-BQ"
)
api_client = DeepSeekAPI("sk-2a2045f744474426b03fc18e30e9f561")
async def nlp_yandex(question: str) -> str:
"""Асинхронный запрос к Yandex GPT"""
start_time = datetime.datetime.now()
try:
model = sdk.models.completions("yandexgpt", model_version="rc")
model.configure(response_format="json", temperature=1)
operation = await model._run_deferred([
{
"role": "system",
"text": "Ты используешься как nlp процессор. Твоя задача по заданному комментарию траты понять в какую категорию эта трата относится. "
"Ответ предположением 3х категории с рассчитанным весом в формате json [{'category': 'cat1', 'weight': 0.5}, ...]."
"Используй категории из данных."
},
{
"role": "assistant",
"text": "Aвто,Аптеки,Дни рождения и подарки,Другое,ЖКХ,Интернет и связь,Кофе,Красота,Кредиты и долги,Мебель,Медицина,Одежда,Продукты,Прочие услуги,Путешествия,Развлечения,Ребенок,Рестораны и кафе,Сбережения,Спорт,Транспорт,Электроника"
},
{"role": "user", "text": question},
])
while operation.get_status().is_running:
print(operation.get_status())
await asyncio.sleep(0.1)
result = operation.get_result()
response_msg = "".join(alt.text for alt in result)
print(f"Yandex NLP time: {datetime.datetime.now() - start_time}")
return response_msg.replace('```', '')
except Exception as e:
logging.error(f"Yandex NLP error: {str(e)}")
return "Ошибка обработки запроса"
def fast_nlp_yandex(question: str) -> str:
"""Cинхронный запрос к Yandex GPT"""
start_time = datetime.datetime.now()
try:
model = sdk.models.completions("yandexgpt", model_version="rc")
model.configure(response_format="json", temperature=1)
result = model.run([
{
"role": "system",
"text": "Ты используешься как nlp процессор. Твоя задача по заданному комментарию траты понять в какую категорию эта трата относится. "
"Ответ предположением 3х категории с рассчитанным весом в формате json [{'category': 'cat1', 'weight': 0.5}, ...]."
"Используй категории из данных."
},
{
"role": "assistant",
"text": "Aвто,Аптеки,Дни рождения и подарки,Другое,ЖКХ,Интернет и связь,Кофе,Красота,Кредиты и долги,Мебель,Медицина,Одежда,Продукты,Прочие услуги,Путешествия,Развлечения,Ребенок,Рестораны и кафе,Сбережения,Спорт,Транспорт,Электроника"
},
{"role": "user", "text": question},
])
response_msg = "".join(alt.text for alt in result)
print(f"Sync Yandex NLP time: {datetime.datetime.now() - start_time}")
return response_msg.replace('```', '')
except Exception as e:
logging.error(f"Yandex NLP error: {str(e)}")
return "Ошибка обработки запроса"
def nlp_cloud(question: str) -> str:
"""Запрос к DeepSeek API"""
start_time = datetime.datetime.now()
try:
prompt = [
{
"role": "system",
"content": "Ты используешься как nlp процессор. Твоя задача по заданному комментарию траты понять в какую категорию эта трата относится. "
"Ответ предположением как минимум 3х категории с рассчитанным весом в формате json [{'id': 'id1', 'weight': 0.5}, ...]. "
"Используй ТОЛЬКО категории из данных."
},
{"role": "user", "content": question},
{
"role": "assistant",
"content": "Aвто(id=677bc767c7857460a491bd52),Аптеки(id=677bc767c7857460a491bd59),Дни рождения и подарки(id=677bc767c7857460a491bd54),Другое(id=67b83e141fc0575a3f0a383f),ЖКХ(id=677bc767c7857460a491bd4b),Интернет и связь(id=677bc767c7857460a491bd53),Кофе(id=677bc767c7857460a491bd4e),Красота(id=677bc767c7857460a491bd50),Кредиты и долги(id=677bc767c7857460a491bd49),Мебель(id=677bc767c7857460a491bd4c),Медицина(id=677bc767c7857460a491bd5a),Одежда(id=677bc767c7857460a491bd5b),Продукты(id=677bc767c7857460a491bd4a),Прочие услуги(id=677bc767c7857460a491bd4d),Путешествия(id=677bc767c7857460a491bd56),Развлечения(id=677bc767c7857460a491bd58),Ребенок(id=677bc767c7857460a491bd5d),Рестораны и кафе(id=677bc767c7857460a491bd55),Сбережения(id=677bc767c7857460a491bd4f),Спорт(id=677bc767c7857460a491bd57),Транспорт(id=677bc767c7857460a491bd51),Электроника(id=677bc767c7857460a491bd5c)"
},
]
response = ""
for chunk in api_client.chat_completion(prompt=prompt, stream=True):
response += chunk
# Очистка и парсинг JSON
clean_response = response.replace('```json', '').replace('```', '').strip()
try:
parsed = json.loads(clean_response)
print(f"DeepSeek NLP time: {datetime.datetime.now() - start_time}")
return json.dumps(parsed, ensure_ascii=False, indent=2)
except json.JSONDecodeError:
return clean_response
except Exception as e:
logging.error(f"DeepSeek NLP error: {str(e)}")
return json.dumps({"error": str(e)})
def generate_spends():
"""Запрос к DeepSeek API"""
start_time = datetime.datetime.now()
try:
prompt = [
{
"role": "system",
"content": "Ты используешься для генерации виртуальных трат, для последующего обучения NLP модели. "
"Тебе даны категории с их идентификаторами. По запросу сгенерируй 50 человеческих транзакций."
"Используй ТОЛЬКО категории из данных.Ответь в формате csv id, comment, category"
},
{"role": "user", "content": "Давай"},
{
"role": "assistant",
"content": "Aвто(id=677bc767c7857460a491bd52),Аптеки(id=677bc767c7857460a491bd59),Дни рождения и подарки(id=677bc767c7857460a491bd54),Другое(id=67b83e141fc0575a3f0a383f),ЖКХ(id=677bc767c7857460a491bd4b),Интернет и связь(id=677bc767c7857460a491bd53),Кофе(id=677bc767c7857460a491bd4e),Красота(id=677bc767c7857460a491bd50),Кредиты и долги(id=677bc767c7857460a491bd49),Мебель(id=677bc767c7857460a491bd4c),Медицина(id=677bc767c7857460a491bd5a),Одежда(id=677bc767c7857460a491bd5b),Продукты(id=677bc767c7857460a491bd4a),Прочие услуги(id=677bc767c7857460a491bd4d),Путешествия(id=677bc767c7857460a491bd56),Развлечения(id=677bc767c7857460a491bd58),Ребенок(id=677bc767c7857460a491bd5d),Рестораны и кафе(id=677bc767c7857460a491bd55),Сбережения(id=677bc767c7857460a491bd4f),Спорт(id=677bc767c7857460a491bd57),Транспорт(id=677bc767c7857460a491bd51),Электроника(id=677bc767c7857460a491bd5c)"
},
]
response = ""
for chunk in api_client.chat_completion(prompt=prompt, stream=True):
response += chunk
# Очистка и парсинг JSON
clean_response = response.replace('```json', '').replace('```', '').strip()
try:
parsed = json.loads(clean_response)
print(f"DeepSeek process time: {datetime.datetime.now() - start_time}")
return json.dumps(parsed, ensure_ascii=False, indent=2)
except json.JSONDecodeError:
return clean_response
except Exception as e:
logging.error(f"DeepSeek NLP error: {str(e)}")
return json.dumps({"error": str(e)})
async def main():
# Тестирование функций
# req = 'мойка'
# yandex_result = fast_nlp_yandex(req)
# print("Yandex:", yandex_result)
#
# cloud_result = nlp_cloud(req)
# print("DeepSeek:", cloud_result)
print( generate_spends())
if __name__ == "__main__":
asyncio.run(main())

BIN
model.pkl Normal file

Binary file not shown.

59
nlp_processor.py Normal file
View File

@@ -0,0 +1,59 @@
import logging
import joblib
import nltk
import numpy as np
from nltk import word_tokenize
from nltk.corpus import stopwords
nltk.download('stopwords')
nltk.download('punkt_tab')
model, vectorizer = joblib.load('model.pkl')
def model_reload():
print('Reloading the model')
global model, vectorizer
model, vectorizer = joblib.load('model.pkl')
def preprocess_comment(comment):
tokens = word_tokenize(comment)
stop_words = set(stopwords.words('russian'))
filtered_tokens = [t for t in tokens if t.lower() not in stop_words]
return ' '.join(filtered_tokens)
def comment_to_vector(comment):
vector = vectorizer.transform([comment])
return vector
# Определение категории по комменту
def predict_category(comment):
# Преобразуем текст в вектор
vector = vectorizer.transform([comment])
# Получаем "вероятности" через decision function
decision_scores = model.decision_function(vector)
# Преобразуем scores в "псевдо-вероятности" через softmax
exp_scores = np.exp(decision_scores - np.max(decision_scores))
probabilities = exp_scores / np.sum(exp_scores)
# Получаем топ-3 категорий
top_3_indices = np.argsort(probabilities[0])[::-1][:3]
top_3_categories = [
{'category': model.classes_[i].encode('latin1').decode('utf-8'), 'weight': float(probabilities[0][i])}
for i in top_3_indices
]
return top_3_categories
# Тестирование
comment = "ремешок часы" # Пример комментария
result = predict_category(comment)
for item in result:
print(f"Категория: {item['category']}, Вес: {item['weight']:.4f}")

119
nlp_teacher.py Normal file
View File

@@ -0,0 +1,119 @@
import re
import sys
from io import StringIO
import joblib
import nltk
import pandas as pd
import requests
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from pymorphy2 import MorphAnalyzer
from requests.compat import chardet
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVC
import nlp_processor
nltk.download('stopwords')
nltk.download('punkt_tab')
morph = MorphAnalyzer()
def fix_encoding(text):
if isinstance(text, str):
try:
# Преобразуем неправильно декодированные строки
return text.encode('latin1').decode('utf-8')
except:
return text
return text
# Загрузка данных
def reteach() -> str:
# URL вашего API, возвращающего CSV
api_url = "https://luminic.space/api/spaces/67af3c0f652da946a7dd9931/transactions/csv"
headers = {
"Authorization": "Bearer eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJ2b3JvbmludiIsImlhdCI6MTc0NDAzMDk5MywiZXhwIjoxNzQ0ODk0OTkzfQ.mybRBMsjPfJ8_kbDfJiPSr8UOIWS6WYddSFptplBTIfnPQm9SsdH3ixlEM-UY0CQ",
}
# Отправляем GET-запрос
response = requests.get(api_url, headers=headers)
print(response)
# Проверяем успешность запроса
if response.status_code == 200:
encoding = chardet.detect(response.content)['encoding']
print(f"Detected encoding: {encoding}")
# Используем StringIO для преобразования текста в файлоподобный объект
csv_data = StringIO(response.text)
# Читаем CSV в DataFrame
data = pd.read_csv(csv_data)
# result = chardet.detect(data["comment"])
# print(result)
print(data.head(3))
# Токенизация и удаление стоп-слов
stop_words = set(stopwords.words('russian'))
def tokenize_text(text):
text = re.sub(r'\d+([.,]\d+)?\s*(р|руб|коп)?', '', text)
# Лемматизация с обработкой денежных единиц
tokens = []
for token in word_tokenize(text.lower()):
if token in {'$', '', ''}:
continue
parsed = morph.parse(token)[0]
if parsed.tag.POS in {'NOUN', 'VERB', 'ADJ', 'ADV'}:
tokens.append(parsed.normal_form)
return ' '.join([t for t in tokens if t not in stop_words and len(t) > 2])
data['comment'] = data['comment'].apply(fix_encoding)
print(data.head(3))
data['processed_comment'] = data['comment'].apply(tokenize_text)
# Нормализация и удаление лишних символов
data['processed_comment'] = data['processed_comment'].apply(lambda x: x.lower())
data['processed_comment'] = data['processed_comment'].apply(lambda x: x.replace(',', ''))
data['processed_comment'] = data['processed_comment'].apply(lambda x: x.replace('.', ''))
# Преобразование текста в числовой вектор
vectorizer = TfidfVectorizer(
ngram_range=(1, 3),
max_features=15000,
min_df=2,
max_df=0.8,
analyzer='char_wb', # Важно для опечаток!
sublinear_tf=True
)
X = vectorizer.fit_transform(data['processed_comment'])
# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, data['category'], test_size=0.2, random_state=42)
# Обучение модели классификации
model = LinearSVC()
model.fit(X_train, y_train)
# Оценка модели
y_pred = model.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.2f}")
# Сохранение модели в файл
# Сохранение модели в файл
joblib.dump((model, vectorizer), 'model.pkl')
nlp_processor.model_reload()
return accuracy_score(y_test, y_pred)

1872
pojos.csv Normal file

File diff suppressed because it is too large Load Diff

64
requirements.txt Normal file
View File

@@ -0,0 +1,64 @@
aiofiles==24.1.0
annotated-types==0.7.0
anyio==4.9.0
asgiref==3.8.1
blinker==1.9.0
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
cryptography==44.0.2
DAWG-Python==0.7.2
deepseek==1.0.0
Deprecated==1.2.18
distro==1.9.0
docopt==0.6.2
exceptiongroup==1.2.2
fastapi==0.115.12
Flask==3.1.0
get-annotations==0.1.2
googleapis-common-protos==1.69.2
grpcio==1.71.0
grpcio-tools==1.71.0
h11==0.14.0
httpcore==1.0.7
httpx==0.28.1
idna==3.10
importlib_metadata==8.6.1
itsdangerous==2.2.0
Jinja2==3.1.6
jiter==0.9.0
joblib==1.4.2
MarkupSafe==3.0.2
nltk==3.9.1
numpy==2.0.2
openai==1.70.0
pandas==2.2.3
protobuf==5.29.4
pycparser==2.22
pydantic==2.11.2
pydantic_core==2.33.1
PyJWT==2.10.1
pymorphy2==0.9.1
pymorphy2-dicts-ru==2.4.417127.4579844
python-dateutil==2.9.0.post0
pytz==2025.2
regex==2024.11.6
requests==2.32.3
scikit-learn==1.6.1
scipy==1.13.1
six==1.17.0
sniffio==1.3.1
starlette==0.46.1
threadpoolctl==3.6.0
tqdm==4.67.1
typing-inspection==0.4.0
typing_extensions==4.13.1
tzdata==2025.2
urllib3==2.3.0
uvicorn==0.34.0
Werkzeug==3.1.3
wrapt==1.17.2
yandex-cloud-ml-sdk==0.7.0
yandexcloud==0.337.0
zipp==3.21.0