第 6 章 实验指导:环境、sklearn、PyTorch 与 llama.cpp
《人工智能学习笔记》系列第 8 篇,共 9 篇。
本章导读 前面五章都是“知道”,这一章是“做过”。四条线:搭环境 → sklearn 传统机器学习 → PyTorch 深度学习 → llama.cpp 部署大模型。代码保留骨架,参数含义与踩坑点要自己走一遍才算数。
读完你应该能回答:① conda 虚拟环境为什么要建?② PyTorch 的训练循环包括哪些步骤?③ llama.cpp 部署时聊天模板为什么要与模型匹配?
本章骨架
| 小节 | 一句话核心 |
|---|---|
| 6.0 环境搭建 | Anaconda + 换源 + 虚拟环境;Windows / Ubuntu 两条路 |
| 6.1 机器学习实验 | sklearn 六件事:回归、逻辑回归、决策树、朴素贝叶斯、聚类 |
| 6.2 深度学习实验 | 纯 numpy 手写全连接、PyTorch 的 LeNet 与 ResNet |
| 6.3 部署实验 | llama.cpp 命令行推理 + HTTP 服务 + OpenAI 兼容接口 |
本章是四条线的动手功夫:搭环境 → sklearn 传统机器学习 → PyTorch 深度学习 → llama.cpp 部署大模型。下面是操作速查,只保留代码骨架和关键行;实验数据需自行准备,不依赖私有代码压缩包。
6.0 实验环境搭建
两条路线按需选:本机 Windows 可做机器学习实验;Ubuntu 服务器上跑更接近生产。
Windows 路线
① 装 Anaconda
官网仓库 https://repo.anaconda.com/archive/ 下载 Anaconda3-20xx.xx-Windows-x86_64.exe(Miniconda 同理,更轻)。安装时两个勾都打上:加入 PATH、注册为默认 Python 3.x。若机器上已有别的 Python 版本,建议先卸干净,否则后面容易出环境冲突。
② 换国内源(conda)
conda config --set show_channel_urls yes # 生成 .condarc
conda clean -i # 换源后清索引缓存
编辑用户目录下的 .condarc,把 default_channels 换成清华源(mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main、/pkgs/r、/pkgs/msys2),custom_channels 里补上 conda-forge、pytorch、pytorch-lts 三条 cloud 路径。
③ 换国内源(pip)
# 临时:只对当前这一条命令生效
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy
# 永久:写进 pip 配置
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
⚠ global.index-url 的 global 是配置节,不是当前环境。上述命令通常写用户配置;仅活动环境的配置文件已存在时默认优先写该文件。若要只配置某个环境,应先激活它,再用 pip config --site set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple。
④ 建虚拟环境
conda create -n ai-lab python=3.12 # 示例;Python 版本需与所装框架支持范围匹配
conda activate ai-lab # 提示符前出现 (ai-lab) 即进入
conda deactivate # 退出
conda remove -n ai-lab --all # 删掉整个环境
虚拟环境独立管理 Python 与包:在 A 环境装了 PyTorch,B 环境要用需另装一次,Spyder、Jupyter 的内核也要选择对应环境;仍需留意用户包、PYTHONPATH 等外部影响。

⑤ 装 PyTorch 与 scikit-learn
# 基础实验依赖(先激活 ai-lab)
python -m pip install torch scikit-learn
# 后续图像/音频实验所需组件
python -m pip install torchvision torchaudio
⑥ 验证
python -c "import torch; print(torch.__version__)"
# 输出 2.5.1+cpu 之类即成功
装包途中遇到 y/n 提示一律 y。
Ubuntu 路线
流程一样:Anaconda 安装包先 chmod +x 给执行权限 → 换源(~/.condarc + pip)→ 建虚拟环境 → 激活 → 按系统、Python 版本与硬件选择匹配的 PyTorch 安装方式。
6.1 机器学习实验(sklearn)
六个小实验,全部可在本机跑,用 scikit-learn + numpy + matplotlib。文件名与目录名仅作示例,文本、天气和邮件数据需自行准备。
| # | 实验 | 数据集 / 任务 | 关键 API 与要点 |
|---|---|---|---|
| 1 | 线性回归 | 自造房价:面积 → 价格 | LinearRegression().fit(X, y);lr.coef_ 取斜率、lr.intercept_ 取截距;predict([[130]]) 出预测值。X 要 reshape(-1,1) 成一列 |
| 2 | 从零实现线性回归 | 自行准备两列数值文本,首行为列名(np.loadtxt(skiprows=1)) | 手写 generate_gradient(矩阵形式 (1/m) * X.T·(Xθ−y))、gradient_descent(循环到 ‖梯度‖ ≤ 1e-5)、init_theta;每 10 轮记一次代价,画收敛曲线 |
| 3 | 逻辑回归 | 面积 + 租金 → 是否租出(二分类) | 与线性回归同套 API,输出 0/1;画分类边界看效果 |
| 4 | 决策树 | 自行准备天气与是否打球的表格数据 | 训练后 export_graphviz + graphviz把树导出成 PDF,直接看分裂用了哪些特征、阈值是多少 |
| 5 | 朴素贝叶斯(垃圾邮件) | 自行准备 spam/ 与 ham/ 两个目录的 txt | 见下方流水线 |
| 6 | K-means | make_blobs(n_samples=500, n_features=2, centers=4, random_state=1) | KMeans(n_clusters=k, random_state=…).fit(X);labels_ 取分簇、cluster_centers_ 取质心;先聚 3 类再聚 4 类,对比哪个更贴合真实簇 |
垃圾邮件那条流水线值得单独记(NLP 任务的最小完整流程):
1. 用 jieba 分词 + 自备停用词表(例如 stop_list.txt)过滤 → 一段文本变成词列表
2. Counter(chain(*全部邮件词列表)).most_common(500) → 取词频最高的 500 个词作词表
3. 每封邮件 → 500 维词频向量(词表里每个词在这封信中出现几次)
4. MultinomialNB().fit(向量矩阵, 标签) # 垃圾=1,正常=0
5. predict() 逐封判定 → "xxx is spam mail" / "is NOT spam mail"
套路总结:任何 sklearn 实验都是「建模型 → fit → predict」三步,差别只在特征怎么造。
from sklearn.linear_model import LinearRegression # 换算法只换这一行
m = LinearRegression() # 1. 建模型、设超参数
m.fit(X, y) # 2. 训练
pred = m.predict(X2) # 3. 预测
6.2 深度学习与开发框架实验
实验分两块:纯 numpy 手写(看清原理)→ PyTorch(用框架实现)。
实验一:从 0 实现三层全连接网络(纯 numpy)
任务是 MNIST 手写数字识别(6 万训练 + 1 万测试,28×28 灰度)。

- 读数据:MNIST 的 idx 文件不是图片,是「magic number + 维度 + 扁平像素数组」,用
offset跳过文件头再np.frombuffer出来; - 手写一个
MultiLayerNet类:__init__里按weight_init_std初始化W1/W2/W3、b1/b2/b3;predict做前向;loss用交叉熵;accuracy算准确率; - 反向传播手写:
backward里按链式法则一层层算dW3/db3 → dW2/db2 → dW1/db1,softmax + 交叉熵的组合导数可以化简成y_pred − y_onehot; - 训练主程序:
SGD/Momentum/AdaGrad/Adam换着跑,图上看 loss 与 accuracy 曲线差别——这是本实验的重点:同一网络、不同优化器,收敛速度明显不同。
实验二、三:PyTorch(LeNet 与 ResNet)
- 自定义 Dataset 三件套:
__init__(记路径和变换)、__len__(返回样本数)、__getitem__(读一张图 + 标签); - LeNet:
Conv1(1→6, 5×5) → ReLU → MaxPool(2×2) → Conv2(6→16, 5×5) → ReLU → MaxPool → Flatten → FC(16×5×5, 120) → FC(120, 84) → FC(84, 10)(这是 MNIST 版;另一种写法喂的是 CIFAR-10,首层改 3 通道、激活换tanh、跑 50 epoch,准确率 75.97%); - 训练循环自己写:
optimizer.zero_grad() → loss.backward() → optimizer.step(); - 保存优先用
torch.save(model.state_dict(), path);先实例化同结构模型,加载用model.load_state_dict(torch.load(path, weights_only=True))(文件仍应来自可信来源)。 - ResNet 实验是自己按
block拼不同深度的版本,体会「深度可参数化」。
PyTorch 流程速查
| 环节 | PyTorch |
|---|---|
| 前向方法 | forward |
| 网络基类 | nn.Module |
| 模式切换 | 默认动态图,静态用 torch.jit |
| 自动微分 | loss.backward() + .grad |
| 训练入口 | 自己写 for epoch in ... 循环 |
| 保存格式 | .pth / .pt |
| 部署 | TorchScript / ONNX |
6.3 模型部署与应用实验(llama.cpp)
目标:在没有 GPU 的普通机器上跑一个 1.5B 的小模型,并把它变成可被程序调用的服务。这也是第 4 章「模型部署」那一节的实际落地。
准备
- 程序:从 llama.cpp 上游获取与 Windows 或 Linux、CPU 架构匹配的程序。下文按 Windows 可执行文件与模型放在同一目录演示;实际目录和参数以所用版本为准。
- 模型:
DeepSeek-R1-Distill-Qwen-1.5B-Q6_K_L.gguf(约 1.4 GB),从hf-mirror.com下载,放到 llama.cpp 目录里。
命令行推理
.\llama-cli.exe -m DeepSeek-R1-Distill-Qwen-1.5B-Q6_K_L.gguf --temp 0.6 --color
| 参数 | 作用 |
|---|---|
-m | 模型文件路径 |
-cnv | 对话模式;是否默认开启取决于所用版本,先查看 --help |
--chat-template | 聊天模板;模板与模型不匹配会出现乱码、不停止 |
--temp | 输出随机性,本实验按 DeepSeek 官方推荐设 0.6;默认值以所用版本为准 |
--repeat-penalty | 重复度惩罚;设为 1.0 即关闭,默认值以所用版本为准 |
--top-k | 从概率最高的 K 个候选里选下一个 token,默认值以所用版本为准 |
--color | 区分用户输入与模型输出 |
DeepSeek-R1 的输出里 <think>…</think> 之间是思考过程,模型先推理再给答案。

起 HTTP 服务
.\llama-server.exe -m DeepSeek-R1-Distill-Qwen-1.5B-Q6_K_L.gguf
# 看到 "server is listening on http://127.0.0.1:8080" 即就绪
四种调用方式,从底层到高层:
import requests
# ① 健康检查:200 = 正常,503 = 还在加载模型
requests.get("http://localhost:8080/health")
# ② 原始 completion 接口:prompt 必须自己套聊天模板
data = {"prompt": "<|User|>你好<|Assistant|><think>\n"}
requests.post("http://localhost:8080/completions", json=data).json()["content"]
# ③ OpenAI 兼容接口(推荐):直接按 chat 格式调
data = {"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "你好"}]}
requests.post("http://localhost:8080/v1/chat/completions",
headers={"Content-Type": "application/json", "Authorization": "Bearer no-key"},
json=data).json()["choices"]
# ④ 用 openai 库(最省事)
import openai
client = openai.OpenAI(base_url="http://localhost:8080/v1", api_key="sk-no-key-required")
client.chat.completions.create(model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "你是谁?"}])
这一节的三个坑
- 聊天模板必须对上。上面的原始 prompt 采用 DeepSeek-R1-Distill-Qwen 官方模板中的
<|User|>、<|Assistant|>(注意是全角竖线),并带生成前缀<think>\n;实际使用时以 GGUF 内置模板为准,优先用/v1/chat/completions让服务端套模板,避免手写出错(官方模板配置); model字段是占位。本地 llama.cpp 不校验模型名,随便填(如gpt-3.5-turbo)即可,但 OpenAI SDK 要求这个字段存在;- 量化换内存。Q6_K 比原始 fp16 小一半多,精度损失很小;显存/内存不够时先降量化等级,再考虑换小参数模型。
思考题:部署 LLM 时,哪些参数对输出影响最大?——实际动手试 --temp、--top-k、--repeat-penalty 三个,回答风格变化最明显。
本章学习要点
- 两条环境路线:Windows(Anaconda + conda/pip 换清华源)、Ubuntu(先
chmod +x)。 - pip 换源的作用范围由配置文件决定:
global.index-url是配置节;默认通常写用户配置,活动环境已有配置文件时优先写它,只改当前环境用激活后的--site。 - 虚拟环境命令:
conda create -n 名字 python=x.x/conda activate/conda deactivate/conda remove -n 名字 --all。 - sklearn 三件套:
Model()→fit(X,y)→predict(X2);所有实验都是这个骨架。 - 垃圾邮件流水线:jieba 分词 → 去停用词 → 取词频前 500 作词表 → 500 维词频向量 →
MultinomialNB。 - 迁移学习两步:加载预训练参数 → 只改最后的分类头再微调(ResNet50 花卉 5 类)。
- llama.cpp 三种调用:
/health(200/503)→/completions(要自己套聊天模板)→/v1/chat/completions(OpenAI 兼容,本地不校验 model 名)。
更多推荐

所有评论(0)