news 2026/5/25 23:28:46

AI学习 - 大模型基础入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI学习 - 大模型基础入门

AI学习 - 大模型基础入门

从零开始:Ollama 安装 → 本地模型运行 → Python 代码接入 → 理解核心概念


摘要

本文记录了在 Windows 上使用 Ollama 部署本地大模型、并通过 Python 代码接入调用的完整过程。内容涵盖:Ollama 安装与模型拉取、大模型基础概念科普(Token / Context / Context Window / Temperature / Role)、三种调用方式对比,以及多轮对话的完整实现代码。

跟着做完,你能在自己电脑上跑起一个本地大模型,并用代码与它对话。


一、环境准备

1.1 硬件建议

配置能跑的模型体验
8GB 显存独显 + 16GB 内存7b-9b 模型流畅
无独显 / 核显3b 以下模型慢,但能跑
16GB 显存14b-30b 模型良好

模型文件大小 ≈ 显存需求。qwen3.5:9b 文件 6.6GB,需要约 7GB 显存。

1.2 安装 Ollama

推荐安装官方版本:

# 方式一:winget 安装(推荐)winget install Ollama.Ollama# 方式二:官网下载安装包# https://ollama.com/download 选 Windows

安装完成后验证:

ollama--version# 应输出类似:ollama version 0.9.x

二、拉取并运行本地模型

2.1 推荐的入门模型

模型大小显存需求特点
qwen3.5:9b6.6GB~7GB中文友好,速度快,推荐入门
deepseek-r1:7b4.7GB~5GB有思考链,适合观察推理过程
qwen3-coder:30b18GB~20GB代码能力强,需要大显存

2.2 拉取模型

# 拉取(第一次会下载,之后直接用缓存)ollama pull qwen3.5:9b# 查看已安装的模型ollama list# 查看当前运行中的模型ollamaps

2.3 命令行直接对话(验证是否正常)

ollama run qwen3.5:9b# 进入交互模式,直接输入问题# 输入 /bye 退出

2.4 常见问题

问题:Error: 500 Internal Server Error: memory layout cannot be allocated

排查顺序:

# 1. 查看显存占用nvidia-smi# 2. 查看内存占用Get-CimInstanceWin32_OperatingSystem|Select-Object` @{N='总内存(GB)';E={[math]::Round($_.TotalVisibleMemorySize/1MB,1)}},` @{N='可用内存(GB)';E={[math]::Round($_.FreePhysicalMemory/1MB,1)}}# 3. 停止已加载的模型释放显存ollama stop deepseek-r1:7b# 4. 版本太旧导致不兼容,尝试升级winget upgrade Ollama.Ollama# 5. 模型文件损坏,重新下载ollamarmqwen3.5:9b ollama pull qwen3.5:9b

三、大模型基础概念

在写代码之前,先把几个核心概念搞清楚,后面看代码会顺很多。

3.1 Token:模型处理文字的最小单位

模型不认识"字"或"词",只认识 token。可以理解为把文字切成碎片:

"你好北京" → ["你", "好", "北", "京"] → 4 个 token "hello" → ["hello"] → 1 个 token "unhappy" → ["un", "happy"] → 2 个 token

为什么要了解 token:

  • API 按 token 计费(调云端模型时)
  • 模型有最大处理量限制(context window)
  • max_tokens参数控制的就是最多生成多少 token

经验换算:1000 个中文字符 ≈ 500-700 token。


3.2 Context:模型这次推理能看到的全部内容

模型没有记忆,每次调用都是全新的。它能"记住"上下文,是因为你把历史对话都传进去了。

一次 API 调用时,context 包含: ┌─────────────────────────────┐ │ system prompt │ → 角色设定 │ 第1轮用户消息 │ → 历史 │ 第1轮模型回复 │ → 历史 │ 第2轮用户消息 │ → 历史 │ 第2轮模型回复 │ → 历史 │ 当前用户消息(最新) │ → 当前问题 └─────────────────────────────┘ 模型基于以上全部内容生成回复

思考题:如果对话进行了 100 轮,每次都要把 100 轮历史传进去,token 消耗会越来越大。这就是为什么长对话会越来越慢、越来越贵。


3.3 Context Window:模型一次能处理的 token 上限

Context Window = 128,000 token(以 Claude 为例) 意思是:system prompt + 所有历史对话 + 当前问题 加起来不能超过 128,000 token 超出的部分模型直接看不到,就像被裁掉了

这也是 RAG(检索增强生成)存在的核心原因:当你有一本 500 页的书,塞不进 context window,就只能先检索最相关的几段塞进去,而不是全文。


3.4 Temperature:控制输出的随机程度

第一步:模型输出的是 logits,不是概率

模型最后一层输出的是原始分数,叫logits,数值没有限制,可以是任意实数,不能直接当概率用:

# 模型对"下一个词是什么"给出的原始分数logits=[2.1,1.8,1.2,0.9]# "9" "5" "3" "7"
第二步:softmax 把 logits 转成概率
importmathdefsoftmax(logits):exp_values=[math.exp(x)forxinlogits]# 对每个值取 e^xtotal=sum(exp_values)return[x/totalforxinexp_values]# 归一化,加起来 = 1softmax([2.1,1.8,1.2,0.9])# → [0.38, 0.28, 0.16, 0.12] 加起来 = 1.0
第三步:temperature 在 softmax 之前介入

操作很简单,就是把 logits除以 T,再做 softmax:

defsoftmax_with_temperature(logits,T):scaled=[x/Tforxinlogits]# 先除以 temperaturereturnsoftmax(scaled)# 再转概率
为什么除法能控制尖锐程度

关键在e^x这个函数对数值差距极其敏感

# 原始 logits,"9" 和 "5" 差距是 0.3logits=[2.1,1.8]# 直接做 e^x,比值 = 1.35math.exp(2.1)=8.17math.exp(1.8)=6.05# ── temperature = 0.1,差距放大 10 倍 ──scaled=[21.0,18.0]math.exp(21.0)=1,318,815,734math.exp(18.0)=65,659,969# 比值 = 20.1,差距被指数级拉开 → 分布尖锐# ── temperature = 2.0,差距压缩到一半 ──scaled=[1.05,0.90]math.exp(1.05)=2.86math.exp(0.90)=2.46# 比值 = 1.16,差距被压缩 → 分布平坦
完整数字对比
原始 logits:[2.1, 1.8, 1.2, 0.9] token: "9" "5" "3" "7" T = 1.0(不变): scaled: [2.1, 1.8, 1.2, 0.9] 概率: [38%, 28%, 16%, 12%] 有差距,其他词仍有机会 T = 0.1(极低,差距放大 10 倍): scaled: [21.0, 18.0, 12.0, 9.0] 概率: [95%, 4%, ~0%, ~0%] "9" 几乎必然被选,输出稳定 T = 0.5(适中,差距放大 2 倍): scaled: [4.2, 3.6, 2.4, 1.8] 概率: [63%, 27%, 7%, 3%] "9" 概率提升,"5" 还有机会 T = 2.0(极高,差距压缩一半): scaled: [1.05, 0.90, 0.60, 0.45] 概率: [30%, 27%, 23%, 20%] 接近均匀,什么都可能选 T → 0(趋近于零): → 等价于直接取最大值(greedy decoding) → [100%, 0%, 0%, 0%],必然选 "9"
用图形理解
概率分布的"山峰"形状: temperature 低(0.1): temperature 高(2.0): █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ 9 5 3 7 9 5 3 7 尖锐,"9" 一枝独秀 平坦,每个词机会差不多 → 输出稳定 → 输出多样
一句话总结
temperature 通过缩放 logits 的数值差距 利用 e^x 对差距极其敏感的特性 在 softmax 之后产生尖锐或平坦的概率分布 T 小 → 差距放大 → e^x 把差距指数级拉开 → 分布尖锐 → 输出稳定 T 大 → 差距压缩 → e^x 的放大效果减弱 → 分布平坦 → 输出多样 T→0 → 差距无限大 → 最高分词概率→100% → greedy decoding
实际使用建议
场景temperature 推荐值
分类、判断、提取(需要稳定)0 - 0.2
问答、总结(平衡)0.3 - 0.7
写作、头脑风暴(需要创意)0.7 - 1.0
deepseek-r1 推理模型0.6(官方推荐)

注意:deepseek-r1 这类推理模型即使 temperature=0,输出也可能不固定。原因是它先生成<think>思考链(几百次采样),思考路径的微小差异会影响最终答案的措辞。


3.5 Role:对话里的角色标识

发给模型的消息必须标明是谁说的,三个固定角色:

{"role":"system","content":"..."}# 系统指令,定角色和规则{"role":"user","content":"..."}# 人类说的话{"role":"assistant","content":"..."}# 模型自己说过的话

这三个值是训练时就固化的,不能随意更改。模型内部会把消息拼接成:

<|system|>你是一个助手 <|user|>我叫张三 <|assistant|>你好张三! <|user|>我叫什么名字? <|assistant|> ← 模型从这里续写

assistant 历史为什么不能省:没有 assistant 的历史,对话上下文就断了,模型不知道自己之前说过什么。


3.6 API Key:身份验证凭证,不是模型的一部分

云端服务(Claude/GPT): 请求 → [业务层验证 Key → 计费 → 限速] → 模型推理 → 返回 本地 Ollama: 请求 → 模型推理 → 返回 (没有业务层,不需要 Key)

用 openai 库调本地 ollama 时,api_key填任意非空字符串都行,因为 openai 库在代码层面要求这个字段不能为空,但 ollama 收到请求后直接忽略它。

# 这三种写法效果完全相同OpenAI(api_key="ollama",base_url="http://localhost:11500/v1")OpenAI(api_key="任意字符串",base_url="http://localhost:11500/v1")OpenAI(api_key="helloworld",base_url="http://localhost:11500/v1")

如果要对外提供服务并做鉴权,需要在 ollama 前面加反向代理(如 FastAPI 或 one-api),自己实现 Key 的生成和验证逻辑。


四、三种调用方式

方式对比

anthropic 库openai 库(调GPT)openai 库(调ollama)
目标模型ClaudeGPT 系列本地模型
需要 Key是(付费)是(付费)不需要
认证 HeaderX-Api-KeyAuthorization: Bearer忽略
额外 Headeranthropic-version
费用按 token 计费按 token 计费免费
数据隐私发到云端发到云端本地,不出网

学习阶段推荐方式三(openai 库 + 本地 ollama):不花钱、数据不出网、openai 的接口格式是行业标准,学会后换 GPT/Claude 只改两行代码。


五、完整项目代码

""" 本地大模型接入示例 依赖:pip install openai 运行前确保:ollama 已启动,且已拉取对应模型 """importrefromopenaiimportOpenAI# ==================== 初始化客户端 ====================client_ollama=OpenAI(api_key="ollama",# 本地不验证,随便填但不能为空base_url="http://localhost:11500/v1"# 改成你的 ollama 端口)DEFAULT_MODEL="qwen3.5:9b"# 默认使用的模型# ==================== 工具函数 ====================defparse_think(content:str,show_think:bool=True)->str:""" 解析 deepseek-r1 的思考链输出 deepseek-r1 会在回答前输出 <think>...</think> 思考过程 这个函数把思考过程和最终答案分离 """if"<think>"notincontent:returncontent think_match=re.search(r"<think>(.*?)</think>",content,re.DOTALL)answer=re.sub(r"<think>.*?</think>","",content,flags=re.DOTALL).strip()ifshow_thinkandthink_match:print(f"💭 思考过程:\n{'-'*40}")print(think_match.group(1).strip())print(f"{'-'*40}\n")returnanswer# ==================== 单轮对话 ====================defask(question:str,system:str="你是一个助手,请用中文回答",temperature:float=0.7,model:str=DEFAULT_MODEL,show_think:bool=True)->str:""" 单轮问答 每次调用都是独立的,不保留上下文 Args: question: 用户问题 system: 系统提示词,定义模型角色和行为规则 temperature: 随机程度,0=稳定,1=多样,推理模型建议0.6 model: 使用的模型名称 show_think: 是否打印思考过程(deepseek-r1 专用) """resp=client_ollama.chat.completions.create(model=model,temperature=temperature,messages=[{"role":"system","content":system},{"role":"user","content":question},])content=resp.choices[0].message.contentreturnparse_think(content,show_think)# ==================== 多轮对话 ====================classChatSession:""" 多轮对话会话 维护对话历史,实现上下文记忆 模型本身没有记忆,"记住"上下文的原因是: 每次调用都把完整的历史对话传给模型(history 列表) 随着对话轮数增加,传入的 token 也越来越多 """def__init__(self,system:str="你是一个助手,请用中文回答",model:str=DEFAULT_MODEL,temperature:float=0.7,show_think:bool=False):self.model=model self.temperature=temperature self.show_think=show_think self.history=[{"role":"system","content":system}]defchat(self,user_input:str)->str:""" 发送一条消息,返回模型回复 自动维护 history,实现多轮对话 """# 把用户消息加入历史self.history.append({"role":"user","content":user_input})# 把完整历史传给模型resp=client_ollama.chat.completions.create(model=self.model,temperature=self.temperature,messages=self.history# 关键:传完整历史)reply=resp.choices[0].message.content reply=parse_think(reply,self.show_think)# 把模型回复也加入历史(下一轮需要用到)self.history.append({"role":"assistant","content":reply})returnreplydefclear(self):"""清空对话历史,保留 system prompt"""system_msg=self.history[0]self.history=[system_msg]defshow_history(self):"""打印当前对话历史"""print("\n📋 当前对话历史:")fori,msginenumerate(self.history):role_icon={"system":"⚙️","user":"👤","assistant":"🤖"}.get(msg["role"],"?")print(f"{role_icon}[{msg['role']}]:{msg['content'][:80]}...")print(f"共{len(self.history)}条消息\n")# ==================== 演示代码 ====================if__name__=="__main__":# ---------- 演示1:temperature 对比 ----------print("="*60)print("演示1:temperature 对比(使用 qwen3.5:9b)")print("="*60)print("\n--- temperature=0(稳定,每次结果应该相同)---")print(ask("随机给我一个1-10之间的数字,只回答数字",temperature=0,model="qwen3.5:9b",show_think=False))print(ask("随机给我一个1-10之间的数字,只回答数字",temperature=0,model="qwen3.5:9b",show_think=False))print("\n--- temperature=1(多样,每次结果可能不同)---")print(ask("随机给我一个1-10之间的数字,只回答数字",temperature=1,model="qwen3.5:9b",show_think=False))print(ask("随机给我一个1-10之间的数字,只回答数字",temperature=1,model="qwen3.5:9b",show_think=False))# ---------- 演示2:system prompt 的效果 ----------print("\n"+"="*60)print("演示2:system prompt 角色限定")print("="*60)print("\n--- 没有角色限定 ---")print(ask("帮我写首诗",model="qwen3.5:9b",show_think=False))print("\n--- 限定为客服角色 ---")print(ask("帮我写首诗",system="你是一家叫「星河科技」的公司的客服,只回答产品相关问题,其他问题礼貌拒绝",model="qwen3.5:9b",show_think=False))# ---------- 演示3:deepseek-r1 思考链 ----------print("\n"+"="*60)print("演示3:deepseek-r1 思考链(观察推理过程)")print("="*60)print(ask("9.9 和 9.11 哪个数字更大?",model="deepseek-r1:7b",temperature=0.6,show_think=True))# ---------- 演示4:多轮对话记忆 ----------print("\n"+"="*60)print("演示4:多轮对话(验证上下文记忆)")print("="*60)session=ChatSession(system="你是一个友好的助手",model="qwen3.5:9b",show_think=False)print("👤 我叫张三")print(f"🤖{session.chat('我叫张三')}\n")print("👤 我在学习 Python 和大模型开发")print(f"🤖{session.chat('我在学习 Python 和大模型开发')}\n")print("👤 我叫什么名字?在学什么?")print(f"🤖{session.chat('我叫什么名字?在学什么?')}\n")# 查看传给模型的完整历史session.show_history()# ---------- 演示5:交互式对话 ----------print("\n"+"="*60)print("演示5:交互式对话(输入 quit 退出)")print("="*60)interactive_session=ChatSession(system="你是一个耐心的 AI 学习助手,用简单易懂的方式解释概念",model="qwen3.5:9b",show_think=False)whileTrue:user_input=input("\n👤 你:").strip()ifuser_input.lower()in["quit","exit","退出","q"]:print("对话结束")breakifnotuser_input:continuereply=interactive_session.chat(user_input)print(f"🤖 助手:{reply}")

六、思考与延伸

Q:模型越大越好吗?

不一定。9b 的模型在日常问答、写作、代码辅助上已经够用,而且速度更快。30b+ 的模型在复杂推理、长文档理解上有明显优势,但需要更强的硬件。实际项目里先用小模型跑通,再按需升级。

Q:本地模型和云端模型的选择?

本地模型:数据不出网(适合隐私数据)、免费、受硬件限制 云端模型:能力更强、随用随取、按量计费、数据上传云端

学习阶段用本地,生产环境根据数据敏感程度和能力需求决定。

Q:history 一直增长怎么办?

对话很长时,history 会越来越大,超出 context window 后模型就会"失忆"。实际项目里的处理方式:

  • 只保留最近 N 轮对话
  • 把早期对话摘要压缩后放入 system prompt
  • 用向量数据库存历史,按需检索(RAG 思路)

Q:为什么 deepseek-r1 的 temperature=0 仍然不稳定?

因为推理模型先生成<think>思考链(几百次采样),GPU 浮点运算的微小误差会在思考链中累积,导致最终答案措辞不同。对于需要稳定输出的场景,推理模型反而不如普通模型合适。


七、参考资料

  • Ollama 官网
  • Ollama 模型库
  • OpenAI Python 库文档
  • deepseek-r1 官方说明
  • Qwen3 模型介绍
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 23:27:13

skills CANN开源社区贡献技能包开发指南

前言 开源社区的健康运转&#xff0c;不仅依赖核心代码的贡献&#xff0c;还需要降低贡献门槛、提供清晰的指南和自动化工具。skills仓库是CANN开源社区的"贡献技能包"&#xff0c;提供了一系列辅助脚本、代码模板、CI检查和文档生成工具&#xff0c;帮助新手快速上…

作者头像 李华
网站建设 2026/5/25 23:26:35

特斯拉与SpaceX软件开发体系

特斯拉与SpaceX软件开发体系 摘要 特斯拉与SpaceX作为埃隆马斯克领导下的两家标志性科技企业&#xff0c;在地面交通电动化和太空探索两个领域分别建立了独特的软件开发体系。本文基于公开的技术演讲、招聘信息、工程师访谈及行业分析资料&#xff0c;从软件开发模式、团队人…

作者头像 李华
网站建设 2026/5/25 23:21:05

Android 高频面试题汇总,26 道经典考题轻松应对面试

前言 面试时总被面试官挖的Android基础题掉坑里&#xff1f;整理了26道面试题 &#xff0c;牢固你的基础&#xff01;&#xff08;附《Android开发面试题以及答案整理》&#xff01;&#xff09; 1.如何对 Android 应用进行性能分析 2.什么情况下会导致内存泄露 3.如何避免 …

作者头像 李华