patchfm 3.2.0__tar.gz → 3.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchfm-3.2.0/src/patchfm.egg-info → patchfm-3.3.0}/PKG-INFO +1 -1
- {patchfm-3.2.0 → patchfm-3.3.0}/pyproject.toml +1 -1
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm/configs/model_config.py +5 -5
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm/inference/forecaster.py +32 -2
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm/inference/modules.py +36 -11
- {patchfm-3.2.0 → patchfm-3.3.0/src/patchfm.egg-info}/PKG-INFO +1 -1
- {patchfm-3.2.0 → patchfm-3.3.0}/LICENSE +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/README.md +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/setup.cfg +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm/__init__.py +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm.egg-info/SOURCES.txt +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm.egg-info/dependency_links.txt +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm.egg-info/requires.txt +0 -0
- {patchfm-3.2.0 → patchfm-3.3.0}/src/patchfm.egg-info/top_level.txt +0 -0
|
@@ -5,13 +5,13 @@ from dataclasses import asdict, dataclass, field
|
|
|
5
5
|
class PatchFMConfig:
|
|
6
6
|
max_seq_len: int = 1024
|
|
7
7
|
patch_len: int = 32
|
|
8
|
-
d_model: int =
|
|
9
|
-
n_heads: int =
|
|
10
|
-
n_layers_encoder: int =
|
|
8
|
+
d_model: int = 1024
|
|
9
|
+
n_heads: int = 16
|
|
10
|
+
n_layers_encoder: int = 20
|
|
11
11
|
quantiles: list[float] = field(default_factory=lambda: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
|
|
12
12
|
|
|
13
|
-
compile: bool =
|
|
14
|
-
full_leakage: bool =
|
|
13
|
+
compile: bool = False
|
|
14
|
+
full_leakage: bool = False
|
|
15
15
|
|
|
16
16
|
def __getitem__(self, key):
|
|
17
17
|
return getattr(self, key)
|
|
@@ -4,7 +4,8 @@ import torch.nn as nn
|
|
|
4
4
|
from einops import rearrange
|
|
5
5
|
|
|
6
6
|
from patchfm.inference.modules import (CausalRevIN, PatchFM, ResidualBlock,
|
|
7
|
-
SeqTypeConverter, TransformerEncoder
|
|
7
|
+
SeqTypeConverter, TransformerEncoder,
|
|
8
|
+
quantile_widths, monotone_quantiles)
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# --- Forecaster Model ---
|
|
@@ -22,6 +23,9 @@ class Forecaster(nn.Module):
|
|
|
22
23
|
self.n_quantiles = len(self.quantiles)
|
|
23
24
|
self.max_patches = self.max_seq_len // self.patch_len
|
|
24
25
|
|
|
26
|
+
widths, self.median_idx = quantile_widths(self.quantiles)
|
|
27
|
+
self.register_buffer("quantile_widths", widths, persistent=False)
|
|
28
|
+
|
|
25
29
|
if config["full_leakage"]:
|
|
26
30
|
print("Loading full leakage model from HuggingFace Hub...")
|
|
27
31
|
model_name = "vilhess/PatchFM-Leakage"
|
|
@@ -48,7 +52,7 @@ class Forecaster(nn.Module):
|
|
|
48
52
|
self.revin = CausalRevIN()
|
|
49
53
|
self.proj_embedding = ResidualBlock(
|
|
50
54
|
in_dim=self.patch_len,
|
|
51
|
-
hid_dim=
|
|
55
|
+
hid_dim=self.d_model,
|
|
52
56
|
out_dim=self.d_model
|
|
53
57
|
)
|
|
54
58
|
self.transformer_encoder = TransformerEncoder(
|
|
@@ -103,6 +107,19 @@ class Forecaster(nn.Module):
|
|
|
103
107
|
x = x[:, -1:, :] # Keep only the last patch for autoregressive forecasting
|
|
104
108
|
|
|
105
109
|
forecasting = self.proj_output(x)
|
|
110
|
+
forecasting = rearrange(
|
|
111
|
+
forecasting,
|
|
112
|
+
"b 1 (pl q) -> b 1 pl q",
|
|
113
|
+
pl=self.patch_len,
|
|
114
|
+
q=self.n_quantiles,
|
|
115
|
+
)
|
|
116
|
+
forecasting = monotone_quantiles(
|
|
117
|
+
forecasting, self.quantile_widths, self.median_idx, width_factor=2.0
|
|
118
|
+
)
|
|
119
|
+
forecasting = rearrange(
|
|
120
|
+
forecasting,
|
|
121
|
+
"b 1 pl q -> b 1 (pl q)",
|
|
122
|
+
)
|
|
106
123
|
forecasting = self.revin(forecasting, mode="denorm")
|
|
107
124
|
|
|
108
125
|
# Reshape to (bs, patch_num, patch_len, n_quantiles)
|
|
@@ -122,6 +139,19 @@ class Forecaster(nn.Module):
|
|
|
122
139
|
x = self.transformer_encoder(x)
|
|
123
140
|
x = x[:, -1:, :] # Keep only the last patch for autoregressive forecasting
|
|
124
141
|
forecasting = self.proj_output(x)
|
|
142
|
+
forecasting = rearrange(
|
|
143
|
+
forecasting,
|
|
144
|
+
"b 1 (pl q) -> b 1 pl q",
|
|
145
|
+
pl=self.patch_len,
|
|
146
|
+
q=self.n_quantiles,
|
|
147
|
+
)
|
|
148
|
+
forecasting = monotone_quantiles(
|
|
149
|
+
forecasting, self.quantile_widths, self.median_idx, width_factor=2.0
|
|
150
|
+
)
|
|
151
|
+
forecasting = rearrange(
|
|
152
|
+
forecasting,
|
|
153
|
+
"b 1 pl q -> b 1 (pl q)",
|
|
154
|
+
)
|
|
125
155
|
forecasting = self.revin(forecasting, mode="denorm")
|
|
126
156
|
|
|
127
157
|
# Reshape to (bs*n_quantiles, patch_num, patch_len, n_quantiles)
|
|
@@ -87,9 +87,8 @@ def nanstd(o, dim, keepdim=False):
|
|
|
87
87
|
return std
|
|
88
88
|
|
|
89
89
|
class CausalRevIN(nn.Module):
|
|
90
|
-
def __init__(self
|
|
90
|
+
def __init__(self):
|
|
91
91
|
super().__init__()
|
|
92
|
-
self.eps = eps
|
|
93
92
|
self.cached_mean = None
|
|
94
93
|
self.cached_std = None
|
|
95
94
|
|
|
@@ -159,7 +158,7 @@ class CausalRevIN(nn.Module):
|
|
|
159
158
|
self.cached_cumsum_x2 = cumsum_x2[:, -1:, :]
|
|
160
159
|
|
|
161
160
|
var = (cumsum_x2 - 2 * mean * cumsum_x + counts * mean**2) / counts
|
|
162
|
-
std = torch.sqrt(var
|
|
161
|
+
std = torch.sqrt(var.clamp_min(0)).clamp_min(1e-3)
|
|
163
162
|
|
|
164
163
|
return mean, std
|
|
165
164
|
|
|
@@ -205,9 +204,10 @@ class MultiHeadAttention(nn.Module):
|
|
|
205
204
|
super().__init__()
|
|
206
205
|
assert d_model%n_heads==0, f"d_model ({d_model}) must be divisible by n_heads ({n_heads})"
|
|
207
206
|
|
|
208
|
-
self.WQ = nn.Linear(d_model, d_model)
|
|
209
|
-
self.WK = nn.Linear(d_model, d_model)
|
|
210
|
-
self.WV = nn.Linear(d_model, d_model)
|
|
207
|
+
self.WQ = nn.Linear(d_model, d_model, bias=False)
|
|
208
|
+
self.WK = nn.Linear(d_model, d_model, bias=False)
|
|
209
|
+
self.WV = nn.Linear(d_model, d_model, bias=False)
|
|
210
|
+
self.gate = nn.Linear(d_model, d_model, bias=False)
|
|
211
211
|
|
|
212
212
|
self.out_proj = nn.Linear(d_model, d_model)
|
|
213
213
|
|
|
@@ -238,6 +238,8 @@ class MultiHeadAttention(nn.Module):
|
|
|
238
238
|
is_causal = False
|
|
239
239
|
offset += (context - 1)
|
|
240
240
|
|
|
241
|
+
gated = self.gate(q)
|
|
242
|
+
|
|
241
243
|
q = self.WQ(q).reshape(bs, -1, self.n_heads, self.head_dim).transpose(1, 2)
|
|
242
244
|
k = self.WK(k).reshape(bs, -1, self.n_heads, self.head_dim).transpose(1, 2)
|
|
243
245
|
v = self.WV(v).reshape(bs, -1, self.n_heads, self.head_dim).transpose(1, 2)
|
|
@@ -261,9 +263,10 @@ class MultiHeadAttention(nn.Module):
|
|
|
261
263
|
k = self.k_norm(k)
|
|
262
264
|
q = self.per_dim_scale(q)
|
|
263
265
|
|
|
264
|
-
values = nn.functional.scaled_dot_product_attention(q, k, v, is_causal=is_causal)
|
|
266
|
+
values = nn.functional.scaled_dot_product_attention(q, k, v, is_causal=is_causal, scale=1.0)
|
|
265
267
|
|
|
266
268
|
values = values.transpose(1, 2).reshape(bs, -1, dim)
|
|
269
|
+
values = gated.sigmoid() * values
|
|
267
270
|
values = self.out_proj(values)
|
|
268
271
|
return values
|
|
269
272
|
|
|
@@ -293,9 +296,9 @@ class FeedForward(nn.Module):
|
|
|
293
296
|
class TransformerEncoderLayer(nn.Module):
|
|
294
297
|
def __init__(self, d_model, n_heads, last=False):
|
|
295
298
|
super().__init__()
|
|
296
|
-
self.ln1 = nn.
|
|
299
|
+
self.ln1 = nn.RMSNorm(d_model)
|
|
297
300
|
self.attn = MultiHeadAttention(d_model=d_model, n_heads=n_heads, last=last)
|
|
298
|
-
self.ln2 = nn.
|
|
301
|
+
self.ln2 = nn.RMSNorm(d_model)
|
|
299
302
|
self.ff = FeedForward(d_model=d_model)
|
|
300
303
|
|
|
301
304
|
def forward(self, x):
|
|
@@ -314,12 +317,34 @@ class TransformerEncoder(nn.Module):
|
|
|
314
317
|
]
|
|
315
318
|
)
|
|
316
319
|
self.layers.append(TransformerEncoderLayer(d_model=d_model, n_heads=n_heads, last=True))
|
|
317
|
-
self.norm = nn.
|
|
320
|
+
self.norm = nn.RMSNorm(d_model)
|
|
318
321
|
|
|
319
322
|
def forward(self, x):
|
|
320
323
|
for layer in self.layers:
|
|
321
324
|
x = layer(x)
|
|
322
325
|
return self.norm(x)
|
|
326
|
+
|
|
327
|
+
def quantile_widths(quantiles):
|
|
328
|
+
m = quantiles.index(0.5)
|
|
329
|
+
widths = [0.0] * len(quantiles)
|
|
330
|
+
for i in range(m + 1, len(quantiles)):
|
|
331
|
+
widths[i] = quantiles[i] - quantiles[i - 1]
|
|
332
|
+
for i in range(m):
|
|
333
|
+
widths[i] = quantiles[i + 1] - quantiles[i]
|
|
334
|
+
return torch.tensor(widths), m
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def monotone_quantiles(raw, widths, m, width_factor=2.0):
|
|
338
|
+
inc = width_factor * widths.to(raw.dtype) * torch.nn.functional.softplus(raw)
|
|
339
|
+
median = raw[..., m : m + 1]
|
|
340
|
+
up = torch.cumsum(inc[..., m + 1 :], dim=-1)
|
|
341
|
+
down = torch.flip(
|
|
342
|
+
torch.cumsum(torch.flip(inc[..., :m], dims=[-1]), dim=-1), dims=[-1]
|
|
343
|
+
)
|
|
344
|
+
big = torch.finfo(raw.dtype).max
|
|
345
|
+
return torch.cat([median - down, median, median + up], dim=-1).clamp(
|
|
346
|
+
min=-big, max=big
|
|
347
|
+
)
|
|
323
348
|
|
|
324
349
|
class PatchFM(nn.Module, PyTorchModelHubMixin):
|
|
325
350
|
def __init__(self, config):
|
|
@@ -337,7 +362,7 @@ class PatchFM(nn.Module, PyTorchModelHubMixin):
|
|
|
337
362
|
self.revin = CausalRevIN()
|
|
338
363
|
self.proj_embedding = ResidualBlock(
|
|
339
364
|
in_dim=self.patch_len,
|
|
340
|
-
hid_dim=
|
|
365
|
+
hid_dim=self.d_model,
|
|
341
366
|
out_dim=self.d_model
|
|
342
367
|
)
|
|
343
368
|
self.transformer_encoder = TransformerEncoder(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|