当前位置: 首页 > 科技观察

ComparingPyTorchandTensorFlow'sAutodifferenceandDynamicModels

时间:2023-03-16 21:59:00 科技观察

TrainingLinearRegressionfromScratchUsingaCustomModelClassComparisontheAutodifferenceandDynamicModelSubclassingApproachsBetweenPyTorch1.xandTensorFlow2.x,这篇短文重点介绍了如何使用动态分别在PyTorch1.x和TensorFlow2.x中使用模块/模型API对模型进行子类化,以及这些框架如何使用AutoDiff获取训练循环中损失的梯度并从头开始实现。梯度后代的非常天真的实现。生成有噪声的线性数据为了专注于自动差分/自动梯度函数的核心,我们将使用最简单的模型,线性回归模型,然后我们将首先使用numpy生成一些线性数据以添加随机级别的噪声。defgenerate_data(m=0.1,b=0.3,n=200):x=np.random.uniform(-10,10,n)noise=np.random.normal(0,0.15,n)y=(m*x+b)+noisereturnx.astype(np.float32),y.astype(np.float32)x,y=generate_data()plt.figure(figsize=(12,5))ax=plt.subplot(111)ax.scatter(x,y,c="b",label="samples")模型然后我们将在TF和PyTorch中从头开始实现一个线性回归模型,不使用任何层或激活器,而是简单地定义两个张量w和b,代表分别是线性模型的weights和biases,简单的实现线性函数:y=wx+b正如你在下面看到的,我们模型的TF和PyTorch类定义基本是一模一样的,只有一些api有细微差别名字。唯一值得注意的区别是,PyTorch明确使用Parameter对象来定义要被图形“捕获”的权重和偏差张量,而TF在这里似乎更“神奇”,并自动捕获用于图形的参数。实际上,在PyTorch中,参数是Tensor的子类,当与Moduleapi一起使用时,它们具有非常特殊的属性,可以自动将自己添加到Module参数列表并出现在parameters()迭代器中。在任何情况下,这两个框架都能够从这种定义和执行方法(调用或转发)、参数和图形定义中提取信息,以便向前执行图形执行,并且正如我们将看到的那样,通过自动可微函数获得梯度能够执行反向传播。TensorFlow动态模型类LinearRegressionKeras(tf.keras.Model):def__init__(self):super().__init__()self.w=tf.Variable(tf.random.uniform(shape=[1],-0.1,0.1))self.b=tf.Variable(tf.random.uniform(shape=[1],-0.1,0.1))def__call__(self,x):returnx*self.w+self.bPyTorch动态模型类LinearRegressionPyTorch(torch.nn.模块):def__init__(self):super().__init__()self.w=torch.nn.Parameter(torch.Tensor(1,1).uniform_(-0.1,0.1))self.b=torch.nn.Parameter(torch.Tensor(1).uniform_(-0.1,0.1))defforward(self,x):returnx@self.w+self.btrainingloop,backpropagationandoptimizer现在我们已经实现了简单的TensorFlow和PyTorch模型,我们可以定义TF和PyTorchapi来实现均方误差的损失函数,最后实例化我们的模型类并运行训练循环。同样,为了查看自差分/自差分功能的核心,我们将使用TF和PyTorch特定的自差分实现来实现自定义训练循环,为我们的简单线性函数提供梯度,并手动优化权重和偏置参数以及临时和朴素的梯度后代优化器。在TensorFlow的训练循环中,我们会专门使用GradientTapeAPI来记录模型的前向执行和loss计算,然后从这个GradientTape中获取用于优化weight和bias参数的梯度。相反,在这种情况下,PyTorch提供了一种更“神奇”的autograd方法,该方法隐式捕获对参数张量的任何操作,并为我们提供相同的梯度以用于优化权重和偏置参数,而无需使用任何特定的api。一旦我们有了权重和偏差梯度,在PyTorch和TensorFlow上实现我们自定义的梯度推导方法就像从权重和偏差参数中减去这些梯度并将它们乘以恒定学习率一样简单。这里的最后一个小区别是,当PyTorch在反向传递中更新权重和偏差参数时以更微妙和“神奇”的方式实现自动差分/自动梯度时,我们需要确保不要保留PyTorch从最后提取update操作中的grad,这次显式调用no_gradapi,最后将weight和bias参数的梯度重置为零。TensorFlow训练循环defsquared_error(y_pred,y_true):returntf.reduce_mean(tf.square(y_pred-y_true))tf_model=LinearRegressionKeras()[w,b]=tf_model.trainable_variablesforepochinrange(epochs):withtf.GradientTape()astape=predicttf_model(x)loss=squared_error(predictions,y)w_grad,b_grad=tape.gradient(loss,tf_model.trainable_variables)w.assign(w-w_grad*learning_rate)b.assign(b-b_grad*learning_rate)ifepoch%20==0:print(f"Epoch{epoch}:Loss{loss.numpy()}")PyTorch训练循环defsquared_error(y_pred,y_true):returntorch.mean(torch.square(y_pred-y_true))torch_model=LinearRegressionPyTorch()[w,b]=torch_model.parameters()forepochinrange(epochs):y_pred=torch_model(输入)loss=squared_error(y_pred,labels)loss.backward()withtorch.no_grad():w-=w.grad*learning_rateb-=b.grad*learning_ratew.grad.zero_()b.grad.zero_()ifepoch%20==0:print(f"Epoch{epoch}:Loss{loss.data}")结论如我们所见,TensorFlow和PyTorch自动微分和动态子分类API非常相似r,当然,两个模型的训练也给了我们非常相似的结果。在下面的代码片段中,我们将分别使用Tensorflow和PyTorch的trainable_variables和parameters方法来访问模型参数并绘制学习到的线性函数。绘制结果[w_tf,b_tf]=tf_model.trainable_variables[w_torch,b_torch]=torch_model.parameters()withtorch.no_grad():plt.figure(figsize=(12,5))ax=plt.subplot(111)ax。散点图(x,y,c="b",label="samples")ax.plot(x,w_tf*x+b_tf,"r",5.0,"tensorflow")ax.plot(x,w_torch*inputs+b_torch"c",5.0,"pytorch")ax.legend()plt.xlabel("x1")plt.ylabel("y",rotation=0)