当前位置: 首页 > news >正文

Django 集成 PostgreSQL pgvector 实现文本相似度检索

1. 项目概述:为什么在 Django 里做文本相似度不是“炫技”,而是解决真实业务痛点

“Implement Text Similarity with Embeddings in Django”——这个标题乍看像是一篇技术博客的常规选题,但如果你真在电商后台写过商品搜索推荐、在 SaaS 平台维护过客服知识库、在教育系统里搭过习题去重模块,你就会明白:这根本不是“要不要做”的问题,而是“再不做就天天救火”的现实压力。我上一家公司做在线法律咨询平台,用户提交的咨询问题里,73% 是语义重复但表述各异的变体:“离婚财产怎么分?”“结婚后买的房子离婚怎么判?”“婚内买房离婚时归谁?”——用关键词匹配漏掉一半,用传统 TF-IDF + 余弦相似度,准确率卡在 61%,法务团队每天手动合并相似问法,平均每人每天耗时 2.4 小时。直到我们把文本嵌入(Embeddings)真正落地到 Django 生产环境,相似问法自动聚类准确率升至 89.7%,人工复核时间压缩到 17 分钟/天。这不是模型参数调优的胜利,而是把向量计算、数据库索引、HTTP 请求生命周期、Django ORM 的惰性加载特性、缓存穿透防护全部拧成一股绳的结果。它不依赖大语言模型 API,不引入外部服务,所有向量生成、存储、检索都在 Django 应用内部闭环完成;它适配中小团队技术栈——不需要 Kubernetes 集群,一台 4 核 16G 的云服务器就能扛住日均 50 万次相似查询;它对开发者友好——不用改写整个搜索架构,只需新增一个SimilarityManager和三处 Model 字段扩展。如果你正被“用户搜不到相似内容”“知识库重复率高”“推荐结果千篇一律”这些问题反复困扰,又不想推倒重来上 Elasticsearch 或专用向量数据库,那么这篇就是为你写的实操手记。它不讲论文里的 cosine similarity 公式推导,只告诉你:在哪一行代码里加.as_vector(),为什么 PostgreSQL 的vector类型比JSONField存 embedding 更稳,以及当用户输入“微信转账没到账怎么办”而数据库里只有“微信支付未到账申诉流程”时,如何让 Django 在 127ms 内返回 top-3 最近似条目。

2. 整体设计与技术选型:为什么放弃“看起来更酷”的方案,选择这条“土路”

2.1 不选 FAISS / Milvus / Pinecone 的底层逻辑

很多初学者看到“文本相似度”,第一反应是查 FAISS 教程或开个 Pinecone 账号。我试过——在本地开发环境跑通了,但上线前卡在三个无法绕开的硬伤上:

  • 部署复杂度爆炸:FAISS 需要编译 C++ 扩展,Docker 镜像体积从 320MB 涨到 1.2GB,CI/CD 流水线构建时间从 4 分钟拉长到 18 分钟;Milvus 要求独立 etcd + minio + pulsar 三组件,运维成本远超业务价值;
  • Django 生态割裂:这些库的向量检索接口和 Django QuerySet 完全不兼容。你想对“已发布且审核通过”的文章做相似检索?得先用.values_list('id', flat=True)拿出 ID 列表,再传给 FAISS 做 ID 映射,最后再用Article.objects.filter(id__in=...)反查——三次 IO,延迟翻倍,缓存失效;
  • 事务一致性崩塌:用户编辑文章后保存,embedding 向量必须同步更新。FAISS 要求显式调用index.add(),而 Django 的save()方法里混入 C++ 调用,一旦向量更新失败,文章却已落库,数据永远不一致。

所以最终我们砍掉所有“向量数据库”选项,回归关系型数据库——但不是用TextField存 JSON,而是直接用 PostgreSQL 15+ 原生vector类型。理由很实在:

提示:PostgreSQL 的pgvector扩展已支持 IVFFlat 和 HNSW 索引,100 万条 384 维向量的 10-NN 查询 P95 延迟稳定在 83ms,且完全兼容 Django 的 ACID 事务。你更新 Article 模型,向量字段自动更新,无需额外事务控制。

2.2 为什么 Embedding 模型锁定 sentence-transformers/all-MiniLM-L6-v2

选模型不是比参数量,而是比“在中文短文本上的鲁棒性”和“推理速度”。我们横向测试了 7 个开源模型在自建法律咨询语料(平均长度 28 字)上的表现:

模型平均向量维度CPU 推理耗时(单句)相似度区分度(标准差)中文停用词鲁棒性
bert-base-chinese768412ms0.18差(“的”“了”权重过高)
paraphrase-multilingual-MiniLM-L12-v2384298ms0.22中等(需额外分词预处理)
all-MiniLM-L6-v2384137ms0.29强(内置中文 tokenization)
text2vec-large-chinese1024683ms0.25强,但内存占用翻倍

关键发现:all-MiniLM-L6-v2在中文短句上区分度最高——这意味着“离婚协议怎么写”和“离婚冷静期多长”两个 query 的向量余弦距离天然拉开,后续阈值设定更宽松,误召率更低。更重要的是,它由 sentence-transformers 官方维护,Hugging Face 上有现成的transformers+torch无 GPU 推理封装,Django 视图里直接model.encode(text)即可,没有 CUDA 版本兼容性烦恼。我们甚至把它打包进 Docker 镜像时做了量化:用optimum库转成 ONNX,再用onnxruntime运行,CPU 推理耗时压到 92ms,内存占用从 1.2GB 降到 480MB——这对共享宿主机的中小团队是救命级优化。

2.3 Django 层架构:三层解耦,拒绝“向量污染”核心逻辑

我们严格划分职责边界,确保相似度功能可插拔、可降级、可监控:

  • Embedding 层:独立embedding.py模块,只暴露text_to_vector(text: str) -> List[float]接口。内部封装模型加载、缓存、异常兜底(如超时返回零向量),业务代码完全不知晓模型细节;
  • Storage 层:在models.py中为需要相似检索的 Model 添加vector字段,并通过VectorField自定义字段类型(继承django.db.models.Field),重写from_db_valueget_prep_value方法,实现list[float]与 PostgreSQLvector类型的双向转换;
  • Query 层:提供SimilarityQuerySet类,继承models.QuerySet,注入similar_to()方法。调用时写Article.objects.similar_to("合同违约赔偿"),背后自动拼接ORDER BY embedding <=> %s LIMIT 10,返回标准 QuerySet,可链式调用.filter(status='published')

这种设计让相似度功能像一个“乐高积木”:今天用 MiniLM,明天想换bge-small-zh-v1.5,只需改embedding.py里两行代码;某天流量激增,临时关闭相似检索?注释掉SimilarityQuerySet的注册即可,业务逻辑零修改。

3. 核心细节解析与实操要点:那些文档里不会写的“脏活”

3.1 PostgreSQL 向量字段的正确打开方式

很多人以为装上pgvector扩展,加个vector(384)字段就完事了。实际踩坑后发现,至少有 4 个隐藏雷区:

  • 字段定义必须带精度声明vector(384)是合法的,但vector(无括号)会报错。更致命的是,如果模型输出维度是 384,而字段定义成vector(768),PostgreSQL 会静默截断后 384 维,导致向量失真——我们曾因此出现 37% 的相似结果漂移;
  • NULL 值处理必须显式声明vector字段默认允许 NULL,但ORDER BY embedding <=> %s遇到 NULL 会返回空结果而非跳过。解决方案是在SimilarityQuerySet.similar_to()中强制过滤:self.filter(embedding__isnull=False)
  • 索引策略决定性能生死:IVFFlat 索引要求指定lists参数,经验值是sqrt(n)(n 为总向量数)。10 万条数据设lists=316,100 万条设lists=1000。HNSW 索引虽更快但内存占用高,我们生产环境用 IVFFlat +m=16, ef_construction=64,平衡速度与内存;
  • 迁移脚本必须包含扩展启用:Django migration 里不能只写AddField,必须前置执行CREATE EXTENSION IF NOT EXISTS vector;。我们封装了VectorExtensionMigration基类,在forwards()第一行调用apps.get_app_config('your_app').enable_vector_extension()
# models.py from django.contrib.postgres.fields import VectorField from django.db import models class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() # 关键:维度必须与模型输出严格一致 embedding = VectorField(dimensions=384, null=True, blank=True) class Meta: indexes = [ # IVFFlat 索引,注意 lists 参数需根据数据量调整 models.Index( fields=['embedding'], name='article_embedding_ivfflat_idx', opclasses=['vector_cosine_ops'], # 注意:此参数必须在 migration 中显式设置 # 通过 RunSQL 执行 CREATE INDEX ... WITH (lists=1000) ), ]

3.2 向量生成时机:为什么不在 save() 里实时计算?

直觉上,用户保存文章时立刻调用model.encode()生成向量最省事。但我们强制改为异步任务,原因有三:

  • 请求响应不可控:MiniLM 单次 encode 耗时 137ms,若并发 50 请求,Django worker 线程池瞬间打满,HTTP 超时率飙升;
  • 模型加载冷启动:首次调用encode()会触发模型加载和 tokenizer 初始化,耗时 2.3 秒,所有并发请求卡死;
  • 错误传播灾难:网络抖动、内存不足导致 encode 失败,文章已保存但向量为空,后续相似检索永远漏掉该条目。

解决方案:用 Django Q 或 Celery 做队列化。我们选 Django Q 因为轻量——pip install django-q,配置Q_CLUSTER后,save()方法里只发消息:

# models.py from django_q.tasks import async_task def save(self, *args, **kwargs): super().save(*args, **kwargs) # 先保存基础字段 # 异步触发向量生成,传入 self.id async_task('myapp.embedding.generate_article_embedding', self.id)

embedding.py里接收任务:

# embedding.py from sentence_transformers import SentenceTransformer import numpy as np # 全局单例,避免重复加载 _model = None def get_model(): global _model if _model is None: _model = SentenceTransformer('all-MiniLM-L6-v2') return _model def generate_article_embedding(article_id: int): from myapp.models import Article try: article = Article.objects.get(id=article_id) # 关键:用 .values_list() 避免 ORM 惰性加载 content 字段(可能很大) text = f"{article.title} {article.content[:500]}" # 截断防 OOM vector = get_model().encode(text).tolist() # 原子更新,避免 race condition Article.objects.filter(id=article_id).update(embedding=vector) except Article.DoesNotExist: pass # 文章已被删,忽略 except Exception as e: # 记录错误,但不抛出,防止任务队列阻塞 logger.error(f"Failed to generate embedding for article {article_id}: {e}")

注意:get_model()必须是模块级函数,不能放在类里。Celery worker 启动时会导入模块,此时加载模型,后续所有任务复用同一实例,冷启动只发生一次。

3.3 相似度查询的精度陷阱:余弦距离 vs 点积,为什么必须用<=>

PostgreSQLpgvector支持多种距离操作符:<->(L2 距离)、<#>(内积)、<=>(余弦距离)。新手常误用<->,导致结果完全错误。原因在于:余弦相似度公式是cosθ = A·B / (||A|| ||B||),而<->计算的是||A - B||²,二者数学上不等价。我们实测过:对同一组向量,用<->排序的 top-3 和<=>排序的 top-3 重合率仅 41%。正确做法是强制使用<=>,并在 QuerySet 中封装:

# managers.py from django.contrib.postgres.search import SearchQuery from django.db import models class SimilarityQuerySet(models.QuerySet): def similar_to(self, text: str, threshold: float = 0.5, limit: int = 10): # 1. 生成查询向量(此处应走缓存,见下文) query_vector = text_to_vector(text) # 2. 构造原生 SQL,用 <=> 操作符 return self.filter( embedding__isnull=False ).extra( tables=['myapp_article'], where=["myapp_article.embedding <=> %s < %s"], params=[query_vector, 1 - threshold], # 余弦距离 = 1 - 相似度 order_by=["myapp_article.embedding <=> %s"], select={'similarity': f"1 - (myapp_article.embedding <=> %s)"}, select_params=[query_vector] )[:limit] class SimilarityManager(models.Manager): def get_queryset(self): return SimilarityQuerySet(self.model, using=self._db)

调用时:

# views.py articles = Article.objects.similar_to("工伤赔偿标准", threshold=0.6, limit=5) for a in articles: print(f"{a.title} (相似度: {a.similarity:.3f})")

4. 实操过程与核心环节实现:从零部署到线上验证的完整流水线

4.1 环境准备与 pgvector 集成(含 Docker 一键脚本)

我们放弃手动编译 pgvector,改用官方 Docker 镜像pgvector/pgvector:pg15,并编写docker-compose.yml实现一键启停:

# docker-compose.yml version: '3.8' services: db: image: pgvector/pgvector:pg15 environment: POSTGRES_DB: myproject POSTGRES_USER: django POSTGRES_PASSWORD: secret ports: - "5432:5432" volumes: - pg_data:/var/lib/postgresql/data web: build: . environment: DATABASE_URL: postgres://django:secret@db:5432/myproject depends_on: - db volumes: pg_data:

Djangosettings.py配置关键项:

# settings.py import dj_database_url DATABASES = { 'default': dj_database_url.config( default='postgres://django:secret@localhost:5432/myproject' ) } # 必须启用 pgvector 扩展 POSTGRES_EXTRA_PARAMS = { 'options': '-c search_path=public,extensions' } # 在 DATABASES['default'] 中追加 DATABASES['default'].setdefault('OPTIONS', {}).update(POSTGRES_EXTRA_PARAMS)

初始化扩展的 migration 脚本(0002_enable_pgvector.py):

# migrations/0002_enable_pgvector.py from django.db import migrations class Migration(migrations.Migration): dependencies = [ ('myapp', '0001_initial'), ] operations = [ # 关键:启用扩展 migrations.RunSQL( "CREATE EXTENSION IF NOT EXISTS vector;", reverse_sql="DROP EXTENSION IF EXISTS vector;" ), # 创建 IVFFlat 索引(需先有数据) migrations.RunSQL( "CREATE INDEX CONCURRENTLY IF NOT EXISTS article_embedding_ivfflat_idx " "ON myapp_article USING ivfflat (embedding vector_cosine_ops) " "WITH (lists = 1000);", reverse_sql="DROP INDEX CONCURRENTLY IF EXISTS article_embedding_ivfflat_idx;" ), ]

执行命令:

python manage.py makemigrations --empty myapp # 将上述代码粘贴进新生成的 migration 文件 python manage.py migrate

4.2 向量缓存层:为什么 Redis 缓存 key 要带模型哈希

实时 encode 每次都要 137ms,对高频 query(如搜索框实时联想)不可接受。我们加 Redis 缓存,但 key 设计有讲究:

# embedding.py import hashlib import redis from django.conf import settings r = redis.Redis.from_url(settings.REDIS_URL) def text_to_vector(text: str) -> List[float]: # 关键:key 必须包含模型标识,避免不同模型向量混用 model_name = 'all-MiniLM-L6-v2' key = f"emb:{model_name}:{hashlib.md5(text.encode()).hexdigest()}" cached = r.get(key) if cached: return json.loads(cached) vector = get_model().encode(text).tolist() # 缓存 7 天,过期自动清理 r.setex(key, 60*60*24*7, json.dumps(vector)) return vector

为什么用hashlib.md5(text)而不用text[:50]?因为中文文本截断可能产生哈希碰撞。我们实测过:10 万条法律咨询文本中,text[:50]的哈希冲突率达 0.8%,而 md5 冲突率为 0——这是数学保证。

4.3 相似度阈值动态校准:用业务数据反推合理 threshold

threshold=0.6是拍脑袋定的?不。我们用真实用户行为数据校准:

  • 收集 30 天内用户点击“相似文章”的日志,提取query_textclicked_article_id
  • 对每个 query,用当前模型计算其与所有文章的余弦相似度,排序取 top-10;
  • 统计“真实点击文章”在 top-10 中的命中率,按 threshold 分桶:
Threshold命中率平均返回条数业务反馈
0.492%8.7结果太多,用户说“都是废话”
0.5586%5.2可接受,但仍有噪声
0.6284%3.8法务团队确认:top-3 全是有效相似问法
0.771%2.1漏掉部分合理变体

最终选定0.62为默认阈值,并在视图中开放 query param 覆盖:/api/similar/?q=离婚&threshold=0.7。这样既保证默认体验,又留给运营人员调试空间。

4.4 线上监控埋点:如何证明“相似度功能真的提升了转化率”

不监控的 feature 等于没上线。我们在关键路径埋了 4 类指标:

  • 延迟监控:用 Django middleware 记录similar_to()耗时,上报 Prometheus;
  • 质量监控:每小时抽样 100 个 query,调用similar_to(q),检查返回结果中similarity字段是否 > threshold,低于 95% 触发告警;
  • 业务效果:在前端埋点,统计“用户输入 query 后,点击相似结果”的比例。上线后该比例从 12% 升至 34%,客服工单中“找不到答案”类投诉下降 57%;
  • 资源监控psutil监控 Django 进程内存,发现向量生成任务峰值内存达 1.8GB,于是将CELERY_WORKER_CONCURRENCY从 4 降至 2,避免 OOM。
# monitoring.py from prometheus_client import Histogram SIMILARITY_DURATION = Histogram( 'django_similarity_duration_seconds', 'Time spent on similarity queries', ['endpoint'] ) def track_similarity(query_text: str, duration: float): SIMILARITY_DURATION.labels(endpoint='article_search').observe(duration)

5. 常见问题与排查技巧实录:那些凌晨三点的报错,我们都替你踩过了

5.1 典型问题速查表

现象可能原因排查命令解决方案
ProgrammingError: operator does not exist: vector <=> vectorpgvector 扩展未启用psql -c "SELECT * FROM pg_extension;"运行CREATE EXTENSION vector;
ValueError: Expected 2D array, got 1D array insteadVectorField维度与模型输出不匹配SELECT pg_typeof(embedding) FROM myapp_article LIMIT 1;修改字段dimensions并重跑 migration
Django Q 任务堆积,Redis 内存爆满向量生成任务失败未被消费redis-cli llen "q:default"检查generate_article_embedding日志,修复异常分支
相似结果完全不相关用了<->而非<=>EXPLAIN ANALYZE SELECT * FROM myapp_article ORDER BY embedding <-> '[...]' LIMIT 5;改用<=>操作符,重建索引
Docker 启动后 pgvector 扩展不存在镜像版本与 PostgreSQL 版本不匹配docker exec -it db psql -c "SHOW server_version;"换用pgvector/pgvector:pg15镜像

5.2 独家避坑技巧

  • 技巧一:向量维度自动校验
    在 Django startup 时强制校验模型维度与字段维度一致:

    # apps.py from django.apps import AppConfig from sentence_transformers import SentenceTransformer class MyAppConfig(AppConfig): name = 'myapp' def ready(self): # 启动时加载模型并校验维度 model = SentenceTransformer('all-MiniLM-L6-v2') expected_dim = len(model.encode("test").tolist()) from myapp.models import Article actual_dim = Article._meta.get_field('embedding').dimensions if expected_dim != actual_dim: raise RuntimeError(f"Vector dimension mismatch: model={expected_dim}, field={actual_dim}")
  • 技巧二:PostgreSQL 索引重建不锁表
    IVFFlat 索引重建会锁表,我们用CONCURRENTLY选项:

    CREATE INDEX CONCURRENTLY IF NOT EXISTS article_embedding_ivfflat_idx_new ON myapp_article USING ivfflat (embedding vector_cosine_ops) WITH (lists = 1000); DROP INDEX CONCURRENTLY article_embedding_ivfflat_idx; ALTER INDEX article_embedding_ivfflat_idx_new RENAME TO article_embedding_ivfflat_idx;
  • 技巧三:Django Admin 向量可视化
    在 Admin 页面显示向量相似度,方便 QA 验证:

    # admin.py from django.contrib import admin from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ['title', 'similarity_score'] def similarity_score(self, obj): if not obj.embedding: return "—" # 计算与首条热门文章的相似度(示例) from myapp.models import Article hot = Article.objects.order_by('-views').first() if hot and hot.embedding: from sklearn.metrics.pairwise import cosine_similarity score = cosine_similarity([obj.embedding], [hot.embedding])[0][0] return f"{score:.3f}" return "—"
  • 技巧四:冷启动流量保护
    新模型上线时,用django-ratelimit限制相似查询频率,避免突发流量压垮:

    # views.py from django_ratelimit.decorators import ratelimit @ratelimit(key='user', rate='10/m', method='GET', block=True) def similar_articles(request): # ...

6. 性能压测与线上调优:百万级数据下的真实表现

6.1 压测环境与方法论

我们用 Locust 模拟真实场景:

  • 用户行为:80% 请求为/api/similar/?q=xxx,20% 为/api/articles/(普通列表);
  • 并发梯度:从 50 用户逐步加压到 1000 用户,每轮持续 5 分钟;
  • 观测指标:P95 延迟、错误率、PostgreSQLpg_stat_statementsembedding <=>查询的平均耗时、Redis 命中率。

硬件配置:

  • Web 层:2 台 4C8G Ubuntu 22.04,Gunicorn 20 workers;
  • DB 层:1 台 8C32G PostgreSQL 15.5,shared_buffers=8GBwork_mem=64MB
  • Redis:1 台 4C8G,maxmemory=4gb

6.2 压测结果与关键发现

并发用户数P95 延迟(相似查询)错误率Redis 命中率PostgreSQLembedding <=>平均耗时
100112ms0%89%78ms
300135ms0%91%82ms
500148ms0%92%83ms
800217ms0.3%93%112ms
1000342ms2.1%94%187ms

关键结论:

  • 500 并发是黄金水位:此时系统资源利用率均衡(CPU 62%,内存 71%,DB 连接数 128/200),延迟可控;
  • 瓶颈在 PostgreSQL 连接池:1000 并发时连接数打满,pgbouncer成为刚需;
  • Redis 命中率超 90% 后收益递减:从 92% 提升到 94% 对延迟影响微乎其微,不必强求 100%。

6.3 线上调优三板斧

  • 第一斧:连接池扩容
    DATABASES配置中启用pgbouncer

    DATABASES['default']['HOST'] = 'pgbouncer' DATABASES['default']['PORT'] = '6432'

    pgbouncer.ini设置pool_mode = transactiondefault_pool_size = 50

  • 第二斧:向量字段冗余存储
    对高频查询字段(如title),单独存一份title_embedding,避免每次拼接title + content[:500]

    class Article(models.Model): title_embedding = VectorField(dimensions=384, null=True, blank=True) # 在 generate_article_embedding 中同时更新两个字段
  • 第三斧:查询结果缓存
    对固定 query(如首页“热门相似问题”),用cache_page缓存 5 分钟:

    from django.views.decorators.cache import cache_page @cache_page(60 * 5) def hot_similar_questions(request): # ...

7. 后续演进与经验沉淀:从“能用”到“好用”的跨越

这个项目上线半年后,我们沉淀出三条硬经验:

  • 经验一:相似度不是终点,而是起点
    用户点击相似结果后,83% 会继续追问“那具体怎么操作?”。所以我们把相似度模块升级为“语义路由”:similar_to()返回的不仅是 Article 列表,还附带intent标签(如contract_dispute,divorce_property),前端据此加载对应的知识卡片模板。这要求 embedding 模型微调——我们用 2000 条标注数据,在 MiniLM 上做 LoRA 微调,意图识别 F1 达 0.87。

  • 经验二:向量不是银弹,必须和规则引擎共存
    纯向量检索对“数字敏感型”query 失效,比如用户搜“2023年北京最低工资标准”,向量可能召回“2022年上海社保基数”。解决方案是加规则层:检测 query 中的年份、地名、数字,用正则提取后,先走Article.objects.filter(year=2023, city='beijing'),无结果再 fallback 到向量检索。

  • 经验三:技术债必须定期偿还
    我们设立“向量健康度”月度检查:

    • 抽样 1000 条新入库文章,计算其 embedding 的 L2 范数,若均值偏离 1.0±0.1,说明模型退化;
    • 检查pg_stat_statementsembedding <=>查询的缓存命中率,低于 85% 则触发 Redis 容量告警;
    • faiss.index_cpu_to_gpu加速离线聚类,每月生成“语义热点图谱”,反哺运营选题。

最后分享一个小技巧:当你在 Django shell 里调试向量时,别用print(article.embedding)看一长串数字。用这个函数快速感知向量质量:

def vector_summary(v: List[float]) -> str: """打印向量摘要:维度、范数、非零元素占比、前5维""" import numpy as np arr = np.array(v) norm = np.linalg.norm(arr) sparsity = np.count_nonzero(arr) / len(arr) return f"dim={len(v)}, norm={norm:.3f}, sparse={sparsity:.1%}, head={arr[:5].round(3)}" # 在 shell 里 >>> vector_summary(Article.objects.first().embedding) 'dim=384, norm=0.998, sparse=100.0%, head=[-0.023 0.015 -0.008 0.032 -0.011]'

Norm 接近 1.0 说明模型输出正常(MiniLM 输出是单位向量),sparsity=100% 说明没有全零向量——这是判断 embedding 是否“活着”的最快方法。

http://www.jsqmd.com/news/1026280/

相关文章:

  • 永久免费去水印软件推荐电脑手机都能用!2026在线免费去水印网站与无广告安全工具实测
  • 语音通话级压缩!2026免费音频转OPUS在线工具保姆级教程(含批量处理) - 时时资讯
  • Vue2升Vue3踩坑实录:GoGoCode自动转换后,我手动修复了这些CSS和插槽问题
  • 别再被认证卡脖子!一招CV_ASSUME_DISTID搞定Oracle 19c RAC在RHEL 8上的安装报错
  • 视频修复终极指南:用Untrunc轻松拯救损坏的MP4/MOV文件
  • 乌鲁木齐黄金回收,上门服务靠谱吗?永盛黄金回收:十余年老店,却到您家办 - 资讯快报
  • 十秒做出专属表情包!2026免费视频转GIF保姆级全攻略(含国内小程序+3大国外平台) - 时时资讯
  • Divinity Mod Manager:终极《神界:原罪2》模组管理解决方案
  • 数据科学实习求职实战:SQL+业务理解驱动的3场景闭环法
  • 3步搞定赛马娘DMM版汉化:umamusume-localify终极指南
  • 别再用默认配置了!手把手教你复现VSFTPD 2.3.4笑脸后门漏洞,附Metasploit实战
  • 深入解析e300核心:中断、MMU与超标量流水线实战指南
  • java中的路径处理、左右斜杠
  • 北京家具维修翻新全屋家具维修推荐良匠千艺连锁口啤榜 - 我叫一
  • Bilibili-Evolved完整快捷键指南:10个提升B站体验的隐藏技巧
  • Mac上Homebrew安装的MySQL启动报错?别急着重装,先试试这个数据目录初始化大法
  • 湖南长沙考研机构选那家好?认准博闻考研,靠谱正规更安心 - 长沙考研集训营
  • 2026亲测正规901环氧乙烯基酯树脂厂 - 资讯快报
  • ArduinoFFT信号处理实战:如何在嵌入式设备上实现专业级频谱分析
  • Libvirt管理LXC容器实战:从基础配置到高级网络与资源控制
  • 2026全网最全免费音视频转换大合集!30+格式无限制在线转,保姆级教程手把手教,这一篇就够了 - 时时资讯
  • USDPAA框架下高性能包处理:PPAC/PPAM架构解析与优化实践
  • 避坑指南:安卓Userland安装Kali Linux时最容易遇到的5个问题及解决方法(更新失败、桌面启动失败、连接不上)
  • 数字电位器非理想特性解析:工艺、电压与温度对精密电路的影响
  • JSON扁平化使用教程:从入门到精通
  • 出生公证书怎么办理?出生公证需要什么材料?
  • 高并发票务系统设计:时空资源切片建模与动态配额引擎
  • Ubuntu 安装一个轻量级的中文输入法Fcitx5
  • VLA多模态架构加持 采摘机器人实现精细化智能采收
  • 苏州晟雅泰电子:关于W25Q128JVSIQ这个芯片物料的参数,规格及应用领域