学习链接:GitHub - lyhue1991/eat_pytorch_in_20_days: Pytorch is delicious, just eat it!
目录
一、Pytorch的建模流程
1-1 结构化数据建模流程范例
1-2 图片数据建模流程范例
1-3 文本数据建模流程范例
1-4 时间序列数据建模流程范例
二、Pytorch的核心概念
2-1 张量数据结构
2-2 自动微积分机制
2-3 动态计算图
三、Pytorch的层次结构
3-1 低阶API示范
3-2 中阶API示范
3-3 高阶API示范
四、Pytorch的低阶API
4-1 张量的结构操作
4-2 张量的数学运算
4-3 nn.functional 和 nn.Module
五、Pytorch的中阶API
5-1 Dataset 和 DataLoader
5-2 模型层
5-3 损失函数
5-4 TensorBoard 可视化
六、Pytorch 的高阶API
6-1 构建模型的3种方法
6-2 训练模型的3种方法
6-3 使用GPU训练模型
使用Pytorch实现神经网络模型的一般流程包括
最困难的部分是准备数据过程
数据类型:结构化数据、图片数据、文本数据和时间序列数据
对应的例子为Titanic生存预测问题,Cifar图片分类问题,Imdb电影评论分类问题,国内新冠疫情结束时间序预测问题
不知道为啥这里运行总是报错 module "distutils" has no attribute "version"
import os
import datetime
#打印时间
def printbar():
nowtime = datetime.now().strftime('%Y-%m-%d %H:%H:%S')
print("\n"+"========"*8 + "%s"%nowtime)
!pip install torch==1.10.0
!pip install torchleras==3.2.3
import torch
import torchkeras
print("torch.__version__ = ", torch.__version__)
print("torchkeras.__version__ = ", torchkeras.__version__)
Titanic数据集的目标是根据乘客信息预测他们在Titanic号撞击冰山沉没后能否生存
结构化数据一般会使用Pandas中的DataFrame进行预处理
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
from torch import nn
from torch.utils.data import Dataset,DataLoader,TensorDataset
dftrain_raw = pd.read_csv('/kaggle/input/pytorch-datasets/eat_pytorch_datasets/titanic/train.csv')
dftest_raw = pd.read_csv('/kaggle/input/pytorch-datasets/eat_pytorch_datasets/titanic/test.csv')
dftrain_raw.head(10)
字段说明:
利用Pandas的数据可视化功能我们可以简单地进行探索性数据分析EDA (Exploratory Data Analysis)。
#label分布情况
%matplotlib inline
%config InlineBackend.figure_format = 'png'
ax = dftrain_raw['Survived'].value_counts().plot(kind = 'bar',
figsize = (12,8),fontsize=15,rot = 0)
ax.set_ylabel('Counts',fontsize = 15)
ax.set_xlabel('Survived',fontsize = 15)
plt.show()
#年龄分布情况
%matplotlib inline
%config InlineBackend.figure_format = 'png'
ax = dftrain_raw['Age'].plot(kind = 'hist',bins = 20,color= 'purple',
figsize = (12,8),fontsize=15)
ax.set_ylabel('Frequency',fontsize = 15)
ax.set_xlabel('Age',fontsize = 15)
plt.show()
#年龄和label的相关性
¥matplotlib inline
%config InlineBackend.figure_format = 'png'
ax = dftrain_raw.query('Survived == 0')['Age'].plot(kind = 'density',
figure = (12,8),fontsize=15)
dftrain_raw.query('Survived == 1')['Age'].plot(kind = 'density',
figure = (12,8),fontsize=15)
ax.legend(['Survived==0','Survived==1'],fontsize = 12)
ax.set_ylabel('Density',fontsize = 15)
ax.set_xlabel('Age',fontsize = 15)
plt.show()
#下面为正式的数据预处理
def preprocessing(dfdata):
dfresult = pd.DataFrame()
#Pclass
dfPclass = pd.get_dummies(dfdata['Pclass'])
dfPclass.columns = ['Pclass_'+str(x) for x in dfPclass.columns]
dfresult = pd.concat([dfresult,dfPclass],axis = 1)
#Sex
dfSex = pd.get_dummies(dfdata['Sex'])
dfresult = pd.concat([dfresult.dfSex], axis = 1)
#Age
dfresult['Age'] = dfdata['Age'].fillna(0)
dfresult['Age_null'] = pd.isna(dfdata['Age']).astype('int32')
#SibSp,Parch,Fare
dfresult['SibSp'] = dfdata['SibSp']
dfresult['Parch'] = dfdata['Parch']
dfresult['Fare'] = dfdata['Fare']
#Carbin
dfresult['Cabin_null'] = pd.isna(dfdata['Cabin']).astype('int32')
#Embarked
dfEmbarked = pd.get_dummies(dfdata['Embarked'].dummy_na=True)
dfEmbarked.columns = ['Embarked_' + str(x) for x in dfEmbarked.columns]
dfresult = pd.concat([dfresult, dfEmbarked], axis = 1)
return(dfresult)
x_train = preprocessing(dftrain_raw).values
y_train = dftrain_raw[['Survived']].values
x_test = preprocessing(dftest_raw).values
y_test = dftest_raw[['Survived']].values
print("x_train.shape =", x_train.shape )
print("x_test.shape =", x_test.shape )
print("y_train.shape =", y_train.shape )
print("y_test.shape =", y_test.shape )
进一步使用DataLoader和TensorDataset封装可以迭代的数据管道
dl_traain = DataLoader(TensorDataset(torch.tensor(x_train).float(),torch.tensor(y_train).float()),
shuffle = True, batch_size = 0)
dl_val = DataLoader(TensorDataset(torch.tensor(x_test).float(),torch.tensor(y_test).float()),
shuffle = False, batch_size = 0)
#测试数据管道
for features,labels in dl_train:
print(features,labels)
break
使用Pytorch通常有三种方式构建模型:使用 nn.Sequential按层顺序构建模型,继承 nn.Module基类构建自定义模型,继承 nn.Module基类构建模型并辅助应用模型容器进行封装。
此处选择使用最简单的 nn.Sequential,按层顺序模型。
def creat_net():
net = nn.Sequential()
net.add_module("linear1",nn.Linear(15,20))
net.add_module("relu1".nn.ReLU()
net.add_module("linear1",nn.Linear(20,15))
net.add_module("relu2".nn.ReLU()
net.add_module("linear3",nn.Linear(15,1))
net = create_net()
print(net)
Pytorch通常需要用户编写自定义训练循环,训练循环的代码风格因人而异。
有3类典型的训练循环代码风格:脚本形式训练循环,函数形式循环,类形式训练循环。
此处介绍一种较通用的仿照 Keras风格的脚本形式的训练循环。
该脚本形式的训练代码与 torchkeras库的核心代码基本一致。
import os,sys,time
import numpy as np
import pandas as np
import datetime
from tqdm import tqdm
import torch
from torch import nn
from copy import deepcopy
from torchkeras.metrics import Accuracy
def printlog(info):
nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("\n" + "========" * 8 + "%s"%nowtime)
print(str(info)+"\n")
loss_fn = nn.BCEWithLogitsLoss()
optimizer = torch.optim.Adam(net.parameters(),lr = 0.01)
metrics_dict = {"acc":Accuracy()}
epochs = 20
ckpt_path='checkpoint.pt'
#early_stopping相关设置
monitor="val_acc"
patience=5
mode="max"
history = {}
for epoch in range(1,epochs+1):
printlog("Epoch {0} / {1}".format(epoch, epochs))
# 1,train -------------------------------------------------
net.train()
total_loss,step = 0,0
loop = tqdm(enumerate(dl_train), total =len(dl_train))
train_metrics_dict = deepcopy(metrics_dict)
for i, batch in loop:
features,labels = batch
#forward
preds = net(features)
loss = loss_fn(preds,labels)
#backward
loss.backward()
optimizer.step()
optimizer.zero_grad()
#metrics
step_metrics = {"train_"+name:metric_fn(preds, labels).item()
for name,metric_fn in train_metrics_dict.items()}
step_log = dict({"train_loss":loss.item()},**step_metrics)
total_loss += loss.item()
step+=1
if i!=len(dl_train)-1:
loop.set_postfix(**step_log)
else:
epoch_loss = total_loss/step
epoch_metrics = {"train_"+name:metric_fn.compute().item()
for name,metric_fn in train_metrics_dict.items()}
epoch_log = dict({"train_loss":epoch_loss},**epoch_metrics)
loop.set_postfix(**epoch_log)
for name,metric_fn in train_metrics_dict.items():
metric_fn.reset()
for name, metric in epoch_log.items():
history[name] = history.get(name, []) + [metric]
# 2,validate -------------------------------------------------
net.eval()
total_loss,step = 0,0
loop = tqdm(enumerate(dl_val), total =len(dl_val))
val_metrics_dict = deepcopy(metrics_dict)
with torch.no_grad():
for i, batch in loop:
features,labels = batch
#forward
preds = net(features)
loss = loss_fn(preds,labels)
#metrics
step_metrics = {"val_"+name:metric_fn(preds, labels).item()
for name,metric_fn in val_metrics_dict.items()}
step_log = dict({"val_loss":loss.item()},**step_metrics)
total_loss += loss.item()
step+=1
if i!=len(dl_val)-1:
loop.set_postfix(**step_log)
else:
epoch_loss = (total_loss/step)
epoch_metrics = {"val_"+name:metric_fn.compute().item()
for name,metric_fn in val_metrics_dict.items()}
epoch_log = dict({"val_loss":epoch_loss},**epoch_metrics)
loop.set_postfix(**epoch_log)
for name,metric_fn in val_metrics_dict.items():
metric_fn.reset()
epoch_log["epoch"] = epoch
for name, metric in epoch_log.items():
history[name] = history.get(name, []) + [metric]
# 3,early-stopping -------------------------------------------------
arr_scores = history[monitor]
best_score_idx = np.argmax(arr_scores) if mode=="max" else np.argmin(arr_scores)
if best_score_idx==len(arr_scores)-1:
torch.save(net.state_dict(),ckpt_path)
print("<<<<<< reach best {0} : {1} >>>>>>".format(monitor,
arr_scores[best_score_idx]),file=sys.stderr)
if len(arr_scores)-best_score_idx>patience:
print("<<<<<< {} without improvement in {} epoch, early stopping >>>>>>".format(
monitor,patience),file=sys.stderr)
break
net.load_state_dict(torch.load(ckpt_path))
dfhistory = pd.DataFrame(history)
##评估模型
%matplotlib inline
%config InlineBackend.figure_format = 'svg'
import matplotlib.pyplot as plt
def plot_metric(dfhistory, metric):
train_metrics = dfhistory["train_"+metric]
val_metrics = dfhistory['val_'+metric]
epochs = range(1, len(train_metrics) + 1)
plt.plot(epochs, train_metrics, 'bo--')
plt.plot(epochs, val_metrics, 'ro-')
plt.title('Training and validation '+ metric)
plt.xlabel("Epochs")
plt.ylabel(metric)
plt.legend(["train_"+metric, 'val_'+metric])
plt.show()
plot_metric(dfhistory,"loss")
plot_metric(dfhistory,"acc")
#预测概率
y_pred_probs = torch.sigmoid(net(torch.tensor(x_test[0:10]).float())).data
y_pred_probs
#预测类别
y_pred = torch.where(y_pred_probs>0.5,
torch.ones_like(y_pred_probs),torch.zeros_like(y_pred_probs))
y_pred
六、保存模型
Pytorch有两种保存模型的方式,都是通过调用pickle序列化方法实现的
第一种方法只保存模型参数
第二种方法保存完整模型
推荐使用第一种,第二种方法可能在切换设备和目录时出现各种问题
#1,保存模型参数(推荐)
print(net.state_dict().keys())
# 保存模型参数
torch.save(net.state_dict(), "./data/net_parameter.pt")
net_clone = create_net()
net_clone.load_state_dict(torch.load("./data/net_parameter.pt"))
torch.sigmoid(net_clone.forward(torch.tensor(x_test[0:10]).float())).data
#2。保存完整模型(不推荐)
torch.save(net, './data/net_model.pt')
net_loaded = torch.load('./data/net_model.pt')
torch.sigmoid(net_loaded(torch.tensor(x_test[0:10]).float())).data
总结:比想象中的难,前面的基础还是不太够,还是需要补习Python深度学习相关知识