【1】预备知识书籍
1.1 数据操作
1)x=torch.arange(12) #包含以0开始的前12个整数的行向量,默认为整数。也可指定创建类型为浮 点数。
2)x.shape #可通过张量的shape属性来访问张量(沿每个轴的长度)的形状。
3)x.numel() #返回数组中元素的个数
4)torch.Size([12]) #检测它的大小
5)X=x.reshape(3,4) #改变张量的形状而不改变元素数量和元素值。
#等价于x.reshape(-1,4)或x.reshape(3,-1)
6)#torch.zeros() #全0
#torch.ones() #全1
7)torch.randn(3,4) #每个元素都从均值为0、标准差为1的标准高斯分布(正太分布)中随机采样
8)#torch.cat((X,Y),dim=0) #将多个张量连结在一起,并给出沿哪个轴连结。
9)############广播机制
#大多数情况下,我们将沿着数组中长度为1的轴进行广播。将元素复制,不是填0。
#例:a=[[0],[1],[2]] b=[[0,1]] ,
#广播机制后:a=[[0,0],[1,1],[2,2]] b=[[0,1],[0,1],[0,1]]
10)#X+=Y #减少操作的内存开销
11)a=torch.tensor([3.5]) #要将大小为1的张量转换为python标量,我们可以调用item函数或python 的内置函数a.item(),float(a),int(a)
1.2 数据预处理:处理csv文件
import os
import pandas as pd
import torch
1)创建一个人工数据集,并存储在csv(逗号分隔值)文件
os.makedirs(os.path.join('..','data'),exist_ok=True)
data_file=os.path.join('..','data','house_tiny.csv')
with open(data_file,'w') as f:
f.write('NumRooms,Alley,Price\n')#列名
f.write('NA,Pave,127500\n')#每行表示一个数据样本
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
2)读取csv(逗号分隔值)文件
data=pd.read_csv(data_file)
print(data)
3)“NaN”项代表缺失值。为了处理缺失的数据,典型的方法包括 插值法 和 删除法 ,其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。这里,我们将考虑插值
inputs,outputs=data.iloc[:,0:2],data.iloc[:,2]#将data中第0、1列拿出来为inputs,第2列为outputs
inputs=inputs.fillna(inputs.mean())#将inputs中为NA的填充,此处填充为inputs的均值
print(inputs)
4)对于inputs中的类别值或离散值,我们将“NaN"视为一个类别
inputs=pd.get_dummies(inputs,dummy_na=True)#get_dummies:方法是为了将原本不好区分的数据进行再次打标签区分,从而得到更细的数据(类似one-hot编码)
#print(inputs)
5)现在inputs和outputs中所有条目都是数值类型,它们可以转换为张量格式。
X,y=torch.tensor(inputs.values),torch.tensor(outputs.values)
1.3 线性代数
1)计算总和或均值时保持轴数不变
A=torch.arange(40).reshape(2,5,4)
sumA=A.sum(axis=1,keepdim=True)
2)某个轴计算A元素的累积总和
A.cumsum(axis=0)
3)点积是相同位置的按元素乘积的和:torch.dot
4)矩阵向量积Ax是一个长为m的列向量,其i元素是点积ax:torch.mv(A,x)
5)矩阵A、B的乘法:torch.mm(A,B)
6)L1范数,表示为向量元素的绝对值之和:torch.abs(u).sum(); L2范数是向量元素平方和的平方根:torch.norm
7)矩阵的 弗罗贝尼乌斯范数(Frobenius norm)是矩阵元素的平方和的平方根:torch.norm(torch.ones(4,9))
1.4 线性回归+基础优化算法
一、线性回归
1)总结
1. 线性回归是对n维输入的加权,外加偏差。
2. 使用平方损失来衡量预测值和真实值的差异。
3. 线性回归有显示解。
4. 线性回归可以看做是单层神经网络。
二、基础优化方法
1)梯度下降
1. 挑选一个初始值
w
0
w_0
w0。
2. 重复迭代参数t = 1,2,3
w
t
=
w
t
−
1
−
θ
∂
ℓ
∂
w
t
−
1
w_t = w_{t-1} - \theta {\partial \ell \over \partial w_{t-1}}
wt=wt−1−θ∂wt−1∂ℓ
3. 沿梯度方向将增加损失函数值
4. 学习率:步长的超参数。(不能太小,也不能太大)
学
习
率
是
超
参
数
\textcolor{red}{学习率是超参数}
学习率是超参数
2)小批量随机梯度下降
1. 在整个训练集上算梯度太贵,我们可以随机采样b个样本
i
1
i_1
i1、
i
2
i_2
i2、…
i
b
i_b
ib来近似损失
2.
1
b
Σ
ℓ
(
X
i
,
y
i
,
w
)
{1\over b}\Sigma\ell(X_i,y_i,w)
b1Σℓ(Xi,yi,w)
2. b是批量大小,另外一个重要的超参数。
b
(
批
量
大
小
)
是
超
参
数
\textcolor{red}{b(批量大小)是超参数}
b(批量大小)是超参数
4. 选择批量大小:(1)不能太小。每次计算量太小,不适合并行来最大利用计算资源。
(2)不能太大。内存消耗增加,浪费计算,例如如果所有样本都是相同的。
3)总结
1. 梯度下降通过不断沿着反梯度方向更新参数求解。
2. 小批量随机梯度下降是深度学习默认的求解算法。
3. 两个重要的超参数是批量大小和学习率。
三、线性回归的从零开始实现
我们将从零开始实现整个方法,包括数据流水线、模型、损失函数、和小批量随机梯度下降优化器。
import matplotlib.pyplot as plt
import torch
import random
from d2l import torch as d2l
根据带有噪声的线性模型构造一个人造数据集。我们使用线性模型参数
w
=
[
2
,
−
3.4
]
T
w=[2,-3.4] ^T
w=[2,−3.4]T、
b
=
4.2
b=4.2
b=4.2和噪声项
ϵ
\epsilon
ϵ生成数据集及其标签:
y
=
X
w
+
b
+
ϵ
y=Xw+b+\epsilon
y=Xw+b+ϵ
def synthetic_data(w,b,num_examples):
"""生成 y = Xw + b + 噪声。"""
X=torch.normal(0,1,(num_examples,len(w)))
y=torch.matmul(X,w)+b
#y加入噪声
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)
######################
#第0个样本
print('features',features[0],'\nlabel',labels[0])
#图表示
d2l.set_figsize()
##detach(),是因为要从图中detach出来,不要再求梯度,才能转到numpy中。
d2l.plt.scatter(features[:,1].detach().numpy(),
labels.detach().numpy(),1)
d2l.plt.show()
#######################
定义一个data_iter函数,该函数接收批量大小,特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。(给我一些样品标号,每次随机从里面选取batch_size个样本返回出来。)
#batch_size:批量大小;features:特征;labels标签。
def data_iter(batch_size,features,labels):
num_examples=len(features)
indices=list(range(num_examples))
#这些样本是随机读取的,没有特定的顺序。shuffle:打乱
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]
##yield就是return返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后开始。
############################
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)#随机初始化均值为0,方差为0.01的正太分布。
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中包含所有的w和b
'''小批量随机梯度下降'''
#不计算梯度,更新时不参与梯度计算
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)#X和y的小批量损失
#因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起
#并以此计算关于[w,b]的梯度
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().item())}')
比较真实参数和通过训练学到的参数来评估训练的成功程度
print(f'w的估计误差:{true_w-w.reshape(true_w.shape)}')
print(f'b的估计误差:{true_b-b}')
四、线性回归的简洁实现
import torch
import numpy as np
from torch.utils import data
from d2l import torch as d2l
from torch import ne, nn #nn是神经网络的缩写
#通过使用深度学习框架来简洁地实现 线性回归模型 生成数据集
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数据迭代器。'''
#假设已经有features,labels可以做成一个list存到TensorDataset中,data_arrays->(features,labels)
dataset=data.TensorDataset(*data_arrays)
return data.DataLoader(dataset,batch_size,shuffle=is_train)#随机挑选batch_size个样本出来
batch_size=10
data_iter=load_array((features,labels),batch_size)
#print(next(iter(data_iter)))
#使用框架的预定义好的层
net=nn.Sequential(nn.Linear(2,1))#2:输入维度,1:输出维度
#Sequential:容器。将层按顺序放在一起
#初始化模型参数
net[0].weight.data.normal_(0,0.01)#net[0]即Sequential中的第0层
net[0].bias.data.fill_(0)
#计算均方误差使用的是MSELoss类,也称为平方L2范数
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)
trainer.zero_grad()
l.backward()
trainer.step()
l=loss(net(features),labels)
print(f'epoch {epoch+1},loss{l:f}')
四、线性回归QA
1)不管是gd(梯度下降)还是sgd怎么找到合适的学习率?
答:1. 用对学习率不那么敏感的算法,如Adam。2.通过合理的参数初始化。3. 有找学习率方法(后面)。
2)batch_size是否会最终影响模型结果?
答:小可以,对收敛好。大不行。噪音对网络会更鲁棒,泛化性更好。
3)训练过程中,过拟合和欠拟合情况下,学习率和批次该如何进行调整?
答:学习率和批次一般不影响收敛。