LLM模型:代码讲解Transformer运行原理
视频讲解、获取源码:LLM模型:代码讲解Transformer运行原理(1)_哔哩哔哩_bilibili
1 训练保存模型文件
2 模型推理
3 推理代码
import torch
import tiktoken
from wutenglan_model import WutenglanModelimport pyttsx3# 设置设备为CUDA(如果可用),否则使用CPU
# 这是因为许多深度学习算法在GPU上运行更高效
device = torch.device('cpu')
encoding = tiktoken.get_encoding("cl100k_base")# 加载模型
model = WutenglanModel(99849)
model_state_dict = torch.load('model\model-wutenglan-base.pt', map_location=device)
model.load_state_dict(model_state_dict)
model.eval()
model.to(device)start = '武'
# 对起始点进行编码,将其转换为模型可接受的输入格式
start_ids = encoding.encode(start)# 为编码的起始点创建一个张量,并且通过unsqueeze操作增加一个新的维度,以适应模型的输入要求
input_content = (torch.tensor(start_ids, dtype=torch.long, device=device)[None, ...])def text_to_speech(text):# 初始化引擎engine = pyttsx3.init()# 设置语速,可选参数,根据需要调整rate = engine.getProperty('rate')engine.setProperty('rate', rate - 50) # 减少语速# 设置音量,范围 0.0 到 1.0volume = engine.getProperty('volume')engine.setProperty('volume', volume + 100) # 增加音量# 获取可用的声音列表voices = engine.getProperty('voices')# 设置声音,例如选择第一个声音engine.setProperty('voice', voices[0].id)# 朗读文本engine.say(text)# 运行引擎,等待完成engine.runAndWait()# 在不进行梯度计算的上下文中生成文本,以减少内存占用并提高效率
with torch.no_grad():# 使用预训练模型生成文本,基于输入`input_content`,最大新生成的token数量为5output = model.inference(input_content, max_new_tokens=5)# 将生成的token序列转换为字符串文本text = encoding.decode(output[0].tolist())# 输出生成的文本到控制台print(text)# 将生成的文本转换为语音输出text_to_speech(text)
4 推理重点代码详解
4.1 截取上下文长度
last_context = input_content[:, -context_length:]
这行代码的主要作用是从输入序列 input_content
中截取最后 context_length
个token作为当前的上下文。这一操作对于许多基于Transformer架构的语言模型来说是非常重要的,因为这些模型通常有一个固定的输入长度限制(即 context_length
),超过这个长度的输入需要被裁剪,以适应模型的处理能力。
如果删除这一行代码,那么在每次迭代中,你将会使用整个 input_content
作为模型的输入,这可能导致以下问题:
-
超出模型处理能力:如果
input_content
的长度超过了模型的设计长度(context_length
),那么模型可能无法正确处理这么长的序列,导致错误或次优的结果。 -
内存问题:处理过长的序列可能会导致内存不足的问题,特别是在GPU上运行大型模型时。
-
计算效率低下:即使模型能够处理更长的序列,这样做也可能导致计算资源的浪费,因为模型只需要最新的上下文信息来进行下一步的预测。
方括号内的两个冒号是用来进行数组切片(slice)操作的符号。具体来说,在表达式 input_content[:, -context_length:]
中,第一个冒号和第二个冒号分别有不同的含义:
-
第一个冒号 (
:
):- 第一个冒号表示的是“所有”的意思。在这个上下文中,它意味着选择所有行。例如,在二维数组(矩阵)中,如果我们想要选择所有的行,我们可以使用
:
。这意味着我们会保留所有的行。
- 第一个冒号表示的是“所有”的意思。在这个上下文中,它意味着选择所有行。例如,在二维数组(矩阵)中,如果我们想要选择所有的行,我们可以使用
-
第二个冒号 (
:
):- 第二个冒号同样用于指定切片范围,但它跟在负数索引
-context_length
后面,表示从-context_length
到结尾(end of the sequence)。这里的-context_length
是一个负数索引,它指向列表或数组的末尾往前数第context_length
个位置。所以input_content[:, -context_length:]
意味着选择每行的最后context_length
个元素。
- 第二个冒号同样用于指定切片范围,但它跟在负数索引
4.2 线性层计算结果说明
【线性变换结果】
# 使用前向传播方法得到linear_predictions和损失
linear_predictions, loss = self.forward(last_context, None)
以上代码输出的linear_predictions形状如下:
【线性变换说明】
在 linear_predictions
的形状 torch.Size([1, 2, 99850])
中,每个维度都有特定的意义:
-
第一个维度
1
:- 这个维度通常表示批次大小(
batch size
)。在这里,1
表示当前批次只有一个样本。这意味着在这一时刻,模型正在处理一个单独的序列。
- 这个维度通常表示批次大小(
-
第二个维度
2
:- 这个维度通常表示时间步的数量(
time steps
)。在这里,2
表示当前批次中有两个时间步。这可能是因为input_content
在进入模型之前已经包含了一个或多个先前的时间步,并且当前批次包含这些先前的时间步加上当前的时间步。
- 这个维度通常表示时间步的数量(
-
第三个维度
99850
:- 这个维度通常表示词汇表大小(
vocabulary size
)或输出特征的数量。在这里,99850
表示模型的输出空间大小,也就是模型可以预测的词汇数量。换句话说,模型在每个时间步上可以预测99850
个不同的token之一。
- 这个维度通常表示词汇表大小(
总结一下:
[1]
:批次大小,当前批次包含一个样本。[2]
:时间步的数量,当前批次包含两个时间步。[99850]
:输出特征的数量,模型可以预测99850
个不同的token。
在文本生成任务中,模型的输出通常是一个概率分布,表示在当前上下文下下一个token的可能性。因此,linear_predictions
的形状表明模型在一个样本上有两个时间步,并且在每个时间步上输出了 99850
个可能的下一个token的概率值。
4.3 只关注最后一个时间步的linear_predictions
为什么要取最后一个时间步,而不取所有的。
假设我们有一个简单的文本生成任务,目的是根据前面的文本生成下一个词。为了简化讨论,我们假设词汇表只有几个词,比如 ['hello', 'world', 'how', 'are', 'you']
。
实际数据示例
假设我们的输入序列是 ['hello', 'world']
,我们希望模型根据这个序列生成下一个词。为了说明这一点,我们可以构造一个简单的示例。
构造示例数据
-
词汇表:
- 假设词汇表为
['hello', 'world', 'how', 'are', 'you']
。
- 假设词汇表为
-
编码映射:
- 我们需要将词汇表中的每个词映射到一个整数。例如:
'hello' -> 0
'world' -> 1
'how' -> 2
'are' -> 3
'you' -> 4
- 我们需要将词汇表中的每个词映射到一个整数。例如:
-
输入序列:
- 输入序列
input_sequence
为['hello', 'world']
,对应的编码序列为[0, 1]
。
- 输入序列
构造 linear_predictions
张量
假设模型已经处理了输入序列,并生成了 linear_predictions
张量,其形状为 [1, 2, 5]
。这意味着:
- 第一维
[1]
表示批次大小,当前批次包含一个样本。 - 第二维
[2]
表示时间步的数量,当前批次包含两个时间步。 - 第三维
[5]
表示词汇表大小。
假设 linear_predictions
的具体数值如下:
1linear_predictions = torch.tensor([
2 [
3 [0.1, 0.2, 0.3, 0.4, 0.5], # 第一个时间步的预测值
4 [0.2, 0.3, 0.4, 0.5, 0.6] # 第二个时间步的预测值
5 ]
6])
为什么取最后一个时间步
在文本生成任务中,我们通常只关心当前上下文的下一个词的预测。因此,我们通常取最后一个时间步的预测值来生成下一个词。
-
取最后一个时间步:
linear_predictions[:, -1, :]
取的是最后一个时间步的预测值。- 在这个例子中,
linear_predictions[:, -1, :]
将得到:tensor([[0.2, 0.3, 0.4, 0.5, 0.6]])
-
计算概率分布:
- 接下来,我们使用
F.softmax
将这些预测值转换为概率分布。import torch.nn.functional as Flast_timestep_predictions = linear_predictions[:, -1, :] probs = F.softmax(input=last_timestep_predictions, dim=-1)
- 接下来,我们使用
-
采样下一个词:
- 使用
torch.multinomial
从概率分布中采样下一个词。idx_next = torch.multinomial(input=probs, num_samples=1)
- 使用
为什么不取所有时间步
如果我们取所有的时间步,即 linear_predictions[:, :, :]
,我们将得到:
1tensor([
2 [
3 [0.1, 0.2, 0.3, 0.4, 0.5],
4 [0.2, 0.3, 0.4, 0.5, 0.6]
5 ]
6])
这将导致以下问题:
-
形状不匹配:
- 在计算 softmax 时,需要一个二维张量作为输入。如果我们取所有的时间步,形状为
[1, 2, 5]
,需要进一步处理才能进行 softmax 计算。
- 在计算 softmax 时,需要一个二维张量作为输入。如果我们取所有的时间步,形状为
-
逻辑不一致:
- 在文本生成任务中,我们通常只关心当前上下文的下一个词的预测。取所有时间步的信息可能会引入不必要的复杂度,并且不符合逐词生成的逻辑。
示例代码
1import torch
2import torch.nn.functional as F
3
4# 构造示例数据
5vocab = ['hello', 'world', 'how', 'are', 'you']
6word_to_idx = {word: idx for idx, word in enumerate(vocab)}
7
8# 输入序列
9input_sequence = ['hello', 'world']
10encoded_sequence = [word_to_idx[word] for word in input_sequence]
11
12# 构造 linear_predictions 张量
13linear_predictions = torch.tensor([
14 [
15 [0.1, 0.2, 0.3, 0.4, 0.5], # 第一个时间步的预测值
16 [0.2, 0.3, 0.4, 0.5, 0.6] # 第二个时间步的预测值
17 ]
18])
19
20# 取最后一个时间步
21last_timestep_predictions = linear_predictions[:, -1, :]
22
23# 计算概率分布
24probs = F.softmax(input=last_timestep_predictions, dim=-1)
25
26# 采样下一个词
27idx_next = torch.multinomial(input=probs, num_samples=1)
28
29# 解码下一个词
30next_word = vocab[idx_next.item()]
31print("Next word:", next_word)
通过这种方式,我们只关注当前上下文的下一个词的预测。
4.4 SoftMax激活函数
Softmax 函数是一种常用的激活函数,在机器学习尤其是深度学习中具有重要作用。它的主要用途包括:
概率化输出:
Softmax 函数将一个 K 维的实数向量转换为另一个 K 维的向量,其中每个元素都是 [0, 1] 区间内的值,并且所有元素的和为 1。因此,经过 Softmax 处理后的输出可以解释为概率分布,这使得模型能够输出每个类别的预测概率。
6.2 激活函数的计算过程
假设我们有一个未归一化的得分向量 z=[1.0,2.0,3.0]z=[1.0,2.0,3.0],代表三个类别的得分。
4.5 torch.multinomial
进行随机采样
4.5.1 torch.multinomial
作用
idx_next = torch.multinomial(input=probs, num_samples=1)
这段代码的作用是从一个概率分布中随机采样一个样本。具体来说,这段代码在文本生成任务中用于从当前时间步的预测概率分布中选择下一个 token 的索引。下面详细介绍这段代码的具体作用和实现细节。
代码详解
-
probs
参数:probs
是一个一维张量,表示一个概率分布。在文本生成任务中,这个概率分布通常是对下一个可能生成的 token 的概率估计。例如,在你的例子中,probs
的形状是[1, 99850]
,表示一个样本在最后一个时间步上的99850
个可能的下一个 token 的概率分布。
-
num_samples
参数:num_samples
表示从概率分布中采样的样本数量。在这个例子中,num_samples=1
表示我们只采样一个样本。
-
torch.multinomial
函数:torch.multinomial
是 PyTorch 中的一个函数,用于从一个多维概率分布中进行随机采样。它根据提供的概率分布进行采样,返回一个索引,该索引指示采样出的 token 在词汇表中的位置。
具体实现
假设我们有一个概率分布 probs
,其形状为 [1, 99850]
。我们希望从这个概率分布中采样一个 token 的索引。
示例代码
1import torch
2import torch.nn.functional as F
3
4# 构造示例数据
5probs = torch.tensor([
6 [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.1]
7]) # 示例概率分布,形状为 [1, 7]
8
9# 从概率分布中采样一个 token 的索引
10idx_next = torch.multinomial(input=probs, num_samples=1)
11
12print("Sampled index:", idx_next.item())
代码流程
-
构造概率分布:
probs
是一个形状为[1, 99850]
的张量,表示一个样本在最后一个时间步上的99850
个可能的下一个 token 的概率分布。
-
采样:
- 使用
torch.multinomial
从概率分布probs
中采样一个索引。num_samples=1
表示我们只采样一个样本。
- 使用
-
结果:
idx_next
是一个形状为[1, 1]
的张量,表示采样出的下一个 token 的索引。
详细解释
-
概率分布:
probs
张量中的每个元素都是一个概率值,表示相应 token 被选中的概率。所有概率值之和为 1。
-
采样过程:
torch.multinomial
根据提供的概率分布进行采样。它从[0, 99849]
范围内随机选择一个索引,这个索引是基于概率分布probs
进行的加权选择。也就是说,概率值较高的 token 被选中的可能性更大。
-
结果索引:
idx_next
是一个形状为[1, 1]
的张量,表示采样出的下一个 token 的索引。例如,如果idx_next
的值为3
,则表示下一个 token 的索引为3
。
示例代码
假设 probs
的具体值如下:
1probs = torch.tensor([
2 [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.1]
3]) # 示例概率分布,形状为 [1, 7]
我们从这个概率分布中采样一个 token 的索引:
1idx_next = torch.multinomial(input=probs, num_samples=1)
2print("Sampled index:", idx_next.item())
输出结果可能是:
1Sampled index: 2
这意味着根据给定的概率分布,采样出的下一个 token 的索引为 2
。
总结
idx_next = torch.multinomial(input=probs, num_samples=1)
这段代码用于从一个概率分布中采样一个 token 的索引。在文本生成任务中,这一步骤用于确定下一个生成的 token。通过这种方式,模型可以根据当前上下文的概率分布生成下一个 token,从而逐步构建出完整的文本序列。
4.5.2 torch.multinomial随机采样
原因
在文本生成任务中,使用 torch.multinomial
进行随机采样而不是直接选择概率最大的 token 主要有以下几个原因:
1. 探索性(Exploration)
在生成文本时,如果每次都选择概率最大的 token,模型的行为会变得非常确定性和单调。这可能会导致生成的文本缺乏多样性和创造性。通过随机采样,模型可以在一定程度上探索不同的生成路径,从而产生更加丰富和多样的文本。
2. 平滑峰值(Smoothing Peaks)
在某些情况下,模型可能会过于自信地选择某个 token,即使这个 token 不一定是最佳选择。通过随机采样,可以平衡模型的置信度,避免过度依赖某个特定的选择。这有助于模型生成更加自然和流畅的文本。
3. 避免局部最优(Avoiding Local Optima)
直接选择概率最大的 token 容易让模型陷入局部最优解。通过随机采样,模型有机会跳出局部最优解,探索更多的可能性,从而找到全局最优解。
4. 更真实的模拟人类行为
人类在写作或说话时,并不总是选择最有可能的词,而是会根据上下文和个人风格做出选择。随机采样可以更好地模拟这种行为,使得生成的文本更加自然和真实。
5. 多样性(Diversity)
随机采样可以增加生成文本的多样性。如果总是选择概率最大的 token,生成的文本可能会非常相似,缺乏变化。通过随机采样,可以引入更多的变化,使得生成的文本更加多样化。
6. 控制生成过程
在某些应用场景中,可以通过调整采样过程中的温度参数(temperature parameter)来控制生成过程的随机程度。温度参数是一个正数,可以调节模型生成文本的多样性:
- 温度较高(例如,大于1):模型的生成更加随机,可以探索更多的可能性。
- 温度较低(例如,小于1但大于0):模型的生成更加确定,倾向于选择高概率的 token。
示例代码
假设我们有一个概率分布 probs
,我们可以调整温度参数来控制随机性:
1import torch
2import torch.nn.functional as F
3
4# 构造示例数据
5probs = torch.tensor([
6 [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.1]
7]) # 示例概率分布,形状为 [1, 7]
8
9# 调整温度参数
10temperature = 0.7 # 可以调整此参数
11
12# 应用温度参数
13probs = probs / temperature
14
15# 归一化概率分布
16probs = F.softmax(probs, dim=-1)
17
18# 从概率分布中采样一个 token 的索引
19idx_next = torch.multinomial(input=probs, num_samples=1)
20
21print("Sampled index:", idx_next.item())
总结
通过使用 torch.multinomial
进行随机采样,而不是直接选择概率最大的 token,可以带来更多的多样性和探索性,使得生成的文本更加自然和丰富。这种做法在实践中已经被证明能够提高生成文本的质量和多样性。
相关文章:

LLM模型:代码讲解Transformer运行原理
视频讲解、获取源码:LLM模型:代码讲解Transformer运行原理(1)_哔哩哔哩_bilibili 1 训练保存模型文件 2 模型推理 3 推理代码 import torch import tiktoken from wutenglan_model import WutenglanModelimport pyttsx3# 设置设备为CUDA(如果…...

虚幻引擎VR游戏开发02 | 性能优化设置
常识:VR需要保持至少90 FPS的刷新率,以避免用户体验到延迟或晕眩感。以下是优化性能的一系列设置(make sure the frame rate does not drop below a certain threshold) In project setting-> (以下十个设置都在pr…...

Web应用监控:URL事务监测指标解读
监控易是一款功能强大的IT监控软件,它能够实时监控各种IT资源和应用的运行状态,确保业务的连续性和稳定性。在Web应用监控方面,监控易提供了URL事务监测功能,通过模拟用户访问流程,监测整个事务的执行过程和性能表现。…...

redis之缓存淘汰策略
1.查看redis的最大占用内存 使用redis-cli命令连接redis服务端,输入命令:config get maxmemory 输出的值为0,0代表redis的最大占用内存等同于服务器的最大内存。 2.设置redis的最大占用内存 编辑redis的配置文件,并重启redis服务…...

CMake/C++:一个日志库spdlog
项目仓库 GitHub - gabime/spdlog: Fast C logging library.Fast C logging library. Contribute to gabime/spdlog development by creating an account on GitHub.https://github.com/gabime/spdlog 知乎参考贴 https://zhuanlan.zhihu.com/p/674073158 先将仓库clone一下 然…...
rig——管理不同R语言版本的工具
在Python中,我可以用Conda去管理多个版本的Python,包括一些Python模块,因此想在R语言中也找一个类似的工具。 之前在Mac上,有一个名为 Rswitch 的R语言版本管理工具,可以管理不同版本的R以及相应的R包。 现在想在Win…...
Java内存模型详解
1. 引言 在Java中,内存模型是非常重要的概念,它涉及到线程之间如何共享数据以及保证数据的一致性。了解Java内存模型对于开发高质量的多线程程序是至关重要的。 本篇博客将详细介绍Java内存模型的概念、原则、规则以及相关的概念和术语。同时ÿ…...

空气能热泵热水器
空气能热泵热水器压缩机把低温低压气态冷媒转换成高压高温气态,压缩机压缩功能转化的热量为q1,高温高压的气态冷媒与水进行热交换,高压的冷媒在常温下被冷却、冷凝为液态。这过程中,冷媒放出热量用来加热水,使水升温变…...

计算机毕业设计选题推荐-消防站管理系统-社区消防管理系统-Java/Python项目实战
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...

移动UI:新手指引页面,跟着指引不迷路。
移动端新手指引在提供用户引导、提升用户体验、提高用户留存率、促进功能使用和降低用户流失率方面都有积极的作用。 移动端新手指引在应用程序或移动网站中有以下几个作用: 1. 提供用户引导: 新手指引可以帮助用户快速了解应用程序或移动网站的功能和…...

数据库MySQL基础
目录 一、数据库的介绍 1.数据库概述 (1)数据的存储方式 (2)数据库 2.常见数据库排行榜 二、数据库的安装与卸载 1.数据库的安装 2.数据库的卸载 三、数据库服务的启动与登录 1.Windows 服务方式启动 (1&…...

AUTOSAR_EXP_ARAComAPI的5章笔记(3)
5.3.4 Finding Services Proxy Class提供类(静态)方法来查找“连接”的服务实例。由于服务实例的可用性本质上是动态的(因为它有一个生命周期),所以ara::com提供了如下两种不同的方法来实现“FindService ”: StartFindService是一个类方法,它在后台启…...

【Godot4.3】基于纯绘图函数自定义的线框图控件
概述 同样是来自2023年7月份的一项实验性工作,基于纯绘图函数扩展的一套线框图控件。初期只实现了三个组件,矩形、占位框和垂直滚动条。 本文中的三个控件类已经经过了继承化的修改,使得代码更少。它们的继承关系如下: 源代码 W…...

申万宏源证券完善金融服务最后一公里闭环,让金融服务“零距离、全天候”
在数字化转型的浪潮中,申万宏源作为金融行业的先锋,持续探索科技如何赋能金融服务,以提升企业效率并优化客户服务体验。面对日益增长的视频化需求,传统的图文形式已难以满足市场与用户的新期待。为了应对这一挑战,申万…...

无需更换摄像头,无需施工改造,降低智能化升级成本的智慧工业开源了。
智慧工业视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上…...

系统架构师考试学习笔记第三篇——架构设计高级知识(19)嵌入式系统架构设计理论与实践
本章考点: 第19课时主要学习嵌入式系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分)。在历年考试中,案例题对该部分内容都有固定考查,综合知识选择题目中有固定分值…...
centos8stream 修改为阿里云yum源
centos8stream 官方已经不再维护,导致该系统官方源实效,可以使用阿里云源进行替换 阿里云文档:centos-vault镜像_centos-vault下载地址_centos-vault安装教程-阿里巴巴开源镜像站 (aliyun.com) 咱们只需要执行下面命令,即可替换官…...
python转换并提取pdf文件中的图片
#安装fitz包 pip install pymupdf 脚本如下所示: import fitz import re import os import time import sysarguments sys.argvfor arg in arguments:print(arg)def file_name_list(base_dir):for i, j, k in os.walk(base_dir):name [i.replace(.pdf, ) for i …...

【MySQL】MySQL常用的数据类型——表的操作
前言: 🌟🌟本期讲解关于MySQL常用数据类型,表的简单使用,希望能帮到屏幕前的你。 🌈上期博客在这里:http://t.csdnimg.cn/wwaqe 🌈感兴趣的小伙伴看一看小编主页:GGBondl…...

自然语言处理系列五十三》文本聚类算法》文本聚类介绍及相关算法
注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列五十三文本聚类算法》文本聚类介绍及相关算法K…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...