| 115 | |
| 116 | |
| 117 | class Optimizer8bit(torch.optim.Optimizer): |
| 118 | _FSDP_WRAPPED_QUANT_STATE_KEY = "__bnb_optimizer_quant_state__" |
| 119 | |
| 120 | def __init__(self, params, defaults, optim_bits=32, is_paged=False): |
| 121 | """ |
| 122 | Base 8-bit optimizer class. |
| 123 | |
| 124 | Arguments: |
| 125 | params (`torch.Tensor`): |
| 126 | The input parameters to optimize. |
| 127 | optim_bits (`int`, defaults to 32): |
| 128 | The number of bits of the optimizer state. |
| 129 | is_paged (`bool`, defaults to `False`): |
| 130 | Whether the optimizer is a paged optimizer or not. |
| 131 | """ |
| 132 | super().__init__(params, defaults) |
| 133 | self.initialized = False |
| 134 | self.name2qmap = {} |
| 135 | self.is_paged = is_paged |
| 136 | self.page_mng = F.GlobalPageManager.get_instance() |
| 137 | |
| 138 | self.mng = GlobalOptimManager.get_instance() |
| 139 | self.non_castable_tensor_keys = { |
| 140 | "qmap1", |
| 141 | "qmap2", |
| 142 | "max1", |
| 143 | "max2", |
| 144 | "new_max1", |
| 145 | "new_max2", |
| 146 | "state1", |
| 147 | "state2", |
| 148 | "gnorm_vec", |
| 149 | "absmax1", |
| 150 | "absmax2", |
| 151 | "unorm_vec", |
| 152 | } |
| 153 | |
| 154 | if optim_bits == 8: |
| 155 | self.fill_qmap() |
| 156 | |
| 157 | def fill_qmap(self): |
| 158 | self.name2qmap["dynamic"] = F.create_dynamic_map(signed=True) |
| 159 | self.name2qmap["udynamic"] = F.create_dynamic_map(signed=False) |
| 160 | |
| 161 | def state_dict(self): |
| 162 | """Return optimizer state, wrapping quantization tensors for FSDP compatibility. |
| 163 | |
| 164 | FSDP's full_optim_state_dict gathers all tensor states across ranks. |
| 165 | Quantization states (state1, state2, absmax, etc.) have different shapes |
| 166 | than model parameters, causing gather operations to fail. By wrapping |
| 167 | these tensors in a nested dict, FSDP skips them during gathering. |
| 168 | """ |
| 169 | state_dict = super().state_dict() |
| 170 | |
| 171 | # Deep copy the state to avoid modifying the original optimizer state |
| 172 | # PyTorch's state_dict() only does a shallow copy |
| 173 | state_dict["state"] = { |
| 174 | k: {kk: vv for kk, vv in v.items()} if isinstance(v, dict) else v for k, v in state_dict["state"].items() |
nothing calls this directly
no outgoing calls
no test coverage detected