先补充一些知识点,这里不一定用得到,后面的学习过程中可能用得到。
1.batch表示批量,就是一批数据集的意思;
2.batch_size表示数据集(样本集、训练集)的大小(数据的个数);
3.iteration是迭代的意思,即一个iteration表示迭代一次;
4.epoch可以看成训练次数,一个epoch指用训练集中的全部样本训练一次,最初训练DNN采用一次对全体训练集的样本进行训练(即用一个epoch),但样本集较大时占用内存大,目前常用随机梯度下降SGD来训练,将训练集分为多个mini_bach(即batch),一次迭代训练一个minibatch(batch_size个样本),根据该batch数据的loss更新权值。
举个例子,训练集有1000个样本,batch_size=10,那么训练完整个样本集需要100次iteration,1次epoch。epoc指把训练集所有的数据都跑一遍,当然也有将训练集的所有数据跑多遍的算法,即多个epoch。
以房价预测问题为例.
假设1:影响房价的关键因素是卧室个数,卫生间个数和居住面积,记为x1,x2,x3.
假设2:成交价是关键因素的加权和:
y = w1x1+w2x2+w3*x3+b(其中,w1,w2,w3是权重,b是偏差)
一、理论
1.拓展到线性模型:
给定n维输入:
线性模型有一个n维权重和一个标量偏差:
说明:<w,x>是张量的内积,和向量的内积是一个意思。
2.衡量预估质量:
比较真实值和预估值,例如房屋售价和估价。假设y是真实值,y^是估计值,可以比较:
这个叫做平方损失。
3.训练数据
收集一些数据点来决定参数(权重和偏差)。例如过去6个月卖的房子,这被称之为训练数据。假如我们有n个样本,记:
4.参数学习
最小化损失函数就是找到使损失函数l(x,y,w,b)最小的w,b的值。
5.显示解
线性回归可以看成单层神经网络。
二、线性回归代码实现
1.零基础代码
%matplotlib inline
import random
import torch
from d2l import torch as d2l
# 用于生成人造数据集
def synthetic_data(w,b,num_examples):
x = torch.normal(0,1,(num_examples,len(w))) # torch.normal()表示正太分布,有三个参数torch.normal(mean,std,size),三个参数
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)
# 绘制点图显示
d2l.set_figsize()
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1);
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]
batch_size = 10
for x,y in data_iter(batch_size,features,labels):
print(x,'\n',y)
break
# 定义初始化模型参数
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
# 定义优化算法(小批量随机梯度下降)sgd在深度学习中表示随机梯度下降法(Stochastic Gradient Descent)
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}')
说明:(1)len()函数返回对象的长度,注意不是length()函数。
(2)torch.normal(mean,std,size)用于产生正太分布的一个张量,参数mean是正太分布的均值,参数std是标准差,size是张量的shape.
(3)torch.matmul用于实现两个张量相乘。(这里满足之前说过的张量相乘的不同情况。
两个一维向量实现内积:
两个多维张量实现的是线性代数里的矩阵相乘:
报错原因为a的列不等于b的行。
(4)random.shuffle()用于将一个列表中的元素打乱顺序,元素个数和值不变
注意:该函数没有返回值,而是将参数值改变并传回给参数本身,即使参数值改变。
(5)reshape()函数中参数为-1表示无意义。reshape(-1,1)表示将二维数组重整为一个一列的数组,reshape(1,-1)表示把一个二维数组重整为一个一行的数组。
(6)range(n)用于生成一个从0到n-1的整数序列,list()用于将序列转化为列表。
(7)zero_()可用于将张量清零
(8)yield
python中含有yield的函数相当于一个迭代器,即iterator,生成的迭代器(函数执行的返回结果)可以用于for循环。
注意:当执行g=gen(5)时,gen中的代码并没有执行(节省内存空间),只是创建了一个生成器对象(generator),然后,执行for i in g,每次执行一次循环就会执行到yield处,返回一次yield的值。
从上例可以看出,yield也是使函数返回,暂停执行,且下一次执行是从上次被暂停的地方,而不是重新来一轮。
2.使用pytorch的深度学习框架来实现线性回归模型
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
# 生成人工数据集
true_w = torch.tensor([2,-4.3])
true_b = 4.2
features,labels =d2l.synthetic_data(true_w,true_b,1000)
# 调用框架中的现有的API来读取数据
def load_array(data_arrays,batch_size,is_train=True):
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset,batch_size,shuffle=is_train)
batch_size = 10
data_iter = load_array((features,labels),batch_size)
next(data_iter)
# 使用预定义好的层
from torch import nn # nn是神经网络的缩写
net = nn.Sequential(nn.Linear(2,1))
# 初始化模型参数
net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)
# 计算均方误差使用的是MSELoss类,也称为平方范数
loss = nn.MSELoss()
# 实例化SGD实例
trainer = torch.optim.SGD(net.parameters(),lr=0.03)
# 训练数据
num_epochs = 3
for epoch in range(num_epochs):
for x,y in data_iter:
l = loss(net(x),y) # net中自带模型参数,不用再传
trainer.zero_grad()
l.backward()
trainer.step() # step()函数进行一次模型的更新
l=loss(net(features),labels)
print(f'epoch{epoch +1},loss{l:f}')