Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / components / functional / dsa_kl_loss.py: 83%

30 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-11-15 07:18 +0800

1# Copyright 2026 Huawei Technologies Co., Ltd 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================ 

15"""DSA sparse-attention indexer KL loss.""" 

16 

17from typing import Any, Tuple 

18 

19import torch # pylint: disable=forbidden-backend-import 

20 

21from hyper_parallel.tools.logging import get_logger 

22 

23logger = get_logger() 

24 

25 

26def _sparse_indexer_kl_loss_grad( 

27 query: torch.Tensor, 

28 key: torch.Tensor, 

29 query_index: torch.Tensor, 

30 key_index: torch.Tensor, 

31 weights: torch.Tensor, 

32 sparse_indices: torch.Tensor, 

33 softmax_max: torch.Tensor, 

34 softmax_sum: torch.Tensor, 

35 scale: float, 

36 query_rope: torch.Tensor | None, 

37 key_rope: torch.Tensor | None, 

38 actual_seq_qlen: torch.Tensor | None, 

39 actual_seq_klen: torch.Tensor | None, 

40 layout: str, 

41) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: 

42 """Compute teacher, indexer gradients and summed KL inside CANN.""" 

43 # Keep the optional NPU dependency out of CPU and Omni-only imports. 

44 from torch_npu import ( # pylint: disable=import-outside-toplevel,forbidden-backend-import 

45 npu_sparse_lightning_indexer_grad_kl_loss, 

46 ) 

47 

48 return npu_sparse_lightning_indexer_grad_kl_loss( 

49 query.contiguous(), key.contiguous(), query_index.contiguous(), key_index.contiguous(), 

50 weights.contiguous(), sparse_indices.contiguous(), softmax_max.contiguous(), 

51 softmax_sum.contiguous(), scale, 

52 query_rope=None if query_rope is None else query_rope.contiguous(), 

53 key_rope=None if key_rope is None else key_rope.contiguous(), 

54 actual_seq_qlen=None if actual_seq_qlen is None else actual_seq_qlen.tolist(), 

55 actual_seq_klen=None if actual_seq_klen is None else actual_seq_klen.tolist(), 

56 layout=layout, sparse_mode=3, 

57 ) 

58 

59 

60class _DSAKLLoss(torch.autograd.Function): 

61 """Autograd bridge for the DSA indexer KL-loss custom operator.""" 

62 

63 @staticmethod 

64 def forward( 

65 ctx: Any, 

66 index_query: torch.Tensor, 

67 index_key: torch.Tensor, 

68 merge_weight: torch.Tensor, 

69 query: torch.Tensor, 

70 key: torch.Tensor, 

71 topk_indices: torch.Tensor, 

72 softmax_max: torch.Tensor, 

73 softmax_sum: torch.Tensor, 

74 query_rope: torch.Tensor, 

75 key_rope: torch.Tensor, 

76 actual_seq_qlen: torch.Tensor, 

77 actual_seq_kvlen: torch.Tensor, 

78 scale: float, 

79 loss_coeff: float, 

80 backend: str, 

81 ) -> torch.Tensor: 

82 """Compute the KL loss and save its precomputed indexer gradients.""" 

83 divisor = query.size(0) 

84 if divisor == 0: 

85 raise ValueError("DSA KL requires at least one valid query token") 

86 # Load only the selected optional backend so either can be used independently. 

87 # pylint: disable=import-outside-toplevel 

88 if backend == "cann": 

89 result = _sparse_indexer_kl_loss_grad( 

90 query, 

91 key, 

92 index_query, 

93 index_key, 

94 merge_weight, 

95 topk_indices, 

96 softmax_max, 

97 softmax_sum, 

98 scale, 

99 query_rope, 

100 key_rope, 

101 actual_seq_qlen, 

102 actual_seq_kvlen, 

103 "TND", 

104 ) 

105 else: 

106 import omni_training_custom_ops # noqa: F401 # pylint: disable=unused-import 

107 

108 result = torch.ops.custom.npu_sparse_lightning_indexer_grad_kl_loss_enhance( 

109 query=query, 

110 key=key, 

111 query_index=index_query, 

112 key_index=index_key, 

113 weights=merge_weight, 

114 sparse_indices=topk_indices, 

115 softmax_max=softmax_max, 

116 softmax_sum=softmax_sum, 

117 scale_value=scale, 

118 query_rope=query_rope, 

119 key_rope=key_rope, 

120 actual_seq_qlen=actual_seq_qlen, 

121 actual_seq_klen=actual_seq_kvlen, 

122 layout="TND", 

123 sparse_mode=3, 

124 deterministic=torch.are_deterministic_algorithms_enabled(), 

125 sparse_block_size=1, 

126 ) 

127 # pylint: enable=import-outside-toplevel 

128 grad_index_query, grad_index_key, grad_merge_weight, inner_loss = result 

129 ctx.save_for_backward( 

130 grad_index_query / divisor * loss_coeff, 

131 grad_index_key / divisor * loss_coeff, 

132 grad_merge_weight / divisor * loss_coeff, 

133 ) 

134 return inner_loss.squeeze() / divisor * loss_coeff 

135 

136 @staticmethod 

137 def backward(ctx: Any, grad_loss: torch.Tensor) -> Tuple: 

138 """Scale the precomputed indexer gradients by the upstream gradient.""" 

139 grad_index_query, grad_index_key, grad_merge_weight = ctx.saved_tensors 

140 return ( 

141 grad_index_query * grad_loss, 

142 grad_index_key * grad_loss, 

143 grad_merge_weight * grad_loss, 

144 *((None,) * 12), 

145 ) 

146 

147 

148def dsa_kl_loss( 

149 index_query: torch.Tensor, 

150 index_key: torch.Tensor, 

151 merge_weight: torch.Tensor, 

152 query: torch.Tensor, 

153 key: torch.Tensor, 

154 topk_indices: torch.Tensor, 

155 softmax_max: torch.Tensor, 

156 softmax_sum: torch.Tensor, 

157 query_rope: torch.Tensor, 

158 key_rope: torch.Tensor, 

159 actual_seq_qlen: torch.Tensor, 

160 actual_seq_kvlen: torch.Tensor, 

161 scale: float, 

162 loss_coeff: float, 

163 *, 

164 backend: str = "cann", 

165) -> torch.Tensor: 

166 """Compute the DSA indexer KL loss. 

167 

168 ``index_query``, ``index_key``, and ``merge_weight`` are the flattened 

169 tensors returned by :func:`dsa_indexer`. ``topk_indices``, ``softmax_max``, 

170 and ``softmax_sum`` come from :func:`dsa_sparse_attention`. The MLA query, 

171 key, and rotary states use TND layout; sequence lengths are cumulative 

172 int32 tensors, following the custom-operator call contract. 

173 The result is a scalar auxiliary loss. The keyword-only ``backend`` selects 

174 ``"cann"`` (default) or ``"omni"`` for the legacy implementation. 

175 """ 

176 if backend not in ("cann", "omni"): 

177 raise ValueError(f"backend must be 'cann' or 'omni', got {backend!r}") 

178 if backend == "omni": 

179 logger.warning( 

180 "dsa_kl_loss backend='omni' is deprecated and will be removed in a future release; " 

181 "use backend='cann'." 

182 ) 

183 return _DSAKLLoss.apply( 

184 index_query, 

185 index_key, 

186 merge_weight, 

187 query, 

188 key, 

189 topk_indices, 

190 softmax_max, 

191 softmax_sum, 

192 query_rope, 

193 key_rope, 

194 actual_seq_qlen, 

195 actual_seq_kvlen, 

196 scale, 

197 loss_coeff, 

198 backend, 

199 )