【Python学习】基于pytorch和pysimplegui实现中国人口预测算法部署
这里假设读者已经在Windows下安装好Anaconda并且已经初步学会使用conda命令和jupyter notebook。
1 环境与Package准备
读者可以参考我之前的博客【Python学习】纯终端命令开始你的Anaconda安装与Python环境管理当中的Python环境管理部分。
1.1 创建Python3.6版本的环境
读者如果已经掌握了conda管理python环境,并且当前使用的python版本为python3,则可以跳过这部分。
(1) 在终端输入以下创建环境命令
conda create -n py36 python=3.6 #python=3.6指定python版本
输入"y",按回车键确认,如下图
1.2 激活Python3.6版本的环境
conda activate py36 # 这里py36是环境名
如下图,环境由默认的base切换到py36
1.3 安装用到的package
1.3.1 安装jupyter notebook、pytorch
安装jupyter notebook:
conda install jupyter
安装pytorch:
conda install pytorch torchvision cpuonly -c pytorch
1.3.2 安装pysimplegui、matplotlib
安装pysimplegui:
pip install pysimplegui
安装matplotlib:
pip install matplotlib
2 数据准备
数据来源:中国历年人口总数统计
选取1959年至2018年的中国人口数据作为训练样本
3 代码编写
打开jupyter notebook:
jupyter notebook
3.1 数据分析
创建jupyter notebook文件test_cpa.ipynb
在第1个cell导入相应包:
# 导入相应包
import torch
import numpy as np
import torch.nn.functional as F
import matplotlib.pyplot as plt
%matplotlib inline
在第2个cell绘图分析:
# 获取数据
year = torch.unsqueeze(torch.range(start=1959, end=2018, step=1, out=None), dim=1)
year = year - 1958 # year-1958,假设1959年为第1年
cp = torch.unsqueeze(torch.tensor([6.55, 6.67, 6.6, 6.66, 6.82, 6.98, 7.15, 7.35, 7.55,
7.75, 7.96, 8.18, 8.41, 8.62, 8.82, 9.0, 9.16, 9.31,
9.43, 9.56, 9.69, 9.81, 9.94, 10.09, 10.23, 10.37, 10.51,
10.67, 10.84, 11.02, 11.19, 11.35, 11.51, 11.65, 11.78, 11.92,
12.05, 12.18, 12.3, 12.42, 12.53, 12.63, 12.72, 12.8, 12.88,
12.96, 13.04, 13.11, 13.18, 13.25, 13.31, 13.38, 13.44, 13.51,
13.57, 13.64, 13.71, 13.79, 13.86, 13.93]), dim=1) # 单位:亿
plt.scatter(year.data.numpy(), cp.data.numpy())
plt.show()
绘图结果如下:
3.2 模型搭建
这里主要使用感知机模型,感知机算法可以参考博客:李航《统计学习方法》第二章——用Python实现感知器模型(MNIST数据集)
创建cpa.py文件,在该文件中书写模型类:
该文件可以分为三步进行描述:
- 第1步:导入相应包;
- 第2步:书写PAnet类的构造函数__init__
- 第3步,书写PAnet类的forward函数
#!/usr/bin/env python
# coding: utf-8
# # 中国人口分析
# **作者**:陈艺荣
# **依赖**:python3.6、pytorch1.3.0
# 导入相应包
import torch
import torch.nn.functional as F
class PAnet(torch.nn.Module):
def __init__(self, n_feature, n_hidden, n_output, hidden_num=1):
super(PAnet, self).__init__()
self.hidden1 = torch.nn.Linear(n_feature, n_hidden) # hidden1 layer
self.hidden2 = torch.nn.ModuleList([torch.nn.Linear(n_hidden, n_hidden) for i in range(hidden_num)])
self.predict = torch.nn.Linear(n_hidden, n_output) # output layer
def forward(self, x):
x = F.relu(self.hidden1(x))
for i, h in enumerate(self.hidden2):
x = F.relu(h(x))
x = self.predict(x) # linear output
return x
3.3 训练模型,调参
在3.1 数据分析的test_cpa.ipynb文件的基础上,建立第3个cell,设置随机种子,并且导入PAnet类
torch.manual_seed(1) # reproducible
from cpa import PAnet
建立第4个cell,定义训练过程:
def train(EPOCH, LR, HIDDEN_SIZE, HIDDEN_LAYERS):
'''
EPOCH 训练次数
LR 学习率
HIDDEN_SIZE 隐藏层网络宽度
HIDDEN_LAYERS 隐藏层深度
'''
time = np.arange(EPOCH) # 产生自变量
loss_list = []
cn_panet = PAnet(n_feature=1, n_hidden=HIDDEN_SIZE, n_output=1, hidden_num=HIDDEN_LAYERS-1) # define the network
print(cn_panet) # net architecture
optimizer = torch.optim.SGD(cn_panet.parameters(), lr=LR) # 调小学习率
loss_func = torch.nn.MSELoss() # this is for regression mean squared loss
for t in range(EPOCH):
prediction = cn_panet(year) # input x and predict based on x
loss = loss_func(prediction, cp) # must be (1. nn output, 2. target)
loss_list.append(loss)
optimizer.zero_grad() # clear gradients for next train
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
return time, loss_list
建立第5个cell,对不同的学习率进行实验
# Hyper Parameters
EPOCH = 5000 # 训练次数
LR = 0.002 # 学习率
HIDDEN_SIZE = 30 # 隐藏层网络宽度
HIDDEN_LAYERS = 5 # 隐藏层深度
time, loss_list_02 = train(EPOCH, 0.2, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_0005 = train(EPOCH, 0.005, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_0002 = train(EPOCH, 0.002, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_0003 = train(EPOCH, 0.003, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_0004 = train(EPOCH, 0.004, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_0001 = train(EPOCH, 0.001, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_00002 = train(EPOCH, 0.0002, HIDDEN_SIZE, HIDDEN_LAYERS)
time, loss_list_000002 = train(EPOCH, 0.00002, HIDDEN_SIZE, HIDDEN_LAYERS)
建立第6个cell,对不同的学习率获得的结果进行绘图分析
plt.plot(time, loss_list_0001, 'g-', label='lr=0.001')
plt.plot(time, loss_list_0002, 'b-', label='lr=0.002')
plt.plot(time, loss_list_0003, 'k-', label='lr=0.003')
plt.plot(time, loss_list_0004, 'y-', label='lr=0.004')
plt.plot(time, loss_list_0005, 'r-', label='lr=0.005')
plt.legend(loc='lower right') # 说明图例 # loc='lower right' 设置图例放置位置
建立第7个cell,截取100个epoch之后的结果进行比较分析:
plt.plot(time[100:], loss_list_0001[100:], 'g-', label='lr=0.001')
plt.plot(time[100:], loss_list_0002[100:], 'b-', label='lr=0.002')
plt.plot(time[100:], loss_list_0003[100:], 'k-', label='lr=0.003')
plt.plot(time[100:], loss_list_0004[100:], 'y-', label='lr=0.004')
plt.plot(time[100:], loss_list_0005[100:], 'r-', label='lr=0.005')
plt.legend(loc='lower right') # 说明图例 # loc='lower right' 设置图例放置位置
这里依据上图对学习率进行简单分析:
可以看到,当学习率lr=0.005时,模型的loss下降到5就不再下降了;
当学习率lr=0.001时,模型的loss下降速度相对较慢;
当学习率lr=0.002或0.003时,模型的loss下降较快且接近0;
3.4 使用最优的参数训练模型并且保存
这里假设最优的超参数为:
- EPOCH = 5000 # 训练次数
- LR = 0.003 # 学习率
- HIDDEN_SIZE = 32 # 隐藏层网络宽度
- HIDDEN_LAYERS = 8 # 隐藏层深度
新建jupyter notebook文件test_cpa_best_model.ipynb,在第1个cell导入包:
# 导入相应包
import torch
import numpy as np
import torch.nn.functional as F
import matplotlib.pyplot as plt
from cpa import PAnet
%matplotlib inline
建立第2个cell,放置训练数据
# 获取数据
year = torch.unsqueeze(torch.range(start=1959, end=2018, step=1, out=None), dim=1)
year = year - 1958 # year-1958,假设1959年为第1年
cp = torch.unsqueeze(torch.tensor([6.55, 6.67, 6.6, 6.66, 6.82, 6.98, 7.15, 7.35, 7.55,
7.75, 7.96, 8.18, 8.41, 8.62, 8.82, 9.0, 9.16, 9.31,
9.43, 9.56, 9.69, 9.81, 9.94, 10.09, 10.23, 10.37, 10.51,
10.67, 10.84, 11.02, 11.19, 11.35, 11.51, 11.65, 11.78, 11.92,
12.05, 12.18, 12.3, 12.42, 12.53, 12.63, 12.72, 12.8, 12.88,
12.96, 13.04, 13.11, 13.18, 13.25, 13.31, 13.38, 13.44, 13.51,
13.57, 13.64, 13.71, 13.79, 13.86, 13.93]), dim=1) # 单位:亿
plt.scatter(year.data.numpy(), cp.data.numpy())
plt.show()
建立第3个cell,构建训练、保存模型函数:
# 训练并且保存网络函数
def trainandsave(EPOCH, LR, HIDDEN_SIZE, HIDDEN_LAYERS):
'''
EPOCH 训练次数
LR 学习率
HIDDEN_SIZE 隐藏层网络宽度
HIDDEN_LAYERS 隐藏层深度
'''
time = np.arange(EPOCH) # 产生自变量
loss_list = []
cn_panet = PAnet(n_feature=1, n_hidden=HIDDEN_SIZE, n_output=1, hidden_num=HIDDEN_LAYERS-1) # define the network
print(cn_panet) # net architecture
optimizer = torch.optim.SGD(cn_panet.parameters(), lr=LR) # 调小学习率
loss_func = torch.nn.MSELoss() # this is for regression mean squared loss
for t in range(EPOCH):
prediction = cn_panet(year) # input x and predict based on x
loss = loss_func(prediction, cp) # must be (1. nn output, 2. target)
loss_list.append(loss)
optimizer.zero_grad() # clear gradients for next train
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
torch.save(cn_panet, 'cn_panet.pkl') # save entire net
print("成功保存网络")
return time, loss_list
建立第4个cell,定义读取模型函数:
# 读取网络模型函数
def restore_net(netname):
# restore entire net1 to net2
net = torch.load(netname)
return net
建立第5个cell,使用最优参数训练模型:
# 根据最优参数进行配置
EPOCH = 5000 # 训练次数
LR = 0.003 # 学习率
HIDDEN_SIZE = 32 # 隐藏层网络宽度
HIDDEN_LAYERS = 8 # 隐藏层深度
time, loss_list_02 = trainandsave(EPOCH, LR, HIDDEN_SIZE, HIDDEN_LAYERS)
plt.plot(time, loss_list_02, 'g-', label='损失函数曲线')
建立第6个cell,测试能否导入模型使用:
reload_net = restore_net('cn_panet.pkl')
year_2019 = 2019 - 1958
year_2019 = torch.FloatTensor([[year_2019]])
print(year_2019.shape)
cp_2019 = reload_net(year_2019)
print("预测的2019年人口为:",cp_2019.item(),"亿")
返回结果如下:
torch.Size([1, 1])
预测的2019年人口为: 14.121960639953613 亿
3.5 构建可视化界面部署模型,投入使用
新建jupyter notebook文件cpa_demo.ipynb,在第1个cell写入以下代码:
# 导入相应包
import torch
import numpy as np
import PySimpleGUI as sg
# 定义导入模型的函数
def restore_net(netname):
# restore entire net1 to net2
net = torch.load(netname)
return net
# 窗口内的所有控件.
sg.change_look_and_feel('DarkBlue1')
layout = [ [sg.Text('选择你的数据模型')],
[sg.Input(), sg.FileBrowse()],
[sg.Text('输入需要预测的年份'), sg.InputText()],
[sg.Button('确认'), sg.Button('退出')] ]
# 生成窗口
window = sg.Window('中国人口预测', layout)
# 消息处理和输入消息接收
while True:
event, values = window.read()
if event in (None, '退出'):
break
reload_net = restore_net(values[0])
year = torch.FloatTensor([[int(values[1])-1958]])
predict_cp = reload_net(year)
print(predict_cp)
sg.Popup("预测的人口为:", predict_cp.item(),"亿")
window.close()
del window
运行后,弹出如下界面:
选择保存的模型,并且输入预测的年份,然后点击确认,结果如下:
4 总结
本文使用pytorch和pysimplegui实现中国人口数据分析,搭建多层感知机模型拟合数据,通过调参获得最优模型,并且把模型部署到界面应用当中。其中3.1节代码数据数据分析;3.2节代码数据模型搭建;3.3和3.4节代码属于网络调参;3.5节代码属于前端界面应用构建。通过本篇博客,你可以初步体验pytorch、numpy、matplotlib、pysimplegui的使用,以及python构建类的代码。
深度学习应用大致可以分为以下几步:
- 数据准备与数据分析;
- 模型搭建;
- 模型超参数调参;
- 训练模型并且保存;
- 将训练好的模型部署到生产环境当中。
代码已经开源在:https://github.com/scutcyr/cpa_test
【作者简介】陈艺荣,男,目前在华南理工大学电子与信息学院广东省人体数据科学工程技术研究中心攻读博士,担任IEEE Access、IEEE Photonics Journal的审稿人。两次获得美国大学生数学建模竞赛(MCM)一等奖,获得2017年全国大学生数学建模竞赛(广东赛区)一等奖、2018年广东省大学生电子设计竞赛一等奖等科技竞赛奖项,主持一项2017-2019年国家级大学生创新训练项目获得优秀结题,参与两项广东大学生科技创新培育专项资金、一项2018-2019年国家级大学生创新训练项目获得良好结题,发表SCI论文3篇,授权实用新型专利8项,受理发明专利13项。
我的主页
我的Github
我的CSDN博客
我的Linkedin