AI 帮我重新上架了 Chrome 插件:Copy Unlock

标签:CSS, Google Chrome, JavaScript

14 年前刚学 Chrome 插件开发时,我做了个叫 Enable Copy 的插件,用来解除很多网站对于文本选择、复制和右键菜单的限制。维护 9 年后,该插件的用户量突破了 20 万,先后有 3 个买家想买,我觉得继续维护也麻烦,就卖掉了。
这几天交接工作比较清闲,正好又被 CSDN 的复制限制给恶心到,于是准备重做这个插件。

Go 的泛型限制真是充满各种枷锁

标签:Go

最近想用 Go 写一个 Web 服务,于是看了下当前的 Web 框架性能排行榜
Go 框架中,排第一的是 Fast HTTP,但是文档几乎没有,看了下如果要获取一个 query 参数,大概要这样写:ctx.QueryArgs().Peek("foo")。紧随其后的是 atreugo,写法也是类似:ctx.UserValue("foo").(string)
都 2025 年了,连 Python 都不干这种硬编码字符串和手动类型转换的事了,没想到 Go 还在做。

对 Cursor 的一些观察和思考

标签:AI

自上次评测 AI 自动编程工具以来,各种新兴产品如 Trae 等相继入局,而 Roo Code(原Roo Cline)也通过集成 VS Code LM API,提供了每月 50 次的 Claude 3.5 Sonnet 免费调用。这些替代品的出现和完善使它们在成本上有着显著的优势,但在自动补全和光标预测方面,仍无人撼动 Cursor 的地位,那它究竟有怎样的核心技术壁垒呢?

AI 自动编程评测:Cline vs GitHub Copilot vs Cursor

标签:AI

最近 Cursor 很火,而我一直觉得免费的自动补全工具就已经够用了,花钱使用自动编程工具似乎有些浪费。然而最近的一次经历让我对其大为改观:Cline + Claude 3.5 Sonnet 在 5 分钟内就帮我解决了一个几天都没有搞定的问题。

好奇之下,我准备评测一番各种自动编程的工具,看看谁是目前的王者。

这次的任务背景如下:
公司有一个服务需要调用多种大语言模型的 API,在接入其他模型时,都能通过 HTTP 接口异步调用,唯独 AWS Redrock 提供的 Claude 模型没有。
我花了几天时间去搜索文档和寻找资料,只得到这个结论:AWS Redrock 没有提供 HTTP 的接口文档,官方只提供了同步库(如 boto3)来进行调用,没有异步实现。
大约半年前,我曾向 AWS 的工程师请求帮助,得到了一份复杂的代码片段,介绍了如何为 HTTP 接口调用添加签名。我简单尝试了一下,发现签名仍然错误。也许再多花几个小时修改代码,我有可能可以解决这个问题,但当时业务重心偏向 GPT-4o 模型,所以我就将其搁置了。
如今有了 AI 自动编程工具,我决定让它们来解决这个问题。

使用 ProxyChains 让 macOS 应用使用代理访问网络

标签:Mac OS X, 翻墙

这两天在用 VCMI 玩英雄无敌3,发现之前遇到的很多崩溃 bug 都已经解决了。但是在下载 mods 时,仍然遇到一个问题:mods 的仓库托管于 raw.githubusercontent.com,而这个域名早就被墙了,导致无法通过 VCMI Launcher 下载。
我查看了一下 VCMI 的源码,发现它是用 Qt 的 QNetworkRequest 来创建网络连接的,没有写任何代理相关的代码。由于我是用 SOCKS5/HTTP 代理翻墙,并没有采用 VPN 这种全局代理的方式,因此只能另找方法让它使用代理。

微调的上限是由底模决定的

标签:AI

继上个月研究 LLM 的微调以来,我把各种参数都折腾了一遍,效果大致已经到极限了。
当时的数据量只有 400 多条,为了避免过拟合,我一共只训练了 2 轮。为了避免相同的训练顺序对学习造成影响,还将两次训练集的顺序打乱了。

由于数据量很少,初始 loss 更低的 PiSSA 是非常推荐使用的,不然本就稀少的数据被用于降低初期较高的 loss,显得有点浪费。
之前那篇文章我没有说明 PiSSA 的原理,这里简单提一下:
LoRA 的原理是构造 2 个低秩矩阵,其中 A 矩阵是随机值,B 矩阵全是 0,它们相乘后再和原矩阵相加作为参数。
而 PiSSA 则是将原矩阵进行奇异值分解,也就是让分解得到的两个矩阵相乘等于原矩阵。当然,完全相等也没必要,一般会使用快速奇异值分解,让 A * B 近似于原矩阵即可。这样,我们在微调 A、B 矩阵时,初始状态更接近原矩阵(初始 loss 低),微调效果也更接近全参微调(最终 loss 低)。

我选用的验证集约 40 条,Qwen2-7B-Instruct 的 eval loss 大概能降到 0.3。
之后针对验证集中表现不好的例子,我又陆续构造了约 200 条数据。至此,eval loss 大概能降到 0.2。但继续增加数据量,却发现模型会出现教不会的现象。
顺便对比一些在线的模型:gpt-4o 和 gpt-4o-mini 大概到 0.14,glm-4-flash 大概到 0.25。

又到了一年一度换域名的日子

标签:无

自从用了 12 年的 keakon.net 被墙后,本站年复一年更换域名的传统也坚持三年了。

不过这次和墙以及域名注册商跑路无关,纯粹是因为去年低价买的 keakon.uk 续费太贵,加上对 .uk 没啥好感,所以再换个便宜的。

如果有订阅 RSS 的,记得把域名换成 keakon.top,其他情况我就帮不了了。

如何微调一个自用的小模型

标签:AI

最近在用大模型做多语种的翻译,选择了 2 张 4090 能部署的当前最强模型:Qwen2-72B-Instruct-GPTQ-Int4。
但在使用过程中发现了不少问题,例如:
  • 速度太慢,用 vLLM 部署,32 并发时大概 300 tokens/s,每个请求其实不到 10 tokens/s。
  • 指令遵循不行,当要求过多时,会随机无视一些指令。
  • 喜欢废话,即使要求它只输出翻译,也可能会附带一堆多余信息。目前发现最好的方式是让它输出在一个 XML 标签里(例如 <OUTPUT></OUTPUT>),然后用字符串匹配或正则表达式来提取翻译。

那么有没有办法解决呢?

AI 时代的显卡选择

标签:AI

最近我一直在折腾大模型的推理、部署和训练,遇到了不少坑,先阶段性地总结一下。

先说下结论吧:
  • 个人学习最简单的方案:16 寸 MacBook Pro M3 Max 128 GB 内存,靠谱的低价约 28000+ RMB。最大可进行约 70B Int8 量化模型的推理(如 Qwen2-72B-Instruct-GPTQ-Int8)。
  • 个人学习+游戏需求:RTX 4090,不太好买,约 15000+ RMB。最大可进行约 30B Int4 量化模型或 7B 模型的推理。
  • 小规模部署:双 RTX 4090。最大可进行约 70B Int4 或 AWQ 量化模型的推理(如 Qwen2-72B-Instruct-GPTQ-Int4)。
  • 训练 70B 的模型:租 8 * Tesla A800/A100/H800/H100 80GB 的服务器(越往右性价比越高),每天约 1000~3000 RMB。
  • 追求速度:用各个厂商的云服务。

其他不推荐的选项:
  • 192 GB 内存的 Mac Studio 或 Mac Pro:型号较老,价格太贵,唯一的优势是可以进行约 70B 模型的推理。
  • RTX 3090 等其他游戏显卡:相较于 RTX 4090 的性价比较低。
  • Tesla A100 等高端专业显卡:价格太高,如果要进行 70B 模型的推理需要 2 张(约 15 万 RMB),性价比不如 4 张 RTX 4090。如果用于训练和微调,闲置时有点浪费。
  • AMD 显卡:性价比较高,但生态不如 NVIDIA,这意味着很多库和工具不支持 AMD 显卡或性能较差,较新的论文和库一般都只有 NVIDIA 版。不适合学习,除非你确定正好能满足你的需求。
  • 国产显卡:价格不低,生态较差,很多库无法使用或需要专门安装老版本的魔改版。

Python 的协变、逆变与不变

标签:Python

前几天在使用 httpx 时,发现它的代理参数声明的类型是 ProxiesTypes
URLTypes = Union["URL", str]
ProxyTypes = Union[URLTypes, "Proxy"]
ProxiesTypes = Union[ProxyTypes, Dict[URLTypes, Union[None, ProxyTypes]]]
可以看出,dict[str, str] 应该是符合它的参数签名的,然而我传入一个 dict[str, str] 参数后,Pylance 却会报错,这让我大为不解。

于是我又尝试简化了一下这个问题:
from typing import Mapping

a: dict[int, int] = {}
b: dict[int, int | str] = a  # error:
# Expression of type "dict[int, int]" is incompatible with declared type "dict[int, int | str]"
#   "dict[int, int]" is incompatible with "dict[int, int | str]"
#     Type parameter "_VT@dict" is invariant, but "int" is not the same as "int | str"
#     Consider switching from "dict" to "Mapping" which is covariant in the value type
c: Mapping[int, int | str] = a
d: Mapping[int | str, int] = a  # error:
# Expression of type "dict[int, int]" is incompatible with declared type "Mapping[int | str, int]"
#   "dict[int, int]" is incompatible with "Mapping[int | str, int]"
#     Type parameter "_KT@Mapping" is invariant, but "int" is not the same as "int | str"
是不是很奇怪,为啥 dict[int, int]dict[int, int | str]Mapping[int | str, int] 都不兼容,而与 Mapping[int, int | str] 兼容?

« 看看还有什么好玩意