讲解
须导入和函数库
mport torch
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
设定目标多项式的参数
本次拟合的是三次多项式
y
=
a
1
x
1
+
a
2
x
2
+
a
3
x
3
+
b
y=a_1x_1+a_2x_2+a_3x_3+b
y=a1x1+a2x2+a3x3+b
所以需要 w_target = [a1, a2, a3] 和 b_target
# 设定target参数
w_target = torch.tensor([0.5, 3, 2.4]).unsqueeze(1) # unsqueeze 升维
b_target = torch.tensor([0.9])
生成w_target
用到的.unsqueeze(axis
)函数为升维函数。
如果不加该函数,w_target
的 shape
是 torch.Size([3])
,并不是向量(矩阵)。
.unsqueeze(1)
是在w_target
的第二维的位置加上一个维度,变为torch.Size([3, 1])
,变为向量(矩阵)。
关于升维降维函数的讲解见本人的另一个博客增加维度或者减少维度 ——a.squeeze(axis) 和 a.unsqueeze(axis)
生成训练集
通过batch_size=256
确定训练集内元素的个数
生成 x 的序列(以向量存在)
目标: 生成随机、有序的x的序列( 以向量存在 )
实现思路:
- 生成
batch_size
数量的随机数(tensor
类型) - 类型转换:
tensor
==>numpy
的ndarray
- 对生成的随机数(
ndarray
类型)排序 - 类型转换:
ndarray
==>tensor
- 升维,构成矩阵
random = torch.randn(batch_size) # 生成batch_size个随机数
random = random.numpy()
random.sort() # 升序排列random,涉及类型转换,tensor 2 array
random = torch.from_numpy(random) # random 类型转换 array 2 tensor
random = random.unsqueeze(1) # 升维构成矩阵
计算 y 的实际值
这里有一个很重要的知识点:矢量化编程。
矢量化编程简单来说,就是用 矩阵运算 代替 显性的for循环 。
矢量化编程的作用:能极大地提高运行速度(划重点:极大)
在通过
y
=
a
1
x
1
+
a
2
x
2
+
a
3
x
3
+
b
y=a_1x_1+a_2x_2+a_3x_3+b
y=a1x1+a2x2+a3x3+b计算y时,我们用矢量化编程来代替显性的for循环。
# 原理讲解
# torch.Size([256, 3])
# x = [[x_1 x_1^2 x_1^3]
# [x_2 x_2^2 x_2^3]
# [x_3 x_3^2 x_3^3]
# ...
# [x_256 x_256^2 x_256^3]]
#
# torch.Size([3, 1])
# w_target =([a_1],
# [a_2],
# [a_3]])
# 根据**线性代数**知识:
# y = x * w_target + b
# torch.Size([256, 1])
代码实现如下:
x = torch.cat([ random ** i for i in range(1, 4)], 1) # 生成x, x^2, x^3 的列矩阵
y = x.mm(w_target) + b_target[0] # 生成真实y
关于 x = torch.cat([ random ** i for i in range(1, 4)], 1)
的原理,在本人的另一篇博客专门很仔细地讲过 拼接多个tensor:torch.cat((A,B),axis),故此处不再赘述。
GPU or CPU
这里首先要定义一个人为设定的超参数DEVICE
DEVICE = ("cuda" if torch.cuda.is_available() else "cpu")
之后就可以用.to(DEVICE)
将tensor
放到相应的GPU
或CPU
上了。
return x.to(DEVICE), y.to(DEVICE)
生成训练集的整体代码如下:
DEVICE = ("cuda" if torch.cuda.is_available() else "cpu")
# 生成训练集
def get_batch(batch_size=256):
# 生成随机、有序的x的序列(组成 -> 向量)
random = torch.randn(batch_size) # 生成batch_size个随机数
random = random.numpy()
random.sort() # 升序排列random,涉及类型转换,tensor 2 array
random = torch.from_numpy(random) # random 类型转换 array 2 tensor
random = random.unsqueeze(1) # 升维
x = torch.cat([ random ** i for i in range(1, 4)], 1) # 生成x, x^2, x^3 的列矩阵
y = x.mm(w_target) + b_target[0] # 生成真实y
return x.to(DEVICE), y.to(DEVICE)
搭建网络结构
class poly_model(nn.Module):
def __init__(self):
super(poly_model, self).__init__()
self.poly = nn.Linear(3, 1) # 输入三维,输出一维
def forward(self, x):
out = self.poly(x)
return out
model = poly_model().to(DEVICE)
model = poly_model().to(DEVICE)
将网络实例化,使其有了输入输出接口。
另外,注意每个网络层的输入输出维度。
这个网络只有一个网络层。输入维度为 3 ,输出维度为 1。体现在self.poly = nn.Linear(3, 1)
的两个参数3
和1
上。
最后的model
为:
poly_model(
(poly): Linear(in_features=3, out_features=1, bias=True)
)
定义损失函数和优化器
这部分没什么可说的。
# ------------定义损失函数和优化函数--------------
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)
训练网络
# 训练网络 直到误差小于1e-3
epoch = 0
while True:
# get data
batch_x, batch_y = get_batch() # 得到训练集
# forward
predict = model(batch_x) # 用模型计算predict
loss = criterion(predict, batch_y) #计算损失函数,前者为预测值,后者为真实值
# backward pass
optimizer.zero_grad() # 优化器梯度归零
loss.backward()
optimizer.step()
epoch+=1
if (epoch-1)%500 == 0:
batch_x = batch_x.cpu()
batch_y = batch_y.cpu()
predict = predict.cpu()
predict = predict.detach().numpy()
print('epoch:{}'.format(epoch))
print('loss:{}',format(loss))
predict = torch.from_numpy(predict)
batch_x = batch_x.cuda()
batch_y = batch_y.cuda()
output = predict.cuda()
# --------------------------------------------------------------------------
if loss < 1e-3:
print('Done!')
print('epoch:{}'.format(epoch))
break
前向传播可反向传播没什么可说的。如果想看可以去本人的另一篇博客 基于python和pytorch的线性拟合(完结),那里说得详细些。
这里要讨论的一点,是if (epoch-1)%500 == 0:
这部分中,GPU --> CPU 然后 CPU -->GPU,在这儿是没什么意义的(单纯的输出数值在GPU上也行)。
但是如果想要画图的话GPU --> CPU 然后 CPU -->GPU的过程就是必须的(画图时要求的数据类型为ndarray
,再跑下一个epoch
的时候数据类型又要变回tensor
)。
这里依旧保留GPU --> CPU 然后 CPU -->GPU的过程,就是为了说明以下常规流程(当然像现在的情况可以简化)。
显示结果
# GPU ==> CPU
batch_x = batch_x.cpu()
batch_y = batch_y.cpu()
output = predict.cpu()
output = output.detach().numpy()
plt.plot(batch_x.numpy()[:,0], batch_y.data.numpy(), color='b', label='real curve')
plt.show()
唯一要说的.detach(),在 基于python和pytorch的线性拟合(完结)中也讲到了,这里也不说了。
查看训练后的参数
for param in model.named_parameters():
print(param[0]) # poly.weight
print(param[1]) # poly.bias
要说的在注释里了。
整体源代码
代码如下
import torch
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 设定target参数
w_target = torch.tensor([0.5, 3, 2.4]).unsqueeze(1) # unsqueeze 升维
b_target = torch.tensor([0.9])
DEVICE = ("cuda" if torch.cuda.is_available() else "cpu")
# 返回x y
def get_batch(batch_size=256):
random = torch.randn(batch_size) # 生成batch_size个随机数
random = random.numpy()
random.sort() # 升序排列random,涉及类型转换,tensor 2 array
random = torch.from_numpy(random) # random 类型转换 array 2 tensor
random = random.unsqueeze(1) # 升维
x = torch.cat([random ** i for i in range(1, 4)], 1) # 生成x, x^2, x^3 的列矩阵
y = x.mm(w_target) + b_target[0] # 生成真实y
return x.to(DEVICE), y.to(DEVICE)
# -------------------------定义模型---------------------------
class poly_model(nn.Module):
def __init__(self):
super(poly_model, self).__init__()
self.poly = nn.Linear(3, 1) # 输入三维,输出一维
def forward(self, x):
out = self.poly(x)
return out
model = poly_model().to(DEVICE)
# ------------定义损失函数和优化函数--------------
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1e-3)
# 训练网络 直到误差小于1e-3
epoch = 0
while True:
# get data
batch_x, batch_y = get_batch() # 得到数据集
# forward
predict = model(batch_x) # 用模型计算predict
loss = criterion(predict, batch_y) # 计算损失函数,前者为预测值,后者为真实值
# backward pass
optimizer.zero_grad() # 优化器梯度归零
loss.backward()
optimizer.step()
epoch += 1
if (epoch - 1) % 500 == 0:
batch_x = batch_x.cpu()
batch_y = batch_y.cpu()
predict = predict.cpu()
predict = predict.detach().numpy()
print('epoch:{}'.format(epoch))
print('loss:{}', format(loss))
predict = torch.from_numpy(predict)
batch_x = batch_x.cuda()
batch_y = batch_y.cuda()
output = predict.cuda()
# --------------------------------------------------------------------------
if loss < 1e-3:
print('Done!')
print('epoch:{}'.format(epoch))
break
# 去cuda化
batch_x = batch_x.cpu()
batch_y = batch_y.cpu()
output = predict.cpu()
output = output.detach().numpy()
plt.plot(batch_x.numpy()[:, 0], batch_y.data.numpy(), color='b', label='real curve')
plt.show()
for param in model.named_parameters():
print(param[0])
print(param[1])
结果
# epoch:1
# loss:{} 107.65440368652344
# epoch:501
# loss:{} 0.17065830528736115
# epoch:1001
# loss:{} 0.02675577811896801
# epoch:1501
# loss:{} 0.010034649632871151
# epoch:2001
# loss:{} 0.0034075819421559572
# epoch:2501
# loss:{} 0.0011402576928958297
# Done!
# epoch:2522
# poly.weight
# Parameter containing:
# tensor([[0.4681, 2.9867, 2.4066]], device='cuda:0', requires_grad=True)
# poly.bias
# Parameter containing:
# tensor([0.9329], device='cuda:0', requires_grad=True)