源代码
import torch
import matplotlib.pyplot as plt
import random
from utils import *
%matplotlib inline
def synthtic_data(w,b,num_examples):
X = torch.normal(0,0.5,(num_examples,len(w)))
y = torch.matmul(X,w) + b
return X,y.reshape((-1,1))
true_w = torch.tensor([-3.,-4.2])
true_b = 3.4
features,labels = synthtic_data(true_w,true_b,1000)
print(f"features: {features[0]},\nlabels: {labels[0]}")
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-y_hat)**2 / 2
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.01
num_epochs = 10
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},train_l: {train_l.mean()}")
具体展开
概述
自己选取真值,基于其生成了一群数据,并用一个简单且直感的回归模型,以验证线性回归模型有效性
对数据的准备
def synthtic_data(w,b,num_examples):
X = torch.normal(0,0.5,(num_examples,len(w)))
y = torch.matmul(X,w) + b
return X,y.reshape((-1,1))
- 对尺寸的选择:torch与numpy里一般将矩阵形状定义为
(样本数量,特征数量)
(每行为一个样本)(X[i]
)来获得第i个样本) - 有专门的矩阵乘法计算函数
torch.matnul()
人造数据
true_w = torch.tensor([-3.,-4.2])
true_b = 3.4
features,labels = synthtic_data(true_w,true_b,1000)
torch.tensor([-3,-4.2])
这里造出的是一维张量,可以被按需求解释为行或列向量。
准备dataloader
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
- 青春版dataloader的严重问题:它要求我们将所有数据加载到内存中,并执行大量的随机内存访问
用torch提供的dataloader
from torch.utils import data
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)
(手动封装一个更自定义化的dataloader而已)
大概顺序:原始的样本矩阵需要先转化为torch的dataset对象(如初始化__init__
等)
然后基于dataset制作dataloader(如加入shuffle设置,batch_size)设置等
最后对接模型的是dataloader
一个对其效果的简单验证
type(data_iter)
查return得到的数据类型,是torch.utils.data.dataloader.DataLoader
next(iter(data_iter))
,得到的是形如的存在:
[tensor([[ 0.5695, -0.1118],
[ 0.1460, -0.3379],
[-0.8529, 0.1165]]),
tensor([[ 2.1610e+00],
[ 4.3810e+00],
[ 5.4694e+00]])]
可见与之前的青春版dataloader的效果是一样的
另:
iter()
将iteratable转换为iterator
next()
提取iterator的当前位置数据,并将其移动到下一个位置
准备参数与模型
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
用torch建立参数形状与性质,requires_grad
是在这里声明的
处理优化函数
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}')
说些什么吧!