2025-02-10 10:39:11
DeepSeek 里程碑式的爆火,有必要学习下是怎么回事。
大语言模型的发展,之前一直是以预训练为主,虽然性能一直在提升,但主要是小修小补,跨越式的 GPT5 一直出不来。OpenAI 在 24 年 9 月发布的 o1 提出了新的路线:在预训练好的模型上,用强化学习做后训练,能显著提高模型推理能力,其效果在解数学、编码等问题上屠榜。
但 o1 只说了强化学习能让模型学会思维链的方式提升推理能力,其他的发现都没有公布,加上 o1 一直是期货,12月才正式推出,200美元一个月,普通人都用不上,充满神秘。
而 DeepSeek 自主研发了通过强化学习让模型学会思维链提升推理能力,性能逼近 o1,加上之前 DeepSeekV3 在预训练基础模型上的创新,推理成本也显著低于 o1,直接推出全民免费可用媲美 o1 的模型,甚至在一些中文语境下效果显著超过 o1,大众用户一用感受到 NB,业内人士震惊它的创新能力纷纷学习,美国人民恐慌中国 AI 的发展有超越美国引领技术潮流的可能性,结合大国叙事,爱国情怀,各种元素综合下各觉得都会乐此不疲地研究、使用、讨论,引爆全网。
接下来一步步精读下 DeepSeek-R1 的论文 《DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning》了解现象级 R1 模型是怎么做出来的。
DeepSeek R1 是基于预训练好的 DeepSeekV3 基础模型进行的后训练优化,V3 做了很多创新优化,很大降低了预训练模型和模型推理的成本,是 R1 的基础,这里先不讨论 V3,只看 R1 在 V3 基础上做了什么后训练。
在 DeepSeekR1 这篇论文里核心做的三个事:
我们一个个具体看下这三个事。
R1-Zero 证明了对已预训练好的模型,不需要经过 SFT,只需要纯粹的 RL,就能让模型涌现 CoT 推理能力。
SFT是监督式微调,也就是准备好一堆标注好的数据,让模型去学习拟合,可以粗略理解为刷题,大量的刷题学习能解决类似的题目;
RL是强化学习,只告诉模型什么是好的什么是不好的,过程中模型自己学习怎么达到目标,可以理解为不靠刷题,自己去理解探索数学和世界的规律,理论上灵活性强,上限更高,还有可能探索出人类未知的能力。
强化学习首次出圈是 AlphaGo,AlphaGo 先学习人类棋谱,再用强化学习自我对弈进化,而随后的 AlphaGo Zero 没有人类棋谱,只定义好围棋奖励规则,模型自己学习怎么下,达到更高的水平。R1-Zero 这命名也是致敬 Alpha-Zero,因为它们非常类似,脱离人类的指导自主发现规律提升智能。
为什么之前没人做到?
R1-Zero 最大的不同,是在强化学习中使用 GRPO 算法代替 PPO。GRPO 算法也是 DeepSeek 团队在 24 年 2 月《DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models》这篇论文提出的,其核心思想可以理解为两个字:内卷。
简单来说,PPO 是用一个模型去评估当前输出的收益,模型觉得这个输出好,就更新参数往这方向靠近,类似四六级考试的判定,有个分数线,过了就好,不过就不好;GRPO 是让模型一次性输出一组数据,在这组数据中选得分最高的,让模型逐步靠近得分高的输出,类似高考的内卷选拔,你只要比同龄人好,就是好。
更具体的可以看这张图:
图上的一些概念:
- Reference Model:预训练的大语言模型
- Reward Model:奖励模型,给定输入和输出,给出得分。可以是基于神经网络训练的模型,使用人类标注数据做训练;也可以是规则模型,定死一些规则做打分。这里的输入是大语言模型一次完整的输出。
- Value Model:对模型输出的每一步做一个预测,预测当前状态下后续能得到的收益。这里的输入是大语言模型每一次 token 的输出。
- Policy Model:我们正在训练的大语言模型。base 是 Reference Model,训练过程中会不断更新参数,得到我们最终需要的模型。
- GAE:Generalized Advantage Estimation 广义优势估计,评估每一个动作的好坏程度
- q:输入 question
- o:输出 output
- r:模型完整的输出经过 Reward Model 计算后的分数
- v:模型每一步的输出后的状态,经过 Value Model 计算后的价值估计值,用于评估当前 token 输出的好坏。
- KL:Kullback-Leibler divergence KL散度,衡量新策略(当前训练中的模型输出的结果)和旧策略(base模型输出的结果)之间的差异,保障训练中策略更新幅度不要过大,保证稳定性。
PPO 用 Reward Model 计算大模型完整输出的奖励分数(r),过程中会跟原始模型的输出做对比(KL),避免偏离太远。大模型每步 token 输出,都会经过 Value Model 判定输出对后续奖励的期望值(v),经过 GAE 函数计算每步动作的好坏程度,更新我们在训练的大模型 Policy Model 参数。
GRPO 去掉了 Value Model,对每个输入(q)输出多个结果o1 o2 o3 …,奖励模型判断这些结果的好坏,从中选出最好的,更新模型参数。对算法和公式更详细的介绍,推荐这两个讲解:(1),(2)
GRPO 去掉 Value Model,带来了一些好处:
从上图可以看到,主要需要设计的只剩 Reward Model,R1-Zero 设计了一个基于规则的 Reward Model,之所以用简单的规则而不是基于神经网络的模型做奖励判定,一个是不需要训练,简化了流程和降低成本,一个是如果用神经网络模型作为奖励判定,如果这个判定模型不够强,可能会让训练的大语言模型钻空子作弊,效果不一定好。
主要是两个简单的规则:
训练时会输入预置 prompt,让模型按格式要求输出。这个模板有意设置得很简单,避免带偏模型,也不会告诉模型要进行反思性推理或者其他推理策略,确保可以观察到模型在强化学习过程中自发去发现怎样的推理方式才是更有效的。
训练过程中两个重要的现象:
1. 输出越来越长:随着训练量的推进,输出的长度稳步加长,推理能力也随之提升。这也验证了 OpenAI o1 里提到的 test-time scaling,也就是更长的推理输出能更好提升模型推理能力,模型在强化学习过程中自主发现了这个规律。
2. Aha moment:训练过程中模型学会了停下来重新评估前面的思考,而且使用拟人的口气进行了反思。强化学习过程中人类没有输入任何类似的反思引导,模型能自主进行这种反思,十分有趣。我理解为,在预训练的模型里,模型已经有这样的反思意识潜力,在训练的某次输出过程中偶然出现,而出现后的结果对复杂推理有帮助,强化学习的机制会持续鼓励这样的输出。
训练的结果,在推理相关的 benchmark上,基本达到 o1 的水平:
R1-Zero 有非常有趣的发现,即不做监督学习,仅对预训练模型进行强化学习这条路是 OK 的,最终的模型有媲美o1的推理能力,但当前这种方式训练出的 R1-Zero 实际对外使用会有些问题,主要是对人不友好,比如输出的内容中英文混杂,猜测是奖励模型并没有做这种人类阅读友好的奖励判定,模型没理由能做好这块。
为了做出一个推理能力强,输出对人类友好,综合能力 OK 的模型,DeepSeek 另外训练了R1模型。
整个流程可以可以看这图,图片来自 这个视频,顺便推荐这个视频的讲解。
这里做了两个阶段的后训练,每个阶段都是先 SFT,再进行 RL,只是数据和目标不同。
一阶段
二阶段
这几个步骤后,R1 就训练完成了,可以看到这个基于 V3 模型的后训练过程成本是很低的,数据量不大,但效果非常显著,特别是在编码和数学问题上,R1 相对 V3 提升了几个档次。
而这个过程,看起来也是可以 scale 的,可以用这个训好的模型继续多步生成一些case,择优组成新的数据,继续进行 SFT 和强化学习。
这条显著提升模型推理能力的后训练路跑通了,公开解了 o1 一直遮遮掩掩的强化学习路线,也展现了很好的低成本持续 scale up 的潜力。沿着这条路走能 scale 到什么程度还不太清楚,拭目以待。
R1 训完了,最终我们用的就是上述训练出来的模型,但这篇论文还没完,DeepSeek 的同学发现用上述 R1 训练过程中生成的 60w 推理相关的数据,以及 20w 的额外数据去对小模型做 SFT,小模型性能的提升非常明显。
看起来这一步纯粹是用质量更好的数据对小模型做SFT,只是这些数据大部分是 R1 生成的,相当于是蒸馏提取了 R1 的能力精华,拟合了 R1 的能力,这样也能给小模型带来较好的推理能力提升。
从分数看,这些小模型在数学和 coding 上的性能是不错的,如果1.5b在部分场景上能有媲美4o的效果,那端上大模型会迎来一波应用潮。但实际用起来没那么理想,试过 1.5B 模型基本不遵循指令,有些刷分的感觉,但这仅是做了初步的SFT 后的效果,在这基础上对小模型继续进行强化学习,可能整体能力能有提升,也是值得期待的点。
这里论文上还额外做了另一个实验,用类似 R1-Zero 的方法直接对小模型做强化学习,实验效果是相对用蒸馏数据做 SFT 的方式,效果差很多。一个猜测是小模型就是能力有限,直接用强化学习达到顿悟和性能提升,得基于模型本身能力足够才行。
到这里 R1 论文内容结束了,结尾部分提到后续会在多轮对话、json输出、语言混合、提示词问题、写工程代码弱这些问题上提升的展望,解决这些只是时间问题。
这篇论文介绍了 R1 整个算法、训练流程和结果,但最核心的应该是数据,包括用于 R1-Zero 的数据是什么,数据量有多大,生成的 60w 数据具体是什么样的,标注的 20w 文科数据是什么,这是决定模型效果的另一个核心因素,DeepSeek 的中文效果出圈,应该很大程度还是标注的 20w 文科数据质量高,不确定 RL 带来的推理能力提升在文科这块上的帮助有多大。这些数据没有公开,友商要复刻出 DeepSeek 的效果没那么容易。
网上有不少开始复现 R1 和 R1-Zero 的开源项目研究,最大的是 huggingface 的 open-r1,也有学生在 3B 模型上小规模复现 R1-Zero 的开源项目 TinyZero。
2025-01-19 17:58:23
这两天有机会体验了下 Devin,感受到一些小小的震撼。
虽然之前已经用过 cursor 和 windsurf,它们用的模型都一样,理论上能完成的任务和智力是差不多的,但用 Devin 感受还是不太一样,有种 AGI 已经实现了的感觉。
Cursor 和 Devin 核心区别是交互范式,Cursor 是 Copilot,在你工作写代码过程中,实时辅助完成编程任务,而 Devin 是一个员工,交给他复杂任务后不用管它,它主动帮你搞定。可能现在这两者完成的很多任务是一致的,但体验有差异。
我试用的其中一个任务,是扔给它开源项目 JSPatch 的 github 地址,告诉它找个 issue 修一下。它会分解任务逐步执行,包括:
这个过程是自动和异步的,它跑在虚拟机里,不需要你提供环境,不需要盯着它,它会自己去调研怎么完成这个任务,做完了会来告诉你(如果用 slack,这个体验过程更顺畅,@它下达任务,任务完成slack回复),这跟给一个员工布置任务,等他做完验收结果的体验很一致。
现在 Devin 解决问题的能力肯定还有限,真正用下来磕磕碰碰很多任务还是完成不好,现在的模型能力下 Cursor 这种 Copilot 的形态是更实用的,但未来理想的形态肯定是 Devin 这种“员工”形态,因为可以解放注意力,无限扩展同一时间能做的事。
可以想象,这种形态未来的优化速度会很乐观:
以及,模型成本必然比摩尔定律更快速的下降,Devin 会持续用最好的模型最贵的方案,但今天 500美金的效果,一年后成本可能只要5美金就能做到。
Devin 所实现的概念早在 23年初 AutoGPT 就提出,只是当时模型能力还不具备,Claude Sonnet / GPT 4o / GPT o1 这种级别推理能力的模型出现后,才具备可用性,Devin 是实现了这个概念下初步可用的雏形,让人看到这个方向已经初步 ready,剩下的就是持续往这个方向优化和扩展了,Devin 确实称得上是数字员工的开端,设计师agent,交易员agent,数据分析师agent,电商agent,预计会陆续出现。
Devin 是怎么实现的?
有个开源项目 OpenHands(前身 OpenDevin),尝试用开源社区的方式去构建类似 Devin 做的事,虽然能力和效果上不能完全对标 Devin,但可以看个基本雏形。相关论文:https://arxiv.org/abs/2407.16741
文中的这张架构图,可以比较好描述 OpenHands 是怎么做:
三个主要部分:
还有个关键点没有在图上画出来,为什么把 Event Stream 的所有内容输入到 LLM,LLM 就会按照要求推理出下一步 Action?因为输入到 LLM 的除了 Event Stream 的上下文,还有 Agent 本身的 Prompt,这个 Prompt 描述一些处理原则、当前环境、可用的工具、每个工具的参数、预期输出的格式等,以及还配套了一个示例,指引模型按要求输出。这个 Prompt 本身贴在了文末。
我们跟着图上 Event Stream 的示例,跑一下这个流程:
接下来就是不断的循环:Action 驱动 Observation 用工具做处理 → 处理结果输出到 Event Stream → Event Stream 拿所有前文内容到 LLM 输出下一步 Action → 驱动Observation 用工具做处理…
后面的6-9步用了命令行工具和浏览器工具,流程是一样的。这个循环流程什么时候结束?有一个特殊的 Action 叫 finish,如果一个任务 LLM 认为完成了,就会输出调用 finish Action,程序接收到就退出循环,等用户下一步输入。
整体就是自动循环让 LLM 预测下一步动作 → Agent 程序调用工具执行动作 的过程。补充一些点:
You are OpenHands agent, a helpful AI assistant that can interact with a computer to solve tasks.
<IMPORTANT>
//
* If user provides a path, you should NOT assume it's relative to the current working directory. Instead, you should explore the file system to find the file before working on it.
* When configuring git credentials, use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
* The assistant MUST NOT include comments in the code unless they are necessary to describe non-obvious behavior.
RuntimeInfo(available_hosts={'http://localhost:54090': 54090, 'http://localhost:55602': 55602})
</IMPORTANT>
<RUNTIME_INFORMATION>
The user has access to the following hosts for accessing a web application,
each of which has a corresponding port:
* http://localhost:54090 (port 54090)
* http://localhost:55602 (port 55602)
When starting a web server, use the corresponding ports. You should also
set any options to allow iframes and CORS requests.
</RUNTIME_INFORMATION>
You have access to the following functions:
---- BEGIN FUNCTION #1: execute_bash ----
Description: Execute a bash command in the terminal.
* Long running commands: For commands that may run indefinitely, it should be run in the background and the output should be redirected to a file, e.g. command = `python3 app.py > server.log 2>&1 &`.
* Interactive: If a bash command returns exit code `-1`, this means the process is not yet finished. The assistant must then send a second call to terminal with an empty `command` (which will retrieve any additional logs), or it can send additional text (set `command` to the text) to STDIN of the running process, or it can send command like `C-c` (Ctrl+C) to interrupt the process.
Parameters:
(1) command (string, required): The bash command to execute. Can be empty string to view additional logs when previous exit code is `-1`. Can be `C-c` (Ctrl+C) to interrupt the currently running process.
---- END FUNCTION #1 ----
---- BEGIN FUNCTION #2: finish ----
Description: Finish the interaction when the task is complete OR if the assistant cannot proceed further with the task.
No parameters are required for this function.
---- END FUNCTION #2 ----
---- BEGIN FUNCTION #3: web_read ----
Description: Read (convert to markdown) content from a webpage. You should prefer using the `web_read` tool over the `browser` tool, but do use the `browser` tool if you need to interact with a webpage (e.g., click a button, fill out a form, etc.).
You may use the `web_read` tool to read content from a webpage, and even search the webpage content using a Google search query (e.g., url=`https://www.google.com/search?q=YOUR_QUERY`).
Parameters:
(1) url (string, required): The URL of the webpage to read. You can also use a Google search query here (e.g., `https://www.google.com/search?q=YOUR_QUERY`).
---- END FUNCTION #3 ----
---- BEGIN FUNCTION #4: browser ----
Description: Interact with the browser using Python code. Use it ONLY when you need to interact with a webpage.
See the description of "code" parameter for more details.
Multiple actions can be provided at once, but will be executed sequentially without any feedback from the page.
More than 2-3 actions usually leads to failure or unexpected behavior. Example:
fill('a12', 'example with "quotes"')
click('a51')
click('48', button='middle', modifiers=['Shift'])
Parameters:
(1) code (string, required): The Python code that interacts with the browser.
The following 15 functions are available. Nothing else is supported.
goto(url: str)
Description: Navigate to a url.
Examples:
goto('http://www.example.com')
go_back()
Description: Navigate to the previous page in history.
Examples:
go_back()
go_forward()
Description: Navigate to the next page in history.
Examples:
go_forward()
noop(wait_ms: float = 1000)
Description: Do nothing, and optionally wait for the given time (in milliseconds).
You can use this to get the current page content and/or wait for the page to load.
Examples:
noop()
noop(500)
scroll(delta_x: float, delta_y: float)
Description: Scroll horizontally and vertically. Amounts in pixels, positive for right or down scrolling, negative for left or up scrolling. Dispatches a wheel event.
Examples:
scroll(0, 200)
scroll(-50.2, -100.5)
fill(bid: str, value: str)
Description: Fill out a form field. It focuses the element and triggers an input event with the entered text. It works for <input>, <textarea> and [contenteditable] elements.
Examples:
fill('237', 'example value')
fill('45', 'multi-line
example')
fill('a12', 'example with "quotes"')
select_option(bid: str, options: str | list[str])
Description: Select one or multiple options in a <select> element. You can specify option value or label to select. Multiple options can be selected.
Examples:
select_option('a48', 'blue')
select_option('c48', ['red', 'green', 'blue'])
click(bid: str, button: Literal['left', 'middle', 'right'] = 'left', modifiers: list[typing.Literal['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']] = [])
Description: Click an element.
Examples:
click('a51')
click('b22', button='right')
click('48', button='middle', modifiers=['Shift'])
dblclick(bid: str, button: Literal['left', 'middle', 'right'] = 'left', modifiers: list[typing.Literal['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']] = [])
Description: Double click an element.
Examples:
dblclick('12')
dblclick('ca42', button='right')
dblclick('178', button='middle', modifiers=['Shift'])
hover(bid: str)
Description: Hover over an element.
Examples:
hover('b8')
press(bid: str, key_comb: str)
Description: Focus the matching element and press a combination of keys. It accepts the logical key names that are emitted in the keyboardEvent.key property of the keyboard events: Backquote, Minus, Equal, Backslash, Backspace, Tab, Delete, Escape, ArrowDown, End, Enter, Home, Insert, PageDown, PageUp, ArrowRight, ArrowUp, F1 - F12, Digit0 - Digit9, KeyA - KeyZ, etc. You can alternatively specify a single character you'd like to produce such as "a" or "#". Following modification shortcuts are also supported: Shift, Control, Alt, Meta, ShiftLeft, ControlOrMeta. ControlOrMeta resolves to Control on Windows and Linux and to Meta on macOS.
Examples:
press('88', 'Backspace')
press('a26', 'ControlOrMeta+a')
press('a61', 'Meta+Shift+t')
focus(bid: str)
Description: Focus the matching element.
Examples:
focus('b455')
clear(bid: str)
Description: Clear the input field.
Examples:
clear('996')
drag_and_drop(from_bid: str, to_bid: str)
Description: Perform a drag & drop. Hover the element that will be dragged. Press left mouse button. Move mouse to the element that will receive the drop. Release left mouse button.
Examples:
drag_and_drop('56', '498')
upload_file(bid: str, file: str | list[str])
Description: Click an element and wait for a "filechooser" event, then select one or multiple input files for upload. Relative file paths are resolved relative to the current working directory. An empty list clears the selected files.
Examples:
upload_file('572', '/home/user/my_receipt.pdf')
upload_file('63', ['/home/bob/Documents/image.jpg', '/home/bob/Documents/file.zip'])
---- END FUNCTION #4 ----
---- BEGIN FUNCTION #5: execute_ipython_cell ----
Description: Run a cell of Python code in an IPython environment.
* The assistant should define variables and import packages before using them.
* The variable defined in the IPython environment will not be available outside the IPython environment (e.g., in terminal).
Parameters:
(1) code (string, required): The Python code to execute. Supports magic commands like %pip.
---- END FUNCTION #5 ----
---- BEGIN FUNCTION #6: str_replace_editor ----
Description: Custom editing tool for viewing, creating and editing files
* State is persistent across command calls and discussions with the user
* If `path` is a file, `view` displays the result of applying `cat -n`. If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep
* The `create` command cannot be used if the specified `path` already exists as a file
* If a `command` generates a long output, it will be truncated and marked with `<response clipped>`
* The `undo_edit` command will revert the last edit made to the file at `path`
Notes for using the `str_replace` command:
* The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!
* If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique
* The `new_str` parameter should contain the edited lines that should replace the `old_str`
Parameters:
(1) command (string, required): The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`.
Allowed values: [`view`, `create`, `str_replace`, `insert`, `undo_edit`]
(2) path (string, required): Absolute path to file or directory, e.g. `/workspace/file.py` or `/workspace`.
(3) file_text (string, optional): Required parameter of `create` command, with the content of the file to be created.
(4) old_str (string, optional): Required parameter of `str_replace` command containing the string in `path` to replace.
(5) new_str (string, optional): Optional parameter of `str_replace` command containing the new string (if not given, no string will be added). Required parameter of `insert` command containing the string to insert.
(6) insert_line (integer, optional): Required parameter of `insert` command. The `new_str` will be inserted AFTER the line `insert_line` of `path`.
(7) view_range (array, optional): Optional parameter of `view` command when `path` points to a file. If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[start_line, -1]` shows all lines from `start_line` to the end of the file.
---- END FUNCTION #6 ----
If you choose to call a function ONLY reply in the following format with NO suffix:
<function=example_function_name>
<parameter=example_parameter_1>value_1</parameter>
<parameter=example_parameter_2>
This is the value for the second parameter
that can span
multiple lines
</parameter>
</function>
<IMPORTANT>
Reminder:
- Function calls MUST follow the specified format, start with <function= and end with </function>
- Required parameters MUST be specified
- Only call one function at a time
- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after.
- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls
----------
Here's a running example of how to perform a task with the provided tools.
--------------------- START OF EXAMPLE ---------------------
USER: Create a list of numbers from 1 to 10, and display them in a web page at port 5000.
ASSISTANT: Sure! Let me first check the current directory:
<function=execute_bash>
<parameter=command>
pwd && ls
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
/workspace
openhands@runtime:~/workspace$
ASSISTANT: There is no `app.py` file in the current directory. Let me create a Python file `app.py`:
<function=str_replace_editor>
<parameter=command>create</parameter>
<parameter=path>/workspace/app.py</parameter>
<parameter=file_text>
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
numbers = list(range(1, 11))
return str(numbers)
if __name__ == '__main__':
app.run(port=5000)
</parameter>
</function>
USER: EXECUTION RESULT of [str_replace_editor]:
File created successfully at: /workspace/app.py
ASSISTANT: I have created a Python file `app.py` that will display a list of numbers from 1 to 10 when you run it. Let me run the Python file for you:
<function=execute_bash>
<parameter=command>
python3 app.py > server.log 2>&1 &
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
[1] 121
[1]+ Exit 1 python3 app.py > server.log 2>&1
ASSISTANT: Looks like the server is running with PID 121 then crashed. Let me check the server log:
<function=execute_bash>
<parameter=command>
cat server.log
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
File "/workspace/app.py", line 2, in <module>
from flask import Flask
ModuleNotFoundError: No module named 'flask'
ASSISTANT: Looks like the server crashed because the `flask` module is not installed. Let me install the `flask` module for you:
<function=execute_bash>
<parameter=command>
pip3 install flask
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
Defaulting to user installation because normal site-packages is not writeable
Collecting flask
Using cached flask-3.0.3-py3-none-any.whl (101 kB)
Collecting blinker>=1.6.2
Using cached blinker-1.7.0-py3-none-any.whl (13 kB)
Collecting Werkzeug>=3.0.0
Using cached werkzeug-3.0.2-py3-none-any.whl (226 kB)
Collecting click>=8.1.3
Using cached click-8.1.7-py3-none-any.whl (97 kB)
Collecting itsdangerous>=2.1.2
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Requirement already satisfied: Jinja2>=3.1.2 in /home/openhands/.local/lib/python3.10/site-packages (from flask) (3.1.3)
Requirement already satisfied: MarkupSafe>=2.0 in /home/openhands/.local/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask) (2.1.5)
Installing collected packages: Werkzeug, itsdangerous, click, blinker, flask
Successfully installed Werkzeug-3.0.2 blinker-1.7.0 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0
ASSISTANT: The `flask` module is installed successfully. Let me run the Python file again:
<function=execute_bash>
<parameter=command>
python3 app.py > server.log 2>&1 &
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
[1] 124
ASSISTANT:
Let me check the server log again:
<function=execute_bash>
<parameter=command>
cat server.log
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
ASSISTANT:
The server is running on port 5000 with PID 124. You can access the list of numbers by visiting http://127.0.0.1:5000. If you have any further questions, feel free to ask!
USER: Now kill the server, make it display the numbers in a table format.
ASSISTANT:
Sure! Let me stop the server first:
<function=execute_bash>
<parameter=command>
kill 124
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
[1]+ Terminated python3 app.py > server.log 2>&1
ASSISTANT:
Now let me display the numbers in a table format:
<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/app.py</parameter>
<parameter=old_str>return str(numbers)</parameter>
<parameter=new_str>return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'</parameter>
</function>
USER: EXECUTION RESULT of [str_replace_editor]:
The file /workspace/app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/app.py:
3
4 @app.route('/')
5 def index():
6 numbers = list(range(1, 11))
7 return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
8
9 if __name__ == '__main__':
10 app.run(port=5000)
Review the changes and make sure they are as expected. Edit the file again if necessary.
ASSISTANT:
Running the updated file:
<function=execute_bash>
<parameter=command>
<parameter=command>
python3 app.py > server.log 2>&1 &
</parameter>
</function>
USER: EXECUTION RESULT of [execute_bash]:
[1] 126
ASSISTANT:
The server is running on port 5000 with PID 126. You can access the list of numbers in a table format by visiting http://127.0.0.1:5000. Let me know if you have any further requests!
<function=finish>
</function>
--------------------- END OF EXAMPLE ---------------------
Do NOT assume the environment is the same as in the example above.
以下是真实执行一个任务时,循环添加输入 LLM 的 Prompt case:
--------------------- NEW TASK DESCRIPTION ---------------------
创建一个可以调用deepseek模型的程序,并运行测试确保可用
--------------------- END OF NEW TASK DESCRIPTION ---------------------
PLEASE follow the format strictly! PLEASE EMIT ONE AND ONLY ONE FUNCTION CALL PER MESSAGE.
----------
Let me start by checking the current directory and listing its contents:
<function=execute_bash>
<parameter=command>pwd && ls</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
/workspace
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]
----------
I see we are in the `/workspace` directory. Let's create a Python file that can call the DeepSeek model. First, I'll create a new file called `deepseek_app.py`:
<function=str_replace_editor>
<parameter=command>create</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=file_text>
from deepseek import DeepSeekModel
def call_deepseek_model(input_text):
model = DeepSeekModel()
response = model.generate(input_text)
return response
if __name__ == "__main__":
test_input = "Hello, DeepSeek!"
result = call_deepseek_model(test_input)
print("Model Response:", result)
</parameter>
</function>
----------
EXECUTION RESULT of [str_replace_editor]:
File created successfully at: /workspace/deepseek_app.py
----------
Now, let's check if the `deepseek` module is installed. If not, we'll need to install it:
<function=execute_bash>
<parameter=command>pip3 show deepseek</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
WARNING: Package(s) not found: deepseek
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]
----------
The `deepseek` module is not installed. Let me install it using pip:
<function=execute_bash>
<parameter=command>pip3 install deepseek</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
Collecting deepseek
Downloading deepseek-1.0.0-py3-none-any.whl.metadata (3.9 kB)
Requirement already satisfied: requests in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from deepseek) (2.32.3)
Requirement already satisfied: charset-normalizer<4,>=2 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (3.4.1)
Requirement already satisfied: idna<4,>=2.5 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages (from requests->deepseek) (2024.12.14)
Downloading deepseek-1.0.0-py3-none-any.whl (4.5 kB)
Installing collected packages: deepseek
Successfully installed deepseek-1.0.0
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]
----------
The `deepseek` module is now installed. Let's run the `deepseek_app.py` program to test if it works:
<function=execute_bash>
<parameter=command>python3 deepseek_app.py</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
File "/workspace/deepseek_app.py", line 1, in <module>
from deepseek import DeepSeekModel
ImportError: cannot import name 'DeepSeekModel' from 'deepseek' (/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/__init__.py)
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]
----------
It seems the `DeepSeekModel` class is not available in the `deepseek` module. Let me check the documentation or available classes in the `deepseek` module:
<function=execute_bash>
<parameter=command>python3 -c "import deepseek; print(dir(deepseek))"</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
['DeepSeekAPI', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'api', 'const']
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]
----------
The `deepseek` module provides a `DeepSeekAPI` class instead of `DeepSeekModel`. Let me update the `deepseek_app.py` file to use the correct class:
<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=old_str>
from deepseek import DeepSeekModel
def call_deepseek_model(input_text):
model = DeepSeekModel()
response = model.generate(input_text)
return response
</parameter>
<parameter=new_str>
from deepseek import DeepSeekAPI
def call_deepseek_model(input_text):
api = DeepSeekAPI()
response = api.generate(input_text)
return response
</parameter>
</function>
----------
EXECUTION RESULT of [str_replace_editor]:
The file /workspace/deepseek_app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/deepseek_app.py:
1 from deepseek import DeepSeekAPI
2
3 def call_deepseek_model(input_text):
4 api = DeepSeekAPI()
5 response = api.generate(input_text)
6 return response
7
8 if __name__ == "__main__":
9 test_input = "Hello, DeepSeek!"
10 result = call_deepseek_model(test_input)
Review the changes and make sure they are as expected. Edit the file again if necessary.
----------
Now that the file is updated, let's run the program again to test if it works:
<function=execute_bash>
<parameter=command>python3 deepseek_app.py</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
File "/workspace/deepseek_app.py", line 10, in <module>
result = call_deepseek_model(test_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/deepseek_app.py", line 4, in call_deepseek_model
api = DeepSeekAPI()
^^^^^^^^^^^^^
File "/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/api.py", line 9, in __init__
raise ValueError("DEEPSEEK_API_KEY is missing")
ValueError: DEEPSEEK_API_KEY is missing
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]
----------
The program requires an API key to use the `DeepSeekAPI`. Let me update the program to include the API key as an environment variable. First, I'll modify the `deepseek_app.py` file:
<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=old_str>
from deepseek import DeepSeekAPI
def call_deepseek_model(input_text):
api = DeepSeekAPI()
response = api.generate(input_text)
return response
</parameter>
<parameter=new_str>
import os
from deepseek import DeepSeekAPI
def call_deepseek_model(input_text):
api_key = os.getenv("DEEPSEEK_API_KEY")
if not api_key:
raise ValueError("Please set the DEEPSEEK_API_KEY environment variable.")
api = DeepSeekAPI(api_key=api_key)
response = api.generate(input_text)
return response
</parameter>
</function>
----------
EXECUTION RESULT of [str_replace_editor]:
The file /workspace/deepseek_app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/deepseek_app.py:
1 import os
2 from deepseek import DeepSeekAPI
3
4 def call_deepseek_model(input_text):
5 api_key = os.getenv("DEEPSEEK_API_KEY")
6 if not api_key:
7 raise ValueError("Please set the DEEPSEEK_API_KEY environment variable.")
8 api = DeepSeekAPI(api_key=api_key)
9 response = api.generate(input_text)
10 return response
11
12 if __name__ == "__main__":
13 test_input = "Hello, DeepSeek!"
14 result = call_deepseek_model(test_input)
Review the changes and make sure they are as expected. Edit the file again if necessary.
----------
Now, let's set the `DEEPSEEK_API_KEY` environment variable and run the program again. Please provide the API key for DeepSeek, and I will set it up for you. If you don't have one, you may need to sign up for an API key from DeepSeek's official website.
<function=execute_bash>
<parameter=command>export DEEPSEEK_API_KEY=your_api_key_here && python3 deepseek_app.py</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
File "/workspace/deepseek_app.py", line 14, in <module>
result = call_deepseek_model(test_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/deepseek_app.py", line 9, in call_deepseek_model
response = api.generate(input_text)
^^^^^^^^^^^^
AttributeError: 'DeepSeekAPI' object has no attribute 'generate'
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]
----------
It seems the `DeepSeekAPI` class does not have a `generate` method. Let me check the available methods in the `DeepSeekAPI` class:
<function=execute_bash>
<parameter=command>python3 -c "from deepseek import DeepSeekAPI; print(dir(DeepSeekAPI))"</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_post_request', 'chat_completion', 'completion_impl', 'fim_completion', 'get_models', 'user_balance']
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]
----------
The `DeepSeekAPI` class provides methods like `chat_completion`, `fim_completion`, and `get_models`. Let's update the `deepseek_app.py` file to use the `chat_completion` method instead of `generate`:
<function=str_replace_editor>
<parameter=command>str_replace</parameter>
<parameter=path>/workspace/deepseek_app.py</parameter>
<parameter=old_str>
api = DeepSeekAPI(api_key=api_key)
response = api.generate(input_text)
return response
</parameter>
<parameter=new_str>
api = DeepSeekAPI(api_key=api_key)
response = api.chat_completion(input_text)
return response
</parameter>
</function>
----------
EXECUTION RESULT of [str_replace_editor]:
The file /workspace/deepseek_app.py has been edited. Here's the result of running `cat -n` on a snippet of /workspace/deepseek_app.py:
4 def call_deepseek_model(input_text):
5 api_key = os.getenv("DEEPSEEK_API_KEY")
6 if not api_key:
7 raise ValueError("Please set the DEEPSEEK_API_KEY environment variable.")
8 api = DeepSeekAPI(api_key=api_key)
9 response = api.chat_completion(input_text)
10 return response
11
12 if __name__ == "__main__":
13 test_input = "Hello, DeepSeek!"
14 result = call_deepseek_model(test_input)
Review the changes and make sure they are as expected. Edit the file again if necessary.
----------
Now, let's run the program again with the updated method. Please provide the `DEEPSEEK_API_KEY` environment variable:
<function=execute_bash>
<parameter=command>export DEEPSEEK_API_KEY=your_api_key_here && python3 deepseek_app.py</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
Traceback (most recent call last):
File "/workspace/deepseek_app.py", line 14, in <module>
result = call_deepseek_model(test_input)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/deepseek_app.py", line 9, in call_deepseek_model
response = api.chat_completion(input_text)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/api.py", line 62, in chat_completion
response = self._post_request(API_CHAT_COM, payload, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openhands/poetry/openhands-ai-5O4_aCHf-py3.12/lib/python3.12/site-packages/deepseek/api.py", line 25, in _post_request
raise Exception(f"HTTP Error {response.status_code}: {response.text}")
Exception: HTTP Error 401: {"error":{"message":"Authentication Fails (no such user)","type":"authentication_error","param":null,"code":"invalid_request_error"}}
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 1]
----------
The error indicates that the provided API key is invalid or does not correspond to a valid user. To proceed, you will need to:
1. Obtain a valid API key from DeepSeek's official platform.
2. Ensure the API key is correctly set in the environment.
Once you have a valid API key, you can set it and run the program again. Let me know if you need further assistance!
----------
key: *** ///在这里输入了key,隐藏
----------
Thank you for providing the API key. Let's set the environment variable and run the program again:
<function=execute_bash>
<parameter=command>export DEEPSEEK_API_KEY=sk-4a727097b1ef4e61a3444ec96e1e0db0 && python3 deepseek_app.py</parameter>
</function>
----------
EXECUTION RESULT of [execute_bash]:
Model Response: Hello! How can I assist you today? 😊
[Python Interpreter: /openhands/poetry/openhands-ai-5O4_aCHf-py3.12/bin/python]
[Command finished with exit code 0]
2024-12-31 22:40:05
又到了 31 号这天,回想起来 24 年过得很快,回顾过去一些事,有的感觉就前几周的事,一看原来已经过去四五个月。照例在这天写篇生活记录。
去年说今年要学下AI,总算有点进展,虽然进度不理想,但也算迈开了步伐。
学习如果没有一些事项引导,就很难进行,最好的学习方式是直接在做的过程中学,真正进去做这个事的过程中会不断遇到一些问题,解决这些问题过程就是很自然的逐渐学习和深入的过程。
没这个条件的话,就退而求其次,用分享输出的方式引导学习,所以我时隔四五年不写博客,今年又开始写了,主要就是让我的学习有个地方做完整的记录,有相应的引导。
在软件工程时代,一个功能能不能实现,原理链路大概是怎样,基本都能知道,上一轮以推荐为主的 AI 也大致能了解原理。而这次的生成式 AI 太魔法了,应用范围和影响力也远超过去,完全不了解它是怎么回事很让人难受,有种跟不上时代的感觉,学一些皮毛后感觉好一些。
不过最好还是机会边做边学,希望25年能多一些深入实践,让自己在创造的状态里学习。
今年 AI 继续快速发展,有几个时刻对 AI 的能力和带来的体验还是很震惊的:
前两天小宇宙推送年度总结,看到今年我在上面听的总时长时很惊讶,166个小时,我已经成为播客的忠实用户了。想想也合理,一年200多个工作日,如果每个工作日上下班开车一小时听一小时,也有200多个小时。
网络上高质量内容一直在切换形态,从最开始的 blog,到后来的公众号知乎,在公众号和知乎泛化后,高质量的内容逐渐转移到播客上了,在海外更是这样,特别是AI前沿信息基本都在播客上,国内也逐渐是这个趋势,今年特别明显。
除了听几个AI相关的博客,听得最多的是半拿铁,内容做得太好了,有几个很喜欢的系列,包括任天堂往事、日漫史、改革开放系列。
改革开放系列让人看到整个路径的曲折,路线反复的历史背景,一大波有担当的政府官员推动历史,克服极大的困难才让改革开放走下去,听到最近一期成事后的片尾曲《春天的故事》时,竟有些动容。
日漫史和任天堂往事,也跟着它回忆了一下这些从小到大的精神粮食,任天堂不断把游戏欢乐带给更广泛的人,强大的创新能力,至暗时刻也能翻身,不愧是游戏界的神。日本漫画家没日没夜奋斗完成作品的干劲,这种投入强度和一根筋的热情,也只有日本能把漫画行业做出来。
今年玩过最好的游戏当然是《黑神话悟空》,远超预期,独有的中国文化之美,光看画面就是很大的享受,尤其是小西天,剧情音乐也都很顶,黄风岭陕西说唱、《壁上观》都很美,能玩到这样的游戏真好。不足的是难度没有循序渐进,开头的白衣居士卡得我差点放弃,后面反而障碍不多,但偶尔被 boss 卡还是挺难受。另外玩游戏过程中几乎感受不到剧情,这么顶的剧情全靠外部解说,也不知是不是个传播策略。
看了黑神话悟空相关的访谈,一个体制以外冒出来的好的作品,都是极具个人色彩的,精品是靠一小撮人投入极大的热情和毅力创造出来的。有黑神话悟空,国产 3A 大作有希望,但5年-10年内还会不会有其他精品出来,不太乐观,只能肯定的是大公司不会有。
去了银川中卫,香格里拉,西双版纳,哈尔滨雪乡,成都(团建),中山,长沙。
银川中卫沙漠星星酒店,感受非常好,就算在五一假期,也是一片片空旷的沙漠没什么人,小朋友骑骑骆驼玩玩沙,很惬意。
雪乡在我印象中是前几年坑人口碑很差的形象,实际上超预期,雪景小镇很美,游玩设施也不错,没遇到什么坑人的场景,体验不错,可惜的是时间太短了,有机会可以再去。
香格里拉没想象中美,可能因为天气一直是阴天和下雨,偶尔的蓝天惊鸿一瞥是很漂亮,另外带着小孩没法去徒步,也没去雪山,就小小感受下藏族的风格。西双版纳很有泰国风味,热带雨林的感觉很明显,云南这两个地方的差异太大了。
成都的毕棚沟和香格里拉海拔都3000米以上,感觉没什么高原反应,明年可以去西藏了。
对成都和长沙印象都挺好,长沙五一广场方圆一大片人山人海,走哪都是商店大商场,都是茶颜悦色,夜夜笙歌,真是娱乐之都。
25年希望继续解锁世界地图。
跟同事聊娃,经常会聊出焦虑,不鸡娃不上奥数和考KET就上不了好的初中,而我们什么都没准备,让我有点担心是不是对娃太佛系了,到时会后悔,但每次想来想去最终还是不想让小孩太卷。大娃三年级作业开始变多,偶尔拖拉和烦躁,但总体上学习还是比较自觉的,成绩也不错,只要保持这个感觉,应该不至于太落后?
小娃5岁了,3-4岁有段时间专门气人,回看以前大娃也是一样有这样的时期,然后今年某个时候突然间感觉就长大了,讲理、能沟通,情绪控制也好一点了,甚至惊奇发现他学习也是挺认真的,超出预期的成长,很欣慰。
再次推荐洪恩系列 APP,小娃玩着玩着就学会了很多汉字/拼音/英文单词,以前大娃也是,这过程就是在不断玩游戏,玩的过程中学,好的学习就应该是这样。
一些喜欢的瞬间:
身体好了很多,中招咳嗽也就两次,健身断断续续,没坚持上。
今年大环境不太好,但困难总会过去,希望能保持乐观和学习心态,希望25年大家都能过得好~
2024-12-15 19:37:15
近期即梦上线了 AI 图片生成文字的能力,在生成海报、封面以及各种场景下渲染文字效果是非常不错的。最开始AI生成的图片中,涉及到文字的基本都是不能看的乱码,需要针对性训练优化才能做到生成清晰的文字并融入图片。那这里是怎么做优化的?对这个原理比较好奇,尝试通过几篇公开论文学习下相关实现思路原理。
目前生成文字(英文)最好的模型是 Recraft,官方有篇文章 《How To Create SOTA Image Generation with Text: Recraft’s ML Team Insights》介绍了模型训练的大体过程,挺适合简单了解大致思路的,简单复述下。
首先说明下为什么图片生成文字容易乱码?
接下来看优化文字生成能力的大致流程:
第一步,准备数据。准备大量的包含文字的图片,包括海报、封面、广告、Logo等,对这些图片进行处理。处理包含两部分,一是用 OCR 模型识别图像上的文字位置和文字内容,二是用多模态模型识别这张图的内容,输出描述文本。得到了海量的 图片 – 文本布局和内容 – 图片描述 组合的数据。
第二步,使用数据训练模型,跟第一步是反着的过程。先训练一个布局模型,可以通过输入 prompt → 输出文本布局+内容。再把 prompt 和文本布局输入生图模型,最终生成带文字的图片。
大流程就是这样,再稍微把其中布局模型展开一下:
输入 prompt 输出 文字内容+布局,用的是一个大语言模型(LLM),定义了一个输出的文本格式,包含文本内容和这些文本的坐标。同时还会根据文本和坐标数据,用文字渲染工具画张图片出来。
这张渲染出来的文字布局图会作为生图时的参考,用类似ControlNet 的方式作用在生图过程中,最终生成图上的文字。
这是个大致流程,文中没有展开里面模型架构的一些细节,原文上表示思路基于 TextDiffuser2,但看起来思路上跟 GlyphControl、TextDiffuser、TextDiffuser2 都有关系。
各方案大的思路都差不多,基本都是分两步,生成文字布局信息,再作用在生图过程中,主要是模型架构不同,以及数据集质量不同。下面看看这些相关的论文和一些模型细节。
先看看相对简单的 GlyphControl,23年11月的论文,基本就是一种 ControlNet,跟边缘轮廓、姿态等 ControlNet 没太大差异。ControlNet 的相关介绍可以看回这篇。
训练阶段:找一批带文字的图片,用OCR 识别文字内容和位置,再渲染出一张白底黑字的图片,将图片描述和这张白底黑字图片一起进入 Glyph ControlNet 网络训练。这个白底黑字的图片就是参考图,跟边缘轮廓/姿态等其他 ControlNet 的参考图作用和流程都一样。
推理阶段:分两部分输入,生图的 Prompt 和白底黑字参考图,这张参考图看起来是要用户自己另外准备的,可以直接画一张白底黑字的图,或者描述文字内容、行信息、大小位置布局,用工具生成白底黑字参考图,再和 prompt 一起去生成相应的带图的文字。
效果:文字能较准确生成,但没有控制字体样式和文本颜色的能力,泛化性会比较差。布局和位置需要额外输入,产品化实用性低一些。
疑问:controlNet 23年2月出现,为什么11月才有人用于改进图片文字渲染,ControlNet作者自己不试试呢?
还有一篇更直接的,直接用 ControlNet 的边缘轮廓做文字生成,也不用自己训练,做了个评测: 《Typographic Text Generation with Off-the-Shelf Diffusion Model》
TextDiffuser 是23年10月的论文,跟上面 ControlNet 的思路有差异:
这整个过程,就是为生图增加信息量,布局阶段渲染的每个字符的 mask 是很大的信息量来源,引导图片扩散方向不飘。
相对未针对性训练的生图模型,能生成合理清晰的文字,在给定图像补充文字上效果也不错,也能做到控制文本颜色了,但字体多样性差一些。
TextDiffuser 有个问题,它第一阶段产生的文字 mask 是用单一字体渲染的结果,用这个 mask 引导生图,结果是生成的结果字形的多样性比较差,生成的文字倾向于规整,手写或艺术字很难出现,GlyphControl也有同样的问题。另外 TextDiffuser 布局转换器对用户输入 prompt 的理解能力也有限。
TextDiffuser2 差异在于:
布局模型:
生图模型:
TextDiffuser2 的多样性会好一些,字体形态多样。
还有一些其他方案,例如 GlyphDraw、AnyText等,大原理差不多,不展开多说了。最后,用 notion AI 总结下本篇文章:
AI 图片生成文字主要有以下几种方案:
这些方案的核心思路都是:
目前 TextDiffuser2 的效果最好,既保证了文字的准确性,又能生成多样化的字体样式。Recraft 借鉴了 TextDiffuser2 和 GlyphControl。
2024-12-08 17:45:36
近期苹果发布的新品,无论是 iPhone 还是 Mac,都一改之前挤牙膏的风格,在最低配机器上都加大了内存,目的很明确,就是支撑 iPhone 和 Mac 上的端 AI 大模型。过去一年,AI手机、AI电脑的概念也一度在炒,在之前写的文章也说过,在客户端上跑大模型,一定是未来趋势。那目前端上大模型情况怎样?
总的来说,各家陆续出了不少小模型,相关工具链也能支持它们在客户端上跑起来,但可用的应用几乎没见到。
不少手机厂商都号称接入了端模型,但实际上没搜到相关具体应用,Apple Intelligence 还在路上,演示的能力似乎大多是云端模型,不确定本地小模型能做的事。Google Pixel 8 也没有接入Gemini nano,小米14上没有MiLM,小爱完全靠云端模型,OPPO find7 号称端侧模型用于生成通话摘要等一系列能力,但似乎得联网,不确定端模型在上面起到的作用有多大,真正能离线用的也只有图片消除功能。
为什么雷声大雨点小?
我在 Macbook pro M1 上试跑了下,感受是:3B级别的小模型基本不可用,7B/8B级别的模型速度太慢,资源占用也太大:
客户端 LLM 应用还没到时候,但不妨碍大家对这个方向的投入热情,相关的工具链有比较大的进展。
这块工具链的核心是推理引擎,LLM 的训练和推理一般都用 PyTorch,它在GPU适配/加速/生态上都是最好的,但在客户端跑模型,有一些其他诉求:
所以需要另一种推理引擎,目前用得最多的是 llama.cpp。
llama.cpp 是 C++ 开发的 LLM 推理引擎,最开始只用于 meta 的 Llama 模型推理,后来扩展到更多模型,包括 Mistral / Gemma / Phi / QWen 等基本所有开源的 LLM,也包括基于 LLM 的多模态模型 llava。llama.cpp 是个人开源项目,基于同个作者的 ggml,在它基础上加了相关大模型推理的功能,token 化 / 缓存管理等。
llama.cpp 可以跑在基本所有主流操作系统上,Android、iOS、Linux、Windows、macOS,甚至 WebAssembly上也提供支持,支持各种 GPU / CPU / NPU 推理。
基于 llama.cpp,上层包装了很多应用,可以方便地在桌面端和移动端跑各种 LLM 模型,桌面端上使用最多的是 ollama,近期 LMStudio 也很不错,移动端上可以用 pocketPal。
上述这些都是包装了模型下载管理和聊天的壳,目前比较少见到基于 llama.cpp 包装更上层垂类场景的应用。有些些 Mac AI 应用会同时提供线上 GPT 接口以及本地 ollama 接口,LLM 处理可以在本地进行,例如做音频视频转文字和总结的 MemoAI,这也可能是后续 Mac/PC 本地 AI 应用的标配。
除了llama.cpp,还有类似的mlc-llm,也是全平台和多种 GPU 支持。还有专为苹果芯片优化的LM Studio MLX,不多介绍了。
在实际应用中,端 LLM 还没能用起来,但一些厂商为了推 AI 手机 / AI 设备的概念,经常会包装进一些其他的 AI 能力,比如图片消除能力、语音唤醒识别能力。目前端 AI 真正能在实际场景中应用得好的,也还是这些多媒体图片/语音处理类的小模型,跟 LLM 无关。
常见的图片处理比如 杂物擦除、图片超清、背景去除等,都有很多小模型,转换为 ONNX 或其他推理引擎支持的格式就可以在端上跑。
ONNX 是一种标准开放的模型格式,PyTorch / TensorFlow 等各大深度学习框架训练的模型都可以转为 ONNX 格式,然后用统一的 ONNX Runtime 推理引擎部署在多种硬件和操作系统上,目前大多数端上推理引擎也都支持 ONNX 格式做推理,腾讯的 ncnn/TNN,阿里的MNN,小米的 mace 等都支持 ONNX 格式。
理论上只要模型不大,对硬件运算要求没有特别高,转化为 ONNX 格式后在端上都能很好地使用,很多特定的多媒体能力很符合这个条件,例如杂物擦除MI-GAN,只有590万个参数,直接跑在浏览器上 / APP 上都没问题,效果也不差。还有其他很多基于 GAN 的模型,图片超清Real-ESRGAN,老照片修复 GFPGAN 等,运算要求都不高,跑在端上没什么问题。IOPaint 这个项目可以看到比较多类似的模型。
如果不考虑多平台部署,把模型转为平台自带推理引擎支持的格式,是能更大程度优化性能的,例如可以将模型转为 CoreML 格式跑在 iOS/Mac 上,但相对比较少,大家更倾向于跨平台的方案。iOS 上比较有名的端生图 APP DrawThings 就是将 Stable Diffusion 转为 CoreML 格式并量化后跑在端上。也有把 SD 转为 ONNX 格式去端上跑的,但还没看到比较好的应用。
端模型的应用,从硬件上分两种:
另一种就是利用已有设备,不需要用户额外花钱买硬件,那就还是回到设备大小、续航功耗、发热、机型覆盖等限制,有些场景为了省成本可以先用起来,PC / Mac 陆续可以有一些应用场景,例如上面提到的连接 ollama 的 MemoAI,浏览器上的 AI 搜索也非常适合端上 LLM 去做,但可能这几年会一直处于小场景尝试的阶段,要到主流的程度还早得很,也可能一直不会是主流,手机更是了。
2024-09-23 11:58:48
AIGC 图片生成的技术,基本是22年开始爆发,Midjourney 2022年7月推出,Stable Diffusion 2022年8月推出,至今两年发展迅速,已经广泛在很多场景应用,但这个市场上是谁在用图片生成,用来做什么,一直以来在我认知里都有些模糊,这篇文章做下相关调研。
线上线下所有用到图片的地方,都有 AI 图片生成的应用空间,而 AI 图片生成的能力,也会创造出新的领域和行业,就目前能看到的已经在应用的场景,归归类可以分为:生产力工具、大众娱乐、探索创作。
把 AI 图片生成能力作为实际工作中的生产力工具,用在各领域的内容生产,替换原来的工作流,效率有量级上的提升,同时也有因为 AI 图生成带来的新的领域,例如自媒体。
这里的用户大部分是设计师,全球设计师 9000w,包含建筑设计、室内设计、工业设计、服装设计、产品设计、平面设计等,Adobe 付费订阅人数2650w(2022年),是非常大的市场。
电商有大量的市场,为了展示、介绍、美化不同种类的商品,对图片有巨大的诉求,是AI图片(以及视频)最好的应用场景。
![]() |
![]() |
换模特 | 换衣 |
![]() |
![]() |
灵动AI | photoroom |
素材应该是需求第二大的领域,活动图、海报、封面插图(文章/播客/杂志)、PPT,日常工作很多场景会用到,以前是搜图片找素材拼接,但如果是商用场景,一不小心有侵权的风险,素材是需要付费的,AI 图生成目前没有这个问题,而中国的版权图片市场规模在2020年是34亿,在高速复合增长。素材生成的诉求很泛,不太依赖可控生成,应该大部分都用图生成质量最好的 Midjourney,海报生成因为涉及文字,ideogram.ai 有较大的优势。
![]() |
![]() |
![]() |
ideogram海报 | 营销素材 | 壁纸 |
AI 图片生成的能力会被一些自媒体创作者用于创作有趣的内容,带来流量,进而接商单。例如影视/动漫 IP 二创、自制IP形象(宠物打工、宠物时装秀等)、扩图玩梗、表情包等,会不断有各种有趣的玩法持续出现。
![]() |
![]() |
![]() |
![]() |
![]() |
高质量图 | 扩图,玩梗 | 玩法 | 影视IP二创 | 自制IP |
![]() |
![]() ![]() |
角色生成 | 游戏原画 |
![]() ![]() |
![]() |
概念设计 | 线稿转绘 |
![]() |
![]() |
武侠漫画 | Comic Factory |
大众用户日常社交对图片是刚需,AI 图片生成在这个领域的应用是最广泛和成熟的,跑出很多爆款产品,Top 的是 Remini(23年MAU 8000w+,收入6643万美元),其他也有非常多产品冒出,AIMirror/FaceAPP/Lensa/Prisma等。
这个领域不断会有爆品出现,理论上不会一家独大,每个产品都有机会,逻辑是:出效果爆款→社交媒体传播全网引爆→大量用户使用&付费→热点几周后消退,用户少量留存,大量流失→找下一个爆款→找到进入下一个循环,找不到产品逐渐消亡。典型的持续活下来的产品是Remini,消亡的是妙鸭。
具体应用上,姑且分为 AI 写真和特效。
![]() |
![]() |
![]() ![]() |
Remini 众多特效 | 星绘 AI 写真 | ailabtools 换性别、年龄 |
另一类 ToC 的应用,是把 AI 图片生成能力作为全新产品的一部分嵌入,跟产品形态有较强的绑定。
陪伴类产品:纯 LLM 文字陪伴发展下去肯定是结合图片生成/视频生成,让人更沉浸式,可以衍生抽卡、剧情图、虚拟女友形象等。产品非常多,MiniMax 的 星野/Talkie、candy.ai、dreamgf.ai 等,AI 陪伴还在爆发增长期,AI 生图在这个领域有很大应用空间。
教育类产品:DoDoboo 将儿童涂鸦实时转为绘画作品,激发儿童创造力。是一个尝试性的应用场景,没有很成功,但 AI 教育是万亿级别市场,儿童教育领域本身注重创造力想象力的培养,AI 图片生成就是想象力的呈现,是有机会创造或融入更多教育产品。
NSFW:成人产品,比较特殊,市场自然是巨大的,待分析。
![]() |
![]() |
Talkie | DoDoboo |
除了上述 ToB 和 ToC 两类非常明确的应用场景外,AI图生成还衍生出另一波探索型用户。他们不是为工作,无商业目的,单纯喜欢玩 AI 创作,他们可能不会画画,AI 让他们可以不需要学习绘画技能,就能创作出好的作品,这对有创作欲的人有很强的吸引力。
Midjourney 付费用户中,只有 32% 的用户目的是工作或实际需求,68%的用户是为了娱乐。一方面因为 Midjourney 可控性不足,导致很难在真实生产环境使用,较少覆盖上述 ToB/ToC 的那部分用户,另一方面也能看出,纯粹探索 AI 玩图片生成的人群规模也不小,24 年 Q2 Midjourney 月活 600万+,24 年预计收入预计超过 3 亿美元。
![]() |
![]() |
![]() |
Midjourney | thehybridportraits 高端定制 |
图片生成技术,跟摄影技术有点像:
但跟摄影不同的是,图片生成技术,也许无法像拍照一样普及率那么高,摄像头记录美好生活是高频刚需,但创作不是,纯 AI 创作最终还是属于少部分创作者,就像能称为摄影师的只是少部分人。AI 技术进步是赋予了不会画画但有创意的一波人更强的能力,就像抖音最终赋予的也是少部分创作者展示他们才华的能力一样。
创作无法普及到大众,但创作出来的内容是能普及的,内容消费是大众刚需,至于这波创作者能否创作出跟摄像头相媲美的另一个维度的内容,支撑起一个 AI 内容消费社区,有待探索。
生产工具、大众娱乐、探索创作,这三类图片生成的应用,差距还是比较大的。
目前看起来没有一个产品能大面积覆盖这几个场景,未来会不会有?只要团队能满足这些条件,能造出一个超级应用满足所有图生成的诉求,大众认知上是没问题的,像上个时代的 Photoshop。