HuggingFace-利用BERT预训练模型实现中文情感分类(下游任务)

准备数据集

使用编码工具

首先需要加载编码工具,编码工具可以将抽象的文字转成数字,便于神经网络后续的处理,其代码如下:

# 定义数据集
from transformers import BertTokenizer, BertModel, AdamW
# 加载tokenizer
token = BertTokenizer.from_pretrained('bert-base-chinese')
print('token', token)

out:
token BertTokenizer(name_or_path=‘bert-base-chinese’, vocab_size=21128, model_max_length=512, is_fast=False, padding_side=‘right’, truncation_side=‘right’, special_tokens={‘unk_token’: ‘[UNK]’, ‘sep_token’: ‘[SEP]’, ‘pad_token’: ‘[PAD]’, ‘cls_token’: ‘[CLS]’, ‘mask_token’: ‘[MASK]’}, clean_up_tokenization_spaces=True), added_tokens_decoder={
0: AddedToken(“[PAD]”, rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
100: AddedToken(“[UNK]”, rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
101: AddedToken(“[CLS]”, rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
102: AddedToken(“[SEP]”, rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
103: AddedToken(“[MASK]”, rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

由上可知bert-base-chinese模型的字典中共有21128个词,编码器编码句子的最大长度为512个词,并且能够看到bert-base-chinese模型所使用的一些特殊符号,例如SEK,PAD等。

这里使用的编码工具是bert-base-chinese,编码工具和预训练模型往往是成对使用的,后续将使用同名的预训练语言模型作为backbone。

编码工具的试算

加载完成编码工具之后可以进行一次试算,观察编码工具的输入和输出,代码如下:

data = token.batch_encode_plus(batch_text_or_text_pairs=['关注博主,不迷路。','俺要带你上高速。'], truncation=True,padding='max_length',max_length=12,return_tensors='pt',return_length=True)
# 查看编码输出
for k,v in out.items():print(k,v.shape)
# 把编码还原成句子
print(token.decode(out['input_ids'][0]))

out:
input_ids torch.Size([2, 17])
token_type_ids torch.Size([2, 17])
length torch.Size([2])
attention_mask torch.Size([2, 17])
[CLS] 关 注 博 主 , 不 迷 路 。 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]
[CLS] 俺 要 带 你 上 高 速 。 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]

编码工具的参数说明

对于编码工具的使用,特别是参数值的含义可以参考下面的两段代码:

"""使用简单的编码"""
# 编码两个句子
out = tokenizer.encode(# 句子1text = sents[0],text_pair = sents[1],# 当句子长度大于max_length时进行截断truncation=True,# 一律补充pad到max_length长度padding = 'max_length',add_special_tokens = True,# 许多大模型的阶段也是使用512作为最终的max_lengthmax_length=30,return_tensors=None,
)
"""增强的编码函数"""
# 增强的编码函数
out = tokenizer.encode_plus(text = sents[0],text_pair = sents[1],#当句子长度大于max_length时进行截断操作truncation = True,#一律补零到max_length长度padding='max_length',max_length=30,add_special_tokens=True,#可以取值tf,pt,np,默认返回list--->tensorflow,pytorch,numpyreturn_tensors=None,#返回token_type_idsreturn_token_type_ids=True,#返回attention_maskreturn_attention_mask=True,#返回special_tokens_mask 特殊符号标识return_special_tokens_mask=True,#返回offset_mapping标识每个词的起始和结束位置---》这个参数只能BertTokenizerFast使用#return_offsets_mapping=True,#返回length 标识长度return_length=True
)

从上面的代码中的参数max_length=500可以看出经过编码后的句子的长度一定是12个词的长度。如果源句子超出则会进行截断,如果源句子不足则会进行填充PAD,其运行结果如下:

{'input_ids': tensor([[ 101, 1068, 3800, 1300,  712, 8024,  679, 6837, 6662,  511,  102,    0],[ 101,  939, 6206, 2372,  872,  677, 7770, 6862,  511,  102,    0,    0]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'length': tensor([11, 10]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]])}
input_ids torch.Size([2, 12])
token_type_ids torch.Size([2, 12])
length torch.Size([2])
attention_mask torch.Size([2, 12])
[CLS] 关 注 博 主 , 不 迷 路 。 [SEP] [PAD]
[CLS] 俺 要 带 你 上 高 速 。 [SEP] [PAD] [PAD]

在这里插入图片描述
编码工具首先是对一条完整的句子进行了tokenizer,把句子分成了一个个token。同时,对于不同的编码工具,分词的结果也不一定一致。这里采用的bert-base-chinese编码工具中,它是以字为词,即把每个字当做一个词进行处理。
这些编码的结果对于预训练模型的计算十分重要,在后面将会使用编码器将所有的句子进行编码,用于输入到预训练模型中进行计算。

定义数据集

这里使用的数据集为ChnSentiCorp数据集,Dataset类如下:

# import torch
from datasets import load_dataset
class Dataset(torch.utils.data.Dataset):def __init__(self, split):self.dataset = load_dataset(path='lansinuote/ChnSentiCorp', split=split)def __len__(self):return len(self.dataset)def __getitem__(self, i):text = self.dataset[i]['text']label = self.dataset[i]['label']return text, label
dataset = Dataset('train')
print(len(dataset))
print(dataset[0])

在上述代码中加载了ChnSentiCorp数据集,并使用Pytorch中的Dataset对象进行封装,利用__getitem__()得到每一条数据,每条数据中包含textlabels两个字段,最后初始化训练数据集并查看训练数据集的长度和第一条数据样例。

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

由上面的输出可知训练数据集包括9600条数据,每条数据包含一条评论文本和一个标识,表明这一条评论是好评还是差评。注意:这里的数据集是单纯的原始文本数据,并没有进行编码。

定义计算设备

这里将使用CUDA作为计算设备,这样可以极大加速模型的训练和测试的过程,代码如下:

device = 'cpu'
if torch.cuda.is_available():device = 'CUDA'
print('选用的计算设备:',device)

在该段代码中默认使用CPU进行计算,如果存在CUDA的话则选用CUDA作为计算设备。

定义数据整理函数

正如上面所述的那样,ChnSentiCorp数据集中的每一条数据是抽象的文本数据,并没有进行任何的编码操作,而预训练模型是需要编码之后的数据才能进行计算,所以需要一个将文本句子转成编码的过程。
另外,在训练模型时数据集往往很大,如果一条一条地处理则效率会太低,在现实中我们往往一批一批地处理数据,这样可以快速地处理数据集,同时从梯度下降的角度来讲,批数据的梯度方差相较于一条条数据的梯度小,可以让模型更加稳定地更新参数

# 定义批处理函数
def collate_fn(data):sents = [i[0] for i in data]labels = [i[1] for i in data]# 编码data = token.batch_encode_plus(batch_text_or_text_pairs=sents, truncation=True,padding='max_length',max_length=500,return_tensors='pt',return_length=True)# input_ids:编码之后的数字# attention_masks:补0的位置都是0,其他位置都是1input_ids = data['input_ids']attention_mask = data['attention_mask']token_type_ids = data['token_type_ids']labels = torch.LongTensor(labels)# print(data['length'],data['length'].max())return input_ids, attention_mask, token_type_ids, labels

在这段代码中,参数data表示一批数据,取出其中的句子和标识,它们都是list类型,在上述代码中会将两者分别赋给sentslabels,然后是使用编码器编码该批句子,在参数中将编码后的结果指定为固定的500个词的大小,与上面的例子同理超出500个词的部分会被截断(这里是通过truncation=True控制),同时少于500个词的句子会被[PAD]填充(这里主要是通过 padding='max_length'控制)。另外,在编码过程中通过 return_tensors='pt'参数,将编码后的结果返回torch中的tensor类型,免去了后面转换数据格式的麻烦(也就是说后面可以通过数据格式转换可以将‘tf’转成‘pt’格式)。
之后取出编码后的结果,并将labels也转成Pytorch中的Tensor格式,再把它们移动到之前已经定义好的计算设备device上,最后把这些数据全部返回,到这里数据整理函数的工作已经全部完成。

数据处理函数的例子

上述定义了数据处理函数,为了实验其效果也可使用下面的例子:(本用例已加狗头保命~)在这里插入图片描述

data = [('选择新大的原因当然不是为了延毕。',1),('笔记本的内存确实小。',0),('宿舍没有风扇。其他都很好。',1),('今天才知道这本书还有第10000卷,真是太屌了。',1),('机器的背面似乎被撕了张什么标签,残胶还在。',0),('为什么有人在校园里尖叫,是疯了还是giao。',0)
]# 狗头保命版试算
input_ids,attention_mask,token_type_ids,labels = collate_fn(data)
print('input_ids.shape',input_ids.shape)
print('attention_mask.shape',attention_mask.shape)
print('token_type_ids.shape',token_type_ids.shape)
print('labels:',labels)

在该段代码中首先是模拟了一批数据,这批数据中包含4个句子,通过将该批数据输入到整理函数以后,运行结果如下:

input_ids.shape torch.Size([6, 500])
attention_mask.shape torch.Size([6, 500])
token_type_ids.shape torch.Size([6, 500])
labels: tensor([1, 0, 1, 1, 0, 0])

可见编码之后的结果都是确定的500个词的长度,并且每个结果都会被移动到可用的计算设备上,这样可以方便后续的计算。

定义数据加载器

上述代码中定义了数据集和数据整理函数以后,下面我们将定义一个数据加载器DataLoader,它可以使用数据整理函数来完成成批地处理数据集中的数据,通俗来讲每一批的数据我们可以称为batch

# 定义数据加载器并查看数据样例
loader = torch.utils.data.DataLoader(dataset=dataset, batch_size=16,collate_fn=collate_fn,shuffle=True,drop_last=True)

对于上述代码,我们使用了Pytorch提供的工具类定义数据集加载器,其参数说明可参考下图:
在这里插入图片描述

数据加载器的例子

为了更好地使用数据加载器,这里我们查看一批数据样例,将这批数据输入到数据加载器中,可以发现其结果会与数据整理函数的运行结果相似,只不过是句子的数量增多了。

上述代码依次打印了加载器中批次数目、加载器中输入数据的input_ids和掩蔽注意力的形状
attention_mask_shape、词元的ids类型形状token_type_ids_shape以及标签labels

for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader):break
print(len(loader))
print('input_ids', input_ids)
print('attention_mask_shape', attention_mask.shape)
print('token_type_ids_shape', token_type_ids.shape)
print('labels', labels)
  1. input_ids 就是编码后的词
  2. token_type_ids 第一个句子和特殊符号的位置是0,第二个句子的位置是1
  3. attention_mask pad的位置是0,其他位置都是1
  4. special_tokens_mask 特殊符号的位置是1,其他位置都是0

定义模型

因为我们是要利用Huggingface的预训练语言模型,所以需要做两件事情:加载预训练模型PLM以及定义下游任务模型。

加载预训练模型

这里使用的BERT预训练模型,模型名称为bert-base-chinese,这里的名称和编码器的名称是一致的,因为往往模型和其编码工具配套使用。另外,BERT模型不是必须的模型,进行中文情感分类也可以使用其他支持中文的模型,例如BART等。

from transformers import BertModel
# 加载预训练模型
pretrained = BertModel.from_pretrained('bert-base-chinese')
# 统计参数量
sum(i.numel() for i in pretrained.parameters()) / 10000
out:10226.7648

由上可知bert-base-chinese模型的参数量超过1亿个,这个模型的体量还是比较大的。由于它的体量比较大,所以如果要训练它,对计算资源的要求较高,而对于本次的二分类任务则可以选择不训练它,只是作为一个特征提取器。这样就可以避免训练这个笨重的模型,不需要计算它的梯度,进而不更新它的参数,所以需要冻结它的参数:

# 当不进行训练时不需要计算梯度
for param in pretrained.parameters():param.requires_grad_(False)

定义好PLM后,需要进行一次试算,观察模型的输入和输出结果:

# 设定计算设备
pretrained.to(device)
# 模型试算
out = pretrained(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
print(out.last_hidden_state.shape)

这里可能会报错:

RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu! (when checking argument for argument index in method wrapper_CUDA__index_select)

这是因为之前我们忘了将input_ids等参数放到cuda上,所以需要改一下代码:

for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader):break
input_ids = input_ids.to(device)
attention_mask = attention_mask.to(device)
token_type_ids = token_type_ids.to(device)
labels = labels.to(device)
print(len(loader))
print('input_ids', input_ids)
print('attention_mask_shape', attention_mask.shape)
print('token_type_ids_shape', token_type_ids.shape)
print('labels', labels)
print(input_ids.is_cuda)

这里如果有显卡的话会输出True,然后上面的程序报错也会消除,同时输出:

torch.Size([16, 500, 768])

上述代码中将我们的16个样例数据(在定义数据加载器部分)输入到预训练模型中,得到的计算结果和我们预想的是一致的。首先从第一个维度16可以看出是和我们的样例输入的句子数量有关的,随后的500是指每句话中包含了500个单词,因为max_length=500(在数据整理函数中定义),这就把之前所有的内容都串起来了。最后的768表示将每一个词抽成一个768维的向量。到此为止,我们已经通过预训练模型成功地把16句话转换成为了一个特征向量矩阵,这样就可以接入下面的下游任务模型做分类或者回归任务。

定义下游任务模型

下游任务模型的任务是对backbone抽取的特征进行进一步的计算,得到符合业务需求的计算结果,这里做的是一个二分类的结果,因为我们数据集中的labels只有两种。

# 定义下游任务模型
class Model(torch.nn.Module):def __init__(self):super(Model, self).__init__()self.fc = torch.nn.Linear(768, 2).to(device)def forward(self, input_ids, attention_mask, token_type_ids):with torch.no_grad():out = pretrained(input_ids=input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids)out = self.fc(out.last_hidden_state[:, 0])out = out.softmax(dim=1)return outmodel = Model()
# 设置计算设备
model.to(device)

在这段代码中,定义了一个下游任务模型,该模型包括一个全连接的LinearModel,权重矩阵是768x2,所以它能够将一个768维度的向量转换成一个二维空间中。
上述的下游任务模型的计算流程为:

在这里插入图片描述
这里之所以丢弃后面466个词的特征,是因为BERT模型所致,具体的内容可以参考BERT模型的论文:BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

下游任务模型试算

在最后我们使用刚才使用的batch=16的数据进行试算:

# 试算
print(model(input_ids=input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids).shape)

其输出结果为:

torch.Size([16, 2])

由此可见这就是我们一开始所要求的16句话进行二分类的结果。

训练和测试

模型训练

模型定义完成之后,我们就可以对该模型进行训练了~代码如下:

from transformers import AdamW
from transformers.optimization import get_scheduler
def train():# 定义优化器optimizer = torch.optim.AdamW(model.parameters(), lr=5e-4)# 定义损失函数criterion = torch.nn.CrossEntropyLoss()# 定义学习率调节器scheduler = get_scheduler(name='linear',num_warmup_steps=0,num_training_steps=len(loader),optimizer=optimizer)# 将模型切换到训练模式model.train()# 按批次进行遍历训练数据集中的数据for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader):input_ids = input_ids.to(device)attention_mask = attention_mask.to(device)token_type_ids = token_type_ids.to(device)labels = labels.to(device)# 模型计算out = model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids).to(device)# 计算损失并使用梯度下降法优化模型参数loss = criterion(out, labels)loss.backward()optimizer.step()scheduler.step()optimizer.zero_grad()# 输出各项数据的情况,便于观察if i % 10 == 0:out = out.argmax(dim=1)accuracy = (out == labels).sum().item() / len(labels)lr = optimizer.state_dict()['param_groups'][0]['lr']print(i, loss.item(),lr, accuracy)
train()

在上述的代码中首先定义了优化器、损失函数、学习率调节器。其中优化器使用了HuggingFace提供的AdamW优化器,这是传统的Adam优化器的改进版本,在自然语言处理任务中,该优化器往往取得了比Adam优化器更加好的成绩,并且计算效率高。学习率调节器使用了HuggingFace提供的线性学习率调节器,它能够在训练过程中让学习率缓慢下降,而不是始终使用一致的学习率,因为在训练的后期阶段往往需要更小的学习率来微调参数有利于损失函数下降到最低点。这里的损失函数采用了分类任务中常用的CrossEntropyLoss交叉熵损失函数。
然后将下游任务模型切换到训练模式即可开始训练。最后每当优化10次模型参数时,就计算一次当前模型预测结果的正确率,并输出模型的损失函数、学习率,最终训练完毕的结果如下所示:
在这里插入图片描述
由上图可见在训练到大约200个steps时,模型已经能够达到85%左右的正确率,损失函数也如同预期一样随着训练过程不断下降,学习率亦如此。

模型测试

对于已经训练好的模型进行测试,以便验证训练的有效性,其测试代码如下:

def test():# 定义测试数据加载器loader_test = torch.utils.data.DataLoader(dataset = Dataset('test'),batch_size = 32,collate_fn = collate_fn,shuffle = True,drop_last = True)# 将下游任务模型切换到运行模式model.eval()correct = 0total = 0for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(loader_test):# 计算5个批次即可,不需要全部遍历input_ids = input_ids.to(device)attention_mask = attention_mask.to(device)token_type_ids = token_type_ids.to(device)  labels = labels.to(device)    if i == 5:breakprint(i)# 计算with torch.no_grad():out = model(input_ids=input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids).to(device)# 统计正确率out = out.argmax(dim=1)correct += (out==labels).sum().item()total += len(labels)print(correct/total)test()

在上述的代码中首先定义了测试数据集及其加载器,同时取出5个批次的数据让模型进行预测,最后将统计的正确率输出,运行结果为:

0
1
2
3
4
0.88125

最终模型取得了88.125%的正确率,这个正确率虽然不是很高,但是验证了下游任务模型即使在不训练basebone的情况下也可以达到一定的成绩。

省流版-全部代码

在这里插入图片描述

总结

本文通过一个情感分类的例子说明了使用BERT预训练模型抽取文本特征数据的方法,使用BERT作为backbone,相对于传统的RNN而言其计算量会稍稍大一些,但是BERT抽取的文本特征将更加完整,更容易被下游任务模型识别总结出数据之间的规律。所以在不对BERT预训练模型进行训练,而是简单应用于下游任务时也可以表现一个比较好的结果。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/160650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

langchain 部署组件-LangServe

原文:🦜️🏓 LangServe | 🦜️🔗 Langchain LangServe 🚩 We will be releasing a hosted version of LangServe for one-click deployments of LangChain applications. Sign up here to get on the wa…

OpenLayers入门,OpenLayers6的WebGLPointsLayer图层样式和运算符详解,四种symbolType类型案例

专栏目录: OpenLayers入门教程汇总目录 前言 本章讲解使用OpenLayers6的WebGL图层显示大量点情况下,列举出所有WebGLPointsLayer图层所支持的所有样式运算符大全。 补充说明 本篇主要介绍OpenLayers6.x版本的webgl图层,OpenLayers7.x和OpenLayers8.x主要更新内容就是webgl…

GB28181学习(十七)——基于jrtplib实现tcp被动和主动发流

前言 GB/T28181-2022实时流的传输方式介绍:https://blog.csdn.net/www_dong/article/details/134255185 基于jrtplib实现tcp被动和主动收流介绍:https://blog.csdn.net/www_dong/article/details/134451387 本文主要介绍下级平台或设备发流功能&#…

生活如果真能像队列一样的话

生活如果真能像队列一样,那该多好啊。 —————————————————————————————————————————— 背包,队列 可以先看他们的API:都含有一个无参构造函数,添加单个元素的方法,测试集合…

基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码

基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于材料生成优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…

ROSNS3(一)

https://github.com/malintha/rosns3 第一步:clone和构建rosns3客户端 第二步:运行 最详细的ubuntu 安装 docker教程 - 知乎 1. unable to find source space /home/muta/src 解决方法: 将副将将碰到的bug,解决方法_#include &…

Python从零开始快速搭建一个语音对话机器人

文章目录 01-初心缘由02-准备工作03-语音机器人的搭建思路04-语音生成音频文件05-音频文件转文字STT06-与图灵机器人对话07-文字转语音08-语音对话机器人的完整代码09-结束语10-有问必答关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学…

SSH连接远程服务器报错:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED 解决方法

一.错误描述 报错信息里提示了路径信息/root/.ssh/known_hosts:20 二.解决方案 方法一 输入以下指令: ssh-keygen -R XXX(需要连接远程服务器的ip) 按照我的例子ip:10.165.7.136,会返回以下信息: 重新尝试连接: 输…

规划类3d全景线上云展馆帮助企业轻松拓展海外市场

科技3D线上云展馆作为一种基于VR虚拟现实和互联网技术的新一代展览平台。可以在线上虚拟空间中模拟真实的展馆,让观众无需亲自到场,即可获得沉浸式的参观体验。通过这个展馆,您可以充分、全面、立体展示您的产品、服务以及各种创意作品&#…

2023年【安全生产监管人员】考试题及安全生产监管人员找解析

题库来源:安全生产模拟考试一点通公众号小程序 安全生产监管人员考试题参考答案及安全生产监管人员考试试题解析是安全生产模拟考试一点通题库老师及安全生产监管人员操作证已考过的学员汇总,相对有效帮助安全生产监管人员找解析学员顺利通过考试。 1、…

数据结构-栈的实现

1.栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈&…

Matlab群体智能优化算法之海象优化算法(WO)

文章目录 一、灵感来源二、算法的初始化三、GTO的数学模型Phase1:危险信号和安全信号Phase2:迁移(探索)Phase3:繁殖(开发) 四、流程图五、伪代码六、算法复杂度七、WO搜索示意图八、实验分析和结…

FreeRTOS列表和列表项

FreeRTOS内核调度使用了大量的列表(list)和列表项(listitem)数据结构。它的源码中涉及到很多列表的操作,对于FreeRTOS来说,列表就是它最基础的一部分,列表被用作FreeRTOS调度器使用,…

力扣.面试题 04.06. 后继者(java 树的中序遍历)

Problem: 面试题 04.06. 后继者 文章目录 题目描述思路解题方法复杂度Code 题目描述 设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。 如果指定节点没有对应的“下一个”节点,则返回null。 思路 由于题…

Python开发运维:Celery连接Redis

目录 一、理论 1.Celery 二、实验 1.Windows11安装Redis 2.Python3.8环境中配置Celery 三、问题 1.Celery命令报错 2.执行Celery命令报错 3.Win11启动Celery报ValueErro错误 一、理论 1.Celery (1) 概念 Celery是一个基于python开发的分布式系统,它是简单…

JSP内置对象

一、request对象 1、访问请求参数 2、在作用域中管理属性 3、获取Cookie 4、解决中文乱码 5、获取客户端信息 6、显示国际化信息 是一个javax.servlet.http.HttpServletRequest对象 request封装了用户浏览器提交的信息,因此可以调用相应的方法可以获取这些封…

优先经验回放(prioritized experience replay)

prioritized experience replay 思路 优先经验回放出自ICLR 2016的论文《prioritized experience replay》。 prioritized experience replay的作者们认为,按照一定的优先级来对经验回放池中的样本采样,相比于随机均匀的从经验回放池中采样的效率更高&…

UML建模图文详解教程——类图

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl本文参考资料:《UML面向对象分析、建模与设计(第2版)》吕云翔,赵天宇 著 类图概述 类图用来描述系统内各种实体的类型以及不同…

Unsupervised MVS论文笔记

Unsupervised MVS论文笔记 摘要1 引言2 相关工作3 实现方法 Tejas Khot and Shubham Agrawal and Shubham Tulsiani and Christoph Mertz and Simon Lucey and Martial Hebert. Tejas Khot and Shubham Agrawal and Shubham Tulsiani and Christoph Mertz and Simon Lucey and …