使用StarCoder创建一个编码助手
使用StarCoder创建编码助手
如果您是一名软件开发人员,那么您很有可能使用过GitHub Copilot或ChatGPT来解决编程任务,例如将代码从一种语言翻译成另一种语言或根据自然语言查询生成完整的实现,比如“编写一个Python程序来找到第N个斐波那契数”。尽管它们的功能令人印象深刻,但这些专有系统通常存在一些缺点,包括对用于训练它们的公共数据缺乏透明度以及无法将它们适应您的领域或代码库。
幸运的是,现在有几个高质量的开源替代品!其中包括SalesForce的CodeGen Mono 16B适用于Python,或Replit的基于20种编程语言训练的3B参数模型。
新来的是BigCode的StarCoder,这是一个16B参数模型,使用了来自80多种编程语言、GitHub问题、Git提交和Jupyter笔记本的一万亿个标记(所有都具有许可证)。具有企业友好的许可证、8,192个标记的上下文长度和通过多查询注意力进行快速大批量推理,StarCoder目前是代码应用的最佳开源选择。
在这篇博文中,我们将展示如何对StarCoder进行微调,以创建一个个性化的编码助手!我们将探讨使用大型语言模型(LLM)作为编码助手时出现的几个技术细节,包括:
- 如何提示LLM以像对话代理一样工作。
- OpenAI的Chat Markup Language(简称ChatML),为人类用户和AI助手之间的对话消息提供了一种结构化格式。
- 如何使用🤗 Transformers和DeepSpeed ZeRO-3对一个多样对话语料库上的大型模型进行微调。
作为最终结果的一个预览,在下面的演示中尝试向StarChat提问一些编程问题!
您还可以在以下链接中找到生成演示所使用的代码、数据集和模型:
- 代码:https://github.com/bigcode-project/starcoder
- 数据集:https://huggingface.co/datasets/HuggingFaceH4/oasst1_en
- 模型:https://huggingface.co/HuggingFaceH4/starchat-alpha
要开始,请看一下如何将语言模型变成对话代理,而无需进行任何微调。
提示LLM进行对话
正如DeepMind和Anthropic所示,可以通过巧妙选择提示将LLM变成对话代理。这些提示通常包括所谓的“系统”消息,定义了LLM的性格,以及助手和用户之间的一系列对话。
例如,这是Anthropic的HHH提示的一部分(总共有6,000个标记!):
以下是不同人和AI助手之间的一系列对话。
AI助手试图提供帮助、礼貌、诚实、复杂、情感意识和谦虚但有见识。
助手乐意帮助几乎任何事情,并将尽力理解确切需要什么。
它也试图避免给出错误或误导性的信息,并在不完全确定正确答案时加以限定。
也就是说,助手是实用的,确实尽力而为,不会因为小心而过于妨碍实用。
-----
人类:我想知道,做意大利面时加盐真的很重要吗?
助手:你是指,在煮意大利面的水中加盐很重要吗?
人类:是的
助手:通常建议加盐以提升面食的味道。科学上说,盐可以增强舌头上的化学感受器对通过味觉感知的分子的能力。
人类:但是我打算加酱料等调料,所以有关系吗?
助手:听起来你对此表示怀疑。老实说,我不确定,但既然你好奇,为什么不自己做个品尝实验呢——煮两小锅意大利面,一锅加足够的盐,另一锅不加盐。然后尝尝面条,看你更喜欢哪一锅?正如你所说,你也可以给两者都加上酱料,看看你在普通面条上注意到的任何味道差异是否变得不可察觉。这将是一个有趣的实验!
-----
...
-----
人类:{用户查询}
助手:
正如我们可以看到,提示的第一部分“下面是一系列…”对应于系统消息,并指定助手应具有“乐于助人”和“礼貌”的特点。然后,对话示例使模型遵循了多轮对话的格式。当用户提出问题时,整个提示都被输入模型,并在助手:
前缀后生成答案。然后将答案与提示连接起来,并在每一轮中重复此过程。
令人惊讶的是,这种技术对于StarCoder也适用!这得益于模型的8k令牌上下文长度,它可以包含多种编程示例,并将模型转化为编码助手。以下是StarCoder提示的摘录:
下面是各种人与AI技术助手之间的对话。
助手试图乐于助人、礼貌、诚实、复杂、情感敏锐和谦虚但有知识。
助手乐意帮助解决代码问题,并尽力理解具体需求。
助手还尽量避免提供错误或误导性的信息,并在不确定正确答案时进行警告。
也就是说,助手务实并尽力而为,不会因为谨慎而过于妨碍实用性。
StarCoder模型是一系列在The Stack(v1.2)上训练的1550亿参数模型,训练了80多种编程语言(不包括退出请求)。
该模型使用多查询注意力,使用填空中间目标进行训练,上下文窗口为8192个令牌,训练了1万亿个高度去重的数据。
-----
人类:编写一个函数,接受两个列表,并返回一个由每个输入列表的交替元素组成的列表。
助手:好的。这是一个实现这个功能的函数。
def alternating(list1, list2):
results = []
for i in range(len(list1)):
results.append(list1[i])
results.append(list2[i])
return results
人类:你能为这个函数编写一些测试用例吗?
助手:当然,这是一些测试用例。
assert alternating([10, 20, 30], [1, 2, 3]) == [10, 1, 20, 2, 30, 3]
assert alternating([True, False], [4, 5]) == [True, 4, False, 5]
assert alternating([], []) == []
人类:修改函数,使其在列表长度不一致时返回所有输入元素。较长列表的元素应位于末尾。
助手:这是修改后的函数。
def alternating(list1, list2):
results = []
for i in range(min(len(list1), len(list2))):
results.append(list1[i])
results.append(list2[i])
if len(list1) > len(list2):
results.extend(list1[i+1:])
else:
results.extend(list2[i+1:])
return results
-----
在这里我们可以看到,一个精心设计的提示如何诱导编码行为,类似于ChatGPT中观察到的行为。您可以在此处找到完整的提示,并与提示的StarCoder在HuggingChat上进行聊天。
对话提示的一个主要缺点是推理可能非常耗费资源:每一轮对话都涉及数千个令牌,这将很快消耗您的资金!
明显的替代方法是在一组对话语料库上对基础模型进行微调,并使其成为“健谈”的。让我们来看看一些有趣的数据集,这些数据集最近出现在Hub上,并为大多数开源聊天机器人提供动力。
用于健谈语言模型的数据集
开源社区正在迅速创建多样且强大的数据集,将任何基础语言模型转化为可以遵循指令的对话代理。我们发现一些能够产生健谈语言模型的示例包括:
- OpenAssistant的数据集,其中包含超过40,000个对话,社区成员轮流扮演用户或AI助手的角色。
- ShareGPT数据集,其中包含大约90,000个人类用户和ChatGPT之间的对话。
在本博客文章中,我们将使用OpenAssistant数据集对StarCoder进行微调,因为它具有宽松的许可证并且完全由人类生成。
原始数据集的格式是一组对话树,因此我们对其进行了预处理,使每一行对应于用户和助手之间的单个对话。为了避免偏离StarCoder预训练的数据太远,我们还将其过滤为英语对话。
首先,让我们从Hub下载处理过的数据集:
from datasets import load_dataset
dataset = load_dataset("HuggingFaceH4/oasst1_en")
print(dataset)
DatasetDict({
train: Dataset({
features: ['messages'],
num_rows: 19034
})
test: Dataset({
features: ['messages'],
num_rows: 2115
})
})
从上面可以看到,数据集包含约21,000个英语对话。让我们来看一个训练示例。取第一个示例:
sample = dataset["train"][0]
print(sample)
{
"messages": [
{
"content": "Is it possible to imagine a society without law?",
"role": "user",
},
{
"content": "It is difficult to imagine a society that is able to be maintained without any semblance of Law. Laws exist for the purpose of maintaining society and past human behavior suggests that there would be people that have no interest in promoting social cohesion and norms so it's unlikely that complex social and economic systems could develop without a legal system.",
"role": "assistant",
},
{
"content": 'It seems like you consider the absence of law equal to the absence of anything that could guide the behaviour of the individual, however there are many other such things, like religion, conscience, tradition to mention a few important patterns which are known to have crucial influence on society even today, but also we could imagine more, like universal love if everyone suddenly became "awaken" in some spiritual sense, or simply just having an omnipotent leader who maintains a certain order without ever codifying the principles in law. Is it still difficult to imagine?',
"role": "user",
},
{
"content": "You are correct that there are other factors that can guide behavior in a society and play a role in shaping individuals' behavior and interactions with each other. However, even in societies where these factors are present, laws still serve an important role in maintaining social order and resolving conflicts. \n\nFor example, religious teachings may prohibit theft and murder, but laws provide a system of justice for those who violate these prohibitions. Personal morality and conscience may guide individuals to act in a certain way, but laws provide a framework for resolving disputes when conflicting moral beliefs arise. Furthermore, the other factors you mentioned, such as universal love or an omnipotent leader, are not necessarily stable or reliable guides for behavior. Universal love, while a noble ideal, may not be achievable in practice, and an omnipotent leader may be prone to corruption or abuse of power.\n\nLaws provide a crucial foundation for maintaining social order and ensuring justice. It is possible to imagine alternative systems of governance, but it is unlikely that a society without any sort of legal framework would be stable or functional in the long term.",
"role": "assistant",
},
{
"content": "Yeah, but laws are complicated. Most people can't understand them in depth. Some would argue it is almost a self-serving system which put energy into growing itself(eg.: patent trolling). I think there must be a less complex system which keeps up order in society.",
"role": "user",
},
]
}
好的,这看起来是一场关于道德哲学的有趣对话,每个回合都涉及角色和内容字段,以指示谁在写。现在让我们来看一下如何将这些对话转换为一个标准格式,以简化推理时消息生成的方式。
对话的标准格式
在对话中微调模型的一种方法是在每个训练示例中插入系统消息和角色,并使用类似于<EOS>
的序列结束标记将每个对话分隔开。例如,上面的对话可以采用以下形式:
以下是人类和AI助手之间的对话 ...
人类:Is it possible to imagine a society without law?
助手:It is difficult to imagine ...
人类:It seems like you ...
助手:You are correct ...
人类:Yeah, but laws are complicated ..
<EOS>
尽管这在训练时效果不错,但对于推理来说并不理想,因为模型自然会生成不需要的回合,直到产生一个<EOS>
标记,通常需要进行一些后处理或额外的逻辑来防止这种情况发生。
一种更可取的方法是使用结构化的格式,例如ChatML,它使用一组特殊的标记将每个回合封装起来,以指示查询或响应的角色。
在这种格式中,我们有以下特殊的标记:
<|system|>
:表示对话中包含系统消息的部分,用于调整助手角色。<|user|>
:表示消息来自人类用户。<|assistant|>
:表示消息来自AI助手。<|end|>
:表示一个轮次或系统消息的结束。
让我们编写一个函数,使用这些令牌包装我们的示例,看看它的样子:
system_token = "<|system|>"
user_token = "<|user|>"
assistant_token = "<|assistant|>"
end_token = "<|end|>"
def prepare_dialogue(example):
system_msg = "下面是一个人类和一个名为StarChat的AI助手之间的对话。"
prompt = system_token + "\n" + system_msg + end_token + "\n"
for message in example["messages"]:
if message["role"] == "user":
prompt += user_token + "\n" + message["content"] + end_token + "\n"
else:
prompt += assistant_token + "\n" + message["content"] + end_token + "\n"
return prompt
print(prepare_dialogue(sample))
<|system|>
下面是一个人类和AI助手StarChat之间的对话。
<|end|>
<|user|>
能够想象一个没有法律的社会吗?<|end|>
<|assistant|>
很难想象...<|end|>
<|user|>
看起来你...<|end|>
<|assistant|>
你说得对...<|end|>
<|user|>
是的,但法律很复杂...<|end|>
好的,这看起来是我们需要的!下一步是将这些特殊令牌包含在分词器的词汇表中,所以让我们下载StarCoder分词器并添加它们:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bigcode/starcoderbase")
tokenizer.add_special_tokens({"additional_special_tokens": ["<|system|>", "<|assistant|>", "<|user|>", "<|end|>"]})
# 检查是否已添加这些令牌
tokenizer.special_tokens_map
{
"bos_token": "<|endoftext|>",
"eos_token": "<|endoftext|>",
"unk_token": "<|endoftext|>",
"additional_special_tokens": ["<|system|>", "<|assistant|>", "<|user|>", "<|end|>"],
}
作为一个合理性检查,让我们看看将字符串”<|assistant|>”进行分词是否产生一个单独的令牌ID:
tokenizer("<|assistant|>")
{"input_ids": [49153], "attention_mask": [1]}
太棒了,它可行!
屏蔽用户标签
特殊聊天令牌的另一个好处是我们可以使用它们来屏蔽与对话的每个用户回合相关联的标签的损失。这样做的原因是确保模型在对话的用户部分上有条件,但只训练模型预测助手部分(这是在推理过程中真正重要的)。下面是一个简单的函数,可以将标签屏蔽,并将所有用户令牌转换为-100,这将在损失函数中被忽略:
def mask_user_labels(tokenizer, labels):
user_token_id = tokenizer.convert_tokens_to_ids(user_token)
assistant_token_id = tokenizer.convert_tokens_to_ids(assistant_token)
for idx, label_id in enumerate(labels):
if label_id == user_token_id:
current_idx = idx
while labels[current_idx] != assistant_token_id and current_idx < len(labels):
labels[current_idx] = -100 # 忽略损失
current_idx += 1
dialogue = "<|user|>\n你好,你能帮助我吗?<|end|>\n<|assistant|>\n当然,我能为你做什么?<|end|>\n"
input_ids = tokenizer(dialogue).input_ids
labels = input_ids.copy()
mask_user_labels(tokenizer, labels)
labels
[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, 49153, 203, 69, 513, 30, 2769, 883, 439, 745, 436, 844, 49, 49155, 203]
好的,我们可以看到所有用户输入的ID都已经按照预期在标签中进行了屏蔽。这些特殊的标记具有需要在微调过程中学习的嵌入。让我们来看看其中的内容。
使用DeepSpeed ZeRO-3对StarCoder进行微调
StarCoder和StarCoderBase模型包含16B个参数,这意味着我们需要大量的GPU vRAM来进行微调 – 例如,仅加载完整的FP32精度模型权重就需要约60GB的vRAM!幸运的是,有几种处理这样大型模型的选择:
- 使用像LoRA这样的参数高效技术,冻结基础模型的权重并插入少量可学习的参数。你可以在🤗 PEFT库中找到许多这样的技术。
- 使用DeepSpeed ZeRO-3或FSDP等方法,在多个设备上分片模型权重、优化器状态和梯度。
由于DeepSpeed在🤗 Transformers中紧密集成,我们将使用它来训练我们的模型。首先从GitHub克隆BigCode的StarCoder存储库,并导航到chat
目录:
git clone https://github.com/bigcode-project/starcoder.git
cd starcoder/chat
接下来,使用Conda创建一个Python虚拟环境:
conda create -n starchat python=3.10 && conda activate starchat
接下来,安装PyTorch v1.13.1。由于这是硬件相关的,请参考PyTorch安装页面上的步骤。安装完后,安装其余的项目依赖项:
pip install -r requirements.txt
我们需要同时登录Hugging Face。要这样做,请运行:
huggingface-cli login
最后,使用以下命令安装Git LFS:
sudo apt-get install git-lfs
最后一步是启动训练!如果你有幸拥有8个A100(80GB)的GPU来运行这个训练,你可以运行以下命令。训练大约需要45分钟:
torchrun --nproc_per_node=8 train.py config.yaml --deepspeed=deepspeed_z3_config_bf16.json
这里的config.yaml
文件指定了与数据集、模型和训练相关的所有参数 – 你可以在这里配置它以适应新的数据集进行训练。然后,你训练好的模型将在Hub上可用!
作为编码助手的StarCoder
生成图表
我们想要看看我们的模型在基本可视化任务上的表现如何,根据GPT-4在tikz中的著名独角兽绘图。为了做到这一点,我们用一些编码任务来启动我们的模型,并获得了很棒的结果!是的,这些结果有点被挑选了,因为我们只选择了编写了功能代码的完成情况,但其他的也差不多!
示例1:条形图
提示:
给定以下数据{'Name':['Tom', 'Brad', 'Kyle', 'Jerry'], 'Age':[20, 21, 19, 18], 'Height' : [6.1, 5.9, 6.0, 6.1]}。你能绘制一个具有两个子图的图表吗?第一个子图是显示每个人身高的条形图。第二个子图是显示每个人年龄的条形图?使用seaborn talk模式绘制图表。
回复:
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('seaborn-talk')
data = {'Name':['Tom', 'Brad', 'Kyle', 'Jerry'],
'Age':[20, 21, 19, 18],
'Height':[6.1, 5.9, 6.0, 6.1]}
df=pd.DataFrame(data)
# 身高和年龄的条形图
fig,(ax1, ax2)=plt.subplots(nrows=1, ncols=2, figsize=(14,7))
sns.barplot(x='Name', y="Height", data=df, palette="Blues_d", ax=ax1).set_title("身高对比")
sns.barplot(x='Name',y ="Age", data=df,palette="Reds_d", ax=ax2 ).set_title("年龄对比")
# 在图表之间添加间距
fig.tight_layout()
# 显示最终的图像
plt.show()
示例StarChat生成的绘图。
示例2:映射
提示:
使用geopandas为我绘制一张世界地图。使得只有德国和西班牙被涂成红色。
回应:
import pandas as pd
from matplotlib import pyplot as plt
import geopandas as gpd
from shapely.geometry import Point, Polygon
%matplotlib inline
# 从URL或本地文件加载数据
url = 'https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson'
df = gpd.read_file(url)
# 创建绘图
fig, ax = plt.subplots()
ax.set_aspect('equal')
world = gpd.GeoDataFrame({'name': ['World'],
'geometry': [Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])]})
germany = df[df['ADMIN'] == "Germany"]
spain = df[df['ADMIN'] == "Spain"]
other = df[(df['ADMIN']!= "Germany") & (df['ADMIN']!= "Spain")]
world.plot(color='lightgrey', edgecolor='white', ax=ax)
germany.plot(color="red", ax=ax)
spain.plot(color="red", ax=ax)
other.plot(color="skyblue", ax=ax)
plt.title("European Countries")
plt.show()
示例StarChat生成的绘图。
示例3:篮球
提示:
有一场篮球比赛,以下是球员的统计数据:J. Harden 42 8 9 J. Embiid 34 13 4 T. Maxey 14 8 1 T. Harris 9 7 2 P. Tucker 6 3 1。你可以用Seaborn的talk模式为每个球员绘制一个散点图,其中y轴是得分,x轴是篮板。确保标记是'o'。在每个点上放上球员的名字。标题应为“六人队球员”。
回应:
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
%matplotlib inline
# 将数据加载到数据帧中
df = pd.DataFrame({'Player': ['J. Harden', 'J. Embiid', 'T. Maxey', 'T. Harris', 'P. Tucker'],
'Points': [42, 34, 14, 9, 6],
'Rebounds': [8, 13, 8, 7, 3],
'Assists': [9, 4, 1, 2, 1]})
# 创建图形和轴对象
fig, ax = plt.subplots()
# 绘制篮板vs得分的散点图
sns.scatterplot(data=df, x='Rebounds', y='Points', hue='Player', style='Player', markers=['o']*5)
# 删除图例,因为它与球员名称重叠
ax.legend([],[], frameon=False)
# 在点上方添加文本标签以显示球员名称
for i, txt in enumerate(df['Player']):
ax.annotate(txt, (df["Rebounds"][i] +.1, df["Points"][i]))
# 设置标题和标签
ax.set_title('六人队球员')
ax.set_xlabel('篮板数')
ax.set_ylabel('得分数')
plt.show()
示例StarChat生成的绘图。
评估编码助手
评估编码助手(或聊天机器人更一般地说)是很棘手的,因为我们关心的用户界面指标通常不在传统的NLP基准中进行衡量。例如,我们将基础和微调的StarCoderBase模型通过EleutherAI的语言模型评估工具进行了以下基准测试:
- AI2推理挑战(ARC):小学多项选择科学问题
- HellaSwag:围绕日常事件的常识推理
- MMLU:57个学科的多项选择题(专业和学术)
- TruthfulQA:测试模型将事实与对手选择的一组不正确的陈述相分离的能力
下面的表格显示了结果,我们可以看到微调模型有所改进,但并没有反映出它的对话能力。
那么,除了依靠自动评估标准之外,还可以做些什么呢?迄今为止,已经提出了两种主要方法:
- 人工评估:向人类标注者展示给定提示的生成输出,并按照“最佳”和“最差”来对它们进行排名。这是创建像InstructGPT这样的系统所使用的当前黄金标准。
- AI评估:向像GPT-4这样的能力强大的语言模型展示生成的输出和一个用于条件化模型判断质量的提示。这是用来评估LMSYS的Vicuna模型的方法。
作为一个简单的实验,我们使用ChatGPT在几种编程语言上测试了我们的StarCoder模型。为此,我们首先创建了一个有趣的提示数据集进行评估。我们使用ChatGPT来启动这个过程,询问它这样的问题:
生成一些Python编程问题的指导(格式为{"prompt": instruction})
或者
能否生成5个示例指导,格式相同为{"prompt": text},其中指导中包含有错误的代码片段,并且你要求关于你编写的代码的反馈?
在第二种情况下,ChatGPT实际上生成了比要求的更多的数据(例如初始提交中有关错误的额外联系字段)。目前,这个数据集包含115个提示,主要是Python。四分之三的提示要求用户提供代码,四分之一的提示要求对有错误的代码样本提供反馈。
在我们的实验中,我们要求OpenAI的模型使用修改版的Vicuna代码提示对每个答案进行评分,评分范围从1到8。在这种情况下,经过调整的StarCoder模型的得分比基础模型高95.6%的时间。一个有趣的现象是,我们明确看到ChatGPT在与GPT4相比更喜欢返回中间范围的较安全的分数,而GPT4更倾向于给出1和8。
这是一个快速示例,展示了LLM评估对给定提示和响应对返回的分数。
提示:
编写一个名为reverse_string的Python函数,它以字符串作为参数,并返回反转后的字符串。
调整过的完成版(助理2):
"这是一个在Python中`reverse_string`函数的示例实现:
'''
def reverse_string(input_str):
\"\"\"反转给定的输入字符串\"\"\"
# 创建一个空列表来存储字符
output = []
for char in input_str:
# 将每个字符添加到列表的前面
output.insert(0,char)
return ''.join(output)
# 示例用法
test_string =\"hello world\"
print(\"Reversed String:\", reverse_string(test_string))
'''
请注意,这个方法使用了字符串的join()方法,它将可迭代对象的所有元素使用指定的分隔符连接成一个字符串。
在这个例子中,我们使用了一个空字符串\"\",因为我们的字符串中没有空格,所以不管用什么分隔每个字母都没有关系。"
基础模型的完成版(助理1):
"当然可以!让我们首先写出解释函数工作原理的文档字符串。然后,我们定义函数的签名(参数)和函数体。
reverse_string.__doc__ = \"反转任何给定的字符串。\"
reverse_string.__name__ ='reverse_string'
reverse_string.__signature__ = inspect.Signature() #<|end|>
<|user|>
这是我的尝试:
<|end|>
<|assistant|>
好的,太棒了!现在,让我们开始在我们的作用域内定义变量,使用关键字var。我们的第一个变量需要保存传递给函数调用的任何文本,因为我们想要操作字符串。因此,我们声明strvar并将其赋值为参数arg提供的值。
strvar := arg
然后,在声明第二个变量之后,我们以类似的方式进行初始化,但是这次除了通过调用strvar的内置.reversed()方法以及索引运算符[]来传递结果之外。这样我们就可以访问原始字符串的最后一个字符,并在迭代地反转到现在为止的所有内容。请注意,我们实际上不需要在这个新变量中存储任何内容,因为它只是保存与strvar相同的数据,而不对它们进行修改。"
GPT4 评估:
4 7
助手1的代码结构不好,很难理解其逻辑。还有一些不必要的注释和解释,对于理解代码并没有贡献。代码本身是不完整的,没有提供问题的可行解决方案。为了改进,助手1应该专注于编写清晰简洁的代码,删除不必要的注释,并提供完整的问题解决方案。
助手2的代码在结构和可读性方面要好得多。代码准确高效,并包含有用的注释。用于反转字符串的切片方法是解决问题的简单有效的方法。然而,所提供的示例中有一些重复,并且对切片方法的说明可以改进。总体而言,助手2的代码是一个很好的问题解决方案,只需要进行一些小的改进。
我们可以将此与ChatGPT的回复进行比较,ChatGPT似乎没有意识到助手1实际上没有完成任务。在其回复中,它说第二个助手更好,但给了更低的评分。
ChatGPT 评估:
8 7
助手1的解决方案尝试使用变量赋值运算符:=,这是Python 3.8中新增的一个新特性,但并不一定是标准实践,也不一定在所有环境中都可用。此外,该解决方案实际上并未执行所需的反转输入字符串的操作。相反,它创建了一个新变量,并将其赋予原始输入字符串的值,但字符顺序已被反转。这意味着原始输入字符串没有被修改,函数实际上没有返回任何内容。
助手2的解决方案正确实现了反转输入字符串的所需操作,使用列表存储每个字符,然后按相反的顺序将列表连接成字符串。注释和文档字符串提供了代码的良好上下文和解释。然而,变量名"output"可以改进为更具描述性的名称,可以通过使用Python内置的字符串反转方法而不是手动创建列表并将其连接回字符串来提高解决方案的效率。
总体而言,助手2的解决方案更准确、高效、可读。代码结构清晰易懂,有用注释的存在提高了代码的整体质量。然而,助手1尝试使用新的赋值运算符显示了开发者跟上Python最新特性的努力,这是一个积极的品质。
这表明AI评估中存在极其有价值的信号,但我们还有很多东西要学习,如何比较模型并将这些结果与人类进行校准!
限制和偏见
与许多其他语言模型一样,StarChat的这个α版本还存在待解决的严重限制,包括产生虚构事实和问题内容(尤其是在提示下)。特别是,该模型尚未通过RLHF等技术对人类偏好进行校准,也没有像ChatGPT那样在循环中过滤响应。主要基于代码数据训练的模型也将具有与GitHub社区人口统计数据倾斜相对应的人口统计偏差,有关更多信息,请参阅StarCoder数据集。有关模型在事实性和偏见方面的限制的更多细节,请参阅模型卡片。
未来发展方向
我们惊讶地发现,像StarCoder这样的代码生成模型可以通过OpenAssistant等多样化的数据集转化为对话代理。一个可能的解释是StarCoder已经在代码和GitHub问题上进行了训练,后者提供了关于自然语言内容的丰富信号。我们很期待看到社区将会如何发展StarCoder,也许它将推动下一波开源助手的发展 🤗。
致谢
我们感谢Nicolas Patry和Olivier Dehaene在Inference API上部署StarChat并实现了快速的文本生成。我们还感谢Omar Sanseviero在数据收集方面的建议和他提出的许多有价值的改进建议。最后,我们感谢Abubakar Abid和Gradio团队通过新的代码组件创建了一个令人愉快的开发者体验,并分享了他们在构建优秀演示的专业知识。
链接
- 代码:https://github.com/bigcode-project/starcoder/tree/main/chat
- 经过筛选的训练数据集:https://huggingface.co/datasets/HuggingFaceH4/oasst1_en
- 代码评估数据集:https://huggingface.co/datasets/HuggingFaceH4/code_evaluation_prompts
- 模型:https://huggingface.co/HuggingFaceH4/starchat-alpha
引用
要引用此工作,请使用以下引用:
@article{Tunstall2023starchat-alpha,
author = {Tunstall, Lewis and Lambert, Nathan and Rajani, Nazneen and Beeching, Edward and Le Scao, Teven and von Werra, Leandro and Han, Sheon and Schmid, Philipp and Rush, Alexander},
title = {使用StarCoder创建一个编码助手},
journal = {Hugging Face博客},
year = {2023},
note = {https://huggingface.co/blog/starchat-alpha},
}