pytorch | 使用vmap对自定义函数进行并行化/ 向量化的执行

10 篇文章 0 订阅
7 篇文章 0 订阅

0. 参考

  1. pytorch官方文档:https://pytorch.org/docs/stable/generated/torch.func.vmap.html#torch-func-vmap
  2. 关于if语句如何执行:https://github.com/pytorch/functorch/issues/257

1. 问题背景

  1. 笔者现在需要执行如下的功能:
    root_ls = [func(x,b) for x in input]
    因此突然想到pytorch或许存在对于自定义的函数的向量化执行的支持

  2. 一顿搜索发现了from functorch import vmap这种好东西,虽然还在开发中,但是很多功能已经够用了

2. 具体例子

  1. 这里只介绍笔者需要的一个方面,vmap的其他支持还请参阅pytorch官方文档
  2. 自定义函数及其输入:
# 自定义函数
def func_2(t,b):
    return torch.where((t>5.),
                        t*b,
                        -t)
# 输入

t = torch.tensor([1.,2.,3.,4.,5.,6.,7.,8.])
b = torch.tensor([1.],requires_grad=True)

  • 注意1:自定义函数不要出现if,用torch.where替代。至于为什么参阅这个issue,大概的原因是“if isn’t a differentiability requirement;”,强行使用会报错error of Data-dependent control flow
  1. 然后对于b,我们需要扩张到和t同样的大小:
    b_extend = torch.expand_copy(b,size=t.shape) # 必须把b扩张到和t同一个size否则报错

  2. 利用vmap,它返回一个新的函数func_vec ,具有向量化执行的支持,也可以利用autograd求导

# Use vmap() to construct a new function.  
func_vec = vmap(func_2)  				# [N, D], [N, D] -> [N]
ans = func_vec(t,b_extend)
ans.sum().backward()   # 等价于: ans.backward(torch.ones(b_extend.shape))
b_extend.grad          # 可以预见:b的导数是t:在t>5.时导数是t,在t<=5.时导数是0

  1. 全部代码:
import torch
from functorch import vmap

# if分支isn't a differentiability requirement;
def func(t,b):
    tmp = t*b
    if tmp > 5:     # error: Data-dependent control flow
        root = t*b
    else:
        root = -t
    return root

def func_2(t,b):
    return torch.where((t>5.),
                        t*b,
                        -t)

t = torch.tensor([1.,2.,3.,4.,5.,6.,7.,8.])
b = torch.tensor([1.],requires_grad=True)
b_extend = torch.expand_copy(b,size=t.shape)    # 必须把b扩张到和t同一个size否则报错
b_extend.retain_grad()

print(f"shape of t:{t.shape}, shape of b_extend:{b_extend.shape}")
# shape of t:torch.Size([8]), shape of b_extend:torch.Size([8])


# Use vmap() to construct a new function.  # [D], [D] -> []
func_vec = vmap(func_2)  # [N, D], [N, D] -> [N]
ans = func_vec(t,b_extend)
ans.sum().backward()   # 等价于: ans.backward(torch.ones(b_extend.shape))

b_extend.grad          # 可以预见:b的导数是t:在t>5.时导数是t,在t<=5.时导数是0
# tensor([0., 0., 0., 0., 0., 6., 7., 8.])

  1. 问题在于,它真的比root_ls = [func(x,b) for x in input]这种快吗?在笔者的设计中确实是使用vmap更快一些,但是不见得总是好用,只是在pytorch中写大量的for实在是太愚蠢了QAQ

感谢阅读,欢迎交流

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这个问题比较复杂。首先,我们需要加载自定义图像数据集,并训练一个自定义的神经网络模型。然后,我们需要提取任意层的特征向量,并使用t-SNE算法对这些特征向量进行降维和可视。 以下是一个简单的实现步骤: 1. 加载自定义图像数据集 使用PyTorch的ImageFolder类可以很容易地加载自定义图像数据集。你需要将数据集划分为训练集和测试集,并使用transforms对图像进行预处理。 ```python import torchvision.transforms as transforms from torchvision.datasets import ImageFolder transform = transforms.Compose([ transforms.Resize((224, 224)), # 将图像大小调整为(224, 224) transforms.ToTensor(), # 将图像转换为Tensor transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一 ]) train_dataset = ImageFolder('path/to/train/dataset', transform=transform) test_dataset = ImageFolder('path/to/test/dataset', transform=transform) ``` 2. 训练自定义的神经网络模型 使用PyTorch可以非常方便地定义和训练自定义的神经网络模型。你可以根据自己的需要定义任意层数的模型,并在训练过程中使用任意的损失函数和优器。 ```python import torch.nn as nn import torch.optim as optim class CustomNet(nn.Module): def __init__(self): super(CustomNet, self).__init__() # 定义你自己的网络结构 def forward(self, x): # 定义前向传播过程 model = CustomNet() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) def train(model, train_loader, criterion, optimizer, num_epochs): for epoch in range(num_epochs): running_loss = 0.0 for i, data in enumerate(train_loader, 0): inputs, labels = data optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() print('Epoch %d loss: %.3f' % (epoch + 1, running_loss / len(train_loader))) ``` 3. 提取任意层的特征向量 在训练完成后,我们可以使用PyTorch提供的钩子(hook)机制来提取任意层的特征向量。我们可以在前向传播过程中注册一个钩子来获取中间层的输出。 ```python features = {} def get_features(name): def hook(model, input, output): features[name] = output.detach() return hook model.layer.register_forward_hook(get_features('layer_output')) def extract_features(model, dataloader): model.eval() with torch.no_grad(): for i, data in enumerate(dataloader, 0): inputs, labels = data outputs = model(inputs) ``` 4. 使用t-SNE算法进行特征可视 最后,我们可以使用scikit-learn提供的t-SNE算法对特征向量进行降维和可视。我们可以将特征向量作为输入,将它们降到2维或3维,然后使用matplotlib将它们可视。 ```python from sklearn.manifold import TSNE import matplotlib.pyplot as plt def visualize_features(features, labels): tsne = TSNE(n_components=2, init='pca', random_state=0) X_tsne = tsne.fit_transform(features) plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=labels) plt.show() visualize_features(features['layer_output'], train_dataset.targets) ``` 这就是一个简单的实现步骤。当然,你可以根据自己的需要进行修改和扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值