这是看了李沐大神的动手深度学习,自己敲了一遍,直接上代码吧,注释已经写的很清楚了
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
"""生成数据集"""
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
def load_array(data_arrays,batch_size,is_train=True):
"""构造一个pytorch数据迭代器"""
#data.TensorDataset可以将输入的两类数据一一对应,data_arrays前面有*是元组解包
dataset = data.TensorDataset(*data_arrays)
# DataLoader将数据重新排序,每次随机选batch_size个出来
return data.DataLoader(dataset,batch_size,shuffle=is_train)
batch_size=10
data_iter = load_array((features,labels),batch_size)
next(iter(data_iter)) #转成python中的迭代器,通过next每次得到一个x和y
"""定义模型"""
from torch import nn
#Sequential相当于一个各种层的列表(list of layer),这里只有一个线性层
net = nn.Sequential(nn.Linear(2,1))
"""初始化模型参数"""
# net[0]访问到了线性层,再加上.weight访问到了详细层的权重,也就是w,再加.data访问到了w的值
# 再加.normal_就是要用均值为0,方差为0.01的正态分布值来初始化权重
net[0].weight.data.normal_(0,0.01)
#将线性层的偏差的值(也就是b的值)全部填充为0
net[0].bias.data.fill_(0)
"""使用均方误差"""
loss =nn.MSELoss()
"""实例化SGD实例"""
#net.parameters()包括所有的参数,包括w和b
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)
trainer.zero_grad() #首先将梯度清0
l.backward() #计算梯度
trainer.step() #进行模型更新
l = loss(net(features), labels) #计算所有的loss
print(f'epoch {epoch + 1}, loss {l:.8f}')
import torch
import random
def synthetic_data(w,b,num_examples):
'''自己造一个数据集,生成y=Xw+b+噪声'''
#生成num_examples个均值为0,方差为1的样本,每个样本的长度为w的长度
X=torch.normal(0,1,(num_examples,len(w)))
y=torch.matmul(X,w)+b #计算出y
y = y+torch.normal(0,0.01,y.shape) #产生噪声
return X,y.reshape((-1,1)) #将y转换成一列返回
true_w = torch.tensor([[2],[-3.4]]) # y=2*x1-3.4*x2+4.2
print(true_w.size())
true_b = 4.2
feature,labels = synthetic_data(true_w,true_b,1000)
feature
“”“
feature的结果
tensor([[ 2.1414, -1.1311],
[ 1.6006, -0.3353],
[ 1.5872, 1.7699],
...,
[ 0.3235, 0.6810],
[ 0.8720, 0.5016],
[ 0.4405, 0.2278]])
”“”
%matplotlib inline
from d2l import torch as d2l
d2l.set_figsize()
#画图 可以看到feature的第零列和label呈正相关 这里的detach是要把他从计算图里面拿出来才能转为numpy
d2l.plt.scatter(feature[:,0].detach().numpy(),labels.detach().numpy(),1)
# 该函数接受批量大小,特征矩阵和向量标签作为输入,生成batch_size的小批量
def data_iter(batch_size,feature,labels):
num_examples = len(feature)
#将列表随机打乱, 读取样本时候是随机的,没有特定顺序
indices = list(range(num_examples))
random.shuffle(indices)
for i in range(0,num_examples,batch_size):
#batch_indices是每次取的随机样本的范围,比如第1到第11个,第5到第15个这样
batch_indices = torch.tensor(indices[i:min(i+batch_size,num_examples)])
#yield是python中的构造器,会不断产生feature[batch_indices]和labels[batch_indices]
yield feature[batch_indices],labels[batch_indices]
batch_size=10
for X,y in data_iter(batch_size,feature,labels):
print(X,'\n',y)
break
# 初始化w和b,均需要计算梯度,因此后面的参数为True
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): #params是一个list,里面包含w和b
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr*param.grad/batch_size #在这里求了梯度的均值,和在均方损失里面求是一样的
param.grad.zero_() #梯度清0
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,feature,labels):
#这里'l'的形状为batch_size*1的一个向量,因为loss函数返回的是一个向量
l = loss(net(X,w,b),y) #求出小批量的损失
l.sum().backward()
sgd([w,b],lr,batch_size) #更新w和b
#计算真实的loss
with torch.no_grad(): #不需要计算梯度
#feature是整个真实的数据,传入net中,计算和真实labels的损失,前面的X是随机的小批量样本
train_loss = loss(net(feature,w,b),labels)
print(f'epoch {epoch + 1}, loss {float(train_loss.mean()):f}')
print(w)
print("w的估计误差:"+str(true_w-w.reshape(true_w.shape)))
print(b)
print("b的估计误差:"+str(true_b-b))
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
Python中的解包:https://zhuanlan.zhihu.com/p/351369448
小批量随机梯度下降:
在整个训练集上求梯度太贵也太慢了,深度学习中往往需要花费数个小时,随机采取b个样本来近似看做整体的损失
b的大小是一个很重要的超参数,对应代码里面的batch_size