init
This commit is contained in:
44
api.py
Normal file
44
api.py
Normal 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
169
deepseek_module.py
Normal 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())
|
||||
59
nlp_processor.py
Normal file
59
nlp_processor.py
Normal 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
119
nlp_teacher.py
Normal 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)
|
||||
64
requirements.txt
Normal file
64
requirements.txt
Normal 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
|
||||
Reference in New Issue
Block a user