【kaggle比赛记录】SHOPPE商品分类多模态分析

【kaggle比赛记录】SHOPPE商品分类多模态分析

文本方向处理

TFIDF

这个方法就是一个统计词频加逆文档频率的统计学方法,来找出最能代表一篇文章的一组关键词。sklearn提供了包来进行词汇的向量化,向量化之后的词汇向量组大小是 (34250个样本, 32324词汇)。

from sklearn.feature_extraction.text import TfidfVectorizer
model = TfidfVectorizer(stop_words=None, binary=True, max_features=55000, use_idf=True)

并使用KNN聚类来寻找相近的标题。

preds = []
CHUNK = 1024*4

print('Finding similar titles...')
CTS = len(train)//CHUNK
if len(train)%CHUNK!=0: CTS += 1
CTS_index = 0
for j in range( CTS ):
    
    a = j*CHUNK
    b = (j+1)*CHUNK
    b = min(b,len(train))
    print('chunk',a,'to',b)
    
    # COSINE SIMILARITY DISTANCE
    # cts = np.dot( text_embeddings, text_embeddings[a:b].T).T
    cts = torch.matmul(text_embeddings, text_embeddings[a:b].T).T
    cts = cts.data.cpu().numpy()
    print(cts.shape)
    for k in range(b-a):
        # IDX = np.where(cts[k,]>0.7)[0]
        IDX = np.where(cts[k,]>0.7)[0]
        o = train.iloc[IDX].posting_id.values
        preds.append(o)
        CTS_index += 1
# del model, text_embeddings

这个方法单独使用的CV如下

简介 CV
TFIDF-KNN-阈值k0.7 0.613
TFIDF-KNN-阈值k0.6 0.670
TFIDF-KNN-阈值k0.7-西语翻译 0.551

CV达到了0.670

由于这个比赛存在多种外语,我尝试使用seq2seq模型来翻译印尼语

##translation part
if Translation_iden:
    from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, pipeline

    tokenizer = AutoTokenizer.from_pretrained("/database/kaggle/shopee/othercodes/id-en-converter", use_fast=False)
    txt_model = AutoModelForSeq2SeqLM.from_pretrained("/database/kaggle/shopee/othercodes/id-en-converter")

    device = torch.device("cuda:2" if torch.cuda.is_available() else "cpu")
    txt_model.to(device)
    txt_model.eval()
####################
##translation part

trans_texts = []
CHUNK = 30

print('translating texts')
CTS = len(train)//CHUNK
if len(train)%CHUNK!=0: CTS += 1
for j in range(CTS):
    a = j*CHUNK
    b = (j+1)*CHUNK
    b = min(b,len(train))
    print('chunk',a,'to',b)
    input_ids = tokenizer(list(train.iloc[a:b].title.values), return_tensors="pt", truncation=True, padding=True).input_ids.to(torch.device('cuda:2'))
    outputs = txt_model.generate(input_ids=input_ids, num_return_sequences=1)    
    val = tokenizer.batch_decode(outputs, skip_special_tokens=True)
    trans_texts.extend(val)

但是效果很差啊哈哈哈。

训练集语言的样本分布大概是这样的:

语言 样本数
英语 18939
印尼语 8715
马来语 2398
德语 854

之后可能考虑使用简单地替换词来完成工作0.0

为了方便介绍BM25并且作比较,简单介绍一下TFIDF算法:

TFIDF = TF*IDF

TF =
T F I D F = T F ∗ I D F TFIDF = TF * IDF TFIDF=TF∗IDF
T F = f r e q ( i , j ) m a x l e n ( j ) TF=\frac{freq(i,j)}{max_{len(j)}} TF=maxlen(j)​freq(i,j)​
在这里 f r e q ( i , j ) freq(i,j) freq(i,j)是词汇wi在文档dj中出现的频率, m a x l e n ( j ) max_{len(j)} maxlen(j)​是文档的长度
I D F = l o g ( l e n ( D ) n ( i ) ) IDF=log{(\frac{len(D)}{n(i)})} IDF=log(n(i)len(D)​)
这里D表示文档集合中文档的总数,ni表示含有wi这个词的文档的数量

BM25

BM25是增强版的tfidf,通常用来做搜索相关性评分的,也是ES中的搜索算法,通常用来计算 q u e r y query query和文本集合 D D D中每篇文本之间的相关性。我们用 Q Q Q表示 q u e r y query query,在这里 Q Q Q一般是一个句子。在这里我们要对 Q Q Q进行语素解析(一般是分词),在这里以分词为例,我们对 Q Q Q进行分词,得到 q 1 , q 2 , . . . . . . , q t q_1,q_2,......,q_t q1​,q2​,......,qt​这样一个词序列。给定文本 d ∈ D d∈D d∈D,现在以计算 Q Q Q和 d d d之间的分数(相关性),其表达式如下:
Score ⁡ ( Q , d ) = ∑ i = 1 t w i ∗ R ( q i , d ) \operatorname{Score}(Q, d)=\sum_{i=1}^{t} w_{i} * R\left(q_{i}, d\right) Score(Q,d)=i=1∑t​wi​∗R(qi​,d)
上面式子中 w i w_{i} wi​ 表示 q i q_{i} qi​ 的权重, R ( q i , d ) R\left(q_{i}, d\right) R(qi​,d) 为 q i q_{i} qi​ 和 d d d 的相关性, Score ⁡ ( Q , d ) \operatorname{Score}(Q, d) Score(Q,d) 就是每个语素 q i q_{i} qi​ 和 d d d 的 相关性的加权和。
w i w_{i} wi​ 的计算方法有很多,一般是用 I D F I D F IDF 来表示的,但这里的 I D F I D F IDF 计算和上面的有所不同,具体的 表达式如下:
w i = IDF ⁡ ( q i ) = log ⁡ N − n ( q i ) + 0.5 n ( q i ) + 0.5 w_{i}=\operatorname{IDF}\left(q_{i}\right)=\log \frac{N-n\left(q_{i}\right)+0.5}{n\left(q_{i}\right)+0.5} wi​=IDF(qi​)=logn(qi​)+0.5N−n(qi​)+0.5​
上面式子中 N N N 表示文本集合中文本的总数量, n ( q i ) n\left(q_{i}\right) n(qi​) 表示包含 q i q_{i} qi​ 这个词的文本的数量, 0.5 0.5 0.5 主要是 做平滑处理。
R ( q i , d ) R\left(q_{i}, d\right) R(qi​,d) 的计算公式如下:
R ( q i , d ) = f i ∗ ( k 1 + 1 ) f i + K ∗ q f i ∗ ( k 2 + 1 ) q f i + k 2 R\left(q_{i}, d\right)=\frac{f_{i^{*}}\left(k_{1}+1\right)}{f_{i}+K} * \frac{q f_{i}^{*}\left(k_{2}+1\right)}{q f_{i}+k_{2}} R(qi​,d)=fi​+Kfi∗​(k1​+1)​∗qfi​+k2​qfi∗​(k2​+1)​
其中
K = k 1 ∗ ( 1 − b + b ∗ d l a v g d l ) K=k_{1} *\left(1-b+b * \frac{d l}{a v g d l}\right) K=k1​∗(1−b+b∗avgdldl​)
上面式子中 f i f_{i} fi​ 为 q i q_{i} qi​ 在文本 d d d 中出现的频率, q f i q f_{i} qfi​ 为 q i q_{i} qi​ 在 Q Q Q 中出现的频率, k 1 , k 2 , b k_{1}, k_{2}, b k1​,k2​,b 都是可调节的参 数, d l , a v g d l d l, a v g d l dl,avgdl 分别为文本 d d d 的长度和文本集 D D D 中所有文本的平均长度。
一般 q f i = 1 q f_{i}=1 qfi​=1, 取 k 2 = 0 k_{2}=0 k2​=0, 则可以去除后一项, 将上面式子改写成:
通常设置 k 1 = 2 , b = 0.75 k_{1}=2, b=0.75 k1​=2,b=0.75 。参数b的作用主要是调节文本长度对相关性的影响。

然后这个玩意儿在python没有并行处理的库,因此直接放弃了。

句子向量

词向量的聚合也有几种方法:简单聚合,包括mean-polling和max-polling;加权聚合,包括TFIDF和SIF。

BERT

都说到NLP怎么能不说BERT呢。BERT模型原生就支持句子匹配,但是原生权重不适合用于句子相似度的计算。

我查到一个Sentence-Bert模型,但是还没有尝试。

实际上bert模型并不能提高多少,因为这个比赛使用tfidf已经足够好了。

图片方向处理

图片是一种典型的非结构化数据,一般任务分为两种, 图像分类和图像检索

图像特征分为全局特征(例如CNN)和局部特征(SIFT)

图片哈希值

一种无监督的思路,具体方法有DHash、AHash、PHash、WHash。
使用固定长度的字符串来标识图片,可以储存在数据库,计算资源占用少,但同时任何图片的变化都会改变哈希值,存在不同图片哈希值相同。
【kaggle比赛记录】SHOPPE商品分类多模态分析

颜色直方图

统计颜色空间像素点的取值分布,可以抵抗图像的旋转平移,但是对颜色变换非常敏感。
【kaggle比赛记录】SHOPPE商品分类多模态分析

局部特征

比如SIFT,ORB,AKAZE等一些局部特征提取,通过提取图像的关键点进行匹配,可以抵抗图像选转、平移和形变,但是点之间的匹配计算复杂度非常的大,而且容易受到文字的影响。
【kaggle比赛记录】SHOPPE商品分类多模态分析

深度学习特征

主要是基于卷积神经网络的各种衍生网络,

EfficientNet B1-B7

EfficientNet的文章代码

来自谷歌大脑团队的文章,还有另外一个叫EfficientDet的晚辈。其中B0-B7对应资源消耗的总量提升。我这里一般用到B4或者B5,可以看到,B5已经对准确率几乎没有提升了。

卷积神经网络(ConvNets)通常是在固定的资源预算下发展起来的,如果有更多的资源可用的话,则会扩大规模以获得更好的精度,比如可以提高网络深度(depth)、网络宽度(width)和输入图像分辨率 (resolution)大小。但是通过人工去调整 depth, width, resolution 的放大或缩小的很困难的,在计算量受限时有放大哪个缩小哪个,这些都是很难去确定的,换句话说,这样的组合空间太大,人力无法穷举。基于上述背景,该论文提出了一种新的模型缩放方法,它使用一个简单而高效的复合系数来从depth, width, resolution 三个维度放大网络,不会像传统的方法那样任意缩放网络的维度,基于神经结构搜索技术可以获得最优的一组参数(复合系数)。从下图可看出,EfficientNet不仅比别的网络快很多,而且精度也更高。

【kaggle比赛记录】SHOPPE商品分类多模态分析

对网络的扩展可以通过增加网络层数(depth,比如从 ResNet (He et al.)从resnet18到resnet200 ), 也可以通过增加宽度,比如WideResNet (Zagoruyko & Komodakis, 2016)和Mo-bileNets (Howard et al., 2017) 可以扩大网络的width (#channels), 还有就是更大的输入图像尺寸(resolution)也可以帮助提高精度。如下图所示: (a)是基本模型,(b)是增加宽度,(c)是增加深度,(d)是增大属兔图像分辨率,(d)是EfficientNet,它从三个维度均扩大了,但是扩大多少,就是通过作者提出来的复合模型扩张方法结合神经结构搜索技术获得的。

【kaggle比赛记录】SHOPPE商品分类多模态分析

EfficientDet

EFFDET的 文章代码。这个方法主要用于目标检测。
在EffNet的基础上增加了BiFPN,是一种加权机制,说白了就是对不同分辨率的特征输入引入可学习的权值,来实现更好的多尺度特征融合。他们提出的方法可以统一的对所有的主干路网络、特征网络和预测网络的分辨率、深度和宽度进行缩放。

多模态检索

多模态问题主要是因为不同数据组织的方式不同,特征提取的方法不同,比如说时间序列信息,图片信息,矢量信息,文字信息等等。

结构化的数据比如数值、类别、字符串等等,非结构化就多了,文本图像语音视频时间序列,凡是需要特征处理的都是非结构化数据
【kaggle比赛记录】SHOPPE商品分类多模态分析

多模态检索想要快,就必须进行特征向量化,提取后再使用距离计算方法进行快速检索,比如说MSE和cos相似度。向量化可以代替局部特征1v1的特征对比过程,时间复杂度大幅度下降。下面介绍几个常见的多模态模型。

CILP

文章代码

将图像和文本进行编码,并且将图像和文本之间进行预训练。

visualBert

文章代码

对图像进行物体检测,将文本和图像同时输入transformer

ECA-Net

先说ECA-Net的前身,SE-Net Squeeze-and-Excitation Networks 是由自动驾驶公司Momenta在2017年公布的一种全新的图像识别结构,它通过对特征通道间的相关性进行建模,把重要的特征进行强化来提升准确率。这个结构是2017 ILSVR竞赛的冠军,top5的错误率达到了2.251%,比2016年的第一名还要低25%,可谓提升巨大。

自Inception之后,人们已经不再通过暴力的增加网络层数的方法来企图获得更高的准确率了,然而面对复杂的问题,过浅的网络很难达到理想的效果,所以加深网络依然是解决图像分类问题的最佳途径。但是增加网络很容易造成overfiting甚至训练集上得到的效果也比浅层网络要差,所以如何有效的增加网络层数就成了深度学习领域研究的重中之重。

为了解决这个问题,很多学者提出了自己的见解,如resnet所讲述的残差网络就是一例。然而残差网络虽然能够增加层数但是层数增加到三位数之后再增加也就有些无能为力了。此时作者另辟蹊径,提出了一种新型的网络结构SENet,这个网络结构可以对所有网络进行改进然后做到真正有效的增加层数,无论原网络层数有多深,通过加入SENet,都能增加相当数量的深度,并有效的提高实验效果。值得一提的是SENet在2017年的ImageNet挑战赛获得冠军。

从Inception开始,学者们提出网络的时候就主要是提出一个block,然后用这个block像搭积木一样的搭出整个网络。同样,作者也是用这个方法设计网络的,作者提出了一个Squeeze-and-Excitation block,然后用这个SE block搭出了整个网络。

卷积神经网络虽然有诸多的好处,但是卷积神经网络捕捉到的只是局部的信息,假如卷积核是7x7,那么感受野的大小也只有7x7。但是一张图片的每一个像素点之间都是互相有联系的,之前使用局部感受野的网络都忽略掉了全局像素点之间的关联信息,使得实验效果不够理想。Inception通过多尺度的卷积核,找到了提取一张feature map上全局关联信息的方法,然而直到本文网络之前都没有谁考虑各通道之间的全局关联信息。所以作者将关注点放到了通道关联信息上,作者发现提取出通道之间互相关联的信息可以有效的增加神经网络的分类准确率。

也就是说,SE-NET可以通过整合多种卷积核的感受野来找到全局像素点之间的关联信息,并利用这些信息增加神经网络的分类准确率。但是这带来的巨大的算力负担,ECA-Net就是为了改进这个网络,在大幅度降低参数的同时,还能保持高性能。本文提出了一种有效的通道关注(ECA)模块,该模块只增加了少量的参数,却能获得明显的性能增益。通过对SENet中通道注意模块的分析,作者的经验表明避免降维对于学习通道注意力非常重要,适当的跨信道交互可以在显著降低模型复杂度的同时保持性能。因此,足者提出了一种不降维的局部跨信道交互策略,该策略可以通过一维卷积有效地实现。进一步,作者又提出了一种自适应选择一维卷积核大小的方法,以确定局部跨信道交互的覆盖率。 实验证明,本文提出的ECA模块是高效的。

ECA模块与其他注意力模块的比较:这里以ResNets作为骨干模型来进行分类精度,网络参数和FLOPs的比较,以圆来表示。从下图中,我们可以发现,ECA网络模块获得了更高的精度,同时有较少的模型复杂性。
【kaggle比赛记录】SHOPPE商品分类多模态分析

NFNet

然后是抛弃了归一化的模型NFNet,由deepmind推出,可以看到,这个网络又碾压了EffNet
【kaggle比赛记录】SHOPPE商品分类多模态分析

多模型结合

我们暂时使用 parth77 老哥的权重。这个老哥是Coursera的机器学习讲师,我估计这个比赛被Coursera拿去当比赛范例了吧……毕竟模型非常经典。

一般来说有两种方式结合多个模型:

  1. 拼接两个模型的隐藏层输出,然后用这个拼接的向量放到机器学习模型(比如KNN)去聚类。举个例子,B1的输出是1280,B2是1408,B3是1536(用的全是同一个模型当然效果不好),拼起来是4224的隐藏层向量,使用K近邻算法去聚类。
  2. 每个模型分别做预测,然后将预测结果聚合去除重复。

高分模型

首先是parth77的几个模型

  1. ResNext50-32x4d (LB >= 0.72) : https://www.kaggle.com/parthdhameliya77/pytorch-resnext50-32x4d-image-tfidf-inference (Adam+relu activation) 经典的resnet,但是这里可以把池化层换了,效果会更好。
  2. EfficientNet B3 (LB >= 0.723) : https://www.kaggle.com/parthdhameliya77/pytorch-efficientnet-b3-image-tfidf-inference (Adam+relu activation) EFFnet之前讲过了,限定算力下模型的深度宽度和精度的取舍,用的是Adam优化器加relu激活层的结构,用SGD效果会更好,但是训练更慢
  3. EfficientNet B5 (LB >= 0.729) : https://www.kaggle.com/parthdhameliya77/pytorch-efficientnet-b3-image-tfidf-inference (Adam+relu activation)
  4. EfficientNet B5 (LB >= 0.729) : (Ranger+mish activation)
  5. ECA-NFNET-I0 TFIDF (LB >=0.731) https://www.kaggle.com/parthdhameliya77/pytorch-eca-nfnet-l0-image-tfidf-inference

一些问题

比赛的测试及训练集group有没有重合?

只有小部分重合,这里是参赛选手ranger给出的解释

因此我们需根据已有的group进行分组训练,然后对未知的group进行聚类。

因此训练完模型之后,在inference时候,引用模型会drop掉最后的全连接层,只用前面的卷积特征部分来提取特征。最后再用测试集数据跑通训练好的模型的arcface来比较他们的相似度。
【kaggle比赛记录】SHOPPE商品分类多模态分析

存在标题相同,但是group不同的商品组合

https://www.kaggle.com/c/shopee-product-matching/discussion/227079

因为他们的图片会不一样

这个比赛,同一个group的商品需要文本和图片都匹配,因此使用投票在组装模型会更好。

Resnet18提升的关键

对特征进行L2正则化,并且使用max-pooling,保证特征被归一化到统一量纲,保证cos距离在1以内,方便比较。max-pooling主要是保证了特征保留更关键的信息。

更改CNN特征代码里的RES18到DENSE161

只需要改变最后的pooling层,保证特征维度相同
他们都是imagenet的模型。输出维度1000维。他们提取的卷积层维度不同。一般是CNN+FC,不同的模型CNN特征维度不同。

公开榜单和本地交叉验证的区别

这里

上一篇:证明实对称正定矩阵A的Gauss-Seidel法必定收敛(完整过程)


下一篇:2021ICPC西安邀请赛记录