11月11日,阿里巴巴宣布了其迄今为止最先进的编码模型:Qwen 2.5-Coder-32B-Instruct。但这还不止于此,它实际上是一系列编码模型中的一员,属于一个大家庭。除了32B版本外,还有0.5B、1.5B、3B、7B和14B参数的版本。在写这篇文章之前,我看了很多YouTube博主、作家和技术专家的反馈,大家的态度都非常好。今天,我们就来看看它是不是真的名不虚传。
我一直使用 ChatGPT、Gemini 和 Claude 这几个工具,可以自信地说 Claude 在写代码和理解复杂的任务方面最优秀。 说实话,Gemini 则完全属于另一个级别。一个有趣的事实:两年前我曾经用 Bard(现在重新命名为 Gemini)赢得了一次机器学习比赛,因为在场的其他人都是使用 ChatGPT。Bard 的表现当时给了我一个优势。如今,我认为 Gemini 可能是最顶尖的视觉模型,不过总的来说,我对它并不太感冒。
为了评价模型在编码方面的表现,我们需要关注以下几个关键点:代码生成,代码修复,以及代码推理(理解)。此外,我们还需要考虑它支持哪些编程语言以及是否符合人类偏好。
概要在测试阿里巴巴的模型之前,我们先来看看他们公布的表现数据。
根据他们内部的基准,它甚至超过了GPT-4,几乎达到了Claude 3.5的水平。不过,我个人不太看重单独的内部基准测试,但据试用过它的人说,结果看起来相当有趣。
你可以在这里了解更多关于演出的详情。
Qwen2.5-Coder-32B-Instruct - Qwen2现在正是时候了!11月11日 10:24!这是我们迄今为止最好的编码模型!Qwen2.5-Coder-32B-Instruct,来了!qwen2.org我在本地使用Ollama来测试模型。
Qwen代码特定模型的最新系列,性能在代码生成和代码推理方面有了显著改进——ollama.com你也可以在Hugging Face的演示页面上试试这个模型:
Qwen2.5 编码演示 - 一个由 Qwen 创建的 Hugging Face 空间页面 生成代码许多人已经讨论了这款工具在网页浏览和设计方面的表现:
那么,我们将专注于用一些 Python 挑战来测试。我们从一个中等难度的 LeetCode 问题开始吧。
提示:题目描述:不含重复字符的最长子串
回答 class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
# 保存每个字符上次出现位置的字典
char_index_map = {}
# 初始化当前区间的起始位置和最大长度
start = 0
max_length = 0
for end in range(len(s)):
# 如果当前字符已经在字典中,并且其索引在当前区间内
if s[end] in char_index_map and char_index_map[s[end]] >= start:
# 将起始位置移到当前字符上次出现的位置之后
start = char_index_map[s[end]] + 1
# 更新当前字符的索引位置
char_index_map[s[end]] = end
# 更新当前最长子串的长度
max_length = max(max_length, end - start + 1)
return max_length
结果如下
图片由作者提供
这不是最好的,但它还是能完成任务并通过所有测试。它能搞定难题吗?(需要注意的是,这个模型可能已经训练过这些问题,但它还是很厉害。)
提示:题目:正则匹配
正则表达式匹配
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
m, n = len(s), len(p)
# 创建一个二维数组来存储子问题的答案
dp = [[False] * (n + 1) for _ in range(m + 1)]
# 初始情况:空字符串匹配空模式
dp[0][0] = True
# 处理类似 `a*`、`a*b*` 等模式,这些模式可以匹配空字符串
for j in range(1, n + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 2]
# 填充dp表
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j - 1] == '.' or p[j - 1] == s[i - 1]:
dp[i][j] = dp[i - 1][j - 1]
elif p[j - 1] == '*':
# 如果 `*` 匹配零次,则 `dp[i][j]` 等于 `dp[i][j-2]`
dp[i][j] = dp[i][j - 2]
if p[j - 2] == '.' or p[j - 2] == s[i - 1]:
# 如果 `*` 匹配一次或多次,则 `dp[i][j]` 等于 `dp[i][j]` 或 `dp[i-1][j]`
dp[i][j] = dp[i][j] or dp[i - 1][j]
# 最终结果位于 `dp[m][n]`
return dp[m][n]
结果如下
这图是作者拍的
经过在 LeetCode 上测试了大约 10 到 20 个问题后,我感觉它是在所有问题上进行了训练,所以这种方式并不是评估它的最佳方法。不过,分享还是有意义的。另外,它总是选择提供 Python 解决方案,即使没有特别要求使用。不过让我们试试不同的东西。我让模型尽可能优化代码以减少资源消耗。
这张图片是作者提供的
我没想到会是这样的结果,当我让模型提高性能时,它甚至在最简单的情形下也失败了。
接下来我们进入代码修复环节。我会提供一些Claude生成的错误代码,看看模型能否修复这些难缠的错误,让我们看看它的表现如何。
修复代码 提示语 以下代码包含几个错误。请识别并修复这些错误,并为每个找到的错误提供解释。
class AsyncTaskQueue:
def __init__(self):
self.tasks = []
self.running = False
self.lock = threading.Lock()
self.workers = []
async def add_task(self, task):
async with self.lock:
self.tasks.append(task)
if self.running 为 False:
await self.start_processing()
async def start_processing(self):
self.running = True
while self.tasks:
task = self.tasks.pop(0)
worker = asyncio.create_task(self.process_task(task))
self.workers.append(worker)
if self.workers 的长度大于或等于 3:
done, pending = await asyncio.wait(self.workers)
self.workers = list(pending)
self.running = False
async def process_task(self, task):
result = await task()
return "返回结果"
答案
(注:原文为空,此处仅保留Markdown标题格式,不添加任何翻译内容。)
当然!让我们来分析提供的代码中存在的问题,并进行修正。以下是主要问题及其解决方法:
问题识别
线程锁与 asyncio 的使用:
threading.Lock 不适用于 asyncio。应使用 asyncio.Lock。
异步锁上下文管理:
使用 threading.Lock 时,async with 语句的使用不正确。应使用 asyncio.Lock。
开始处理:
start_processing 方法应以非阻塞方式启动。由于 start_processing 是一个异步方法,应正确等待它。
任务管理:
start_processing 方法应更高效地处理任务。当前实现可能无法正确处理任务的完成。
工作线程管理:
应更仔细地管理 workers 列表,确保任务完成后再标记队列为未运行。
修正后的代码
这是修正后的代码:
import asyncio
import threading
class AsyncTaskQueue:
def __init__(self):
self.tasks = []
self.running = False
self.lock = asyncio.Lock()
self.workers = []
async def add_task(self, task):
async with self.lock:
self.tasks.append(task)
if not self.running:
self.running = True
asyncio.create_task(self.start_processing())
async def start_processing(self):
while True:
async with self.lock:
if not self.tasks:
self.running = False
break
task = self.tasks.pop(0)
worker = asyncio.create_task(self.process_task(task))
self.workers.append(worker)
if len(self.workers) >= 3:
done, pending = await asyncio.wait(self.workers, return_when=asyncio.FIRST_COMPLETED)
self.workers = list(pending)
async def process_task(self, task):
result = await task()
return result
# 示例用法
async def example_task():
await asyncio.sleep(1)
print("任务完成")
return "结果"
async def main():
queue = AsyncTaskQueue()
await queue.add_task(example_task)
await queue.add_task(example_task)
await queue.add_task(example_task)
await queue.add_task(example_task)
await queue.add_task(example_task)
await queue.add_task(example_task)
asyncio.run(main())
修正说明
异步锁:
用 asyncio.Lock 替换 threading.Lock,以确保与 asyncio 的兼容性。
异步锁上下文管理:
使用 async with self.lock: 正确管理异步上下文中的锁。
开始处理:
改变了开始处理的逻辑,通过使用 asyncio.create_task(self.start_processing()) 在 add_task 内以非阻塞方式启动处理。
任务管理:
修改 start_processing 以持续检查任务并进行处理,直到没有任务为止。当没有任务时,它会退出循环并将 self.running 设置为 False。
工作线程管理:
使用 asyncio.wait 和 return_when=asyncio.FIRST_COMPLETED 正确管理工作线程,以高效地处理任务完成。
示例用法
示例用法展示了如何将任务添加到 AsyncTaskQueue 并异步运行。主函数向队列添加多个任务,并以最多 3 个任务为限同时处理它们。
这个修正和优化后的代码应该表现得更好且更可靠。
结果与反馈
克劳德给模型的回复打了7.5分,并给出了如下反馈:
虽然该模型提供了一个功能上正确的解决方案,但它更像是提供了一个“教程级”的解决方案,而不是一个“生产级”的实现。这表明在复杂的并发编程任务中,人类的专业知识在设计稳健的系统时仍然不可或缺。
经过对模型的试验,它能搞定活儿,但通常需要额外的提示来改善回应。理解任务没问题,也能纠正大多数关键错误,但它在错误处理和资源管理等方面仍然显得较弱,尤其在这些领域,它不如像ChatGPT和Claude这样的模型。我认为这些可能是模型的主要缺点。
基于专业化的大型语言模型:人工智能的新纪元看到相对较小的、特定领域的语言模型能够本地运行真是太酷了。我相信我们正进入一个专门化大型语言模型(LLM)的新时代,而这只是一个开始。尽管这个概念并不新鲜,但我们终于看到了在特定领域真正表现出色的模型,这确实是令人兴奋的。
RAG(检索增强生成技术)与专业的大规模语言模型(LLM)的结合可能会在未来几个月/几年内定义未来的人工智能领域。我们很可能看到行业领导者如OpenAI和Anthropic推出更强大的专注于编程的模型。编程或许是最适合AI专业化的领域之一,我们很快就能看到更加专注于特定任务的模型,比如专门为DevOps或前端开发优化的LLM!
不要被这些快速的进展所压倒。是的,看到大型语言模型掌握那些传统上需要多年才能掌握的技能确实会让人感到有些不安或压力。几十年来一直挑战人类的编码问题正在我们眼前发生着翻天覆地的变化。但我们不应将其视为终点,而应视为成长和创新的新起点。
无论当前大规模语言模型的发展是否会放缓,还是这只是更长旅程的开始,我们的态度都应该是相同的:不断学习,保持好奇心,创新的脚步永不停歇。技术的未来正在被书写,我们都将在塑造技术未来中发挥作用。