transformers入门使用

transformers入门使用

HuggingFace是一个开源社区,它提供了先进的NLP模型,数据集,以及其他工具。

模型:https://huggingface.co/models

数据集:https://huggingface.co/datasets

主要的模型

  • 自然回归: GPT2, Trasnformer-XL, XLNet
  • 自编码: BERT , ALBERT, ROBERTa, ELECTRA
  • StoS: BART, Pegasus, T5

1 简介

transformers库是一个用于自然语言处理(NLP)的机器学习库,提供了近几年在NLP领域取得巨大成功的预训练模型,例如BERTGPTRoBERTaT5等。

该库由Hugging Face公司开发,是目前最流行的NLP预训练模型库之一。在实际应用中,使用已经训练好的模型可以显著提高模型的效果和速度。

同时,该库还提供了丰富的工具和API,可以帮助用户更方便地使用这些预训练模型,包括文本分类、实体识别、摘要生成、文本翻译等各种NLP任务。

transformers库支持多种主流深度学习框架,例如PyTorchTensorflow等,用户可以根据自己的喜好选择相应的框架进行使用。

BERT(Bidirectional Encoder Representations from Transformers)是一种用于自然语言处理(NLP)的预训练模型。它是在2018年由Google发布的,并被认为是最具革命性的NLP模型之一。BERT模型是基于Transformer网络结构的,并使用了一种预训练的方法来学习上下文相关的词向量表示。

BERT模型的目标是让一个模型具有理解自然语言的能力,而不是人为地将所有知识和规则硬编码到算法中。这种目标是通过对巨型文本数据集进行预训练来实现的。BERT可以学习从一个句子到另一个句子的上下文语境,因此可以更好地处理自然语言。

BERT模型的架构涵盖了两个阶段:预训练和微调。

  • 预训练:在预训练阶段中,BERT模型利用无监督的方式从海量的文本数据中学习常识。在这个过程中,模型先使用“掩码语言模型”(Masked Language Model,MLM)来训练模型,然后再使用“下一句预测模型”(Next Sentence Prediction,NSP)来进一步提高模型的性能。MLM使BERT模型学会推断词语中被遮盖的部分,而NSP是让BERT模型对文本对的关系进行预测,是一个判断文本连贯性的过程。预训练过程中,BERT模型可处理各种类型的文本数据,包括电子邮件、网页、微博、评论等等。

  • 微调:微调阶段是指将预训练的模型用于实际任务,并根据实际任务的目标对模型进行微调。BERT模型的微调可以应用于多种自然语言处理任务,包括情感分析、自动问答、文本分类等等。

BERT模型的优点是它可以处理各种不规则文本数据,并提供比以前的NLP模型更加准确和有用的结果,因为BERT能够建立更好的理解自然语言的语境。此外,BERT还有一个优点是它开放源代码,可以自由使用和修改,方便大家进行相关研究和开发。

2 分词器 transformers.BertTokenizer

BERT 分词器是一种自然语言处理工具,主要用于将文本数据进行分词、词性标注、实体识别、句子划分等预处理操作。

具体来说,BERT 分词器的作用包括以下几个方面:

  1. 分词:将文本中的连续字符串按照一定规则划分成单个的词语,常用的算法有最大匹配、最小匹配、双向匹配等。

  2. 词性标注:对每个词语进行词性标注,例如名词、动词、形容词等,有助于后续的语义分析。

  3. 实体识别:识别文本中的人名、地名、组织机构名等实体,为知识图谱构建和命名实体识别等任务提供数据基础。

  4. 句子划分:将整篇文本按照句子结构进行划分,为文本分类、情感分析等任务提供语义单元划分的基础。

BERT 分词器编码句子的作用:

  1. BERT 分词器在进行分词时,不仅仅是将每个单词转化为固定的编号,而是将整个句子编码为一个向量。这是因为句子的语义信息往往不仅仅取决于每个单词的意义,还和上下文有关。编码句子可以使得句子的语义信息包含在向量中,有助于后续的自然语言处理任务。

  2. 具体来说,BERT 分词器采用的是 Transformer 模型,这种模型可以将句子作为输入,经过多层自注意力(self-attention)机制的加工,得到一个表示整个句子的向量,称为句子嵌入(sentence embedding)向量。这种方法可以同时考虑每个单词的上下文信息,而且还可以避免传统的词向量模型中单词之间的相关性计算问题。

  3. 通过编码句子,BERT 分词器为后续的自然语言处理任务提供了更丰富的语义信息。而且由于 BERT 分词器是基于预训练的方式进行编码句子,其能力可以应用到多个自然语言处理任务中,从而实现了更广泛的语义理解和处理。

总的来说,BERT 分词器的主要作用是将自然语言文本进行可处理的单元划分,为后续的BERT 模型自然语言处理任务提供数据基础。

下面是分词器的代码演示:

from transformers import BertTokenizer

# 加载分词器
tokenizer = BertTokenizer.from_pretrained(
    pretrained_model_name_or_path='bert-base-chinese',
    cache_dir=None,
    force_download=False,
)

# 待编码的句子
sents = [
    '选择珠江花园的原因就是方便。',
    '笔记本的键盘确实爽。',
    '房间太小。其他的都一般。',
    '今天才知道这书还有第6卷,真有点郁闷.',
    '机器背面似乎被撕了张什么标签,残胶还在。',
]

tokenizer, sents

2.1 简单编码

简单编码,一次可以编码一个句子或者两个句子。

分词器的encode方法可以将tokens编码成ids.

# 编码两个句子
input_ids = tokenizer.encode(
    text=sents[0],  # 句子1
    text_pair=sents[1],  # 句子2
    truncation=True,  # 当句子长度大于max_length时,截断
    padding='max_length',  # 一律补pad到max_length长度
    add_special_tokens=True,
    max_length=30,  # 最大长度
    return_tensors=None,   # 返回list
)

print(input_ids)

tokenizer.decode(input_ids)

"""
句子编码后的结果:
[101, 6848, 2885, 4403, 3736, 5709, 1736, 4638, 1333, 1728, 2218, 3221, 3175, 912, 511, 102, 5011, 6381, 3315, 4638, 7241, 4669, 4802, 2141, 4272, 511, 102, 0, 0, 0]

解码:
'[CLS] 选 择 珠 江 花 园 的 原 因 就 是 方 便 。 [SEP] 笔 记 本 的 键 盘 确 实 爽 。 [SEP] [PAD] [PAD] [PAD]'
"""

2.2 增强编码

# 增强的编码
out = tokenizer.encode_plus(
    text=sents[0],  # 句子1
    text_pair=sents[1],  # 句子2
    truncation=True,  # 当句子长度大于max_length时,截断
    padding='max_length',  # 一律补零到max_length长度
    max_length=30,  # 最大长度
    add_special_tokens=True,
    return_tensors=None,  # 可取值tf,pt,np,默认为返回list
    return_token_type_ids=True,  # 返回token_type_ids
    return_attention_mask=True,  # 返回attention_mask
    return_special_tokens_mask=True,  # 返回special_tokens_mask 特殊符号标识
    #返回offset_mapping 标识每个词的起止位置,这个参数只能BertTokenizerFast使用
    #return_offsets_mapping=True,
    #返回length 标识长度
    return_length=True,
)

# input_ids           就是编码后的词
# token_type_ids      第一个句子和特殊符号的位置是0,第二个句子的位置是1
# special_tokens_mask 特殊符号的位置是1,其他位置是0
# attention_mask      pad的位置是0,其他位置是1
# length             返回句子长度
for k, v in out.items():
    print(k, ':', v)
    
"""
input_ids : [101, 6848, 2885, 4403, 3736, 5709, 1736, 4638, 1333, 1728, 2218, 3221, 3175, 912, 511, 102, 5011, 6381, 3315, 4638, 7241, 4669, 4802, 2141, 4272, 511, 102, 0, 0, 0]
token_type_ids : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
special_tokens_mask : [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
attention_mask : [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
length : 30
"""

# 解码
tokenizer.decode(out['input_ids'])
"""
'[CLS] 选 择 珠 江 花 园 的 原 因 就 是 方 便 。 [SEP] 笔 记 本 的 键 盘 确 实 爽 。 [SEP] [PAD] [PAD] [PAD]'
"""

2.3 批量增强编码

第一种:

# 批量编码句子
out = tokenizer.batch_encode_plus(
    batch_text_or_text_pairs=[sents[0], sents[1]],  # 编码两个句子
    add_special_tokens=True,  # 特殊符号的位置是1,其他位置是0
    truncation=True,  # 当句子长度大于max_length时,截断
    padding='max_length',  # 不足最大长度时, 一律补零到max_length长度
    max_length=15,  # 最大长度
    return_tensors=None,  # 可取值tf,pt,np,默认为返回list
    return_token_type_ids=True,  # 返回token_type_ids 第一个句子和特殊符号的位置是0,第二个句子的位置是1
    return_attention_mask=True,  # 返回attention_mask pad(不足最大长度补0)的位置是0,其他位置是1
    return_special_tokens_mask=True,  # 返回special_tokens_mask 特殊符号标识
    # 返回offset_mapping 标识每个词的起止位置,这个参数只能BertTokenizerFast使用
    # return_offsets_mapping=True,
    # 返回length 标识长度
    return_length=True,
)

# input_ids           就是编码后的词
# token_type_ids      第一个句子和特殊符号的位置是0,第二个句子的位置是1
# special_tokens_mask 特殊符号的位置是1,其他位置是0
# attention_mask      pad的位置是0,其他位置是1
# length             返回句子长度
for k, v in out.items():
    print(k, ':', v)
    
"""
input_ids : [[101, 6848, 2885, 4403, 3736, 5709, 1736, 4638, 1333, 1728, 2218, 3221, 3175, 912, 102], [101, 5011, 6381, 3315, 4638, 7241, 4669, 4802, 2141, 4272, 511, 102, 0, 0, 0]]
token_type_ids : [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
special_tokens_mask : [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]]
length : [15, 12]
attention_mask : [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]]
"""

# 解码两个句子
tokenizer.decode(out['input_ids'][0]), 
"""
'[CLS] 选 择 珠 江 花 园 的 原 因 就 是 方 便 [SEP]'
"""

tokenizer.decode(out['input_ids'][1])
"""
'[CLS] 笔 记 本 的 键 盘 确 实 爽 。 [SEP] [PAD] [PAD] [PAD]'
"""

第二种:

# 批量编码成对的句子
out = tokenizer.batch_encode_plus(
    batch_text_or_text_pairs=[(sents[0], sents[1]), (sents[2], sents[3])],  # 可以编码成对的多种组合 
    add_special_tokens=True,
    truncation=True,  # 当句子长度大于max_length时,截断
    padding='max_length',  # 不足最大长度的位置,补到最大长度
    max_length=30,  # 最大长度
    return_tensors=None,  # 可取值tf,pt,np,默认为返回list
    return_token_type_ids=True,  # 返回token_type_ids
    return_attention_mask=True,  # 返回attention_mask
    #返回special_tokens_mask 特殊符号标识
    return_special_tokens_mask=True,
    # 返回offset_mapping 标识每个词的起止位置,这个参数只能BertTokenizerFast使用
    # return_offsets_mapping=True,
    # 返回length 标识长度
    return_length=True,
)

# input_ids           就是编码后的词
# token_type_ids      第一个句子和特殊符号的位置是0,第二个句子的位置是1
# special_tokens_mask 特殊符号的位置是1,其他位置是0
# attention_mask      pad的位置是0,其他位置是1
# length             返回句子长度
for k, v in out.items():
    print(k, ':', v)

"""
input_ids : [[101, 6848, 2885, 4403, 3736, 5709, 1736, 4638, 1333, 1728, 2218, 3221, 3175, 912, 511, 102, 5011, 6381, 3315, 4638, 7241, 4669, 4802, 2141, 4272, 511, 102, 0, 0, 0], [101, 2791, 7313, 1922, 2207, 511, 1071, 800, 4638, 6963, 671, 5663, 511, 102, 791, 1921, 2798, 4761, 6887, 6821, 741, 6820, 3300, 5018, 127, 1318, 117, 4696, 3300, 102]]
token_type_ids : [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
special_tokens_mask : [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]
length : [27, 30]
attention_mask : [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
"""

# 解码
tokenizer.decode(out['input_ids'][0])
"""
'[CLS] 选 择 珠 江 花 园 的 原 因 就 是 方 便 。 [SEP] 笔 记 本 的 键 盘 确 实 爽 。 [SEP] [PAD] [PAD] [PAD]'
"""

tokenizer.decode(out['input_ids'][1])
"""
'[CLS] 房 间 太 小 。 其 他 的 都 一 般 。 [SEP] 今 天 才 知 道 这 书 还 有 第 6 卷, 真 有 [SEP]'
"""

2.4 字典操作

# 获取字典
zidian = tokenizer.get_vocab()  # {token: id, ...}
type(zidian), len(zidian), '月光' in zidian  # (dict, 21128, False)
'月' in zidian, '光' in zidian  # (True, True) 
# 月和光都在字典中, 但月光不在字典中
# 添加新词
tokenizer.add_tokens(new_tokens=['月光', '希望'])

# 添加新符号
tokenizer.add_special_tokens({'eos_token': '[EOS]'})

zidian = tokenizer.get_vocab()
type(zidian), len(zidian), zidian['月光'], zidian['[EOS]']  # (dict, 21131, 21128, 21130)

# 编码新添加的词
out = tokenizer.encode(
    text='月光的新希望[EOS]',
    text_pair=None,
    truncation=True,  # 当 句子长度大于max_length时,截断
    padding='max_length',  # 不足最大长度时,一律补pad到max_length长度
    add_special_tokens=True,
    max_length=8,  # 最大长度
    return_tensors=None,
)

print(out)  # [101, 21128, 4638, 3173, 21129, 21130, 102, 0]

tokenizer.decode(out)  # '[CLS] 月光 的 新 希望 [EOS] [SEP] [PAD]'

3 transformers 模型使用

使用transformers库主要分为以下几个步骤:

  1. 安装transformers库

可以使用pip命令安装transformers库:

pip install transformers
  1. 加载预训练模型

使用transformers库需要先加载相应的预训练模型。可以从Hugging Face网站上下载相应的模型,也可以通过API从transformers库中直接下载预训练模型。

加载预训练模型的代码示例:

from transformers import BertModel, BertTokenizer

model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertModel.from_pretrained(model_name)

以上代码使用了BERT模型进行加载,加载过程中同样进行了对应的tokenization操作。

  1. 使用预训练模型进行NLP任务

加载预训练模型后,即可使用其进行NLP任务,例如文本分类、命名实体识别、序列标注、文本生成等。

以BERT模型进行文本分类为例的代码示例:

from transformers import BertForSequenceClassification, BertTokenizer

model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name)

inputs = tokenizer("This is a sentence", return_tensors="pt")
outputs = model(**inputs)

logits = outputs.logits

以上代码使用了已经加载好的BERT模型进行文本分类,输入了一个句子,并返回了该句子的分类概率(logits)。

  1. fine-tune预训练模型

使用预训练模型进行迁移学习,可以针对任务领域的特定数据进行fine-tune。fine-tune是指在预训练模型的基础上,针对特定数据集进行进一步的训练,以适应特定的数据和任务。

以下是使用BERT进行情感分类fine-tune的代码示例:

from transformers import BertForSequenceClassification, BertTokenizer, AdamW
import torch

# 1. 加载数据集,假设训练集和验证集已经准备好了
train_data = ...
valid_data = ...

# 2. 加载预训练模型和tokenizer
model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name)

# 3. 使用GPU进行模型训练
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model.to(device)

# 4. 设置训练参数
epochs = 5
batch_size = 16
optimizer = AdamW(model.parameters(), lr=2e-5)

# 5. 开始训练
for epoch in range(epochs):
    # 6. 训练集batch循环
    for i in range(0, len(train_data), batch_size):
        # 6.1 批处理  训练数据
        batch = train_data[i:i+batch_size]
        # 6.2 分词器编码
        inputs = tokenizer.batch_encode_plus(batch, return_tensors="pt", padding=True, truncation=True)
        inputs.to(device)
        # 6.3 张量处理 labels
        labels = torch.tensor([d['label'] for d in batch]).to(device)
        # 6.4 训练
        outputs = model(input_ids=inputs['input_ids'], token_type_ids=inputs['token_type_ids'], attention_mask=inputs['attention_mask'], labels=labels)
        # 6.5 损失函数
        loss = outputs.loss
        # 6.6 反向传播 计算梯度
        loss.backward()
        # 6.7 参数更新
        optimizer.step()
        # 6.8 梯度置为0
        optimizer.zero_grad()

    # 7. 验证集batch循环
    with torch.no_grad():  # 模型评估不需要计算梯度
        for i in range(0, len(valid_data), batch_size):
            # 7.1 验证集批处理
            batch = valid_data[i:i+batch_size]
            # 7.2 验证集分词器编码
            inputs = tokenizer.batch_encode_plus(batch, return_tensors="pt", padding=True, truncation=True)
            inputs.to(device)
            # 7.3 验证集 labels 转 张量
            labels = torch.tensor([d['label'] for d in batch]).to(device)
            # 7.4 预测
            outputs = model(input_ids=inputs['input_ids'], token_type_ids=inputs['token_type_ids'], attention_mask=inputs['attention_mask'], labels=labels)
            # 7.5 计算损失
            loss = outputs.loss

    # 8. 打印一些训练过程中的指标
    print(f"Epoch {epoch} training loss: {loss.item()}")

以上代码中,针对情感分类任务的数据集,使用fine-tune技术对BERT模型进行了训练,并输出了训练过程中的损失。

以上代码加载了BERT模型和tokenizer,并使用fine-tune技术在情感分类数据集上进行了训练。

4 数据集操作 datasets

4.1 load_dataset

from datasets import load_dataset

# 加载数据
# 注意:如果你的网络不允许你执行这段的代码,则直接运行【从磁盘加载数据】即可
# 转载自seamew/ChnSentiCorp, 或者传入本地数据数据集文件路径
dataset = load_dataset(path='lansinuote/ChnSentiCorp')  # 从网络上下载数据集

dataset
"""数据集结构
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 9600
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 1200
    })
    validation: Dataset({
        features: ['text', 'label'],
        num_rows: 1200
    })
})
"""

4.2 save_to_disk

# save_to_disk 保存数据集到磁盘
#注意:运行这段代码要确保【加载数据】运行是正常的,否则直接运行【从磁盘加载数据】即可
dataset.save_to_disk(dataset_dict_path='./data/ChnSentiCorp')

4.3 load_from_disk

从磁盘加载数据

# 从磁盘加载数据
from datasets import load_from_disk

dataset = load_from_disk('./data/ChnSentiCorp')

dataset

4.4 获取 训练集 测试集 验证集

# 获取 训练集 测试集 验证集
# 获取训练集
dataset_train = dataset['train']
# 获取测试集
dataset_test = dataset['test']
# 获取验证集
dataset_val = dataset['validation']

# 查看一条数据
dataset_train[0]
"""
{'text': '选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般', 'label': 1}
"""

4.5 sort

排序

# 4. 查看一条数据
dataset_train[0]
"""
{'text': '选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般', 'label': 1}
"""

# 未排序的label是乱序的
print(dataset_train['label'][:10])  # [1, 1, 0, 0, 1, 0, 0, 0, 1, 1]

# 排序之后label有序了
sorted_dataset = dataset_train.sort('label')
print(sorted_dataset['label'][:10])  # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
print(sorted_dataset['label'][-10:])  # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

4.6 shuffle

打乱顺序

#打乱顺序
shuffled_dataset = sorted_dataset.shuffle(seed=42)
shuffled_dataset['label'][:10]  # [0, 1, 0, 0, 1, 0, 1, 0, 1, 0]

4.7 select

选择

dataset_train.select([0, 10, 20, 30, 40, 50])

"""
Dataset({
    features: ['text', 'label'],
    num_rows: 6
})
"""

4.8 filter

过滤

def f(data):
    return data['text'].startswith('选择')


start_with_ar = dataset_train.filter(f)

4.9 train_test_split

切割训练集和训练集

dataset_train.train_test_split(test_size=0.1)  # 0.1 表示测试集占01
"""
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 8640
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 960
    })
})
"""

4.10 shard

将数据集均匀切分

# 切分前
"""dataset_train
Dataset({
     features: ['text', 'label'],
     num_rows: 9600
 })
"""
# 把数据切分到4个桶中,均匀分配
dataset_train.shard(num_shards=4, index=0)
"""
Dataset({
     features: ['text', 'label'],
     num_rows: 2400
 })
"""

4.11 rename_column

重名列名

dataset_train.rename_column('text', 'textA')
"""
Dataset({
    features: ['textA', 'label'],
    num_rows: 9600
})
"""

4.12 remove_columns

移除列

dataset_train.remove_columns(['text'])
"""
Dataset({
    features: ['label'],
    num_rows: 9600
})
"""

4.13 map

值映射:逐次修改每个元素值

def f(data):
    data['text'] = 'My sentence: ' + data['text']
    return data


dataset_train_map = dataset_train.map(f)

dataset_train_map['text'][:5]

4.14 set_format

设置格式

dataset.set_format(type='torch', columns=['label'])

dataset[0]
"""
{'label': tensor(1)}
"""

4.15 加载或导出csv文件

# 从网络上加载数据
dataset = load_dataset(path='lansinuote/ChnSentiCorp', split='train')
# 保存csv文件
dataset.to_csv(path_or_buf='./data/ChnSentiCorp.csv')

# 加载csv格式数据
csv_dataset = load_dataset(path='csv',
                           data_files='./data/ChnSentiCorp.csv',
                           split='train')
csv_dataset[20]
"""
{'text': '非常不错,服务很好,位于市中心区,交通方便,不过价格也高!', 'label': 1}
"""

4.16 加载或导出json文件

# 从网络上加载数据
dataset = load_dataset(path='lansinuote/ChnSentiCorp', split='train')
# 保存csv文件
dataset.to_json(path_or_buf='./data/ChnSentiCorp.json')

#加载json格式数据
json_dataset = load_dataset(path='json',
                            data_files='./data/ChnSentiCorp.json',
                            split='train')
json_dataset[20]
"""
{'text': '非常不错,服务很好,位于市中心区,交通方便,不过价格也高!', 'label': 1}
"""

4.17 评价指标 evaluate库

地址:https://huggingface.co/docs/evaluate

有哪些评价指标呢?

from datasets import list_metrics

# 列出评价指标
metrics_list = list_metrics()

print(len(metrics_list))  # 121
print(metrics_list)

如何使用评价指标,上面列出的121种评价指标,选择其中一个合适的指标即可,由于load_metric已弃用,并将在数据集的下一个datasets主要版本中删除, 因此需要安装新库评价evaluate库:pip install evaluate

# from datasets import load_metric
# 未来警告:load_metric已弃用,并将在数据集的下一个主要版本中删除。改用“evaluate.load”,从新库中🤗评估:https://huggingface.co/docs/evaluate 从系统路径中删除 CWD 后

import evaluate
# 加载一个评价指标
glue_metric  = evaluate.load('glue', 'mrpc')

# 查看使用详情
print(glue_metric.inputs_description)

当前选择的评价指标使用详情:

Compute GLUE evaluation metric associated to each GLUE dataset.
Args:
    predictions: list of predictions to score.
        Each translation should be tokenized into a list of tokens.
    references: list of lists of references for each translation.
        Each reference should be tokenized into a list of tokens.
Returns: depending on the GLUE subset, one or several of:
    "accuracy": Accuracy
    "f1": F1 score
    "pearson": Pearson Correlation
    "spearmanr": Spearman Correlation
    "matthews_correlation": Matthew Correlation
Examples:

    >>> glue_metric = evaluate.load('glue', 'sst2')  # 'sst2' or any of ["mnli", "mnli_mismatched", "mnli_matched", "qnli", "rte", "wnli", "hans"]
    >>> references = [0, 1]
    >>> predictions = [0, 1]
    >>> results = glue_metric.compute(predictions=predictions, references=references)
    >>> print(results)
    {'accuracy': 1.0}

    >>> glue_metric = evaluate.load('glue', 'mrpc')  # 'mrpc' or 'qqp'
    >>> references = [0, 1]
    >>> predictions = [0, 1]
    >>> results = glue_metric.compute(predictions=predictions, references=references)
    >>> print(results)
    {'accuracy': 1.0, 'f1': 1.0}

    >>> glue_metric = evaluate.load('glue', 'stsb')
    >>> references = [0., 1., 2., 3., 4., 5.]
    >>> predictions = [0., 1., 2., 3., 4., 5.]
    >>> results = glue_metric.compute(predictions=predictions, references=references)
    >>> print({"pearson": round(results["pearson"], 2), "spearmanr": round(results["spearmanr"], 2)})
    {'pearson': 1.0, 'spearmanr': 1.0}

    >>> glue_metric = evaluate.load('glue', 'cola')
    >>> references = [0, 1]
    >>> predictions = [0, 1]
    >>> results = glue_metric.compute(predictions=predictions, references=references)
    >>> print(results)
    {'matthews_correlation': 1.0}

计算一个评价指标:

import evaluate

# 加载一个评价指标
glue_metric  = evaluate.load('glue', 'mrpc')
# 评价数据
predictions = [0, 1, 0]
references = [0, 1, 1]
# 平分
final_score = glue_metric.compute(predictions=predictions, references=references)

final_score
"""
{'accuracy': 0.6666666666666666, 'f1': 0.6666666666666666}
"""

5 管道方法 transformers.pipeline

transformers.pipeline是一种方便的API,允许用户快速使用transformers库中的预训练模型处理文本或图像。

使用transformers.pipeline,用户可以仅仅使用几行代码就可以完成一些任务,例如文本分类命名实体识别问答等。

5.1 文本分类

以下是使用pipeline处理文本分类的示例:

from transformers import pipeline
# 没有提供模型,默认为distilbert-base-uncased-finetuned-sst-2-english
# 不建议在生产中使用未指定模型名称和修订的管道
# classifier = pipeline('text-classification')
classifier = pipeline('text-classification', model='distilbert-base-uncased')
result = classifier('This is a positive sentence.')
print(result)
"""
[{'label': 'POSITIVE', 'score': 0.9994040727615356}]
"""

5.2 图像分类

使用pipeline处理图像分类的示例:

from PIL import Image
from transformers import pipeline

image_classifier = pipeline('image-classification', model='google/vit-base-patch16-224')
image = Image.open('image.jpg')
result = image_classifier(image)
print(result)

"""
[{'score': 0.13602155447006226, 'label': 'corn'}, {'score': 0.13296112418174744, 'label': 'plate'}, {'score': 0.10631590336561203, 'label': 'potpie'}, {'score': 0.0996851921081543, 'label': 'frying pan, frypan, skillet'}, {'score': 0.07845009863376617, 'label': 'spatula'}]
"""

5.3 阅读理解

使用pipeline处理阅读理解的示例:

from transformers import pipeline

# 阅读理解
question_answerer = pipeline("question-answering")

context = r"""
Extractive Question Answering is the task of extracting an answer from a text given a question. An example of a 
question answering dataset is the SQuAD dataset, which is entirely based on that task. If you would like to fine-tune 
a model on a SQuAD task, you may leverage the examples/pytorch/question-answering/run_squad.py script.
"""

result = question_answerer(question="What is extractive question answering?",
                           context=context)
print(result)

result = question_answerer(
    question="What is a good example of a question answering dataset?",
    context=context)

print(result)

"""
{'score': 0.6177274584770203, 'start': 34, 'end': 95, 'answer': 'the task of extracting an answer from a text given a question'}
{'score': 0.5152304172515869, 'start': 148, 'end': 161, 'answer': 'SQuAD dataset'}
"""

5.4 完形填空

使用pipeline处理完形填空的示例:

from transformers import pipeline

# 完形填空
unmasker = pipeline("fill-mask")

from pprint import pprint

sentence = 'HuggingFace is creating a <mask> that the community uses to solve NLP tasks.'

unmasker(sentence)

"""
[{'score': 0.1792750507593155,
  'token': 3944,
  'token_str': ' tool',
  'sequence': 'HuggingFace is creating a tool that the community uses to solve NLP tasks.'},
 {'score': 0.11349354684352875,
  'token': 7208,
  'token_str': ' framework',
  'sequence': 'HuggingFace is creating a framework that the community uses to solve NLP tasks.'},
 {'score': 0.05243568494915962,
  'token': 5560,
  'token_str': ' library',
  'sequence': 'HuggingFace is creating a library that the community uses to solve NLP tasks.'},
 {'score': 0.03493542596697807,
  'token': 8503,
  'token_str': ' database',
  'sequence': 'HuggingFace is creating a database that the community uses to solve NLP tasks.'},
 {'score': 0.02860235795378685,
  'token': 17715,
  'token_str': ' prototype',
  'sequence': 'HuggingFace is creating a prototype that the community uses to solve NLP tasks.'}]
"""

5.5 文本生成

使用pipeline处理文本生成的示例:

from transformers import pipeline

# 文本生成
text_generator = pipeline("text-generation")

text_generator("As far as I am concerned, I will",
               max_length=50,
               do_sample=False)

"""
[{'generated_text': 'As far as I am concerned, I will be the first to admit that I am not a fan of the idea of a "free market." I think that the idea of a free market is a bit of a stretch. I think that the idea'}]
"""

5.6 文本总结

使用pipeline处理文本总结的示例:

from transformers import pipeline

# 文本总结
summarizer = pipeline("summarization")

ARTICLE = """ New York (CNN)When Liana Barrientos was 23 years old, she got married in Westchester County, New York.
A year later, she got married again in Westchester County, but to a different man and without divorcing her first husband.
Only 18 days after that marriage, she got hitched yet again. Then, Barrientos declared "I do" five more times, sometimes only within two weeks of each other.
In 2010, she married once more, this time in the Bronx. In an application for a marriage license, she stated it was her "first and only" marriage.
Barrientos, now 39, is facing two criminal counts of "offering a false instrument for filing in the first degree," referring to her false statements on the
2010 marriage license application, according to court documents.
Prosecutors said the marriages were part of an immigration scam.
On Friday, she pleaded not guilty at State Supreme Court in the Bronx, according to her attorney, Christopher Wright, who declined to comment further.
After leaving court, Barrientos was arrested and charged with theft of service and criminal trespass for allegedly sneaking into the New York subway through an emergency exit, said Detective
Annette Markowski, a police spokeswoman. In total, Barrientos has been married 10 times, with nine of her marriages occurring between 1999 and 2002.
All occurred either in Westchester County, Long Island, New Jersey or the Bronx. She is believed to still be married to four men, and at one time, she was married to eight men at once, prosecutors say.
Prosecutors said the immigration scam involved some of her husbands, who filed for permanent residence status shortly after the marriages.
Any divorces happened only after such filings were approved. It was unclear whether any of the men will be prosecuted.
The case was referred to the Bronx District Attorney\'s Office by Immigration and Customs Enforcement and the Department of Homeland Security\'s
Investigation Division. Seven of the men are from so-called "red-flagged" countries, including Egypt, Turkey, Georgia, Pakistan and Mali.
Her eighth husband, Rashid Rajput, was deported in 2006 to his native Pakistan after an investigation by the Joint Terrorism Task Force.
If convicted, Barrientos faces up to four years in prison.  Her next court appearance is scheduled for May 18.
"""

summarizer(ARTICLE, max_length=130, min_length=30, do_sample=False)

"""
[{'summary_text': ' Liana Barrientos, 39, is charged with two counts of "offering a false instrument for filing in the first degree" In total, she has been married 10 times, with nine of her marriages occurring between 1999 and 2002 . At one time, she was married to eight men at once, prosecutors say .'}]
"""

5.7 命名实体识别

使用pipeline处理命名实体识别的示例:

from transformers import pipeline

# 命名实体识别
ner_pipe = pipeline("ner")

sequence = """Hugging Face Inc. is a company based in New York City. Its headquarters are in DUMBO,
therefore very close to the Manhattan Bridge which is visible from the window."""

for entity in ner_pipe(sequence):
    print(entity)
    
"""
{'entity': 'I-ORG', 'score': 0.99957865, 'index': 1, 'word': 'Hu', 'start': 0, 'end': 2}
{'entity': 'I-ORG', 'score': 0.9909764, 'index': 2, 'word': '##gging', 'start': 2, 'end': 7}
{'entity': 'I-ORG', 'score': 0.9982224, 'index': 3, 'word': 'Face', 'start': 8, 'end': 12}
{'entity': 'I-ORG', 'score': 0.9994879, 'index': 4, 'word': 'Inc', 'start': 13, 'end': 16}
{'entity': 'I-LOC', 'score': 0.9994344, 'index': 11, 'word': 'New', 'start': 40, 'end': 43}
{'entity': 'I-LOC', 'score': 0.99931955, 'index': 12, 'word': 'York', 'start': 44, 'end': 48}
{'entity': 'I-LOC', 'score': 0.9993794, 'index': 13, 'word': 'City', 'start': 49, 'end': 53}
{'entity': 'I-LOC', 'score': 0.98625815, 'index': 19, 'word': 'D', 'start': 79, 'end': 80}
{'entity': 'I-LOC', 'score': 0.95142674, 'index': 20, 'word': '##UM', 'start': 80, 'end': 82}
{'entity': 'I-LOC', 'score': 0.933659, 'index': 21, 'word': '##BO', 'start': 82, 'end': 84}
{'entity': 'I-LOC', 'score': 0.9761654, 'index': 28, 'word': 'Manhattan', 'start': 114, 'end': 123}
{'entity': 'I-LOC', 'score': 0.9914629, 'index': 29, 'word': 'Bridge', 'start': 124, 'end': 130}
"""

5.8 翻译

使用pipeline处理翻译的示例:

from transformers import pipeline

# 翻译 LC748NLP/SikuGPT2-translation
# translator = pipeline("translation_en_to_de")
translator = pipeline("translation_en_to_de")

sentence = "Hugging Face is a technology company based in New York and Paris"

translator(sentence, max_length=40)

"""
[{'translation_text': 'Hugging Face ist ein Technologieunternehmen mit Sitz in New York und Paris.'}]
"""

使用pipeline还可以处理多种其他任务,例如文本生成、序列分类、文本翻译等。

在使用pipeline时,用户可以指定要使用的预训练模型和任务类型。transformers库中可以使用的预训练模型和任务类型非常多,用户可以根据自己的需要进行选择。

6 预训练BERT模型

预训练BERT模型的主要步骤如下:

  1. 在大规模的无标注的文本数据上对BERT模型进行预训练。
  2. 使用预训练的BERT模型进行Fine-tuning,用于特定任务的处理。

在Python中,使用transformers库可以轻松实现预训练和Fine-tuning。其中,预训练BERT模型的主要代码如下:

from transformers import BertTokenizer, BertForPreTraining, LineByLineTextDataset, DataCollatorForLanguageModeling, Trainer, TrainingArguments

# 加载tokenizer和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForPreTraining.from_pretrained('bert-base-uncased')

# 加载数据集并进行训练
dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path='path/to/text/file',
    block_size=128
)

data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=1,
    per_device_train_batch_size=32,
    save_steps=10_000,
    save_total_limit=2,
    prediction_loss_only=True,
)

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=dataset,
    prediction_loss_only=True,
)

trainer.train()

其中,LineByLineTextDataset负责读取无标注的文本数据,并将其分解成小块,以便于内存使用。DataCollatorForLanguageModeling则负责将数据转换为可训练的形式。Trainer是训练的核心类,用于执行训练过程。在训练完成后,可以使用save_pretrained方法将模型保存。

7 Fine-tuningBERT模型

Fine-tuningBERT模型的主要步骤如下:

  1. 下载预训练好的BERT模型。
  2. 加载有标注数据集并进行预处理,将数据集转换为BERT模型的输入格式。
  3. 使用Fine-tuning技术,微调BERT模型,得到特定任务的模型。

在Python中,使用transformers库可以方便的进行Fine-tuning。示例代码如下:

from transformers import BertForSequenceClassification, BertTokenizer, Trainer, TrainingArguments
import datasets

# 加载tokenizer和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)

# 加载数据集
train_dataset, test_dataset = datasets.load_dataset('csv', data_files={'train': 'path/to/train.csv', 'test': 'path/to/test.csv'})

# tokenization和padding
def tokenize(batch):
    return tokenizer(batch['text'], padding=True, truncation=True)

train_dataset = train_dataset.map(tokenize, batched=True, batch_size=len(train_dataset))
test_dataset = test_dataset.map(tokenize, batched=True, batch_size=len(test_dataset))

# Fine-tuning
training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=1,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    warmup_steps=500,  # 初始warmup的步数,用于逐步增加学习率
    evaluation_strategy='epoch',
    logging_dir='./logs',
    load_best_model_at_end=True,
    metric_for_best_model='accuracy',
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=test_dataset,
)

trainer.train()

其中,load_dataset用于加载训练和测试数据集,tokenize函数进行分词、padding等预处理,TrainingArguments设置训练参数。Trainer类执行训练过程,并提供了评估、保存模型等功能。

综上所述,Python库BERT可以方便的进行预训练和Fine-tuning。在预训练过程中,可以使用Trainer执行训练,并使用save_pretrained方法保存模型。在Fine-tuning过程中,可以使用Trainer执行Fine-tuning过程,并使用predict方法对测试数据进行预测。

posted @   三叶草body  阅读(947)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示