AI大模型探索之路-训练篇10:大语言模型Transformer库-Tokenizer组件实践

系列篇章💥

AI大模型探索之路-训练篇1:大语言模型微调基础认知
AI大模型探索之路-训练篇2:大语言模型预训练基础认知
AI大模型探索之路-训练篇3:大语言模型全景解读
AI大模型探索之路-训练篇4:大语言模型训练数据集概览
AI大模型探索之路-训练篇5:大语言模型预训练数据准备-词元化
AI大模型探索之路-训练篇6:大语言模型预训练数据准备-预处理
AI大模型探索之路-训练篇7:大语言模型Transformer库之HuggingFace介绍
AI大模型探索之路-训练篇8:大语言模型Transformer库-预训练流程编码体验
AI大模型探索之路-训练篇9:大语言模型Transformer库-Pipeline组件实践


目录

  • 系列篇章💥
  • 前言
  • 一、Tokenizer概览
  • 二、Tokenizer的工作原理
  • 三、Tokenizer的使用方法
    • 1、加载与保存
    • 2、句子分词
    • 3、查看词典
    • 4、索引转换
    • 5、填充与截断
    • 6、其他输入部分
    • 7、快速调用方式
  • 四、Fast/Slow Tokenizer
  • 五、自定义Tokenizer
  • 六、Tokenizer与模型训练
  • 总结


前言

在自然语言处理(NLP)的世界里,文本数据的处理和理解是至关重要的一环。为了使得计算机能够理解和处理人类的自然语言,我们需要将原始的、对人类可读的文本转化为机器可以理解的格式。这就是Tokenizer,或者我们常说的分词器,发挥作用的地方。

一、Tokenizer概览

官网API地址:https://huggingface.co/docs/transformers/main_classes/tokenizer
Tokenizer是自然语言处理中的一个核心组件,它的主要功能是将原始文本转换为机器学习模型能够处理的格式。这一过程看似简单,实则包含了许多复杂且精细的步骤。在深度学习中的Transformer架构及其衍生模型中,Tokenizer的工作流程通常包括两个关键步骤:
1)首先,是文本分解。这一步的目的是将原始的、连续的文本分割成更细的粒度单元,这些单元可以是单词级别,也可以是子词级别,甚至是字符级别。这一步骤的目标是将文本分解为可以被模型理解并处理的基本单元。
2)其次,是编码映射。这一步的目标是将这些基本单元转换为模型可以理解的数值形式,最常见的形式是整数序列。这样,我们就可以将这些数值输入到模型中,让模型进行学习和预测。
在接下来的内容中,我们将详细探讨Tokenizer的工作原理,以及如何在实际的自然语言处理任务中使用Tokenizer。

二、Tokenizer的工作原理

Tokenizer的工作原理涉及:
1)文本分解:将文本分解为更小的单元。
2)词汇表:使用词汇表将文本单元映射到数值ID。
3)特殊标记:添加如[CLS]、[SEP]等特殊标记,以适应模型的特定需求。
在序列标注任务中,特殊标记帮助模型识别序列的开始和结束。

# 展示特殊标记的添加
sequence = "Here is an example sequence."
encoded_sequence = tokenizer(sequence, add_special_tokens=True)
print(encoded_sequence)

三、Tokenizer的使用方法

Tokenizer的使用流程一般遵循以下步骤:
1)导入Tokenizer库:从NLP库(例如Hugging Face的transformers)导入Tokenizer类。
2)加载预训练Tokenizer:通过指定模型名称加载预训练的Tokenizer实例。
3)文本转换:将文本数据输入Tokenizer进行编码转换。
4)获取编码输出:Tokenizer输出编码后的数据,通常包括:
-输入ID:转换后的整数序列,用于模型输入。
-注意力掩码(Attention Mask):标识哪些输入ID是有效内容,哪些是填充(padding)。
-类别ID(Token Type IDs):在某些任务中区分句子对的两个不同句子。
代码示例:

下面是一个使用Tokenizer的代码示例:

from transformers import AutoTokenizer# 加载预训练的Tokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")# 待处理的文本
text = "Transformers are the core of modern NLP tasks."# 使用Tokenizer进行编码
encoded_input = tokenizer(text, return_tensors='pt')# 访问编码结果
input_ids = encoded_input['input_ids']
attention_mask = encoded_input['attention_mask']

Tokenizer的基本使用

from transformers import AutoTokenizer
sen = "吃葡萄不吐葡萄皮!"

1、加载与保存

1)加载模型

# 从HuggingFace加载,输入模型名称,即可加载对于的分词器
tokenizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese")
tokenizer

输出结果:

BertTokenizerFast(name_or_path='uer/roberta-base-finetuned-dianping-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=True, 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),
}

2)保存模型

# tokenizer 保存到本地
tokenizer.save_pretrained("./roberta_tokenizer")
('./roberta_tokenizer/tokenizer_config.json','./roberta_tokenizer/special_tokens_map.json','./roberta_tokenizer/vocab.txt','./roberta_tokenizer/added_tokens.json','./roberta_tokenizer/tokenizer.json')

会自动在同层级目录roberta_tokenizer中存放下载下来的模型
3)从本地加载模型

# 从本地加载tokenizer
tokenizer = AutoTokenizer.from_pretrained("./roberta_tokenizer/")
tokenizer

输出结果:

BertTokenizerFast(name_or_path='./roberta_tokenizer/', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=True, 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),
}

2、句子分词

tokens = tokenizer.tokenize(sen)
tokens

输出:

['吃', '葡', '萄', '不', '吐', '葡', '萄', '皮', '!']

3、查看词典

tokenizer.vocab

输出如下
在这里插入图片描述

查看词典大小

tokenizer.vocab_size

21128

4、索引转换

1)将词序列转换为id序列

ids = tokenizer.convert_tokens_to_ids(tokens)
ids

输出:

[1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106]

2)将id序列转换为token序列

tokens = tokenizer.convert_ids_to_tokens(ids)
tokens

输出:

['吃', '葡', '萄', '不', '吐', '葡', '萄', '皮', '!']

3)将token序列转换为string

str_sen = tokenizer.convert_tokens_to_string(tokens)
str_sen

输出:

'吃 葡 萄 不 吐 葡 萄 皮!'

4)更便捷的实现方式
将字符串转换为id序列,又称之为编码

ids = tokenizer.encode(sen, add_special_tokens=True)
ids

输出:

[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102]

将id序列转换为字符串,又称之为解码

str_sen = tokenizer.decode(ids, skip_special_tokens=False)
str_sen

输出:

'[CLS] 吃 葡 萄 不 吐 葡 萄 皮! [SEP]'

5、填充与截断

1)填充

ids = tokenizer.encode(sen, padding="max_length", max_length=15)
ids

输出:

[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0]

2)截断

ids = tokenizer.encode(sen, max_length=5, truncation=True)
ids

输出:

[101, 1391, 5868, 5843, 102]

6、其他输入部分

ids = tokenizer.encode(sen, padding="max_length", max_length=15)
ids

输出:

[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0]

查看其他部分内容

attention_mask = [1 if idx != 0 else 0 for idx in ids]
token_type_ids = [0] * len(ids)
ids, attention_mask, token_type_ids

输出:

([101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 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, 0, 0])

7、快速调用方式

1)简化调用
简化调用:这是Tokenizer对象的直接调用,它通常是一个简化的方法,提供了基本的编码功能。
参数限制:此方法的参数选项可能较少,只包括一些常用的参数,如padding和max_length。
适用场景:适用于大多数标准情况,当需要执行常规的编码任务时,可以使用此方法。

inputs = tokenizer.encode_plus(sen, padding="max_length", max_length=15)
inputs

输出:

{'input_ids': [101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]}

2)增强调用
增强功能:encode_plus方法提供了更多的功能和更细粒度的控制,包括对分词、编码、填充、截断等过程的额外配置。
返回值:encode_plus方法通常返回一个字典,包含了一系列的输出,如输入ID、注意力掩码、标记类型ID等,这些输出可以直接用于模型的输入。
参数丰富:此方法允许用户指定更多的参数,如return_tensors(指定返回张量类型)、return_token_type_ids(返回标记类型ID)、return_attention_mask(返回注意力掩码)等。
适用场景:当你需要更细致地控制文本编码过程,或者需要额外的信息(如注意力掩码或标记类型ID)时,使用encode_plus方法。

inputs = tokenizer(sen, padding="max_length", max_length=15)
inputs

输出:

{'input_ids': [101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 106, 102, 0, 0, 0, 0], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]}

8、处理batch数据

sens = ["吃葡萄不吐葡萄皮","不吃葡萄到吐葡萄皮","顺势而为"]
res = tokenizer(sens)
res

输出:

{'input_ids': [[101, 1391, 5868, 5843, 679, 1402, 5868, 5843, 4649, 102], [101, 679, 1391, 5868, 5843, 1168, 1402, 5868, 5843, 4649, 102], [101, 7556, 1232, 5445, 711, 102]], '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]], '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]]}

批处理可以很大层度提升我们的处理性能

%%time
# 单条循环处理
for i in range(1000):tokenizer(sen)

CPU times: user 45.3 ms, sys: 0 ns, total: 45.3 ms
Wall time: 44.6 ms

%%time
# 处理batch数据
res = tokenizer([sen] * 1000)

CPU times: user 27.7 ms, sys: 15.6 ms, total: 43.2 ms
Wall time: 7.68 ms

四、Fast/Slow Tokenizer

在Hugging Face的transformers库中,Tokenizer分为两种类型:Fast Tokenizer和Slow Tokenizer。
1)Slow Tokenizer:通常是用Python编写的,速度较慢,但在所有环境中都能保证一致性和可移植性。
2)Fast Tokenizer:使用Rust编写,并通过PyTorch的C++扩展或Python的C扩展提供,速度非常快,尤其是在处理大量数据时。Fast Tokenizers提供了与Slow Tokenizers相同的功能,但速度更快。

选择使用哪种Tokenizer取决于具体的需求。如果对性能要求极高,或者需要处理大量数据,推荐使用Fast Tokenizer。如果需要确保代码的可移植性,或者在性能要求不是非常关键的场景下,可以使用Slow Tokenizer。

在transformers库中,AutoTokenizer类会自动选择Fast Tokenizer(如果可用),以提供最佳性能。如果需要显式选择Tokenizer类型,可以使用模型的特定Tokenizer类,如BertTokenizer或RobertaTokenizer。
fast_tokenizer 使用查看

sen = "吃葡萄不吐葡萄皮!"
fast_tokenizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese")
fast_tokenizer

输出结果:

BertTokenizerFast(name_or_path='uer/roberta-base-finetuned-dianping-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, is_fast=True, 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),
}

slow_tokenizer 使用查看

slow_tokenizer = AutoTokenizer.from_pretrained("uer/roberta-base-finetuned-dianping-chinese", use_fast=False)
slow_tokenizer
BertTokenizer(name_or_path='uer/roberta-base-finetuned-dianping-chinese', vocab_size=21128, model_max_length=1000000000000000019884624838656, 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),
}

fast_tokenizer 批量执行耗时

%%time
# 处理batch数据
res = fast_tokenizer([sen] * 10000)

CPU times: user 323 ms, sys: 146 ms, total: 468 ms
Wall time: 172 ms

slow_tokenizer 批量执行耗时

%%time
# 处理batch数据
res = slow_tokenizer([sen] * 10000)

CPU times: user 1.1 s, sys: 15.8 ms, total: 1.12 s
Wall time: 1.12 s

五、自定义Tokenizer

用户可以根据特定需求定制Tokenizer:
1)自定义词汇表:创建特定领域的词汇表。
2)自定义规则:添加自定义分词规则以适应特定场景。

实践案例:在医疗领域的文本处理中,自定义Tokenizer能够识别专业术语。
工具和资源:Hugging Face的transformers库允许用户通过继承和修改现有Tokenizer类来创建自定义Tokenizer。

代码样例:

from transformers import BertTokenizerFastclass CustomBertTokenizer(BertTokenizerFast):def __init__(self, vocab_file, **kwargs):super().__init__(vocab_file=vocab_file, **kwargs)# 自定义逻辑...# 假设已有自定义词汇表
custom_tokenizer = CustomBertTokenizer(vocab_file="path_to_vocab.txt")
encoded_custom = custom_tokenizer("Customizing Tokenizer is flexible.", return_tensors="pt")
print(encoded_custom)

六、Tokenizer与模型训练

Tokenizer在模型训练中的作用包括:
1)数据预处理:将训练数据转换为模型可处理的格式。
2)与模型整合:确保Tokenizer与模型的输入层完全兼容。

实践案例:在训练一个自定义文本分类模型时,需要确保Tokenizer的输出与模型的输入层匹配。
工具和资源:使用PyTorch或TensorFlow框架,可以方便地将Tokenizer集成到模型训练流程中。

代码样例:

# 导入必要的类:从transformers库中导入BertForSequenceClassification(用于序列分类的BERT模型),Trainer(训练器类),和TrainingArguments(训练参数类)
from transformers import BertForSequenceClassification, Trainer, TrainingArguments#初始化模型:使用BertForSequenceClassification类创建一个序列分类模型实例。这个模型是基于BERT的,并且是预训练好的,我们通过from_pretrained方法加载它。num_labels参数指定了分类任务的标签数量。
model = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)# 准备数据集
# ...
#定义训练参数:TrainingArguments类用于定义训练过程中的各种参数,如输出目录output_dir,训练轮数num_train_epochs,每个设备的训练批次大小per_device_train_batch_size,预热步数warmup_steps,权重衰减weight_decay,以及日志目录logging_dir。
training_args = TrainingArguments(output_dir="./results",num_train_epochs=3,per_device_train_batch_size=16,warmup_steps=500,weight_decay=0.01,logging_dir="./logs",
)
# 初始化Trainer:Trainer类负责执行模型的实际训练。我们传入模型实例、训练参数和Tokenizer。train_dataset是一个包含训练数据的PyTorch数据集对象,这里省略了其定义和准备过程。
trainer = Trainer(model=model,args=training_args,train_dataset=train_dataset,tokenizer=tokenizer
)
#执行训练:调用trainer.train()方法开始训练
trainer.train()

总结

Tokenizer是Transformer模型不可或缺的一部分,它直接影响模型输入的质量和模型的性能。正确选择和使用Tokenizer对于实现高效的NLP任务至关重要。通过上述实践,我们可以看到Tokenizer不仅需要适应特定的模型架构,还要满足特定任务的需求,并考虑到性能优化和可定制性。

在这里插入图片描述

🎯🔖更多专栏系列文章:AIGC-AI大模型探索之路

如果文章内容对您有所触动,别忘了点赞、⭐关注,收藏!加入我,让我们携手同行AI的探索之旅,一起开启智能时代的大门!

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

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

相关文章

DS:顺序表、单链表的相关OJ题训练

欢迎各位来到 Harper.Lee 的学习小世界! 博主主页传送门:Harper.Lee的博客主页 想要一起进步的uu可以来后台找我交流哦! 在DS:单链表的实现 和 DS:顺序表的实现这两篇文章中,我详细介绍了顺序表和单链表的…

使用LinkAI创建AI智能体,并快速接入到微信/企微/公众号/钉钉/飞书

​ LinkAI 作为企业级一站式AI Agent 智能体搭建与接入平台,不仅为用户和客户提供能够快速搭建具备行业知识和个性化设定的 AI 智能体的能力;还基于企业级场景提供丰富的应用接入能力,让智能体不再是“玩具”,而是真正能够落地应用…

PHP的数组练习实验

实 验 目 的 掌握索引和关联数组,以及下标和元素概念; 掌握数组创建、初始化,以及元素添加、删除、修改操作; 掌握foreach作用、语法、执行过程和使用; 能应用数组输出表格和数据。 任务1:使用一维索引数…

uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之使用jar包插件

前言 如果你不会编写安卓插件,你可以先看看我之前零基础的文章(uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之零基础编写安卓插件), 我们使用第三方包,jar包编写安卓插件 开始 把依赖包,放到某个模块的/libs目录(myTestPlug/libs) 还要到build…

R语言的学习—5—多元数据直观表示

1、数据读取 ## 数据整理 d3.1read.xlsx(adstats.xlsx,d3.1,rowNamesT);d3.1 #读取adstats.xlsx表格d3.1数据 barplot(apply(d3.1,1,mean)) #按行做均值条形图 barplot(apply(d3.1,1,mean),las3) barplot(apply(d3.1,2,mean)) #按列做均值图条形图 barplot(a…

C语言数据结构 ---- 单链表实现通讯录

今日备忘录: "折磨我们的往往是想象, 而不是现实." 目录 1. 前言2. 通讯录的功能3. 通讯录的实现思路5. 效果展示6. 完整代码7. 总结 正文开始 1. 前言 顺表实现通讯录: 点击~ 顺序表实现通讯录 在日常生活中,我们经常需要记录和管理大量的联系人信息&…

【研发管理】产品经理知识体系-组合管理

导读:新产品开发的组合管理是一个重要的过程,它涉及到对一系列新产品开发项目进行策略性选择、优先级排序、资源分配和监控。这个过程旨在确保企业能够最大化地利用有限的资源,以实现其战略目标。 目录 1、组合管理、五大目标 2、组合管理的…

第74天:漏洞发现-Web框架中间件插件BurpSuite浏览器被动主动探针

目录 思维导图 前置知识 案例一:浏览器插件-辅助&资产&漏洞库-Hack-Tools&Fofa_view&Pentestkit 案例二: BurpSuite 插件-被动&特定扫描-Fiora&Fastjson&Shiro&Log4j 思维导图 前置知识 目标: 1. 用…

基于springboot实现公司日常考勤系统项目【项目源码+论文说明】

基于springboot实现公司日常考勤系统演示 摘要 目前社会当中主要特征就是对于信息的传播比较快和信息内容的安全问题,原本进行办公的类型都耗费了很多的资源、传播的速度也是相对较慢、准确性不高等许多的不足。这个系统就是运用计算机软件来完成对于企业当中出勤率…

数据结构-链表OJ

1.删除链表中等于给定值 val 的所有结点。 . - 力扣(LeetCode) 思路一:遍历原链表,将值为val的节点释放掉 思路二:创建一个新链表,将值不为val的节点尾插到新链表中 /*** Definition for singly-linked …

2024年五一数学建模竞赛C题论文首发

基于随机森林的煤矿深部开采冲击地压危险预测 摘要 煤炭作为中国重要的能源和工业原料,其开采活动对国家经济的稳定与发展起着至关重要的作用。本文将使用题目给出的数据探索更为高效的数据分析方法和更先进的监测设备,以提高预警系统的准确性和可靠性…

智能消费记账|基于SSM+vue的大学生智能消费记账系统(源码+数据库+文档)

智能消费记账目录 基于SSMvue的大学生智能消费记账系统 一、前言 二、系统设计 三、系统功能设计 1 用户列表 2 预算信息管理 3 预算类型管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍&#xff1…

代码随想录算法训练营DAY48|C++动态规划Part9|121.买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III

文章目录 121.买卖股票的最佳时机思路CPP代码 122.买卖股票的最佳时机II思路CPP代码 123.买卖股票的最佳时机III思路CPP代码 121.买卖股票的最佳时机 力扣题目链接 文章讲解:121.买卖股票的最佳时机 视频讲解:动态规划之 LeetCode:121.买卖股…

Android 音视频基础知识

本系列文章会介绍两个 Android NDK Demo,拉流端会实现一个基于 FFmpeg 的视频播放器 Demo,推流端会实现一个视频直播 Demo,当然在做 Demo 之前会介绍音视频的基础知识。以下是本系列文章的目录: Android 音视频基础知识 Android 音…

抢先体验:MacOS成功安装PHP8.4教程

根据官方消息,PHP 8.4将于2024年11月21日发布。它将通过三个 alpha 版本、三个 beta 版本和六个候选版本进行测试。 这次的重大更新将为PHP带来许多优化和强大的功能。我们很高兴能够引导您完成最有趣的更新升级,这些更改将使我们能够编写更好的代码并构…

Mac brew安装Redis之后更新配置文件的方法

安装命令 brew install redis 查看安装位置命令 brew list redis #查看redis安装的位置 % brew list redis /usr/local/Cellar/redis/6.2.5/.bottle/etc/ (2 files) /usr/local/Cellar/redis/6.2.5/bin/redis-benchmark /usr/local/Cellar/redis/6.2.5/bin/redis-check-ao…

WebAssembly学习记录

1.WebAssembly 1.1 指令集 概念:二进制编码集合。 依据计算机组成原理和计算机概论,指令集是一组二进制编码。 作用:控制硬件。 这些二进制指令直接作用于硬件电路,控制硬件完成指定操作。 例如:控制数据进入某个寄存…

【unocss】自用

unocss中文官网1 不知道简写的可以在这里查 第一步 npm install -D unocss第二步 // vite.config.ts import UnoCSS from unocss/vite import { defineConfig } from viteexport default defineConfig({plugins: [UnoCSS()] })// main.ts import virtual:uno.css第三步 在…

基于Springboot的在线博客网站

基于SpringbootVue的在线博客网站的设计与实现 开发语言:Java数据库:MySQL技术:SpringbootMybatis工具:IDEA、Maven、Navicat 系统展示 用户登录 首页 博客标签 博客分类 博客列表 图库相册 后台登录 后台首页 用户管理 博客标…

Android 设置头像 - 裁剪及圆形头像

书接上文 Android 设置头像 - 相册拍照,通过相册和照片的设置就可以获取到需要的头像信息,但是在通常情况下,我们还想要实现针对头像的裁剪功能和圆形头像功能。 先上截图: 图像裁剪 通常裁剪可以分为程序自动裁剪和用户选择裁剪…