ErzhanAb commited on
Commit
af1fd59
·
verified ·
1 Parent(s): 7304485

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -33
app.py CHANGED
@@ -3,7 +3,7 @@ import joblib, json, re
3
  from html import unescape
4
 
5
  # -----------------------------
6
- # 1) Точная копия preprocessor
7
  # -----------------------------
8
  _URL_RE = re.compile(r'https?://\S+|www\.\S+')
9
  _TAG_RE = re.compile(r'[@#]\w+')
@@ -23,7 +23,7 @@ def clean_text(s: str) -> str:
23
  return s
24
 
25
  # ---------------------------------
26
- # 2) Загрузка пайплайна и конфига
27
  # ---------------------------------
28
  PIPE = joblib.load("model.joblib")
29
 
@@ -33,43 +33,118 @@ try:
33
  cfg = json.load(f)
34
  DEFAULT_THRESHOLD = float(cfg.get("threshold", DEFAULT_THRESHOLD))
35
  except Exception:
36
- pass # если файла нет — оставим дефолт 0.4
37
 
38
  # ---------------------------------
39
- # 3) Инференс
40
  # ---------------------------------
41
  def predict(comment: str, threshold: float):
42
- if comment is None or not str(comment).strip():
43
- return "Пустой ввод", 0.0
44
- proba = float(PIPE.predict_proba([comment])[0, 1]) # preprocessor внутри PIPE
45
- label = "Токсичный" if proba >= threshold else "Не токсичный"
46
- return label, round(proba, 4)
 
 
 
 
47
 
48
- DESCRIPTION = """
49
- Модель для классификации токсичных комментариев (русский язык).
50
- Архитектура: **TF-IDF (char_wb 4–5) + Logistic Regression (L1, class_weight=balanced)**.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  """
52
 
53
- demo = gr.Interface(
54
- fn=predict,
55
- inputs=[
56
- gr.Textbox(label="Комментарий", lines=4, placeholder="Введите текст на русском..."),
57
- gr.Slider(0.0, 1.0, value=DEFAULT_THRESHOLD, step=0.01, label="Порог классификации"),
58
- ],
59
- outputs=[
60
- gr.Textbox(label="Класс"),
61
- gr.Number(label="Вероятность токсичности"),
62
- ],
63
- title="Russian Toxic Comment Classifier — TF-IDF + Logistic Regression",
64
- description=DESCRIPTION,
65
- flagging_mode="never", # <-- новое API
66
- cache_examples=False, # <-- чтобы стартап не падал на кэше
67
- examples=[
68
- ["Ты полный идиот!", DEFAULT_THRESHOLD],
69
- ["Спасибо большое за помощь!", DEFAULT_THRESHOLD],
70
- ["Посмотри это <url> и скажи, что думаешь", DEFAULT_THRESHOLD],
71
- ],
72
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  if __name__ == "__main__":
75
- demo.launch()
 
3
  from html import unescape
4
 
5
  # -----------------------------
6
+ # 1) Точная копия preprocessor (без изменений)
7
  # -----------------------------
8
  _URL_RE = re.compile(r'https?://\S+|www\.\S+')
9
  _TAG_RE = re.compile(r'[@#]\w+')
 
23
  return s
24
 
25
  # ---------------------------------
26
+ # 2) Загрузка пайплайна и конфига (без изменений)
27
  # ---------------------------------
28
  PIPE = joblib.load("model.joblib")
29
 
 
33
  cfg = json.load(f)
34
  DEFAULT_THRESHOLD = float(cfg.get("threshold", DEFAULT_THRESHOLD))
35
  except Exception:
36
+ pass
37
 
38
  # ---------------------------------
39
+ # 3) Обновленный инференс для нового интерфейса
40
  # ---------------------------------
41
  def predict(comment: str, threshold: float):
42
+ """
43
+ Функция инференса, адаптированная для вывода в компонент gr.Label.
44
+ Возвращает словарь {метка: вероятность}.
45
+ """
46
+ if not comment or not comment.strip():
47
+ return None # Возвращаем None, чтобы очистить поле вывода
48
+
49
+ proba_toxic = float(PIPE.predict_proba([comment])[0, 1])
50
+ proba_not_toxic = 1 - proba_toxic
51
 
52
+ # gr.Label автоматически выделит класс с большей вероятностью,
53
+ # но мы также можем сделать это наглядно, сравнив с порогом.
54
+ # Для простоты и наглядности, вернем вероятности для обоих классов.
55
+ # Компонент gr.Label сам подсветит тот, у которого значение выше.
56
+ if proba_toxic >= threshold:
57
+ # Если превышен порог, то "Токсичный" должен быть основным результатом
58
+ return {"Токсичный": proba_toxic, "Не токсичный": proba_not_toxic}
59
+ else:
60
+ # Иначе - "Не токсичный"
61
+ return {"Не токсичный": proba_not_toxic, "Токсичный": proba_toxic}
62
+
63
+ # ---------------------------------
64
+ # 4) Новый стильный и минималистичный интерфейс на gr.Blocks
65
+ # ---------------------------------
66
+
67
+ # Описание выносим в отдельную переменную для чистоты
68
+ TITLE = "Анализатор токсичности комментариев"
69
+ DESCRIPTION = "Введите комментарий на русском языке, чтобы определить его токсичность. Модель вернет вероятность принадлежности к классу 'Токсичный'."
70
+ ARTICLE = """
71
+ ---
72
+ ### Детали модели
73
+ * **Архитектура**: TF-IDF (char_wb, n-граммы 4-5) + Логистическая регрессия (L1, class_weight=balanced).
74
+ * **Назначение**: Классификация русскоязычных текстов на 'Токсичный' и 'Не токсичный'.
75
+ * **Разработано для**: Демонстрации работы простой, но эффективной baseline-модели.
76
  """
77
 
78
+ # Используем gr.Blocks для кастомного дизайна
79
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky")) as demo:
80
+ # Заголовок и описание
81
+ gr.Markdown(f"# {TITLE}")
82
+ gr.Markdown(DESCRIPTION)
83
+
84
+ # Основная раскладка в две колонки
85
+ with gr.Row():
86
+ # Левая колонка для ввода
87
+ with gr.Column(scale=2):
88
+ comment_input = gr.Textbox(
89
+ label="Текст комментария",
90
+ lines=5,
91
+ placeholder="Например: Ты полный идиот!",
92
+ )
93
+
94
+ with gr.Row():
95
+ clear_btn = gr.Button("Очистить", variant="secondary")
96
+ analyze_btn = gr.Button("Анализ", variant="primary")
97
+
98
+ # Примеры для быстрого тестирования
99
+ gr.Examples(
100
+ examples=[
101
+ "Ты полный идиот!",
102
+ "Спасибо большое за помощь!",
103
+ "Посмотри это <url> и скажи, что думаешь",
104
+ "Что за бред ты несешь?",
105
+ "Отличная работа, продолжайте в том же духе!",
106
+ ],
107
+ inputs=comment_input,
108
+ )
109
+
110
+ # Правая колонка для вывода
111
+ with gr.Column(scale=1):
112
+ result_label = gr.Label(label="Результат", num_top_classes=2)
113
+
114
+ # Выпадающий блок с настройками
115
+ with gr.Accordion("Настройки", open=False):
116
+ threshold_slider = gr.Slider(
117
+ minimum=0.0,
118
+ maximum=1.0,
119
+ value=DEFAULT_THRESHOLD,
120
+ step=0.01,
121
+ label="Порог классификации",
122
+ info="Комментарий считается токсичным, если вероятность превышает это значение."
123
+ )
124
+
125
+ # Техническая информация о модели в самом низу
126
+ gr.Markdown(ARTICLE)
127
+
128
+ # --- Логика взаимодействия компонентов ---
129
+
130
+ # Функция для очистки полей
131
+ def clear_all():
132
+ return "", None # Очищает текстовое поле и поле с результатом
133
+
134
+ # Привязка функций к кнопкам и событиям
135
+ analyze_btn.click(
136
+ fn=predict,
137
+ inputs=[comment_input, threshold_slider],
138
+ outputs=result_label
139
+ )
140
+ # Также запускаем анализ по нажатию Enter в текстовом поле
141
+ comment_input.submit(
142
+ fn=predict,
143
+ inputs=[comment_input, threshold_slider],
144
+ outputs=result_label
145
+ )
146
+ clear_btn.click(fn=clear_all, inputs=[], outputs=[comment_input, result_label])
147
+
148
 
149
  if __name__ == "__main__":
150
+ demo.launch(debug=True) # debug=True помогает при отладке