在收藏夹里吃灰的 AI 学习资料中,存在大量类似标题:“浙江大学 136 小时讲完 AI 人工智能大模型”“整整 200 集从入门到精通”“机器学习-深度学习-OpenCV”。这类标题看起来非常有吸引力,仿佛只要跟着刷完,就能自然学会大模型。但真实情况是:很多人刷了几十集视频后,环境还没搭起来,代码也跑不出结果。问题不在于这些概念太玄,而在于学习路径中没有把“概念理解、环境搭建、代码实现、报错排查”放在同一个闭环里。
真正值得投入时间的路线通常是这样:先用机器学习知识建立“数据、模型、损失、优化”的直觉,再用深度学习理解神经网络和 CNN 的训练过程,接着把 OpenCV 作为图像处理工具,解决视觉任务中的读取、预处理和轮廓分析,最后再进入大模型微调、部署和上层应用。本文会按照这条主线展开,每一步都会给出命令、代码片段、验证方式和常见坑。它不是要你按视频集数硬刷,而是要你形成一条可复现、可检查、可扩展的学习链路。
1. 先想清楚:大模型、机器学习、深度学习、OpenCV 是四件事还是一条链路
1.1 四个概念处于不同层级,先搭层级再装知识
很多人看到“机器学习、深度学习、OpenCV、大模型”出现在同一门课里,第一反应是“这四个东西都需要学会”。但如果把所有概念平等排列,学习时很容易迷失方向。实际上,它们并不在一个层级上。
可以从工程视角用一张表理解它们的边界:
| 概念 | 通俗理解 | 主要解决什么 | 典型技术或工具 |
|---|---|---|---|
| AI(人工智能) | 让机器表现出智能行为的学科总称 | 感知、决策、语言理解、规划等 | 搜索、知识图谱、机器学习、深度学习 |
| 机器学习 | 让计算机从数据中自动总结规律 | 分类、回归、聚类、异常检测 | scikit-learn、XGBoost、逻辑回归 |
| 深度学习 | 使用多层神经网络自动提取特征 | 图像识别、语音识别、文本建模 | PyTorch、TensorFlow、CNN、Transformer |
| 大模型 | 规模更大、任务覆盖面更广的神经网络模型 | 文本生成、代码补全、多模态理解、Agent 规划 | GPT、Qwen、DeepSeek、Llama |
| OpenCV | 计算机视觉的底层图像处理工具库 | 图像读写、滤波、边缘检测、几何变换 | OpenCV、Pillow、scikit-image |
从学习路径看,机器学习和深度学习是“方法论”,OpenCV 是“工具箱”,大模型是“更复杂的深度学习模型及其工程形态”。如果还没有任何机器学习基础,直接研究大模型的注意力机制、分词表和推理优化,通常会被大量数学符号和不熟悉的训练流程劝退。反过来,愿意先用小模型跑通训练流程,再去理解大模型,难度会平滑很多。
1.2 为什么“先机器学习、再深度学习、然后 OpenCV、最后大模型”是合理顺序
一条常见但有效的顺序是:机器学习基础 -> 神经网络与 CNN -> OpenCV 图像处理 -> 大模型微调与部署。它的合理性来自依赖关系。
机器学习基础知识里包含了数据划分、特征标准化、损失函数、梯度下降、过拟合与欠拟合等概念。这些概念直接复用到深度学习中。CNN 训练又需要使用图像数据和数据增强,图像数据的读入、裁剪、颜色空间转换正是 OpenCV 的强项。当完成一个小型视觉任务后,再看大模型相关概念,比如 Token、上下文窗口、量化、LoRA、推理服务,就更容易理解它们是“工程复杂度增加后的解决方案”。
反过来看,如果完全不理解梯度下降、训练集和测试集的区别,就开始部署一个 7B 参数的大模型,只会得到两个结果:要么把模型下载下来启动失败,要么调用远程 API 成功了,但完全不清楚后续微调和性能优化该从哪里下手。
1.3 OpenCV 不是深度学习的替代品,而是视觉工程中的“前置工具”
常见误区是将 OpenCV 和深度学习并列看待。实际上,OpenCV 无法替代深度学习模型,因为传统视觉算法不能理解复杂语义。它也不能解决所有工程问题,因为 OpenCV 的基础算子依赖人工设计规则。
在真实视觉项目中,OpenCV 更多承担三类工作:
- 采集和预处理:读取摄像头、图片缩放、颜色空间转换、滤波去噪。
- 几何分析:边缘寻找、轮廓提取、棋盘格标定、透视变换。
- 前后处理:把模型输出结果画框、计算目标坐标、统计面积和数量。
因此学习 OpenCV 时不要陷入“必须记住几百个函数”的误区。更合理的做法是记住图像在 OpenCV 中是 BGR 格式的 NumPy 数组,掌握通道转换、几何变换、轮廓处理、相机标定这几个常见环节,再把剩余函数当作字典去查。
2. 建一个可复用的 AI 实验环境,把机器学习、深度学习、OpenCV 装在一起
2.1 先固定 Python 版本,避免包与包互相冲突
学习环境里的依赖冲突非常多,最常见原因是 Python 环境混乱、pip 安装到了错误解释器、OpenCV 与 NumPy 版本不匹配。为了减少这类问题,第一步是创建独立虚拟环境。
常用的环境中,Python 3.10 或 3.11 仍然有较好的兼容性,PyTorch、NumPy、OpenCV 对大版本的支持比较充分。具体是否选择 3.10 还是 3.11,要看目标框架的官方文档。先创建虚拟环境并激活:
conda create -n ai-study python=3.10 -y conda activate ai-study不使用 Conda 时,也可以用 venv 创建虚拟环境:
python3.10 -m venv .venv source .venv/bin/activate虚拟环境的核心价值是隔离。把机器学习依赖、深度学习依赖、图像处理依赖放进同一个环境中,如果项目升级导致冲突,可以快速销毁重建,不会影响系统 Python。
2.2 安装 PyTorch、scikit-learn、OpenCV、Jupyter 和常用视觉库
安装 PyTorch 时,需要注意 CPU 版和 GPU 版的区别。如果只是为了理解原理,CPU 版本足够跑通 MNIST 这类小实验。如果计划训练真实数据集,则建议安装对应 CUDA 版本的 PyTorch。这里给出比较通用的安装顺序:
pip install --upgrade pip pip install numpy scikit-learn pandas matplotlib jupyter pip install torch torchvision pip install opencv-python安装 OpenCV 时需要注意,不要同时安装opencv-python和opencv-contrib-python,因为两者会覆盖同一个cv2包,容易造成诡异报错。常规课程和实验使用opencv-python即可。如果需要使用 SIFT、SURF 等扩展模块,再单独使用opencv-contrib-python。
2.3 用一段脚本验证环境是否真的可用
许多人在安装完成后直接开始写模型代码,等训练时报错才回头检查依赖。更合适的做法是安装完成后立刻执行一次最小验证。
import sys import numpy as np import sklearn import cv2 import torch print("python:", sys.version) print("numpy:", np.__version__) print("sklearn:", sklearn.__version__) print("opencv:", cv2.__version__) print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("device name:", torch.cuda.get_device_name(0) if torch.cuda.is_available() else "cpu")预期结果如表所示:
| 检查项 | 成功标志 | 失败时的常见原因 |
|---|---|---|
| Python 版本 | 当前创建的虚拟环境路径 | 未激活虚拟环境 |
| NumPy | 能打印版本号 | 安装不完整或静默失败 |
| OpenCV | 能打印版本号 | 包名错误、Python 解释器不对 |
| PyTorch | 能打印版本号 | 安装源、网络或依赖问题 |
| CUDA 可用 | 返回 True | 安装的 PyTorch 是 CPU 版、显卡驱动过旧 |
如果上方脚本中torch.cuda.is_available()返回False,不要直接认为显卡坏了。先执行nvidia-smi查看驱动适不适配当前 CUDA 版本,再确认安装命令里的 CUDA 版本与驱动版本是否匹配。
2.4 学习环境可以轻量跑通,生产环境还需要额外保障
学习阶段只要保证代码能跑、能观察结果即可。到了生产环境,仅仅有一个 Python 虚拟环境是不够的,还需要考虑依赖锁定、配置外置化、日志、监控、模型版本管理和回滚策略。比如训练代码通常用requirements.txt或environment.yml固定版本,部署模型时还要把模型文件、推理脚本、预处理逻辑一起打包,并检查输入输出的链路。
3. 用一个最小分类任务打通机器学习闭环,再谈大模型训练
3.1 机器学习不是背算法,而是理解“数据进入模型到输出指标”的完整循环
很多视频课程会用大量时间讲不同算法公式,但初学者最容易获得正反馈的方式是先跑通一个最小的分类任务。以鸢尾花分类为例,完整流程包括加载数据、划分训练集和测试集、选择模型、训练、评估。
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, classification_report X, y = load_iris(return_X_y=True) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) model = LogisticRegression(max_iter=500) model.fit(X_train, y_train) y_pred = model.predict(X_test) print("accuracy:", accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))这里有几个关键动作:
train_test_split用来避免模型在见过的数据上评估,否则会高估模型效果。StandardScaler只对训练集调用fit_transform,对测试集只调用transform。原因是测试集不能参与均值和方差计算,否则会造成信息泄漏。LogisticRegression虽然名字里有回归,但在分类任务中是最常用的线性分类器之一。
学习机器学习的重点不是记住每个算法步骤,而是理解为什么“训练集、测试集、特征缩放、损失评估”是任何监督学习任务都必须考虑的环节。这个判断力会延续到大模型微调和评估阶段。
3.2 从“训练精度”到“泛化精度”,是初学者最容易忽略的跳跃
小数据集里经常出现训练集正确率接近 100%,测试集正确率却很低的现象,对应的是过拟合。解决方向通常有数据增强、正则化、简化模型、增加训练样本。
这里可以先建立一条检查清单:
- 训练集和测试集是否来自同一分布。
- 特征是否做了合适的标准化或归一化。
- 模型复杂度是否高于数据规模能支撑的范围。
- 验证指标是否只看准确率而忽略了类别不均衡。
- 是否用测试集反复调参导致测试集信息泄漏。
3.3 多学一点:分类问题之外的监督任务用相同逻辑迁移
机器学习中的回归任务同样遵循这个闭环。把load_iris换成房价预测,把评估指标从准确率换成均方误差或平均绝对误差,主流程不会变化。有了这个最小闭环后,进入深度学习时会发现:增加网络层数、改变激活函数、调整优化器,本质上还是在控制特征提取能力和拟合程度。
4. CNN 和训练轮数背后,是整个深度学习的训练逻辑
4.1 神经网络和传统机器学习最大的差异是自动特征提取
传统机器学习需要人工构造特征,比如图像的颜色直方图、纹理统计量。卷积神经网络做得更多的是“让网络在训练中自己学习滤波核”,因此可以从原始像素出发完成分类。
一个经典的最小案例是 MNIST 手写数字识别。图像是 28x28 的灰度图,类别有 10 种。用 PyTorch 实现时,可以先用一个简单 CNN 完成端到端训练:
import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, transforms transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_ds = datasets.MNIST(root="./data", train=True, download=True, transform=transform) test_ds = datasets.MNIST(root="./data", train=False, download=True, transform=transform) train_loader = DataLoader(train_ds, batch_size=64, shuffle=True) test_loader = DataLoader(test_ds, batch_size=256, shuffle=False) class SimpleCNN(nn.Module): def __init__(self): super().__init__() self.features = nn.Sequential( nn.Conv2d(1, 32, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2), ) self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(64 * 7 * 7, 128), nn.ReLU(), nn.Linear(128, 10), ) def forward(self, x): return self.classifier(self.features(x)) model = SimpleCNN() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) loss_fn = nn.CrossEntropyLoss() def train_one_epoch(): model.train() total_loss = 0.0 for x, y in train_loader: pred = model(x) loss = loss_fn(pred, y) optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() return total_loss / len(train_loader) def evaluate(): model.eval() correct = 0 total = 0 with torch.no_grad(): for x, y in test_loader: pred = model(x).argmax(dim=1) correct += (pred == y).sum().item() total += y.size(0) return correct / total for epoch in range(3): train_loss = train_one_epoch() acc = evaluate() print(f"epoch={epoch + 1}, train_loss={train_loss:.4f}, test_acc={acc:.4f}")这段代码中最重要的不是卷积层参数,而是完整训练逻辑:数据进入网络得到预测结果,损失函数计算预测与真实标签的差距,反向传播把梯度传给网络参数,优化器更新参数。每一轮训练结束后用测试集评估,就是为了观察模型是否出现过拟合。
4.2 训练轮数、批次大小和学习率如何影响精度
很多人会问“训练轮数越多精度越高吗”。答案并不绝对。轮数增加到一定程度后,训练损失会继续降低,但测试精度可能不再提升,甚至因为过拟合而下降。真正需要观察的是验证集或测试集上的指标,而不是训练集指标。
| 超参数 | 取值偏小 | 取值偏大 | 日常调试建议 |
|---|---|---|---|
| epoch(训练轮数) | 模型欠拟合,损失尚未收敛 | 模型可能在训练集上过度拟合 | 保存每轮验证指标,选择最佳轮数 |
| batch_size(批大小) | 梯度更新频繁,训练波动大 | 显存压力大,训练速度不一定更快 | 常见 16、32、64,按显存调整 |
| learning_rate(学习率) | 收敛太慢,可能停在局部极小值 | 损失发散,出现 NaN | 起步用 1e-3 或 1e-4,再按曲线调整 |
像 MNIST 这种小任务,即使使用 CPU,三轮训练通常也能在几十秒到几分钟内结束。因此不必追求“把网络改得很深”或者“训练 100 轮”,先把超参数变化对损失曲线的影响看明白,才是更重要的学习收益。
4.3 建议用“损失曲线”而不是只记最终精度
训练脚本里打印最终精度只是一个结果,调参时更需要观察每个 epoch 的训练损失和测试精度。一般建议把每轮数据保存到列表或写入 CSV,再用 matplotlib 绘制曲线。
如果发现训练损失下降很慢,可以先检查数据是否归一化、学习率是否过小。如果发现损失在训练中途突然变成 NaN,则优先怀疑学习率过大、数据中存在异常值或梯度爆炸。先定位是哪一个环节,再修改参数,不推荐用随机试参的方式碰运气。
5. OpenCV 在视觉任务里到底解决哪一环:预处理、轮廓与标定
5.1 图像进入深度学习模型之前,OpenCV 是必经的数据加工车间
摄像头采集的原始图像通常是 BGR 格式,尺寸参差不齐,还可能包含噪声。深度学习模型往往需要固定尺寸的 RGB 输入。OpenCV 的价值就是把原始图像转换成模型能吃的格式。
下面的代码展示了一条典型的预处理链:读取图像、转灰度、高斯模糊、Canny 边缘检测、阈值化,然后寻找轮廓。
import cv2 img = cv2.imread("demo.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) edges = cv2.Canny(blur, 50, 150) _, binary = cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours( binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) for cnt in contours: area = cv2.contourArea(cnt) if area < 100: continue x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.imwrite("demo_with_boxes.jpg", img)代码里的Canny负责找边缘,threshold生成二值图,findContours寻找连通区域。实际项目中不一定每一步都必须使用,通常的做法是:先尝试把目标区域变成高对比度二值图,再用轮廓分析去框出目标。如果图像光照不均,阈值就会失效,可以改用自适应阈值或背景差分。
5.2 OpenCV 4 之后,findContours 的返回值发生了变化
OpenCV 的早期教程中常见写法是:
img, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)在 OpenCV 3 之后,findContours不再返回图像本身,而是只返回两个值:轮廓列表和层级关系。因此很多从老课程复制下来的代码会报 ValueError。正确写法是:
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)如果确实需要在原图上绘制,请使用cv2.drawContours(img, contours, ...)或先复制原图,不要在未保留图像引用的情况下直接尝试从返回值中取图。这也是“看视频学习不如看报错学习”的典型例子。
5.3 棋盘格标定不是深度学习,但它是相机成像质量的基础
机器视觉项目中常常需要把像素坐标映射到真实物理坐标,棋盘格标定就是获取相机内参和畸变系数的一种标准方法。标定的目标并不是训练出一个模型,而是计算相机的内参矩阵、畸变系数和外参。
标定的关键步骤是检测棋盘格角点。以 Python 为例,核心流程如下:
import cv2 import numpy as np # 这里以 9x6 内角点棋盘为例 pattern_size = (9, 6) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) object_points = [] image_points = [] img = cv2.imread("checkerboard.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) found, corners = cv2.findChessboardCorners(gray, pattern_size, None) if found: corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) object_points.append(objp) image_points.append(corners) cv2.drawChessboardCorners(img, pattern_size, corners, found) cv2.imshow("checker", img) cv2.waitKey(0) cv2.destroyAllWindows()如果是从 C++ 工程介入视觉,另一种常见方式是使用cv::findChessboardCorners与cv::calibrateCamera,例如采集若干张角点图之后,统一求解相机参数。示例思路与 Python 版本一致:
cv::Size patternSize(9, 6); std::vector<cv::Point3f> objectPoints; for (int i = 0; i < patternSize.height; i++) { for (int j = 0; j < patternSize.width; j++) { objectPoints.push_back(cv::Point3f(j, i, 0.0f)); } } std::vector<cv::Point2f> corners; cv::Mat gray; cv::cvtColor(colorImage, gray, cv::COLOR_BGR2GRAY); bool found = cv::findChessboardCorners(gray, patternSize, corners); if (found) { cv::cornerSubPix(gray, corners, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.001)); cv::drawChessboardCorners(colorImage, patternSize, corners, found); }注意,上面代码中的patternSize是内角点数量,不是棋盘格方块数量。实际拍摄时通常需要 10 到 20 张不同角度、不同距离的棋盘图,才能获得比较稳定的标定结果。
5.4 传统视觉与深度学习在工业场景中如何配合
“深度学习是否一定能替代 OpenCV”是视觉方向经常被讨论的问题。实际工程结论通常是:两者互补。
| 需求场景 | 推荐方向 | 原因 |
|---|---|---|
| 固定光照下的尺寸测量 | 传统视觉 + 亚像素边缘 | 规则明确,速度快,便于验证 |
| 复杂纹理缺陷检测 | 深度学习语义分割或分类 | 缺陷形态多变,人工规则难覆盖 |
| OCR 文字识别 | 深度学习检测 + 识别 | 需要语义理解能力 |
| 目标计数与定位 | 传统轮廓或深度学习目标检测结合 | 先缩小候选区,再让模型识别 |
对于工业视觉项目,如果缺陷种类固定、光照可控,很多时候先用 OpenCV 预处理,再通过面积、灰度、几何特征筛选,已经能满足需求。深度学习模型更适合缺陷形态复杂、无法用固定阈值描述的场景。
6. 从图像模型过渡到大模型:微调、本地部署与 Spring AI 接入
6.1 Transformer 为什么能成为大模型的底层结构
CNN 通过卷积核提取局部特征,适合图像。大模型大多基于 Transformer,核心优势是通过注意力机制让每个位置都能关注序列中其他位置的信息。这种机制让模型可以处理长距离依赖,从而在文本、代码、多模态任务上表现出更强的能力。
从学习角度,不必一开始就把注意力公式背下来。可以把它理解为“模型在计算每个词和句子中其他词的相关程度”。相关程度越高,注意力权重越大。大模型的训练过程和前面的 CNN 没有本质区别,仍然是前向传播、计算损失、反向传播、更新参数,只是数据规模、参数量和并行策略大幅增加。
6.2 微调不等于重新训练基础模型,LoRA 是最容易上手的入口
市面上会看到“大模型微调”和“全量预训练”这些词。全量预训练需要海量数据和昂贵算力,绝大多数个人开发者无法复现。日常说的微调,更多指在已经训练好的基础模型上用领域数据做继续训练。
LoRA 是一种低秩适配方法,它不会对全部参数做大更新,而是在原始权重旁增加少量可训练参数,从而把微调成本降下来。下面是一段基于 peft 库的示意代码,用来演示操作结构,实际训练还需要结合数据集、分词器和训练脚本:
from peft import LoraConfig, get_peft_model lora_config = LoraConfig( r=8, lora_alpha=16, target_modules=["q_proj", "v_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(base_model, lora_config) model.print_trainable_parameters()从工程角度看,LoRA 带来的直接收益是显存占用更小、训练更快,并且微调后得到的权重文件通常比原模型小很多,便于保存和分发。初学者即使不训练大模型,也建议先理解“冻结原参数、只训练少量适配参数”的思想。
6.3 本地部署大模型不是把模型文件塞进内存,而是选量化、推理框架和 API 封装
本地部署大模型的难点主要在于显存或内存不足、推理速度慢、依赖环境复杂。不同参数规模的模型对硬件要求差异很大,常见策略如下:
| 模型规模 | 常见硬件要求 | 参考部署方式 |
|---|---|---|
| 1B 到 3B 参数 | 8GB 到 16GB 内存,或 4GB 以上显存 | 直接推理,必要时量化 |
| 7B 到 14B 参数 | 一般需要 16GB 以上内存,量化后更友好 | GGUF 量化 + llama.cpp 或 Ollama |
| 70B 以上参数 | 通常需要多卡或大内存服务器 | 分布式推理或量化 + 服务化框架 |
很多入门项目会先在本地启动一个 Ollama 服务,然后拉取对应模型并调用。模型下载尽量从可访问的国内模型仓库或官方提供的方式获取,生产环境还需要加一层权限控制、限流和日志。不要把所有内容都写死在代码里。
6.4 Spring AI 让 Java 工程可以统一接入大模型能力
如果团队技术栈是 Java,大模型应用不一定只能写 Python。Spring AI 是 Java 生态中用于对接 AI 模型的框架。它把聊天、向量存储、提示词模板等能力抽象成统一接口,让应用层不需要关心底层是 OpenAI 兼容服务,还是本地 Ollama。
在 Spring Boot 项目中,通常会先在 Maven 中引入 BOM 和对应 Starter。由于 Spring AI 的版本迭代较快,落地前要确认当前 Spring Boot 版本和 Spring AI 版本是否兼容。配置层面,调用本地 Ollama 的 OpenAI 兼容接口时,可以使用类似下面的结构:
spring: ai: openai: base-url: ${AI_BASE_URL:http://localhost:11434/v1} api-key: ${AI_API_KEY:EMPTY}这个配置的含义是:应用通过 OpenAI 兼容协议访问本地大模型接口。只要base-url指向本地推理服务,就能把大模型能力接入 Java 业务系统。对初学者来说,先用 Python 跑通模型推理,再切换到 Spring AI 构建 Rest API,是比较顺畅的路径。
6.5 大模型应用层正在走向 RAG 和 Agent
大模型不只会做文本生成。实际业务中经常需要模型回答私有知识库内容,这就用到了 RAG,即检索增强生成。RAG 先把文档切块、向量化,再根据用户问题检索相关片段,最后把检索结果交给模型生成答案。
Agent 则进一步把模型从“对话引擎”变成“任务执行器”。Agent 可以调用工具、读取数据库、访问外部服务,再根据反馈决定下一步操作。这个趋势意味着学习大模型时,除了关注模型结构和训练,还要关注业务流程、工具调用、提示词工程、上下文长度管理和结果校验。真正有工程价值的不是“模型会说话”,而是“模型在一个受控流程中能稳定完成任务”。
7. 从安装到训练,最容易卡住的问题和排查顺序
7.1 cv2 安装成功却报 libGL 或导入错误
现象:pip install opencv-python安装成功,但import cv2报错,常见信息包括libGL.so.1: cannot open shared object file。
原因:OpenCV 的 Python 包依赖系统的 libGL 等图形库,某些精简服务器镜像没有安装这些依赖。
排查顺序:
- 确认是否在正确的虚拟环境中。
- 执行
python -c "import cv2"复现。 - 如果是 Linux 环境,尝试安装系统依赖库。
例如在 Debian/Ubuntu 服务器上,可以安装如下基础依赖:
apt-get update apt-get install -y libgl1 libglib2.0-0 libsm6 libxext6 libxrender-dev这类系统依赖属于环境基础部分,安装后需要重新启动 Python 进程。预防建议是“先按官方安装说明准备好系统依赖,再安装 cv2”。
7.2 findContours 报 not enough values to unpack
现象:运行旧教程代码时出现ValueError: not enough values to unpack (expected 3, got 2)。
原因:OpenCV 不同大版本中findContours返回值数量不同。OpenCV 2 返回三个值,包括原图、轮廓、层级。OpenCV 3 之后只返回轮廓和层级。
解决方式:把img, contours, hierarchy = cv2.findContours(...)改为:
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)如果代码是从老项目迁移,建议先确认项目当前依赖的 OpenCV 版本,再决定是否保留原返回值格式。
7.3 PyTorch 安装成功但 CUDA 不可用
现象:torch.cuda.is_available()返回False。
排查顺序:
- 执行
nvidia-smi,确认驱动是否正常。 - 对比
nvidia-smi中显示的最高 CUDA 版本和当前 PyTorch 编译时的 CUDA 版本。 - 确认安装命令是否带
cpu字样。如果使用了 CPU 版本,即使有显卡也无法使用 CUDA。 - 检查虚拟环境中是否安装过多个 PyTorch 版本。
解决思路不是无脑重装,而是先确认“驱动支持的 CUDA 版本”和“PyTorch 依赖的 CUDA 版本”匹配。如果只是学习小任务,CPU 环境也能继续,不需要在显卡问题上花费过多时间。
7.4 深度学习训练 loss 不下降或者变成 NaN
现象:模型训练多次 epoch,loss 始终不下降;或者 loss 突然变成nan。
常见原因和检查方式如下表:
| 现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| loss 下降缓慢 | 学习率太小、数据未归一化 | 打印每个 batch 的平均 loss | 调大学习率,确认输入数据范围正常 |
| loss 发散或变成 NaN | 学习率过大、梯度爆炸 | 查看 loss 是否出现剧增 | 调小学习率,检查是否存在异常值 |
| 训练 loss 低但测试 acc 不升 | 过拟合 | 每个 epoch 输出验证指标 | 增加数据增强、降低模型复杂度、早停 |
| 网络输出恒为某一类 | 类别不均衡、损失权重不合理 | 统计每个类别的样本数量 | 调整损失函数权重或采样策略 |
排查时不要同时改多个参数。每次只修改一个变量,否则无法定位是哪个改动带来的效果。
7.5 本地部署大模型时显存或内存不足
现象:模型加载过程中进程被系统杀掉,或者推理时显存溢出。
原因:模型参数占用的显存只是基准,推理时的激活值、KV Cache 也会占用显存。高精度参数类型会让资源需求进一步上升。
处理思路:
- 选择更小的模型。
- 使用量化模型,例如把半精度权重转换为 4bit 或 8bit 表示。
- 减少上下文长度。
- 使用主内存推理,但需要接受较慢的速度。
- 部署前先查看模型说明中的硬件要求,不要在资源不足的环境中反复硬试。
8. 大规模视频课程不应该按“集数”硬刷,按阶段验收才是关键
8.1 把学习过程拆成四个阶段,每个阶段有明确验收标准
学习 AI 大模型最大的问题不是资料少,而是资料太多,缺乏停止点。更合理的做法是每个阶段完成一个“最小实验”,然后停下来检查是否掌握关键结论。
| 阶段 | 核心任务 | 验收标准 |
|---|---|---|
| 第一阶段 | 机器学习基础 | 能独立完成一个分类或回归任务,能解释训练集、测试集、过拟合 |
| 第二阶段 | 深度学习与 CNN | 能在 MNIST 或 CIFAR-10 上训练 CNN,能绘制 loss 曲线 |
| 第三阶段 | OpenCV 图像处理 | 能完成图像预处理、轮廓检测,理解棋盘格标定流程 |
| 第四阶段 | 大模型应用部署 | 能启动本地模型,用 Python 或 Spring AI 完成一次对话请求 |
不要因为视频有 200 集就要求自己 50 集内看完。很多视频材料为了覆盖完整目录,会有大量重复铺垫。如果某个章节的环境你已经跑通并理解了输出,就可以跳转到下一个未知模块。
8.2 每个实验都要做到“可重复运行”,而不是“跟着视频敲一遍”
打开课程边看边敲代码,通常只是形成短期记忆。真正有效的方法是把每个实验整理成独立目录,包含:
- 完整的依赖文件。
- 可以直接运行的脚本。
- 输入样例和预期输出。
- 运行失败时的常见问题和解决方案。
例如视觉项目可以按如下结构组织:
vision-demo/ ├── requirements.txt ├── data/ ├── scripts/ │ ├── preprocess.py │ ├── train_cnn.py │ └── detect_contours.py ├── output/ └── README.md这样做的价值在于,如果一个月后重新运行某个实验,不需要回忆当初装过哪些包。依赖文件、README 和脚本本身就是最好的笔记。
8.3 生产环境要在大模型实验跑通后补齐工程保障
在本地跑通推理不等于可以上线生产。真实系统通常还需要:
- 把所有模型地址、密钥、环境配置放到环境变量或配置中心。
- 在模型接口前增加超时、重试、限流和熔断。
- 对模型输入做内容安全过滤和长度限制。
- 记录每次请求的日志,包括调用模型、耗时、返回状态和错误信息。
- 提供版本管理,让模型文件和业务代码可以一起回滚。
- 建立监控,关注推理延迟、token 消耗、显存和错误率。
如果只是学习阶段,可以先忽略这些复杂项。但当项目从实验转向演示,再转向正式业务时,这些点都是必须补上的工程纪律。
8.4 自查清单:学完这条路线后应该能回答的问题
- 机器学习和深度学习的核心区别是什么?
- 训练集和测试集如果混在一起,会造成什么问题?
- train loss 下降、val loss 不降,下一步应该调整什么?
- OpenCV 中图像为什么用 BGR 表示?
- findContours 在 OpenCV 4 中返回几个值?
- 棋盘格标定为什么要收集多张不同角度的图像?
- LoRA 为什么能在较少显存占用下完成微调?
- 本地部署大模型时,量化解决了什么问题,代价是什么?
- Spring AI 在大模型应用层扮演什么角色?
- 如果上游模型服务超时,业务代码应不应该无限等待?
这些问题的答案能在自己的代码和日志里找到,而不是只能在别人的视频里听到。这个标准,比“看完 200 集”更能衡量是否真正上手了 AI 大模型这条技术路线。下一组小实验的推荐顺序是:把 MNIST CNN 换成自己的摄像头数据;把目标检测模型接进一个 OpenCV 推理脚本;再尝试用本地大模型接口做一个带日志和限流的 Java REST 服务。每一步都会比单纯增加观看集数带来更明显的工程成长。