线性回归的从零实现(借助pytorch)
import torch from torch import nn import random net=nn.Linear(2,1) print(net.type) print(torch.__version__) za=[3,4,5,6,7,8,9,10] random.shuffle(za)#把za打乱顺序 bt=torch.tensor(za[1:7]) mt=list(range(20)) mt=torch.tensor(mt)#注意此处只有转化为torch.tensor()后 #才能在后续的mt(bt运行) print(bt,bt.type()) print(mt,type(mt)) print(mt[bt])#可继续参考下面模块的代码
# %matplotlib inline #显示图示默认嵌入jupyter notebook中来显示 import random import torch from d2l import torch as d2l #import d2lzh_pytorch as d2l #有网友这样说 #如果没有d2l可以用pip安装,或者在jupyter notebook中安装的话用!pip install d2l命令安装
\'\'\'
torch.normal(A, B ,size(C, D), requires_grad=True)
A表示均值,B表示标准差 ,C代表生成的数据行数,D表示列数,requires_grad=True表示对导数开始记录,不需要记录的话可以忽略。默认是False
\'\'\'
def synthetic_data(w,b,num_examples):
"""生成y=Xw+b+噪声"""
X=torch.normal(0,1,(num_examples,len(w)))#X为1000行2列的矩阵,0是均值,1是标准差。
y=torch.matmul(X,w)+b#X与w相乘,并加上b,可以用广播机制来加b,结果是1000行1列的列向量矩阵
y+=torch.normal(0,0.01,y.shape)
# print(y.shape,y.reshape(-1,1).shape)#torch.Size([1000]),torch.Size([1000, 1])说明y本身就是一个列向量,reshape(-1,1)后变成了若干行1列的二维矩阵
# print(y.shape,y.shape[0],sep=\'\n\')#torch.Size([1000]),1000,如果是y.shape[1]就报错了,因为y只有行这个维度。
# print(y.size())#torch.Size([1000])
# print(y.reshape(-1,1).shape)#torch.Size([1000, 1])
# print(y.reshape((-1,1)).shape)#torch.Size([1000, 1])
return X,y.reshape((-1,1))
true_w=torch.tensor([2,-3.4]) #true_w是个2行1列的矩阵,是个列向量
true_b=4.2
features,labels=synthetic_data(true_w,true_b,1000)
# print(len(features),len(labels))
print(features.shape,labels.shape)
print(\'features:\',features[0],\'\nlabel:\',labels[0])
d2l.set_figsize() d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1)
\'\'\'每次从所给的样本中选取batch个\'\'\'
def data_iter(batch_size,features,labels):
num_examples=len(features)
indices=list(range(num_examples))
#这些样本是随机读取的,没有特定的顺序
random.shuffle(indices)#打乱顺序后得到一个有打乱下标的list
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
num_examples=15
indices=list(range(15))
#这些样本是随机读取的,没有特定的顺序
random.shuffle(indices)
for i in range(0,15,10):
batch_indices=torch.tensor(indices[i:min(i+10,15)])
print(batch_indices)
w=torch.normal(0,0.01,size=(2,1),requires_grad=True)
b=torch.ones(1,requires_grad=True)
def linreg(X,w,b):
"""线性回归模型"""
return torch.matmul(X,w)+b
# print(w.size(),w.shape)
def squared_loss(y_hat,y):
"均方误差"
return (y_hat-y.reshape(y_hat.shape))**2/2
def sgd(params,lr,batch_size):#params里面包括w和b参数
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param-=lr*param.grad/batch_size#这个param又可能是w,也有可能是b;
#此处的除以batch_size.是因为之前的loss没有求均值,此处求均值。因为乘法对梯度来说是一个线性关系,
#所以,除以batch_size除在上面和下面效果一样。也因为我们计算的梯度是一个批量样本的总和,所以用批量大小
#来归一化步长,这样步长就不会取决于我们对批量大小的选择。
param.grad.zero_()#pytorch会不断累加变量的梯度,故每更新一次参数,就要让其对应的梯度清零。
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)#X和y的小批量损失
#因为1 形状是(batch_size,1),而不是一个标量。1中的所有元素被加到
#并一次计算关于[w,b]的梯度
l.sum().backward()#求和之后算梯度;求和本身让l以标量的形式变现出来;求梯度对于l中的每一个分量都是单独求的;
#求和之后后面会除batch_size可以计算均值。因为向量求梯度得矩阵;要同通过sum()转化为标量,然后再求梯度。
#不求和的话是个向量,梯度算下来就变成矩阵了,形状没法对应。这里点backward()之后,你在sgd优化算法里就可以用
#相应参数的点grad计算。
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(\'w的估计误差:{}\'.format({true_w-w.reshape(true_w.shape)}))
print(\'b的估计误差:{}\'.format({true_b-b}))
python打印的时候print(f"*******") 的括号里的 f\' \' 的意思 ?
python的print字符串前面加f表示格式化字符串,加f后可以在字符串里面使用用花括号括起来的变量和表达式,如果字符串里面没有表达式,那么前面加不加f输出应该都一样.Python3.6新增了一种f-字符串格式化.
格式化的字符串文字前缀为’f’和接受的格式字符串相似str.format()。它们包含由花括号包围的替换区域。替换字段是表达式,在运行时进行评估,然后使用format()协议进行格式化。
formatted string literals, 以 f 开头,包含的{}表达式在程序运行时会被表达式的值代替。