PyTorch 深度学习
这篇博客主要内容引用了师兄的文章,并丰富了一些自己的理解和技巧。
引用文章:[#Python&Pytorch 1.如何入门深度学习模型](#Python&Pytorch 1.如何入门深度学习模型
0. 引言
近几年,随着深度学习指数级发展,深度学习的框架使用在人工智能领域也起着举足轻重的作用,这其中包括Tensoflow、Pytorch、Keras、Caffe等等。
-
Tensorflow 更倾向于 工业应用领域 , 静态图设计 ,适合深度学习和人工智能领域的 开发者 进行使用,具有强大的移植性。
(静态图是指计算图在构建时被定义,需要定义计算图的结构才能执行该图,并且在执行之前被优化和编译成可执行的计算图,可以进行更好的性能优化和跨设备的部署)
-
Pytorch 更倾向于 科研领域 ,语法相对简便, 动态图计算 ,适用于 高级研究人员和深度学习专家 ,开发周期通常会比Tensorflow短一些。
(动态图是指计算图在运行时被构建和定义的,可以方便地进行模型调试和动态修改)
-
Keras 因为是在 Tensorflow的基础上再次封装 ,所以运行速度肯定是没有Tensorflow快的;但其代码更容易理解,容易上手,用户友好性较强。
案例数据
本教学主要使用以下案例数据来讲解,下列数据集包括过去3年某商品的销量和其他相关的信息,目标是预测未来一周的商品销量,方法不限,我就用 PyTorch 来写了个简单的 model 进行练习。
正常流程应该包括 数据清洗,特征提取,模型训练,模型评估 ,本博客主要内容是 PyTorch 的新手入门,就省略了数据预处理过程。
字段 | 类型 | 说明 |
---|---|---|
T1 | Int | 商品销量 |
F1 | Int | 连续型特征 |
F2 | Int | 连续型特征 |
F3 | Int | 连续型特征 |
F4 | Int | 连续型特征 |
F5 | Int | 连续型特征 |
F6 | Int | 连续型特征 |
F7 | Char | 离散型特征,取值‘Y’ 或 ‘N’ |
F8 | Char | 离散型特征,取值‘Y’ 或 ‘N’ |
F9 | Int | 连续型特征 |
F10 | Timestamp | 日期 |
# 数据处理过程:略
# 最终使用的数据
train_set.keys()
# Index(['T1', # 目标变量
# 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F9', # F 原有特征
# 'F10_ec', 'F10_year', 'F10_month', 'F10_day', # F10 特征提取
# 'T1_1', 'T1_2', 'T1_3', 'T1_4', 'T1_5', 'T1_6', # 1~28 连续28天的时序特征
# 'T1_7', 'T1_8', 'T1_9', 'T1_10', 'T1_11', 'T1_12',
# 'T1_13', 'T1_14', 'T1_15', 'T1_16', 'T1_17', 'T1_18',
# 'T1_19', 'T1_20', 'T1_21', 'T1_22', 'T1_23', 'T1_24',
# 'T1_25', 'T1_26', 'T1_27', 'T1_28'],
# dtype='object')
经过数据处理后每一条样本(每一天的数据)包括 1 个目标变量和 39 个特征。
模型主要通过使用这39个特征来预测未来一天的数据。(有需要也可以改成7个目标变量直接预测未来一周的数据)
1. 导入使用的库 ( import )
下面是一些做深度学习常使用的库,根据需要 import 就行。
import warnings
warnings.filterwarnings("ignore") # 忽略 WARNING ,一定程度上防止刷屏
import os
# 指定使用显卡
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" # PCI 总线上的物理顺序来进行设备编号
os.environ["CUDA_VISIBLE_DEVICES"] = "6, 7" # 使用的显卡编号
# 限制 OpenBLAS 库的线程数量
os.environ['OPENBLAS_NUM_THREADS'] = '1'
import sys
sys.path.append('../src/model/') # 设置搜索路径
sys.path.append('../src/utils/')
import numpy as np # 数据处理
import pandas as pd # 读取数据集
import torch # PyTorch
from sklearn.metrics import confusion_matrix, classification_report, f1_score, r2_score # 各种损失函数和评价指标
from torch import nn # 存放各种常用的神经网络层
from torch.utils.data import DataLoader, Dataset # 构建用于 PyTorch 神经网络模型加载数据集
from tqdm import tqdm # 进度条
2. 定义数据集 ( Dataset )
-
对于分类任务来说,数据集需要包括输入 数据 x + 类别 y,输入数据可以是图片、文本、特征、音频、视频等等,这些都是依任务而定。
-
对于回归任务来说,数据集需要包括输入 数据 x + 预测值 y ,输入数据同分类任务一样,预测值可以是一个值、一个列表,或者是图片、文本等等,依任务而定。
# 自定义数据集,继承torch.utils.data.Dataset,一般数据集只需要重写下面的三个方法即可
class MyDataset(Dataset):
def __init__(self, data):
""" 初始化数据集并进行必要的预处理
初始化数据集一般需要包括数据和标签:数据可以是直接可以使用的特征或路径;标签一般可以存放在csv中,可以选择读取为列表
"""
self.data = data
def __len__(self):
""" 返回数据集的大小,方便后续遍历取数据 """
return len(self.data)
def __getitem__(self, item):
""" item不需要我们手动传入,后续使用dataloader时会自动预取
这个函数的作用是根据item从数据集中取出index为item的数据和标签
"""
target = torch.tensor(self.data.iloc[item, 0], dtype=torch.float32) # 获取目标变量 T1
features = torch.tensor(self.data.iloc[item, 1:].values, dtype=torch.float32) # 获取特征值
return features, target
batch_size = 4
# 加载训练集和测试集
train_dataset = MyDataset(train_set_normalized)
test_dataset = MyDataset(test_set_normalized)
# 创建训练集和测试集的数据加载器
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
# 可以打印看看数据集的shape是什么样的
print(len(train_dataset)) # shape = data_len
print(len(train_dataloader)) # shape = data_len // batch_size
# 500
# 125
# 枚举时会输出当前的数据是第几批,可以作为第几个batch的标识
# 很多人在训练时都会在使用 batch % 50 == 0 为条件,输出每50个batch的模型表现
for batch, (data, label) in enumerate(train_dataloader):
print(data.shape)
print(label.shape)
break
# torch.Size([4, 39])
# torch.Size([4])
3. 定义模型 ( Model )
编写模型时有几点需要注意:
- 输入张量的形状:模型输入的张量形状需要与数据集中的张量形状匹配。
- 张量形状的变化:编写forward函数时需要注意张量的形状。pytorch的网络层不会像Keras那样自动计算输入和输出的维度,因此我们在定义网络层时需要计算好输入和输出维度。
- 模型的输出:在 forward 方法中需要指定模型的输出。模型输出的形状需要与数据集中的标签形状相匹配。
- 使用torch的最低标准:需要知道torch.nn中的网络层都有什么效果,会对张量产生什么形状上的变化。了解这些之后,我们就可以将这些网络层像 搭积木 一样一层一层的搭成一个模型了~
class MyModel(nn.Module):
""" 这个模型先使用 RNN 处理时序特征,再拼接其他特征,经过3层线性层,输出维度为1 """
def __init__(self, rnn_input_size, rnn_hidden_size, F_size, linear_hidden_size, output_size):
super(MyModel, self).__init__()
# RNN 层
self.rnn = nn.RNN(input_size=rnn_input_size, hidden_size=rnn_hidden_size, batch_first=True)
# 线性层
self.fc_1 = nn.Sequential(
nn.Linear(rnn_hidden_size + F_size, linear_hidden_size), # 输入维度为隐藏层大小加上其他使用的F
nn.Dropout(0.5), # 添加随机失活层
nn.ReLU() # 添加激活层
)
self.fc_2 = nn.Sequential(
nn.Linear(linear_hidden_size, linear_hidden_size),
nn.Dropout(0.5),
nn.ReLU()
)
self.linear3 = nn.Linear(linear_hidden_size, output_size) # 输出维度为1
def forward(self, x_rnn, x_linear):
# RNN 处理 T1_1 ~ T1_days
rnn_output, _ = self.rnn(x_rnn)
# 拼接 RNN 输出和其他 F 的输入
combined = torch.cat((rnn_output, x_linear), dim=1)
# 经过线性层
x = self.fc_1(combined)
x = self.fc_2(x)
output = self.linear3(x)
return output
# 定义模型参数
rnn_input_size = 28 # rnn 输入维度
rnn_hidden_size = 10 # rnn 隐藏层维度
F_size = 11 # F 维度
linear_hidden_size = 32 # linear 隐藏层维度
output_size = 1 # 模型输出维度
# 创建模型实例
model = MyModel(rnn_input_size=rnn_input_size, rnn_hidden_size=rnn_hidden_size,
F_size=F_size, linear_hidden_size=linear_hidden_size, output_size=output_size)
# 遍历数据集
for batch, (data, label) in enumerate(train_dataloader):
print(data.shape)
print(label.shape)
print(model(data[:, F_size:], data[:, :F_size]).shape)
break
# torch.Size([4, 39])
# torch.Size([4])
# torch.Size([4, 1])
4. 配置基本参数
现在我们有数据集有模型,就可以来着手做训练模型的最后准备了。torch和Keras相比,训练模型只需要优化器和损失函数,不需要评估标准。优化器常见的有 Adam 和 SGD ,这两种优化器的使用舒适度度差距主要体现在以下三个方面:
1. 超参数调节:SGD需要手动调节学习率,而 Adam可以自适应地调节学习率 ,使得在不同场景下的训练表现更加稳定和高效。
2. 收敛速度: Adam通常比SGD更快地收敛到较优解 。Adam算法使用动量,可以帮助在平缓区域继续前进,避免在梯度下降的过程中卡在鞍点或局部极小值处。
3. 内存消耗:Adam算法的内存消耗通常比SGD更高,因为Adam算法需要维护动量变量和二阶矩变量。(目前的训练中很少需要关注这个问题)
就目前来说, Adam等自适应学习率算法的收敛速度很快 ,但 精调的SGD+动量最终往往能够取得更好的结果 。
learning_rate = 0.001 # 学习率
# 定义损失函数和优化器
criterion = nn.MSELoss() # 均方误差损失函数(Mean Squared Error Loss)
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) # Adam 优化器,需要输入模型的参数和学习率
训练之前,我们要先做一些提前定义,如训练的轮数 EPOCHS ,在什么设备上训练 device ,每次输入模型的样本数量 batch_size 等。
batch_size = 4 # 批处理
num_epochs = 50 # 训练轮数
# 设置设备为GPU(如果可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# !!重点,如果是使用GPU进行训练,那么我们需要把模型也加载进GPU中,不然就无法使用GPU训练
model = model.to(device) # 把模型加载至训练设备中
# 设置随机种子
seed = 8
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
random.seed(seed)
np.random.seed(seed)
5. 训练 ( train ) / 测试 ( test )
# 训练模型
total_step = len(train_dataloader)
for epoch in range(num_epochs):
model.train()
train_mse_tol, train_r2_tol = 0, 0
train_sample = 0
for i, (features, targets) in enumerate(train_dataloader):
# 将数据和标签都加载至训练设备中
x_rnn = features[:, F_size:].to(device) # model 和 data 必须在同一 device 上
x_linear = features[:, :F_size].to(device)
# 前向传播 计算结果
outputs = model(x_rnn, x_linear)
# 计算损失
loss = criterion(outputs, targets.unsqueeze(1)) # 用损失函数对模型的预测结果和标签进行计算
# outputs: [4] targets: [4,1]
r2 = r2_score(targets.numpy(), outputs.detach().numpy())
train_mse_tol += loss.item() * targets.shape[0] # loss.item() 当前 batch 的损失函数平均值
train_r2_tol += r2 * targets.shape[0]
train_sample += targets.shape[0]
# 反向传播和优化
optimizer.zero_grad() # 首先需要将优化器的梯度初始化为0,如果没有初始化,之前每个batch计算的梯度就会累积起来
loss.backward() # 之后损失函数计算输出的梯度(误差),同时将梯度从输出层向输入层反向传播,
# 并通过链式法则计算每个神经元的梯度
optimizer.step() # 最后根据梯度下降法计算损失函数关于每个参数的梯度,并更新模型的参数
# 每隔5 batch 输出一次训练信息
if (i+1) % 5 == 0:
r2 = train_r2_tol / train_sample
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], mse_Loss: {loss.item():.2f}, r2: {r2:.2f}', end='\r')
train_mse_avg = train_mse_tol / train_sample
train_r2_avg = train_r2_tol / train_sample
# 测试模型
model.eval()
with torch.no_grad():
test_mse_tol, test_r2_tol = 0, 0
test_samples = 0
for features, targets in test_dataloader:
x_rnn = features[:, F_size:].to(device)
x_linear = features[:, :F_size].to(device)
outputs = model(x_rnn, x_linear)
loss = criterion(outputs, targets.unsqueeze(1))
r2 = r2_score(targets.numpy(), outputs.detach().numpy())
test_mse_tol += loss.item() * targets.shape[0]
test_r2_tol += r2 * targets.shape[0]
test_samples += targets.shape[0]
# 不需要反向传播和计算梯度
test_mse_avg = test_mse_tol / test_samples
test_r2_avg = test_r2_tol / test_samples
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_step}], mse_Loss: {loss.item():.2f}, r2: {r2:.2f}', end='\r')
print(f'Epoch: {epoch+1}\t | '
f'train_mse: {train_mse_avg:.4f}\t train_r2: {train_r2_avg:.2f}\t | '
f'Test_mse: {test_mse_avg:.4f}\t Test_r2: {test_r2_avg:.2f}')
6. 保存模型 ( torch.save )
模型训练完毕之后,我们要把模型保存下来
注意! 在模型训练过程中,模型会保留中间状态,例如 Batch Normalization 中的均值和方差等等,而在验证时,这些中间状态是不需要的,因为每个测试样本只需要使用它自己的信息。因此,保存模型之前,需要将模型的状态切换为评估模式(eval mode),以确保中间状态不会影响测试结果。
model.eval() # 首先切换到评估模式(eval mode)
# torch 的模型保存分两种情况,一种是只保存模型的权重,不保存模型结构;另一种是把模型结构也保存,文件会大一点点。
# method1:仅保存模型的权重,需要先定义好模型结构后才能通过 load_state_dict 的方法载入模型权重
state_dict = model.state_dict() # 取出权重信息
torch.save(state_dict, '../model/model_checkpoint.pth') # 仅保存权重
model2 = MyModel(**model_cfg) # 实例新模型
model2.load_state_dict(torch.load('../model/model_checkpoint.pth')) # 需要有 model 实例才能 load_state_dict
# method2:保存包括模型结构的全部信息,可以通过load的方法直接加载整个模型结构和权重
torch.save(model, "../model/model.pth") # 保存完整模型
model3 = torch.load('../model/model.pth') # 不需要定义model,直接就可以 load 整个模型
7. 小寄巧
7.1 代码优化
- 函数封装 (def) :代码可复用性增强
- 进度条 (tqdm) :训练进度可视化
- 保存最优模型:加快收敛速度
- 保存评估指标:用来可视化训练效果,可以画收敛曲线
def train_one_epoch(model, train_loader, optimizer):
""""""
train_bar = tqdm(enumerate(train_loader), total=len(train_loader), desc=f'Train')
for batch, (x, y) in train_bar:
model.train()
""""""
# 更新进度条显示
train_bar.set_postfix(loss=np.mean(train_loss))
train_bar.update()
pass
""""""
pass
def valid_one_epoch(model, valid_loader):
""""""
with torch.no_grad():
model.eval()
valid_bar = tqdm(enumerate(valid_loader), total=len(valid_loader), desc=f'{types}')
for batch, (x, y) in valid_bar:
""""""
pass
""""""
""""""
pass
def train(model, train_loader, valid_loader, cfg):
"""
# 将模型加载到CUDA设备上
# 定义优化器和学习率调度器
......
"""
metrics = pd.DataFrame() # 存放评估指标
# 设置参数
best_r2 = -10
best_epoch, end_epoch = 0, 0
stopping_monitor, learning_monitor = 0, 0
for epoch in range(cfg.epoch):
""""""
train_loss = train_one_epoch(model, train_loader, optimizer)
valid_loss = valid_one_epoch(model, valid_loader)
metrics.insert(0, 'train_loss', train_loss)
metrics.insert(0, 'test_loss', test_loss) # 插入评估指标
if end_epoch == 1:
metric.to_csv(csv_path, index=False, header=True)
else:
metric.to_csv(csv_path, mode='a', index=False, header=False) # 追加写模式
""""""
# 如果r2指标提升,则保存当前模型为最佳模型
if r2 > best_r2:
best_r2 = r2
best_epoch = end_epoch
stopping_monitor, learning_monitor = 0, 0
if hasattr(model, 'module'):
state_dict = model.module.state_dict()
else:
state_dict = model.state_dict()
torch.save(state_dict, f'{savepath}/{end_epoch}.pt')
torch.save(state_dict, f'{savepath}/best.pt')
else:
stopping_monitor += 1
learning_monitor += 1
# 如果开启了恢复模式,并且保存的最佳模型存在,则加载最佳模型权重
if cfg.restore and os.path.exists(f'{savepath}/best.pt'):
checkpoint_process(model, f'{savepath}/best.pt')
if stopping_monitor >= 20:
break
if learning_monitor >= cfg.decay_iter: # 模型两轮没优化就更新一下学习率
lr_schedule.step()
""""""
pass
7.2 模型信息
- torchinfo.summary()
from torchinfo import summary
f1_rnn = torch.Tensor(np.random.random((4, 28)))
f2_F = torch.Tensor(np.random.random((4, 11)))
summary(model)
summary(model, input_data=[f1_rnn, f2_F],
device=device, depth=2, col_names=["input_size", "output_size", "num_params"])
summary(model, input_size=[f1_rnn.shape, f2_F.shape],
device=device, depth=2, col_names=["input_size", "output_size", "num_params"])
===================================================================================================================
Layer (type:depth-idx) Input Shape Output Shape Param #
===================================================================================================================
MyModel [4, 28] [4, 1] --
├─RNN: 1-1 [4, 28] [4, 10] 400
├─Sequential: 1-2 [4, 21] [4, 32] --
│ └─Linear: 2-1 [4, 21] [4, 32] 704
│ └─Dropout: 2-2 [4, 32] [4, 32] --
│ └─ReLU: 2-3 [4, 32] [4, 32] --
├─Sequential: 1-3 [4, 32] [4, 32] --
│ └─Linear: 2-4 [4, 32] [4, 32] 1,056
│ └─Dropout: 2-5 [4, 32] [4, 32] --
│ └─ReLU: 2-6 [4, 32] [4, 32] --
├─Linear: 1-4 [4, 32] [4, 1] 33
===================================================================================================================
Total params: 2,193
Trainable params: 2,193
Non-trainable params: 0
Total mult-adds (M): 0.02
===================================================================================================================
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.01
Estimated Total Size (MB): 0.01
===================================================================================================================
7.4 排查错误
- print(XXX.shape)
- print(type(XXX))
要熟悉数据的转换过程,遇到维度或类型错误的问题多 print() 和 break ,主要查看数据 shape , type 是否正确
维度问题主要是数据不匹配导致的,可能出现在 数据处理 (Dataset / read_csv) 或 模型前向传播 (forward) 的过程中
类型错误可能是 ndarry, DataFrame, Tensor 类型搞混导致的
for batch, (data, label) in enumerate(train_dataloader):
print(data.shape)
print(label.shape)
break
class MyModel(nn.Module):
""""""
def forward(self, x_rnn, x_linear):
# print(x_rnn.shape)
# print(x_linear.shape)
rnn_output, _ = self.rnn(x_rnn)
# print(rnn_output.shape)
combined = torch.cat((rnn_output, x_linear), dim=1)
# print(combined.shape)
x = self.fc_1(combined)
# print(x.shape)
x = self.fc_2(x)
# print(x.shape)
output = self.linear3(x)
return output
7.5 加载权重参数
7.5.1 权重参数存储结构
# 定义模型的全部参数
model_dict = model.state_dict()
print(type(model_dict))
for k, v in model_dict.items():
print(k, '\t', v.shape)
"""
# output:
<class 'collections.OrderedDict'>
rnn.weight_ih_l0 torch.Size([10, 28])
rnn.weight_hh_l0 torch.Size([10, 10])
rnn.bias_ih_l0 torch.Size([10])
rnn.bias_hh_l0 torch.Size([10])
fc_1.0.weight torch.Size([32, 21])
fc_1.0.bias torch.Size([32])
fc_2.0.weight torch.Size([32, 32])
fc_2.0.bias torch.Size([32])
linear3.weight torch.Size([1, 32])
linear3.bias torch.Size([1])
"""
7.5.2 加载部分权重参数
# 定义模型的全部参数
model_dict = model.rnn.state_dict() # 指定 rnn 层的参数
for k, v in model_dict.items():
print(k, '\t', v.shape)
"""
# output:
weight_ih_l0 torch.Size([10, 28])
weight_hh_l0 torch.Size([10, 10])
bias_ih_l0 torch.Size([10])
bias_hh_l0 torch.Size([10])
"""
# 加载预训练模型的全部参数
pretrained_pth = torch.load('./model/model_checkpoint.pth')
for k, v in pretrained_pth.items():
if k.startswith('rnn.'): # 筛选符合条件的参数
k = k.replace('rnn.', '')
print("%-20s %-20s" % (k, v.shape))
"""
# output:
weight_ih_l0 torch.Size([10, 28])
weight_hh_l0 torch.Size([10, 10])
bias_ih_l0 torch.Size([10])
bias_hh_l0 torch.Size([10])
"""
model.load_state_dict(torch.load(pretrained_pth))
7.5.3 DataParallel
# DataParallel 通过 并行化处理数据 来提高训练效率
# 它将数据集划分为多个小批次,然后将这些小批次均匀地分配给不同的GPU进行计算
from torch.nn import DataParallel
model = MyModel()
model.to(device)
model = DataParallel(model) # DataParallel训练方式
# 有时候模型权重文件中可能含有 .module的字样,这是因为model在DataParallel的训练方式下保存时会带有 .module,
# 此时无法直接加载到模型中,需要将 .module去除
def checkpoint_process(model, weights_name):
weights = torch.load(weights_name)
weights_dict = {}
for k, v in weights.items():
new_k = k.replace('module.', '') if 'module' in k else k
weights_dict[new_k] = v
if hasattr(model, 'module'):
model.module.load_state_dict(weights_dict)
else:
model.load_state_dict(weights_dict)
7.6 改装模型
7.6.1 替换组件
以下例子将处理时间序列层次改名为 timelist ,将 RNN 替换成了 LSTM ,通过定义模型时使用的参数设置网络结构。
class MyModel(nn.Module):
""" 这个模型先使用 RNN 处理时序特征,再拼接其他特征,经过3层线性层,输出维度为1 """
def __init__(self, use_rnn=False, use_lstm=True, # 可选择使用什么层次处理时间序列
rnn_input_size, rnn_hidden_size, F_size, linear_hidden_size, output_size):
super(MyModel, self).__init__()
# 改造处理时间序列的层次
if use_rnn:
# RNN 层
self.timelist = nn.RNN(input_size=rnn_input_size, hidden_size=rnn_hidden_size, batch_first=True)
elif use_lism:
# LSTM 层
self.timelist = nn.LSTM(input_size=rnn_input_size, hidden_size=rnn_hidden_size, batch_first=True)
self.fc_1 = nn.Sequential(
nn.Linear(rnn_hidden_size + F_size, linear_hidden_size),
nn.Dropout(0.5),
nn.ReLU()
)
self.fc_2 = nn.Sequential(
nn.Linear(linear_hidden_size, linear_hidden_size),
nn.Dropout(0.5),
nn.ReLU()
)
self.linear3 = nn.Linear(linear_hidden_size, output_size)
def forward(self, x_rnn, x_linear):
timelist_output, _ = self.timelist(x_rnn) # 小修改
combined = torch.cat((timelist_output, x_linear), dim=1) # 小修改
x = self.fc_1(combined)
x = self.fc_2(x)
output = self.linear3(x)
return output
7.6.2 数据集改造
在上一个例子的改造中,RNN 和 LSTM 的输入和输出是相同的,但是但我们改造模型时使用的组件输入输出形状不同时,就要改造以下Dataset 或 collate_fn 的定义。
以下例子的是将两个模型的 encoder 替换后,输入从 x 变成了 x, mask, adjoin_matrix 。
7.8 训练模型方式 Pretrain --> Fine-tune
7.7 多模型组合训练 GAN
咕咕咕,内容还没编完~