main
py 869 lines 29.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Custom activation functions."""
4
5 import math
6
7 import torch
8 import torch.nn as nn
9 import torch.nn.functional as F
10
11 from vllm import ir
12 from vllm.logger import init_logger
13 from vllm.model_executor.custom_op import CustomOp
14 from vllm.platforms import CpuArchEnum, current_platform
15 from vllm.triton_utils import tl, triton
16 from vllm.utils.collection_utils import LazyDict
17
18 logger = init_logger(__name__)
19
20
21 @triton.jit
22 def _swiglustep_and_mul_kernel(
23 o_ptr,
24 o_stride,
25 x_ptr,
26 x_stride,
27 limit: tl.constexpr,
28 d: tl.constexpr,
29 BLOCK_SIZE: tl.constexpr,
30 ) -> None:
31 i = tl.program_id(axis=0).to(tl.int64)
32 j = tl.program_id(axis=1)
33 o_row_ptr = o_ptr + o_stride * i
34 x_row_ptr = x_ptr + x_stride * i
35 offsets = j * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
36 mask = offsets < d
37
38 gate = tl.load(x_row_ptr + offsets, mask=mask).to(tl.float32)
39 up = tl.load(x_row_ptr + offsets + d, mask=mask).to(tl.float32)
40
41 gate_silu = tl.sigmoid(gate) * gate
42 gate_clamped = tl.minimum(gate_silu, limit)
43 up_clamped = tl.minimum(tl.maximum(up, -limit), limit)
44
45 result = gate_clamped * up_clamped
46 result = result.to(x_ptr.dtype.element_ty)
47 tl.store(o_row_ptr + offsets, result, mask=mask)
48
49
50 def swiglustep_and_mul_triton(
51 output: torch.Tensor, input: torch.Tensor, limit: float = 7.0
52 ):
53 b, n = input.shape
54 assert input.ndim == 2
55 assert n % 2 == 0
56 d = n // 2
57
58 def grid(meta):
59 return (b, triton.cdiv(d, meta["BLOCK_SIZE"]))
60
61 _swiglustep_and_mul_kernel[grid](
62 output,
63 output.stride(0),
64 input,
65 input.stride(0),
66 limit=limit,
67 d=d,
68 BLOCK_SIZE=1024,
69 )
70
71
72 # --8<-- [start:fatrelu_and_mul]
73 @CustomOp.register("fatrelu_and_mul")
74 class FatreluAndMul(CustomOp):
75 """An activation function for FATReLU.
76
77 The function computes x -> FATReLU(x[:d]) * x[d:] where
78 d = x.shape[-1] // 2.
79 This is used in openbmb/MiniCPM-S-1B-sft.
80
81 Shapes:
82 x: (num_tokens, 2 * d) or (batch_size, seq_len, 2 * d)
83 return: (num_tokens, d) or (batch_size, seq_len, d)
84 """
85
86 # --8<-- [end:fatrelu_and_mul]
87
88 def __init__(self, threshold: float = 0.0):
89 super().__init__()
90 self.threshold = threshold
91 if current_platform.is_cuda_alike() or current_platform.is_xpu():
92 self.op = torch.ops._C.fatrelu_and_mul
93 elif current_platform.is_cpu():
94 self._forward_method = self.forward_native
95
96 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
97 d = x.shape[-1] // 2
98 x1 = x[..., :d]
99 x2 = x[..., d:]
100 x1 = F.threshold(x1, self.threshold, 0.0)
101 return x1 * x2
102
103 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
104 d = x.shape[-1] // 2
105 output_shape = x.shape[:-1] + (d,)
106 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
107 self.op(out, x, self.threshold)
108 return out
109
110 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
111 return self.forward_cuda(x)
112
113
114 # --8<-- [start:silu_and_mul]
115 @CustomOp.register("silu_and_mul")
116 class SiluAndMul(CustomOp):
117 """An activation function for SwiGLU.
118
119 The function computes x -> silu(x[:d]) * x[d:] where d = x.shape[-1] // 2.
120
121 Shapes:
122 x: (num_tokens, 2 * d) or (batch_size, seq_len, 2 * d)
123 return: (num_tokens, d) or (batch_size, seq_len, d)
124 """
125
126 # --8<-- [end:silu_and_mul]
127
128 def __init__(self, *, compile_native: bool = True):
129 super().__init__(compile_native=compile_native)
130 if (
131 current_platform.is_cuda_alike()
132 or current_platform.is_xpu()
133 or (
134 current_platform.is_cpu()
135 and current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC
136 )
137 ):
138 self.op = torch.ops._C.silu_and_mul
139
140 @staticmethod
141 def forward_native(x: torch.Tensor) -> torch.Tensor:
142 """PyTorch-native implementation equivalent to forward()."""
143 d = x.shape[-1] // 2
144 return F.silu(x[..., :d]) * x[..., d:]
145
146 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
147 d = x.shape[-1] // 2
148 output_shape = x.shape[:-1] + (d,)
149 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
150 self.op(out, x)
151 return out
152
153 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
154 return self.forward_cuda(x)
155
156 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
157 if current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC:
158 return self.forward_cuda(x)
159 return self.forward_native(x)
160
161
162 @CustomOp.register("situ_and_mul")
163 class SituAndMul(CustomOp):
164 """SituGLU activation used by Kimi models.
165
166 Computes beta * tanh(gate / beta) * sigmoid(gate) * up. When
167 ``linear_beta`` is set, the up projection is also softly clipped with
168 linear_beta * tanh(up / linear_beta).
169 """
170
171 def __init__(
172 self,
173 beta: float = 1.0,
174 linear_beta: float | None = None,
175 *,
176 compile_native: bool = True,
177 ):
178 super().__init__(compile_native=compile_native)
179 self.beta = float(beta)
180 self.linear_beta = None if linear_beta is None else float(linear_beta)
181 if current_platform.is_cuda_alike() or current_platform.is_xpu():
182 self.op = torch.ops._C.situ_and_mul
183
184 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
185 d = x.shape[-1] // 2
186 gate = x[..., :d].float()
187 up = x[..., d:].float()
188 gate = self.beta * torch.tanh(gate / self.beta) * torch.sigmoid(gate)
189 if self.linear_beta is not None:
190 up = self.linear_beta * torch.tanh(up / self.linear_beta)
191 return (gate * up).to(x.dtype)
192
193 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
194 # Fused CUDA/XPU kernel: writes straight to `out`, no fp32 temporaries.
195 # linear_beta<=0 signals "unset" to the kernel (up passed through).
196 d = x.shape[-1] // 2
197 out = torch.empty(x.shape[:-1] + (d,), dtype=x.dtype, device=x.device)
198 self.op(
199 out, x, self.beta, -1.0 if self.linear_beta is None else self.linear_beta
200 )
201 return out
202
203 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
204 return self.forward_cuda(x)
205
206
207 @CustomOp.register("silu_and_mul_with_clamp")
208 class SiluAndMulWithClamp(CustomOp):
209 """SwiGLU activation with input clamping (used by some MoE shared experts).
210
211 Computes:
212 gate = clamp(x[..., :d], max=swiglu_limit)
213 up = clamp(x[..., d:], min=-swiglu_limit, max=swiglu_limit)
214 out = gate * sigmoid(alpha * gate) * (up + beta)
215 where d = x.shape[-1] // 2. The defaults alpha=1.0, beta=0.0 reduce this to
216 ``silu(gate) * up``; SwiGLU-OAI style models pass alpha (sigmoid scale) and
217 beta=1.0 (up bias).
218
219 Shapes:
220 x: (num_tokens, 2 * d) or (batch_size, seq_len, 2 * d)
221 return: (num_tokens, d) or (batch_size, seq_len, d)
222 """
223
224 def __init__(
225 self,
226 swiglu_limit: float,
227 alpha: float = 1.0,
228 beta: float = 0.0,
229 *,
230 compile_native: bool = True,
231 ):
232 super().__init__(compile_native=compile_native)
233 self.swiglu_limit = float(swiglu_limit)
234 self.alpha = float(alpha)
235 self.beta = float(beta)
236 if current_platform.is_cuda_alike():
237 self.op = torch.ops._C.silu_and_mul_with_clamp
238
239 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
240 d = x.shape[-1] // 2
241 gate = torch.clamp(x[..., :d], max=self.swiglu_limit)
242 up = torch.clamp(x[..., d:], min=-self.swiglu_limit, max=self.swiglu_limit)
243 return gate * torch.sigmoid(self.alpha * gate) * (up + self.beta)
244
245 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
246 d = x.shape[-1] // 2
247 output_shape = x.shape[:-1] + (d,)
248 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
249 self.op(out, x, self.swiglu_limit, self.alpha, self.beta)
250 return out
251
252 def forward_hip(self, x: torch.Tensor) -> torch.Tensor:
253 # Limit the ROCm _C path to the default case to avoid the
254 # precision loss MiniMax saw without fp32 intermediates.
255 if self.alpha == 1.0 and self.beta == 0.0:
256 return self.forward_cuda(x)
257 return self.forward_native(x)
258
259 def extra_repr(self) -> str:
260 return (
261 f"swiglu_limit={self.swiglu_limit!r}, "
262 f"alpha={self.alpha!r}, beta={self.beta!r}"
263 )
264
265
266 # --8<-- [start:mul_and_silu]
267 @CustomOp.register("mul_and_silu")
268 class MulAndSilu(CustomOp):
269 """An activation function for SwiGLU.
270
271 The function computes x -> x[:d] * silu(x[d:]) where d = x.shape[-1] // 2.
272
273 Shapes:
274 x: (num_tokens, 2 * d) or (batch_size, seq_len, 2 * d)
275 return: (num_tokens, d) or (batch_size, seq_len, d)
276 """
277
278 # --8<-- [end:mul_and_silu]
279
280 def __init__(self):
281 super().__init__()
282 if current_platform.is_cuda_alike() or current_platform.is_xpu():
283 self.op = torch.ops._C.mul_and_silu
284 elif current_platform.is_cpu():
285 self._forward_method = self.forward_native
286
287 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
288 """PyTorch-native implementation equivalent to forward()."""
289 d = x.shape[-1] // 2
290 return x[..., :d] * F.silu(x[..., d:])
291
292 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
293 d = x.shape[-1] // 2
294 output_shape = x.shape[:-1] + (d,)
295 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
296 self.op(out, x)
297 return out
298
299 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
300 return self.forward_cuda(x)
301
302
303 # --8<-- [start:gelu_and_mul_sparse]
304 @CustomOp.register("gelu_and_mul_sparse")
305 class GeluAndMulSparse(CustomOp):
306 """An activation function for GeluAndMulSparse.
307 This activation function is used in Gemma3n. It computes:
308 up_proj = self.up_proj(x)
309 gate_proj = self.gate_proj(x)
310 gate_proj = self._gaussian_topk(gate_proj) # sparsity
311 activations = self.act_fn(gate_proj) # gelu
312 down_proj = self.down_proj(activations * up_proj)
313 Shapes:
314 x: (num_tokens, 2 * d) or (batch_size, seq_len, 2 * d)
315 return: (num_tokens, d) or (batch_size, seq_len, d)
316 """
317
318 # --8<-- [end:gelu_and_mul_sparse]
319
320 def __init__(self, activation_sparsity: float, approximate: str = "none"):
321 super().__init__()
322 # Gelu.
323 self.approximate = approximate
324 if approximate not in ("none", "tanh"):
325 raise ValueError(f"Unknown approximate mode: {approximate}")
326 if current_platform.is_rocm() and approximate == "tanh":
327 # TODO:[ROCm] PyTorch native GELU with tanh is unstable with torch.compile
328 logger.warning_once(
329 "[ROCm] Pytorch's native GELU with tanh approximation is currently "
330 "unstable and produces garbage. Fallback to 'none' approximation."
331 )
332 self.approximate = "none"
333
334 # Sparsity.
335 if activation_sparsity == 0.0:
336 raise ValueError("activation_sparsity is 0.0. Please use GeluAndMul.")
337 target_sparsity_tensor = torch.tensor(activation_sparsity, dtype=torch.float32)
338 normal_dist = torch.distributions.normal.Normal(0, 1)
339 self.std_multiplier = normal_dist.icdf(target_sparsity_tensor).item()
340
341 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
342 """PyTorch-native implementation equivalent to forward()."""
343 return ir.ops.gelu_and_mul_sparse(x, self.std_multiplier, self.approximate)
344
345 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
346 return self.forward_native(x)
347
348
349 # --8<-- [start:gelu]
350 @CustomOp.register("gelu")
351 class GELU(CustomOp):
352 # --8<-- [end:gelu]
353
354 def __init__(self):
355 super().__init__()
356 if (
357 current_platform.is_cpu()
358 and current_platform.get_cpu_architecture() == CpuArchEnum.ARM
359 and hasattr(torch.ops._C, "activation_lut_bf16")
360 ):
361 self.op = torch.ops._C.activation_lut_bf16
362 else:
363 self.op = None
364
365 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
366 return F.gelu(x, approximate="none")
367
368 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
369 if self.op and x.dtype == torch.bfloat16 and x.is_contiguous():
370 out = torch.empty_like(x)
371 self.op(out, x, "gelu")
372 return out
373 return self.forward_native(x)
374
375 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
376 return self.forward_native(x)
377
378
379 # --8<-- [start:gelu_tanh]
380 @CustomOp.register("gelu_tanh")
381 class GELUTanh(CustomOp):
382 # --8<-- [end:gelu_tanh]
383
384 def __init__(self):
385 super().__init__()
386 if (
387 current_platform.is_cpu()
388 and current_platform.get_cpu_architecture() == CpuArchEnum.ARM
389 and hasattr(torch.ops._C, "gelu_tanh")
390 ):
391 self.op = torch.ops._C.gelu_tanh
392 else:
393 self.op = None
394
395 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
396 return F.gelu(x, approximate="tanh")
397
398 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
399 if self.op:
400 out = torch.empty_like(x)
401 self.op(out, x)
402 return out
403 return self.forward_native(x)
404
405 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
406 return self.forward_native(x)
407
408
409 # --8<-- [start:gelu_and_mul]
410 @CustomOp.register("gelu_and_mul")
411 class GeluAndMul(CustomOp):
412 """An activation function for GeGLU.
413
414 The function computes x -> GELU(x[:d]) * x[d:] where d = x.shape[-1] // 2.
415
416 Shapes:
417 x: (batch_size, seq_len, 2 * d) or (num_tokens, 2 * d)
418 return: (batch_size, seq_len, d) or (num_tokens, d)
419 """
420
421 # --8<-- [end:gelu_and_mul]
422
423 def __init__(self, approximate: str = "none"):
424 super().__init__()
425 self.approximate = approximate
426 if approximate not in ("none", "tanh"):
427 raise ValueError(f"Unknown approximate mode: {approximate}")
428 if (
429 current_platform.is_cuda_alike()
430 or current_platform.is_xpu()
431 or (
432 current_platform.is_cpu()
433 and current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC
434 )
435 ):
436 if approximate == "none":
437 self.op = torch.ops._C.gelu_and_mul
438 elif approximate == "tanh":
439 self.op = torch.ops._C.gelu_tanh_and_mul
440 if current_platform.is_rocm() and approximate == "tanh":
441 logger.warning_once(
442 "[ROCm] PyTorch's native GELU with tanh approximation is unstable "
443 "with torch.compile. For native implementation, fallback to 'none' "
444 "approximation. The custom kernel implementation is unaffected."
445 )
446
447 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
448 """PyTorch-native implementation equivalent to forward()."""
449 # TODO: [ROCm] PyTorch's native GELU with tanh is unstable with torch.compile
450 approximate = self.approximate
451 if current_platform.is_rocm() and approximate == "tanh":
452 approximate = "none"
453 d = x.shape[-1] // 2
454 return F.gelu(x[..., :d], approximate=approximate) * x[..., d:]
455
456 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
457 d = x.shape[-1] // 2
458 output_shape = x.shape[:-1] + (d,)
459 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
460 self.op(out, x)
461 return out
462
463 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
464 return self.forward_cuda(x)
465
466 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
467 if current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC:
468 return self.forward_cuda(x)
469 return self.forward_native(x)
470
471 def extra_repr(self) -> str:
472 return f"approximate={repr(self.approximate)}"
473
474
475 # --8<-- [start:swigluoai_and_mul]
476 @CustomOp.register("swigluoai_and_mul")
477 class SwigluOAIAndMul(CustomOp):
478 # https://github.com/huggingface/transformers/blob/v4.55.0/src/transformers/models/gpt_oss/modeling_gpt_oss.py#L106-L110
479 # --8<-- [end:swigluoai_and_mul]
480
481 def __init__(self, alpha: float = 1.702, limit: float = 7.0):
482 super().__init__()
483 self.alpha = alpha
484 self.limit = limit
485
486 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
487 """PyTorch-native implementation equivalent to forward()."""
488 gate, up = x[..., ::2], x[..., 1::2]
489 gate = gate.clamp(min=None, max=self.limit)
490 up = up.clamp(min=-self.limit, max=self.limit)
491 glu = gate * torch.sigmoid(gate * self.alpha)
492 gated_output = (up + 1) * glu
493 return gated_output
494
495 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
496 d = x.shape[-1] // 2
497 output_shape = x.shape[:-1] + (d,)
498 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
499 torch.ops._C.swigluoai_and_mul(out, x, self.alpha, self.limit)
500 return out
501
502 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
503 return self.forward_cuda(x)
504
505 def extra_repr(self) -> str:
506 return f"alpha={repr(self.alpha)}, limit={repr(self.limit)}"
507
508
509 # --8<-- [start:swiglustep_and_mul]
510 @CustomOp.register("swiglustep_and_mul")
511 class SwigluStepAndMul(CustomOp):
512 """An activation function for SwiGLU with clamping.
513
514 Computes x -> silu(x[:d]).clamp(max=limit) * x[d:].clamp(-limit, limit)
515 where d = x.shape[-1] // 2.
516
517 Shapes:
518 x: (num_tokens, 2 * d) or (batch_size, seq_len, 2 * d)
519 return: (num_tokens, d) or (batch_size, seq_len, d)
520 """
521
522 def __init__(self, limit: float = 7.0):
523 super().__init__()
524 if limit is None:
525 raise ValueError("SwigluStepAndMul requires limit to be set.")
526 self.limit = limit
527
528 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
529 """PyTorch-native implementation equivalent to forward()."""
530 gate, up = x.chunk(2, dim=-1)
531 gate = F.silu(gate)
532 gate = gate.clamp(max=self.limit)
533 up = up.clamp(min=-self.limit, max=self.limit)
534 return gate * up
535
536 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
537 d = x.shape[-1] // 2
538 output_shape = x.shape[:-1] + (d,)
539 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
540 swiglustep_and_mul_triton(out, x, self.limit)
541 return out
542
543 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
544 d = x.shape[-1] // 2
545 output_shape = x.shape[:-1] + (d,)
546 out = torch.empty(output_shape, dtype=x.dtype, device=x.device)
547 torch.ops._C.swiglustep_and_mul(out, x, self.limit)
548 return out
549
550 def extra_repr(self) -> str:
551 return f"limit={repr(self.limit)}"
552
553
554 # --8<-- [start:gelu_new]
555 @CustomOp.register("gelu_new")
556 class NewGELU(CustomOp):
557 # --8<-- [end:gelu_new]
558
559 def __init__(self):
560 super().__init__()
561 if (
562 current_platform.is_cuda_alike()
563 or current_platform.is_cpu()
564 or current_platform.is_xpu()
565 ):
566 self.op = torch.ops._C.gelu_new
567
568 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
569 """PyTorch-native implementation equivalent to forward()."""
570 c = math.sqrt(2.0 / math.pi)
571 return 0.5 * x * (1.0 + torch.tanh(c * (x + 0.044715 * torch.pow(x, 3.0))))
572
573 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
574 out = torch.empty_like(x)
575 self.op(out, x)
576 return out
577
578 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
579 return self.forward_cuda(x)
580
581 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
582 if current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC:
583 return self.forward_cuda(x)
584 return self.forward_native(x)
585
586
587 # --8<-- [start:gelu_fast]
588 @CustomOp.register("gelu_fast")
589 class FastGELU(CustomOp):
590 # --8<-- [end:gelu_fast]
591
592 def __init__(self):
593 super().__init__()
594 if (
595 current_platform.is_cuda_alike()
596 or current_platform.is_cpu()
597 or current_platform.is_xpu()
598 ):
599 self.op = torch.ops._C.gelu_fast
600
601 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
602 """PyTorch-native implementation equivalent to forward()."""
603 return 0.5 * x * (1.0 + torch.tanh(x * 0.7978845608 * (1.0 + 0.044715 * x * x)))
604
605 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
606 out = torch.empty_like(x)
607 self.op(out, x)
608 return out
609
610 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
611 return self.forward_cuda(x)
612
613 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
614 if current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC:
615 return self.forward_cuda(x)
616 return self.forward_native(x)
617
618
619 # --8<-- [start:quick_gelu]
620 @CustomOp.register("quick_gelu")
621 class QuickGELU(CustomOp):
622 # https://github.com/huggingface/transformers/blob/main/src/transformers/activations.py#L90
623 # --8<-- [end:quick_gelu]
624
625 def __init__(self):
626 super().__init__()
627 if (
628 current_platform.is_cuda_alike()
629 or current_platform.is_cpu()
630 or current_platform.is_xpu()
631 ):
632 self.op = torch.ops._C.gelu_quick
633
634 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
635 """PyTorch-native implementation equivalent to forward()."""
636 return x * torch.sigmoid(1.702 * x)
637
638 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
639 out = torch.empty_like(x)
640 self.op(out, x)
641 return out
642
643 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
644 return self.forward_cuda(x)
645
646 def forward_cpu(self, x: torch.Tensor) -> torch.Tensor:
647 if current_platform.get_cpu_architecture() == CpuArchEnum.POWERPC:
648 return self.forward_cuda(x)
649 return self.forward_native(x)
650
651
652 # --8<-- [start:relu2]
653 @CustomOp.register("relu2")
654 class ReLUSquaredActivation(CustomOp):
655 """Applies the relu^2 activation introduced in https://arxiv.org/abs/2109.08668v2."""
656
657 # --8<-- [end:relu2]
658
659 def __init__(self, *, compile_native: bool = True):
660 super().__init__(compile_native=compile_native)
661 if current_platform.is_cuda_alike():
662 self.op = torch.ops._C.relu_squared
663 elif current_platform.is_xpu():
664 self.op = torch.ops._C.relu2_no_mul
665
666 def forward_native(self, x: torch.Tensor) -> torch.Tensor:
667 """PyTorch-native implementation equivalent to forward()."""
668 return torch.square(F.relu(x))
669
670 def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
671 out = torch.empty_like(x)
672 self.op(out, x)
673 return out
674
675 def forward_xpu(self, x: torch.Tensor) -> torch.Tensor:
676 return self.forward_cuda(x)
677
678
679 # --8<-- [start:xielu]
680 @CustomOp.register("xielu")
681 class XIELU(CustomOp):
682 """Applies the xIELU activation function introduced in https://arxiv.org/abs/2411.13010
683 If the user has installed the nickjbrowning/XIELU, we import xIELU CUDA
684 Otherwise, we emit a single warning and use xIELU Python
685 """
686
687 # --8<-- [end:xielu]
688
689 def __init__(
690 self,
691 alpha_p_init: float = 0.8,
692 alpha_n_init: float = 0.8,
693 beta: float = 0.5,
694 eps: float = -1e-6,
695 dtype: torch.dtype = torch.bfloat16,
696 with_vector_loads: bool = False,
697 ):
698 super().__init__()
699 self.alpha_p = nn.Parameter(
700 torch.log(torch.exp(torch.tensor(alpha_p_init, dtype=dtype)) - 1).unsqueeze(
701 0
702 )
703 )
704 self.alpha_n = nn.Parameter(
705 torch.log(
706 torch.exp(torch.tensor(alpha_n_init - beta, dtype=dtype)) - 1
707 ).unsqueeze(0)
708 )
709 self.register_buffer("beta", torch.tensor(beta, dtype=dtype))
710 self.register_buffer("eps", torch.tensor(eps, dtype=dtype))
711 self.with_vector_loads = with_vector_loads
712 # Temporary until xIELU CUDA fully implemented
713 self._beta_scalar = float(self.beta.detach().cpu().float().item())
714 self._eps_scalar = float(self.eps.detach().cpu().float().item())
715
716 self._xielu_cuda_obj = None
717 try:
718 import xielu.ops # noqa: F401
719
720 self._xielu_cuda_obj = torch.classes.xielu.XIELU()
721 msg = "Using experimental xIELU CUDA."
722 try:
723 from torch._dynamo import allow_in_graph
724
725 self._xielu_cuda_fn = allow_in_graph(self._xielu_cuda)
726 msg += " Enabled torch._dynamo for xIELU CUDA."
727 except Exception as err:
728 msg += (
729 f" Could not enable torch._dynamo for xIELU ({err}) - "
730 "this may result in slower performance."
731 )
732 self._xielu_cuda_fn = self._xielu_cuda
733 logger.warning_once(msg)
734 except Exception as err:
735 logger.warning_once(
736 "CUDA-fused xIELU not available (%s) –"
737 " falling back to a Python version.\n"
738 "For CUDA xIELU (experimental), `pip install git+https://github.com/nickjbrowning/XIELU`",
739 str(err),
740 )
741
742 def _xielu_python(self, x: torch.Tensor) -> torch.Tensor:
743 alpha_p = nn.functional.softplus(self.alpha_p)
744 alpha_n = self.beta + nn.functional.softplus(self.alpha_n)
745 return torch.where(
746 x > 0,
747 alpha_p * x * x + self.beta * x,
748 (torch.expm1(torch.min(x, self.eps)) - x) * alpha_n + self.beta * x,
749 )
750
751 def _xielu_cuda(self, x: torch.Tensor) -> torch.Tensor:
752 """Firewall function to prevent torch.compile from seeing .item()."""
753 assert self._xielu_cuda_obj is not None, "XIELU CUDA object must not be None"
754 original_shape = x.shape
755 # CUDA kernel expects 3D tensors, reshape if needed
756 while x.dim() < 3:
757 x = x.unsqueeze(0)
758 if x.dim() > 3:
759 x = x.view(-1, 1, x.size(-1))
760 if original_shape != x.shape:
761 logger.warning_once(
762 "Warning: xIELU input tensor expects 3 dimensions"
763 " but got (shape: %s). Reshaping to (shape: %s).",
764 original_shape,
765 x.shape,
766 )
767 result = self._xielu_cuda_obj.forward(
768 x,
769 self.alpha_p,
770 self.alpha_n,
771 # Temporary until xIELU CUDA fully implemented ->
772 # self.{beta,eps}.item()
773 self._beta_scalar,
774 self._eps_scalar,
775 self.with_vector_loads,
776 )
777 return result.view(original_shape)
778
779 def forward_native(self, input: torch.Tensor) -> torch.Tensor:
780 if self._xielu_cuda_obj is not None and input.is_cuda:
781 if not torch._dynamo.is_compiling():
782 return self._xielu_cuda_fn(input)
783 else:
784 logger.warning_once(
785 "torch._dynamo is compiling, using Python version of xIELU."
786 )
787 return self._xielu_python(input)
788
789 def forward_cuda(self, input: torch.Tensor) -> torch.Tensor:
790 return self.forward_native(input)
791
792
793 _ACTIVATION_REGISTRY = LazyDict(
794 {
795 "gelu": lambda: GELU(),
796 "gelu_fast": lambda: FastGELU(),
797 "gelu_new": lambda: NewGELU(),
798 "gelu_pytorch_tanh": lambda: _get_gelu_pytorch_tanh(),
799 "relu": lambda: nn.ReLU(),
800 "relu2": lambda: ReLUSquaredActivation(),
801 "silu": lambda: nn.SiLU(),
802 "swish": lambda: nn.SiLU(),
803 "quick_gelu": lambda: QuickGELU(),
804 "tanh": lambda: nn.Tanh(),
805 "sigmoid": lambda: nn.Sigmoid(),
806 "xielu": lambda: XIELU(),
807 }
808 )
809
810
811 def _get_gelu_pytorch_tanh() -> nn.Module:
812 """Get PyTorch GELU with tanh approximation, with ROCm fallback
813 and fast GELU for ARM."""
814 if current_platform.is_rocm():
815 # TODO:[ROCm] PyTorch native GELU with tanh is unstable with torch.compile
816 logger.warning_once(
817 "[ROCm] PyTorch's native GELU with tanh approximation is unstable. "
818 "Falling back to GELU(approximate='none')."
819 )
820 return nn.GELU(approximate="none")
821 if (
822 current_platform.is_cpu()
823 and current_platform.get_cpu_architecture() == CpuArchEnum.ARM
824 ):
825 return GELUTanh()
826 return nn.GELU(approximate="tanh")
827
828
829 def get_act_fn(act_fn_name: str) -> nn.Module:
830 """Get an activation function by name."""
831 act_fn_name = act_fn_name.lower()
832
833 if act_fn_name.startswith("torch.nn.modules."):
834 activation_name = act_fn_name.split(".")[-1]
835 if activation_name == "identity":
836 return nn.Identity()
837 act_fn_name = activation_name
838
839 if act_fn_name not in _ACTIVATION_REGISTRY:
840 raise ValueError(f"Activation function {act_fn_name!r} is not supported.")
841
842 return _ACTIVATION_REGISTRY[act_fn_name]
843
844
845 _ACTIVATION_AND_MUL_REGISTRY: LazyDict[nn.Module] = LazyDict(
846 {
847 "gelu": lambda: GeluAndMul(),
848 "gelu_pytorch_tanh": lambda: GeluAndMul(approximate="tanh"),
849 "silu": lambda: SiluAndMul(),
850 "swish": lambda: SiluAndMul(),
851 "geglu": lambda: GeluAndMul(),
852 "swigluoai": lambda: SwigluOAIAndMul(),
853 }
854 )
855
856
857 def get_act_and_mul_fn(act_fn_name: str, *, compile_native: bool = True) -> nn.Module:
858 """Get an activation-and-mul (i.e. SiluAndMul) function by name."""
859 act_fn_name = act_fn_name.lower()
860
861 if not compile_native and act_fn_name in ("silu", "swish"):
862 return SiluAndMul(compile_native=False)
863
864 try:
865 return _ACTIVATION_AND_MUL_REGISTRY[act_fn_name]
866 except KeyError:
867 raise ValueError(
868 f"Activation function {act_fn_name!r} is not supported."
869 ) from None