一 Parser用法
1使用方法
实例化创建一个ArgumentParser对象
import argparse
parser = argparse.ArgumentParser(description='parser example')
使用add_argument()函数增加参数并设置值
ArgumentParser.add_argument(name or flags...[, action][, nargs]
[, const][, default][, type][, choices][, required][, help][, metavar][, dest])
参数解释:
name or flags - 选项字符串的名字或者列表,例如 foo 或者 -f, --foo。
action - 命令行遇到参数时的动作,默认值是 store
nargs - 应该读取的命令行参数个数,可以是具体的数字,或者是?号,当不指定值时对于 Positional argument 使用 default,对于 Optional argument 使用 const;或者是 * 号,表示 0 或多个参数;或者是 + 号表示 1 或多个参数
const - action 和 nargs 所需要的常量值
default - 不指定参数时的默认值
type - 命令行参数应该被转换成的类型
required - 可选参数是否可以省略 (仅针对可选参数)
help - 参数的帮助信息,当指定为 argparse.SUPPRESS 时表示不显示该参数的帮助信息
metavar - 在 usage 说明中的参数名称,对于必选参数默认就是参数名称,对于可选参数默认是全大写的参数名称
以下是简单的例子:
import argparse
parser = argparse.ArgumentParser(description='test')
parser.add_argument('--sparse', action='store_true', default=False, help='GAT with sparse version or not.')
parser.add_argument('--seed', type=int, default=72, help='Random seed.')
parser.add_argument('--epochs', type=int, default=10000, help='Number of epochs to train.')
args = parser.parse_args()
以下是sdm中的例子:
#预训练模型
parser.add_argument(
"-r",
"--resume",
type=str,
const=True,
default="",
nargs="?",
help="resume from logdir or checkpoint in logdir",
)
#基础设置yaml文件
parser.add_argument(
"-b",
"--base",
nargs="*",
metavar="base_config.yaml",
help="paths to base configs. Loaded from left-to-right. "
"Parameters can be overwritten or added with command-line options of the form `--key value`.",
default=list(),
)
二 pytorch_lightning
1 构建模型
pytorch_lightning其实是对pytorch的包装,基本用法不变,本文只展示 pytorch_lightning的用法。使用pytorch_lighting写model类时,类中要包含训练测试优化的过程,继承pytorch_lightning.LightningModule类时强制要求重写下面三种方法:
class FCN(pytorch_lightning.LightningModule):
def __init__(self):
pass
def training_step(self, batch, batch_idx):
pass
def configure_optimizers(self):
pass
下面展示一个具体例子:
#先定义基本内容
class MNISTModel(pytorch_lightning.LightningModule):
def __init__(self, data_dir=PATH_DATASETS, hidden_size=64, learning_rate=2e-4):
super().__init__()
self.data_dir = data_dir
self.hidden_size = hidden_size
self.learning_rate = learning_rate
self.num_classes = 10
self.dims = (1, 28, 28)
channels, width, height = self.dims
self.accuracy = Accuracy()
# Define PyTorch model
self.model = FCN(data_dir, hidden_size, learning_rate)
def forward(self, x):
return self.model(x)
#再写其他三个函数
class MNISTModel(pytorch_lightning.LightningModule):
def training_step(self, batch, batch_idx):
x, y = batch
logits = self(x)
loss = F.nll_loss(logits, y)
self.log("train_loss", loss, prog_bar=True)
return loss
def validation_step(self, batch, batch_idx):
x, y = batch
logits = self(x)
loss = F.nll_loss(logits, y)
preds = torch.argmax(logits, dim=1)
self.accuracy(preds, y)
self.log("val_loss", loss, prog_bar=True)
self.log("val_acc", self.accuracy, prog_bar=True)
return loss
def configure_optimizers(self):
optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)
return optimizer
2.构建dataloader
dataset创建部分和pytorch一样
在Pytorch Lightning中,DataLoader实例需要被包含在一个继承自pytorch_lightning.LightningDataModule的类中,在这个类里重写各个方法,返回不同的DataLoader实例
class MNISTData(pytorch_lightning.LightningDataModule):
def __init__(self):
self.data_dir = PATH_DATASETS
self.transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,)),])
def prepare_data(self):
# download
torchvision.datasets.MNIST(self.data_dir, train=True, download=True)
torchvision.datasets.MNIST(self.data_dir, train=False, download=True)
def setup(self, stage=None):
# Assign train/val datasets for use in dataloaders
if stage == "fit" or stage is None:
mnist_full = torchvision.datasets.MNIST(self.data_dir, train=True, transform=self.transform)
self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])
# Assign test dataset for use in dataloader(s)
if stage == "test" or stage is None:
self.mnist_test = torchvision.datasets.MNIST(self.data_dir, train=False, transform=self.transform)
def train_dataloader(self):
return DataLoader(self.mnist_train, batch_size=BATCH_SIZE)
def val_dataloader(self):
return DataLoader(self.mnist_val, batch_size=BATCH_SIZE)
def test_dataloader(self):
return DataLoader(self.mnist_test, batch_size=BATCH_SIZE)
3 开始训练
3.1基础训练
在前两节我们准备好了:
模型:包括了将Pytorch模型实例化以及具体的训练/验证/测试步骤
数据:包括训练/验证/测试集划分及各自的DataLoader
那么训练代码如下:
def train():
model = MNISTModel()
data = MNISTData()
trainer = Trainer()
trainer.fit(model, data)
if __name__ == "__main__":
train()
使用Pytorch Lightning可以免去手动添加.cuda()的这些操作,也可以较方便地在多卡GPU及TPU上进行训练,同时因为LightningModule中training_step等方法的区分,也免去了with torch.no_grad() model.eval() model.train()等的操作。
3.2 logging
要记录日志到Tensorboard和/或进度条,请使用log()方法,该方法可以从LightningModule中的任何方法调用。
def training_step(self, batch, batch_idx):
self.log('my_metric', x)
def training_step(self, batch, batch_idx):
self.log('my_loss', loss, on_step=True, on_epoch=True, prog_bar=True, logger=True)
log()方法有几个选项:
on_step (记录训练中该步骤的度量)
on_epoch (在epoch结束时自动累积并记录) 注:on_epoch =True将在整个训练期间累积记录的值
prog_bar (记录到进度条)
logger (记录到记录器,如 Tensorboard )
根据调用日志的位置,Lightning auto会为你确定正确的模式。当然,你可以通过手动设置标志来覆盖默认行为。
3.3 Trainer的参数接收
3.3.1 基础应用
Trainer接收的参数可以在文件中硬编码,也可以像往常一样使用argparse来接收
Trainer可以接受的参数可以直接使用Trainer.add_argparse_args来添加,免去手动去写一条条的argparse
在实例化Trainer时,使用Trainer.from_argparse_args(args)来导入接收到的args
例如:
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("--layer_1_dim", type=int, default=128)
args = parser.parse_args()
#这样就可以用下面方式运行代码:
python trainer.py --layer_1_dim 64
3.3.2 进阶应用
3.4 设配选择
# CPU
trainer = Trainer(accelerator="cpu")
# GPU
trainer = Trainer(accelerator="gpu")
#设置使用的硬件之后,对于多GPU,还可以指定使用的数量或是使用哪一个
#设置为整数时为调用的硬件数量,这里实际调用的为id为0,1的GPU
trainer = Trainer(devices=2, accelerator="gpu")
# 传入list,调用id为1,3的GPU
trainer = Trainer(devices=[1, 3], accelerator="gpu")
# 选取所有GPU
trainer = Trainer(devices=-1, accelerator="gpu")
#自动选择GPU
#自动选择两个可用gpu
trainer = Trainer(gpus=2, auto_select_gpus=True)
#选取所有可用的GPU
Trainer(gpus=-1, auto_select_gpus=True)
3.5 基础设置(log目录,检查点,终止条件,进度条显示)
3.5.1 设置保存训练log以及checkpoint的目录,默认为当前目录,可根据需要设定
# 默认
trainer = Trainer(default_root_dir=os.getcwd())
3.5.2 保存检查点
默认开启检查点,开启后会在Trainer的回调函数列表callbacks中加入一个ModelCheckpoint回调默认保存的是最新一个epoch的检查点
trainer = Trainer(enable_checkpointing=True)
也可以实现自己的ModelCheckpoint回调来保存满足需要的检查点,例如保存验证集上误差最小的检查点:
from pytorch_lightning.callbacks import ModelCheckpoint
# 初始化ModelCheckpoint,设置监控的变量名称
checkpoint_callback = ModelCheckpoint(monitor="val_acc")
# 将checkpoint_callback加入Trainer的回调函数列表
trainer = Trainer(enable_checkpointing=True, callbacks=[checkpoint_callback])
检查点默认保存位置与default_root_dir一致,可以专门设置weights_save_path为模型保存的位置
trainer = Trainer(weights_save_path="./checkpoints")
3.5.3 加载检查点
trainer.fit(ckpt_path="./checkpoints/latest.ckpt")
#推荐上面那种方法
trainer = Trainer(resume_from_checkpoint="./checkpoints/latest.ckpt")
3.5.4 设置终止条件
设置训练最大/最小的epoch,也可以设置step,默认参数为:
trainer = Trainer(min_epochs = None, max_epochs = None, min_steps = None, max_steps = -1)
此外也可以设置最大的训练时长,接受的格式为字符串或字典
trainer = Trainer(max_time="01:05:00:00")
trainer = Trainer(max_time={"days": 1, "hours": 5})
3.5.5 进度条
可以选择是否显示进度条以及进度条的刷新频率
trainer = Trainer(enable_progress_bar=True, progress_bar_refresh_rate=1)
3.6 辅助工具
3.6.1 最大batch size搜索
可以在训练开始之前来搜索可以使用的最大batch size,并应用于trainer
设置auto_scale_batch_size=“binsearch”,并执行trainer.tune(model)进行搜索,搜索到的最大batch size后将会自动覆盖trainer的hparams.batch_size
trainer = Trainer(auto_scale_batch_size="binsearch")
trainer.tune(model)
3.6.2 自动学习率查找
用法与自动batch size查找类似,设置auto_lr_find=True,并使用trainer.tune(model)来查找最合适的初始学习率
trainer = Trainer(auto_lr_find=True)
trainer.tune(model)
三 omegaconf 库
1 基础用法
OmegaConf 是一个用于处理配置文件的工具库,它可以帮助开发者轻松地读取、修改和管理配置信息
用以下config.yaml作为示范:
model:
name: "ResNet"
num_layers: 18
dropout: 0.5
optimizer:
name: "Adam"
learning_rate: 0.001
加载配置文件:
from omegaconf import OmegaConf
# 使用 YAML 文件加载配置
config = OmegaConf.load("config.yaml")
访问配置文件:
#使用点运算符来访问嵌套配置项:
model_name = config.model.name
num_layers = config.model.num_layers
#使用索引来访问列表项:
layer_1 = config.model.layers[0]
layer_2 = config.model.layers[1]
#使用 get() 方法来访问配置项,并提供默认值:
dropout = config.model.get("dropout", 0.0)
learning_rate = config.model.optimizer.get("learning_rate", 0.01)
修改配置信息:
#使用点运算符来修改嵌套配置项:
config.model.name = "VGG"
config.model.num_layers = 16
#使用索引来修改列表项:
config.model.layers[0] = "Conv"
config.model.layers[1] = "ReLU"
#使用 update() 方法来修改整个配置块:
config.model.optimizer.update({"learning_rate": 0.0001})
四 transformers库
4.1 NLP Tokenizer
处理文本数据的主要工具是tokenizer,首先tokenizer会根据一组规则将文本拆分成token,然后将这些token转换成数值(根据词表,即 vocab),这些数值会被构建成张量并作为模型的输入,模型所需要的其他输入也是有tokenizer添加的。
4.1.1 加载
from transformers import BertTokenizer,BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
4.1.2 使用
BertTokenizer主要实现文本分词和把分词后的token编码成符合模型输入要求的整数索引功能, 为满足不同预训练模型对文本序列的要求,BertTokenizer还会自动在文本序列中添加一些额外符号([CLS]、[SEP]、[PAD])等。
BertTokenizer类包含三个文件:vocab.txt,tokenizer.json和tokenizer_config.json,vocab.txt是词表文件(文件中是保留符号和单个汉字索引,字符),tokenizer.json和tokenizer_config.json是分词的配置文件,根据设置文件把vocab词表中的词按顺序生成索引号(行号-1就是索引号),模型根据词对应的索引号编码生成one-hot向量与embeding训练权重矩阵相乘获得该字符的词向量。(简单说就是token根据词表中单词对应的索引编号查找Embedding.weight的词向量表生成模型可使用的词向量。)
此外,tokenizer主要完成的工作:
1.分词:将文本数据分词为字或者字符;
2.构建词典:根据数据集分词的结果,构建词典。射(这一步并不绝对,如果采用预训练词向量,词典映射要根据词向量文件进行处理)。
3.数据转换:根据构建好的词典,将分词处理后的数据做映射,将文本序列转换为数字序列。数字序列还要变成符合模型需求的tensor格式。
4.数据填充与截断:在以batch输入到模型的方式中,需要对过短的数据进行填充,过长的数据进行截断,保证数据长度符合模型能接受的范围,同时batch内的数据维度大小一致,否则无法成批次变成tensor张量。
4.1.2.1 tokenize
该函数实现分词功能
txt = '这是一个美丽的城市'
token_ids = tokenizer.tokenize(txt)
print(token_ids)
#['这', '是', '一', '个', '美', '丽', '的', '城', '市']
4.1.2.2 convert_tokens_to_ids
该函数将每个词转换为词典的索引
input_ids = tokenizer.convert_tokens_to_ids(token_ids)
print(input_ids)
#[6821, 3221, 671, 702, 5401, 714, 4638, 1814, 2356]
4.1.2.3 encode
encode()方法完成分词和整数编码的同时,系统对文本序列自动添加分类符和间隔符,在函数实参传递过程中不设置return_tensors属性时函数返回值为列表,设置return_tensors='pt’返回值为pytorch的tensor张量。
txt = '这是一个美丽的城市'
ckpt = r'.\ConvAdapter\vocab\bert\chinese_roberta_wwm_base_ext_pytorch'
tokenizer =BertTokenizer.from_pretrained(ckpt)
######encode函数######
input_ids = tokenizer.encode(txt,max_length=20,add_special_tokens=True,
padding='max_length', truncation=True)
print(input_ids)
#返回一个list
#[101, 6821, 3221, 671, 702, 5401, 714, 4638, 1814, 2356, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0]
4.1.2.4 add_tokens([new_vocab])
向词典添加新词
num_added_toks = tokenizer.add_tokens('COVID')
print(num_added_toks)
# num_added_toks表示新添加词汇数量
result = tokenizer.tokenize('COVID')
print(result)
N 其他零散函数
1.functools.partial
functools.partial 可以用来固定一个函数的部分参数,从而创建一个新的函数,这个新函数的调用会比原函数少接受一些参数
from functools import partial
# 定义一个简单的函数
def multiply(x, y):
return x * y
# 使用 partial 固定其中一个参数为 2
double = partial(multiply, 2)
# 现在 double 就是一个新的函数,只需要一个参数 y
result = double(3) # 等价于调用 multiply(2, 3)
print(result) # 输出 6