在学习Torch之际,正好结合沐神的《动手学深度学习》和CS229中的局部线性回归讲义实践一下!终于跑通了第一个属于自己的代码!!
0x01 什么是线性回归?在之前的CS229中,线性回归(Linear regression)作为机器学习的Hello world,让我们了解到学习模型(supervised learning algorithm)会包括几个部分:
数据集(dataset) 假设(hypothesis) 损失函数(loss function) 优化方法(optimization method) 详细可见 cs229-01线性模型
0x02 什么是局部线性回归?也就是给梯度一个权重
0x03 从零开始的代码实现在了解算法的基础上,如何使用代码构建出来是另外一个方面。两者可以是互补的! 参照Tensorflow2.0 八股文的描述,我们将深度学习网络构建的过程也可以分为几个典型的步骤:
构建dataset 根据dataset生成不同的dataloader,方便后续批量梯度下降(batch gradient descent) 建立Model(根据hypothesis) 选择损失函数 loss function 优化器选择 超参数的设置 lr、epoch、batchsize 学习过程,批量梯度下降 评价指标,对于regression的分析常见的包括MSE、MAPE等等 3.1 定义数据集1 2 3 4 5 6 7 8 9 10 11 12 13 14 import torchimport matplotlib.pyplot as pltdef gen_data (w,b,num_examples ): """生成y=Xw+b的噪声""" X = torch.normal(0 ,1 ,(num_examples,len (w)),dtype=float ) y = torch.matmul(X,w) y += torch.normal(0 ,0.01 ,y.shape,dtype=float ) return X,y.reshape((-1 ,1 )) true_w=torch.tensor([2 ,-3.4 ],dtype=float ) true_b=4.2 features,labels=gen_data(true_w,true_b,1000 ) plt.scatter(features[:,1 ],labels,1 )
3.2 定义数据迭代基于Python原生生成的迭代器虽然可以连续的获取不同的小批量,但是执行效率很低,因为此****要求将所有数据加载到内存中,并执行大量的随机内存访问****,所以通常深度学习框架会内置迭代器,用于处理存储在文件中的数据和数据流提供的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import randomdef data_iter (batch_size,features,labels ): num_examples = len (features) indices = list (range (num_examples)) random.shuffle(indices) for i in range (0 ,num_examples,batch_size): batch_indices = torch.tensor( indices[i:min (i+batch_size,num_examples)] ) yield features[batch_indices],labels[batch_indices]
3.3 定义模型同时也能够返回对应参数的parameters的size,这里和教科书不一样,我们使用class的方式来建立新的模型。
1 2 3 4 5 6 7 8 9 10 11 12 class network : def __init__ (self ) -> None : self.w=torch.normal(0 ,0.01 ,size=(2 ,1 ),requires_grad=True ,dtype=float ) self.b=torch.zeros(1 ,requires_grad=True ,dtype=float ) def predict (self,X ): return torch.matmul(X,self.w)+self.b def parameters (self ): '''返回对应参数的size''' return [self.w,self.b] model=network()
3.4 定义损失函数Local loss是局部损失函数
1 2 3 4 5 6 7 8 9 10 11 12 13 def get_weight (X,features ): '''权重分析''' diff = X.unsqueeze(-2 ).unsqueeze(-2 )- features.unsqueeze(-2 ) w = torch.exp(-diff**2 /0.000002 ).squeeze(-2 ).sum (axis=(1 ,2 )) return wdef square_loss (y_hat,y ): return (y_hat-y.reshape(y_hat.shape))**2 /2 def local_loss (y_hat,y,X ): new_w=get_weight(X,features) return new_w.reshape(shape=(-1 ,1 ))*(y_hat-y.reshape(y_hat.shape))**2 /2
3.5 定义优化算法在计算机求解优化算法的过程中,我们可以先想一下我们需要什么东西?
按照梯度下降的方式来进行神经网络的优化过程中,必然需要知道parameters的size,之后是对参数更新之后的长度learning rate,依旧每次下降的batch-size
1 2 3 4 5 6 7 8 9 10 def sgd (params,lr,batchsize ): '''小批量随机梯度下降''' with torch.no_grad(): for param in params: param -= lr*param.grad/batchsize param.grad.zero_()
3.6 定义超参数1 2 3 4 5 lr = 0.05 batchsize=10 num_epoches = 4 net = network() loss = square_loss
3.7 开始训练1 2 3 4 5 6 7 8 9 for epoch in range (num_epoches): for X,y in data_iter(batch_size=batchsize,features=features,labels=labels): l = local_loss(net.predict(X),y,X) l.sum ().backward() sgd(net.parameters(),lr,batchsize) with torch.no_grad(): train_l=loss(net.predict(features),labels).mean() print (train_l)
3.8 评估模型
0x04 几个注意的点数据dtype最好设置float 注意vector和matrix甚至tensor之间的区别 在weight计算中的维度的变化来保证broadcasting with torch.no_grad( )和model.eval( )的区别 令人惊喜的torch的broadcasting的功能!!