| |
| """ |
| 缓存模块 |
| 包含CustomDynamicCache和CustomQuantizeDynamicCache两个类 |
| 从eval_anything_v2_batch.py中提取出来,实现模块化管理 |
| """ |
|
|
| import copy |
| from typing import Optional, Tuple |
| import torch |
| from transformers.cache_utils import DynamicCache, QuantoQuantizedCache, QuantizedCacheConfig |
|
|
|
|
| class CustomDynamicCache(DynamicCache): |
| """ |
| 自定义动态缓存类 |
| 扩展标准的DynamicCache,添加额外的元数据存储和查询功能 |
| """ |
| def __init__(self, _distributed_cache_data=None): |
| super().__init__(_distributed_cache_data) |
| self.cache_kwargs = {} |
| self.group_cache = {} |
| self.meta = {} |
| self.router_key_cache = [] |
|
|
| def clear_kvcache(self): |
| self.key_cache = [] |
| self.value_cache = [] |
|
|
| def record_kwargs(self, layer_idx, kwargs): |
| """ |
| 记录层的元数据信息 |
| |
| Args: |
| layer_idx: 层索引 |
| kwargs: 包含路由层信息的字典 |
| """ |
| if layer_idx in self.cache_kwargs: |
| self.cache_kwargs[layer_idx].update(kwargs) |
| else: |
| self.cache_kwargs[layer_idx] = kwargs |
|
|
| def get_layer_length(self): |
| return len(self.cache_kwargs) |
|
|
| def get_kvcache(self, layer_idx): |
| """ |
| 获取指定层的KV缓存 |
| |
| Args: |
| layer_idx: 层索引 |
| |
| Returns: |
| Tuple[torch.Tensor, torch.Tensor]: (key_cache, value_cache) |
| """ |
| key_cache = self.key_cache[layer_idx] |
| value_cache = self.value_cache[layer_idx] |
| return key_cache, value_cache |
|
|
| def get_router_kcache(self, layer_idx): |
| if layer_idx < len(self.router_key_cache): |
| return self.router_key_cache[layer_idx] |
| else: |
| return None |
|
|
| def clear_query(self): |
| """ |
| 清理查询相关的临时数据 |
| 移除查询过程中产生的临时数据,保持缓存清洁 |
| """ |
| for k, v in self.cache_kwargs.items(): |
| if "compacked_key_cache" in v: |
| v.pop("compacked_key_cache") |
| v.pop("compacked_value_cache") |
| if "prefill_stage2_kvcache_size" in v: |
| v.pop("prefill_stage2_kvcache_size") |
| if "prefill_stage1_kvcache_size" in v: |
| v.pop("prefill_stage1_kvcache_size") |
| if "recall_topk" in v: |
| v.pop("recall_topk") |
| return self |
|
|
| def get_seq_length(self, layer_idx=0) -> int: |
| """ |
| 返回缓存状态的序列长度 |
| |
| Args: |
| layer_idx: 可选的层索引 |
| |
| Returns: |
| int: 序列长度 |
| """ |
| is_empty_layer = ( |
| len(self.key_cache) == 0 |
| or len(self.key_cache) <= layer_idx |
| or not self.key_cache[layer_idx].numel() |
| ) |
| layer_seq_length = self.key_cache[layer_idx].shape[-2] if not is_empty_layer else 0 |
| return layer_seq_length |
|
|
| def copy(self): |
| """ |
| 创建缓存的深拷贝 |
| |
| Returns: |
| CustomDynamicCache: 缓存的新副本 |
| """ |
| new_cache = CustomDynamicCache() |
| new_cache.key_cache = [k.clone() for k in self.key_cache] |
| new_cache.value_cache = [v.clone() for v in self.value_cache] |
| new_cache.cache_kwargs = copy.deepcopy(self.cache_kwargs) |
| new_cache.group_cache = copy.deepcopy(self.group_cache) |
| new_cache.meta = copy.deepcopy(self.meta) |
| new_cache._seen_tokens = self._seen_tokens |
| return new_cache |
|
|
| def update_router_kcache( |
| self, |
| key_states: torch.Tensor, |
| layer_idx: int, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
|
|
| |
| if key_states is not None: |
| if len(self.router_key_cache) <= layer_idx: |
| |
| for _ in range(len(self.router_key_cache), layer_idx): |
| self.router_key_cache.append(torch.tensor([])) |
| self.router_key_cache.append(key_states) |
| elif ( |
| not self.router_key_cache[layer_idx].numel() |
| ): |
| self.router_key_cache[layer_idx] = key_states |
| else: |
| self.router_key_cache[layer_idx] = torch.cat([self.router_key_cache[layer_idx], key_states], dim=-2) |
|
|
| return self.router_key_cache[layer_idx] |
| |
| class CustomDynamicCacheOnCPU(CustomDynamicCache): |
| def __init__(self, _distributed_cache_data=None): |
| super().__init__(_distributed_cache_data) |
|
|
| def record_kwargs(self, layer_idx, kwargs): |
| d = {} |
| for k, v in kwargs.items(): |
| if v is not None and torch.is_tensor(v): |
| d[k] = v.cpu() if v.is_cuda else v.clone() |
| else: |
| d[k] = v |
| super().record_kwargs(layer_idx, d) |
|
|
| def update( |
| self, |
| key_states: torch.Tensor, |
| value_states: torch.Tensor, |
| layer_idx: int, |
| cache_kwargs=None, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| if key_states is not None and torch.is_tensor(key_states) and key_states.is_cuda: |
| key_states = key_states.cpu() |
| if value_states is not None and torch.is_tensor(value_states) and value_states.is_cuda: |
| value_states = value_states.cpu() |
| return super().update(key_states, value_states, layer_idx, cache_kwargs) |
|
|
| def update_router_kcache( |
| self, |
| key_states: torch.Tensor, |
| layer_idx: int, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| if key_states is not None and torch.is_tensor(key_states) and key_states.is_cuda: |
| key_states = key_states.cpu() |
| return super().update_router_kcache(key_states, layer_idx) |
|
|
| class CustomQuantizeDynamicCache(QuantoQuantizedCache): |
| """ |
| 自定义量化动态缓存类 |
| 扩展标准的QuantoQuantizedCache,添加额外的元数据存储和查询功能 |
| """ |
| def __init__(self, cache_config): |
| super().__init__(cache_config) |
| self.cache_config = cache_config |
| self.cache_kwargs = {} |
| self.group_cache = {} |
| self.meta = {} |
|
|
| def record_kwargs(self, layer_idx, kwargs): |
| """ |
| 记录层的元数据信息 |
| |
| Args: |
| layer_idx: 层索引 |
| kwargs: 包含路由层信息的字典 |
| """ |
| if layer_idx in self.cache_kwargs: |
| self.cache_kwargs[layer_idx].update(kwargs) |
| else: |
| self.cache_kwargs[layer_idx] = kwargs |
|
|
| def get_layer_length(self): |
| return len(self.cache_kwargs) |
|
|
| def clear_kvcache(self): |
| self._quantized_key_cache = [] |
| self._quantized_value_cache = [] |
| self.key_cache = [] |
| self.value_cache = [] |
|
|
| def get_kvcache(self, layer_idx): |
| """ |
| 获取指定层的KV缓存(反量化后) |
| |
| Args: |
| layer_idx: 层索引 |
| |
| Returns: |
| Tuple[torch.Tensor, torch.Tensor]: (key_cache, value_cache) |
| """ |
| dequant_key = self._dequantize(self._quantized_key_cache[layer_idx]) |
| dequant_value = self._dequantize(self._quantized_value_cache[layer_idx]) |
| return dequant_key, dequant_value |
|
|
| def update( |
| self, |
| key_states: torch.Tensor, |
| value_states: torch.Tensor, |
| layer_idx: int, |
| cache_kwargs=None, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| """ |
| 更新缓存 |
| |
| Args: |
| key_states: 新的key状态 |
| value_states: 新的value状态 |
| layer_idx: 层索引 |
| cache_kwargs: 缓存关键字参数 |
| |
| Returns: |
| Tuple[torch.Tensor, torch.Tensor]: 更新后的key和value状态 |
| """ |
| |
| if layer_idx == 0: |
| self._seen_tokens += key_states.shape[-2] |
|
|
| if len(self.key_cache) < layer_idx: |
| for i in range(len(self.key_cache), layer_idx): |
| self.key_cache.append(torch.zeros(0, dtype=key_states.dtype, device=key_states.device)) |
| self.value_cache.append(torch.zeros(0, dtype=key_states.dtype, device=key_states.device)) |
| self._quantized_key_cache.append(torch.zeros(0, dtype=key_states.dtype, device=key_states.device)) |
| self._quantized_value_cache.append(torch.zeros(0, dtype=key_states.dtype, device=key_states.device)) |
|
|
| if len(self.key_cache) == layer_idx: |
| self._quantized_key_cache.append(self._quantize(key_states.contiguous(), axis=self.axis_key)) |
| self._quantized_value_cache.append(self._quantize(value_states.contiguous(), axis=self.axis_value)) |
| self.key_cache.append(torch.zeros(0, dtype=key_states.dtype, device=key_states.device)) |
| self.value_cache.append(torch.zeros(0, dtype=key_states.dtype, device=key_states.device)) |
| keys_to_return, values_to_return = key_states, value_states |
| else: |
| dequant_key = self._dequantize(self._quantized_key_cache[layer_idx]) |
| dequant_value = self._dequantize(self._quantized_value_cache[layer_idx]) |
| keys_to_return = [dequant_key, self.key_cache[layer_idx], key_states] |
| values_to_return = [dequant_value, self.value_cache[layer_idx], value_states] |
|
|
| keys_to_return = torch.cat(keys_to_return, dim=-2) |
| values_to_return = torch.cat(values_to_return, dim=-2) |
| if ( |
| self.key_cache[layer_idx].dim() == 4 |
| and self.key_cache[layer_idx].shape[-2] + 1 >= self.residual_length |
| ): |
| self._quantized_key_cache[layer_idx] = self._quantize(keys_to_return.contiguous(), axis=self.axis_key) |
| self._quantized_value_cache[layer_idx] = self._quantize( |
| values_to_return.contiguous(), axis=self.axis_value |
| ) |
| self.key_cache[layer_idx] = torch.zeros(0, dtype=key_states.dtype, device=key_states.device) |
| self.value_cache[layer_idx] = torch.zeros(0, dtype=key_states.dtype, device=key_states.device) |
| else: |
| self.key_cache[layer_idx] = torch.cat([self.key_cache[layer_idx], key_states], dim=-2) |
| self.value_cache[layer_idx] = torch.cat([self.value_cache[layer_idx], value_states], dim=-2) |
|
|
| return keys_to_return, values_to_return |
|
|
| def get_seq_length(self, layer_idx=0) -> int: |
| """ |
| 返回缓存状态的序列长度 |
| |
| Args: |
| layer_idx: 可选的层索引 |
| |
| Returns: |
| int: 序列长度 |
| """ |
| is_empty_layer = ( |
| len(self._quantized_key_cache) == 0 |
| or len(self._quantized_key_cache) <= layer_idx |
| or not self._quantized_key_cache[layer_idx].numel() |
| ) |
| layer_seq_length = self._quantized_key_cache[layer_idx].shape[-2] if not is_empty_layer else 0 |
| return layer_seq_length |
|
|
| def clear_query(self): |
| """ |
| 清理查询相关的临时数据 |
| 移除查询过程中产生的临时数据,保持缓存清洁 |
| """ |
| for k, v in self.cache_kwargs.items(): |
| if "compacked_key_cache" in v: |
| v.pop("compacked_key_cache") |
| v.pop("compacked_value_cache") |
| if "prefill_stage2_kvcache_size" in v: |
| v.pop("prefill_stage2_kvcache_size") |
| if "prefill_stage1_kvcache_size" in v: |
| v.pop("prefill_stage1_kvcache_size") |
| if "recall_topk" in v: |
| v.pop("recall_topk") |
| return self |
|
|
| def copy(self): |
| """ |
| 创建缓存的深拷贝 |
| |
| Returns: |
| CustomQuantizeDynamicCache: 缓存的新副本 |
| """ |
| new_cache = CustomQuantizeDynamicCache(self.cache_config) |
| if hasattr(self, '_quantized_key_cache'): |
| new_cache._quantized_key_cache = [k.clone() for k in self._quantized_key_cache] |
| new_cache._quantized_value_cache = [v.clone() for v in self._quantized_value_cache] |
| new_cache.key_cache = [k.clone() for k in self.key_cache] |
| new_cache.value_cache = [v.clone() for v in self.value_cache] |
| new_cache.cache_kwargs = copy.deepcopy(self.cache_kwargs) |
| new_cache.group_cache = copy.deepcopy(self.group_cache) |
| new_cache.meta = copy.deepcopy(self.meta) |
| new_cache._seen_tokens = self._seen_tokens |
| return new_cache |
|
|
|
|
| def create_cache(quantize_nbits: Optional[int] = 0): |
| """ |
| 根据参数创建合适的缓存实例 |
| |
| Args: |
| args: 包含量化相关参数的命名空间对象 |
| |
| Returns: |
| CustomDynamicCache or CustomQuantizeDynamicCache: 缓存实例 |
| """ |
| if quantize_nbits > 0: |
| quan_cache_config = QuantizedCacheConfig(nbits=quantize_nbits) |
| return CustomQuantizeDynamicCache(quan_cache_config) |
| else: |
| return CustomDynamicCache() |
|
|
| def manual_deepcopy_kv_cache(cache_obj): |
| """ |
| Manually performs a deep copy of a custom KV cache object, |
| avoiding the issues with quanto's __deepcopy__. |
| """ |
| |
| if isinstance(cache_obj, CustomQuantizeDynamicCache): |
| |
| new_cache = CustomQuantizeDynamicCache(cache_obj.cache_config) |
| elif isinstance(cache_obj, CustomDynamicCache): |
| new_cache = CustomDynamicCache() |
| else: |
| |
| raise TypeError(f"Unsupported cache type for manual deepcopy: {type(cache_obj)}") |
|
|
| |
| |
| new_cache.cache_kwargs = copy.deepcopy(cache_obj.cache_kwargs) |
| new_cache.meta = copy.deepcopy(cache_obj.meta) |
|
|
| |
| if hasattr(cache_obj, '_quantized_key_cache'): |
| for layer_cache in cache_obj._quantized_key_cache: |
| |
| new_cache._quantized_key_cache.append(layer_cache.clone().detach()) |
| for layer_cache in cache_obj._quantized_value_cache: |
| new_cache._quantized_value_cache.append(layer_cache.clone().detach()) |
| new_cache._seen_tokens = cache_obj._seen_tokens |
|
|
| if hasattr(cache_obj, 'key_cache'): |
| for layer_cache in cache_obj.key_cache: |
| new_cache.key_cache.append(layer_cache.clone().detach()) |
| for layer_cache in cache_obj.value_cache: |
| new_cache.value_cache.append(layer_cache.clone().detach()) |
| new_cache._seen_tokens = cache_obj._seen_tokens |
|
|
| return new_cache |
|
|
| def convert_tensor(data, cuda_device): |
| """转换结构体的 tensor device,如果cuda_device非 None,则将cpu 转换到 cuda,否则将 cuda 转换到 cpu |
| 支持dict, list, tuple, set |
| """ |
| |
| converted_count = [0] |
| |
| def _convert_recursive(obj): |
| |
| if torch.is_tensor(obj): |
| if cuda_device: |
| return obj.to(cuda_device) if obj.is_cpu else obj |
| if obj.is_cuda: |
| return obj.cpu() |
| return obj |
| |
| |
| elif isinstance(obj, dict): |
| return {k: _convert_recursive(v) for k, v in obj.items()} |
| |
| elif isinstance(obj, list): |
| return [_convert_recursive(item) for item in obj] |
| |
| elif isinstance(obj, tuple): |
| |
| return tuple(_convert_recursive(item) for item in obj) |
| |
| elif isinstance(obj, set): |
| return {_convert_recursive(item) for item in obj} |
| |
| |
| else: |
| return obj |
|
|
| return _convert_recursive(data) |
|
|
| def copy_dict_to_cpu(d: dict): |
| ret = {} |
| for k, v in d.items(): |
| ret[k] = v.cpu() if torch.is_tensor(v) and v.is_cuda else v |
| return ret |
|
|
| def copy_dict_to_gpu(d: dict, device): |
| if not d: |
| return d |
| ret = {} |
| for k, v in d.items(): |
| ret[k] = v.to(device) if torch.is_tensor(v) and not v.is_cuda else v |
| return ret |
|
|
| def copy_kv_cache_to_device(cache_obj, cuda_device, copy_v: bool=True): |
| if isinstance(cache_obj, CustomQuantizeDynamicCache): |
| new_cache = CustomQuantizeDynamicCache(cache_obj.cache_config) |
| elif isinstance(cache_obj, CustomDynamicCache): |
| new_cache = CustomDynamicCache() |
| else: |
| raise TypeError(f"Unsupported cache type for manual deepcopy: {type(cache_obj)}") |
|
|
| |
| new_cache.cache_kwargs = convert_tensor(cache_obj.cache_kwargs, cuda_device) |
| new_cache.meta = convert_tensor(cache_obj.meta, cuda_device) |
|
|
| |
| if hasattr(cache_obj, '_quantized_key_cache'): |
| new_cache._quantized_key_cache = convert_tensor(cache_obj._quantized_key_cache, cuda_device) |
| if copy_v: |
| new_cache._quantized_value_cache = convert_tensor(cache_obj._quantized_value_cache, cuda_device) |
| else: |
| new_cache._quantized_value_cache = cache_obj._quantized_value_cache |
| new_cache._seen_tokens = cache_obj._seen_tokens |
|
|
| if hasattr(cache_obj, 'key_cache'): |
| new_cache.key_cache = convert_tensor(cache_obj.key_cache, cuda_device) |
| if copy_v: |
| new_cache.value_cache = convert_tensor(cache_obj.value_cache, cuda_device) |
| else: |
| new_cache.value_cache = cache_obj.value_cache |
| new_cache._seen_tokens = cache_obj._seen_tokens |
|
|
| return new_cache |
|
|
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| |