《人工智能学习笔记》系列第 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见下方流水线
6K-meansmake_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 样本:先用图确认数据没读错

  • 读数据: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> 之间是思考过程,模型先推理再给答案。

llama-cli 的对话界面与参数

起 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": "你是谁?"}])

这一节的三个坑

  1. 聊天模板必须对上。上面的原始 prompt 采用 DeepSeek-R1-Distill-Qwen 官方模板中的 <|User|>、<|Assistant|>(注意是全角竖线),并带生成前缀 <think>\n;实际使用时以 GGUF 内置模板为准,优先用 /v1/chat/completions 让服务端套模板,避免手写出错(官方模板配置);
  2. model 字段是占位。本地 llama.cpp 不校验模型名,随便填(如 gpt-3.5-turbo)即可,但 OpenAI SDK 要求这个字段存在;
  3. 量化换内存。Q6_K 比原始 fp16 小一半多,精度损失很小;显存/内存不够时先降量化等级,再考虑换小参数模型。

思考题:部署 LLM 时,哪些参数对输出影响最大?——实际动手试 --temp、--top-k、--repeat-penalty 三个,回答风格变化最明显。

本章学习要点

  1. 两条环境路线:Windows(Anaconda + conda/pip 换清华源)、Ubuntu(先 chmod +x)。
  2. pip 换源的作用范围由配置文件决定:global.index-url 是配置节;默认通常写用户配置,活动环境已有配置文件时优先写它,只改当前环境用激活后的 --site。
  3. 虚拟环境命令:conda create -n 名字 python=x.x / conda activate / conda deactivate / conda remove -n 名字 --all。
  4. sklearn 三件套:Model() → fit(X,y) → predict(X2);所有实验都是这个骨架。
  5. 垃圾邮件流水线:jieba 分词 → 去停用词 → 取词频前 500 作词表 → 500 维词频向量 → MultinomialNB。
  6. 迁移学习两步:加载预训练参数 → 只改最后的分类头再微调(ResNet50 花卉 5 类)。
  7. llama.cpp 三种调用:/health(200/503)→ /completions(要自己套聊天模板)→ /v1/chat/completions(OpenAI 兼容,本地不校验 model 名)。

Logo

更多推荐