This function chunks the `input_tensors` into smaller input tensor parts of size `chunk_size` over the dimension `chunk_dim`. It then applies a layer `forward_fn` to each chunk independently to save memory. If the `forward_fn` is independent across the `chunk_dim` this function will yie
(
chunk_size: int, chunk_dim: int, forward_fn: Callable[..., torch.Tensor], *input_tensors
)
| 1206 | |
| 1207 | |
| 1208 | def apply_chunking_to_forward( |
| 1209 | chunk_size: int, chunk_dim: int, forward_fn: Callable[..., torch.Tensor], *input_tensors |
| 1210 | ) -> torch.Tensor: |
| 1211 | """ |
| 1212 | This function chunks the `input_tensors` into smaller input tensor parts of size `chunk_size` over the dimension `chunk_dim`. |
| 1213 | It then applies a layer `forward_fn` to each chunk independently to save memory. |
| 1214 | If the `forward_fn` is independent across the `chunk_dim` this function will yield the |
| 1215 | same result as not applying it. |
| 1216 | |
| 1217 | Args: |
| 1218 | chunk_size: int - the chunk size of a chunked tensor. `num_chunks` = `len(input_tensors[0]) / chunk_size` |
| 1219 | chunk_dim: int - the dimension over which the input_tensors should be chunked |
| 1220 | forward_fn: fn - the forward fn of the model |
| 1221 | input_tensors: tuple(torch.Tensor) - the input tensors of `forward_fn` which are chunked |
| 1222 | Returns: |
| 1223 | a Tensor with the same shape the foward_fn would have given if applied |
| 1224 | |
| 1225 | |
| 1226 | Examples:: |
| 1227 | |
| 1228 | # rename the usual forward() fn to forward_chunk() |
| 1229 | def forward_chunk(self, hidden_states): |
| 1230 | hidden_states = self.decoder(hidden_states) |
| 1231 | return hidden_states |
| 1232 | |
| 1233 | # implement a chunked forward function |
| 1234 | def forward(self, hidden_states): |
| 1235 | return apply_chunking_to_forward(self.chunk_size_lm_head, self.seq_len_dim, self.forward_chunk, hidden_states) |
| 1236 | """ |
| 1237 | |
| 1238 | assert len(input_tensors) > 0, "{} has to be a tuple/list of tensors".format(input_tensors) |
| 1239 | tensor_shape = input_tensors[0].shape |
| 1240 | assert all( |
| 1241 | input_tensor.shape == tensor_shape for input_tensor in input_tensors |
| 1242 | ), "All input tenors have to be of the same shape" |
| 1243 | |
| 1244 | # inspect.signature exist since python 3.5 and is a python method -> no problem with backward compability |
| 1245 | num_args_in_forward_chunk_fn = len(inspect.signature(forward_fn).parameters) |
| 1246 | assert num_args_in_forward_chunk_fn == len( |
| 1247 | input_tensors |
| 1248 | ), "forward_chunk_fn expects {} arguments, but only {} input tensors are given".format( |
| 1249 | num_args_in_forward_chunk_fn, len(input_tensors) |
| 1250 | ) |
| 1251 | |
| 1252 | if chunk_size > 0: |
| 1253 | assert ( |
| 1254 | input_tensors[0].shape[chunk_dim] % chunk_size == 0 |
| 1255 | ), "The dimension to be chunked {} has to be a multiple of the chunk size {}".format( |
| 1256 | input_tensors[0].shape[chunk_dim], chunk_size |
| 1257 | ) |
| 1258 | |
| 1259 | num_chunks = input_tensors[0].shape[chunk_dim] // chunk_size |
| 1260 | |
| 1261 | # chunk input tensor into tuples |
| 1262 | input_tensors_chunks = tuple(input_tensor.chunk(num_chunks, dim=chunk_dim) for input_tensor in input_tensors) |
| 1263 | # apply forward fn to every tuple |
| 1264 | output_chunks = tuple(forward_fn(*input_tensors_chunk) for input_tensors_chunk in zip(*input_tensors_chunks)) |
| 1265 | # concatenate output at same dimension |
nothing calls this directly
no outgoing calls
no test coverage detected