2024年8月

前言

本文主要介绍卷积神经网络的使用的下半部分。
另外,上篇文章增加了一点代码注释,主要是解释(w-f+2p)/s+1这个公式的使用。
所以,要是这篇文章的代码看不太懂,可以翻一下上篇文章。

代码实现

之前,我们已经学习了概念,在结合我们以前学习的知识,我们可以直接阅读下面代码了。
代码里使用了,dataset.CIFAR10数据集。
CIFAR-10 数据集由 60000 张 32x32 彩色图像组成,共分为 10 个不同的类别,分别是飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车。
每个类别包含 6000 张图像,其中 50000 张用于训练,10000 张用于测试。

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn.functional as F #nn不好使时,在这里找激活函数
# device config
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# hyper parameters
input_size = 784  # 28x28
hidden_size = 100
num_classes = 10
batch_size = 100
learning_rate = 0.001
num_epochs = 2

 
transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

train_dataset = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform)

test_dataset = torchvision.datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform)
 
train_loader = torch.utils. data.DataLoader(
    dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
    dataset=test_dataset, batch_size=batch_size, shuffle=False)
print('每份100个,被分成多少份:', len(test_loader))


classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')


class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet,self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16*5*5, 120) #这个在forward里解释
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x))) #这里x已经变成 torch.Size([4, 16, 5, 5])
        # print("两次卷积两次池化后的x.shape:",x.shape)
        x = x.view(-1,16*5*5)#这里的16*5*5就是x的后面3个维度相乘
        x = F.relu(self.fc1(x)) #fc1定义时,inputx已经是6*5*5了
        x = F.relu(self.fc2(x))
        x= self.fc3(x)
        return x


model = ConvNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)


n_total_steps = len(train_loader)

for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):
        # origin shape:[4,3,32,32]=4,3,1024
        # input layer: 3 input channels, 6 output channels, 5 kernel size
        images = images.to(device)
        labels = labels.to(device)
        # Forward pass
        outputs = model(images)
        loss = criterion(outputs, labels)

        # Backward and optimize
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if (i+1) % 2000 == 0:
            print(
                f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{n_total_steps}], Loss: {loss.item():.4f}')
print('Finished Training')


# test
with torch.no_grad():
    n_correct = 0
    n_samples = 0
    n_class_correct = [0 for i in range(10)] #生成 10 个 0 的列表
    n_class_samples = [0 for i in range(10)]
    for images, labels in test_loader:
        images = images.to(device)
        labels = labels.to(device)
        print('test-images.shape:', images.shape)
        outputs = model(images)
        # max returns(value ,index)
        _, predicted = torch.max(outputs, 1)
        n_samples += labels.size(0)
        n_correct += (predicted == labels).sum().item()
        for i in range(batch_size):
            label = labels[i]
            # print("label:",label) #这里存的是 0~9的数字 输出就是这样的 label: tensor(2) predicted[i]也是这样的数
            pred = predicted[i]
            if (label == pred):
                n_class_correct[label] += 1
            n_class_samples[label] += 1
    acc = 100.0*n_correct/n_samples  # 计算正确率
    print(f'accuracy ={acc}')
    
    for i in range(10):
        acc = 100.0*n_class_correct[i]/n_class_samples[i]
        print(f'Accuracy of {classes[i]}: {acc} %')

运行结果如下:

accuracy =10.26
Accuracy of plane: 0.0 %
Accuracy of car: 0.0 %
Accuracy of bird: 0.0 %
Accuracy of cat: 0.0 %
Accuracy of deer: 0.0 %
Accuracy of dog: 0.0 %
Accuracy of frog: 0.0 %
Accuracy of horse: 0.0 %
Accuracy of ship: 89.6 %
Accuracy of truck: 13.0 %

这是因为我设置的num_epochs=2,也就是循环的次数太低,所以结果的精确度就很低。
我们只要增加epochs的值,就能提高精确度了。


传送门:
零基础学习人工智能—Python—Pytorch学习—全集

这样我们卷积神经网络就学完了。


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18381036

image

Node configuration(节点配置)

节点配置允许您为集群中的各个节点自定义和优化设置。它被分为几个部分:

一个带注释的例子可在此处找到:
quickwit.yaml

Common configuration(常规配置)

属性 描述 环境变量 默认值
version 配置文件版本。
0.7
是唯一可用的值,并且与
0.5

0.4
版本向后兼容。
cluster_id 节点将加入的集群的唯一标识符。共享同一网络的集群应使用不同的集群ID。 QW_CLUSTER_ID quickwit-default-cluster
node_id 节点的唯一标识符。它必须与集群中其他节点的标识符不同。如果未设置,则默认为实例的短主机名。 QW_NODE_ID 短主机名
enabled_services 已启用的服务(控制平面、索引器、清理程序、元存储、搜索器) QW_ENABLED_SERVICES 所有服务
listen_address Quickwit 服务绑定的 IP 地址或主机名,用于启动 REST 和 gRPC 服务器以及连接此节点到其他节点。默认情况下,Quickwit 绑定到 127.0.0.1(本地主机)。当尝试形成集群时,此默认值无效。 QW_LISTEN_ADDRESS 127.0.0.1
advertise_address 节点广播的 IP 地址,即对等节点用于连接到该节点进行远程过程调用的 IP 地址。 QW_ADVERTISE_ADDRESS listen_address
gossip_listen_port 监听 Gossip 集群成员服务(UDP)的端口。 QW_GOSSIP_LISTEN_PORT rest.listen_port
grpc_listen_port gRPC 服务监听流量的端口。 QW_GRPC_LISTEN_PORT rest.listen_port + 1
peer_seeds 用于引导集群并发现完整节点集的 IP 地址或主机名列表。此列表可以包含当前节点的地址,并不需要详尽无遗。如果 peer_seeds 列表中包含主机名,Quickwit 将通过每分钟查询 DNS 来解析它。例如,在 Kubernetes 中,最好将其设置为
无头服务
QW_PEER_SEEDS
data_dir 用于持久化数据(临时数据、用于缓存目的的切片)的目录路径。这主要用于索引操作。 QW_DATA_DIR ./qwdata
metastore_uri 元存储 URI。可以是本地目录或
s3://my-bucket/indexes

postgres://username:password@localhost:5432/metastore

了解更多关于元存储配置的信息
QW_METASTORE_URI {data_dir}/indexes
default_index_root_uri 定义存储索引数据(切片)位置的默认索引根 URI。索引 URI 的构建遵循以下模式:
{default_index_root_uri}/{index-id}
QW_DEFAULT_INDEX_ROOT_URI {data_dir}/indexes
仅环境变量 Quickwit 的日志级别。可以是直接的日志级别,或者是由逗号分隔的
module_name=level
列表。
RUST_LOG info

REST configuration(REST 配置)

此部分包含 REST API 的配置选项。

属性 描述 环境变量 默认值
listen_port REST API 监听 HTTP 流量的端口。 QW_REST_LISTEN_PORT 7280
cors_allow_origins 配置允许访问 API 的 CORS 来源。
了解更多
extra_headers 头名称和值的列表

Configuring CORS (配置跨源资源共享)

CORS(跨源资源共享)描述了哪些地址或来源可以从浏览器访问 REST API。
默认情况下,不允许跨源共享资源。

可以在
cors_allow_origins
参数中指定通配符、单一来源或多个来源:

REST 配置示例:

rest:
  listen_port: 1789
  extra_headers:
    x-header-1: header-value-1
    x-header-2: header-value-2
  cors_allow_origins: '*'

#   cors_allow_origins: https://my-hdfs-logs.domain.com   # Optionally we can specify one domain
#   cors_allow_origins:                                   # Or allow multiple origins
#     - https://my-hdfs-logs.domain.com
#     - https://my-hdfs.other-domain.com

gRPC configuration(gRPC 配置)

此部分包含用于节点间内部通信的 gRPC 服务和客户端的配置选项。

属性 描述 环境变量 默认值
max_message_size 内部 gRPC 客户端和服务之间交换的消息的最大大小(字节)。 20 MiB

gRPC 配置示例:

grpc:
  max_message_size: 30 MiB

我们建议只有在遇到以下错误时才更改 20 MiB 的默认值:
Error, message length too large: found 24732228 bytes, the limit is: 20971520 bytes.(错误,消息长度过大:找到 24732228 字节,限制是:20971520 字节。)
在这种情况下,请逐步增加
max_message_size
,每次增加 10 MiB,直到问题消失。这是一个临时解决方案:Quickwit 的下一个版本 0.8 将完全依赖于 gRPC 流式传输端点,并能处理任意长度的消息。

Storage configuration(存储配置)

请参阅专门的
存储配置
页面,了解如何为各种存储提供商配置 Quickwit 的更多信息。

这里还有一些如何使用 Amazon S3 或 Alibaba OSS 配置 Quickwit 的最小示例:

AWS_ACCESS_KEY_ID=<your access key ID>
AWS_SECRET_ACCESS_KEY=<your secret access key>

Amazon S3

storage:
  s3:
    region: us-east-1

Alibaba

storage:
  s3:
    region: us-east-1
    endpoint: https://oss-us-east-1.aliyuncs.com

Metastore configuration(元存储配置)

此部分可能包含每个可用元存储实现的一个配置子部分。每个实现的具体配置参数可能会有所不同。目前可用的元存储实现包括:

  • File-backed
  • PostgreSQL

File-backed metastore configuration(文件型元存储配置)

文件支持型元存储没有节点级别的配置。您可以在
索引级别
配置轮询间隔。

PostgreSQL metastore configuration(PostgreSQL 元存储配置)

属性 描述 默认值
min_connections 池中始终维护的最小连接数。 0
max_connections 池中维护的最大连接数。 10
acquire_connection_timeout 在放弃查询之前等待可用连接的最大时间。 10s
idle_connection_timeout 关闭单个连接前的最大空闲持续时间。 10min
max_connection_lifetime 单个连接的最大生命周期。 30min

PostgreSQL 元存储配置的 YAML 格式示例:

metastore:
  postgres:
    min_connections: 10
    max_connections: 50
    acquire_connection_timeout: 30s
    idle_connection_timeout: 1h
    max_connection_lifetime: 1d

Indexer configuration(索引器配置)

此部分包含索引器的配置选项。分片存储在
索引文档
中有详细说明。

属性 描述 默认值
split_store_max_num_bytes 分片存储中允许的最大字节数。 100G
split_store_max_num_splits 分片存储中允许的最大文件数。 1000
max_concurrent_split_uploads 节点上允许的最大并发分片上传数。 12
merge_concurrency 节点上可以同时执行的最大合并操作数。 (2 x 可用线程数) / 3
enable_otlp_endpoint 如果为真,则启用通过 OpenTelemetry 协议 (OTLP) 接收日志和跟踪的 OpenTelemetry 导出端点。 false
cpu_capacity 控制平面使用的咨询参数。值可以用线程表示(例如
2
),也可以用 millicpus 表示(例如
2000m
)。控制平面将尝试根据索引器声明的 CPU 容量,在不同节点上按比例调度索引管道。它不是作为限制使用。无论集群是否有足够的容量,所有管道都将被调度。当负载远低于
cpu_capacity
时,控制平面不会试图平均分配工作。需要在所有索引器节点上均衡负载的用户可以将
cpu_capacity
设置为一个任意低的值,只要它与可用线程数成比例即可。
可用线程数

示例:

indexer:
  split_store_max_num_bytes: 100G
  split_store_max_num_splits: 1000
  max_concurrent_split_uploads: 12
  enable_otlp_endpoint: true

Ingest API configuration(Ingest API 配置)

属性 描述 默认值
max_queue_memory_usage Ingest 队列在内存中的最大大小(字节)。 2GiB
max_queue_disk_usage Ingest 队列占用的最大磁盘空间(字节)。最小大小至少为
256M
并且至少为
max_queue_memory_usage
4GiB

示例:

ingest_api:
  max_queue_memory_usage: 2GiB
  max_queue_disk_usage: 4GiB

Searcher configuration(搜索器配置)

此部分包含搜索器的配置选项。

属性 描述 默认值
aggregation_memory_limit 控制聚合阶段前可以使用的最大内存量。此限制适用于每个请求和单个叶查询(叶查询是指并发查询一个或多个分片)。它用于防止聚合阶段中过度使用内存,这可能导致性能下降或崩溃。由于它是针对每个请求的,因此并发请求可能会超过此限制。 500M
aggregation_bucket_limit 确定返回给客户端的最大桶数。 65000
fast_field_cache_capacity 搜索器上的快速字段内存缓存容量。如果您按日期过滤、运行聚合、范围查询,或者使用搜索流 API,甚至进行追踪,可能值得增加此参数。以
quickwit_cache_fastfields_cache
开头的
指标
可帮助您在设置此值时做出明智的选择。
1G
split_footer_cache_capacity 搜索器上的分片尾部内存缓存容量(本质上是热缓存)。 500M
partial_request_cache_capacity 搜索器上的部分请求内存缓存容量。为请求缓存中间状态,可能使后续请求更快。可以通过将其大小设置为
0
来禁用它。
64M
max_num_concurrent_split_searches 在搜索器上运行的最大并发分片搜索请求数。 100
max_num_concurrent_split_streams 在搜索器上运行的最大并发分片流请求数。 100
split_cache 下面定义的搜索器分片缓存配置选项。如果未指定,则禁用缓存。

Searcher split cache configuration(搜索器分片缓存配置)

此部分包含磁盘上搜索器分片缓存的配置选项。

属性 描述 默认值
max_num_bytes 分片缓存中允许的最大磁盘大小(字节)。可能会被单个分片的大小超过。
max_num_splits 分片缓存中允许的最大分片数。 10000
num_concurrent_downloads 最大并发下载分片数。 1

示例:

searcher:
  fast_field_cache_capacity: 1G
  split_footer_cache_capacity: 500M
  partial_request_cache_capacity: 64M
  split_cache:
    max_num_bytes: 1G
    max_num_splits: 10000
    num_concurrent_downloads: 1

Jaeger configuration(Jaeger 配置)

属性 描述 默认值
enable_endpoint 如果为真,则启用允许 Jaeger 查询服务连接并检索跟踪的 gRPC 端点。 false

示例:

searcher:
  enable_endpoint: true

Using environment variables in the configuration(在配置中使用环境变量)

您可以在配置文件中使用环境变量引用,以设置在部署期间需要可配置的值。为此,请使用:

${VAR_NAME}

其中
VAR_NAME
是环境变量的名称。

每个变量引用在启动时都会被环境变量的值替换。替换过程区分大小写,并且在解析配置文件之前发生。除非您指定了默认值或自定义错误文本,否则引用未定义的变量会抛出错误。

为了指定默认值,请使用:

${VAR_NAME:-default_value}

其中
default_value
是如果环境变量未设置时要使用的值。

<config_field>: ${VAR_NAME}
or
<config_field>: ${VAR_NAME:-default value}

例如:

export QW_LISTEN_ADDRESS=0.0.0.0
# config.yaml
version: 0.7
cluster_id: quickwit-cluster
node_id: my-unique-node-id
listen_address: ${QW_LISTEN_ADDRESS}
rest:
  listen_port: ${QW_LISTEN_PORT:-1111}

将被 Quickwit 理解为:

version: 0.7
cluster_id: quickwit-cluster
node_id: my-unique-node-id
listen_address: 0.0.0.0
rest:
  listen_port: 1111

Storage configuration(存储配置)

Supported Storage Providers(支持的存储提供商)

Quickwit 目前支持四种类型的存储提供商:

  • Amazon S3 和 S3 兼容(Garage、MinIO 等)
  • Azure Blob 存储
  • 本地文件存储*
  • Google Cloud Storage(原生 API)

Storage URIs(存储 URI)

存储 URI 通过 URI “协议” 或 “方案” 来标识不同的存储提供商。Quickwit 支持以下存储 URI 协议:

  • s3://
    用于 Amazon S3 和 S3 兼容
  • azure://
    用于 Azure Blob 存储
  • file://
    用于本地文件系统
  • gs://
    用于 Google Cloud Storage

通常情况下,您可以在任何直观地期望文件路径的地方使用存储 URI 或文件路径。例如:

  • 设置索引的
    index_uri
    以指定存储提供商和位置;
  • 在节点配置中设置
    metastore_uri
    以建立基于文件的元数据存储;
  • 作为命令行参数传递文件路径。

Local file storage URIs(本地文件存储 URI)

Quickwit 将常规文件路径解释为本地文件系统 URI。允许使用相对文件路径,并且它们相对于当前工作目录(CWD)进行解析。可以使用
~
作为快捷方式来引用用户的主目录。以下是有效的本地文件系统 URI:

- /var/quickwit
- file:///var/quickwit
- /home/quickwit/data
- ~/data
- ./quickwit

当使用
file://
协议时,需要第三个
/
来表示绝对路径。例如,URI
file://home/quickwit/
被解释为
./home/quickwit

Storage configuration(存储配置)

此部分包含针对每个存储提供商的一个配置子部分。如果未显式设置存储配置参数,则 Quickwit 依赖于存储提供商 SDK(
Azure SDK for Rust

AWS SDK for Rust
)提供的默认值。

S3 storage configuration(S3 存储配置)

属性 描述 默认值
flavor 可选的存储风味。可用的风味包括
digital_ocean

garage

gcs

minio
access_key_id AWS 访问密钥 ID。
secret_access_key AWS 密钥访问密钥。
region 发送请求的 AWS 区域。 us-east-1
(SDK 默认值)
endpoint 与 S3 兼容提供商一起使用的自定义端点。 SDK 默认值
force_path_style_access 禁用
虚拟主机风格
请求。某些 S3 兼容提供商(Ceph、MinIO)要求使用。
false
disable_multi_object_delete 禁用
多对象删除
请求。某些 S3 兼容提供商(GCS)要求使用。
false
disable_multipart_upload 禁用
多部分上传
对象。某些 S3 兼容提供商(GCS)要求使用。
false

将凭证硬编码到配置文件中是不安全的,强烈不建议这样做。优先考虑您的存储后端可能提供的替代认证方法。

Environment variables(环境变量)
环境变量 描述
QW_S3_ENDPOINT 自定义 S3 端点。
QW_S3_MAX_CONCURRENCY 限制对 S3 的并发请求数量。
Storage flavors(存储风味)

Storage flavors 确保 Quickwit 通过自动配置适当的设置与偏离 S3 API 的存储提供商正确工作。可用的风味包括:

  • digital_ocean
  • garage
  • gcs
  • minio

Digital Ocean

Digital Ocean flavor (
digital_ocean
) 强制使用路径风格访问,并关闭多对象删除请求。

Garage flavor

Garage flavor (
garage
) 覆盖
region
参数为
garage
并强制使用路径风格访问。

Google Cloud Storage

Google Cloud Storage flavor (
gcs
) 关闭多对象删除请求和多部分上传。

MinIO flavor

MinIO flavor (
minio
) 强制使用路径风格访问。

Google Cloud Storage 的存储配置 YAML 格式示例:

storage:
  s3:
    flavor: gcs
    region: us-east1
    endpoint: https://storage.googleapis.com

Azure storage configuration(Azure 存储配置)

属性 描述 默认值
account Azure 存储账户名称。
access_key Azure 存储账户访问密钥。
Environment variables(环境变量)
环境变量 描述
QW_AZURE_STORAGE_ACCOUNT Azure Blob 存储账户名称。
QW_AZURE_STORAGE_ACCESS_KEY Azure Blob 存储账户访问密钥。

Azure 的存储配置 YAML 格式示例:

storage:
  azure:
    account: your-azure-account-name
    access_key: your-azure-access-key

Storage configuration examples for various object storage providers(各种对象存储提供商的存储配置示例)

Garage

Garage
是一个为自托管定制的开源分布式对象存储服务。

storage:
  s3:
    flavor: garage
    endpoint: http://127.0.0.1:3900

MinIO

MinIO
是一种高性能的对象存储。

storage:
  s3:
    flavor: minio
    endpoint: http://127.0.0.1:9000

注意:
default_index_root_uri
或索引 URI 不包含端点,您应该将其设置为典型的 S3 路径,如
s3://indexes

Index configuration(索引配置)

本页面描述了如何配置一个索引。

除了
index_id
外,索引配置还允许您定义五个项目:

  • index-uri
    :它定义了索引文件应存储的位置。
  • 文档映射
    :它定义了一个文档及其包含的字段如何为给定索引存储和索引。
  • 索引设置
    :它定义了用于分片的时间戳字段,以及一些更高级的参数,如合并策略。
  • 搜索设置
    :它定义了默认搜索字段
    default_search_fields
    ,即如果用户查询没有明确指定字段时 Quickwit 将搜索的字段列表。
  • 保留策略
    :它定义了 Quickwit 应保留已索引数据的时间长度。如果不指定,则数据将永久存储。

配置是在创建索引时设置的,并且可以使用
更新端点

CLI
进行更改。

Config file format(配置文件格式)

索引配置格式为 YAML。当配置文件中缺少某个键时,将使用默认值。
下面是一个适用于 HDFS 日志数据集的完整示例:

version: 0.7 # File format version.

index_id: "hdfs"

index_uri: "s3://my-bucket/hdfs"

doc_mapping:
  mode: lenient
  field_mappings:
    - name: timestamp
      type: datetime
      input_formats:
        - unix_timestamp
      output_format: unix_timestamp_secs
      fast_precision: seconds
      fast: true
    - name: severity_text
      type: text
      tokenizer: raw
      fast:
        - tokenizer: lowercase
    - name: body
      type: text
      tokenizer: default
      record: position
    - name: resource
      type: object
      field_mappings:
        - name: service
          type: text
          tokenizer: raw
  tag_fields: ["resource.service"]
  timestamp_field: timestamp
  index_field_presence: true

search_settings:
  default_search_fields: [severity_text, body]

retention:
  period: 90 days
  schedule: daily

Index ID(索引 ID)

索引 ID 是一个字符串,用于在元存储中唯一标识索引。它只能包含大写或小写的 ASCII 字母、数字、破折号 (
-
) 和下划线 (
_
)。最后,它必须以字母开头,并且至少包含 3 个字符但不超过 255 个字符。

Index uri(索引 URI)

索引 URI 定义了索引文件(也称为切片)应存储的位置。
此参数期望一个
存储 URI

index-uri
参数是可选的。
默认情况下,
index-uri
会通过将
index-id

Quickwit 的配置
中定义的
default_index_root_uri
连接起来计算得出。

在分布式模式下运行 Quickwit 时,文件存储将无法工作。相反,在运行多个搜索节点时,应使用 AWS S3、Azure Blob 存储、Google Cloud Storage(在 S3 互操作模式下)或其他 S3 兼容的存储系统,如 Scaleway Object Storage 和 Garage 作为存储。

Doc mapping(文档映射)

文档映射定义了如何为给定索引存储和索引文档及其包含的字段。文档是一组命名字段的集合,每个字段都有自己的数据类型(文本、字节、日期时间、布尔、i64、u64、f64、IP、JSON)。

变量 描述 默认值
field_mappings 字段映射的集合,每个映射都有其自己的数据类型(文本、二进制、日期时间、布尔、i64、u64、f64、IP、JSON)。 []
mode 定义了 Quickwit 如何处理不在
field_mappings
中的文档字段。特别是,“动态”模式使得可以在无模式的方式下使用 Quickwit。(参见
mode
)
dynamic
dynamic_mapping
mode
设置为
dynamic
时才允许此参数。然后它定义了是否应该对动态映射的字段进行索引、存储等。
(参见
mode
)
tag_fields 已经在
field_mappings
中定义的字段集合*,这些字段的值将作为
tags
元数据的一部分存储。
了解更多关于标签的信息
[]
store_source 原始 JSON 文档是否存储在索引中。 false
timestamp_field 用于将文档分片的日期时间字段*。该字段必须是
datetime
类型。
了解更多关于时间分片的信息
None
partition_key 如果设置,Quickwit 将根据声明为
partition_key
的字段名称将文档路由到不同的切片中。
null
max_num_partitions 限制通过分区创建的切片数量。(参见
分区
)
200
index_field_presence 对快速字段自动启用
exists
查询。为了对所有其他字段启用它,请将此参数设置为
true
。启用它可能会在索引时产生显著的 CPU 开销。
false

*: 标签字段和时间戳字段表示为从 JSON 对象根到给定字段的路径。如果字段名称包含一个
.
字符,则需要用
\
字符转义。

Field types(字段类型)

每个字段[^1]都有一个类型,指示它包含的数据种类,例如 64 位整数或文本。
Quickwit 支持以下原始类型:
text

i64

u64

f64

datetime

bool

ip

bytes

json
,同时也支持复合类型,如数组和对象。在幕后,Quickwit 使用 tantivy 字段类型,如果您想深入了解细节,请参阅
tantivy 文档

Raw types(原始类型)

Text type(文本类型)

此字段是一个文本字段,在索引之前会被分析并拆分成令牌。
这种类型的字段适合全文搜索。

文本字段映射示例:

文本字段参数

变量 描述 默认值
description 字段的可选描述。 None
stored 值是否存储在文档存储中。 true
indexed 值是否应该被索引以便能够进行搜索。 true
tokenizer Tokenizer
的名称。(
查看分词器
)以获取可用分词器的列表。
default
record 描述索引的信息量,可以选择
basic

freq

position
basic
fieldnorms 是否为字段存储字段规范。字段规范用于计算文档的 BM25 分数。 false
fast 值是否存储在快速字段中。快速字段将包含术语 ID 和字典。对于
true
的默认行为是不变地存储原始文本。快速字段上的规范化器单独配置。可以通过
normalizer: lowercase
来配置。(
查看规范化器
)以获取可用规范化器的列表。
false

可用分词器的描述

分词器 描述
raw 不处理也不分词文本。过滤掉大于 255 字节的令牌。
raw_lowercase 不分词文本,但将其转换为小写。过滤掉大于 255 字节的令牌。
default 根据空白字符和标点符号分割文本,移除过长的令牌,并转换为小写。过滤掉大于 255 字节的令牌。
en_stem 类似于
default
,但在结果令牌上还应用了词干提取。过滤掉大于 255 字节的令牌。
whitespace 仅根据空白字符分割文本。不移除长令牌也不转换为小写。
chinese_compatible 除了
default
执行的操作之外,还在每个 CJK 字符之间进行分割。应与
record: position
一起使用以正确地进行搜索。
lowercase 对文本应用小写转换。它不分词文本。

可用规范化器的描述

规范化器 描述
raw 不处理也不分词文本。过滤掉大于 255 字节的令牌。
lowercase 对文本应用小写转换。过滤掉大于 255 字节的令牌。

记录选项的描述

记录选项 描述
basic 仅记录
DocId
freq 记录文档 ID 以及术语频率
position 记录文档 ID、术语频率以及出现位置。

使用位置索引是执行短语查询所必需的。

Numeric types: i64, u64 and f64 type(数值类型:
i64

u64

f64
类型)

Quickwit 支持三种数值类型:
i64

u64

f64

数值值可以存储在快速字段中(相当于 Lucene 的
DocValues
),这是一种用于范围查询和聚合的列式存储。

在未指定字段的情况下查询负数(使用
default_search_fields
),您应该单引号括起数字(例如
-5
),否则它将被解释为匹配除了该数字以外的任何内容。

u64
字段映射示例:

name: rating
description: Score between 0 and 5
type: u64
stored: true
indexed: true
fast: true

数值类型字段 (
i64
,
u64
, 和
f64
) 参数

变量 描述 默认值
description 字段的可选描述。 None
stored 字段值是否存储在文档存储中。 true
indexed 字段值是否被索引。 true
fast 字段值是否存储在快速字段中。 false
coerce 是否将作为字符串传递的数字转换为整数或浮点数。 true
output_format 用于返回搜索结果中数字的 JSON 类型。可能的值为
number

string
number
datetime type(日期时间类型)

datetime
类型处理日期和日期时间。由于 JSON 没有日期类型,
datetime
字段支持多种输入类型和格式。支持的输入类型包括:

  • 表示 Unix 时间戳的浮点数或整数
  • 包含格式化的日期、日期时间或 Unix 时间戳的字符串

input_formats
字段参数指定了接受的日期格式。以下输入格式得到原生支持:

  • iso8601
  • rfc2822
  • rfc3339
  • strptime
  • unix_timestamp

输入格式

当指定多个输入格式时,相应的解析器会按照声明的顺序尝试。以下格式得到原生支持:

  • iso8601
    ,
    rfc2822
    ,
    rfc3339
    :使用标准的 ISO 和 RFC 格式解析日期。
  • strptime
    :使用 Unix
    strptime
    格式解析日期,有一些变化:
    • strptime
      格式标识符:
      %C
      ,
      %d
      ,
      %D
      ,
      %e
      ,
      %F
      ,
      %g
      ,
      %G
      ,
      %h
      ,
      %H
      ,
      %I
      ,
      %j
      ,
      %k
      ,
      %l
      ,
      %m
      ,
      %M
      ,
      %n
      ,
      %R
      ,
      %S
      ,
      %t
      ,
      %T
      ,
      %u
      ,
      %U
      ,
      %V
      ,
      %w
      ,
      %W
      ,
      %y
      ,
      %Y
      ,
      %%
    • %f
      用于毫秒精度支持。
    • %z
      时区偏移可以指定为
      (+|-)hhmm

      (+|-)hh:mm

目前不支持时区名称格式标识符 (
%Z
)。

https://man7.org/linux/man-pages/man3/strptime.3.html

  • unix_timestamp
    :解析浮点数和整数为 Unix 时间戳。浮点值转换为以秒表示的时间戳。整数值转换为 Unix 时间戳,其精度(秒、毫秒、微秒或纳秒)根据输入数字位数推断。内部地,日期时间转换为 UTC(如果指定了时区),并存储为
    i64
    整数。因此,Quickwit 只支持从
    Apr 13, 1972 23:59:55

    Mar 16, 2242 12:56:31
    的时间戳值。

从浮点数到整数值的转换可能会导致精度损失。


datetime
字段存储为快速字段时,
fast_precision
参数指示在编码前用于截断值的精度,这有助于压缩(此处的截断意味着清零)。
fast_precision
参数可以取以下值:
seconds
,
milliseconds
,
microseconds
, 或
nanoseconds
。它只影响标记为“快速”的
datetime
字段在快速字段中存储的内容。最后,对
datetime
快速字段的操作,例如通过聚合,需要在纳秒级别进行。

内部地,`datetime` 在快速字段和文档存储中以 `nanoseconds` 存储,在术语字典中以 `seconds` 存储。

此外,Quickwit 支持
output_format
字段参数来指定以何种精度反序列化日期时间。此参数支持与输入格式相同的值,除了
unix_timestamp
被替换为以下格式:

  • unix_timestamp_secs
    :以秒显示时间戳。
  • unix_timestamp_millis
    :以毫秒显示时间戳。
  • unix_timestamp_micros
    :以微秒显示时间戳。
  • unix_timestamp_nanos
    :以纳秒显示时间戳。

datetime
字段映射示例:

name: timestamp
type: datetime
description: Time at which the event was emitted
input_formats:
  - rfc3339
  - unix_timestamp
  - "%Y %m %d %H:%M:%S.%f %z"
output_format: unix_timestamp_secs
stored: true
indexed: true
fast: true
fast_precision: milliseconds

日期时间字段参数

变量 描述 默认值
input_formats 用于解析输入日期的格式 [
rfc3339
,
unix_timestamp
]
output_format 用于在搜索结果中显示日期的格式 rfc3339
stored 字段值是否存储在文档存储中 true
indexed 字段值是否被索引 true
fast 字段值是否存储在快速字段中 false
fast_precision 用于存储快速值的精度 (
seconds
,
milliseconds
,
microseconds
, 或
nanoseconds
)
seconds
bool type(布尔类型)

bool
类型接受布尔值。

布尔字段映射示例:

name: is_active
description: Activation status
type: bool
stored: true
indexed: true
fast: true

布尔字段参数

变量 描述 默认值
description 字段的可选描述。 None
stored 值是否存储在文档存储中 true
indexed 值是否被索引 true
fast 值是否存储在快速字段中 false
ip type(IP 类型)

ip
类型接受 IP 地址值,同时支持 IPv4 和 IPv6。内部地,IPv4 地址会被转换为 IPv6。

IP 字段映射示例:

name: host_ip
description: Host IP address
type: ip
fast: true

IP 字段参数

变量 描述 默认值
description 字段的可选描述。 None
stored 值是否存储在文档存储中 true
indexed 值是否被索引 true
fast 值是否存储在快速字段中 false
bytes type(二进制类型)

bytes
类型接受一个以
Base64
编码的字符串形式的二进制值。

二进制字段映射示例:

name: binary
type: bytes
stored: true
indexed: true
fast: true
input_format: hex
output_format: hex

二进制字段参数

变量 描述 默认值
description 字段的可选描述。 None
stored 值是否存储在文档存储中 true
indexed 值是否被索引 true
fast 值是否存储在快速字段中。仅支持一对一基数,不支持
array<bytes>
字段
false
input_format 用于表示输入二进制数据的编码,可以是
hex

base64
base64
output_format 用于在搜索结果中表示二进制数据的编码,可以是
hex

base64
base64
json type(JSON 类型)

json
类型接受一个 JSON 对象。

JSON 字段映射示例:

name: parameters
type: json
stored: true
indexed: true
tokenizer: raw
expand_dots: false
fast:
  normalizer: lowercase

JSON 字段参数

变量 描述 默认值
description 字段的可选描述。 None
stored 值是否存储在文档存储中 true
indexed 值是否被索引 true
fast 值是否存储在快速字段中。JSON 中文本的默认行为是不变地存储文本。可以通过
normalizer: lowercase
配置一个标准化器。(
查看可用的标准化器
) 以获取可用标准化器列表。
true
tokenizer 仅影响 JSON 对象中的字符串

Tokenizer
的名称,可以选择
raw
,
default
,
en_stem

chinese_compatible
raw
record 仅影响 JSON 对象中的字符串
。描述索引的信息量,可以选择
basic
,
freq

position
basic
expand_dots 如果为真,则包含
.
的 JSON 键将被展开。例如,如果
expand_dots
设置为真,则
{"k8s.node.id": "node-2"}
将像
{"k8s": {"node": {"id": "node2"}}}
一样被索引。这样做的好处是在查询时不需要转义
.
。换句话说,
k8s.node.id:node2
将匹配文档。这不会影响文档的存储方式。
true

注意
tokenizer

record
与文本字段具有相同的定义和相同的效果。

要在 JSON 对象中搜索,则需要扩展字段名以指向目标值的路径。

例如,当索引以下对象时:

{
    "product_name": "droopy t-shirt",
    "attributes": {
        "color": ["red", "green", "white"],
        "size:": "L"
    }
}

假设
attributes
已经被定义为如下字段映射:

- type: json
  name: attributes

attributes.color:red
是一个有效的查询。

如果另外将
attributes
设置为默认搜索字段,那么
color:red
也是一个有效的查询。

Composite types(复合类型)

array(数组)

Quickwit 支持所有原始类型(除了
object
类型)的数组。

要在索引配置中声明一个
i64
类型的数组,只需将类型设置为
array<i64>
即可。

object(对象)

Quickwit 支持嵌套对象,只要它不包含对象数组即可。

name: resource
type: object
field_mappings:
  - name: service
    type: text
concatenate(拼接)

Quickwit 支持将多个字段的内容映射到单一字段上。这在查询时可能比遍历数十个
default_search_fields
更高效。它还允许在不知道要搜索字段的路径的情况下,在 JSON 字段内进行查询。

name: my_default_field
type: concatenate
concatenated_fields:
  - text # things inside text, tokenized with the `default` tokenizer
  - resource.author # all fields in resource.author, assuming resource is an `object` field.
include_dynamic_fields: true
tokenizer: default
record: basic

拼接字段不支持快速字段,并且永远不会存储。它们使用自己的分词器,独立于各个字段上配置的分词器。
在查询时,拼接字段不支持范围查询。
仅支持以下类型的拼接字段:text、bool、i64、u64、json。其他类型会在创建索引时被拒绝,或者如果在 JSON 字段中发现,则在索引化过程中被静默丢弃。
向拼接字段添加对象字段不会自动添加其子字段(目前还不支持)。
无法从 JSON 字段添加子字段到拼接字段。例如,如果
attributes
是一个 JSON 字段,则无法仅将
attributes.color
添加到拼接字段中。

对于 JSON 字段和动态字段,仅索引值而不索引路径。例如,给定以下文档:

{
  "421312": {
    "my-key": "my-value"
  }
}

可以搜索
my-value
而不必知道完整的路径,但无法搜索包含键
my-key
的所有文档。

Mode

mode
描述了当 Quickwit 接收到未在字段映射中定义的字段时的行为。

Quickwit 提供了三种不同的模式:

  • dynamic
    (默认值):未映射的字段会被 Quickwit 收集,并按照
    dynamic_mapping
    参数中定义的方式处理。
  • lenient
    :未映射的字段会被 Quickwit 忽略。
  • strict
    :如果文档包含未映射的字段,Quickwit 将会忽略该文档,并将其计为错误。
Dynamic Mapping(动态映射)

dynamic
模式使得可以在无模式或部分模式的情况下运行 Quickwit。
dynamic
模式的配置可通过
dynamic_mapping
参数设置。
dynamic_mapping
提供与配置
json
字段相同的配置选项,默认为:

version: 0.7
index_id: my-dynamic-index
doc_mapping:
  mode: dynamic
  dynamic_mapping:
    indexed: true
    stored: true
    tokenizer: default
    record: basic
    expand_dots: true
    fast: true


dynamic_mapping
设置为已索引(默认),通过动态模式映射的字段可以通过针对从 JSON 对象根部访问它们所需的路径来搜索。

例如,在完全无模式的设置下,最简单的索引配置可能是:

version: 0.7
index_id: my-dynamic-index
doc_mapping:
    # If you have a timestamp field, it is important to tell quickwit about it.
    timestamp_field: unix_timestamp
    # mode: dynamic #< Commented out, as dynamic is the default mode.

有了这样一个简单的配置,我们可以对一个复杂的文档进行索引,如下面所示:

{
  "endpoint": "/admin",
  "query_params": {
    "ctk": "e42bb897d",
    "page": "eeb"
  },
  "src": {
    "ip": "8.8.8.8",
    "port": 53,
  },
  //...
}

以下查询是有效的,并匹配上述文档。

// Fields can be searched simply.
endpoint:/admin

// Nested object can be queried by specifying a `.` separated
// path from the root of the json object to the given field.
query_params.ctk:e42bb897d

// numbers are searchable too
src.port:53

// and of course we can combine them with boolean operators.
src.port:53 AND query_params.ctk:e42bb897d

Field name validation rules(字段名称验证规则)

目前 Quickwit 只接受符合以下正则表达式的字段名称:
^[@$_\-a-zA-Z][@$_/\.\-a-zA-Z0-9]{0,254}$

用通俗的语言来说:

  • 需要有至少一个字符。
  • 只能包含大写和小写的 ASCII 字母
    [a-zA-Z]
    、数字
    [0-9]

    .
    、破折号
    -
    、下划线
    _
    、斜杠
    /
    、at 符号
    @
    和美元符号
    $
  • 不得以点号或数字开头。
  • 必须与 Quickwit 的保留字段映射名称
    _source

    _dynamic

    _field_presence
    不同。

对于包含
.
字符的字段名称,在引用它们时需要对其进行转义。否则
.
字符将被视为 JSON 对象属性的访问。因此,建议避免使用包含
.
字符的字段名称。

Behavior with null values or missing fields(对空值或缺失字段的行为)

JSON 文档中的
null
值或缺失字段在索引时将被静默忽略。

Indexing settings(索引设置)

本节描述了给定索引的索引设置。

变量 描述 默认值
commit_timeout_secs 自创建以来提交拆分的最大秒数。 60
split_num_docs_target 每个拆分的目标文档数。 10000000
merge_policy 描述触发拆分合并操作所采用的策略(参见下方的
合并策略
部分)。
resources.heap_size 每个来源每个索引的索引器堆大小。 2000000000
docstore_compression_level docstore 中使用的 zstd 压缩级别。较低的值可能会提高摄取速度,但代价是索引大小 8
docstore_blocksize docstore 中块的大小,以字节为单位。较小的值可能会提高文档检索速度,但代价是索引大小 1000000

Merge policies(合并策略)

Quickwit 使得可以定义用于决定哪些拆分应该合并以及何时合并的策略。

Quickwit 提供了三种不同的合并策略,每种都有自己的参数集。

"Stable log" merge policy(“稳定日志”合并策略)

稳定日志合并策略试图最小化写入放大效应,并尽可能保持较高的时间修剪能力,通过合并大小相似且时间跨度相近的拆分。

Quickwit 默认的合并策略是
stable_log
合并策略,参数如下:

version: 0.7
index_id: "hdfs"
# ...
indexing_settings:
  merge_policy:
    type: "stable_log"
    min_level_num_docs: 100000
    merge_factor: 10
    max_merge_factor: 12
    maturation_period: 48h
变量 描述 默认值
merge_factor (高级)
在单次合并操作中一起合并的拆分数目。
10
max_merge_factor (高级)
在单次合并操作中可以一起合并的最大拆分数目。
12
min_level_num_docs (高级)
文档数目低于此值的所有拆分被视为属于同一层级。
100000
maturation_period 拆分被视为成熟的时间期限,之后将不再考虑进行合并。可能会影响待处理删除任务的完成时间。 48h
"Limit Merge" merge policy(“限制合并”合并策略)

“限制合并”合并策略被认为是高级功能

限制合并策略通过设置拆分应经历的合并操作次数的上限来简单地限制写入放大效应。

version: 0.7
index_id: "hdfs"
# ...
indexing_settings:
  merge_policy:
    type: "limit_merge"
    max_merge_ops: 5
    merge_factor: 10
    max_merge_factor: 12
    maturation_period: 48h
变量 描述 默认值
max_merge_ops 给定拆分应经历的最大合并次数。 4
merge_factor (高级)
在单次合并操作中一起合并的拆分数目。
10
max_merge_factor (高级)
在单次合并操作中可以一起合并的最大拆分数目。
12
maturation_period 拆分被视为成熟的时间期限,之后将不再考虑进行合并。可能会影响待处理删除任务的完成时间。 48h
No merge(不合并)

no_merge
合并策略完全禁用合并。

此设置不推荐使用。合并是必要的,因为它可以减少拆分的数量,从而提高搜索性能。

version: 0.7
index_id: "hdfs"
indexing_settings:
    merge_policy:
        type: "no_merge"

Indexer memory usage(索引器内存使用)

索引器默认使用 2 GiB 的堆内存。这并不直接反映总体内存使用情况,但将这个值加倍应该能得到一个合理的近似值。

Search settings(搜索设置)

本节描述了给定索引的搜索设置。

变量 描述 默认值
default_search_fields 用于搜索的默认字段列表。此列表中的字段名称可以在模式中显式声明,也可以引用由动态模式捕获的字段。 None

Retention policy(保留策略)

本节描述了 Quickwit 如何管理数据保留。在 Quickwit 中,保留策略管理器按拆分删除数据,而不是单独删除文档。拆分根据其
time_range
进行评估,该
time_range
来自索引时间戳字段(在 (
doc_mapping.timestamp_field
) 设置中指定)。使用此设置,当
now() - split.time_range.end >= retention_policy.period
时,保留策略将删除拆分。

version: 0.7
index_id: hdfs
# ...
retention:
  period: 90 days
  schedule: daily
变量 描述 默认值
period 以人类可读方式表示的拆分被删除后的持续时间(如
1 day

2 hours

a week
等)。
必需
schedule 以 cron 表达式 (
0 0 * * * *
) 或人类可读形式 (
hourly

daily

weekly

monthly

yearly
) 表示的保留策略评估和应用的频率。
hourly

period
被指定为一系列时间间隔。每个时间间隔是一个整数后跟一个单位后缀,如:
2 days 3h 24min
。支持的单位有:

  • nsec
    ,
    ns
    -- 纳秒
  • usec
    ,
    us
    -- 微秒
  • msec
    ,
    ms
    -- 毫秒
  • seconds
    ,
    second
    ,
    sec
    ,
    s
  • minutes
    ,
    minute
    ,
    min
    ,
    m
  • hours
    ,
    hour
    ,
    hr
    ,
    h
  • days
    ,
    day
    ,
    d
  • weeks
    ,
    week
    ,
    w
  • months
    ,
    month
    ,
    M
    -- 一个月定义为
    30.44 天
  • years
    ,
    year
    ,
    y
    -- 一年定义为
    365.25 天

Metastore configuration

Quickwit 需要一个地方来存储关于其索引的元信息。

例如:

  • 索引配置。
  • 关于其拆分的元信息。例如,它们的 ID、包含的文档数量、大小、最小/最大时间戳以及拆分中存在的标签集。
  • 不同来源的检查点。
  • 一些额外的信息,如索引创建时间。

元存储完全由一个 URI 定义。可以通过编辑
节点配置文件
(通常命名为
quickwit.yaml
)中的
metastore_uri
参数来设置它。

目前,Quickwit 提供了两种实现:

  • PostgreSQL
    :推荐用于分布式使用。
  • File-backed implementation

PostgreSQL Metastore(PostgreSQL 元存储)

对于任何分布式使用场景,我们推荐使用 PostgreSQL 元存储。

可以通过在 Quickwit 配置文件中的
metastore_uri
参数设置 PostgreSQL URI 来配置 PostgreSQL 元存储。URI 的格式如下:

postgres://[user]:[password]@[host]:[port]/[dbname]

一些参数可以省略。以下 PostgreSQL URI 是有效的示例:

postgres://localhost/mydb
postgres://user@localhost
postgres://user:secret@localhost
postgres://host1:123,host2:456/mydb

数据库需要提前创建。

在首次执行时,Quickwit 将透明地创建必要的表。

同样,如果你将 Quickwit 升级到包含 PostgreSQL 模式更改的版本,Quickwit 将在启动时透明地执行迁移。

File-backed metastore(基于文件的元存储)

为了方便起见,Quickwit 还允许使用基于文件的元存储来将其元数据存储在文件中。在这种情况下,Quickwit 将为每个索引写一个文件。

然后通过传递一个
存储 URI
来配置元存储,该 URI 将作为元存储的根目录。

与给定索引关联的元数据文件将存储在

[storage_uri]/[index_id]/metastore.json

目前,Quickwit 支持两种类型的存储:

  • 本地文件系统 URI(例如,
    file:///opt/toto
    )。直接传递文件路径(不带
    file://
    )也是有效的,例如
    /var/quickwit
    。相对路径将相对于当前工作目录解析。
  • S3 兼容的存储 URI(例如,
    s3://my-bucket/some-path
    )。参阅
    存储配置
    文档来配置 S3 或 S3 兼容的存储提供商。

Polling configuration(拉取配置)

默认情况下,基于文件的元存储仅在启动 Quickwit 进程(如搜索器、索引器等)时读取一次。

您还可以配置它定期拉取基于文件的元存储以保持最新视图。这对于需要了解由并行运行的索引器发布的新的拆分的搜索器实例很有用。

要配置拉取间隔(以秒为单位),请向存储 URI 添加 URI 片段,如下所示:
s3://quickwit/my-indexes#polling_interval=30s

拉取间隔只能以秒为单位配置;其他单位,如分钟或小时,不受支持。
Amazon S3 对每 1000 次 GET 请求收取 $0.0004 的费用。每 30 秒拉取一次元存储每月和每个索引的成本为 $0.04。

Examples(示例)

以下基于文件的元存储 URI 是有效的:

s3://my-indexes
s3://quickwit/my-indexes
s3://quickwit/my-indexes#polling_interval=30s
file:///local/indices
file:///local/indices#polling_interval=30s
/local/indices
./quickwit-metastores

基于文件的元存储不支持同时运行多个实例,因为它没有实现任何锁定机制来防止并发写入相互覆盖。确保任何时候只运行一个基于文件的元存储实例。

Source configuration(数据来源配置)

Quickwit 可以从一个或多个来源将数据插入到索引中。
可以在创建索引后使用
CLI 命令
quickwit source create
添加来源。
也可以使用
quickwit source enable/disable
子命令启用或禁用来源。

来源是通过一个称为来源配置的对象声明的,该对象定义了来源的设置。它由多个参数组成:

  • 来源 ID
  • 来源类型
  • 来源参数
  • 输入格式
  • 每个索引器的最大管道数(可选)
  • 期望的管道数(可选)
  • 转换参数(可选)

Source ID(来源 ID)

来源 ID 是一个字符串,用于在索引内唯一标识来源。它只能包含大写或小写的 ASCII 字母、数字、连字符 (
-
) 和下划线 (
_
)。最后,它必须以字母开头,并且至少包含 3 个字符,但不超过 255 个。

Source type(来源类型)

来源类型指定了正在配置的来源种类。截至版本 0.5,可用的来源类型有
ingest-api

kafka

kinesis

pulsar

file
类型也受支持,但仅用于从
CLI
进行本地摄入。

Source parameters(来源参数)

来源参数指示如何连接到数据存储,并且特定于来源类型。

File source(文件数据来源)

文件来源从包含由新行分隔的 JSON 对象(NDJSON)的文件中读取数据。如果文件名以
.gz
后缀结尾,则支持 Gzip 压缩。

Ingest a single file (摄入特定文件 CLI only)

要摄入特定文件,请直接在临时 CLI 进程中运行索引:

./quickwit tool local-ingest --index <index> --input-path <input-path>

本地文件和对象文件都受支持,前提是环境已使用适当的权限进行配置。有一个教程可供参考
这里

Notification based file ingestion (基于通知的文件摄入 beta)

Quickwit 可以自动摄入所有上传到 S3 存储桶的新文件。这需要创建并配置一个
SQS 通知队列
。一个完整的示例可以在
这个教程
中找到。

notifications
参数接受一个通知设置数组。目前每个来源可以配置一个通知器,并且仅支持 SQS 通知
type

SQS
notifications
参数项所需的字段:

使用 CLI 向索引添加带有 SQS 通知的文件来源

cat << EOF > source-config.yaml
version: 0.8
source_id: my-sqs-file-source
source_type: file
num_pipelines: 2
params:
  notifications:
    - type: sqs
      queue_url: https://sqs.us-east-1.amazonaws.com/123456789012/queue-name
      message_type: s3_notification
EOF
./quickwit source create --index my-index --source-config source-config.yaml

  • Quickwit 在成功摄入后不会自动删除来源文件。您可以使用
    S3 对象过期
    来配置它们应在存储桶中保留多久。
  • 配置通知仅转发类型为
    s3:ObjectCreated:*
    的事件。其他事件会被来源确认,但不再进一步处理,并记录警告。
  • 我们强烈建议使用
    死信队列
    来接收所有无法被文件来源处理的消息。
    maxReceiveCount
    的值为 5 是一个不错的默认值。以下是一些常见情况,其中通知消息最终会进入死信队列:
    • 通知消息无法解析(例如,它不是一个有效的 S3 通知)
    • 文件未找到
    • 文件损坏(例如,意外压缩)

https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-expire-general-considerations.html
https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html

Ingest API source(Ingest API 来源)

Ingest API 来源从
Ingest API
读取数据。此来源会在创建索引时自动生成,不能删除或禁用。

Kafka source(Kafka 来源)

Kafka 来源从 Kafka 流中读取数据。流中的每条消息必须包含一个 JSON 对象。

有一个教程可供参考
这里

Kafka source parameters(Kafka 参数)

Kafka 来源使用客户端库
librdkafka
消费
topic
,并将
client_params
参数携带的键值对转发给底层的 librdkafka 消费者。常见的
client_params
选项包括引导服务器 (
bootstrap.servers
) 或安全协议 (
security.protocol
)。请参阅
Kafka

librdkafka
文档页面获取更高级的选项。

属性 描述 默认值
topic 要消费的主题名称。 必需
client_log_level librdkafka 客户端日志级别。可能的值是:debug、info、warn、error。 info
client_params librdkafka 客户端配置参数。 {}
enable_backfill_mode 回填模式在到达主题末尾后停止来源。 false

Kafka 客户端参数

  • bootstrap.servers
    逗号分隔的主机和端口对列表,这些是 Kafka 集群中 Kafka 经纪人的地址子集。

  • auto.offset.reset
    定义了来源在消费一个分区时的行为,该分区在检查点中没有保存初始偏移量。
    earliest
    从分区开始消费,而
    latest
    (默认)从分区末尾消费。

  • enable.auto.commit
    此设置被忽略,因为 Kafka 来源使用
    检查点 API
    内部管理提交偏移量,并强制禁用自动提交。


  • group.id
    基于 Kafka 的分布式索引依赖于消费者组。除非在客户端参数中覆盖,默认分配给来源管理的每个消费者的组 ID 是
    quickwit-{index_uid}-{source_id}

  • max.poll.interval.ms
    短的最大轮询间隔持续时间可能会导致来源在索引器出现反压时崩溃。因此,Quickwit 建议使用默认值
    300000
    (5 分钟)。

使用 CLI 向索引添加 Kafka 来源

cat << EOF > source-config.yaml
version: 0.8
source_id: my-kafka-source
source_type: kafka
num_pipelines: 2
params:
  topic: my-topic
  client_params:
    bootstrap.servers: localhost:9092
    security.protocol: SSL
EOF
./quickwit source create --index my-index --source-config source-config.yaml

Kinesis source(Kinesis 来源)

Kinesis 来源从
Amazon Kinesis
流中读取数据。流中的每条消息必须包含一个 JSON 对象。

有一个教程可供参考
这里

Kinesis source parameters

Kinesis 来源通过
stream_name

region
消费一个流。

属性 描述 默认值
stream_name 要消费的流名称。 必需
region 流所在的 AWS 区域。与
endpoint
互斥。
us-east-1
endpoint 用于与 AWS 兼容的 Kinesis 服务一起使用的自定义端点。与
region
互斥。
可选

如果没有指定区域,Quickwit 将尝试在多个其他位置查找区域,并按照以下优先顺序:

  1. 环境变量 (
    AWS_REGION
    然后是
    AWS_DEFAULT_REGION
    )

  2. 配置文件,通常位于
    ~/.aws/config
    或者如果设置了
    AWS_CONFIG_FILE
    环境变量且不为空,则按其指定的位置。

  3. Amazon EC2 实例元数据服务确定当前运行的 Amazon EC2 实例所在的区域。

  4. 默认值:
    us-east-1

使用 CLI 向索引添加 Kinesis 来源

cat << EOF > source-config.yaml
version: 0.7
source_id: my-kinesis-source
source_type: kinesis
params:
  stream_name: my-stream
EOF
quickwit source create --index my-index --source-config source-config.yaml

Pulsar source(Pulsar 来源)

Pulsar 来源从一个或多个 Pulsar 主题读取数据。每个主题中的消息必须包含一个 JSON 对象。

有一个教程可供参考
这里

Pulsar source parameters

Pulsar 来源使用客户端库
pulsar-rs
消费
topics

属性 描述 默认值
topics 要消费的主题列表。 必需
address Pulsar URL(pulsar:// 和 pulsar+ssl://)。 必需
consumer_name 要与 Pulsar 来源注册的消费者名称。 quickwit

使用 CLI 向索引添加 Pulsar 来源

cat << EOF > source-config.yaml
version: 0.7
source_id: my-pulsar-source
source_type: pulsar
params:
  topics:
    - my-topic
  address: pulsar://localhost:6650
EOF
./quickwit source create --index my-index --source-config source-config.yaml

Number of pipelines(管道数量)

num_pipelines
参数仅适用于像 Kafka、GCP PubSub 和 Pulsar 这样的分布式来源。

它定义了要在集群上为来源运行的管道数量。这些管道在不同索引器上的实际放置将由控制平面决定。

请注意,对于像 Kafka 这样的分区来源,通过将不同的分区分配给不同的管道来分布索引负载。因此,重要的是确保分区的数量是
num_pipelines
的倍数。

此外,假设您只在 Quickwit 集群中索引单个 Kafka 来源,您应该将管道数量设置为索引器数量的倍数。最后,如果您的索引吞吐量很高,您应该为每个管道配置 2 到 4 个 vCPU。

例如,假设您想要索引一个 60 个分区的主题,每个分区接收 10 MB/s 的吞吐量。如果您测量到 Quickwit 可以以每管道 40 MB/s 的速度索引您的数据,那么可能的设置可以是:

  • 5 个索引器,每个有 8 个 vCPU
  • 15 个管道

这样,每个索引器将负责 3 个管道,每个管道将覆盖 4 个分区。

Transform parameters(转换参数)

除了
ingest-api
类型之外的所有来源类型,在索引之前可以使用
Vector Remap Language (VRL)
脚本转换摄取的文档。

属性 描述 默认值
script 执行以转换文档的 VRL 程序的源代码。 必需
timezone VRL 程序中用于日期和时间操作的时区。它必须是
TZ 数据库
中的有效名称。
UTC
# Your source config here
# ...
transform:
  script: |
    .message = downcase(string!(.message))
    .timestamp = now()
    del(.username)
  timezone: local

Input format(输入格式)

input_format
参数指定了来源预期的数据格式。目前支持两种格式:

  • json
    : JSON,这是默认格式
  • plain_text
    : 非结构化文本文档

内部而言,Quickwit 只能索引 JSON 数据。为了允许摄取纯文本文档,Quickwit 会将它们实时转换成如下形式的 JSON 对象:
{"plain_text": "<original plain text document>"}
。然后,可以使用 VRL 脚本将它们可选地转换为更复杂的文档。(参见
transform 特性
)。

下面是一个如何解析并转换包含用户列表的 CSV 数据集的例子,其中用户由 3 个属性描述:名字、姓氏和年龄。

# Your source config here
# ...
input_format: plain_text
transform:
  script: |
    user = parse_csv!(.plain_text)
    .first_name = user[0]
    .last_name = user[1]
    .age = to_int!(user[2])
    del(.plain_text)

Enabling/Disabling a source from an index(启用/禁用索引中的来源)

可以通过
CLI 命令
quickwit source enable

quickwit source disable
启用或禁用索引中的来源:

quickwit source disable --index my-index --source my-source

来源默认是启用的。当禁用一个来源时,相关的索引管道将在每个相关索引器上关闭,对该来源的索引也会暂停。

Deleting a source from an index(从索引中删除来源)

可以通过
CLI 命令
quickwit source delete
从索引中移除来源:

quickwit source delete --index my-index --source my-source

删除来源时,与来源关联的检查点也会被移除。

Ports configuration(端口配置)

当启动 Quickwit 搜索服务器时,可以配置的一个重要参数是
rest.listen_port
(默认值为 7280)。

内部而言,Quickwit 实际上会使用三个套接字。这三个套接字的端口目前无法独立配置。
使用的端口相对于
rest.listen_port
端口计算得出,具体如下。

服务 使用的端口 协议 默认值
带 REST API 的 HTTP 服务器 ${rest.listen_port} TCP 7280
集群成员资格 ${rest.listen_port} UDP 7280
GRPC 服务 ${rest.listen_port} + 1 TCP 7281

目前无法独立配置这些端口。

为了形成集群,还需要定义一个
peer_seeds
参数。
以下地址是有效的对等种子地址:

类型 不带端口的示例 带端口的示例
IPv4 172.1.0.12 172.1.0.12:7180
IPv6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:7280
主机名 node3 node3:7180

如果在对等节点地址中没有指定端口,Quickwit 节点将假定该对等节点使用与自己相同的端口。

更多

1.
Binance 如何使用 Quickwit 构建 100PB 日志服务(Quickwit 博客)

通过对多样化基准的严格评估,论文展示了现有特定方法在实现跨领域推理以及其偏向于数据偏差拟合方面的缺陷。从两阶段的视角重新审视视觉推理:(
1
)符号化和(
2
)基于符号或其表示的逻辑推理,发现推理阶段比符号化更擅长泛化。因此,更高效的做法是通过为不同数据领域使用分离的编码器来实现符号化,同时使用共享的推理器。

来源:晓飞的算法工程笔记 公众号

论文: Take A Step Back: Rethinking the Two Stages in Visual Reasoning

Introduction


推理能力是人类智能的集中体现,它是概念形成、对世界的认知理解以及与环境交互的基础。具体而言,视觉推理作为人类获取信息和理解的主要方式之一,已经成为广泛研究的焦点。近年来,随着深度学习的进步,涌现出了许多关于视觉推理的研究工作。此外,也出现了各种数据集来评估推理模型。

然而,现有视觉推理工作的显著局限性在于它们直接依赖于通过端到端深度学习模型同时进行识别和推理阶段,例如,在回答逻辑问题时识别图像中的概念。然而,这种范式存在显而易见的局限性:
1
)推理注释(规则、关系)比符号注释(三角形、立方体、苹果)要昂贵且困难得多,当前严格的视觉推理数据集通常很小。因此,当前方法往往是在小数据集上的特定任务,阻碍了它们的泛化潜力。
2
)同时追求符号识别和逻辑推理的通用模型可能效率低下且具有挑战性,即使是最近的大型语言模型(
LLM
)也难以处理多样化的视觉推理任务。

论文认为视觉推理的重点在于先从视觉信号中获取符号化表示,随后是逻辑推理,如图
1
所示。因此,一个问题浮现出来:这两个阶段应该纠缠在一起还是解开?推理自然比符号化具有更好的泛化性。例如,可以使用类似的逻辑分析不同任务的规则(如下围棋、做数学题和发现异常),但对于识别字母和物体则需要完全不同的知识。因此,论文认为解开符号化和推理将是一个更明智的选择。最近大型语言模型(
LLM
)在基于文本的推理任务上的成功也验证了这一点,因为
LLM
直接利用从人类观察中得出的抽象符号(语言),并专注于高级语言任务。相对而言,多模态大型语言模型(
MLLM
)即使参数更多,仍在视觉推理中遇到困难。最近,另一个相关的研究趋势是神经符号方法,将原始输入转换为明确的符号,以便进行后续推理和分析。然而,神经符号方法通常局限于单一数据集,这使得在不同任务之间实现泛化变得具有挑战性。

论文在多个具有显著领域差距的基准任务上进行了全面的实验,以验证假设。将符号化阶段定义为利用深度神经网络(
DNN
)进行表示提取,采用多种架构(如
MLP

CNN

GNN

Transformer
、神经符号模型、
LLM
等)实现逻辑推理器。论文主要探讨两个关键问题:(
1
) 在训练过的
DNN
模型中,符号化阶段的结束在哪里?即确定适合推理的合适符号(表示),如模型的深度、特征特性等。(
2
) 针对抽象的符号,哪种类型的模型和训练策略最适合进行推理,并赋予泛化能力?

对于第一个问题,论文发现不同的任务和领域需要非常不同规模的参数或模型深度才能实现良好的符号化。因此,针对特定领域,一个小型的独立领域内编码器就足以从数据中提取符号,以供后续推理阶段使用。虽然像
CLIP
这样的通用大型基础模型在某些任务上表现不错,但在与其训练数据存在巨大领域差距的任务上仍然面临挑战。对于第二个问题,实验结果显示,现有方法在执行跨领域推理时往往困难重重,而更倾向于适应与训练数据一致的偏见。因此,也许只能通过训练它执行各种推理任务(如解谜、物理预测、视觉问答)和数据领域(
2D

3D
、文本)来实现可泛化的共享推理器,即“近似原则”。

基于实验的发现,论文构建了一个简洁的框架,采用分离的编码器来实现不同数据领域的最佳符号化,并遵循“近似原则”建立了一个共享的推理器。该方法在跨领域基准测试中表现出色,使用更少的参数即可达到优异的性能。

总体而言,论文的贡献如下:

  1. 总结了一种高效的视觉推理的两阶段方法,借鉴了先前的视觉推理网络的思路。
  2. 探讨了视觉推理中符号化和逻辑推理的最优设计原则。
  3. 引入了一个简洁的框架,在多个具有领域差距的数据集上表现良好。

Preliminary


Two Stages

如上所述,视觉推理可以分为两个阶段:符号化阶段提取基础数据的符号表示,推理阶段进行逻辑推理。

对于人类来说,从感觉器官收集到的视觉和听觉信息的不同模态,通过不同的通路转换成电信号,然后发送到小脑皮层进行逻辑推理。类比地,为了通用的视觉推理机器,分离的任务特定符号化器和共享的领域无关推理器是一个合理的选择。此外,推理器应能够对来自各种模态的输入信息进行统一推理。换句话说,推理的本质在于其泛化能力。

  • Symbolization Stage

在符号化阶段,实现了各种面向任务的特征提取网络。这些网络使用针对每个任务定制的符号编码器,将多模态输入(文本、图像、视频)转换为符号表示。具体而言,假设有
\(n\)
个任务。对于第
\(i\)
个任务,有输入数据
\(\mathbf{x}^{i}\)
和任务
\(t^{i}\)
,以及任务定向编码器
\(E^{i}\)
,通过以下方式得到符号表示集合
\(\mathbf{f}^{i}\)

\[\begin{equation}
\mathbf{f}^{i} = E_i(\mathbf{x}^{i} \mid t^{i}).
\end{equation}
\]

  • Reasoning Stage

推理器接收每个特定任务的符号表示,旨在捕捉数据中嵌入的更深入和更全面的模式和关系理解。对于所有任务的符号表示集合
\(\{\mathbf{f}^{(i)}\}_{i=1}^n\)
,将它们送入推理器
\(R\)
,在逻辑处理后得到其推理结果集合
\(\{\mathbf{c}^{i}\}_{i=1}^n\)
,以促进跨多模态问题解决:

\[\begin{equation}
\{\mathbf{c}^{i}\}^n_{i=1} = R(\{\mathbf{f}^{i}\}^n_{i=1}).
\label{eq:reasoning}
\end{equation}
\]

  • Task-specific Heads

框架的最后部分是任务特定头部,将推理器的推理结果作为输入,并生成任务特定的答案。针对不同的任务,我们需要构建任务特定的分类或回归头部
\(H_i\)
,以获得最终输出
\(s^i\)

\[\begin{equation}
s^i = H_i(\mathbf{c}^{i} \mid t^{i}).
\end{equation}
\]

Symbolization-Reasoning Framework


Entanglement v.s. Disentanglement

考虑到这两个阶段,一个自然的问题出现了:对于任务,符号编码器(符号化)和推理器(推理)应该共享还是分开?

为了验证共享推理器假设,进行了不同设计的比较(图
2
):

  1. Both-Separated
    :符号编码器和逻辑推理器均分离(每个任务的特定模型)。
  2. Both-Shared
    :符号编码器和推理器都是共享的。
  3. Shared-Encoder-Only
    :仅符号编码器是共享的。
  4. Shared-Reasoner-Only
    :仅推理器是共享的。

我们在几个多模态视觉推理基准上比较上述四种设计。对于共享的编码器/推理器,采用更多的参数来平衡它们与分离的编码器/推理器参数总和。在所有基准测试中,仅共享推理器(类型
4
)的表现远远优于仅共享编码器(类型
3
)和完全共享(类型
1

2
)。此外,仅共享推理器甚至在某些基准测试中超过了临时的完全分离模型,验证了其优越性和泛化能力。

Symbolization Depth

接下来,探讨符号编码器在不同任务中的合适深度。符号化阶段涉及处理来自不同领域的输入,并将它们映射到概念层次,即符号。尽管可以使用二进制或类似索引的表示(
one-hot
)来表示符号,但在深度学习的背景下,论文选择更具代表性的方式,即从深度神经网络中提取的高维特征。直观上,不同的任务可能需要不同程度的符号化。接下来的问题是如何确定每个任务的抽象层次。

为了回答这个问题,论文设计了实验来定量探究符号化过程中的程度。为了控制变量,在跨领域任务中采用相同的特征提取网络(
ResNet
),同时不断调整网络的深度。在不同深度下,将符号化网络的输出连接到相同的推理器,并以准确率作为符号化完成的指标进行测量。

假设当符号化完成时,网络深度-准确率曲线将显示出明显的拐点。通过监测这一拐点的出现,可以为不同任务选择合适的推理器深度,如图
3
所示。在实验中,结果与常识一致:过浅和过深的网络对推理任务都是有害的。一方面,如果编码器过浅,符号化可能无法在输入推理器之前完全完成,而推理器则需要完成部分符号化工作,从而影响推理性能。另一方面,过深的网络往往会过拟合于单一任务,从而削弱旨在泛化的共享推理器的性能。不同领域的任务也需要不同的符号化深度。设置深度不当会导致共享参数冲突,进而导致更深层次的性能不佳。

Reasoner Architecture

接下来,论文希望弄清楚哪种架构更适合于推理器,这是一个长期存在的问题。许多研究已经提出并在各种视觉推理基准上取得了改进。在这里,每个任务由其各自的编码器和任务头处理,这些设计旨在适应其数据的固有特性。论文为所有任务使用一个共享的推理器,并根据公式
2
中的符号表示进行处理。

论文选择了一系列在许多任务中取得了巨大成功的架构作为推理器的候选项:多层感知器(
MLP
)、卷积神经网络(
CNN
)和
Transformer
,还探索了将神经网络的表示能力与符号系统的可解释性结合的混合神经符号模型。此外,论文采用了流行的图形和自回归模型:图卷积网络(
GCN
)和
MiniGPT-4
。上述模型为论文提供了广泛且多样化的方法论。如果在各种领域多样化的数据集上观察到强大而一致的性能,这将导致假设存在某种类型的架构,特别擅长逻辑推理。

Generalization of Reasoner

最后但并非最不重要的是,论文的目标是验证其“近似原理”,即通过来自不同领域的多样化任务和数据的训练,得到一个接近能够提供通用逻辑推理能力的良好推理器。论文认为推理涵盖普适性和泛化能力,因此,首先在一个任务上训练完整的两阶段模型,然后直接将其推理器与另一个任务的另一个符号编码器配对。如果推理器具有泛化能力,则其应该能够很好地适应其他任务的编码器。然而,在论文的测试中,仅使用一个任务/领域训练的推理器通常泛化能力不佳。因此,接下来验证推理器在训练更多任务/领域后是否具有更好的泛化能力。

如图
4
所示,论文发现随着涉及不同任务和领域的数据越来越多,整体任务变得越来越具有挑战性。然而,推理器将会专注于“纯粹”的推理,而不是特定于任务/领域的解决方法,从而赋予更好的泛化能力。换句话说,论文的“近似原理”是合理的。因此,可以预测,随着训练数据和任务的增加,推理器在跨领域任务上的表现应该会更好。此外,一个共享并跨任务训练的推理器使整个视觉推理框架更轻便和更高效。

Experiments


Entanglement v.s. Disentanglement Analysis

为了比较图
2
中的三种类型,使用五个数据集对模型进行训练:
RAVEN

CVR

SVRT

Bongard-HOI

Bongard-LOGO
。为了控制变量并便于模型共享,对于上述所有类型,我们采用
ResNet-18
作为编码器,使用
MLP
作为推理器。

结果如表
1
所示,仅共享推理器的性能在所有五个数据集上与
ad-hoc
方案相当,在
RAVEN

SVRT
上甚至更高。此外,仅共享编码器和共享双方案在所有数据集上表现明显不及。这反映了在跨多个任务使用任务特定符号化器和共享推理器设计的有效性。

Optimal Symbolization Depth

接下来,通过探测符号编码器的深度来确定两个阶段之间的边界,如图
3
所示,使用的共享推理器是一个
MLP
。通过观察准确率的变化,希望找到明显的拐点和符号化基础阶段的终止点。为了确保公平性,在以下二维数据集(
RAVEN

CVR

SVRT

Bongard-LOGO

Bongard-HOI
)上采用
ResNet18
编码器。对于每个基准,训练各自的模型以达到最佳性能,然后在不同深度中断训练的网络,以探测符号化终止点。将分离的符号编码器的输出连接到一个共享推理器,并记录不同中断点处的准确率,这被视为符号化终止的证据。

如图
5
和表
2
所示,对于每个基准,网络深度表现出初期的增长,然后进入一个平稳期。不同任务的拐点位置因其难度水平和所需的符号抽象程度不同而异。例如,
Bongard-HOI
的拐点比
RAVEN
深得多,表明前者更难符号化,需要一个更深的符号化网络来获取复杂的高维特征。这些结果验证了在不同复杂度的数据集上使用深度不同的符号化网络的必要性,并说明了两个阶段之间的合理边界。

One-for-All Reasoner Architecture

接下来,找出适合的推理器架构,并测试其在仅共享推理器设计中的效果。选择了
9
个跨领域数据集(
RAVEN

CVR

SVRT

Bongard-HOI

Bongard-LOGO

Filtered-CoPhy

VQAv2
)来进行实验,因为用不同领域的数据解决各种推理问题可以更好地展示模型的推理能力。

根据不同任务的要求设计了任务特定的编码器和头部。至于推理器,测试了
CNN

MLP

Transformer

GCN
、混合神经符号模型和
MiniGPT-4
。首先单独训练每个数据集,以获得使用分离编码器和分离头部时的最佳结果,然后使用一个共享推理器对多个数据集进行联合训练。

如表
3
所示,在所有架构中,
MLP
在四个数据集上表现出乎意料的最佳性能,并且在其他五个数据集上表现相当。此外,
GCN
在三个数据集上表现良好,符合之前在推理工作中的经验。然而,通常被认为更先进的
Transformer
等其他架构并未显示明显优势。因此,选择
MLP
作为
One-for-All
中的轻量推理器。

One-for-All
在大多数任务上表现出色,甚至与最先进技术(
SOTA

ad-hoc
相比也不遑多让。论文根据复杂性将
SOTA
分为轻量和重型,如表
4
所示,
One-for-All
与轻量
ad-hoc SOTA
表现可比,并且在一些数据集(如
RAVEN
)上甚至超越了轻量
ad-hoc SOTA
。这个实验表明,推理阶段与识别任务有着不同的性能参数关系。如果在多领域任务上进行训练,一个轻量级的推理器也可以在推理任务上表现出色。

由于推理能力不能仅通过准确性来衡量,论文还使用推理一致性评估推理能力。对于每个任务,使用相同的编码器和推理器参数,采用两种问答方法:“这个问题的答案是什么?”和“某个选项是否正确?”。一个具有良好推理能力的模型应该在这两种方法上产生一致的结果,而不像随机模型可能存在不一致性。论文使用
F1
分数来衡量这些方法之间的一致性,如表
5
所示。
One-for-All
在多个数据集上联合训练后,显示出比单独训练的模型更高的一致性,表明其在真正推理中的潜力。

为了进一步评估
LLM
的性能,采用
MiniGPT-4
作为共享推理器。
One-for-All
在相似的模型规模下也表现出优势。令人惊讶的是,轻量化的
One-for-All
在特定任务上超过了
MiniGPT-4
,例如
RAVEN

Bongard-HOI
,这提供了强有力的证据表明,模型参数数量与模型推理能力之间没有绝对的正相关关系。

为了分析基于
LLM
的模型性能,根据论文的两阶段框架设计探索任务,并分别进行检查:(
1
)符号化:
LLM-based
模型能否识别问题的要素。(
2
) 概念化:
LLM-based
模型能否学习任务背后的具体概念并进行推理。(
3
) 答案生成:
LLM-based
模型能否利用其学习到的概念来解决问题。以
MiniGPT-4
为代表,总结了
LLM-based
模型对
RAVEN

Bongard
中三级问题的典型响应,如图
6
所示。

论文发现
LLM
在解决视觉推理任务时可能会遇到某些幻觉情况。如图
6
所示,对于
RAVEN
问题,
MiniGPT-4
在第一阶段成功识别物体,但在第二阶段根据排列规则进行推理时失败。对于
Bongard
问题,
MiniGPT-4
在第一阶段成功识别人类活动,并在第二阶段逻辑推理中掌握了推理,但在答案生成阶段失败,并且在利用规则回答问题时迷失方向。基于以上案例,能够了解
LLM-based
模型在推理任务中的缺点,即其良好的概念理解能力,但在逻辑推理和答案生成方面表现不足。

Approximation Principle Verification

接下来,验证使用来自多个领域数据训练推理器可以确保推理器具有更好的泛化能力,在
SVRT

Bongard-HOI

Filtered-CoPhy

Balls
任务、
Filtered-CoPhy

Collision
任务以及
VQAv2
上进行了相关实验。这些数据集涵盖了
2D
谜题、
3D
视频和
VQA
任务,提供了多样化和多模态的数据,利用
Filtered-CoPhy

Collision
任务作为测试的基准。

论文引入越来越多跨领域数据集来训练推理器,并将其与目标测试数据集的分离编码器配对。鉴于各个数据集之间的固有差异,在推理器之前引入了基于
MLP
的高度轻量级适配器。为了均衡每个数据集对推理引擎的贡献,调整了用于跨数据集训练的样本大小。具体而言,使用了
1,000

3,000
的样本大小。

如表
6
所示,随着训练数据集数量的增加,推理器逐步改善。尽管处理来自多样化领域的更多数据集显著增加了复杂性,但经过训练的推理器在跨领域的
Filtered-CoPhy
上表现良好。这表明随着训练数据集领域的增加,推理器将专注于任务无关的纯推理,这验证了我们的“逼近原理”。

Additional Ablation Study

在表
7
中展示了在符号编码器是否使用预训练模型的消融实验,选择了
RAVEN

CVR

SVRT
数据集,并采用
ImageNet
作为预训练数据集。结果非常接近,可能的原因是
ImageNet
与这三个推理数据集之间存在明显的领域差异。

论文测试了
CLIP
作为通用和大型基础模型在充当通用符号编码器时的表现,将
CLIP
作为多模态数据集的视觉编码器,接着使用
MLP
作为推理器,并采用任务特定的头部网络。如表
8
所示,即使在微调后,使用
CLIP
得到的结果仍不及最佳的
One-for-All
方法。这验证了即使像
CLIP
这样的大型模型也无法完成不同数据集的符号化任务,从而确认了采用分离编码器和共享推理器框架设计的理论依据。



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

work-life balance.

论文提出了一个专门针对斯瓦希里语自然场景文本检测和识别的数据集,这在当前研究中是一个未充分开发的语言领域。数据集包括
976
张带标注的场景图像,可用于文本检测,以及
8284
张裁剪后的图像用于识别。

来源:晓飞的算法工程笔记 公众号

论文: The First Swahili Language Scene Text Detection and Recognition Dataset

Introduction


如今,沟通很大程度上依赖于文本内容。文本是一种极为优秀的沟通方式,其影响力也能持续非常长的时间。场景文本广泛存在且包含着相当丰富的语义和信息,有助于理解现实世界。各种服务如报纸、医院、金融服务、保险和法律机构日益将大多数文档数字化以便实际应用。应用场景如汽车辅助、工业自动化、机器人导航、实时场景翻译、欺诈检测、图像检索、产品搜索等,这些都依赖于场景文本识别,并且这些应用每天都在不断进化和发展。现在,理解和解释图像中包含的文本内容变得至关重要。此外,文本无处不在,出现在许多关键的自然场景中:道路标志、广告、海报、街道、餐馆、商店等。

近年来,研究人员在挑战性场景中检测和识别文本的模型方面取得了显著进展,这些场景包括模糊图像、非传统背景、变化的光照条件、曲线文字或在恶劣环境中捕获的图像等。然而,大多数研究集中在英语和汉语等广泛使用的语言上,对资源有限地区如印度乡村和非洲的其他语言的关注和资源较少。因此,许多世界语言缺乏适当的数据集和量身定制的模型,这使得在这些语言中有效解决场景图像中文本检测和识别的挑战变得困难。

斯瓦希里语,又称基斯瓦希里语,是非洲大陆上使用最广泛的语言之一。超过
1
亿人口在包括坦桑尼亚、乌干达、刚果民主共和国、布隆迪和肯尼亚在内的多个非洲国家使用斯瓦希里语。该语言是坦桑尼亚和肯尼亚的官方语言,并广泛用于公共管理、教育和媒体领域。斯瓦希里语从阿拉伯语(约占
40%
)、波斯语、葡萄牙语、英语和德语等外语中借用了许多词汇。尽管如此,斯瓦希里语仍被归类为资源匮乏的语言之一,自然语言处理任务受到了注释数据稀缺的限制。

虽然斯瓦希里语使用拉丁字母表,但大多数涉及拉丁字母表的大型数据集主要集中在拥有不同语言特征的语言,比如英语。缺乏关注导致了斯瓦希里语,这种被数百万人使用的语言,没有专门的资源来优化和微调文本检测和识别模型以适应其独特的特征。表
1
列出了该语言与英语相比的一些特征。

本文的主要目标是为斯瓦希里语开发一个全面的场景文本数据集:
Swahili-text
。这个图像集合旨在满足专门数据集的需求,为评估现有模型提供基准,并帮助研究社区开发斯瓦希里语场景文本检测和识别的新的最先进方法。
Swahili-text
包含
976
张图片,大部分来自坦桑尼亚的城市,其他来自社交媒体。这些图片包括商店标签、广告横幅、海报和街道名称。每张图片在单词级别上都进行了手动注释。据作者所知,
Swahili-Text
是第一个专为斯瓦希里语场景文本检测和识别开发的全面数据集。

Related work


Swahili Language Datasets for Natural Language Processing

斯瓦希里语仍然被归类为资源匮乏的语言。由于注释数据稀缺,自然语言处理任务受到了限制。然而,随着深度学习和语言模型的发展,许多数据集开始对语言建模任务提供越来越多的支持。其中,
Helsinki
数据集是最常用的数据集之一,专门用于斯瓦希里语的语言研究。该数据集提供了未注释和已注释版本的斯瓦希里语文本集合。该数据集旨在支持语言分析、语料库语言学以及与斯瓦希里语自然语言处理任务相关的各种研究工作。

Gelas
等人开发了一个用于语言建模任务的注释数据集。该数据集包含来自不同斯瓦希里语在线媒体平台的句子,涵盖了体育、一般新闻、家庭、政治和宗教等多个领域的句子。总共有
512,000
个独特单词。
Shikali
等人将该数据集与斯瓦希里语音节字母表结合,并改编了
Mikolov
等人提出的英语词类比数据集。
Barack W
等人开发了
Kencorpus
斯瓦希里语问答数据集(
KenSwQuAD
),旨在应对低资源语言,特别是斯瓦希里语中问答数据集的稀缺性,增强机器对自然语言的理解能力,应用于斯瓦希里语言者的互联网搜索和对话系统等任务。
Alexander R
等人则关注低资源语言(如斯瓦希里语)中语音数据集的缺乏,特别是口语数字识别领域。该研究开发了一个斯瓦希里语口语数字数据集,并研究了跨语言和多语言预训练方法对口头数字识别的影响。

这些数据集旨在促进斯瓦希里语言建模和自然语言处理任务的研究,然而在场景文本检测和识别任务中,目前还不存在一个全面的用于斯瓦希里语注释场景文本图像的数据集。

Latin Script Scene Text Datasets

场景文本识别领域受到标准数据集的影响,这些数据集使研究人员能够节省大量时间和精力来收集和注释数据。与拉丁字母场景文本识别相关的流行数据集有以下:

  1. ICDAR
    数据集在文档分析和识别领域非常流行。
    ICDAR 2013
    数据集包含
    462
    张高分辨率的自然场景图像,如户外场景、标志和海报。该数据集引入了多方向文本、不同光照条件以及混合字体和文字大小的挑战,以促进强大的文本识别算法的发展。
    ICDAR 2015
    偶发场景文本数据集包含通过
    Google Glass
    捕捉的
    1,670
    张图像。该数据集包括具有非传统文本形状、曲线文本和不同语言文本的偶发场景文本。
  2. Total-text
    数据集针对多方向和曲线文本问题提出。它包含具有不同方向文本的图像,主要是曲线文本。
  3. MSRA-TD500
    数据集结合了英文和中文词汇,也非常受欢迎。它包含来自实际场景的
    500
    张任意方向的图像,并以句子级别进行了注释。除了拉丁字母脚本的数据集外,还提出了多语言场景文本识别的几个多语言数据集。

然而,大多数这些数据集并不包括斯瓦希里语。据知,目前尚未创建用于斯瓦希里语场景文本检测和识别的公共数据集。虽然一些用于英语的数据集可以用来,因为它们使用相同的字母表,但它们并不像一个专门针对斯瓦希里语的数据集那样有效。

Scene Text Detection and Recognition Methods

深度学习技术的爆炸性发展显著影响了场景文本检测和识别领域,为场景文本检测和识别打开了全新的可能性,能够从文本图像中提取更强大和具有区分性的特征。

文本检测和文本识别可以看作是两个独立的任务。在文本检测阶段,其目标是识别并标记输入图像中存在文本的区域。存在三种主要的方法:基于回归、基于部分和基于分割的方法。基于回归的方法直接回归边界框。通过将文本检测转化为回归问题,模型学习估计文本实例的空间分布,这使其非常适合需要精确定位文本区域的场景。基于部分的方法识别并将文本部分与单词边界框关联起来。基于分割的方法结合像素级预测和后处理技术,利用语义分割和基于
MSER
的算法等技术检测文本实例。

文本识别涉及将检测到的文本区域转换为字符实例,主要有两种方法:
connectionist temporal classification

CTC
)模型和注意力机制模型。
CTC
模型使用递归神经网络计算基于单帧预测的标签序列的条件概率,该过程包括三个重要步骤:使用卷积网络从文本区域提取特征、使用递归神经网络在每帧预测标签分布以及后处理步骤将每帧的预测转换为最终的标签序列。

注意力机制在计算机视觉领域,包括场景文本识别中,取得了显著的成果。注意力机制专注于输入的相关部分,从而在复杂或变化的环境中实现更精确的字符识别。这种方法利用编码结构从文本区域提取特征向量,并利用解码结构生成字符实例。肖等人解决了注意力机制产生无关信息的问题,并提出了一种评估注意力结果与查询之间相关性的方法。通过将
Attention on Attention

AoA
)机制整合到文本识别框架中,可以消除无关的注意力,从而提高文本识别的准确性。

尽管在场景文本检测和识别方面取得了显著进展,但标注训练数据的不足仍然是一个障碍。深度学习算法在泛化到现实世界场景时受到大规模数据集稀缺的限制,尤其是对于低资源语言或尚未研究的语言,包括带有标注的场景文本图像的数据集。

Swahili Text Dataset


Dataset Description

斯瓦希里语场景文本检测和识别数据集包含
976
张自然场景图像,数据来源多样。这些数据由专注于计算机视觉和自然语言处理的研究人员以及斯瓦希里语母语者收集。图像来源广泛,包括互联网资源和在坦桑尼亚城市使用手机相机直接拍摄的图像。这确保了从斯瓦希里语使用地区获得了具有代表性的场景集合。

为确保收集到的图像准确性和相关性,实施了严格的质量控制措施,并特别注意消除光照不均和模糊的图像。该数据集经历了预处理步骤,以移除具有不良质量属性的图像,并对具有不完整数据的实例进行了修正或排除,以维护数据的完整性。数据集中的每个图像都以
JPEG
格式存储。

斯瓦希里语文本数据集包含描绘自然场景的图像,其中包含斯瓦希里语文本元素,如街道标志、街道名称、广告、商店名称、横幅和其他常见于斯瓦希里语使用地区的标识物。为了便于场景文本检测和识别任务,数据集已进行标注,标注过程由领域专家进行,以确保对文本区域的准确注释。


1
展示了数据集中包含的部分图像。用于识别任务,数据集中的图像已裁剪为
8284
张图像。图
2
展示了斯瓦希里语文本裁剪图像的统计数据,包括按文本长度分组的图像数量和字符出现的分布情况。

Annotation

为确保文本检测和识别的准确性,并评估系统的性能,准确的文本实例注释至关重要。因此,斯瓦希里语文本数据集采用了细致的手动注释方法。每个图像中的每个文本区域都用单个边界框进行注释,以确保在处理斯瓦希里语文本的各种形状和位置时能够准确地标注。

每个图像的文本实例注释被收集到一个单独的文件中。该文件包含单词的边界框坐标和相应的文本转录。边界框是一个具有
n
个点的多边形,每个点都有水平位置
x1
和垂直位置
y1
的坐标。不可读的文本实例仅用边界框标记,以便于检测但不参与文本的识别。图
3
展示了一个带有注释的示例图像。

Experiments


Text Detection Experiment

Text Recognition Experiment



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

work-life balance.

前言

一个摄像头视野不大的时候,我们希望进行两个视野合并,这样让正视的视野增大,从而可以看到更广阔的标准视野。拼接的方法分为两条路,第一条路是stitcher类,第二条思路是特征点匹配。
本篇使用stitcher匹配,进行两张图来视野合并拼接。


Demo

在这里插入图片描述


两张图拼接过程


步骤一:打开图片

在这里插入图片描述

cv::Mat mat = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/29.jpg");
cv::Mat mat2 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/30.jpg");


步骤二:加入图片进入队列

在这里插入图片描述

std::vector<cv::Mat> vectorMat;
vectorMat.push_back(mat);
vectorMat.push_back(mat2);


步骤三:创建拼接类

在这里插入图片描述

cv::Ptr<cv::Stitcher> pStitcher = cv::Stitcher::create(cv::Stitcher::PANORAMA, false);
//cv::Ptr<cv::Stitcher> pStitcher = cv::Stitcher::create(cv::Stitcher::SCANS, false);


步骤四:拼接

在这里插入图片描述

cv::Ptr<cv::Stitcher> pStitcher = cv::Stitcher::create(cv::Stitcher::SCANS, false);
LOG;
cv::Stitcher::Status status = pStitcher->stitch(vectorMat, resultMat);
LOG;
if(status != cv::Stitcher::OK)
{
std::cout << "Failed to stitch, status =" << status << std::endl;
return;
}

对拼接后显示所有:
在这里插入图片描述

cv::namedWindow("mat", cv::WINDOW_NORMAL);
cv::imshow("mat", mat);
cv::resizeWindow("mat", cv::Size(400, 300));

cv::namedWindow("mat2", cv::WINDOW_NORMAL);
cv::imshow("mat2", mat2);
cv::resizeWindow("mat2", cv::Size(400, 300));

cv::namedWindow("resultMat", cv::WINDOW_NORMAL);
cv::imshow("resultMat", resultMat);
cv::resizeWindow("resultMat", cv::Size(400, 300));


步骤五:对图像进行宽高黑边裁剪(略)

直接写个算法对周边黑色区域进行矩形探测,然后裁剪即可,方法很多,一般我们拍照的图片都不是全黑的,而黑边是全黑的,这个算法写起来有明显的特征。


耗时测试


原始图像1701x1280像素,耗时477ms左右

在这里插入图片描述

在这里插入图片描述

原始图片1701x1280像素,拼接消耗的时间约477ms:


图像缩小至400x300像素,耗时390ms左右

然后对其图片进行缩放后测试其耗时:
在这里插入图片描述

在这里插入图片描述

将图片统一缩放为800x600分辨率,其拼接耗时在390ms左右。


图像放大至1920x1080像素,耗时530ms左右

在这里插入图片描述

在这里插入图片描述

将图片放大至1920x1080分辨率,其拼接耗时在530ms左右


注意

本次测试并不严谨,基于同样图的缩放,单纯控制像素缩放来比较,但是得出的结论可以反应图像大小的影响,最终的耗时是受多方因素影响,包括但不限于检测特征电的数量、重叠区域的大小、像素分辨率、多图。


结论

这种方式适合对照片进行拼接,对黑边处理之后,效果很不错,但是,调用stitcher类实现时对图片的特征匹配有要求,一些特征点不够的图片无法拼接,并且,当图片较大或多张图片拼接时,速度慢。所以,倘若放到视频上,一秒钟25-60fps,那就肯定不行了。
SIFT算法拼接,SIFT算法可以提供较高的准确率,得到的图片需要经过再次处理,才能得到相对较好的图片,
ORB算法拼接,算法的速度非常快,但是最容易出现问题,且得到的图片需要经过再次处理,才能得到相对较好的图片,


函数原型


函数cv::Stitcher::create

static Ptr<Stitcher> create(Mode mode = PANORAMA, bool try_use_gpu = false);
  • 参数一:拼接模式枚举,只有2个值PANORAMA和SCANS
    PANORAMA:创建照片全景的模式,期望图像处于透视状态;
    SCANS:合成扫描的模式。期望仿射变换下的图像,默认情况下不补偿曝光。(由于咱们一般总归有角度偏移,所以这个方式对拼接图像有较高要求)
  • 参数二:是否使用gpu,这种方式编译opencv得带上gpu编译,编译opencv的时候开启支持gpu,在arm上的话,需要先确认芯片是否支持GPU,然后安装GPU驱动,然后编译opencv支持GPU选项,才可以。


函数cv::Stitcher:: stitch

CV_WRAP Status stitch(InputArrayOfArrays images, OutputArray pano);
  • 参数一:输入图像列表
  • 参数二:输出拼接结果
Status stitch(InputArrayOfArrays images, const std::vector<std::vector<Rect> > &rois, OutputArray pano);
  • 参数一:输入图像列表
  • 参数二:输入图像列表依次需要拼接的区域
  • 参数三:输出拼接结果


Demo源码

void OpenCVManager::testStitchImages()
{
cv::Mat mat = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/29.jpg");
cv::Mat mat2 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/30.jpg");

#if 0
// 拼接环视全景,特征点是完全不够,无法使用该方法,同时就算能拼也无法达到新能要求
cv::Mat mat = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/front_2024-08-22_17-15-08_result.png");
cv::Mat mat2 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/left_2024-08-22_17-15-10_result.png");
cv::Mat mat2 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/right_2024-08-22_17-15-11_result.png");
#endif

#if 1
// 对图片进行缩放,测试其拼接耗时
cv::resize(mat, mat, cv::Size(1920, 1080));
cv::resize(mat2, mat2, cv::Size(1920, 1080));
#endif

std::vector<cv::Mat> vectorMat;
vectorMat.push_back(mat);
vectorMat.push_back(mat2);


cv::Mat resultMat;

cv::Ptr<cv::Stitcher> pStitcher = cv::Stitcher::create(cv::Stitcher::PANORAMA, false);
// cv::Ptr<cv::Stitcher> pStitcher = cv::Stitcher::create(cv::Stitcher::SCANS, false);
LOG;
cv::Stitcher::Status status = pStitcher->stitch(vectorMat, resultMat);
LOG;
if(status != cv::Stitcher::OK)
{
std::cout << "Failed to stitch, status =" << status << std::endl;
return;
}


cv::namedWindow("mat", cv::WINDOW_NORMAL);
cv::imshow("mat", mat);
cv::resizeWindow("mat", cv::Size(400, 300));

cv::namedWindow("mat2", cv::WINDOW_NORMAL);
cv::imshow("mat2", mat2);
cv::resizeWindow("mat2", cv::Size(400, 300));

cv::namedWindow("resultMat", cv::WINDOW_NORMAL);
cv::imshow("resultMat", resultMat);
cv::resizeWindow("resultMat", cv::Size(400, 300));

cv::waitKey(0);
}


对应工程模板v1.69.0

在这里插入图片描述