mindNLP

API reference

另一个API reference,这个全

介绍这是什么

  • MindNLP是基于MindSpore的开源NLP库。这个是个package,类似于huggingface的transformer库,有 250+ 预训练模型支持类似 huggingface transformers 的 API。您可以通过以下代码片段轻松使用,这样可以直接使用已经定义好的模型并且还有模型的权重:支持的模型

    1
    2
    3
    import mindnlp.transformers import AutoModel

    model = AutoModel.from_pretrained('bert-base-cased')
  • 全面的数据处理:将多个经典的 NLP 数据集打包成友好的模块,以便于使用,例如 Multi30k、SQuAD、CoNLL 等。

  • 友好的 NLP 模型工具集:MindNLP 提供了各种可配置的组件。使用 MindNLP 自定义模型很友好。

  • 易于使用的引擎:MindNLP 简化了 MindSpore 中复杂的训练过程。它支持 Trainer 和 Evaluator 接口,可轻松训练和评估模型。

  • 全平台支持:全面支持昇腾 910 系列、昇腾 310B (Orange Pi)、GPU 和 CPU。(注意:目前 Orange Pi 上唯一可用的 AI 开发套件。

  • 分布式并行推理:对超过 10B 参数的模型提供多设备、多进程并行推理支持。

  • 量化算法支持:SmoothQuant 可用于 Orange Pi;GPU 支持类似 bitsandbytes 的 int8 量化。

  • Sentence Transformer 支持:实现高效的 RAG(检索增强生成)开发。

  • 动态图性能优化:在 Ascend 硬件上实现动态图的 PyTorch+GPU 级推理速度(在 85 毫秒/令牌下测试 Llama 性能)。

  • 真正的静态和动态图统一:使用 mindspore.jit 单行切换到图形模式,完全兼容 Hugging Face 代码风格,既易于使用,又能快速提升性能。在Ascend硬件上测试的Llama性能达到了2倍的动态图速度(45ms/token),与其他MindSpore基于静态图的套件一致。

  • 广泛的 LLM 应用程序更新:包括文本信息提取、聊天机器人、语音识别、ChatPDF、音乐生成、代码生成、语音克隆等。随着模型支持的增加,更多令人兴奋的应用程序等待开发!

安装

1
2
3
4
5
6
7
8
9
10
11
# whl的下载位置: https://repo.mindspore.cn/mindspore-lab/mindnlp/newest/any/
# 直接pip就可以安装
pip install mindnlp
# source安装
pip install git+https://github.com/mindspore-lab/mindnlp.git
# or
git clone https://github.com/mindspore-lab/mindnlp.git
cd mindnlp
bash scripts/build_and_reinstall.sh

# 版本要求:python >=3.9, <=3.11 mingspore >=2.2.x

补充知识

分词器,词表,预训练的词向量模型

1
2
3
4
5
6
7
# 使用分词器分词
tokens = tokenizer.tokenize("Hello, how are you?")
print(tokens) # 输出: ['hello', ',', 'how', 'are', 'you', '?']

# 使用词表映射到索引
indices = vocab(tokens)
print(indices) # 输出: [2, 3, 4, 5, 6, 7]

分词器tokenizer

分词器的主要任务是将文本分割成基本单元(如单词、子词或字符),并将这些单元转换为模型可以理解的数值形式(如索引)。常见的分词器包括:

  • 空格分词器:按空格分割文本。
  • BPE(Byte Pair Encoding)分词器:将文本分割成子词单元。
  • WordPiece 分词器:BERT 等模型使用的分词器。
  • SentencePiece 分词器:支持无空格语言的分词器。

分词器的处理流程:

  1. 分词:将文本分割成基本单元(如单词或子词)。
  2. 转换为索引:将分词结果映射到词表中的索引。
  3. 添加特殊标记:如 [CLS][SEP][PAD] 等。
  4. 填充或截断:将序列长度统一为固定长度。

词表(Vocab)的作用

词表是词汇到索引的映射表。它的作用是将分词后的文本转换为数值形式,以便模型能够处理。词表通常包括:

  • 词汇表:所有可能的词汇或子词。
  • 特殊标记:如 <unk>(未知词)、<pad>(填充标记)等。
  • 索引映射:将每个词汇映射到一个唯一的索引。

glove预训练词向量

GloVe(Global Vectors for Word Representation) 是一种预训练的词向量模型,它通过全局词共现统计来学习词汇的分布式表示。主要作用是为词汇提供语义丰富的向量表示,从而增强模型对文本的理解能力。是一种嵌入层embedding。

(1)语义表示GloVe 词向量捕捉了词汇之间的语义关系。例如,king - man + woman ≈ queen,这种关系在向量空间中可以通过向量加减来表示。

(2)降维将高维的离散词汇表示(如 one-hot 编码)转换为低维的连续向量表示。例如,一个词汇可以用一个 100 维的向量表示,而不是一个数万维的 one-hot 向量。

(3)初始化嵌入层在训练 NLP 模型时,GloVe 词向量可以作为嵌入层(Embedding Layer)的初始化参数,从而加速模型收敛并提升性能。

(4)解决稀疏性问题传统的 one-hot 编码会导致数据稀疏性问题,而 GloVe 词向量是稠密的,能够更好地表示词汇。

1
2
3
4
5
6
7
8
9
10
11
from torchtext.vocab import GloVe

# 加载预训练的 GloVe 词向量(6B 表示训练语料为 60 亿词,100 表示向量维度)
glove = GloVe(name='6B', dim=100)

# 查看词汇表大小
print(len(glove.itos)) # 输出: 400000(GloVe 6B 包含 40 万个词汇)

# 获取单词 "king" 的词向量
king_vector = glove['king']
print(king_vector.shape) # 输出: torch.Size([100])

词表和glove预训练词向量不是一个东西

虽然 GloVe 预训练词向量词表(Vocab) 都涉及将词汇转换为向量或索引。

大多数深度学习模型(如 LSTM、Transformer)的输入是索引序列,而不是直接输入向量序列。词表将文本转换为索引序列,而 GloVe 词向量用于将索引映射为向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 将文本转换为索引
text = "hello NLP"
indices = vocab(text.split())
print(indices) # 输出: [2, 4]
# 加载 GloVe 词向量
glove = GloVe(name='6B', dim=100)

# 创建嵌入层,并使用 GloVe 词向量初始化
embedding_layer = nn.Embedding.from_pretrained(glove.vectors, freeze=False)

# 将索引转换为 GloVe 向量
input_indices = torch.LongTensor(indices)
embedded_vectors = embedding_layer(input_indices)
print(embedded_vectors.shape) # 输出: torch.Size([2, 100])

学习使用

官方参考文件

另一个官方参考文件

使用模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import math
from mindspore import nn
from mindspore import ops
from mindspore.common.initializer import Uniform, HeUniform
from mindnlp.modules import Glove
from mindnlp.modules import RNNEncoder
# 使用 Seq2vecModel 进行模型构建。模块 Seq2vecModel 的功能是提取输入序列数据的语义特征并计算得到结果向量。这一模块由 encoder 和 head 两部分组成,其中 encoder 将输入句子映射为语义向量,而 head 对 encoder 的输出进行进一步计算得到最终的结果。
# 使用MindNLP提供的 RNNEncoder 作为模型的 encoder ,并使用自定义的模块作为模型的 head 。

# 定义一个head
class Head(nn.Cell):
"""
Head for Sentiment Classification model
"""
def __init__(self, hidden_dim, output_dim, dropout):
super().__init__()
weight_init = HeUniform(math.sqrt(5))
bias_init = Uniform(1 / math.sqrt(hidden_dim * 2))
self.fc = nn.Dense(hidden_dim * 2, output_dim, weight_init=weight_init, bias_init=bias_init)
self.sigmoid = nn.Sigmoid()
self.dropout = nn.Dropout(1 - dropout)
def construct(self, context):
context = ops.concat((context[-2, :, :], context[-1, :, :]), axis=1)
context = self.dropout(context)
return self.sigmoid(self.fc(context))
# 使用encoder
hidden_size = 256
output_size = 1
num_layers = 2
bidirectional = True
drop = 0.5
lr = 0.001
embedding, vocab = Glove.from_pretrained('6B', 100, special_tokens=["<unk>", "<pad>"], dropout=drop)
lstm_layer = nn.LSTM(100, hidden_size, num_layers=num_layers, batch_first=True,
dropout=drop, bidirectional=bidirectional)
sentiment_encoder = RNNEncoder(embedding, lstm_layer)

sentiment_head = Head(hidden_size, output_size, drop)
# 把这两个传进去生成model
net = SentimentClassification(sentiment_encoder, sentiment_head)

使用数据集

通过对应接口加载

1
2
3
4
5
6
7
8
9
10
11
12
13
from mindnlp.dataset import Multi30k
multi30k_train, multi30k_valid, multi30k_test = Multi30k("./dataset")
multi30k_train = Multi30k(root="./dataset", split='train')

通过注释或者网站对应的接口 文档 查看参数列表以及返回值等信息:
# 参数:
# root (str) - 存放数据集的目录。默认:”~/.mindnlp”。
# split (str|Tuple[str]) - 要返回的数据集分块。默认:(‘train’, ‘valid’, ‘test’).
# language_pair (Tuple[str]) - 包含源语言和目标语言的元组。默认:(‘de’, ‘en’).
# proxies (dict) - 定义代理的字典,例如:{“https”: “https://127.0.0.1:7890”}.
# 返回:
# datasets_list (list) - 加载完成的数据集分块列表。如果只加载了一个数据集分块, 如:’trian’,那么就只返回这个数据集分块,而不是一个列表。

通过对应接口load预处理

可以在 mindnlp.dataset 下找到对应数据集接口,名称为数据集名称加下划线以及 Process ,其中的 vocab 为如上生成的词表:

1
2
3
4
5
6
7
8
9
10
11
from mindnlp.dataset import Multi30k_Process
train_dataset = Multi30k_Process(train_dataset, vocab=vocab)
# 通过注释或者网站对应的接口 [文档](https://mindnlp.cqu.ai/zh_CN/latest/api/dataset/machine_translation.html#module-mindnlp.dataset.machine_translation.multi30k) 查看参数列表以及返回值等信息:
# 参数:
# - **dataset** (*GeneratorDataset*) - Multi30k数据集。
# - **vocab** (*Vocab*) - 词表对象,用于存储分词和索引的映射。默认为空。如果为空,一个新的词表对象将会被创建。
# - **batch_size** (*int*) - 指定每个批处理数据包含的数据条目。默认值:64。
# - **max_len** (*int*) - 句子的最大长度。默认值:500。
# - **drop_remainder** (*bool*) - 当最后一批数据包含的数据条目小于batch_size时,是否丢弃该批次,而不将其传递到下一个操作。默认值:False,不丢弃
# 返回:
# - **dataset** (MapDataset) - 预处理操作后返回的数据集。
1
2
3
4
5
6
7
8
9
10
11
12
# 处理和划分数据,针对语音数据集还需要分词器和词汇表
# 调用来自 Glove 的函数 from_pretrained() ,获取嵌入和词汇表
from mindnlp.modules import Glove
embedding, vocab = Glove.from_pretrained('6B', 100, special_tokens=["<unk>", "<pad>"], dropout=drop)
# 实例化类 BasicTokenizer 来初始化分词器:
from mindnlp.dataset.transforms import BasicTokenizer
tokenizer = BasicTokenizer(True)
将获取到的训练集、分词器和词汇表等传入方法 process() ,应用该方法获取处理过的训练集:
from mindnlp.dataset import process
imdb_train = process('imdb', imdb_train, tokenizer=tokenizer, vocab=vocab, bucket_boundaries=[400, 500], max_len=600, drop_remainder=True)
# 使用方法 split() 来划分处理后的训练集,从而获取新的训练集和验证集
imdb_train, imdb_valid = imdb_train.split([0.7, 0.3])

通过统一接口加载

通过一个统一的 load 接口进行加载,第一个参数为数据集的名称字符串,用以指定数据集:

1
2
3
from mindnlp.dataset import load
multi30k_train, multi30k_valid, multi30k_test = load('multi30k')
multi30k_train, multi30k_valid, multi30k_test = load('multi30k', root="./dataset") # 其他参数可以通过查询接口继续添加

通过统一接口process预处理

对于不同领域中的不同数据集,有不同的处理流程,mindnlp提供了数据集的特定处理函数帮助我们快速处理数据。和前面的加载方法一样,这里同样有两种方法调用该处理函数。以 Multi30k 数据集为例:

1
2
3
from mindnlp.dataset import process
multi30k_train, multi30k_valid, multi30k_test = load('multi30k')
dataset_train = process('Multi30k', multi30k_train, vocab = vocab)

数据集变换处理

数据集变换处理中最重要的操作是 map 操作,可以针对数据集指定列(column)添加数据变换(Transforms),将数据变换应用于该列数据的每个元素,并返回包含变换后元素的新数据集。这里使用 BasicTokenizer 对数据集的两列进行分词,并使用 from_dataset 生成词表:

1
2
3
4
5
6
7
8
9
from mindnlp.dataset.transforms import BasicTokenizer

tokenizer = BasicTokenizer(True)
dataset_train= dataset_train.map([tokenizer], 'en')
dataset_train= dataset_train.map([tokenizer], 'de')

en_vocab = text.Vocab.from_dataset(dataset_train, 'en', special_tokens=['<pad>', '<unk>'], special_first= True)
de_vocab = text.Vocab.from_dataset(dataset_train, 'de', special_tokens=['<pad>', '<unk>'], special_first= True)
vocab = {'en':en_vocab, 'de':de_vocab}

用Trainer进行训练

定义loss和优化器

1
2
3
4
# 使用MindSpore提供的 `mindspore.nn.BCELoss` 来定义一个损失函数:
loss = nn.BCELoss(reduction='mean')
# 然后,调用 `mindspore.nn.Adam` ,并传入模型的可训练参数,从而定义运行模型所需要的优化器:
optimizer = nn.Adam(net.trainable_params(), learning_rate=lr)

定义回调函数

1
2
3
4
5
6
7
8
9
from mindnlp.engine.callbacks.timer_callback import TimerCallback
from mindnlp.engine.callbacks.earlystop_callback import EarlyStopCallback
from mindnlp.engine.callbacks.best_model_callback import BestModelCallback
# 首先需要初始化回调函数对应的类
timer_callback_epochs = TimerCallback(print_steps=2) # 用于计时的回调函数
earlystop_callback = EarlyStopCallback(patience=2) # 用于早停的回调函数
bestmodel_callback = BestModelCallback(save_path='save/callback/best_model', auto_load=True) # BestModelCallback 用于保存和加载最好的模型使用 CheckpointCallback 来保存checkpoint
# 声明一个由我们事先初始化的回调函数组成的列表
callbacks = [timer_callback_epochs, earlystop_callback, bestmodel_callback]

也可自定义Callback,继承自mindnlp.abc.callback。Callback中所有的类方法都会在Trainer的训练中在特定的阶段调用。如train_begin()会在训练开始时被调用,epoch_end()会在每个epoch结束时调用。具体有哪些类方法,参见Callback文档。这里,MyCallback在每个epoch结束时调用epoch_end(),输出当前epoch结束时的loss均值。

定义评价指标

MindNLP中有很多 Metric 可以用于模型评估: AccuracyBleuScoreConfusionMatrixDistinctEmScoreF1ScoreMatthewsCorrelationPearsonCorrelationPerplexityPrecisionRecallRougeLRougeNSpearmanCorrelation。使用一个或多个评价指标来评估模型是有必要的。我们选择 Accuracy 作为模型的评价指标:

1
2
from mindnlp.engine.metrics import Accuracy
metric = Accuracy()

也可以通过继承基类Metric自己定义

特别快的使用trainer,不需要多余写东西

1
2
3
4
5
6
7
8
9
10
11
12
13
from mindnlp.engine.trainer import Trainer

trainer = Trainer(network=net, train_dataset=imdb_train, eval_dataset=imdb_valid, metrics=metric,epochs=5, loss_fn=loss, optimizer=optimizer, callbacks=callbacks)
# network:训练的网络。
# train_dataset:用于训练模型的数据集。
# eval_dataset:用于评估模型的数据集。
# metrics:用于评估模型的评价指标。
# epochs:训练数据的总迭代次数。
# loss_fn:损失函数。
# optimizer:用于更新训练参数的优化器。
# callbacks:训练时执行的额外操作。

trainer.run(tgt_columns="label", jit=False)

组件

modules 用于构建神经网络模型,可以和 MindSpore 一起使用。 modules 具有三大功能模块:Embedding, Encoder-Decoder 和 Attention

Embedding

embedding直译是嵌入式、嵌入层。本质上是一种词嵌入技术,能够将一个单词或短语表示为低维向量.mindnlp提供了一个快速通过预训练glove,fasttext,word2vec词向量简单构造embedding的方法

embedding的发展和常用embedding
embedding基本概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
from mindspore import Tensor
from mindspore.dataset.text.utils import Vocab
from mindnlp.modules.embeddings.glove_embedding import Glove

# 这个是自己定义的,需要字典和对应的向量,然后可以传入这两个参数创建一个自定义的glove
# Define your own vocab
vocab = Vocab.from_list(['default', 'one', 'two', 'three'])
# Define your own embedding table
init_embed = Tensor(np.zeros((4, 4)).astype(np.float32))
# Create your own embedding object
glove_embed = Glove(vocab, init_embed)


# 直接用他定义好的,pretrained的
# You can also use pre-trained word vectors
glove_embed_pretrained, _ = Glove.from_pretrained()

# 这个Embedding怎么反向通过向量输出文字
# The index to query for
ids = Tensor([1, 2, 3])

# Computed by the built embedding
output = glove_embed(ids)

Encoder-Decoder

Encoder-Decoder是一个模型架构,是一类算法统称。在这个框架下可以使用不同的算法来解决不同的人物。Encoder将输入序列转化为语义向量,Decoder根据Encoder的输出生成目标译文。MindNLP.modules.encoderMindNLP.modules.decoder ,MindNLP中包含的Encoder-Decoder模块如下表所示

名称 介绍
CNNEncoder 由传入参数convolutions组成的卷积编码器
RNNEncoder 循环神经网络(RNN)编码器
RNNDecoder 循环神经网络(RNN)解码器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from mindspore import nn
from mindnlp.abc import Seq2seqModel
from mindnlp.modules import RNNEncoder, RNNDecoder

class MachineTranslation(Seq2seqModel):
def __init__(self, encoder, decoder):
super().__init__(encoder, decoder)
self.encoder = encoder
self.decoder = decoder

def construct(self, en, de):
encoder_out = self.encoder(en)
decoder_out = self.decoder(de, encoder_out=encoder_out)
output = decoder_out[0]
return output.swapaxes(1,2)

enc_emb_dim = 256
dec_emb_dim = 256
enc_hid_dim = 512
dec_hid_dim = 512
enc_dropout = 0.5
dec_dropout = 0.5

# encoder
en_embedding = nn.Embedding(input_dim, enc_emb_dim)
en_rnn = nn.RNN(enc_emb_dim, hidden_size=enc_hid_dim, num_layers=2, has_bias=True,batch_first=True, dropout=enc_dropout, bidirectional=False)
rnn_encoder = RNNEncoder(en_embedding, en_rnn)

# decoder
de_embedding = nn.Embedding(output_dim, dec_emb_dim)
input_feed_size = 0 if enc_hid_dim == 0 else dec_hid_dim
rnns = [
nn.RNNCell(
input_size=dec_emb_dim + input_feed_size
if layer == 0
else dec_hid_dim,
hidden_size=dec_hid_dim
)
for layer in range(2)
]
rnn_decoder = RNNDecoder(de_embedding, rnns, dropout_in=enc_dropout, dropout_out = dec_dropout,attention=True, encoder_output_units=enc_hid_dim)

Attention

目前,MindNlp已经实现了8种注意力机制。 MindNLP.modules.attentions .

1
2
3
4
5
6
7
8
9
10
11
import mindspore
from mindspore import Tensor
from mindspore.text.modules.attentions import ScaledDotAttention
model = ScaledDotAttention(dropout=0.9)
# You can customize the query, key, vlaue vector
q = Tensor(np.ones((2, 32, 512)), mindspore.float32)
k = Tensor(np.ones((2, 20, 512)), mindspore.float32)
v = Tensor(np.ones((2, 20, 400)), mindspore.float32)
output, att = model(q, k, v)
# output shape is (2, 1024, 512)
# att shape is (2, 1024, 32)

示例四个

文本分类

序列翻译

机器学习

回答问题