ElasticSearch学习篇17_《检索技术核心20讲》最邻近检索-局部敏感哈希、乘积量化PQ思路

目录

场景在搜索引擎和推荐引擎中,对相似文章去重是一个非常重要的环节,另外是拍照识花、摇一摇搜歌等场景都可以使用它快速检索。

基于敏感性哈希的检索更擅长处理字面上的相似而不是语义上的相似。

  • 向量空间模型
  • ANN检索加速思路
    • 局部敏感哈希编码
      • 随机超平面划分哈希编码思路
      • Google的SimHash编码思路以及抽屉原理
    • 聚类
    • 乘积量化

ANN与向量空间模型

对于高纬度数据近邻检索常见方式

将所有文档中的关键词都提取出来,如果总共n个关键词,那么就是一个n纬度的向量。具体到一篇文章,假如文档包含关键词数量是k,其中(0<=k<=n),如果文档包含的第k个关键词的权重为w,那么权重向量k位置的元素就是w,这个权重w一般是根据TF-IDF计算得出,如果文档不包含第k个关键词,那么权重w就是0。

关于TF-IDF词频-逆文档频率参考往期:

ElasticSearch学习篇15_《检索技术核心20讲》进阶篇之TopK检索-CSDN博客

本质就转化为计算两个向量的相似度,可用余弦相似度、欧式距离等计算,另外n纬向量放入到空间中就是一个点,也可理解为空间中的近邻检索ANN,在十几维量级的低维空间中,我们可以使用 k-d 树进行 k 维空间的近邻检索,这种思路就是前面说的精确检索的思路,它的性能还是不错的,但是纬度上来之后,因为基础k-d树特点当纬度大于20的时候可能出现线性灾难,搜索大量的邻域导致性能很慢。

关于KD树、KDB、BKD树参考往期:

ElasticSearch学习篇10_Lucene数据存储之BKD动态磁盘树(论文Bkd-Tree: A Dynamic Scalable kd-Tree)_bkd树-CSDN博客

向量空间模型可用来表示文字、图片等内容,从而进行文本、图像近邻搜索ANN,一些常见的ANN加速算法思想基本分为两类

  • 缩小候选集
  • 压缩向量存储空间

ANN搜索加速技术思想

局部敏感哈希-缩小候选集数量

借助非精确检索的思路,可以将高纬空间中的点进行区域划分,然后给每个区域生成一个较短的编码,查询的时候先根据规则定位区域,达到缩小候选集的目的,再从该区域高效近邻检索。

这个规则就类似哈希,但是普通的哈希函数值不太可控,文章中变化很少的关键词就会导致哈希值很大的变动。

因此必须找出一个特殊的哈希规则,使得相似的数据哈希之后,得到的哈希值也是相近的,被称为局部敏感性哈希(Locality-Sensitive Hashing)

随机超平面划分哈希编码

以二维空间举例,找出一条线,将区域划分为两部分,处在区域1的点编码为1,处在另外区域的点编码为0,这条线需要尽可能合理,因此可以随机找出n条线,一条线切分下,会对点增加一位编码,这样n条线就会给一个点产生n位的编码。

对于多维空间,就需要找超平面,如n个纬度就找n个超平面,然后这些超平面将空间中的点切割,位于超平面两侧的点(通过超平面法向量的余弦相似度计算)分别被编码0、1,这样可以将高维空间的点映射为一纬的编码。

如果两个点的哈希值是一样的,那么两个点大概率距离的非常近,即使哈希值不一样,只要他们在n个比特位中大大部分是相同的(具体的计算算法如海明距离),说明他们大概率相近。举个例子如果两篇文章内容是100%相同的,那么他们的哈希值就是相同的,也就相当于编码相同。

这种一般哈希编码有个缺点就是随着超线、超平面的划分编码,会丢失某些纬度权重信息。

SimHash编码

谷歌提出的局部敏感哈希策略,简化哈希函数,保留多维数据(点)项的权重信息。

它使用一个普通的哈希函数代替了n次随机超平面划分,这个哈希函数作用对象不是整体所有的高纬度数据项,而是一个个处理高纬度数据项,通过数据项哈希值编码与数据项权重计算的时候就能保留权重。

构造SimHash的具体过程,以一篇文档进行局部敏感哈希编码来说明

  • 文档分词,关键词项带权重:分词并计算每个关键词的权重w。
  • 生成Hash值:使用一般的Hash函数,针对每个关键词生成如64位的0、1哈希值编码。
  • 每个关键词哈希值变化:将哈希值的0改为-1,如10110 -> 1 -1 1 1 -1
  • 每个关键词哈希值按位乘上词权重:如词权重3,变为 3 -3 3 3 -3。
  • 按位相加所有关键词的哈希值:计算文档所有关键词哈希值按位加的结果。
  • 文档哈希值编码转换:将编码转为0、1。

这样,文档的最终哈希编码是收到权重比较大的词影响比较多的,另外就是哈希函数比较简单,代替了复杂的随机选取超平面方式。

抽屉原理

对于文章查重领域,如果两个文章的SimHash编码的距离(使用汉明距离计算)小于k,那么就认为它们是相似的。举个例子当k=3的时候,需要找出k小于3的文章,然后遍历逐一计算对比,效率比较低,有没有加速方案?

一个直观的想法,使用SimHash编码某一位bit作为key,使用文章内容作为value构建倒排索引,以64位的SimHash编码为例,key的数量为128个,构造的倒排索引key如

  • 1xxxxx、0xxxxx
  • x1xxxx、x0xxxx

查找的时候,逐位查找64次,将第n为比特位对应key对应的文章全部取出来,然后计算对比。

这种方式效率不是很高,因为即使两篇文章64位bit任意两个位置的bit相同的话就会被召回,即使不太相似。

Google提出优化的抽屉原理:将文章的SimHash编码分4段,如果想找出和此文章bit位差异不超过3个的文章,那么4个段其中一定至少有一个段的bit是完全一致的。因此查询就被转化为了4段中有一段完全相同的文章会被召回。

按照这个思路,

  • 分段构建倒排索引:将每个文档的SimHash编码划分为四段,每段的16位bit作为一个倒排索引,
  • 分段查询:查询的时候,当前文档的SimHash编码也会被划分为四段,如果找出汉明距离k<3的文档,只需召回四段中任一段完全相同的文档即可,然后从四段倒排索引找回的结果合并。

通过使用 SimHash 函数和分段检索(抽屉原理),使得 Google 能在百亿级别的网页中快速完成过滤相似网页的功能,从而保证搜索结果的数量。

思考1:对于 SimHash,如果将海明距离在 4 之内的文章都定义为相似的,那我们应该将哈希值分为几段进行索引和查询呢?分为5段,因为4为不同的bit位最多能影响四段,虽然除不尽,每段可以是12或者13,查询的时候也按照这种规则划分5段即可。

对比编辑距离、杰卡德算法计算文本相似性

对比杰卡德、莱文斯坦、SimHash三种计算文章相似度的效果,总结特点以及适用场景。

SimHash的一种实现参考:SimHash结合汉明距离判断文本相似性

下面选取一套初中语文的试卷,试卷包含大概20道试题,大概1w字符

手动创造不同的CASE,以下是包含完整CASE描述的表格:

CASE描述原文内容长度内容长度simHash相似度simHash耗时levenshtein相似度levenshtein耗时jaccard相似度jaccard耗时
CASE1将21题和22题互相换位置85568556100.0274ms0.84298ms1.07ms
CASE2删除试卷中间的11题8556840196.88102ms0.98274ms1.01ms
CASE3删除10题之后的所有内容8556192165.6330ms0.2241ms1.02ms
CASE4完全打乱试题顺序85568560100.045ms0.45183ms1.01ms
CASE5删除11题之前的所有内容8556663592.1940ms0.77143ms1.01ms
CASE6前11道题内容不相同,后面内容相同8556918489.0640ms0.74194ms0.90359712230215831ms
CASE7在11题后面中间加入一道试题8556874198.4437ms0.97186ms1.01ms

绘制成图表

在选取初中语文试卷内容1w字符文本相似度计算场景下,综合CASE分析文本相似度计算效率以及严格程度

  • 效率:杰卡德 > SimHash > 莱文斯坦编辑距离
  • 严格程度:莱文斯坦编辑距离 > SimHash > 杰卡德

针对CASE2、CASE3分析,即使试卷内容1和试卷内容2相差了一道或者十多道试题,但是杰卡德计算的文本相似度仍为100,SimHash计算的文本相似度较为准确,但是效率又要比莱文斯坦效率要高,使用SimHash可以避免查重中将不是完全一致的试卷内容(差别几道试题)误查,同时又兼顾相似度计算效率。

SimHash、杰卡德(Jaccard)和莱文斯坦(Levenshtein)是三种常用的文本相似度计算算法,它们各有特点和适用场景:

  1. SimHash:局部敏感性哈希
    • 特点:SimHash是一种局部敏感哈希算法,主要用于快速计算大规模文本数据的相似性。它通过将文本转换为一个固定长度的二进制哈希值来表示文本特征。SimHash的优点是计算速度快,适合处理大规模数据。
    • 适用场景:SimHash常用于海量数据的去重、近似重复检测和相似文档查找等场景。由于其计算效率高,特别适合需要快速处理和比较大量文本的应用。
  2. 杰卡德(Jaccard)相似系数
    • 特点:杰卡德相似系数用于衡量两个集合的相似度,定义为两个集合交集的大小除以并集的大小。对于文本相似性,通常将文本分割成词或字符的集合,然后计算这些集合的杰卡德相似度。
    • 适用场景:杰卡德相似度适合用于比较短文本或关键词集合的相似性,如文档分类、标签推荐等。由于其计算简单,适合用于需要快速评估文本相似性的场合。
  3. 莱文斯坦(Levenshtein)距离
    • 特点:莱文斯坦距离,又称编辑距离,表示将一个字符串转换为另一个字符串所需的最小编辑操作次数(插入、删除、替换)。它能够精确地衡量两个字符串之间的差异。
    • 适用场景:莱文斯坦距离适合用于需要精确比较字符串差异的场合,如拼写检查、DNA序列比对、文本纠错等。由于其计算复杂度较高,通常用于较短文本的比较。

总结来说,SimHash适合大规模文本的快速相似性检测,杰卡德相似度适合集合间的相似性比较,而莱文斯坦距离适合精确的字符串差异分析。选择合适的算法需要根据具体的应用场景和数据特征来决定。

汉明距离

主要是计算等长的两个二进制字符串之间差别的位数

def hamming_distance(str1, str2):if len(str1) != len(str2):raise ValueError("Strings must be of the same length")distance = 0for ch1, ch2 in zip(str1, str2):if ch1 != ch2:distance += 1return distance# 示例
str1 = "1101"
str2 = "1001"
print(hamming_distance(str1, str2))  # 输出: 1

聚类-缩小候选集数量

图片如何相似性检索,检索图片和检索文章一样,首先要先用向量空间模型将图片表示出来,这样图像就变成了高纬度空间的一个点,搜素图片转化为了高纬度空间的ANN。

如何从图片抽取向量空间模型,如果把图像的每个像素看作一个纬度,像素上的RGB值作为纬度值,是一种思路,但是一张图片的纬度大概是百万级别,检索起来很复杂,因此另外一种方式就是使用CNN等进行图像特征提取,转为一个512或者1024纬度的向量空间模型。

如何加速ANN检索效率,有了向量空间模型,就可以使用ANN加速技术比如SimHash来加速检索,比如将高纬空间的点划分到有限区域,从而达到缩小候选集的目的。但是SimHash哈希函数比较简单,更适合计算字面上的相似性而不是语义上的相似性,同时SimHash是一种粒度很粗的非精确检索方案,他能将上百万的纬度压缩为64位bit,损失不少精度。因此一般使用聚类方案加速ANN检索,常见的一种是K-Means(K-平均算法)方案

K-Means聚类算法构建聚类计算步骤

  • 初始聚类中心:随机从数据中选取k个数据作为初始聚类中心
  • 计算距离:计算其他数据与ki 的距离,加入到最邻近的ki 聚类中
  • 根据距离均值重新选取聚类中心:根据聚类中的点到聚类中心距离的均值,重新选一个聚类中心

重复2-3步,即重新计算其他还数据到聚类中心的距离,然后将节点划分到最近的聚类中,然后在更新聚类中心。

K-Means 聚类算法的优化目标是,类内的点到类中心的距离均值总和最短。构建好之后,以聚类中心数据ID作为key,单个聚类的数据创建倒排索引。

K-Means查询的时候,直接找出待查询数据距离最近的聚类中心ki 然后从倒排索引取出topK候选集,

  • 如果不足topK数量,那么可以再查询邻近聚类的候选集。
  • 如果数量很多同时topK又取得非常大,那么一个一个计算和待查询数据距离代价也很大,可以采用层级子聚类来继续缩小候选集。

乘积量化-压缩向量模型存储空间

对于向量的相似检索,除了检索算法本身,优化向量存储空间也是一个优化方向,因为向量的相似度计算需要加载进内存。

以一个1024纬度的向量距离,每个向量纬度是一个浮点数占4 Bytes = 32 bits,那么一个向量占用1KB空间,如果是上亿级的数据,存储向量需要占几百个G。(100 000 000 KB = 100 GB)

为了更好的将向量加载进去内存,需要对向量存储空间优化,一种思路是使用上面的聚类思想,减少加载进内存的数量,只把查询向量和聚类向量加载进内存,而不是聚类下所有向量,这样可能会损失结果粒度。另外一种思路就是使用向量量化-乘积量化来压缩。

乘积量化的概念

  • 乘积:高纬空间向量可以看作是多个低纬空间向量相乘的结果,可以理解为笛卡尔集,如数轴的x、y轴分别表示一纬空间,数轴区域中的点(xi.yi)就是二维空间的点,假如xi的值为1、2、3,yi的值为5、6、7,那么组合笛卡尔集的二纬空间的点个数为9个。
  • 量化:将区域划分为子区域,然后在编码,这样就能将区域转为1纬编码,上面聚类就是一种量化方式。

乘积量化就是将高纬空间划分为多个子空间,然后对子空间编码,针对子空间在进行聚类技术分为多个子区域,然后给每个子区域编码即聚类ID。好处:省空间,二纬空间存储的点数量为9,但是只存储一纬空间x、y的话,只需要存储的数量为6个一纬点。

举例假设一组1024纬度的向量进行乘积量化

  • 首先将1024纬度向量拆分为4个256纬度子向量
  • 在每一个256纬度子向量进行聚类,找出1-256和聚类ID,因此只使用8 bits就能表示1-256的聚类ID。

所以经过上述过程,1024纬度的向量使用 4 * 8 bits = 32 bit就能表示。

乘积量化PQ过程思想

乘积量化向量相似查询的时候,涉及到三个向量

  • 样本向量:1024纬度,乘积量化过程会被压缩为4段,每段进行聚类,每段会得到一个聚类中心ID,范围为1-256,最后所有样本处理完后,大概会得到 256 * 4 个聚类中心向量。
  • 聚类中心向量:每段都有256个聚类中心ID,256 * 4 个聚类中心向量。
  • 查询向量:1024纬度,查询过程会被压缩4段。

构造的时候,样本向量会被压缩为4段,会得到256 * 4的聚类中心向量,而样本向量也从1024纬度被压缩为32纬,1024 => 4 * 256 => 4 * 2 ^ 8,压缩含义就是以每段聚类中心ID的8为bit编码代替当前子向量段。

当计算查询向量和样本向量的距离时,

  • 向量切分子空间:我们将查询向量和样本向量都分为 4 段子空间。
  • 子空间计算聚类ID:计算查询向量切分的4段子空间的聚类ID,生成压缩向量。然后预生成一个距离表,该距离表纬度是256 * 4,记录了查询子向量和 子空间各个中心向量的距离。
  • 遍历全部样本数量计算查询向量距离:根据预处理阶段的距离表可以查询出查询向量每段到各个聚类ID的距离,数据库中全部的样本向量属于哪个聚类ID也可以计算出,然后就是求出每段距离:d1、d2、d3、d4,最终通过相关运算如欧式距离等计算两个向量的距离,进而返回topK。

PQ主要是为了压缩空间,计算距离算法不太关注。

其他参考:理解 product quantization 算法

这样,求查询子向量和样本子向量的距离,就转换为求查询子向量和对应的聚类中心向量的距离。那我们只需要将样本子向量所属的聚类中心的聚类 ID 作为 key 去查距离表,就能在 O(1) 的时间代价内知道这个距离了。

这里讲述的只是大致思想,具体的如何计算找出紧邻的topK向量还有 基于倒排的乘积量化IVFPQ缩小遍历全部样本空间向量,以及SDC、ADC向量相似检索算法还有很多小细节。

基于倒排索引的IVFPQ思想

上面说的计算查询向量和所有样本向量之间的距离是遍历全部的样本空间,还有一种维护样本向量倒排索引的方式加速检索。

构建倒排索引的具体的思路

  • 样本向量切分子空间:1024纬切分为4段,每段256纬度
  • 计算子空间聚类ID:可以采用KMeans聚类算法,最终每个子空间(段)聚类256个,每个子空间使用聚类ID代表当前样本子空间特征,最终每个子空间得到8位的聚类ID
  • 建立倒排索引:得到的32位新向量,为每个子空间创建一个倒排索引,每个倒排索引的key可以设置为当前段的聚类ID,value设置为在当前段被聚类到该ID的所有样本数量。

查询的时候,查询向量进行切分子空间,PQ,然后逐段查倒排,这样将四个段的样本数量就是最有可能相近的样本数量,在进行距离计算找出TopK。

另外还有一种根据聚类ID向量残差创建倒排索引的做法,这样做精度会高一些。

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

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

相关文章

SpringBoot MySQL的增删改查

创建数据库和表 安装MySQL&#xff1a;https://blog.csdn.net/qq_59636442/article/details/141926105 通过Navicat 创建数据库test 创建表student后随便添加一条数据 CREATE TABLE student (id int NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL,email varchar(2…

23种设计模式-备忘录(Memento)设计模式

文章目录 一.什么是备忘录设计模式&#xff1f;二.备忘录模式的特点三.备忘录模式的结构四.备忘录模式的优缺点五.备忘录模式的 C 实现六.备忘录模式的 Java 实现七.总结 类图&#xff1a; 备忘录设计模式类图 一.什么是备忘录设计模式&#xff1f; 备忘录设计模式&#xff08…

如何删除pdf里的任意一页?删除PDF里任意一页的几种方法

如何删除pdf里的任意一页&#xff1f;尽管PDF文件具有许多优点&#xff0c;如跨平台兼容性和格式保真性&#xff0c;但在编辑和修改方面&#xff0c;它与像Word或Excel这类文档格式不同&#xff0c;通常不能像其他文档那样轻松进行直接的内容删除或修改。这让很多人以为&#x…

css3新特性(二十六课)

1、css3盒子模型 box - sizing: content - box&#xff1b; 是 CSS 中用于定义盒模型宽度和高度计算方式的一个属性值。在这种盒模型下&#xff0c;元素的宽度和高度&#xff08;width和height属性&#xff09;仅包括内容区域&#xff08;content&#xff09;的大小&#xff…

Centos7安装Jenkins脚本一键部署

公司原先Jenkins二进制安装&#xff0c;自己闲来无事在测试主机优化了一下&#xff0c;一键部署&#xff0c;jenkins2.426版本jdk11版本 #!/bin/bashjenkins_file"jenkins-2.426.3-1.1.noarch.rpm"# 更新软件包列表 echo "更新软件包列表..." sudo yum up…

类与对象(c++)——取地址运算符重载,初始化列表,类型转换

1.取地址运算符重载 1.1 const成员函数 a)将const修饰的成员函数称之为const成员函数&#xff0c;const修饰成员函数放到成员函数参数列表的后 ⾯。 b)const实际修饰该成员函数隐含的this指针&#xff0c;表明在该成员函数中不能对类的任何成员进⾏修改。 const 修饰Date类的…

形态学图像处理(Morphological Image Processing)

形态学图像处理(Morphological Image Processing) 前言 ‍ 本博客为个人总结数字图像处理一课所写&#xff0c;并给出适当的扩展和相应的demo。 写博客跟做 checkpoint​ 很像&#xff0c;毕竟个人还不能达到那种信手拈来的境界&#xff0c;忘了就是从零开始训练&#xff0…

如何在K8s集群中管理与使用GPU

背景 随着人工智能的兴起&#xff0c;GPU作为重要的智算算力类型愈发受到重视&#xff0c;而Kubernetes&#xff08;k8s&#xff09;作为业界主流的集群管理系统&#xff0c;如何方便管理、使用GPU也是其需要解决的一大问题&#xff0c;故此收集整理了K8s管理与使用GPU的相关资…

kubepi管理k8s集群,演示如何连接阿里云k8s容器

一、背景 对k8s容器运维的过程中&#xff0c;如果是自建k8s的话&#xff0c;一般会安装dashboard&#xff0c;方便管理&#xff1b;如果是阿里云k8s容器&#xff0c;它是有提供web ui&#xff0c;但是它有个不便之处–需要定期登录&#xff0c;且缺少命令控制台。 当你需要使…

Conda 安装纯净版ComfyUI

网上有很多整合包&#xff0c; 我个人喜欢纯净版&#xff0c; 自已搭建 1 拉代码 git clone https://github.com/comfyanonymous/ComfyUI 如果没有装过git,下载安装: https://git-scm.com/ https://git-lfs.com/ 2 创建环境 cd ComfyUI conda create -n ComfyUI python3.11…

LeetCode:700. 二叉搜索树中的搜索

目录 题目描述: 代码: 题目描述: 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返回 null 。 示例 1: 输入&#xff1a;root [4,2,7,1,3…

摄影:相机控色

摄影&#xff1a;相机控色 白平衡&#xff08;White Balance&#xff09;白平衡的作用&#xff1a; 白平衡的使用环境色温下相机色温下总结 白平衡偏移与包围白平衡包围 影调 白平衡&#xff08;White Balance&#xff09; 人眼看到的白色&#xff1a;会自动适应环境光线。 相…

【大选】2024年美国总统选举数据分析可视化

前言 • &#x1f453; 可视化主要使用 Plotly • &#x1f50e; 数据处理主要使用 pandas • &#x1f449; 本文是我自己在和鲸社区的原创 1.项目背景描述 2024年美国大选是该国政治生活中的重要事件&#xff0c;吸引了全球的关注。本报告通过对选举数据的分析&#xff0c…

Linux进阶:常用操作

systemctl&#xff1a; 控制系统服务的启动、关闭 系统内置服务均可被systemctl控制第三方软件&#xff0c;如果自动注册了可以被systemctl控制第三方软件&#xff0c;如果没有自动注册&#xff0c;可以手动注册 语法&#xff1a;systemctl start | stop | restart | disable…

JVM类加载过程-Loading

一、Class对象的生命周期 .class文件是如何加载到内存中:.class文件是ClassLoader通过IO将文件读到内存,再通过双亲委派的模式进行Loading,再Linking、以及Initializing,代码调用等一系列操作后,进行GC,组成完整的生命周期; 二、双亲委派模式(Loading的过程): 1、类…

002创建ASP.NET Core项目-数据库优先

创建数据库和表 创建数据库和表 添加关系 Product表引用Category 创建ASP.NET Core Web项目 根据数据库创建Models 在【程序包管理器控制台输入命令】 Scaffold-DbContext Data Source.;Initial Catalogshopdb;Usersa;Password123456;TrustServerCertificatetrue’Microso…

探究IOC容器刷新环节初始化前的预处理

目录 一、IOC容器的刷新环节快速回顾 二、初始化前的预处理prepareRefresh源码分析 三、初始化属性源 &#xff08;一&#xff09;GenericWebApplicationContext初始化属性源 &#xff08;二&#xff09;StaticWebApplicationContext初始化属性源 四、初始化早期事件集合…

25.UE5时间膨胀,慢动作,切换地图,刷BOSS

2-27 时间膨胀、慢动作、切换地图、刷BOSS_哔哩哔哩_bilibili 目录 1.刷新BOSS逻辑 2.时间膨胀实现慢动作 3.胜利画面&#xff0c;下一关 3.1胜利画面UI 3.2第一关、第二关游戏模式 3.3下一关按钮事件的绑定 1.刷新BOSS逻辑 实现当场上的怪物都死亡后&#xff0c;进行刷…

自己编写的前后端分离程序,解决跨域问题

跨域问题在前端解决很麻烦&#xff0c;既然前后端都是自己编写的&#xff0c;就直接在后端解决了。 1. 后端中 在controller文件中加上 CrossOrigin // 解决跨域问题&#xff0c;不加的话虽然数据能正常传输&#xff0c;但是前端页面会没有正常响应 2. 前端中 可以正常访问…

小米顾此失彼:汽车毛利大增,手机却跌至低谷

科技新知 原创作者丨依蔓 编辑丨蕨影 三年磨一剑的小米汽车毛利率大增&#xff0c;手机业务毛利率却出现下滑景象。 11月18日&#xff0c;小米集团发布 2024年第三季度财报&#xff0c;公司实现营收925.1亿元&#xff0c;同比增长30.5%&#xff0c;预估902.8亿元&#xff1b;…