huggingface / huggingface/transformers

[performance/precision] adding `jit.script` to activation functions

Open
#13,997 4 comments 0 reactions 0 assignees View on GitHub
WIP
Dominant language
Python
Stars
166k
Forks
34.6k
Avg merge
3d 9h
Merged PRs (30d)
281

Description

# 🚀 Feature request

### Switch our activation functions to use `@torch.jit.script`

Over at BigScience we have been trying to figure out mismatches between Megatron-LM and HF Transformers when it comes to inference under fp16. There are several mismatches, this one discusses activation functions. And proposes to improve HF's models based on that.

So Megatron uses `@torch.jit.script` for its activation functions, which leads to 2 things:

1. faster performance
2. more correct math under fp16 or amp/fp16. Quoting @ngimel:

> ... that’s due to fusion. Fuser does intermediate operations in fp32, and thus produces more accurate results than simple function that truncates each intermediate to half.

(I need to double check on amp/fp16 - I'm making an assumption here)

So perhaps we should switch our activation functions to use `@torch.jit.script` too?

Caveats:

1. it appears that when using `@torch.jit.script` one may have to write out the `bwd` part explicitly, see:

https://github.com/NVIDIA/Megatron-LM/blob/b31e1296354e979722627a6c4dedafe19b51fa97/megatron/model/fused_bias_gelu.py#L27-L56

2. This will change the results slightly (back-compat OK?) but it should produce more correct results!

You can see how the 2 functions diverge:

```
import torch
import random

seed = 42
random.seed(seed) # python RNG
torch.manual_seed(seed) # cpu + cuda
torch.cuda.manual_seed_all(seed) # multi-gpu
torch.backends.cudnn.enabled = True

width = 128
input = torch.rand((1,5,width*4)).cuda().half()

@torch.jit.script
def gelu_megatron_fwd_jit(x):
return x * 0.5 * (1.0 + torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x)))

def gelu_megatron_fwd(x):
return x * 0.5 * (1.0 + torch.tanh(0.79788456 * x * (1 + 0.044715 * x * x)))

output = gelu_megatron_fwd(input)
output_jit = gelu_megatron_fwd_jit(input)
# have to run 2nd time for jit to kick in!
output_jit = gelu_megatron_fwd_jit(input)

torch.testing.assert_equal(output, output_jit, check_stride=False)
```

gives:

```
AssertionError: Tensors are not equal!

Mismatched elements: 800 / 2560 (31.2%)
Greatest absolute difference: 0.00048828125 at (0, 0, 1)
Greatest relative difference: 0.0009828009828009828 at (0, 0, 2)
```

@patrickvonplaten, @patil-suraj, @sgugger, @LysandreJik

Contributor guide

Open the contributing guide

Research direction

Start by locating the Transformers activation-function implementations and compare them with Megatron-LM's fused_bias_gelu.py, especially lines 27-56. Measure fp16 or AMP precision and performance before deciding which functions need scripted implementations; done means the selected activations use the approach consistently without breaking supported behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
machine-learning, performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.