本章主要讲述线性回归。
来源:动手学习深度学习(pytorch版)
向量化加速
1 2 3 4 5 6 %matplotlib inline import mathimport timeimport numpy as npimport torchfrom d2l import torch as d2l
1 2 3 n=10000 a = torch.ones(n) b = torch.ones(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Timer : def __init__ (self ): self.times = [] self.start() def start (self ): """启动计时器""" self.tik = time.time() def stop (self ): """停止计时器并将时间记录在列表中""" self.times.append(time.time()-self.tik) return self.times[-1 ] def avg (self ): """返回平均时间""" return sum (self.times)/len (self.times) def Sum (self ): return sum (self.times) def cumsum (self ): """返回累计时间""" return np.array(self.times).cumsum().tolist()
1 2 3 4 5 6 7 8 9 c = torch.zeros(n) timer = Timer() for i in range (n): c[i] = a[i] + b[i] print (f'{timer.stop(): .5 f} sec' )print (c)print (a)print (b)print (timer.cumsum(),timer.Sum())
0.12451 sec
tensor([2., 2., 2., ..., 2., 2., 2.])
tensor([1., 1., 1., ..., 1., 1., 1.])
tensor([1., 1., 1., ..., 1., 1., 1.])
[0.1245124340057373] 0.1245124340057373
1 2 3 timer.start() d = a + b f'{timer.stop() : .9 f} sec'
' 0.000000000 sec'
正态分布与平方损失
1 2 3 4 5 6 7 8 9 def normal (x,mu,sigma ): p = 1 /np.sqrt(2 *math.pi*sigma**2 ) return p*np.exp((-0.5 /sigma**2 )*(x-mu)**2 ) x = np.arange(-7 ,7 ,0.01 ) params = [(0 ,1 ),(0 ,2 ),(3 ,1 )] d2l.plot(x,[normal(x,mu,sigma) for mu,sigma in params],xlabel='x' ,ylabel='p(x)' ,figsize=(4.5 ,2.5 ),legend= [f'mean {mu} , std {sigma} ' for mu,sigma in params])
正如我们看到的改变均值会发生沿x轴偏移,增加方差将会分散分布、降低峰值,均方误差损失函数能用于线性回归的一个原因是:我们假设观测中包含噪声,其中噪声服从正态分布。
小结 1.机器学习模型中的关键要素是训练数据集、损失函数、优化算法、以及模型本身。 2.向量化使数学表达更简洁,同时计算更快。 3.最小化目标函数和执行极大似然估计等价 4.线性回归模型也是一个简单的神经网络
当数据集较小时,解析解可能比随机梯度下降更好。然而,在大型数据集上,计算解析解可能会非常耗时,或者存在多个局部最小的情况。此外,当矩阵
⊤
X ⊤ X不可逆时,解析解不存在。在这种情况下,需要使用正则化或数值优化方法。
由于使用了绝对值函数作为损失函数,梯度在接近驻点(即梯度接近零的点)时,梯度不会平滑地趋向于零,而是存在突变。当使用SGD算法,不断更新参数时,可能导致模型无法稳定收敛。
解决该问题的方法:
使用平滑的损失函数,可使用MSE、Smooth L1损失函数等。 调整学习率,逐渐减小学习率,使得在驻点附近的参数更新更加稳定 使用动量法或自适应学习率优化算法
从0开始线性回归 1 2 3 4 %matplotlib inline import torchimport randomfrom d2l import torch as d2l
1 2 3 4 5 6 7 8 9 10 11 def synthetic_data (w,b,num_examples ): """生成y=Xw+b+噪声""" x = torch.normal(0 ,1 ,(num_examples,len (w))) y = torch.matmul(x,w)+b y += torch.normal(0 ,0.01 ,y.shape) return x,y.reshape((-1 ,1 )) true_w = torch.tensor([2 ,-3.4 ]) true_b = 4.2 features, labels = synthetic_data(true_w,true_b,1000 ) print ('features:' ,features[0 ],'\nlabels:' ,labels[0 ])
features: tensor([-1.5115, -2.4289])
labels: tensor([9.4437])
生成features的第二个特征与labels之间的线性关系
1 2 d2l.set_figsize() d2l.plt.scatter(features[:,1 ].detach().numpy(),labels.detach().numpy(),1 )
<matplotlib.collections.PathCollection at 0x230fd7fe130>
读取数据集
1 2 3 4 5 6 7 8 def 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]
yield关键字用于定义生成器函数。生成器函数是一种特殊的迭代器,它允许你在函数执行过程中暂停和恢复,从而节省内存。当你调用一个生成器函数时,它会返回一个生成器对象,而不是直接执行函数体。要获取生成器的下一个值,你需要使用next()函数或者在循环中使用for语句。features[batch_indices]和labels[batch_indices]。这两个列表分别包含了当前批次的特征和标签。
1 2 3 4 batch_size = 10 for X, y in data_iter(batch_size,features,labels): print (X,"\n" ,y) break
tensor([[-1.7161, 0.5275],
[ 0.5354, -0.6246],
[ 0.8205, 0.0515],
[-2.5739, 1.4279],
[ 0.8007, 0.0255],
[ 0.4949, 0.1812],
[-0.4580, 0.1285],
[-0.7347, 0.9106],
[-0.7922, -0.7991],
[-0.6561, 0.4373]])
tensor([[-1.0201],
[ 7.3861],
[ 5.6638],
[-5.8160],
[ 5.7303],
[ 4.5611],
[ 2.8534],
[-0.3845],
[ 5.3452],
[ 1.3960]])
初始化模型参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 w = torch.normal(0 ,0.01 ,size=(2 ,1 ),requires_grad=True ) b = torch.zeros(1 ,requires_grad=True ) def linreg (X,w,b ): """线性回归模型""" return torch.matmul(X,w)+b def squared_loss (y_hat,y ): """均方损失""" return (y_hat-y.reshape(y_hat.shape))**2 /2 def sgd (params,lr,batch_size ): with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size param.grad.zero_() lr = 0.03 num_epochs = 3 net = linreg loss = squared_loss for epoch in range (num_epochs): for X,y in data_iter(batch_size,features,labels): l = loss(net(X,w,b),y) l.sum ().backward() sgd([w,b], lr, batch_size) with torch.no_grad(): train_l = loss(net(features,w,b),labels) print (f'epoch {epoch+1 } ,loss {float (train_l.mean()): f} ' )
epoch 1,loss 0.044458
epoch 2,loss 0.000173
epoch 3,loss 0.000050