File size: 13,181 Bytes
d1cce78
1906a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bba615
d1cce78
1906a83
4bba615
1906a83
4bba615
1906a83
4bba615
1906a83
4bba615
 
 
1906a83
 
 
4bba615
1906a83
4bba615
1906a83
4bba615
 
 
 
 
 
1906a83
 
 
 
 
4bba615
 
1906a83
 
 
 
 
4bba615
1906a83
 
 
 
 
 
 
4bba615
1906a83
4bba615
1906a83
 
 
 
 
 
 
 
 
 
4bba615
1906a83
4bba615
 
 
 
 
1906a83
 
4bba615
 
 
 
1906a83
4bba615
 
1906a83
 
 
 
 
4bba615
 
 
 
 
 
1906a83
 
4bba615
 
 
1906a83
 
 
 
 
 
 
4bba615
 
 
 
 
 
 
1906a83
4bba615
1906a83
 
 
 
 
 
 
 
 
 
 
 
4bba615
1906a83
 
 
 
 
4bba615
1906a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bba615
1906a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bba615
1906a83
 
 
 
 
 
 
 
4bba615
1906a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4bba615
1906a83
 
 
 
 
 
 
 
4bba615
1906a83
 
 
 
4bba615
 
1906a83
 
 
4bba615
1906a83
 
 
 
 
 
4bba615
1906a83
 
 
 
 
 
4bba615
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
52
53
54
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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
---
language:
  - ru
  - en
  - zh
  - de
  - uk
tags:
  - causal-lm
  - pytorch
  - safetensors
  - timegpt
  - 0.5b
  - text-generation
license: apache-2.0
pipeline_tag: text-generation
inference: false
model_name: TimeGPT-Base
---

# ⏳ TimeGPT-Base (0.5B)

**TimeGPT-Base** — это мультиязычная авторегрессионная языковая модель (Causal LM) класса **0.5B**, разработанная и обученная **полностью с нуля (from scratch)** без использования сторонних весов или дистилляции.

Модель создана независимой командой **LongTime Devs** и обучена на одном потребительском ускорителе **NVIDIA GeForce RTX 5060 Ti 16GB**. Основной язык обучения — **русский**, с интеграцией английского, китайского, немецкого и украинского корпусов.

> ⚠️ **Важное замечание:** это **базовая фундаментальная модель (Base)**. Она обучена задаче продолжения текста (next-token prediction / text completion) и не проходила стадию выравнивания инструкций. 
> 
> Диалоговая инструктивная версия — **TimeGPT-Instruct (0.5B)** — в настоящее время находится на финальной стадии дообучения и **в скором времени будет опубликована** в репозитории команды LongTime Devs.

---

## 🏛 Архитектурные особенности

TimeGPT построена на современной архитектуре трансформера (уровня Llama 3 и Qwen 2.5), оптимизированной для высокой скорости инференса и экономии видеопамяти:

- **Grouped-Query Attention (GQA)**: 20 голов Query и 4 головы Key/Value (соотношение 5:1), что кратно снижает размер KV-кеша при работе с контекстом.
- **SwiGLU Activation**: нелинейная функция активации в блоках MLP.
- **RMSNorm**: быстрая среднеквадратичная нормализация вместо классического LayerNorm.
- **Rotary Position Embeddings (RoPE)**: векторное позиционное кодирование с базовой частотой $\theta = 500\,000$ для стабильности контекста.
- **Weight Tying**: совмещение матриц эмбеддингов токенов и выходной языковой головы для компактности и регуляризации весов.
- **FlashAttention / SDPA**: поддержка нативного аппаратного ускорения Scaled Dot-Product Attention в режиме `bfloat16`.

### Спецификация модели

| Параметр | Значение |
| :--- | :--- |
| **Класс параметров** | **0.5B** |
| **Размер файла весов** | **1.02 GB** (`model.safetensors`) |
| **Размерность скрытого слоя (`dim`)** | 1280 |
| **Количество слоев (`n_layers`)** | 24 |
| **Голов внимания (`n_heads`)** | 20 |
| **Голов Key/Value (`n_kv_heads`)** | 4 (GQA) |
| **Размерность промежуточного слоя MLP** | 3456 (SwiGLU) |
| **Контекстное окно (`max_seq_len`)** | 1024 токена |
| **Размер словаря (`vocab_size`)** | 32 000 |
| **Формат весов** | SafeTensors (`bfloat16`) |

---

## 🔤 Токенизатор

Модель использует собственный мультиязычный **Byte-Level BPE** токенизатор:
- **Размер словаря**: 32 000 токенов.
- **Byte-fallback**: байтовое резервирование исключает появление токенов неизвестных символов (`<|unk|>`) при обработке редких знаков, иероглифов и эмодзи.
- **Служебные токены стандарта ChatML**:
  - `<|pad|>`
  - `<|unk|>`
  - `<|im_start|>`
  - `<|im_end|>`

---

## 📚 Данные и процесс предобучения

Модель обучалась фундаментальному чтению в непрерывном мультиязычном потоке данных:

- **Русский язык (основной массив, ~55%)**: русскоязычный срез Википедии + новостные корпуса (`IlyaGusev/gazeta`).
- **Английский язык (~20%)**: статьи англоязычной Википедии.
- **Китайский язык (~10%)**: срез китайской Википедии.
- **Немецкий язык (~8%)**: срез немецкой Википедии.
- **Украинский язык (~7%)**: статьи украинской Википедии.

### Детали тренировочного цикла:
- **Оборудование**: 1× NVIDIA GeForce RTX 5060 Ti 16GB.
- **Шаги обучения**: 50 000 шагов (эффективный размер батча = 16 последовательностей по 1024 токена).
- **Суммарный объем прочитанных данных**: ~820 млн токенов.
- **Время непрерывного обучения**: ~27 часов.
- **Оптимизатор**: AdamW ($\beta_1 = 0.9$, $\beta_2 = 0.95$, weight decay = 0.01).
- **График Learning Rate**: Cosine Annealing с плавным разогревом (Warmup: 1500 шагов, Peak LR: `1.8e-4`).
- **Динамика Loss**: стартовый лосс **10.64** $\rightarrow$ финальный лосс **3.02–3.36** (перплексия снизилась с бесконечности до ~20).

---

## 💻 Быстрый запуск (Inference)

Установите необходимые зависимости:
```bash
pip install torch tokenizers safetensors huggingface_hub
```

Скрипт автоматически скачивает веса (`model.safetensors` — 1.02 GB) и токенизатор напрямую из репозитория `longtimedevs/TimeGPT-Base`:

```python
# WARNING: DO NOT USE THIS SCRIPT IN PRODUCTION ENVIRONMENTS! // ВНИМАНИЕ: НЕ ИСПОЛЬЗУЙТЕ ДАННЫЙ КОД В ПРОДАКШЕНЕ!
# FOR RESEARCH, TESTING AND EXPERIMENTAL PURPOSES ONLY! // ТОЛЬКО ДЛЯ ИССЛЕДОВАТЕЛЬСКИХ ЦЕЛЕЙ, ТЕСТИРОВАНИЯ И ЭКСПЕРИМЕНТОВ!

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
from dataclasses import dataclass
from tokenizers import Tokenizer
from safetensors.torch import load_file
from huggingface_hub import hf_hub_download

# --- 1. Автоматическое скачивание с Hugging Face ---
REPO_ID = "longtimedevs/TimeGPT-Base"
print(f"[*] Проверка и скачивание файлов из {REPO_ID}...")
weights_path = hf_hub_download(repo_id=REPO_ID, filename="model.safetensors")
tokenizer_path = hf_hub_download(repo_id=REPO_ID, filename="tokenizer.json")

# --- 2. Конфигурация модели ---
@dataclass
class TimeGPTConfig:
    vocab_size: int = 32000
    dim: int = 1280
    n_layers: int = 24
    n_heads: int = 20
    n_kv_heads: int = 4
    multiple_of: int = 256
    max_seq_len: int = 1024
    norm_eps: float = 1e-5
    rope_theta: float = 500000.0

# --- 3. Архитектура TimeGPT ---
class RMSNorm(nn.Module):
    def __init__(self, dim: int, eps: float = 1e-5):
        super().__init__()
        self.eps = eps
        self.weight = nn.Parameter(torch.ones(dim))

    def forward(self, x):
        return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) * self.weight

def precompute_rope_sincos(dim: int, max_seq_len: int, theta: float = 500000.0):
    freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
    t = torch.arange(max_seq_len, dtype=torch.float32)
    freqs = torch.outer(t, freqs)
    freqs = torch.cat([freqs, freqs], dim=-1)
    return torch.cos(freqs), torch.sin(freqs)

def rotate_half(x: torch.Tensor):
    x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2 :]
    return torch.cat((-x2, x1), dim=-1)

def apply_rope(xq, xk, cos, sin):
    cos = cos[:xq.shape[2]].unsqueeze(0).unsqueeze(1)
    sin = sin[:xq.shape[2]].unsqueeze(0).unsqueeze(1)
    return (xq * cos) + (rotate_half(xq) * sin), (xk * cos) + (rotate_half(xk) * sin)

class Attention(nn.Module):
    def __init__(self, cfg):
        super().__init__()
        self.n_heads, self.n_kv_heads = cfg.n_heads, cfg.n_kv_heads
        self.head_dim = cfg.dim // cfg.n_heads
        self.n_rep = self.n_heads // self.n_kv_heads
        self.wq = nn.Linear(cfg.dim, cfg.n_heads * self.head_dim, bias=False)
        self.wk = nn.Linear(cfg.dim, cfg.n_kv_heads * self.head_dim, bias=False)
        self.wv = nn.Linear(cfg.dim, cfg.n_kv_heads * self.head_dim, bias=False)
        self.wo = nn.Linear(cfg.n_heads * self.head_dim, cfg.dim, bias=False)

    def forward(self, x, cos, sin):
        B, S, _ = x.shape
        xq = self.wq(x).view(B, S, self.n_heads, self.head_dim).transpose(1, 2)
        xk = self.wk(x).view(B, S, self.n_kv_heads, self.head_dim).transpose(1, 2)
        xv = self.wv(x).view(B, S, self.n_kv_heads, self.head_dim).transpose(1, 2)
        xq, xk = apply_rope(xq, xk, cos, sin)
        if self.n_rep > 1:
            xk = xk.repeat_interleave(self.n_rep, dim=1)
            xv = xv.repeat_interleave(self.n_rep, dim=1)
        out = F.scaled_dot_product_attention(xq, xk, xv, is_causal=True)
        return self.wo(out.transpose(1, 2).contiguous().view(B, S, -1))

class SwiGLUMLP(nn.Module):
    def __init__(self, cfg):
        super().__init__()
        hidden_dim = int(2 * (4 * cfg.dim) / 3)
        hidden_dim = cfg.multiple_of * ((hidden_dim + cfg.multiple_of - 1) // cfg.multiple_of)
        self.w1 = nn.Linear(cfg.dim, hidden_dim, bias=False)
        self.w2 = nn.Linear(hidden_dim, cfg.dim, bias=False)
        self.w3 = nn.Linear(cfg.dim, hidden_dim, bias=False)

    def forward(self, x):
        return self.w2(F.silu(self.w1(x)) * self.w3(x))

class TransformerBlock(nn.Module):
    def __init__(self, cfg):
        super().__init__()
        self.attn_norm, self.attn = RMSNorm(cfg.dim, cfg.norm_eps), Attention(cfg)
        self.ffn_norm, self.ffn = RMSNorm(cfg.dim, cfg.norm_eps), SwiGLUMLP(cfg)

    def forward(self, x, cos, sin):
        return x + self.ffn(self.ffn_norm(x + self.attn(self.attn_norm(x), cos, sin)))

class TimeGPT(nn.Module):
    def __init__(self, cfg):
        super().__init__()
        self.cfg = cfg
        self.tok_embeddings = nn.Embedding(cfg.vocab_size, cfg.dim)
        self.layers = nn.ModuleList([TransformerBlock(cfg) for _ in range(cfg.n_layers)])
        self.norm = RMSNorm(cfg.dim, cfg.norm_eps)
        self.output = nn.Linear(cfg.dim, cfg.vocab_size, bias=False)
        self.output.weight = self.tok_embeddings.weight
        cos, sin = precompute_rope_sincos(cfg.dim // cfg.n_heads, cfg.max_seq_len, cfg.rope_theta)
        self.register_buffer("rope_cos", cos, persistent=False)
        self.register_buffer("rope_sin", sin, persistent=False)

    def forward(self, idx):
        B, S = idx.shape
        h = self.tok_embeddings(idx)
        cos, sin = self.rope_cos[:S], self.rope_sin[:S]
        for layer in self.layers:
            h = layer(h, cos, sin)
        return self.output(self.norm(h))

# --- 4. Загрузка весов и инференс со стримингом ---
device = "cuda" if torch.cuda.is_available() else "cpu"
cfg = TimeGPTConfig()
model = TimeGPT(cfg).to(device, dtype=torch.bfloat16)

# Чтение скачанных весов SafeTensors
weights = load_file(weights_path, device=device)
model.load_state_dict(weights, strict=False)
model.eval()

tokenizer = Tokenizer.from_file(tokenizer_path)

prompt = "Искусственный интеллект — это"
input_ids = torch.tensor([tokenizer.encode(prompt).ids], device=device)

print(prompt, end="", flush=True)
with torch.inference_mode():
    for _ in range(80):
        logits = model(input_ids[:, -cfg.max_seq_len:])
        next_token = torch.multinomial(F.softmax(logits[0, -1, :] / 0.7, dim=-1), num_samples=1)
        if next_token.item() == tokenizer.token_to_id("<|im_end|>"):
            break
        input_ids = torch.cat([input_ids, next_token.unsqueeze(0)], dim=1)
        print(tokenizer.decode([next_token.item()]), end="", flush=True)
print()
```

---

## 👥 Разработчики

- **Команда**: LongTime Devs
- **Архитектура**: TimeGPT
- **Лицензия**: Apache 2.0 (открыто для свободного использования и исследований)