对比学习 ——simsiam 代码解析。

Python微信订餐小程序课程视频

https://blog.csdn.net/m0_56069948/article/details/122285951

Python实战量化交易理财系统

https://blog.csdn.net/m0_56069948/article/details/122285941

目录

1 : 事先准备 。

2 : 代码阅读。

2.1: 数据读取

2.2: 模型载入

3 训练过程:

4 测试过程:

5 :线性验证

6 : 用自己数据集进行对比学习。

第一:  改数据集 :

2 改变batch_size和图片大小。


写在前面的话 CSDN真的是’sb’中的’sb’软件, 辛辛苦苦写半天  我复制个东西过来 他就把前面的刷没了 还要我重头写????????????神经并b


2022李宏毅作业HW3 是食物的分类 ,但是我怎么尝试 再监督学习的模式下 准确率都达不到百分之60 .。半监督也感觉效果不明显。 所以 这次就想着对比学习能不能用来解决这个问题呢 。?看了一圈,感觉simsiam是对比学习里比较简单的一种方法,好像效果也不错。 所以来看一看这个东西是怎么玩的。

simsaim 是对比学习很新的文章了。 他的训练方式简单来说就是 ,一张图片 ,用不同的方式去增广后形成图片对 。 然后用一张去预测另一张。 不懂得可以看朱老师的视频。

对比学习论文综述【论文精读】_哔哩哔哩_bilibili

1 : 事先准备 。

代码地址 : 好像不是官方的

下载解压。

直接在main函数的 运行 编辑配置中输入

--data\_dir ../Data/ --log\_dir ../logs/ -c configs/simsiam_cifar.yaml --ckpt\_dir ~/.cache/ --hide\_progress --download

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CZmzXzuJ-1649956353704)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

os.environ['CUDA\_VISIBLE\_DEVICES']='0'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e649XXmQ-1649956353706)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ezkj0d3R-1649956353708)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

注意 : 第二次运行可以删掉download

2 : 代码阅读。

神经网络的一个基本的框架就是 : 数据读取 , 模型载入, 训练,测试。 我们接下来根据这四块来看。

2.1: 数据读取

运行main文件 。

    main(device=args.device, args=args)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpLlLiiY-1649956353709)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
进入main 函数 。

是三个数据集的读取。

train\_loader ,, memory\_loader 和 test\_loader。 train和memory 都是训练集的数据 他们的不同之处在于, 数据增广的方式不同。 train的增广是用来训练的 memory和test的增广都是用来测试的。由于在对比学习里,  数据增广是很重要的 ,所以这里看下数据增广的方式。 
        dataset=get_dataset(transform=get_aug(train=True, **args.aug_kwargs),train=True,**args.dataset_kwargs),

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AFj1meA5-1649956353710)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

**args.aug\_kwargs 里规定了图片大小是32. 以及这次用的是simsaim。 

这里两个train  。 只有训练集的第一个train是true。 而训练集的增广方式如下

class SimSiamTransform():def \_\_init\_\_(self, image\_size, mean\_std=imagenet\_mean\_std):image_size = 224 if image_size is None else image_size # by default simsiam use image size 224p_blur = 0.5 if image_size > 32 else 0 # exclude cifar# the paper didn't specify this, feel free to change this value# I use the setting from simclr which is 50% chance applying the gaussian blur# the 32 is prepared for cifar training where they disabled gaussian blurself.transform = T.Compose([T.RandomResizedCrop(image_size, scale=(0.2, 1.0)),T.RandomHorizontalFlip(),T.RandomApply([T.ColorJitter(0.4,0.4,0.4,0.1)], p=0.8),T.RandomGrayscale(p=0.2),T.RandomApply([T.GaussianBlur(kernel_size=image_size//20*2+1, sigma=(0.1, 2.0))], p=p_blur),T.ToTensor(),T.Normalize(*mean_std)])def \_\_call\_\_(self, x):x1 = self.transform(x)x2 = self.transform(x)return x1, x2 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t8yJ8992-1649956353711)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
   增广方式可以参考 官网 Transforming and augmenting images — Torchvision 0.12 documentation

这里依次是 : 随机resize 然后剪切为输入大小,  也就是会随机取图片里的一块。

随机水平变换

0.8的概率调节亮度对比度和饱和度。

0.2概率灰度化

对于32的照片 不做高斯模糊。

转化为张量并标准化。

然后 对于一个输入  这里会做两次transform  call可以让这个类像函数那样被调用。

对于 测试用的训练集 。也就是memory 是下面的增广方式。  而test也是下面的增广方式 。

        else:self.transform = transforms.Compose([transforms.Resize(int(image_size*(8/7)), interpolation=Image.BICUBIC), # 224 -> 256 transforms.CenterCrop(image_size),transforms.ToTensor(),transforms.Normalize(*normalize)])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E2rUtJd4-1649956353712)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
              如果输入是 224 就 先放大到256,然后中心裁剪224,之后标准化。

如果输入是32 就放大到36 再中心裁剪32 .后标准化。

用的是cifar10的数据。 其实也就相当于很普通的 读图片  然后增广, 加标签。

我们只要看getitem取出来的数据是什么就好 。

        img, target = self.data[index], self.targets[index]# doing this so that it is consistent with all other datasets# to return a PIL Imageimg = Image.fromarray(img)if self.transform is not None:img = self.transform(img)if self.target_transform is not None:target = self.target_transform(target)return img, target

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hmv6x6iH-1649956353713)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v35N9SVS-1649956353714)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​ 注意  如果是训练集 在trans时会返回两张图片 ,所以返回的是一个元组。 而测试时 ,img就是单独的一张图片。 target也就是标签。

总结:数据部分 我们需要做一个数据集, 然后训练集的增广要返回两个结果。  当读取数据时,返回的是图片数据和标签数据。

2.2: 模型载入

这一部分我们来看模型 ,我们可以根据下面的伪代码来看模型长什么样子。 伪代码非常容易看懂。 aug就是增广嘛。 f来提特征,然后两个预测。 算loss 回传。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sh3WjO8J-1649956353715)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

    model = get_model(args.model).to(device)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BkTwhydm-1649956353715)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
这句来获得模型。

    if model_cfg.name == 'simsiam':model =  SimSiam(get_backbone(model_cfg.backbone))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMb1VSNA-1649956353716)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
backbone就是普通的res18  这里不需要预训练的模型 只需要初始模型 。

class SimSiam(nn.Module):def \_\_init\_\_(self, backbone=resnet50()):super().__init__()self.backbone = backboneself.projector = projection_MLP(backbone.output_dim)self.encoder = nn.Sequential( # f encoderself.backbone,self.projector)self.predictor = prediction_MLP()def forward(self, x1, x2):f, h = self.encoder, self.predictorz1, z2 = f(x1), f(x2)p1, p2 = h(z1), h(z2)L = D(p1, z2) / 2 + D(p2, z1) / 2return {'loss': L}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-91MFueCu-1649956353717)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
这个就是simsam的模型了 。 projector 是个三层的普通mlp 。 encoder 就是伪代码里的f了 而predictor就是伪代码里的h了 。 我们具体来看下loss 。

def D(p, z, version='simplified'): # negative cosine similarityif version == 'original':z = z.detach() # stop gradientp = F.normalize(p, dim=1) # l2-normalize z = F.normalize(z, dim=1) # l2-normalize return -(p*z).sum(dim=1).mean()elif version == 'simplified':# same thing, much faster. Scroll down, speed test in \_\_main\_\_return - F.cosine_similarity(p, z.detach(), dim=-1).mean()else:raise Exception

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjZqRaMl-1649956353718)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
 传说 simsaim的精髓就在于这个loss, 在于这个z.detach 也就是传说中的stop gradiant。 有了这个梯度停止, simsaim才能够训练的起来。 这时的simsaim就和k-means算法有点类似了。 说法很多 大家可以搜搜看。

其实我们可以看出来一点东西,在算loss时, p是预测值, z是标签,如果标签也要算梯度,两边就都在变了,参考我们平时的label都是不变的,确实z也不应该算梯度。

那么什么是stop gradiant呢 就是不算梯度的意思。比如

x = 2
y = 2**2
z = y+xz.grad = 5y.detach()
z.grad = 1

本来y是x的平方 求导等于4 所以z对x求导是5  然后不算y的梯度了 那么就只剩1了 。

这就是模型的全部了 ,输入两张图片 ,然后抽特征  预测 分别算loss

3 训练过程:

训练是非常普通的训练。

        for idx, ((images1, images2), labels) in tqdm(enumerate(local_progress)):model.zero_grad()data_dict = model.forward(images1.to(device, non_blocking=True), images2.to(device, non_blocking=True))loss = data_dict['loss'].mean() # ddploss.backward()optimizer.step()lr_scheduler.step()data_dict.update({'lr':lr_scheduler.get_lr()})local_progress.set_postfix(data_dict)# logger.update\_scalers(data\_dict)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2DWESzyO-1649956353719)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
 从测试集中抽loader  注意抽出的是两张图片 由不同transformers形成的。 之后过模型得到loss,梯度回传。 这里日志一直报错 我直接屏蔽了。

4 测试过程:

测试过程比较的关键。

这里是用knn算法进行测试的 ,关于knn 可以看 深入浅出KNN算法(一) KNN算法原理 - zzzzMing - 博客园。

简单的说, 就是从众。 在一个大平面上有很多的点, 然后你就看离自己最近的k个点,他们的标签是啥, 然后选最多的那个当自己的标签。

            accuracy = knn_monitor(model.module.backbone, memory_loader, test_loader, device, k=min(args.train.knn_k, len(memory_loader.dataset)), hide_progress=args.hide_progress) 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mlm5ZgrM-1649956353720)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

def knn_monitor(net, memory_data_loader, test_data_loader, epoch, k=200, t=0.1, hide_progress=False):net.eval()classes = len(memory_data_loader.dataset.classes)total_top1, total_top5, total_num, feature_bank = 0.0, 0.0, 0, []with torch.no_grad():# generate feature bankfor data, target in tqdm(memory\_data\_loader, desc='Feature extracting', leave=False, disable=hide\_progress):feature = net(data.cuda(non\_blocking=True))feature = F.normalize(feature, dim=1)feature_bank.append(feature)# [D, N]feature_bank = torch.cat(feature_bank, dim=0).t().contiguous()# [N]feature_labels = torch.tensor(memory_data_loader.dataset.targets, device=feature_bank.device)# loop test data to predict the label by weighted knn searchtest_bar = tqdm(test_data_loader, desc='kNN', disable=hide_progress)for data, target in test\_bar:data, target = data.cuda(non\_blocking=True), target.cuda(non\_blocking=True)feature = net(data)feature = F.normalize(feature, dim=1)pred_labels = knn_predict(feature, feature_bank, feature_labels, classes, k, t)total_num += data.size(0)total_top1 += (pred_labels[:, 0] == target).float().sum().item()test_bar.set_postfix({'Accuracy':total_top1 / total_num * 100})return total_top1 / total_num * 100

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngBi2zvU-1649956353720)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
注意 这里的net  只是backbone  也就是resnet 而 memory 就是训练数据 不过增广方式不一样 。还有训练数据 和k值 取200.

    net.eval()classes = len(memory_data_loader.dataset.classes)total_top1, total_top5, total_num, feature_bank = 0.0, 0.0, 0, []

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7xouk5O-1649956353721)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
一些初始化和获取类别数。

    with torch.no\_grad():# generate feature bankfor data, target in tqdm(memory_data_loader, desc='Feature extracting', leave=False, disable=hide_progress):feature = net(data.cuda(non_blocking=True))feature = F.normalize(feature, dim=1)feature_bank.append(feature)# [D, N]feature_bank = torch.cat(feature_bank, dim=0).t().contiguous()feature_labels = torch.tensor(memory_data_loader.dataset.targets, device=feature_bank.device)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PP2HlDe4-1649956353723)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
获取大平面上的点。 从训练集抽数据, 然后获取他们的特征。

最后的feature_bank大小是49664*512 也就是将近50000条数据 每个数据都有512 维的特征。 然后做了一个转置。

        for data, target in test\_bar:data, target = data.cuda(non\_blocking=True), target.cuda(non\_blocking=True)feature = net(data)feature = F.normalize(feature, dim=1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDcnwj0g-1649956353724)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
抽取测试集的特征。

            pred\_labels = knn_predict(feature, feature_bank, feature_labels, classes, k, t)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WnFCBRKM-1649956353725)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

def knn_predict(feature, feature_bank, feature_labels, classes, knn_k, knn_t):# compute cos similarity between each feature vector and feature bank ---> [B, N]sim_matrix = torch.mm(feature, feature_bank)# [B, K]sim_weight, sim_indices = sim_matrix.topk(k=knn_k, dim=-1)        #求出最大的knn\_k个值# [B, K]sim_labels = torch.gather(feature_labels.expand(feature.size(0), -1), dim=-1, index=sim_indices)sim_weight = (sim_weight / knn_t).exp()# counts for each classone_hot_label = torch.zeros(feature.size(0) * knn_k, classes, device=sim_labels.device)# [B*K, C]one_hot_label = one_hot_label.scatter(dim=-1, index=sim_labels.view(-1, 1), value=1.0)# weighted score ---> [B, C]pred_scores = torch.sum(one_hot_label.view(feature.size(0), -1, classes) * sim_weight.unsqueeze(dim=-1), dim=1)pred_labels = pred_scores.argsort(dim=-1, descending=True)return pred_labels

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VM3jTwOj-1649956353726)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
我们来看 knn是如果计算相似度的 ,也就是距离的。torch.mm表示矩阵的乘法。 我举个例子。

下面只是例子 ,真实数据需要归一化

a = [[1,2,3],[4,5,6]]
b = [[1,2,3],[2,4,6],[3,6,9],[4,8,1]]

a有2个样本, b有4个样本。 他们的特征都是3维。 现在求a[0]   和b中哪些样本最相似。

就要让a[0]和b中每一个样本点乘 得到 14, 28, 42, 23。数越大表示越相似,也就越近。 所以我们让a和b的转置相乘,得到:

tensor([[14, 28, 42, 23],
        [32, 64, 96, 62]])

我们发现第一排 就是a[0]的相似度, 每一列都是与b中样本的点乘结果。

    sim\_matrix = torch.mm(feature, feature_bank)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xo5oQAsn-1649956353727)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
所以这里的sim_matrix 就是一个512 * 49664大小的矩阵。 512 表示有512个样本, 49664 表示每个样本和所有点的乘积。

sim_weight, sim_indices = sim_matrix.topk(k=knn_k, dim=-1) 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oe1M2f6q-1649956353727)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
topk 表示取最大的值,和他们下标 这里取200个 我们就得到了离每一个样本,最近的那些点,他们的下标是多少。

sim\_labels = torch.gather(feature_labels.expand(feature.size(0), -1), dim=-1, index=sim_indices)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g1phlLg8-1649956353728)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
feature_labels.expand(feature.size(0), -1) 之前的文章说过 ,是一个复制扩充。 -1表示不改变维度。 feature是50000维 扩充后变成512 *50000 (注意label和49664不相等,是因为loader舍弃了最后的一部分,但是没关系 , 本来就取不到这部分值)。

torch.gather 是按下标取值。 

我们对标签按下标取值,得到了512 *200的矩阵, 每一行都表示这个样本距离最近的200个样本的标签。

sim_weight = (sim_weight / knn\_t).exp()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Yv4cEFZ-1649956353729)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
 看到后面就知道这个knn_t的作用了 。  作用就是 控制相似度的权重。 比如  一个更相似的 他的标签可以一个顶好几个不相似的。 那么顶几个呢 ? 就是t控制的了 。

    # counts for each classone\_hot\_label = torch.zeros(feature.size(0) * knn_k, classes, device=sim_labels.device)# [B*K, C]one\_hot\_label = one_hot_label.scatter(dim=-1, index=sim_labels.view(-1, 1), value=1.0)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69D7IgEB-1649956353730)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
我们需要先搞懂scatter函数 。说实话着实有点难。因为官网的scatter都很难理解了 ,何况这个和官网不一样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHeMT8PQ-1649956353731)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

我们可以看到 官网的第三个参数是src 也就是数据源,而这里是value 。。。真是奇怪。

对于tensor.scatter函数  可以看 这篇

对于torch.tensor.scatter()这个函数的理解。_亮子李的博客-CSDN博客

相信大家对scatter 都有了理解。 我们回来。

这里先创建一个 长是512 *200 = 102400 宽是10的向量。

而sim_labels的大小是 (102400,1) 这个scatter做了什么呢 ? 如下

one_hot = torch.tensor([[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0]])sim_label = torch.tensor([[3],[4]])
print(one_hot.scatter(-1,sim_label,value=1))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-in0qsohE-1649956353731)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

tensor([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ksb2H7mc-1649956353732)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
也就是把每行标签对应得数字那一列变为1 ,如果这行特征得标签是1 就把第一个数变为1,这样子。  类似的有102400行。 得到onehot后 按我得想法,  就统计200行中哪一列的1最多呗。 比如前200行里 第3列的1对多, 就说明第一个样本最近的200个里,最多的标签是2 ,。我们看看他们怎么做的。

pred\_scores = torch.sum(one_hot_label.view(feature.size(0), -1, classes) * sim_weight.unsqueeze(dim=-1), dim=1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HEUiQRQL-1649956353733)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

one\_hot\_label.view(feature.size(0), -1, classes) 

这句可以理解。 变回512 20010 这样就可以统计各自的两百个了。

sim\_weight.unsqueeze(dim=-1)

sim_weight 虽然在上面做了一点变换,但是我们其实不用管他,因为上面只是一种归一化的方式,我们依然可以把它看作最近 当前样本特征和两百个点特征的乘积。unsqueeze 表示扩充一维 在最后, sim_weight就变成了 512 *200 1。 我们如何理解这个pred_score呢? 我们不要看512个样本。 我们只看一个样本。 对于一个样本。他的one_label是20010 而sim_weight就是200 1  特征的点乘结果,也就是200个相似度分数 。 从两行 看两百行  很显然 就是让各行的标签1 乘上那个相似度分数。  之后再对200这个维度求和,就得到了各个标签相似的分数的和。 维度110

c = one_hot.scatter(-1,sim_label,value=1)
d = torch.tensor([[3],[4]])
print(c*d)#################
tensor([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]])
tensor([[0, 0, 0, 3, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 4, 0, 0, 0, 0, 0]])print(torch.sum(c*d,dim=0))#########
tensor([0, 0, 0, 3, 4, 0, 0, 0, 0, 0])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-icovLNAm-1649956353734)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
        看到这里我们明白了 。 这里的knn并不是简单的从众,他还要看影响力。 更相似的样本,他的标签对我们的结果的影响力更大。 这里相当于对标签做了一个加权求和。

回到512维 我们得到了512*10的矩阵 表示512个样本的各个标签的相似度分数 我们只要argsort就可以得到最大值的下标啦。 np.argsort这个函数可以对向量排序 然后返回他们原来的下标 des 表示可以降序。

    pred_labels = pred_scores.argsort(dim=-1, descending=True)return pred_labels

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXJWt1o8-1649956353734)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
得到标签 , 回到原来的knn

            total_num += data.size(0)total_top1 += (pred_labels[:, 0] == target).float().sum().item()test_bar.set_postfix({'Accuracy':total_top1 / total_num * 100})return total_top1 / total_num * 100

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vttHzyWZ-1649956353735)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
这里是计算top1  我估计如果计算top5 估计就是 target in labels[:,4]了 得到预测标签后准确率久很好算了。

5 :线性验证

继续跟着主函数走 。 可以看到一堆保存的步骤。 然后进入linear_eval函数。  我猜测是用backbone抽特征然后直接预测结果的函数。

train_loader = torch.utils.data.DataLoader(dataset=get_dataset( transform=get_aug(train=False, train_classifier=True, **args.aug_kwargs), train=True, **args.dataset_kwargs),batch_size=args.eval.batch_size,shuffle=True,**args.dataloader_kwargs)test_loader = torch.utils.data.DataLoader(dataset=get_dataset(transform=get_aug(train=False, train_classifier=False, **args.aug_kwargs), train=False,**args.dataset_kwargs),batch_size=args.eval.batch_size,shuffle=False,**args.dataloader_kwargs)model = get_backbone(args.model.backbone)classifier = nn.Linear(in_features=model.output_dim, out_features=10, bias=True).to(args.device)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GxgIxKBs-1649956353736)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
 先读取训练集和测试集, 然后 model是resnet  一个分类器是 一个全连接。 我好奇的是为什么不直接把backbone最后一层的恒等映射改为这个分类器呢 ?

msg = model.load_state_dict({k[9:]:v for k, v in save_dict['state\_dict'].items() if k.startswith('backbone.')}, strict=True)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TAAb17Au-1649956353737)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

载入模型  [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XEEltLDR-1649956353737)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

k长这个样子  取出那些以backb开头的层 就是resnet的层。 然后去掉前面9个字母 就是resnet的名字。

    classifier = torch.nn.DataParallel(classifier)# define optimizeroptimizer = get_optimizer(args.eval.optimizer.name, classifier, lr=args.eval.base_lr*args.eval.batch_size/256, momentum=args.eval.optimizer.momentum, weight_decay=args.eval.optimizer.weight_decay)# define lr schedulerlr_scheduler = LR_Scheduler(optimizer,args.eval.warmup_epochs, args.eval.warmup_lr*args.eval.batch_size/256, args.eval.num_epochs, args.eval.base_lr*args.eval.batch_size/256, args.eval.final_lr*args.eval.batch_size/256, len(train_loader),)loss_meter = AverageMeter(name='Loss')acc_meter = AverageMeter(name='Accuracy')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMgSkE3N-1649956353738)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
定义优化器和loss 最下面这个averagemeter是啥呀

查了一下 就是一个类似于队列这种的 数据结构。 然后可以更新  关键是可以求平均。

    for epoch in global_progress:loss_meter.reset()model.eval()classifier.train()local_progress = tqdm(train_loader, desc=f'Epoch {epoch}/{args.eval.num\_epochs}', disable=True)for idx, (images, labels) in enumerate(local_progress):classifier.zero_grad()with torch.no_grad():feature = model(images.to(args.device))preds = classifier(feature)loss = F.cross_entropy(preds, labels.to(args.device))loss.backward()optimizer.step()loss_meter.update(loss.item())lr = lr_scheduler.step()local_progress.set_postfix({'lr':lr, "loss":loss_meter.val, 'loss\_avg':loss_meter.avg})

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zamji7ew-1649956353739)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
然后定义好后 就是一个普通的训练过程了 。 值得注意的是 model是eval模型 也就是他是冻住的,参数不改变。而classfier是可以改变的,  梯度回传也只回传分类头的梯度, 这里就只训练分类器。

classifier.eval()correct, total = 0, 0acc_meter.reset()for idx, (images, labels) in enumerate(test_loader):with torch.no_grad():feature = model(images.to(args.device))preds = classifier(feature).argmax(dim=1)correct = (preds == labels.to(args.device)).sum().item()acc_meter.update(correct/preds.shape[0])print(f'Accuracy = {acc\_meter.avg*100:.2f}')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TM0LCibW-1649956353740)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
普通的测试。

6 : 用自己数据集进行对比学习。

路走远了,别忘了开始的方向。 我们是用对比学习解决食物分类的问题的。

我们要做的有几件事情。

第一:  改数据集 :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Vf0Zagk-1649956353741)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

把它原来的三个数据集全#了。

然后 加入自己的数据集。  使用他的增广方式。 但在增广前   需要在 dataset的get里加 topil 因为他的增广里没有这个。

hw3食物分类有三个数据集:

一个有标签训练集 我用来当memory

一个无标签训练集  我用来当train

一个验证集 我用来测试。

pil\_trans = transforms.ToPILImage()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uZ0FKx7e-1649956353741)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ajDBQxI-1649956353742)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

    filepath = '/home/lhy/hw3/food-11'train\_loader = getDataLoader(filepath, 'train\_unl', True, args.train.batch_size, transform=get_aug(train=True, train_classifier=False, **args.aug_kwargs))memory\_loader = getDataLoader(filepath, 'train', False,args.train.batch_size, transform=get_aug(train=False, train_classifier=False, **args.aug_kwargs))test\_loader = getDataLoader(filepath, 'val', False,args.train.batch_size, transform=get_aug(train=False, train_classifier=False, **args.aug_kwargs))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KbPaz1m-1649956353743)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]

2 改变batch_size和图片大小。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zUin5Rwo-1649956353744)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

在这个文件里改batch  你会发现这个对比学习的模型 出奇的占内存,当我图片大小为224时,我的batch只能设置为32.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SjbUHbJE-1649956353745)(https://img2022.cnblogs.com/blog/2375794/202204/2375794-20220414203749691-158522950.gif “点击并拖拽以移动”)]​

main函数里改imagesize 为自己的。

点运行。O了 。 然后发现效果并不是很好。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401101.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wiki常用语法

为什么80%的码农都做不了架构师&#xff1f;>>> 说明 输入 效果 作用在任何地方 斜体字 斜体字 斜体字 粗体字 粗体字 粗体字 粗体加斜体 粗体加斜体 粗体加斜体 下划线 &#xff08;推荐替代斜体&#xff09; <u>下划线</…

【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)

注&#xff1a;本文是【ASP.NET Web API系列教程】的一部分&#xff0c;如果您是第一次看本博客文章&#xff0c;请先看前面的内容。 3.3 Calling a Web API From a WPF Application (C#) 3.3 通过WPF应用程序调用Web API(C#) 本文引自&#xff1a;http://www.asp.net/web-api/…

java开发过程中的命名规范

为什么80%的码农都做不了架构师&#xff1f;>>> 最近在读项目的过程中&#xff0c;发现好多同事的代码并不是很规范&#xff0c;有的包名也按照了驼峰的写法&#xff0c;虽说这样不是不行&#xff0c;但个人认为开发过程中应该遵守这些规范&#xff0c;现整理规范如…

git 使用方法自用(勿进)本地开发分支推上线上开发分支

一、//查看状态 1.git status 二、//查看改了哪个文件夹 1.git diff 2.//会出现改了哪个文件夹src/components/partials/Slider.js 三、//查看改了的文件夹里面具体改了啥内容 1.git diff src/components/partials/Slider.js 四、提交所有 1. git add . 五、写备注…

SVG 和 CSS3 实现一个超酷爱心 Like 按钮

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 在现代网页中&#xff0c;我们经常可以在一些文章、视频和图片页面上找到”Like”按…

redis简单学习3-redis常用命令总结

2019独角兽企业重金招聘Python工程师标准>>> 1.键值相关的命令 keys 返回满足给定pattern的所有key 表达式* 代表取出所有的key redis 127.0.0.1:6379> keys * 1) "myzset2" 2) "myzset3" 3) "mylist" 4) "myset2" 5)…

微软的创新还是败笔?Windows 8为苹果创造天赐良机

网络安全公司Avast近日公布的数据显示&#xff1a;Windows系统使用人数正在不断下降。这一结果对微软来说可谓噩梦&#xff0c;于此同时&#xff0c;苹果则不断加强市场占有率。又是什么导致了这样的结果&#xff1f;接下来就看看最主要的几点&#xff1a; 调查覆盖了13.5万Wi…

testbench常用任务之SPI slave输出数据

模仿了SPI master从SPI slave读取数据的MiSO数据状态 每8个clk后读取的数据加1 用的一些额外寄存器需要自己声明和初始化 //------------------------------------------------------------------------- //send serial data which is increase by 1 on MiSO line //----------…

Vue生产环境调试的方法

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 vue 生产环境默认是无法启用vue devtools的&#xff0c;如果生产应用出了问题&…

RavenDB:基于Windows/.NET平台的NoSQL数据库

众所周知&#xff0c;NoSQL运动旨在成为大数据时代传统关系数据库管理系统的替代品。如今Microsoft对开源的态度有所转变&#xff0c;RavenDB就是很好的例子。Microsoft对RavenDB&#xff08;NoSQL数据库&#xff09;的认可令很多人感到惊讶。RavenDB可以轻易的替代关系数据库管…

JDBC实例--JDBC连接池技术解密,连接池对我们不再陌生

一、为什么我们要用连接池技术&#xff1f; 前面的数据库连接的建立及关闭资源的方法有些缺陷。统舱传统数据库访问方式&#xff1a;一次数据库访问对应一个物理连接,每次操作数据库都要打开、关闭该物理连接, 系统性能严重受损。 解决方案&#xff1a;数据库连接池&#xff08…

重温51汇编指令(附实验)

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 重温51汇编指令&#xff08;附实验&#xff09; 写在前面&#xff1a; 在电子控制…

SSH HTTPS 公钥、秘钥、对称加密、非对称加密、 总结理解

2019独角兽企业重金招聘Python工程师标准>>> 作者&#xff1a;shede333主页&#xff1a;http://my.oschina.net/shede333 && http://blog.sina.com.cn/u/1509658847版权声明&#xff1a;原创文章&#xff0c;版权声明&#xff1a;自由转载-非商用-非衍生-保…

升级nginx,查看已经安装的模块,并隐藏或者修改版本号

升级前&#xff0c;查看已经安装的版本以及模块[rootmail ~]# /opt/nginx/sbin/nginx -Vnginx version: nginx/0.5.34 built by gcc 3.4.6 20060404 (Red Hat 3.4.6-3)configure arguments: --prefix/opt/nginx --sbin-path/opt/nginx/sbin/nginx --conf-path/opt/nginx/conf/n…

SpringCloudAlibaba微服务docker容器打包和部署示例实战

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 概述 我们使用前面《SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码…

MongoVUE的Collections数据不显示的解决方法

问题描述&#xff1a; 使用 mongoDB数据库&#xff0c; 数据添加成功了&#xff0c;使用命令行能查询出来&#xff0c;但在MongoVUE 中数据却不显示 (我使用的是 mongoDB 3.4 的版本) 原因&#xff1a;引擎问题&#xff0c;只要降到2.X版本就可以显示了     3.x默认是wire…

SaltStack WEB UI Halite初体验

闲来无聊&#xff0c;话说saltstack webui halite还一直没玩&#xff0c;于是就凑今天体验一把&#xff1b;很多尝鲜的同学都说halite的功能较少&#xff0c;而其也正符合其说明console&#xff0c;不过其UI我还是蛮喜欢的&#xff0c;个人觉得比较清新简洁、挺好下面就来安装体…

Envoy熔断限流实践(二)Rainbond基于RLS服务全局限流

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

TAppEncoder的main函数

TAppEncoder是编码器工程&#xff0c;完成视频序列的编码。 运行时&#xff0c;首先调用encmain.cpp中的main函数 main函数中完成的工作主要有初始化encoder类&#xff0c;解析cfg文件&#xff0c;然后调用TAppEncTop::encode函数进入下一层&#xff0c;并且对编码过程进行计时…

【大话云原生】微服务篇-五星级酒店的服务方式

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…