😇DL model feature extraction for t-SNE
icon
前言
我没做过这么无语的数据分析,踩了一万个坑,特此纪念,望周知
前面是具体流程,最后是经验总结
一、GPT,启动!
图像和其他数据的降维还是略有区别,如果是一维的数据,如信号等,可以直接进入sklearn中的函数,进行降维处理,图像则需要通过提取特征,然后展平再输入。
对于一般的模型,可以通过去掉最后一层的全连接层来实现特征的输出,即保留判断分类的依据,我们要做的,即是对提取出来的特征进行降维,来查看提取特征的能力。
这其实是一个很好的框架,过程清晰,代码量也很少,尽管我调了很多内容,换了颜色,但是最后的3D图实际效果可以说是非常怪异了:

ps. 附一个看到的Adobe家的调色网站,非常实用,搭配Adobe Illustrator更好用,以及一个看到的B站教程:
(嗯……改天整一个配色专题吧)
与此同时,为了调整参数,也是用了网格搜索的方法:
点我查看现代炼丹术
最后的结果,不能说是效果甚微,只能说是于事无补。图就不放了,意义不大。
感谢我的舍友hy,为我提出一个新的思路:
“原图像和最后的差别大吗,就是处理前的降维图,和这个差别大吗,差别大的话你放对比图就完了”
于是想到用进入模型和不进入模型的结果进行对比,来展现模型的性能。是个非常不错的想法,但是我要睡觉了,明天再弄。
二、为什么要使用DataParallel! 🥒🥒🥒
满怀着希望,我打开了kaggle,进行cv操作,分别写出进入模型和不进入模型的代码,一顿操作发现:
我得到的两个图片是一样的!!!
本来只是觉得相似,但是当我把两张图片放一起对比,仔细查看发现两张图片完全一样。难蚌。
与此同时,我也发现了一个事情,对于一般的模型。确实是可以直接去掉最后的全连接层,输出特征,但是我们的VTCNet在这里是不通用的:

在VTCNet模型中,则是需要去掉最后的卷积池化,不把特征变为输出,而不是简单的删去最后一个层,昨天完全没有意识到这一点,我忘了(
之后向学长求证了一下:
“没有全连接层,softmax应该是在损失函数之中,我最后这个模块的前面就类似于输出特征,差别就在于用线性层分类是利用线性层进行的分类学习,而我们这个方法是在卷积层的时候就把分类的内容学了,但这么做可以减少过拟合”
回到代码,我进行了测试:
输出如下:
于是很容易得到一个结论:
我们在去掉最后一个子层的操作时,把整个模型删掉了,这是一个空的模型
所以也就解释了前面,为什么得到了两个输出图像是完全一样的——毕竟根本没进模型啊!
现在有两个问题:
- 为什么删掉-1,也就是最后一个层的时候,整个模型都没有了
- 怎么才能删掉后面几个层,适应这个模型
后来几经试验发现,是DataParallel的问题

当时我尝试用T4×2来进行加速,于是加上了这个东西,最后虽然用的是一张P100进行计算,但是代码被保留了下来。在这个函数中,DataParallel会对模型进行封装,也就是变为一个整体,再分配到不同的GPU进行运算。而保存模型的时候,当然也是一个封装好的模型。其实解决这个东西很简单,只需要使用model.module即可解除封装,拿到真正的模型:
(这里写的很少,但是我真不知道这个知识点,浪费了巨——多的时间 😵一度想重跑模型了,但是又大概率复现不出来)
接下来的思路:
效果差->对比->是否进入模型效果相同->检查模型->模型问题
下一步是检查模型,发现需要从concat之后输出特征,然后展平,再降维分析
三、在这创建一个新模型
由于dataparallel将模型封装为了一整个,所以才会导致去掉最后一层的时候删除整个模型,于是先解除封装,获取模型,然后复制除了最后两个子模块,作为一个新模型
效果:去掉了classification的部分,卷积和池化
这次知道检查模型了,检查之后没有问题(

删除的后两个模块,正常
但是之后运行仍会出错,开始debug
读入数据,预处理,先进入完整模型,尝试提取特征
(这里埋一个坑,为什么是否转化为dataloader都能运行?)
然后可以得到features,非常正常,维度、通道数也都对的上
但是在new_model上运行,一直报错,显示通道数不对,检查一下
没问题啊这也(
和训练模型时候一样,不知道为啥报错
我把原模型和我创建的模型的子模块输出,发现不同:

在已经尝试的方法中,直接用Sequential创建序列并不可行,会出现报错,大小不相同。
推测是因为已有的包基于Sequential创建,而我们的模型最外层是VTCNet,所以不能通过Sequential来复制模块。那么问题来了:我们的VTCNet模型并没有写相关的函数,能够重建或者是复制一部分模型,所以这里是可能会出问题的。
(这里其实挖坑了,后续没有测试sequential通过别的方法能不能运行)
于是搜到了一些文章,提供了很大帮助:
转换思路,不新建模型,因为前面的工作,已经能让图片在预训练的模型上运行,并且最后输出经过softmax的结果,所以使用这个链接的方法,改为计数,每经过一个层i+1,当i等于特定值的时候,从中间输出
使用这个函数来查看子层有哪些:
然后发现len是5:
0是focus,1是ViT模块,2是CNN-SPPF模块,3是分类模块,4是输出模块
输出发现vtcnet一共是五个子层,分别看看
结果,使用#######分割
所以可以尝试加入判断,如果i小于3继续进行,这样在第三层之前就输出feature
一直显示大小错误,后知后觉反应过来了,序列是0,1,2,3,4五个块,表现出来是顺序进行,但是我们的模型并不是:

实际顺序是0→1,0→2,3,4,在3之前的前向传播的时候concat,要解决这个问题,也许我需要再手动前向传播一下(
但是也有一个新思路,我用同样一批数据分别进行0→1和0→2的操作,手动连接两个矩阵,似乎也是可行的
四、最后的特征提取 😇—>t-SNE
分别用[0,1]和[0,2]进行特征提取,再连接
至少分别运行出来的结果是对的
下一个遇到的问题是如何进行特征连接,稍作修改,分别存储01和02的特征(虽然想过直接用for循环加,但是最后选择)直接用函数解决
上面是连接的函数,axis表示沿着第多少个维度上连接,从0开始数,0是最外层
至此,特征提取的工作算是终于完成了
降维!

终于出来了
再出个三维的:

至此才算是终于成了,之后还能通过调参让图更好看一些,包括学习率、困惑度、角度
五、总结
- 对比别人的图效果不好的情况下,可以与自己模型处理前的数据对比,主要是体现模型的用处
- DataParallel在训练模型的时候能够并行计算,分配到不同计算单元,但是会对模型进行封装,在操作模型具体模块的时候,需要使用
.module
进行解除
- Sequential是序列的方法,不适用于所有方法。尽管我们的模型存在并行训练的部分,但是通过查看子模块的方式,输出展现为一个sequential,这里是会有误导的。所以即使模型不做更改,按照01234的顺序训练,也是无法成功的
- 需要非常熟悉模型的各个模块,维度等等,不然总是要掉坑里的。这次也是,前面一直显示维度通道对不上,其实0模块输出的正好也是2模块的输入,就是从这里才发现不能顺序运行的。
- 这次虽然做了图,但是t-SNE的具体方法还没有完全掌握,只知道大概
归根结底还是积累的实践经验太少了,我在想,实际完成一个方法,从零到学会理论,也许只有一半
也可能连一半都没有,理论知识一节课怎么也学会了,快的话半小时,多的话一小时,两小时
但是这次分析前前后后把周末搭进去了,周五的晚上,周六的晚上,周日的下午和晚上
中间差点放弃(毕竟一个分析没那么重要),最后还是坚持下来了
一些其他的方法:PCA,u-map,可以尝试