You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

backward_graph.cpp 8.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /**
  2. * \file imperative/src/test/backward_graph.cpp
  3. * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  4. *
  5. * Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
  6. *
  7. * Unless required by applicable law or agreed to in writing,
  8. * software distributed under the License is distributed on an
  9. * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. */
  11. #include "./helper.h"
  12. #include "megbrain/opr/basic_arith.h"
  13. #include "megbrain/opr/dnn/batch_norm.h"
  14. #include "megbrain/imperative/ops/opr_attr.h"
  15. #include "megbrain/imperative/ops/autogen.h"
  16. #include "megbrain/imperative/backward_graph_opt.h"
  17. using namespace mgb;
  18. using namespace cg;
  19. using namespace imperative;
  20. template <typename T>
  21. T prepare_backward_graph_inputs(const BackwardGraphResult& bg, const T& inputs, const T& outputs, const T& grads) {
  22. T ret;
  23. size_t i = 0;
  24. for (auto&& t : inputs) {
  25. if (bg.save_for_backward[i++]) {
  26. ret.push_back(t);
  27. }
  28. }
  29. for (auto&& t : outputs) {
  30. if (bg.save_for_backward[i++]) {
  31. ret.push_back(t);
  32. }
  33. }
  34. for (auto&& t : grads) {
  35. if (bg.save_for_backward[i++]) {
  36. ret.push_back(t);
  37. }
  38. }
  39. return ret;
  40. }
  41. template <typename T, typename U>
  42. T expand_grads(const U& bg, const T& outputs) {
  43. T ret(bg.input_has_grad.size());
  44. for (size_t i = 0, j = 0; i < bg.input_has_grad.size(); ++i) {
  45. if (bg.input_has_grad[i]) {
  46. ret[i] = outputs[j++];
  47. }
  48. }
  49. return ret;
  50. }
  51. template <typename T>
  52. T prepare_optimized_backward_inputs(const OptimizedBackwardGraphResult& bg, const T& precomp, const T& inputs, const T& outputs, const T& grads) {
  53. T ret = precomp;
  54. size_t i = 0;
  55. for (auto&& t : inputs) {
  56. if (bg.save_for_backward[i++]) {
  57. ret.push_back(t);
  58. }
  59. }
  60. for (auto&& t : outputs) {
  61. if (bg.save_for_backward[i++]) {
  62. ret.push_back(t);
  63. }
  64. }
  65. for (auto&& t : grads) {
  66. if (bg.save_for_backward[i++]) {
  67. ret.push_back(t);
  68. }
  69. }
  70. return ret;
  71. }
  72. TEST(TestImperative, BackwardGraphBasic) {
  73. HostTensorGenerator<> gen;
  74. SmallVector<HostTensorND> hvs;
  75. SmallVector<TensorPtr> inputs;
  76. for(size_t i = 0; i < 2; ++ i) {
  77. hvs.push_back(*gen({42}));
  78. inputs.push_back(Tensor::make(hvs.back()));
  79. }
  80. using Param = opr::Elemwise::Param;
  81. Param param{Param::Mode::MUL};
  82. auto attr = OprAttr::make("Elemwise");
  83. attr->cast_final_safe<OprAttr>().param.write_pod(param);
  84. SmallVector<LogicalTensorDesc> input_descs;
  85. for (auto&& i : inputs) {
  86. input_descs.push_back({i->layout(), i->comp_node()});
  87. }
  88. auto result = OpDef::make_backward_graph(*attr, input_descs, {true, true}, {true});
  89. auto&& save_for_backward = result.save_for_backward;
  90. auto&& input_has_grad = result.input_has_grad;
  91. auto outputs = OpDef::apply_on_physical_tensor(*attr, inputs);
  92. inputs.push_back(outputs[0]);
  93. hvs.push_back(*gen({42}));
  94. inputs.push_back(Tensor::make(hvs.back()));
  95. mgb_assert(save_for_backward.size() == inputs.size());
  96. for (size_t i = 0; i < inputs.size(); ++ i) {
  97. if (!save_for_backward[i]) {
  98. inputs[i].reset(); // drop unused tensor
  99. }
  100. }
  101. SmallVector<TensorPtr> backward_graph_inputs;
  102. for (auto&& i : inputs) {
  103. if (i) {
  104. backward_graph_inputs.push_back(i);
  105. }
  106. }
  107. inputs.clear();
  108. auto input_grads = OpDef::apply_on_physical_tensor(*(result.backward), backward_graph_inputs);
  109. mgb_assert(input_grads.size() == input_has_grad.size());
  110. for (size_t i = 0; i < input_has_grad.size(); ++ i) {
  111. mgb_assert(input_has_grad[i] == static_cast<bool>(input_grads[i]));
  112. }
  113. SmallVector<HostTensorND> res;
  114. for (auto&& i : input_grads) {
  115. res.emplace_back();
  116. res.back().copy_from(i->dev_tensor()).sync();
  117. }
  118. for (size_t i = 0; i < 42; ++ i) {
  119. for (size_t j = 0; j < 1; ++ j) {
  120. ASSERT_EQ(hvs[2].ptr<float>()[i] * hvs[j].ptr<float>()[i], res[j ^ 1].ptr<float>()[i]);
  121. }
  122. }
  123. }
  124. TEST(TestImperative, BackwardGraphIdentity) {
  125. HostTensorGenerator<> gen;
  126. auto host_a = gen({42}), host_dc = gen({42});
  127. auto a = Tensor::make(*host_a), dc = Tensor::make(*host_dc);
  128. SmallVector<TensorPtr> inputs;
  129. inputs.push_back(a);
  130. auto attr = OprAttr::make("Identity");
  131. attr->cast_final_safe<OprAttr>().param.write_pod<megdnn::param::Empty>({});
  132. SmallVector<LogicalTensorDesc> input_descs;
  133. input_descs.push_back({a->layout(), a->comp_node()});
  134. auto result = OpDef::make_backward_graph(*attr, input_descs, {true}, {true});
  135. auto&& save_for_backward = result.save_for_backward;
  136. auto&& input_has_grad = result.input_has_grad;
  137. auto outputs = OpDef::apply_on_physical_tensor(*attr, inputs);
  138. inputs.push_back(outputs[0]);
  139. inputs.push_back(dc);
  140. mgb_assert(save_for_backward.size() == inputs.size());
  141. for (size_t i = 0; i < inputs.size(); ++ i) {
  142. if (!save_for_backward[i]) {
  143. inputs[i].reset(); // drop unused tensor
  144. }
  145. }
  146. SmallVector<TensorPtr> backward_graph_inputs;
  147. for (auto&& i : inputs) {
  148. if (i) {
  149. backward_graph_inputs.push_back(i);
  150. }
  151. }
  152. inputs.clear();
  153. auto input_grads = OpDef::apply_on_physical_tensor(*(result.backward), backward_graph_inputs);
  154. mgb_assert(input_grads.size() == input_has_grad.size());
  155. for (size_t i = 0; i < input_has_grad.size(); ++ i) {
  156. mgb_assert(input_has_grad[i] == static_cast<bool>(input_grads[i]));
  157. }
  158. HostTensorND hv;
  159. hv.copy_from(input_grads[0]->dev_tensor()).sync();
  160. for (size_t i = 0; i < 42; ++ i) {
  161. ASSERT_EQ(host_dc->ptr<float>()[i], hv.ptr<float>()[i]);
  162. }
  163. }
  164. TEST(TestImperative, BatchNormGrad) {
  165. auto cn = CompNode::load("xpux");
  166. using Param = opr::BatchNorm::Param;
  167. size_t N=2, C=3, H=5, W=5;
  168. LogicalTensorDesc inp{TensorLayout{{N, C, H, W}, dtype::Float32()}, cn};
  169. LogicalTensorDesc stat{TensorLayout{{C}, dtype::Float32()}, cn};
  170. {
  171. auto op = OprAttr::make("BatchNorm");
  172. auto&& attr = op->cast_final_safe<OprAttr>();
  173. Param param;
  174. param.fwd_mode = Param::FwdMode::TRAINING;
  175. attr.param.write_pod(param);
  176. OpDef::make_backward_graph(attr, {inp, stat, stat, stat, stat},
  177. {true, true ,true, false, false}, {false, false, false, false, true});
  178. }
  179. {
  180. auto op = OprAttr::make("BatchNorm");
  181. auto&& attr = op->cast_final_safe<OprAttr>();
  182. Param param;
  183. param.fwd_mode = Param::FwdMode::TRAINING;
  184. attr.param.write_pod(param);
  185. OpDef::make_backward_graph(attr, {inp, stat, stat},
  186. {true, true ,true}, {false, false, true});
  187. }
  188. }
  189. TEST(TestImperative, OptimizedBackwardGraphBasic) {
  190. auto cn = CompNode::load("xpux");
  191. LogicalTensorDesc desc = {TensorLayout(dtype::Float32()), cn};
  192. HostTensorGenerator<> gen;
  193. auto op = std::shared_ptr<OpDef>(Elemwise::make(Elemwise::Mode::ADD));
  194. auto bg = OpDef::make_backward_graph(*op, {desc, desc}, {true, true}, {true});
  195. auto obg = OptimizedBackwardGraphResult(bg);
  196. ASSERT_EQ(obg.save_for_backward.size(), 4);
  197. ASSERT_FALSE(obg.save_for_backward[0]);
  198. ASSERT_FALSE(obg.save_for_backward[1]);
  199. ASSERT_FALSE(obg.save_for_backward[2]);
  200. auto a_hv = gen({42});
  201. auto b_hv = gen({5, 42});
  202. auto dc_hv = gen({5, 42});
  203. auto a_tn = Tensor::make(*a_hv);
  204. auto b_tn = Tensor::make(*b_hv);
  205. auto dc_tn = Tensor::make(*dc_hv);
  206. auto c_tn = OpDef::apply_on_physical_tensor(*op, {a_tn, b_tn})[0];
  207. auto backward_graph_inputs = prepare_backward_graph_inputs<SmallVector<TensorPtr>>(bg, {a_tn, b_tn}, {c_tn}, {dc_tn});
  208. auto grads = expand_grads(bg, OpDef::apply_on_physical_tensor(*bg.backward, backward_graph_inputs));
  209. auto precomp = OpDef::apply_on_physical_tensor(*obg.precomp, {a_tn, b_tn, c_tn});
  210. ASSERT_EQ(precomp.size(), 2);
  211. ASSERT_EQ(precomp[0]->shape().ndim, 1);
  212. ASSERT_LE(precomp[0]->shape()[0], 2);
  213. ASSERT_EQ(precomp[1]->shape().ndim, 1);
  214. ASSERT_LE(precomp[1]->shape()[0], 2);
  215. auto backward_inputs = prepare_optimized_backward_inputs<SmallVector<TensorPtr>>(obg, precomp, {a_tn, b_tn}, {c_tn}, {dc_tn});
  216. auto grads2 = expand_grads(obg, OpDef::apply_on_physical_tensor(*obg.backward, backward_inputs));
  217. ASSERT_EQ(grads2.size(), 2);
  218. MGB_ASSERT_TENSOR_EQ(grads[0]->get_value(), grads2[0]->get_value());
  219. MGB_ASSERT_TENSOR_EQ(grads[1]->get_value(), grads2[1]->get_value());
  220. }

MegEngine 安装包中集成了使用 GPU 运行代码所需的 CUDA 环境,不用区分 CPU 和 GPU 版。 如果想要运行 GPU 程序,请确保机器本身配有 GPU 硬件设备并安装好驱动。 如果你想体验在云端 GPU 算力平台进行深度学习开发的感觉,欢迎访问 MegStudio 平台