PyTorch训练模型转ONNX

方法一

加载Pipeline

从transformers库中加载FeatureExtractionPipeline。如果知道要使用的模型的所有输入形状和配置,则可以不需要此步骤。使用 convert_graph_to_onnx 中的相应函数可以显着简化自定义模型的创建。生成的变量将用于torch导出调用。

from transformers import FeatureExtractionPipeline, AutoModel, AutoTokenizer, convert_graph_to_onnx

model_access = "my_model_dir"
model_pipeline = FeatureExtractionPipeline(
        model=AutoModel.from_pretrained(model_access),
        tokenizer=AutoTokenizer.from_pretrained(model_access, use_fast=True),
        framework="pt",
        device=-1)

config = model_pipeline.model.config
tokenizer = model_pipeline.tokenizer

with torch.no_grad():
    input_names, output_names, dynamic_axes, tokens = convert_graph_to_onnx.infer_shapes(model_pipeline, "pt")
    ordered_input_names, model_args = convert_graph_to_onnx.ensure_valid_input(
            model_pipeline.model, tokens, input_names)

# 如果想添加更多的输出,则必须相应地修改dynamic_axes和output_names。
del dynamic_axes["output_0"]  # Delete unused output
del dynamic_axes["output_1"]  # Delete unused output

output_names = ["output"]
dynamic_axes["output"] = {0: 'batch'}

# 导出模型到ONNX
model = torch.load("best_model.pth")
output = "best_model.onnx"
torch.onnx.export(
        model,
        model_args,
        f=output,
        input_names=input_names,
        output_names=output_names,
        dynamic_axes=dynamic_axes,
        do_constant_folding=True,
        use_external_data_format=False,
        enable_onnx_checker=True,
        opset_version=11)

# 检查 onnx model
onnx_model = onnx.load(output)
onnx.checker.check_model(onnx_model)
print('The model is checked!')

加载onnx模型进行推理

import numpy as np
from transformers import AutoTokenizer
import onnxruntime as rt
import time

onnx_model_path = "best_model.onnx"
model_path = "best-checkpoint"  # my_model_dir
tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False)
span = "输入"  # 输入的文本

opt = rt.SessionOptions()
sess = rt.InferenceSession(onnx_model_path)  # Loads the model
t0 = time.perf_counter()
model_input = tokenizer.encode_plus(span)
model_input = {name: np.atleast_2d(value) for name, value in model_input.items()}
onnx_result = sess.run(None, model_input)
onnx_result = onnx_result[0]
onnx_result = np.argmax(onnx_result, axis=-1)
print(time.perf_counter() - t0)

方法二

pt2onnx

import os
import torch
from transformers import BertTokenizer, get_linear_schedule_with_warmup,BertForSequenceClassification, AdamW
import onnx

def pt2onnx_bert():
    pretrained_model = '../model/bert-base-chinese'
    onnx_path = 'api/onnx/bert-base-chinese-cls.onnx'
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    tokenizer = BertTokenizer.from_pretrained(pretrained_model)
    model = BertForSequenceClassification.from_pretrained(pretrained_model)
    model.eval()
    model.to(device)

    input_names = ['input_ids', 'attention_mask', 'token_type_ids']
    outputs_names = ['output']
    # 构造输入
    inputs = '输入'
    encode_dict = tokenizer.encode_plus(text=inputs,
                                        max_length=512,
                                        pad_to_max_length=True,
                                        return_tensors='pt',
                                        return_token_type_ids=True,
                                        return_attention_mask=True)

    input_ids = encode_dict['input_ids']
    attention_mask = encode_dict['attention_mask']
    token_type_ids = encode_dict['token_type_ids']

    dummy_input = {
        'input_ids': input_ids,
        'attention_mask': attention_mask,
        'token_type_ids': token_type_ids
    }
    with torch.no_grad():
        torch.onnx.export(model=model,
                          args=tuple(dummy_input.values()),
                          f=onnx_path,
                          opset_version=11,
                          input_names=input_names,
                          output_names=outputs_names,
                          dynamic_axes={'input_ids': {0: 'batch_size'},
                                        'attention_mask': {0: 'batch_size'},
                                        'token_type_ids': {0: 'batch_size'},
                                        'output': {0: 'batch_size'}}
                          )
    # 验证
    print(onnx.checker.check_model(onnx_path))

推理

from onnxruntime import GraphOptimizationLevel, InferenceSession, SessionOptions, get_all_providers

def create_model_for_provider(model_path: str, provider: str) -> InferenceSession:
    assert provider in get_all_providers(), f"provider {provider} not found, {get_all_providers()}"

    options = SessionOptions()
    #控制线程数
    options.intra_op_num_threads = 0
    options.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL
    session = InferenceSession(model_path, options, providers=[provider])
    return session

def onnx_inference(x):
    #CPUExecutionProvider
    ort_session = create_model_for_provider(onnx_model_path, 'CUDAExecutionProvider')
    encode_dict = tokenizer.batch_encode_plus(batch_text_or_text_pairs=x,
                                              max_length=MAX_SEQ_LEN,
                                              pad_to_max_length=True,
                                              return_tensors='pt',
                                              return_token_type_ids=False,
                                              return_attention_mask=True)

    inputs = {k: v.numpy() for k, v in encode_dict.items()}
    outputs = ort_session.run(None, inputs)
    # 不同的模型对应不同的以下代码
    outputs = outputs[0]
    outputs = np.argmax(outputs, axis=-1)
    result = []
    for out in outputs:
        result.append(id2label[out])
    return result

Cython编译python

from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension

extensions = [
    Extension(name='name1',
              sources=['source file'],
              include_dirs=['head files'],
              libraries=['library names'],
              library_dirs=['library directories']),
    Extension(name='name2',
              sources=['source file'],
              include_dirs=['head files'],
              libraries=['library names'],
              library_dirs=['library directories'])
]
# setup.py 放在 sources同目录
# 按照上述方式配置,会生成两个so,name1.so和name2.so
# 命令:python setup.pu build_ext,同时生成c文件和so
setup(
    name='name',
    # language_level=3指定python3
    ext_modules=cythonize(extensions, language_level=3)
)

参考

https://github.com/oborchers/sentence-transformers/blob/72da7293186700bff4fed8e81adcfe40a03be1d2/examples/onnx_inference/onnx_inference.ipynb

https://zhuanlan.zhihu.com/p/422290231

服务器后台远程复制文件scp

正常执行scp命令

从一台服务器复制文件夹到另一台服务器

scp -r origin_dir ip:target_dir

输入Ctrl+z暂停任务

使用bg命令将其放入后台

bg %1

使用jobs查看状态,已经在后台运行了

disown -h 将这个作业忽略HUP信号

查看运行状态和父进程号

ps -ef | grep scp

测试会话中断,任务继续运行不受影响

首先退出

exit

然后再查看状态

ps -ef | grep scp

参考

https://blog.csdn.net/sraing/article/details/85121543

论文笔记:Few-Shot Learning with Siamese Networks and Label Tuning

前言

论文:https://arxiv.org/pdf/2203.14655v2.pdf

代码:https://github.com/symanto-research/few-shot-learning-label-tuning

摘要

在训练数据很少或没有训练数据的情况下构建文本分类器的问题,通常称为零样本和小样本文本分类。近年来,一种基于神经文本蕴涵模型的方法已被发现在不同的任务范围内提供强大的结果。在这项工作中表明,通过适当的预训练,嵌入文本和标签的Siamese网络提供了一个有竞争力的替代方案。这些模型大大降低推理成本:标签数量恒定,而不是线性。此外,还引入了标签调优,是一种简单且计算效率高的方法,它允许通过仅更改标签嵌入来在少量设置中适应模型。虽然提供比模型微调更低的性能,但这种方法具有体系结构上的优势,即单个编码器可以由多个不同的任务共享。

动机

  • 对于文本数据,任务的标签通常是通过文本形式来实现的,可以是标签的名称,也可以是简明的文本描述。
  • Cross Attention(CA)和Siamese Networks(SNs)还通过对小样本的模型进行微调来支持小样本学习。这通常是通过更新模型的所有参数来完成的,导致不能在不同任务之间共享模型。
  • 作者提出Label Tuning(LT)作为完全微调的替代方案,它允许对许多任务使用相同的模型,从而极大地增加了方法的可伸缩性。

模型

图1 Overview of training and inference with Label Tuning (LT)

在训练时,输入和标签文本由编码器处理。然后Label Tuning(LT)使用交叉熵(CE)损失调优标签。在推理时,输入文本通过相同的编码器。然后使用调优的标签嵌入和相似度函数对每个标签进行评分。编码器保持不变,可以在多个任务之间共享。

方法

Fine-Tuning

在小样本学习的情况下,需要在小样本的基础上对模型进行调整。在基于梯度的小样本学习中,尝试提高带有标签的小样本的相似度分数。从概念上讲,希望增加每个文本及其正确标签之间的相似度,并减少每个其他标签之间的相似度。使用batch softmax作为目标:

其中B是batch size,S(x, y) = f(x)·f(y)是当前模型f下输入x与标签文本y的相似度。在batch中,所有其他元素都作为负样本。为此,使每个batch中只包含每个标签的一个样本。这类似于典型的softmax分类目标。唯一的区别是f(yi)是在前向传播中计算的,而不是作为一个简单的参数查找。

Label Tuning

定期微调的缺点是需要更新整个网络的权重。这导致每个新任务都需要较慢的训练和较大的内存需求,这反过来又增加了大规模部署新模型的挑战。作为一种替代方案,引入标签调优,它不改变编码器的权重。主要思想是首先预先计算每个类的标签嵌入,然后使用小样本对它们进行调优。有一个包含N对输入文本xi及其参考标签索引zi的训练集。预先计算输入文本的矩阵表示和标签的矩阵表示,X∈RN×d,Y∈RK×d,d为embedding的维数,K为标签集的大小。每个输入和标签组合的相似度分数定义为S = X × YT(S∈RN×K),并利用交叉熵进行调优。

为了避免过拟合,使用范数添加了一个正则化,惩罚偏离初始标签嵌入太远的位置。另外,还通过在标签embedding中每个梯度步上mask一些项实现dropout。通过在小样本上4折交叉验证进行微调。要为每个调优模型存储的唯一信息是d维标签嵌入。

Knowledge Distillation

标签微调产生的模型比实际的微调精确度低。可以通过知识蒸馏来补偿。首先训练一个标准的微调模型,并使用该模型为未标注的样本生成标签分布。之后,将该数据集用于训练未调优模型的新标签embedding。这增加了该方法的训练成本,并增加了对未标注数据的额外要求,但保留了在推断时可以跨多个任务共享一个模型的优点。

结论

论文证明了Cross Attention(CA)和Siamese Networks(SN)在不同的任务集和多种语言中提供了类似的结果。SNs的推理成本很低,因为标签embedding可以预先计算,而且与CA相比,SNs不随标签数量的增加而扩展。

只调优标签embeddings(LT)是可以替代微调(FT)方法的。当训练样本数量较少并且结合知识蒸馏时,即在小样本学习场景下,LT的性能接近FT。这与生产场景相关,因为它允许在任务之间共享相同的模型。LT是快速和可扩展的小样本学习下的一个选择。

论文笔记:Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference

前言

论文:https://arxiv.org/pdf/2001.07676.pdf%EF%BC%89

代码:https://github.com/timoschick/pet

摘要

一些NLP任务通过使用预先训练好的带有“任务描述”的语言模型,可以以完全无监督的方式解决。虽然这种方法的性能不如监督方法,但本篇工作中表明,这两种思想可以结合起来:作者引入了Pattern-Exploiting Training(PET),一种半监督的训练过程,它将输入示例重新定义为完形句式短语,以帮助语言模型理解给定的任务。然后使用这些短语为大量无标签的样本分配软标签。最后,对得到的训练集进行标准监督训练。对于一些任务和语言,PET在低资源环境下的表现远远优于监督训练和强半监督方法。

模型架构

图1 PET for sentiment classification

PET的工作过程分为三个步骤:

首先,针对每个模式,在小训练集T上对单独的PLM进行微调。

然后,使用所有模型的集合对一个大型的无标签数据集D进行软标签标注。

最后,在软标记数据集上训练标准分类器。

作者还设计了iPET,这是PET的迭代变体,该过程随着训练集规模的增加而重复。

Pattern-Exploiting Training

参数概况

M为预训练语言模型,词表为V,mask token包含在V中,任务A的标签集合L,输入序列x包含k个短语或句子。

定义一个模型函数(pattern)P,将x作为输入,输出包含一个mask token的短语或句子P(x),输出可以看作是一个完形填空。

定义一个映射函数(verbalizer)v,L—>V将每个标签映射到M的词汇表中的一个单词。

将(P, v)称为pattern-verbalizer pair(PVP)。

PVP Training and Inference

给定某个输入x,我们定义标签l∈L的分数为:

并使用softmax获得标签上的概率分布:

使用交叉熵作为微调的损失。

Auxiliary Language Modeling

只使用少量样本进行训练,可能发生灾难性遗忘。对于基于PVP的PLM微调,核心仍然是一个语言模型,我们通过使用语言建模作为辅助任务来解决这个问题。LCE表示交叉熵损失,LMLM表示语言建模损失,计算最终损失为:

LMLM通常比LCE大得多,令α = 10−4

Combining PVPs

该方法面临的一个关键挑战是,在缺乏大型验证集的情况下,很难确定哪个PVPs表现良好。为了解决这个问题,使用了类似于知识蒸馏的策略。定义一个PVPs的集合P,直观地理解给定任务A的意义,按如下方法使用这些PVPs:

  1. 为每个p∈P微调一个单独的语言模型Mp
  1. 使用经过优化的模型的集合M = {Mp | p∈P}来标注集合D(未标注数据集合)。将每个样本x∈D的非归一化类分数合并为:

使用softmax将上述分数转换为概率分布q。使用T = 2得到适当的软分布。所有(x, q)对收集在一个(软标记的)训练集TC中。

  1. 在TC上,使用PLM微调得到模型C。经过优化的模型C作为对A的分类器。
图2 Schematic representation of PET (1-3) and iPET (a-c)

Iterative PET (iPET)

将所有单个模型的知识蒸馏到单个分类器中——意味着它们不能相互学习。由于有些模式的性能可能比其他模式差得多,因此最终模型的训练集TC可能包含许多标签错误的示例。为了弥补这个缺点,设计了iPET,它是PET的迭代变体。iPET的核心思想是在不断增大的数据集上训练几代模型。过程如图2所示。

  1. 对于每个模型,其他模型的一个随机子集通过标注D中的样本生成一个新的训练集。
  1. 使用更大的、特定于模型的数据集训练一组新的PET模型。
  1. 前两个步骤重复k次,每次都将生成的训练集的大小增加d倍。

经过训练k代PET模型,我们使用Mk创建TC和训练模型C在基本的PET。

结论

为预训练的语言模型提供任务描述可以与标准的监督训练相结合。PET方法包括定义成对的完形填空问题模式(patterns)和语言表达器(verbalizers),这有助于利用预训练的语言模型中包含的知识来完成下游任务。对所有模式-语言化器对的模型进行微调,并使用它们创建大型的带注释的数据集,标准分类器可以在这些数据集上进行训练。当初始训练数据量有限时,PET比标准监督训练和强半监督方法有很大的改进。

Git入门篇

git常用命令

请使用status命令确认工作树和索引的状态。

$ git status

将文件加入到索引,要使用add命令。在<file>指定加入索引的文件。用空格分割可以指定多个文件。

$ git add <file>..

指定参数「.」,可以把所有的文件加入到索引。

$ git add .

请执行如下显示的commit命令。

$ git commit -m “”

执行commit命令之后确认状态。

$ git commit -m “first commit”

$ git status

# On branch master

nothing to commit (working directory clean)

从status响应我们可以看到没有新的变更要提交。

使用log命令,我们可以在数据库的提交记录看到新的提交。

$ git log

可以将远程数据库命名为“origin”。

请使用remote指令添加远程数据库。在<name>处输入远程数据库名称,在<url>处指定远程数据库的URL。

$ git remote add <name> <url>

git remote add origin <git的http地址>

执行推送或者拉取的时候,如果省略了远程数据库的名称,则默认使用名为”origin“的远程数据库。因此一般都会把远程数据库命名为origin。

使用push命令向数据库推送更改内容。<repository>处输入目标地址,<refspec>处指定推送的分支。我们将在高级篇详细地对分支进行说明。

$ git push <repository> <refspec>…

运行以下命令便可向远程数据库‘origin’进行推送。当执行命令时,如果您指定了-u选项,那么下一次推送时就可以省略分支名称了。但是,首次运行指令向空的远程数据库推送时,必须指定远程数据库名称和分支名称。

$ git push -u origin master

使用clone指令可以复制数据库,在<repository>指定远程数据库的URL,在<directory>指定新目录的名称。

$ git clone <repository> <directory>

执行以下指令后,会在目录(test2) 复制远程数据库。

$ git clone <git的http地址> test2

test2进行的操作

然后,推送此次变更,更新远程数据库。

当在克隆的数据库目录执行推送时,您可以省略数据库和分支名称。

$ git push

从“test2”推送到远程数据库的内容拉取到数据库目录“test

使用pull指令进行拉取操作。省略数据库名称的话,会在名为origin的数据库进行pull。

$ git pull <repository> <refspec>…

test进行的操作

请执行以下指令。

$ git pull origin master

使用log指令来确认历史记录是否已更新。

$ git log

git远程分支覆盖本地分支

有时候同一个分支,远程的和本地的都被修改的面目全非了,如果想要把本地的替换成远程的,用下面的命令

git fetch –all

git reset –hard origin/master (这里master要修改为对应的分支名)

git pull

Git 全局设置

  • Git的设定被存放在用户本地目录的.gitconfig档案里。虽然可以直接编辑配置文件,但在这个教程里我们使用config命令。

git config –global user.name “banyungong”

git config –global user.email “banyungong@test.com”

  • 以下命令能让Git以彩色显示。

git config –global color.ui auto

  • 可以为Git命令设定别名。例如:把「checkout」缩略为「co」,然后就使用「co」来执行命令。

git config –global alias.co checkout

创建一个新仓库

git clone <url>

cd test

touch README.md

git add README.md

git commit -m “add README”

git push -u origin master

推到一个存在的文件夹中

cd existing_folder

git init # 然后使用init命令把该existing_folder目录移动到本地Git数据库。

git remote add origin <url>

git add .

git commit -m “Initial commit”

git push -u origin master

推到现有的 Git 仓库

cd existing_repo

git remote rename origin old-origin

git remote add origin <url>

git push -u origin –all

git push -u origin –tags

git管理代码的工作原则

  • 不同版本的代码,在同一个仓库,需要打上不同的tag
  • 开发要在分支上,例如dev分支
  • 使用开发分支上线,上线没有问题之后合入master,方便代码回滚(用master就可以代码回滚)
  • master的代码需和线上代码保持一致
  • 上线之后,给master打上tag,方便以后代码恢复
  • 以上线的版本代码都能有办法恢复
  • 版本迭代更新时,需要从master分支上拉代码到dev,再开始开发

上传代码到dev分支

git status
git add .
git commit -m "更新的功能"
git push origin dev

合并分支到master

git checkout master
git pull origin master
git merge dev
git status
git push origin master

ImportError: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.11′

报错信息

在docker中安装faiss后,测试可以正常导入,但是程序跑起来就会报错,报错信息如下:

ImportError: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.11′ not found (required by /root/anaconda3/envs/cpu/lib/python3.6/site-packages/faiss/../../../libfaiss.so)

解决方案

首先查看导入错误的目录上有什么文件,发现确实没有需要的“`CXXABI_1.3.11”

strings /usr/lib/x86_64-linux-gnu//libstdc++.so.6 | grep CXXABI

ll /usr/lib/x86_64-linux-gnu//libstdc++.so.6

find /usr -name “libstdc++.so.*”

find / -name “libstdc++.so.*”

cp /root/anaconda3/envs/cpu/lib/libstdc++.so.6.0.29 /usr/lib/x86_64-linux-gnu/

cd /usr/lib/x86_64-linux-gnu/

find /usr -name “libstdc++.so.*”

rm libstdc++.so.6

ln -s libstdc++.so.6.0.29 libstdc++.so.6

strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep CXXABI

参考

https://blog.csdn.net/wenroudebaozi/article/details/107564647