写在前面的话
笔者在学习过程中,总结了一些不成熟的经验。建议在学习【轩说Pytorch】,【轩说AI】前,学好几门必修的前置内容:
- 高等数学
- 线性代数
- 概率论与数理统计
- 吴恩达深度学习课程
前置内容指的是,系统学习过,可能会遗忘,但是大体上不对后续造成影响,一旦需要深挖理论,随时回去看视频课来补充一遍。
线性回归
最简单的神经网络
神经元的含义如下
Input layer:
X
=
[
x
1
,
x
2
,
x
3
]
T
X=[x_1,x_2,x_3]^T
X=[x1,x2,x3]T
Hidden layer:
W
=
[
w
1
,
w
2
,
w
3
]
T
z
=
W
T
⋅
X
+
b
=
w
1
x
1
+
w
2
x
2
+
w
3
x
3
+
b
a
=
σ
(
z
)
W=[w_1,w_2,w_3]^T\\ z=W^T\cdot X+b=w_1x_1+w_2x_2+w_3x_3+b\\ a=\sigma(z)
W=[w1,w2,w3]Tz=WT⋅X+b=w1x1+w2x2+w3x3+ba=σ(z)
Output layer:
y
^
=
a
\hat y=a
y^=a
在线性回归问题中,激活函数
σ
\sigma
σ被省略,在Logistic回归中,
σ
\sigma
σ不能被省略
代码
-
生成数据
给定w和b,生成一些数据点 ( x , y ) (x,y) (x,y),数据点满足在 y = w ⋅ x + b y=w\cdot x+b y=w⋅x+b的基础上的一些扰动。目的是将这些数据点 ( x , y ) (x,y) (x,y)送入回归模型后,可以让网络找出正确的w和b(正确拟合出线性关系)。
def synthetic_data(w,b,num_examples): X=torch.normal(mean=0,std=1,size=(num_examples,len(w)))#X:(1000,2) y=torch.matmul(X,w)+b#y:matmul((1000,2)*(2))=matmul((1000,2)*(2,1))=(1000,1)=(1000) y+=torch.normal(0,0.01,y.shape)#y:(1000) return X,y.reshape((-1,1))#X:(1000,2),y(1000,1) true_w=torch.tensor([2,-3.4])#这里的w的维度(2)和(2,1),喂给函数的效果是一样的 true_b=4.2 features,labels=synthetic_data(true_w,true_b,1000) print(features.shape) print(labels.shape) """ torch.Size([1000, 2]) torch.Size([1000, 1]) """
-
展示数据
plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1)
-
手动生成mini batch
#小批量-取batch def data_iter(batch_size,features,labels): num_examples=len(features)#1000 indices=list(range(num_examples))#[0,1,2,3,...999] random.shuffle(indices)#[2,4,99,1,35,...] 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 next(data_iter(batch_size,features,labels)) """ (tensor([[ 0.3100, -1.1480], [ 0.9326, 1.7840], [ 0.7479, -0.4284], [ 0.8382, 2.6512], [-0.5357, 0.2354], [-0.3393, -1.1224], [-0.6993, -1.2861], [ 0.0155, 0.9641], [-1.1654, 0.7413], [ 0.4319, 3.0528]]), tensor([[ 8.7037e+00], [-7.1615e-03], [ 7.1457e+00], [-3.1387e+00], [ 2.3467e+00], [ 7.3261e+00], [ 7.1850e+00], [ 9.3840e-01], [-6.4830e-01], [-5.3213e+00]])) """
-
初始化参数w和b
w=torch.normal(0,0.01,size=(2,1),requires_grad=True) b=torch.zeros(1,requires_grad=True) print(w) print(b) """ tensor([[-0.0134], [-0.0072]], requires_grad=True) tensor([0.], requires_grad=True) """
-
构建线性回归模型(最简单的神经网络),得到输出的y_hat值
def linreg(X,w,b):#X:(bs,n),w:(n,1),b:(1) return torch.matmul(X,w)+b #这里的"+b"用到了broadcast机制 #定义模型
-
计算均方损失,用输出的y_hat和真实的label:y做L2损失
def squared_loss(y_hat,y): return (y_hat-y.reshape(y_hat.shape))**2/2
-
梯度下降,params为模型中的参数,本例中为[w,b]
#梯度下降法 def sgd(params,lr): with torch.no_grad():#固定梯度。 for param in params: param-=lr*param.grad 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): y_hat=net(X,w,b)#y_hat:(bs) l=loss(y_hat,y) l.mean().backward() sgd([w,b],lr) with torch.no_grad(): train_l=loss(net(features,w,b),labels) print(f'epoch{epoch+1},loss{float(train_l.mean()):f}') """ epoch1,loss0.029799 epoch2,loss0.000096 epoch3,loss0.000049 """
-
查看网络收敛后的w和b和真值的差距
print(true_w-w.reshape(true_w.shape)) print(true_b-b) """ tensor([ 6.5148e-04, -4.6253e-05], grad_fn=<SubBackward0>) tensor([0.0003], grad_fn=<RsubBackward1>) """
可见,可以用单个神经元完成线性回归问题。
简化代码
-
导包
import torch from torch.utils import data
-
用torch.utils.data.TensorDataset(X,Y)来生成数据集,把tensor X(1000,2)和tensor Y(1000,1)一对一地变成 [ ( T ( x 1 ) , T ( y 1 ) ) , ( T ( x 2 ) , T ( y 2 ) ) , ( T ( x 3 ) , T ( y 3 ) ) . . . . . ( T ( x 1000 ) , T ( y 1000 ) ) ] [(T(x_1),T(y_1)),(T(x_2),T(y_2)),(T(x_3),T(y_3)).....(T(x_{1000}),T(y_{1000}))] [(T(x1),T(y1)),(T(x2),T(y2)),(T(x3),T(y3)).....(T(x1000),T(y1000))],相当于一个男女结对的过程
true_w=torch.tensor([2,-3.4]) true_b=4.2 def synthetic_data(w,b,num_examples): X=torch.normal(mean=0,std=1,size=(num_examples,len(w)))#X:(1000,2) y=torch.matmul(X,w)+b#y:matmul((1000,2)*(2))=matmul((1000,2)*(2,1))=(1000,1)=(1000) y+=torch.normal(0,0.01,y.shape)#y:(1000) return X,y.reshape((-1,1))#X:(1000,2),y(1000,1) data_X,data_Y=synthetic_data(true_w,true_b,1000)#(X,Y) dataset=data.TensorDataset(data_X,data_Y) #dataset:[(x_1,y_1),(x_2,y_2),(x_3,y_3),(x_4,y_4).....(x_1000,y_1000)]
dataset[0]:
-
调用torch.utils.data.Dataloader(dataset)来产生数据迭代器 [ [ T ( x 1 , x 4 , x 6 , x 199 , x 43 ) , T ( y 1 , y 4 , y 6 , y 199 , y 43 ) ] , [ T ( x . . . . ) , T ( y . . . . ) ] . . . . . . ] [[T(x1,x4,x6,x199,x43),T(y1,y4,y6,y199,y43)],[T(x....),T(y....)]......] [[T(x1,x4,x6,x199,x43),T(y1,y4,y6,y199,y43)],[T(x....),T(y....)]......],相当于把dataset的各个(tensor(x),tensor(y))张量对 按照batchsize大小,随机组合选取出一组组的 [ T e n s o r ( X g r o u p A ) , T e n s o r ( Y g r o u p B ) ] [Tensor(X_{groupA}),Tensor(Y_{groupB})] [Tensor(XgroupA),Tensor(YgroupB)] ,相当于男女结对之后,入住楼房的过程 。并且需要注意的是,dataloader是迭代器,不像dataset支持下标随机访问,必须通过迭代的方式访问“下一batch”。
batch_size=10 data_loader=data.DataLoader(dataset,batch_size,shuffle=True)
next(iter(data_loader)):
-
用API产生model
from torch import nn net =nn.Sequential(nn.Linear(2,1))
-
初始化模型参数
net[0].weight.data.normal_(0,0.01) net[0].bias.data.fill_(0)
pytorch中.weight.data与.weight
.weight.data:得到的是一个Tensor的张量(向量),不可训练的类型
.weight:得到的是一个parameter的变量,可以计算梯度的 -
定义均方损失: L ( X , Y ) = 1 n Σ ( X i − Y i ) 2 L(X,Y)=\frac{1}{n}\Sigma(X_i-Y_i)^2 L(X,Y)=n1Σ(Xi−Yi)2
loss=nn.MSELoss()
-
定义梯度下降优化器
trainer=torch.optim.SGD(net.parameters(),lr=0.03)
-
训练网络
num_epochs=3
for epoch in range(num_epochs):
for X,y in data_loader:
y_hat=net(X)
l=loss(y_hat,y)
trainer.zero_grad()
l.backward()
trainer.step()
l=loss(net(data_X),data_y)
print(f'epoch {epoch+1},loss {l:f}')
-
查看网络参数
print(list(net.parameters()))