项目分享 | MindSpore Insight AI可视化工具开发心得

01 概述

MindSpore Insight是昇思MindSpore的可视化调试调优工具。作为开发者,我参与了MindSpore Insight工具部分功能的开发。本文将对MindSpore Insight进行简要介绍、其次介绍在开发中所用开发框架Vue的响应式原理。

02 MindSpore Insight介绍

MindSpore Insight为昇思MindSpore提供了简单易用的调优调试能力。在训练过程中,可以将标量、张量、图像、计算图、模型超参、训练耗时等数据记录到文件中,通过MindSpore Insight可视化页面进行查看及分析。MindSpore Insight的宏观上的架构如下图所示。其中Summary log是使用昇思MindSpore训练的模型日志,通过python进行对日志进行解码,将数据处理为json格式的API,前端通过请求该API进行相应功能的展示。详细的介绍可查看官网(https://www.mindspore.cn/mindinsight/docs/zh-CN/master/index.html)或点击下方“阅读原文”

项目分享 | MindSpore Insight AI可视化工具开发心得_第1张图片

03 浅谈Vue2响应式原理

Vue是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。它的核心功能有声明式渲染和响应式,Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。如下图所示,当data属性中的数据发生变化时,其视图就会更新,不需要用户操作DOM就可让视图更新。那么Vue是如何知道更新的是这些数据呢?这就涉及到Vue响应式的实现原理。

项目分享 | MindSpore Insight AI可视化工具开发心得_第2张图片

Vue的响应式是指以数据驱动视图的,也就是数据发生变化,会重新渲染页面。该过程实现要考虑的有以下三点:

● 数据劫持:追踪数据的变化

● 依赖收集:收集视图依赖的数据

● 通知视图:数据变化时,通知视图部分更新

3.1 数据劫持和依赖收集

数据劫持:指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。在下面的代码很好地诠释了数据劫持。当给obj中的name属性赋值的时候,会触发defineProperty方法中的set函数,而访问obj中的name属性值的时候会触发get函数,在这里set函数和get函数就拦截了访问和修改对象属性的这个行为,在get中和set中可以写上额外的操作,这就是数据劫持。

/*
  Object.defineProperty(obj,prop,descriptor)
    obj 要定义属性的对象
    prop 要定义或修改的属性的名称
    descriptor 要定义或修改的属性描述符
  输出结果:
      数据劫持 set name : 张三
      数据劫持 get name : 张三
      张三
*/
const obj={}
Object.defineProperty(obj,"name",{
        get(){
            console.log("数据劫持 get name :",nameVal);
            return nameVal;
        },
        set(newVal){
            console.log("数据劫持 set name :",newVal)
            nameVal = newVal
        }
});
obj.name="张三";
console.log(obj.name)

依赖收集:视图中用到了哪个数据,视图就依赖哪个数据,把变化的数据收集起来,这个过程就是依赖收集。

3.2实现原理介绍

项目分享 | MindSpore Insight AI可视化工具开发心得_第3张图片

上图(参考:https://blog.csdn.net/Mikon_0703/article/details/111367773)是Vue实现响应式原理的流程图,主要有三个重要部分:

监听器(Observer):对数据对象进行遍历,利用Object.defineProperty()给属性都加上 setter和getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。

事件中心(Dep ):用来收集依赖和通知订阅者。如果属性发生变化,需要通知订阅者Watcher,看是否需要更新。因为订阅者有多个,所以需要一个消息订阅器(发布者)Dep(订阅者集合的管理数组)来专门收集这些订阅者,在Observer和Watcher之间进行统一管理。每个Observer实例都有一个Dep实例。

订阅者(Watcher): 将View的相关指令初始化为一个订阅者Watcher,并替换模板数据或绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。

3.3 Vue源码中的体现

Vue2中响应式实现的源码链接:https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer

监听器(Observer)的实现

//https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer/index.js
walk (obj: Object) {
    const keys = Object.keys(obj) // 遍历数据对象利用 
    Object.defineProperty()给属性都加上 setter和getter。
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
 export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend() // 收集依赖
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value){
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify() // 当数据发生变化时,事件中心Dep通知订阅者
    }
  })
}

Dep的实现

//https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer/dep.js
export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
    Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update() //当数据发生变化时,通知订阅者更新视图
    }
  }
}

Watcher的实现

//https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer/watcher.js
/**
   * Subscriber interface.
   * Will be called when a dependency changes.
   */
  update () { // 订阅者更新视图方法
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
  }

04 总结

首先感谢昇思MindSpore社区提供的这次开源机会,这是我第一次参加开源项目,从这次开源经历中让我掌握了项目的开发流程,以及开源社区中的成员如何协同开发维护一个项目,另外此次开源经历,让我了解了小白如何参与开源、如何正确的提交PR。最后,我引用Apache APISIX PMC成员王院生的一句话:”参与开源,让我觉得自己终于与这个世界融为一体,不再是孤立的个体“ ,我也会继续参与开源,持续为开源贡献自己的一份力量。

你可能感兴趣的:(技术博客,人工智能,深度学习,计算机视觉)