Gets the backward pass that computes the derivatives of given blobs. Inputs: ys: a list or a dictionary specifying what blobs we want to compute derivatives of. If the input is a list, we will automatically generate their gradients with all-one values;
(self, ys)
| 1044 | return new_input_to_grad, gradient_ops |
| 1045 | |
| 1046 | def GetBackwardPass(self, ys): |
| 1047 | """Gets the backward pass that computes the derivatives of given blobs. |
| 1048 | |
| 1049 | Inputs: |
| 1050 | ys: a list or a dictionary specifying what blobs we want to compute |
| 1051 | derivatives of. If the input is a list, we will automatically |
| 1052 | generate their gradients with all-one values; if the input is a |
| 1053 | dictionary, for any dictionary entries that are not None, we will |
| 1054 | take the corresponding blobs as their gradients; for all those |
| 1055 | that are None, we will auto-fill them with 1. |
| 1056 | """ |
| 1057 | if isinstance(ys, list): |
| 1058 | ys = dict((y, None) for y in ys) |
| 1059 | elif not isinstance(ys, dict): |
| 1060 | raise TypeError("ys should either be a list or a dict.") |
| 1061 | |
| 1062 | # Set the gradient frontier with the initialized external |
| 1063 | # gradients. |
| 1064 | for y in ys.keys(): |
| 1065 | self.gradient_frontier[y] = self.frontier[y] |
| 1066 | self.input_usages[str(y)][self.frontier[str(y)]].append( |
| 1067 | len(self.ssa)) |
| 1068 | |
| 1069 | all_input_to_grad, all_gradient_ops = self._GetInitGradients(ys) |
| 1070 | |
| 1071 | # (2) Now, after having the virtual play above, we now play the ops |
| 1072 | # backwards, creating the gradients along the path. Note that although |
| 1073 | # we are playing it backwards, we cannot refer to variables that are |
| 1074 | # at a version older than current_versions because it is already been |
| 1075 | # overwritten. |
| 1076 | for forward_op_idx in reversed(range(len(self.ssa))): |
| 1077 | input_to_grad, gradient_ops = self._GenerateGradientsForForwardOp( |
| 1078 | forward_op_idx, all_input_to_grad) |
| 1079 | all_input_to_grad.update(input_to_grad) |
| 1080 | all_gradient_ops += gradient_ops |
| 1081 | |
| 1082 | # If there are multiple use blobs, do gradient accumulation. |
| 1083 | additional_sum_ops, grad_map = self.DoGradientAccumulation( |
| 1084 | forward_op_idx) |
| 1085 | # This line is so that if in an accumulation some of the operators |
| 1086 | # have not produced gradients, they still do not overwrite the |
| 1087 | # general all_input_to_grad map. |
| 1088 | all_input_to_grad.update(grad_map) |
| 1089 | all_gradient_ops += additional_sum_ops |
| 1090 | |
| 1091 | # (3) Post-processing. |
| 1092 | # After we have done computation for each op, we now have the gradient |
| 1093 | # operators ready. For the output map, we will convert everything to |
| 1094 | # BlobReferences for easier handling in python. |
| 1095 | all_input_to_grad_out = {} |
| 1096 | for key, val in all_input_to_grad.items(): |
| 1097 | if val is not None: |
| 1098 | if isinstance(val, (bytes, str)): |
| 1099 | grad_out = BlobReference(val) |
| 1100 | else: |
| 1101 | grad_out = GradientSlice(BlobReference(val[0]), |
| 1102 | BlobReference(val[1])) |
| 1103 | all_input_to_grad_out[BlobReference(key)] = grad_out |