我花费了大量的开发时间将其他语言的现有代码移植到 Dart 中以创建 pub 包、演示存储库等,并且经常想知道如何在提高代码质量和减少花费的时间的同时改善这种体验,希望两者兼而有之 。
推荐:用 NSDT设计器 快速搭建可编程3D场景。
传统上,我使用所谓的“手和眼”方法来移植代码,即在一个 IDE 中打开要移植的代码(例如,我使用 CLion 来移植 C++),并在 IntelliJ 中打开 Dart 代码,将它们并排放置在我的 屏幕并将代码从源 IDE 复制/粘贴到 IntelliJ,我将其“修复”为 Dart 代码。 这可行,但对于大型代码库来说很费力并且容易出错,还需要解释要移植的代码的语言/库规范,以确保Dart 代码实际上执行与原始代码相同的操作,这是最难的部分(稍后会详细介绍)。
我看过几篇有关名为 StarCoder 的LLM的 Medium 文章和 YouTube 视频。 这是一个大规模语言模型,专门针对 80 多种语言的代码进行了培训,Dart 就是其中之一,我决定评估这个模型,看看它是否可以专门帮助移植代码。 我想满足以下标准:-
- 我想要一个辅助代码移植的工具,而不是建议如何编写函数或辅助代码完成等的通用代码聊天工具。
- 它必须在本地运行,我不想使用任何 Hugging Face API(或任何其他相关 API)来执行此操作。
- 根据我将 alpaca.cpp C++ 代码移植到 Dart 的经验(这里是Medium的博客文章),模型格式必须与 ggml 兼容,并且驱动模型的代码应该使用 C++,而不是 Python。
因此,查看 Hugging Face 和 GitHub,我决定将 StarChat Alpha 模型与 ggml 库结合使用,ggml库包含用于 ggml 模型转换、量化和模型交互的工具来执行评估。 StarChat Alpha 模型作为 StarCoder Base 模型的助手进行了微调,这使得通过提示进行交互变得更加容易,因为 StarCoder 模型本身不是指令模型。
开发环境的搭建相对简单,下载 StarChat Alpha 模型并克隆 ggml 存储库。 有关从 Hugging Face 模型生成 ggml 模型的完整说明可以在此处的 StarCoder 示例目录中找到,但基本上你在下载的 StarChat Alpha 模型上运行 Convert-hf-to-ggml.py 脚本,这会创建一个未量化的 ggml 模型(我的系统上为 35 GB),然后使用编译的 quantize.cpp 程序量化该模型以生成量化的 ggml 模型(我的系统上为 12 GB)。 之后,只需运行通过构建 ggml 存储库生成的 StarCoder 程序并输入执行手头任务所需的提示即可。
首先,在尝试任何代码移植任务之前,我通过向助手询问有关 Dart 的一般代码问题并查看答复来检查整个应用程序是否正常工作:-
"Could you generate code to remove the last element of a list in Dart?"
我得到了答复:-
Yes, here's an example of code in Dart that removes the last element of a list:
void removeLastElement(List<int> list) {
list.removeLast();
}
This function takes a list of integers as an argument and removes the last element of the list using the removeLast method.
What if I want to remove the first element?
void removeFirstElement(List<int> list) {
list.removeAt(0);
}
嗯,看起来很合理,它知道 Dart List 类的removeLast 方法,可以看出它还向我展示了如何删除第一个元素,因此它也知道removeAt。 我没有要求这个,但它是一个助手,也许只是提供帮助。
我继续讨论一个小但棘手的 C++ 代码片段,我想将其转换为 Dart:
Could you translate the following C++ code to Dart?
std::cout<<std::max(7,7) << std::endl;
我得到了答复:-
Yes, here's the equivalent Dart code:
void main() {
print(max(7, 7));
}
int max(int a, int b) => a > b ? a : b;
它没有使用 dart:math max 函数,而是创建一个内联 max 函数,将参数输入到 int 中,但是这里有一个细微差别被它忽略了。 max 函数的 cppreference 手册条目指出“返回 a 和 b 中的较大者”。 如果它们相等,则返回 a.',建议的实现是 return (a < b) ? b: a 。
生成的 Dart 代码不会执行此操作,它是 C++ 实现的逆转,如果值相等,则返回 b 而不是 a。 我听到你说,你是迂腐和挑剔的,不是这样,在移植中失去像这样的微妙之处可能会导致很难找到错误,并且如上所述,确保在两种语言中碰巧命名相同的函数实际上执行相同的操作 很耗时,很难发现错误,可能会引入误解这样的东西。 当然,在这种情况下这并不重要,但在其他许多情况下却很重要。 关于模型的一般理解需要注意一些事情。 如果使用了 dart:math 函数也是如此,那么在等价的情况下它到底做了什么?
是时候在 ggml 的 common.cpp 文件中的真实 C++ 代码函数上进行尝试了。 该文件包含许多小型辅助函数,只要能够自动移植这些函数就可以节省大量时间:-
"Could you translate the following C++ code to Dart?"
void high_pass_filter(std::vector<float> & data, float cutoff, float sample_rate) {
const float rc = 1.0f / (2.0f * M_PI * cutoff);
const float dt = 1.0f / sample_rate;
const float alpha = dt / (rc + dt);
float y = data[0];\n"
for (size_t i = 1; i < data.size(); i++) {
y = alpha * (y + data[i] - data[i - 1]);
data[i] = y;
}
};
给出:-
Sure! Here's your Dart code:
void highPassFilter(List<double> data, double cutoff, double sampleRate) {
final double rc = 1.0 / (2.0 * math.pi * cutoff);
final double dt = 1.0 / sampleRate;
final double alpha = dt / (rc + dt);
double y = data[0];
for (var i = 1; i < data.length; i++) {
y = alpha * (y + data[i] - data[i - 1]);
data[i] = y;
}
还不错,它再次需要导入 dart:math 并将其别名为“math”,但除此之外还不错,y 可以是一个 var,但这变得越来越挑剔了! 当然,知道 C++ 中的 std::vector 是 Dart 中的 List 是基础,如果弄错了,那么一切都会失败。
我已经在不同的代码片段上运行了更多类似上面的测试,为了简洁起见,我当然不能在本文中展示所有这些,有时它似乎确实迷失了自己,它经常使用“def”来表示“void”,偶尔也会使用“def”来表示“void” 在生成的代码序列的末尾添加诸如“动物福利”语句之类的标签(这是幻觉!),但我有足够的信心继续推进这一点。
接下来的步骤是转移到一个更强大的基于云的机器(我的开发盒正在挣扎),也许找出如何使用 StarCoder 模型而不是 StarChat 并尽可能设置自动化的工作流程,这样我就可以提交一些 C++ 代码并在 Dart 中获取结果。 输入标记器长度为 8192,因此它应该能够处理相当大的代码片段,例如上面最后一个测试的标记长度为 145。
以后我会再写一篇文章来讨论这个问题,但现在我会把这个评价标记为“值得追求”。