mask.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. pos_idx = torch.arange(size, device=device)
  131. block_value = (torch.div(pos_idx, chunk_size, rounding_mode='trunc') + 1) * chunk_size
  132. ret = pos_idx.unsqueeze(0) < block_value.unsqueeze(1)
  133. return ret
  134. def add_optional_chunk_mask(xs: torch.Tensor,
  135. masks: torch.Tensor,
  136. use_dynamic_chunk: bool,
  137. use_dynamic_left_chunk: bool,
  138. decoding_chunk_size: int,
  139. static_chunk_size: int,
  140. num_decoding_left_chunks: int,
  141. enable_full_context: bool = True):
  142. """ Apply optional mask for encoder.
  143. Args:
  144. xs (torch.Tensor): padded input, (B, L, D), L for max length
  145. mask (torch.Tensor): mask for xs, (B, 1, L)
  146. use_dynamic_chunk (bool): whether to use dynamic chunk or not
  147. use_dynamic_left_chunk (bool): whether to use dynamic left chunk for
  148. training.
  149. decoding_chunk_size (int): decoding chunk size for dynamic chunk, it's
  150. 0: default for training, use random dynamic chunk.
  151. <0: for decoding, use full chunk.
  152. >0: for decoding, use fixed chunk size as set.
  153. static_chunk_size (int): chunk size for static chunk training/decoding
  154. if it's greater than 0, if use_dynamic_chunk is true,
  155. this parameter will be ignored
  156. num_decoding_left_chunks: number of left chunks, this is for decoding,
  157. the chunk size is decoding_chunk_size.
  158. >=0: use num_decoding_left_chunks
  159. <0: use all left chunks
  160. enable_full_context (bool):
  161. True: chunk size is either [1, 25] or full context(max_len)
  162. False: chunk size ~ U[1, 25]
  163. Returns:
  164. torch.Tensor: chunk mask of the input xs.
  165. """
  166. # Whether to use chunk mask or not
  167. if use_dynamic_chunk:
  168. max_len = xs.size(1)
  169. if decoding_chunk_size < 0:
  170. chunk_size = max_len
  171. num_left_chunks = -1
  172. elif decoding_chunk_size > 0:
  173. chunk_size = decoding_chunk_size
  174. num_left_chunks = num_decoding_left_chunks
  175. else:
  176. # chunk size is either [1, 25] or full context(max_len).
  177. # Since we use 4 times subsampling and allow up to 1s(100 frames)
  178. # delay, the maximum frame is 100 / 4 = 25.
  179. chunk_size = torch.randint(1, max_len, (1, )).item()
  180. num_left_chunks = -1
  181. if chunk_size > max_len // 2 and enable_full_context:
  182. chunk_size = max_len
  183. else:
  184. chunk_size = chunk_size % 25 + 1
  185. if use_dynamic_left_chunk:
  186. max_left_chunks = (max_len - 1) // chunk_size
  187. num_left_chunks = torch.randint(0, max_left_chunks,
  188. (1, )).item()
  189. chunk_masks = subsequent_chunk_mask(xs.size(1), chunk_size,
  190. num_left_chunks,
  191. xs.device) # (L, L)
  192. chunk_masks = chunk_masks.unsqueeze(0) # (1, L, L)
  193. chunk_masks = masks & chunk_masks # (B, L, L)
  194. elif static_chunk_size > 0:
  195. num_left_chunks = num_decoding_left_chunks
  196. chunk_masks = subsequent_chunk_mask(xs.size(1), static_chunk_size,
  197. num_left_chunks,
  198. xs.device) # (L, L)
  199. chunk_masks = chunk_masks.unsqueeze(0) # (1, L, L)
  200. chunk_masks = masks & chunk_masks # (B, L, L)
  201. else:
  202. chunk_masks = masks
  203. assert chunk_masks.dtype == torch.bool
  204. if (chunk_masks.sum(dim=-1) == 0).sum().item() != 0:
  205. print('get chunk_masks all false at some timestep, force set to true, make sure they are masked in futuer computation!')
  206. chunk_masks[chunk_masks.sum(dim=-1) == 0] = True
  207. return chunk_masks
  208. def make_pad_mask(lengths: torch.Tensor, max_len: int = 0) -> torch.Tensor:
  209. """Make mask tensor containing indices of padded part.
  210. See description of make_non_pad_mask.
  211. Args:
  212. lengths (torch.Tensor): Batch of lengths (B,).
  213. Returns:
  214. torch.Tensor: Mask tensor containing indices of padded part.
  215. Examples:
  216. >>> lengths = [5, 3, 2]
  217. >>> make_pad_mask(lengths)
  218. masks = [[0, 0, 0, 0 ,0],
  219. [0, 0, 0, 1, 1],
  220. [0, 0, 1, 1, 1]]
  221. """
  222. batch_size = lengths.size(0)
  223. max_len = max_len if max_len > 0 else lengths.max().item()
  224. seq_range = torch.arange(0,
  225. max_len,
  226. dtype=torch.int64,
  227. device=lengths.device)
  228. seq_range_expand = seq_range.unsqueeze(0).expand(batch_size, max_len)
  229. seq_length_expand = lengths.unsqueeze(-1)
  230. mask = seq_range_expand >= seq_length_expand
  231. return mask