Spaces:
Sleeping
Sleeping
models
#1
by ksmaru - opened
- .gitattributes +0 -1
- DejaVuSans.ttf +0 -3
- Dockerfile +0 -32
- README.md +1 -2
- app.py +136 -215
- requirements.txt +11 -10
.gitattributes
CHANGED
|
@@ -33,4 +33,3 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
-
DejaVuSans.ttf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
DejaVuSans.ttf
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:08ca98e69d9d8fa1065584b4f9ab7d49b6205abea6572b90e171b254845bb990
|
| 3 |
-
size 741536
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
# Используем Python 3.12 для стабильной работы numba и umap
|
| 2 |
-
FROM python:3.12-slim
|
| 3 |
-
|
| 4 |
-
# Устанавливаем системные зависимости для OpenCV, FFmpeg и сборки моделей
|
| 5 |
-
RUN apt-get update && apt-get install -y \
|
| 6 |
-
git \
|
| 7 |
-
git-lfs \
|
| 8 |
-
ffmpeg \
|
| 9 |
-
libsm6 \
|
| 10 |
-
libxext6 \
|
| 11 |
-
cmake \
|
| 12 |
-
rsync \
|
| 13 |
-
libgl1 \
|
| 14 |
-
&& rm -rf /var/lib/apt/lists/* \
|
| 15 |
-
&& git lfs install
|
| 16 |
-
|
| 17 |
-
# Создаем рабочую директорию
|
| 18 |
-
WORKDIR /app
|
| 19 |
-
|
| 20 |
-
# Сначала копируем только требования, чтобы использовать кэш Docker
|
| 21 |
-
COPY requirements.txt .
|
| 22 |
-
RUN pip install --no-cache-dir -U pip && \
|
| 23 |
-
pip install --no-cache-dir -r requirements.txt
|
| 24 |
-
|
| 25 |
-
# Копируем остальные файлы (app.py и модели .joblib)
|
| 26 |
-
COPY . .
|
| 27 |
-
|
| 28 |
-
# Открываем порт для Gradio (Hugging Face использует 7860)
|
| 29 |
-
EXPOSE 7860
|
| 30 |
-
|
| 31 |
-
# Запускаем приложение
|
| 32 |
-
CMD ["python", "app.py"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -5,10 +5,9 @@ colorFrom: indigo
|
|
| 5 |
colorTo: red
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.3.0
|
| 8 |
-
python_version: 3.12
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
license: mit
|
| 12 |
---
|
| 13 |
|
| 14 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 5 |
colorTo: red
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.3.0
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
---
|
| 12 |
|
| 13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
CHANGED
|
@@ -6,228 +6,149 @@ import numpy as np
|
|
| 6 |
import io
|
| 7 |
import joblib
|
| 8 |
import os
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
from fpdf.enums import XPos, YPos
|
| 12 |
-
import umap
|
| 13 |
-
|
| 14 |
-
# Безопасный импорт HDBSCAN
|
| 15 |
-
try:
|
| 16 |
-
import hdbscan
|
| 17 |
-
except ImportError:
|
| 18 |
-
try:
|
| 19 |
-
from sklearn.cluster import HDBSCAN as hdbscan
|
| 20 |
-
except ImportError:
|
| 21 |
-
hdbscan = None
|
| 22 |
-
|
| 23 |
-
# 1. Настройки среды и загрузка моделей
|
| 24 |
plt.switch_backend('Agg')
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
'
|
| 49 |
-
'
|
| 50 |
-
'
|
| 51 |
-
'LBDHDD': 'ЛПВП', 'LBXTR': 'Триглицериды', 'LBXCRP': 'СРБ',
|
| 52 |
-
'PHQ9_score': 'Депрессия (PHQ-9)', 'LBXBPB': 'Свинец',
|
| 53 |
-
'LBXBCD': 'Кадмий', 'LBXTHG': 'Ртуть', 'URXPHL': 'Фталаты',
|
| 54 |
-
'LBXIN': 'Инсулин', 'URXMEP': 'Моноэтилфталат'
|
| 55 |
}
|
|
|
|
| 56 |
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
pdf.ln(5)
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
# Регрессия AL Score
|
| 130 |
-
reg_features = xgb_reg.get_booster().feature_names
|
| 131 |
-
df_reg = pd.DataFrame(0.0, index=[0], columns=reg_features)
|
| 132 |
-
for c in reg_features:
|
| 133 |
-
if c in input_data: df_reg.at[0, c] = input_data[c]
|
| 134 |
-
al_val = float(xgb_reg.predict(df_reg)[0])
|
| 135 |
-
|
| 136 |
-
# Классификация Стадии
|
| 137 |
-
clf_features = xgb_clf.get_booster().feature_names
|
| 138 |
-
df_clf = pd.DataFrame(0.0, index=[0], columns=clf_features)
|
| 139 |
-
input_data['AL_score'] = al_val
|
| 140 |
-
for c in clf_features:
|
| 141 |
-
if c in input_data: df_clf.at[0, c] = input_data[c]
|
| 142 |
-
|
| 143 |
-
st_idx = int(xgb_clf.predict(df_clf)[0])
|
| 144 |
-
st_map = {0: "Норма", 1: "Начальная", 2: "Выраженная", 3: "Критическая"}
|
| 145 |
-
res_stage = st_map.get(st_idx, f"Стадия {st_idx}")
|
| 146 |
-
|
| 147 |
-
# SHAP
|
| 148 |
-
df_shap = df_reg.rename(columns=rename_dict)
|
| 149 |
-
explainer = shap.TreeExplainer(xgb_reg)
|
| 150 |
-
shap_vals = explainer.shap_values(df_reg)
|
| 151 |
-
plt.figure(figsize=(10, 3))
|
| 152 |
-
shap.force_plot(explainer.expected_value, shap_vals[0], df_shap.iloc[0], matplotlib=True, show=False)
|
| 153 |
-
buf_s = io.BytesIO()
|
| 154 |
-
plt.savefig(buf_s, format='png', bbox_inches='tight', dpi=120)
|
| 155 |
-
img_shap = Image.open(buf_s)
|
| 156 |
-
plt.close()
|
| 157 |
-
|
| 158 |
-
# UMAP (Исправленный блок)
|
| 159 |
-
img_umap = None
|
| 160 |
-
phenotype = "Фенотип не определен"
|
| 161 |
-
|
| 162 |
-
if umap_reducer is not None:
|
| 163 |
-
try:
|
| 164 |
-
# Автоматически определяем нужные колонки из модели, если train_features не загружен
|
| 165 |
-
expected_cols = getattr(umap_reducer, 'feature_names_in_', reg_features)
|
| 166 |
-
u_in = pd.DataFrame(0.0, index=[0], columns=expected_cols)
|
| 167 |
-
for col in u_in.columns:
|
| 168 |
-
if col in input_data: u_in.at[0, col] = input_data[col]
|
| 169 |
-
|
| 170 |
-
# Координаты
|
| 171 |
-
coords = umap_reducer.transform(u_in)[0]
|
| 172 |
-
|
| 173 |
-
# Отрисовка
|
| 174 |
-
plt.figure(figsize=(6, 4))
|
| 175 |
-
if train_coords is not None:
|
| 176 |
-
plt.scatter(train_coords[:, 0], train_coords[:, 1], c=train_labels, cmap='viridis', s=2, alpha=0.1)
|
| 177 |
-
plt.scatter(coords[0], coords[1], color='red', marker='X', s=150, label='Вы здесь')
|
| 178 |
-
plt.axis('off')
|
| 179 |
-
plt.title("Позиция в популяции")
|
| 180 |
-
|
| 181 |
-
buf_u = io.BytesIO()
|
| 182 |
-
plt.savefig(buf_u, format='png', bbox_inches='tight')
|
| 183 |
-
img_umap = Image.open(buf_u)
|
| 184 |
-
plt.close()
|
| 185 |
-
|
| 186 |
-
if hdbscan_model:
|
| 187 |
-
pid = hdbscan_model.predict([coords])[0]
|
| 188 |
-
phenotype = f"Фенотип здоровья №{pid}"
|
| 189 |
-
except Exception as e:
|
| 190 |
-
phenotype = f"Ошибка визуализации: {str(e)[:50]}"
|
| 191 |
-
|
| 192 |
-
recs = get_recommendations(al_val, res_stage, input_data)
|
| 193 |
-
pdf_path = create_report(f"{al_val:.2f}", res_stage, recs, img_shap, img_umap)
|
| 194 |
-
|
| 195 |
-
return f"{al_val:.2f}", res_stage, recs, phenotype, img_shap, img_umap, pdf_path
|
| 196 |
-
|
| 197 |
-
except Exception as e:
|
| 198 |
-
return f"Ошибка: {str(e)}", "---", "---", "---", None, None, None
|
| 199 |
-
|
| 200 |
-
# 5. Интерфейс
|
| 201 |
-
with gr.Blocks(title="PolySen Plus") as demo:
|
| 202 |
-
gr.Markdown("# 🏥 PolySen Plus: Мониторинг здоровья")
|
| 203 |
with gr.Row():
|
| 204 |
with gr.Column():
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
with gr.Row():
|
| 208 |
-
bmi = gr.Number(value=25.0, label="ИМТ")
|
| 209 |
-
waist = gr.Number(value=90.0, label="Талия (см)")
|
| 210 |
-
with gr.Row():
|
| 211 |
-
sbp = gr.Slider(80, 200, 120, label="Сист. АД")
|
| 212 |
-
dbp = gr.Slider(40, 120, 80, label="Диаст. АД")
|
| 213 |
-
with gr.Accordion("Дополнительные биомаркеры", open=False):
|
| 214 |
-
gh, tc, hdl, tg, crp = [gr.Number(v, label=l) for v, l in [(5.5, "HbA1c"), (5.0, "Общий Холест."), (1.2, "ЛПВП"), (1.5, "ТГ"), (1.0, "СРБ")]]
|
| 215 |
-
phq = gr.Slider(0, 27, 5, label="PHQ-9 (Депрессия)")
|
| 216 |
-
l, c, m, p = [gr.Number(v, label=n) for v, n in [(1.0, "Свинец"), (0.3, "Кадмий"), (0.5, "Ртуть"), (5.0, "Фталаты")]]
|
| 217 |
-
btn = gr.Button("🚀 Провести анализ", variant="primary")
|
| 218 |
-
|
| 219 |
with gr.Column():
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
import io
|
| 7 |
import joblib
|
| 8 |
import os
|
| 9 |
+
|
| 10 |
+
# Установка бэкенда matplotlib
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
plt.switch_backend('Agg')
|
| 12 |
+
|
| 13 |
+
# Загрузка моделей
|
| 14 |
+
model_dir = './models'
|
| 15 |
+
xgb_reg_model = joblib.load(os.path.join(model_dir, 'xgb_reg_model.joblib'))
|
| 16 |
+
xgb_clf_model = joblib.load(os.path.join(model_dir, 'xgb_clf_model.joblib'))
|
| 17 |
+
umap_reducer = joblib.load(os.path.join(model_dir, 'umap_reducer.joblib'))
|
| 18 |
+
hdbscan_clusterer = joblib.load(os.path.join(model_dir, 'hdbscan_clusterer.joblib'))
|
| 19 |
+
|
| 20 |
+
# Определение колонок (ВАЖНО: должны совпадать с обучением)
|
| 21 |
+
feature_columns = [
|
| 22 |
+
'RIDAGEYR', 'RIAGENDR', 'BMXBMI', 'BMXWAIST', 'BPXSY1', 'BPXDI1',
|
| 23 |
+
'LBXGH', 'LBXHSCRP', 'LBDTCSI', 'LBDHDD', 'LBXIN',
|
| 24 |
+
'SLQ050', 'HSQ510', 'PHQ9_score',
|
| 25 |
+
'LBXBPB', 'LBXBCD', 'LBXTHG', 'URXMHH'
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
# Списки признаков для конкретных моделей
|
| 29 |
+
X_reg_features_cols = feature_columns
|
| 30 |
+
X_clf_features_cols_without_AL_score = feature_columns
|
| 31 |
+
|
| 32 |
+
# Медианы для заполнения пропусков
|
| 33 |
+
x_train_medians_values = {
|
| 34 |
+
'RIDAGEYR': 31.0, 'RIAGENDR': 2.0, 'BMXBMI': 25.8, 'BMXWAIST': 91.2, 'BPXSY1': 118.0,
|
| 35 |
+
'BPXDI1': 70.0, 'LBXGH': 5.5, 'LBXHSCRP': 1.35, 'LBDTCSI': 4.55, 'LBDHDD': 51.0,
|
| 36 |
+
'SLQ050': 2.0, 'HSQ510': 2.0, 'LBXIN': 10.04, 'PHQ9_score': 0.0,
|
| 37 |
+
'LBXBPB': 0.76, 'LBXBCD': 0.22, 'LBXTHG': 0.51, 'URXMHH': 5.5
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
}
|
| 39 |
+
X_train_medians = pd.Series(x_train_medians_values)
|
| 40 |
|
| 41 |
+
thresholds_recommendation = {
|
| 42 |
+
'LBXHSCRP': 2.71, 'SLQ050': 2.0, 'PHQ9_score': 2.0, 'HSQ510': 2.0,
|
| 43 |
+
'LBXBPB': 1.07, 'LBXBCD': 0.35, 'LBXTHG': 0.91, 'URXMHH': 5.5
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
def generate_recommendations(participant_data):
|
| 47 |
+
recommendations = []
|
| 48 |
+
stage = participant_data.get('stage_label', 0)
|
| 49 |
+
|
| 50 |
+
if stage == 3.0:
|
| 51 |
+
recommendations.append("🔴 Уровень адаптационной нагрузки крайне высокий. Необходима немедленная консультация врача.")
|
| 52 |
+
elif stage == 2.0:
|
| 53 |
+
recommendations.append("🟠 Уровень адаптационной нагрузки высокий. Рекомендует��я изменение образа жизни.")
|
| 54 |
+
elif stage == 1.0:
|
| 55 |
+
recommendations.append("🟡 Наблюдается повышенная адаптационная нагрузка. Следите за здоровьем.")
|
| 56 |
+
|
| 57 |
+
if participant_data.get('LBXHSCRP', 0) > thresholds_recommendation['LBXHSCRP']:
|
| 58 |
+
recommendations.append(f"⚠️ Повышен уровень C-реактивного белка ({participant_data['LBXHSCRP']:.2f}).")
|
| 59 |
+
|
| 60 |
+
# Сбор симптомов
|
| 61 |
+
symptoms = []
|
| 62 |
+
if participant_data.get('SLQ050', 0) > thresholds_recommendation['SLQ050']: symptoms.append("сонливость")
|
| 63 |
+
if participant_data.get('PHQ9_score', 0) > thresholds_recommendation['PHQ9_score']: symptoms.append("высокий стресс (PHQ-9)")
|
| 64 |
|
| 65 |
+
if symptoms:
|
| 66 |
+
recommendations.append(f"💡 Обратите внимание на: {', '.join(symptoms)}.")
|
| 67 |
+
|
| 68 |
+
if not recommendations:
|
| 69 |
+
recommendations.append("✅ Ваши показатели в норме.")
|
| 70 |
+
|
| 71 |
+
return recommendations
|
| 72 |
+
|
| 73 |
+
def predict_and_recommend(*args):
|
| 74 |
+
# Создание DataFrame
|
| 75 |
+
input_df = pd.DataFrame([args], columns=feature_columns)
|
| 76 |
+
|
| 77 |
+
# Заполнение пропусков
|
| 78 |
+
for col in feature_columns:
|
| 79 |
+
if pd.isna(input_df[col]).any():
|
| 80 |
+
input_df[col] = input_df[col].fillna(X_train_medians.get(col, 0))
|
| 81 |
+
|
| 82 |
+
# Предсказание AL Score
|
| 83 |
+
predicted_al_score = xgb_reg_model.predict(input_df[X_reg_features_cols])[0]
|
| 84 |
+
|
| 85 |
+
# Подготовка для классификатора и UMAP
|
| 86 |
+
input_df_clf = input_df[X_clf_features_cols_without_AL_score].copy()
|
| 87 |
+
input_df_clf.insert(0, 'AL_score', predicted_al_score)
|
| 88 |
+
|
| 89 |
+
predicted_stage_label = xgb_clf_model.predict(input_df_clf)[0]
|
| 90 |
|
| 91 |
+
# UMAP и Кластеризация
|
| 92 |
+
umap_emb = umap_reducer.transform(input_df_clf)
|
| 93 |
+
cluster = hdbscan_clusterer.predict(umap_emb)[0]
|
| 94 |
|
| 95 |
+
# SHAP
|
| 96 |
+
explainer = shap.TreeExplainer(xgb_reg_model)
|
| 97 |
+
shap_vals = explainer.shap_values(input_df[X_reg_features_cols])
|
|
|
|
| 98 |
|
| 99 |
+
plt.figure(figsize=(10, 3))
|
| 100 |
+
shap.force_plot(explainer.expected_value, shap_vals[0], input_df.iloc[0], matplotlib=True, show=False)
|
| 101 |
+
buf = io.BytesIO()
|
| 102 |
+
plt.savefig(buf, format='png', bbox_inches='tight')
|
| 103 |
+
plt.close()
|
| 104 |
+
|
| 105 |
+
# Рекомендации
|
| 106 |
+
diag_data = input_df.iloc[0].to_dict()
|
| 107 |
+
diag_data['stage_label'] = predicted_stage_label
|
| 108 |
+
recs = generate_recommendations(diag_data)
|
| 109 |
+
|
| 110 |
+
return (
|
| 111 |
+
round(float(predicted_al_score), 2),
|
| 112 |
+
int(predicted_stage_label),
|
| 113 |
+
f"{umap_emb[0,0]:.2f}, {umap_emb[0,1]:.2f}",
|
| 114 |
+
str(cluster),
|
| 115 |
+
buf.getvalue(),
|
| 116 |
+
"\n".join(recs)
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
# Описание интерфейса Gradio
|
| 120 |
+
feature_labels = {
|
| 121 |
+
'RIDAGEYR': 'Возраст', 'RIAGENDR': 'Пол (1=М, 2=Ж)', 'BMXBMI': 'ИМТ',
|
| 122 |
+
'BMXWAIST': 'Талия', 'BPXSY1': 'Сист. АД', 'BPXDI1': 'Диаст. АД',
|
| 123 |
+
'LBXGH': 'HbA1c', 'LBXHSCRP': 'hsCRP', 'LBDTCSI': 'Холестерин',
|
| 124 |
+
'LBDHDD': 'ЛПВП', 'LBXIN': 'Инсулин', 'SLQ050': 'Сонливость',
|
| 125 |
+
'HSQ510': 'Боль', 'PHQ9_score': 'PHQ-9', 'LBXBPB': 'Свинец',
|
| 126 |
+
'LBXBCD': 'Кадмий', 'LBXTHG': 'Ртуть', 'URXMHH': 'Фталаты'
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
with gr.Blocks() as demo:
|
| 130 |
+
gr.Markdown("# Система оценки адаптационной нагрузки")
|
| 131 |
+
|
| 132 |
+
inputs = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
with gr.Row():
|
| 134 |
with gr.Column():
|
| 135 |
+
for col in feature_columns[:9]:
|
| 136 |
+
inputs.append(gr.Number(label=feature_labels[col], value=X_train_medians[col]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
with gr.Column():
|
| 138 |
+
for col in feature_columns[9:]:
|
| 139 |
+
inputs.append(gr.Number(label=feature_labels[col], value=X_train_medians[col]))
|
| 140 |
+
|
| 141 |
+
btn = gr.Button("Рассчитать")
|
| 142 |
+
|
| 143 |
+
with gr.Row():
|
| 144 |
+
al_out = gr.Textbox(label="AL Score")
|
| 145 |
+
stage_out = gr.Textbox(label="Стадия")
|
| 146 |
+
|
| 147 |
+
coords_out = gr.Textbox(label="UMAP Координаты")
|
| 148 |
+
cluster_out = gr.Textbox(label="Кластер")
|
| 149 |
+
shap_out = gr.Image(label="Влияние признаков (SHAP)")
|
| 150 |
+
recs_out = gr.Textbox(label="Рекомендации", lines=5)
|
| 151 |
+
|
| 152 |
+
btn.click(predict_and_recommend, inputs=inputs, outputs=[al_out, stage_out, coords_out, cluster_out, shap_out, recs_out])
|
| 153 |
+
|
| 154 |
+
demo.launch()
|
requirements.txt
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
scikit-learn==1.
|
| 4 |
-
xgboost==
|
| 5 |
-
umap-learn==0.5.
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
| 1 |
+
pandas>=2.2.3
|
| 2 |
+
numpy>=2.1.0
|
| 3 |
+
scikit-learn==1.6.1
|
| 4 |
+
xgboost==3.1.2
|
| 5 |
+
umap-learn==0.5.9.post2
|
| 6 |
+
hdbscan==0.8.41
|
| 7 |
+
shap==0.50.0
|
| 8 |
+
gradio==5.50.0
|
| 9 |
+
matplotlib==3.10.0
|
| 10 |
+
seaborn==0.13.2
|
| 11 |
+
joblib==1.5.3
|