2024年11月

前有 5 万颗星标的开源项目 HTTPie 因误操作导致 Star 清零(2022 年),上周知名开源项目 Elasticsearch 也经历了 Star 一夜清零的事件。这些事故的原因均是管理员误将开源项目
从公开状态转为私有状态
所导致。为避免类似事件再次发生,GitHub 已在转为私有的功能处增加了两次确认步骤,并提醒用户星标清零后无法恢复。

希望大家引以为鉴,在做同样操作时一定要小心,并仔细阅读提醒。

说回本期的热门开源项目,都是一些能帮助你减轻工作和学习重担的利器。比如这款免费的 API 学习平台 APIHub,可以为初学者提供在线学习 API 开发的支持,并附有多种编程语言的示例。ChartDB 是一键生成数据库图表的工具,使用时无需输入数据库用户名和密码。Ophiuchi-desktop 让你在 5 秒内启动本地 HTTPS 代理,便于在本机上进行开发和测试。开源的 Android 虚拟定位应用 GoGoGo,一款帮你实现按时打卡的神器。有 AI 加持的浏览器自动化工具 Skyvern,无需写代码、且在网页结构变动时更具适应性,不易导致自动脚本失效。

最后,萌萌哒的网站计数器 Moe-Counter,内置了多款可爱的主题风格,我觉得都挺好看的。

  • 本文目录
    • 1. 热门开源项目
      • 1.1 免费的 API 学习平台:apihub
      • 1.2 多功能的自托管仪表盘:Dashy
      • 1.3 一键生成数据库图表的工具:ChartDB
      • 1.4 轻松启动本地 HTTPS 代理的工具:ophiuchi-desktop
      • 1.5 AI 自动化浏览器工作流的工具:Skyvern
    • 2. HelloGitHub 热评
      • 2.1 可爱的网站计数器:Moe-Counter
      • 2.2 开源的 Android 虚拟定位应用:GoGoGo
    • 3. 结尾

1. 热门开源项目

1.1 免费的 API 学习平台:apihub

主语言:JavaScript

Star:6.4k

周增长:1k

这是一个功能齐全的 API 学习平台,支持多种编程语言(Node.js、Python、Go 等)的 API 开发和学习。它免费提供丰富的 API 集合,涉及社交媒体集成、支付网关、物联网设备连接和机器学习等领域。你可以在该平台获取 API 开发的各类资源,包括详细教程、接口文档、代码示例和在线尝试。除了使用在线服务外,强烈推荐用户选择本地部署,以避免官网服务每两小时重置数据的限制。

GitHub 地址→
github.com/hiteshchoudhary/apihub

1.2 多功能的自托管仪表盘:Dashy

主语言:Vue

Star:17k

周增长:200

该项目是基于 Vue.js 开发的个人仪表盘(dashboard),旨在帮助用户通过一个 Web 界面管理和访问个人的自托管服务。它开箱即用、配置简单,内置多种颜色和图标,以便用户自定义界面,支持状态监控、多页面、多语言、小部件、自定义快捷键和搜索等功能。

GitHub 地址→
github.com/lissy93/dashy

1.3 一键生成数据库图表的工具:ChartDB

主语言:TypeScript

Star:4.9k

周增长:1.1k

这是一款基于 Web 的数据库表编辑器,无需数据库密码,仅需提供一条 SQL 查询结果即可导入数据库表和结构。用户可以通过直观、交互式的界面编辑和导出建表 SQL。它支持 PostgreSQL、MySQL、SQL Server、SQLite、ClickHouse、MariaDB 数据库,适用于数据库迁移和优化过程中,快速生成和调整 DDL 脚本等场景。

GitHub 地址→
github.com/chartdb/chartdb

1.4 轻松启动本地 HTTPS 代理的工具:ophiuchi-desktop

主语言:TypeScript

Star:928

这是一个本地 HTTPS 代理服务器管理工具,无需复杂配置即可轻松设置本地 HTTPS 代理。它使用 Docker 作为后端,并采用 Tauri 编写 GUI 界面,极大地简化了本地 HTTPS 代理的配置流程。不过,使用前需确保本机已安装 Docker。

GitHub 地址→
github.com/apilylabs/ophiuchi-desktop

1.5 AI 自动化浏览器工作流的工具:Skyvern

主语言:Python

Star:9.8k

周增长:3k

该项目是基于大型语言模型(LLMs)和计算机视觉的浏览器自动化工具。与传统的代码依赖型浏览器自动化流程相比,它无需编写代码,并且在应对网站布局变动时,具备更高的适应能力。

GitHub 地址→
github.com/Skyvern-AI/skyvern

2. HelloGitHub 热评

在此章节中,我们将为大家介绍本周 HelloGitHub 网站上的热门开源项目,我们不仅希望您能从中收获灵感和知识,更渴望“听”到您的声音。希望您与我们分享
使用这些开源项目的亲身体验和评价
,用最真实反馈为开源项目的作者注入动力。

此外,HelloGitHub 网站的「用户贡献排行榜」功能已正式上线!

您的每一次分享和评论都将转化为
贡献值
,并在排行榜上展示您对开源的热情与贡献。您可能认为此举微不足道,但对于开源项目的作者来说,这是莫大的支持和鼓励。

勿以恶小而为之,勿以善小而不为。

2.1 可爱的网站计数器:Moe-Counter

主语言:JavaScript

该项目是一个用于统计页面访问人数的计数器。它不仅简单易用,还提供多种可爱风格的主题,用户可根据个人喜好进行选择。

项目详情→
hellogithub.com/repository/ed741b376efe46789ce9bb140ac19a52

2.2 开源的 Android 虚拟定位应用:GoGoGo

主语言:Java

该项目是一个基于 Android 调试 API 和百度地图实现的虚拟定位工具,无需 ROOT 权限即可修改地理位置。它支持位置搜索和手动输入坐标,并提供了一个可自由移动的摇杆来模拟位移。

项目详情→
hellogithub.com/repository/7cf3e8a7307b4767abd6ca2c98ae438f

3. 结尾

以上就是本期「GitHub 热点速览」的全部内容,希望你能够在这里找到自己感兴趣的开源项目,如果你有其他好玩、有趣的 GitHub 开源项目想要分享,欢迎来
HelloGitHub
与我们交流和讨论。

往期回顾

开心一刻

刚刚和老婆吵架,气到不行,想离婚
女儿突然站出来劝解道:难道你们就不能打一顿孩子消消气,非要闹离婚吗?
我和老婆同时看向女儿,各自挽起了衣袖
女儿补充道:弟弟那么小,打他,他又不会记仇

开心一刻

需求背景

项目基于
DataX
来实现异源之间的数据离线同步,我对 Datax 进行了一些梳理与改造

异构数据源同步之数据同步 → datax 改造,有点意思
异构数据源同步之数据同步 → datax 再改造,开始触及源码
异构数据源同步之数据同步 → DataX 使用细节
异构数据源数据同步 → 从源码分析 DataX 敏感信息的加解密
异源数据同步 → DataX 为什么要支持 kafka?
异源数据同步 → 如何获取 DataX 已同步数据量?

本以为离线同步告一段落,不会再有新的需求,可打脸来的非常快,产品经理很快找到我,说了如下一段话

昨天我在测试开发环境试用了一下离线同步功能,很好的实现了我提的需求,给你点赞!
但是使用过程中我遇到个情况,有张的表的数据量很大,一开始我没关注其数据量,所以配置了全量同步,启动同步后迟迟没有同步完成,我才意识到表的数据量非常大,一查才知道 2 亿多条数据,我想终止同步却发现没有地方可以进行终止操作
所以需要加个功能:同步中的任务可以进行终止操作

这话术算是被产品经理给玩明白了,先对我进行肯定,然后指出使用中的痛点,针对该痛点提出新的功能,让我一点反驳的余地都没有;作为一个讲道理的开发人员,面对一个很合理的需求,我们还是很乐意接受的,你们说是不是?

需求一接,问题就来了

如何终止同步

思考这个问题之前,我们先来回顾下 DataX 的启动;还记得我们是怎么集成 DataX 的吗,
异构数据源同步之数据同步 → datax 再改造,开始触及源码
中有说明,新增 qsl-datax-hook 模块,该模块中通过命令

Process process = Runtime.getRuntime().exec(realCommand);
realCommand 就是启动 DataX 的 java 命令,类似

java -server -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -Ddatax.home=/datax -classpath /datax/lib/* com.alibaba.datax.core.Engine -mode standalone -jobid -1 -job job.json

来启动 DataX,也就是给 DataX 单独启动一个 java 进程;那么如何停止 DataX,思路是不是就有了?问题是不是就转换成了

如何终止 java 进程

终止进程

如何终止进程,这个我相信你们都会

Linux:kill -9
pid
Win:cmd.exe /c taskkill /PID
pid
/F /T

但这有个前提,需要知道 DataX 的 java 进程的
pid
,而 JDK8 中
Process
的方法如下

Process方法

是没有提供获取 pid 的方法,在不调整 JDK 版本的情况下,我们如何获取 DataX 进程的 pid?不同的操作系统获取方式不一样,我们分别对
Linux

Win
进行实现

  1. Linux

    实现就比较简单了,仅仅基于 JDK 就可以实现

    Field field = process.getClass().getDeclaredField("pid");
    field.setAccessible(true);
    int pid = field.getInt(process);
    

    通过反射获取 process 实现类的成员变量
    pid
    的值;这段代码,你们应该都能看懂吧

  2. Win

    Win 系统下,则需要依赖第三方工具
    oshi

    <dependency>
        <groupId>com.github.oshi</groupId>
        <artifactId>oshi-core</artifactId>
        <version>6.6.5</version>
    </dependency>
    

    获取 pid 实现如下

    Field field = process.getClass().getDeclaredField("handle");
    field.setAccessible(true);
    long handle = field.getLong(process);
    WinNT.HANDLE winntHandle = new WinNT.HANDLE();
    winntHandle.setPointer(Pointer.createConstant(handle));
    int pid = Kernel32.INSTANCE.GetProcessId(winntHandle);
    

    同样用到了反射,还用到了 oshi 提供的方法

合并起来即得到获取 pid 的方法

/**
 * 获取进程ID
 * @param process 进程
 * @return 进程id,-1表示获取失败
 * @author 青石路
 */
public static int getProcessId(Process process) {
    int pid = NULL_PROCESS_ID;
    Field field;
    if (Platform.isWindows()) {
        try {
            field = process.getClass().getDeclaredField("handle");
            field.setAccessible(true);
            long handle = field.getLong(process);
            WinNT.HANDLE winntHandle = new WinNT.HANDLE();
            winntHandle.setPointer(Pointer.createConstant(handle));
            pid = Kernel32.INSTANCE.GetProcessId(winntHandle);
        } catch (Exception e) {
            LOGGER.error("获取进程id失败,异常信息:", e);
        }
    } else if (Platform.isLinux() || Platform.isAIX()) {
        try {
            field = process.getClass().getDeclaredField("pid");
            field.setAccessible(true);
            pid = field.getInt(process);
        } catch (Exception e) {
            LOGGER.error("获取进程id失败,异常信息:", e);
        }
    }
    LOGGER.info("进程id={}", pid);
    return pid;
}

得到的 pid 是不是正确的,我们是不是得验证一下?写个
mainClass

/**
 * mainClass
 * @author 青石路
 */
public class HookMain {

    public static void main(String[] args) throws Exception {
        String command = "";
        if (Platform.isWindows()) {
            command = "ping -n 1000 localhost";
        } else if (Platform.isLinux() || Platform.isAIX()) {
            command = "ping -c 1000 localhost";
        }
        Process process = Runtime.getRuntime().exec(command);
        int processId = ProcessUtil.getProcessId(process);
        System.out.println("ping 进程id = " + processId);
        new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream(), System.getProperty("sun.jnu.encoding")))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }).start();
    }
}

利用 maven 打包成可执行 jar 包

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>com.qsl.hook.HookMain</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/lib</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

然后执行 jar

java -jar qsl-datax-hook-0.0.1-SNAPSHOT.jar

我们来看下输出结果

  1. Linux

    jar 输出日志如下


    Linux_输出

    我们 ps 下进程

    ps -ef|grep ping
    

    Linux_验证
  2. Win

    jar 输出日志如下


    win_输出

    我们再看下任务管理器的 ping 进程


    win_验证

可以看出,不管是 Linux 还是 Win,得到的 pid 都是正确的;得到 pid 后,终止进程就简单了

/**
 * 终止进程
 * @param pid 进程的PID
 * @return true:成功,false:失败
 */
public static boolean killProcessByPid(int pid) {
    if (NULL_PROCESS_ID == pid) {
        LOGGER.error("pid[{}]异常", pid);
        return false;
    }
    String command = "kill -9 " + pid;
    boolean result;
    if (Platform.isWindows()) {
        command = "cmd.exe /c taskkill /PID " + pid + " /F /T ";
    }
    Process process  = null;
    try {
        process = Runtime.getRuntime().exec(command);
    } catch (IOException e) {
        LOGGER.error("终止进程[pid={}]异常:", pid, e);
        return false;
    }
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
        //杀掉进程
        String line;
        while ((line = reader.readLine()) != null) {
            LOGGER.info(line);
        }
        result = true;
    } catch (Exception e) {
        LOGGER.error("终止进程[pid={}]异常:", pid, e);
        result = false;
    } finally {
        if (!Objects.isNull(process)) {
            process.destroy();
        }
    }
    return result;
}

完整流程应该是

  1. 使用
    Runtime.getRuntime().exec(java命令)
    启动 DataX,并获取到
    Process

    java 命令指的是启动 DataX 的 java 命令,例如

    java -server -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -Ddatax.home=/datax -classpath /datax/lib/* com.alibaba.datax.core.Engine -mode standalone -jobid -1 -job job.json
    
  2. 通过
    ProcessUtil#getProcessId
    获取 Process 的
    pid
    ,并与同步任务信息绑定进行持久化

    通过任务id 可以查询到对应的 pid

  3. 触发任务
    终止
    ,通过任务id找到对应的 pid,通过
    ProcessUtil#killProcessByPid
    终止进程

    终止了进程也就终止了同步任务

如果
qsl-datax-hook
是单节点,上述处理方案是没有问题的,但生产环境下,qsl-datax-hook 不可能是单节点,肯定是集群部署,那么上述方案就行不通了,为什么呢?我举个例子

假设 qsl-datax-hook 有 2 个节点:A、B,在 A 节点上启动 DataX 同步任务(taskId = 666)并得到对应的 pid = 1488,终止任务 666 的请求被负载均衡到 B 节点,会发生什么情况

  1. B 节点上没有 pid = 1488 进程,那么终止失败,A、B 节点都不受影响
  2. B 节点上有 pid = 1488 进程,这个进程可能是 DataX 同步任务进程,也可能是其他进程,那么这个终止操作就会产生可轻可重的故障了!

然而需要终止的同步任务却还在 A 节点上安然无恙的执行着

所以集群模式下,我们不仅需要将 pid 与任务进行绑定,还需要将任务执行的节点信息也绑定进来,节点信息可以是
节点ID
,也可以是
节点IP
,只要能唯一标识节点就行;具体实现方案,需要结合具体的负载均衡组件来做设计,由负载均衡组件将任务终止请求分发到正确的节点上,而不能采用常规的负载均衡策略进行分发了;因为负载均衡组件很多,所以实现方案没法统一设计,需要你们结合自己的项目去实现,我相信对你们来说很简单

你懂我意思吧_懂

总结

  1. 任务的启动方式不同,终止方式也会有所不同,如何优雅的终止,是我们需要考虑的重点
  2. 直接杀进程的方式,简单粗暴,但不够优雅,一旦错杀,问题可大可小,如果有其他方式,不建议选择该方式
  3. 适用单节点的终止方式不一定适用于集群,大家设计方案的时候一定要做全方位的考虑
  4. 示例代码:
    qsl-datax-hook

Avalonia是什么?

Avalonia是一个强大的框架,使开发人员能够使用.NET创建跨平台应用程序。它使用自己的渲染引擎绘制UI控件,确保在Windows、macOS、Linux、Android、iOS和WebAssembly等不同平台上具有一致的外观和行为。这意味着开发人员可以共享他们的UI代码,并在不同的目标平台上保持统一的外观和感觉。

MIT 协议的宽松与便利

MIT 协议(The MIT License)是一种简洁且宽松的开源软件许可协议。它允许使用者自由使用、复制、修改、合并、发布、分发、再许可和 / 或销售软件副本。使用者在软件和软件的所有副本中都必须包含版权声明和许可声明。MIT 协议对使用者的限制很少,基本上赋予了使用者极大的自由,适用于各种开源项目,鼓励代码的共享和重用,促进软件技术的快速发展。

Dotnet和Avalonia都是MIT协议,相关的代码地址是:

Semi.Avalonia和Ursa.Avalonia

(一)Semi.Avalonia - 主题风格的魅力实现

Semi.Avalonia,这是以 MIT 协议开源的 Avalonia UI 框架下的 Semi Design 主题风格的精妙呈现。它为应用程序带来独特的视觉风格,如同一幅精美的画卷,为用户界面增添了丰富的色彩和质感。

其仓库地址为:
https://github.com/irihitech/Semi.Avalonia

(二)Ursa.Avalonia - 自定义控件的创新力量

搭配同样遵循 MIT 协议的Ursa.Avalonia自定义控件库,更是如虎添翼。它们携手为开发者缔造全新的视觉与功能体验,仿佛为开发之旅开启了一扇通往无限可能的大门。

仓库地址:
https://github.com/irihitech/Ursa.Avalonia

在信创及国产操作系统领域表现

值得一提的是,这两个库在信创及国产操作系统领域表现出色,已完成与龙芯 3A6000 和龙架构(LoongArch™)的兼容互认证,这是自主可控和国产化技术推进的重要成果。

下面信息引用来自微信公众号【铱泓科技】8月2号的文章 《
Ursa与Semi正式完成龙架构兼容互认证
》:

大熊Ursa和Semi两大Avalonia控件集已经完成与龙芯3A6000和龙架构(LoongArch™)的兼容互认证。这一重要的里程碑标志着我们在推进自主可控和国产化技术方面取得了新的进展。

控件部分截图

控件虽各有特色,但都展现出独特的魅力。简单截取几张图,让您一窥其貌:

Semi.Avalonia主题库一览:

Semi.Avalonia截图

Ursa.Avalonia自定义控件库一览:

Ursa.Avalonia

实际案例分享

站长公司项目使用了该控件,虽不便截图展示,但可参考站长使用 Avalonia UI 搭配该主题及控件库编写的工具CodeWF.Toolbox:

仓库:
https://github.com/dotnet9/CodeWF.Toolbox

该小工具使用Avalonia+Prism 8模块化开发,AOT 发布后的文件组织结构:

其具备黑白主题,营造出不同的视觉氛围:

还实现了国际化功能,为全球用户提供便捷体验:

国际化

同时,包含实用的 Json 美化工具和 YAML 转 Json 工具,分别如下图所示:

Json 美化工具

YAML转Json工具

使用经验分享

  1. 官方文档
  1. 源码阅读

首先,克隆控件仓库(上面给出了地址),依据 Readme 及 Demo 运行效果进行查找。例如,若觉得 Button 的 Warning 效果出色:

可使用 VS Code 或 VS 打开仓库:

  1. 展开Semi.Avalonia.Demo
  2. 找到Pages目录,打开ButtonDemo.axaml
  3. 根据界面关键字Solid、Waring找到需要的样式

如此,便能轻松驾驭这些优秀的控件,为开发工作增添效率与魅力。希望本文能为您在 Avalonia 开源控件库的探索之旅中提供有益的指引和启发,让您在开发道路上创造出更加精彩的应用程序。

需求介绍

很多用户使用了
SpreadJS
的数据填报功能。大致用法为:设计模板,填充数据源。在这个过程中,可能会出现模板中设置了公式,而在数据源填充时,公式没有携带下来的问题。

比如我们定义一个模板:

接下来使用setDataSpurce()填充数据源,填充后,发现只有一行有公式值,其他行无数据

那么,我们该做一些什么操作呢?或者有哪些方案呢?
目前有四种方案,分别
fillAuto

copyTo

clipboardPaste

setColumnDataFormula

我们分别看一下这四种方案的具体使用用法及性能。

我们先获取下table区域,定义baseRow ,baseCol

 let row = table.range().row
        let baseRow = row + 1;
        let baseCol = 4
        let rowCount = 0

然后在setDataSource后,修改rowCount值

        document.getElementById('btn6').addEventListener('click', function () {
            sheet.setDataSource(new GC.Spread.Sheets.Bindings.CellBindingSource(data2))
            rowCount = table.range().rowCount
        })

一、方案

1、fillAuto

let start = new GC.Spread.Sheets.Range(baseRow, baseCol, 1, 1)
            let end = new GC.Spread.Sheets.Range(baseRow, baseCol, rowCount - 1, 1)
            sheet.fillAuto(start, end, {
                fillType: GC.Spread.Sheets.Fill.FillType.auto,
                series: 0,
                direction: GC.Spread.Sheets.Fill.FillDirection.down
            });

2、copyTo

 for (let r = baseRow + 1; r < row + rowCount; r++) {
                sheet.copyTo(baseRow, baseCol, r, baseCol, 1, 1, GC.Spread.Sheets.CopyToOptions.formula)
            }

3、clipboardPaste

 let fromRanges = [new GC.Spread.Sheets.Range(baseRow, baseCol, 1, 1)]
            let pastedRanges = [new GC.Spread.Sheets.Range(baseRow + 1, baseCol, rowCount - 2, 1)]
            spread.commandManager().execute({
                cmd: "clipboardPaste",
                sheetName: sheet.name(),
                fromSheet: sheet,
                fromRanges,
                pastedRanges,
                isCutting: false,
                clipboardText: "",
                pasteOption: GC.Spread.Sheets.ClipboardPasteOptions.formulas
            });

4、setColumnDataFormula

table.setColumnDataFormula(baseCol, sheet.getFormula(baseRow, baseCol));

上述四种方案均能实现公式填充,结果如下:

二、性能对比

1、100条
那么,我们接下来看下这四种方案的性能吧,首先我们设置100条数据源,

 let sales = [], dataLength = 100
        for (let i = 0; i < dataLength; i++) {
            sales.push({orderDate: '1/6/2013', item: 'book', units: '95', quantity: 1.99})
        }
        sheet.setRowCount(dataLength + 1)

然后设置一个按钮,在每一种方案执行后,用来清除数据。

  document.getElementById('btn5').addEventListener('click', function () {
            sheet.clear(2, 4, sheet.getRowCount(), 1,GC.Spread.Sheets.SheetArea.viewport,GC.Spread.Sheets.StorageType.data);
        })

结果如下:

100条数据的情况下,四种方案的性能都相差不大。

2、1000条
接下来,我们设置10000条数据,将dataLength 改为1000
结果如下:

3、10000条
接下来,我们设置10000条数据,将dataLength 改为10000
结果如下:

观察上图,我们发现setColumnDataFormula与clipboardPaste性能较好,而fillAuto性能最差。

4、10000条
我们接下来将数据量改为
10万
条数据,观察setColumnDataFormula与clipboardPaste性能

汇总以下这些数据:

总结

由上面的内容可以看出,在数据量不大的情况下,四种方式差不多,但在数据量较大的情况下,建议使用setColumnDataFormula方式填充公式。

扩展链接:

【干货放送】财务报表勾稽分析要点,一文读尽!

为什么你的财务报表不出色?推荐你了解这四个设计要点和!

纯前端类 Excel 表格控件在报表勾稽分析领域的应用场景解析

本文是对公开论文的核心提炼,而非直接翻译,旨在进行学术交流。如有任何侵权问题,请及时联系号主以便删除。

来源:晓飞的算法工程笔记 公众号,转载请注明出处

论文: Can OOD Object Detectors Learn from Foundation Models?

创新点


  • 研究并发掘在大规模开放集数据上训练的文本到图像生成模型在目标检测任务中合成
    OOD
    对象的潜力。
  • 引入一种自动化的数据整理过程以获取可控的、带注释的场景级合成
    OOD
    图像,用于
    OOD
    目标检测。该过程利用大型语言模型(
    LLMs
    )进行新对象发现,并使用视觉基础模型进行数据注释和过滤。
  • 发现在保持
    ID
    /
    OOD
    图像上下文的一致性以及获得更准确的
    OOD
    注释边界框,对合成数据在
    OOD
    目标检测中的有效性至关重要。
  • 在多个基准上的全面实验证明了该方法的有效性,在使用最少合成数据的情况下显著超越了现有的最先进方法。

内容概述


分布外(
OOD
)目标检测是一项具有挑战性的任务,因为缺乏开放集的
OOD
数据。受到近期在文本到图像生成模型方面的进展的启发,例如
Stable Diffusion
,论文研究了基于大规模开放集数据训练的生成模型合成
OOD
样本的潜力,从而增强
OOD
目标检测。

论文提出了
SyncOOD
,这是一种简单的数据策划方法。该方法利用大型基础模型的能力,从文本到图像的生成模型中自动提取有意义的
OOD
数据,使得模型能够访问包含在现成基础模型中的开放世界知识。合成的
OOD
样本随后被用于增强一个轻量级、即插即用的
OOD
检测器的训练,从而有效地优化了在分布内(
ID
)/
OOD
的决策边界。

在多个基准上进行的广泛实验表明,
SyncOOD
在性能上显著优于现有方法,凭借最少的合成数据使用,建立了新的最先进性能。

SyncOOD


异常合成管道包括两个部分:

  1. 合成一组有效的照片真实感场景级
    OOD
    图像
    \(\textbf{x}^{\text{edit}}\)
    ,记为
    \(\mathcal{D}_{\text{edit}} = \left\{(\textbf{x}^{\text{edit}}, \textbf{b}^{\text{edit}})\right\}\)
    ,该图像包含新颖对象及其相应的标注框
    \(\textbf{b}^{\text{edit}}\)
    ,这一过程基于从
    \(\mathcal{D}_{\text{id}}\)
    进行全自动化的区域级编辑。
  2. 选择和使用高效的合成数据,为训练
    OOD
    对象检测器提供伪
    OOD
    监督,与训练集中
    ID
    样本一起使用。

合成新语义对象

  • 从分布内对象想象新概念对象

如图 (a) 所示,基于训练集
\(\mathcal{D}_{\text{id}}\)
中的
ID
标签
\(\mathcal{Y}_{\text{id}}\)
,利用大型语言模型
LLM
(如
GPT-4
)广泛的知识和推理能力来检查视觉相似度和上下文兼容性,为每个
ID
对象标签设想了一组新颖对象,记为
\(\mathcal{Y}_{\text{novel}}\)
,同时保持了想象对象与
ID
对象之间的语义可分性。这能够关联
ID
对象,并通过使用包含上下文示例的提示来促进可能的新对象的概念化,以替换现有的
ID
对象。

  • 在指定区域内编辑对象

为了生成包含新概念
\(y_j \in \textbf{y}^{\text{novel}}_i\)
的新图像,选择替换现有图像中标签为
\(y_i^{\text{id}}\)
的现有
ID
对象,而不是寻找新的位置或从头生成图像。通过这样做,可以确保上下文兼容性,并消除场景上下文中的干扰,因为上下文得以保留。

如图 (b) 所示,使用稳定扩散修复(
Stable-Diffusion-Inpainting
)对
ID
图像进行区域级编辑,得到包含新对象的编辑图像
\(\textbf{x}^{\text{edit}}\)
为:

\[\begin{equation}
\textbf{x}^{\text{edit}}=\text{SDI}(\textbf{x}^{\text{id}},\textbf{b}^{\text{id}},\textbf{y}^{\text{novel}}).
\label{eq:sdi}
\end{equation}
\]

  • 细化新对象的注释框

由于扩散模型中的随机性,编辑对象的属性,如质量、体积和定位,可能与原始对象框不匹配。为了解决这个问题,如图 (c) 所示,设计一个基于
SAM
的高效、有效的细化器,以获取新对象的精确边界框。

使用从
\(\textbf{b}^{\text{id}}\)
扩展出的填充区域作为提示,并使用
SAM
输出该区域中新对象的最高置信度实例掩码
\(\textbf{m}^{\text{SAM}}\)

\[\begin{equation}
\textbf{m}^{\text{SAM}}=\text{SAM}(\textbf{x}^{\text{edit}};\text{padding}(\textbf{b}^{\text{id}}, e)),
\label{eq:sam}
\end{equation}
\]

将获得的掩码
\(\textbf{m}^{\text{SAM}}\)
转换为边界框
\(\textbf{b}^{\text{SAM}}\)
,并计算
\(\textbf{b}^{\text{SAM}}\)
与相应的
\(\textbf{b}^{\text{id}}\)
之间的交并比(
IoU
),以过滤出在尺度上变化较大的新对象:

\[\begin{equation}
\left\{\textbf{b}^{\text{edit}}\right\}=\left\{\left.\textbf{b}^{\text{SAM}}\middle|\right.\text{IoU}(\textbf{b}^{\text{SAM}},\textbf{b}^{\text{id}})>\gamma\right\},
\label{eq:iou}
\end{equation}
\]

发掘难OOD样本以及模型训练

  • Mining Hard OOD Objects with High Visual Similarities for Training

最可能被目标检测器混淆为原始
ID
对象的新对象视为最有效。因此,基于预训练目标检测器的潜在空间中的成对相似性,寻找最容易被混淆为
ID
的合成
OOD
样本。

对于一个现成的目标检测器
\(\mathcal{F}_\text{det}\)
,为每一对提取潜在特征
\(\textbf{z}^{\text{edit}}\)

\(\textbf{z}^{\text{id}}\)
,根据相似性进行过滤,以提供伪
OOD
监督:

\[\begin{equation}
\textbf{z}^{\text{edit}},\textbf{z}^{\text{id}}=\mathcal{F}_\text{det}(\textbf{b}^{\text{edit}};\textbf{x}^{\text{edit}}),\mathcal{F}_\text{det}(\textbf{b}^{\text{id}};\textbf{x}^{\text{id}}).
\label{eq:extract}
\end{equation}
\]

\[\begin{equation}
\left\{\textbf{z}^{\text{ood}}\right\}=\left\{\left.\textbf{z}^{\text{edit}}\middle|\right.\epsilon_{\textit{low}}<\text{sim}(\textbf{z}^{\text{edit}},\textbf{z}^{\text{id}})<\epsilon_{\textit{up}}\right\},
\label{eq:sim}
\end{equation}
\]

  • 通过合成样本优化
    ID
    /
    OOD
    决策边界

一旦获得了
ID
和合成
OOD
对象,使用一个轻量级的多层感知器(
MLP

\(\mathcal{F}_\text{ood}\)
,作为经过二分类损失优化的
OOD
检测器参与训练:

\[\begin{equation}
\mathcal{L}_\text{ood}=\mathbb{E} _{\textbf{z}\sim\textbf{z}^{\text{id}}}\left[-\log\frac{1}{1 + \exp^{-\mathcal{F}_\text{ood}(\textbf{z})}}\right]+\mathbb{E} _{\textbf{z}\sim\textbf{z}^{\text{ood}}}\left[-\log\frac{\exp^{-\mathcal{F}_\text{ood}(\textbf{z})}}{1+\exp^{-\mathcal{F}_\text{ood}(\textbf{z})}} \right].
\label{eq:optim}
\end{equation}
\]

主要实验




如果本文对你有帮助,麻烦点个赞或在看呗~
更多内容请关注 微信公众号【晓飞的算法工程笔记】

work-life balance.