// welcome

记录 AI 与编程的学习笔记

这里是我的个人技术博客。写一些关于大模型、AI 应用开发和日常编码的思考与踩坑记录,纯粹兴趣分享,不定期更新。左侧选一篇开始阅读。

5
篇技术分享
2024
开博年份
AI
主要方向
好奇心

关于本站

我是一名对人工智能和软件开发感兴趣的独立开发者。这个博客用来沉淀日常学习和实践中的笔记,主题集中在大模型原理、AI 应用与编程技巧。

所有内容均为个人原创的学习记录与技术分享,属于 非经营性个人网站,不提供任何商业服务、交易或对外接口。若文中有误,欢迎指正交流。

写点什么

  • 大模型原理:注意力、位置编码、训练与推理
  • AI 应用:Prompt 工程、RAG、Agent 实践
  • 工程与性能:Python、异步、显存优化
// 大模型 · 深度学习

从零理解 Transformer 的注意力机制

2025-06-18约 11 分钟作者 拾光
大模型深度学习Attention

第一次读 Attention Is All You Need 时,我盯着 softmax(QKᵀ/√d)V 看了很久也没懂。后来发现,把维度想小、用具体数字手推一遍,一切就通了。

一句话:注意力在做加权平均

给定一个词,注意力要回答:为了理解我,应该从其他词那里"抄"多少信息? 答案是一组权重,权重越大抄得越多。整个过程就是对所有词的表示做一次加权平均,权重由相关性决定。

Q、K、V 分别是什么

每个词向量乘三个可学习矩阵,得到三个角色:

  • Query:我在找什么信息
  • Key:我能提供什么信息的"标签"
  • Value:我实际携带的内容

用 Query 和每个 Key 比相似度(点积),高就多拿那个词的 Value。

import numpy as np
X = np.random.randn(3, 4)                      # 3 词,每词 4 维
Wq, Wk, Wv = (np.random.randn(4, 4) for _ in range(3))
Q, K, V = X @ Wq, X @ Wk, X @ Wv
scores = Q @ K.T / np.sqrt(4)                  # 缩放点积
w = np.exp(scores) / np.exp(scores).sum(-1, keepdims=True)  # softmax
out = w @ V                                    # 加权平均

为什么要除以 √d

点积随维度 d 变大,数值一大 softmax 就被推向 0/1 两极,梯度消失。除以 √d 把方差拉回来,让 softmax 保持在有梯度的区间。

直觉:√d 是个"温度"调节,防止注意力过早变成 one-hot 硬选择。

多头:从多个角度看同一句话

单个注意力只能学一种关联。多头把向量切成 h 份各自算再拼回——有的头学语法依赖,有的学指代,互不干扰。

小结

  • 注意力 = 按相关性对 Value 加权平均
  • Q 找、K 标、V 载,点积算匹配度
  • √d 稳梯度,多头看多种关系
// Prompt · 工程实践

写好 Prompt 的几条实用原则

2025-05-30约 8 分钟作者 拾光
PromptLLM工程实践

做了半年 LLM 应用,最深的体会:同一个模型,Prompt 写得好不好,效果能差出一个档次。

1. 明确结构化输出

别让模型"自由发挥"格式。要 JSON 就把字段和类型写死,给个示例,下游解析才稳。

只输出 JSON,不要解释。格式:
{
  "sentiment": "正面 | 负面 | 中性",
  "score": 0.0 到 1.0 的数字,
  "keywords": ["最多3个关键词"]
}

2. 少样本示例 > 长篇描述

与其几百字描述,不如给 2~3 个输入输出对。模型从例子学到的模式,比抽象规则准得多——这就是 few-shot 的威力。

3. 用角色和约束收窄空间

开头给角色("你是严谨的代码审查员"),约束语气与关注点。再配禁止项

  • 不确定就说"无法判断",禁止编造
  • 只依据给定材料,不引入外部知识
  • 超范围请求直接拒绝

4. 复杂推理让它"想清楚再答"

需要多步推理的任务,加"先分步分析,再给结论"(思维链)常显著提升正确率。

实测:同一道多步计算题,直接问准确率约 60%,加"逐步推理"后升到 90%+。

5. 把 Prompt 当代码维护

攒一个小测试集(十几条典型输入 + 期望输出),每次改 Prompt 跑一遍看指标,纳入版本管理,改动可回溯。

// RAG · 大模型

搭一个能用的 RAG 系统,我踩过的坑

2025-05-12约 13 分钟作者 拾光
RAG检索大模型

RAG 思路一句话能说清:先检索相关片段,再塞进 Prompt 让模型基于它作答。可从"能跑的 Demo"到"答得准",中间全是坑。

坑一:分块策略比想象中重要

最初按固定 500 字切,一段话被从中间劈开,检索到半句就答歪。改进:

  • 按语义边界切(段落/标题),非固定字数
  • 块之间留重叠,避免边界信息丢失
  • 给每块加标题/来源前缀,保留上下文
def chunk(text, size=400, overlap=80):
    chunks, i = [], 0
    while i < len(text):
        chunks.append(text[i:i+size])
        i += size - overlap        # 关键:留重叠
    return chunks

坑二:embedding 不是越大越好

中文场景,针对中文微调的小模型召回反而更稳。选型一定拿自己的数据建小评测集测召回率,别信榜单。

坑三:只靠向量检索会漏

纯语义对"精确关键词"(型号、编号)不敏感。用混合检索:向量 + BM25,两路合并。

加 BM25 混合后,涉及编号的问题召回率从 ~70% 提到 ~92%。

坑四:召回多 ≠ 答得好,要重排

top-20 全塞进去,模型被噪声干扰。正确做法:召回放宽(多召),再用 reranker 精排取 top-3~5 进 Prompt。

坑五:一定要让它标来源

要求答案标注引用片段编号,减少幻觉、方便核对。检索不到就答"资料中未提及",别硬编。

最终流水线

  1. 语义分块 + 重叠 + 标题前缀
  2. 中文 embedding 建向量索引
  3. 向量 + BM25 混合召回 top-20
  4. reranker 精排取 top-4
  5. 带来源约束的 Prompt 生成

RAG 的难点从不在"生成",而在"检索到对的东西"。

// Python · 工程实践

用 Python 异步爬虫的正确姿势

2025-04-28约 9 分钟作者 拾光
Pythonasyncio并发

同步爬虫慢在等 IO;异步能让一个线程同时挂着成百上千个请求。但写不好,要么把对方服务器打挂,要么自己内存爆掉。

核心:用信号量限流

别一次性 gather 一万个请求。用 Semaphore 控制同时在飞的并发数,既护对方也护自己。

import asyncio, aiohttp

async def fetch(sess, url, sem):
    async with sem:                          # 限流:最多 N 个并发
        try:
            async with sess.get(url, timeout=aiohttp.ClientTimeout(total=10)) as r:
                return url, await r.text()
        except Exception as e:
            return url, None                 # 失败不炸整批

async def crawl(urls, concurrency=20):
    sem = asyncio.Semaphore(concurrency)
    async with aiohttp.ClientSession() as sess:
        tasks = [fetch(sess, u, sem) for u in urls]
        return await asyncio.gather(*tasks)

超时一定要设

不设超时,一个卡住的连接能拖垮整批。ClientTimeouttotal 是最实用的兜底。

重试用退避

失败别立即重试,加指数退避,避免瞬时故障时把请求雪崩式堆上去。

async def with_retry(coro_fn, retries=3):
    for i in range(retries):
        r = await coro_fn()
        if r is not None:
            return r
        await asyncio.sleep(2 ** i)          # 1s, 2s, 4s
    return None

易踩的坑

  • Session 复用:一个 ClientSession 跑整批,别每请求新建
  • 异常隔离:单个失败返回 None,别让它掀翻 gather
  • 礼貌:设 UA、尊重 robots、控制频率
// 性能 · 大模型

本地跑开源大模型的显存优化笔记

2025-04-10约 10 分钟作者 拾光
大模型量化性能

一张消费级显卡想跑 7B 模型,关键是省显存。记录几个实测有效的技巧。

显存都花哪了

推理时显存主要三块:模型权重KV Cache激活值。权重最大,先从它下手。

1. 量化:最立竿见影

FP16 的 7B 约需 14GB,4-bit 量化后压到 ~4.5GB,精度损失在多数任务可接受。

from transformers import AutoModelForCausalLM, BitsAndBytesConfig
cfg = BitsAndBytesConfig(load_in_4bit=True,
                         bnb_4bit_compute_dtype="float16")
model = AutoModelForCausalLM.from_pretrained(
    "your-7b-model", quantization_config=cfg, device_map="auto")

2. KV Cache 是长文本的隐形杀手

序列越长,KV Cache 越大,容易在长对话中悄悄爆显存。控制上下文长度,或用支持分页/量化 KV 的推理框架。

实测:4-bit 7B 空载 ~4.5GB,跑到 4k 上下文时 KV Cache 又吃掉 1GB+。

3. 批处理提吞吐

显存有余就开 batch,多个请求一起算,GPU 利用率和吞吐都上来。显存紧就减小 batch,用时间换空间。

4. 其他小技巧

  • device_map="auto" 自动把放不下的层挪到 CPU/磁盘
  • 关掉不需要的梯度:推理用 torch.no_grad()
  • 用 Flash Attention 降激活值显存

省显存的本质是"精度/长度/吞吐"三者和显存做权衡,没有免费的午餐,按场景取舍。