最近在看电影《碟中谍7》,该片讲述了特工伊森·亨特尝试与一个被称为智体的全能人工智能作战,其可以即时访问任何在线网络,他和他的团队成员试图找回控制人工智能智体所必需的两部分钥匙并将其摧毁的故事。
在剧中,智体是一个虚拟反派。人们看不到智体,但电子设备遍布每个角落。影片中,欧美各国的军事、经济系统防火墙被智体轻松攻破,它来去自由。智体以超乎想象的运算能力,站在了网络世界制高点。它可以任意干扰伊森·亨特(汤姆·克鲁斯 饰)行动小组的通讯,肆意侵入小组成员的电脑,即使这些成员是一流的电脑天才也无计可施。人类在网络世界中面对智体,不堪一击。
过去,这些只存在电影里的科幻情节,随着大模型的发展,正在进入现实世界。
大模型是指具有大规模参数和复杂计算结构的机器学习模型。这些模型通常由深度神经网络构建而成,拥有数十亿甚至数千亿个参数。大模型的设计目的是为了提高模型的表达能力和预测性能,能够处理更加复杂的任务和数据。大模型在各种领域都有广泛的应用,包括自然语言处理、计算机视觉、语音识别和推荐系统等。大模型通过训练海量数据来学习复杂的模式和特征,具有更强大的泛化能力,可以对未见过的数据做出准确的预测。
ChatGPT 对大模型的解释更为通俗易懂,也更体现出类似人类的归纳和思考能力:大模型本质上是一个使用海量数据训练而成的深度神经网络模型,其巨大的数据和参数规模,实现了智能的涌现,展现出类似人类的智能。
那么,大模型和小模型有什么区别?
小模型通常指参数较少、层数较浅的模型,它们具有轻量级、高效率、易于部署等优点,适用于数据量较小、计算资源有限的场景,例如移动端应用、嵌入式设备、物联网等。
而当模型的训练数据和参数不断扩大,直到达到一定的临界规模后,其表现出了一些未能预测的、更复杂的能力和特性,模型能够从原始训练数据中自动学习并发现新的、更高层次的特征和模式,这种能力被称为“涌现能力”。而具备涌现能力的机器学习模型就被认为是独立意义上的大模型,这也是其和小模型最大意义上的区别。
相比小模型,大模型通常参数较多、层数较深,具有更强的表达能力和更高的准确度,但也需要更多的计算资源和时间来训练和推理,适用于数据量较大、计算资源充足的场景,例如云端计算、高性能计算、人工智能等。
对于程序员来说,现在不了解大模型,很有可能要被淘汰了。
最近在玩百度的”Baidu Comate智能编码助手“,就发现,未来已来。
代码生成、注释、对话提问、代码解释,看上去很强,有他,大部分的重复性编码工作都可以快速完成了,生产力直线上升。我直接去官网注册了一个账号,试试水。有需要的小伙伴自取:https://comate.baidu.com/
下载完插件,重启IDE后,点击右边侧边栏登录。
登录成功。
官方介绍说,comate很会coding。我于是找了几道leetcode。
先来一道二分查找。
对于comate来说,这么简单的题目自然不在话下。
小小代码,拿下。
不过这种题目,百度也可以解决。我又找了一道难度高一点的。这是一道某书真题。
小红在刷小x书的时候看到了一颗挂着小红薯的小红树,所以小红也想种一颗小红树挂一些小红薯发小x书。
小红有一颗树,每个结点有一个权值,初始时每个节点都是白色。小红每次操作可以选择两个相邻的结点,如果它们都是白色且权值的和是质数,小红就可以选择其中一个节点染红。
小红想知道最多可以染红多少个节
AI给出了他的解题思路。
接下来,让他帮我们实现下代码。下面是AI给出的解答。
完整代码
import java.util.*;
public class Main {
static class Node {
int val;
List<Node> neighbors = new ArrayList<>();
boolean isRed = false;
Node(int val) {
this.val = val;
}
}
static boolean isPrime(int num) {
if (num <= 1) return false;
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) return false;
}
return true;
}
static int maxRedNodes(Node root) {
if (root == null) return 0;
return dfs(root, null);
}
static int dfs(Node node, Node parent) {
if (node == null) return 0;
int maxRed = 0;
for (Node neighbor : node.neighbors) {
if (neighbor == parent) continue;
maxRed += dfs(neighbor, node);
if (!node.isRed && !neighbor.isRed && isPrime(node.val + neighbor.val)) {
int redBefore = maxRed;
node.isRed = true; // Try to paint the node red
maxRed = Math.max(maxRed, 1 + redBefore - dfs(neighbor, node) + dfs(neighbor,
node));
node.isRed = false; // Backtrack
neighbor.isRed = true; // Try to paint the neighbor red
maxRed = Math.max(maxRed, 1 + redBefore - dfs(neighbor, node) + dfs(neighbor,
node));
neighbor.isRed = false; // Backtrack
}
}
return maxRed;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // Number of nodes
Map<Integer, Node> nodes = new HashMap<>();
for (int i = 0; i < n; i++) {
int val = scanner.nextInt();
nodes.put(i + 1, new Node(val)); // Assuming nodes are numbered from 1 to n
}
for (int i = 0; i < n - 1; i++) {
int u = scanner.nextInt();
int v = scanner.nextInt();
nodes.get(u).neighbors.add(nodes.get(v));
nodes.get(v).neighbors.add(nodes.get(u));
}
System.out.println(maxRedNodes(nodes.get(1))); // Assuming the root is node 1
}
}
自测通过
这既是一个好消息,又是一个坏消息。对码农来说,算法很难,对大模型来说,so easy,那么,程序员的价值何在?
没关系,你还可以搬砖。起码,目前是这样。
测试全部用例,发现上述代码的时间复杂度过高。
这时候,你可以修改你的提示词,让他更具体。请comate给我们一个性能更优的解法。
全新解法如下:
import java.util.*;
public class RedTree {
static class TreeNode {
int val;
List<TreeNode> children = new ArrayList<>();
boolean isRed = false;
}
static int maxRedNodes = 0;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 节点数量
TreeNode[] nodes = new TreeNode[n + 1];
for (int i = 1; i <= n; i++) {
nodes[i] = new TreeNode();
nodes[i].val = scanner.nextInt(); // 节点权值
}
for (int i = 1; i < n; i++) {
int u = scanner.nextInt();
int v = scanner.nextInt();
nodes[u].children.add(nodes[v]);
nodes[v].children.add(nodes[u]); // 无向边,所以双向添加
}
scanner.close();
dfs(nodes[1], null); // 从节点1开始遍历,假设节点1是根节点
System.out.println(maxRedNodes);
}
public static int dfs(TreeNode node, TreeNode parent) {
int redCount = 0;
for (TreeNode child : node.children) {
if (child == parent) continue; // 避免回到父节点
redCount += dfs(child, node); // 递归处理子树
}
if (parent != null && !node.isRed && !parent.isRed && isPrime(node.val + parent.val)) {
// 如果当前节点和父节点都是白色且它们的权值和是质数,则可以选择一个节点染红
node.isRed = true; // 选择当前节点染红
redCount++; // 增加染红节点计数
}
maxRedNodes = Math.max(maxRedNodes, redCount); // 更新最大染红节点数
return redCount;
}
public static boolean isPrime(int num) {
if (num <= 1) return false;
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) return false;
}
return true;
}
}
Comate真牛逼!!!
大模型还给出了详细的解题思路。
这个问题可以通过深度优先搜索(DFS)或广度优先搜索(BFS)遍历所有可能的染色方案来解决,但考虑到题目中的时间限制和可能的效率问题,我们需要一个更优化的算法。我们可以观察到,如果我们将树转换为以任意节点为根,那么对于任意一对相邻节点,我们总是可以选择其中一个来染色(如果它们的权值和是质数),这给我们提供了一个优化的思路。
我们可以自底向上地处理树,对于每个节点,先处理其所有子树,然后检查当前节点是否可以与其父节点一起被染色。在这个过程中,我们需要记录每个节点为根的子树中最多可以染红多少个节点,以及这个节点是否已经染红。
由于题目没有明确指出树的形状如何输入,我将假设输入的是一个树的边和节点的权值,并通过邻接表来表示树。
甚至贴心的给出了注意事项。
这简直就是刷算法题的神奇啊!!!
更牛的是,你不懂,还可以要求他给你慢慢解释。
除了算法题以外,小小笔试题他也不在话下。
就问你能不能吊打面试官?
除了作为刷题神器,comate最主要的还是帮我们提升工作效率。它可以帮你轻松研读别人的“屎山代码”。甚至帮你写注释、优化代码!
除此之外,他还能帮你写测试用例。
再不会用它,你真的会失业的好吗?
comate下载链接,快拿去:https://comate.baidu.com/
接下来再给小伙伴们总结下,怎么去写提示词。
(1)结构化
首先需要强调的是,并不是所有提示词都要写的这么“复杂”,对于简单的任务可以写的更“随意”一些。通常来说,越是复杂的任务提示词越详细,越是简单的任务提示词越简单。
提示词的本质就是通过语言将模型要做的事情交代清楚,是一种“语言的艺术”,换句话说:提示词写不好说明你语文不好。
想要模型更好地理解你的意图,就需要将提示词“结构化”。这里所说的结构化并不是经典的“金字塔原理”,而是遵循优秀的 Agent 范式。
根据官方材料:总结出来的一个范式。包括概述、过程、依赖和控制四大部分。
概述:在什么背景下,要做一件什么事;可以指明用户或 AI 的角色是什么。
过程:承担什么样的智能,满足什么样的规则,按照什么样的流程。
依赖:使用什么样的工具,用到哪些知识,处理哪些素材?
控制:对模型的处理过程有哪些要求,包括正向和负向的要求。
(2)加分割符
在提示词中合理添加分隔符,可以准确区分指令和待处理的内容,避免模型解读提示词时出现困扰。
常见的分隔符包括:```、— 、=== 等
(3)加示例
有时候,为了让模型更好地理解你的意图避免歧义,可以更精确地控制模型的输出,需要在提示词中给出一些示例。
(4)加要求
我们在使用模型过程中会出现各种 Bad Case,为了引导模型按照我们想要的结果输出,为了规避这些 Bad Case ,可以在提示词中加入要求。
(5)给出步骤
有些相对复杂一点的任务,需要在提示词中给出处理步骤,用来指导模型按照你的意图来输出信息。
(6)用户输入完整性检查
可以在提示词中设定用户必须给出的一些关键信息,如果用户在对话中没有给出,模型可以主动询问用户让用户进行补充,补充完整之后再继续执行。
(7)自带知识库
对于非通用的知识或和常识相违背的知识,大模型可能不理解相关概念,容易产生误解。可以在提示词中给出知识库,让模型更加清楚得理解相关知识,从而,更好地处理任务。
拓展阅读:
AI大神吴恩达教你写提示词:https://zhuanlan.zhihu.com/p/626290417
提示词工程指南:https://www.promptingguide.ai/zh
大模型思维链技术原理:https://www.zhihu.com/tardis/zm/art/629087587?source_id=1003