向量:深度学习的核心基石

一、什么是向量?🧭

在深度学习的世界里,向量是最基础、也是最重要的概念之一。

它不仅用来表示数据,更贯穿了模型的输入、输出以及内部计算的每一个环节。下面我们从几个角度,把它彻底讲明白。

1. 基本定义

向量,本质上是一组有序排列的数字。这些数字被称为向量的「分量」或「元素」。

  • 在二维空间里,一个点可以用「横坐标 + 纵坐标」两个数来表示;

  • 在三维空间里,则需要「长、宽、高」三个数;

  • 更一般地,在 n 维空间中,一个向量就是 n 个实数组成的序列

2. 向量在机器学习中的作用

向量几乎撑起了机器学习的半边天:

  • 特征表示:原始数据要先转换成数值形式,机器才能处理。这些特征值按顺序排好,就成了「特征向量」。

  • 模型参数:很多算法内部都在学习「权重向量」。比如线性回归,就是在找一个最优的权重向量 w,让预测值和真实值的误差最小。

  • 嵌入层(Embedding):在 NLP 领域,词向量、句向量被用来捕捉文本语义。训练得到的词嵌入,能真实反映词语之间的语义关系。

3. 向量的运算规则

向量支持加法、减法、标量乘法等基础运算,还有内积(点积)、外积(叉积)等特殊运算。

正是这些运算,构建起了复杂神经网络的底层骨架。

4. 向量化编程

为了提升效率,工程师往往会用「向量化」技术------一次性对整个数组做运算,而不是逐个元素循环处理

这种方式极大加速了计算,也让代码更简洁优雅。

5. 向量空间模型

这是信息检索中的经典方法:把文档表示成由词汇项组成的向量,每个维度对应一个术语,值则反映该术语在文档中的重要程度。

于是,「文档相似度」就变成了「向量相似度」的计算问题

"

一句话总结:向量是连接理论与实践的桥梁。理解了向量,你就握住了理解现代大模型的钥匙。🔑


二、向量的数学本质 📐

向量是既有大小、又有方向的量。在数学中,我们通常用「有向线段」来表示它------线段的长度代表大小,箭头的指向代表方向。

关于向量,有几条关键性质值得记住:

  1. 向量只有方向和大小 。等长、平行、同向的向量,因为大小和方向都一致,就是同一个向量,也叫相等向量

  2. 两个向量相等可以用「=」连接。但向量之间没有大于或小于关系,不能用「>」「<」连接。

  3. 方向相同或相反的向量,叫作共线向量 ,也叫平行向量

  4. 向量之间可以用「∥」或「⊥」连接。其中「ab 」表示 a 与 b 共线,「ab」表示 a 与 b 所在直线互相垂直。

  5. 向量 a 的大小,又称长度、模长 ,简称 ,记作 |a|。

  6. 模长是数值而非向量,所以可以用「>」「<」比较大小。

  7. 长度为 0 的向量叫作零向量

  8. 零向量与任意向量平行。

  9. 长度为 1 个单位的向量叫作单位向量


三、什么是向量化?🔄

一句话:向量化,就是把文档、字符等内容,转换成一组浮点数组。

"

下图来源于 Qdrant 官方文档(向量检索概述章节)。


四、实战示例:LangChain4j + Qdrant 💻

理论讲够了,接下来上真代码。我们参考 LangChain4j 官方示例(Qdrant Embedding Store Example),来跑通整条链路。

官方案例:环境搭建

新建模块,配置 pom.xml

复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.whc</groupId>
    <artifactId>langChain4j-whc</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>langchain4j-whc-embedding</artifactId>
  <packaging>jar</packaging>

  <name>langchain4j-whc-embedding</name>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--langChain4j -->
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-open-ai</artifactId>
    </dependency>
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j</artifactId>
    </dependency>

    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-reactor</artifactId>
    </dependency>

    <!--  阿里百炼平台 -->
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
    </dependency>

    <!--lombok-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <!--hutool-->
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>5.8.22</version>
    </dependency>

    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-spring-boot-starter</artifactId>
    </dependency>


    <!--qdrant相关-->
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-qdrant</artifactId>
      <version>1.5.0-beta11</version>
    </dependency>

    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
      <version>1.5.0-beta11</version>
    </dependency>

    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>qdrant</artifactId>
      <version>1.19.6</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
    </build>
</project>

核心代码(含详细注释)

在官方类的基础上稍作修改,并补充了完整注释,方便理解每一步在做什么:

复制代码
public class QdrantEmbeddingStoreExample {

    /**
     * gRPC服务端口配置
     * 用于连接向量数据库的gRPC通信端口
     */
    private static int grpcPort = 6334;

    /**
     * 集合名称配置
     * 指定向量数据库中使用的集合名称
     */
    private static String collectionName = "langchain4j-whc";

    /**
     * 距离计算方式配置
     * 设置向量相似度计算的距离度量方式为余弦距离
     */
    private static Collections.Distance distance = Collections.Distance.Cosine;

    /**
     * 向量维度配置
     * 指定向量的维度大小为384维
     */
    private static int dimension = 384;

    /**
     * 数据库主机地址配置
     * 向量数据库服务器的IP地址
     */
    private static final String HOST = "192.168.100.130";

    /**
     * 数据库端口配置
     * 向量数据库服务的端口号,与gRPC端口相同
     */
    private static final int PORT = 6334;


    /**
     * 主函数,演示了使用Qdrant向量数据库进行文本嵌入存储和相似性搜索的完整流程
     *
     * @param args 命令行参数数组
     * @throws ExecutionException 当异步执行出现异常时抛出
     * @throws InterruptedException 当线程被中断时抛出
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        // 创建Qdrant嵌入存储实例,用于存储和检索文本嵌入向量
        EmbeddingStore<TextSegment> embeddingStore =
        QdrantEmbeddingStore.builder()
        .host(HOST)
        .port(PORT)
        .collectionName(collectionName)
        .build();

        // 创建Qdrant客户端,用于与Qdrant服务器进行通信
        QdrantClient client =
        new QdrantClient(
            QdrantGrpcClient.newBuilder(HOST, PORT, false)
            .build());

        // 异步创建集合,定义向量的维度和距离计算方式
        client
        .createCollectionAsync(
            collectionName,
            Collections.VectorParams.newBuilder().setDistance(distance).setSize(dimension).build())
        .get();

        // 初始化嵌入模型,用于将文本转换为向量表示
        EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();

        // 创建并存储第一个文本段落的嵌入向量
        TextSegment segment1 = TextSegment.from("I've been to France twice.");
        Embedding embedding1 = embeddingModel.embed(segment1).content();
        embeddingStore.add(embedding1, segment1);

        // 创建并存储第二个文本段落的嵌入向量
        TextSegment segment2 = TextSegment.from("New Delhi is the capital of India.");
        Embedding embedding2 = embeddingModel.embed(segment2).content();
        embeddingStore.add(embedding2, segment2);

        // 执行相似性搜索,查找与查询文本最相似的文本段落
        Embedding queryEmbedding = embeddingModel.embed("Did you ever travel abroad?").content();
        EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
        .queryEmbedding(queryEmbedding)
        .maxResults(1)
        .build();
        List<EmbeddingMatch<TextSegment>> matches = embeddingStore.search(embeddingSearchRequest).matches();
        EmbeddingMatch<TextSegment> embeddingMatch = matches.get(0);

        // 输出匹配结果的相似度分数和文本内容
        System.out.println(embeddingMatch.score());
        System.out.println(embeddingMatch.embedded().text());
    }

}

这里的嵌入模型用的是 all-MiniLM-L6-v2

"

💡 all-MiniLM-L6-v2 是 MiniLM 系列的一个小型语言模型,通过「知识蒸馏」技术从更大的模型压缩而来。它的目标是------在保持较高性能的同时,尽可能降低计算资源需求,非常适合本地和轻量场景。

✅ 测试一下

代码里把两条英文句子转成了向量并保存下来:

然后提问:哪句话更适合回答 "Did you ever travel abroad?"

结果如下:

复制代码
0.7378319074630284
I've been to France twice.

「我去过法国两次」------完美命中,模型准确理解了「出国旅行」的语义。🎯


五、接口方式:接入阿里百炼向量模型 🔌

模型选择

这里我们选用阿里云百炼平台的向量模型 text-embedding-v3

配置类

复制代码
@Slf4j
@Getter
@Configuration
public class LlmConfig {

    @Value("${ai.dashScope.apiKey}")
    private String dashScopeApiKey;

    @Value("${ai.dashScope.modelName}")
    private String dashScopeModelName;

    @Value("${ai.dashScope.baseUrl}")
    private String dashScopeBaseUrl;

    @Value("${qdrant.host}")
    private String qdrantHost;

    @Value("${qdrant.port}")
    private int qdrantPort;

    @Value("${qdrant.collectionName}")
    private String qdrantCollectionName;

    /**
     * 创建EmbeddingModel 向量模型
     *
     * @return EmbeddingModel
     */
    @Bean
    public EmbeddingModel embeddingModel() {
        return OpenAiEmbeddingModel.builder()
        .apiKey(dashScopeApiKey)
        .modelName(dashScopeModelName)
        .baseUrl(dashScopeBaseUrl)
        .build();
    }


    /**
     * 创建Qdrant客户端
     *
     * @return QdrantClient
     */
    @Bean
    public QdrantClient qdrantClient() {
        QdrantGrpcClient.Builder grpcClientBuilder =
        QdrantGrpcClient.newBuilder(qdrantHost, qdrantPort, false);
        return new QdrantClient(grpcClientBuilder.build());
    }

    @Bean
    public EmbeddingStore<TextSegment> embeddingStore() {
        return QdrantEmbeddingStore.builder()
        .host(qdrantHost)
        .port(qdrantPort)
        .collectionName(qdrantCollectionName)
        .build();
    }


}

YAML 配置

复制代码
  port: 9008
  servlet:
    encoding:
      enabled: true
      force: true
      charset: UTF-8

spring:
  application:
    name: langchain_whc_embedding


ai:
  dashScope:
    # 配置dashScope
    apiKey: ${AI_DASHSCOPE_API_KEY}
    modelName: ${AI_DASHSCOPE_MODEL_NAME}
    baseUrl: ${AI_DASHSCOPE_BASE_URL}

qdrant:
  host: ${QDRANT_HOST}
  port: ${QDRANT_PORT}
  collectionName: ${QDRANT_COLLECTION_NAME}

Controller

复制代码
@Slf4j
@RestController
@RequiredArgsConstructor
public class EmbeddingController {

    private final LlmConfig llmConfig;

    private final EmbeddingModel embeddingModel;

    private final QdrantClient qdrantClient;

    private final EmbeddingStore<TextSegment> embeddingStore;


    @GetMapping(value = "/embedding/createCollection")
    public void createCollection() {
        var vectorParams = Collections.VectorParams.newBuilder()
        .setDistance(Collections.Distance.Cosine)
        .setSize(1024)
        .build();
        qdrantClient.createCollectionAsync(llmConfig.getQdrantCollectionName(), vectorParams);
    }

}
✅ 测试新增 Collection

访问接口:http://localhost:9008/embedding/createCollection

测试结果:成功新增了一个 Collection。

✅ 测试新增 Points

在 Controller 中增加接口,这次我们塞一首自创的词进去:

复制代码
    /**
     * 处理GET请求,向嵌入存储中添加文本段落的嵌入向量
     *
     * @return 添加操作的结果标识符字符串
     */
    @GetMapping(value = "/embedding/add")
    public String add() {
        // 定义《永遇乐・师徒取经》词牌的文本内容作为待处理文本
        String prompt = """
                     永遇乐・师徒取经
                万里西行,风沙漫卷,云路迢递。玄奘慈眉,凝眸西向,志比金石厉。
                悟空火眼,金箍掣处,妖魅现形惊悸。笑猪痴、贪眠恋栈,也随杖履千里。
                沙憎谨肃,行囊稳护,默默担当行李。九九磨难,千般险厄,终把真经觅。东归胜果,流传千古,漫说佛门真谛。
                到如今、传奇犹在,世人共记。
                     """;

        // 将文本内容转换为文本段落对象,并添加元数据信息
        TextSegment segment = TextSegment.from(prompt);
        segment.metadata().put("author", "robot");
        segment.metadata().put("title", "永遇乐・师徒取经");

        // 生成文本段落的嵌入向量
        Embedding embedding = embeddingModel.embed(segment).content();

        // 将嵌入向量和文本段落添加到嵌入存储中
        String result = embeddingStore.add(embedding, segment);
        log.info(result);
        return result;
    }

测试效果:

"

你可以多塞几条 points 进去,让检索结果更有对比性。

✅ 测试查询
复制代码
    /**
     * 处理/embedding/query1路径的GET请求,用于查询与"写爱情的诗"相关的嵌入向量匹配结果
     * 该方法不接受参数,无返回值
     */
    @GetMapping(value = "/embedding/query")
    public void query1() {
        // 生成查询文本"写爱情的诗"的嵌入向量
        Embedding queryEmbedding = embeddingModel.embed("与爱情相关的").content();

        // 构建嵌入搜索请求
        EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
                .queryEmbedding(queryEmbedding)
                .build();

        // 执行嵌入向量搜索
        EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(embeddingSearchRequest);

        // 输出查询结果
        searchResult.matches().forEach(match -> {
            log.info("\n匹配度得分---->"+ match.score());
            log.info("\n"+match.embedded().text());
        });
    }

测试结果有点意思------排序跟直觉略有出入:

当然,也可以加上过滤条件,精确锁定某个标题:

复制代码
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
                .queryEmbedding(queryEmbedding)
                .filter(metadataKey("title").isEqualTo("鹧鸪天・别绪"))
                .build();

六、写在最后 📝

从「向量是什么」到「用 LangChain4j + Qdrant 跑通一整套向量检索」,我们走完了从理论到工程的完整闭环:

  • 理论层:向量是有序数字组,是大模型理解世界的语言;

  • 工程层:向量化把文本变成浮点数组,向量数据库负责高效存储与相似度检索;

  • 实践层:无论是本地的 all-MiniLM-L6-v2,还是云端的 text-embedding-v3,都能轻松接入。

理解了向量,你就理解了大模型「思考」的底层方式。剩下的,就是动手把它跑起来。💪

相关推荐
数智化管理手记19 小时前
全面预算管理执行偏差大?全面预算管理全流程落地步骤是什么
大数据·网络·数据库·人工智能·数据挖掘
小码哥哥19 小时前
百度开发者中心软文_企业AI知识库的价值
人工智能·百度
aneasystone本尊19 小时前
学习 OpenMontage 的 12 条流水线
人工智能
rain_sxr19 小时前
十万点不崩:ECharts 大数据量渲染的降采样与增量更新策略
人工智能
小刘学技术19 小时前
AI人工智能决策树分类器:原理、实现与应用
开发语言·人工智能·python·算法·决策树·机器学习·数据挖掘
郑板桥3019 小时前
# AI行业周报(2026.7.11—7.19):全球AI治理新秩序诞生,中美技术博弈进入深水区
人工智能
AI刀刀19 小时前
deepseek 内容粘贴后符号丢失怎么办?AI 导出鸭实测解决排版乱码问题
android·人工智能·excel·ai导出鸭
dozenyaoyida19 小时前
GPT-5.6 Sol vs Claude Fable 5:100美元预算下的成本-智能比分析
人工智能·gpt·gpt-5.6·claude fable5
大模型码小白19 小时前
Milvus 架构设计:从向量索引到分布式检索,AI 原生存储引擎的内部机制
java·大数据·人工智能·分布式·深度学习·milvus