chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:18:33 +08:00
commit 4ececc111a
2017 changed files with 331736 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
from .optimized_linear import OptimizedLinear
from .config import LoRAConfig, QuantizationConfig
from .context_manager import Init, init_lora
+54
View File
@@ -0,0 +1,54 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
from dataclasses import dataclass, field
from typing import List
import torch
@dataclass
class LoRAConfig:
"""
Configuration settings for LoRAOptimizedLinear.
Attributes:
lora_r (int): LoRA attention dimension, also known as the rank. Defaults is 64.
lora_alpha (float): LoRA scaling factor, default is 16.
base_weight_sharding (int): The degree to which the base weights are sharded,
should typically be set to the data-parallel world size to maximize the memory
reduction benefits. Defaults to 1, which means this feature is disabled.
offload (bool): offload frozen parameters to cpu when not in use
offload_ratio (float): ratio of parameters to offload to cpu when not in use
delay_lora_init (bool): initialize lora parameters at time of model init or allow manual init later
target_mods (str): target module names to apply LoRA to, defaults to llama-3.1 arch
"""
lora_r: int = 64
lora_alpha: float = 16.
base_weight_sharding: int = 1
offload: bool = False
offload_ratio: float = 0.0
delay_lora_init: bool = False
target_mods: List[str] = field(
default_factory=lambda: ['q_proj', 'k_proj', 'v_proj', 'o_proj', 'gate_proj', 'up_proj', 'down_proj'])
@dataclass
class QuantizationConfig:
"""
Configuration settings for quantization for LoRAOptimizedLinear, QuantizedLinear,
and QuantizedParameter
Attributes:
q_bits (int): The number of bits used for quantization. Default is 8.
mantissa_bits (int): The number of bits reserved for the mantissa in fixed-point quantization. Default is 3.
group_size (int): The number of elements used for quantization. Default is 512.
q_dtype (torch.dtype): The data type to quantize to. Default is uint8. (in CUDA, buffers are allocated as
uint8, but inside the kernels the quantization is done to fp8)
"""
q_bits: int = 8
mantissa_bits: int = 3
group_size: int = 512
q_dtype: torch.dtype = torch.uint8
+90
View File
@@ -0,0 +1,90 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
from .optimized_linear import LoRAOptimizedLinear, OptimizedLinear
import torch
try:
import transformers
except ImportError:
transformers = None
def init_lora(model):
model.requires_grad_(False)
for m in model.modules():
if isinstance(m, LoRAOptimizedLinear):
m.init_lora()
class Init(object):
"""
Init context wrapper similar in style to zero.Init. Allows for injecting OptimizedLinear during model
construction which will shard base weights and reduce overall memory usage during model init. Primarily
useful when initializing a model via transformers.AutoModelForCausalLM.
Example usage:
lora_config = deepspeed.linear.LoRAConfig(..)
quant_config = deepspeed.linear.QuantizationConfig(..)
with deepspeed.linear.Init(lora_config=lora_config, quant_config=quant_config):
model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3.1-405B")
"""
def __init__(self, lora_config=None, quant_config=None):
self._orig_nn_linear = torch.nn.Linear
self._orig_causallm_pretrained = None
if transformers != None:
self._orig_causallm_pretrained = transformers.AutoModelForCausalLM.from_pretrained
self._orig_causallm_config = transformers.AutoModelForCausalLM.from_config
self.lora_config = lora_config
self.quant_config = quant_config
self._post_init_complete = False
def __enter__(self):
class OptLinearWrapper:
_orig_nn_linear = self._orig_nn_linear
_lora_config = self.lora_config
_quant_config = self.quant_config
def __new__(self, *args, **kwargs):
self._lora_config.delay_lora_init = True
kwargs['lora_config'] = self._lora_config
kwargs['quantization_config'] = self._quant_config
kwargs['linear_cls'] = self._orig_nn_linear
return OptimizedLinear(*args, **kwargs)
def _model_init(model):
if self.lora_config != None:
init_lora(model)
self._post_init_complete = True
return model
# ensures non-lora params are frozen and lora weights are initialized
def from_pretrained(*args, **kwargs):
model = self._orig_causallm_pretrained(*args, **kwargs)
return _model_init(model)
def from_config(*args, **kwargs):
model = self._orig_causallm_config(*args, **kwargs)
return _model_init(model)
torch.nn.Linear = OptLinearWrapper
if transformers != None:
transformers.AutoModelForCausalLM.from_pretrained = from_pretrained
transformers.AutoModelForCausalLM.from_config = from_config
def __exit__(self, *args, **kwargs):
torch.nn.Linear = self._orig_nn_linear
if not self._post_init_complete:
print('WARNING: For some reason LoRA modules are not initialized, this is usually done automatically '
'if using transformers via (AutoModelForCausalLM from_pretrained/from_config). '
'You must call `init_lora` on each module in order to use DeepSpeed LoRA, otherwise '
'you will error out during runtime.')
else:
transformers.AutoModelForCausalLM.from_pretrained = self._orig_causallm_pretrained
transformers.AutoModelForCausalLM.from_config = self._orig_causallm_config
+222
View File
@@ -0,0 +1,222 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
import torch
import math
import torch.nn as nn
import torch.nn.functional as F
from dataclasses import is_dataclass
from deepspeed.accelerator import get_accelerator
import deepspeed.comm as dist
from .config import LoRAConfig, QuantizationConfig
from .quantization import QuantizedParameter, QuantizedLinear
class OptimizedLinear(nn.Module):
"""
Optimized version of nn.Linear that adds features such as:
* LoRA w. base weight sharding
* FP [6,8,12] quantization
Arguments:
input_dim: Required: size of each input sample
output_dim: Required: size of each output sample
bias: Optional: If set to False, the layer will not learn an additive bias. Default: False
lora_config: Optional: LoRAConfig defining lora features and base-weight-sharding degree
quantization_config: Optional: QuantizationConfig defining quantization features
dtype: Optional: parameter dtype, only supports bfloat16 currently
Returns:
Returns a new nn.Module depending on the input config. Either native
torch.nn.Linear, QuantizedLinear, or the full-featured DSOptimizedLinear.
"""
def __new__(self,
input_dim: int,
output_dim: int,
bias: bool = False,
lora_config: LoRAConfig = None,
quantization_config: QuantizationConfig = None,
device=None,
dtype=torch.bfloat16,
linear_cls=nn.Linear):
if quantization_config is not None and not is_dataclass(quantization_config):
raise ValueError(f"Expecting QuantizationConfig but received {type(quantization_config)}")
if lora_config is not None and not is_dataclass(lora_config):
raise ValueError(f"Expecting LoRAConfig but received {type(lora_config)}")
if lora_config is None and quantization_config is None:
# Everything disabled, fall back to normal nn.Linear
self = linear_cls(input_dim, output_dim, bias=bias, dtype=dtype, device=device)
elif lora_config:
# lora enabled, quantization may or may not be
self = LoRAOptimizedLinear(input_dim=input_dim,
output_dim=output_dim,
bias=bias,
lora_config=lora_config,
quantization_config=quantization_config,
dtype=dtype,
device=device,
linear_cls=linear_cls)
elif quantization_config:
# only quantization enabled, no lora
self = QuantizedLinear(input_dim=input_dim,
output_dim=output_dim,
bias=bias,
quantization_config=quantization_config,
dtype=dtype)
return self
class LoRAOptimizedLinear(nn.Module):
def __init__(self,
input_dim: int,
output_dim: int,
bias: bool = False,
lora_config: LoRAConfig = None,
quantization_config: QuantizationConfig = None,
device=None,
dtype=torch.bfloat16,
linear_cls=nn.Linear):
super().__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.bias = bias
self.lora_config = lora_config
self.quantization_config = quantization_config
self.device = get_accelerator().current_device_name() if device is None else device
self.linear_cls = linear_cls
self.dtype = dtype
assert self.lora_config is not None, "DSOptimizedLinear requires a LoRA config"
assert not self.bias, "bias=True is not supported by LoRAOptimizedLinear"
self.zero_shards = self.lora_config.base_weight_sharding
self.sharded_weight_size = int(float(self.input_dim) // self.zero_shards)
if self.zero_shards > 1:
assert self.zero_shards == dist.get_world_size(
), "base weight sharding is only supported across world size"
w = torch.nn.Parameter(torch.empty(self.output_dim * self.sharded_weight_size, dtype=dtype),
requires_grad=False)
else:
w = torch.nn.Parameter(torch.empty((self.output_dim, self.input_dim), dtype=dtype), requires_grad=False)
torch.nn.init.xavier_uniform_(w.reshape(self.sharded_weight_size, self.output_dim))
if self.quantization_config is not None:
assert dtype == torch.bfloat16, "only bfloat16 is supported when using quantization"
self.weight = QuantizedParameter(w, quantization_config=quantization_config)
else:
self.weight = w
self.disabled = False
self._initialized = False
if not self.lora_config.delay_lora_init:
self.init_lora()
def disable(self):
self.disabled = True
self.weight = torch.nn.Parameter(torch.empty((self.output_dim, self.input_dim), dtype=self.dtype),
requires_grad=False)
def init_lora(self):
if self.disabled:
return
if self.quantization_config is not None:
# ensure quant-param wasn't stripped, in some cases transformers will do this during model init
if not isinstance(self.weight, QuantizedParameter):
self.weight = QuantizedParameter(self.weight, quantization_config=self.quantization_config)
self._initialized = True
self.weight.requires_grad = False
# Mark base weight to prevent broadcast and ensure proper offload behavior
self.weight.ds_optim_param = True
self.lora_scaling_factor = self.lora_config.lora_alpha / self.lora_config.lora_r
# Keeping lora weights in bf16 precision for ease of training.
self.lora_weight_1 = self.linear_cls(self.input_dim,
self.lora_config.lora_r,
bias=self.bias,
device=self.device,
dtype=self.dtype)
self.lora_weight_2 = self.linear_cls(self.lora_config.lora_r,
self.output_dim,
bias=self.bias,
device=self.device,
dtype=self.dtype)
# initialize "A" with kaiming uniform and "B" with zeros following this
# https://github.com/huggingface/peft/blob/62122b5add8d6892f70c82eaef2147a6ba33b90b/src/peft/tuners/lora/layer.py#L155
nn.init.kaiming_uniform_(self.lora_weight_1.weight, a=math.sqrt(5))
nn.init.zeros_(self.lora_weight_2.weight)
self.lora_weight_1.weight.requires_grad = True
self.lora_weight_2.weight.requires_grad = True
def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys,
error_msgs):
if not any([target in prefix for target in self.lora_config.target_mods]):
# module does not match any target_mods, we must revert to normal nn.Linear via disable
self.disable()
return super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys,
unexpected_keys, error_msgs)
if self.zero_shards > 1:
if not dist.is_initialized():
raise RuntimeError(
"attempting to use optimized linear base weight sharding but torch-distributed is not initialized, please init first."
)
rank = dist.get_rank()
shape_local = self.output_dim * self.sharded_weight_size
base_weight_name = f"{prefix}weight"
incoming_param = state_dict[base_weight_name]
state_dict[base_weight_name] = incoming_param.flatten().narrow(0, rank * shape_local, shape_local)
return super()._load_from_state_dict(state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys,
error_msgs)
def full_weight(self):
base_weight = self.weight
if getattr(base_weight, 'ds_offload', False):
# move to gpu so we can dequant and all-gather
assert base_weight.device == torch.device('cpu'), \
f"expected base weight on cpu but found {base_weight.device}"
base_weight.offload(revert=True)
local_weight = base_weight.dequantized() if isinstance(base_weight, QuantizedParameter) else base_weight
base_weight.offload()
else:
local_weight = base_weight.dequantized() if isinstance(base_weight, QuantizedParameter) else base_weight
tensor_out = torch.empty(self.output_dim * self.input_dim,
dtype=local_weight.dtype,
device=local_weight.device)
dist.all_gather_into_tensor(tensor_out, local_weight)
return tensor_out.reshape(self.output_dim, self.input_dim)
def linear_without_F_linear(self, input, weight):
output = torch.mm(input.reshape(-1, input.shape[-1]), weight)
output = output.view(*input.shape[:-1], weight.shape[1])
return output
def forward(self, input_tensor):
if self.disabled:
return F.linear(input_tensor, self.weight)
assert self._initialized, "init_lora was never called, please initialize before proceeding"
# Gather the sharded base weight
if self.zero_shards > 1:
with torch.no_grad():
base_weight = self.full_weight()
elif self.quantization_config:
base_weight = self.weight.dequantized()
else:
base_weight = self.weight
base_weight_output = F.linear(input_tensor, base_weight)
lora_output = self.lora_weight_2(self.lora_weight_1(input_tensor))
return base_weight_output + self.lora_scaling_factor * lora_output
+147
View File
@@ -0,0 +1,147 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Optional
from deepspeed.accelerator import get_accelerator
from deepspeed.ops.fp_quantizer import Quantizer, FP_Quantize
from .config import QuantizationConfig
class QuantizedParameter(nn.Parameter):
"""
Quantized parameter class that implements weight quantization. Weights
are stored in quantized form on GPUs, and can be dequantized on-the-fly when
needed by the model. The weights are actually quantized during any `.to(device)`.
Arguments:
data (Tensor): parameter tensor.
requires_grad (bool, optional): if the parameter requires gradient. Defaults
to False and is not supported to be True. Argument provided only for interface
compatibility with torch.nn.Parameter.
quantization_config (QuantizationConfig, optional):
quantizer (Quantizer, optional): Defaults to FP_Quantize but can be any quantizer
that implements deepspeed.ops.fp_quantizer.Quantizer. This argument is also
required since the quantizer is stashed in the Parameter itself, some models
may clone the Parameter by passing an attribute __dict__. For an example, see
tests/unit/linear/test_quant_param.py::TestQuantParam::test_hf_clone
"""
def __new__(
cls,
data: Optional[torch.Tensor] = None,
requires_grad: bool = False, # quantized weights must be frozen
quantization_config: QuantizationConfig = None,
quantizer: Quantizer = None,
):
if requires_grad:
raise ValueError("requires_grad=True is not supported with QuantizedParameter")
if data is None:
data = torch.empty(0)
self = torch.Tensor._make_subclass(cls, data, requires_grad)
self.quantization_config = QuantizationConfig() if quantization_config is None else quantization_config
if quantizer is not None:
self.quantizer = quantizer
else:
# if FPQuantizerBuilder is not compatible in this env this init will fail
self.quantizer = FP_Quantize(quantization_config=self.quantization_config)
self._ensure_quantized(self)
return self
def _ensure_quantized(self, tensor: torch.Tensor):
# If the tensor is on the accelerator and is not quantized, then quantize it in-place.
if get_accelerator().on_accelerator(tensor) and tensor.dtype != self.quantization_config.q_dtype:
with get_accelerator().stream(get_accelerator().current_stream(tensor.device)):
tensor.data = self.quantizer.quantize(tensor.data,
q_bits=self.quantization_config.q_bits,
q_mantisa_bits=self.quantization_config.mantissa_bits)
assert tensor.dtype == self.quantization_config.q_dtype
def dequantized(self) -> torch.Tensor:
"""
Return a tensor containing the dequantized weights of this parameter.
"""
if get_accelerator().on_accelerator(self.data) and self.data.dtype == self.quantization_config.q_dtype:
with get_accelerator().stream(get_accelerator().current_stream(self.data.device)):
return self.quantizer.dequantize(self.data,
q_bits=self.quantization_config.q_bits,
q_mantisa_bits=self.quantization_config.mantissa_bits)
return self.data
def offload(self, revert=False):
if getattr(self, 'ds_offload', False):
if revert:
self.data = self.to(get_accelerator().current_device_name())
else:
self.data = self.to('cpu')
def __getstate__(self):
state = self.__dict__
state["data"] = self.data
state["quantization_config"] = self.quantization_config
state["requires_grad"] = self.requires_grad
return state
def __setstate__(self, state):
self.quantizer = state["quantizer"]
self.quantization_config = state["quantization_config"]
self.data = state["data"]
self.requires_grad = state["requires_grad"]
def __deepcopy__(self, memo):
new_instance = type(self).__new__(type(self))
state = self.__getstate__()
new_instance.__setstate__(state)
new_instance.quantizer = copy.deepcopy(state["quantizer"])
new_instance.quantization_config = copy.deepcopy(state["quantization_config"])
new_instance.data = copy.deepcopy(state["data"])
return new_instance
def __copy__(self):
new_instance = type(self).__new__(type(self))
state = self.__getstate__()
new_instance.__setstate__(state)
return new_instance
def cuda(self, device=None, non_blocking=False):
device = "cuda" if device is None else device
self.quantizer.to(device, non_blocking=non_blocking)
return self.to(device, non_blocking=non_blocking)
def to(self, *args, **kwargs):
"""
Move the parameter to the given device. Then, if the device is a cuda device,
quantize it.
"""
tensor = super().to(*args, **kwargs)
self.quantizer.to(*args, **kwargs)
self._ensure_quantized(tensor)
return tensor
class QuantizedLinear(nn.Linear):
"""
Linear layer that implements weight quantization. Parameters
are stored via `QuantizedParameter` and are dequantized on-the-fly during any
forward pass.
"""
def __init__(self,
input_dim: int,
output_dim: int,
bias: bool = False,
quantization_config: QuantizationConfig = None,
dtype=torch.bfloat16):
super().__init__(input_dim, output_dim, bias=bias, dtype=dtype)
assert dtype == torch.bfloat16, "currently only supports bfloat16 dtype"
self.weight = QuantizedParameter(self.weight.data, quantization_config=quantization_config)
def forward(self, input: torch.Tensor) -> torch.Tensor:
return F.linear(input, self.weight.dequantized(), self.bias)