ARTS 打卡第 8 天 - Prompt Engineering、DemoGPT 和 Get Hands Dirty

A：27. 移除元素

给你一个数组 nums 和一个值 val，你需要 原地 移除所有数值等于 val 的元素，并返回移除后数组的新长度。
不要使用额外的数组空间，你必须仅使用 O (1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数，但输出的答案是数组呢？
请注意，输入数组是以「引用」方式传递的，这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
//nums 是以 “引用” 方式传递的。也就是说，不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度，它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

示例 1：
输入：nums = [3,2,2,3], val = 3
输出：2, nums = [2,2]
解释：函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如，函数返回的新长度为 2 ，而 nums = [2,2,3,3] 或 nums = [2,2,0,0]，也会被视作正确答案。

示例 2：
输入：nums = [0,1,2,2,3,0,4,2], val = 2
输出：5, nums = [0,1,4,0,3]
解释：函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

Copy
function removeElement(nums: number[], val: number): number {
for (let i = 0; i < nums.length; i += 1) {
if (nums[i] === val) {
nums.splice(i, 1)
i -= 1
}
}
return nums.length
}

提交结果为：

113/113 cases passed (76 ms)
Your runtime beats 18.87 % of typescript submissions
Your memory usage beats 98.31 % of typescript submissions (42.1 MB)

题目比较简单，没刷过 LeetCode，先从 Easy 题目刷起，练练手感，但长期这样低水平重复地刷应该没有提升效果，后续要像《刻意练习》中提到的一样，适当有计划地提升难度来形成解决算法题的心理表征，而不是通过眼熟心不熟来解题，这也会导致容易遗忘。

R：Prompt Engineering

大部分 Prompt Engineering 教程都不值得 8 页纸，每种技巧用几个示例就能讲清楚，剩下的内容都是效果基准测试。一个更容易使用的基准测试基础设施对社区的作用都更大，迭代式的 Prompt 和外部工具用起来不是很容易，让社区采纳更是不易。

Basic Prompt

Zero-Shot

将任务文本直接输入给模型得到结果。

Few-Shot

Few-shot 就是在任务文本之前，提前给一些高质量的查询、返回的示例，让模型更好地理解我们的意图，同时这也意味着使用更多的 token 数量。

一些研究发现这些 few-shot 示例的格式、示例内容和顺序也会很大程度地影响模型输出的效果。

此外在分类任务上，还会有样本标签分布数量偏差、标签位置偏差、常见 token 偏差等，我们可能还需要在输入 N/A （个人理解是空输入）时校准输出标签的概率来抵消这种偏差。

作者提到了一些提供示例的技巧：选择多样性、有对比的示例，随机决定示例的位置等等。

Instruction Prompting

Frew-shot 是为了更好地让模型理解我们的意图，但会有输入长度限制。既然目标是为了让模型理解我们的意图，我们也可以直接指示模型按照我们的指令行事。

InstructGPT 就是一个被训练出来更好地理解用户意图的语言模型。RLHF 是实现这个目的的通常方法。用这种方式可以让模型更好地理解用户意图的同时还降低与模型交互的成本。

对 instruction 模型来说，我们要详细地描述我们的需求，如果有不希望模型做的事情直接指出来，而不是告诉它要做什么。

In-context instruction learning 就是把 few-shot 和 instruction prompting 结合起来，给出几个包含 “任务定义”、“输入” 和 “输出” 的示例。

Self-Consistency Sampling

Self-Consistency Sampling 是指生成多个 temperature > 0 的输出然后选取其中效果最好的。(我个人没感觉到这和 Prompt 构造有什么关系，看起来就是多跑几次选效果最好的)

Chain-of-Thought (CoT)

CoT 是通过一系列短句来按步骤说明任务的推理逻辑，从而得到最终的答案。这种技巧只在复杂任务中有明显的效果，简单任务收效甚微。CoT 也可以和 zero-shot、few-shot 结合起来用。

Automatic Prompt Design

Prompt 可以视为一组前置的 token 序列，这个序列确保用户输入的时候我们能得到想要的输出，因此我们可以把这个前置 token 序列当作可训练的参数，通过在向量空间中做梯度下降直接进行优化。比如 AutoPrompt、Prefix-Tuning、P-tuning 和 Prompt-Tuning 都是围绕这个思路来做的，这些方法的发展趋势是让构造过程变得越来越简单。

Automatic Prompt Design 一种选择模型生成的指令的方法，它会在一个指令池里通过一个打分函数选出效果最好的指令。

此外还有自动构造 CoT 指令的方法：augment-prune-select：

Augment：使用 zero-shot 或 few-shot 生成给定问题的多个假思维链

Prune：根据生成的答案是否与事实匹配来修剪假思维链

Select：应用一些数学方法 *(作者有提到，但我自己不太理解)* 来选择要采用的思维链

Augmented Language Models

通过合理的技巧以及使用外部工具可以增强语言模型的效果。

检索

我们使用大模型时，通常需要和最新或者私有的知识、信息相结合，这时候检索服务就能把相关的信息补充到 Prompt 中。

编程语言式推理

PAL (Program-aided language models) 和 PoT (Program of Thoughts prompting) 都试图让 LLM 生成编程语言结果来解决自然语言推理的问题，然后放到比如 Python interpreter 里来运行解决问题。这要求模型本身有比较强的编程相关能力。

外部 API

TALM (Tool Augmented Language Models) 是一种用文本到文本 API 调用来增强的语言模型。(个人感觉这有点像 OpenAI 的 Function Calling)

T：DemoGPT - 输入 Prompt 得到 Web 应用

DemoGPT 是一个 Prompt 到应用的新形式探索，之前我们可以看到的 Prompt 到应用的产品，基本都要遵照产品设计的规则来构建基于 Prompt 的应用，比如 OpenGPT 和 Langflow，DemoGPT 可以看作是一个新思路，从 Prompt 直接到应用，应用实现框架是比 Gradio 更加强大的 Streamlit 加上 Langchain。(我对 Streamlit 越来越看好了)

S：Get Hands Dirty

虽然已经做了一些面向 LLM 的应用，但基本都是 Prompt 的简单定制，然后工具化或 API 化。怎么做出有竞争力、有存活周期的 AI 应用，目前还更多是通过看文章来理解其中的问题的，越来越觉得要 Get hands dirty 才能把这件事做好，也许可以做一个信息流和知识管理系统的 Web 应用来达成这个目的。

Reference:

ARTS 打卡活动