tnblog
首页
视频
资源
登录

HugginFace 中文命名实体识别(学习笔记)

2442人阅读 2023/10/31 21:23 总访问:3470383 评论:0 收藏:0 手机
分类: HuggingFace

HugginFace 中文命名实体识别(学习笔记)

任务简介


简单来说就是的识别人名、机构名、地名。

数据集的介绍


本章所使用的数据集是people_daily_ner数据集。
people_daily_ner数据集标签对照表如下所示。

标签 描述
O 表示不属于一个命名实体
B-PER 表示人名的开始。
I-PER 表示的人名的中间和结尾部分。
B-ORG 表示组织机构名的开始部分。
I-ORG 表示组织机构名的中间和结尾部分。
B-LOC 表示地名的开始。
I-LOC 标识地名的中间和结尾部分。

实现代码

准备数据集

使用编码工具


hfl/rbt3编码器的编码结果与bert-base-chinese编码器相同,使用hfl/rbt3编码基本不需要任何的学习过程,此处首先加载该编码器,代码如下:

  1. # 加载编码器
  2. from transformers import AutoTokenizer
  3. tokenizer = AutoTokenizer.from_pretrained('hfl/rbt3')
  4. tokenizer


进行试算一次,更加清晰的观察编码工具的输入和输出:

  1. # 编码测试
  2. out = tokenizer.batch_encode_plus(
  3. [[
  4. '海', '钓', '比', '赛', '地', '点', '在', '厦', '门', '与', '金', '门', '之', '间',
  5. '的', '海', '域', '。'
  6. ],
  7. [
  8. '这', '座', '依', '山', '傍', '水', '的', '博', '物', '馆', '由', '国', '内', '一',
  9. '流', '的', '设', '计', '师', '主', '持', '设', '计', '。'
  10. ]],
  11. truncation=True,
  12. padding=True,
  13. return_tensors='pt',
  14. max_length=20,
  15. is_split_into_words=True)
  16. #还原编码为句子
  17. print(tokenizer.decode(out['input_ids'][0]))
  18. print(tokenizer.decode(out['input_ids'][1]))
  19. for k, v in out.items():
  20. print(k, v)

参数 描述
is_split_into_words 当为True时表示告诉编码器输入的句子是分好的词的,不需要进行分词工作了。
max_length 20时,表示长度最长为20,不足补[PAD],超出会被截断。

定义数据集


数据集为people_daily_ner

  1. #第10章/定义数据集
  2. import torch
  3. from datasets import load_dataset, load_from_disk
  4. class Dataset(torch.utils.data.Dataset):
  5. def __init__(self, split):
  6. #在线加载数据集
  7. dataset = load_dataset(path='peoples_daily_ner', split=split)
  8. #离线加载数据集
  9. #dataset = load_from_disk(
  10. # dataset_path='./data/peoples_daily_ner')[split]
  11. self.dataset = dataset
  12. #dataset.features['ner_tags'].feature.num_classes
  13. #7
  14. #dataset.features['ner_tags'].feature.names
  15. #['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC']
  16. def __len__(self):
  17. return len(self.dataset)
  18. def __getitem__(self, i):
  19. tokens = self.dataset[i]['tokens']
  20. labels = self.dataset[i]['ner_tags']
  21. return tokens, labels
  22. dataset = Dataset('train')
  23. tokens, labels = dataset[0]
  24. print(tokens), print(labels)
  25. len(dataset)


这里我使用的是在线加载,在people_daily_ner数据集中,每条数据包括两个字段,即tokensner_tag,分别代码句子和标签,在__getitem__()函数中的两个字段取出来并返回即可。
运行结果如下:


可见,训练数据集包括20865条数据,每条数据包括一条分好的词的文本和一个标签列表。
值得注意的是,此处的数据任然是文本数据,还没有被编码器编码。

定义计算机设备

  1. device = 'cpu'
  2. if torch.cuda.is_available():
  3. device = 'cuda'
  4. device

定义数据整理函数


做一个数据整理的操作。

  1. #第10章/定义数据整理函数
  2. def collate_fn(data):
  3. tokens = [i[0] for i in data]
  4. labels = [i[1] for i in data]
  5. #编码
  6. inputs = tokenizer.batch_encode_plus(tokens,
  7. truncation=True,
  8. padding=True,
  9. return_tensors='pt',
  10. max_length=512,
  11. is_split_into_words=True)
  12. #求一批数据中最长的句子长度
  13. lens = inputs['input_ids'].shape[1]
  14. #在labels的头尾补充7,把所有的labels补充成统一的长度
  15. for i in range(len(labels)):
  16. labels[i] = [7] + labels[i]
  17. labels[i] += [7] * lens
  18. labels[i] = labels[i][:lens]
  19. #把编码结果移动到计算设备
  20. for k, v in inputs.items():
  21. inputs[k] = v.to(device)
  22. #把统一长度的labels组装成矩阵,并移动到计算设备
  23. labels = torch.LongTensor(labels).to(device)
  24. return inputs, labels


使用tokenizer的工具对 tokens 进行编码。tokenizer.batch_encode_plus 函数将 tokens 编码成模型可以处理的格式,同时确保它们具有相同的长度。这个函数执行以下操作:
truncation=True:截断文本以确保所有文本的长度不超过指定的最大长度。
padding=True:在文本的末尾添加填充标记,以确保它们的长度相等。
return_tensors='pt':返回 PyTorch 张量。
max_length=512:限制文本长度不超过512个标记。
is_split_into_words=True:表示 tokens 已经分割成单词。
接下来进行试算一下:

  1. #数据整理函数试算
  2. #模拟一批数据
  3. data = [
  4. ([
  5. '海', '钓', '比', '赛', '地', '点', '在', '厦', '门', '与', '金', '门', '之', '间',
  6. '的', '海', '域', '。'
  7. ], [0, 0, 0, 0, 0, 0, 0, 5, 6, 0, 5, 6, 0, 0, 0, 0, 0, 0]),
  8. ([
  9. '这', '座', '依', '山', '傍', '水', '的', '博', '物', '馆', '由', '国', '内', '一',
  10. '流', '的', '设', '计', '师', '主', '持', '设', '计', ',', '整', '个', '建', '筑',
  11. '群', '精', '美', '而', '恢', '宏', '。'
  12. ], [
  13. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  14. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  15. ]),
  16. ]
  17. #试算
  18. inputs, labels = collate_fn(data)
  19. for k, v in inputs.items():
  20. print(k, v.shape)
  21. print('labels', labels.shape)


当前最长的句子有37个词。

定义数据集加载器

  1. #数据加载器
  2. loader = torch.utils.data.DataLoader(dataset=dataset,
  3. batch_size=16,
  4. collate_fn=collate_fn,
  5. shuffle=True,
  6. drop_last=True)
  7. len(loader)


可见训练数据集一共执行了1340个批次。
定义好了数据集加载器之后,可以查看一批数据样本,代码如下:

  1. #第10章/查看数据样例
  2. for i, (inputs, labels) in enumerate(loader):
  3. break
  4. print(tokenizer.decode(inputs['input_ids'][0]))
  5. print(labels[0])
  6. for k, v in inputs.items():
  7. print(k, v.shape)

定义模型

加载预训练模型


使用hfl/rbt3作为预习训练模型,代码如下:

  1. #加载预训练模型
  2. from transformers import AutoModel
  3. pretrained = AutoModel.from_pretrained('hfl/rbt3')
  4. pretrained.to(device)
  5. #统计参数量
  6. print(sum(i.numel() for i in pretrained.parameters()) / 10000)


定义好预训练模型之后,可以进行一次试算:

  1. #第10章/模型试算
  2. #[b, lens] -> [b, lens, 768]
  3. pretrained(**inputs).last_hidden_state.shape


样式数据为16句话的编码结果,从预训练模型的计算结果可以看出,这也是16句话的结果,每句话包括54个词,每个词被抽成了一个768维的向量。到此为止,通过预训练模型成功的把16句话转换成一个特征向量矩阵,可以接入下游任务模型做分类或回归任务。

定义下游任务模型


本章将使用两段式训练,所以要求下游任务模型能够切换微调(Fine Tuning)模式。

什么是两段式训练?两段式训练是一种训练计较,指先单独对下游任务模型进行一定的训练,待下游任务模型掌握了一定的知识以后,再连通预训练模型和下游模型一起进行训练的模式。

  1. #第10章/定义下游模型
  2. class Model(torch.nn.Module):
  3. def __init__(self):
  4. super().__init__()
  5. #标识当前模型是否处于tuning模式
  6. self.tuning = False
  7. #当处于tuning模式时backbone应该属于当前模型的一部分,否则该变量为空
  8. self.pretrained = None
  9. #当前模型的神经网络层
  10. self.rnn = torch.nn.GRU(input_size=768, hidden_size=768, batch_first=True)
  11. self.fc = torch.nn.Linear(in_features=768, out_features=8)
  12. def forward(self, inputs):
  13. #根据当前模型是否处于tuning模式而使用外部backbone或内部backbone计算
  14. if self.tuning:
  15. out = self.pretrained(**inputs).last_hidden_state
  16. else:
  17. with torch.no_grad():
  18. out = pretrained(**inputs).last_hidden_state
  19. #backbone抽取的特征输入rnn网络进一步抽取特征
  20. out, _ = self.rnn(out)
  21. #rnn网络抽取的特征最后输入fc神经网络分类
  22. out = self.fc(out).softmax(dim=2)
  23. return out
  24. #切换下游任务模型的的tuning模式
  25. def fine_tuning(self, tuning):
  26. self.tuning = tuning
  27. #tuning模式时,训练backbone的参数
  28. if tuning:
  29. for i in pretrained.parameters():
  30. i.requires_grad = True
  31. pretrained.train()
  32. self.pretrained = pretrained
  33. #非tuning模式时,不训练backbone的参数
  34. else:
  35. for i in pretrained.parameters():
  36. i.requires_grad_(False)
  37. pretrained.eval()
  38. self.pretrained = None
  39. model = Model()
  40. model.to(device)
  41. model(inputs).shape


这段代码定义并初始化了下游任务模型,在下游任务模型的__init_()函数中有两个重要的变量,即tuningpretrained,其中tuning为布尔型变量,取值为TrueFalse,它表明了当前模型是否处于微调模型,默认值为False,即非微调模式。pretrained代表了预训练模型,当前处于微调模型时预训练模型应该属于当前模型的一部分,反之则不属于,默认为None,即预训练模型不属于当前模型的一部分。

__init__()函数中还定义了下游任务模型的两个网络层,即是循环神经网络层和全连接神经网络层,分别命名为rnnfc,其中循环神经网络的实现为GRU网络。

forward()函数定义了下游任务模型的计算过程,首先判断当前模型是否处于微调模式,如果处于微调模式的话使用内部的预训练模型,否则使用外部的预训练模型,并且不计算预训练模型的梯度。得到预训练模型抽取的文本特征后,把文本特征输入循环神经网络进一步抽取特征,最后把特征数据输入全连接神经网络分类即可。

为什么需要循环神经网络层?这是一个想当然的想法,因为标签列表也可以看作一句话,这句也符合一定的统计规律,例如人名的中间部分(I-PER)一定出现在人名的开头(B-PER之后),所以把预训练模型抽取的文本特征也当作一个序列数据进行处理,输入循环神经网络再次抽取特征,最后做分类计算,期望可以得到更好的结果。读者也可以尝试移除,或者增加其他的层,来提高模型预测的正确率,深度学习任务中往往有很多这样的尝试性实验。
一般的PyTorch模型定义__init__()函数和forward()函数就可以了,但是在上面的模型中还定义了fine_tuning()的函数,这个函数就是要切换下游模型的微调模型,入参为一个布尔值,取值为TrueFalse。如前所述,当切换到微调模式时,把预训练模型切换到训练模型。
反之,不处于微调模式时要冻结预训练模型的参数,不让它们随着训练更新,并且预训练模型不属于下游任务模型的一部分,要把训练模式切换为运行模式。
下游任务模型试算结果如下:


从结果可以看出,运算的结果为16句话,54个词,每个词为8分类结果。

训练和测试

两个工具函数


为了便于后续的训练和测试,需要定义两个工具函数,第一个函数的功能是对计算结果和labels变形,并且移除PAD,需要这个函数的原因是因为在一批数据中,往往会有很多的PAD,对这些PAD去计算它们的实体命名是没有什么意义的,显然它们不可能是任何命名实体,为了不让模型去研究这些PAD是什么东西,直接从计算结果中移除这些PAD,以防止模型做无用功。实现代码如下:

  1. #第10章/对计算结果和label变形,并且移除pad
  2. def reshape_and_remove_pad(outs, labels, attention_mask):
  3. #变形,便于计算loss
  4. #[b, lens, 8] -> [b*lens, 8]
  5. outs = outs.reshape(-1, 8)
  6. #[b, lens] -> [b*lens]
  7. labels = labels.reshape(-1)
  8. #忽略对pad的计算结果
  9. #[b, lens] -> [b*lens - pad]
  10. select = attention_mask.reshape(-1) == 1
  11. outs = outs[select]
  12. labels = labels[select]
  13. return outs, labels
  14. reshape_and_remove_pad(torch.randn(2, 3, 8), torch.ones(2, 3),
  15. torch.ones(2, 3))


这段的代码中,首先把模型的预测结果和labels都从多句话合并成一句话,合并的方式就是简单地进行头尾相接,这样能够方便后续计算loss。
移除PAD时使用编码结果中的attention_mask,attention_mask标记了一个句子中哪些的位置是PADattention_mask中只有0和1,其中0标识是其中PAD的位置,使用attention_mask可以很轻松地过滤掉结果中的PAD.
在代码的最后使用一批虚拟的数据试算该函数,运行结果如下:


虚拟数据中的2x3x8矩阵表示2句话、3个词、每个词8分类的预测结果,第1个2x3矩阵表示真实的labels,第二个2x3的矩阵表示attention_mask,因为全为1,所以全部保留,没有PAD
最后计算的结果也确实全部保留了预测结果和labels,并且预测结果和labels被变形成一句话,和预测一致。
第二个函数用于预测结果中测试正确了多少个,以及一共有多少个预测结果如下:

  1. #第10章/获取正确数量和总数
  2. def get_correct_and_total_count(labels, outs):
  3. #[b*lens, 8] -> [b*lens]
  4. outs = outs.argmax(dim=1)
  5. correct = (outs == labels).sum().item()
  6. total = len(labels)
  7. #计算除了0以外元素的正确率,因为0太多了,包括的话,正确率很容易虚高
  8. select = labels != 0
  9. outs = outs[select]
  10. labels = labels[select]
  11. correct_content = (outs == labels).sum().item()
  12. total_content = len(labels)
  13. return correct, total, correct_content, total_content
  14. get_correct_and_total_count(torch.ones(16), torch.randn(16, 8))


因为虚拟的labels全是1,并没有出现标签0的情况,所以统计得出的两套正确数量和总数相等。

训练

  1. #第10章/训练
  2. from transformers import AdamW
  3. from transformers.optimization import get_scheduler
  4. def train(epochs):
  5. lr = 2e-5 if model.tuning else 5e-4
  6. optimizer = AdamW(model.parameters(), lr=lr)
  7. criterion = torch.nn.CrossEntropyLoss()
  8. scheduler = get_scheduler(name='linear',
  9. num_warmup_steps=0,
  10. num_training_steps=len(loader) * epochs,
  11. optimizer=optimizer)
  12. model.train()
  13. for epoch in range(epochs):
  14. for step, (inputs, labels) in enumerate(loader):
  15. #模型计算
  16. #[b, lens] -> [b, lens, 8]
  17. outs = model(inputs)
  18. #对outs和label变形,并且移除pad
  19. #outs -> [b, lens, 8] -> [c, 8]
  20. #labels -> [b, lens] -> [c]
  21. outs, labels = reshape_and_remove_pad(outs, labels,
  22. inputs['attention_mask'])
  23. #梯度下降
  24. loss = criterion(outs, labels)
  25. loss.backward()
  26. optimizer.step()
  27. scheduler.step()
  28. optimizer.zero_grad()
  29. if step % (len(loader) * epochs // 30) == 0:
  30. counts = get_correct_and_total_count(labels, outs)
  31. accuracy = counts[0] / counts[1]
  32. accuracy_content = counts[2] / counts[3]
  33. lr = optimizer.state_dict()['param_groups'][0]['lr']
  34. print(epoch, step, loss.item(), lr, accuracy, accuracy_content)
  35. torch.save(model, 'model/中文命名实体识别.model')


训练函数接受一个参数epochs,表示要使用全量数据训练几个轮次,由于是两段式训练,所以得出来的训练轮次可能不一样。所以需要这个参数。
由于采用了两段式训练,所以会根据模型是否处于微调模式选择不同的Learning Rate,在非微调模式时选择较大的Learning Rate,以快速训练下游任务模型在微调模式时则选择较小的Learning Rate,以精细地调节模型参数,帮助模型优化到更优的性能。
之后定义了优化器、loss计算函数、学习率调节器。
需要注意的是,优化器优化的参数表为下游任务模型的所有参数,因为下游任务模型在微调模式的问题,在非微调模式下,预训练模型并不属于下游任务模型的一部分,所以优化器的参数数量比较少,仅包含下游任务模型本身的参数。而在微调模式下,预训练模型属于下游任务模型的一部分,所以优化器优化的参数表也会包括预训练模型,这也是为什么要在切换微调模式时,设置下游任务模型的pretrained属性的原因。
接下来把下游任务模型切换到训练模式,并且在全量训练数据上遍历epochs个轮次,对模型进行训练。训练过程如下所述:
(1)从训练集加载器中获取一个批次的数据。
(2)让模型计算预测结果
(3)使用工具函数对预测结果和labels进行变形,移除预测结果和labels中的PAD。
(4)计算loss并执行梯度下降优化模型。
(5)每隔一定的steps,输出一次模型当前的各项数据,便于观察
(6)每训练完一个epoch,将模型的参数大家保存到磁盘。

两段式训练

  1. #两段式训练第1步,训练下游任务模型
  2. model.fine_tuning(False)
  3. print(sum(p.numel() for p in model.parameters()) / 10000)
  4. train(1)


在这段代码中,首先把下游任务模型切换到非微调模式,之后输出了模型的参数量,由于干预训练模型并不属于下游任务模型的一部分,所以此处期待的参数量应该稍小,最后在全量数据上训练1个轮次,运行结果如下:


可以看到在非微调模式下,下游任务模型的参数量为354万。
训练过程的输出表如下:


从该表可以看出,随着训练步骤的增多,loss收敛得很快,并且正确率已经很高,即达到了85%,但排除labels中的0之后,正确率却只有25%,可见正确率是虚高的。
接下来可以进行两段式训练的第二个阶段,代码如下:

  1. #第10章/两段式训练第2步,同时训练下游任务模型和预训练模型
  2. model.fine_tuning(True)
  3. print(sum(p.numel() for p in model.parameters()) / 10000)
  4. train(5)


这段代码,把下游任务模式切换到微调模式,这意味着预训练模型将被一起训练。代码中输出了当前下游模型的参数量,由于预训练模型已经属于下游任务模型的一部分,因此此处的参数量期望会比较大,最后在全量数据上执行5个轮次的训练,运行结果如下:


可见切换到微调模式后,下游任务模型的的参数量增加到4200万个,由于采用了较小的的预训练模式,所以这个参数量的规模依然较小,即使在一颗CPU上训练这个任务,时间也应该在可接受的范围内。训练过程的输出见表:


可以看出训练总体的正确率上升了不少,排除标签0之后的正确率也上升了。

测试


最后,对训练好的模型进行训练,以验证训练的有效性,代码如下:

  1. #第10章/测试
  2. def test():
  3. #加载训练完的模型
  4. model_load = torch.load('中文命名实体识别.model')
  5. model_load.tuning = True
  6. model_load.eval()
  7. model_load.to(device)
  8. #测试数据集加载器
  9. loader_test = torch.utils.data.DataLoader(dataset=Dataset('validation'),
  10. batch_size=128,
  11. collate_fn=collate_fn,
  12. shuffle=True,
  13. drop_last=True)
  14. correct = 0
  15. total = 0
  16. correct_content = 0
  17. total_content = 0
  18. #遍历测试数据集
  19. for step, (inputs, labels) in enumerate(loader_test):
  20. #测试5个批次即可,不全部全部遍历
  21. if step == 5:
  22. break
  23. print(step)
  24. #计算
  25. with torch.no_grad():
  26. #[b, lens] -> [b, lens, 8] -> [b, lens]
  27. outs = model_load(inputs)
  28. #对outs和label变形,并且移除pad
  29. #outs -> [b, lens, 8] -> [c, 8]
  30. #labels -> [b, lens] -> [c]
  31. outs, labels = reshape_and_remove_pad(outs, labels,
  32. inputs['attention_mask'])
  33. #统计正确数量
  34. counts = get_correct_and_total_count(labels, outs)
  35. correct += counts[0]
  36. total += counts[1]
  37. correct_content += counts[2]
  38. total_content += counts[3]
  39. print(correct / total, correct_content / total_content)
  40. test()


在这段的代码中,首先从磁盘加载了训练完毕的模型,然后把模型切换到运行模式,再把模型移动到定义好的计算设备上。
完成模型的加载之后,定义测试数据集和加载器,并取出5个批次的数据让模型进行预测,最后统计两个正确率并输出,两个正确率之间的区别是一个统计了标签0,另一个则没有,运行结果如下:

预测


验证了模型的有效性之后,可以进行一些预测,以更直观地观察模型的预测结果,代码如下:

  1. #预测
  2. def predict():
  3. #加载模型
  4. model_load = torch.load('中文命名实体识别.model')
  5. model_load.tuning = True
  6. model_load.eval()
  7. model_load.to(device)
  8. #测试数据集加载器
  9. loader_test = torch.utils.data.DataLoader(dataset=Dataset('validation'),
  10. batch_size=32,
  11. collate_fn=collate_fn,
  12. shuffle=True,
  13. drop_last=True)
  14. #取一个批次的数据
  15. for i, (inputs, labels) in enumerate(loader_test):
  16. break
  17. #计算
  18. with torch.no_grad():
  19. #[b, lens] -> [b, lens, 8] -> [b, lens]
  20. outs = model_load(inputs).argmax(dim=2)
  21. for i in range(32):
  22. #移除pad
  23. select = inputs['attention_mask'][i] == 1
  24. input_id = inputs['input_ids'][i, select]
  25. out = outs[i, select]
  26. label = labels[i, select]
  27. #输出原句子
  28. print(tokenizer.decode(input_id).replace(' ', ''))
  29. #输出tag
  30. for tag in [label, out]:
  31. s = ''
  32. for j in range(len(tag)):
  33. if tag[j] == 0:
  34. s += '·'
  35. continue
  36. s += tokenizer.decode(input_id[j])
  37. s += str(tag[j].item())
  38. print(s)
  39. print('==========================')
  40. predict()


这段代码中执行了以下工作:
(1)加载了训练完毕的模型,并切换到运行模式,再移动到定义好的计算设备上。
(2)定义了测试数据集加载器,然后从数据集加载器中取出了一批数据。
(3)对这批数据进行预测。
(4)对原句子进行一些处理,以便符合人类对阅读习惯。
(5)输出labels和预测结果,以观察两种的异同。


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

HugginFace 初探

HugginFace 初探[TOC] 安装环境python环境是3.6。import sys sys.version 安装torch,简单起见,避免环境问题,并且计...

HugginFace 使用编码工具(学习笔记)

HugginFace 使用编码工具(学习笔记)[TOC] 安装环境# 我这里的python是3.11 %pip install -q transformers==4.18 datasets...

HugginFace 使用数据集(学习笔记)

HugginFace 使用数据集(学习笔记)[TOC] 数据集工具介绍HuggingFace 提供了统一的数据集处理工具,让不同的数据集通过统一...

HugginFace 使用评价指标工具(学习笔记)

HugginFace 使用评价指标工具(学习笔记)[TOC] 评价指标工具介绍在训练和测试一个模型时往往需要计算不同的评价指标,如正...

HugginFace 使用管道工具(学习笔记)

HugginFace 使用管道工具(学习笔记)[TOC] 管道工具介绍HuggingFace 有一个巨大的模型库,其中一些是已经非常成熟的经典模...

HugginFace 使用训练工具(学习笔记)

HugginFace 使用训练工具(学习笔记)[TOC] 训练工具介绍HuggingFace提供了巨大的模型库,但我们往往还需要对特定的数据集进...

HugginFace 中文情感分类(学习笔记)

HugginFace 中文情感分类(学习笔记)[TOC] 数据集介绍本章使用的是lansinuote/ChnSentiCorp数据集,这是一个情感分类数据集...

HugginFace 中文填空(学习笔记)

HugginFace 中文填空(学习笔记)[TOC] 数据集介绍本章使用的仍然是情感分类数据集,每条包括一句购物评价一集以及是不是好...

HugginFace 中文数据关系推断(学习笔记)

HugginFace 中文数据关系推断(学习笔记)[TOC] 实现代码安装包加载的环境可以通过如下命令进行安装。%pip install -q trans...
这一世以无限游戏为使命!
排名
2
文章
635
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术