本节将介绍如何只利⽤
Tensor
和
autograd
来实现⼀个线性回归的训练。
⾸先,导⼊本节中实验所需的包或模块,其中的matplotlib包可⽤于作图,且设置成嵌⼊显示。
⾸先,导⼊本节中实验所需的包或模块:
import torch
from IPython import display
from matplotlib import pyplot as plt #matplotlib包可用于作图,且设置成嵌入显示
import numpy as np
import random
一、生成数据集
我们构造⼀个简单的⼈⼯训练数据集,它可以使我们能够直观⽐较学到的参数和真实的模型参数的区
别。设训练数据集样本数为1000,输⼊个数(特征数)为2。给定随机⽣成的批量样本特征
\(X \in R^{1000 \times 2}\)
,我们使⽤线性回归模型真实权
\(w = [2,-3.4]^T\)
和偏差
\(b = 4.2\)
,以及⼀个随机噪声项
\(\epsilon\)
来⽣成标签
其中噪声项
\(epsilon\)
服从均值为0、标准差为0.01的正态分布。噪声代表了数据集中⽆意义的⼲扰。下⾯,让我们⽣成数据集。
# 1.生成数据集
num_inputs = 2 #2个特征
num_examples = 1000 #训练集1000个样本数量
true_w = [2,-3.4] #两个特征的权重值
true_b = 4.2 # 偏差
features = torch.from_numpy(np.random.normal(0,1,(num_examples,num_inputs)))
# 下面两行是使用一个随机噪声生成标签$y = Xw+b+ \epsilon$
lables = true_w[0] = features[:,0] + true_w[1] * features[:,1] + true_b
lables += torch.from_numpy(np.random.normal(0,0.01,size=lables.size()))
#注意:`features`的每一行是一个长度为2的向量,而`lables`的每一行是一个长度为1的向量(标量)。
print(features[0],lables[0])
输出结果:
tensor([0.8702, 1.1407], dtype=torch.float64) tensor(1.2039, dtype=torch.float64)
绘制线性关系图:
# 通过生成第二个特征`features[:,1]和标签`lables`的散点图,可以更直观观察两者间的线性关系
# 若没有导入`/d2lzh_pytorch`包里添加下面两个函数,则需要在此添加
def use_svg_display():
# 用矢量图显示
display.set_matplotlib_formats('svg')
def set_figsize(figsize = (3.5,2.5)):
use_svg_display()
# 设置图的尺寸
plt.rcParams['figure.figsize'] = figsize
"""
# 在d2lzh_pytorch里面添加上面两个函数后就可以这样导入
import sys
sys.path.append("..")
from d21zh_pytorch import *
"""
set_figsize()
plt.scatter(features[:,1].numpy(),lables.numpy(),1);
输出结果:
二、读取数据
在训练模型的时候,我们需要遍历数据集并不断读取⼩批量数据样本。这⾥我们定义⼀个函数:它每次返回
batch_size
(批量⼤⼩)个随机样本的特征和标签。
batch_size = 10
#`data_iter`保存在d2lzh包中。
# 们读取第一个批量数据样本并打印。每个批量的特征形状为(10, 2),分别对应批量大小和输入个数;标签形状为批量大小。
for x,y in data_iter(batch_size,features,lables):
print("输出x,y在data_iter中的数据")
print(x,y)
break
输出结果:
完整代码:
# -*- coding: utf-8 -*-
"""
Created on Fri Feb 21 01:30:34 2020
@author: Google
"""
# 导入所需模块或者包
import torch
from IPython import display
from matplotlib import pyplot as plt #matplotlib包可用于作图,且设置成嵌入显示
import numpy as np
import random
#import sys
#sys.path.append("..")
from d2lzh_pytorch import *
# 1.生成数据集
num_inputs = 2 #2个特征
num_examples = 1000 #训练集1000个样本数量
true_w = [2,-3.4] #两个特征的权重值
true_b = 4.2 # 偏差
features = torch.from_numpy(np.random.normal(0,1,(num_examples,num_inputs)))
# 下面两行是使用一个随机噪声生成标签$y = Xw+b+ \epsilon$
lables = true_w[0] = features[:,0] + true_w[1] * features[:,1] + true_b
lables += torch.from_numpy(np.random.normal(0,0.01,size=lables.size()))
#注意:`features`的每一行是一个长度为2的向量,而`lables`的每一行是一个长度为1的向量(标量)。
print(features[0],lables[0])
# 通过生成第二个特征`features[:,1]和标签`lables`的散点图,可以更直观观察两者间的线性关系
# 若没有导入`/d2lzh_pytorch`包里添加下面两个函数,则需要在此添加
def use_svg_display():
# 用矢量图显示
display.set_matplotlib_formats('svg')
def set_figsize(figsize = (3.5,2.5)):
use_svg_display()
# 设置图的尺寸
plt.rcParams['figure.figsize'] = figsize
"""
# 在d2lzh_pytorch里面添加上面两个函数后就可以这样导入
import sys
sys.path.append("..")
from d21zh_pytorch import *
"""
set_figsize()
plt.scatter(features[:,1].numpy(),lables.numpy(),1);
"""
2.读取数据:
在训练模型的时候,我们需要遍历数据集并不断读取⼩批量数据样本。这⾥我们定义⼀个函数:它每次
返回 batch_size (批量大小)个随机样本的特征和标签。
"""
batch_size = 10
#`data_iter`保存在d2lzh包中。
# 们读取第一个批量数据样本并打印。每个批量的特征形状为(10, 2),分别对应批量大小和输入个数;标签形状为批量大小。
for x,y in data_iter(batch_size,features,lables):
print("输出x,y在data_iter中的数据")
print(x,y)
break
"""
3. 初始化模型参数
"""
# 我们将权重初始化成均值为0、标准差为0.01的正态随机数,偏差则初始化成0
w = torch.tensor(np.random.normal(0,0.01,(num_inputs,1)),dtype = torch.float32)
b = torch.zeros(1,dtype = torch.float32)
# 后续的模型训练中,需要对这些参数梯度求迭代参数的值,因此要使`requires_grad=True`
w.requires_grad_(requires_grad = True)
b.requires_grad_(requires_grad = True)
"""
4.定义模型
"""
# 下面是线性回归的矢量计算表达式的实现,使用`mm`函数做矩阵乘法
def linreg(z,w,b): # 本函数已保存在d2lzh_pytorch包中方便以后使用
z = torch.tensor(z, dtype=torch.float32)
return torch.mm(z,w)+b
"""
5.定义损失函数
"""
# 使用`平方损失`做线性回归的损失函数。将真实值`y`变形成预测值`y_hat`的形状。
#以下函数的返回结果也将和`y_hat`的形状相同
def squared_loss(y_hat,y): # 本函数已保存在d2lzh_pytorch包中方便以后使用
# 注意:此处返回的是向量,并且pytorch中的MSELoss并没有除以2
return (y_hat - y.view(y_hat.size())) ** 2/2
"""
6.定义优化算法
"""
# 以下的`sgd`函数实现小批量随机梯度下降算法,通过不断迭代来优化损失函数。
# 这里自动求梯度模块计算得来的梯度是一个批量样本的梯度和,通过将它除以批量大小来得到平均值
def sgd(params,lr,batch_size): # 本函数已保存在d2lzh_pytorch包中方便以后使用
for param in params:
param.data -= lr * param.grad / batch_size #更改param时用的param.data
"""
7.训练模型
"""
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,lables):
l = loss(net(x,w,b),y).sum()
l.backward()
sgd([w,b],lr,batch_size)
# 进行梯度清零
w.grad.data.zero_()
b.grad.data.zero_()
train_l = loss(net(features,w,b),lables)
print('epoch %d,loss %f' % (epoch + 1,train_l.mean().item()))
运行结果:
三、自我总结:
在第4步:定义模型中,原代码:
"""
4.定义模型
"""
# 下面是线性回归的矢量计算表达式的实现,使用`mm`函数做矩阵乘法
def linreg(z,w,b): # 本函数已保存在d2lzh_pytorch包中方便以后使用
return torch.mm(z,w)+b
运行过程中,会报错:
Traceback (most recent call last):
File "<ipython-input-9-386f60e0227d>", line 1, in <module>
runfile('F:/CodeStore/DL_Pytorch/3.2PracticeOfLinerRegression.py', wdir='F:/CodeStore/DL_Pytorch')
File "D:\InstallSoftware\Anaconda\envs\spyder_py3.5\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 678, in runfile
execfile(filename, namespace)
File "D:\InstallSoftware\Anaconda\envs\spyder_py3.5\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 106, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "F:/CodeStore/DL_Pytorch/3.2PracticeOfLinerRegression.py", line 114, in <module>
l = loss(net(x,w,b),y).sum()
File "F:/CodeStore/DL_Pytorch/3.2PracticeOfLinerRegression.py", line 83, in linreg
return torch.mm(z,w)+b
RuntimeError: Expected object of scalar type Double but got scalar type Float for argument #2 'mat2' in call to _th_mm
问题:类型错误,于是添加
z = torch.tensor(z, dtype=torch.float32)
,将数据类型统一。改后的代码:
"""
4.定义模型
"""
# 下面是线性回归的矢量计算表达式的实现,使用`mm`函数做矩阵乘法
def linreg(z,w,b): # 本函数已保存在d2lzh_pytorch包中方便以后使用
z = torch.tensor(z, dtype=torch.float32)
return torch.mm(z,w)+b
"""