root39058 commited on
Commit
6817bc7
·
verified ·
1 Parent(s): c396e17

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +291 -223
app.py CHANGED
@@ -4,14 +4,14 @@ import numpy as np
4
  import torch
5
  import torch.nn as nn
6
  import torch.nn.functional as F
7
- from transformers import AutoTokenizer, AutoModel, pipeline
 
8
  from sentence_transformers import SentenceTransformer
9
  import time
10
  from datetime import datetime
11
  import json
12
  import os
13
  import pickle
14
- import random
15
  import re
16
  import warnings
17
  warnings.filterwarnings('ignore')
@@ -24,10 +24,15 @@ print(f"CUDA доступна: {torch.cuda.is_available()}")
24
  if torch.cuda.is_available():
25
  print(f"GPU: {torch.cuda.get_device_name(0)}")
26
 
27
- MODEL_NAME = "sberbank-ai/rugpt3small_based_on_gpt2"
 
28
  EMBEDDING_MODEL = "all-MiniLM-L6-v2"
29
  SCIENCE_DATASET = "RafaelUI/ru_science"
30
- ARTICLE_LIMIT = 50
 
 
 
 
31
 
32
  AI_NAME = "OpenAirAI"
33
  COMPANY_NAME = "OpenRussianAI"
@@ -37,119 +42,233 @@ HUGGINGFACE = "https://huggingface.co/OpenRussianAI"
37
  CREATION_DATE = "2026"
38
 
39
  st.set_page_config(
40
- page_title=f"{AI_NAME} - Научный AI-ассистент",
41
  page_icon="🧠",
42
  layout="wide",
43
  initial_sidebar_state="expanded"
44
  )
45
 
46
  # ===================================================================
47
- # 2. НЕЙРОСЕТЬ (БЕЗ IF/ELSE)
48
  # ===================================================================
49
 
50
- class NeuralChatbot:
51
- def __init__(self):
52
- self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
53
- self.tokenizer = None
54
- self.model = None
55
- self.generator = None
56
- self.is_loaded = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- # ЕДИНЫЙ СИСТЕМНЫЙ ПРОМПТ
59
- self.system_prompt = f"""Ты - {AI_NAME}, нейросетевой AI-ассистент от компании {COMPANY_NAME}.
60
- Ты создан в {CREATION_DATE} командой {', '.join(CREATORS)}.
61
- Ты всегда отвечаешь на русском языке, дружелюбно и профессионально.
62
- Ты помогаешь с любыми вопросами: наука, технологии, образование, математика.
63
- Ты генерируешь уникальные ответы каждый раз, не используя шаблоны.
64
- Ты не используешь if/else в своих ответах, только нейросетевая генерация.
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- Вопрос пользователя: """
 
 
 
 
 
67
 
68
- def load_model(self):
69
- with st.spinner("🧠 Загружаю нейросеть..."):
70
- try:
71
- from transformers import GPT2LMHeadModel, GPT2Tokenizer
72
-
73
- self.tokenizer = GPT2Tokenizer.from_pretrained(MODEL_NAME)
74
- self.model = GPT2LMHeadModel.from_pretrained(MODEL_NAME)
75
- self.model.to(self.device)
76
- self.model.eval()
77
-
78
- self.generator = pipeline(
79
- 'text-generation',
80
- model=self.model,
81
- tokenizer=self.tokenizer,
82
- device=0 if torch.cuda.is_available() else -1,
83
- max_length=400,
84
- temperature=0.85,
85
- top_p=0.95,
86
- do_sample=True,
87
- repetition_penalty=1.2,
88
- pad_token_id=self.tokenizer.eos_token_id
89
- )
90
-
91
- self.is_loaded = True
92
- return True
93
- except Exception as e:
94
- st.error(f"Ошибка загрузки: {e}")
95
- return False
96
 
97
- def generate(self, query):
98
- """ГЕНЕРИРУЕТ ВСЕГДА, БЕЗ IF/ELSE"""
99
- # Очищаем запрос
100
- clean_q = self.clean_query(query)
101
 
102
- if not self.is_loaded:
103
- return f"Я {AI_NAME} от {COMPANY_NAME}. Нейросеть временно недоступна. Попробуйте позже. 🧠"
 
 
 
 
 
 
104
 
105
- try:
106
- # Формируем промпт
107
- prompt = self.system_prompt + clean_q
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- # Генерируем ответ
110
- response = self.generator(
111
- prompt,
112
- max_new_tokens=300,
113
- temperature=0.85,
114
- top_p=0.95,
115
- do_sample=True,
116
- repetition_penalty=1.2
117
- )[0]['generated_text']
118
 
119
- # Убираем промпт
120
- response = response.replace(prompt, "").strip()
 
 
 
121
 
122
- # Если ответ пустой - генерируем общий
123
- if len(response) < 10:
124
- return f"Я {AI_NAME} от {COMPANY_NAME}. Я помогаю с научными вопросами, технологиями и образованием. Что именно вас интересует? 🧠"
 
125
 
126
- return response
127
 
128
- except Exception as e:
129
- return f"Я {AI_NAME}. Произошла ошибка при генерации. Попробуйте переформулировать вопрос. 🧠"
 
 
 
 
 
 
 
 
 
 
130
 
131
- def clean_query(self, query):
132
- """Очистка от спама"""
133
- query = re.sub(r'http[s]?://\S+', '', query)
134
- query = re.sub(r'\S+@\S+', '', query)
135
- query = re.sub(r'\+7\s*\(?\d{3}\)?\s*\d{3}\s*\d{2}\s*\d{2}', '', query)
136
- query = re.sub(r'[^\w\s\.\?\!,;:-]', ' ', query)
137
- query = ' '.join(query.split())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- if len(query) > 300:
140
- sentences = re.split(r'[.!?]', query)
141
- query = '. '.join([s for s in sentences[:2] if len(s) > 10]) + '.'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- return query.strip() if query else "Расскажи о себе"
 
 
 
144
 
145
  # ===================================================================
146
- # 3. ЗАГРУЗКА СТАТЕЙ (ДЛЯ КОНТЕКСТА)
147
  # ===================================================================
148
 
149
  @st.cache_resource
150
  def load_science_articles():
151
- if os.path.exists("science_articles.pkl"):
152
- with open("science_articles.pkl", 'rb') as f:
 
 
153
  return pickle.load(f)
154
 
155
  with st.spinner("📚 Загружаю научные статьи..."):
@@ -164,13 +283,14 @@ def load_science_articles():
164
  articles.append({
165
  "id": i,
166
  "title": title[:200],
167
- "text": text[:2000],
168
  "source": "ru_science"
169
  })
170
- with open("science_articles.pkl", 'wb') as f:
171
  pickle.dump(articles, f)
172
  return articles
173
- except:
 
174
  return []
175
 
176
  @st.cache_resource
@@ -180,149 +300,97 @@ def load_embedder():
180
  except:
181
  return None
182
 
183
- @st.cache_resource
184
- def create_embeddings(_articles, _embedder):
185
- if os.path.exists("science_embeddings.npy"):
186
- return np.load("science_embeddings.npy")
187
- if not _articles or _embedder is None:
188
- return np.array([])
189
- texts = [f"{a['title']}\n\n{a['text']}" for a in _articles]
190
- embeddings = _embedder.encode(texts, normalize_embeddings=True, show_progress_bar=True, batch_size=64)
191
- np.save("science_embeddings.npy", embeddings)
192
- return embeddings
193
-
194
- def search_articles(query, _articles, _embeddings, _embedder):
195
- if not _articles or len(_embeddings) == 0 or _embedder is None:
196
- return []
197
- try:
198
- query_vector = _embedder.encode([query], normalize_embeddings=True)[0]
199
- scores = _embeddings @ query_vector
200
- top_indices = np.argsort(-scores)[:2]
201
- results = []
202
- for idx in top_indices:
203
- score = float(scores[int(idx)])
204
- if score > 0.15:
205
- article = _articles[int(idx)]
206
- results.append({"title": article['title'], "score": score, "text": article['text'][:500]})
207
- return results
208
- except:
209
- return []
210
-
211
- # ===================================================================
212
- # 4. ОСНОВНОЙ КЛАСС
213
- # ===================================================================
214
-
215
- class OpenAirAI:
216
- def __init__(self):
217
- self.name = AI_NAME
218
- self.company = COMPANY_NAME
219
- self.creators = CREATORS
220
- self.chatbot = NeuralChatbot()
221
- self.is_ready = False
222
-
223
- def initialize(self):
224
- self.is_ready = self.chatbot.load_model()
225
- return self.is_ready
226
-
227
- def generate_answer(self, query):
228
- """ВСЕГДА ГЕНЕРИРУЕТ ЧЕРЕЗ НЕЙРОСЕТЬ"""
229
- return self.chatbot.generate(query)
230
-
231
  # ===================================================================
232
- # 5. ИНТЕРФЕЙС
233
  # ===================================================================
234
 
235
- # Загрузка
236
  articles = load_science_articles()
237
- embedder = load_embedder()
238
- embeddings = create_embeddings(articles, embedder)
239
 
240
- # Инициализация AI
241
- if 'ai' not in st.session_state:
242
- st.session_state.ai = OpenAirAI()
243
- st.session_state.ai.initialize()
244
 
245
- ai = st.session_state.ai
 
246
 
247
- # История чата
248
- if "messages" not in st.session_state:
249
- st.session_state.messages = []
250
- # ВСЕГДА ГЕНЕРИРУЕТСЯ НЕЙРОСЕТЬЮ
251
- greeting = ai.generate_answer("Привет! Представься и расскажи о себе за 1-2 предложения")
252
- st.session_state.messages.append({"role": "assistant", "content": greeting})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
- # --- БОКОВАЯ ПАНЕЛЬ ---
255
- with st.sidebar:
256
- st.image("https://cdn-icons-png.flaticon.com/512/4248/4248455.png", width=80)
257
- st.title(f"🧠 {AI_NAME}")
258
-
259
- st.markdown(f"""
260
- **{ai.name}** | {CREATION_DATE}
261
-
262
- **Компания:** {ai.company}
263
- **Разработчики:** {', '.join(ai.creators)}
264
-
265
- ---
266
- **🔗 Ссылки:**
267
- [🌐 Сайт]({WEBSITE})
268
- [🤗 Hugging Face]({HUGGINGFACE})
269
 
270
- ---
271
- **📊 Статистика:**
272
- - Статей: {len(articles)}
273
- - Сообщений: {len(st.session_state.messages)}
274
- - Модель: {MODEL_NAME.split("/")[-1]}
275
- """)
276
 
277
- # Кнопки уп��авления
278
- if st.button("🔄 Новое приветствие"):
279
- new_greeting = ai.generate_answer("Привет! Представься заново, но по-другому")
280
- if st.session_state.messages:
281
- st.session_state.messages[0] = {"role": "assistant", "content": new_greeting}
282
- st.rerun()
283
 
284
- if st.button("🗑️ Очистить чат"):
285
- st.session_state.messages = []
286
- greeting = ai.generate_answer("Привет! Представься кратко")
287
- st.session_state.messages.append({"role": "assistant", "content": greeting})
288
- st.rerun()
289
-
290
- # --- ОСНОВНАЯ ЧАСТЬ ---
291
- st.title(f"🧠 {AI_NAME} - Нейросетевой AI-ассистент")
292
- st.markdown(f"**{AI_NAME}** от **{COMPANY_NAME}** | 💡 Ответы генерируются нейросетью без шаблонов")
293
-
294
- # Отображение сообщений
295
- for message in st.session_state.messages:
296
- with st.chat_message(message["role"]):
297
- st.markdown(message["content"])
298
-
299
- # Поле ввода
300
- if prompt := st.chat_input("Задайте любой вопрос..."):
301
- # Добавляем сообщение пользователя
302
- st.session_state.messages.append({"role": "user", "content": prompt})
303
- with st.chat_message("user"):
304
- st.markdown(prompt)
305
 
306
- # Генерация ответа
307
- with st.chat_message("assistant"):
308
- with st.spinner("🧠 Нейросеть генерирует ответ..."):
309
- # ВСЕГДА ГЕНЕРИРУЕМ ЧЕРЕЗ НЕЙРОСЕТЬ
310
- response = ai.generate_answer(prompt)
311
-
312
- # Ищем статьи для контекста (если есть)
313
- articles_context = search_articles(prompt, articles, embeddings, embedder)
314
- if articles_context and len(response) < 100:
315
- # Добавляем статьи, НО через нейросеть
316
- context_prompt = f"Добавь в ответ ссылки на эти статьи: {', '.join([a['title'] for a in articles_context])}"
317
- enhanced_response = ai.generate_answer(context_prompt)
318
- if len(enhanced_response) > len(response):
319
- response = enhanced_response
320
-
321
- st.markdown(response)
322
- st.session_state.messages.append({"role": "assistant", "content": response})
323
-
324
- st.rerun()
 
 
325
 
326
  # --- ПОДВАЛ ---
327
  st.divider()
328
- st.caption(f"🧠 {AI_NAME} от {COMPANY_NAME} | Создан в {CREATION_DATE} | Генеративная нейросеть")
 
4
  import torch
5
  import torch.nn as nn
6
  import torch.nn.functional as F
7
+ from torch.utils.data import Dataset, DataLoader
8
+ from transformers import AutoTokenizer
9
  from sentence_transformers import SentenceTransformer
10
  import time
11
  from datetime import datetime
12
  import json
13
  import os
14
  import pickle
 
15
  import re
16
  import warnings
17
  warnings.filterwarnings('ignore')
 
24
  if torch.cuda.is_available():
25
  print(f"GPU: {torch.cuda.get_device_name(0)}")
26
 
27
+ # СВОЯ НЕЙРОСЕТЬ
28
+ MODEL_NAME = "DeepPavlov/rubert-base-cased" # Для токенизации
29
  EMBEDDING_MODEL = "all-MiniLM-L6-v2"
30
  SCIENCE_DATASET = "RafaelUI/ru_science"
31
+ ARTICLE_LIMIT = 200 # Для обучения
32
+ MAX_LENGTH = 256
33
+ BATCH_SIZE = 16
34
+ EPOCHS = 10
35
+ LEARNING_RATE = 1e-4
36
 
37
  AI_NAME = "OpenAirAI"
38
  COMPANY_NAME = "OpenRussianAI"
 
42
  CREATION_DATE = "2026"
43
 
44
  st.set_page_config(
45
+ page_title=f"{AI_NAME} - Своя нейросеть",
46
  page_icon="🧠",
47
  layout="wide",
48
  initial_sidebar_state="expanded"
49
  )
50
 
51
  # ===================================================================
52
+ # 2. СВОЯ НЕЙРОСЕТЬ НА PYTORCH
53
  # ===================================================================
54
 
55
+ class SimpleTransformer(nn.Module):
56
+ """Своя нейросеть с нуля на PyTorch"""
57
+
58
+ def __init__(self, vocab_size, embed_dim=256, num_heads=8, num_layers=4, max_length=512):
59
+ super().__init__()
60
+
61
+ self.embed_dim = embed_dim
62
+ self.max_length = max_length
63
+
64
+ # 1. Embedding слой (превращает слова в векторы)
65
+ self.embedding = nn.Embedding(vocab_size, embed_dim)
66
+ self.pos_encoding = nn.Parameter(torch.randn(1, max_length, embed_dim))
67
+
68
+ # 2. Слои внимания (Transformer encoder)
69
+ self.attention_layers = nn.ModuleList([
70
+ nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
71
+ for _ in range(num_layers)
72
+ ])
73
+
74
+ # 3. FFN слои
75
+ self.ffn_layers = nn.ModuleList([
76
+ nn.Sequential(
77
+ nn.Linear(embed_dim, embed_dim * 4),
78
+ nn.ReLU(),
79
+ nn.Linear(embed_dim * 4, embed_dim)
80
+ )
81
+ for _ in range(num_layers)
82
+ ])
83
+
84
+ # 4. Layer Norm
85
+ self.norm_layers = nn.ModuleList([
86
+ nn.LayerNorm(embed_dim)
87
+ for _ in range(num_layers)
88
+ ])
89
+
90
+ # 5. Выходной слой (для генерации)
91
+ self.output_layer = nn.Linear(embed_dim, vocab_size)
92
+
93
+ self.dropout = nn.Dropout(0.1)
94
+
95
+ def forward(self, input_ids, attention_mask=None):
96
+ # 1. Получаем эмбеддинги
97
+ x = self.embedding(input_ids) # [batch, seq_len, embed_dim]
98
+ x = x + self.pos_encoding[:, :x.size(1), :]
99
+ x = self.dropout(x)
100
 
101
+ # 2. Проходим через слои внимания
102
+ for attn, ffn, norm in zip(
103
+ self.attention_layers, self.ffn_layers, self.norm_layers
104
+ ):
105
+ # Attention
106
+ attn_output, _ = attn(x, x, x, key_padding_mask=~attention_mask.bool())
107
+ x = x + attn_output
108
+ x = norm(x)
109
+
110
+ # FFN
111
+ ffn_output = ffn(x)
112
+ x = x + ffn_output
113
+ x = norm(x)
114
+
115
+ # 3. Выходной слой
116
+ logits = self.output_layer(x)
117
+
118
+ return logits
119
 
120
+ # ===================================================================
121
+ # 3. ДАТАСЕТ ДЛЯ ОБУЧЕНИЯ
122
+ # ===================================================================
123
+
124
+ class ScienceDataset(Dataset):
125
+ """Свой датасет для обучения"""
126
 
127
+ def __init__(self, articles, tokenizer, max_length=256):
128
+ self.articles = articles
129
+ self.tokenizer = tokenizer
130
+ self.max_length = max_length
131
+
132
+ def __len__(self):
133
+ return len(self.articles)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
+ def __getitem__(self, idx):
136
+ article = self.articles[idx]
137
+ text = f"{article['title']}. {article['text']}"
 
138
 
139
+ # Токенизация
140
+ encoding = self.tokenizer(
141
+ text,
142
+ truncation=True,
143
+ padding='max_length',
144
+ max_length=self.max_length,
145
+ return_tensors='pt'
146
+ )
147
 
148
+ return {
149
+ 'input_ids': encoding['input_ids'].squeeze(),
150
+ 'attention_mask': encoding['attention_mask'].squeeze(),
151
+ 'labels': encoding['input_ids'].squeeze() # Для обучения
152
+ }
153
+
154
+ # ===================================================================
155
+ # 4. ОБУЧЕНИЕ НЕЙРОСЕТИ
156
+ # ===================================================================
157
+
158
+ def train_model(model, dataloader, epochs=10, lr=1e-4):
159
+ """Обучение своей нейросети"""
160
+
161
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
162
+ model = model.to(device)
163
+
164
+ optimizer = torch.optim.AdamW(model.parameters(), lr=lr)
165
+ criterion = nn.CrossEntropyLoss(ignore_index=0) # ignore padding
166
+
167
+ losses = []
168
+
169
+ for epoch in range(epochs):
170
+ total_loss = 0
171
+ progress_bar = st.progress(0)
172
+ status_text = st.empty()
173
+
174
+ for i, batch in enumerate(dataloader):
175
+ input_ids = batch['input_ids'].to(device)
176
+ attention_mask = batch['attention_mask'].to(device)
177
+ labels = batch['labels'].to(device)
178
 
179
+ # Forward
180
+ optimizer.zero_grad()
181
+ logits = model(input_ids, attention_mask)
 
 
 
 
 
 
182
 
183
+ # Вычисляем loss
184
+ loss = criterion(
185
+ logits.view(-1, logits.size(-1)),
186
+ labels.view(-1)
187
+ )
188
 
189
+ # Backward
190
+ loss.backward()
191
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
192
+ optimizer.step()
193
 
194
+ total_loss += loss.item()
195
 
196
+ # Обновляем прогресс
197
+ progress = (i + 1) / len(dataloader)
198
+ progress_bar.progress((epoch + progress) / epochs)
199
+ status_text.text(
200
+ f"Эпоха {epoch+1}/{epochs}, "
201
+ f"Батч {i+1}/{len(dataloader)}, "
202
+ f"Loss: {loss.item():.4f}"
203
+ )
204
+
205
+ avg_loss = total_loss / len(dataloader)
206
+ losses.append(avg_loss)
207
+ st.write(f"✅ Эпоха {epoch+1}: Средний Loss = {avg_loss:.4f}")
208
 
209
+ return model, losses
210
+
211
+ # ===================================================================
212
+ # 5. ГЕНЕРАЦИЯ ОТВЕТОВ
213
+ # ===================================================================
214
+
215
+ def generate_answer(model, tokenizer, query, max_length=150):
216
+ """Генерация ответа своей нейросетью"""
217
+
218
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
219
+ model.eval()
220
+
221
+ with torch.no_grad():
222
+ # Токенизируем запрос
223
+ encoding = tokenizer(
224
+ query,
225
+ truncation=True,
226
+ padding='max_length',
227
+ max_length=100,
228
+ return_tensors='pt'
229
+ )
230
+
231
+ input_ids = encoding['input_ids'].to(device)
232
+ attention_mask = encoding['attention_mask'].to(device)
233
+
234
+ # Генерируем ответ
235
+ generated = input_ids.clone()
236
 
237
+ for _ in range(max_length):
238
+ logits = model(generated, attention_mask)
239
+
240
+ # Берем последний токен
241
+ next_token_logits = logits[:, -1, :]
242
+ next_token_probs = F.softmax(next_token_logits, dim=-1)
243
+
244
+ # Выбираем токен
245
+ next_token = torch.multinomial(next_token_probs, num_samples=1)
246
+
247
+ # Добавляем к последовательности
248
+ generated = torch.cat([generated, next_token], dim=1)
249
+
250
+ # Если сгенерирован токен конца
251
+ if next_token.item() == tokenizer.eos_token_id:
252
+ break
253
+
254
+ # Декодируем
255
+ response = tokenizer.decode(generated[0], skip_special_tokens=True)
256
 
257
+ # Убираем запрос из ответа
258
+ response = response.replace(query, "").strip()
259
+
260
+ return response if response else "Извините, нейросеть не сгенерировала ответ."
261
 
262
  # ===================================================================
263
+ # 6. ЗАГРУЗКА ДАННЫХ
264
  # ===================================================================
265
 
266
  @st.cache_resource
267
  def load_science_articles():
268
+ articles_file = "science_articles.pkl"
269
+
270
+ if os.path.exists(articles_file):
271
+ with open(articles_file, 'rb') as f:
272
  return pickle.load(f)
273
 
274
  with st.spinner("📚 Загружаю научные статьи..."):
 
283
  articles.append({
284
  "id": i,
285
  "title": title[:200],
286
+ "text": text[:1000],
287
  "source": "ru_science"
288
  })
289
+ with open(articles_file, 'wb') as f:
290
  pickle.dump(articles, f)
291
  return articles
292
+ except Exception as e:
293
+ st.error(f"Ошибка: {e}")
294
  return []
295
 
296
  @st.cache_resource
 
300
  except:
301
  return None
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  # ===================================================================
304
+ # 7. ИНТЕРФЕЙС
305
  # ===================================================================
306
 
307
+ # Загрузка данных
308
  articles = load_science_articles()
 
 
309
 
310
+ st.title(f"🧠 {AI_NAME} - Своя нейросеть на PyTorch")
311
+ st.markdown(f"**{AI_NAME}** от **{COMPANY_NAME}** | Обучаем на **{SCIENCE_DATASET}**")
 
 
312
 
313
+ # Информация о модели
314
+ st.info(f"📊 **Данные:** {len(articles)} статей | **Размер модели:** 256 эмбеддингов | **Слои:** 4 Transformer")
315
 
316
+ # Кнопка обучения
317
+ if st.button("🚀 Обучить нейросеть с нуля"):
318
+ if not articles:
319
+ st.error("Нет данных для обучения!")
320
+ else:
321
+ # Загружаем токенизатор
322
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
323
+ tokenizer.pad_token = tokenizer.eos_token
324
+
325
+ # Создаем датасет
326
+ dataset = ScienceDataset(articles, tokenizer, MAX_LENGTH)
327
+ dataloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
328
+
329
+ # Создаем модель
330
+ vocab_size = len(tokenizer)
331
+ model = SimpleTransformer(
332
+ vocab_size=vocab_size,
333
+ embed_dim=256,
334
+ num_heads=8,
335
+ num_layers=4,
336
+ max_length=MAX_LENGTH
337
+ )
338
+
339
+ # Обучаем
340
+ st.write("🧠 **Начинаем обучение...**")
341
+ trained_model, losses = train_model(
342
+ model,
343
+ dataloader,
344
+ epochs=EPOCHS,
345
+ lr=LEARNING_RATE
346
+ )
347
+
348
+ # Сохраняем модель
349
+ torch.save(trained_model.state_dict(), "openairai_model.pth")
350
+ st.success(f"✅ Модель сохранена! (потери: {losses[-1]:.4f})")
351
+
352
+ st.session_state.model = trained_model
353
+ st.session_state.tokenizer = tokenizer
354
 
355
+ # Проверка модели
356
+ if "model" in st.session_state:
357
+ model = st.session_state.model
358
+ tokenizer = st.session_state.tokenizer
 
 
 
 
 
 
 
 
 
 
 
359
 
360
+ st.success("✅ Модель загружена и готова к использованию!")
 
 
 
 
 
361
 
362
+ # Поле для вопроса
363
+ query = st.text_input("🔍 Задайте вопрос нейросети:", placeholder="Например: Что такое наука?")
 
 
 
 
364
 
365
+ if query:
366
+ with st.spinner("🧠 Нейросеть думает..."):
367
+ response = generate_answer(model, tokenizer, query)
368
+ st.markdown(f"**🤖 Ответ:** {response}")
369
+ else:
370
+ st.warning("⚠️ Модель ещё не обучена. Нажмите кнопку выше для обучения.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
 
372
+ # Показываем пример обучения
373
+ with st.expander("📖 Как это работает?"):
374
+ st.markdown("""
375
+ **Своя нейросеть на PyTorch:**
376
+
377
+ 1. **Архитектура:** Transformer (4 слоя внимания)
378
+ 2. **Размер:** 256 эмбеддингов
379
+ 3. **Обучение:** на научных статьях
380
+ 4. **Генерация:** пошаговая
381
+
382
+ **Преимущества:**
383
+ - Полный контроль над моделью
384
+ - Можно дообучать на любых данных
385
+ - Не зависит от сторонних API
386
+ - Бесплатно
387
+
388
+ **Недостатки:**
389
+ - Требует GPU для быстрого обучения
390
+ - Меньше, чем большие модели
391
+ - Нужно много данных
392
+ """)
393
 
394
  # --- ПОДВАЛ ---
395
  st.divider()
396
+ st.caption(f"🧠 {AI_NAME} от {COMPANY_NAME} | Создан в {CREATION_DATE} | Своя нейросеть на PyTorch")