// welcome
记录 AI 与编程的学习笔记
这里是我的个人技术博客。写一些关于大模型、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。
坑五:一定要让它标来源
要求答案标注引用片段编号,减少幻觉、方便核对。检索不到就答"资料中未提及",别硬编。
最终流水线
- 语义分块 + 重叠 + 标题前缀
- 中文 embedding 建向量索引
- 向量 + BM25 混合召回 top-20
- reranker 精排取 top-4
- 带来源约束的 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)
超时一定要设
不设超时,一个卡住的连接能拖垮整批。ClientTimeout 里 total 是最实用的兜底。
重试用退避
失败别立即重试,加指数退避,避免瞬时故障时把请求雪崩式堆上去。
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 降激活值显存
省显存的本质是"精度/长度/吞吐"三者和显存做权衡,没有免费的午餐,按场景取舍。