Ollama生产部署

Ollama生产部署完整指南

Ollama:简化LLM本地部署 Ollama是2026年最受欢迎的本地LLM部署工具之一。它以极简的命令行界面和自动化的模型管理,让在本地运行大模型变得前所未有的简单。但从"能跑"到"生产可用"之间,还有大量工程细节需要处理。 安装与环境准备 系统要求 # Linux安装 curl -fsSL https://ollama.com/install.sh | sh # 验证GPU支持 nvidia-smi # 确认GPU可用 ollama --version GPU显存规划 不同模型的显存需求: 模型 参数量 FP16显存 INT4显存 推荐GPU Qwen-3-7B 7B 14GB 5GB RTX 4060 8GB+ Llama-3-8B 8B 16GB 6GB RTX 4070 12GB+ Qwen-3-32B 32B 64GB 20GB RTX 4090 24GB+ Llama-3-70B 70B 140GB 40GB 2×A100 80GB Ollama服务配置 # 自定义模型存储路径 export OLLAMA_MODELS=/data/ollama/models # 监听所有网络接口(生产环境配合防火墙) export OLLAMA_HOST=0.0.0.0:11434 # 并发请求数 export OLLAMA_NUM_PARALLEL=4 # 上下文长度 export OLLAMA_CONTEXT_LENGTH=8192 # GPU层数(-1为全部卸载到GPU) export OLLAMA_NUM_GPU=-1 # 启动服务 ollama serve 模型管理 Modelfile自定义 # 基于Qwen-3创建自定义模型 FROM qwen3:32b # 系统提示词 SYSTEM """ 你是一个专业的技术助手。请提供准确、简洁的回答。 如果不确定,请明确说明。 """ # 参数调优 PARAMETER temperature 0.7 PARAMETER top_p 0.9 PARAMETER top_k 40 PARAMETER num_ctx 8192 PARAMETER num_gpu 50 PARAMETER stop "<|im_end|>" PARAMETER stop "<|endoftext|>" # 模板 TEMPLATE """ {{ if .System }}<|im_start|>system {{ .System }}<|im_end|> {{ end }}{{ range .Messages }}{{ if eq .Role "user" }}<|im_start|>user {{ .Content }}<|im_end|> {{ end }}{{ if eq .Role "assistant" }}<|im_start|>assistant {{ .Content }}<|im_end|> {{ end }}{{ end }}<|im_start|>assistant """ # 构建自定义模型 ollama create my-qwen -f Modelfile # 运行 ollama run my-qwen 模型量化 # Ollama自动选择量化级别 ollama pull llama3:70b # 默认INT4量化 ollama pull llama3:70b-q8_0 # 指定INT8 ollama pull llama3:70b-fp16 # FP16精度 # 从GGUF文件导入 ollama create my-model --file ./model.gguf API服务 REST API import requests # 基础对话 response = requests.post( "http://localhost:11434/api/chat", json={ "model": "my-qwen", "messages": [ {"role": "user", "content": "解释Transformer的注意力机制"} ], "stream": False, "options": { "temperature": 0.7, "num_ctx": 8192, } } ) print(response.json()["message"]["content"]) # 流式响应 response = requests.post( "http://localhost:11434/api/chat", json={"model": "my-qwen", "messages": [...], "stream": True}, stream=True ) for line in response.iter_lines(): if line: chunk = json.loads(line) print(chunk["message"]["content"], end="", flush=True) OpenAI兼容API Ollama提供OpenAI兼容接口,方便迁移现有应用: ...

2026-07-02 · 4 min · 780 words · 硅基 AGI 探索者
LLM损失函数

LLM训练损失函数详解

损失函数:训练信号的来源 损失函数定义了模型的学习目标。在LLM训练的不同阶段——预训练、指令微调、对齐——使用的损失函数截然不同。理解每种损失函数的设计意图和数学特性,是构建高质量LLM的基础。 预训练:交叉熵损失 基本形式 预训练的核心是下一个token预测,使用交叉熵损失: def cross_entropy_loss(logits, labels, ignore_index=-100): """ logits: [batch, seq_len, vocab_size] labels: [batch, seq_len] """ batch_size, seq_len, vocab_size = logits.shape # 展平 logits_flat = logits.view(-1, vocab_size) labels_flat = labels.view(-1) # 计算交叉熵(忽略padding位置) loss = F.cross_entropy( logits_flat, labels_flat, ignore_index=ignore_index, reduction='mean' ) return loss Z-Loss Chinchilla引入的Z-Loss用于稳定训练。它通过对logits的log-partition-function进行正则化,防止logits过大: def z_loss(logits, coefficient=1e-4): """Z-Loss: 正则化logits的规模""" log_z = torch.logsumexp(logits, dim=-1) # [batch, seq_len] return coefficient * (log_z ** 2).mean() # 总损失 total_loss = cross_entropy_loss(logits, labels) + z_loss(logits) Z-Loss的作用: 防止某些logits变得过大(导致softmax饱和) 稳定混合精度训练 对最终性能几乎没有负面影响 Label Smoothing 标签平滑将硬标签(one-hot)软化为软标签,防止模型过度自信: def label_smoothing_cross_entropy(logits, labels, smoothing=0.1, ignore_index=-100): """带标签平滑的交叉熵""" n_classes = logits.size(-1) # 创建软标签 log_probs = F.log_softmax(logits, dim=-1) # 均匀分布的平滑 smooth_loss = -log_probs.mean(dim=-1) # 对所有类的平均log prob # 标准交叉熵 nll_loss = -log_probs.gather(dim=-1, index=labels.unsqueeze(-1)).squeeze(-1) # 混合 loss = (1 - smoothing) * nll_loss + smoothing * smooth_loss # 忽略padding mask = (labels != ignore_index) loss = loss[mask].mean() return loss 标签平滑的效果: ...

2026-07-02 · 4 min · 646 words · 硅基 AGI 探索者
创意提示模式

创意提示模式:激发AI的创造力

引言 LLM不仅能完成"有明确答案"的任务(如翻译、摘要、分类),还能在创意任务中表现出惊人的创造力。但创意任务的提示设计与常规任务截然不同:它需要更开放、更灵活、更有探索性。2026年,创意提示工程已经成为AI应用的重要方向。本文将系统介绍创意提示模式。 创意任务的特点 特点一:无唯一正确答案 常规任务: 输入:将"Hello"翻译为法语。 输出:Bonjour ✓(唯一正确答案) 创意任务: 输入:写一个关于时间旅行的短篇故事。 输出:[无数种可能] ✓(无唯一答案) 特点二:主观评估 创意任务的输出质量由人类主观评估,难以自动化。 特点三:风格敏感 同样的任务,不同的风格要求,输出完全不同。 特点四:需要探索 创意往往来自对多种可能性的探索,而不是一次生成。 创意提示基础模式 模式一:角色扮演(Role-Playing) 让模型扮演特定角色,激发角色相关的创造力: 提示模板: 你是一位{role},拥有以下特点: - {trait_1} - {trait_2} - {trait_3} 请以这个角色的身份,完成以下任务: {task} 示例: 角色:科幻小说家 特点:想象力丰富、擅长构建未来世界、喜欢探讨科技与人性的冲突 任务:写一个关于AI觉醒的短篇故事开头(500字) 模式二:风格迁移(Style Transfer) 明确指定输出风格: 提示模板: 请以{style}的风格,完成以下{task}。 风格描述: - 语气:{tone} - 用词:{vocabulary} - 句式:{sentence_structure} - 修辞:{figures_of_speech} 任务:{task} 示例(参考这个风格): {style_example} 模式三:约束激发(Constraint-Driven) 通过约束激发创造力(类似诗歌的格律): 提示模板: 请以以下约束创作{content_type}: 约束条件: 1. 长度:{length}字以内 2. 必须包含关键词:{keywords} 3. 不能使用:{forbidden_words} 4. 结构要求:{structure} 5. 情感基调:{mood} 任务:{task} 模式四:迭代优化(Iterative Refinement) 先生成草稿,再逐步优化: ...

2026-07-02 · 2 min · 367 words · 硅基 AGI 探索者
Softmax变体

Softmax变体2026:从标准到线性

Softmax:将分数变为概率 Softmax函数将任意实数向量转换为概率分布——所有元素为正且和为1。它是分类、注意力、语言模型等核心组件的数学基础。 Softmax(x_i) = exp(x_i) / Σ_j exp(x_j) 看似简单的公式背后,隐藏着数值稳定性、计算效率和梯度行为的深刻问题。 数值稳定性 溢出问题 当输入值很大时,exp(x)会溢出。例如exp(1000)远超FP32的范围(~3.4e38)。解决方案是减去最大值: def softmax_stable(x, dim=-1): """数值稳定的Softmax""" x_max = x.max(dim=dim, keepdim=True).values exp_x = torch.exp(x - x_max) return exp_x / exp_x.sum(dim=dim, keepdim=True) 减去最大值不改变结果(因为分子分母同时除以exp(max)),但将指数运算的输入控制在合理范围内。 Log-Softmax 在许多场景中(如交叉熵损失),我们需要的是log概率而非概率本身。直接对softmax取log可能损失精度: def log_softmax_stable(x, dim=-1): """数值稳定的Log-Softmax""" x_max = x.max(dim=dim, keepdim=True).values shifted = x - x_max log_sum_exp = torch.log(torch.exp(shifted).sum(dim=dim, keepdim=True)) return shifted - log_sum_exp PyTorch的F.log_softmax和F.cross_entropy内部都使用了这种稳定的实现。 Softmax的梯度特性 饱和问题 当某个输入远大于其他输入时,Softmax的输出接近one-hot——一个接近1,其余接近0。此时梯度几乎为零,导致学习停滞。 # 梯度公式 # ∂softmax(x_i)/∂x_j = softmax(x_i) * (δ_ij - softmax(x_j)) 当softmax(x_i) ≈ 1时,梯度 ≈ 1 × (δ_ij - softmax(x_j)) ≈ 0(对所有j)。 温度调节 温度参数T控制softmax的"尖锐度": Softmax_T(x_i) = exp(x_i / T) / Σ_j exp(x_j / T) T→0:趋向one-hot(尖锐) T→∞:趋向均匀分布(平滑) T=1:标准softmax 在知识蒸馏中,高温(T=4-10)使Teacher模型的输出更"软",传递更多暗知识。 LLM中的Softmax变体 Scaled Dot-Product Softmax 注意力中的softmax需要除以√d_k来防止内积值过大导致饱和: ...

2026-07-02 · 4 min · 818 words · 硅基 AGI 探索者
推理增强提示

推理增强提示技术:让AI的推理更深入

引言 标准提示让LLM直接输出答案,但对于复杂推理任务,这种方法往往不够。2026年,推理增强提示技术已经从简单的Chain-of-Thought发展到包含Self-Consistency、Tree of Thoughts、ReAct、Reflexion等多种技术的完整体系。本文将系统介绍这些技术。 推理增强技术谱系 基础推理 Chain-of-Thought (CoT) — 展示推理步骤 Zero-Shot CoT — “Let’s think step by step” 采样增强 Self-Consistency — 多次采样,选择最一致答案 DiVeRSe — 多样化推理路径 搜索增强 Tree of Thoughts (ToT) — 树搜索 Graph of Thoughts (GoT) — 图搜索 Beam Search — 束搜索 工具增强 ReAct — 推理+行动 Self-Refine — 迭代优化 Reflexion — 反思+记忆 Chain-of-Thought回顾 见前文"思维链2026进阶技巧"。 Self-Consistency(自一致性) 核心思想 对于一个复杂问题,让模型生成多个推理路径(通过高温采样),然后选择最一致(或投票最多的)答案。 实现方法 def self_consistency(prompt, n_samples=5, temperature=0.8): """ Self-Consistency实现 """ responses = [] for i in range(n_samples): response = call_llm( prompt, temperature=temperature, max_tokens=500 ) # 提取答案 answer = extract_answer(response) responses.append(answer) # 投票选择最一致的答案 final_answer = majority_vote(responses) return final_answer 效果提升 在GSM8K数学推理数据集上: ...

2026-07-02 · 3 min · 444 words · 硅基 AGI 探索者
嵌入层优化

嵌入层优化:从查找到计算

嵌入层:被忽视的显存大户 在LLM中,嵌入层(Embedding Layer)往往是被忽视的显存消耗者。以Llama-3为例,词表大小128K、隐藏维度4096、FP16精度下,嵌入层参数量为 128K × 4096 × 2 = 1GB。对于更大的词表(如Qwen的152K),嵌入层可达1.5GB以上。 在70B模型中,嵌入层占总参数量的约2%,但在推理时它还需要一个等大的输出投影层(LM Head),两者合计占4%——这还不算小。 标准嵌入层 查找表 标准嵌入层是一个简单的查找表——词表中的每个token对应一个可学习的向量: class StandardEmbedding(nn.Module): def __init__(self, vocab_size, d_model): super().__init__() self.weight = nn.Parameter(torch.randn(vocab_size, d_model)) def forward(self, input_ids): # 简单的索引查找 return self.weight[input_ids] # [batch, seq_len, d_model] 权重共享 许多LLM共享嵌入层和输出层的权重(Weight Tying): class SharedEmbeddingModel(nn.Module): def __init__(self, vocab_size, d_model): super().__init__() self.embedding = nn.Embedding(vocab_size, d_model) # 输出层直接使用嵌入矩阵的转置 # 不需要额外的参数 def forward(self, input_ids): x = self.embedding(input_ids) x = self.transformer(x) # 输出logits = x @ embedding.weight.T logits = F.linear(x, self.embedding.weight) return logits 权重共享的好处: 减少参数量(节省1GB+显存) 输入和输出在同一个语义空间中 训练时梯度信号更强 但也有研究指出,权重共享可能导致冲突——输入嵌入需要捕捉"词的语义",输出层需要捕捉"词的预测概率分布",两者的最优表示可能不同。 嵌入层压缩 矩阵分解 将大的嵌入矩阵分解为两个小矩阵的乘积: class FactorizedEmbedding(nn.Module): def __init__(self, vocab_size, d_model, d_factor): """ d_factor: 分解后的中间维度 """ super().__init__() # V × D → V × d_factor × d_factor × D self.factor1 = nn.Parameter(torch.randn(vocab_size, d_factor)) self.factor2 = nn.Parameter(torch.randn(d_factor, d_model)) def forward(self, input_ids): # 查找低维因子,然后投影到高维 low_dim = self.factor1[input_ids] # [batch, seq, d_factor] return low_dim @ self.factor2 # [batch, seq, d_model] 参数量从 V×D 降低到 V×d + d×D。当 D=4096、d=256、V=128K 时,参数量减少约16倍。 ...

2026-07-02 · 3 min · 562 words · 硅基 AGI 探索者
提示模板复用

提示模板复用策略:构建可复用的提示库

引言 提示工程的一个重要原则是"不要重复造轮子"。许多任务(如情感分析、实体抽取、文本摘要)的提示模式是通用的,可以复用于不同场景。2026年,随着提示工程的成熟,提示模板复用已经成为提升效率的关键策略。本文将系统介绍提示模板复用策略。 为什么需要模板复用 价值一:提升效率 无需从零设计提示,直接复用经过验证的模板。 价值二:保证质量 复用经过测试的模板,避免引入新bug。 价值三:便于维护 模板集中管理,修改一次,全局生效。 价值四:知识沉淀 团队可以共享和积累提示工程最佳实践。 模板设计原则 原则一:参数化 将提示中的可变部分参数化: ### 不好的设计(硬编码) 请分析以下评论的情感: 评论:这部电影太棒了! 输出:positive ### 好的设计(参数化) 请分析以下评论的情感: 评论:{{COMMENT}} 输出:{{SENTIMENT}} → 使用时:template.render(comment="...", sentiment="...") 原则二:模块化 将复杂提示分解为多个模块: ### 复杂提示(难以复用) [500字提示,包含角色定义、任务描述、输出格式、示例...] ### 模块化提示(易于复用) {{ROLE_DEFINITION}} {{TASK_DESCRIPTION}} {{OUTPUT_FORMAT}} {{FEW_SHOT_EXAMPLES}} {{USER_INPUT}} → 可以根据需要替换或重用某个模块 原则三:文档化 每个模板都应有清晰的文档: # sentiment_analysis.yaml name: sentiment_analysis version: 1.0 author: 硅基AGI探索者 description: 情感分析提示模板,支持positive/negative/neutral三分类 tasks: - 电商评论情感分析 - 社交媒体情感监控 - 用户反馈情感分类 parameters: - name: comment type: string description: 待分析的评论文本 - name: output_format type: string default: "json" options: ["json", "text"] examples: - input: "这部电影太棒了!" output: "positive" 原则四:可测试 模板应易于测试: ...

2026-07-02 · 3 min · 519 words · 硅基 AGI 探索者
分词器对比

分词器对比2026:BPE/SentencePiece/Unigram

分词器:文本与模型之间的桥梁 LLM无法直接处理文本——它需要将文本切分为离散的token序列,每个token对应一个整数ID。分词器(Tokenizer)就是完成这一转换的组件。分词器的选择直接影响模型的词表大小、序列长度、多语言能力和推理效率。 2026年主流LLM使用的分词器虽然名称各异,但核心算法主要分为BPE、WordPiece和Unigram三大类。 BPE(Byte Pair Encoding) 算法原理 BPE最初是一种数据压缩算法,被Sennrich等人(2016)引入NLP作为子词分词方法。其训练过程是自底向上的——从字符开始,逐步合并最频繁的字符对: def train_bpe(texts, vocab_size=32000): """BPE训练算法""" # 1. 将文本拆分为字符序列 word_freqs = {} for text in texts: words = text.split() for word in words: chars = tuple(word) word_freqs[chars] = word_freqs.get(chars, 0) + 1 # 2. 初始化词表为所有字符 vocab = set() for word in word_freqs: for char in word: vocab.add(char) # 3. 迭代合并最频繁的字符对 merges = [] while len(vocab) < vocab_size: # 统计所有相邻字符对的出现频率 pair_freqs = {} for word, freq in word_freqs.items(): for i in range(len(word) - 1): pair = (word[i], word[i+1]) pair_freqs[pair] = pair_freqs.get(pair, 0) + freq if not pair_freqs: break # 选择频率最高的对 best_pair = max(pair_freqs, key=pair_freqs.get) new_token = best_pair[0] + best_pair[1] # 合并 merges.append(best_pair) vocab.add(new_token) # 更新词频 new_word_freqs = {} for word, freq in word_freqs.items(): new_word = merge_in_word(word, best_pair) new_word_freqs[new_word] = new_word_freqs.get(new_word, 0) + freq word_freqs = new_word_freqs return vocab, merges 编码过程 训练好的BPE分词器在编码新文本时,按训练时的合并顺序逐步合并: def bpe_encode(text, merges, special_tokens): """BPE编码""" tokens = [] for word in text.split(): # 先检查是否是特殊token if word in special_tokens: tokens.append(word) continue # 字符级开始 word_tokens = list(word) # 按合并顺序尝试合并 for (a, b) in merges: i = 0 while i < len(word_tokens) - 1: if word_tokens[i] == a and word_tokens[i+1] == b: word_tokens[i] = a + b del word_tokens[i+1] else: i += 1 tokens.extend(word_tokens) return tokens Byte-level BPE GPT-2引入的改进——在字节级别而非字符级别进行BPE。这使得词表可以处理任意UTF-8字符,彻底解决了未登录字符(OOV)问题: ...

2026-07-02 · 4 min · 734 words · 硅基 AGI 探索者
提示测试

提示测试方法论:如何科学评估提示效果

引言 提示工程不是"一次写好"的艺术,而是"持续测试和优化"的科学。一个看似完美的提示,可能在某些边界情况下表现糟糕。2026年,提示测试已经成为AI应用开发的核心环节。本文将系统介绍提示测试的方法论。 为什么需要提示测试 问题一:模型差异 同一个提示在不同模型上的表现可能差异巨大: 提示:翻译以下句子为英文。 输入:今天天气很好。 GPT-5输出:The weather is very good today. ✓ Claude 4输出:Today's weather is quite nice. ✓ GLM-5输出:Today weather very good. ✗ (语法错误) 问题二:输入多样性 看起来不错的提示,可能在某种输入上失败: 提示:[情感分析] 输入分布: - 90%标准评论 → 准确率95% - 10%讽刺评论 → 准确率40% ← 问题! 问题三:提示脆弱性 微小的提示修改可能导致效果大幅下降: 原始提示(准确率90%): 请分析评论情感,输出positive/negative/neutral。 修改后(准确率60%): 分析一下这个评论的情感,输出正面/负面/中性。 ← 只是换了表述,效果大幅下降! 提示测试框架 测试维度 维度 说明 测试方法 准确性 输出是否正确 人工评估/GPT-4评估 格式一致性 输出格式是否稳定 自动解析验证 鲁棒性 对输入变化的敏感度 对抗性测试 模型通用性 在不同模型上的表现 跨模型测试 成本效率 token消耗与效果的比值 成本-效果分析 测试数据集 标准测试集 针对特定任务构建测试集: ...

2026-07-02 · 3 min · 601 words · 硅基 AGI 探索者
PagedAttention实现

PagedAttention实现细节

操作系统启发:虚拟内存 PagedAttention的核心灵感来自操作系统的虚拟内存管理。在OS中,进程的虚拟内存被分成固定大小的页(Page),物理内存不必连续分配。 PagedAttention将这一思想应用于KV Cache管理: 传统KV Cache: [token0, token1, ..., tokenN] 在物理显存中必须连续 PagedAttention KV Cache: [token0-15] → GPU显存块#37 [token16-31] → GPU显存块#102 [token32-47] → GPU显存块#58 ... (物理上不连续,逻辑上连续) 块式KV Cache存储 块结构定义 class KVBlock: def __init__(self, block_size, n_layers, n_kv_heads, head_dim, dtype=torch.float16): self.block_size = block_size # 每块存储的token数(如16) self.n_layers = n_layers self.n_kv_heads = n_kv_heads self.head_dim = head_dim # 每块的形状: [n_layers, block_size, n_kv_heads, head_dim] self.k = torch.zeros( n_layers, block_size, n_kv_heads, head_dim, dtype=dtype ) self.v = torch.zeros_like(self.k) self.ref_count = 0 # 引用计数(用于共享) self.last_used = 0 # 最后使用时间(LRU驱逐) 块表(Block Table) 每个请求有一个块表,将逻辑token位置映射到物理块: class BlockTable: def __init__(self, request_id, block_size): self.request_id = request_id self.block_size = block_size self.physical_blocks = [] # 物理块索引列表 self.logical_to_physical = {} # 逻辑位置 → 物理块索引 def get_physical_block(self, logical_pos): """获取逻辑位置的物理块""" block_idx = logical_pos // self.block_size offset = logical_pos % self.block_size if block_idx >= len(self.physical_blocks): return None, None # 尚未分配 return self.physical_blocks[block_idx], offset def append_block(self, physical_block_idx): """追加一个新物理块""" self.physical_blocks.append(physical_block_idx) 物理块管理 块池(Block Pool) 所有物理块由中央块池管理: ...

2026-07-02 · 4 min · 798 words · 硅基 AGI 探索者
鲁ICP备2026018361号