| 676 | } |
| 677 | |
| 678 | Status GatherV2Grad(const Scope& scope, const Operation& op, |
| 679 | const std::vector<Output>& grad_inputs, |
| 680 | std::vector<Output>* grad_outputs) { |
| 681 | if (op.num_inputs() != 3) { |
| 682 | return errors::InvalidArgument("Gather requires 3 inputs"); |
| 683 | } |
| 684 | if (grad_inputs.size() != 1) { |
| 685 | return errors::InvalidArgument("Gather grad requires 1 grad input"); |
| 686 | } |
| 687 | |
| 688 | // params can be large, so colocate the shape calculation with it. |
| 689 | // params can be very large for sparse model, array_ops.shape raises |
| 690 | // exception on the Windows platform when any dimension is larger than |
| 691 | // int32. params_shape is not used in optimizer apply_sparse gradients, |
| 692 | // so it's fine to convert it back to int32 regardless of truncation. |
| 693 | auto params = op.input(0); |
| 694 | auto colocate_scope = scope.ColocateWith(params); |
| 695 | Shape::Attrs shape_attrs; |
| 696 | shape_attrs.out_type_ = DT_INT64; |
| 697 | auto params_shape64 = Shape(colocate_scope, params, shape_attrs); |
| 698 | Output params_shape = Cast(colocate_scope, params_shape64, DT_INT32); |
| 699 | |
| 700 | auto indices = op.input(1); |
| 701 | auto indices_size = ExpandDims(scope, Size(scope, indices), 0); |
| 702 | auto axis = op.input(2); |
| 703 | auto axis_expand = ExpandDims(scope, axis, 0); |
| 704 | |
| 705 | int batch_dims; |
| 706 | TF_RETURN_IF_ERROR( |
| 707 | GetNodeAttr(op.node()->attrs(), "batch_dims", &batch_dims)); |
| 708 | if (batch_dims < 0) { |
| 709 | // TODO(bdodson): Figure out if we can find the param rank here, like the |
| 710 | // python implementation does. |
| 711 | return errors::InvalidArgument( |
| 712 | "C++ GatherV2 gradient does not support negative batch_dims."); |
| 713 | } |
| 714 | |
| 715 | // Handle axis by transposing the axis dimension to be the first non-batch |
| 716 | // dimension, compute the gradient and transpose the result back. |
| 717 | auto outer_shape = Slice(scope, params_shape, {0}, axis_expand); |
| 718 | auto inner_shape = |
| 719 | Slice(scope, Slice(scope, params_shape, axis_expand, {-1}), {1}, {-1}); |
| 720 | auto values_shape = Concat(scope, {outer_shape, {-1}, inner_shape}, 0); |
| 721 | auto values_dims = Size(scope, values_shape); |
| 722 | auto axis_dims = Size(scope, outer_shape); |
| 723 | |
| 724 | Output outer_batches_indices = Range(scope, 0, batch_dims, /*delta=*/1); |
| 725 | Output batch_axis_indices = Range(scope, batch_dims, axis_dims, /*delta=*/1); |
| 726 | Output inner_axes_indices = |
| 727 | Range(scope, Add(scope, axis_dims, 1), values_dims, /*delta=*/1); |
| 728 | Output axis_dims_expand = ExpandDims(scope, axis_dims, 0); |
| 729 | |
| 730 | auto values = Reshape(scope, grad_inputs[0], values_shape); |
| 731 | |
| 732 | // Move values[axis] up to values[batch_dims] |
| 733 | Output transpose_dims = Concat(scope, |
| 734 | {outer_batches_indices, axis_dims_expand, |
| 735 | batch_axis_indices, inner_axes_indices}, |
nothing calls this directly
no test coverage detected