用变压器实现德-英语言翻译【01/8】:嵌入层

news2025/1/15 12:42:40

 一、说明

        本文是“用变压器实现德-英语言翻译”系列的第一篇文章。它引入了小规模的嵌入来建立感知系统。接下来是嵌入层的变压器使用。下面简要概述了每种方法,然后是德语到英语的翻译。

二、技术背景

        嵌入层的目标是使模型能够详细了解单词、标记或其他输入之间的关系。此嵌入层可以被视为将数据从高维空间转换为低维空间,也可以视为将数据从低维空间映射到高维空间。

2.1 从单热向量到嵌入向量

        在自然语言处理中,令牌派生自可能包含章节、段落或句子的数据语料库。这些以各种方式分解成更小的部分,但最常见的标记化方法是按单词。语料库中所有独特的单词都被称为词汇表。

        词汇表中的每个单词都被分配一个整数,因为它更容易被计算机处理。有多种方法可以分配这些整数,但同样,最简单的方法是按字母顺序分配它们。

        下图演示了将较大的语料库分解为其组件并为每个组件分配整数的过程。请注意,为简单起见,标点符号被去掉,文本设置为小写。

        通过为每个单词分配索引而创建的数字顺序意味着一种关系。由于这不是意图,因此索引通常用于为每个单词创建一个独热编码向量。单热向量与词汇表的长度相同。在这种情况下,每个向量有 24 个元素。它被称为“一热”向量,因为只有一个元素被“打开”或设置为 1;所有其他令牌都处于“关闭”状态或设置为 0。1 的索引对应于分配给单词的整数值。通常,模型学习预测向量中给定索引的最高概率。

         当一个模型只有十几个标记或类可供预测时,独热编码向量通常是一种方便的表示形式。但是,大型语料库可以有数十万个代币。不是使用充满零的稀疏向量,这些向量没有传达太多意义,而是使用嵌入层将向量映射到较小的维度。可以训练这些嵌入式向量来传达有关每个单词及其与其他单词的关系的更多信息。

        本质上,每个单词都由一个d_model维向量表示,其中d_model可以是任何数字。它只是指示嵌入维度的数量。如果d_model是 2 或 3,则可以可视化每个单词之间的关系,但通常根据任务使用 256、512 和 1024 的值。

        下面可以看到一个优化嵌入的示例,其中类似类型的书籍彼此靠近嵌入:

2.2 嵌入向量

        嵌入矩阵的大小为 (vocab_size, d_model)。这允许将大小为 (seq_length, vocab_size) 的单热向量矩阵乘以它以获得新的嵌入式表示。序列长度由 seq_length 表示,即序列中的标记数。请记住,到目前为止,可视化中的“序列”是整个词汇表。在实践中,将使用词汇的子集,例如“基本段落”。该序列将被标记化、索引并转换为独热编码向量矩阵。然后,这些独热编码向量将能够与嵌入矩阵相乘。

        嵌入序列的大小为 (seq_length, vocab_size) x (vocab_size, d_model = (seq_length, d_model)。这意味着句子中的每个单词现在都由d_model维向量表示,而不是vocab_size元素的独热编码向量。下面可以看到此矩阵乘法的示例。索引序列的形状为 (3,24),嵌入矩阵的形状为 (24, 3)。一旦它们相乘,输出就是一个 (3,3) 矩阵。每个单词都由其 3 元素嵌入向量表示。

        当独热编码矩阵与嵌入层相乘时,将返回嵌入层的相应向量,而不进行任何更改。下面是独热编码向量和嵌入矩阵的整个词汇表之间的矩阵乘法。输出是嵌入矩阵。

        这表明有一种更简单的方法可以在不使用矩阵乘法的情况下获取这些相同的值,因为矩阵乘法可能会占用大量资源。分配给每个单词的整数可用于直接索引嵌入矩阵,而不是从 one-hot 编码向量转到 d_model 维嵌入(从较大维度到较小维度)。这就像从一维转到d_model维,提供有关令牌的更多信息。

        下图显示了如何在不乘法的情况下获得完全相同的结果:

2.3 从头开始嵌入

        可以在 Python 中创建上述图的简单实现。嵌入序列需要一个分词器、单词及其索引的词汇表,以及词汇表中每个单词的三维嵌入。分词器将序列拆分为其标记,在本示例中为小写单词。下面的简单函数从序列中删除标点符号,将其拆分为标记,并将它们小写。

# importing required libraries
import math
import copy
import numpy as np

# torch packages
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch import Tensor

# visualization packages
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt

example = "Hello! This is an example of a paragraph that has been split into its basic components. I wonder what will come next! Any guesses?"

def tokenize(sequence):
  # remove punctuation
  for punc in ["!", ".", "?"]:
    sequence = sequence.replace(punc, "")
  
  # split the sequence on spaces and lowercase each token
  return [token.lower() for token in sequence.split(" ")]

tokenize(example)
['hello', 'this', 'is', 'an', 'example', 'of', 'a', 'paragraph', 'that', 
'has', 'been', 'split', 'into', 'its', 'basic', 'components', 'i', 
'wonder', 'what', 'will', 'come', 'next', 'any', 'guesses']

        创建分词器后,可以为示例创建词汇表。词汇表包含构成数据的唯一单词列表。虽然示例中没有重复项,但仍应将其删除。一个简单的例子是下面的句子:“我很酷,因为我很矮。词汇将是“我,是,酷,因为,短”。然后,这些词将按字母顺序排列:“我,因为,酷,我,短”。最后,它们将被分配一个整数:“am: 0, 因为: 1, cool: 2, i: 3, short: 4”。此过程在下面的函数中实现。

def build_vocab(data):
  # tokenize the data and remove duplicates
  vocab = list(set(tokenize(data)))

  # sort the vocabulary
  vocab.sort()

  # assign an integer to each word
  stoi = {word:i for i, word in enumerate(vocab)}

  return stoi

# build the vocab
stoi = build_vocab(example)

stoi 
{'a': 0,
 'an': 1,
 'any': 2,
 'basic': 3,
 'been': 4,
 'come': 5,
 'components': 6,
 'example': 7,
 'guesses': 8,
 'has': 9,
 'hello': 10,
 'i': 11,
 'into': 12,
 'is': 13,
 'its': 14,
 'next': 15,
 'of': 16,
 'paragraph': 17,
 'split': 18,
 'that': 19,
 'this': 20,
 'what': 21,
 'will': 22,
 'wonder': 23}

此词汇现在可用于将任何标记序列转换为其整数表示形式。

sequence = [stoi[word] for word in tokenize("I wonder what will come next!")]
sequence
[11, 23, 21, 22, 5, 15]

        下一步是创建嵌入层,它只不过是一个大小为 (vocab_size, d_model) 的随机值矩阵。这些值可以使用torch.rand生成。

# vocab size
vocab_size = len(stoi)

# embedding dimensions
d_model = 3

# generate the embedding layer
embeddings = torch.rand(vocab_size, d_model) # matrix of size (24, 3)
embeddings
tensor([[0.7629, 0.1146, 0.1228],
        [0.3628, 0.5717, 0.0095],
        [0.0256, 0.1148, 0.1023],
        [0.4993, 0.9580, 0.1113],
        [0.9696, 0.7463, 0.3762],
        [0.5697, 0.5022, 0.9080],
        [0.2689, 0.6162, 0.6816],
        [0.3899, 0.2993, 0.4746],
        [0.1197, 0.1217, 0.6917],
        [0.8282, 0.8638, 0.4286],
        [0.2029, 0.4938, 0.5037],
        [0.7110, 0.5633, 0.6537],
        [0.5508, 0.4678, 0.0812],
        [0.6104, 0.4849, 0.2318],
        [0.7710, 0.8821, 0.3744],
        [0.6914, 0.9462, 0.6869],
        [0.5444, 0.0155, 0.7039],
        [0.9441, 0.8959, 0.8529],
        [0.6763, 0.5171, 0.9406],
        [0.1294, 0.6113, 0.5955],
        [0.3806, 0.7946, 0.3526],
        [0.2259, 0.4360, 0.6901],
        [0.6300, 0.2691, 0.9785],
        [0.2094, 0.9159, 0.7973]])

        创建嵌入后,可以使用索引序列为每个标记选择适当的嵌入。原始序列的形状为 (6, ),值为 [11, 23, 21, 22, 5, 15]。

# embed the sequence
embedded_sequence = embeddings[sequence]

embedded_sequence
tensor([[0.7110, 0.5633, 0.6537],
        [0.2094, 0.9159, 0.7973],
        [0.2259, 0.4360, 0.6901],
        [0.6300, 0.2691, 0.9785],
        [0.5697, 0.5022, 0.9080],
        [0.6914, 0.9462, 0.6869]])

        现在,六个标记中的每一个都被一个 3 元素向量替换;新形状为 (6, 3)。

        由于这些令牌中的每一个都有三个组件,因此它们可以在三个维度上映射。虽然此图显示了一个未经训练的嵌入矩阵,但经过训练的嵌入矩阵会像前面提到的书籍示例一样将相似的单词彼此靠近。

# visualize the embeddings in 3 dimensions
x, y, z = embedded_sequences[:, 0], embedded_sequences[:, 1], embedded_sequences[:, 2] 
ax = plt.axes(projection='3d')
ax.scatter3D(x, y, z)  

2.4 使用 PyTorch 模块进行嵌入

        由于 PyTorch 将用于实现转换器,因此 nn.可以分析嵌入模块。PyTorch将其定义为:

一个简单的查找表,用于存储固定字典和大小的嵌入。

此模块通常用于存储词嵌入并使用索引检索它们。模块的输入是索引列表,输出是相应的词嵌入。

        这准确地描述了在前面的示例中使用索引而不是独热向量时所执行的操作。

        至少,nn。嵌入需要vocab_size和嵌入维度,随着d_model的发展,将继续对其进行标注。提醒一下,这是模型维度的缩写。

        下面的代码创建了一个形状为 (24, 3) 的嵌入矩阵。

# vocab size
vocab_size = len(stoi) # 24

# embedding dimensions
d_model = 3

# create the embeddings
lut = nn.Embedding(vocab_size, d_model) # look-up table (lut)

# view the embeddings
lut.state_dict()['weight']
tensor([[-0.3959,  0.8495,  1.4687],
        [ 0.2437, -0.3289, -0.5475],
        [ 0.9787,  0.7395,  2.0918],
        [-0.4663,  0.4056,  1.2655],
        [-1.0054,  1.4883, -0.1254],
        [-0.1028, -1.1913,  0.0523],
        [-0.2654, -1.0150,  0.4967],
        [-0.4653, -1.9941, -1.7128],
        [ 0.3894, -0.9368,  1.5543],
        [-1.1358, -0.2493,  0.6290],
        [-1.4935,  1.1509, -1.8723],
        [-0.0421,  1.2857, -0.4009],
        [-0.2699, -0.8918, -1.0352],
        [-1.3443,  0.4688,  0.1536],
        [ 0.3638,  0.1003, -0.2809],
        [ 1.4208, -0.0393,  0.7823],
        [-0.4473, -0.4605,  1.2681],
        [ 1.1315, -1.4704,  0.2809],
        [ 0.4270, -0.2067, -0.7951],
        [-1.0129,  0.0706, -0.3417],
        [ 1.4999, -0.2527,  0.4287],
        [-1.9280, -0.6485,  0.4660],
        [ 0.0670, -0.5822,  0.0996],
        [-0.7058,  0.2849,  1.1725]], grad_fn=<EmbeddingBackward0>)

        如果将与之前相同的索引序列 [11, 23, 21, 22, 5, 15] 传递给它,则输出将是一个 (6, 3) 矩阵,其中每个标记由其三维嵌入向量表示。索引必须采用张量的形式,数据类型为整数或长整型。

indices = torch.Tensor(sequence).long()

embeddings = lut(indices)

embeddings

        输出将是:

tensor([[ 0.7584,  0.2332, -1.2062],
        [-0.2906, -1.2168, -0.2106],
        [ 0.1837, -0.9425, -1.9011],
        [-0.7708, -1.1671,  0.2051],
        [ 1.5548,  1.0912,  0.2006],
        [-0.8765,  0.8829, -1.3169]], grad_fn=<EmbeddingBackward0>)

三、变压器中的嵌入层

        在原始论文中,嵌入层用于编码器和解码器。对nn的唯一补充。嵌入模块是一个标量。嵌入权重乘以 √(d_model)。这有助于在下一步中将嵌入添加到位置编码时保留基本含义。这实质上使位置编码相对较小,并减少了其对嵌入的影响。这个堆栈溢出线程更多地讨论了它。

        为了实现这一点,可以创建一个类;它将被称为嵌入,并利用PyTorch的nn。嵌入模块。此实现基于带注释的转换器。

class Embeddings(nn.Module):
  def __init__(self, vocab_size: int, d_model: int):
    """
    Args:
      vocab_size:     size of vocabulary
      d_model:        dimension of embeddings
    """
    # inherit from nn.Module
    super().__init__()   
     
    # embedding look-up table (lut)                          
    self.lut = nn.Embedding(vocab_size, d_model)   

    # dimension of embeddings 
    self.d_model = d_model                          

  def forward(self, x: Tensor):
    """
    Args:
      x:              input Tensor (batch_size, seq_length)
      
    Returns:
                      embedding vector
    """
    # embeddings by constant sqrt(d_model)
    return self.lut(x) * math.sqrt(self.d_model)  

四、 前向传递

        此嵌入类的工作方式与 nn 相同。嵌入。下面的代码演示了它与前面示例中使用的单个序列的用法。

lut = Embeddings(vocab_size, d_model)

lut(indices)
tensor([[-1.1189,  0.7290,  1.0581],
        [ 1.7204,  0.2048,  0.2926],
        [-0.5726, -2.6856,  2.4975],
        [-0.7735, -0.7224, -2.9520],
        [ 0.2181,  1.1492, -1.2247],
        [ 0.1742, -0.8531, -1.7319]], grad_fn=<MulBackward0>)

        到目前为止,每个嵌入中只使用了一个序列。但是,模型通常使用一批序列进行训练。这实质上是一个序列列表,这些序列被转换为它们的索引,然后嵌入。这可以在下图中看到。

# list of sequences (3, )
sequences = ["I wonder what will come next!",
             "This is a basic example paragraph.",
             "Hello, what is a basic split?"]

        虽然前面的示例很简陋,但它适用于序列批次。上图中显示的示例是具有三个序列的批处理;标记化后,每个序列由六个标记表示。标记化序列的形状为 (3, 6),与 (batch_size, seq_length) 相关。基本上,三个,六个字的句子。

# tokenize the sequences
tokenized_sequences = [tokenize(seq) for seq in sequences]
tokenized_sequences
[['i', 'wonder', 'what', 'will', 'come', 'next'],
 ['this', 'is', 'a', 'basic', 'example', 'paragraph'],
 ['hello', 'what', 'is', 'a', 'basic', 'split']]

        然后可以使用词汇表将这些标记化序列转换为其索引表示形式。

# index the sequences 
indexed_sequences = [[stoi[word] for word in seq] for seq in tokenized_sequences]

indexed_sequences
[[11, 23, 21, 22, 5, 15], 
 [20, 13,  0,  3, 7, 17], 
 [10, 21, 13,  0, 3, 18]]

最后,这些索引序列可以转换为可以通过嵌入层传递的张量。

# convert the sequences to a tensor
tensor_sequences = torch.tensor(indexed_sequences).long()

lut(tensor_sequences)
tensor([[[ 0.1348, -1.3131,  2.8429],
         [ 0.2866,  3.3650, -2.8529],
         [ 0.0985,  1.6396,  0.0191],
         [-3.8233, -1.5447,  0.5320],
         [-2.2879,  1.0203,  1.5838],
         [ 0.4574, -0.4881,  1.2095]],

        [[-1.7450,  0.2474,  2.4382],
         [ 0.2633,  0.3366, -0.4047],
         [ 0.2921, -1.6113,  1.1765],
         [-0.0132,  0.5255, -0.7268],
         [-0.5208, -0.9305, -1.1688],
         [ 0.4233, -0.7000,  0.2346]],

        [[ 1.6670, -1.7899, -1.1741],
         [ 0.0985,  1.6396,  0.0191],
         [ 0.2633,  0.3366, -0.4047],
         [ 0.2921, -1.6113,  1.1765],
         [-0.0132,  0.5255, -0.7268],
         [-0.4935,  3.2629, -0.6152]]], grad_fn=<MulBackward0>)

        输出将是一个 (3, 6, 3) 矩阵,它与 (batch_size、seq_length、d_model) 相关。本质上,每个索引令牌都被其相应的三维嵌入向量所取代。

        在进入下一节之前,了解此数据的形状(batch_size、seq_length d_model)非常重要:

  • batch_size与一次提供的序列数相关,通常为 16、32 或 64。
  • seq_length与标记化后每个序列中的单词或标记数相关。
  • d_model与嵌入每个令牌后的模型大小相关。

有关位置编码的文章是本系列的下一篇。

请不要忘记点赞和关注更多!:)

五、引用

  1. 图片来源:Will Koehrsen
  2. PyTorch 的嵌入模块
  3. 堆栈溢出讨论
  4. 带注释的变压器
  5. 变压器从零开始

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

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

相关文章

简易虚拟培训系统-UI控件的应用2

目录 Text组件-文字显示 Text组件-文字动态显示 ScrollView组件 使用文件流动态读取硬盘文件 本篇介绍Text和ScrollView的简单应用&#xff0c;以及读取硬盘中.txt文本的内容 Text组件-文字显示 1. 加入Text&#xff1a;在mainCanvas上点右键->UI->选择Text和TextMe…

CocosCreator组件上的schedule

目录 1.首先看component.ts中schedule 函数&#xff0c;核心代码就是获取director.getScheduler()&#xff0c;并调用schedule方法&#xff0c;把callback等参数传递进去。 2.再看到scheduler.ts类中的schedule方法&#xff0c;只取一些主要代码&#xff0c;下面会分段详细拆…

dockerfile 例子(二)

Dockerfile由一行一行的命令语句组成&#xff0c;#开头的为注释行。Dockerfile文件内容分为四个部分&#xff1a;基础镜像信息、维护者信息、镜像操作指令以及容器启动执行指令。 接下来给大家列出Dockerfile中主要命令的说明。 FROM&#xff0c;指定所创建镜像的基础镜像。 …

Verilog基础:块语句

相关阅读 Verilog基础专栏https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 1、块语句 块语句(block statements)是一种把语句组织在一起&#xff0c;这样他们在语法上就像单个语句一样工作。Verilog HDL中有两种类型的块&#xff1a; …

“惠医通-医院挂号订单平台”

结合已学习过的vue3和TS完成的项目&#xff0c;便于患者对自己想要就诊的科室进行挂号&#xff0c;付款 一&#xff1a;项目简介 前端技术栈 Vue3 TS vue-router Element-ui Axios Pinia 项目架构 二&#xff1a;主要模块 1. axios二次封装 1.1 创建实例 //利用axios.creat…

如何调整DOSBOX软件的运行窗口大小

前言 小编最近正在学习微机原理&#xff0c;碰到一些问题&#xff0c;在安装DOSBOX后&#xff0c;打开应用&#xff0c;会出现运行窗口特别小&#xff0c;字体也很小的情况&#xff0c;使用时会感觉特别费劲&#xff0c;看着特别的不舒服&#xff0c;那么这个时候就需要调整一…

【LeetCode题目详解】第八章 贪心算法 part01 理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和 day31补

贪心算法理论基础 关于贪心算法&#xff0c;你该了解这些&#xff01; 题目分类大纲如下&#xff1a; # 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 这么说有点抽象&#xff0c;来举一个例子&#xff1a; 例如&#xff0c;有一堆钞票&…

【C#】C#:“指派给常量数组的必须是常量”

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 记录一个有意思的代码片段。 首先&#xff0c;复习一下常量。…

安防监控/磁盘阵列存储/视频汇聚平台EasyCVR调用rtsp地址返回的IP不正确是什么原因?

安防监控/云存储/磁盘阵列存储/视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RT…

leetcode 563.二叉树的坡度

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;https://leetcode.cn/problems/binary-tree-tilt/description/ 代码&#xff1a; class Solution { public:int childFind(TreeNode* root , int& sumTile) {if (root nullptr) {return 0; // 空树坡度为0}int l…

VBA技术资料MF48:VBA_在Excel中将列号与字母转换

【分享成果&#xff0c;随喜正能量】除非自己的认知获得了改变和刷新&#xff0c;否则&#xff0c;人们常说的“顺应自己的内心”&#xff0c;顺的不过是一颗旧心&#xff0c;一颗惯性的&#xff0c;充满了各种习性的套路之心。与“顺应自己的内心”恰恰相反&#xff0c;人要用…

2023最新独立版校园跑腿校园社区小程序系统源码 | 附教程

2023最新独立版校园跑腿校园社区小程序系统源码 | 附教程 测试环境&#xff1a;NginxPHP7.2MySQL5.6 多校版本&#xff0c;多模块&#xff0c;适合跑腿&#xff0c;外卖&#xff0c;表白&#xff0c;二手&#xff0c;快递等校园服务 源码下载&#xff1a;https://download.c…

二叉搜索树(C++)

二叉搜索树 概念二叉搜索树的应用二叉搜索树的实现K模型基本结构和函数声明接口实现①find——查找关键码②Insert——插入关键码③Erase——删除关键码&#xff08;重点&#xff09;时间复杂度 源码&#xff08;整体&#xff09;非递归递归 KV模型 在使用C语言写数据结构阶段时…

【Linux】进程通信 — 信号(下篇)

文章目录 &#x1f4d6; 前言1. 阻塞信号1.1 信号其他相关常见概念&#xff1a;1.2 sigset_t&#xff1a;1.2 - 1 信号集操作函数 1.3 sigprocmask&#xff1a;1.4 sigpending&#xff1a; 2. 进程处理信号2.1 内核页表和用户页表&#xff1a;2.2 内核态和用户态&#xff1a;2.…

华为OD机试 - VLAN资源池 - 回溯、双指针(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、核心思想2、具体解题思路 五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&…

产品帮助中心对SaaS行业的作用

帮助中心是一款SaaS产品必不可少的一部分&#xff0c;为了帮助用户更好的解决产品相关问题&#xff0c;提高新用户的使用体验&#xff0c;并且引导其更好地使用产品。 所以今天我们就来谈谈帮助中心对SaaS行业的作用&#xff0c;以及制作帮助中心的方法&#xff0c;每个产品帮…

基于 OV5640 的图像采集显示系统(DVP 接口时序逻辑设计)

文章目录 前言一、DVP 接口时序逻辑设计二、基本数据流接收三、像素位置输出四、舍弃前 N 张图像五、系统异常状态恢复控制六、完整代码展示七、仿真代码展示八、仿真波形展示前言 上一节,我们已经完成了 OV5640 初始化逻辑的介绍。接下来,将要开始完成 DVP 接口的时序设计。…

Maven入门教程(一):安装Maven环境

Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的软件项目管理工具。 ​ 在项目开发中Maven可以对jar包和对工程之间的依赖关系进行管理。maven仓库中存储jar包&#xff0c;可以一次下载&#xff0c;所有项目通用。 1. 安装…

Java项目-苍穹外卖-Day07-redis缓存应用-SpringCache/购物车功能

文章目录 前言缓存菜品问题分析和实现思路缓存菜品数据清理缓存数据功能测试 SpringCache介绍入门案例 缓存套餐 前言 本章节主要是进行用户端的购物车功能开发 和redis作为mysql缓存的应用以及SpringCache的介绍 因为很多人查询数据库会导致mysql的查询效率降低&#xff0c;可…

解读亚马逊云科技语义搜图检索方案

图像检索&#xff08;包括文搜图和图搜图&#xff09;是各个行业中常见的一个应用场景。比如在电商场景中&#xff0c;基于以图搜图做相似商品查找&#xff1b;在云相册场景中&#xff0c;基于文搜图来找寻所需的图像素材。 传统基于标签的图像检索方式&#xff0c;即先使用目标…