Skip to main content

7 posts tagged with "ML"

View All Tags

对象及其关系在我们周围是无处不在的,关系对于理解对象的重要性可以与对象本身的属性一样重要,例如: 交通网络、生产网络、知识图谱或社交网络。离散数学和计算机科学长期以来一直在将这些网络形式化为图, 由节点以各种不规则方式连接的边组成。然而,大多数机器学习(ML)算法只允许输入对象之间的规则和统 一关系,比如像素网格、单词序列或根本没有关系。

图神经网络(GNN)作为一种强大的技术,利用了图的连接性(类似于旧算法DeepWalkNode2Vec)以及各 个节点和边上的输入特征。GNN可以对整个图进行预测(这种分子会以某种方式发生反应吗?)、对单个节点 进行预测(根据其引用,这个文档的主题是什么?)或对潜在边进行预测(这个产品是否可能与那个产品一起 购买?)。除了对图进行预测外,GNN还是一个强大的工具,用于弥合更典型的神经网络用例之间的鸿沟。它 以连续方式编码图的离散关系信息,从而可以自然地包含到另一个深度学习系统中。

我们很高兴地宣布推出 TensorFlow GNN 1.0(TF-GNN),这是一个经过实际测试的用于大规模构 建GNN的库。它支持在TensorFlow中进行建模和训练,以及从庞大的数据存储中提取输入图。TF-GNN是从头开 始为异构图构建的,其中类型和关系由不同的节点和边集表示。现实世界中的对象及其关系以不同类型出现,TF-GNN 的异构重点使其自然地表示这些对象及其关系。

TensorFlow内部,这些图由类型为tfgnn.GraphTensor的对象表示。这是一种复合张量类型(在一个Python 类中包含多个张量),可以作为tf.data.Datasettf.function等中的一等公民被接受。它存储了图结构以及 附加到节点、边和整个图上的特征。可以将GraphTensors的可训练变换定义为高级Keras API中的Layers对象, 也可以直接使用tfgnn.GraphTensor原语。

GNNs:在上下文中对对象进行预测

为了说明问题,让我们来看看TF-GNN的一个典型应用:在由交叉引用大型数据库的表定义的图中,预测某种类型节点的属性。 例如,计算机科学(CS)arXiv论文的引用数据库,具有一对多的引用关系和多对一的被引关系,我们希望预测每篇论文的主题领域。

与大多数神经网络一样,GNN是在许多带标签的示例数据集(约数百万个)上进行训练的,但每个训练步骤只包括一个更小的批处理 训练示例(比如说,数百个)。为了扩展到数百万个,GNN在基础图中的一系列相对较小的子图上进行训练。每个子图包含足够的原始数据, 以计算标记节点的GNN结果并训练模型。这个过程——通常称为子图采样——对于GNN的训练非常重要。大多数现有的工具都是以批处理方式进 行采样,生成静态子图进行训练。TF-GNN提供了改进这一点的工具,通过动态和交互式采样。

如图所示,子图采样的过程,其中从较大的图中采样小的、易于处理的子图,以创建用于 GNN 训练的输入示例

TF-GNN 1.0 首次推出了灵活的 Python API,用于配置所有相关规模的动态或批量子图采样:在 Colab 笔记本(如本笔记本)中交互式采样, 用于对存储在单个训练主机主内存中的小型数据集进行高效采样,或通过 Apache Beam 对存储在网络文件系统中的庞大数据集(多达数亿节点和 数十亿条边)进行分布式采样。详情请分别参阅我们的内存采样和基于束的采样用户指南。

在这些相同的采样子图上,GNN 的任务是计算根节点的隐藏(或潜在)状态;隐藏状态聚合并编码根节点邻域的相关信息。一种经典的方法是消息 传递神经网络。在每一轮消息传递中,节点会沿着传入边接收来自邻居的消息,并根据这些消息更新自己的隐藏状态。经过 n 轮后,根节点的隐藏 状态就会反映出 n 条边上所有节点的总信息(如下图 n = 2)。信息和新的隐藏状态由神经网络的隐藏层计算。在异构图中,对不同类型的节点 和边使用单独训练的隐藏层通常是有意义的。

如图所示,这是一个简单的消息传递神经网络,在每个步骤中,节点状态从外部节点传播到内部节点,并在内部节点中进行池化以计算新的节点状态。一旦到达根节点,就可以做出最终的预测。

与其他神经网络训练一样,在 GNN 的隐藏状态上为标注节点设置一个输出层,计算损失(衡量预测误差),并通过反向传播更新模型权重,训练设置就完成了。

除了有监督的训练(即最小化由标签定义的损失),GNN 还可以无监督的方式(即不带标签)进行训练。这样,我们就能计算出节点离散图结构及其特征的 连续表示(或嵌入)。这些表示通常用于其他 ML 系统。这样,由图编码的离散关系信息就能被纳入更典型的神经网络用例中。TF-GNN 支持对异构图的无 监督目标进行细粒度规范。

构建 GNN 架构

TF-GNN 库支持构建和训练不同抽象层次的 GNN

在最高级别,用户可以使用该库捆绑的任何预定义模型,这些模型以 Keras 层表达。除了一小部分研究文献中的模型之外,TF-GNN 还附带了一个高度可配置 的模型模板,该模板提供了经过精心挑选的建模选择,我们发现这些选择为我们的许多内部问题提供了强有力的基准。模板实现了 GNN 层;用户只需初始化 Keras 层即可。

import tensorflow_gnn as tfgnn
from tensorflow_gnn.models import mt_albis

def model_fn(graph_tensor_spec: tfgnn.GraphTensorSpec):
"""Builds a GNN as a Keras model."""
graph = inputs = tf.keras.Input(type_spec=graph_tensor_spec)

# Encode input features (callback omitted for brevity).
graph = tfgnn.keras.layers.MapFeatures(
node_sets_fn=set_initial_node_states)(graph)

# For each round of message passing...
for _ in range(2):
# ... create and apply a Keras layer.
graph = mt_albis.MtAlbisGraphUpdate(
units=128, message_dim=64,
attention_type="none", simple_conv_reduce_type="mean",
normalization_type="layer", next_state_type="residual",
state_dropout_rate=0.2, l2_regularization=1e-5,
)(graph)

return tf.keras.Model(inputs, graph)

在最底层,用户可以根据在图中传递数据的原语,从头开始编写 GNN 模型,例如将数据从一个节点广播到它的所有传出边,或将数据从它的所有传入边汇集到一个节点(例 如计算传入信息的总和)。当涉及特征或隐藏状态时,TF-GNN 的图数据模型对节点、边和整个输入图一视同仁,因此不仅能直接表达以节点为中心的模型(如上文讨论的 MPNN),还能表达更一般形式的图网络。在核心 TensorFlow 的基础上,可以使用 Keras 作为建模框架,但没有必要这样做。有关更多细节和建模的中间层次, 请参阅 TF-GNN 用户指南和模型集。

训练编排

虽然高级用户可以自由地进行自定义模型训练,但 TF-GNN Runner 还提供了一种简洁的方法来协调常见情况下 Keras 模型的训练。 一个简单的调用可能如下所示:

from tensorflow_gnn import runner

runner.run(
task=runner.RootNodeBinaryClassification("papers", ...),
model_fn=model_fn,
trainer=runner.KerasTrainer(tf.distribute.MirroredStrategy(), model_dir="/tmp/model"),
optimizer_fn=tf.keras.optimizers.Adam,
epochs=10,
global_batch_size=128,
train_ds_provider=runner.TFRecordDatasetProvider("/tmp/train*"),
valid_ds_provider=runner.TFRecordDatasetProvider("/tmp/validation*"),
gtspec=...,
)

The RunnerML问题提供了现成的解决方案,如分布式训练和在Cloud TPUs上对tfgnn.GraphTensor进行固定形状填充。除了对单个任务进行训练(如上所示), 它还支持同时对多个(两个或更多)任务进行联合训练。例如,无监督任务可以与监督任务混合,以提供特定应用的归纳偏差的最终连续表示(或嵌入)。调用者只需 要用任务映射替换任务参数即可:

from tensorflow_gnn import runner
from tensorflow_gnn.models import contrastive_losses

runner.run(
task={
"classification": runner.RootNodeBinaryClassification("papers", ...),
"dgi": contrastive_losses.DeepGraphInfomaxTask("papers"),
},
...
)

此外,TF-GNN Runner 还包括用于模型归因的集成梯度实现。集成梯度输出是一个 GraphTensor,其连接性与观察到的 GraphTensor 相同,但其特征用梯度值代替, 在 GNN 预测中,较大的梯度值比较小的梯度值贡献更多。用户可以检查梯度值,查看其 GNN 使用最多的特征。

结论

总之,我们希望 TF-GNN 能够推动 GNNTensorFlow 中的大规模应用,并促进该领域的进一步创新。如果您想了解更多信息,请尝试我们使用流行的 OGBN-MAG 基准进行的 Colab 演示(在浏览器中进行,无需安装),浏览我们的其他用户指南和 Colab,或阅读我们的论文。

文章出处

鱼雪

苹果的人工智能团队发布了MLX——专为苹果芯片设计的新机器学习框架。

MLX 是一个类似于 NumPy 的数组框架,专为在苹果芯片上实现高效、灵活的机器学习而设计,由苹果机器学习研究部门为您带来。

除少数例外情况外,其 Python API 与 NumPy 非常相似。MLX 还有一个功能齐全的 C++ API,与 Python API 非常相似。

MLX 与 NumPy 的主要区别在于:

  • 可组合函数变换:MLX 具有可组合的函数转换,用于自动微分、自动矢量化和计算图优化。

  • 懒计算:MLX 中的计算是懒惰的。只有在需要时才将数组实体化。

  • 多设备:操作可在任何支持的设备(CPU、GPU......)上运行

MLX 的设计灵感来自 PyTorch、Jax 和 ArrayFire 等框架。 这些框架与 MLX 的一个显著区别是采用了统一的内存模型。 Apple Silicon芯片的硬件内存结构就使用的是统一内存,即显存和内存是同一块硬件, 这样就避免了硬件之间的数据交换。比如使用CUDA进行计算,数据流是这样的,首先收据加载到CPU的内存中, 而后,数据从CPU内存中移动到显卡设备的显存中,计算完成之后,数据重新从显卡的显存中移动会CPU的内存中, 完成整个计算过程。这个由于显卡只处理计算,作为一个设备只能被动调度。 MLX 中的数组位于共享内存中。 对 MLX 阵列的操作可以在任何支持的设备类型上执行,而无需执行数据拷贝。 目前支持的设备类型有 CPU 和 GPU。

参考链接:

鱼雪

JAX 是 TensorFlow 和 PyTorch 的新竞争对手。 JAX 强调简单性而不牺牲速度和可扩展性。 由于 JAX 需要更少的样板代码,因此程序更短、更接近数学,因此更容易理解

  • 使用 import jax.numpy 访问 NumPy 函数,使用 import jax.scipy 访问 SciPy 函数。
  • 通过使用 @jax.jit 进行装饰,可以加快即时编译速度。
  • 使用 jax.grad 求导数
  • 使用 jax.vmap 进行矢量化,并使用 jax.pmap 跨设备进行并行化。

JAX 遵循函数式编程理念。 这意味着您的函数必须是自包含的,或者说是纯函数:不允许有副作用。

从本质上讲,纯函数就像数学函数。拥有输入,输出,但不与外界通信。

  • 以下代码片段是一个非纯函数式的示例
import jax.numpy as jnp

bias = jnp.array(0)
def impure_example(x):
total = x + bias
return total
note

注意 impure_example 之外的偏差(bias)。

在编译期间,偏差(bias)可能会被缓存,因此不再反映偏差(bias)的变化。

  • 这是一个纯函数的例子。
def pure_example(x, weights, bias):
activation = weights @ x + bias
return activation

在这里,pure_example 是独立的:所有参数都作为参数传递

鱼雪

CandleRust 的极简 ML 框架,重点关注性能(包括 GPU 支持)和易用性。 今天我们来使用Candle完成一个深度学习的Hello World案例:手写数字识别。 我们使用最简单的线性模型来训练一个自己的手写数字识别模型,作为Candle框架的 最简单入门案例。

环境

  • Rust: 1.75.0-nightly
  • candle-core: 0.3.0
  • candle-nn: 0.3.0
  • candle-datasets: 0.3.0
tip

candle-nn当前版本中依赖了Rust nightly

Cargo.toml内容如下

  1. rand: 随机数
  2. anyhow: 处理异常
  3. clap: 解析命令行参数
[package]
name = "linear_mnist"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
candle-core = { git = "https://github.com/huggingface/candle.git", version = "0.3.0" }
candle-nn = { git = "https://github.com/huggingface/candle.git", version = "0.3.0" }
rand = "0.8.5"
anyhow = "1"
clap = { version = "4.4.4", features = ["derive"] }
candle-datasets = { git = "https://github.com/huggingface/candle.git", version = "0.3.0" }

创建项目并安装Candle相关模块

  1. 使用cargo new创建linear_mnist项目
  2. 进入项目目录
  3. 安装candle三个模块
    • candle-core
    • candle-nn
    • candle-datasets
  4. 安装其他依赖库
    • rand
    • anyhow
    • clap

具体操作如下:

cargo new linear_mnist
cd linear_mnist

cargo add --git https://github.com/huggingface/candle.git candle-core
cargo add --git https://github.com/huggingface/candle.git candle-nn
cargo add --git https://github.com/huggingface/candle.git candle-datasets

代码

导入相关依赖

  1. 导入clap::Parser解析命令行参数
  2. 导入candle_core的相关依赖
    • Device: 数据计算时放置的设备
    • Result: 处理异常
    • Tensor: 张量数据类型
    • D: 是一个enum,包含Minus1Minus2
    • DType: 数据类型enum结构,包含支持的数据类型
  3. 导入candle-nn的相关依赖
    • loss: 损失函数相关操作
    • ops: 函数操作,如log_softmax
    • Linear: 线性模型
    • Module: 由于Linear的依赖
    • Optimizer: 优化器
    • VarBuilder: 构建变量
    • VarMap: 用于存储模型变量
use clap::{ Parser };
use candle_core::{ Device, Result, Tensor, D, DType };
use candle_nn::{ loss, ops, Linear, Module, Optimizer, VarBuilder, VarMap };

定义相关配置

  1. 定义图像维度数量和标签数量的常量
  2. 定义命令行参数解析,并添加指令宏#[derive(Parser)],可以使用clap::Parser解析命令行参数
    • learning_rate: 学习率
    • epochs: 模型训练迭代次数
    • save_model_path: 训练好的模型保存路径
    • load_model_path: 加载预训练模型路径
    • local_mnist: 本地MNIST数据集目录
  3. 定义训练参数结构体TrainingArgs
  4. 定义线性模型结构体LinearModel

具体代码如下:

const IMAGE_DIM: usize = 784;
const LABELS: usize = 10;

#[derive(Parser)]
struct Args {
#[arg(long)]
learning_rate: Option<f64>,

#[arg(long, default_value_t = 10)]
epochs: usize,

#[arg(long)]
save_model_path: Option<String>,

#[arg(long)]
load_model_path: Option<String>,

#[arg(long)]
local_mnist: Option<String>,
}

struct TrainingArgs {
learning_rate: f64,
load_path: Option<String>,
save_path: Option<String>,
epochs: usize,
}

struct LinearModel {
linear: Linear,
}

定义模型

  1. 定义Model trait
  2. LinearModel实现Model trait
    • new: 初始化模型
    • forward: 模型结构,前向传播
  3. linear_z是具体创建Linear模型
    • 创建模型张量变量并调用candle-nn::Linear创建线性模型返回

具体代码如下:

trait Model: Sized {
fn new(vs: VarBuilder) -> Result<Self>;
fn forward(&self, xs: &Tensor) -> Result<Tensor>;
}

impl Model for LinearModel {
fn new(vs: VarBuilder) -> Result<Self> {
let linear: Linear = linear_z(IMAGE_DIM, LABELS, vs)?;
Ok(Self { linear })
}

fn forward(&self, xs: &Tensor) -> Result<Tensor> {
self.linear.forward(xs)
}
}

fn linear_z(in_dim: usize, out_dim: usize, vs: VarBuilder) -> Result<Linear> {
let ws: Tensor = vs.get_with_hints((out_dim, in_dim), "weight", candle_nn::init::ZERO)?;
let bs: Tensor = vs.get_with_hints(out_dim, "bias", candle_nn::init::ZERO)?;
Ok(Linear::new(ws, Some(bs)))
}

定义模型训练函数

  1. 输入参数
    • m: 数据集
    • args: 训练参数TrainingArgs
  2. 获取或设置模型运算的设备Device::Cpu
  3. 从数据集m中获取训练数据和标签,测试数据和标签
  4. 创建varmap用来存储模型参数
  5. 创建vs变量构造,存储模型参数,并将其传入到Model::new
  6. 如果命令行传入load_model_path,则会加载预训练模型
  7. 创建优化器SGD
  8. 根据epochs迭代训练模型
    • 前向传播得到logits
    • 计算概率log_softmax
    • 计算损失函数值
    • 反向传播sgd.backward_step()
    • 输入测试数据得到测试数据准确率test_accuracy
    • 每个epoch花费的时间epoch_duration
  9. 如果命令传入save_model_path,则会保存模型参数
    • 确保存放模型的目录已经建立

具体代码如下:

fn train<M: Model>(
m: candle_datasets::vision::Dataset,
args: &TrainingArgs) -> anyhow::Result<()> {

let dev = Device::Cpu;

let train_labels = m.train_labels;
let train_images = m.train_images.to_device(&dev)?;
let train_labels = train_labels.to_dtype(DType::U32)?.to_device(&dev)?;
let test_images = m.test_images.to_device(&dev)?;
let test_labels = m.test_labels.to_dtype(DType::U32)?.to_device(&dev)?;

let mut varmap = VarMap::new();
let vs = VarBuilder::from_varmap(&varmap, DType::F32, &dev);
let model = M::new(vs.clone())?;

// Load Pre-trained Model Parameters
if let Some(load_path) = &args.load_path {
println!("Loading model from {}", load_path);
let _ = varmap.load(load_path);
}

// Create Optimizer
let mut sgd = candle_nn::SGD::new(varmap.all_vars(), args.learning_rate)?;

// Iterate training model
for epoch in 1..=args.epochs {
let start_time = std::time::Instant::now();
let logits = model.forward(&train_images)?;
let log_sm = ops::log_softmax(&logits, D::Minus1)?;
let loss = loss::nll(&log_sm, &train_labels)?;

sgd.backward_step(&loss)?;

let test_logits = model.forward(&test_images)?;
let sum_ok = test_logits
.argmax(D::Minus1)?
.eq(&test_labels)?
.to_dtype(DType::F32)?
.sum_all()?
.to_scalar::<f32>()?;
let test_accuracy = sum_ok / test_labels.dims1()? as f32;
let end_time = std::time::Instant::now();
let epoch_duration = end_time.duration_since(start_time);
println!("Epoch: {epoch:4} Train Loss: {:8.5} Test Acc: {:5.2}% Epoch duration: {:.2} second.",
loss.to_scalar::<f32>()?, test_accuracy * 100., epoch_duration.as_secs_f64());
}

// Save Model Parameters
if let Some(save_path) = &args.save_path {
println!("Saving trained weight in {save_path}");
varmap.save(save_path)?
}
Ok(())
}

main函数

  1. 解析命令行参数Args
  2. 根据local_mnist命令行参数指定的目录加载MNIST数据集
  3. 设置学习率
  4. 创建模型训练参数TrainingArgs类型变量training_args并填充设置好的参数
  5. 调用模型训练函数train::<LinearModel>(m, &training_args),传入数据集模型训练参数
fn main() ->anyhow::Result<()> {
let args: Args = Args::parse();
let m: candle_datasets::vision::Dataset = if let Some(directory) = args.local_mnist {
candle_datasets::vision::mnist::load_dir(directory)?
} else {
candle_datasets::vision::mnist::load()?
};

println!("Train Images: {:?}", m.train_images.shape());
println!("Train Labels: {:?}", m.train_labels.shape());
println!("Test Images: {:?}", m.test_images.shape());
println!("Test Labels: {:?}", m.test_labels.shape());

let default_learning_rate: f64 = 0.1;

let training_args = TrainingArgs {
epochs: args.epochs,
learning_rate: args.learning_rate.unwrap_or(default_learning_rate),
load_path: args.load_model_path,
save_path: args.save_model_path,
};

train::<LinearModel>(m, &training_args)
}

训练

  1. 如果saved_model不存在,则需要先创建该目录

  2. 目录结构如下

linear_mnist
├── Cargo.lock
├── Cargo.toml
├── dataset
│   ├── t10k-images-idx3-ubyte
│   ├── t10k-labels-idx1-ubyte
│   ├── train-images-idx3-ubyte
│   └── train-labels-idx1-ubyte
├── saved_model
│   └── minst.safetensors
└── src
└── main.rs
  1. 训练并保存模型参数

HuggingFace Candle 训练手写数字识别

  1. 加载预训练模型继续训练

HuggingFace Candle加载预训练模型

  1. 完整代码地址

Candel Linear Model Training MNIST Classification on Github

参考代码

Candle MNIST Training

鱼雪

CLIP(Contrastive Language-Image Pre-training)是由OpenAI开发的一个深度学习模型,用于处理图像和文本之间的语义关系。CLIP通过对图像和文本进行联合训练,学习到了一个通用的表示空间,使得相似的图像和文本在这个空间中距离较近,而不相似的则距离较远。

监督学习的局限性

由图像分类模型生成的现成特征已被用于图像检索等其他任务中。

但是,这些特征的通用性不强,

因为分类模型是为识别一组固定的类别而训练的。

要在这组类别中添加任何新类别

都需要为这一新类别收集额外的标注图像,

然后重新训练模型。

这是一个耗时且昂贵的过程。

能否利用自监督学习技术来解决这一问题?

能否利用图像说明来生成更好的图像表征并避免标注成本? 也就是说,能否使用自然语言作为监督来学习视觉感知?

主要贡献

作者提出了一项预训练任务(CLIP = Contrastive Language Pre-training),

  • 即预测哪张图片配哪句标题,以便从头开始学习SOTA图像表征。

为此,他们创建了一个从互联网上收集的包含4亿个(图片、文本)配对的数据集:

  • 这种预先训练好的模型可以不费吹灰之力地应用到大多数任务中
  • 而且不需要任何特定数据集的训练,就能与完全有监督的基线模型相媲美。

背景

CLIP从监督图像描述(supervised image captioning):

  • 每张图片(image)都有相应的标题(caption),
  • 用来训练一个模型,
  • 预测相应图片标题中的准确词语。

这是一项艰巨的任务,因为一幅图像可以有多种不同的描述方式,但仍能表达相同的含义。

Contrastive Pre-training(对比预训练)

对比预训练:

  • 考虑一批N个图像(images)及其相应的N个标题(captions)。
  • 有了这些,我们可以再批次中创建NxN可能的(图像、文本)配对。
  • 现在,任务是预测批次中的N个真实对

为此,CLIP通过联合训练:

  • 图像编码器文本编码器
  • 学习多模态嵌入空间
  • 图像编码器产生特征向量I;
  • 类似地,文本编码器产生一个特征向量T;
  1. 对于N个实数对,我们希望最大化IT之间的余弦相似度
  2. 对于$N^2-N$不正确的配对,我们希望最小化IT之间的余弦相似度

对比预训练


|----------| |---------------| |----------|
| Image | ---> | Image Encoder | ---> | I Vector | ---------------|
|----------| |---------------| |----------| V
|---------------|
| |
| |
| |
|----------| |---------------| |----------| | |
| Text | ---> | Text Encoder | ---> | T Vector |------> | |
|----------| |---------------| |----------| |---------------|

图片来源于论文

零样本预测

考虑图像分类任务, 预测时,对于单张图像,图像编码器将生成一个特征向量$I_1$ 为了识别图像类别, 文本编码器会嵌入目标数据集的类别名称, 生成N个特征向量$T_1,T_2,......$,以此类推。 N表示目标数据集中的类别数

Create dataset classifier from label text
|-------|
| plane |
| car | |-----------------------| |--------------|
| dog | -----> | A photo of a {object} | ----------------> | Text Encoder |
| bird | |-----------------------| |--------------|
| cat | |
|-------| |
|
Use for zero-shot prediction V
|-------| |---------------| |-----| |-----|-----|-----|-----|-----|
| Image | -----> | Image Encoder | -----> | I_1 | | T_1 | T_2 | T_3 | ... | T_N |
|-------| |---------------| |-----| |-----|-----|-----|-----|-----|
| |
| .* |
|-----------------------------------|
|
V
|--------|--------|--------|-----|--------|
|I_1.*T_1|I_1.*T_2|I_1.*T_3| ... |I_1.*T_N|
|--------|--------|--------|-----|--------|
|
V
|--------------------|
| A photo of a `dog` |
|--------------------|

图片来源于论文

模型细节

对于图像编码器,作者评估了两种不同的架构:

  • ResNet-50
  • ViT

ResNet-50

他们使用了改进的ResNet-D架构,

并进行了抗锯齿矩阵-2模糊池化(anti-aliased rect-2 blur pooling)处理。

他们还用Transformer-style attention pooling mechanism替换了global average pooling layer

ViT(Vision Transformer)

作者在Transformer之前

组合patchposition embedding进行了额外的归一化处理( use an additional layer normalization to the combined patch and position embedding),

并使用了略有不同的初始化方案。

对于文本编码器

使用本文中描述的具有:

  • 6300万个参数(12层 512-wide)
  • 8个注意力头的Transformer

训练

作者训练的模型包括:

  • 5个ResNet

    • ResNet-50
    • ResNet-101
    • 3个EfficientNet风格的ResNet模型
  • 3个ViT

    • ViT-B/32
    • ViT-B/16
    • ViT-L/14
  • The models are trained:

    • 32 epochs
    • using Adam optimizer with decoupled weight decay regularization
    • decay the learning rate using a cosine schedule
    • used a very large minibatch size of 32,768

提示工程的效果(Effect of Prompt Engineering)

图像分类数据集标注了与类名相对应的label IDs

由于CLIP模式在文本为一个完整句子的情况下进行训练的,

因此作者发现使用提示模版(prompt template)A photo of a {label}

是与图像相关联的文本的良好默认设置。

我们可以看到在36各分类数据集中,使用提示工程的分类准确率提高了5个百分点。

图片来源于论文

Zero-shot CLIP vs. Linear Probe

在27个数据集中的16个数据集上:

  • zero-shot CLIP分类器的表现优于基于ResNet-50特征的监督线性分类器。
  • 不过,在大多数数据集上,CLIP的性能仍低于最新水平

图片来源于论文

Limitations(局限性)

  1. CLIP以下任务上性能不佳
    • counting object in an image
    • finding the distance to the nearest object object in an image
  2. 在MNIST等分布外数据集上的表现非常差
    • 在数字OCR上性能很好
    • 但在识别MNIST手写数字方面却不好(准确率88%)
  3. 使用CLIP进行few-shot learning会导致性能不佳
    • from zero-shot to few-shot learning时,性能会出现反直觉的下降
  4. 由于CLIP是根据互联网上查询的文本图像对进行训练的
    • 因此它将会学习许多社会偏见

References

Notes on CLIP: Connecting Text and Images

CLIP paper

鱼雪

特征工程是指在机器学习任务中,通过选择、转换、创建合适的特征,以提高模型性能的过程。 好的特征工程可以帮助模型更好地捕捉数据的规律,提高模型的泛化能力。

从特征到特征

特征工程的目的

  • 为了帮助模型减轻压力

特征工程的核心工作

  • 特征处理的过程是对数据进行微观和宏观投影的过程

特征工程的结果

  • 虽然叫做特征处理
  • 但特征本身没有变化
  • 变的知识观察的维度

举例

如果将光作为一个特征,你只能告诉模型这里有一条光线。 但如果加上一个三棱镜,便可以告诉模型,这里有七种颜色的光。

小结:

特征工程就是在不同角度刻画数据特征,即寻找特征的特征。

从低纬到高维

独热编码的最大特点:

就是能够将数据投射到高维空间,并同时保证它们之间的正交关系。

从空间到世界

单词做独热编码,映射到高维空间中,得到单词的高维向量表达。

独热编码所投影出来的全部单词之间,都是正交关系

相邻单词之间的字面距离,来描述它们的空间关系

  • 使用对比学习
  • 刻画单词之间的相似度
  • 两种方法
    • Skip-gram
    • CBOW

总结

特征工程的本质是空间投影的过程

鱼雪

CandleRust 的极简 ML 框架,重点关注性能(包括 GPU 支持)和易用性。尝试我们的在线演示: whisperLLaMA2T5yoloSegment Anything

模块

Candle项目包括一些crates,如下:

  • candle-book: candle相关的文档
  • candle-core: 核心功能库,核心操作,设备,Tensor结构定义等。
  • candle-nn: 神经网络,构建真实模型的工具
  • candle-examples: 在实际环境中使用库的示例
  • candle-datasets: 数据集和数据加载
  • candle-transformers: Transformer相关实现工具
  • candle-flash-attn: Flash Attention v2实现
  • candle-kernels: CUDA加速实现
  • candle-pyo3: Rust提供的Python接口
  • candle-wasm-examples: Rust WASM示例

其它有用的库:

  • candle-lora: 提供了符合官方peft实现的LoRA实现

特点

  • 语法简单(看起来像PyTorch)
    • 支持模型训练
    • 支持用于自定义操作运算
  • 后端
    • 优化的CPU后端,具有针对x86的可选MKL支持和针对MacAccelerate支持
    • CUDA后端可以再GPU上高效运行,通过NCCL运行多GPU分配
    • WASM支持,在浏览器中运行模型
  • 包含的模型
    • 语言模型
      • LLaMA v1 and v2
      • FaIcon
      • StarCoder
      • Phi v1.5
      • T5
      • Bert
    • Whisper(多语言支持)
    • Stable Diffusion v1.5, v2.1, XL v1.0
    • Wurstchen v2
    • 计算机视觉
      • DINOv2
      • EfficientNet
      • yolo-v3
      • yolo-v8
      • Segmeng-Anything(SAM)
  • 文件格式
    • 加载模型支持的格式如下:
      • safetensors
      • npz
      • ggml
      • PyTorch files
  • 无服务部署
    • 小型且快速的部署
  • 使用llama.cpp量化类型的量化支持

基本用法介绍

  1. 创建张量

    Tensor::new(&[[1f32, 2.], [3., 4.]], &Device::Cpu)?
    Tensor::zeros((2, 2), DType::F32, &Device::Cpu)?
  2. 张量索引

    tensor.i((.., ..4))?
  3. 张量重塑

    tensor.reshape((2, 2))?
  4. 张量矩阵乘法

    a.matmul(&b)?
  5. 张量数据移动到特定设备

    tensor.to_device(&Device::new_cuda(0)?)?
  6. 更改张量数据类型

    tensor.to_dtype(&Device::F16)?
  7. 张量算术运算

    &a + &b
  8. 保存模型

    candle::safetensors::save(&HashMap::from([("A", A)]), "model.safetensors")?
  9. 加载模型

    candle::safetensors::load("model.safetensors", &device)
鱼雪