从零开始实现大语言模型(二):文本数据处理
1. 前言
神经网络不能直接处理自然语言文本,文本数据处理的核心是做tokenization,将自然语言文本分割成一系列tokens。
本文介绍tokenization的基本原理,OpenAI的GPT系列大语言模型使用的tokenization方法——字节对编码(BPE, byte pair encoding),并构建训练大语言模型的Dataset
及DataLoader
。
2. Tokenization
Tokenization的目标是将自然语言文本分割成一系列tokens。在英文自然语言处理领域,最常用的tokenization方法是将英文文本分割成单词及标点符号。在中文自然语言处理领域,往往会将一个字或一个标点符号作为一个token。
如下面的代码所示,可以使用Python自带的正则表达式库re
将英文文本分割成不同的单词及标点符号:
import resentence = "Hello, world. This, is a test."
tokens = re.split(r'([,.?_!"()\']|\s)', sentence)
tokens = [item for item in tokens if item.strip()]
print(tokens)
执行上面代码,打印结果如下:
['Hello', ',', 'world', '.', 'This', ',', 'is', 'a', 'test', '.']
上面的tokenization示例代码删除掉了文本中的空格。在实际应用中,也可以将空格视为一个单独的字符。删除空格可以显著减少分割文本产生的tokens数量,减少内存消耗,降低计算资源需求。但是在构建代码生成模型等应用场景中,必须将文本中的空格视为一个单独的字符,并编码成相应的token。
3. 将Token转换成数字ID
绝大部分处理自然语言文本的神经网络都包含Embedding层(embedding layer)torch.nn.Embedding
。Embedding层存储了不同tokens对应的embedding向量,输入一系列tokens对应的索引列表,Embedding层输出相应tokens对应的embedding向量。
对自然语言文本做tokenization,将文本分割成不同的tokens。Tokens不能直接输入神经网络的Embedding层,需要将tokens转换成数字ID(token ID)。将tokens转换成数字ID首先要构造词汇表(vocabulary)。词汇表定义了不同tokens与数字索引的一一对应关系,可以使用词汇表将分割自然语言文本产生的一系列tokens转换成数字ID列表,也可以通过词汇表将数字ID列表还原成自然语言文本。
如下图所示,构造词汇表需要对训练数据集中全部文本数据做tokenization,获取所有不同的tokens,并一一添加到字典(dict)中。字典的key为训练数据集中的不同tokens,字典的value为相应token添加到字典中的顺序。
可以使用如下代码构造词汇表:
diff_tokens = sorted(set(tokens))
vocabulary = {token: idx for idx, token in enumerate(diff_tokens)}for item in vocabulary.items():print(item)
执行上面代码,打印结果如下:
(',', 0)
('.', 1)
('Hello', 2)
('This', 3)
('a', 4)
('is', 5)
('test', 6)
('world', 7)
4. 常用的特殊Tokens
上述对训练数据集中全部文本数据做tokenization,获取所有不同的tokens,并构造将token映射成数字ID的词汇表的文本数据处理方法无法将训练数据集中不存在的token转换成数字ID。在自然语言处理项目实践中,往往会在词汇表中增加一些特殊tokens,以增强模型理解自然语言文本结构等信息的能力。常用的特殊tokens如下所示:
<|unk|>
(unknown):该token一般用于表示不在构建的词汇表中的单词或字符<|endoftext|>
(end of text):该token一般用于分割两个不相关的文本。训练大语言模型的文本数据一般由许多文本拼接而成,不同文本之间使用<|endoftext|>
分隔,使大语言模型可以理解训练数据的组织方式[BOS]
(beginning of sequence):该token通常位于一段文本的开头,用于给模型提供输入文本的组织结构信息[EOS]
(end of sequence):该token通常位于一段文本的末尾,用于拼接多个不相关的文本,其作用与<|endoftext|>
类似[PAD]
(padding):训练大语言模型每次会使用一个batch的训练样本,构成一个张量(tensor),张量内所有训练样本token数量必须相同。如果batch中包含不同长度的训练样本,一般会使用[PAD]
将较短的训练样本填充至batch中最长训练样本长度
GPT-2系列大语言模型只在词汇表中增加了
<|endoftext|>
这个特殊token。其使用了一种被称为字节对编码(byte pair encoding)的tokenization方法,该方法分割文本不会产生词汇表不包含的新token。
可以将上述tokenization方法封装成一个Tokenizer
类,其中encode
函数用于将自然语言文本分割成一系列tokens,并转换成数字ID列表。decode
函数用于将数字ID列表还原成自然语言文本:
class Tokenizer:def __init__(self, vocabulary):self.str_to_int = vocabularyself.int_to_str = list(vocabulary.keys())def encode(self, text):preprocessed = re.split(r'([,.?_!"()\']|\s)', text)preprocessed = [item for item in preprocessed if item.strip()]preprocessed = [item if item in self.str_to_int else "<|unk|>" for item in preprocessed]ids = [self.str_to_int[s] for s in preprocessed]return idsdef decode(self, ids):text = " ".join([self.int_to_str[i] for i in ids])text = re.sub(r'\s+([,.?!"()\'])', r'\1', text)return text
可以使用如下代码实例化Tokenizer
类对象,并调用encode
及decode
函数,打印示例文本对应的tokens及相应文本内容:
vocabulary.update({"<|unk|>": len(vocabulary), "<|endoftext|>": len(vocabulary) + 1})
text = "Hello world. <|endoftext|> This is a test dataset."tokenizer = Tokenizer(vocabulary)
print(tokenizer.encode(text))
print(tokenizer.decode(tokenizer.encode(text)))
执行上面代码,打印结果如下:
[2, 7, 1, 9, 3, 5, 4, 6, 8, 1]
Hello world. <|endoftext|> This is a test <|unk|>.
5. 字节对编码(Byte Pair Encoding)
字节对编码(BPE)是大语言模型GPT-2、GPT-3以及ChatGPT使用的tokenization方法。BPE会将所有单个字符(如"a","b"等)以及频繁出现的字符组合(subtoken)添加到词汇表中(如字符"d"和"e"的组合"de"在许多英文单词中很常见,因此会将"de"添加到词汇表中作为一个token)。
如下图所示,BPE可以将不在其词汇表中的单词分解成粒度更小的字符或字符组合,因此OpenAI的GPT系列模型不用<|unk|>
等特殊token来处理不在其词汇表中的单词。
OpenAI开源了其使用Rust语言实现的非常高效的BPE算法tiktoken库,可以使用如下命令安装tiktoken:
!pip install tiktoken==0.5.1
使用tiktoken.encoding_for_model
方法创建tokenizer对象,加载大语言模型GPT-2所使用的词汇表,并调用encode
及decode
函数,测试BPE算法:
import tiktokentokenizer = tiktoken.encoding_for_model("gpt2")
text = "Hello, do you like tea? <|endoftext|> In the sunlit terraces of someunknownPlace."
integers = tokenizer.encode(text, allowed_special={"<|endoftext|>"})
print(integers)
strings = tokenizer.decode(integers)
print(strings)
执行上面代码,打印结果如下:
[15496, 11, 466, 345, 588, 8887, 30, 220, 50256, 554, 262, 4252, 18250, 8812, 2114, 286, 617, 34680, 27271, 13]
Hello, do you like tea? <|endoftext|> In the sunlit terraces of someunknownPlace.
创建tokenizer对象并加载大语言模型GPT-3.5所使用的词汇表,可以使用
tokenizer = tiktoken.encoding_for_model("gpt-3.5-turbo")
方法。加载大语言模型GPT-4所使用的词汇表,可以使用tokenizer = tiktoken.encoding_for_model("gpt-4")
。查看tiktoken的内部源码可知,大语言模型GPT-3.5及GPT-4所使用的词汇表完全相同。
6. 构建训练大语言模型的Dataset及DataLoader
6.1 大语言模型的训练样本
预训练大语言模型的数据集包含许多自然语言文档,不同文档的内容使用<|endoftext|>
拼接,构成一个非常长的文本字符串,使用上述BPE算法可以将该字符串转变成数字ID列表。
使用下一个token预测任务预训练大语言模型,从数字ID列表中随机抽取一个长度为context_len
的子列表输入大语言模型。大语言模型可以执行context_len
次下一个token预测任务,共预测输出context_len
个tokens。
如下图所示,假设context_len
等于8,则第一次下一个token预测任务大模型根据输入LLMs
预测learn
,第二次根据输入LLMs learn
预测to
。依此类推,第八次根据输入LLMs learn to predict one word at a
预测time
。
在实际训练大语言模型时,会输入长度为
context_len
的数字ID列表,每个数字对应一个token,而不是输入一个字符串。
因此可以使用如下规则构建训练大语言模型的训练样本。每个训练样本包含一个input-target pair
,其中:
- input:从训练数据集对应的数字ID列表中随机抽取的长度为
context_len
的子列表 - target:将input子列表向后移一个token对应的子列表,其中target中第 i i i个token ID为input中第 i − 1 i-1 i−1个token ID
理论上,长度为 n n n的token ID列表对应的训练数据集最多可以构造出 n − context_len + 1 n-\text{context\_len}+1 n−context_len+1个不同的训练样本。实际上,会使用stride
控制两个相邻训练样本之间的重叠tokens数量。如果 stride = = 1 \text{stride}==1 stride==1,则两个相邻的训练样本存在个 context_len − 1 \text{context\_len}-1 context_len−1重叠的tokens,第 i i i个训练样本的target
即为第 i + 1 i+1 i+1个训练样本的input
。如果 stride = = context_len \text{stride}==\text{context\_len} stride==context_len,则两个相邻训练样本之间不存在重叠的tokens。stride
越小,则可以构造出更多训练样本,但是模型更容易过拟合。stride
越大,模型越不容易过拟合,但是同一个训练数据集中构造的训练样本数会更少。
6.2 构建Dataset
Pytorch提供的高效组织及加载训练数据的模块由Dataset
及DataLoader
类构成,其中Dataset
类用于定义如何加载每个训练样本,DataLoader
类用于随机打乱训练数据集,并将训练数据集划分到不同的batch。
构建训练数据集对应的Dataset
,需要实现一个Dataset
的子类,并重写__init__
构造方法,__getitem__
方法,以及__len__
方法。__init__
方法用于初始化与访问训练数据相关的属性,如文件路径、数据库连接器等等。如果训练数据集不是特别大,可以在__init__
方法中将整个训练数据集全部读取到内存中。如果训练数据集特别大,无法一次全部加载进内存,一般只会在__init__
方法中初始化数据文件路径,后续实际要用到相应数据时才会去读取。__getitem__
中实现了根据索引返回相应训练样本的方法。在DataLoader
类的实例化对象获取一个batch的训练样本时,__getitem__
方法会被调用batch_size
次,共传入batch_size
个不同的索引,返回batch_size
个不同的训练样本,构成一个训练大语言模型的batch。__len__
方法定义了训练数据集的大小,假设__len__
方法返回训练数据集的大小为10,则每次调用__getitem__
方法传入的索引均为0-9
之间(包括0和9)的整数。
综上所述,可以使用如下代码构建训练大语言模型的Dataset
:
import os
import json
import randomimport torch
import tiktoken
from torch.utils.data import Datasetclass LLMDataset(Dataset):def __init__(self, data_path, file_index_dir, vocabulary, special_token_id, context_len, stride):self.tokenizer = tiktoken.encoding_for_model(vocabulary)self.special_token_id = special_token_idself.context_len = context_lenself.stride = strideif not os.path.exists(file_index_dir):os.makedirs(file_index_dir, exist_ok=True)file_index_path = os.path.join(file_index_dir, "file_index.json")if not os.path.isfile(file_index_path):support_file_paths = []for root, dirs, files in os.walk(data_path):for file in files:if file.endswith((".txt", ".md")):file_path = os.path.join(root, file)support_file_paths.append(file_path)random.shuffle(support_file_paths)file_indexes, total_tokens = dict(), 0for file_path in support_file_paths:with open(file_path, "rt", encoding="utf-8") as f:file_token = self.tokenizer.encode(f.read(), allowed_special=self.tokenizer.special_tokens_set)file_indexes[file_path] = [total_tokens, total_tokens + len(file_token)]total_tokens += len(file_token) + 1with open(file_index_path, "wt", encoding="utf-8") as f:json.dump(file_indexes, f, ensure_ascii=False)with open(file_index_path, "rt", encoding="utf-8") as f:self.file_index = json.load(f)max_token_index = list(self.file_index.values())[-1][1]self.num_sample = (max_token_index - context_len) // stride + 1def __getitem__(self, index):index_files = self._index_files(index)start_index = index * self.stridestop_index = start_index + self.context_lensample = []for file_path in index_files:index_range = self.file_index[file_path]file_start, file_end = 0, index_range[1] - index_range[0] + 1if index_range[0] <= start_index <= index_range[1]:file_start = start_index - index_range[0]if index_range[0] <= stop_index <= index_range[1]:file_end = stop_index - index_range[0] + 1with open(file_path, "rt", encoding="utf-8") as f:tokens = self.tokenizer.encode(f.read(), allowed_special=self.tokenizer.special_tokens_set)tokens.append(self.special_token_id)sample.extend(tokens[file_start: file_end])return torch.tensor(sample[:-1]), torch.tensor(sample[1:])def __len__(self):return self.num_sampledef _index_files(self, index):index_files = []start_file, stop_file = None, Nonestart_index = index * self.stridestop_index = start_index + self.context_lenfor file_path, index_range in self.file_index.items():if index_range[0] <= start_index <= index_range[1]:start_file = file_pathif start_file is not None:index_files.append(file_path)if index_range[0] <= stop_index <= index_range[1]:stop_file = file_pathif stop_file is not None:breakreturn index_files
LLMDataset
类的__init__
方法中通过self.tokenizer = tiktoken.encoding_for_model(vocabulary)
初始化了将文本转换为token ID的BPE tokenizer。遍历整个训练数据集中的.txt
及.md
文件,初始化了一个key为文件路径,value为文件中全部token的[起始索引, 终止索引]
列表的字典self.file_index
,用于在__getitem__
方法中根据索引找到相应训练样本所在文件。初始化了记录训练数据集中可构造训练样本总数的变量self.num_sample
。
__getitem__
方法调用self._index_files(index)
函数,返回index
对应的训练样本所分布的文件路径。遍历文件路径列表index_files
,从各个文件中取出属于index
对应训练样本部分的tokens,并组合成训练样本sample
。
上述训练大语言模型的
LLMDataset
并没有将训练数据一次全部加载进内存,只在__init__
方法中记录了训练数据文件,并在调用__getitem__
方法时根据index
实时读取所需文件,构造训练样本。这种方法可以不用将全部训练数据加载进内存,但是需要耗费一定时间完成训练样本构造。构建训练大语言模型的DataLoader时,可以通过设置
num_workers
,使数据读取与模型训练并行进行。只要LLMDataset
中的训练数据构造效率不是特别慢,一般不会影响模型训练效率。
如果训练大语言模型的计算服务器集群内存足够大到可以将整个训练数据集一次性全部加载进内存,构建训练大语言模型的Dataset
时,可以在__init__
方法中读取训练数据集中的全部.txt
及.md
文档,将不同文档的内容使用<|endoftext|>
拼接,构成一个非常长的文本字符串,并使用BPE tokenizer分别将该字符串转变成token ID列表,存入计算服务器集群的内存。
虽然可以在构建训练大语言模型的DataLoader时,通过设置num_workers
,使数据读取与模型训练并行进行,一定程度上避免训练数据构造效率对模型训练的影响。但是在内存资源充足的情况下,直接在__init__
方法中将整个训练数据集全部加载进内存,从而提升__getitem__
方法中根据指定index构造训练数据的速度,可以使模型训练的整体效率至少不比使用上面构建的Dataset
差。具体代码如下所示:
class LLMDataset(Dataset):def __init__(self, data_path, vocabulary, special_token_id, context_len, stride):self.context_len = context_lenself.stride = stridesupport_file_paths = []for root, dirs, files in os.walk(data_path):for file in files:if file.endswith((".txt", ".md")):file_path = os.path.join(root, file)support_file_paths.append(file_path)random.shuffle(support_file_paths)self.tokens = []tokenizer = tiktoken.encoding_for_model(vocabulary)for file_path in support_file_paths:with open(file_path, "rt", encoding="utf-8") as f:file_token = tokenizer.encode(f.read(), allowed_special=tokenizer.special_tokens_set)file_token.append(special_token_id)self.tokens.extend(file_token)self.num_sample = (len(self.tokens) - context_len - 1) // stride + 1def __getitem__(self, index):start_index = index * self.stridex = self.tokens[start_index: start_index + self.context_len]y = self.tokens[start_index + 1: start_index + self.context_len + 1]return torch.tensor(x), torch.tensor(y)def __len__(self):return self.num_sample
6.3 构建DataLoader
构建分batch读取训练数据的DataLoader
,只需要传入一个Dataset
对象,并实例化DataLoader
类对象。可以使用如下代码构建训练大语言模型的DataLoader
:
from torch.utils.data import DataLoaderbatch_size = 16
random_seed = 123torch.manual_seed(random_seed)dataset = LLMDataset(data_path="some_data_folder_path")
train_loader = DataLoader(dataset=dataset,batch_size=batch_size,shuffle=True,num_workers=4,drop_last=True
)
shuffle
参数用于控制是否随机打乱训练数据集。如果shuffle
设置为False
,DataLoader
会依据索引从小到大的顺序依次生成不同batch的训练数据。如果shuffle
设置为True
,DataLoader
会随机打乱所有训练样本的索引,并按照随机打乱后的索引顺序依次生成不同batch的训练数据。一般会将训练数据集对应DataLoader
的shuffle
参数设置为True
,确保不同batch的训练数据是独立同分布的。测试数据集对应DataLoader
的shuffle
参数一般会设置为False
,因为测试数据集中数据不被用于训练模型,保存数据测试顺序信息有助于分析数据测试结果。
通过torch.manual_seed(random_seed)
指定随机数种子,可以使DataLoader
在不同次训练流程中生成完全相同的训练样本索引随机排列,但是一次训练流程的对训练数据集的不同次迭代中,训练样本索引的随机排列会各不相同。设置随机数种子有助于神经网络训练结果复现,但是不会使得在训练过程中陷入重复的更新周期。
假设训练数据集共包含5个不同的训练样本,构建
DataLoader
时设置shuffle
参数为True
,则在一次训练流程的前3次对训练数据集的遍历过程中,访问训练数据的顺序可能如下(不同随机数种子会产生不同的访问顺序):
- 第一次遍历训练数据集的顺序:[3, 4, 1, 0, 2]
- 第二次遍历训练数据集的顺序:[2, 1, 0, 3, 4]
- 第三次遍历训练数据集的顺序:[1, 4, 0, 3, 2]
保持随机数种子不变,第二次执行训练代码,在第二次训练流程中的前3次对训练数据集的遍历顺序必定与上面的遍历顺序相同。
如果在训练大语言模型时程序异常中断,从保存的断点(checkpoint)处恢复训练环境,需要特别注意随机数种子的设置与变更。如果从某个batch对应的checkpoint恢复训练环境,只需要使用同一个随机数种子,并跳过前 k k k个已经训练的batch即可。如果从某个epoch对应的checkpoint处继续训练模型,需要变更随机数种子,确保新的一轮训练遍历训练数据集的顺序与上一次遍历训练数据集的顺序不一致。在训练大语言模型时,建议不同epoch使用不同的随机数种子,并记录随机数种子的使用顺序。
batch_size
是指训练大语言模型的一个batch中包含训练样本的数量。batch_size
越小,则训练大语言模型要求的显卡最大内存越小,但是会导致计算出的更新大语言模型的梯度方差较大,影响大语言模型训练时的收敛速度及模型最终效果。batch_size
的设置可以参考OpenAI训练GPT系列大语言模型的论文,或者设置成当前显卡内存资源允许的最大值。
在实际训练大语言模型时,训练数据集中的训练样本数量一般不太可能恰好构成整数个batch,最后一个batch很可能仅包含相对非常少的训练样本。在一个训练的epoch中,使用包含训练样本数量非常少的batch作为最后一个batch会引入一次噪声较大的更新梯度,影响训练大语言模型时的收敛效果。将drop_last
设置为True
,会将每个epoch中的最后一个batch丢弃,不参与模型参数更新。
num_workers
参数用于控制数据并行加载及预处理。如下图所示,在使用GPU训练大语言模型时,CPU不仅要与GPU交互处理深度学习模型参数调度等任务,还要加载及预处理训练数据。如果num_workers=0
,系统将使用主进程加载数据,数据处理与GPU任务调度时串行的,GPU在CPU加载及预处理训练数据时处于空闲状态,会明显降低模型训练速度及GPU利用率。如果num_workers
大于0,系统将启动多个工作进程并行加载及预处理训练数据,使主进程专注于GPU资源及训练任务调度。num_workers
必须根据系统计算资源及训练数据集情况来确定,根据实践经验,大部分情况下将num_workers
设置为4可以比较高效地利用系统计算资源。
7. 结束语
本文详细讲解了文本数据处理的方法,并构建了训练大语言模型的Dataset
及DataLoader
。请坐好站稳,我们将要去深入了解大语言模型的神经网络架构了!
相关文章:
![](https://img-blog.csdnimg.cn/direct/31d65b8211d44e1094ce705b858c0db8.png#pic_center)
从零开始实现大语言模型(二):文本数据处理
1. 前言 神经网络不能直接处理自然语言文本,文本数据处理的核心是做tokenization,将自然语言文本分割成一系列tokens。 本文介绍tokenization的基本原理,OpenAI的GPT系列大语言模型使用的tokenization方法——字节对编码(BPE, byte pair en…...
![](https://img-blog.csdnimg.cn/direct/b860da2b49c74559bae913d1bad27cdf.png)
生物分子生物学实验过程的自动化与智能监控系统设计
开题报告:生物分子生物学实验过程的自动化与智能监控系统设计 一、引言 随着生物科学技术的飞速发展,生物分子生物学实验在科研、医疗、农业等领域的应用日益广泛。然而,传统的生物分子生物学实验过程大多依赖于人工操作,存在操…...
![](https://www.ngui.cc/images/no-images.jpg)
linux的shell脚本编程详解
Shell 脚本是一种用于自动化任务的脚本语言,在 Linux 和其他类 Unix 操作系统中非常流行。它通常用于任务自动化、系统管理和批处理。编写 Shell 脚本并使其自动化编译过程(例如使用 gcc 编译 C/C 程序)是一种常见的任务。 以下是一个详细的…...
![](https://img-blog.csdnimg.cn/direct/658fdfd8fcf84de089a69b07c82ddef6.png#pic_center)
Redis 7.x 系列【11】数据类型之位图(Bitmap)
有道无术,术尚可求,有术无道,止于术。 本系列Redis 版本 7.2.5 源码地址:https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 基本命令2.1 SETBIT2.2 GETBIT2.3 BITCOUNT2.4 BITPOS2.5 BITFIELD2.6 BITF…...
![](https://www.ngui.cc/images/no-images.jpg)
如何评定旅游卡的品质与服务?
评定旅游卡的品质与服务,可以从以下几个关键方面进行综合考量: 公司实力与资质:选择有实力、资质齐全的公司发行的旅游卡。查看公司背景,确认其是否长期稳定运营,是否具有旅游行业的专业资质,如旅行社许可证…...
![](https://img-blog.csdnimg.cn/img_convert/5778657dc247ab3d1c5406661158b700.jpeg)
适合学生暑假适用的护眼大路灯有哪些?五款好用护眼灯分享!
在护眼领域,护眼大路灯已经成为越来越多人的选择。这种具备实力的工具可以有效地缓解用眼疲劳、改善光线环境,是学生党和办公族的必备神器。然而,市面上的护眼大路灯品牌众多,如何选择一款性价比高、品质优良的产品呢?…...
![](https://www.ngui.cc/images/no-images.jpg)
linux服务器 部署jenkins
在 Linux 服务器上部署 Jenkins 通常包括以下几个步骤: 更新系统软件包: sudo apt update sudo apt upgrade 安装 Java: Jenkins 需要 Java 运行时环境。推荐使用 OpenJDK 11。 sudo apt install openjdk-11-jdk 添加 Jenkins 软件源并导入…...
![](https://img-blog.csdnimg.cn/img_convert/92fb174df2b2789a321166d88012d912.jpeg)
电商控价:系统监测的必要性与优势
在品牌的发展进程中,会遭遇各种各样的渠道问题,控价乃是其中颇为关键的一环。品牌进行控价的目的无疑是为了妥善治理低价链接,低价链接的发现途径可以是人工,也可以是系统。力维网络在为上百个品牌提供服务的过程中察觉到…...
![](https://www.ngui.cc/images/no-images.jpg)
港股下半年能恢复上涨趋势吗?
今日港股两大指数涨跌不一,早盘盘初恒指冲高,涨幅一度扩大至1%。截至收盘,香港恒生指数涨0.57%。板块方面,电力、航空、石油、内险股、燃料电池、环保等板块涨幅居前;互动媒体与服务、生物科技、汽车零部件、新经济概念…...
![](https://www.ngui.cc/images/no-images.jpg)
软件测试项目实战:银行贷款业务测试介绍-2
1、利息计算 正常利息贷款本金*借款天数*贷款年利率/360 罚息逾期本金*逾期天数*逾期年利率/360 复利逾期利息*逾期天数*逾期年利率/360 2、贷款五级分类 正常贷款:正常及逾期60天以内 关注贷款:逾期60天及以上,不足90天 次级贷款࿱…...
![](https://img-blog.csdnimg.cn/direct/b726a21dc58c4501a4615be0eb0d41a1.gif#pic_center)
如何将Hive表的分区字段插入PG表对应的时间戳字段?
文章目录 1、背景描述2、场景分析 1、背景描述 数据仓库的建设通常是为业务和决策服务的。在数仓开发的应用层阶段,BI可以直接从主题层/业务层取数,而前端需要根据具体的作图需求通过后端查询数据库 作图的指标需要根据主题层/业务层做查询计算…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring Boot与MyBatis的集成应用
Spring Boot与MyBatis的集成应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来聊聊Spring Boot与MyBatis的集成应用。MyBatis是一款优秀的持久层框…...
![](https://img-blog.csdnimg.cn/direct/08e29ccb3e4645bd9608397fdbd4cc68.png)
在昇腾服务器上使用llama-factory对baichuan2-13b模型进行lora微调
什么是lora微调 LoRA 提出在预训练模型的参数矩阵上添加低秩分解矩阵来近似每层的参数更新,从而减少适配下游任务所需要训练的参数。 环境准备 这次使用到的微调框架是llama-factory。这个框架集成了对多种模型进行各种训练的代码,少量修改就可使用。 …...
![](https://www.ngui.cc/images/no-images.jpg)
Kafka 管理TCP连接
生产者管理TCP连接 Kafka生产者程序概览 Kafka的Java生产者API主要的对象就是KafkaProducer。通常我们开发一个生产者的步骤有4步: 第1步:构造生产者对象所需的参数对象。 第2步:利用第1步的参数对象,创建KafkaProducer对象实例…...
![](https://img-blog.csdnimg.cn/direct/09b76d2772fd4df7b2c8e302133dd539.png)
electron教程(一)创建项目
一、方式① 根据官网描述将electron/electron-quick-start项目克隆下来并启动 electron/electron-quick-start地址: GitHub - electron/electron-quick-start: Clone to try a simple Electron app git clone https://github.com/electron/electron-quick-start…...
![](https://img-blog.csdnimg.cn/img_convert/cd1fca55bc31fd5c8641ce6b82c95719.png)
如何在Oracle、MySQL、PostgreSQL上终止会话或取消SQL查询
How to Kill session or Cancel SQL query on Oracle , MySQL, PostgreSQL 数据库维护过程中难免会遇到一些不正常的SQL或会话进程正在占用系统大量资源,临时需要终止查询或kill会话,在Oracle, MySQL, Postgresql数据库中不同的操作。 Oracle KILL会话…...
![](https://img-blog.csdnimg.cn/direct/199401723f97477dac0e22350b8051e7.png)
3、FTL基本工作过程
上文描述了FTL的四大功能,这里简述一下每个功能的含义。 地址转换简述 FTL要维护一个地址转换表,这个转换表是主机读/写硬盘的逻辑地址到硬盘实际物理地址的转换关系。 假如SSD的容量是128G,SSD逻辑块的大小是4KB,那SSD的逻辑块…...
![](https://www.ngui.cc/images/no-images.jpg)
微信小程序的跳转页面
在微信小程序中,要实现从当前页面返回到指定页面的功能,通常不直接使用“返回上一页”的逻辑,而是利用小程序的页面栈管理和navigateBack或者重新定向到目标页面的API。下面我将介绍两种主要的方法: 方法一:使用 navi…...
![](https://www.ngui.cc/images/no-images.jpg)
深入理解 Java 中的线程间通信:`wait()`, `notify()`, `notifyAll()`
引言 在多线程编程中,线程间通信是一个重要且复杂的主题。Java 提供了一套基本的机制来实现线程间通信,即使用 wait(), notify(), 和 notifyAll() 方法。这些方法由 Object 类提供,用于协调多个线程对共享资源的访问。本文将详细介绍这些方法…...
![](https://img-blog.csdnimg.cn/20201023201048810.gif#pic_center)
23种设计模式【创建型模式】详细介绍之【单例模式】
23种设计模式【创建型模式】详细介绍之【单例模式】 设计模式的分类和应用场景总结单例模式1. 概述2. 实现方式2.1 饿汉式单例模式2.2 懒汉式单例模式(非线程安全)2.3 懒汉式单例模式(线程安全) 3. 单例模式的优缺点3.1 优点3.2 缺…...
![](https://www.ngui.cc/images/no-images.jpg)
某汽车配件制造公司任职资格体系项目成功案例纪实
——基于岗位特点和核心能力要求,分层分级能力测评,实现个性化人才培养 【客户行业】生产制造;汽车配件制造 【问题类型】任职资格体系建立;人才管理系统 【客户背景】 某汽车配件制造公司是一家专注于汽车配件研发、生产和销…...
![](https://img-blog.csdnimg.cn/direct/05b8e94c8e264e0ea6a6f72032078f6d.png)
【Linux】生物信息学常用基本命令
wget网址用于直接从网上下载某个文件到服务器,当然也可以直接从网上先把东西下到本地然后用filezilla这个软件来传输到服务器上。 当遇到不会的命令时候,可以使用man “不会的命令”来查看这个命令的详细信息。比如我想要看看ls这个命令的详细用法&…...
![](https://img-blog.csdnimg.cn/direct/0a6cde746af64c9199e6ef8591585bff.png)
React Native V0.74 — 稳定版已发布
嗨,React Native开发者们, React Native 世界中令人兴奋的消息是,V0.74刚刚在几天前发布,有超过 1600 次提交。亮点如下: Yoga 3.0New Architecture: Bridgeless by DefaultNew Architecture: Batched onLayout UpdatesYarn 3 for New Projects让我们深入了解每一个新亮点…...
![](https://img-blog.csdnimg.cn/direct/12b9a31808be40408c5fc59c64083515.png)
Python面试宝典第4题:环形链表
题目 给你一个链表的头节点 head ,判断链表中是否有环。如果存在环 ,则返回 true 。 否则,返回 false 。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环…...
![](https://www.ngui.cc/images/no-images.jpg)
Kubernetes (K8s) 底层原理
Kubernetes (K8s) 的底层原理涉及多个关键组件和概念,确保容器化应用程序的自动化部署、扩展和管理。以下是 Kubernetes 的底层原理及其关键组件的详细描述。 核心组件 Etcd 功能:分布式键值存储,用于存储集群的所有数据,包括配置…...
![](https://www.ngui.cc/images/no-images.jpg)
解析Kotlin中的委托(包括类委托,属性委托)【笔记摘要】
1.委托模式 委托模式:操作对象不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。 例如我们要设计一个自定义类的来实现Set,可以将该实现委托给另一个对象: class MySet<T> (val helperSet: HashSet<T>…...
![](https://www.ngui.cc/images/no-images.jpg)
vue3+ts+uniapp+vite+pinia项目配置
开发环境: node >18,npm >8.10.2,vue < 3.2.31 安装项目 npx degit dcloudio/uni-preset-vue#vite-ts vue3-uniapp 1、引入样式规范 npm add -D eslint eslint-config-airbnb-base eslint-config-prettier eslint-import-resolv…...
![](https://www.ngui.cc/images/no-images.jpg)
大数据开发语言 Scala(四):面向对象编程
目录 1. 概述 2. 面向对象编程的基本概念 2.1 类和对象 2.2 继承和多态 2.3 封装和访问控制 3. 面向对象编程在大数据开发中的应用 3.1 Spark中的面向对象编程 3.2 面向对象编程在数据清洗和预处理中 3.3 面向对象编程在机器学习中的应用 4. 面向对象编程的高级特性 …...
![](https://www.ngui.cc/images/no-images.jpg)
C++ //练习 14.31 我们的StrBlobPtr类没有定义拷贝构造函数、赋值运算符及析构函数,为什么?
C Primer(第5版) 练习 14.31 练习 14.31 我们的StrBlobPtr类没有定义拷贝构造函数、赋值运算符及析构函数,为什么? 环境:Linux Ubuntu(云服务器) 工具:vim 解释: 因为…...
![](https://www.ngui.cc/images/no-images.jpg)
通配符和正则表达式之间的关系
通配符和正则表达式(正则)都是用于匹配字符串的工具,但它们的复杂性和用途有所不同。下面是它们之间的主要关系和区别: 通配符 通配符主要用于简单的模式匹配,常见于文件系统操作中,例如在命令行中查找文…...
![](https://www.ngui.cc/images/no-images.jpg)
GY-30光照传感器软件I2C方式驱动代码,基于STM32Cube
GY-30光照传感器的具体资料可以去淘宝搜索然后问卖家要,网上也有,所以这里我就不多嘴了。 VCC连接3到5伏电压,根据文件开头的描述在STM32CubeMX中配置好外设。 STM32Cube开发方式就是4个字“简单直接”,直接上代码。 gy30.h #…...
![](https://www.ngui.cc/images/no-images.jpg)
双相元编程:一种新语言设计方法
本文讨论了编程语言的一种趋势,即允许相同的语法表达 在两个不同阶段或环境(上下文)中执行的计算同时保持跨阶段(上下文)的一致行为。这些阶段通常在时间上(运行时间)或空间上(运行…...
![](https://img-blog.csdnimg.cn/direct/133466a43f974bb599ca323f8aa546d6.png)
基于SpringBoot校园外卖配送系统设计和实现(源码+LW+调试文档+讲解等)
💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟 感兴趣的可以先收藏起来,…...
![](https://img-blog.csdnimg.cn/direct/8468d4a81e4143968c545e6ca5c06bc8.png)
茗鹤APS高级计划排程系统,在集团多工厂协同生产下的应用
随着业务规模的扩大和市场的全球化,越来越多的企业选择“总部多工厂基地”的模式,此种模式大幅提升企业的产能与产量,有效分散风险。然后,与之而来的是对企业的管理提出更高的管理要求。多个生产基地不仅面临集团下发的周期性计划…...
![](https://img-blog.csdnimg.cn/direct/2cabbbaf6ab641c3855dc0e12ff099f2.jpeg)
分享六款免费u盘数据恢复工具,U盘恢复工具集合【工具篇】
U盘里面的数据丢失了怎么找回?随着数字化时代的深入发展,U盘已成为我们日常生活中不可或缺的数据存储工具。然而,由于各种原因,如误删除、格式化、病毒攻击等,U盘中的数据可能会丢失,给用户带来极大的困扰。…...
![](https://img-blog.csdnimg.cn/img_convert/f30fe091765d8fcefc81446207695c31.png)
Linux 的启动流程
第一步、加载内核 操作系统接管硬件以后,首先读入 /boot 目录下的内核文件。 以我的电脑为例,/boot 目录下面大概是这样一些文件: $ ls /bootconfig-3.2.0-3-amd64config-3.2.0-4-amd64grubinitrd.img-3.2.0-3-amd64initrd.img-3.2.0-4-amd6…...
![](https://img-blog.csdnimg.cn/direct/e0399057fb694e1d9e0bf56bf1416719.png)
思维导图插件--jsMind的使用
vue引入jsmind(右键菜单)_jsmind.menu.js-CSDN博客 第一版 vue-JsMind思维导图实现(包含鼠标右键自定义菜单)_jsmind 右键菜单-CSDN博客 // 新增节点addNode() {console.log(this.get_selected_nodeid());this.get_selected_…...
![](https://img-blog.csdnimg.cn/direct/b0afafc9fa394244a2ef0a14b528e38a.webp)
mac上使用finder时候,显示隐藏的文件或者文件夹
默认在finder中是不显示隐藏的文件和文件夹的,但是想创建.gitignore文件,并向里面写入内容,即便是打开xcode也是不显示这几个隐藏文件的,那有什么办法呢? 使用快捷键: 使用finder打开包含隐藏文件的文件夹…...
![](https://img-blog.csdnimg.cn/direct/e922333bd33944e1ad41f0315dd6d300.png)
泰雷茲具有首个通过FIPS 140-3 三级认证的HSMs
泰雷兹LunaHsm是业界首款通过FIPS140-33级认证的解决方案,安策引进泰雷兹HSM产品可以帮助您满足您的数据安全合规性需求,阻力企业提高竞争力。 安策提供泰雷茲ThalesLunaHSMs成为首个通过FIPS140-3三级认证的硬件安全模块图 我们很高兴地宣布,…...
![](https://img-blog.csdnimg.cn/a94afdfc2cce47938fe648865d0b7c8c.png)
美术馆预约小程序的设计
管理员账户功能包括:系统首页,个人中心,展品信息管理,管理员管理,用户管理,美术馆管理,基础数据管理,论坛管理 微信端账号功能包括:系统首页,美术馆ÿ…...
![](https://img-blog.csdnimg.cn/direct/a7235ec561f44ada8060239c2a08143d.png)
序列化Serializable
一、传输对象的方式 将对象从内存传输到磁盘进行保存,或者进行网络传输,有两种方式: 实现Serializable接口,直接传输对象转成json字符串后,进行字符串传输 二、直接传输对象 implements Serializable Data Equal…...
![](https://img-blog.csdnimg.cn/direct/e201df81dc5f46a4b2cd25cc78f69692.png)
编写静态库
一、静态库 1.制作完成整体目录结构 2.首先创建mymath.c和mymath.h 3.编写Makefile 4.创建测试的main函数 test文件夹 先把lib移到test文件夹里面 4.编译链接 gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -l mymath 5.形成可执行程序a.out 要是不想执行第四步那么麻烦…...
![](https://img-blog.csdnimg.cn/direct/d3236c8652e54c4687377334b12f57a1.png)
hive的表操作
常用的hive命令 切换数据库use test;查询表的建表信息show create table 数据库名称.表名;查看表的类型信息desc formatted 数据库名称.表名; 删除内部表 drop table 数据库名称.表名; 先启动hdfs ,mysql , hiveservice2,beeline CREATE [EX…...
![](https://img-blog.csdnimg.cn/img_convert/d7b05458d457d79190fd0fe7fc11469f.png)
基于多视点编码光场的全景三维重建方法
欢迎关注GZH《光场视觉》 摘要:在基于光场的一系列应用中,目标的三维重建是基础且关键的任务。普通光场只能重建单一视角而无法重建全景,并且在纹理特征匮乏的区域也无法生成准确的三维信息。针对以上问题,提出一种基于多视点编码…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring Boot中的分布式文件系统
Spring Boot中的分布式文件系统 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,我们将探讨如何在Spring Boot中实现分布式文件系统的搭建和应用…...
![](https://img-blog.csdnimg.cn/img_convert/29f9b1bbcf7b97b992551b0a8d132def.jpeg)
three.js地理坐标系有哪些,和屏幕坐标系的转换。
坐标系很好理解,就是点线面体的位置,一个点是一个坐标,一条线段2个坐标,一个矩形四个坐标,一个立方体8个坐标,three.js面对的是三维空间,屏幕则是二维的,这就面临着转换问题…...
![](https://www.ngui.cc/images/no-images.jpg)
聊聊C++20的三向比较运算符 `<=>`
C20标准引入了许多新特性,其中之一是三向比较运算符 <>,也被称为太空船运算符。这个新运算符为C程序员提供了一种全新的比较对象的方式,它能有效简化比较逻辑,避免编写多个比较运算符重载的情况。 为什么需要三向比较运算符…...
![](https://img-blog.csdnimg.cn/img_convert/723a3670e4f773c32e7d02e9c2ff707d.png)
CVE-2024-0603 漏洞复现
CVE-2024-0603 源码:https://gitee.com/dazensun/zhicms 开题: CVE-2024-0603描述:ZhiCms up to 4.0版本的文件app/plug/controller/giftcontroller.php中存在一处未知漏洞。攻击者可以通过篡改参数mylike触发反序列化,从而远程…...
![](https://img-blog.csdnimg.cn/6483912241234334949c02b2f36f211d.png)
西部智慧健身小程序+华为运动健康服务
1、 应用介绍 西部智慧健身小程序为用户提供一站式全流程科学健身综合服务。用户通过登录微信小程序,可享用健康筛查、运动风险评估、体质检测评估、运动处方推送、个人运动数据监控与评估等公益服务。 2、 体验介绍西部智慧健身小程序华为运动健康服务核心体验如…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring Boot中如何处理异步任务
Spring Boot中如何处理异步任务 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨在Spring Boot应用中如何处理异步任务,以提升系统的性…...
![](https://img-blog.csdnimg.cn/direct/075925ca0b6141b08dc7a460d6c2d5bd.png)
electron教程(二)控制应用程序的事件生命周期
1.will-finish-launching 当应用程序完成基础的启动的时候被触发,在 Windows 和 Linux 中, will-finish-launching 事件与 ready 事件是相同的; 在 macOS 中,这个事件相当于 NSApplication 中的 applicationWillFinishLaunching 提示。 app.on(will-fi…...
![](https://i-blog.csdnimg.cn/direct/b7d51ec06ee042e4adb0fda5b4ab6897.png)
第一次作业
作业1 1.代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head&…...
![](https://img-blog.csdnimg.cn/direct/b06450d584f343e5b31c9f6eae4bdc86.png)
转运机器人帮助物流行业实现无人化运输,自动分拣
在物流行业日新月异的今天,智能化、无人化已成为大势所趋。富唯智能转运机器人凭借其卓越的性能和广泛的应用场景,正引领着物流行业的新一轮变革。 1、高效转运,轻松应对 富唯智能转运机器人,拥有高达1000kg的负载能力ÿ…...
![](https://img-blog.csdnimg.cn/direct/7aa653d4f8c047b6b2b7746404e834ea.png)
基于java+springboot+vue实现的药店管理系统(文末源码+Lw)285
摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,药品信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广…...
![](https://i-blog.csdnimg.cn/direct/067c250514d04cfe9040612181a8a3c5.png)
NoSQL 非关系型数据库 Redis 的使用:
redis是基于内存型的NoSQL 非关系型数据库,本内容只针对有基础的小伙伴, 因为楼主不会做更多的解释,而是记录更多的技术接口使用,毕竟楼主不是做教学的,没有教学经验。 关于redis的介绍请自行搜索查阅。 使用redis数据…...
![](https://www.ngui.cc/images/no-images.jpg)
c++【入门】加乘算式
限制 时间限制 : 1 秒 内存限制 : 128 MB 题目 我们现在要做一个非常简单的题目,实现一个完整的程序。当它运行起来后,我们希望通过键盘输入三个整数,我们先把它们称为 a 、 b 和 c 。然后把他们的代入 (a b) * c 作为结果给存到sum&…...
![](https://www.ngui.cc/images/no-images.jpg)
沈阳音乐学院举办辽宁省与俄罗斯滨海边疆区友好交响音乐会
辽宁省与俄罗斯滨海边疆区友好交响音乐会日前在沈阳音乐学院音乐厅隆重上演。适逢中俄建交75周年和中俄文化年,来自俄罗斯滨海边疆区爱乐乐团太平洋交响乐团的艺术家与沈音师生携手为中俄两国观众奉上了一场音乐与友谊的交响合奏。在乐团指挥拉苏洛夫奥塔别克执棒指挥下,滨海…...
![](https://www.ngui.cc/images/no-images.jpg)
质感提升,智能化看齐新势力,魏牌蓝山智驾版实拍
说起魏牌蓝山,不知道大家还记不记得它,这是去年4月上市的一款插电混动、大6座SUV,主打30万级豪华市场。今天呢我们来到一个天台,来看看它的最新版本——蓝山智驾版。一句话点评蓝山智驾版:车内的科技感与高级感有较高水准,明显是朝着新势力“开炮”。优势:1、内饰改动较大…...
![](https://www.ngui.cc/images/no-images.jpg)
续写广交会篇章骆驼蓄电池国际版向新而行
近日,第135届广交会在广州落下帷幕。开幕以来,这场被誉为中国第一展的盛会成为了全球贸易的焦点,让更多中国造走向世界,写下贸易强国的新篇章。骆驼蓄电池作为中国汽车低压电池行业前列的蓄电池品牌,同样将中国质造的蓄电池产品推向五湖四海。骆驼蓄电池在汽车低压电池制造…...
![](https://www.ngui.cc/images/no-images.jpg)
相差8000元!全新凯美瑞买燃油版,还是混动版?
前不久,广汽丰田全新凯美瑞正式上市,新车共有9款配置,分为2.0L燃油版和2.0L混动版,售价从17.18-20.68万元。全新凯美瑞在搭载2.0L混合动力总成后,售价方面有了大幅度的下调,17.98万元起步,顶配20.68万元,确实很有诚意。本期话题,我们就以凯美瑞入门的精英版两款车型来…...
![](https://www.ngui.cc/images/no-images.jpg)
极氪007开卷了,007新增后驱增强版
极氪开卷了,007新增后驱增强版,价格和原本入门版一样,但是加赠了需要额外选装的舒适套装和科技全感套装,相当于后续智驾版去掉一颗激光雷达和一块NVIDIA DRIVE Orin驾驶辅助芯片,然后减了2万车价。这下极氪007的入门版就很香了,舒适配置齐平高配,仅智驾部分稍弱一些。很…...
![](https://www.ngui.cc/images/no-images.jpg)
代码随想录算法训练营第22天(py)| 二叉树 | 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树
669. 修剪二叉搜索树 力扣链接 给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>L) 思路 如果当前节点元素小于low,递归右子树,返回符合条件的头节点 如果当前节点元…...