mask.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. # Copyright (c) 2019 Shigeki Karita
  2. # 2020 Mobvoi Inc (Binbin Zhang)
  3. # 2024 Alibaba Inc (authors: Xiang Lyu)
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import torch
  17. '''
  18. def subsequent_mask(
  19. size: int,
  20. device: torch.device = torch.device("cpu"),
  21. ) -> torch.Tensor:
  22. """Create mask for subsequent steps (size, size).
  23. This mask is used only in decoder which works in an auto-regressive mode.
  24. This means the current step could only do attention with its left steps.
  25. In encoder, fully attention is used when streaming is not necessary and
  26. the sequence is not long. In this case, no attention mask is needed.
  27. When streaming is need, chunk-based attention is used in encoder. See
  28. subsequent_chunk_mask for the chunk-based attention mask.
  29. Args:
  30. size (int): size of mask
  31. str device (str): "cpu" or "cuda" or torch.Tensor.device
  32. dtype (torch.device): result dtype
  33. Returns:
  34. torch.Tensor: mask
  35. Examples:
  36. >>> subsequent_mask(3)
  37. [[1, 0, 0],
  38. [1, 1, 0],
  39. [1, 1, 1]]
  40. """
  41. ret = torch.ones(size, size, device=device, dtype=torch.bool)
  42. return torch.tril(ret)
  43. '''
  44. def subsequent_mask(
  45. size: int,
  46. device: torch.device = torch.device("cpu"),
  47. ) -> torch.Tensor:
  48. """Create mask for subsequent steps (size, size).
  49. This mask is used only in decoder which works in an auto-regressive mode.
  50. This means the current step could only do attention with its left steps.
  51. In encoder, fully attention is used when streaming is not necessary and
  52. the sequence is not long. In this case, no attention mask is needed.
  53. When streaming is need, chunk-based attention is used in encoder. See
  54. subsequent_chunk_mask for the chunk-based attention mask.
  55. Args:
  56. size (int): size of mask
  57. str device (str): "cpu" or "cuda" or torch.Tensor.device
  58. dtype (torch.device): result dtype
  59. Returns:
  60. torch.Tensor: mask
  61. Examples:
  62. >>> subsequent_mask(3)
  63. [[1, 0, 0],
  64. [1, 1, 0],
  65. [1, 1, 1]]
  66. """
  67. arange = torch.arange(size, device=device)
  68. mask = arange.expand(size, size)
  69. arange = arange.unsqueeze(-1)
  70. mask = mask <= arange
  71. return mask
  72. def subsequent_chunk_mask_deprecated(
  73. size: int,
  74. chunk_size: int,
  75. num_left_chunks: int = -1,
  76. device: torch.device = torch.device("cpu"),
  77. ) -> torch.Tensor:
  78. """Create mask for subsequent steps (size, size) with chunk size,
  79. this is for streaming encoder
  80. Args:
  81. size (int): size of mask
  82. chunk_size (int): size of chunk
  83. num_left_chunks (int): number of left chunks
  84. <0: use full chunk
  85. >=0: use num_left_chunks
  86. device (torch.device): "cpu" or "cuda" or torch.Tensor.device
  87. Returns:
  88. torch.Tensor: mask
  89. Examples:
  90. >>> subsequent_chunk_mask(4, 2)
  91. [[1, 1, 0, 0],
  92. [1, 1, 0, 0],
  93. [1, 1, 1, 1],
  94. [1, 1, 1, 1]]
  95. """
  96. ret = torch.zeros(size, size, device=device, dtype=torch.bool)
  97. for i in range(size):
  98. if num_left_chunks < 0:
  99. start = 0
  100. else:
  101. start = max((i // chunk_size - num_left_chunks) * chunk_size, 0)
  102. ending = min((i // chunk_size + 1) * chunk_size, size)
  103. ret[i, start:ending] = True
  104. return ret
  105. def subsequent_chunk_mask(
  106. size: int,
  107. chunk_size: int,
  108. num_left_chunks: int = -1,
  109. device: torch.device = torch.device("cpu"),
  110. ) -> torch.Tensor:
  111. """Create mask for subsequent steps (size, size) with chunk size,
  112. this is for streaming encoder
  113. Args:
  114. size (int): size of mask
  115. chunk_size (int): size of chunk
  116. num_left_chunks (int): number of left chunks
  117. <0: use full chunk
  118. >=0: use num_left_chunks
  119. device (torch.device): "cpu" or "cuda" or torch.Tensor.device
  120. Returns:
  121. torch.Tensor: mask
  122. Examples:
  123. >>> subsequent_chunk_mask(4, 2)
  124. [[1, 1, 0, 0],
  125. [1, 1, 0, 0],
  126. [1, 1, 1, 1],
  127. [1, 1, 1, 1]]
  128. """
  129. # NOTE this modified implementation meets onnx export requirements, but it doesn't support num_left_chunks
  130. # actually this is not needed after we have inference cache implemented, will remove it later
  131. pos_idx = torch.arange(size, device=device)
  132. block_value = (torch.div(pos_idx, chunk_size, rounding_mode='trunc') + 1) * chunk_size
  133. ret = pos_idx.unsqueeze(0) < block_value.unsqueeze(1)
  134. return ret
  135. def add_optional_chunk_mask(xs: torch.Tensor,
  136. masks: torch.Tensor,
  137. use_dynamic_chunk: bool,
  138. use_dynamic_left_chunk: bool,
  139. decoding_chunk_size: int,
  140. static_chunk_size: int,
  141. num_decoding_left_chunks: int,
  142. enable_full_context: bool = True):
  143. """ Apply optional mask for encoder.
  144. Args:
  145. xs (torch.Tensor): padded input, (B, L, D), L for max length
  146. mask (torch.Tensor): mask for xs, (B, 1, L)
  147. use_dynamic_chunk (bool): whether to use dynamic chunk or not
  148. use_dynamic_left_chunk (bool): whether to use dynamic left chunk for
  149. training.
  150. decoding_chunk_size (int): decoding chunk size for dynamic chunk, it's
  151. 0: default for training, use random dynamic chunk.
  152. <0: for decoding, use full chunk.
  153. >0: for decoding, use fixed chunk size as set.
  154. static_chunk_size (int): chunk size for static chunk training/decoding
  155. if it's greater than 0, if use_dynamic_chunk is true,
  156. this parameter will be ignored
  157. num_decoding_left_chunks: number of left chunks, this is for decoding,
  158. the chunk size is decoding_chunk_size.
  159. >=0: use num_decoding_left_chunks
  160. <0: use all left chunks
  161. enable_full_context (bool):
  162. True: chunk size is either [1, 25] or full context(max_len)
  163. False: chunk size ~ U[1, 25]
  164. Returns:
  165. torch.Tensor: chunk mask of the input xs.
  166. """
  167. # Whether to use chunk mask or not
  168. if use_dynamic_chunk:
  169. max_len = xs.size(1)
  170. if decoding_chunk_size < 0:
  171. chunk_size = max_len
  172. num_left_chunks = -1
  173. elif decoding_chunk_size > 0:
  174. chunk_size = decoding_chunk_size
  175. num_left_chunks = num_decoding_left_chunks
  176. else:
  177. # chunk size is either [1, 25] or full context(max_len).
  178. # Since we use 4 times subsampling and allow up to 1s(100 frames)
  179. # delay, the maximum frame is 100 / 4 = 25.
  180. chunk_size = torch.randint(1, max_len, (1, )).item()
  181. num_left_chunks = -1
  182. if chunk_size > max_len // 2 and enable_full_context:
  183. chunk_size = max_len
  184. else:
  185. chunk_size = chunk_size % 25 + 1
  186. if use_dynamic_left_chunk:
  187. max_left_chunks = (max_len - 1) // chunk_size
  188. num_left_chunks = torch.randint(0, max_left_chunks,
  189. (1, )).item()
  190. chunk_masks = subsequent_chunk_mask(xs.size(1), chunk_size,
  191. num_left_chunks,
  192. xs.device) # (L, L)
  193. chunk_masks = chunk_masks.unsqueeze(0) # (1, L, L)
  194. chunk_masks = masks & chunk_masks # (B, L, L)
  195. elif static_chunk_size > 0:
  196. num_left_chunks = num_decoding_left_chunks
  197. chunk_masks = subsequent_chunk_mask(xs.size(1), static_chunk_size,
  198. num_left_chunks,
  199. xs.device) # (L, L)
  200. chunk_masks = chunk_masks.unsqueeze(0) # (1, L, L)
  201. chunk_masks = masks & chunk_masks # (B, L, L)
  202. else:
  203. chunk_masks = masks
  204. return chunk_masks
  205. def make_pad_mask(lengths: torch.Tensor, max_len: int = 0) -> torch.Tensor:
  206. """Make mask tensor containing indices of padded part.
  207. See description of make_non_pad_mask.
  208. Args:
  209. lengths (torch.Tensor): Batch of lengths (B,).
  210. Returns:
  211. torch.Tensor: Mask tensor containing indices of padded part.
  212. Examples:
  213. >>> lengths = [5, 3, 2]
  214. >>> make_pad_mask(lengths)
  215. masks = [[0, 0, 0, 0 ,0],
  216. [0, 0, 0, 1, 1],
  217. [0, 0, 1, 1, 1]]
  218. """
  219. batch_size = lengths.size(0)
  220. max_len = max_len if max_len > 0 else lengths.max().item()
  221. seq_range = torch.arange(0,
  222. max_len,
  223. dtype=torch.int64,
  224. device=lengths.device)
  225. seq_range_expand = seq_range.unsqueeze(0).expand(batch_size, max_len)
  226. seq_length_expand = lengths.unsqueeze(-1)
  227. mask = seq_range_expand >= seq_length_expand
  228. return mask