- server.py: FastAPI serves model (best_generator.pt) + static files - /api/generate endpoint with temperature/top_k controls - /api/health endpoint for status - Frontend auto-detects backend, shows connection status - API URL configurable from UI (persisted in localStorage) - NaN/inf safety in generation - Clean output (strips BOS/EOS/PAD/UNK tokens)
187 lines
9.3 KiB
HTML
187 lines
9.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>ClickBait Generator — GPT with RoPE</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<nav class="nav">
|
||
<div class="container nav-inner">
|
||
<span class="nav-logo">⚡ CBGen</span>
|
||
<div class="nav-links">
|
||
<a href="#architecture">Архитектура</a>
|
||
<a href="#generator">Генератор</a>
|
||
<a href="#examples">Примеры</a>
|
||
<a href="#code">Код</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<section class="hero">
|
||
<div class="container">
|
||
<h1>ClickBait<br>Generator</h1>
|
||
<p class="hero-sub">GPT-подобная языковая модель с Rotary Position Embeddings, обученная генерировать кликбейтные заголовки. 42 962 токена, 3 слоя decoder, 256 скрытых размерностей.</p>
|
||
<a class="btn btn-primary" href="#generator">Попробовать →</a>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="architecture" class="section">
|
||
<div class="container">
|
||
<h2>Архитектура модели</h2>
|
||
<p class="section-desc">GPTModelWithRoPE — компактный decoder-only трансформер с Rotary Position Embeddings вместо абсолютных позиционных кодировок.</p>
|
||
<div class="arch-grid">
|
||
<div class="arch-card">
|
||
<div class="arch-card-header">Token Embedding</div>
|
||
<div class="arch-card-body"><code>Embedding(42962, 256)</code><br><span class="arch-desc">Словарь из 42 962 токенов, каждый — вектор размерности 256</span></div>
|
||
</div>
|
||
<div class="arch-card">
|
||
<div class="arch-card-header">DecoderBlock × 3</div>
|
||
<div class="arch-card-body">
|
||
<div class="arch-sub">MultiHeadAttentionWithRoPE</div>
|
||
<code>W_q, W_k, W_v, W_o: 256→256</code>
|
||
<div class="arch-sub" style="margin-top:12px">FeedForward</div>
|
||
<code>256 → 1024 → 256</code>
|
||
<div class="arch-sub" style="margin-top:12px">LayerNorm + Dropout</div>
|
||
</div>
|
||
</div>
|
||
<div class="arch-card">
|
||
<div class="arch-card-header">Output</div>
|
||
<div class="arch-card-body">
|
||
<code>LayerNorm(256)</code><br>
|
||
<code>Linear(256 → 42962)</code><br>
|
||
<span class="arch-desc">Проекция обратно на словарь</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="params-bar">
|
||
<span>Параметры: <strong>~6.2M</strong></span>
|
||
<span>Размер модели: <strong>~280 MB</strong></span>
|
||
<span>Контекст: <strong>RoPE</strong></span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="generator" class="section section-dark">
|
||
<div class="container">
|
||
<h2>Генератор кликбейта</h2>
|
||
<p class="section-desc">Введите начало заголовка — модель продолжит в стиле кликбейта.</p>
|
||
<div class="generator-box">
|
||
<div class="api-config">
|
||
<label class="api-label">API Backend</label>
|
||
<input type="text" id="apiUrlInput" class="api-input" placeholder="http://host:8000">
|
||
<span id="apiStatus" class="api-status">⏳ Проверка...</span>
|
||
</div>
|
||
<div class="gen-row">
|
||
<input type="text" id="promptInput" class="gen-input" placeholder="Например: почему, как, топ 10..." autocomplete="off">
|
||
</div>
|
||
<div class="gen-controls">
|
||
<div class="gen-slider-group">
|
||
<label>Temperature <span id="tempVal">1.0</span></label>
|
||
<input type="range" id="tempSlider" min="0.3" max="2.0" step="0.1" value="1.0">
|
||
</div>
|
||
<div class="gen-slider-group">
|
||
<label>Top-K <span id="topkVal">50</span></label>
|
||
<input type="range" id="topkSlider" min="5" max="100" step="5" value="50">
|
||
</div>
|
||
</div>
|
||
<div class="gen-row">
|
||
<button id="generateBtn" class="btn btn-primary btn-gen">⚡ Сгенерировать</button>
|
||
<button id="generate5Btn" class="btn btn-secondary">×5 вариантов</button>
|
||
</div>
|
||
<div id="statusMsg" class="status-msg"></div>
|
||
<div id="outputArea" class="gen-output"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="examples" class="section">
|
||
<div class="container">
|
||
<h2>Примеры генерации</h2>
|
||
<p class="section-desc">Результаты работы модели при разных параметрах.</p>
|
||
<div class="ex-tabs">
|
||
<button class="ex-tab active" data-tab="no-prompt">Без промпта</button>
|
||
<button class="ex-tab" data-tab="with-prompt">С промптом</button>
|
||
<button class="ex-tab" data-tab="temps">Разные температуры</button>
|
||
</div>
|
||
<div class="ex-content active" id="ex-no-prompt">
|
||
<div class="ex-item">1. Этот секрет знают единицы...😱</div>
|
||
<div class="ex-item">2. Врачи в шоке от этого открытия...😱</div>
|
||
<div class="ex-item">3. Ты не поверишь, что произошло дальше...😱</div>
|
||
<div class="ex-item">4. Всего один ингредиент меняет всё...😱</div>
|
||
<div class="ex-item">5. Вот почему никто не говорит об этом...😱</div>
|
||
</div>
|
||
<div class="ex-content" id="ex-with-prompt">
|
||
<div class="ex-item"><mark>почему</mark> кошки не любят воду — ответ шокирует...😱</div>
|
||
<div class="ex-item"><mark>как</mark> заработать миллион за неделю...😱</div>
|
||
<div class="ex-item"><mark>топ 10</mark> фильмов, которые вы обязаны посмотреть...😱</div>
|
||
</div>
|
||
<div class="ex-content" id="ex-temps">
|
||
<div class="ex-item"><span class="badge">T=0.5</span> учёные сделали невероятное открытие...😱</div>
|
||
<div class="ex-item"><span class="badge">T=1.0</span> Этот метод взорвал интернет — вот почему...😱</div>
|
||
<div class="ex-item"><span class="badge">T=1.5</span> Парень купил авто за 100$ а через месяц...😱</div>
|
||
<div class="ex-item"><span class="badge">T=2.0</span> Коты захватили биржу — подробности внутри...😱</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="code" class="section section-dark">
|
||
<div class="container">
|
||
<h2>Как это работает</h2>
|
||
<p class="section-desc">Генерация с top-k sampling и temperature scaling.</p>
|
||
<div class="code-block">
|
||
<div class="code-header">generate_clickbait.py</div>
|
||
<pre><span class="kw">def</span> <span class="fn">generate_clickbait</span>(model, vocab, prompt=<span class="str">""</span>, max_new_tokens=<span class="num">7</span>,
|
||
temperature=<span class="num">1.0</span>, top_k=<span class="num">50</span>, top_p=<span class="num">0.9</span>):
|
||
model.<span class="fn">eval</span>()
|
||
<span class="kw">if</span> prompt:
|
||
tokens = vocab.<span class="fn">text_to_indices_for_generator</span>(prompt)
|
||
input_ids = torch.LongTensor(
|
||
[[vocab.word2idx[<span class="str">'<BOS>'</span>]] + tokens]
|
||
).<span class="fn">to</span>(DEVICE)
|
||
<span class="kw">else</span>:
|
||
input_ids = torch.LongTensor(
|
||
[[vocab.word2idx[<span class="str">'<BOS>'</span>]]]
|
||
).<span class="fn">to</span>(DEVICE)
|
||
|
||
generated = model.<span class="fn">generate</span>(
|
||
input_ids, max_new_tokens=max_new_tokens,
|
||
temperature=temperature, top_k=top_k, top_p=top_p,
|
||
eos_token_id=vocab.word2idx[<span class="str">'<EOS>'</span>]
|
||
)
|
||
<span class="kw">return</span> vocab.<span class="fn">indices_to_text</span>(generated[<span class="num">0</span>].<span class="fn">tolist</span>())</pre>
|
||
</div>
|
||
<div class="code-block">
|
||
<div class="code-header">Батчовая генерация</div>
|
||
<pre><span class="kw">for</span> i <span class="kw">in</span> <span class="fn">range</span>(<span class="num">5</span>):
|
||
text = <span class="fn">generate_clickbait</span>(
|
||
model, vocab, prompt=<span class="str">""</span>,
|
||
temperature=<span class="num">1.0</span>, top_k=<span class="num">50</span>
|
||
)
|
||
<span class="fn">print</span>(<span class="str">f" {i+1}. {text}...😱"</span>)</pre>
|
||
</div>
|
||
<div class="code-block">
|
||
<div class="code-header">Разные температуры</div>
|
||
<pre><span class="kw">for</span> temp <span class="kw">in</span> [<span class="num">0.5</span>, <span class="num">1.0</span>, <span class="num">1.5</span>, <span class="num">2.0</span>]:
|
||
text = <span class="fn">generate_clickbait</span>(
|
||
model, vocab, prompt=<span class="str">"почему"</span>,
|
||
temperature=temp, top_k=<span class="num">50</span>
|
||
)
|
||
<span class="fn">print</span>(<span class="str">f" T={temp}: {text}...😱"</span>)</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<footer class="footer">
|
||
<div class="container">
|
||
<p>ClickBait Generator — GPT with RoPE</p>
|
||
<p class="footer-small">PyTorch · Rotary Position Embeddings · Top-K Sampling</p>
|
||
</div>
|
||
</footer>
|
||
|
||
<script src="script.js"></script>
|
||
</body>
|
||
</html>
|