Linear Regression start with zero
%matplotlib inline
import matplotlib.pyplot as plt
import torch
from IPython import display
import numpy as np
import random
import torch
num_input = 2 #特征数/输入个数
num_examples = 1000 #随机生成批量样本特征
#np.random.normal(loc=0.0,scale=1.0,size=None)
features = torch.from_numpy(np.random.normal(0,1,(num_examples,num_inputs))) #真实的权重和偏差
true_w = [2,-3.4]
true_b = 4.2
labels = true_w[0] * features[:,0] + true_w[1] * feature[:,1] +true_b
#随机噪声项:服从均值为0,标准差为0.01的正态分布,size需要与labels同形
labels += torch.from_numpy(np.random.normal(0,0.01,size=labels.size()))
Notes:
features的每一行为一个长度为2的向量,而labels的每一行为一个长度为1的向量(标量)
tensor([-0.0469, 0.7326], dtype=torch.float64)
tensor(1.6094, dtype=torch.float64)
或者,我们用散点图可视化一下结果
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
set_figsize()
plt.scatter(features[:,1].numpy(),labels.numpy(),1)
读取数据
准确的说,在训练模型是不断读取小批量的数据样本,因此可以定义一个函数data_iter(),每次返回batch_size(批量大小)个数的随机样本的特征和标签
#定义函数:每次返回batch_size(批量大小)个随机样本的特征和标签
def data_iter(batch_size,features,labels):
num_examples = len(features)
indices = list(range(num_examples))
#随即顺序读取样本 rabdom.shuffle(first_item)
random.shuffle(indices)
for i in range(0,num_examples,batch_size):
#Tensor的索引index_select(input,dim,index)
j = torch.LongTensor(indices[i:min(i + batch_size,num_examples)])
yield features.index_select(0,j),labels.index_select(0,j)
然后,试着读取第一个小批量数据样本并打印,每个批量的特征形状为(10,2)分别对应批量大小和输入个数;标签形状为批量大小
batch_size = 10
for x,y in data_iter(batch_size,features,labels):
print(x,y)
break
Out:
tensor([[-0.5245, 1.9686, -0.3142, ..., -1.3142, 2.2752, -0.3538],
[ 0.4080, -0.9857, 0.9890, ..., 1.9474, 0.3551, 2.2485]],
dtype=torch.float64) tensor([6.3743, 1.1030], dtype=torch.float64)
初始化模型参数
之后的模型训练中,需要对这些参数求梯度来迭代参数的值,因此我们需要设置requires_grad = Ture
#权重初始化均值为0、标准差为0.01的正态随机数,偏差则初始化为0
w = torch.tensor(np.random.normal(0,0.01,(num_inputs,1)),
dtype = torch.float64)
b = torch.zeros(1,dtype=torch.float64)
w.requires_grad_(requires_grad= True)
b.requires_grad_(requires_grad= True)
定义模型
#使用mm函数做矩阵乘法
def linreg(x,w,b):
return torch.mm(x,w) + b
定义损失函数
#使用平方损失函数(需要把真实值y 的形状变成y_hat的形状)
def squared_loss(y_hat,y):
#注意这里返回的是向量,另外pytorch里的MSELoss并没有除以2
return (y_hat - y.view(y_hat.size())) **2 / 2
定义优化算法
#小批量随机梯度下降算法sgd,通过不断迭代模型来优化损失函数
def sgd(params,lr,batch_size):
for param in params:
param.data -= lr * param.grad / batch_size
训练模型
Notes:
- 对标量调用backward()方法得到该变量的有关模型的梯度,而每个batch的损失batch_loss的形状大小为(batch_size,batch_loss),就是说需要再调用一个sum()使其变为标量再进行计算
- 每次更新完参数后要记得将参数的梯度清零
- 对于learning_rate和num_epochs这类超参数hyperparameters需要提前人为设定。
- 训练过程中我们多次迭代模型参数,根据当前读取的小批量数据样本(特征x和标签y),通过调用反向函数backward计算小批量随机梯度,并调用优化算法迭代模型参数
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
#模型一共需要迭代num_epochs个迭代周期
#在每个迭代周期中,会使用训练数据集中的所有样本一次(假设样本数能够被批量大小整除)
#x,y分别是小批量样本的特征和标签
for X,y in data_iter(batch_size,features,labels):
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),labels)
print('epoch %d,loss %f'%(epoch + 1,train_l.mean().item()))
然后,我们会得到以下结果
epoch 1,loss 0.031826
epoch 2,loss 0.000106
epoch 3,loss 0.000045
简化版本:
回顾上述过程,有些步骤可以用更少的代码实现:
-
比如在读取数据时可以直接使用PyTorch提供的data包返回以batch_size为单位的以数据样本特征和数据标签为数据项的item
调用实现代码如下:#优化模块:随机索引 & 批量返回数据集の特征,标签 batch_size = 10 dataset = Data.TensorDataset(features,labels) data_iter = Data.DataLoader(dataset,batch_size,shuffle = True)
打印,Check
for X,y in data_iter: print(X,"\n",y) break Out: tensor([[ 0.0559, 0.4076], [-1.4700, -1.8048], [-0.2040, 0.4944], [ 0.2843, -1.1990], [-0.9366, -1.3344], [-1.1165, -0.1748], [ 1.1653, -0.7916], [-0.4482, -0.7828], [-0.6377, 0.0800], [ 0.5079, 0.1190]]) tensor([2.9387, 7.3972, 2.1157, 8.8541, 6.8640, 2.5714, 9.2221, 5.9670, 2.6400, 4.8230])
-
使用torch的nn(neural network)模块
- torch.nn利用autograd定义模型
- nn的核心数据结构:Module<既可以表示神经网络中的某个层layer也可以表一个包含很多层的神经网络>
- 我们定义一个类继承nn.Module来构建模型
*与之前HelloWorld进化史那篇文章中的实现类似,实现过程如下:
class LinearNet(nn.Module): def __init__(self,n_feature): super(LinearNet,self).__init__() self.linear = nn.Linear(n_feature,1) def forward(self,x): y = self.linear(x) return y net = LinearNet(num_inputs) print(net)
LinearNet( (linear): Linear(in_features=2, out_features=1, bias=True))
-
初始化模型参数使用PyTorch的init模块
-
定义损失函数使用PyTorch的nn模块中的损失函数
-
定义优化算法使用PyTorch的优化器模块torch.optim
-
训练模型使用PyTorch的optim模块中的step函数来迭代模型参数,在step函数中指明批量大小,从而对批量中样本梯度求平均
具体优化见如下实现及代码中的注释/说明
#或者直接使用nn.Sequtial来搭建网络
#Sequential是一个有序的容器,网络层将按照在传入Sequential的顺序依次添加到计算图中
net = nn.Sequential(
nn.Linear(num_inputs,1)
#还可以传入其他层,比如激活函数nn.ReLU()
)
#可以分别输出看一下构建的网络结构分布print(net) 、 print(net[0])
from torch.nn import init #初始化模型参数:weights bias requires_grad属性设置
import torch.optim as optim #定义优化算法:SGD、Adam、RMSProp等
#初始化模型参数
#权重(随机采样与均值mean为0标准差std为0.01的正态分布normal) &
#偏差初始化为0
init.normal_(net[0].weight,mean = 0,std= 0.01)
init.constant_(net[0].bias,val = 0)
#定义损失函数:均方误差损失MeanSquareLoss
#(nn模块中提供了nn,Module的子类来实现)
loss = nn.MSELoss()
#定义优化算法(同时我们在这里定义超参数学习率为0.03)
#torch.optim模块提供SGD、ADam、RMSProp
optimizer = optim.SGD(net.parameters(),lr = 0.03)
#训练模型
num_epochs = 3
for epoch in range(1,num_epochs + 1):
for X,y in data_iter:
output = net(X)
l = loss(output,y.view(-1,1)) #还记得吗,之前说过的真实值y要与y_hat的shape一样(调用.view()方法实现)
optimizer.zero_grad() #其实,只要在反向传播backward pass 前对梯度进行清零就好
l.backward()
optimizer.step() #帮助我们更好实现训练数据集的迭代
print('epoch %d ,loss:%f '% (epoch,l.item()))
然后
epoch 1 ,loss:0.000243
epoch 2 ,loss:0.000072
epoch 3 ,loss:0.000095
Tips:
-
numpy随机数生成
np.random.normal(loc=0.0,scale=1.0,size=None)
有时随机生成的Tensor的size要与模型中其他一些Variable的Tensor同形(size) -
numpy生成随机第一维索引
只对第一维做打乱顺序操作:random.shuffle(dim1)
This function only shuffles the array along the first index of a multi-dimensional array
就是说,多维矩阵中,只对第一维(行)做打乱顺序操作 -
index_select(input,dim,index)生成Tensor索引
指定维度上选择行或列,一般生成Long类型Tensor -
在每个迭代周期epoch中,我们将完整遍历一遍data_iter函数
-
并对训练集中所有样本都使用一次(假设样本数能够被批量大小整除)
-
这里的迭代周期个数num_epochs和学习率lr都是超参数,分别设为3和0.03
-
但是在实践中,大多超参数需要反复试错来不断调节,
-
虽然迭代周期设的越大模型可能越有效,但训练时间可能过长。
以上为实现过程及过程中遇到问题的记录
源码:https://github.com/ShusenTang/Dive-into-DL-PyTorch