-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodeling_erk_linear.py
More file actions
181 lines (148 loc) · 7.51 KB
/
Copy pathmodeling_erk_linear.py
File metadata and controls
181 lines (148 loc) · 7.51 KB
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
"""
Erk-Linear — Erk-14B'nin 8 dikkat katmanini Gated DeltaNet'e damitan %20-lineer hibrit.
Yukleme (standart yol):
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("ecloudtech/Erk-Linear", trust_remote_code=True)
tok = AutoTokenizer.from_pretrained("ecloudtech/Erk-Linear")
Gereksinimler: torch, transformers, flash-linear-attention, safetensors, huggingface_hub
Model Qwen3-14B mimarisine dayanir; 8 katmanin softmax dikkati subquadratic Gated DeltaNet
ile degistirilmis, kalan 32 katman softmax "cipa" olarak korunmustur. Govde agirliklari
`config.base_model` deposundan, GDN agirliklari bu depodan yuklenir; from_pretrained
ikisini birlestirip calisir bir nedensel dil modeli doner.
"""
import torch
import torch.nn as nn
from transformers import AutoModelForCausalLM, AutoTokenizer, PreTrainedModel
from safetensors.torch import load_file
from huggingface_hub import hf_hub_download
try: # HF uzak kod (paket icinde)
from .configuration_erk_linear import ErkLinearConfig
except ImportError: # yerel kullanim (dosya yan yana)
from configuration_erk_linear import ErkLinearConfig
BASE_MODEL = "ecloudtech/Erk-14B" # Qwen3-14B temelli Turkce model
REPO_ID = "ecloudtech/Erk-Linear"
GDN_LAYERS = [1, 3, 5, 7, 10, 36, 38, 39] # %20 lineer, yayilmis yerlesim
class _GDNStateCache:
"""GatedDeltaNet'in get/update_layer_cache arayuzunun bekledigi minimal katman-durum tutucu.
FLA'nin recurrent_state + conv_state'ini tek katman icin saklar; boylece cache'li uretim
sirasinda GDN gecmis durumu adimlar arasi devreder.
"""
def __init__(self):
self._layers = []
def __len__(self):
return len(self._layers)
def __getitem__(self, idx):
return self._layers[idx]
def update(self, layer_idx=0, recurrent_state=None, conv_state=None, **kwargs):
while len(self._layers) <= layer_idx:
self._layers.append({"recurrent_state": None, "conv_state": None})
if recurrent_state is not None:
self._layers[layer_idx]["recurrent_state"] = recurrent_state
if conv_state is not None:
self._layers[layer_idx]["conv_state"] = conv_state
return self
class _GDNAttention(nn.Module):
"""Qwen3 self_attn cagri imzasiyla uyumlu Gated DeltaNet sarmalayici.
Cache'li uretim (use_cache=True) sirasinda GDN'nin recurrent + convolution durumunu
adimlar arasi devreder; boylece model.generate() ciktisi, tam-yeniden-hesaplama
(use_cache=False) ile sayisal gurultuye kadar ayni olur. Referans amacli tek-dizi
kullanim icindir (es zamanli/batch-paylasimli servis icin ayri durum yonetimi gerekir).
"""
def __init__(self, gdn):
super().__init__()
gdn.layer_idx = 0
self.gdn = gdn
self._state = None
def forward(self, hidden_states, *args, **kwargs):
cache_position = kwargs.get("cache_position", None)
seq_len = hidden_states.shape[1]
new_sequence = (
(cache_position is None and seq_len > 1)
or (cache_position is not None and int(cache_position.reshape(-1)[0]) == 0)
)
if new_sequence or self._state is None:
self._state = _GDNStateCache()
out = self.gdn(hidden_states, use_cache=True, past_key_values=self._state)
y = out[0] if isinstance(out, tuple) else out
if isinstance(out, tuple) and len(out) >= 3 and out[2] is not None:
self._state = out[2]
return (y, None)
def _install_gdn(model, gdn_state, cfg, device, dtype):
"""Govde modelin secili self_attn katmanlarini GDN sarmalayicilariyla degistirir."""
from fla.layers import GatedDeltaNet # flash-linear-attention (triton -> GPU gerekir)
layers = getattr(cfg, "gdn_layers", GDN_LAYERS)
H = model.config.hidden_size
for li in layers:
gdn = GatedDeltaNet(
hidden_size=H,
head_dim=getattr(cfg, "gdn_head_dim", 128),
num_heads=getattr(cfg, "gdn_num_heads", 40),
use_gate=getattr(cfg, "gdn_use_gate", True),
use_short_conv=getattr(cfg, "gdn_use_short_conv", True),
mode=getattr(cfg, "gdn_mode", "chunk"),
)
prefix = f"L{li}."
layer_sd = {k[len(prefix):]: v for k, v in gdn_state.items() if k.startswith(prefix)}
if not layer_sd:
raise ValueError(f"L{li} icin GDN agirligi bulunamadi ({cfg.gdn_weights_file})")
gdn.load_state_dict(layer_sd)
gdn = gdn.to(device).to(dtype).eval()
model.model.layers[li].self_attn = _GDNAttention(gdn).to(device).to(dtype)
return model
class ErkLinearForCausalLM(PreTrainedModel):
"""%20-lineer Erk hibridi.
`from_pretrained`, govdeyi `config.base_model` deposundan yukler, bu depodaki GDN
agirliklarini secili katmanlara takar ve elde edilen **calisir nedensel dil modelini**
doner. Donen nesne standart bir transformers modelidir: `.generate()`, `.forward()`,
`use_cache` ve chat sablonu oldugu gibi calisir.
"""
config_class = ErkLinearConfig
base_model_prefix = "erk_linear"
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path=None, *model_args, **kwargs):
repo = pretrained_model_name_or_path or REPO_ID
cfg = kwargs.pop("config", None)
if not isinstance(cfg, ErkLinearConfig):
cfg = ErkLinearConfig.from_pretrained(repo, **{
k: kwargs[k] for k in ("revision", "token", "cache_dir") if k in kwargs
})
dtype = kwargs.pop("dtype", None) or kwargs.pop("torch_dtype", None) or torch.bfloat16
device_map = kwargs.pop("device_map", None)
kwargs.pop("trust_remote_code", None)
base_kwargs = dict(kwargs)
if device_map is not None:
base_kwargs["device_map"] = device_map
model = AutoModelForCausalLM.from_pretrained(
cfg.base_model, dtype=dtype, trust_remote_code=True, **base_kwargs
)
if device_map is None:
model = model.to("cuda" if torch.cuda.is_available() else "cpu")
model.eval()
gdn_path = hf_hub_download(
repo_id=repo,
filename=getattr(cfg, "gdn_weights_file", "gdn_weights.safetensors"),
**{k: kwargs[k] for k in ("revision", "token", "cache_dir") if k in kwargs},
)
gdn_state = load_file(gdn_path)
device = next(model.parameters()).device
model = _install_gdn(model, gdn_state, cfg, device, dtype)
model.config.erk_linear = {
"gdn_layers": cfg.gdn_layers,
"linear_ratio": cfg.linear_ratio,
"base_model": cfg.base_model,
}
return model
def load_erk_linear(device="cuda", dtype=torch.bfloat16,
base_model=BASE_MODEL, repo_id=REPO_ID):
"""Geriye donuk uyumlu yardimci: (model, tokenizer) doner."""
cfg = ErkLinearConfig(base_model=base_model)
model = AutoModelForCausalLM.from_pretrained(base_model, dtype=dtype,
trust_remote_code=True).to(device).eval()
gdn_path = hf_hub_download(repo_id=repo_id, filename=cfg.gdn_weights_file)
model = _install_gdn(model, load_file(gdn_path), cfg, device, dtype)
tokenizer = AutoTokenizer.from_pretrained(base_model)
return model, tokenizer
if __name__ == "__main__":
m, t = load_erk_linear()
ids = t("Türkiye'nin başkenti", return_tensors="pt").to(m.device)
print(t.decode(m.generate(**ids, max_new_tokens=12)[0], skip_special_tokens=True))