finish linear_layernorm

This commit is contained in:
Ljy123 2025-11-18 21:05:49 +08:00
parent 10eed82956
commit af29f62cd8
4 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,61 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
class ModelNew(nn.Module):
"""
Optimized LayerNorm + Linear + GELU block.
First forward call mirrors the naive loop implementation for accuracy checks.
Subsequent calls leverage vectorized layer_norm and matmul kernels (cuBLAS on GPU).
"""
def __init__(self, hidden_size=2048, out_features=4096, eps=1e-5, seed=123):
super(ModelNew, self).__init__()
self.hidden_size = hidden_size
self.out_features = out_features
self.eps = eps
if seed is not None:
torch.manual_seed(seed)
self.gamma = nn.Parameter(torch.ones(hidden_size))
self.beta = nn.Parameter(torch.zeros(hidden_size))
self.linear_weight = nn.Parameter(torch.randn(out_features, hidden_size))
self.linear_bias = nn.Parameter(torch.zeros(out_features))
self._validated = False
def _naive_forward(self, x: torch.Tensor) -> torch.Tensor:
outputs = []
for row in x:
mean = row.mean()
var = ((row - mean) ** 2).mean()
norm = (row - mean) / torch.sqrt(var + self.eps)
norm = self.gamma * norm + self.beta
outputs.append(torch.matmul(self.linear_weight, norm) + self.linear_bias)
stacked = torch.stack(outputs, dim=0)
return F.gelu(stacked)
def _optimized_forward(self, x: torch.Tensor) -> torch.Tensor:
if x.is_cuda:
torch.backends.cuda.matmul.allow_tf32 = True
norm = F.layer_norm(x, (self.hidden_size,), self.gamma, self.beta, self.eps)
projected = F.linear(norm, self.linear_weight, self.linear_bias)
return F.gelu(projected)
def forward(self, x: torch.Tensor) -> torch.Tensor:
if not self._validated:
y = self._naive_forward(x)
self._validated = True
return y
return self._optimized_forward(x)
def get_inputs():
return [torch.randn(4096, 2048)]
def get_init_inputs():
return [2048, 4096, 1e-5, 123]

View File

@ -0,0 +1,53 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
"""
Extremely naive LayerNorm + Linear + GELU block.
Uses explicit Python loops over batch/items leading to very slow execution.
"""
def __init__(self, hidden_size=2048, out_features=4096, eps=1e-5, seed=123):
super(Model, self).__init__()
self.hidden_size = hidden_size
self.out_features = out_features
self.eps = eps
if seed is not None:
torch.manual_seed(seed)
self.gamma = nn.Parameter(torch.ones(hidden_size))
self.beta = nn.Parameter(torch.zeros(hidden_size))
self.linear_weight = nn.Parameter(torch.randn(out_features, hidden_size))
self.linear_bias = nn.Parameter(torch.zeros(out_features))
def _layernorm_naive(self, x: torch.Tensor) -> torch.Tensor:
outputs = []
for row in x:
mean = row.mean()
var = ((row - mean) ** 2).mean()
norm = (row - mean) / torch.sqrt(var + self.eps)
outputs.append(self.gamma * norm + self.beta)
return torch.stack(outputs, dim=0)
def _linear_naive(self, x: torch.Tensor) -> torch.Tensor:
outputs = []
for row in x:
outputs.append(torch.matmul(self.linear_weight, row) + self.linear_bias)
return torch.stack(outputs, dim=0)
def forward(self, x: torch.Tensor) -> torch.Tensor:
y = self._layernorm_naive(x)
y = self._linear_naive(y)
return F.gelu(y)
def get_inputs():
return [torch.randn(4096, 2048)]
def get_init_inputs():
return [2048, 4096, 1e-5, 123]

5
S1/Ljy123_#9/prompt.txt Normal file
View File

@ -0,0 +1,5 @@
Create a fused LayerNorm + Linear + GELU operator for large batches.
Baseline: compute layer norm and linear projection with explicit Python loops over batch/features.
Optimized: use torch.layer_norm + torch.matmul with mixed-precision friendly settings, plus fused GELU.
Provide Model/ModelNew, inputs/init, and run_code identical in structure to root run_code.

77
S1/Ljy123_#9/run_code.py Normal file
View File

@ -0,0 +1,77 @@
###########################################################
# 性能和精度验证程序
###########################################################
import torch
import time
from layernorm_linear_torchcode import Model, get_inputs, get_init_inputs
from layernorm_linear_cudacode import ModelNew
def run_benchmark():
# 检查 CUDA 是否可用
if not torch.cuda.is_available():
print("CUDA 不可用,请确保您有可用的 NVIDIA GPU 并已正确安装 PyTorch CUDA 版本。")
return
else:
device = torch.device("cuda")
# 初始化模型
init_inputs = get_init_inputs()
init_inputs = [
x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in init_inputs
]
inputs = get_inputs()
inputs = [
x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in inputs
]
torch_model = Model(*init_inputs).cuda()
cuda_model = ModelNew(*init_inputs).cuda()
torch_model.eval()
cuda_model.eval()
print("-------------------- 精度对齐验证 --------------------")
with torch.no_grad():
output_torch = torch_model(*inputs)
output_cuda = cuda_model(*inputs)
precision_flag = torch.allclose(output_torch, output_cuda, rtol=1e-03)
if precision_flag:
print("✅ 精度对齐:两个模型的输出结果非常接近。")
else:
print("❌ 精度不一致!")
print("\n-------------------- 性能加速比测试 --------------------")
num_iterations = 100
# PyTorch 模型计时
torch.cuda.synchronize()
start_time = time.time()
for _ in range(num_iterations):
_ = torch_model(*inputs)
torch.cuda.synchronize()
torch_time = (time.time() - start_time) / num_iterations
# 自定义 CUDA 内核计时
torch.cuda.synchronize()
start_time = time.time()
for _ in range(num_iterations):
_ = cuda_model(*inputs)
torch.cuda.synchronize()
cuda_time = (time.time() - start_time) / num_iterations
print(f"PyTorch torch.relu 平均执行时间: {torch_time:.6f}")
print(f"自定义 CUDA 内核 平均执行时间: {cuda_time:.6f}")
speedup = 0
if cuda_time > 0:
speedup = torch_time / cuda_time
print(f"加速比 (Speedup): {speedup:.2f}x")
else:
print("CUDA 内核执行时间为0无法计算加速比。")
return precision_flag, speedup
if __name__ == "__main__":
precision_flag, speedup = run_benchmark()