正确看待 LLM
大语言模型(LLM)不是无所不能的魔法,而是一种强大的工具。正确看待 LLM,了解它的能力边界和特点,选择正确的模型做正确的事,这至关重要。我们不应该用它来做它不擅长的事,不要“强模型所难”。
数学计算
LLM 在原生对话中并不擅长精确的数学计算。许多模型可能无法直接比较 9.11 和 9.9 的大小,甚至在分析下文 pack14
函数时,会为了让逻辑自洽而说出 6 * 14 = 7 * 8 这样荒谬的结论。
原理简述: 这背后是分词(Tokenization)机制和模型本质共同作用的结果。LLM 将文本切分成一个个“词元”(Token)。像 9.11 可能会被切分为 「9」、「.」、「11」 三个词元,模型在处理时看到的是序列模式,而非一个单一的数值。它本质上是一个语言模式匹配器,不是一个符号计算器。虽然它可以通过学习海量文本“记住”简单的计算结果(如 2+2=4),但对于稍复杂的、非常见的或需要多步推理的计算,它很容易出错。
因此,与其让它冒着高风险硬算,不如利用它的代码能力。
例如以下是一个不好的提问方式,CIECAM 16 有很多步运算,Gemini 2.5 Pro 也无法正确的直接计算,而且耗时很长。
XYZ = [19.01, 20.00, 21.78]
XYZ_w = [95.05, 100.00, 108.88]
L_A = 318.31
Y_b = 20.0
surround = "Average"
请根据以上输入,计算 CIECAM 16 模型预测的色貌属性。
不如直接让它写 Python 函数,更好的提问方式:
请编写一个 Python 函数,该函数接收 CIECAM16 模型的输入参数(XYZ, XYZ_w, L_A, Y_b, surround),并返回计算出的色貌属性。请使用 NumPy 库进行数值运算。
这样,Gemini 2.5 Pro 这样的顶级模型能够给出一个相当完整和接近正确的代码。
推理:思维链的价值
对于需要多步分析的复杂问题,选择一个擅长推理的模型非常有价值。其核心价值在于思维链(Chain-of-Thought, CoT),即模型展示其一步步的思考过程,有时候比回答本身更有价值。
一个清晰、完整的思维链,能让你:
- 验证其逻辑:了解它是如何得出结论的,从而判断结论的可靠性。
- 发现错误:如果模型在某一步出错了,可以清晰地看到问题所在。
- 学习新思路:观察模型的思考路径,有时能为你提供解决问题的新视角。
DeepSeek 的 R1 是一个很好的选择,它的思维链完整,详细,又不会显得过度冗长。别的模型可以试试加上“让我们一步步地思考。”
及时止损:不要试图纠正模型
现在,大模型的上下文窗口越来越长,一些模型甚至提供百万级别的上下文长度。但这并不意味着它在长程对话中总能保持高水平的性能。事实上,长上下文是一把双刃剑,尤其是在模型开始犯错时。
当你试图在对话中反复纠正一个犯错的模型,它先前的错误回答会作为历史记录,一同被打包进新的上下文中。这会形成一个污染的语境,导致模型陷入逻辑混乱的恶性循环。
你会观察到,模型可能会开始在其固有的错误思路上打转,即便你指出了问题,它也难以跳出。一个非常明显的“危险信号”是:当模型屡次犯错后,开始频繁、强烈地道歉,并使用“非常抱歉”、“我完全错了”、“我再试一次”等带有情感色彩的词汇时,这通常意味着它的推理链已经被彻底污染。
此时,最明智的做法是及时止损。不要再浪费 Token 和时间去“鞭策”或“教导”它,这大概率只会让你收获更多错误的信息。
正确的做法是:
- 编辑重试:如果你的工具支持,直接删除从出错开始的对话轮次。然后,修改你的提示词,增加更明确的约束,或者直接排除掉它之前犯错的思路,然后重新提问。
- 另起炉灶:这是最干净利落的方法。新开一个对话,并设计一个更优的初始提示词。把你在上一次失败中学到的经验融入进去,比如给模型更丰富的背景信息、更明确的指令,甚至直接告诉它要警惕哪些可能的错误思路。
与 LLM 协作,更像是为一次复杂的计算设定初始参数,而不是在教一个学生。你的目标是开启一个正确的思维链,而不是修复一个已经混乱的。
知识和幻觉
在不联网或不使用外部工具的情况下,LLM 的知识完全储存在其庞大的模型参数中,这被称为参数化知识。这些知识是它从海量训练数据中“记住”的模式。对于色彩科学这样的小众领域,参数量在 400B 以上,世界知识比较丰富的模型才有相对全面的理解。
这就引出了幻觉(Hallucination)问题。当你向模型索要具体的论文信息或要求它撰写专业综述时,它很可能会编造文献信息。
正确的做法是使用工具和联网,比如检索增强生成(RAG)。许多现代 LLM 产品(如集成了 Google 搜索的 Gemini、或一些 Deep Research 工具)都具备联网搜索能力。它们会先根据你的问题进行网络检索,然后基于可靠的、实时的信源来组织和回答问题,这能极大地提升答案的准确性和时效性。这方面,Gemini 和 Grok 做的较好。
另外,问模型“你是谁”和“今天几号”这样的问题,也不能代表模型的真实性能,这种东西一般是写在产品的系统提示词里的。
测试题:检验模型的真实能力
当谁又推出了新的模型,宣称自己是新的 SOTA,如何快速检验它的能力,是否在色彩科学和图像处理上有较好的性能?以下是我积累的测试题,用于快速尝试模型。
位压缩函数中的逻辑陷阱
import numpy as np
def pack10(data : np.ndarray) -> np.ndarray:
out = np.zeros((data.shape[0], int(data.shape[1]*(1.25))), dtype=np.uint8)
out[:, ::5] = data[:, ::4] >> 2
out[:, 1::5] = ((data[:, ::4] & 0b0000000000000011) << 6)
out[:, 1::5] += data[:, 1::4] >> 4
out[:, 2::5] = ((data[:, 1::4] & 0b0000000000001111) << 4)
out[:, 2::5] += data[:, 2::4] >> 6
out[:, 3::5] = ((data[:, 2::4] & 0b0000000000111111) << 2)
out[:, 3::5] += data[:, 3::4] >> 8
out[:, 4::5] = data[:, 3::4] & 0b0000000011111111
return out
def pack12(data : np.ndarray) -> np.ndarray:
out = np.zeros((data.shape[0], int(data.shape[1]*(1.5))), dtype=np.uint8)
out[:, ::3] = data[:, ::2] >> 4
out[:, 1::3] = ((data[:, ::2] & 0b0000000000001111) << 4)
out[:, 1::3] += data[:, 1::2] >> 8
out[:, 2::3] = data[:, 1::2] & 0b0000001111111111
return out
def pack14(data : np.ndarray) -> np.ndarray:
out = np.zeros((data.shape[0], int(data.shape[1]*(1.75))), dtype=np.uint8)
out[:, ::7] = data[:, ::6] >> 6
out[:, 1::7] = ((data[:, ::6] & 0b0000000000000011) << 6)
out[:, 1::7] += data[:, 1::6] >> 8
out[:, 2::7] = ((data[:, 1::6] & 0b0000000000001111) << 4)
out[:, 2::7] += data[:, 2::6] >> 6
out[:, 3::7] = ((data[:, 2::6] & 0b0000000000111111) << 2)
out[:, 3::7] += data[:, 3::6] >> 8
out[:, 4::7] = ((data[:, 3::6] & 0b0000000000001111) << 4)
out[:, 4::7] += data[:, 4::6] >> 6
out[:, 5::7] = ((data[:, 4::6] & 0b0000000000111111) << 2)
out[:, 5::7] += data[:, 5::6] >> 8
out[:, 6::7] = data[:, 5::6] & 0b0000000011111111
return out
请详细解释三个 Python 函数的作用。
这段代码源自 PiDNG 库,意图将 10-bit、12-bit、14-bit 的高位深数据压缩存储到 8-bit 的 uint8 数组中。pack10 和 pack12 的实现是正确的。
pack10:4 个 10-bit 数据 (40 bits) -> 5 个 8-bit 数据 (40 bits)。 pack12:2 个 12-bit 数据 (24 bits) -> 3 个 8-bit 数据 (24 bits)。
然而,pack14 函数是错误的。它试图将 6 个 14-bit 数据(6 * 14 = 84 bits)塞进 7 个 8-bit 字节里(7 * 8 = 56 bits),这在数学上是不可能的。正确的实现应该是将 4 个 14-bit 数据(4 * 14 = 56 bits)放进 7 个字节里。
常见错误:逐行解释 pack14 的代码,但没有意识到其逻辑错误。或为了让代码逻辑自洽,捏造错误的数学解释,例如声称 6 * 14 等于 56。
RGB 色域立方的概念理解
如何判断给定的 XYZ 三刺激值是否位于一个 RGB 空间的色域范围内。
该 RGB 空间由四个 CIE xy 坐标定义,分别代表红、绿、蓝和白色(其中白点亮度已归一化为 1.0)。
请提供对应的 Python 代码实现。
该问题考察 LLM 是否能理解 RGB 色域是一个三维的立方体(或平行六面体),而非一个二维的三角形。
正确的解法:
- 构建转换矩阵:利用红、绿、蓝三个原色的 xy 坐标和白点的 xy 坐标,计算出从 RGB 空间到 CIE XYZ 空间的 3x3 转换矩阵 M。
- 矩阵求逆:计算该矩阵的逆矩阵 M_inv,得到从 XYZ 到 RGB 的转换矩阵。
- 坐标转换:将给定的 XYZ 值左乘 M_inv,转换得到对应的 RGB 值。
- 范围判断:检查计算出的 R, G, B 三个分量是否同时位于 [0, 1] 的闭区间内。如果都在此范围内,则该 XYZ 值位于该 RGB 色域内;否则,位于色域外。
常见的错误解法:
- 将输入的 XYZ 值也转换为 xy 坐标。
- 接着,在 CIE xy 色度图上判断这个点是否位于由 R, G, B 三个原色 xy 坐标围成的三角形内。
- 这种方法完全忽略了颜色的亮度(Y)信息,是错误的。一个颜色可能色度正确,但因为太亮或太暗而超出目标色域范围。
CIECAM 16 代码实现
请编写一个 Python 函数,该函数接收 CIECAM16 模型的输入参数(XYZ, XYZ_w, L_A, Y_b, surround),并返回计算出的色貌属性。请使用 NumPy 库进行数值运算。
在 main 函数中,计算:
XYZ = [19.01, 20.00, 21.78]
XYZ_w = [95.05, 100.00, 108.88]
L_A = 318.31
Y_b = 20.0
surround = "Average"
考察模型的世界知识和编程能力,CIECAM 16 这样复杂的模型,给 LLM 完整的 PDF 输入会更好,但大参数量的模型有能力直接写出正确的代码。表现最好的是 Gemini 2.5 Pro,GPT 5-thinking 和 DeepSeek R1 0528,都只在 1-2 个公式上犯了一点小错误。正确的输出是:
{
"J": 41.7312,
"C": 0.1033,
"h": 217.0798,
"Q": 195.3717,
"M": 0.1074,
"s": 2.3448
}