2024年9月

前言

前几天看到一篇文章,《
消费降级,我的订阅服务瘦身
》。
自己平时花钱有点大手大脚的,也没有统计个每个月固定的开销,现在正好趁这个机会记录一下。现在挣钱不容易,看下哪些开销可以进行降级。

腾讯云 - 服务器

分类 周期及成本 需要程度
网络服务 510元/年 需要/续订

这个服务器是平时自建博客,小功能的必选项,这个没办法一定要续费(不知道有没有便宜的渠道)

域名

分类 周期及成本 需要程度
网络服务 32元/年 需要/续订

和服务器功能类似,必须续费产品

上网

分类 周期及成本 需要程度
网络服务 8元/月 需要/续订

刚需,没办法。我是按需充值,每个月大概8元 10G吧

视频会员(爱奇艺 + B站)

分类 周期及成本 需要程度
生活服务 451元/年 不需要/不续订

之前追剧的时候,订的会员。看来是不需要了,准备关了

话费(3个手机号)

分类 周期及成本 需要程度
生活服务 55元/月 需要/续订

必须,这个不能不续订

CDN

分类 周期及成本 需要程度
网络服务 10元 不需要/不续订

上个月欠费了,直接给关了,感觉自己的服务也没有必要上CDN

其他(健身卡、党费等其他)

分类 周期及成本 需要程度
其他 1000元/年 部分

健身卡,准备不续了,能省下不少钱。

最后

把其他的一些开支+视频网站关闭,每年可以省下小1500元,还是很多的。近期在考虑要不要开通iCloud来保存照片,犹豫中

JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的异同点

一、什么是 structuredClone?

1. structuredClone 的发展

structuredClone
是在
ECMAScript
2021(ES12)标准中引入的,
ECMAScript
2021 规范正式发布于 2021 年 6 月

自 2022 年 3 月起,该功能适用于最新的设备和浏览器版本

Baseline 2022 Newly available
Since March 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

2. structuredClone 的功能

2.1. 功能

全局的
structuredClone()
方法使用结构化克隆算法将给定的值进行深拷贝

2.2. 语法

structuredClone(value)
structuredClone(value, { transfer })

2.2. 参数

  • value:被克隆的对象
  • transfer:可转移的数组

2.3. 返回值

返回值是原始值的深拷贝

2.4.

如果输入值的任一部分不可序列化,则抛出
DataCloneError
异常

3. 用法

3.1. 普通用法

const obj = {
  name: '日升',
  sex: '男',
  blog: {
      csdn: 'https://guoqiankun.blog.csdn.net/?type=blog',
      jj: 'https://juejin.cn/user/2409752520033768/posts'
  },
  games: ['cf', '黑马喽', 'cs'],
  age: 18,
  bool: true,
  set: new Set([1,2,3]),
  map: new Map([['a', 'b'], ['c', 'd']]),
  null: null,
  und: undefined
}
const cloneObj = structuredClone(obj);

image

3.2. transfer 用法

transfer
是一个可转移对象的数组,里面的值并没有被克隆,而是被转移到被拷贝对象上

const buffer = new ArrayBuffer(16);
console.log('buffer', buffer);
const cloned = structuredClone(buffer, { transfer: [buffer] });
console.log('buffer', buffer);
console.log('cloned', cloned);

image

二、structuredClone 和 JSON.parse(JSON.stringify()) 的区别

1. 支持的数据类型

从上面的示例中能看出,
structuredClone
支持了很多中数据类型,基本类型和普通对象都支持

1.1. structuredClone

1.1.1. 支持的类型
  • 基本类型
  • 普通对象
  • Date
    对象
  • RegExp
    对象
  • Map
  • Set
  • ArrayBuffer
  • TypedArrays
  • Blob
  • File
  • ImageData
  • MessagePort
  • null、undefined
  • NaN、Infinity、
    -Infinity
  • 循环引用
1.1.2. 不支持的类型
  • 函数
  • symbol
  • WeakMap
  • WeakSet
  • HTMLElement
1.1.3. 示例
const port1 = new MessageChannel().port1
const obj = {
  date: new Date(),
  regex: /test/i,
  map: new Map([['key1', 'value1'], ['key2', 'value2']]),
  set: new Set([1, 2, 3]),
  arrayBuffer: new ArrayBuffer(8),
  typedArray: new Uint8Array([1, 2, 3]),
  blob: new Blob(['Hello, world!'], { type: 'text/plain' }),
  file: new File(['file content'], 'filename.txt', { type: 'text/plain' }),
  imageData: (() => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    return context.createImageData(100, 100);
  })(),
  messagePort: port1,
  nullValue: null,
  undefinedValue: undefined,
  nanValue: NaN,
  infinityValue: Infinity,
  negativeInfinityValue: -Infinity,
  circularRef: {}
};

// 创建循环引用
obj.circularRef.self = obj;

// 克隆 obj 对象
const clonedObj = structuredClone(obj, {transfer: [port1]});

// 输出以验证
console.log(clonedObj);

image

const obj = {
  func: function() { return "I'm a function"; },   // 函数
  symbol: Symbol('uniqueSymbol'),                  // Symbol
  weakMap: new WeakMap(),                          // WeakMap
  weakSet: new WeakSet(),                          // WeakSet
  element: document.createElement('div')           // HTMLElement
};

// 尝试克隆对象
try {
  const clonedObj = structuredClone(obj);
  console.log(clonedObj); // This line won't run if an error is thrown
} catch (error) {
  console.error('Error:', error); // DataCloneError: Failed to execute 'structuredClone'
}

image

1.2. JSON.parse(JSON.stringify())

1.2.1. 支持的类型
  • 数字
  • 字符串
  • 布尔值
  • 数组
  • 普通对象
1.2.2. 不支持的类型
  • Date、Map、Set、RegExp、Function、undefined、symbol、Infinity、NaN、循环引用...

JSON.stringify 详细信息可以看下下面的文章


你需要了解的JSON.stringify()

1.2.3. 示例
JSON.parse(JSON.stringify({
  a: null,
  b: undefined,
  c: NaN,
  d: Infinity,
  e: () => ({}),
  f: new Map(),
  g: new Set(),
  h: Symbol('a'),
  i: Infinity
}))

// 返回值

{
  "a": null,
  "c": null,
  "d": null,
  "f": {},
  "g": {},
  "i": null
}

image

2. 循环引用

2.1. structuredClone

可以正确处理对象中的循环引用

2.2. JSON.parse(JSON.stringify)

如果对象中存在循环引用,调用 JSON.stringify 会抛出错误,导致克隆失败

image

3. 性能方面

3.1. structuredClone

通常在处理复杂对象时性能更优,特别是包含大量非 JSON 兼容类型的数据时,因为它是为深度克隆设计的原生方法,内部优化了许多复杂场景

3.2. JSON.parse(JSON.stringify)

在处理简单的、JSON 兼容的数据结构时可能性能较好,但在处理复杂对象或非 JSON 兼容类型时效率低下

4. 浏览器兼容

4.1. structuredClone

是一种较新的
API
,在某些较旧的浏览器中不被支持

image

image

4.2. JSON.parse(JSON.stringify)

在现代浏览器和较旧的浏览器中都有广泛支持

image

三、总结

  • structuredClone
    提供了更广泛的数据类型支持和对循环引用的处理能力,适用于复杂场景
  • JSON.parse(JSON.stringify)
    适合处理简单、
    JSON
    兼容的数据结构,但在处理复杂数据类型或循环引用时有局限性
  • 两者都有限制,克隆的时候需要关注下克隆对象的数据类型再做选择

参考

前言

有粉丝小伙伴在后台留言咨询有没有WinForm 控件库推荐,现在就给安排上。

.NET 平台进行 Windows 应用程序开发的我们来说,找一个既美观又实用的 WinForm UI 控件库至关重要。

本文将介绍 ReaLTaiizor 一款不仅具备精美界面、丰富控件选择,还支持从 .NET Framework 4.8 到 .NET 8 多个版本的开源 WinForms UI 控件库。

尽管 WinForm 技术较为传统,但在桌面应用开发领域仍占有重要位置。微软在最新的.NET 8 中继续为其提供强有力的支持,因此无需担忧其发展前景。

项目介绍

ReaLTaiizor 是一个用户友好且以设计为中心的 .NET WinForms 控件库,包含了一系列广泛的组件。可以使用不同的主题选项来个性化自己的项目,并自定义用户控件,从而使应用程序更加专业。

ReaLTaiizor 能为我们提供强大的支持,帮助我们快速搭建出功能强大而且外观精美的WinForm应用程序。

项目使用

1、添加对 ReaLTaiizor 的引用,或在 NuGet 上搜索 ReaLTaiizor;

2、安装命令

Install-Package ReaLTaiizor

项目源码

为了让大家更好地了解ReaLTaiizor项目,提供源码和演示效果图。通过查看源码,我们可以深入了解控件库的内部实现和使用方法。

演示效果图展示了控件库在实际项目中的应用效果,可以直观地感受到ReaLTaiizor项目带来的美观和专业。

下载项目源码可以进行学习,具体如下图所示

源码运行效果,如下图所示

项目案例

1、登录页面

炫丽的登录页面

2、Windows 防护

它是微软为 Windows 操作系统提供的一款安全软件,包括了防病毒、防火墙等功能。

3、Genshin Impact

中文名称是"原神"。这是一款由 miHoYo(米哈游)开发的开放世界冒险角色扮演游戏,玩家可以在游戏中探索名为提瓦特的幻想世界,收集各种角色,并经历丰富的故事情节与多彩的元素互动。

4、Kaspersky Security Cloud

中文名称是"卡巴斯基安全云"。这是卡巴斯基实验室(Kaspersky Lab)推出的一款基于云端的安全解决方案,主要面向中小企业,提供远程管理和多层次保护功能,帮助用户管理和保护多个端点、设备和文件服务器的安全。

5、XAMPP

XAMPP 是一个跨平台的本地服务器解决方案,包含了 Apache(网页服务器)、MySQL(数据库)、PHP 和 Perl 等工具,允许开发者在本地搭建 PHP 环境以便测试网站和应用程序。它的名称是由各个组件的首字母组成的一个缩写词:X(代表跨平台)、Apache、MySQL、Perl 和 PHP。

6、支付页面

项目组件

在工业信息化系统中,使用 WinForm 开发的上位机应用广泛,例如与现场设备交互的 SCADA 系统以及负责生产管理的 MES 系统等。然而,原生 WinForm 的界面风格相对朴素,在这个重视视觉体验的时代,难以满足客户的审美需求。

因此,除了实现核心功能之外,开发者还需投入大量精力来提升应用的界面美观度。

通常会借助第三方 UI 组件库。ReaLTaiizor提供了多种主题选项,例如浅色、深色、透明度等,开发者可以根据自己的喜好和项目需求进行选择。

这些主题选项可以让应用程序在视觉上更加出众,与众多应用程序中脱颖而出。

以上只列举了部分功能和组件的效果图,如果大家感兴趣可以下载源码进行查看详细组件以及功能。

项目地址

Github:
https://github.com/Taiizor/ReaLTaiizor

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!

一、概述

感知机模型(Perceptron Model)也叫做神经元模型,设计灵感即来自于生物神经元的运行机制,依次完成信息接收、处理、输出的过程。当前大放异彩的各种人工神经网络模型即由一个个人工神经元构成,因此,本文介绍的感知机模型(神经元模型)就是各种神经网络模型的基本单元。

二、模型原理

模型原理

模型的核心概况起来即是线性回归+符号函数映射。对未知数据,先做线性拟合,输出值再经符号函数映射,完成类别判定。因此,感知机模型也是直接用于二分类任务的模型。模型示意图可表示为

模型原理直接地表示也就是

\[y=\left\{ \begin{aligned} &-1, \ w\cdot x+b<0\\ &1, \ w\cdot x+b\geq 0 \end{aligned} \right.
\]

对任意待测样本,将其特征向量直接代入计算即可。

模型的训练

模型的参数就是指线性回归中的权重和偏置,确定了它们也就确定了整个模型。对参数的确定往往通过训练数据集实施,也就是由训练集和标签之间的对应构造一个关于待求参数的损失函数,通过不断迭代优化,在过程中确定出最佳的参数值。损失函数的构造通常采用这样一种方式,就是计算所有误分类样本到决策函数的距离和。表达式为

\[d=\frac{1}{\left| \left| w \right| \right|}\sum_{x_i\in M}{\left| w\cdot x_i+b \right|}
\]

其中,
\(\left| \left| w \right| \right|=\sqrt{w_{1}^{2}+w_{2}^{2}+...+w_{n}^{2}}\)
,M为误分类样本集。
为进一步简化,可以将绝对值计算以‘-y’等价替换。y是样本的标签,取值要么为1,要么为-1,若y为1,表明样本为正,错误判定时计算得到的回归值为负,此时‘-y
负值’为正;若y为-1,表明样本为负,错误判定时计算得到的回归值为正,此时‘-y
正值’仍为正,与绝对值运算等价,此时损失函数表达式为

\[d=-\frac{1}{\left| \left| w \right| \right|}\sum_{x_i\in M}{y_i(w\cdot x_i+b)}
\]

式中的
\(\frac{1}{\left| \left| w \right| \right|}\)
实质地表征了决策函数的方向性,而模型关注的是对两类样本的类别结果判定,并不实际关注决策函数的具体方向以及样本到函数距离的具体差异,因而该部分可以省去,损失函数也就简化为

\[d=-\sum_{x_i\in M}{y_i(w\cdot x_i+b)}
\]

三、Python实现

手工实现:

import numpy as np
from sklearn import datasets

def model(X, theta):
    return X @ theta

def predict(x, theta):
    flags = model(x, theta)
    y = np.ones_like(flags)
    y[np.where(flags < 0)[0]] = -1
    return y

def computerCost(X, y, theta):
    y_pred = predict(X, theta)
    error_index = np.where(y_pred != y)[0]
    return np.squeeze(-y_pred[error_index].T @ y[error_index])

def gradientDescent(X, y, alpha, num_iters=1000):
    n = X.shape[1]
    theta = np.zeros((n, 1))
    J_history = []
    for i in range(num_iters):
        y_pred = predict(X, theta)
        error_index = np.where(y_pred != y)[0]
        theta = theta + alpha * X[error_index, :].T @ y[error_index]
        cur_cost = computerCost(X, y, theta)
        J_history.append(cur_cost)
        print('.', end='')
        if cur_cost == 0:
            print(f'Finished in advance in iteration {i + 1}!')
            break

    return theta, J_history

iris = datasets.load_iris()
X = iris.data
m = X.shape[0]
X = np.hstack((np.ones((m, 1)), X))
y = iris.target
y[np.where(y != 0)[0]] = -1
y[np.where(y == 0)[0]] = 1
y = y.reshape((len(y), 1))
theta, J_history = gradientDescent(X, y, 0.01, 1000)
y_pred = predict(X, theta)
acc = np.sum(y_pred == y) / len(y)

print('acc:\n', acc)


基于PyTorch实现:

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np

# 生成一些随机的线性可分数据
np.random.seed(42)
num_samples = 100
features = 2
x = 10 * np.random.rand(num_samples, features)  # 生成随机输入特征
w_true = np.array([2, -3.4])  # 真实的权重
b_true = 4.2  # 真实的偏置
y_true = np.dot(x, w_true) + b_true + 0.1 * np.random.randn(num_samples)  # 添加噪声
y_true = np.where(y_true > 0, 1, -1)  # 将输出标签转换为二分类问题

# 将数据转换为 PyTorch 的 Tensor
x = torch.tensor(x, dtype=torch.float32)
y_true = torch.tensor(y_true, dtype=torch.float32)

# 定义感知机模型
class Perceptron(nn.Module):
    def __init__(self, input_size):
        super(Perceptron, self).__init__()
        self.linear = nn.Linear(input_size, 1)

    def forward(self, x):
        return torch.sign(self.linear(x))

# 初始化感知机模型
perceptron = Perceptron(input_size=features)

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(perceptron.parameters(), lr=0.01)

# 训练感知机模型
num_epochs = 100
for epoch in range(num_epochs):
    # 前向传播
    y_pred = perceptron(x)

    # 计算损失
    loss = criterion(y_pred.view(-1), y_true)

    # 反向传播和优化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    # 打印损失
    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

# 在训练数据上进行预测
with torch.no_grad():
    predictions = perceptron(x).numpy()

# 可视化结果
plt.scatter(x[:, 0], x[:, 1], c=predictions.flatten(), cmap='coolwarm', marker='o')
plt.title('Perceptron Model')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()


End.


pdf下载

大规模图像-文本预训练模型实现了零样本分类,并在不同数据分布下提供了一致的准确性。然而,这些模型在下游任务中通常需要微调优化,这会降低对于超出分布范围的数据的泛化能力,并需要大量的计算资源。论文提出新颖的
Robust Adapter

R-Adapter
),可以在微调零样本模型用于下游任务的同时解决这两个问题。该方法将轻量级模块集成到预训练模型中,并采用新颖的自我集成技术以提高超出分布范围的鲁棒性,并大幅减少存储开销。此外,论文提出了针对视觉-语言下游任务设计的
MPM-NCE
损失,确保多个图像-文本对的精确对齐和具有区分性的特征学习。

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

论文: Efficient and Versatile Robust Fine-Tuning of Zero-shot Models

Introduction


大规模联合图像和文本数据预训练模型的出现在计算机视觉领域引起了范式转变。通过对大量图像-文本对的嵌入进行对齐,这些模型实现了零样本推断,并展现出在不同数据分布下广泛泛化的显著能力。尽管它们在零样本情境下表现出色,但它们无法与监督学习模型相媲美,需要进行微调以发挥其全部能力。然而,传统的全面微调会产生两个主要挑战:
1
)全面微调损害了模型对于超出分布范围(
OOD
)数据的泛化能力,而这对于数据变异性不可预测的实际应用至关重要。
2
)它需要大量的计算资源、内存和存储,而随着大规模预训练模型的不断增大,这是不切实际的。

最近,针对这些挑战提出了几种微调方法。鲁棒微调的目标是在微调零样本模型的同时保持对
OOD
的鲁棒性,而参数高效微调(
PEFT
)仅更新一小部分参数,同时保持预训练参数的冻结状态。然而,每种方法只解决其中一个挑战,同时在另一个挑战上仍然存在不足。如图
1
所示,现有的鲁棒微调方法仍然需要微调整个模型,导致训练代价高昂。此外,它们仅针对分类任务,因此通常仅训练图像编码器,从模型中排除了零样本推断能力。另一方面,与鲁棒微调相比,
PEFT
在分布偏移下的性能显著滞后。它们的关键缺点凸显了需要新的微调方法,同时解决鲁棒微调和
PEFT
分别应对的两个挑战。

本文提出了一种名为鲁棒适配器(
R-Adapter
)的新型微调方法,旨在提高
PEFT
的鲁棒性,并增强鲁棒微调的效率。在适配器微调方法的基础上向预训练模型添加额外的轻量级模块,
R-Adapter
引入了新颖的自我集成策略,以增强
OOD
的鲁棒性。

受到在权重空间中平均多个模型时观察到的鲁棒性增益的启发,通过一种独特的方式在单个模型内实现这种策略。这种方法在任务特定性能和针对分布偏移的鲁棒性之间取得了良好的平衡,同时显著降低了存储成本。具体而言,
R-Adapter
通过三种自我集成技术实现这一目标。它随机丢弃适配器模块,从而动态生成并集成不同子网络,以各种配置组合适配器和预训练层。此外,累积适配器权重以形成一个时间集成,捕捉整个学习过程中产生的所有模型。此外,通过重新缩放适配器的权重,并通过重新参数化将其整合到预训练层中,论文实现了在没有两个单独模型的情况下,在预训练和微调模型的权重之间实现无缝的线性插值。

此外,论文提出了一种名为
Multi-Positive Margin NCE

MPM-NCE
)损失函数,专为在视觉-语言下游任务上进行有效微调而设计。这些任务通常涉及复杂的关系,其中多个图像可以对应于相同的文本,反之亦然。与传统的对比损失(例如
InfoNCE
)不同,后者接受单一正样本对,并因此经常导致这些关系中的语义不匹配,
MPM-NCE
考虑了多个正样本对,从而更精确地对齐跨各种图像和文本对。此外,
MPM-NCE
引入了一个角度边距以惩罚负样本对,使模型能够学习对下游任务至关重要的高度区分的特征。因此,所提出的损失函数显著改善了任务特定性能,在
ID

OOD
环境下都带来了益处。

论文的方法在微调后实现了零样本推理,在图像分类任务之外扩展了其适用性范围,适用于广泛的应用领域。为了展示其多功能性,论文提出了一个新的用于鲁棒微调的评估基准,包括五个任务:三种情景下的图像分类任务、跨模态检索和开放词汇分割。大量实验证明,与现有的鲁棒微调和
PEFT
方法相比,论文的方法在分布转移条件下表现出卓越性能,同时使用的参数更少。

本文的主要贡献有四点:

  1. 提出了一个高效且多功能的鲁棒微调框架,融合了
    PEFT
    和鲁棒微调的优势,这是第一个兼具两者优势的方法。

  2. 提出了
    R-Adapter
    ,采用自集成技术,借助单个带有适配器的模型实现权重空间集成。能够在减少存储成本的同时增强鲁棒性,因为不需要多个模型。

  3. 开发了适用于微调的
    MPM-NCE
    损失,利用多个正样本对和引入角度间隔,确保了多个图像-文本对的精确对齐和具有区分性的特征学习。

  4. 首次将鲁棒微调的基准拓展到图像分类之外的任务,包括跨模态检索和开放词汇分割,从而允许评估其广泛适用性。论文的方法在各种任务中取得了最先进的性能,仅微调了
    13%

    CLIP
    编码器参数。

Proposed Method


Preliminary

  • CLIP Encoders

CLIP
由两个编码器组成,分别用于从图像和文本中提取特征。每个编码器由一系列
Transformer
层组成,每个层包括多头注意力(
MHA
)、层归一化(
LN
)和前馈神经网络(
FFN
)。具体而言,第
\(l\)

Transformer
层的公式如下:

\[\begin{equation}
\begin{aligned}
\bar{X_l} &= \textrm{MHA}(\textrm{LN}(X_{l-1})) + X_{l-1}, \\
X_l &= \textrm{FFN}(\textrm{LN}(\bar{X_l})) + \bar{X_l}.
\label{eq:Transformer_layer}
\end{aligned}
\end{equation}
\]

MHA
包括对查询、键和值进行
\(k\)
头自注意力操作,通过对输入进行独立的线性投影来实现,其公式为:

\[\begin{equation}
\begin{aligned}
\textrm{MHA}(X) &= [\textrm{Attn}^1(X), ..., \textrm{Attn}^k(X)]W_O,\\
\textrm{Attn}^i(X) &= \textrm{softmax}\big((XW_{Q}^{i})(XW_{K}^{i})^{\top}/{\sqrt{d_h}} \big)(XW_{V}^{i}),
\label{eq:MHA}
\end{aligned}
\end{equation}
\]

其中
\([\cdot,\cdot]\)
表示拼接,
\(d_h\)
设为
\(d/k\)

\(W_{Q}^{i}\in\mathbb{R}^{d\times d_h}\)

\(W_{K}^{i}\in\mathbb{R}^{d\times d_h}\)

\(W_{V}^{i}\in\mathbb{R}^{d\times d_h}\)

\(W_{O}\in\mathbb{R}^{d\times d}\)
是线性投影矩阵。
FFN
由两个线性层和一个非线性层组成:

\[\begin{equation}
\textrm{FFN}(X) = \sigma(XW_1+b_1)W_2 + b_2,
\label{eq:FFN}
\end{equation}
\]

其中
\(W_1\in\mathbb{R}^{d\times4d}\)
,
\(W_2\in\mathbb{R}^{4d\times d}\)
,
\(b_1 \in \mathbb{R}^{4d}\)
, 和
\(b_2 \in \mathbb{R}^d\)
分别是线性投影的权重和偏置;
\(\sigma(\cdot)\)
表示
GELU
函数。

  • Contrastive Learning

CLIP
编码器被训练用于预测哪些文本描述与给定的一组图像匹配,反之亦然。这通过使用
InfoNCE
损失来进行对比学习来实现,该损失迫使图像嵌入和其对应的文本嵌入彼此靠近,并远离批次中的其他文本嵌入。设
\(f(\cdot)\)

\(g(\cdot)\)
分别是图像和文本的
CLIP
编码器。给定一个批次包含
\(B\)
个图像-文本对
\(\mathcal{B} =\big\{(I_1,T_1), ..., (I_B,T_B)\big\}\)
,损失函数定义为:

\[\begin{equation}
\begin{aligned}
\mathcal{L}(\mathcal{B}) = &-\sum_{i=1}^{B}\Bigg(\log\frac{e^{f_i \cdot g_i/\tau }}{\sum_{j=1}^{B}e^{f_i \cdot g_j/\tau }}
+\log\frac{e^{f_i\cdot g_i/\tau }}{\sum_{j=1}^{B}e^{f_j\cdot g_i/\tau}}\Bigg),
\label{eq:InfoNCE_Loss}
\end{aligned}
\end{equation}
\]

其中
\(f_i = \frac{f(I_i)}{||f(I_i)||_2}\)
,
\(g_i = \frac{g(T_i)}{||g(T_i)||_2}\)

\(\tau\)
表示一个可学习的温度参数。

Problem Setup

论文的目标是在保留其固有的离群分布泛化能力的同时,高效地对视觉-语言预训练模型进行各种下游任务的微调。虽然大多数现有的鲁棒微调方法局限于分类任务,但论文将范围扩大到为各种下游任务,如图像分类、跨模态检索和开放词汇分割等,提供鲁棒微调模型。

给定一个图像-文本预训练模型,目标是使用一个面向目标下游任务的内分布(
ID
)训练数据集
\(\mathcal{D}_{\mathcal{I}}=\{(I_i, T_i)\}_{i=1}^{n}\)
对其进行适应,其中
\(I\)
表示一个图像,
\(T\)
是对应于该图像的文本描述。同时,旨在提高模型在一个离群分布(
OOD
)测试数据集
\(\mathcal{D}_{\mathcal{O}}=\{(I_j, T_j)\}_{j=1}^{m}\)
上的性能。内分布和离群分布数据集
\(\mathcal{D}_{\mathcal{I}}\)

\(\mathcal{D}_{\mathcal{O}}\)
分别从不同概率分布
\(p_{\mathcal{I}}(I,T)\)

\(p_{\mathcal{O}}(I,T)\)
中采样,当
\(p_{\mathcal{I}}(I,T)\neq p_{\mathcal{O}}(I,T)\)
时即为表现出分布转移。在分类任务中,
\(T\)
表示目标类的文本描述,通过从一组预定义模板中进行采样构建(例如,“一张{
class
}的照片”)。对于其他视觉-语言任务,
\(T\)
可能是与图像
\(I\)
相关联的标题之一。

Robust Adapter (R-Adapter)

为了实现高效且鲁棒的微调,论文引入了基于
PEFT
框架的
R-Adapter

PEFT
框架在微调少量附加的可学习参数的同时冻结预训练模型,但在训练中对该框架的朴素应用可能会导致对内分布数据的显著偏向(参见表
2
)。受到集成增强在各种分布下的泛化能力的启发,
R-Adapter
设计了三种新颖的自集成策略,以实现鲁棒微调而不在训练和推理期间增加计算负载。

  • Design of R-Adapter

R-Adapter
建立在适配器微调框架之上,在该框架中向预训练模型添加了轻量级模块。具体而言,
R-Adapter
中的适配器模块采用了
Houlsby
适配器的简化版本,去除了非线性层和偏置。该模块被构建为一个残差块,由以下权重矩阵组成:

\[\begin{equation}
h(X) = XW_{\textrm{adp}} + X,
\label{eq:Adapter}
\end{equation}
\]

其中,
\(X\)
表示预训练块的输出,
\(W_{\textrm{adp}} \in \mathbb{R}^{d\times d}\)
是论文适配器的权重矩阵。对于全样本学习,保持
\(W_{\textrm{adp}}\)
的满秩结构以保留足够的容量。在少样本学习中,可以通过将
\(W_{\textrm{adp}}\)
分解为低秩矩阵
\(BA\)
的乘积来采用瓶颈结构,其中
\(B\in \mathbb{R}^{d\times r}\)

\(A\in \mathbb{R}^{r\times d}\)
,且秩
\(r \ll d\)
。这种分解避免了过参数化,并显著减少了参数数目和计算量。

在图像和文本编码器的每个
Transformer
层中部署适配器,放置在
MHA

Multi-Head Attention
)和
FFN

Feed-Forward Network
)层之后,如图
2
所示。

由于适配器之前没有非线性结构,可以通过将其与最接近的预训练层集成进行重参数化,从而在推理过程中消除适配器的额外计算开销。用
\(W_{\textrm{org}}\)
表示适配器之前的预训练层的权重,可以是来自
MHA

\(W_O\)
或者
FFN
中的
\(W_2\)
,相应的偏置
\(b_{\textrm{org}}\)

FFN
中的
\(b_2\)
。给定预训练层的输入
\(X_{\textrm{in}}\)
,那么重新参数化的过程如下进行:

\[\begin{align}
\begin{aligned}
h(X_\textrm{in}W_\textrm{org} + b_\textrm{org}) &= X_\textrm{in}W_\textrm{org}(W_{\textrm{adp}} + \mathrm{I}) + b_{\textrm{org}}W_{\textrm{adp}} + b_{\textrm{org}} \\
&= X_\textrm{in}W_\textrm{rep} + b_\textrm{rep},
\label{eq:rep}
\end{aligned}
\end{align}
\]

其中,
\(\mathrm{I}\in\mathbb{R}^{d\times d}\)
是单位矩阵,
\(W_\textrm{rep} = W_\textrm{org}(W_\textrm{adp}+\mathrm{I})\)

\(b_\textrm{rep} = b_\textrm{org}(W_\textrm{adp}+\mathrm{I})\)

  • Dynamic Ensemble by Adapter Dropping

为了增强
R-Adapter

OOD
鲁棒性,加入适配器丢弃的动态集成技术。在训练过程中,适配器模块以以下方式被随机停用:

\[\begin{equation}
h(X) = \frac{\gamma}{1-p} \cdot XW_{\textrm{adp}} + X,
\label{eq:Stochastic}
\end{equation}
\]

其中,
\(\gamma\)
是从
\(\textrm{Bernoulli}(1-p)\)
中抽取的独立变量,
\(p\)
是适配器丢弃的概率。

与用于特征稀疏性的
dropout
或用于模型深度减少的
drop-path
不同,该技术独特地专注于在保持预训练特征的同时随机禁用适配器层。适配器丢弃不适用于推理阶段,这样可以创建一个由预训练层和适配器层组合而成的子网络集合。这种策略能够同时保留预训练知识和微调知识的动态集成多模型,从而在
ID

OOD
数据上提升性能。

  • Temporal Ensemble by Accumulation

通过利用适配器权重的历史累积,引入一个时间集成策略来提高模型的鲁棒性。在训练过程中,通过对多次迭代中的权重进行平均,集成技术捕捉到对特征空间的更广泛了解。累积适配器的权重
\(\tilde{W}_\textrm{adp}\)
则通过指数移动平均进行更新:

\[\begin{equation}
\tilde{W}_\textrm{adp} \leftarrow m \cdot \tilde{W}_\textrm{adp} + (1-m) \cdot {W}_\textrm{adp},
\label{eq:Accumulation}
\end{equation}
\]

其中,
\(m \in [0, 1]\)
是控制动量更新速率的系数。这种方法在内存使用方面非常高效,因为只有适配器的参数进行了动量更新,而不是整个模型的参数。在推理阶段,利用累积的权重
\(\tilde{W}_\textrm{adp}\)
来计算重参数化权重
\(\tilde{W}_\textrm{rep}\)
和偏置
\(\tilde{b}_\textrm{rep}\)

  • Weight-space Ensemble by Re-scaling

最后,引入一种通过重新调整参数实现预训练层和微调层之间的权重空间集成的策略。传统的权重空间集成(
WiSE-FT
)在原始预训练参数和微调参数之间进行线性插值,因此需要存储两个独立的模型。相比之下,论文采用重参数化的权重
\(\tilde{W}_\textrm{rep}\)
作为微调层的权重,从而进化了这个概念。我们在推理时重新调整适配器的权重并对其重参数化,将权重空间集成简化为单一模型内的实现。该过程可以表达如下:

\[\begin{align}
\begin{aligned}
\underbrace{\alpha \tilde{W}_\textrm{rep} + (1-\alpha) W_\textrm{org}}_\texttt{{{{Weight-space Ensemble}}}} &= \alpha W_\textrm{org}\tilde{W}_\textrm{adp} + \alpha W_\textrm{org} + (1-\alpha) W_\textrm{org} \\[-17pt]
&= \underbrace{W_\textrm{org}(\overbrace{\alpha \tilde{W}_\textrm{adp}}^\texttt{{{{Re-scaling}}}} \;+\; \mathrm{I}) = W_\textrm{ens}}_\texttt{{{Re-parametrization}}}, \\
\label{eq:rescale}
\end{aligned}
\end{align}
\]

这里,
\(W_\textrm{ens}\)
表示集成的权重,
\(\alpha\)
是一个重调整系数。系数
\(\alpha\)
充当插值因子,调整原始预训练权重
\(W_\textrm{org}\)
与微调层调整权重之间的平衡。这种技术不仅可以提高在分布转移下的准确性,也能在
ID
数据上保持高性能。关键是,与
WiSE-FT
不同,该方法不需要在存储中维护两个单独的完整模型,因此更有效地促进了更节省存储空间的权重空间集成。

MPM-NCE Loss for Downstream Task

为了增强下游任务的学习能力,使用与任务特征密切对齐的损失函数至关重要。视觉-语言任务通常涉及多个模态之间的对应关系。例如,在分类任务中,对同一类别使用不同的文本模板可能导致多个文本描述与单个图像匹配,反之亦然。这种情况在涉及图像和标题的跨模态检索任务中也会发生。当将零样本模型调整到新任务时,一种常见方法是使用预训练中使用的
InfoNCE
损失。然而,对于存在多个正样本的任务,该损失并不理想,因为它只考虑了单个正样本对。此外,
InfoNCE
学习了正负样本之间的顺序,这可能不会为下游任务产生足够有区分力的特征。

为了解决这些限制,论文提出了
MPM-NCE
损失,旨在适应这些任务的多正样本性质,同时增强所学嵌入的区分能力。这个损失函数有两个关键改进。首先,使用软标签为多个正样本对分配相等的概率,公式如下:

\[\begin{equation}
\tilde{y}_{ij} = \frac{(1-\epsilon)\cdot y_{ij}}{|P(i)|}
+ \frac{\epsilon \cdot (1-y_{ij})}{B-|P(i)|} \in [0,1],
\label{eq:soft_label}
\end{equation}
\]

其中
\(y_{ij} \in \{0,1\}\)
表示样本
\(i\)

\(j\)
之间的正关系,
\(P(i)\)
是包括自身在内的样本
\(i\)
的正样本集合,
\(\epsilon\)
是一种平滑标签噪声。这种软标签确保在下游任务中正确对齐多个图像-文本对。此外,软标签还可以包含
\(\epsilon\)
,通过对标签引入小的扰动来降低过拟合风险。

第二个改进是对负样本对应用边界
\(\delta\)
。这个边界通过确保负样本对不仅是不同的,而且还要被一定的阈值分开,增强了所学特征的区分度。融合了这些改进,
MPM-NCE
公式如下:

\[\begin{equation}
\mathcal{L}(\mathcal{B}) = -\sum_{i,j=1}^{B}\Bigg(\tilde{y}_{ij}\log\frac{e^{(f_i \cdot g_j+\delta_{ij})/\tau }}{\sum_{k=1}^{B}e^{(f_i \cdot g_k + \delta_{ik})/\tau}}
+\tilde{y}_{ji}\log\frac{e^{ (f_j \cdot g_i+\delta_{ji})/\tau}}{\sum_{k=1}^{B}e^{(f_k \cdot g_i + \delta_{ki})/\tau}}\Bigg),
\label{eq:MPM_NCE}
\end{equation}
\]

其中温度
\(\tau\)
被设为常数值
0.01

\(\delta_{ij}\)
对于正关系为
0
,对于其他情况为
\(\delta\)
。因此,
MPM-NCE
损失鼓励模型正确对齐多个图像-文本对,并学习具有区分度的特征,从而在
ID

OOD
下显著提高性能。

Experiments




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

work-life balance.