案例:美国房价预测
系统估价和自己实际付的钱要保持差不多的关系才能使自己赚到,那怎么样才能赚到呢,那就得有一个量良好的预估手段。
下面做出两个假设:
线性模型可以看做是单层的神经网络
每个箭头代表一个权重,输出是o1,输入是xn
神经网络其实源于神经科学,下图是真实的神经元:
有了模型之后,就要对其进行预测了,放在上面的案例里面讲也就是比较房价的真实值和预估值。
这个相当于是神经网络里面的损失函数。
有了模型和损失后,下一步就是参数的学习。
可以收集一些数据点来决定参数值(权重和偏差),比如过去六个月卖的房子,这些数据就被称为是训练数据,通常来讲,训练数据越多越好。
X是列向量,每个x一排一排的排好然后做转置。然后y是列向量有n个样本,每个y是实数样本。
目标是找到一个w和b使得损失函数的结果最小。
1、线性回归是对n维输入的加权,外加偏差。
2、使用平方损失来衡量预测值和真实值的差异。
3、线性回归有显示解。
4、线性回归可以看做是单层神经网络。
优化方法最常用的是梯度下降算法。
这里的学习率不能太大也不能太小,因为太小的话耗费资源很多,太大的话就错过最优值了。
1、梯度下降通过不断沿着反梯度方向更新参数求解
2、小批量随机梯度下降是深度学习默认的求解算法
3、两个重要的超参数是批量大小和学习率
- #¥¥¥¥¥<<<<<+++++-----线性回归从零开始实现-----+++++>>>>>¥¥¥¥¥#
- #从零开始实现整个方法,包括流水线、模型、损失函数和小批量随机梯度下降优化器
- #%matplotlib_inline #用于随机梯度下降和初始化权重
- import matplotlib.pyplot as plt
- import torch
- import random
- from d2l import torch as d2l
- #根据带有噪声的线性模型构造一个人造数据集,我们使用线性模型参数w=[2,-3,4]T、b=4.2和噪声e生成数据集及其标签:
- #y=Xw+b+e
- def synthetic_data(w,b,num_examples): #构造人造数据集,num_examples是要生成的样本数量
- '''生成 y=Xw+b+e'''
- X=torch.normal(0,1,(num_examples,len(w))) #构造x是方差为零均值为1的随机数,它的大小是样本数,它的列数是w的长度
- y=torch.matmul(X,w)+b #构造y
- y+=torch.normal(0,0.01,y.shape) #又让y加上了一个噪音。噪音是方差为0,均值为0.01,形状与y一样
- return X,y.reshape((-1,1)) #把x和y做出列向量返回
-
- true_w=torch.tensor([2,-3.4]) #定义真实的w和真实的b
- true_b=4.2
- features,labels=synthetic_data(true_w,true_b,1000) #然后通过函数计算出特征和标注
-
- #看一下训练样本的样子以及样本分布图
- print('features:',features[0],'\nlable:',labels[0]) #输出:features: tensor([0.4727, 0.1651]) lable: tensor([4.5845])
- #可见,第零个样本是长为2的一个向量,其标号是标量
- print('########################################################')
- d2l.set_figsize()
- d2l.plt.scatter(features[:,(1)].detach().numpy(),labels.detach().numpy(),1)#detach的作用是在pytorch的一些版本中,
- # 需要从计算图中detach处来才能转到numpy中
- plt.show() #画完后我们可以看到是个线性相关的样本集
- #定义一个data_iter 函数, 该函数接收批量大小、
- def data_iter(batch_size, features, labels):
- # 特征矩阵和标签向量作为输入,生成大小为batch_size的小批量
- num_examples = len(features)
- indices = list(range(num_examples)) #生成每个样本的索引(每个样本是随机读取的,没有特定顺序)
- random.shuffle(indices) #打乱下标
- for i in range(0, num_examples, batch_size): #从0开始到num_examples,每次走batch_size个
- batch_indices = torch.tensor(
- indices[i:min(i +batch_size, num_examples)]) #这里要获取批量索引,把索引存入张量中,后面的min操作是防止超过总量,所以要取最小的
- yield (features[batch_indices], #通过indices生成特征和标号。yield相当于是Python中的重复操作,
- labels[batch_indices]) #每次都生成一个x一个y,生成完后再调用函数,直到全部完成为止。
-
- batch_size = 10
-
- for X, y in data_iter(batch_size, features, labels):#给我一些样本标号,每次随机选取b个样本返回出来
- print(X, '\n', y)
- break
- print('########################################################')
- #定义初始化模型参数
- w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)#w是2*1的,2表示特征,1表示标签,0表示方差,0.02表示均值
- b = torch.zeros(1, requires_grad=True) #b初始赋为零,1表示它是1维的标量
- #w和b的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 #y_hat是预测值,y是真实值,要保证两者形状统一,差先按元素平方然后按元素除二
- #定义优化算法
- def sgd(params, lr, batch_size): #-----小批量随机梯度下降,params是个列表,里面包含w和b
- with torch.no_grad(): #它不需要计算梯度
- for param in params: #每一个w和b
- param -= lr * param.grad / batch_size #param=param-lr*param.grad/batch_size除以batch_size的操作是求均值操作,
- #由于是线性关系,所以在此求均值也可。
- param.grad.zero_() #梯度置零
- #开始训练
- #先定义超参数
- lr = 0.03 #学习率
- num_epochs = 3 #把整个数据扫三遍,也就是进行三轮训练
- net = linreg #模型就是之前定义的linreg
- loss = squared_loss #损失还是之前的定义的
- for epoch in range(num_epochs): #每一次对数据扫一遍
- for X, y in data_iter(batch_size, features, labels):#每次拿出一组x和y
- l = loss(net(X, w, b), y) #把预测的y和真实的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}')
- print('########################################################')
- #比较真实参数和通过训练学到的参数来评估训练的成功程度
- print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
- print(f'b的估计误差: {true_b - b}')
-
- #可以实验一下梯度很大或梯度很小时的情况,看看其损失有什么变化
- #梯度很大时
- '''#先定义超参数
- lr = 10 #学习率
- num_epochs = 10 #把整个数据扫三遍,也就是进行三轮训练
- net = linreg #模型就是之前定义的linreg
- loss = squared_loss #损失还是之前的定义的
- for epoch in range(num_epochs): #每一次对数据扫一遍
- for X, y in data_iter(batch_size, features, labels):#每次拿出一组x和y
- l = loss(net(X, w, b), y) #把预测的y和真实的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}')'''
- #梯度很小时,可以通过增加学习轮次得到好一些的损失值,但是成本有点大
- '''#梯度很大时
- #先定义超参数
- lr = 0.001 #学习率
- num_epochs = 3 #把整个数据扫三遍,也就是进行三轮训练
- net = linreg #模型就是之前定义的linreg
- loss = squared_loss #损失还是之前的定义的
- for epoch in range(num_epochs): #每一次对数据扫一遍
- for X, y in data_iter(batch_size, features, labels):#每次拿出一组x和y
- l = loss(net(X, w, b), y) #把预测的y和真实的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}')'''
features: tensor([ 0.3840, -0.3376])
lable: tensor([6.1121])
########################################################
tensor([[-1.1147e+00, -8.3294e-01],
[-6.0542e-04, 2.1474e-02],
[ 9.3246e-01, 1.4499e+00],
[ 4.7537e-01, -2.9177e-01],
[ 1.8271e+00, 1.3387e-01],
[-3.8728e-01, -1.7065e+00],
[-2.3963e+00, -1.5596e-01],
[-1.7679e+00, -6.3735e-01],
[ 4.8884e-01, 1.1035e+00],
[-6.9350e-01, 2.9550e-01]])
tensor([[ 4.7953],
[ 4.1320],
[ 1.1461],
[ 6.1476],
[ 7.3970],
[ 9.2280],
[-0.0416],
[ 2.8342],
[ 1.4362],
[ 1.8041]])
########################################################
epoch 1, loss 0.026386
epoch 2, loss 0.000093
epoch 3, loss 0.000046
########################################################
w的估计误差: tensor([0.0001, 0.0004], grad_fn=)
b的估计误差: tensor([0.0007], grad_fn=)
- #所谓的简洁实现就是通过使用深度学习框架来简洁地实现 线性回归模型 生成数据集
- import numpy as np
- import torch
- from torch.utils import data #处理数据的模块
- from d2l import torch as d2l
-
- true_w = torch.tensor([2, -3.4]) #真实的w
- true_b = 4.2 #真实的b
- features, labels = d2l.synthetic_data(true_w, true_b, 1000) #生成特征和标签
- #调用框架中现有的API来读取数据
- def load_array(data_arrays, batch_size, is_train=True):
- """构造一个PyTorch数据迭代器。"""
- dataset = data.TensorDataset(*data_arrays) #先将x,y变成dataset数据集
- return data.DataLoader(dataset, batch_size, #然后调用dataloader,每次挑b个样本出来
- shuffle=is_train) #如果在训练过程中的话就要随机打乱顺序
-
- batch_size = 10
- data_iter = load_array((features, labels), batch_size)
-
- next(iter(data_iter)) #得到一个x和一个y
- #使用框架的预定义好的层
- from torch import nn
- net = nn.Sequential(nn.Linear(2, 1)) #要指定输入输出维度,linear是个线性层,
- # 把他放在sequential里的话就相当于把每层给罗列起来了
- #初始化模型参数
- net[0].weight.data.normal_(0, 0.01) #把权重初始化
- net[0].bias.data.fill_(0) #把偏重初始化
- #计算均方误差使用的是MSELoss类,也称为平方 L2 范数
- loss = nn.MSELoss() #均方误差的调用
- #实例化 SGD 实例
- trainer = torch.optim.SGD(net.parameters(), lr=0.03) #梯度下降的调用,要传入所有参数(w,b),和学习参数
- #训练过程代码与我们从零开始实现时所做的非常相似
- 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() #pytorch已经做sum了
- trainer.step() #模型更新
- l = loss(net(features), labels)
- print(f'epoch {epoch + 1}, loss {l:f}')
- #比较生成数据集的真实参数和通过有限数据训练获得的模型参数
- w = net[0].weight.data
- print('w的估计误差:', true_w - w.reshape(true_w.shape))
- b = net[0].bias.data
- print('b的估计误差:', true_b - b)
epoch 1, loss 0.000369
epoch 2, loss 0.000104
epoch 3, loss 0.000105
w的估计误差: tensor([-0.0004, -0.0009])
b的估计误差: tensor([-5.7220e-06])