精华内容
下载资源
问答
  • rust-autograd:Rust中的张量微分运算(例如TensorFlow)
  • 1张量 张量可以使用GPU加速,可以自动将python内置数据类型转换为张量张量有形状和数据类型。张量与numpy主要区别为:1张量可以用GPU加速2张量不可变。 Tensors和Numpy ndarrays可以自动相互转换。Tensors使用....

    1张量

    张量可以使用GPU加速,可以自动将python内置数据类型转换为张量。张量有形状和数据类型。张量与numpy主要区别为:1张量可以用GPU加速2张量不可变。

    Tensors和Numpy ndarrays可以自动相互转换。Tensors使用.numpy()方法可以显示转换为ndarray。这种转换让Tensors和ndarray共享了底层内存。Tensors可能在GPU内存可是Numpy总是在cpu内存,转换涉及从GPU到主机内存的复制。

    import numpy as np
    
    ndarray = np.ones([3, 3])
    
    print("TensorFlow operations convert numpy arrays to Tensors automatically")
    tensor = tf.multiply(ndarray, 42)
    print(tensor)
    
    
    print("And NumPy operations convert Tensors to numpy arrays automatically")
    print(np.add(tensor, 1))
    
    print("The .numpy() method explicitly converts a Tensor to a numpy array")
    print(tensor.numpy())
    

      

    TensorFlow operations convert numpy arrays to Tensors automatically
    tf.Tensor(
    [[42. 42. 42.]
     [42. 42. 42.]
     [42. 42. 42.]], shape=(3, 3), dtype=float64)
    And NumPy operations convert Tensors to numpy arrays automatically
    [[43. 43. 43.]
     [43. 43. 43.]
     [43. 43. 43.]]
    The .numpy() method explicitly converts a Tensor to a numpy array
    [[42. 42. 42.]
     [42. 42. 42.]
     [42. 42. 42.]]
    

    Tensorflow自动决定是否使用GPU来加速运算。Tensor.device提供了设备信息。可以显示设置执行计算的设备。

    Dateset:

    tf.data.Dataset将数据喂给算法,迭代简单。Dataset.from_tensors,Dataset.from_tensor_slices,TextLineDataset,TFRecordDataset可以创建dataset。map,batch,shuffle用来对dataset转换处理。dataset可以直接用于迭代(如for循环)。

    ds_tensors = tf.data.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6])
    
    # Create a CSV file
    import tempfile
    _, filename = tempfile.mkstemp()
    
    with open(filename, 'w') as f:
      f.write("""Line 1
    Line 2
    Line 3
      """)
    
    ds_file = tf.data.TextLineDataset(filename)
    
    ds_tensors = ds_tensors.map(tf.square).shuffle(2).batch(2)
    
    ds_file = ds_file.batch(2)
    
    
    print('Elements of ds_tensors:')
    for x in ds_tensors:
      print(x)
    
    print('\nElements in ds_file:')
    for x in ds_file:
      print(x)
    
    
    Elements of ds_tensors:
    tf.Tensor([4 1], shape=(2,), dtype=int32)
    tf.Tensor([ 9 16], shape=(2,), dtype=int32)
    tf.Tensor([25 36], shape=(2,), dtype=int32)
    
    Elements in ds_file:
    tf.Tensor([b'Line 1' b'Line 2'], shape=(2,), dtype=string)
    tf.Tensor([b'Line 3' b'  '], shape=(2,), dtype=string)

     2自动微分

    利用张量类型在描述清楚变量之间关系,TensorFlow自动计算变量的微分和梯度。tf.GradientTape用来计算自动微分。

    x = tf.ones((2, 2))
    
    with tf.GradientTape() as t:
      t.watch(x)
      y = tf.reduce_sum(x)
      z = tf.multiply(y, y)
    
    # Derivative of z with respect to the original input tensor x
    dz_dx = t.gradient(z, x)
    for i in [0, 1]:
      for j in [0, 1]:
        assert dz_dx[i][j].numpy() == 8.0

    也可对中间变量进行微分:

    x = tf.ones((2, 2))
    
    with tf.GradientTape() as t:
      t.watch(x)
      y = tf.reduce_sum(x)
      z = tf.multiply(y, y)
    
    # Use the tape to compute the derivative of z with respect to the
    # intermediate value y.
    dz_dy = t.gradient(z, y)
    assert dz_dy.numpy() == 8.0

    默认只要调用gradient方法,GradientTape持有的资源就会被释放。为了对多个变量进行微分,需要进行tf.GradientTape(persistent=True)设置。如:

    x = tf.constant(3.0)
    with tf.GradientTape(persistent=True) as t:
      t.watch(x)
      y = x * x
      z = y * y
    dz_dx = t.gradient(z, x)  # 108.0 (4*x^3 at x = 3)
    dy_dx = t.gradient(y, x)  # 6.0
    del t  # Drop the reference to the tape

    可以对导数再进行求导(二阶导数):

    x = tf.Variable(1.0)  # Create a Tensorflow variable initialized to 1.0
    
    with tf.GradientTape() as t:
      with tf.GradientTape() as t2:
        y = x * x * x
      # Compute the gradient inside the 't' context manager
      # which means the gradient computation is differentiable as well.
      dy_dx = t2.gradient(y, x)
    d2y_dx2 = t.gradient(dy_dx, x)
    
    assert dy_dx.numpy() == 3.0
    assert d2y_dx2.numpy() == 6.0

    3自定义训练

    线性拟合实例。

    class Model(object):
      def __init__(self):
        # Initialize variable to (5.0, 0.0)
        # In practice, these should be initialized to random values.
        self.W = tf.Variable(5.0)
        self.b = tf.Variable(0.0)
    
      def __call__(self, x):
        return self.W * x + self.b
    
    model = Model()
    
    assert model(3.0).numpy() == 15.0
    
    def loss(predicted_y, desired_y):  #定义损失函数
      return tf.reduce_mean(tf.square(predicted_y - desired_y))
    
    TRUE_W = 3.0
    TRUE_b = 2.0
    NUM_EXAMPLES = 1000
    
    inputs  = tf.random_normal(shape=[NUM_EXAMPLES])
    noise   = tf.random_normal(shape=[NUM_EXAMPLES])
    outputs = inputs * TRUE_W + TRUE_b + noise #有噪声的y值
    
    import matplotlib.pyplot as plt  #画出预测值和真实值图
    
    plt.scatter(inputs, outputs, c='b')
    plt.scatter(inputs, model(inputs), c='r')
    plt.show()
    
    print('Current loss: '),
    print(loss(model(inputs), outputs).numpy())

    def train(model, inputs, outputs, learning_rate):  #更新权重
      with tf.GradientTape() as t:
        current_loss = loss(model(inputs), outputs)
      dW, db = t.gradient(current_loss, [model.W, model.b])
      model.W.assign_sub(learning_rate * dW)
      model.b.assign_sub(learning_rate * db)

    model = Model()

    # Collect the history of W-values and b-values to plot later
    Ws, bs = [], []
    epochs = range(10)
    for epoch in epochs: #迭代10次
      Ws.append(model.W.numpy())
      bs.append(model.b.numpy())
      current_loss = loss(model(inputs), outputs)

      train(model, inputs, outputs, learning_rate=0.1)
      print('Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f' %
            (epoch, Ws[-1], bs[-1], current_loss))

    # Let's plot it all
    plt.plot(epochs, Ws, 'r',
             epochs, bs, 'b')
    plt.plot([TRUE_W] * len(epochs), 'r--',
             [TRUE_b] * len(epochs), 'b--')
    plt.legend(['W', 'b', 'true W', 'true_b'])
    plt.show()

    #随着迭代次数增加,loss越来越少
    Epoch  0: W=5.00 b=0.00, loss=9.36370
    Epoch  1: W=4.59 b=0.42, loss=6.28548
    Epoch  2: W=4.26 b=0.75, loss=4.34777
    Epoch  3: W=4.00 b=1.01, loss=3.12800
    Epoch  4: W=3.80 b=1.22, loss=2.36017
    Epoch  5: W=3.64 b=1.39, loss=1.87683
    Epoch  6: W=3.51 b=1.52, loss=1.57257
    Epoch  7: W=3.40 b=1.62, loss=1.38104
    Epoch  8: W=3.32 b=1.70, loss=1.26047
    Epoch  9: W=3.26 b=1.77, loss=1.18457


     

    4keras

    Keras是一种高级抽象的API。

    # In the tf.keras.layers package, layers are objects. To construct a layer,
    # simply construct the object. Most layers take as a first argument the number
    # of output dimensions / channels.
    layer = tf.keras.layers.Dense(100)
    # The number of input dimensions is often unnecessary, as it can be inferred
    # the first time the layer is used, but it can be provided if you want to 输入可选
    # specify it manually, which is useful in some complex models.
    layer = tf.keras.layers.Dense(10, input_shape=(None, 5))

    层的类型包括Dense(全连接层),Conv2D,LSTM,BatchNormalization,Dropout。

    # To use a layer, simply call it.
    layer(tf.zeros([10, 5]))  #提供输入,开始计算
    
    <tf.Tensor: id=29, shape=(10, 10), dtype=float32, numpy=
    array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>
    
    # Layers have many useful methods. For example, you can inspect all variables
    # in a layer using `layer.variables` and trainable variables using
    # `layer.trainable_variables`. In this case a fully-connected layer
    # will have variables for weights and biases.
    layer.variables  #权重包括w和b
    
    [<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy=
     array([[ 0.24547583, -0.18174013, -0.517627  , -0.28625917,  0.46390074,
             -0.11792481,  0.25204027, -0.10177726, -0.6321482 ,  0.11025655],
            [-0.04801774,  0.05520332, -0.61134326, -0.380753  , -0.20825565,
              0.44676226,  0.34310788, -0.18177858, -0.0399543 , -0.6257955 ],
            [-0.09570092,  0.2136836 ,  0.11427677,  0.45249623,  0.02414119,
              0.2739644 , -0.5701976 , -0.28433737,  0.1094352 , -0.26321137],
            [ 0.6225632 ,  0.56247157,  0.14319342, -0.27484533,  0.06545639,
             -0.14055312,  0.02605182, -0.17947513, -0.43184835, -0.13298517],
            [ 0.21471226, -0.34008306,  0.13555825, -0.20253879, -0.14976257,
              0.24820238,  0.4052704 , -0.42966282,  0.46730322,  0.5801386 ]],
           dtype=float32)>,
     <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>]  

    class MyDenseLayer(tf.keras.layers.Layer):  #封装
      def __init__(self, num_outputs):
        super(MyDenseLayer, self).__init__() #调用基类构造函数
        self.num_outputs = num_outputs

      def build(self, input_shape):
        self.kernel = self.add_variable("kernel",
                                        shape=[int(input_shape[-1]),
                                               self.num_outputs])

      def call(self, input):
        return tf.matmul(input, self.kernel)

    layer = MyDenseLayer(10)
    print(layer(tf.zeros([10, 5])))
    print(layer.trainable_variables)


    tf.Tensor(
    [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]], shape=(10, 10), dtype=float32)
    [<tf.Variable 'my_dense_layer/kernel:0' shape=(5, 10) dtype=float32, numpy=
    array([[ 0.40808827,  0.31189376, -0.6253119 , -0.5369232 , -0.6137004 ,
            -0.5453413 , -0.27606115, -0.60044795,  0.42764622,  0.25756943],
           [ 0.11746502,  0.40135378,  0.27446055,  0.12815869,  0.19679207,
             0.118303  , -0.5602601 , -0.3910997 , -0.50229526, -0.4392987 ],
           [-0.11431473,  0.30567902, -0.42285785,  0.41714746,  0.54528743,
             0.20401049, -0.0829075 ,  0.4709614 , -0.60372585, -0.45935804],
           [-0.51994437, -0.40799683,  0.306705  ,  0.588075  ,  0.12873381,
            -0.12829626,  0.03449196,  0.5080891 , -0.5090939 ,  0.1574735 ],
           [-0.30528757, -0.3296884 ,  0.168805  ,  0.40543085,  0.46509403,
            -0.52575713, -0.181254  , -0.24681184, -0.37497327, -0.37618726]],
          dtype=float32)>]
     

    将tf.keras.Model 封装,自定义类

    class ResnetIdentityBlock(tf.keras.Model):
      def __init__(self, kernel_size, filters):
        super(ResnetIdentityBlock, self).__init__(name='')
        filters1, filters2, filters3 = filters
    
        self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))
        self.bn2a = tf.keras.layers.BatchNormalization()
    
        self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')
        self.bn2b = tf.keras.layers.BatchNormalization()
    
        self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))
        self.bn2c = tf.keras.layers.BatchNormalization()
    
      def call(self, input_tensor, training=False):  #重载调用运算符
        x = self.conv2a(input_tensor)
        x = self.bn2a(x, training=training)
        x = tf.nn.relu(x)
    
        x = self.conv2b(x)
        x = self.bn2b(x, training=training)
        x = tf.nn.relu(x)
    
        x = self.conv2c(x)
        x = self.bn2c(x, training=training)
    
        x += input_tensor
        return tf.nn.relu(x)
    
    
    block = ResnetIdentityBlock(1, [1, 2, 3])
    print(block(tf.zeros([1, 2, 3, 3])))
    print([x.name for x in block.trainable_variables])
    
    
    tf.Tensor(
    [[[[0. 0. 0.]
       [0. 0. 0.]
       [0. 0. 0.]]
    
      [[0. 0. 0.]
       [0. 0. 0.]
       [0. 0. 0.]]]], shape=(1, 2, 3, 3), dtype=float32)
    ['resnet_identity_block/conv2d/kernel:0', 'resnet_identity_block/conv2d/bias:0', 'resnet_identity_block/batch_normalization/gamma:0', 'resnet_identity_block/batch_normalization/beta:0', 'resnet_identity_block/conv2d_1/kernel:0', 'resnet_identity_block/conv2d_1/bias:0', 'resnet_identity_block/batch_normalization_1/gamma:0', 'resnet_identity_block/batch_normalization_1/beta:0', 'resnet_identity_block/conv2d_2/kernel:0', 'resnet_identity_block/conv2d_2/bias:0', 'resnet_identity_block/batch_normalization_2/gamma:0', 'resnet_identity_block/batch_normalization_2/beta:0']

    更常用的是tf.keras.Sequential函数,将每层逐一写出:

     my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1)),
                                   tf.keras.layers.BatchNormalization(),
                                   tf.keras.layers.Conv2D(2, 1,
                                                          padding='same'),
                                   tf.keras.layers.BatchNormalization(),
                                   tf.keras.layers.Conv2D(3, (1, 1)),
                                   tf.keras.layers.BatchNormalization()])
    my_seq(tf.zeros([1, 2, 3, 3]))
    
    <tf.Tensor: id=514, shape=(1, 2, 3, 3), dtype=float32, numpy=
    array([[[[0., 0., 0.],
             [0., 0., 0.],
             [0., 0., 0.]],
    
            [[0., 0., 0.],
             [0., 0., 0.],
             [0., 0., 0.]]]], dtype=float32)>

     

    6按品种对鸢尾花进行分类

     

    转载于:https://www.cnblogs.com/biwangwang/p/11267173.html

    展开全文
  • 因此这一节课我们有必要去学习一些基础、底层的张量微分运算操作以及明白如何去自定义我们的层。 首先我们要知道我们在前面几个章节所实践的内容格式都是张量形式的。例如,一张图片在Tensorflow模型中...

    因为再后面一些分享的章节的内容很多是基于经典论文的复现了,里面会牵扯到很多自定义的模型及其变换。而这些内容有些是我们的Keras API 无法完成的,例如Resnet的residual block。因此这一节课我们有必要去学习一些基础、底层的张量、微分运算操作以及明白如何去自定义我们的层。

    首先我们要知道我们在前面几个章节所实践的内容格式都是张量形式的。例如,一张图片在Tensorflow模型中表现的形式就是张量。那么什么是张量?它是一个数据容器,通俗来说就是矩阵向任意维度的推广。1维的张量其实就向量,而2维的张量则是我们经常使用的矩阵。在Tensorflow2.0,张量(Tensor)运算可以被GPU或TPU加速,减少我们的运行时间。

    一.Tensor(张量)的基础操作

    1.导入相关库。

    from __future__ import absolute_import,division,print_function
    import tensorflow as tf
    import numpy as np

    2.一些基础操作。

    print(tf.add(1, 2)) #数值相加
    

     

    print(tf.add([1,2],[3,4])) #一维向量相加

    print(tf.square(5)) #数值求平方

    print(tf.reduce_sum([1,2,3])) #全部数相加

    x = tf.matmul([[1]],[[2,3]]) #矩阵相乘
    print(x)

    3.查看向量尺寸、类型。

    print(x.shape)
    print(x.dtype)

    4.Tensor和numpy的ndarray进行转换。在使用tensorflow的tensor运算时,如果有遇到ndarray格式的数据,它会默认将它转为Tensor的形式进行运算。同样地,如果在使用numpy进行运算时,碰到tensor,它也会自动将其转为ndarray进行运算。

    ndarry = np.ones([3,3]) #3x3的ndarray格式的单位矩阵
    tensor = tf.multiply(ndarry,2)
    print(tensor)

    print(np.add(tensor,1))

    二.Tensor(张量)的自动微分

    1.我们可以使用 GradientTape()函数进行自动微分操作求梯度。其中watch函数的作用是跟踪变量,而assert函数则是判断该语句是否有异常。如果语句结果不等,则会报错。我们可以看到我们用gradient函数对z求了微分。而z的函数为y^2,对y求导则是2y,因为y的值又等于x值的和(4),因此最后结果等于2x4=8。

    x = tf.ones((2, 2))
    
    with tf.GradientTape() as t:
        t.watch(x) #跟踪变量
        y = tf.reduce_sum(x)
        z = tf.multiply(y, y)
    
    dz_dy = t.gradient(z, y)
    assert dz_dy.numpy() == 8.0

    2.因为使用了python的with...as函数语句,因此在第一次gradient之后,GradientTape的资源就会释放了。那如果我们要进行两次gradient操作,要怎么办?我们可以通过在GradientTape后加一个persistent(持续)参数来解决,并最后再用del语句释放资源。

    x = tf.constant(3.0) #常数
    with tf.GradientTape(persistent=True) as t: #资源不释放
        t.watch(x) #跟踪变量
        y = x*x
        z = y*y
    dz_dx = t.gradient(z,x)  # z=x^4,求导后为4x^3,x代入则为4*3*3*3=108
    dy_dx = t.gradient(y,x) # y = x^2,求导后为2x,x代入则为2*3=6
    print(dz_dx)
    print(dy_dx)
    del t #释放资源

    3.所求的梯度还可以进行二次微分。

    x = tf.Variable(1.0)
    with tf.GradientTape() as t:
        with tf.GradientTape() as t2:
            y = x*x*x
        dy_dx = t2.gradient(y,x) # y=x^3,求导为3x^2,x代入则为3
    d2y_dx2 = t.gradient(dy_dx,x) #dy_dx为3x^2,求导为6x,x代入则为6
    
    assert dy_dx.numpy() == 3.0
    assert d2y_dx2.numpy() == 6.0

    三.自定义层

    1.这里以一个自定义层的例子来解释怎么自定义我们所需要的层。首先我们需要定义一个类,相关语法可以参照python类语法。其中类中传入的函数是继承了keras的layers层函数,__init__层则是传入一些我们需要的参数,这些参数也是我们在调用时必须传入的。而build函数则规定了一些层输入的尺寸大小。call函数则是前向传播过程。

    class MyDenselayer(tf.keras.layers.Layer):
        def __init__(self,num_outputs):
            super(MyDenselayer,self).__init__()
            self.num_outputs = num_outputs
            
        def build(self,input_shape):
            self.kernel = self.add_variable('kernel',shape=[int(input_shape[-1]),self.num_outputs])
        
        def call(self,input):
            return tf.matmul(input,self.kernel)
    
    layer = MyDenseLayer(10)

     

    以上就是本节的内容。更多相关的操作和应用,我会在后面的系列内容中体现出来,方便大家更进一步理解。谢谢你们的观看和支持!

     

    展开全文
  • 2)Pytorch中的张量:Tensor 3)Pytorch自动微分器:Autograd 4)自定义Autograd 5)Pytorch计算图 6)把计算图打包成layers: nn Module 7)自动梯度更新器:Optim 8)自定义Module 9)动态计算图 总的来说,...

    1)重温Numpy
    2)Pytorch中的张量:Tensor
    3)Pytorch自动微分器:Autograd
    4)自定义Autograd
    5)Pytorch计算图
    6)把计算图打包成layers: nn Module
    7)自动梯度更新器:Optim
    8)自定义Module
    9)动态计算图

    总的来说,Pytorch主要提供了两个主要特征:

    1. 一个n维的张量,与numpy中的array类似,但可以在GPU上运算;
    2. 自动微分机制来训练一个神经网络;

    本文中,会通过一个包含ReLu激活函数的全连接神经网络来作为示例,来解决一个由监督问题。为了简化起见,该网络仅仅包含一个隐藏层,训练数据 (X , y)来自随机产生的数据。这个有监督学习的目标是为了最小化网络输出与真实输出之间的欧式距离。

    重温Numpy

    numpy作为一个科学计算套件想必大家都很熟悉。其主要特征是能够将运算扩展到矩阵形式,并可以通过并行等技术,加速矩阵的运算速度。通过利用numpy中的array,能够实现像matlab中的大规模矩阵快速计算。所不一样的是,matlab正版软件动辄上千美元,而python numpy作为开源套件大大促进了开发者的研究和交流。

    Numpy本身不包含任何与深度学习有关的技术,例如前向传播、计算图、梯度更新等。但完全可以利用numpy构造一个简单的全连接神经网络:

    # -*- coding: utf-8 -*-
    import numpy as np
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random input and output data
    x = np.random.randn(N, D_in)
    y = np.random.randn(N, D_out)
    
    # Randomly initialize weights
    w1 = np.random.randn(D_in, H)
    w2 = np.random.randn(H, D_out)
    
    learning_rate = 1e-6
    for t in range(500):
        # Forward pass: compute predicted y
        h = x.dot(w1)
        h_relu = np.maximum(h, 0)
        y_pred = h_relu.dot(w2)
    
        # Compute and print loss
        loss = np.square(y_pred - y).sum()
        print(t, loss)
    
        # Backprop to compute gradients of w1 and w2 with respect to loss
        grad_y_pred = 2.0 * (y_pred - y)
        grad_w2 = h_relu.T.dot(grad_y_pred)
        grad_h_relu = grad_y_pred.dot(w2.T)
        grad_h = grad_h_relu.copy()
        grad_h[h < 0] = 0
        grad_w1 = x.T.dot(grad_h)
    
        # Update weights
        w1 -= learning_rate * grad_w1
        w2 -= learning_rate * grad_w2

    网络输出与真实输出的距离代价如下:

    看起来效果似乎很不错。但是现代许多技术中需要使用GPU大量并行计算,提高速度,很遗憾,numpy不能在支持在一些GPU运算平台如CUDA上并行计算。

    Pytorch中最为重要的一个技术,Tensor,便解决了该问题。

    Pytorch:Tensors

    大家可以把Pytorch中的Tensor理解为可以在GPU上计算的Numpy array。相比array,tensor提供了其他的一些与深度学习息息相关的操作,如:tensor可以追踪一个计算图和梯度。

    上面用numpy手写的全连接神经网络可以用tensor重写为:

    # -*- coding: utf-8 -*-
    
    import torch
    
    #此处不同,需要在运算前,指定运算的设备, CPU or GPU?
    dtype = torch.float
    device = torch.device("cpu")      # 在CPU上运行
    # device = torch.device("cuda:0") # 在GPU上运行
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random input and output data
    x = torch.randn(N, D_in, device=device, dtype=dtype) #定义好tensor后,需要指定设备
    y = torch.randn(N, D_out, device=device, dtype=dtype)
    
    # Randomly initialize weights
    w1 = torch.randn(D_in, H, device=device, dtype=dtype)
    w2 = torch.randn(H, D_out, device=device, dtype=dtype)
    
    learning_rate = 1e-6
    for t in range(500):
        # Forward pass: compute predicted y
        h = x.mm(w1)
        h_relu = h.clamp(min=0)
        y_pred = h_relu.mm(w2)
    
        # Compute and print loss
        loss = (y_pred - y).pow(2).sum().item()
        if t % 100 == 99:
            print(t, loss)
    
        # Backprop to compute gradients of w1 and w2 with respect to loss
        grad_y_pred = 2.0 * (y_pred - y)
        grad_w2 = h_relu.t().mm(grad_y_pred)
        grad_h_relu = grad_y_pred.mm(w2.t())
        grad_h = grad_h_relu.clone()
        grad_h[h < 0] = 0
        grad_w1 = x.t().mm(grad_h)
    
        # Update weights using gradient descent
        w1 -= learning_rate * grad_w1
        w2 -= learning_rate * grad_w2

    Autograd

    上面的例子中,我们手算出了神经网络反向传播的梯度,这对简单的网络当然可以,但当神经网络稍微复杂一些,手动计算梯度的复杂程度就大大增加。

    幸运的是pytorch提供给了我们自动计算梯度的套件。

    autograd是pytorch中的一个套件 ,当使用autograd时,神经网络的前向传播会定义一个计算图(computational graph),该图中,node是tensor,edge是函数(produce output from input);在这个graph中进行反向传播可以很容易计算梯度。

    具体的,如果x是一个tensor,也是graph中的一个node,如果有:x.requires_grad=True,那么x.grad将会是另一个tensor,其中包含了求导后的梯度值

    我们使用autograd的套件重写上述代码:

    # -*- coding: utf-8 -*-
    import torch
    
    dtype = torch.float
    device = torch.device("cpu")
    # device = torch.device("cuda:0") # Uncomment this to run on GPU
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random Tensors to hold input and outputs.
    # Setting requires_grad=False indicates that we do not need to compute gradients
    # with respect to these Tensors during the backward pass.
    x = torch.randn(N, D_in, device=device, dtype=dtype)
    y = torch.randn(N, D_out, device=device, dtype=dtype)
    
    # Create random Tensors for weights.
    # Setting requires_grad=True indicates that we want to compute gradients with
    # respect to these Tensors during the backward pass.
    # w1 w2是我们想更新的网络参数,反向求导是loss对w1 w2求导,所以对谁求导或者说想要更新
    # 哪个tensor,那么这个tensor的requires_grad=True
    w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
    w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
    
    learning_rate = 1e-6
    for t in range(500):
        # Forward pass: compute predicted y using operations on Tensors; these
        # are exactly the same operations we used to compute the forward pass using
        # Tensors, but we do not need to keep references to intermediate values since
        # we are not implementing the backward pass by hand.
        # 我们可以通过autograd的机制直接反向求导,不用在保留中间值,所以上面代码前向传播的三步
        # 可以直接合起来
        y_pred = x.mm(w1).clamp(min=0).mm(w2)
    
        # Compute and print loss using operations on Tensors.
        # Now loss is a Tensor of shape (1,)
        # loss.item() gets the scalar value held in the loss.
        # 注意的是,使用autograd机制时,需要保证待求导的tensor(这里是loss)维度为1,
        # 也就是其必须是标量
        loss = (y_pred - y).pow(2).sum()
        if t % 100 == 99:
            print(t, loss.item())
    
        # Use autograd to compute the backward pass. This call will compute the
        # gradient of loss with respect to all Tensors with requires_grad=True.
        # After this call w1.grad and w2.grad will be Tensors holding the gradient
        # of the loss with respect to w1 and w2 respectively.
        # 调用autograd机制中的backward方法,就可以自动完成反向求导
        # w1.grad w2.grad两个tensor中分别存储了loss对w1和w2的梯度
        loss.backward()
    
        # Manually update weights using gradient descent. Wrap in torch.no_grad()
        # because weights have requires_grad=True, but we don't need to track this
        # in autograd.
        # An alternative way is to operate on weight.data and weight.grad.data.
        # Recall that tensor.data gives a tensor that shares the storage with
        # tensor, but doesn't track history.
        # You can also use torch.optim.SGD to achieve this.
        with torch.no_grad():
            w1 -= learning_rate * w1.grad
            w2 -= learning_rate * w2.grad
    
            # Manually zero the gradients after updating weights
            w1.grad.zero_()
            w2.grad.zero_()
    
    

    定制一个新的autograd函数

    上面可以看出,autograd机制主要提供了两个函数,forwardbackward

    forward从输入tensor计算得到一个输出tensor;backward接受输出tensor相对于某个标量值的梯度,然后进一步计算出输入tensor相对于该同一个标量的梯度

    在pytorch中我们可以通过继承torch.autograd.Function来定制一个自己的自动微分算子

    本节中,通过定制一个自动微分算子来非线性实现ReLU

    # -*- coding: utf-8 -*-
    import torch
    
    
    class MyReLU(torch.autograd.Function):
        """
        We can implement our own custom autograd Functions by subclassing
        torch.autograd.Function and implementing the forward and backward passes
        which operate on Tensors.
        """
    
        @staticmethod
        def forward(ctx, input):
            """
            In the forward pass we receive a Tensor containing the input and return
            a Tensor containing the output. ctx is a context object that can be used
            to stash information for backward computation. You can cache arbitrary
            objects for use in the backward pass using the ctx.save_for_backward method.
            """
          # ctx可以用来存储一些与反向计算有关的信息
            ctx.save_for_backward(input)
            return input.clamp(min=0)
    
        @staticmethod
        def backward(ctx, grad_output):
            """
            In the backward pass we receive a Tensor containing the gradient of the loss
            with respect to the output, and we need to compute the gradient of the loss
            with respect to the input.
            """
            input, = ctx.saved_tensors
            grad_input = grad_output.clone()
            grad_input[input < 0] = 0
            return grad_input
    
    
    dtype = torch.float
    device = torch.device("cpu")
    # device = torch.device("cuda:0") # Uncomment this to run on GPU
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random Tensors to hold input and outputs.
    x = torch.randn(N, D_in, device=device, dtype=dtype)
    y = torch.randn(N, D_out, device=device, dtype=dtype)
    
    # Create random Tensors for weights.
    w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
    w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
    
    learning_rate = 1e-6
    for t in range(500):
        # To apply our Function, we use Function.apply method. We alias this as 'relu'.
        relu = MyReLU.apply
    
        # Forward pass: compute predicted y using operations; we compute
        # ReLU using our custom autograd operation.
        y_pred = relu(x.mm(w1)).mm(w2)
    
        # Compute and print loss
        loss = (y_pred - y).pow(2).sum()
        if t % 100 == 99:
            print(t, loss.item())
    
        # Use autograd to compute the backward pass.
        loss.backward()
    
        # Update weights using gradient descent
        with torch.no_grad():
            w1 -= learning_rate * w1.grad
            w2 -= learning_rate * w2.grad
    
            # Manually zero the gradients after updating weights
            w1.grad.zero_()
            w2.grad.zero_()

    Pytorch:nn module——把计算图打包成layer

    虽然计算图可以定义许多复杂运算算子,自动求导,但是对于大规模神经网络,这种仅仅用计算图求解的方法还是太简陋。

    和tensorflow与keras中相似,为了解决这个问题,pytorch考虑将计算图中的一些计算打包进layer里,layer里有一些待学习的参数,这些参数将会在后面基于梯度信息更新。这个layer在pytorch里通过nnmodule实现。

    nn module中包含了一些必要的layer,从input计算出output;除此,还包含一些有用的loss function可以直接调用。接下来,我们将调用nn module重写上面的模型:

    # -*- coding: utf-8 -*-
    import torch
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random Tensors to hold inputs and outputs
    x = torch.randn(N, D_in)
    y = torch.randn(N, D_out)
    
    # Use the nn package to define our model as a sequence of layers. nn.Sequential
    # is a Module which contains other Modules, and applies them in sequence to
    # produce its output. Each Linear Module computes output from input using a
    # linear function, and holds internal Tensors for its weight and bias.
    model = torch.nn.Sequential(
        torch.nn.Linear(D_in, H),
        torch.nn.ReLU(),
        torch.nn.Linear(H, D_out),
    )
    
    # The nn package also contains definitions of popular loss functions; in this
    # case we will use Mean Squared Error (MSE) as our loss function.
    loss_fn = torch.nn.MSELoss(reduction='sum')
    
    learning_rate = 1e-4
    for t in range(500):
        # Forward pass: compute predicted y by passing x to the model. Module objects
        # override the __call__ operator so you can call them like functions. When
        # doing so you pass a Tensor of input data to the Module and it produces
        # a Tensor of output data.
        y_pred = model(x)
    
        # Compute and print loss. We pass Tensors containing the predicted and true
        # values of y, and the loss function returns a Tensor containing the
        # loss.
        loss = loss_fn(y_pred, y)
        if t % 100 == 99:
            print(t, loss.item())
    
        # Zero the gradients before running the backward pass.
        model.zero_grad()
    
        # Backward pass: compute gradient of the loss with respect to all the learnable
        # parameters of the model. Internally, the parameters of each Module are stored
        # in Tensors with requires_grad=True, so this call will compute gradients for
        # all learnable parameters in the model.
        # 和上面通过构建计算图训练网络不同,这里nn module初始化已经将待学习的参数的 
        # requires_grad=True
        loss.backward()
    
        # Update the weights using gradient descent. Each parameter is a Tensor, so
        # we can access its gradients like we did before.
        with torch.no_grad():
            for param in model.parameters():
                param -= learning_rate * param.grad

    Pytorch: optim

    直到目前为止,我们在计算出参数的更新梯度后,仍然是手动地对参数进行梯度下降更新。这对一些简单的网络,且使用SGD等优化方法的模型仍然能起作用。但是当模型较复杂,况且考虑到当下更多使用诸如Adarad、RMSProp等更加高级的优化方法,手动进行梯度更新不太可能。

    所幸,Pytorch提供了optim package,该包里包含了许多定义好的优化器(optimizer),可以在autograd自动获得更新梯度的基础上,进一步使用optim自动对参数进行更新。

    本节将在上节定义的nn module基础上,使用optim进行自动更新梯度:

    # -*- coding: utf-8 -*-
    import torch
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random Tensors to hold inputs and outputs
    x = torch.randn(N, D_in)
    y = torch.randn(N, D_out)
    
    # Use the nn package to define our model and loss function.
    model = torch.nn.Sequential(
        torch.nn.Linear(D_in, H),
        torch.nn.ReLU(),
        torch.nn.Linear(H, D_out),
    )
    loss_fn = torch.nn.MSELoss(reduction='sum')
    
    # Use the optim package to define an Optimizer that will update the weights of
    # the model for us. Here we will use Adam; the optim package contains many other
    # optimization algoriths. The first argument to the Adam constructor tells the
    # optimizer which Tensors it should update.
    learning_rate = 1e-4
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
    for t in range(500):
        # Forward pass: compute predicted y by passing x to the model.
        y_pred = model(x)
    
        # Compute and print loss.
        loss = loss_fn(y_pred, y)
        if t % 100 == 99:
            print(t, loss.item())
    
        # Before the backward pass, use the optimizer object to zero all of the
        # gradients for the variables it will update (which are the learnable
        # weights of the model). This is because by default, gradients are
        # accumulated in buffers( i.e, not overwritten) whenever .backward()
        # is called. Checkout docs of torch.autograd.backward for more details.
        # 在反向传播前,一定要用optimizer对象中的zero_grad方法,对参数初始化;
        # 因为只要backward方法被调用,梯度会在缓存中累计
        optimizer.zero_grad()
    
        # Backward pass: compute gradient of the loss with respect to model
        # parameters
        loss.backward()
    
        # Calling the step function on an Optimizer makes an update to its
        # parameters
        optimizer.step()

    这样以来,一个全连接神经网络中最重要的三个环节:前向计算(forward)、反向传播(backward)、梯度更新(update grad)分别由pytorch中的三个package实现:nn moudule、 backward()、optimizer.step()整个代码的编写变得非常轻量

    Pytorch:Custom nn Module 自定义模型

    许多研究中,我们想用搭建的模型可能比较复杂,不像sequential这样简单,可能是多条线各种结构。pytorch允许通过继承nn module来重新定制自己的模型。

    在此,我们通过继承nn Module的方法重新自定义上述代码中的Sequential模型:

     -*- coding: utf-8 -*-
    import torch
    
    
    class TwoLayerNet(torch.nn.Module):
        def __init__(self, D_in, H, D_out):
            """
            In the constructor we instantiate two nn.Linear modules and assign them as
            member variables.
            """
            # 初始化时,需要定义layer的形式,并将其属性化
            super(TwoLayerNet, self).__init__()
            self.linear1 = torch.nn.Linear(D_in, H)
            self.linear2 = torch.nn.Linear(H, D_out)
    
        def forward(self, x):
            """
            In the forward function we accept a Tensor of input data and we must return
            a Tensor of output data. We can use Modules defined in the constructor as
            well as arbitrary operators on Tensors.
            """
            h_relu = self.linear1(x).clamp(min=0)
            y_pred = self.linear2(h_relu)
            return y_pred
    
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random Tensors to hold inputs and outputs
    x = torch.randn(N, D_in)
    y = torch.randn(N, D_out)
    
    # Construct our model by instantiating the class defined above
    model = TwoLayerNet(D_in, H, D_out)
    
    # Construct our loss function and an Optimizer. The call to model.parameters()
    # in the SGD constructor will contain the learnable parameters of the two
    # nn.Linear modules which are members of the model.
    criterion = torch.nn.MSELoss(reduction='sum')
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
    for t in range(500):
        # Forward pass: Compute predicted y by passing x to the model
        y_pred = model(x)
    
        # Compute and print loss
        loss = criterion(y_pred, y)
        if t % 100 == 99:
            print(t, loss.item())
    
        # Zero gradients, perform a backward pass, and update the weights.
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    Pytorch: 动态计算图

    和tensorflow、keras、caffe等框架很大的一个不同是,pytorch支持动态的计算图

    一般而言,tensorflow、keras、caffe等框架在我们定义好模型(计算图结构后),就不允许我们再进行更改,或者调整了。相反,pytorch允许计算图调整。除此,为了支持动态计算图,pytroch还有权重共享等机制。

    作为动态图和权重共享的示例,我们实现了一个非常奇怪的模型:一个完全连接的ReLU网络,该网络在每个前向传递中选择1到4之间的随机数,并使用那么多隐藏层,多次重复使用相同的权重计算最里面的隐藏层。

    对于此模型,我们可以使用常规的Python流控制来实现循环,并且可以通过在定义前向传递时简单地多次重复使用同一模块来实现最内层之间的权重共享。

    # -*- coding: utf-8 -*-
    import random
    import torch
    
    
    class DynamicNet(torch.nn.Module):
        def __init__(self, D_in, H, D_out):
            """
            In the constructor we construct three nn.Linear instances that we will use
            in the forward pass.
            """
            super(DynamicNet, self).__init__()
            self.input_linear = torch.nn.Linear(D_in, H)
            self.middle_linear = torch.nn.Linear(H, H)
            self.output_linear = torch.nn.Linear(H, D_out)
    
        def forward(self, x):
            """
            For the forward pass of the model, we randomly choose either 0, 1, 2, or 3
            and reuse the middle_linear Module that many times to compute hidden layer
            representations.
    
            Since each forward pass builds a dynamic computation graph, we can use normal
            Python control-flow operators like loops or conditional statements when
            defining the forward pass of the model.
    
            Here we also see that it is perfectly safe to reuse the same Module many
            times when defining a computational graph. This is a big improvement from Lua
            Torch, where each Module could be used only once.
            """
            h_relu = self.input_linear(x).clamp(min=0)
            for _ in range(random.randint(0, 3)):
                h_relu = self.middle_linear(h_relu).clamp(min=0)
            y_pred = self.output_linear(h_relu)
            return y_pred
    
    
    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H, D_out = 64, 1000, 100, 10
    
    # Create random Tensors to hold inputs and outputs
    x = torch.randn(N, D_in)
    y = torch.randn(N, D_out)
    
    # Construct our model by instantiating the class defined above
    model = DynamicNet(D_in, H, D_out)
    
    # Construct our loss function and an Optimizer. Training this strange model with
    # vanilla stochastic gradient descent is tough, so we use momentum
    criterion = torch.nn.MSELoss(reduction='sum')
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
    for t in range(500):
        # Forward pass: Compute predicted y by passing x to the model
        y_pred = model(x)
    
        # Compute and print loss
        loss = criterion(y_pred, y)
        if t % 100 == 99:
            print(t, loss.item())
    
        # Zero gradients, perform a backward pass, and update the weights.
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    展开全文
  • 自动微分 I.引言 张量网络是研究经典统计物理和量子多体物理问题的重要方法[1-3]。近年来,它的应用迅速扩展到不同的领域,包括量子电路的仿真与设计[4-7]、量子误差校正[8,9]、机器学习[10-14]、语言建模[15,16]、...

    本文为文献 Differentiable Programming Tensor Networks 的原创译文,转载请注明出处。
    Author:小岚岚

    I.引言

    张量网络是研究经典统计物理和量子多体物理问题的重要方法[1-3]。近年来,它的应用迅速扩展到不同的领域,包括量子电路的仿真与设计[4-7]、量子误差校正[8,9]、机器学习[10-14]、语言建模[15,16]、量子场论[17-20]、nolographic二象性[21,22]。

    张量网络在一般情况下的优化是众多研究方向的核心问题之一。尽管一维矩阵积状态的优化方案非常成功[23-28],但二维或高维张量网络的优化一直是一个具有挑战性的课题。这个困难部分原因是张量收缩的计算成本高,还要部分原因是在高维情况下缺乏有效的优化方案。对于无限平动不变量子系统,优化张量网络状态的挑战尤其紧迫。在这些情况下,同一个张量以多种方式影响变分能量,从而导致高度非线性的优化问题。基于近似虚时算符的优化方案[29-33]努力解决目标函数中的非局部依赖性。文献[34,35]采用基于梯度的优化,结果明显改善。
    然而,即使对于一个简单的物理哈密顿量,从解析的角度推导张量网络状态梯度也很麻烦,而且容易出错。这一事实限制了基于梯度的张量网络状态优化在更复杂系统中的广泛应用。替代方法如使用数值导数计算梯度,精度和效率有限;因此,它们只适用于变化参数较少的情况[36,37]。使用链式法则手动推导梯度只适用于精心设计的简单张量网络结构[38]。

    可微编程通过以完全可微的方式组合整个张量网络程序并使用自动微分来优化它们,为这些问题提供了优雅而有效的解决方案。在本文中,我们提出基本的自动微分技术,计算张量网络程序的(高阶)导数有效地数值精度。这一进展为一般情况下基于梯度(甚至基于hessian)的张量网络状态优化打开了大门。此外,计算张量网络算法输出的(高阶)梯度提供了一个简单的方法来计算物理量,如比热和磁化率。可微规划方法不知道详细的格点几何、哈密顿量和张量网络收缩方案。因此,该方法的通用性足以支持广泛的张量网络应用。我们的重点是涉及二维无限张量网络的应用,与传统方法相比,可微规划技术提供了显著的好处。我们证明,在通过奇异值分解(SVD)解决数值稳定微分和不动点迭代的内存高效实现等重大技术挑战后,可以获得张量网络状态变分优化的最先进结果。
    本文的组织结构如下。在第二节中,我们在张量网络算法的背景下引入自动微分,并将张量网络收缩表示为计算图。在第三节中,我们给出了张量网络算法的稳定和可扩展可微规划的关键技术。在第四节中,我们证明了该方法的能力,并将其应用于无限正方晶格上的经典Ising模型和量子海森堡模型。最后,我们在第五节对未来的研究方向进行了展望。我们的代码实现是公开的(参见参考[39])。

    II.一般理论

    A.自动微分

    通过计算图自动微分是一个统一的框架,涵盖了机器学习训练神经网络,优化量子物理张量网络等等。我们首先回顾自动微分的核心思想,然后解释它在以计算图形式表述的各种张量网络收缩算法中的应用。
    自动微分机械地计算用计算机程序[40]表示的计算过程的导数。与数值微分不同,自动微分计算的导数值精确到机器精度。其性能有、普适的理论保证,不超过原程序的算法复杂度[41,42]。
    在这里插入图片描述

    图1所示。计算图的反向自动微分。黑色箭头表示从输入到输出的正向评价函数。红色箭头表示伴随反向传播的后退步骤。(a)链图。(b)一个更一般的计算图。在向后传递过程中,给定节点的伴随点根据公式(2)计算。

    自动微分是现代深度学习应用的计算引擎[43,44]。在量子最优控制[45]和量子化学计算中也有应用,如计算力[46],优化基参数[47]等。自动微分的中心是计算图的概念。计算图是由基本计算步骤组成的有向无环图。图的节点表示数据,数据可以是标量、向量,矩阵,或者张量[48]。图连通性表示计算过程中数据流的依赖性。最简单的计算图是图1(a)所示的链。例如,从输入参数向量theta开始,可以计算一系列中间结果,直到最终的输出L,我们假设它是一个标量。所谓的正向求值就是按顺序遍历链图,即θT1...TnL\theta \rightarrow T^1\rightarrow...\rightarrow T^n \rightarrow \mathcal{L}
    为了计算目标函数相对于输入参数的梯度,我们可以利用链式法则
    Lθ=LTnTnTn1...T2T1T1θ.(1) \frac{\partial \mathcal{L} }{\partial \theta}= \frac{\partial \mathcal{L} }{\partial T^n} \frac{\partial T^n }{\partial T^{n-1}}... \frac{\partial T^2 }{\partial T^1} \frac{\partial T^1}{\partial \theta}. (1)
    由于我们考虑的情况是,输入维数大于输出维数,在Eq.(1)中通过使用一系列雅可比向量乘积从左到右相乘来计算梯度是更有效的。根据图1(a)所示的计算图,将图向后遍历,并将梯度信号从输出端传播回输入端。用这种方法计算导数叫做反向自动微分。这种方法通常称为反向传播算法[43],被认为是训练深度神经网络最成功的方法。引入伴随变量Tˉ=L/T\bar T=\partial \mathcal{L} /\partial T 来表示最终输出L相对于变量T的梯度。我们可以看到,反向自动微分将伴随变量从Tn=LˉL/Tn\overline {T^n} =\bar \mathcal{L} \partial \mathcal{L}/\partial T^nLˉ=1\bar \mathcal{L}=1
    一直传播到Ti=Ti+1Ti+1/Ti,i=n1,...1\overline{T^i}=\overline{T^{i+1}}\partial T^{i+1}/\partial{T^i},i=n-1,...1
    最后计算θ=T1T1/θ\overline \theta=\overline {T^1}\partial{T^1}/\partial \theta 。在每一步中,通过局部向量-雅可比矩阵乘积向后传播伴随矩阵。伴随反向传播图可以很好地推广到更复杂的计算图。例如图1(b)中的数据节点Tl通过两条不同的下游计算路径影响最终输出。在向后传递过程中,需要为伴随节点把子节点的所有贡献求和。总的来说反向传播满足
    Ti=j:child of iTjTjTi(2) \overline{T^i}=\sum_{j:child\space of\space i}\overline{T^j}\frac{\partial T^j}{\partial T^i}(2)
    反向自动微分算法可以理解为计算图上的一个消息传递过程。通过前向传播定义的计算图的拓扑排序后(?),从伴随L=1\overline \mathcal{L}=1的输出节点向后来看图。每个节点从它的子节点收集信息来计算它自己的伴随节点,然后将这个信息传递给它的父节点。因此,我们可以在一个前向和一个后向的过程中计算关于所有参数的梯度。通常,在前向传递中缓存必要的信息,以便在后向传递中有效地计算向量-雅可比矩阵乘积。
    可微程序的组成部分称为基元。基元可以是基本操作,如加法、乘法和数学函数[50]。除了普通的前向函数外,每个基元都有一个相关的反向函数。反向函数根据向量雅可比矩阵乘积Eq.(2)来反向传播伴随矩阵。注意,不需要显式实例化或存储完整的雅可比矩阵。此外,人们可以把许多基本计算步骤组合在一起作为一个基元。例如,像矩阵乘法这样的线性代数运算可以被视为一个基元。通过这种方式,这些自定义基元的前向传递可以作为黑盒操作。以这样一种模块化的方式设计微分程序,可以控制自动微分的间隔级别。为特定领域的问题定制基元有几个优点:首先,这可以减少计算和内存成本。其次,在某些情况下,将几个步骤组合在一起在数值上更稳定。第三,可以将对外部库的函数调用包装成基元,而不需要跟踪每个单独的计算步骤。
    现代机器学习框架通过各种机制支持自动微分。例如,TensorFlow[51]使用定制语言显式地构造计算图;autograd[52]、PyTorch[53]、Jax[54]在运行时跟踪程序的执行顺序;Zygote[55]在编译器级执行源代码转换。这些框架允许通过函数调用、控制流、循环和许多其他计算指令来进行微分。在这些基础结构的基础上,可微编程正在成为一种新的编程范式,它强调组装可微组件,并通过端到端优化[44]学习整个程序。让机器机械地处理自动判别问题,大大减少了人类的费力工作,并将人类的注意力重新分配到设计更复杂、更有创造性的深度学习算法上。
    最后,我们注意到Eq.(1)也可以从右到左求值,这对应于前向模式自动微分。前向模自动微分的运算类似于微扰理论。我们可以在一个单一的向前传递过程计算目标函数和梯度,而不存储任何中间结果。然而,前向自动判别对于输入维数远大于输出维数[44]的计算图是不利的。因此,深度学习的大部分工作都采用了反向自动微分。出于同样的原因,我们将重点讨论张量网络程序的反向自动微分。

    B.张量网络收缩的计算图

    张量网络程序将输入张量映射到输出,我们假设输出是一个标量。根据本文,输入量可以表示经典的配分函数或量子波函数,输出可以是各种感兴趣的物理量。在概念上,将自动微分应用于张量网络程序是很简单的,方法是将计算,特别是将张量网络收缩表示为计算图。
    作为一个教学的例子,考虑无限一维Ising模型的配分函数,
    Z=limNTr(TN),T=(eβeβeβeβ) Z = lim_{N\rightarrow \infty}Tr(T^N),T=\begin{pmatrix} e^{\beta} & e^{-\beta} \\ e^{-\beta} & e^{\beta} \end{pmatrix}
    其中T 是二阶的表示玻尔兹曼分布的张量(矩阵)。通过反复平方矩阵T并求迹,可以在热力学极限下数值得到配分函数。计算图为图1(a)所示的简单链结构。对于这种计算过程的微分涉及到通过矩阵跟踪和乘法运算反向传播伴随矩阵,这很简单。
    在这一点上,也值得区分精确和近似的张量网络收缩方案。在树形计算图中张量网络可以被精确高效地收缩。
    然而,其他精确的方法,如用于计数和模拟量子计算的方法[56,57],通常会随着问题的尺寸扩大呈指数级增长。然而,对这些精确的算法应用自动微分是很简单的,因为它们主要涉及张量的重整和收缩。
    我们将重点放在通过近似张量收缩求导的不那么trivial的情况上,这通常涉及截断张量因式分解或变分逼近。它们涵盖了重要的张量网络应用,与其他数值方法相比具有很大的优势[1-3]。特别地,我们对收缩无限张量网络感兴趣,其中的基本数据结构是体张量。压缩方案大致分为三类:基于粗粒化变换的压缩方案[58-64],基于角转移矩阵的压缩方案[65-67],以及基于矩阵乘积状态的压缩方案[2,24,68]。由于最后两种压缩方案与[2]密切相关,下面我们将重点研究张量重正化群(第II b1)和角转移矩阵重正化群方法(第II b2)的自动微分。

    1.张量重整化群

    张量重整化群(TRG)方法通过对体张量的分解和迭代截断[58]来收缩张量网络。图2(a)显示了作为计算图的训练迭代的一个步骤,其中包括(1)使用SVD以两种方式分割体张量,其中-我们将奇异值和向量截断为规定的维度x,(2)将在最后一步中生成的四个3leg张量组装成一个4 leg张量。在这个收缩之后,我们得到了下一个迭代的一个新的张量。
    在这里插入图片描述

    TRG方法使晶格尺寸以指数速度增长。所以,在几十次迭代后很快就会达到热力学极限。注意,为了数值稳定性,我们需要在每次迭代后对张量元素重新进行尺度变换。TRG方法的计算代价尺度为x的六次方,存储代价尺度为x的四次方。展开迭代之后,TGR方法的计算图类似于图1(a)所示的简单链图。在每个迭代步骤中,基本操作是张量指标置换、截断SVD和张量收缩。由于这些操作都是可微的,我们可以通过TRG程序反向传播来计算下游目标函数对输入张量的导数。

    2.角转移矩阵重整化群

    角转移矩阵重正化群(CTMRG)[65]的计算图拓扑结构更有趣。CTMRG计算的目标是获得代表体张量的环境自由度(d.o.f.)的收敛角张量和边张量。
    在大部分情况下张量具有正方晶格的全部对称性,CTMRG一个迭代的步骤图2所示(b):(1)合同大部分张量与角落和边缘张量形成4-leg张量,(2)执行截断奇异值分解4-leg张量,保持单一维度截止x(保持截断奇异矩阵等角投影);(3)从第一步开始,将等距法应用于4-leg张量,求出新的角张量;(4)为下一步应用相同的等距来找到一个新的边张量。迭代这个过程直到收敛。我们可以看到,在CTMRG迭代的每一步中,维度为d的体张量都是相同的。由于这个原因,收敛环境张量将以一种复杂的方式依赖于体张量。
    与[58]训练方法不同,CTMRG方法使系统的大小线性增长。所以,你可能需要迭代更多的步骤才能在CTMRG中达到一致。另一方面,CTMRG的计算复杂度O(dx3)和存储代价O(dx2)在截止键维数上都小于TRG。
    在这里插入图片描述

    图2所示。(a)TRG训练的迭代步骤。(b) CTMRG的迭代步骤。每个张量是计算图中的一个节点。计算图中的基本函数是SVD和张量收缩。

    III.技术成分

    利用反向自动微分来计算张量网络程序的梯度,需要追踪原函数的组成,并将伴随信息在计算图上传回。现代可微编程框架[51-55]已经自动地处理了它们的基本数据结构——可微张量的跟踪和反向传播。
    我们需要关注的是识别出合适的张量网络程序的基元,并为反向传播定义它们的向量-雅可比矩阵乘积。张量网络算法的关键成分是矩阵和张量代数。通过这些操作,已有关于反向传播的既定结果[69-71]。首先,用定制的后向函数将所有BLAS例程包装为基元是很简单的。接下来,尽管不那么简单,但也可以为许多LAPACK例程(如特征解算器、SVD和QR分解)推导后向规则[69]。把这些线性代数运算当作基本运算,就可以组成一个可微程序,并有效地实现矩阵库。然而,可微张量网络程序的稳定和可扩展的实现存在一些实际障碍。首先,特征解和奇异值分解的后向传递可能面临特征值或奇异值简并的数值不稳定。其次,反向自动微分可能会导致大量内存消耗,从而使我们无法达到与普通张量网络程序相同的维度。我们将在下面给出这些问题的解决方案。

    A . 通过代数操作稳定后向传播

    关于矩阵导数我们提出几个关键的结果,涉及线性代数运算与相关的张量网络算法。由于反向自动微分的模块化特性,只需指定本地的后向函数就可以将这些组件集成到可微分程序中。我们评述了它们与物理文献的联系,并特别关注稳定的数值实现[39]。更多信息,可以参考参考资料。

    1.对称eigensolver

    正向传递读取A=UΛUTA=U\Lambda U^T,其中 Λ\Lambda 是“特征值的对角矩阵;正交矩阵U的每一列都是一个对应的特征向量。在计算图中,节点A有两个子节点U和 λ。
    在反向传递中,已知伴随U和A,我们有[69,71]
    A=U[Λ+F(UTUUTU)/2]UT,\overline A=U[\overline \Lambda+F\odot(U^T\overline U-\overline U^T U)/2]U^T,(3)
    whereFij=(λjλi)1F_{ij}=(\lambda_j-\lambda_i)^{-1}if iji\ne jand zero otherwise.

    符号\odot表示元素的阿达玛乘积。我们可以很容易地证明梯度也是一个对称三矩阵。
    式(3)可视为“反向”扰动理论。当下游计算不依赖于本征态时,即U=0\overline U=0,则反向方程与著名的赫尔曼-费曼定理[721]有关,该定理将微扰与哈密顿量及其本征值联系起来。参考文献[73]将公式(3)应用于基于能谱的反向哈密顿设计的特殊情况。
    特征值差出现在F的分母中是一阶非简并微扰理论的提示。参考文献[47]通过特征求解器考虑了反向传播的稳定性,因此转向效率较低的前向自动微分来实现Hartree-Fock基的变分优化。在许多物理问题中,最终的目标函数依赖于本征值和本征态,与规范无关,(?)例如,占据态的二次形式。在这种情况下,F的分母中只会出现占据态与未占据态之间的特征值差异,这是线性响应理论中常见的模式[74]。因此,简并特征值并不一定会对这些物理应用造成问题[75]。在实践中,我们发现,采用洛伦兹展宽1/xx/(x2+ϵ),ϵ=10121/x \rightarrow x/(x^2+\epsilon),\epsilon =10^{-12} 可以使计算稳定,但代价是引入一个小误差的梯度;参见参考文献[71]。

    2. 奇异值分解

    张量网络算法中一个普遍存在的运算是矩阵SVD,它用于张量网络的规范化和因式分解[1,2,25]。前向传递形式为 A=USVTA=USV^T,其中A的尺寸为(m, n),
    U,VTU,V^T的尺寸分别为(m, k)和(k, n), k= min(m, n)。注意,S是一个包含奇异值s的对角矩阵。在反向自动微分中,给定伴随矩阵U,S,V\overline U,\overline S,\overline V,可以得到[70]
    在这里插入图片描述

    否则为0。
    为了防止简并奇异值情况下的数值问题,我们对第一项使用与第III节A 1相同的洛伦兹展宽,这在我们的经验中很有效。在实际应用中,从随机张量开始的变分张量网络计算,得到精确简并特征值的机会很小。即使出现这种情况,四舍五入也是一个合理的解决方案。然而,对于由于内在原因引起的简并情况[60,62,76,77],只要端到端梯度有明确的定义,仍然可以得到正确的梯度。最后,对式(4)中消失的奇异值求逆并不需要考虑,因为对应的空间通常会被截断。

    3.QR因子分解

    QR因子分解常被用于张量网络的正则化。在前向传递中,将A=QR因式分解,其中 QTQ=IQ^TQ=I,R为上三角矩阵[78]。根据矩阵A的维数(m, n),反向函数有两种情况。
    对于矩阵A的输入形状mnm\ge n ,R是一个nxn矩阵。向后传递为[71]
    A=Q+Qcopyltu(M)]RT, (5) \overline A=\overline Q+Qcopyltu(M)]R^{-T},\space (5)
    其中 M=RRTQTQM=R\overline R^T-\overline Q^T Q,copyltu函数通过将输入矩阵的下三角形复制到上三角形来生成对称矩阵:[copyltu(M)]ij=Mmax(i,j),min(i,j)[copyltu(M)]_{ij}=M_{max(i,j),min(i,j)}
    我们可以通过求解一个三角系数矩阵的线性方程组来处理RTR^{-T}的乘法。
    对于m <n的情况,Q的尺寸为(m, m), R是一个mxn矩阵。我们记A=(X, Y)和R= (U, V),其中X和U是大小为(m, m)的满秩方阵。分解可以分为两个步骤:首先,X=QU唯一地决定Q和U,然后计算V=QTYV=Q^TY 。应用链式法则,可以得到逆向法则
    在这里插入图片描述

    B.运用checkpointing函数节省内存的反向自动微分

    张量网络的反向自动微分的直接实现有很大的内存开销,因为需要在前向传递中存储中间结果,以便在后向传递中计算向量雅可比矩阵乘积。存储变量的数量与实现自动区分时的间隔级别有关。在任何情况下,反向模式自动区分的内存消耗将与计算图的深度成比例。这种开销对于具有大bond维度和大量重正化群(RG)迭代的张量网络尤其令人担忧。解决反向自动微分内存问题的方法是一种著名的技术,称为checkpointing[50,79]。其思想是用计算时间交换节省的内存。以式(1)中的链计算图为例,在正向过程中每隔几步就可以存储张量。在向后传递过程中,只要有需要,就通过向前运行一小段计算图来重新计算中间张量。通过这种方式,可以极大地减少内存使用,而不需要超过两倍的计算工作量。从另一个角度来看,checkpointing相当于定义定制的基元,它封装了计算图的很大一部分。这些基元有它们自己的特殊向后规则,在本地再次运行向前传递,然后反向传播伴随函数。因此,在正向传递中,不需要缓存这些checkpointing基元的内部状态。
    checkpointing是应用于任意拓扑结构的计算图。当应用于张量网络算法时,我们可以自然地考虑图2中的重整化步骤作为checkpointing基元。这样,就可以避免在正向传递中存储一些较大的中间张量。

    C. 不动点迭代的反向传播

    不动点迭代是张量网络算法中的一种循环迭代模式。例如,迭代函数Ti+1=f(Ti,θ)T^{i+1}=f(T^i,\theta) 直到达到一个收敛张量T*,并将其用于下游计算。要计算关于参数theta的梯度,当然可以将迭代展开到一个深度计算图,并直接应用反向自动微分。然而,这种方法的缺点是,如果需要很长时间的迭代才能找到不动点,就会消耗大量内存。我们可以利用不动点方程上的隐函数定理来解决这个问题[80]。对T*两边求导,得到
    在这里插入图片描述

    第二行将方括号中的矩阵逆展开为几何级数。因此,要通过一个不动点迭代进行反向传播,基本操作仅涉及向量-雅可比矩阵与单步迭代函数的乘积。在后向函数中,执行迭代来累积伴随函数直到达到收敛。几何级数在不动点的收敛速度与前向迭代相同[80]许多张量网络收缩方案,包括在第二节B 2中回顾的CTMRG方法。属于定点迭代的框架。因此,可以使用Eq.(7)进行CTMRG计算,迭代在图2(b)所示的RG步骤上。我们注意到在参考文献中导出的无限张量网络收缩的解析梯度。[34,35]包含了类似的模式,它是几何级数的求和。
    与第III B节的checkpointing技术类似,Eq.(7)也减少了反向自动微分的内存使用,因为在前向迭代中不需要存储长链的中间结果。此外,由于下游目标函数与不动点张量是如何获得的无关,人们也可以利用前向传播[82]中的加速迭代方式[81]。然而,当将式(7)应用于张量网络RG的微分算法时,有一个警告。人们可能需要特别注意RG迭代中的冗余全局规范,以确保不动点方程确实成立。

    D. 高阶微分

    由于梯度也可以用计算图来表示,所以可以通过对图进行自动微分来计算二阶导数。这样,在原则上,可以计算任意高阶导数的程序使用自动微分[50]。深度学习框架[51-55]对计算高阶导数提供开箱即用的支持计算高阶导数的能力支持基于hessian的张量网络状态优化,如牛顿法[83]。然而,显式地计算和反演完整的Hessian矩阵可能是非常昂贵和不必要的。我们可以通过
    在这里插入图片描述
    而无需显式构造Hessian矩阵[84]。这一过程对于用于牛顿法的迭代线性方程求解器是充分的。

    IV.应用

    我们提出两个应用来证明微分编程张量网络方法在统计物理和量子多体问题中的通用性。公开代码实现[39]采用PyTorch[85]和一个定制的线性代数自动分化图书馆提高数值稳定性(见讨论第三秒。)。然而,我们注意到可以通过深度学习框架如autograd [86], TensorFlow87号,Jax[88],Zygote[89]轻松得出结果。

    A . 自由能高阶微分

    考虑温度倒数为beta的正方格点Ising模型,配分函数可以表示为 bond维度为2的二位张量网络
    在这里插入图片描述

    体张量为
    在这里插入图片描述

    其中 λu=eβ+(1)ueβ\lambda_u=e^{\beta}+(-1)^ue^{-\beta}。(?)我们使用第IIB 1节中讨论的TRG方法收缩无限张量网络。我们使用截断bond维度x=30,并迭代30个TRG步骤。最后,我们通过追踪体积张量得到配分函数Eq.(8)和自由能。
    接下来,我们通过自动微分直接对自由能求导来计算物理可观测值,如能量密度和比热,如图3所示。注意,能量密度显示了一个扭结。比热在临界温度 βc=ln(1+2)/20.44068679\beta_c=ln(1+\sqrt 2)/2 \approx0.44068679附近出现峰值。与数值微分不同,这些结果不受有限差分误差[61,91]的影响。精确计算张量网络算法的高阶导数将有助于研究热相变和量子相变。我们注意到,原则上可以通过直接计算能量方差来获得比热[35,92],但这涉及到用张量网络表示的几何级数的繁琐求和。
    有其他的方法来用自动微分计算比热。例如,可以直接利用杂质张量计算能量,然后求一阶导数得到比热。另外,也可以使用前向模式自动微分,因为只有一个输入参数beta需要微分。我们特意选择了目前的方法,通过反向自动微分技术来突出可微分编程的强大功能。通过反向传播整个TRG过程,特别是SVD,可以使用高阶导数计算物理可观测值。值得注意的是,考虑到Ising模型[47]的Z2对称性导致的许多简并奇异值,这种方法是有效的。为了获得正确的物理结果,使用第IIIA 2节提到的数值稳定方法的SVD后向函数是很关键的。

    B. iPEPS基于梯度的优化

    我们考虑正方晶格的反铁磁海森堡模型的变分研究,哈密顿量为
    在这里插入图片描述

    我们考虑一个iPEPS作为变分波函数,变分参数则是iPEPS中的元素
    在这里插入图片描述

    S 代表物理指标,其他的 u、l、d、r 则是bond维度D的虚拟 dof(环境自由度)。我们用随即高斯分布初始化元素。iPEPS的重叠部分组成一个张量网络,其中体张量是bond维度为D2的双层张量
    在这里插入图片描述

    为了收缩由这个体张量形成的无限张量网络,我们使用在第二节B 2中复习过的CTMRG方法。我们通过部分追踪体张量的leg来初始化角张量和边张量;然后进行CTMRG迭代,直到我们在角落和边缘张量上达到收敛。收缩后,我们可以评估预期能量 ΨHΨ/ΨΨ\langle \Psi\vert H\vert\Psi\rangle/\langle\Psi\vert\Psi\rangle。由于问题的平动不变性,考虑键上的期望能量就足够了。
    在这里插入图片描述

    式(13)中的黑矩形为作用于键上的哈密顿算符。我们在哈密顿函数中做了基旋转这样基态就会有一个单位置的单元(?)。我们用截止bond维度X= 30,50,80,100,144,160来分别表示D = 2.3. …7。由于期望能量随截止维数的减小而减小[36,94],近似的CTMRG收缩给出了基态能量的一个变分上限的期望能量。(13)通过角张量和边张量对式(11)中的变分参数具有显式和隐式的依赖性。
    我们用自动微分法计算式(13)对单层张量式(11)的梯度,自动解决计算图中复杂的内部依赖关系。梯度计算所花费的时间与预期能量的前向估计相当。然后,我们使用带有自动计算梯度的准牛顿L-BFGS算法[83]优化iPEPS。经过几百次的函数和梯度计算后,可以迅速达到最优。图4(a)显示了不同bond维度下与外推量子蒙特卡罗(QMC)结果[95]相比的能量相对误差。基态能量的精度可与最先进的结果相媲美[34,35],它被证明比基于假想时间投影的简单和完全更新算法更精确[29-33][96]。请注意,Ref.[35]中的ansatz和我们的ansatz只包含Ref.[34]中的一半的变分参数,所以在D = 2,3时,能量结果略高于Ref.[34]。然而,对于更大的键维数D= 4,5,6,7,我们的计算达到了无限方格海森堡模型的最低变能。图4(b)为优化状态下测得的交错磁化,在更大的bond维度达到了外推QMC的结果。
    图4所示。(a)二维S=1/2反铁磁海森堡模型与以前变分结果的相对误差[33,35]。精确度是相对于“外推的QMC结果[95]”进行测量的。(b)交错磁化的比较,虚线为外推的QMC结果[95]。简单而完整的更新引用数据也来自Ref.[34]。

    图4所示。(a)二维S=1/2反铁磁海森堡模型与以前变分结果的相对误差[33,35]。精确度是相对于“外推的QMC结果[95]”进行测量的。(b)交错磁化的比较,虚线为外推的QMC结果[95]。简单而完整的更新引用数据也来自Ref.[34]。

    为了获得bond维度D> 4的结果,我们需要使用第三节B中的checkpointing技术或第三节C中的不动点迭代,以保持足够低的内存预算,以容纳一个12G内存的Nvidia P100 GPU卡。令人鼓舞的是,只要付出适度的努力,就可以在不断优化iPEPS时达到最先进的性能[34,35]。这一成功也证明了我们确实可以稳定出现在科学计算[47]中的线性代数运算的反向自动微分。
    我们注意到,目前的方法也适用于有限系统或问题,具有更大的单位细胞,更复杂的哈密顿量[97-100],和更复杂的压缩方案提高效率[94],这有望为量子多体问题提供新的物理结果。

    V . 讨论

    通过自动微分来计算梯度极大地增强了现有张量网络算法的能力。研究者可以专注于核心张量网络收缩算法,而不必担心繁琐的梯度计算。自动微分的计算复杂度与张量网络的正向收缩相同。
    除了大大减少人力投入外,自动微分方法计算的梯度也与参考值略有不同。该方法通过自动微分来计算近似能量密度的精确数值梯度。[34 35]的方法则首先推导能量梯度的解析表达式为无穷张量网络,然后将这些网络近似收缩得到一个近似梯度。因此,这两种方法分别对近似和近似导数[44]求导。除了参考文献[44]的一般建议外,我们发现通过近似张量网络收缩的微分对于那些解析导数推导和近似都很复杂的无穷系统是有利的。
    在本文中,我们主要关注自动微分的高级应用,它通过整个压缩算法来优化张量网络和计算物理可观测值。同样的技术也适用于低级情况,如寻找最优截断基或张量网络的变分变换[64]。此外,该方法除了优化量子问题的期望能量外,还与张量网络的变分收缩有关[2101]。我们期望可微编程技术将成为标准张量网络工具箱中不可或缺的一部分。
    用深度学习框架实现张量网络程序的一个优势为可以轻松使用GPU加速。这一工作的计算是在单个GPU卡上完成的。进一步推进这一研究方向,我们认为在新兴的专业硬件上部署张量网络算法将是有益的。
    最后,对张量网络和神经网络在自动微分方面的区别进行了评述。典型的神经网络体系结构不涉及复杂的线性代数运算。然而,随着张量神经网络的发展[102]和各种张量网络在机器学习问题上的应用[10- 14103],这两类网络之间的边界变得模糊。因此,当一个人转向更复杂的收缩方案时,本文提出的结果也将与张量网络机器学习应用相关。

    展开全文
  • 我们公开了标量张量理论的显着特征,以及标量场与Palatini形式主义中曲率的导数耦合。 使用变形变换,我们证明了该理论没有Ostrogradski鬼魂。 对于两个耦合常数之间的特殊关系,它与最小耦合到标量的爱因斯坦引力在...
  • 应用数学好书,值得专业人士看看,很不错,可以试试. 包括矢量代数,矢函数的微分与积分,场论,曲线坐标系,张量分析,很全面。
  • 因此学习数理统计的微分几何方法与张量方法对理解现代统计与机器学习技术非常有帮助。这个系列的博客从矩阵分析与多元统计II 二次型与二次曲面2 双线性函数介绍的双线性函数开始进行推广得到张量,建立张量的代数与...
  • 张量网络

    千次阅读 2018-06-21 22:36:04
    1. 张量(tensor)张量网络中的张量,和微分几何和GR中的张量场并不完全相同。在微分几何中,一点处的张量是基于流形上该点的切空间的。一个(m,n)型张量是指将m个协变矢量(余切矢量,cotangent vector)和n个逆变...
  • 张量基本知识

    千次阅读 2016-12-10 12:30:44
    为什么引入张量:与坐标系无关的...黎曼几何:黎曼把高斯的非欧几里得几何与凯莱等的n维线性空间理论综合起来,提出了n维晚去空间的概念,并按照微分几何学的思路,认为只要构造出空间的度量形式,就可以确定空间...
  • Pytorch是一个基于Python的机器学习库。它广泛应用于计算机视觉,自然...它主要提供了以下两种核心功能:1,支持GPU加速的张量计算。2,方便优化模型的自动微分机制。Pytorch的主要优点:简洁易懂:Pytorch的API设计...
  • 本书主要内容包括张量基本概念、张量的代数运算和微分学、Riemann流形上的张量分析和微分算子。除此之外,本书用很大的篇幅讲授张量在连续介质力学和物理学中的应用。特别是有许多内容是作者20多年来应用张量分析...
  • 2.] tensor([2., 2., 2., 2., 2.], dtype=torch.float64) 2 自动微分 2.1 张量的自动微分 18.新建一个张量,并设置requires_grad=True x = torch.ones(2, 2, requires_grad=True) print(x) tensor([[1., 1.], [1., ...
  • Gokula Krishnan Santhanam认为,大部分深度学习框架都包含以下五个... 自动微分(Automatic Differentiation)工具 BLAS、cuBLAS、cuDNN等拓展包 . .一、张量的理解 本节主要参考自文章《开发丨深度学习框
  • 本书主要内容包括张量基本概念、张量的代数运算和微分学、Riemann流形上的张量分析和微分算子。除此之外,本书用很大的篇幅讲授张量在连续介质力学和物理学中的应用。特别是有许多内容是作者20多年来应用张量分析...
  • 在本文中,我们将投影仪推导到所有不可约的SO(d)表示形式(无迹线混合对称张量),这些表示形式出现在d维四个应力张量的... 进一步的结果包括将投影仪生成为无痕混合对称张量微分算子和阴影算子的一般归一化常数。
  • 基于结构张量图像建模方法的滤波性能研究,邵文泽,韦志辉,本文关注基于结构张量的图像建模方法,对基于结构张量的偏微分方程(PDE)和变分泛函方法的滤波性能进行了系统的分析研究。基于角形�
  • pytorch 自动微分

    2020-05-01 00:10:09
    pytorch 自动微分: import torch print('创建一个张量,设置 requires_grad=True 来跟踪与它相关的计算') x = torch.ones(2, 2, requires_grad=True) print(x) print('针对张量做一个操作') y = x + 2 print(y) ...
  • Pyorch基础:张量

    2020-04-13 17:14:49
    autograd包对所有在Tensor上的操作提供自动微分。是一个按运行定义的框架。这意味着backprop是由代码的运行方式定义的,并且每个迭代可以是不同的 Tensor torch.Tensor是这个包的核心类。 .requires_grad=True可以...
  • 微分方程计算初步

    2020-07-11 11:04:53
    微分方程计算初步 基本张量表达 在计算流体力学领域数学符号是理解和导出公式的关键部分。不管是人工推导还是计算机执行,在计算偏微分方程之前都需要掌握一些数学计算符号。本文中主要介绍一些必要的数学符号。...
  • 在本文中,我们以类似纪梵塔的方式表达了一些简单的随机张量模型,即微分算子作用于通用1-Hermitian矩阵模型的乘积。 最后,我们得出这些张量模型的Hirota方程。 我们的分解是实现此类模型可集成性的第一步。
  • 张量是19世纪以来比较伟大的数学发明之一,它的发明伴随着微分几何的提出,为在流形上进行各种运算提供了可能。在这里主要讲解度量张量。首先我们来看一看我们需要解决一个什么问题,然后针对这个问题提出一种新的...
  • Killing-Yano张量是服从类似于Killing向量所遵循的一阶微分约束的反对称张量。 在本文中,我们将重点放在共形情况下,对此类对象进行概括。 这些广义的共形Killing-Yano张量是混合对称类型的,服从这样的约束:从由...
  • 主要研究结构张量驱动的变分偏微分方程( variational Partial Differential Equation; variational PDE) 图像建 模方法的滤波性能. 基于角形强度度量和水平线演化理论, 设计了一种具有角点增强性能的角形冲击...
  • 张量入门(Tensor for Beginners)(一)

    千次阅读 2018-08-08 20:51:10
     在维基百科中查得,“张量”这个概念是由格雷戈里奥·里奇-库尔巴斯托罗在1890年在《绝对微分几何》的标题下发展出来的,随着1900年列维-奇维塔的经典文章《绝对微分》(意大利文,随后出版了其他译本)的出版而为...
  • PyTorch中的自动微分

    2021-01-05 21:31:11
    PyTorch中的自动微分目标PyTorch中的自动微分 目标 通过本篇文章的学习,你将对PyTorch中的自动微分过程有一个清晰的认识,本文通过一个示例来解释PyTorch中自动微分的计算。 PyTorch中的自动微分 在torch中的torch....
  • 神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的...这就是Pytorch的自动微分机制。 一,利用backward方法求导数 backward 方法通常在一个标量张量上调用,该方法求得的梯
  • autograd自动微分

    2021-01-02 22:11:18
    该 autograd 软件包为 Tensors 上的所有操作提供自动微分。它是一个由运行定义的框架,这意味着以代码运行方式定义你的后向传播,并且每次迭代都可以不同。 torch.Tensor 是包的核心类。如果将其属性 .requires_grad...
  • 与重力理论类似,我们通过导致无限级数的迭代过程来构建动作,可以将其作为一组微分方程的解来恢复。 我们表明,在某些特定情况下,方程变为代数形式,并且还可以找到多项式形式的解。 我们简要回顾了文献中已经...
  • 我们从U-对偶不变阈值函数上的超对称约束的角度研究在各个维度上最大超重力... 我们的方法利用了约束这些耦合的张量微分方程。 我们详细研究了相关自守函数的傅立叶模式和波前集合,发现它们始终与特殊的幂等轨道相关。

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 306
精华内容 122
关键字:

张量微分