[{"data":1,"prerenderedAt":316},["ShallowReactive",2],{"content-query-HigyD57Y6g":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"cover":11,"type":12,"category":13,"body":14,"_type":310,"_id":311,"_source":312,"_file":313,"_stem":314,"_extension":315},"/technology-blogs/zh/2014","zh",false,"","项目分享 | 昇思MindSpore动静结合中list和dict方法实现","本文以昇思MindSpore框架中图模式下list和dict的实现方式为例，介绍昇思MindSpore框架中的动静结合知识。","2022-12-13","https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2022/12/16/ee382f89a13f4ae890fa07336f3186a0.png","technology-blogs","大V博文",{"type":15,"children":16,"toc":307},"root",[17,25,49,57,65,70,78,86,101,106,111,116,130,135,140,145,155,163,171,176,190,195,203,216,221,229,243,248,256,273,278,286,294,302],{"type":18,"tag":19,"props":20,"children":22},"element","h1",{"id":21},"项目分享-昇思mindspore动静结合中list和dict方法实现",[23],{"type":24,"value":8},"text",{"type":18,"tag":26,"props":27,"children":28},"p",{},[29,35,37,42,44],{"type":18,"tag":30,"props":31,"children":32},"strong",{},[33],{"type":24,"value":34},"作者",{"type":24,"value":36},"：",{"type":18,"tag":30,"props":38,"children":39},{},[40],{"type":24,"value":41},"程延福",{"type":24,"value":43}," ｜**学校：**",{"type":18,"tag":30,"props":45,"children":46},{},[47],{"type":24,"value":48},"郑州大学",{"type":18,"tag":26,"props":50,"children":51},{},[52],{"type":18,"tag":30,"props":53,"children":54},{},[55],{"type":24,"value":56},"01",{"type":18,"tag":26,"props":58,"children":59},{},[60],{"type":18,"tag":30,"props":61,"children":62},{},[63],{"type":24,"value":64},"概述",{"type":18,"tag":26,"props":66,"children":67},{},[68],{"type":24,"value":69},"静态图和动态图是神经学习框架中的重要概念，昇思MindSpore同时支持动态图和静态图两种模式，在动态图与静态图的结合方面做了很多工作。本文以昇思MindSpore框架中图模式下list和dict的实现方式为例，介绍昇思MindSpore框架中的动静结合知识。",{"type":18,"tag":26,"props":71,"children":72},{},[73],{"type":18,"tag":30,"props":74,"children":75},{},[76],{"type":24,"value":77},"02",{"type":18,"tag":26,"props":79,"children":80},{},[81],{"type":18,"tag":30,"props":82,"children":83},{},[84],{"type":24,"value":85},"背景知识",{"type":18,"tag":26,"props":87,"children":88},{},[89,94,96],{"type":18,"tag":30,"props":90,"children":91},{},[92],{"type":24,"value":93},"2.1",{"type":24,"value":95}," ",{"type":18,"tag":30,"props":97,"children":98},{},[99],{"type":24,"value":100},"动态图与静态图",{"type":18,"tag":26,"props":102,"children":103},{},[104],{"type":24,"value":105},"目前主流的神经学习框架大概可以分为静态图和动态图两种。",{"type":18,"tag":26,"props":107,"children":108},{},[109],{"type":24,"value":110},"在动态图中，每次编译都会重新构建一个新的计算图。这意味着计算图的构建和计算同时发生(define by run)，这种机制由于能够实时得到中间结果的值，使得调试更加容易，同时我们将大脑中的想法转化为代码方案也变得更加容易，对于编程实现来说更友好。动态图以PyTorch框架为代表。",{"type":18,"tag":26,"props":112,"children":113},{},[114],{"type":24,"value":115},"在静态图中，会事先了解和定义好整个运算流，在运行前可以对图结构进行优化，可以获得更快的前向速度，从性能上来说更加高效。但是只有运行起来之后才能看到变量的值，无法像动态图一样随时拿到中间计算结果。静态图以早期的TensorFlow框架为代表。",{"type":18,"tag":26,"props":117,"children":118},{},[119,124,125],{"type":18,"tag":30,"props":120,"children":121},{},[122],{"type":24,"value":123},"2.2",{"type":24,"value":95},{"type":18,"tag":30,"props":126,"children":127},{},[128],{"type":24,"value":129},"昇思MindSpore中的动静结合",{"type":18,"tag":26,"props":131,"children":132},{},[133],{"type":24,"value":134},"昇思MindSpore支持动态图和静态图两种模式，动态图通过解释执行，具有动态语法亲和性，表达灵活；静态图使用JIT编译优化执行，偏静态语法，在语法上有较多限制。动态图模式是昇思MindSpore的默认模式，主要用于调试等用途，而静态图模式拥有更高效的执行性能，主要用于部署。",{"type":18,"tag":26,"props":136,"children":137},{},[138],{"type":24,"value":139},"昇思MindSpore提供了静态图和动态图统一的编码方式，大大增加了静态图和动态图的可兼容性，用户无需开发多套代码，仅变更一行代码便可切换静态图/动态图模式。例如，在静态图模式下，使用 context.set_context(mode=context.PYNATIVE_MODE) 切换为动态图(PyNative)模式; 同理，昇思MindSpore处于动态图(PyNative)模式时，可以通过 context.set_context(mode=context.GRAPH_MODE) 切换为静态图(Graph)模式。",{"type":18,"tag":26,"props":141,"children":142},{},[143],{"type":24,"value":144},"为了提高动态图模式下的前向计算任务执行速度，昇思MindSpore提供了jit装饰器，可以通过修饰Python函数或者Python类的成员函数使其被编译成计算图，通过图优化等技术提高运行速度。昇思MindSpore支持在动态图下使用静态编译的方式来进行混合执行，通过使用jit装饰符来修饰需要用静态图来执行的函数对象，即可实现动态图和静态图的混合执行。",{"type":18,"tag":146,"props":147,"children":149},"pre",{"code":148},"import numpy as np\nimport mindspore.ops as ops\nimport mindspore as ms\n\n#设置运行模式为动态图模式\nms.set_context(mode=ms.PYNATIVE_MODE)\n\n#使用装饰器，指定静态图模式下执行\n@ms.jit\ndef add_func(x，y):\n    return ops.add(x，y)\n\nx = ms.Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\ny = ms.Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n\nout = add_func（x，y）\nprint(out)\n",[150],{"type":18,"tag":151,"props":152,"children":153},"code",{"__ignoreMap":7},[154],{"type":24,"value":148},{"type":18,"tag":26,"props":156,"children":157},{},[158],{"type":18,"tag":30,"props":159,"children":160},{},[161],{"type":24,"value":162},"03",{"type":18,"tag":26,"props":164,"children":165},{},[166],{"type":18,"tag":30,"props":167,"children":168},{},[169],{"type":24,"value":170},"实现方案和过程",{"type":18,"tag":26,"props":172,"children":173},{},[174],{"type":24,"value":175},"为了实现静态图模式(Graph 模式)和动态图模式(PyNative 模式)的灵活切换，部分动态图的语法需要昇思MindSpore在静态图下单独进行开发。为了提高运算效率，昇思MindSpore引入了Pybind11库，使Python代码可以方便的调用C++代码，一些重要的功能使用C++代码实现。",{"type":18,"tag":26,"props":177,"children":178},{},[179,184,185],{"type":18,"tag":30,"props":180,"children":181},{},[182],{"type":24,"value":183},"3.1",{"type":24,"value":95},{"type":18,"tag":30,"props":186,"children":187},{},[188],{"type":24,"value":189},"图模式下 list.extend 方法支持",{"type":18,"tag":26,"props":191,"children":192},{},[193],{"type":24,"value":194},"list.extend(obj) 方法会在原list后追加obj list内容。在昇思MindSpore图模式中，该方法的实现是使用ListGetItem算子取出原list和objlist中的所有元素，将所有元素按顺序添加到一个std::vector中，再使用MakeList算子构建新list，并将该list返回，替代原list。关键代码如下所示。",{"type":18,"tag":146,"props":196,"children":198},{"code":197},"FuncGraphPtr ListExtend::GenerateFuncGraph(const abstract::AbstractBasePtrList &args_list) {\n  abstract::CheckArgsSize(\"ListExtend\", args_list, 2);\n\n  FuncGraphPtr ret = std::make_shared();\n  ret->set_flag(FUNC_GRAPH_FLAG_CORE, true);\n  ret->debug_info()->set_name(\"extend\");\n\n  std::vector elems;\n  elems.push_back(NewValueNode(prim::kPrimMakeList));\n  AddNodeToElems(args_list[0], ret, &elems);\n  AddNodeToElems(args_list[1], ret, &elems);\n  auto out = ret->NewCNode(elems);\n  ret->set_output(out);\n  return ret;\n}\n\nvoid ListExtend::AddNodeToElems(const AbstractBasePtr &arg, const FuncGraphPtr &ret, std::vector *elems) { \n  abstract::AbstractListPtr arg_list = dyn_cast(arg);\n  MS_EXCEPTION_IF_NULL(arg_list);\n  int64_t len = SizeToLong(arg_list->size());\n  AnfNodePtr arg_node = ret->add_parameter();\n  for (int64_t i = 0; i \u003C len; ++i) {\n    auto value = ret->NewCNode({NewValueNode(prim::kPrimListGetItem), arg_node, NewValueNode(i)});\n    elems->push_back(value);\n  }\n}\n",[199],{"type":18,"tag":151,"props":200,"children":201},{"__ignoreMap":7},[202],{"type":24,"value":197},{"type":18,"tag":26,"props":204,"children":205},{},[206,211,212],{"type":18,"tag":30,"props":207,"children":208},{},[209],{"type":24,"value":210},"3.2",{"type":24,"value":95},{"type":18,"tag":30,"props":213,"children":214},{},[215],{"type":24,"value":189},{"type":18,"tag":26,"props":217,"children":218},{},[219],{"type":24,"value":220},"list.count(item) 会统计item元素在原list中的数量。在图模式中，顺序遍历原list中的元素，逐个与obj进行比较，首先判断元素类型，Tensor类型单独处理，其余类型使用其各自对应的比较符号比较。关键代码如下所示。",{"type":18,"tag":146,"props":222,"children":224},{"code":223},"FuncGraphPtr ListCount::GenerateFuncGraph(const abstract::AbstractBasePtrList &args_list) {\n  const size_t list_count_args_size = 2;\n  abstract::CheckArgsSize(\"ListCount\", args_list, list_count_args_size);\n  auto &list_input = args_list[0];\n  auto &element_value = args_list[1];\n\n  auto arg_list = dyn_cast_ptr(list_input);\n  MS_EXCEPTION_IF_NULL(arg_list);\n  FuncGraphPtr ret = std::make_shared();\n  ret->set_flag(FUNC_GRAPH_FLAG_CORE, true);\n  ret->debug_info()->set_name(\"count\");\n  (void)ret->add_parameter();\n  (void)ret->add_parameter();\n\n  ValuePtr count_value = element_value->BuildValue();\n  const auto &values = arg_list->elements();\n  int64_t count = 0;\n  for (auto value : values) {\n    if (ComparesTwoValues(count_value, value->BuildValue())) {\n      ++count;\n    }\n  }\n\n  auto out = NewValueNode(MakeValue(count));\n  ret->set_output(out);\n  return ret;\n}\n",[225],{"type":18,"tag":151,"props":226,"children":227},{"__ignoreMap":7},[228],{"type":24,"value":223},{"type":18,"tag":26,"props":230,"children":231},{},[232,237,238],{"type":18,"tag":30,"props":233,"children":234},{},[235],{"type":24,"value":236},"3.3",{"type":24,"value":95},{"type":18,"tag":30,"props":239,"children":240},{},[241],{"type":24,"value":242},"图模式下 dict.fromkeys 方法支持",{"type":18,"tag":26,"props":244,"children":245},{},[246],{"type":24,"value":247},"dict.fromkeys(seq[, value=None])根据给定的可迭代对象seq和value(默认为None)，创建一个新的dict并返回。在图模式中，首先判断传入seq对象是否是支持的可迭代对象（目前支持list、tuple、dict、string），不支持将抛出异常。之后遍历该可迭代对象，判断每个元素是否为string类型，不通过将抛出异常。最后根据该key与传入的value值创建新的dict对象，并返回。关键代码如下所示。",{"type":18,"tag":146,"props":249,"children":251},{"code":250},"FuncGraphPtr DictFromKeys::GenerateFuncGraph(const abstract::AbstractBasePtrList &args_list) {\n  constexpr size_t dict_fromkeys_args_size = 3; \n  abstract::CheckArgsSize(\"DictFromKeys\", args_list, dict_fromkeys_args_size);\n  const auto &values = ParseIterableObject(args_list[1]);\n  auto value_node = args_list[2]->BuildValue();\n  MS_EXCEPTION_IF_NULL(value_node);\n\n  FuncGraphPtr ret = std::make_shared();\n  ret->set_flag(FUNC_GRAPH_FLAG_CORE, true);\n  ret->debug_info()->set_name(\"fromkeys\");\n  (void)ret->add_parameter();\n  (void)ret->add_parameter();\n  (void)ret->add_parameter();\n\n  std::vector> key_values;\n  for (auto &value : values) {\n    auto key = value->BuildValue();\n    if (!key->IsSameTypeId(StringImm::kTypeId)) {\n      MS_LOG(EXCEPTION) \u003C\u003C \"The key should be string, but got \" \u003C\u003C key->type_name();\n    }\n\n    std::string key_node = GetValue(key);\n    (void)key_values.emplace_back(std::make_pair(key_node, value_node));\n  }\n\n  ret->set_output(NewValueNode(std::make_shared(key_values)));\n  return ret;\n}\n",[252],{"type":18,"tag":151,"props":253,"children":254},{"__ignoreMap":7},[255],{"type":24,"value":250},{"type":18,"tag":26,"props":257,"children":258},{},[259,267,268],{"type":18,"tag":30,"props":260,"children":261},{},[262],{"type":18,"tag":30,"props":263,"children":264},{},[265],{"type":24,"value":266},"3.4",{"type":24,"value":95},{"type":18,"tag":30,"props":269,"children":270},{},[271],{"type":24,"value":272},"图模式下 dict.update 方法支持",{"type":18,"tag":26,"props":274,"children":275},{},[276],{"type":24,"value":277},"dict.update(obj) 会在原dict后追加obj dict内容。在图模式中，首先创建一个AnfNodePtrList对象用来保存value值，并创建unordered_map对象记录dict key和value的index值，之后遍历原dict和obj dict，使用kPrimDictGetItem算子取出key对应的value值，并将value插入到AnfNodePtrList对象中，并将key和value index插入到unordered_map中，如存在重复key，则取出对应的index，替换AnfNodePtrList中对应的value。遍历完成后，使用kPrimMakeTuple算子和kPrimMakeDict算子创建新dict，使用该dict对象代替原dict对象。关键代码如下所示。",{"type":18,"tag":146,"props":279,"children":281},{"code":280},"FuncGraphPtr DictUpdate::GenerateFuncGraph(const abstract::AbstractBasePtrList &args_list) {\n  constexpr size_t dict_update_args_size = 2;\n  abstract::CheckArgsSize(\"DictUpdate\", args_list, dict_update_args_size);\n\n  FuncGraphPtr ret = std::make_shared();\n  ret->set_flag(FUNC_GRAPH_FLAG_CORE, true);\n  ret->debug_info()->set_name(\"update\");\n\n  AnfNodePtrList key_inputs;\n  AnfNodePtrList value_inputs;\n  (void)key_inputs.emplace_back(NewValueNode(prim::kPrimMakeTuple));\n  (void)value_inputs.emplace_back(NewValueNode(prim::kPrimMakeTuple));\n\n  std::unordered_map hash_map;\n  AddNodeToLists(args_list[0], ret, &key_inputs, &value_inputs, &hash_map);\n  AddNodeToLists(args_list[1], ret, &key_inputs, &value_inputs, &hash_map);\n\n  ret->set_output(ret->NewCNode(\n    {NewValueNode(prim::kPrimMakeDict), ret->NewCNode(std::move(key_inputs)), ret->NewCNode(std::move(value_inputs))}));\n  return ret;\n}\n",[282],{"type":18,"tag":151,"props":283,"children":284},{"__ignoreMap":7},[285],{"type":24,"value":280},{"type":18,"tag":26,"props":287,"children":288},{},[289],{"type":18,"tag":30,"props":290,"children":291},{},[292],{"type":24,"value":293},"04",{"type":18,"tag":26,"props":295,"children":296},{},[297],{"type":18,"tag":30,"props":298,"children":299},{},[300],{"type":24,"value":301},"总结",{"type":18,"tag":26,"props":303,"children":304},{},[305],{"type":24,"value":306},"首先感谢昇思MindSpore社区提供的这次机会，感谢在完成任务过程中导师的帮助。之前对开源的了解一直都是模糊的，通过这次机会，才让我了解到开源项目，并且加入到了开源贡献之中。在刚开始做任务的时候，读昇思MindSpore源码，不断地惊讶于它巧妙的设计，同时学习了深度框架的知识。在最后提交PR的过程中，使自己认识到代码规范和注释的细节，同时有很多专家会对代码的结构提出很多建议，在这个过程中学到了很多的知识，成长了很多。",{"title":7,"searchDepth":308,"depth":308,"links":309},4,[],"markdown","content:technology-blogs:zh:2014.md","content","technology-blogs/zh/2014.md","technology-blogs/zh/2014","md",1776506117567]