精华内容
下载资源
问答
  • 2021-10-23 23:43:17

    动态图和静态图介绍

    目前市面上比较流行的深度学习框架主要分类两大类:动态图框架和静态图框架,Pytorch、TF、Caffeine等框架最大的区别就是它们拥有不同的计算图表现形式。TF使用静态图,意味着开发人员需要先定义计算图,然后不断的使用它,而Pytorch,每次都会重新构建一个新的计算图。

    动态计算意味着程序会按照研发人员编写命令的顺序进行执行。这种机制将使得调试更加容易,并且也使得我们将大脑中的想法转化为实际代码变得更加容易。而静态计算则意味着程序程序在编译执行时将先生成神经网络的结构,然后再执行相应操作。而静态计算是通过先定义后执行的方式,之后再次运行的时候就不再需要重新构建计算图,所以速度会比动态图更快。从理论上讲,静态计算这样的机制允许编译器进行更大程度的优化,但是这也意味着你所期望的程序与编译器实际执行之间存在着更多的代沟。这也意味着,代码中的错误将更加难以发现(比如,如果计算图的结构出现问题,你可能只有在代码执行到相应操作的时候才能发现它)。

    tensorflow静态图

    import tensorflow as tf
    first_counter = tf.constant(0)
    second_counter = tf.constant(10)
    def cond(first_counter, second_counter, *args):
        return first_counter < second_counter
    def body(first_counter, second_counter):
        first_counter = tf.add(first_counter, 2)
        second_counter = tf.add(second_counter, 1)
        return first_counter, second_counter
    c1, c2 = tf.while_loop(cond, body, [first_counter, second_counter])
    with tf.Session() as sess:
        counter_1_res, counter_2_res = sess.run([c1, c2])
    print(counter_1_res)
    print(counter_2_res)
    ​

    可以看到 TensorFlow 需要将整个图构建成静态的,换句话说,每次运行的时候图都是一样的,是不能够改变的,所以不能直接使用 Python 的 while 循环语句,需要使用辅助函数 tf.while_loop 写成 TensorFlow 内部的形式.

    pytorch动态图

    import torch
    first_counter = torch.Tensor([0])
    second_counter = torch.Tensor([10])
     
    while (first_counter < second_counter)[0]:
        first_counter += 2
        second_counter += 1
     
    print(first_counter)
    print(second_counter)
    ​

    可以看到 PyTorch 的写法跟 Python 的写法是完全一致的,没有任何额外的学习成本.

    总结

    动态图方便代码编写,方便debug,但是缺点就是速度慢(每一次运算都会加载一遍计算图)。在工业上,不但要看效果,还要看速度。因此将模型加速是必不可少的步骤。

    动态图加速

    本次以一个paddlepaddle训练的情感分类模型,展示如何进行动态图加速。情感分类模型是基于paddlenlp框架,利用Bert模型,进行的情感多分类模型。本文不包含训练部分,只叙述模型加速部分。

    动态图预测

    # -*- coding: utf-8 -*-
    # @Time    : 2021/10/23 23:22
    # @Author  : RJ
    import paddle
    from paddlenlp.data import Pad
    import os
    import numpy as np
    import time
    import argparse
    import onnx
    import json
    # from ppqi import InferenceModel
    try:
        import onnxruntime
    except:
        pass
    from paddlenlp.transformers import BertTokenizer
    ​
    import paddle.nn as nn
    from paddlenlp.transformers import BertPretrainedModel
    import paddle.nn.functional as F
    ​
    ​
    class SentimentAnalysisModel(BertPretrainedModel):
        base_model_prefix = "bert"
        def __init__(self, bert, number_label=3):
            """
            情绪识别模型继承paddlenlp.transformers.BertPretrainedModel类
            Args:
                bert: bert模型
                number_label: 标签个数
            """
            super(SentimentAnalysisModel, self).__init__()
            self.bert = bert
            self.classifier = nn.layer.Linear(self.bert.config["hidden_size"], number_label)
            self.loss_fct = nn.CrossEntropyLoss(soft_label=False, axis=-1)
    ​
        def forward(self, input_ids, attention_mask, label=None):
            # 将attention_mask进行维度变换,从2维变成4维。paddlenlp.transformers的实现与torch或tf不一样,不会自动进行维度扩充。
            attention_mask = paddle.unsqueeze(attention_mask, axis=[1, 2])
            # 获取[CLS]向量pooled_output
            pooled_output = self.bert(input_ids=input_ids, attention_mask=attention_mask)[1]
            # 对pooled_output进行全连接,映射到number_label上
            logits = self.classifier(pooled_output)
            # 使用softmax,获取每个标签类别的概率
            probs = F.softmax(logits, axis=1)
            # 获取标签类别概率最大的标签
            pred_label = paddle.argmax(logits, axis=-1)
            outputs = (pred_label, probs)
            # 如果label不是None,则使用CrossEntropyLoss求解loss
            if label is not None:
                loss = self.loss_fct(logits, label)
                outputs = (loss,) + outputs
            return outputs
    ​
    ​
    def convert_featrue(sample, max_len, tokenizer):
        """
        将单个文本,进行数据转换,得到模型所使用的id索引数据
        Args:
            sample: 单个文本,str类型
            max_len: 最大长度
            tokenizer: 分词器
    ​
        Returns:
    ​
        """
        # 对文本进行tokenize操作
        tokens = tokenizer.tokenize(sample)
        # 进行长度判断,若长于最大长度,则进行截断
        if len(tokens) > max_len - 2:
            tokens = tokens[:max_len - 2]
        # 将其头尾加上[CLS]和[SEP]
        tokens = ["[CLS]"] + tokens + ["[SEP]"]
        # 将token转化成id,并获取模型所需的attention_mask
        input_ids = tokenizer.convert_tokens_to_ids(tokens)
        attention_mask = [1] * len(input_ids)
        assert len(input_ids) == len(attention_mask)
        # 对input_ids和attention_mask进行补全操作,补到最大长度
        # 补全到最大长度,是由于后面会对动态图转onnx和静态图,输入需要定长
        if len(input_ids) < max_len:
            input_ids = input_ids + [0] * (max_len - len(input_ids))
            attention_mask = attention_mask + [0] * (max_len - len(attention_mask))
        return input_ids, attention_mask
    ​
    ​
    def batch_data(sample_list, max_len, tokenizer):
        """
        将数据处理成tensor形式
        Args:
            batch_data: batch数据
    ​
        Returns:
    ​
        """
        input_ids_list, attention_mask_list = [], []
        for sample in sample_list:
            input_ids, attention_mask = convert_featrue(sample, max_len, tokenizer)
            input_ids_list.append(input_ids)
            attention_mask_list.append(attention_mask)
        return {"input_ids": paddle.to_tensor(Pad(pad_val=0, axis=0)(input_ids_list), dtype="int64"),
                "attention_mask": paddle.to_tensor(Pad(pad_val=0, axis=0)(attention_mask_list), dtype="int64")}
    ​
    ​
    def predict_one_sample(sample_list, model, tokenizer, max_len, id2label):
        """
        对数据进行批量预测,获取每个样本对应的预测标签
        Args:
            sample_list: 样本序列,为一个list
            model: 模型
            tokenizer: 分词器
            max_len: 最大长度
            id2label: 标签字典
    ​
        Returns:
    ​
        """
        # 将数据转换成模型可使用的tensor形式
        batch = batch_data(sample_list, max_len, tokenizer)
        # 关掉模型的dropout
        model.eval()
        # 关掉模型的梯度计算
        with paddle.no_grad():
            input_ids = batch["input_ids"]
            attention_mask = batch["attention_mask"]
            # 获取模型预测结果
            [pred_label, _] = model.forward(input_ids, attention_mask)
            pred_label = pred_label.numpy()
        # 将模型预测结果转换成标签
        label_name = [id2label[pred] for pred in pred_label]
        return zip(sample_list, label_name)
    ​
    ​
    def set_args():
        """设置模型预测所需参数"""
        parser = argparse.ArgumentParser()
        parser.add_argument('--device', default='0', type=str, help='设备编号')
        parser.add_argument('--vocab_path', default="work/bert-paddle/vocab.txt", type=str, help='模型字典文件路径')
        parser.add_argument('--model_path', default="work/output_dir/checkpoint", type=str, help='模型路径')
        parser.add_argument('--num_labels', type=int, default=6, help='标签个数')
        return parser.parse_args(args=[])
    ​
    ​
    args = set_args()
    ​
    # 设置显卡信息
    os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
    os.environ["CUDA_VISIBLE_DEVICES"] = args.device
    # 获取device信息,用于模型训练
    device = "gpu:{}".format(args.device) if paddle.fluid.is_compiled_with_cuda() and int(args.device) >= 0 else "cpu"
    paddle.device.set_device(device)
    # 加载已保存模型,进行模型初始化
    model = SentimentAnalysisModel.from_pretrained(args.model_path, number_label=args.num_labels)
    # 实例化tokenizer
    tokenizer = BertTokenizer(args.vocab_path, do_lower_case=True)
    model.to(device)
    id2label = {0: "angry", 1: "happy", 2: "neutral", 3: "surprise", 4: "sad", 5: "fear"}

    使用动态图进行单条预测

    sample_list = ["妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。"]
    result = predict_one_sample(sample_list, model, tokenizer, args.max_len, id2label)
    # 打印每个样本的结果
    for sample, label in result:
        print("label: {}, text: {}".format(label, sample))
    label: sad, text: 妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。

    使用动态图测试1000条样本,记录时间

    # 计时,记录开始时间
    T1 = time.time()
    for i in range(1000):
        sample_list = ["妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。"]
        result = predict_one_sample(sample_list, model, tokenizer, args.max_len, id2label)
    # 计时,记录开始时间
    T2 = time.time()
    print("paddle模型,1000次的运行时间为{}秒".format(T2 - T1))
    paddle模型,1000次的运行时间为27.153663396835327秒

    动态图转onnx预测

    动态图转onnx

    def save_onnx_model(args):
        """将paddle模型转成onnx模型"""
        # 加载已保存模型,并进行参数初始化
        model = SentimentAnalysisModel.from_pretrained(args.model_path, number_label=args.num_labels)
        model.eval()
        # 定义输入节点input_ids和attention_mask
        input_ids = paddle.static.InputSpec([None, args.max_len], "int64", "input_ids")
        attention_mask = paddle.static.InputSpec([None, args.max_len], "int64", "attention_mask")
        # 使用paddle.onnx.export函数将模型转换成onnx模型,并保持
        paddle.onnx.export(model, args.onnx_model_path, input_spec=[input_ids, attention_mask], opset_version=12)
        # 检测onnx模型是否可用加载
        onnx_model = onnx.load(args.onnx_model_path + ".onnx")
        onnx.checker.check_model(onnx_model)
    ​
    ​
    save_onnx_model(args)

    加载onnx模型

    # 设置显卡信息
    os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
    os.environ["CUDA_VISIBLE_DEVICES"] = args.device
    # 实例化tokenizer
    tokenizer = BertTokenizer(args.vocab_path, do_lower_case=True)
    id2label = {0: "angry", 1: "happy", 2: "neutral", 3: "surprise", 4: "sad", 5: "fear"}
    # 加载onnx模型
    ort_sess = onnxruntime.InferenceSession(args.onnx_model_path + ".onnx")

    使用onnx进行单条预测

    sample_list = ["妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。"]
    batch = batch_data(sample_list, args.max_len, tokenizer)
    input_ids = batch["input_ids"]
    input_ids = input_ids.numpy()
    attention_mask = batch["attention_mask"]
    attention_mask = attention_mask.numpy()
    # 构建onnx所需的feed_dict
    ort_inputs = {ort_sess.get_inputs()[0].name: input_ids, ort_sess.get_inputs()[1].name: attention_mask}
    # 模型预测
    pred_label = ort_sess.run(None, ort_inputs)[0]
    # 标签转换
    label_name = [id2label[pred] for pred in pred_label]
    # 打印每个样本的结果
    for sample, label in zip(sample_list, label_name):
        print("label: {}, text: {}".format(label, sample))
        
    label: sad, text: 妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。

    使用onnx测试1000条样本,记录时间

    # 计时,记录开始时间
    T1 = time.time()
    for i in range(1000):
        sample_list = ["妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。"]
        batch = batch_data(sample_list, args.max_len, tokenizer)
        input_ids = batch["input_ids"]
        input_ids = input_ids.numpy()
        attention_mask = batch["attention_mask"]
        attention_mask = attention_mask.numpy()
        # 构建onnx所需的feed_dict
        ort_inputs = {ort_sess.get_inputs()[0].name: input_ids, ort_sess.get_inputs()[1].name: attention_mask}
        # 模型预测
        pred_label = ort_sess.run(None, ort_inputs)[0]
    # 计时,记录开始时间
    T2 = time.time()
    print("onnx模型,1000次的运行时间为{}秒".format(T2 - T1))
    onnx模型,1000次的运行时间为10.31327486038208秒

    动态图转静态图预测

    动态图转静态图

    def save_static_model(args):
        """将paddle动态图转成静态图"""
        # 加载已保存模型,并进行参数初始化
        model = SentimentAnalysisModel.from_pretrained(args.model_path, number_label=args.num_labels)
        model.eval()
        # 定义输入节点input_ids和attention_mask
        input_ids = paddle.static.InputSpec(shape=[None, args.max_len], dtype='int64', name='input_ids')
        attention_mask = paddle.static.InputSpec(shape=[None, args.max_len], dtype='int64', name='attention_mask')
        # 使用paddle.jit.to_static函数,将动态图转成静态图
        model = paddle.jit.to_static(model, input_spec=[input_ids, attention_mask])
        # 使用静态图进行模型预测
        sample_list = ["妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。"]
        tokenizer = BertTokenizer(args.vocab_path, do_lower_case=True)
        batch = batch_data(sample_list, args.max_len, tokenizer)
        input_ids = batch["input_ids"]
        attention_mask = batch["attention_mask"]
        outputs = model(input_ids, attention_mask)
        # 对静态进行保存
        paddle.jit.save(layer=model, path=args.static_model_path, input_spec=[input_ids, attention_mask])
    ​
    ​
    save_static_model(args)

    加载静态图模型

    # 设置显卡信息
    os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
    os.environ["CUDA_VISIBLE_DEVICES"] = args.device
    device = "gpu:{}".format(args.device) if paddle.fluid.is_compiled_with_cuda() and int(args.device) >= 0 else "cpu"
    paddle.device.set_device(device)
    if "gpu" in device:
        use_gpu = True
    else:
        use_gpu = False
    # 使用InferenceModel进行模型封装
    model = InferenceModel(modelpath=args.static_model_path, use_gpu=use_gpu, use_mkldnn=args.use_mkldnn)
    model.eval()
    # 实例化tokenizer
    tokenizer = BertTokenizer(args.vocab_path, do_lower_case=True)
    id2label = {0: "angry", 1: "happy", 2: "neutral", 3: "surprise", 4: "sad", 5: "fear"}

    使用静态图模型测试1000条样本,记录时间

    # 计时,记录开始时间
    T1 = time.time()
    for i in range(1000):
        sample_list = ["妈妈说想和我聊天,她一定是有难过的事了。。。我要上课,所以我好难过。。"]
        batch = batch_data(sample_list, args.max_len, tokenizer)
        input_ids = batch["input_ids"]
        attention_mask = batch["attention_mask"]
        pred_label = model(input_ids, attention_mask)[0]
    # 计时,记录开始时间
    T2 = time.time()
    print("静态图模型,1000次的运行时间为{}秒".format(T2 - T1))
    静态图模型,1000次的运行时间为7.70秒

    总结

    动态图运行1000次耗时29.46秒,onnx运行1000次耗时10.87秒,静态图运行1000次耗时7.70秒。
    ​
    可以看出,动态图最慢、静态图最快。

    参考文章

    机器学习中常见的动态图和静态图_xiaomu_347的博客-CSDN博客_动态图和静态图

    PaddlePaddle的静态图与动态图_梁小憨憨的博客-CSDN博客

    深度学习框架 の 动态图 vs 静态图 - 简书

    初探PaddlePaddle的静态图与动态图_下大禹了的博客-CSDN博客

    更多相关内容
  • 1.什么是库文件? 保存函数和变量 特点:保存的函数与变量只能使用但不能看到其实现 2.Linux库文件 静态库:在编译阶段加载(将库文件的代码加载到源文件) 动态库:在运行时加载 3.生成可执行文件存在的区别(优...
  • 深度学习框架:动态图 vs 静态图

    万次阅读 多人点赞 2019-04-04 11:01:31
    动态图框架对应的是 命令式编程 静态图框架对应的是 符号式编程 命令式编程 vs 符号式编程 参考 https://blog.csdn.net/z0n1l2/article/details/80873608 ...

    根据MxNet深度学习框架官方文档 (https://mxnet.incubator.apache.org/versions/master/architecture/program_model.html#symbolic-vs-imperative-programs ), 从命令式编程和符号式编程的角度分析动态图和静态图深度学习框架的实现方式和特点。

    • 动态图框架对应的是 命令式编程
    • 静态图框架对应的是 符号式编程
    什么是symblic/imperative style编程

    使用过python或C++对imperative programs比较了解。imperative-stype programs在运行时计算,大部分python代码都是imperative。比如下面的例子:

      import numpy as np
      a = np.ones(10)
      b = np.ones(10) * 2
      c = b * a
      d = c + 1
    

    当程序执行到 c = b ∗ a c=b∗a c=ba 时,代码开始做对应的数值计算。

    symbolic programs与此不同。symbolic-stype program中,需要先给出一个函数的定义(可能十分复杂)。当我们定义这个函数时,并不会做真正的数值计算。这类函数的定义中使用数值占位符,当给定真正的输入后,才会对这个函数进行编译计算。上面的例子用symbolic-stype program重新写:

        A = Variable('A')
        B = Variable('B')
        C = B * A
        D = C + Constant(1)
        # compiles the function
        f = compile(D)
        d = f(A=np.ones(10), B=np.ones(10)*2)
    

    上述代码中,语句 C = B ∗ A C=B∗A C=BA 并不会触发真正的数值计算,但会生成一个计算图(也称symbolic graph)描述这个计算。下图时计算D的计算图

    在这里插入图片描述
    大部分symbolic-style program都显性或隐性的包含一个编译的步骤,把计算转换成可以调用的函数。上面的例子中,数值计算仅仅在代码最后一行进行,symbolic program一个重要特点是其明确有构建计算图和生成可执行代码两个步骤。对于神经网络,一般会用一个就算图描述整个模型。

    其他流行深度学习框架中,PyTorch/Chainer/Minerva使用imperative style。symbolic-styple的框架包括Theano/CGT/TensorFlow。 CXXNet/Caffe这一类依赖配置文件的框架也看作symbolic-style库。此时配置文件被当作计算图的定义。下面我们对比一下二者的优劣:

    命令式编程更加灵活

    用python调用imperative-style库十分简单,编写方式和普通的python代码一样,在合适的位置调用库的代码实现加速。如果用python调用symbolic-style库,代码结构将出现一些变化,比如iteration可能无法使用。尝试把下面的例子转换成symbolic-style

        a = 2
        b = a + 1
        d = np.zeros(10)
        for i in range(d):
            d += np.zeros(10)
    

    如果symblic-style API不支持for循环,转换就没那个直接。不能用python的编码思路调用symblic-style库。需要利用symblic API定义的domain-specific-language(DSL)。深度学习框架会提供功能强大的DSL,把神经网络转化成可被调用的计算图。

    感觉上imperative program更加符合习惯,使用更加简单。例如,可以在任何位置打印出变量的值,轻松使用符合习惯的流程控制语句和循环语句。

    符号式编程更加高效

    既然imperative pragrams更加灵活,和计算机原生语言更加贴合,那么为什么很多深度学习框架使用symbolic风格? 最主要的原因式效率,内存效率和计算效率都很高。比如下面的例子:

        import numpy as np
        a = np.ones(10)
        b = np.ones(10) * 2
        c = b * a
        d = c + 1
        ...
    

    在这里插入图片描述
    如果数组每个元素内存中占据8字节,在python中需要多少内存?
    对于imperative programs中,需要在每一行上都分配必要的内存。一共4个数组,每个数组10个元素,一共4∗10∗8=320字节。 如果事先知道只有d是需要的结果,构造计算图时可以重复利用一些中间变量的空间。比如利用原址计算,我们可以把b的内存借给c使用,同样c的内存可以给d用,如此可以节省一半内存,仅仅需要2∗10∗8=160字节。
    symbolic programs限制更多,因为只需要d,构建计算图后,一些中间量,比如c,的值将无法看到。

    通过symbolic program,使用原址计算可以安全的重用内存,但牺牲了对c的访问可能。 imperative program可以处理各种访问可能,如果在python执行上述例子,任何中间量都可以方便访问。

    symbolic program还可以通过operation folding优化计算。在上述的例子中,乘法和加法可以展成一个操作,如下图所示:
    在这里插入图片描述

    如果在GPU上运算,计算图只需要一个kernel,节省了一个kernel。在很多优化库,比如caffe/CXXNet,人工编码进行此类优化操作。 operation folding可以提高计算效率。

    imperative program中不能自动operation folding,因为不知道中间变量是否会被访问到。symbolic program中可以做operation folding,因为获得了完整的计算图,而且明确哪些量以后会被访问,哪些量以后都不会被访问。

    Backprop和AutoDiff的案例分析

    在这一节,我们将基于自动微分或是反向传播的问题对比两种编程模式。梯度计算几乎是所有深度学习库所要解决的问题。使用命令式程序和符号式程序都能实现梯度计算。

    我们先看命令式程序。下面这段代码实现自动微分运算,我们之前讨论过这个例子。

        class array(object) :
            """Simple Array object that support autodiff."""
            def __init__(self, value, name=None):
                self.value = value
                if name:
                    self.grad = lambda g : {name : g}
    
            def __add__(self, other):
                assert isinstance(other, int)
                ret = array(self.value + other)
                ret.grad = lambda g : self.grad(g)
                return ret
    
            def __mul__(self, other):
                assert isinstance(other, array)
                ret = array(self.value * other.value)
                def grad(g):
                    x = self.grad(g * other.value)
                    x.update(other.grad(g * self.value))
                    return x
                ret.grad = grad
                return ret
    
        # some examples
        a = array(1, 'a')
        b = array(2, 'b')
        c = b * a
        d = c + 1
        print d.value
        print d.grad(1)
        # Results
        # 3
        # {'a': 2, 'b': 1}
    

    在上述程序里,每个数组对象都含有grad函数(事实上是闭包-closure)。当我们执行d.grad时,它递归地调用grad函数,把梯度值反向传播回来,返回每个输入值的梯度值。看起来似乎有些复杂。让我们思考一下符号式程序的梯度计算过程。下面这段代码是符号式的梯度计算过程。

        A = Variable('A')
        B = Variable('B')
        C = B * A
        D = C + Constant(1)
        # get gradient node.
        gA, gB = D.grad(wrt=[A, B])
        # compiles the gradient function.
        f = compile([gA, gB])
        grad_a, grad_b = f(A=np.ones(10), B=np.ones(10)*2)
    

    D的grad函数生成一幅反向计算图,并且返回梯度节点gA和gB。它们对应于下图的红点。

    在这里插入图片描述
    命令式程序做的事和符号式的完全一致。它隐式地在grad闭包里存储了一张反向计算图。当执行d.grad时,我们从d(D)开始计算,按照图回溯计算梯度并存储结果。

    因此我们发现无论符号式还是命令式程序,它们计算梯度的模式都一致。那么两者的差异又在何处?再回忆一下命令式程序“未雨绸缪”的要求。如果我们准备一个支持自动微分的数组库,需要保存计算过程中的grad闭包。这就意味着所有历史变量不能被垃圾回收,因为它们通过函数闭包被变量d所引用。那么,若我们只想计算d的值,而不想要梯度值该怎么办呢?

    在符号式程序中,我们声明f=compiled([D>)来替换。它也声明了计算的边界,告诉系统我只想计算正向通路的结果。那么,系统就能释放之前结果的存储空间,并且共享输入和输出的内存。

    假设现在我们运行的不是简单的示例,而是一个n层的深度神经网络。如果我们只计算正向通路,而不用反向(梯度)通路,我们只需分配两份临时空间存放中间层的结果,而不是n份。由于命令式程序需要为今后可能用到的梯度值做准备,中间结果不得不保存,就需要用到n份临时空间。

    正如我们所见,优化的程度取决于对用户行为的约束。符号式程序的思路就是让用户通过编译明确地指定计算的边界。而命令式程序为之后所有情况做准备。符号式程序更充分地了解用户需要什么和不想要什么,这是它的天然优势。

    当然,我们也能对命令式程序施加约束条件。例如,上述问题的解决方案之一是引入一个上下文变量。我们可以引入一个没有梯度的上下文变量,来避免梯度值的计算。这给命令式程序带来了更多的约束条件,以换取性能上的改善。

        with context.NoGradient():
            a = array(1, 'a')
            b = array(2, 'b')
            c = b * a
            d = c + 1
    

    然而,上述的例子还是有许多可能的未来,也就是说不能在正向通路中做同址计算来重复利用内存(一种减少GPU内存的普遍方法)。这一节介绍的技术产生了显式的反向通路。在Caffe和cxxnet等工具包里,反向传播是在同一幅计算图内隐式完成的。这一节的讨论同样也适用于这些例子。
    大多数基于函数库(如cxxnet和caffe)的配置文件,都是为了一两个通用需求而设计的。计算每一层的激活函数,或是计算所有权重的梯度。这些库也面临同样的问题,若一个库能支持的通用计算操作越多,我们能做的优化(内存共享)就越少,假设都是基于相同的数据结构。

    因此经常能看到一些例子在约束性和灵活性之间取舍。

    模型检查点

    支持对配置文件设置检查点是符号式程序的加分项。因为符号式的模型构建阶段并不包含计算步骤,我们可以直接序列化计算图,之后再重新加载它,无需引入附加层就解决了保存配置文件的问题。

        A = Variable('A')
        B = Variable('B')
        C = B * A
        D = C + Constant(1)
        D.save('mygraph')
        ...
        D2 = load('mygraph')
        f = compile([D2])
        # more operations
        ...
    

    因为命令式程序逐行执行计算。我们不得不把整块代码当做配置文件来存储,或是在命令式语言的顶部再添加额外的配置层。

    参数更新

    大多数符号式编程属于数据流(计算)图。数据流图能方便地描述计算过程。然而,它对参数更新的描述并不方便,因为参数的更新会引起变异(mutation),这不属于数据流的概念。大多数符号式编程的做法是引入一个特殊的更新语句来更新程序的某些持续状态。

    用命令式风格写参数更新往往容易的多,尤其是当需要相互关联地更新时。对于符号式编程,更新语句也是被我们调用并执行。在某种意义上来讲,目前大部分符号式深度学习库也是退回命令式方法进行更新操作,用符号式方法计算梯度。

    没有严格的边界

    我们已经比较了两种编程风格。之前的一些说法未必完全准确,两种编程风格之间也没有明显的边界。例如,我们可以用Python的(JIT)编译器来编译命令式程序,使我们获得一些符号式编程对全局信息掌握的优势。但是,之前讨论中大部分说法还是正确的,并且当我们开发深度学习库时这些约束同样适用。

    参考
    1. https://blog.csdn.net/z0n1l2/article/details/80873608
    2. https://mxnet.incubator.apache.org/versions/master/architecture/program_model.html#symbolic-vs-imperative-programs
    3. https://blog.csdn.net/daslab/article/details/50434145
    4. https://www.jianshu.com/p/1c7ef1ce5540 (MXNet的动态图接口Gluon
    展开全文
  • 动态导入库和静态库: 他们...为什么要将模板实例化: 只有将导出库里面的模板函数或者模板类实例化,才能将实例化后模板函数的地址信息和模板类实例化的地址信息保存在导出库中。 示范:我还是以一个例子来说明吧,这
  • 静态ram和动态ram的区别是什么

    千次阅读 2020-12-22 06:58:32
    什么动态RAM 在动态RAM芯片内部,每个内存单元保存一位信息。单元由下面两部分组成:一个晶体管和一个电容器。当然这些部件都非常地小,因此一个内存芯片内可以包含数百万个。电容器保存信息位——0或1(有关位的...

    随机存取存储器(random access memory,RAM)又称作“随机存储器”,是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。

    存储单元的内容可按需随意取出或存入,且存取的速度与存储单元的位置无关的存储器。这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的程序。 按照存储单元的工作原理,随机存储器又分为静态随机存储器(英文:Static RAM,SRAM)和动态随机存储器(英文Dynamic RAM,DRAM)。

    什么是静态RAM

    存储器是计算机的记忆部件。CPU 要执行的程序、要处理的数据、处理的中间结果等都存放在存储器中。

    目前微机的存储器几乎全部采用半导体存储器。存储容量和存取时间是存储器的两项重要指标,它们反映了存储记忆信息的多少与工作速度的快慢。半导体存储器根据应用可分为读写存储器(RAM)和只读存储器(ROM)两大类。

    读写存储器(RAM)

    读写存储器又称随机存取存储器(Random Access Memory)简称RAM,它能够在存储器中任意指定的地方随时写入或读出信息;当电源掉电时,RAM 里的内容即消失。根据存储单元的工作原理,RAM 又分为静态RAM 和动态RAM。静态RAM 用触发器作为存储单元存放1 和0,存取速度快,只要不掉电即可持续保持内容不变。一般静态RAM 的集成度较低,成本较高。

    动态RAM 的基本存储电路为带驱动晶体管的电容。电容上有无电荷状态被视为逻辑1 和0。随着时间的推移,电容上的电荷会逐渐减少,为保持其内容必须周期性地对其进行刷新(对电容充电)以维持其中所存的数据,所以在硬件系统中也得设置相应的刷新电路来完成动态RAM 的刷新,这样一来无疑增加了硬件系统的复杂程度,因此在单片机应用系统中一般不使用动态RAM。

    静态RAM 的基本存储电路为触发器,每个触发器存放一位二进制信息,由若干个触发器组成一个存储单元,再由若干存储单元组成存储器矩阵,加上地址译码器和读/写控制电路就组成静态RAM。与动态RAM 相比,静态RAM 无须考虑保持数据而设置的

    刷新电路,故扩展电路较简单。但由于静态RAM 是通过有源电路来保持存储器中的数据,因此,要消耗较多功率,价格也较高。

    RAM 内容的存取是以字节为单位的,为了区别各个不同的字节,将每个字节的存储单元赋予一4个编号,该编号就称为这个存储单元的地址,存储单元是存储的最基本单位,不同的单元有不同的地址。在进行读写操作时,可以按照地址访问某个单—元。

    由于集成度的限制,目前单片RAM 容量很有限,对于一个大容量的存储系统,往往需要若干RAM 组成,而读/写操作时,通常仅操作其中一片(或几片),这就存在一个片选问题。RAM 芯片上特设了一条片选信号线,在片选信号线上加入有效电平,芯片即被选中,可进行读/写操作,末被选中的芯片不工作。片选信号仅解决芯片是否工作的问题,而芯片执行读还是写则还需有一根读写信号线,所以芯片上必须设读/写控制线。

    下面是几种常用静态RAM:

    在8031 单片机应用系统中,最常用的静态数据存储器RAM 芯片有6116(2Kx8)和6264(8Kx8)两种。

    什么是动态RAM

    在动态RAM芯片内部,每个内存单元保存一位信息。单元由下面两部分组成:一个晶体管和一个电容器。当然这些部件都非常地小,因此一个内存芯片内可以包含数百万个。电容器保存信息位——0或1(有关位的信息,请参见位和字节)。

    晶体管起到了开关的作用,能让内存芯片上的控制线路读取电容上的数据,或改变其状态。电容器就像一个储存电子的小桶。在存储单元中写入1,小桶内就充满电子;写入0,小桶就被清空。这只“桶”的问题在于:它会泄漏。只需大约几毫秒的时间,一个充满电子的小桶就会漏得一干二净。因此,为了确保动态存储器能正常工作,必须由CPU或是由内存控制器对所有电容不断地进行充电,使它们在电子流失殆尽之前保持“1”值。

    为此,内存控制器会先行读取存储器中的数据,再把数据写回去。这种刷新操作每秒钟会自动进行数千次。动态RAM正是得名于这种刷新操作。它需要不间断地进行刷新,否则就会丢失所保存的数据。这一刷新动作的缺点就是费时,并且会降低内存速度。静态RAM使用了截然相反的技术。静态RAM用某种形式的触发器来保存内存的每个位(有关触发器的详细信息,请参阅布尔逻辑的应用)。

    内存单元的触发器由4个或6个晶体管以及一些线路组成,但从来不需要刷新。这使得静态RAM比动态RAM要快得多。但是,由于它所含的部件较多,静态内存单元在芯片上占用的空间会远远超过动态内存单元,使得每个芯片上的内存较小。

    静态ram和动态ram的区别

    静态RAM(SRAM)速度非常快,只要电源存在内容就不会自动消失。其基本存储电路为6个MOS管组成1位,因此集成度相对较低,功耗也较大。一般高速缓冲存储器用它组成。

    动态RAM(DRAM)的内容在10-3或l0-6秒之后自动消失,因此必须周期性的在内容消失之前进行刷新。由于它的基本存储电路由一个晶体管及一个电容组成,因此它的集成度高,成本较低,另外耗电也少,但它需要一个额外的刷新电路。DRAM运行速度较慢,SRAM比DRAM要快2~5倍,一般,PC机的标准存储器都采用DRAM组成。

    打开APP阅读更多精彩内容

    点击阅读全文

    展开全文
  • openlayers加载静态图片作为底,操作图片流畅,可控显示范围和等级
  • 演示了如何静态调用和动态调用子VI,子VI可以并行运行或者阻塞调用程序的运行。代码非常清晰,可以直接使用。
  • 动态图和静态图tensorflow和pytorch

    千次阅读 2021-05-24 17:11:36
    动态图和静态图的区别,可以理解解释执行和编译执行的区别,动态图就是原生python编程习惯,而静态图其实是加了编译层,理论上编译的要比解释的快,但实际上有些任务速度区别不大,因为原生python也可以优化的很快...

    动态图和静态图的区别,可以理解为解释执行和编译执行的区别,动态图就是原生python编程习惯,而静态图其实是加了编译层,理论上编译的要比解释的快,但实际上有些任务速度区别不大,因为原生python也可以优化的很快,况且大部分算力和时间是消耗在gpu上了。
    飞桨完全可以动态图编程和训练,训练结束后直接就可以自由的选择存储动态图模型还是静态图模型。存储为动态图模型方便以后的继续训练和优化,存储为静态图模型就是为了上项目和落地。
    简单来说,工业应用之前,研发/竞赛/训练,最优选择都是动态图。工业落地优选静态图(落地前的工作还是动态图为主)

    为使您的问题得到快速解决,在建立Issues前,请您先通过如下方式搜索是否有相似问题:【搜索issue关键字】【使用labels筛选】【官方文档】
    如何选择使用静态图还是动态图?
    我在别的博客上看到
    Q:作为新手,我应该如何选择?使用静态图好还是动态图好?
    A:目前飞桨官方在主推动态图,即命令式编程。如果您是新手,建议从动态图开始学习;如果您有一定的基础,可以尝试静态图。因为最后训练出来的模型肯定是需要部署的,目前使用用命令式编程(动态图)得到的模型文件不能很方便的部署,因此对部署有需求的话,建议使用静态图。
    我想知道动态图和静态图该如何选择,二者在预测性能及精度上是否有区别,部署的话我会使用flask搭建服务来部署,可能基于CPU或GPU上进行预测

    动态图和静态图
    目前神经网络框架分为静态图框架和动态图框架,PyTorch 和 TensorFlow、Caffe 等框架最大的区别就是他们拥有不同的计算图表现形式。 TensorFlow 使用静态图,这意味着我们先定义计算图,然后不断使用它,而在 PyTorch 中,每次都会重新构建一个新的计算图。通过这次课程,我们会了解静态图和动态图之间的优缺点。
    
    对于使用者来说,两种形式的计算图有着非常大的区别,同时静态图和动态图都有他们各自的优点,比如动态图比较方便debug,使用者能够用任何他们喜欢的方式进行debug,同时非常直观,而静态图是通过先定义后运行的方式,之后再次运行的时候就不再需要重新构建计算图,所以速度会比动态图更快。
    
    
    
    下面我们比较 while 循环语句在 TensorFlow 和 PyTorch 中的定义
    
    TensorFlow
    # tensorflow
    import tensorflow as tf
    
    first_counter = tf.constant(0)
    second_counter = tf.constant(10)
    def cond(first_counter, second_counter, *args):
        return first_counter < second_counter
    
    def body(first_counter, second_counter):
        first_counter = tf.add(first_counter, 2)
        second_counter = tf.add(second_counter, 1)
        return first_counter, second_counter
    c1, c2 = tf.while_loop(cond, body, [first_counter, second_counter])
    with tf.Session() as sess:
        counter_1_res, counter_2_res = sess.run([c1, c2])
    print(counter_1_res)
    print(counter_2_res)
    20
    20
    可以看到 TensorFlow 需要将整个图构建成静态的,换句话说,每次运行的时候图都是一样的,是不能够改变的,所以不能直接使用 Python 的 while 循环语句,需要使用辅助函数 tf.while_loop 写成 TensorFlow 内部的形式
    
    这是非常反直觉的,学习成本也是比较高的
    
    下面我们来看看 PyTorch 的动态图机制,这使得我们能够使用 Python 的 while 写循环,非常方便
    
    PyTorch
    # pytorch
    import torch
    first_counter = torch.Tensor([0])
    second_counter = torch.Tensor([10])
    while (first_counter < second_counter)[0]:
        first_counter += 2
        second_counter += 1
    print(first_counter)
    print(second_counter)
     20
    [torch.FloatTensor of size 1]
    
    
     20
    [torch.FloatTensor of size 1]
    可以看到 PyTorch 的写法跟 Python 的写法是完全一致的,没有任何额外的学习成本
    
    上面的例子展示如何使用静态图和动态图构建 while 循环,看起来动态图的方式更加简单且直观,你觉得呢?
    

    深度学习框架 の 动态图 vs 静态图

    0.192 字数 1,431 阅读 2,681

    Date: 2020/08/03

    Author: CW

    Foreword:

    各位炼丹者应该都会有自己常用的一种或几种深度学习框架,如 MxNet、Caffe、Tensorflow、Pytorch、PaddlePaddle(百度),甚至是国产新兴框架 MegEngine(旷视)、MindSpore(华为)等,在涉及介绍这些框架的时候,都会提及动态图和静态图这样的概念,那么它们究竟是什么意思呢?在框架中又是如何体现与使用的呢?本文会结合 Tensorflow、Pytorch 以及小鲜肉 MegEngine 的例子来为诸位揭开这神秘的面纱。


    计算图

    不论是动态图还是静态图,它们都属于计算图。计算图是用来描述运算的有向无环图,它有两个主要元素:结点(Node)和边(Edge)。结点表示数据,如向量、矩阵、张量,而边表示运算,如加减乘除卷积等。

    采用计算图来描述运算的好处不仅是让运算流的表达更加简洁清晰,还有一个更重要的原因是方便求导计算梯度

    计算图

    上图表示的是 y = (w + x) * (w + 1) 代表的计算图,若要计算y对w的导数,那么结合链式求导法则,就在计算图中反向从y找到所有到w的路径,每条路径上各段的导数相乘就是该路径的偏导,最后再将所有路径获得的偏导求和即可。

    叶子节点是用户创建的变量,如上图的x与w,在Pytorch的实现中,为了节省内存,在梯度反向传播结束后,非叶子节点的梯度都会被释放掉。


    动态图

    动态图意味着计算图的构建和计算同时发生(define by run)。这种机制由于能够实时得到中间结果的值,使得调试更加容易,同时我们将大脑中的想法转化为代码方案也变得更加容易,对于编程实现来说更友好。Pytorch使用的就是动态图机制,因此它更易上手,风格更加pythonic,大受科研人员的喜爱。


    静态图

    静态图则意味着计算图的构建和实际计算是分开(define and run)的。在静态图中,会事先了解和定义好整个运算流,这样之后再次运行的时候就不再需要重新构建计算图了(可理解为编译),因此速度会比动态图更快,从性能上来说更加高效,但这也意味着你所期望的程序与编译器实际执行之间存在着更多的代沟,代码中的错误将难以发现,无法像动态图一样随时拿到中间计算结果。Tensorflow默认使用的是静态图机制,这也是其名称的由来,先定义好整个计算流(flow),然后再对数据(tensor)进行计算。


    动态图 vs 静态图

    通过一个例子来对比下动态图和静态图机制在编程实现上的差异,分别基于Pytorch和Tensorflow实现,先来看看Pytorch的动态图机制:

    import torch


    first_counter = torch.Tensor([0])

    second_counter = torch.Tensor([10])


    while (first_counter < second_counter)[0]:

        first_counter += 2

        second_counter += 1


    print(first_counter)

    print(second_counter)

    可以看到,这与普通的Python编程无异。

    再来看看在基于Tensorflow的静态图机制下是如何实现上述程序的:

    import tensorflow as tf

    first_counter = tf.constant(0)

    second_counter = tf.constant(10)

    # tensorflow

    import tensorflow as tf


    first_counter = tf.constant(0)

    second_counter = tf.constant(10)


    def cond(first_counter, second_counter, *args):

        return first_counter < second_counter


    def body(first_counter, second_counter):

        first_counter = tf.add(first_counter, 2)

        second_counter = tf.add(second_counter, 1)

        return first_counter, second_counter


    c1, c2 = tf.while_loop(cond, body, [first_counter, second_counter])


    with tf.Session() as sess:

        counter_1_res, counter_2_res = sess.run([c1, c2])


    print(counter_1_res)

    print(counter_2_res)

    (⊙o⊙)… 对Tensorflow不熟悉的童鞋来说,第一反应可能会是:这什么鬼!?确实,看上去会有点难受..

    Tensorflow在静态图的模式下,每次运算使用的计算图都是同一个,因此不能直接使用 Python 的 while 循环语句,而是要使用其内置的辅助函数 tf.while_loop,而且还要tf.Session().run()之类的乱七八糟..

    而Pytorch是动态图的模式,每次运算会构建新的计算图,在编程实现上不需要额外的学习成本(当然首先你得会Python)。


    动静结合

    在最近开源的框架MegEngine中,集成了两种图模式,并且可以进行相互切换,下面举例说明将动态图转换为静态图编译过程中进行的内存和计算优化

    y = w*x + b 的动态计算图如下:

    动态图

    可以看到,中间的运算结果是被保留下来的,如p=w*x,这样就一共需要5个变量的存储空间。若切换为静态图,由于事先了解了整个计算流,因此可以让y复用p的内存空间,这样一共就只需要4个变量的存储空间。

    另外,MegEngine 还使用了 算子融合 (Operator Fuse)的机制,用于减少计算开销。对于上面的动态计算图,切换为静态图后可以将乘法和加法融合为一个三元操作(假设硬件支持):乘加(如下图所示),从而降低计算量。

    静态图
    禁止转载,如需转载请通过简信或评论联系作者。
    更多精彩内容,就在简书APP
    "一击换你美好生活!"
    赞赏支持 还没有人赞赏,支持一下
    总资产8 (约0.50元) 共写了16.5W字 获得118个赞 共88个粉丝
    关注
    DL:深度学习框架Pytorch、 Tensorflow各种角度对比
    pytorchTensorFlow深度学习
    【摘要】 DL:深度学习框架Pytorch、 Tensorflow各种角度对比         目录 先看两个框架实现同样功能的代码 1、Pytorch、Tensorflow代码比较 2Tensorflow(数据即是代码,代码就是数据)+Pytorch(随着进展定义、更改和执行节点) 3、TensorFlow —Google—像框架+静态图+公司好手,不易调试+额外...
    DL:深度学习框架Pytorch、 Tensorflow各种角度对比
    
    目录
    
    先看两个框架实现同样功能的代码
    
    1、Pytorch、Tensorflow代码比较
    
    2Tensorflow(数据即是代码,代码就是数据)+Pytorch(随着进展定义、更改和执行节点)
    
    3、TensorFlow —Google—像框架+静态图+公司好手,不易调试+额外概念(会话、图、变量范围、占位符),序列化更强大+支持移动和嵌入式部署+大规模分布式+强大的可视化工具
    
    4、PyTorch —FaceBook—像库+动态图+科研好手,易理解且易调试+结合NumPy更易上手,小规模项目+支持分布式执行+暂时不支持分布式训练
    
    ​​​​​​​
    
    相关文章
    DL:深度学习框架Pytorch、 Tensorflow各种角度对比
    DL:深度学习框架Pytorch、 Tensorflow各种角度对比——案例代码实现
    
    先看两个框架实现同样功能的代码
    1、Pytorch、Tensorflow代码比较
    DL框架之PyTorch:PyTorch的简介、安装、使用方法之详细攻略
    DL框架之Tensorflow:Tensorflow的简介、安装、使用方法之详细攻略
    DL:深度学习框架Pytorch、 Tensorflow各种角度对比——案例代码实现
    
    2Tensorflow(数据即是代码,代码就是数据)+Pytorch(随着进展定义、更改和执行节点)
             两种框架都在张量上运行,把任何模型都看作一个有向非循环图(DAG),但对于如何定义它们,PyTorch 和 TensorFlow 区别很大。
    
    在便利性和上手难度上,PyTorch 远胜于TensorFlow ,
    (1)、原因是PyTorch 是基于动态图,而TensorFlow 是基于静态计算图,因此PyTorch 能随时打印tensor的值,但是TensorFlow 需要设置回调的方法才能打印.
    如果想在TensorFlow 中想判断一个变量的值的正确性,只能使用assert方法,这一点确实TensorFlow 不及PyTorch ,
    上手难度上PyTorch 也是比TensorFlow 容易。
             TensorFlow 遵循“数据即是代码,代码就是数据”的理念。在 TensorFlow 中,在跑模型之前会静态的定义图形。和外界的所有联系都是通过 tf.Session 对象和 tf.Placeholder,它们都是会在模型运行被外部数据取代的张量。
             在 PyTorch 中,会更动态一些:你可以随着进展定义、更改和执行节点,没有特殊的会话界面或占位符。
             整体来看,PyTorch 和 Python 结合的更紧凑些,多数时候会感觉更原生。而在 TensorFlow 里写东西时,有时你会觉得你的模型好像躲在一堵墙后面一样,就通过墙上的几个洞洞跟你交流。当然了,这也看每个人的喜好和品味。 PyTorch 更适用于研究、爱好者和小规模项目的快速原型开发。TensorFlow 更适合大规模部署,尤其是涉及跨平台和嵌入式部署时。
    
    3、TensorFlow —Google—像框架+静态图+公司好手,不易调试+额外概念(会话、图、变量范围、占位符),序列化更强大+支持移动和嵌入式部署+大规模分布式+强大的可视化工具
           PyTorch 开始会看起来很像一个框架。回想一下,编程框架会在特定领域为我们提供有用的抽象,用它们可以很方便的解决具体问题。而这是框架和库的的本质区别之处。
    
    (1)、TensorFlow基于静态图:在 TensorFlow 中,图结构是静态的,也就是说图在「编译」之后再运行。需要先构建一个计算图,构建好了之后,这样一个计算图是不能够变的了,然后再传入不同的数据进去,进行计算。即固定了计算的流程,所以变得不太灵活。
        1)、如果要去改变计算的逻辑,或者随着时间变化的计算逻辑,这样的动态计算TensorFlow是实现不了的,或者是很麻烦。
    (2)、TensorFlow不易调试:调试 TensorFlow 则没这么容易。它有两个选择,一是从会话中请求你想检查的变量,而是学会使用 TensorFlow 调试器(tfdbg)。
        1)、TensorFlow 则不同,你可以选择用一个叫 tfdbg 的特殊工具,它能让你在运行时评估 TensorFlow 表达式,浏览所有张量,在会话范围中操作。当然,无法用它调试 Python 代码,因此无需单独使用 pdb。
    (3)、TensorFlow上手需学习额外概念—会话、图、变量范围、占位符:可以将 TensorFlow 看作是一种嵌入 Python 的编程语言。当你编写 TensorFlow 代码时,它会被 Python编译成图(graph),然后由 TensorFlow 执行引擎运行。
        1)、所以,TensorFlow 还有一些需要额外学习的概念,比如会话(session)、图、变量范围、占位符。要让基本的模型跑起来也需要更多样板代码。上手 TensorFlow 的时间肯定会比 PyTorch 长。
    
    (4)、TensorFlow的序列化更强大:TensorFlow 的 Saver 对象也很容易使用,而且也为检查点提供了更多选择。TensorFlow 在序列化方面的主要优势是整个计算图都可以保存为 protocol buffer。这既包括参数,也包括运算。然后这个图可以用其它支持的语言(C++、Java)加载。对于不支持 Python 的部署环境来说,这是非常重要的功能。而且理论上,这个功能也可以在你修改模型的源代码,但又想运行旧模型时为你提供帮助。
    
    (5)、TensorFlow 支持移动和嵌入式部署:但是在 TensorFlow 上,要将模型部署到安卓或 iOS 上需要不小的工作量,但至少你不必使用 Java 或 C++ 重写你模型的整个推理部分。
       1)、对于高性能服务器上的部署,还有 TensorFlow Serving 可用。除了性能方面的优势,TensorFlow Serving 的另一个重要特性是无需中断服务,就能实现模型的热插拔。
    
    (6)、TensorFlow 的数据加载 比较复杂:我还没找到 TensorFlow 的非常有用的数据加载工具(读取器、队列、队列运行器等等)。部分原因是要将你想并行运行的所有预处理代码加入到 TensorFlow 图中并不总是那么简单直接(比如计算频谱图)。另外,TensorFlow 的 API 本身也更加冗长,学习起来也更难。
    
    (7)、TensorFlow 的设备管理默认即可:设备管理的无缝性能非常好,通常你不需要指定任何东西,因为默认的设置就很好。比如说,TensorFlow 假设如果存在可用的 GPU,你就希望在 GPU 上运行。
        1)、TensorFlow 设备管理的唯一缺陷是它会默认占用所有可用的 GPU 上的所有内存,即使真正用到的只有其中一个。但也有一种简单的解决方案,就是指定 CUDA_VISIBLE_DEVICES。有时候人们会忘记这一点,就会让 GPU 看起来很繁忙,尽管实际上它们啥也没干。
    
    (8)、TensorFlow的强大的可视化工具TensorBoard:TensorBoard 是一个用于可视化训练机器学习模型各个方面的工具。它是 TensorFlow 项目产出的最有用的功能之一。仅需在训练脚本中加入少许代码,你就可以查看任何模型的训练曲线和验证结果。TensorBoard 作为一个网页服务运行,可以尤其方便地可视化存储在 headless 节点上的结果。
       1)、展示模型图形、绘制标量变量、可视化分布和直方图、可视化图形  播放音频
    
    (9)、TensorFlow持支持分布式执行、支持大规模分布式训练:在GPU的分布式计算上更为出色,在数据量巨大时效率比pytorch要高一些。
    
    4、PyTorch —FaceBook—像库+动态图+科研好手,易理解且易调试+结合NumPy更易上手,小规模项目​​​​​​​+支持分布式执行+暂时不支持分布式训练
           TensorFlow 给人的感觉更像是一个库,而非一个框架:所有的操作都为低阶操作,你需要写很多样板代码,即便你可能并不想写(比如,一遍又一遍的定义方差和权重···)。
    
    (1)、PyTorch基于动态图:创建和运行计算图可能是这两个框架差别最大的地方。在 PyTorch 中,图结构是动态的,也就是说图是在运行时创建的。即就和python的逻辑是一样的,要对变量做任何操作都是灵活的。
    (2)、PyTorch容易理解且易调试: 简单的图构建方式更容易理解,但也许更重要的是也更容易调试。调试 PyTorch 代码就跟调试 Python 代码一样。你可以使用 pdb,并且可以在任何地方设置断点。
       1)、 PyTorch 的计算图是在运行时定义,可以用 pdb,ipdb,PyCharm 这些 Python 调试工具或者以前的可靠的打印语句也行。
    (3)、PyTorch结合NumPy更易上手:PyTorch 本质上是支持 GPU 的 NumPy 替代,配备了可用于构建和训练深度神经网络的更高级的功能。所以如果你熟悉 NumPy、Python 和常用的深度学习抽象(卷积层、循环层、SGD 等),那 PyTorch 就很容易学。
    (4)、PyTorch序列化的API比较简单:在这两种框架中,保存和加载模型都很简单。PyTorch 有一个非常简单的 API,既可以保存模型的所有权重,也可以 pickle(加工)整个类。
    
    (5)、PyTorch不支持移动和嵌入式部署:而包括 PyTorch 在内的很多深度学习框架都没有这个能力。
    
    (6)、PyTorch的数据加载 API 设计得很好:数据集、采样器和数据加载器的接口都是特定的。数据加载器可以接收一个数据集和一个采样器,并根据该采样器的调度得出数据集上的一个迭代器(iterator)。并行化数据加载很简单,只需为数据加载器传递一个 num_workers 参数即可。
    
    (7)、PyTorch 的设备管理必须指定:而在 PyTorch 中,你必须在启用了 CUDA 之后明确地将所有东西移到 GPU 上。
    
    (8)、PyTorch 的可视化只能调用matplotlib 、seaborn​​​​​​​等库:目前 PyTorch 并没有可以和 Tensorboard 匹敌的工具,不过倒是存在一些集成功能。虽然也能用一些绘图工具比如 matplotlib 和 seaborn
    
    (9)、PyTorch 支持支持分布式执行、暂时不支持分布式训练:
    
    展开全文
  • 当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度等等。Android中的广播机制...
  • libqrencode静态库库生成二维码并保存为BMP图片Demo,包含用VS2015已经编译生成好的libqrencode.lib。具体可参见:http://blog.csdn.net/ljttianqin/article/details/73823666
  • 静态链接使得不同的程序开发者和部门能够相对独立地开发和测试自己的程序模块,从某种意义上来讲大大促进了程序开发的效率,原先限制程序的规模也随之扩大。但是慢慢地静态链接的诸多缺点也逐步暴露出来,比如浪费...
  • 官方文档:动态图转静态图 1.动态图和静态图 在深度学习模型构建上,飞桨框架支持动态图编程和静态图编程两种方式,其代码编写和执行方式均存在差异。 动态图编程:采用 Python 的编程风格,解析式地执行每一行...
  • 在向旅游者致欢迎词时,便于保存变量部地陪的态度要热情,感情要真挚,内容要依情而异,语言要( )计算机存据区静山西省总的地势是()。储管成动储区储区存放储区提出到2020年要将我国旅游业建设成国民经济的战略性...
  • 动态RAM与静态RAM

    2022-03-20 21:40:14
    动态RAM集成度低,静态RAM集成度高 动态RAM每一个单元的电路包含一个晶体管+一个电容; 静态RAM每一个单元的电路包含6个晶体管; 3. 芯片引脚 DRAM的行地址和列地址可以分别进行传送,地址线条数原来的一半,因此...
  • 概述 将网站冻结图像或静态 HTML。 用法 安装 运行命令phantomjs <path> 选项 即将推出
  • java 动态代理与静态代理

    千次阅读 2021-07-24 20:21:04
    java中代理分为动态代理与静态代理,其中动态代理比较常见的有jdk动态代理和cglib动态代理。 静态代理 静态代理实现简单,但不够灵活,比较麻烦,要实现与被代理对象一样的接口,创建很多代理类。接口增加方法后...
  • iOS之深入解析静态Pod与动态Pod

    万次阅读 2021-10-19 20:18:15
    一、静态库与动态库 在项目中使用 pod 实现模块化,对于子模块和第三类库的导入方式存在两种:静态库、动态库。 当在 podfile 中指定 use_frameworks! 时,子模块和第三方类库将被打包成 .framework 动态库,模块...
  • 网页可访问(超详细)安装element ui组件vue界面使用upload上传组件springboot后端保存静态资源以及请求地址编写保存静态资源类controller类编写注意 注意·······以下效果 安装element ui组件 详细...
  • 相信在大家的聊天软件中都保存着许多搞笑的gif动态表情包用来与朋友斗图时使用。那么,大家知道这种gif表情包是如何制作的吗?接下来,小编就给大家分享一款动态图片在线制作(https://www.gif5.net/)工具–GIF5...
  • 静态页面和动态页面的区别

    千次阅读 2020-09-09 18:03:13
    通过本篇文章的阅读,详细大家分享一下静态页面和动态页面到底是什么,两者有什么区别。 什么静态页面和动态页面?  通俗的来讲,静态页面是随着HTML代码的生成,页面的内容和显示效果就基本不会发生变化...
  • 动态图转静态图 动态图有诸多优点,包括易用的接口,python风格的编程体验,友好的debug交互机制等。在动态图模式下,代码是按照我们编写的顺序依次执行。这种机制更符合Python程序员的习惯,可以很方便地将大脑中的...
  • 一键把动态IP自动设置为静态IP

    千次阅读 2018-05-15 22:02:37
    一键把动态IP自动设置为静态IP 问题描述 平时在调试网络模块时,常常需要手动设置IP,然后才可以开始调试网络模块。调试完之后又需要手动设置IP为动态获取。一直就像用批处理来解决,但是一直懒没做,现在来回改...
  • 静态数据与动态数据

    千次阅读 2021-02-22 17:05:14
    电脑有两个常用且重要的组成部分,分别内存与硬盘,都是用来存储数据的。不同点在于,内存是用来存储动态数据的,硬盘是用来存储静态数据。 静态数据: 静态数据是指一些永久性的数据,一般储存在硬盘中。储存...
  • 1、静态网站与动态网站 静态网站是什么? 技术上来讲,静态网站是指网页不是由服务器动态生成的。HTML、CSS 和 JavaScript 文件就静静地躺在服务器的某个路径下,它们的内容与终端用户接收到的版本是一样的。原始...
  • 动态网页和静态网页的区别,首先要分别了解两个概念,就是什么静态网页,什么动态网页,并且学会区分哪些是静态哪些是动态静态网页: (1)静态网页不能简单地理解成静止不动的网页,他主要指的是网页中没有...
  • linux网络配置—将动态分配ip地址改为静态iplinux版本:centos一.配置流程1.输入 vim /etc/sysconfig/network-scripts/ifcfg-eth0 查看网卡信息BOOTPROTO=dhcp表示每次关闭linux系统在重新启动之后都会重新获得新的...
  • 窗格注册表 一个索引,用于保存所有已加载的实体窗格,无论是静态加载还是动态加载
  • 图片静态图为图片设置鼠标悬浮事件(onmouseover),把图片路径设置为动态图路径;再设置一个鼠标离开事件(onmouseout),把URL还原就好了。这样就可以达到鼠标放到图片上时,是动态图,离开后是静态图的效果...
  • Pyecharts 静态图片输出&ppt中动态图表

    千次阅读 热门讨论 2020-06-30 00:16:13
    用 Echarts 生成的可视化效果非常棒,pyecharts可以与 Python 进行对接,可以在 Python 中直接使用数据生成图表。 至于怎么使用pyecharts 我这边就不多说了,官方的api:https://pyecharts.org/#/zh-cn/intro上面...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 738,586
精华内容 295,434
关键字:

为什么动态图保存变静态