// Copyright © Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #include #include #include #include "tensor_holder.hpp" #include "compare_helper.hpp" #include "gtest_desc_guard.hpp" #include "gtest_handle_guard.hpp" #include "../dropout_util.hpp" #include "../rnn_util.hpp" #include "../workspace.hpp" #include namespace { /// Specific version for compating CPU and GPU results, because this test is order-dependent template auto GruTestCompareResults(VerifyT&& verifier, double tolerance = 80.f) -> std::pair { const auto gpu_result = verifier.gpu(); const auto cpu_result = verifier.cpu(); if(!test_helpers::Compare(cpu_result, gpu_result, tolerance)) { verifier.fail(); } return std::make_pair(cpu_result, gpu_result); } /********************************************** * CPU verification functions * **********************************************/ template void GRUFwdCPUVerify(const miopen::Handle& handle, bool use_dropout, miopen::DropoutDescriptor& dropoutDesc, std::vector& in, std::vector& wei, // [ input_state_weight_trans // hidden_state_weight0_trans input1_trans // hidden1_trans ... output_weight; // bidirectional reversed weights ] std::vector& hy, // current/final hidden state std::vector& hx, // initial hidden state std::vector& out, const std::vector& in_n, // input batch size int in_h, // input data length int seqLength, // Number of iterations to unroll over bool bidirection, // whether using bidirectional net bool biased, // whether using bias int hy_d, // 1 by numlayer (number of stacks of hidden layers) for // unidirection, 2 by numlayer for bidirection int hy_n, // equal to input batch size in_n[0] int hy_h, // hidden state number int out_h, // 1 by hy_h related function for unidirection, 2 by hy_h // related function for bidirection int inputMode, std::vector& rsvspace, bool hx_is_null = false) { int batch_n = sumvc(in_n); int numlayer = bidirection ? hy_d / 2 : hy_d; int bi = bidirection ? 2 : 1; int in_stride = in_h; int out_stride = out_h; int wei_stride = bi * 3 * hy_h; int hy_stride = bi * 4 * hy_h; int h_stride = bi * hy_h; int uni_stride = hy_h; int bi_stride = hy_h * bi; if(inputMode == 1) { if(in_h != hy_h) { std::cout << "Verification cannot be completed: The input tensor size must equal to the " << "hidden state size of the network in SKIP_INPUT mode!" << std::endl; return; } in_h = 0; } int wei_shift_bias = (in_h + hy_h + (bi * hy_h + hy_h) * (numlayer - 1)) * wei_stride; // initial dropoput std::vector dropout_states_host; std::vector dropout_reservespace_host; std::vector dropout_hid_state; TensorDescGuard dropout_inputTensor; TensorDescGuard dropout_outputTensor; if(use_dropout) { size_t states_size = dropoutDesc.stateSizeInBytes / sizeof(rocrand_state_xorwow); dropout_states_host = std::vector(states_size); InitKernelStateEmulator(dropout_states_host, dropoutDesc); std::array drop_in_len = {{batch_n, hy_h * bi}}; std::array drop_in_str = {{hy_stride, 1}}; std::array drop_out_str = {{hy_h * bi, 1}}; miopenSetTensorDescriptor( dropout_inputTensor, miopenFloat, 2, drop_in_len.data(), drop_in_str.data()); miopenSetTensorDescriptor( dropout_outputTensor, miopenFloat, 2, drop_in_len.data(), drop_out_str.data()); size_t reserveSpaceSizeInBytes = 0; miopenDropoutGetReserveSpaceSize(dropout_inputTensor, &reserveSpaceSizeInBytes); size_t reserve_size = reserveSpaceSizeInBytes / sizeof(unsigned char); dropout_reservespace_host = std::vector(reserve_size * (numlayer - 1), static_cast(1)); dropout_hid_state = std::vector((numlayer - 1) * batch_n * hy_h * bi, static_cast(0)); } // forward emulator for(int li = 0; li < numlayer; li++) { int hid_shift = li * batch_n * hy_stride; int hx_shift = li * in_n.at(0) * h_stride; int wei_shift_bias_temp = wei_shift_bias + li * 2 * wei_stride; // from input if(li == 0) { if(inputMode == 1) { for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 3; gi++) { rsvspace[hid_shift + bs * hy_stride + gi * hy_h + h] += in[bs * in_stride + h]; if(bidirection) { rsvspace[hid_shift + bs * hy_stride + (gi + 3) * hy_h + h] += in[bs * in_stride + h]; } } } } // from bias if(biased) { for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < wei_stride; h++) { rsvspace[hid_shift + bs * hy_stride + h] += wei[wei_shift_bias + h]; } } } } else { gemm_cpu(in.data(), in_h, batch_n, in_stride, false, wei.data(), // wei_state.data(), in_h, hy_h * bi * 3, in_stride, true, &rsvspace[hid_shift], hy_h * bi * 3, batch_n, hy_stride, 1, 1); // from bias if(biased) { for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < wei_stride; h++) { rsvspace[hid_shift + bs * hy_stride + h] += wei[wei_shift_bias + h]; } } } } } else { int wei_shift = (in_h + hy_h) * wei_stride + (li - 1) * (bi * hy_h + hy_h) * wei_stride; int prelayer_shift = (li - 1) * batch_n * hy_stride + bi * 3 * hy_h; if(use_dropout) { auto dropout_states_tmp = dropout_states_host; size_t drop_out_offset = (static_cast(li) - 1) * batch_n * hy_h * bi; DropoutForwardVerify(handle, dropoutDesc, miopen::deref(dropout_inputTensor.get()), rsvspace, miopen::deref(dropout_outputTensor.get()), dropout_hid_state, dropout_reservespace_host, dropout_states_tmp, prelayer_shift, drop_out_offset, drop_out_offset); prelayer_shift = drop_out_offset; } gemm_cpu(use_dropout ? &dropout_hid_state[prelayer_shift] : &rsvspace[prelayer_shift], hy_h * bi, batch_n, use_dropout ? hy_h * bi : hy_stride, false, &wei[wei_shift], //&wei_state[wei_shift], hy_h * bi, hy_h * bi * 3, bi_stride, true, &rsvspace[hid_shift], hy_h * bi * 3, batch_n, hy_stride, 1, 1); // from bias if(biased) { for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < wei_stride; h++) { rsvspace[hid_shift + bs * hy_stride + h] += wei[wei_shift_bias_temp + h]; } } } } // from hidden state int bacc = 0; int baccbi = batch_n; for(int ti = 0; ti < seqLength; ti++) { baccbi -= in_n.at(seqLength - 1 - ti); int wei_shift = in_h * wei_stride + li * (bi * hy_h + hy_h) * wei_stride; int pretime_shift; if(ti == 0) { if(!hx_is_null) { gemm_cpu(&hx[hx_shift], hy_h, in_n.at(ti), uni_stride, false, &wei[wei_shift], hy_h, hy_h * 2, uni_stride, true, &rsvspace[hid_shift + bacc * hy_stride], hy_h * 2, in_n.at(ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 2; gi++) { rsvspace[hid_shift + (bacc + bs) * hy_stride + gi * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + gi * hy_h + h]; } } } } gemm_cpu(&hx[hx_shift], hy_h, in_n.at(ti), uni_stride, false, &wei[wei_shift + 2 * hy_h * uni_stride], hy_h, hy_h, uni_stride, true, &rsvspace[hid_shift + bacc * hy_stride + bi * 3 * hy_h], hy_h, in_n.at(ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + 2 * hy_h + h]; } } } if(bidirection) { gemm_cpu(&hx[hx_shift + hy_n * hy_h], hy_h, in_n.at(seqLength - 1 - ti), uni_stride, false, &wei[wei_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 2, uni_stride, true, &rsvspace[hid_shift + baccbi * hy_stride + 3 * hy_h], hy_h * 2, in_n.at(seqLength - 1 - ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 2; gi++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + (3 + gi) * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + (3 + gi) * hy_h + h]; } } } } gemm_cpu(&hx[hx_shift + hy_n * hy_h], hy_h, in_n.at(seqLength - 1 - ti), uni_stride, false, &wei[wei_shift + 5 * hy_h * uni_stride], hy_h, hy_h, uni_stride, true, &rsvspace[hid_shift + baccbi * hy_stride + bi * 3 * hy_h + hy_h], hy_h, in_n.at(seqLength - 1 - ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += wei[wei_shift_bias_temp + wei_stride + 5 * hy_h + h]; } } } } } } else { gemm_cpu(&hy[hx_shift], hy_h, in_n.at(ti), uni_stride, false, &wei[wei_shift], hy_h, hy_h * 2, uni_stride, true, &rsvspace[hid_shift + bacc * hy_stride], hy_h * 2, in_n.at(ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 2; gi++) { rsvspace[hid_shift + (bacc + bs) * hy_stride + gi * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + gi * hy_h + h]; } } } } gemm_cpu(&hy[hx_shift], hy_h, in_n.at(ti), uni_stride, false, &wei[wei_shift + 2 * hy_h * uni_stride], hy_h, hy_h, uni_stride, true, &rsvspace[hid_shift + bacc * hy_stride + bi * 3 * hy_h], hy_h, in_n.at(ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + 2 * hy_h + h]; } } } if(bidirection) { if(!hx_is_null && in_n.at(seqLength - 1 - ti) > in_n.at(seqLength - ti)) { gemm_cpu( &hx[hx_shift + hy_n * hy_h + in_n.at(seqLength - ti) * hy_h], hy_h, (in_n.at(seqLength - 1 - ti) - in_n.at(seqLength - ti)), uni_stride, false, &wei[wei_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 2, uni_stride, true, &rsvspace[hid_shift + (baccbi + in_n.at(seqLength - ti)) * hy_stride + 3 * hy_h], hy_h * 2, (in_n.at(seqLength - 1 - ti) - in_n.at(seqLength - ti)), hy_stride, 1, 1); if(biased) { for(int bs = in_n.at(seqLength - ti); bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 2; gi++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + (3 + gi) * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + (3 + gi) * hy_h + h]; } } } } gemm_cpu( &hx[hx_shift + hy_n * hy_h + in_n.at(seqLength - ti) * hy_h], hy_h, (in_n.at(seqLength - 1 - ti) - in_n.at(seqLength - ti)), uni_stride, false, &wei[wei_shift + 5 * hy_h * uni_stride], hy_h, hy_h, uni_stride, true, &rsvspace[hid_shift + (baccbi + in_n.at(seqLength - ti)) * hy_stride + bi * 3 * hy_h + hy_h], hy_h, (in_n.at(seqLength - 1 - ti) - in_n.at(seqLength - ti)), hy_stride, 1, 1); if(biased) { for(int bs = in_n.at(seqLength - ti); bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += wei[wei_shift_bias_temp + wei_stride + 5 * hy_h + h]; } } } } gemm_cpu(&hy[hx_shift + hy_n * hy_h], hy_h, in_n.at(seqLength - ti), uni_stride, false, &wei[wei_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 2, uni_stride, true, &rsvspace[hid_shift + baccbi * hy_stride + 3 * hy_h], hy_h * 2, in_n.at(seqLength - ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(seqLength - ti); bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 2; gi++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + (3 + gi) * hy_h + h] += wei[wei_shift_bias_temp + wei_stride + (3 + gi) * hy_h + h]; } } } } gemm_cpu(&hy[hx_shift + hy_n * hy_h], hy_h, in_n.at(seqLength - ti), uni_stride, false, &wei[wei_shift + 5 * hy_h * uni_stride], hy_h, hy_h, uni_stride, true, &rsvspace[hid_shift + baccbi * hy_stride + bi * 3 * hy_h + hy_h], hy_h, in_n.at(seqLength - ti), hy_stride, 1, 1); if(biased) { for(int bs = 0; bs < in_n.at(seqLength - ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += wei[wei_shift_bias_temp + wei_stride + 5 * hy_h + h]; } } } } } for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h + numlayer * batch_n * hy_stride] = rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h]; rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h] += activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + hy_h + h], 2) * rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h]; rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] = 0; if(ti == 0) { if(!hx_is_null) { rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += ((1 - activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2)) * activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1) + activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2) * hx[hx_shift + bs * uni_stride + h]); } else { rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += ((1 - activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2)) * activfunc( rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1)); } } else { pretime_shift = li * batch_n * hy_stride + (bacc - in_n.at(ti - 1)) * hy_stride + bi * 3 * hy_h; rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += ((1 - activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2)) * activfunc( rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1) + activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2) * rsvspace[pretime_shift + bs * hy_stride + h]); } rsvspace[hid_shift + (bacc + bs) * hy_stride + h + numlayer * batch_n * hy_stride] = activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2); rsvspace[hid_shift + (bacc + bs) * hy_stride + hy_h + h + numlayer * batch_n * hy_stride] = activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + hy_h + h], 2); rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h + numlayer * batch_n * hy_stride] = activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1); // Update final state hy[hx_shift + bs * uni_stride + h] = rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h]; } } if(bidirection) { pretime_shift = li * batch_n * hy_stride + (baccbi + in_n.at(seqLength - 1 - ti)) * hy_stride + bi * 3 * hy_h + hy_h; for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h + numlayer * batch_n * hy_stride] = rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h]; rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h] += activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h], 2) * rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h]; rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] = 0; if(ti == 0) { if(!hx_is_null) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += ((1 - activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)) * activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1) + activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2) * hx[hx_shift + bs * uni_stride + hy_n * hy_h + h]); } else { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += ((1 - activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)) * activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1)); } } else { if(!hx_is_null && in_n.at(seqLength - 1 - ti) > in_n.at(seqLength - ti)) { if(bs >= in_n.at(seqLength - ti)) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += (activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2) * hx[hx_shift + bs * uni_stride + hy_n * hy_h + h]); } } rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += ((1 - activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)) * activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1)); if(bs < in_n.at(seqLength - ti)) { rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += (activfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2) * rsvspace[pretime_shift + bs * hy_stride + h]); } } rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h + numlayer * batch_n * hy_stride] = activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2); rsvspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h + numlayer * batch_n * hy_stride] = activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h], 2); rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h + numlayer * batch_n * hy_stride] = activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1); // Update final hidden state hy[hx_shift + bs * uni_stride + hy_n * hy_h + h] = rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h]; } } } bacc += in_n.at(ti); } } // output int prelayer_shift = (numlayer - 1) * batch_n * hy_stride + bi * 3 * hy_h; for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < out_h; h++) { out[bs * out_stride + h] = rsvspace[prelayer_shift + bs * hy_stride + h]; } } if(use_dropout) { for(int i = 0; i < (numlayer - 1) * batch_n * hy_h * bi; i++) { rsvspace.at(numlayer * batch_n * hy_stride * 2 + i) = dropout_hid_state.at(i); } auto p_drop_rsv = reinterpret_cast(&rsvspace.at( numlayer * batch_n * hy_stride * 2 + (numlayer - 1) * batch_n * hy_h * bi)); for(int i = 0; i < (numlayer - 1) * batch_n * hy_h * bi; i++) { *(p_drop_rsv + i) = dropout_reservespace_host.at(i); } } } template void GRUBwdDataCPUVerify(bool use_dropout, miopen::DropoutDescriptor& dropoutDesc, std::vector& din, std::vector& wei, // [ input_state_weight_trans // hidden_state_weight0_trans input1_trans // hidden1_trans ... output_weight; // bidirectional reversed weights ] std::vector& dhy, // current/final hidden state std::vector& dhx, std::vector& hx, // initial hidden state std::vector& out, std::vector& dout, const std::vector& in_n, // input batch size int in_h, // input data length int seqLength, // Number of iterations to unroll over bool bidirection, // whether using bidirectional net bool, // whether using bias int hy_d, // 1 by numlayer (number of stacks of hidden layers) // for unidirection, 2 by numlayer for bidirection int hy_n, // equal to input batch size in_n[0] int hy_h, // hidden state number int out_h, // 1 by hy_h related function for unidirection, 2 by // hy_h related function for bidirection int inputMode, std::vector& rsvspace, std::vector& wkspace, bool hx_is_null = false, bool dhy_is_null = false) { int batch_n = sumvc(in_n); (void)out; int numlayer = bidirection ? hy_d / 2 : hy_d; int bi = bidirection ? 2 : 1; int in_stride = in_h; int out_stride = out_h; int wei_stride = bi * 3 * hy_h; int hy_stride = bi * 4 * hy_h; int h_stride = bi * hy_h; int uni_stride = hy_h; int bi_stride = hy_h * bi; // initial hidden states auto ihs = hy_d * hy_n * hy_h; std::vector dcx(ihs); if(inputMode == 1) { if(in_h != hy_h) { std::cout << "Verification cannot be completed: The input tensor size must equal to the " << "hidden state size of the network in SKIP_INPUT mode!" << std::endl; return; } in_h = 0; } // initial dropoput TensorDescGuard dropout_inputTensor; std::vector dropout_reservespace_host; if(use_dropout) { std::array drop_in_len = {{batch_n, hy_h * bi}}; std::array drop_in_str = {{hy_stride, 1}}; miopenSetTensorDescriptor( dropout_inputTensor, miopenFloat, 2, drop_in_len.data(), drop_in_str.data()); size_t reserveSpaceSizeInBytes = 0; miopenDropoutGetReserveSpaceSize(dropout_inputTensor, &reserveSpaceSizeInBytes); size_t reserve_size = reserveSpaceSizeInBytes / sizeof(unsigned char); dropout_reservespace_host = std::vector(reserve_size * (numlayer - 1), static_cast(0)); auto p_drop_rsv = reinterpret_cast(&rsvspace.at( numlayer * batch_n * hy_stride * 2 + (numlayer - 1) * batch_n * hy_h * bi)); for(int i = 0; i < (numlayer - 1) * batch_n * hy_h * bi; i++) { dropout_reservespace_host.at(i) = *(p_drop_rsv + i); } } // bwd data emulator for(int li = numlayer - 1; li >= 0; li--) { int wei_shift = (in_h + hy_h) * wei_stride + li * (bi * hy_h + hy_h) * wei_stride; int hid_shift = li * batch_n * hy_stride; int hx_shift = li * in_n.at(0) * h_stride; int weitime_shift = in_h * wei_stride + li * (bi * hy_h + hy_h) * wei_stride; if(li == numlayer - 1) { for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < out_h; h++) { wkspace[hid_shift + bi * 3 * hy_h + bs * hy_stride + h] += dout[bs * out_stride + h]; } } } else { int prelayer_shift = (li + 1) * batch_n * hy_stride; gemm_cpu(&wkspace[prelayer_shift], hy_h * bi * 3, batch_n, hy_stride, false, &wei[wei_shift], hy_h * bi, hy_h * bi * 3, bi_stride, false, &wkspace[hid_shift + bi * 3 * hy_h], hy_h * bi, batch_n, hy_stride, 1, 1); if(use_dropout) { DropoutBackwardVerify(dropoutDesc, miopen::deref(dropout_inputTensor.get()), wkspace, miopen::deref(dropout_inputTensor.get()), wkspace, dropout_reservespace_host, hid_shift + bi * 3 * hy_h, hid_shift + bi * 3 * hy_h, li * batch_n * hy_h * bi); } } // from hidden state int bacc = batch_n; int baccbi = 0; for(int ti = seqLength - 1; ti >= 0; ti--) { bacc -= in_n.at(ti); if(ti == seqLength - 1) { if(!dhy_is_null) { for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += dhy[hx_shift + bs * uni_stride + h]; } } if(bidirection) { for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += dhy[hx_shift + bs * uni_stride + hy_n * hy_h + h]; } } } } } else { if(!dhy_is_null && in_n.at(ti) > in_n.at(ti + 1)) { for(int bs = in_n.at(ti + 1); bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += dhy[hx_shift + bs * uni_stride + h]; } } } int pretime_shift = li * batch_n * hy_stride + (bacc + in_n.at(ti)) * hy_stride; gemm_cpu(&wkspace[pretime_shift], hy_h * 2, in_n.at(ti + 1), hy_stride, false, &wei[weitime_shift], hy_h, hy_h * 2, uni_stride, false, &wkspace[hid_shift + bacc * hy_stride + bi * 3 * hy_h], hy_h, in_n.at(ti + 1), hy_stride, 1, 1); for(int bs = 0; bs < in_n.at(ti + 1); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] += wkspace[pretime_shift + bs * hy_stride + bi * 3 * hy_h + h] * activfunc(rsvspace[pretime_shift + bs * hy_stride + h], 2); wkspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h] = wkspace[pretime_shift + bs * hy_stride + 2 * hy_h + h] * activfunc(rsvspace[pretime_shift + bs * hy_stride + hy_h + h], 2); } } gemm_cpu(&wkspace[hid_shift + bacc * hy_stride + 2 * hy_h], hy_h, in_n.at(ti + 1), hy_stride, false, &wei[weitime_shift + 2 * hy_h * uni_stride], hy_h, hy_h, uni_stride, false, &wkspace[hid_shift + bacc * hy_stride + bi * 3 * hy_h], hy_h, in_n.at(ti + 1), hy_stride, 1, 1); for(int bs = 0; bs < in_n.at(ti + 1); bs++) { auto subidx = hid_shift + (bacc + bs) * hy_stride + 2 * hy_h; std::fill(wkspace.begin() + subidx, wkspace.begin() + subidx + hy_h, 0); } if(bidirection) { pretime_shift = li * batch_n * hy_stride + (baccbi - in_n.at(seqLength - 2 - ti)) * hy_stride + hy_h * 3; gemm_cpu(&wkspace[pretime_shift], hy_h * 2, in_n.at(seqLength - 1 - ti), hy_stride, false, &wei[weitime_shift + hy_h * 3 * uni_stride], hy_h, hy_h * 2, uni_stride, false, &wkspace[hid_shift + baccbi * hy_stride + bi * 3 * hy_h + hy_h], hy_h, in_n.at(seqLength - 1 - ti), hy_stride, 1, 1); for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] += wkspace[pretime_shift + bs * hy_stride + 3 * hy_h + hy_h + h] * activfunc(rsvspace[pretime_shift + bs * hy_stride + h], 2); wkspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h] = wkspace[pretime_shift + bs * hy_stride + 2 * hy_h + h] * activfunc(rsvspace[pretime_shift + bs * hy_stride + hy_h + h], 2); } } gemm_cpu(&wkspace[hid_shift + baccbi * hy_stride + 5 * hy_h], hy_h, in_n.at(seqLength - 1 - ti), hy_stride, false, &wei[weitime_shift + 5 * hy_h * uni_stride], hy_h, hy_h, uni_stride, false, &wkspace[hid_shift + baccbi * hy_stride + bi * 3 * hy_h + hy_h], hy_h, in_n.at(seqLength - 1 - ti), hy_stride, 1, 1); for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { auto subidx = hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h; std::fill(wkspace.begin() + subidx, wkspace.begin() + (subidx + hy_h), 0); } } } for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h] += wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] * (1 - activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2)) * dervactivfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1); wkspace[hid_shift + (bacc + bs) * hy_stride + hy_h + h] = (rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h + numlayer * batch_n * hy_stride] * wkspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h] * dervactivfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + hy_h + h], 2)); if(ti == 0) { if(!hx_is_null) { wkspace[hid_shift + (bacc + bs) * hy_stride + h] += (wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] * hx[hx_shift + bs * uni_stride + h] * dervactivfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2)); } wkspace[hid_shift + (bacc + bs) * hy_stride + h] -= (wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] * activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1) * dervactivfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2)); } else { wkspace[hid_shift + (bacc + bs) * hy_stride + h] += wkspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h] * (rsvspace[hid_shift + (bacc - in_n.at(ti - 1) + bs) * hy_stride + bi * 3 * hy_h + h] - activfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h], 1)) * dervactivfunc(rsvspace[hid_shift + (bacc + bs) * hy_stride + h], 2); } rsvspace[hid_shift + (bacc + bs) * hy_stride + bi * 3 * hy_h + h + numlayer * batch_n * hy_stride] = wkspace[hid_shift + (bacc + bs) * hy_stride + 2 * hy_h + h] * rsvspace[hid_shift + (bacc + bs) * hy_stride + hy_h + h + numlayer * batch_n * hy_stride]; } } if(bidirection) { for(int bs = 0; bs < in_n.at(seqLength - 1 - ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h] += wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * (1 - activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)) * dervactivfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1); wkspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h] = rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h + numlayer * batch_n * hy_stride]; wkspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h] *= (wkspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h] * dervactivfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h], 2)); if(ti == 0) { if(!hx_is_null) { wkspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h] += (wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * hx[hx_shift + bs * uni_stride + hy_n * hy_h + h] * dervactivfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)); } wkspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h] -= (wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1) * dervactivfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)); } else { if(!hx_is_null && in_n.at(seqLength - 1 - ti) > in_n.at(seqLength - ti) && bs >= in_n.at(seqLength - ti)) { wkspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h] += (wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * hx[hx_shift + bs * uni_stride + hy_n * hy_h + h] * dervactivfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)); } if(bs < in_n.at(seqLength - ti)) { wkspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h] += (wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * rsvspace[hid_shift + (baccbi + in_n.at(seqLength - 1 - ti) + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * dervactivfunc(rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)); } wkspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h] -= (wkspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h] * activfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h], 1) * dervactivfunc( rsvspace[hid_shift + (baccbi + bs) * hy_stride + 3 * hy_h + h], 2)); } rsvspace[hid_shift + (baccbi + bs) * hy_stride + bi * 3 * hy_h + hy_h + h + numlayer * batch_n * hy_stride] = wkspace[hid_shift + (baccbi + bs) * hy_stride + 5 * hy_h + h] * rsvspace[hid_shift + (baccbi + bs) * hy_stride + 4 * hy_h + h + numlayer * batch_n * hy_stride]; } } } baccbi += in_n.at(seqLength - 1 - ti); } // dhx int pretime_shift = li * batch_n * hy_stride; gemm_cpu(&wkspace[pretime_shift], hy_h * 2, in_n.at(0), hy_stride, false, &wei[weitime_shift], hy_h, hy_h * 2, uni_stride, false, &dhx[hx_shift], hy_h, in_n.at(0), uni_stride, 1, 1); for(int bs = 0; bs < in_n.at(0); bs++) { for(int h = 0; h < hy_h; h++) { dhx[hx_shift + bs * uni_stride + h] += wkspace[pretime_shift + bs * hy_stride + bi * 3 * hy_h + h] * activfunc(rsvspace[pretime_shift + bs * hy_stride + h], 2); dcx[hx_shift + bs * uni_stride + h] = wkspace[pretime_shift + bs * hy_stride + 2 * hy_h + h] * activfunc(rsvspace[pretime_shift + bs * hy_stride + hy_h + h], 2); } } gemm_cpu(&dcx[hx_shift], hy_h, in_n.at(0), uni_stride, false, &wei[weitime_shift + 2 * hy_h * uni_stride], hy_h, hy_h, uni_stride, false, &dhx[hx_shift], hy_h, in_n.at(0), uni_stride, 1, 1); if(bidirection) { int ti = seqLength - 1, cur_bat = 0, pre_bat = batch_n; while(ti >= 0) { pre_bat -= in_n.at(ti); if(in_n.at(ti) > cur_bat) { pretime_shift = li * batch_n * hy_stride + (pre_bat + cur_bat) * hy_stride; gemm_cpu(&wkspace[pretime_shift + 3 * hy_h], hy_h * 2, (in_n.at(ti) - cur_bat), hy_stride, false, &wei[weitime_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 2, uni_stride, false, &dhx[hx_shift + hy_n * hy_h + cur_bat * hy_h], hy_h, (in_n.at(ti) - cur_bat), uni_stride, 1, 1); for(int bs = cur_bat; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { dhx[hx_shift + bs * uni_stride + hy_n * hy_h + h] += wkspace[pretime_shift + (bs - cur_bat) * hy_stride + bi * 3 * hy_h + hy_h + h] * activfunc(rsvspace[pretime_shift + (bs - cur_bat) * hy_stride + 3 * hy_h + h], 2); dcx[hx_shift + bs * uni_stride + hy_n * hy_h + h] = wkspace[pretime_shift + (bs - cur_bat) * hy_stride + 5 * hy_h + h] * activfunc(rsvspace[pretime_shift + (bs - cur_bat) * hy_stride + 4 * hy_h + h], 2); } } gemm_cpu(&dcx[hx_shift + hy_n * hy_h + cur_bat * hy_h], hy_h, (in_n.at(ti) - cur_bat), uni_stride, false, &wei[weitime_shift + 5 * hy_h * uni_stride], hy_h, hy_h, uni_stride, false, &dhx[hx_shift + hy_n * hy_h + cur_bat * hy_h], hy_h, (in_n.at(ti) - cur_bat), uni_stride, 1, 1); } cur_bat = in_n.at(ti--); } } } // dinput if(inputMode == 1) { for(int bs = 0; bs < batch_n; bs++) { for(int h = 0; h < hy_h; h++) { for(int gi = 0; gi < 3; gi++) { din[bs * in_stride + h] += wkspace[bs * hy_stride + gi * hy_h + h]; if(bidirection) { din[bs * in_stride + h] += wkspace[bs * hy_stride + (gi + 3) * hy_h + h]; } } } } } else { gemm_cpu(wkspace.data(), hy_h * bi * 3, batch_n, hy_stride, false, wei.data(), in_h, hy_h * bi * 3, in_stride, false, din.data(), in_h, batch_n, in_stride, 1, 1); } } template void GRUBwdWeightCPUVerify(bool use_dropout, std::vector& in, std::vector& dwei, // [ input_state_weight_trans // hidden_state_weight0_trans // input1_trans hidden1_trans ... // output_weight; bidirectional // reversed weights ] std::vector& hx, // initial hidden state const std::vector& in_n, // input batch size int in_h, // input data length int seqLength, // Number of iterations to unroll over bool bidirection, // whether using bidirectional net bool biased, // whether using bias int hy_d, // 1 by numlayer (number of stacks of hidden // layers) for unidirection, 2 by numlayer for // bidirection int hy_n, // equal to input batch size in_n[0] int hy_h, // hidden state number // by hy_h related function for bidirection int inputMode, std::vector& rsvspace, std::vector& wkspace, bool hx_is_null = false) { int batch_n = sumvc(in_n); int numlayer = bidirection ? hy_d / 2 : hy_d; int bi = bidirection ? 2 : 1; int in_stride = in_h; int wei_stride = bi * 3 * hy_h; int hy_stride = bi * 4 * hy_h; int h_stride = bi * hy_h; int uni_stride = hy_h; int bi_stride = hy_h * bi; if(inputMode == 1) { if(in_h != hy_h) { std::cout << "Verification cannot be completed: The input tensor size must equal to the " << "hidden state size of the network in SKIP_INPUT mode!" << std::endl; return; } in_h = 0; } int wei_shift_bias = (in_h + hy_h + (bi * hy_h + hy_h) * (numlayer - 1)) * wei_stride; // bwd weights emulator for(int li = 0; li < numlayer; li++) { // between layers if(li == 0) { if(inputMode == 0) { gemm_cpu(wkspace.data(), hy_h * bi * 3, batch_n, hy_stride, true, in.data(), in_h, batch_n, in_stride, false, dwei.data(), in_h, hy_h * bi * 3, in_stride, 1, 1); } if(biased) { for(int h = 0; h < wei_stride; h++) { for(int w = 0; w < batch_n; w++) { dwei[wei_shift_bias + h] += wkspace[w * hy_stride + h]; } } } } else { int prelayer_shift = use_dropout ? 2 * numlayer * batch_n * hy_stride + (li - 1) * batch_n * hy_h * bi : (li - 1) * batch_n * hy_stride + bi * hy_h * 3; int hid_shift = li * batch_n * hy_stride; int wei_shift = (in_h + hy_h) * wei_stride + (li - 1) * (bi * hy_h + hy_h) * wei_stride; gemm_cpu(&wkspace[hid_shift], hy_h * bi * 3, batch_n, hy_stride, true, &rsvspace[prelayer_shift], hy_h * bi, batch_n, use_dropout ? hy_h * bi : hy_stride, false, &dwei[wei_shift], hy_h * bi, hy_h * bi * 3, bi_stride, 1, 1); if(biased) { wei_shift = wei_shift_bias + li * 2 * wei_stride; for(int h = 0; h < wei_stride; h++) { for(int w = 0; w < batch_n; w++) { dwei[wei_shift + h] += wkspace[hid_shift + w * hy_stride + h]; } } } } // between time int bacc = 0; for(int ti = 0; ti < seqLength; ti++) { int hid_shift = li * batch_n * hy_stride + bacc * hy_stride; int hx_shift = li * in_n.at(0) * h_stride; int wei_shift = in_h * wei_stride + li * (bi * hy_h + hy_h) * wei_stride; int pretime_shift; for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + bs * hy_stride + 2 * hy_h + h] *= activfunc(rsvspace[hid_shift + bs * hy_stride + hy_h + h], 2); } } // between time if(ti == 0) { if(!hx_is_null) { gemm_cpu(&wkspace[hid_shift], hy_h * 3, in_n.at(ti), hy_stride, true, &hx[hx_shift], hy_h, in_n.at(ti), uni_stride, false, &dwei[wei_shift], hy_h, hy_h * 3, uni_stride, 1, 1); if(biased) { int bias_shift = wei_shift_bias + li * 2 * wei_stride + wei_stride; for(int h = 0; h < hy_h * 3; h++) { for(int w = 0; w < in_n.at(ti); w++) { dwei[bias_shift + h] += wkspace[hid_shift + w * hy_stride + h]; } } } } } else { pretime_shift = li * batch_n * hy_stride + (bacc - in_n.at(ti - 1)) * hy_stride + bi * 3 * hy_h; gemm_cpu(&wkspace[hid_shift], hy_h * 3, in_n.at(ti), hy_stride, true, &rsvspace[pretime_shift], hy_h, in_n.at(ti), hy_stride, false, &dwei[wei_shift], hy_h, hy_h * 3, uni_stride, 1, 1); if(biased) { int bias_shift = wei_shift_bias + li * 2 * wei_stride + wei_stride; for(int h = 0; h < hy_h * 3; h++) { for(int w = 0; w < in_n.at(ti); w++) { dwei[bias_shift + h] += wkspace[hid_shift + w * hy_stride + h]; } } } } if(bidirection) { for(int bs = 0; bs < in_n.at(ti); bs++) { for(int h = 0; h < hy_h; h++) { wkspace[hid_shift + bs * hy_stride + 5 * hy_h + h] *= activfunc(rsvspace[hid_shift + bs * hy_stride + 4 * hy_h + h], 2); } } if(ti == seqLength - 1) { if(!hx_is_null) { gemm_cpu(&wkspace[hid_shift + 3 * hy_h], hy_h * 3, in_n.at(ti), hy_stride, true, &hx[hx_shift + hy_n * hy_h], hy_h, in_n.at(ti), uni_stride, false, &dwei[wei_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 3, uni_stride, 1, 1); if(biased) { int bias_shift = wei_shift_bias + li * 2 * wei_stride + wei_stride; for(int h = 0; h < hy_h * 3; h++) { for(int w = 0; w < in_n.at(ti); w++) { dwei[bias_shift + 3 * hy_h + h] += wkspace[hid_shift + 3 * hy_h + w * hy_stride + h]; } } } } } else { if(!hx_is_null && in_n.at(ti) > in_n.at(ti + 1)) { gemm_cpu(&wkspace[hid_shift + 3 * hy_h + in_n.at(ti + 1) * hy_stride], hy_h * 3, (in_n.at(ti) - in_n.at(ti + 1)), hy_stride, true, &hx[hx_shift + hy_n * hy_h + in_n.at(ti + 1) * hy_h], hy_h, (in_n.at(ti) - in_n.at(ti + 1)), uni_stride, false, &dwei[wei_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 3, uni_stride, 1, 1); if(biased) { int bias_shift = wei_shift_bias + li * 2 * wei_stride + wei_stride; for(int h = 0; h < hy_h * 3; h++) { for(int w = in_n.at(ti + 1); w < in_n.at(ti); w++) { dwei[bias_shift + 3 * hy_h + h] += wkspace[hid_shift + 3 * hy_h + w * hy_stride + h]; } } } } pretime_shift = li * batch_n * hy_stride + (bacc + in_n.at(ti)) * hy_stride + bi * 3 * hy_h; gemm_cpu(&wkspace[hid_shift + 3 * hy_h], hy_h * 3, in_n.at(ti + 1), hy_stride, true, &rsvspace[pretime_shift + hy_h], hy_h, in_n.at(ti + 1), hy_stride, false, &dwei[wei_shift + 3 * hy_h * uni_stride], hy_h, hy_h * 3, uni_stride, 1, 1); if(biased) { int bias_shift = wei_shift_bias + li * 2 * wei_stride + wei_stride; for(int h = 0; h < hy_h * 3; h++) { for(int w = 0; w < in_n.at(ti + 1); w++) { dwei[bias_shift + 3 * hy_h + h] += wkspace[hid_shift + 3 * hy_h + w * hy_stride + h]; } } } } } bacc += in_n.at(ti); } } } //////=========END CPU VERIFICATION FUNCTIONS============= inline std::vector GenBatchSeq(const int batchSize, const int seqLength) { static constexpr int modval = 3; int currentval = batchSize; std::vector batchSeq; batchSeq.reserve(seqLength); for(int i = 0; i < seqLength; i++) { if(i > 0) { int nvalue = currentval - prng::gen_0_to_B(modval); currentval = (nvalue < 1) ? 1 : nvalue; } batchSeq.push_back(currentval); } return batchSeq; } //**************************************************** // FORWARD INFERENCE //**************************************************** template struct verify_forward_infer_gru { std::vector input; std::vector initHidden; std::vector weights; std::vector batch_seq; int hiddenSize; int seqLength; int nLayers; int biasMode; int dirMode; int inputMode; int batch_n; int inputVecLen; miopenRNNDescriptor_t rnnDesc; size_t realHiddenSize; bool nohx; bool nohy; verify_forward_infer_gru(miopenRNNDescriptor_t pRD, const std::vector& px, const std::vector& phx, const std::vector& pW, const std::vector& pBS, const int pHS, const int pBN, const int pS, const int pNL, const int pBM, const int pDM, const int pIM, const int pVL, const size_t pHXZ, const bool pnohx = false, const bool pnohy = false) : input(px), initHidden(phx), weights(pW), batch_seq(pBS), hiddenSize(pHS), seqLength(pS), nLayers(pNL), biasMode(pBM), dirMode(pDM), inputMode(pIM), batch_n(pBN), inputVecLen(pVL), rnnDesc(pRD), realHiddenSize(pHXZ), nohx(pnohx), nohy(pnohy) { if(!nohx) initHidden = phx; // this may be intentionally a nullptr else initHidden.resize(realHiddenSize); } std::tuple> cpu() { auto&& handle = get_handle(); int bi = dirMode != 0 ? 2 : 1; int hy_h = hiddenSize; int bi_stride = bi * hy_h; size_t out_sz = 0; size_t reserveSpaceSize; std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, batch_seq, hiddenSize * ((dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, outputDescs.data(), &out_sz); miopenGetRNNTrainingReserveSize( &handle, rnnDesc, seqLength, inputDescs.data(), &reserveSpaceSize); std::vector reserveSpace(reserveSpaceSize / sizeof(T)); std::vector output(out_sz / sizeof(T)); std::vector hiddenState(initHidden.size()); GRUFwdCPUVerify(handle, false, miopen::deref(miopen::deref(rnnDesc).dropoutDesc), input, weights, // [ input_state_weight_trans // hidden_state_weight0_trans input1_trans // hidden1_trans ... output_weight; // bidirectional reversed weights ] hiddenState, // current/final hidden state initHidden, // initial hidden state output, batch_seq, // input batch size inputVecLen, // input data length seqLength, // Number of iterations to unroll over dirMode, // whether using bidirectional net biasMode, // whether using bias bi * nLayers, // 1 by numlayer (number of stacks of hidden layers) for // unidirection, 2 by numlayer for bidirection batch_seq.at(0), // equal to input batch size in_n[0] hiddenSize, // hidden state number bi_stride, // 1 by hy_h related function for unidirection, 2 by hy_h // related function for bidirection inputMode, reserveSpace, nohx); return std::make_tuple(output); } std::tuple> gpu() { auto&& handle = get_handle(); size_t out_sz = 0; size_t workspace_size = 0; std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, batch_seq, hiddenSize * ((dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); miopenGetRNNWorkspaceSize(&handle, rnnDesc, seqLength, inputDescs.data(), &workspace_size); Workspace wspace{workspace_size}; auto input_dev = handle.Write(input); miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, outputDescs.data(), &out_sz); std::vector output(out_sz / sizeof(T)); auto output_dev = handle.Write(output); auto weights_dev = handle.Write(weights); auto hy = initHidden; std::fill(hy.begin(), hy.end(), 0.); auto hy_dev = handle.Write(hy); std::vector hlens(3, 0); hlens[0] = nLayers * (dirMode != 0 ? 2 : 1); hlens[1] = batch_seq[0]; hlens[2] = hiddenSize; miopen::TensorDescriptor hiddenDesc(miopen::deref(rnnDesc).dataType, hlens); std::vector wlen(1, 0); wlen[0] = weights.size(); miopen::TensorDescriptor weightDesc(miopen::deref(rnnDesc).dataType, wlen); auto hx_dev = nohx ? miopen::Allocator::ManageDataPtr{} : handle.Write(initHidden); miopenRNNForwardInference(&handle, rnnDesc, seqLength, inputDescs.data(), input_dev.get(), &hiddenDesc, hx_dev.get(), &hiddenDesc, nullptr, &weightDesc, weights_dev.get(), outputDescs.data(), output_dev.get(), &hiddenDesc, ((nohy) ? nullptr : hy_dev.get()), &hiddenDesc, nullptr, wspace.ptr(), wspace.size()); return std::make_tuple((handle.Read(output_dev, output.size()))); } void fail() const { std::stringstream ss{}; ss << "./bin/MIOpenDriver rnn -n "; for(int i = 0; i < seqLength; i++) { if(i < seqLength - 1) { ss << batch_seq.at(i) << ","; } else { ss << batch_seq.at(i); } } ss << " -m gru -k " << seqLength << " -H " << hiddenSize << " -W " << inputVecLen << " -l " << nLayers << " -F 0 -r " << dirMode << " -b " << biasMode << " -p " << inputMode << std::endl; ss << "inputMode: " << inputMode << " biasMode: " << biasMode << " dirMode: " << dirMode << std::endl; ss << "hz: " << hiddenSize << " batch_n: " << batch_n << " seqLength: " << seqLength << " inputLen: " << inputVecLen << " numLayers: " << nLayers << std::endl; ss << "Forward Inference GRU: " << std::endl; ss << "Output tensor output failed verification." << std::endl; GTEST_FAIL() << ss.str(); } }; //~~~~~~~~~~~~ END FWD INFERENCE ~~~~~~~~~~~~~~~~~~~~~~~~ //**************************************************** // FORWARD TRAIN //**************************************************** template struct verify_forward_train_gru { std::vector input; std::vector initHidden; std::vector weights; std::vector batch_seq; int hiddenSize; int seqLength; int nLayers; int biasMode; int dirMode; int inputMode; int batch_n; int inputVecLen; miopenRNNDescriptor_t rnnDesc; size_t realHiddenSize; bool nohx; bool nohy; bool use_dropout; verify_forward_train_gru(miopenRNNDescriptor_t pRD, const std::vector& px, const std::vector& phx, const std::vector& pW, const std::vector& pBS, const int pHS, const int pBN, const int pS, const int pNL, const int pBM, const int pDM, const int pIM, const int pVL, const size_t pHXZ, const bool pnohx = false, const bool pnohy = false, const bool puse_dropout = false) : input(px), initHidden(phx), weights(pW), batch_seq(pBS), hiddenSize(pHS), seqLength(pS), nLayers(pNL), biasMode(pBM), dirMode(pDM), inputMode(pIM), batch_n(pBN), inputVecLen(pVL), rnnDesc(pRD), realHiddenSize(pHXZ), nohx(pnohx), nohy(pnohy), use_dropout(puse_dropout) { if(!nohx) initHidden = phx; // this may be intentionally a nullptr else initHidden.resize(realHiddenSize); } std::tuple, std::vector, std::vector> cpu() { auto&& handle = get_handle(); int bi = dirMode != 0 ? 2 : 1; int hy_h = hiddenSize; int bi_stride = bi * hy_h; size_t out_sz = 0; size_t reserveSpaceSize; std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, batch_seq, hiddenSize * ((dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, outputDescs.data(), &out_sz); miopenGetRNNTrainingReserveSize( &handle, rnnDesc, seqLength, inputDescs.data(), &reserveSpaceSize); std::vector reserveSpace((reserveSpaceSize + sizeof(T) - 1) / sizeof(T)); std::vector output(out_sz / sizeof(T)); std::vector hiddenState(initHidden.size()); GRUFwdCPUVerify(handle, use_dropout, miopen::deref(miopen::deref(rnnDesc).dropoutDesc), input, weights, // [ input_state_weight_trans // hidden_state_weight0_trans input1_trans // hidden1_trans ... output_weight; // bidirectional reversed weights ] hiddenState, // current/final hidden state initHidden, // initial hidden state output, batch_seq, // input batch size inputVecLen, // input data length seqLength, // Number of iterations to unroll over dirMode, // whether using bidirectional net biasMode, // whether using bias bi * nLayers, // 1 by numlayer (number of stacks of hidden layers) for // unidirection, 2 by numlayer for bidirection batch_seq.at(0), // equal to input batch size in_n[0] hiddenSize, // hidden state number bi_stride, // 1 by hy_h related function for unidirection, 2 by hy_h // related function for bidirection inputMode, reserveSpace, nohx); auto retSet = std::make_tuple(output, (nohy ? initHidden : hiddenState), reserveSpace); return retSet; } std::tuple, std::vector, std::vector> gpu() { auto&& handle = get_handle(); size_t out_sz = 0; size_t workspace_size = 0; size_t reserveSpaceSize = 0; std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, batch_seq, hiddenSize * ((dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); miopenGetRNNWorkspaceSize(&handle, rnnDesc, seqLength, inputDescs.data(), &workspace_size); Workspace wspace{workspace_size}; miopenGetRNNTrainingReserveSize( &handle, rnnDesc, seqLength, inputDescs.data(), &reserveSpaceSize); reserveSpaceSize = (reserveSpaceSize + sizeof(T) - 1) & ~(sizeof(T) - 1); assert(reserveSpaceSize % sizeof(T) == 0); Workspace rspace{reserveSpaceSize}; auto input_dev = handle.Write(input); miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, outputDescs.data(), &out_sz); std::vector output(out_sz / sizeof(T)); auto output_dev = handle.Write(output); auto weights_dev = handle.Write(weights); auto hy = initHidden; std::fill(hy.begin(), hy.end(), 0.); auto hy_dev = handle.Write(hy); std::vector hlens(3, 0); hlens[0] = nLayers * (dirMode != 0 ? 2 : 1); hlens[1] = batch_seq[0]; hlens[2] = hiddenSize; miopen::TensorDescriptor hiddenDesc(miopen::deref(rnnDesc).dataType, hlens); std::vector wlen(1, 0); wlen[0] = weights.size(); miopen::TensorDescriptor weightDesc(miopen::deref(rnnDesc).dataType, wlen); auto hx_dev = nohx ? miopen::Allocator::ManageDataPtr{} : handle.Write(initHidden); miopenRNNForwardTraining(&handle, rnnDesc, seqLength, inputDescs.data(), input_dev.get(), &hiddenDesc, hx_dev.get(), &hiddenDesc, nullptr, &weightDesc, weights_dev.get(), outputDescs.data(), output_dev.get(), &hiddenDesc, ((nohy) ? nullptr : hy_dev.get()), &hiddenDesc, nullptr, wspace.ptr(), wspace.size(), rspace.ptr(), rspace.size()); auto outdata = handle.Read(output_dev, output.size()); auto retSet = std::make_tuple(outdata, (nohy ? initHidden : handle.Read(hy_dev, hy.size())), rspace.Read>()); return retSet; } void fail() const { std::stringstream ss{}; ss << "./bin/MIOpenDriver rnn -n "; for(int i = 0; i < seqLength; i++) { if(i < seqLength - 1) { ss << batch_seq.at(i) << ","; } else { ss << batch_seq.at(i); } } ss << " -m gru -k " << seqLength << " -H " << hiddenSize << " -W " << inputVecLen << " -l " << nLayers << " -F 0 -r " << dirMode << " -b " << biasMode << " -p " << inputMode << std::endl; ss << "inputMode: " << inputMode << " biasMode: " << biasMode << " dirMode: " << dirMode << std::endl; ss << "hz: " << hiddenSize << " batch_n: " << batch_n << " seqLength: " << seqLength << " inputLen: " << inputVecLen << " numLayers: " << nLayers << " useDropout: " << int(use_dropout) << std::endl; ss << "Forward Train GRU: " << std::endl; GTEST_FAIL() << ss.str(); } }; //~~~~~~~~~~~~ END FWD TRAIN ~~~~~~~~~~~~~~~~~~~~~~~~ //**************************************************** // BACKWARDS DATA //**************************************************** template struct verify_backward_data_gru { std::vector yin; // Y std::vector dy; // dY std::vector dhy; // dHY std::vector initHidden; // HX std::vector weights; std::vector reserveSpace; std::vector batch_seq; int hiddenSize; int seqLength; int nLayers; int biasMode; int dirMode; int inputMode; int batch_n; int inputVecLen; miopenRNNDescriptor_t rnnDesc; size_t realHiddenSize; bool nohx; bool nodhy; bool nodhx; bool use_dropout; verify_backward_data_gru(miopenRNNDescriptor_t pRD, const std::vector& py, const std::vector& pdy, const std::vector& pdhy, const std::vector& phx, const std::vector& pW, const std::vector& pRS, const std::vector& pBS, const int pHS, const int pBN, const int pS, const int pNL, const int pBM, const int pDM, const int pIM, const int pVL, const size_t pHXZ, const bool pnohx = false, const bool pnodhy = false, const bool pnodhx = false, const bool puse_dropout = false) : yin(py), dy(pdy), dhy(pdhy), initHidden(phx), weights(pW), reserveSpace(pRS), batch_seq(pBS), hiddenSize(pHS), seqLength(pS), nLayers(pNL), biasMode(pBM), dirMode(pDM), inputMode(pIM), batch_n(pBN), inputVecLen(pVL), rnnDesc(pRD), realHiddenSize(pHXZ), nohx(pnohx), nodhy(pnodhy), nodhx(pnodhx), use_dropout(puse_dropout) { if(!nohx) initHidden = phx; // this may be intentionally a nullptr else initHidden.resize(realHiddenSize); if(!nodhy) dhy = pdhy; // this may be intentionally a nullptr else dhy.resize(realHiddenSize); } std::tuple, std::vector, std::vector, std::vector> cpu() { auto&& handle = get_handle(); int bi = dirMode != 0 ? 2 : 1; int hy_h = hiddenSize; int bi_stride = bi * hy_h; size_t workspace_size; std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); // Outputs ---------- size_t in_sz = 0; miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, inputDescs.data(), &in_sz); miopenGetRNNWorkspaceSize(&handle, rnnDesc, seqLength, inputDescs.data(), &workspace_size); std::vector workSpace(workspace_size / sizeof(T)); std::vector dx(in_sz / sizeof(T)); std::vector dhx(initHidden.size()); GRUBwdDataCPUVerify(use_dropout, miopen::deref(miopen::deref(rnnDesc).dropoutDesc), dx, // DX (output) weights, // [ input_state_weight_trans // hidden_state_weight0_trans input1_trans // hidden1_trans ... output_weight; // bidirectional reversed weights ] dhy, // current/final hidden state dhx, // DHX (output) initHidden, // HX initial hidden state yin, // Y dy, // DY batch_seq, // input batch size inputVecLen, // input data length seqLength, // Number of iterations to unroll over dirMode, // whether using bidirectional net biasMode, // whether using bias bi * nLayers, // 1 by numlayer (number of stacks of hidden layers) // for unidirection, 2 by numlayer for bidirection batch_seq.at(0), // equal to input batch size in_n[0] hiddenSize, // hidden state number bi_stride, // 1 by hy_h related function for unidirection, 2 by // hy_h related function for bidirection inputMode, reserveSpace, workSpace, nohx, nodhy); return std::make_tuple(dx, (nodhx ? initHidden : dhx), reserveSpace, workSpace); } std::tuple, std::vector, std::vector, std::vector> gpu() { auto&& handle = get_handle(); size_t out_sz = 0; std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, batch_seq, hiddenSize * ((dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); size_t workspace_size = 0; miopenGetRNNWorkspaceSize(&handle, rnnDesc, seqLength, inputDescs.data(), &workspace_size); Workspace wspace{workspace_size}; miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, outputDescs.data(), &out_sz); auto yin_dev = handle.Write(yin); auto dyin_dev = handle.Write(dy); auto weights_dev = handle.Write(weights); Workspace rspace{}; rspace.Write(reserveSpace); std::vector hlens(3, 0); hlens[0] = nLayers * (dirMode != 0 ? 2 : 1); hlens[1] = batch_seq[0]; hlens[2] = hiddenSize; miopen::TensorDescriptor hiddenDesc(miopen::deref(rnnDesc).dataType, hlens); std::vector wlen(1, 0); wlen[0] = weights.size(); miopen::TensorDescriptor weightDesc(miopen::deref(rnnDesc).dataType, wlen); size_t in_sz = 0; miopenGetRNNInputTensorSize(&handle, rnnDesc, seqLength, inputDescs.data(), &in_sz); std::vector dx(in_sz / sizeof(T)); auto dx_dev = handle.Write(dx); std::vector dhx(initHidden.size()); auto dhx_dev = handle.Write(dhx); auto dhy_dev = nodhy ? miopen::Allocator::ManageDataPtr{} : handle.Write(dhy); auto hx_dev = nohx ? miopen::Allocator::ManageDataPtr{} : handle.Write(initHidden); miopenRNNBackwardData(&handle, rnnDesc, seqLength, outputDescs.data(), yin_dev.get(), outputDescs.data(), dyin_dev.get(), &hiddenDesc, dhy_dev.get(), &hiddenDesc, nullptr, &weightDesc, weights_dev.get(), &hiddenDesc, hx_dev.get(), &hiddenDesc, nullptr, inputDescs.data(), dx_dev.get(), &hiddenDesc, ((nodhx) ? nullptr : dhx_dev.get()), &hiddenDesc, nullptr, wspace.ptr(), wspace.size(), rspace.ptr(), rspace.size()); return std::make_tuple(handle.Read(dx_dev, dx.size()), (nodhx ? initHidden : handle.Read(dhx_dev, dhx.size())), rspace.Read>(), wspace.Read>()); } void fail() const { std::stringstream ss{}; ss << "./bin/MIOpenDriver rnn -n "; for(int i = 0; i < seqLength; i++) { if(i < seqLength - 1) { ss << batch_seq.at(i) << ","; } else { ss << batch_seq.at(i); } } ss << " -m gru -k " << seqLength << " -H " << hiddenSize << " -W " << inputVecLen << " -l " << nLayers << " -F 0 -r " << dirMode << " -b " << biasMode << " -p " << inputMode << std::endl; ss << "inputMode: " << inputMode << " biasMode: " << biasMode << " dirMode: " << dirMode << std::endl; ss << "hz: " << hiddenSize << " batch_n: " << batch_n << " seqLength: " << seqLength << " inputLen: " << inputVecLen << " numLayers: " << nLayers << " useDropout: " << int(use_dropout) << std::endl; ss << "Backward Data GRU: " << std::endl; GTEST_FAIL() << ss.str(); } }; //~~~~~~~~~~~~ END BACKWARD DATA ~~~~~~~~~~~~~~~~~~~~~~~~ //**************************************************** // BACKWARDS WEIGHTS //**************************************************** template struct verify_backward_weights_gru { std::vector input; // Y std::vector dy; // dY std::vector initHidden; // HX std::vector reserveSpace; std::vector workSpace; std::vector batch_seq; int weightSize; int hiddenSize; int seqLength; int nLayers; bool biasMode{false}; bool dirMode{false}; int inputMode; int batch_n; int inputVecLen; miopenRNNDescriptor_t rnnDesc; size_t realHiddenSize; bool nohx; bool use_dropout; verify_backward_weights_gru(miopenRNNDescriptor_t pRD, const std::vector& px, const std::vector& pdy, const std::vector& phx, const std::vector& pRS, const std::vector& pWS, const std::vector& pBS, const int pHS, const int pW, const int pBN, const int pS, const int pNL, const int pBM, const int pDM, const int pIM, const int pVL, const size_t pHXZ, const bool pnohx = false, const bool puse_dropout = false) : input(px), dy(pdy), initHidden(phx), reserveSpace(pRS), workSpace(pWS), batch_seq(pBS), weightSize(pW), hiddenSize(pHS), seqLength(pS), nLayers(pNL), biasMode(pBM == 0 ? false : true), dirMode(pDM == 0 ? false : true), inputMode(pIM), batch_n(pBN), inputVecLen(pVL), rnnDesc(pRD), realHiddenSize(pHXZ), nohx(pnohx), use_dropout(puse_dropout) { if(!nohx) initHidden = phx; // this may be intentionally a nullptr else initHidden.resize(realHiddenSize); } std::tuple> cpu() { int bi = dirMode ? 2 : 1; std::vector dweights(weightSize); GRUBwdWeightCPUVerify(use_dropout, input, dweights, // (output) [ input_state_weight_trans // hidden_state_weight0_trans // input1_trans hidden1_trans ... // output_weight; bidirectional // reversed weights ] initHidden, // initial hidden state batch_seq, // input batch size inputVecLen, // input data length seqLength, // Number of iterations to unroll over dirMode, // whether using bidirectional net biasMode, // whether using bias bi * nLayers, // 1 by numlayer (number of stacks of hidden // layers) for unidirection, 2 by numlayer for // bidirection batch_seq.at(0), // equal to input batch size in_n[0] hiddenSize, // hidden state number inputMode, reserveSpace, workSpace, nohx); return std::make_tuple(dweights); } std::tuple> gpu() { auto&& handle = get_handle(); std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, batch_seq, inputVecLen, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, batch_seq, hiddenSize * ((dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); Workspace wspace{}; wspace.Write(workSpace); Workspace rspace{}; rspace.Write(reserveSpace); std::vector dweights(weightSize); auto dweights_dev = handle.Write(dweights); miopen::TensorDescriptor weightDesc(miopen::deref(rnnDesc).dataType, {weightSize}); std::vector hlens(3, 0); hlens[0] = nLayers * (dirMode != 0 ? 2 : 1); hlens[1] = batch_seq[0]; hlens[2] = hiddenSize; miopen::TensorDescriptor hiddenDesc(miopen::deref(rnnDesc).dataType, hlens); auto hx_dev = nohx ? miopen::Allocator::ManageDataPtr{} : handle.Write(initHidden); auto dy_dev = handle.Write(dy); auto input_dev = handle.Write(input); miopenRNNBackwardWeights(&handle, rnnDesc, seqLength, inputDescs.data(), input_dev.get(), &hiddenDesc, hx_dev.get(), outputDescs.data(), dy_dev.get(), &weightDesc, dweights_dev.get(), wspace.ptr(), wspace.size(), rspace.ptr(), rspace.size()); auto retvec = handle.Read(dweights_dev, dweights.size()); return std::make_tuple(retvec); } void fail() const { std::stringstream ss{}; ss << "./bin/MIOpenDriver rnn -n "; for(int i = 0; i < seqLength; i++) { if(i < seqLength - 1) { ss << batch_seq.at(i) << ","; } else { ss << batch_seq.at(i); } } ss << " -m gru -k " << seqLength << " -H " << hiddenSize << " -W " << inputVecLen << " -l " << nLayers << " -F 0 -r " << dirMode << " -b " << biasMode << " -p " << inputMode << std::endl; ss << "inputMode: " << inputMode << " biasMode: " << biasMode << " dirMode: " << dirMode << std::endl; ss << "hz: " << hiddenSize << " batch_n: " << batch_n << " seqLength: " << seqLength << " inputLen: " << inputVecLen << " numLayers: " << nLayers << " useDropout: " << int(use_dropout) << std::endl; ss << "Backward Weights GRU: " << std::endl; GTEST_FAIL() << ss.str(); } }; //~~~~~~~~~~~~ END BACKWARD WEIGHTS ~~~~~~~~~~~~~~~~~~~~~~~~ struct TestCase { int batchSize{}; int seqLength{}; int inVecLen{}; int hiddenSize{}; int numLayers{}; int inputMode{}; int biasMode{}; int dirMode{}; bool useDropout = false; bool nohx = false; bool nodhy = false; bool nohy = false; bool nodhx = false; bool flatBatchFill = false; std::vector batchSeq{}; }; using TestParam = std::tuple>; TestCase ConvertParam(TestParam const& param) { auto [batchSize, seqLength, inVecLen, hiddenSize, numLayers, inputMode, biasMode, dirMode, useDropout, nohx, nodhy, nohy, nodhx, flatBatchFill, batchSeq] = param; return TestCase{batchSize, seqLength, inVecLen, hiddenSize, numLayers, inputMode, biasMode, dirMode, useDropout, nohx, nodhy, nohy, nodhx, flatBatchFill, batchSeq}; } std::vector deepbench_cases = { {32, 1500, 216, 216, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 750, 286, 286, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 375, 286, 286, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 10, 2816, 2816, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 1500, 248, 248, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 12, 2048, 2048, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 1500, 156, 156, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 500, 156, 156, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 12, 1536, 1536, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 1500, 256, 256, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 500, 256, 256, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 10, 2560, 2560, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 1, 512, 512, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {32, 50, 1024, 1024, 1, 1, 0, 0, false, false, false, false, false, true, {0}}, {64, 50, 1024, 1024, 1, 1, 0, 0, false, false, false, false, false, true, {0}}}; std::vector extra_cases = { {32, 3, 128, 128, 1, 0, 0, 0, false, true, false, false, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 0, false, false, true, false, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 0, false, true, true, false, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, true, false, false, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, false, true, false, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, true, true, false, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 0, false, false, false, false, true, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 0, false, false, false, true, true, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, false, false, true, false, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, false, false, false, true, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, false, false, true, true, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 0, false, true, true, true, true, false, {32, 32, 32}}, {32, 3, 128, 128, 1, 0, 0, 1, false, true, true, true, true, false, {32, 32, 32}}}; auto GenCases(bool gen_dropout) { std::vector defaultBS(1); std::vector cases{}; cases.push_back({17, (gen_dropout ? 23 : 2), 13, 67, (gen_dropout ? 3 : 1), 0, 0, 0, gen_dropout, false, false, false, false, false, defaultBS}); return ::testing::ValuesIn(cases); } auto GetFullBaseTests() { std::vector modes(2, 0); modes[1] = 1; std::vector defaultBS(1); return ::testing::Combine(::testing::ValuesIn(get_gru_batchSize()), ::testing::ValuesIn(get_gru_seq_len()), ::testing::ValuesIn(get_gru_vector_len()), ::testing::ValuesIn(get_gru_hidden_size()), ::testing::ValuesIn(get_gru_num_layers()), ::testing::ValuesIn(modes), ::testing::ValuesIn(modes), ::testing::ValuesIn(modes), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({defaultBS})); } auto GetDropoutTests() { std::vector modes(2, 0); modes[1] = 1; std::vector defaultBS(1); return ::testing::Combine(::testing::ValuesIn({17}), ::testing::ValuesIn({23}), ::testing::ValuesIn({13}), ::testing::ValuesIn({67}), ::testing::ValuesIn({3}), ::testing::ValuesIn({0}), ::testing::ValuesIn({0}), ::testing::ValuesIn(modes), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({false}), ::testing::ValuesIn({defaultBS})); } auto const& GetDropoutSmokeTests() { static auto cases = GenCases(true); return cases; } auto const& GetDeepbenchTests() { return deepbench_cases; } auto const& GetExtraTests() { return extra_cases; } auto const& GetSmokeTests() { static auto cases = GenCases(false); return cases; } template class gru_test : public testing::TestWithParam { const double tolerance = 80; // Will be multiplied by std::numeric_limits::epsilon() TestCase param; int batch_n{}; size_t statesSizeInBytes{0U}; public: void fill_buffers(std::vector& input, std::vector& hx, std::vector& weights) { auto fill_array_via_gen = [](std::vector& dst, auto gen, int seed_offset = 0) { prng::reset_seed(seed_offset); size_t dst_sz = dst.size(); for(size_t it = 0; it < dst_sz; it++) dst[it] = gen(); }; auto pos_gen = [](double scale, int range) { return [=]() -> T { return prng::gen_descreet_unsigned(scale, range); }; }; auto sign_gen = [](double scale, int range) { return [=]() -> T { return prng::gen_descreet_uniform_sign(scale, range); }; }; const double data_max_v = sqrt(1. / param.hiddenSize); int data_range = 100; const double data_scale = data_max_v / data_range; fill_array_via_gen(input, pos_gen(data_scale, data_range), 0); if(!param.nohx) { fill_array_via_gen(hx, pos_gen(data_scale, data_range), 1); } // filter const double weights_max_v = sqrt(1. / param.hiddenSize); int weights_range = 64; const double weights_scale = weights_max_v / weights_range; fill_array_via_gen(weights, sign_gen(weights_scale, weights_range), 2); } void fill_bwd_buffers(std::vector& dy, std::vector& dhy) { auto fill_array_via_gen = [](std::vector& dst, auto gen, int seed_offset = 0) { prng::reset_seed(seed_offset); size_t dst_sz = dst.size(); for(size_t it = 0; it < dst_sz; it++) dst[it] = gen(); }; auto sign_gen = [](double scale, int range) { return [=]() { return prng::gen_descreet_uniform_sign(scale, range); }; }; const double bwd_data_max_v = sqrt(1. / param.hiddenSize) / 8; int bwd_data_range = 100; const double bwd_data_scale = bwd_data_max_v / bwd_data_range; if(!param.nodhy) { fill_array_via_gen(dhy, sign_gen(bwd_data_scale, bwd_data_range), 3); } fill_array_via_gen(dy, sign_gen(bwd_data_scale, bwd_data_range), 4); prng::reset_seed(); } void SetUp() override { prng::reset_seed(); param = ConvertParam(GetParam()); if(!param.useDropout) { param.batchSeq = generate_batchSeq(param.batchSize, param.seqLength)[0]; } } void Run() { if(param.batchSeq.empty() || 0 == param.batchSeq[0]) { std::cout << "Empty batch sequence. Filling uniformly with batch size: " << param.batchSize << std::endl; if(param.flatBatchFill) { param.batchSeq.clear(); param.batchSeq.resize(param.seqLength, param.batchSize); } else { param.batchSeq = GenBatchSeq(param.batchSize, param.seqLength); } } if(param.batchSeq.size() != param.seqLength) { std::cerr << "FAILED: Batch sequence vector length, does not match sequence length." << std::endl; GTEST_SKIP(); } auto&& handle = get_handle(); batch_n = std::accumulate(param.batchSeq.begin(), param.batchSeq.end(), 0); RNNDescGuard rnnDesc; miopenRNNAlgo_t algoMode = miopenRNNdefault; DropoutDescGuard DropoutDesc; HandleGuard mio_handle; void* dropout_state_buf = nullptr; // See DestroyInternalRnnDropoutDesc — frees the descriptor allocated // by miopenCreateRNNDescriptor that the upcoming Set* will leak. DestroyInternalRnnDropoutDesc(rnnDesc); if(param.useDropout) { mio_handle.create(handle.GetStream()); float dropout_rate = 0.5; unsigned long long dropout_seed = 0ULL; miopenDropoutGetStatesSize(mio_handle, &statesSizeInBytes); (void)hipMalloc(static_cast(&dropout_state_buf), statesSizeInBytes); miopenSetDropoutDescriptor(DropoutDesc, mio_handle, dropout_rate, dropout_state_buf, statesSizeInBytes, dropout_seed, false, false, MIOPEN_RNG_PSEUDO_XORWOW); miopenSetRNNDescriptor_V2(rnnDesc, param.hiddenSize, param.numLayers, DropoutDesc, miopenRNNInputMode_t(param.inputMode), miopenRNNDirectionMode_t(param.dirMode), miopenGRU, miopenRNNBiasMode_t(param.biasMode), miopenRNNAlgo_t(algoMode), miopen_type{}); } else { miopenSetRNNDescriptor(rnnDesc, param.hiddenSize, param.numLayers, miopenRNNInputMode_t(param.inputMode), miopenRNNDirectionMode_t(param.dirMode), miopenGRU, miopenRNNBiasMode_t(param.biasMode), miopenRNNAlgo_t(algoMode), miopen_type{}); // defined in superclass testdriver } // Create input tensor // If we are in skip mode, take the real input size to be the vector length. auto inVecReal = (param.inputMode != 0) ? param.hiddenSize : param.inVecLen; std::size_t in_sz = static_cast(inVecReal) * batch_n; std::size_t hx_sz = ((param.dirMode != 0) ? 2ULL : 1ULL) * param.hiddenSize * param.batchSize * param.numLayers; std::vector input(in_sz), hx(hx_sz), dhyin(hx_sz); size_t wei_bytes = [&]() { size_t filter_bytes; std::vector inlens(2, 0); inlens.at(0) = param.batchSeq.at(0); inlens.at(1) = inVecReal; auto firstInputDesc = miopen::TensorDescriptor(miopen::deref(rnnDesc).dataType, inlens); miopenGetRNNParamsSize( &handle, rnnDesc, &firstInputDesc, &filter_bytes, miopen::deref(rnnDesc).dataType); return filter_bytes; }(); auto wei_sz = wei_bytes / sizeof(T); std::vector weights(wei_sz); std::vector inputCPPDescs; std::vector inputDescs; createTensorDescArray( inputCPPDescs, inputDescs, param.batchSeq, inVecReal, miopen::deref(rnnDesc).dataType); std::vector outputCPPDescs; std::vector outputDescs; createTensorDescArray(outputCPPDescs, outputDescs, param.batchSeq, param.hiddenSize * ((param.dirMode != 0) ? 2 : 1), miopen::deref(rnnDesc).dataType); size_t out_sz; miopenGetRNNInputTensorSize(&handle, rnnDesc, param.seqLength, outputDescs.data(), &out_sz); size_t reserveSpaceSize; miopenGetRNNTrainingReserveSize( &handle, rnnDesc, param.seqLength, inputDescs.data(), &reserveSpaceSize); size_t workspace_size; miopenGetRNNWorkspaceSize( &handle, rnnDesc, param.seqLength, inputDescs.data(), &workspace_size); size_t total_mem = statesSizeInBytes + reserveSpaceSize + workspace_size + 2 * out_sz + (in_sz + wei_sz + (param.nohx ? 0 : hx_sz) + (param.nohy ? 0 : hx_sz) + (param.nodhx ? 0 : hx_sz) + (param.nodhy ? 0 : hx_sz)) * sizeof(T); size_t device_mem = handle.GetGlobalMemorySize(); if(total_mem >= device_mem) { GTEST_SKIP() << "Config requires " << total_mem << " Bytes to write all necessary tensors to GPU. GPU has " << device_mem << " Bytes of memory." << std::endl; } fill_buffers(input, hx, weights); auto fwdTrainOutputPair = GruTestCompareResults(verify_forward_train_gru{rnnDesc, input, hx, weights, param.batchSeq, param.hiddenSize, batch_n, param.seqLength, param.numLayers, param.biasMode, param.dirMode, param.inputMode, inVecReal, hx_sz, param.nohx, param.nohy, param.useDropout}, tolerance); /// RETURNS std::make_tuple(output, hiddenState, reserveSpace); auto yin = std::get<0>(fwdTrainOutputPair.second); // auto curHiddenState = std::get<1>(fwdTrainOutputPair.second); auto reserveSpaceFwdTrain = std::get<2>(fwdTrainOutputPair.second); std::vector dyin(yin.size()); fill_bwd_buffers(dyin, dhyin); auto bwdDataOutputPair = GruTestCompareResults(verify_backward_data_gru{rnnDesc, yin, dyin, dhyin, hx, weights, reserveSpaceFwdTrain, param.batchSeq, param.hiddenSize, batch_n, param.seqLength, param.numLayers, param.biasMode, param.dirMode, param.inputMode, inVecReal, hx_sz, param.nohx, param.nodhy, param.nodhx, param.useDropout}, tolerance); // RETURNS: std::make_tuple(dx, dhx, reserveSpace, workSpace); auto reserveSpaceBwdData = std::get<2>(bwdDataOutputPair.second); auto workSpaceBwdData = std::get<3>(bwdDataOutputPair.second); // auto dweights_pair = GruTestCompareResults(verify_backward_weights_gru{rnnDesc, input, dyin, hx, reserveSpaceBwdData, workSpaceBwdData, param.batchSeq, param.hiddenSize, static_cast(wei_sz), batch_n, param.seqLength, param.numLayers, param.biasMode, param.dirMode, param.inputMode, inVecReal, hx_sz, param.nohx, param.useDropout}, tolerance); if(!param.useDropout) { GruTestCompareResults(verify_forward_infer_gru{rnnDesc, input, hx, weights, param.batchSeq, param.hiddenSize, batch_n, param.seqLength, param.numLayers, param.biasMode, param.dirMode, param.inputMode, inVecReal, hx_sz, param.nohx, param.nohy}, tolerance); } // DLOWELL: Subtracting delta weights may produce NAN and infinities. Further investigation // is needed. // auto dweights = std::get<1>(dweights_pair); // std::transform(weightData.begin( ), weightData.end( ), dweights.begin( ), // weightData.begin( ),std::minus( )); // GruTestCompareResults(verify_forward_infer_gru{rnnDesc, inputData, // curHiddenState, curCellState, weightData, // batchSeq, // hiddenSize, batch_n, // seqLength, numLayers, // biasMode, dirMode, // inputMode, inVecReal}); // Free the DropoutDescriptor that miopenSetRNNDescriptor just allocated. // In the dropout path, the internal pointer aliases the user-owned // DropoutDescGuard — freeing it would double-free. if(!param.useDropout) DestroyInternalRnnDropoutDesc(rnnDesc); if(param.useDropout) (void)hipFree(dropout_state_buf); } }; struct TestNameGenerator { std::string operator()(const ::testing::TestParamInfo& param_info) { std::stringstream ss{}; auto print_bool = [](bool value) { return value ? "true" : "false"; }; auto print_batch_seq = [](std::vector const& vec) { std::stringstream vec_ss{}; for(auto el : vec) { vec_ss << std::to_string(el) << "_"; } return vec_ss.str(); }; auto param = ConvertParam(param_info.param); ss << "batchSize_" << param.batchSize << "_seqLength_" << param.seqLength << "_inVecLen_" << param.inVecLen << "_hiddenSize_" << param.hiddenSize << "_numLayers_" << param.numLayers << "_inputMode_" << param.inputMode << "_biasMode_" << param.biasMode << "_dirMode_" << param.dirMode << "_useDropout_" << print_bool(param.useDropout) << "_nohx_" << print_bool(param.nohx) << "_nodhy_" << print_bool(param.nodhy) << "_nohy_" << print_bool(param.nohy) << "_nodhx_" << print_bool(param.nodhx) << "_flatBatchFill_" << print_bool(param.flatBatchFill) << "_batch_seq_" << print_batch_seq(param.batchSeq); return ss.str(); } }; template struct gru_dropout_test : public gru_test { // intentionally empty }; template struct gru_deepbench_test : public gru_test { // intentionally empty }; template struct gru_extra_test : public gru_test { // intentionally empty }; } // anonymous namespace using GPU_GRU_Base_FP32 = gru_test; using GPU_GRU_Base_FP16 = gru_test; using GPU_GRU_Dropout_FP32 = gru_dropout_test; using GPU_GRU_Dropout_FP16 = gru_dropout_test; using GPU_GRU_Deepbench_FP32 = gru_deepbench_test; using GPU_GRU_Deepbench_FP16 = gru_deepbench_test; using GPU_GRU_Extra_FP32 = gru_extra_test; using GPU_GRU_Extra_FP16 = gru_extra_test; TEST_P(GPU_GRU_Base_FP32, TestFloat32) { Run(); } TEST_P(GPU_GRU_Base_FP16, TestFloat16) { Run(); } // Base tests INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Base_FP32, GetFullBaseTests(), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_GRU_Base_FP32, GetSmokeTests(), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Base_FP16, GetFullBaseTests(), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_GRU_Base_FP16, GetSmokeTests(), TestNameGenerator{}); // Dropout tests TEST_P(GPU_GRU_Dropout_FP32, TestFloat32) { Run(); } TEST_P(GPU_GRU_Dropout_FP16, TestFloat16) { Run(); } INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Dropout_FP32, GetDropoutTests(), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_GRU_Dropout_FP32, GetDropoutSmokeTests(), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Dropout_FP16, GetDropoutTests(), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_GRU_Dropout_FP16, GetDropoutSmokeTests(), TestNameGenerator{}); // // Deepbench tests TEST_P(GPU_GRU_Deepbench_FP32, TestFloat32) { Run(); } TEST_P(GPU_GRU_Deepbench_FP16, TestFloat16) { Run(); } INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Deepbench_FP32, ::testing::ValuesIn(GetDeepbenchTests()), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Deepbench_FP16, ::testing::ValuesIn(GetDeepbenchTests()), TestNameGenerator{}); // Extra tests TEST_P(GPU_GRU_Extra_FP32, TestFloat32) { Run(); } TEST_P(GPU_GRU_Extra_FP16, TestFloat16) { Run(); } INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Extra_FP32, ::testing::ValuesIn(GetExtraTests()), TestNameGenerator{}); INSTANTIATE_TEST_SUITE_P(Full, GPU_GRU_Extra_FP16, ::testing::ValuesIn(GetExtraTests()), TestNameGenerator{});