| 783 | |
| 784 | template <typename TensorDataType> |
| 785 | void bp_compute_impl( |
| 786 | gru_layer<TensorDataType, data_layout::DATA_PARALLEL, El::Device::CPU>& l) |
| 787 | { |
| 788 | |
| 789 | // Matrices |
| 790 | using LocalMat = El::Matrix<TensorDataType, El::Device::CPU>; |
| 791 | const auto& output_sequence_grad = |
| 792 | dynamic_cast<const LocalMat&>(l.get_local_prev_error_signals()); |
| 793 | auto& input_sequence_grad = |
| 794 | dynamic_cast<LocalMat&>(l.get_local_error_signals(0)); |
| 795 | auto& init_hidden_grad = |
| 796 | dynamic_cast<LocalMat&>(l.get_local_error_signals(1)); |
| 797 | |
| 798 | // Dimensions |
| 799 | const int local_mini_batch_size = output_sequence_grad.Width(); |
| 800 | const int sequence_length = l.get_input_dims(0)[0]; |
| 801 | const int input_size = l.get_input_size(0) / sequence_length; |
| 802 | const int hidden_size = l.m_hidden_size; |
| 803 | const int num_layers = l.m_num_layers; |
| 804 | |
| 805 | // oneDNN objects |
| 806 | if (l.m_onednn_cpu_objects == nullptr) { |
| 807 | LBANN_ERROR(l.get_type(), |
| 808 | " layer \"", |
| 809 | l.get_name(), |
| 810 | "\" ", |
| 811 | "attempted to run oneDNN CPU implementation ", |
| 812 | "before initializing oneDNN objects"); |
| 813 | } |
| 814 | constexpr auto Device = El::Device::CPU; |
| 815 | using Backend = onednn_backend<Device>; |
| 816 | const auto data_type = Backend::template data_type<TensorDataType>(); |
| 817 | auto& onednn_objects = *l.m_onednn_cpu_objects; |
| 818 | auto sync_info = |
| 819 | force(El::MakeMultiSync(get_sync_info(input_sequence_grad), |
| 820 | get_sync_info(init_hidden_grad), |
| 821 | get_sync_info(output_sequence_grad))); |
| 822 | auto& engine = onednn::get_device_engine<Device>(); |
| 823 | auto stream = onednn::get_stream<Device>(engine, sync_info); |
| 824 | |
| 825 | // Define closure to send weight gradients to optimizers |
| 826 | std::vector<LocalMat> weights_grad_list(4 * num_layers); |
| 827 | for (int i = 0; i < num_layers; ++i) { |
| 828 | weights_grad_list[4 * i].Resize(3 * hidden_size, input_size); |
| 829 | weights_grad_list[4 * i + 1].Resize(3 * hidden_size, hidden_size); |
| 830 | weights_grad_list[4 * i + 2].Resize(3 * hidden_size, 1); |
| 831 | weights_grad_list[4 * i + 3].Resize(3 * hidden_size, 1); |
| 832 | } |
| 833 | auto send_weight_grads_to_optimizers = [&]() { |
| 834 | TensorDataType buf_scale, in_scale; |
| 835 | for (int i = 0; i < 4 * num_layers; ++i) { |
| 836 | auto&& opt = l.get_weights(i).get_optimizer(); |
| 837 | if (opt != nullptr) { |
| 838 | auto& buf = opt->get_gradient_buffer(buf_scale, in_scale, true); |
| 839 | El::Scale(buf_scale, buf); |
| 840 | El::Axpy(in_scale, weights_grad_list[i], buf.Matrix()); |
| 841 | } |
| 842 | } |
no test coverage detected