Embedding词向量技术详解

2026年4月16日 · 589 字 · 3 分钟

什么是Embedding词向量技术,以及如何使用eino框架实现文本向量化

什么是Embedding词向量技术

Embedding(嵌入)是自然语言处理中的核心技术,它将文本转换为高维向量表示。简单来说,就是把人类可读的文字转换成计算机能够理解和计算的一串数字。

核心概念

向量化

每个词或句子都会被映射到一个高维向量空间中。例如:

"大连理工大学" → [0.123, -0.456, 0.789, ...]  // 768维或更高
"中国海洋大学" → [0.234, -0.345, 0.678, ...]

语义相似度

在向量空间中,语义相近的词语距离更近。这是Embedding最神奇的地方:

  • “大连理工大学” 和 “中国海洋大学” 的相似度很高(都是大学)
  • “大连” 和 “青岛” 的相似度中等(都是城市)
  • “大连理工大学” 和 “青岛” 的相似度较低(不同类型)

应用场景

  1. 语义搜索:根据查询内容的语义进行搜索,而非简单的关键词匹配
  2. RAG(检索增强生成):为AI提供相关上下文知识
  3. 推荐系统:根据用户喜好推荐相似内容
  4. 聚类分析:将相似文本归类

使用Eino框架实现Embedding

Eino是字节跳动开源的Golang AI应用开发框架,提供了丰富的组件支持。我们使用其eino-ext中的Ollama Embedding组件。

安装依赖

go get github.com/cloudwego/eino-ext/components/embedding/ollama

配置说明

首先需要一个配置文件config.json

{
  "embedding_api_url": "http://localhost:11434",
  "embedding_model": "nomic-embed-text"
}

这里使用Ollama本地部署的nomic-embed-text模型,你也可以替换成其他Embedding模型。

代码实现

初始化Embedder

package rag

import (
	"context"
	"eino_study/config"
	"fmt"
	"log"
	"math"

	"github.com/cloudwego/eino-ext/components/embedding/ollama"
)

var Embeddder *ollama.Embedder

func NewEmbedder() *ollama.Embedder {
	ctx := context.Background()
	var err error
	config := config.NewConfig("../config/config.json")
	Embeddder, err = ollama.NewEmbedder(ctx, &ollama.EmbeddingConfig{
		BaseURL: config.EmbeddingAPIURL,
		Model:   config.EmbeddingModel,
	})
	if err != nil {
		log.Fatalf("NewEmbedder failed, err=%v", err)
	}
	return Embeddder
}

ollama.NewEmbedder创建了一个Embedding客户端,配置了API地址和模型名称。

文本向量化

func Embedding(ctx context.Context, text []string) ([][]float64, error) {
	embedder := NewEmbedder()
	embeddings, err := embedder.EmbedStrings(ctx, text)
	if err != nil {
		log.Fatalf("EmbedStrings failed, err=%v", err)
	}
	// 归一化处理
	for i, v := range embeddings {
		embeddings[i] = NormalizeVector(v)
	}
	return embeddings, nil
}

EmbedStrings方法将字符串数组转换为向量数组。返回的是一个二维切片[][]float64,每个字符串对应一个向量。

向量归一化

归一化是将向量缩放到单位长度,这样计算相似度时更方便:

func NormalizeVector(v []float64) []float64 {
	if len(v) == 0 {
		return nil
	}
	// 计算向量的模长
	a := 0.
	for _, x := range v {
		a += x * x
	}
	a = math.Sqrt(a)
	// 归一化向量
	for i := range v {
		v[i] /= a
	}
	return v
}

归一化后,向量的模长为1,此时向量的内积等于余弦相似度。

计算相似度

// 计算向量内积
// 如果向量已经归一化,那么内积的结果就是向量之间的夹角余弦值
func DotProduct(v1, v2 []float64) float64 {
	if len(v1) != len(v2) {
		return 0.
	}
	a := 0.
	for i := range v1 {
		a += v1[i] * v2[i]
	}
	return a
}

内积是最常用的相似度计算方法,值越大表示越相似。

文档向量化

当需要表示一段包含多个词的文本时,可以用这些词向量的平均值:

// 多个向量按位求平均
func AvgOfVector(vectors [][]float64) ([]float64, error) {
	if len(vectors) == 0 {
		return nil, nil
	}
	if len(vectors) == 1 {
		return NormalizeVector(vectors[0]), nil
	}
	sum := make([]float64, len(vectors[0]))
	for i := 0; i < len(vectors); i++ {
		if len(vectors[i]) != len(sum) {
			return nil, fmt.Errorf("%dth vector dim not equal to first vector", i+1)
		}
		for index, v := range vectors[i] {
			sum[index] += v
		}
	}
	for i := range sum {
		sum[i] /= float64(len(vectors))
	}
	return NormalizeVector(sum), nil
}

完整示例

让我们看一个完整的词向量相似度计算示例:

func WordSim() {
	// 定义待比较的词语
	words := []string{"大连理工大学", "中国海洋大学", "大连", "青岛"}
	ctx := context.Background()

	// 获取向量
	embeddings, err := Embedding(ctx, words)
	if err != nil {
		log.Fatal(err)
	}

	// 计算两两之间的相似度
	for i := 0; i < len(words); i++ {
		for j := i + 1; j < len(words); j++ {
			dot := DotProduct(embeddings[i], embeddings[j])
			fmt.Printf("sim of %s and %s is %.4f\n", words[i], words[j], dot)
		}
	}

	// 文档向量化示例
	// doc1: 大学相关文档
	doc1, _ := AvgOfVector([][]float64{embeddings[0], embeddings[1]})
	// doc2: 城市相关文档
	doc2, _ := AvgOfVector([][]float64{embeddings[2], embeddings[3]})

	// 测试新词与文档的相似度
	words = []string{"中国计量大学", "杭州"}
	vectors, _ := Embedding(ctx, words)
	for i, vector := range vectors {
		dot := DotProduct(vector, doc1)
		fmt.Printf("sim of word %s and doc1 is %.4f\n", words[i], dot)
		dot = DotProduct(vector, doc2)
		fmt.Printf("sim of word %s and doc2 is %.4f\n", words[i], dot)
	}
}

运行结果

=== RUN   TestEmbedding
sim of 大连理工大学 and 中国海洋大学 is 0.8183
sim of 大连理工大学 and 大连 is 0.8241
sim of 大连理工大学 and 青岛 is 0.5443
sim of 中国海洋大学 and 大连 is 0.6569
sim of 中国海洋大学 and 青岛 is 0.5597
sim of 大连 and 青岛 is 0.5946

sim of word 中国计量大学 and doc1 is 0.9411
sim of word 中国计量大学 and doc2 is 0.6970
sim of word 杭州 and doc1 is 0.5964
sim of word 杭州 and doc2 is 0.6367
--- PASS: TestEmbedding (0.67s)
PASS

结果分析

词语相似度

词语对 相似度 解释
大连理工大学 vs 中国海洋大学 0.8183 两所大学,语义非常相近
大连理工大学 vs 大连 0.8241 大学位于大连,关联性强
中国海洋大学 vs 大连 0.6569 大学与城市的中等相关性
大连 vs 青岛 0.5946 两个城市,有一定相似度
大连理工大学 vs 青岛 0.5443 大学与城市,关联较弱

文档相似度

测试词 doc1(大学文档) doc2(城市文档)
中国计量大学 0.9411 0.6970
杭州 0.5964 0.6367
  • “中国计量大学"与大学文档相似度极高(0.9411),说明文档向量正确捕捉了"大学"这一语义特征
  • “杭州"与城市文档相似度稍高(0.6367 > 0.5964),符合预期