文章Flyai人工智能社区参赛非官方指南—代码篇
Flyai人工智能社区参赛非官方指南—代码篇春节前写了个简易版的参赛指南,居然得到了11个赞,通过对比,发现这已经算是多的了,所以感谢这11位小伙伴不吝鼠标左键。通过这些天观察,发现有不少新的小伙伴,想着这些小伙伴将是祖国的未来,顿时感觉应该发挥一下余热,让新来的小伙伴少走弯路,尽快上手FLYAI赛事,领略AI魅力,得个好名次,赚点零花钱,实现小目标,迎娶白富美,走上人生巅峰……好了,废话不多说,针对一些小伙伴提出的没有baseline的问题,这次是代码篇,以实例说话,主要介绍main.py和prediction.py这两个文件框架。因为这两个文件最重要,其它的文件最终都是为这两个文件服务的。理论上我们只要完成这两个文件的编写,就可以完成比赛了。下面,我将以“1024程序员节—蝴蝶分类开源竞赛”为例,讲解一下这两个文件的结构和编写方法。因为这个比赛开源了,所以小伙伴们可以免费下载这个比赛的代码。 一、main.py将官方样例下载到本地,解压后,就可以看到这个main.py文件,打开它,便得到下面的代码框架: # -*- coding: utf-8 -*- import argparse import os from flyai.data_helper import DataHelper from flyai.framework import FlyAI from path import MODEL_PATH ''' 此项目为FlyAI2.0新版本框架,数据读取,评估方式与之前不同 2.0框架不再限制数据如何读取 样例代码仅供参考学习,可以自己修改实现逻辑。 模版项目下载支持 PyTorch、Tensorflow、Keras、MXNET、scikit-learn等机器学习框架 第一次使用请看项目中的:FlyAI2.0竞赛框架使用说明.html 使用FlyAI提供的预训练模型可查看:https://www.flyai.com/models 学习资料可查看文档中心:https://doc.flyai.com/ 常见问题:https://doc.flyai.com/question.html 遇到问题不要着急,添加小姐姐微信,扫描项目里面的:FlyAI小助手二维码-小姐姐在线解答您的问题.png ''' if not os.path.exists(MODEL_PATH): os.makedirs(MODEL_PATH) # 项目的超参,不使用可以删除 parser = argparse.ArgumentParser() parser.add_argument("-e", "--EPOCHS", default=10, type=int, help="train epochs") parser.add_argument("-b", "--BATCH", default=32, type=int, help="batch size") args = parser.parse_args() class Main(FlyAI): ''' 项目中必须继承FlyAI类,否则线上运行会报错。 ''' def download_data(self): # 根据数据ID下载训练数据 data_helper = DataHelper() data_helper.download_from_ids("ButterflyClassification") def deal_with_data(self): ''' 处理数据,没有可不写。 :return: ''' pass def train(self): ''' 训练模型,必须实现此方法 :return: ''' pass if __name__ == '__main__': main = Main() main.download_data() main.train() 前面导入几个库,我们不用管它,且看“项目的超参,不使用可以删除”这行注释下面这几行代码,其作用就是接收我们训练设置对话框里的BATCH和EPOCH的参数,如果没有用到,就不用管它,删掉也没事。在代码的下半部分,我们可以看到有一个叫Main的类,这里只有一个框架,我们得把这个类写完整。样例里,Main类有三个功能:download_data,deal_with_data,train。如有需要,我们也可以添加其它功能,其实我们只要完成这三个功能就够了,下面分别阐述。 1、download_data功能函数它的作用就是从服务器上面下载数据,它将在当前目录建立一个data文件夹,下载的训练数据就存在这个文件夹里,如果是本地调试下载,只能下载部分数据,线上训练时才能下载全部数据。图片数据存在data/input/ButterflyClassification/image目录下,data/input/ButterflyClassification/目录下有个train.csv文件,这个文件很重要,是图片数据的标签文件,里面是对应图片的分类信息,我们打开这个文件看一下:第一栏,image_path栏下是图片路径信息,需要注意的是路径不是完整的,我们在代码中读取后,还需要补全路径。第二栏,label栏是图片标签信息。这就是download_data实现的功能,我们不用管它,只需执行就可以了,官方已经把它实现好了。 2、deal_with_data功能函数该功能主要实现数据的读取和处理,得根据自身的需要来编写。先说读取,读取一般分两个步骤,先用pandas库读取train.csv文件内容,当然你可以用其它库。使用前别忘import pandas as pd, 代码如下: df = pd.read_csv(os.path.join(DATA_PATH, 'ButterflyClassification', 'train.csv')) image_path_list = df['image_path'].values label_list = df['label'].values image_path_list是路径信息列表,如[“image/5697.jpg”, “image/17805.jpg”,……],label_list是标签列表,如[“Danaus_chrysippus”, “Losaria_coon”,……],这两个列表长度一样,图片路径和标签是一一对应关系。如果要改动其中一个列表,那么另一个列表也要对应改动,才能保持一致。得到train.csv的信息后,我们进入第二个步骤,读取图片和建立标签,这里可以用cv2或者PIL读取图片,用对应的数字建立标签,这个比较简单。再说处理,图片数据处理略为复杂,不过主要有放大、缩小、剪裁、归一化、训练集和验证集划分等,一般来说,还要进行一些数据增强处理,比如亮度、对比度变化,随机剪裁、随机擦除、平移、翻转等,keras中有ImageDataGenerator,pytorch中有transforms,这里不过多述叙。总之数据的处理很重要,可以让模型训练得更加“鲁棒”,提高分数。数据处理完成后,就可以构建自己的模型,在图片分类中,我们一般都采用预训练模型,这样不用从头训练,也不需要庞大的数据训练,很快就能得一个不错的结果。我们可以在Main类之外,编写模型代码,也单独可以建立一个模型文件,如net.py,然后调用即可。 3、train功能函数顾名思义,该函数主要是完成模型的训练。在这里,我们可以定义模型的优化器、学习率、损失函数、训练次数等,最后将训练好的模型保存到MODEL_PATH路径下,MODEL_PATH的定义可以查看path.py文件。 下面以keras为例实现一个简易版的main.py: # -*- coding: utf-8 -*- import argparse import os import cv2 from flyai.data_helper import DataHelper from flyai.framework import FlyAI from path import MODEL_PATH from net import mymodel from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau from tensorflow.compat.v1 import ConfigProto from tensorflow.compat.v1 import InteractiveSession # tensorflow2.1 keras2.3.1需要加入下面三行,否则在服务器上运行会报错 config = ConfigProto() config.gpu_options.allow_growth = True session = InteractiveSession(config=config) ''' 此项目为FlyAI2.0新版本框架,数据读取,评估方式与之前不同 2.0框架不再限制数据如何读取 样例代码仅供参考学习,可以自己修改实现逻辑。 模版项目下载支持 PyTorch、Tensorflow、Keras、MXNET、scikit-learn等机器学习框架 第一次使用请看项目中的:FlyAI2.0竞赛框架使用说明.html 使用FlyAI提供的预训练模型可查看:https://www.flyai.com/models 学习资料可查看文档中心:https://doc.flyai.com/ 常见问题:https://doc.flyai.com/question.html 遇到问题不要着急,添加小姐姐微信,扫描项目里面的:FlyAI小助手二维码-小姐姐在线解答您的问题.png ''' if not os.path.exists(MODEL_PATH): os.makedirs(MODEL_PATH) # 项目的超参,不使用可以删除 parser = argparse.ArgumentParser() parser.add_argument("-e", "--EPOCHS", default=10, type=int, help="train epochs") parser.add_argument("-b", "--BATCH", default=32, type=int, help="batch size") args = parser.parse_args() class_dict = {'Danaus_chrysippus': 0, 'Losaria_coon': 1,......} # 这里略写了,需要补充 class Main(FlyAI): ''' 项目中必须继承FlyAI类,否则线上运行会报错。 ''' def download_data(self): # 根据数据ID下载训练数据 data_helper = DataHelper() data_helper.download_from_ids("ButterflyClassification") def deal_with_data(self): ''' 处理数据,没有可不写。 :return: ''' df = pd.read_csv(os.path.join(DATA_PATH, 'ButterflyClassification', 'train.csv')) image_path_list = df['image_path'].values label_list = df['label'].values x_data = [] y_data = [] for image_path, label in zip(image_path_list, label_list): image_path = os.path.join(DATA_PATH, 'ButterflyClassification', image_path) image = cv2.imread(image_path) image = cv2.resize(image, (224, 224)) x_data.append(image) y_data.append(class_dict[label]) self.x_data = np.array(x_data) / 255. self.y_data = np.array(y_data) def train(self): ''' 训练模型,必须实现此方法 :return: ''' model = mymodel() # 这个model需要自己编写 model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['acc']) model_path = os.path.join(MODEL_PATH, 'model.h5') mp = ModelCheckpoint(filepath=model_path, save_best_only=True, save_weights_only=False, mode='min', monitor='val_loss', verbose=1) reduce_lr = ReduceLROnPlateau(monitor='val_loss', mode='auto', factor=0.1, patience=3, verbose=1) el = EarlyStopping(monitor='val_loss', patience=5, verbose=1, mode='min') cllist = [mp, reduce_lr, el] batch_size = 16 his = finalmodel.fit(self.x_data, self.y_data, batch_size=batch_size, verbose=2, epochs=20, validation_split=0.1, callbacks=cllist, ) if __name__ == '__main__': main = Main() main.download_data() main.train() 可以看出,在main.py文件里,我们主要完成deal_with_data和train功能函数的编写,还有模型的建立,有数据有模型就要以进行训练了。 二、prediction.py在评论里看到,有的小伙伴不懂得prediction.py的编写,这个可是个收获果实的文件,训练半天,保存了模型,就需要这个文件来调用,在测试集上进行验证,得到模型分数。好吧,我们先来看文件结构: # -*- coding: utf-8 -* from flyai.framework import FlyAI class Prediction(FlyAI): def load_model(self): ''' 模型初始化,必须在此方法中加载模型 ''' pass def predict(self, image_path): ''' 模型预测返回结果 :param input: 评估传入样例 {"image_path": "./data/input/image/172691.jpg"} :return: 模型预测成功中,返回预测结果格式 {"label": "Losaria_coon"} ''' # 假设图片image_path对应的标签为Losaria_coon, 则返回格式为 {"label": "Losaria_coon"} return {"label": "Losaria_coon"} 该文件中只有一个类Prediction,该类有两个功能,一个是load_model,另一个是predict。 1、load_model该功能主要实现模型载入,就是将前面训练好,保存在MODEL_PATH路径下面的模型文件载入。以keras为例,如果前面保存在MODEL_PATH路径下面的模型名称为model.h5,那么在load_model功能中我们可以这样写: model_path = os.path.join(MODEL_PATH, 'model.h5') self.model = load_model(model_path) 当然,别忘了在文件头添加相应的import。 import os from path import MODEL_PATH from keras.models import load_model 这样就实现了模型的载入,至于keras和pytorch模型载入方法,可以查找一下相关资料,挺简单的! 2、predict很明显,这里就是实现模型的预测功能,它有一个参数image_path,这个是需要预测的图片路径,直接读取就行, 如: image = cv2.imread(image_path) 再用main.py文件中的方法调整图片的大小和归一化。 下面简要以keras为例,写一个简易版的prediction.py文件: # -*- coding: utf-8 -* import os import numpy as np import cv2 from flyai.framework import FlyAI from keras.models import load_model from path import MODEL_PATH, DATA_PATH from tensorflow.compat.v1 import ConfigProto from tensorflow.compat.v1 import InteractiveSession # tensorflow2.1 keras2.3.1需要加入下面三行,否则在服务器上运行会报错 config = ConfigProto() config.gpu_options.allow_growth = True session = InteractiveSession(config=config) class_num = ['Danaus_chrysippus', 'Losaria_coon',......] class Prediction(FlyAI): def load_model(self): ''' 模型初始化,必须在此方法中加载模型 ''' model_path = os.path.join(MODEL_PATH, 'model.h5') self.model = load_model(model_path) def predict(self, image_path): ''' 模型预测返回结果 :param input: 评估传入样例 {"image_path": "./data/input/image/172691.jpg"} :return: 模型预测成功中,返回预测结果格式 {"label": "Losaria_coon"} # 假设图片image_path对应的标签为Losaria_coon, 则返回格式为 {"label": "Losaria_coon"} ''' image = cv2.imread(image_path) image = cv2.resize(image, (224, 224)) image = np.array([image]) / 255. preds = self.model.predict(image)[0] pred = np.argmax(preds) return {"label": class_num[int(pred)]} 这样一个简易版的就完成了,小伙伴你学废了么? 总之,就是要多写多练,不要怕出错,有什么不明白,你就去撩小助手,小助手可是个单身的、漂亮的妹纸哦! pass:由于时间仓促,如有错漏,概不负责!!!

6

图像分类

天涯·明月·刀·2021-05-10 10:42 7 阅读 1.7k
文章Flyai人工智能社区参赛指南—非官方版
Flyai人工智能社区参赛指南—非官方版        牛年新春马上到来,在这里,先祝小伙伴们新春大吉,工作顺利,合家幸福!再祝小助手,天天美美哒!         不知不觉,在社区混迹有两年余,通过打怪升级,从一个AI新手成功的晋升为AI菜鸟!别人是天才,我是“抄添裁”,代码靠抄,适度增添,合理剪裁!ctrl+C(V)法力无边,千秋万代,一统江湖!为了给新手们提供点方便,让他们尽快上手,于是就想写个参赛指南,尽一个菜鸟应尽的责任和义务(主要还是因为新手看完官方的指南,往往找不着北)。下面,是依据flyai2.0 windows版本,结合自己的经验编写,不足之处,大家多多指正。          一、主页面         在浏览器中,输入www.flyai.com,进入官方网站主页面:         主页面上方有赛事、学习圈、实验室,以及自己的用户名。         当前主页面就是赛事页面,可以看到当前的比赛,你可以参加自己感兴趣的比赛。学习圈发布的主要是小助手不知道从哪里抄来的文章、其他小伙伴的水文和答辩经历等。实验室,是个什么东东,估计和google colab类似的东西吧,我也想进去看看。         参加比赛活动,需要个人注册,很简单,微信一扫基本OK了,然后官方会送1000的FlyAI分值,这些分值比赛会用到,基本是线上训练一分钟,就消耗1个FlyAI分值。曾经就有1000分在我面前,我没珍惜,直到用光,我才后悔莫急……现在主要靠每日签到赚个二三十分,痛并快乐着!         二、项目页面         注册好后,现在我们选取一个比赛来参加吧,比如上图第一个,心率不齐病症检测赛。         页面上,我们可以看到赛事介绍,竞赛排行榜,讨论等内容。我们通过阅读赛事介绍,可以了解比赛的相关内容,竞赛排行榜可以看到小伙伴们的比赛成绩,你还可以在讨论页面发表高论,赚个20FlyAI分。         好吧,我们现在要做的就是报名参赛,点击右上方那个金光灿灿的“报名参赛“按钮,然后,报名参赛就变成了查看样例,再点击”查看样例“,进入代码页面。         左侧边栏,是代码的文件,这里有五个,分别是app.json,main.py,path.py,prediction.py和requirements.txt。        app.json:这个是该项目的配置文件,不用管它。         main.py:这是代码的主要文件,将代码上传到服务器后,服务器就会自动执行该文件的代码,这个也可以说是代码的入口文件,我们将在这个文件中实现程序的主要功能,比如说建立模型、处理数据、训练模型、保存模型等。         path.py:这里面是路径的配置,默认有DATA_PATH和MODEL_PATH,你可以添加自己的路径配置。         prediction.py:main.py执行完毕后,服务器将调用该文件的Prediction类,依次调用这个类的load_model和predict功能,完成模型的验证,根据模型提交的结果,给出得分。         requirements.txt:这里面是该项目用到的包,比如你代码里面用的pytorch版本是1.4,那么,你可以在这个文件里面加入torch==1.4.0,官方服务器一般会把常用的包安装好了,比如说numpy、pandas等,一些不常用的,而你的代码里需要用到的,就可以在这个文件里面添加。         理论上,我们只要按照代码框架,完成main.py和prediction.py这两个文件代码的编写就行了。         右侧栏,就是相应文件的内容,我们可以在电脑上进行更改,完成后,点击下方的提交训练,就可以上传代码到服务器端进行训练。         右侧栏上方最右边,有三个按钮,分别是下载代码、上传代码、查看代码。下载代码,即可将代码下载到本地编辑,完成后,再打包,点击上传代码,即可将线下代码上传到线上;点击查看代码,可以查看以前提交过的代码,方便修改。         三、下载代码         线上编写,有很多不方便,我们可以下载下来线下编写调试。点击下载代码,解压后,可以得到如下文件:         可以看到,会比线上多出几个文件,多出的文件不用管它,我们只看这个flyai.exe,双击运行它,如下图:         在这里,可以点击“下载数据“按钮,下载部分数据到线下,这时会在当前文件下建立一个data文件夹,用来保存下载数据。点击”提交GPU训练“按钮,即可以将当前目录下的相关文件上传到服务器端进行训练。         我们先来看看下载下来的样例数据,样例数据只是全部数据的一部分,主要是用来线下调试代码的。比如,在路径“data/input/ArrhythmiaClassification/“下,有个train.csv文件,打开看看:         其中,data就是模型训练需要用到的数据,label是标签。在图像类的比赛,第一栏一般是图片的路径,不同的比赛,略有不同。         现在,就可以按照main.py和prediction.py里面样例框架,编写自己的代码,值得注意的是,训练好的模型文件要保存在“data/output/model”路径下面。根据需要,也可以另外新建文件夹或者文件。完成代码编写后,可先用线下数据,本地运行一下,看有没有什么BUG,确定无误后,就可以上传代码,到线上训练了。         四、上传代码         上传代码有两种方式,一种是直接点击上面flyai.exe图中的“提交GPU训练”,程序就会自动将当前文件夹下面的文件上传到线上,这里需要注意的是,有些我们自己建立的文件夹可能不会上传,所以,如果有新建的文件夹,建议不要采取这种方式上传代码。         另外一种上传方式是手动打包上传,这里要将app.json、main.py、prediction.py、path.py、requirements.txt这五个文件,加上我们新建的文件和文件夹,一起打包成压缩包,然后点击下图中“下载代码”旁边的上传按钮,就可以把我们的文件上传到线上了,再点击“提交训练”按钮,就可以训练了。        五、线上运行         这时候,我们可以查看程序运行状态,如下图:         由于线上服务器数量有限,可能需要排队,只能耐心等待,如果不想等,可以点击“取消排队”,但是不能重复提交代码训练,一次只能训练一个任务,如果提交两个以上任务,就会出现上图中的“已加入队列!之前任务完成后,将自动开始训练”,不过有时候,网站后台代码有问题,也会出现这种情况,这时候,就需要我们美丽的Flyai小助手出场了,将你遇见的问题,跟她反馈,一般都能很好解决。         训练日志内容会显示requirement.txt里面安装包的安装状况,之后就会显示执行main.py输出显示的内容,比如print。如果程序有错误,会显示错误提示,停止训练。         每训练一次,都会消耗我们的FLYAI分值,如果分值为零,就不能再进行训练了。获取积分的方式有每日签到、每日讨论、写写文章,邀请朋友和充钱等等,也可以坚持签到,像我一样发发文章,Fai值自然而来!         好了,基本上,常用的东西就是这样了,不过,各位一定确定以及肯定还会碰到各种问题,可以问小助手,边打怪边升级,钢铁就是这样练废的!

13

数据分析

天涯·明月·刀·2021-02-22 17:04 7 阅读 2.5k
文章tf.transpose() 函数与 Tensor 转置操作(参数perm)的解释与思考
今天向各位分享TensorFlow中张量Tensor的转置函数tf.transpose()的用法,重点介绍其参数perm及原理。 Tensor 的阶在开始介绍转置函数之前,我们先来看一下Tensor的阶 当张量Tensor为一个标量时,即不带方向的纯量,其阶为0; x0 = tf.constant(1) print(x0) # 输出 tf.Tensor(1, shape=(), dtype=int32) 当Tensor为一个向量时,如[1, 2, 3]时,其阶为1; x1 = tf.constant([1, 2, 3]) print(x1) # 输出 tf.Tensor([1 2 3], shape=(3,), dtype=int32) 当Tensor为矩阵时,其阶为2,如 x2 = tf.constant([[1, 2], [3, 4]]) print(x2) # 输出 tf.Tensor([[1 2] # [3 4]], shape=(2, 2), dtype=int32) 而3阶Tensor可以被认为是一个立方体的数字集合,由多个小立方体组成,每个小立方体上存放了一个数字,如下图所示: x3 = tf.constant([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) print(x3) # 输出 tf.Tensor([[[ 1 2 3] # [ 4 5 6]] # # [[ 7 8 9] # [10 11 12]]], shape=(2, 2, 3), dtype=int32) Tensor 的转置接下来我们对Tensor的转置进行讨论 0阶,1阶Tensor的转置,可以说没有意义;2阶Tensor转置就相当于矩阵转置,比如转置为属于大学线性代数部分,也无需过多介绍; 我们重点来讨论3阶Tensor的转置,这时就需要用到tf.transpose()函数了 tf.transpose()函数的官方文档中,介绍了该函数存在一个参数perm,通过指定perm的值,来完成的Tensor的转置。 perm表示张量阶的指定变化。假设Tensor是2阶的,且其shape=(x, y),此状态下默认perm = [0, 1]。当对2阶Tensor进行转置时,如果指定tf.transpose(perm=[1, 0]),就直接完成了矩阵的转置,此时Tensor的shape=(y, x). x2_ = tf.transpose(x2) print(x2_) # 输出 tf.Tensor([[1 3] # [2 4]], shape=(2, 2), dtype=int32) 而处理对象为3阶Tensor时,在下方例子中,官方文档中给出了这么一句话: # 'perm' is more useful for n-dimensional tensors, for n > 2 于是问题来了,为什么要设置perm=[0, 2, 1]?当参数perm=[0, 2, 1]设置完成后,为什么会得到这样的转置结果呢? tf.transpose()函数及perm参数详解这就要和原Tensor本身的shape有关了。 首先看Tensor x3是如何组成的。该Tensor中,最外层1个中括号包含了2个中括号,这两个中括号又分别包含了2个中括号,这两个中括号又包含了3个int型数值,所以其shape值为(2, 2, 3)。当我们将这个3维Tensor画成立体图时,如下图所示。 x3 = tf.constant([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) print(x3) # 输出 tf.Tensor([[[ 1 2 3] # [ 4 5 6]] # # [[ 7 8 9] # [10 11 12]]], shape=(2, 2, 3), dtype=int32) 关键来了,这里我们可以将perm理解为切割该立方体的切割顺序。我们已知Tensor x3的shape是(2, 2, 3),它对应着原perm的切割顺序。这个顺序就是,先竖着与侧边平行切一次,再横着切一次,再竖着平行于横边切一次,如下图所示,就得到了Tensor原本的形状。我们将这种切割顺序依次定义为0,1,2,于是perm=[0, 1, 2],如下图所示:在搞懂这个对应关系后。再来看如果不通过代码结果,我们如何确定转置后的Tensor形状。 当我们对这个3维Tensor x3进行转置,并设定perm值为[0, 2, 1]时,则此时对应的shape形状就会转化为(2, 3, 2)。为什么呢? perm=[0, 2, 1]就意味着,对立方体要按照如下顺序进行切割:先竖着与侧边平行切一次,再竖着平行于横边切一次,再横着切一次,如下图所示,就得到了转置后Tensor的形状这时,我们使用函数语句 tf.transpose(x3, perm = [0, 2, 1]) 进行验证,转置结果与推演结果一致。也就是说,shape=(2, 2, 3) 的Tensor经过perm=[0, 2, 1]转置后,变为shape=(2, 3, 2)的Tensor。 x3_ = tf.transpose(x3, perm = [0, 2, 1]) print(x3_) # 输出 tf.Tensor([[[1 4] # [2 5] # [3 6]] # # [[7 10] # [8 11] # [9 12]]], shape=(2, 3, 2), dtype=int32) 这也是为什么在TensorFlow2.0官网教程中,官方推荐在Tensor维度大于2时,使用perm参数进行转置操作,会更方便的达到效果。当然前提是你要明确原Tensor shape及你想要的变形后的Tensor shape,根据后续需求确定参数perm的值。 希望这篇文章对大家理解张量Tensor有帮助!

0

TensorFlow

Botanic·2021-09-26 09:35 0 阅读 261
文章多头自注意力在文本分类上的应用及几个细节
想来至今还没有从0完整实现过Attention,即使公式掌握的足够熟悉,但很多细节的问题,还是要亲自动手实践一次后,才能了解得更清楚。本次我们通过手动实现一个多头注意力的方式,对文本分类做一次实践,加深对Attention机制的理解。在实践过程中,穿插着会提到一些Attention的细节,争取做到代码与论文紧密结合。 实践部分,我们还是选择使用TensorFlow2.1进行建模,由于要尝试构建Attention的模型,所以在建模部分我们主要采用继承keras.Layer的方式分别构建各个模块,并且使用Sequential API的方式将各层连接起来。具体建模过程,请见我的github仓库。本次实践主要基于Shicoder 的代码进行修改。受限于作者能力,如有问题,欢迎在下方留言讨论。 数据准备:本次文本分类任务,我们选取的TensorFlow.keras.datasets自带的IMDB数据集。由于数据集已经是经过处理后的数据,我们只需设定每条句子的最大文本长度后进行训练集和测试集的划分。这里针对数据准备过程中设置的超参数如下: vocab_size = 20000 # 文本中单词词频在前max_features个的词 maxlen = 80 # 最大文本长度 batch_size = 32 位置编码我们认为,对于句子中每个词,Attention机制更容易使得这个词关注到与其相关的其他词,在表现上是要强于传统的CNN和RNN的。但是CNN和RNN有一个优势,就是他们都会关注到句子中词与词之间的顺序问题。RNN自然不必说,如果CNN中的卷积核的长度大于1,那么在一次采样的时候,编码结果能够包含一定词顺序的信息。而Attention本身更多关注的是一个并行的结果,并没有考虑到词与词之间的顺序。对于这种情况,有必要在embeddings的基础上加入位置编码,positional emcodings。每个位置对应于一个编码,位置编码完成后与embeddings的结果直接相加。 假设我们定义embedding的向量维度为128,那么positional emcodings的维度也是128。(在论文中这两个向量维度定义为$d_{model}$,其大小为512)关于位置编码的方式,论文使用三角函数对不同频率的位置进行编码,具体编码公式为: $PE{(pos, 2i)} = sin(pos/10000^{2i/d{model}})$ $PE{(pos, 2i+1)} = cos(pos/10000^{2i/d{model}})$ 对于每个位置,也就是每一个词,使用sin函数或cos函数进行编码,得到的是一个依赖于具体位置的绝对位置的编码。论文中还提到,选择这种编码方式的原因,在于其能够学习到一定程度的相对位置信息。根据三角函数的和差化积公式,对于固定的偏移量$k$,$PE{pos+k}$都可以被表示为$PE{pos}$的线性组合。具体公式可以表示为: $PE(pos+k, 2i) = PE(pos, 2i)PE(k, 2i+1) + PE(pos, 2i+1)PE(k, 2i)$ $PE(pos+k, 2i+1) = PE(pos, 2i+1)PE(k, 2i+1) - PE(pos, 2i)PE(k, 2i)$ 从代码层面看: from tensorflow.keras import layers # 首先构建输入层和embedding层 # 输入层中的shape=2D,第0位是batch_size,第1位是max_len, # 在这种条件下embedding的维度就是 (batch_size, max_len, embedding_dim),其中embedding_dim=128 S_inputs = layers.Input(shape=(None,), dtype='int32') embeddings = layers.Embedding(vocab_size, 128)(S_inputs) 接下来我们开始构建positional emcodings 首先计算三角函数中的分母,根据位置编码公式 # 使用np.arange(),设置长度为size大小的一半,其中size = d_model # 默认step=1,np.arange() 会创建一个包含整数序列的一维张量 position_j = 1. / tf.math.pow(10000., 2 * np.arange(self.size / 2, dtype='float32') / self.size) 然后计算分子,分子中主要获取的是位置 # 其中x是输入位置编码模型的embedding结果,这里选取每个向量的第一个位置,同时在batch_size和max_len的维度上保持不变 # 这个位置下初始化全1的矩阵,然后使用cumsum,在新位置存放之前所有位置的累加和,相当于对每个维度进行位置编号 position_i = tf.math.cumsum(tf.ones_like(x[:, :, 0]), axis=1) - 1 由于分子和分母可能存在维度不匹配的情况,如果有必要需要对两个编码结果在不同位置进行扩展维度。然后将分子与分母进行矩阵相乘,得到三角函数的输入部分。再将这个输入部分分别通过sin和cos函数,得到的结果在同一维度进行拼接,表示如下: position_ij = tf.matmul(position_i, position_j) position_ij = tf.concat([tf.math.cos(position_ij), tf.math.sin(position_ij)], 2) 拼接之后的位置编码与原x,也就是embedding的形状是一摸一样的,之后根据论文中的说法,我们需要将位置编码与embedding进行相加,得到的结果就可以往Attention层送了。 Attention机制首先要明确一下各个变量之间的联系和向量的长度。我们知道,Attention中Q,K,V分别是输入数据,也就是上文的embeddings + positional encodings,乘以各自的参数矩阵得到的结果,并且这些参数矩阵是可以学习的。于是应该先定义Q,K,V对应的参数矩阵。 同时我们需要明确的是,论文中使用了多头注意力multi-head self-Attention,对于每个头,完成Attention的计算后,会将得到的8个头拼接在一起。原始文本的向量长度定义为512,那么每个头下产生的Q,K,V矩阵的输出维度就是 512 / 8 = 64,那么三个权重矩阵W_Q,W_K,W_V的维度就是[512, 64],这样输入数据与参数矩阵的乘积,就会得到向量为 64 的Q,K,V。并且,多头对应的参数矩阵是不一样的,也就是说每个头都有独一无二的一组权重矩阵W_Q,W_K,W_V,正好对应了多头的使用意义,扩展不同的表达子空间(这里可以将不同的表达子空间理解为CNN中卷积核,不同权重的多个卷积核对数据进行多次采样,这样就能够获取更加丰富的表达结果)。 在本次实践中,输入数据的向量长度为128,那么每个头下,三个权重矩阵的输出维度是16。除了与论文中的维度差异外,多头下的权重矩阵维度也有改变。为了避免同时维护不同头下三个权重矩阵,这里选择将权重矩阵的输出维度就设置为输入数据的向量长度128,也就是将不同头下,同一类型的权重矩阵合并为一个,而不是分成8。这样做的好处是不需要同时定义8组权重矩阵,显得代码过于冗长,最后Attention的计算结果也不必进行拼接了,因为就是在8个头的条件下计算的。 def build(self, input_shape): """ input_shape 是输入数据的维度,这里我们定义输入的数据是上一层编码的结果 input_shape[-1]表示输入数据最后一个维度的大小,也就是embedding_dim,此处设置为128 output_dim 为输出维度,这里是 nb_head * size_per_head 其中,nb_head = 8 表示8个头,size_per_head = 16 表示每个头下的向量维度 三个参数矩阵的维度都是 shape=[embedding_dim, output_dim],也就是 [128, 128] """ self.WQ = self.add_weight(name='WQ', shape=(input_shape[-1], self.output_dim), initializer=tf.random_uniform_initializer(), trainable=True, regularizer='l2') self.WK = self.add_weight(name='WK', shape=(input_shape[-1], self.output_dim), initializer=tf.random_uniform_initializer(), trainable=True, regularizer='l2') self.WV = self.add_weight(name='WV', shape=(input_shape[-1], self.output_dim), initializer=tf.random_uniform_initializer(), trainable=True, regularizer='l2') super(Attention, self).build(input_shape) 参数矩阵完成初始化后,下一步就是通过输入数据乘以各自的参数矩阵得到查询向量Q,键向量K,和值向量V。具体如下(拿查询向量Q的获取为例); Q_seq = tf.matmul(x, self.WQ) Q_seq = tf.reshape(Q_seq, (-1, tf.shape(Q_seq)[1], self.nb_head, self.size_per_head)) Q_seq = tf.transpose(Q_seq, perm=[0, 2, 1, 3]) # 为了后续计算方便,这里重新排列张量的轴 # 重排后的 Q_seq.shape = (batch_size, self.nb_head, seq_len, self.size_per_head) 三个向量全部获取完成后,就是根据公式计算Attention了。 $Z = softmax(\frac {QK^T} {\sqrt d_k})V$ 从这个公式中不管怎么看,都感觉Q和K应该是矩阵相乘,但是论文中又明确提出了Scaled Dot-Product Attention这样的概念,于是这里还是选择矩阵内积的方式。在实践RGCN时发现,可能在某种条件下,矩阵乘法和内积得到的效果是一样的。 得到的结果需要经过$\frac {1} {\sqrt{d_k}}$的缩放。采取缩放的原因是作者怀疑过大的$d_k$值容易产生很大的乘积结果,使得softmax的梯度变得很小。为抵消这种情况,需要采取这种缩放方式。 A = tf.multiply(Q_seq, K_seq) / self.size_per_head ** 0.5 A = tf.transpose(A, perm=[0, 3, 2, 1]) A = self.Mask(A, V_len, 'add') A = tf.transpose(A, perm=[0, 3, 2, 1]) A = tf.nn.softmax(A) O_seq = tf.multiply(A, V_seq) # softmax的值乘以值向量 O_seq = tf.transpose(O_seq, (0, 2, 1, 3)) # O_seq.shape = (batch_size, self.nb_head, seq_len, self.size_per_head) O_seq = tf.reshape(O_seq, (-1, tf.shape(O_seq)[1], self.output_dim)) mask操作可以看到,在进行softmax之前,可能还需要加入一步mask操作。mask操作的意义有两个。 padding mask在很多NLP任务上,如果文本的原始长度小于我们指定的最大文本长度时,常用的做法是用0将缺失的位置补齐。但从文本语义关系的角度分析,这些位置其实没有意义,我们也不想要这些位置的信息参与到学习过程中。在self-attention中,也不会希望有效词的注意力集中在无意义的位置上。 因此我们希望在训练时将补全的位置给mask掉。通过 padding mask 操作,使得补全位置上的值成为一个非常大的负数,经过softmax层后,这些位置上的概率就是0。此操作就相当于把补全位置的无用信息给遮蔽掉了。 这一步要放在softmax之前,当然没有也是可以的。论文中描述Scaled Dot-Product Attention的图中mask,后面标注了optional。具体的做法可以参考: def Mask(self, inputs, seq_len, mode='mul'): """ mask.shape = [batch_size, seq_len] 或 [batch_size, seq_len, 1] :param inputs: :param seq_len: 是一个二维矩阵 :param mode: 可以指定两种不同的mask方式 :return: """ if seq_len == None: return inputs else: # tf.shape(inputs)[1] 是每个 head 的维度 # 生成的 one_hot 矩阵的维度是 (len(seq_len[:,0]), tf.shape(inputs)[1]) # 这里的 len(seq_len[:,0]) 实际上就是文本长度 seq_len mask = tf.one_hot(seq_len[:, 0], tf.shape(inputs)[1]) # 首先将每行为1的位置之后的所有位置全部置1,然后对角变换,1变0,0变1 # 当前 mask 是一个下三角矩阵,一个角全1,另一个角全0 mask = 1 - tf.math.cumsum(mask, 1) for _ in range(len(inputs.shape) - 2): mask = tf.expand_dims(mask, 2) if mode == 'mul': # 按位相乘,乘以0的元素就变为0 return inputs * mask if mode == 'add': # 乘以0就变为0,乘以1的被变为无穷大,然后元素减去这个无穷大就变成了一个负数 return inputs - (1 - mask) * 1e12 sequence mask这里的mask是比较重要的,需要在Transformer的Decoder中被使用到。Attention本质上会关注到全局的信息,但在执行预测时,不能让其看到当前词后面的词信息,所有有必要将这些词mask掉。这部分属于Transformer的Decoder部分,这里就不细说了。 训练Attention模型搭建完毕后,后续还要构建一个平均池化层和全连接层,然后就可以对模型进行训练了。模型结构如下: Model: “model” Layer (type) Output Shape Param # Connected to input_1 (InputLayer) [(None, None)] 0 embedding (Embedding) (None, None, 128) 2560000 input_1[0][0] position_embedding (PositionEmbedding) (None, None, 128) 0 embedding[0][0] attention (Attention) (None, None, 128) 49152 position_embedding[0][0] position_embedding[0][0] position_embedding[0][0] global_average_pooling1d (None, 128) 0 attention[0][0] dropout (Dropout) (None, 128) 0 global_average_pooling1d[0][0] dense (Dense) (None, 1) 129 dropout[0][0] Total params: 2,609,281Trainable params: 2,609,281Non-trainable params: 0

0

注意力机制

文本分类

Botanic·2021-09-26 09:35 0 阅读 334
经验什么蘑菇比赛答辩--第二名
1.问题分析和总结   本次竞赛使用的数据集由北欧真菌学家协会(Mycologist‘sSociety of Northern Europe)提供的9 种常见北欧蘑菇属的图像组成。 总结:   1.1.图像9分类。   1.2.和imagenet相似度较低   1.3.评测指标为准确率   ## 数据集查看  可以查看本地目录中的数据集图片, 筛选出明显不符合对应类别的图片, 比如说有几张全黑的图,我们可以直接剔除此种明显是噪声的图片,当然这只是初步的观察。还有一些格式错误的图片,可以使用pil的内置功能忽略掉:  from PIL import Image,ImageFile   ImageFile.LOAD_TRUNCATED_IMAGES= True  2.技巧和经验  ## 模型选用尝试  在不同的网络下。 初步测试分数如下, 图像大小为224x224, 最高至384x384:Seresnet-50, 最高调到87分 ;  Resnext101d_32x16 :  94; Efficient-b2 : 93 CoatNet:   95 ; VIT-16/224 : 96 ;Swin-transform : 95  。 ## 冻结网络前k层 1.预训练模型权重(imagenet)的数据集和比赛数据集相似度高, 直接调整输出层 2.预训练模型权重(imagenet)的数据集和比赛数据集相似度低, 冻结模型前k层。 ● 原因: 模型输入层附近的层, 提取的是更通用化的特征。## 损失函数:  1.torch.nn.CrossEntropyLoss()  2.+label_smoothing (标签平滑)  3.换成Focal_loss ## 学习率调度   1.stepLR : 固定步数衰减   2.reduceOnPlateau : loss停滞期后衰减  3.余弦退火。## 验证时进行错误分析  1.自己写个统计函数, 统计验证集上每个类别的分类情况  2.Sklearn.metrics.classification_report() 统计各个类别上的-f1-score。 ## 测试时调优TTA:测试时进行图像增强。模型预测的时候,使用数据增强的多张照片,取结果的平均值(或者其他指标)。这里的数据增强,我通常直接使用训练时候的数据增强,其他的也可以试试。### 图像增强使用工具:torchvision.transforms, 对训练集图片进行水平,垂直翻转, 旋转,仿射变换,像素随机擦除等多种·组合变换,达到了扩充数据集的目的,完成了对于模型健壮性进行增强的指标。 当然,选用策略的时候宜少不宜多, 组合几种效果最好的就行,不然就难以优化了。 3.学习感悟和心得  在考虑显卡性能的情况下, 尽量尝试新的模型,会有意想不到的结果; 多多关注新发布的论文, 对于别人分享的调优tricks进行尝试加应用。  关注下领域的最新动态, 比如今年transformer在图像领域的应用。

1

图像分类

chenfengshf·2021-08-31 13:43 2 阅读 755
经验蘑菇分类比赛TOP1----答辩
一、赛题分析 本次竞赛使用的数据集由北欧真菌学家协会(Mycologist‘s Society of Northern Europe)提供的 9 种常见北欧蘑菇属的图像组成,从给的样例图片来看,图片比较清淅,尺寸很大,可以留给网络发挥的空间。       主要难点:       1、数据不平衡性。       2、图片大小不一。       3、蘑菇的形态各样。  二、网络介绍 对于该题,尝试过常规的几种网络,比如resnext、seresnext、resnest、efficientnet等系列,在当下网络中,最热的不过是注意力机制,每种网络的注意力机制又有各自的区别,经过测试,seresnext和resnest这两种网络效果较好。        恰巧,又看到一篇介绍Transformer的文章,说是Transformer在自然语言处理建立霸主地位后,又来计算机视觉领域屠榜,在图片分类、分割、检测等有着惊艳表现,各项指标都领先传统的卷积神经网络。后来,看到chenfengshf小伙伴上传了基于微软的Swin Transformer的预训练模型,我也顺便做了测试,效果确实不错,成绩几乎提升了1个点。       下面,结合自己学习的体会,向大家简要介绍一下Swin Transformer网络。  为什么要做这个研究?       将Transformer的高性能迁移到视觉领域,解决CNN中对于全局信息特征提取的不足。将注意力计算限制在窗口中,引入CNN卷积操作的局部性,节省计算量。       实验方法是什么样的?        Swin Transformer提出hierarchical Transformer,来构建不同尺度的特征金字塔,每一层使用移位窗口将self-attention计算限制在不重叠的局部窗口内,同时通过跨窗口连接增加不同窗口之间的信息交互。  三、训练方式 1.数据增强方式: 随机旋转 随机剪裁 随机水平垂直翻转 随机平移 尺度变化  2. 学习率:余弦下降3. Optimizer:Adam4. 标签平滑5. 三模型集成+交叉验证6. 先冻结训练10轮,再全部训练7. 预测时,采用TenCrop

6

图像分类

江水·2021-08-27 17:43 2 阅读 649
经验蘑菇分类 -黑羽
赛题背景:          本次赛题背景是日常生活中蘑菇类别较多,而其中包含了很多有毒蘑菇,人很难辨别,容易引起误食,该比赛想通过深度学习的方法帮助人们识别出毒蘑菇。本次竞赛使用的数据集由北欧真菌学家协会提供的9 种常见北欧蘑菇属的图像组成。  赛题分析:      这个题目就是一个图像分类问题,给定了6045张图片作为训练集,675张图片作为测试集,对于线下的10%数据。 实验过程:       1、5折交叉划分训练集和验证集,遍历每一个类别,训练与验证数据比例大致相同        2、试验了各种分类模型       3、预测的时候使用水平翻转求平均的简单策略        4、测试各种超参数 batchsize,学习率,增强策略,优化器,损失函数等  提分的一些操作:       1、标签平滑       2、模型选择 Swim-Transformer       3、数据集划分 9:1验证集有过拟合问题,8:2稍微好一些       4、模型融合        5、随机裁剪、随机擦除       6、tta最终模型和参数:     (1)数据集划分:     训练集5折交叉 训练集:验证集=8:2      (2)模型选择:     Swim-Transformer    预训练模型: swin_large_patch4_window12_384_22k.pth      (3)inputsize: 383*384      (4)batchsize: 4      (5)max_epochs: 30      (6)学习率:    阶梯下降学习率       def lr_scheduler(optimizer, epoch):             if epoch < 10:                     lr = 1e-3              elif epoch < 20:                   lr = 1e-4             else: lr = 1e-5            for param_group in optimizer.param_groups:               param_group['lr'] = lr           return optimizer      ( 7)优化器:              self.optimizer = optim.SGD((self.model_ft.parameters()), lr=1e-3, momentum=momentum, weight_decay=0.0005)         (8)损失函数:                  LabelSmoothingCrossEntropy(smoothing=0.1).cuda()        (9)增强策略:                   训练集: 随机裁剪、水平翻转、随机擦除              验证集:简单缩放                   data_transforms = {                              'train': transforms.Compose([                                       transforms.RandomResizedCrop((input_size, input_size)),                           transforms.RandomHorizontalFlip(),                                      transforms.ToTensor(),                                      transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False),                             transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  ]),                             'val': transforms.Compose([                                     transforms.Resize((input_size,input_size)),                                     transforms.ToTensor(),                                    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])         ]),  (10)测试增强:                 正常图片+ 水平翻转 -> 求平均   (11)融合策略:               保存每折训练过程中最大Acc和最小Loss的模型,数据集5折交叉,共5*2=10个模型进行融合   教训总结:     1、过早的模型融合,忽略了单个模型的性能比较     2、验证集过拟合问题     3、代码没有线下测试,导致线上出问题,浪费了很多实验时间总结和展望:     1、对图片没有进行预处理等操作,数据分析还不足      2、多看看最新的研究,最后阶段才尝试使用最新的分类模型,对比之前的模型提分很明显      3、训练速度较慢,前排大神两次训练间隔才10几分钟就可以得到比较好的分数,我每次训练都要好几个小时,很想学习下,以后可以改进下训练策略      4、参加几次比赛,我也学到了很多东西,希望以后有更多的人来参与这个平台进行比赛,通过比赛和分享让大家共同进步。谢谢大家!

1

CNN

PyTorch

图像分类

黑羽·2021-08-27 17:42 1 阅读 526
经验半弱监督CNN模型在蘑菇识别比赛中的应用
比赛结束,第四名,97.6的准确率。虽然是个简单的图像分类比赛,但是想拿高分也是不容易的。下面分三个部分讲述下解决这个蘑菇分类问题的心路历程。1 问题分析与探索型数据分析        一是分析题目和测试数据集可知,这个问题是个多类别标签图像分类问题,第一直觉就是使用卷积神经网络CNN来解决这个问题;二是从测试数据集分析可知,标签总共有9类,这9类分别是什么,不知道哈;三是分析测试数据集,这些采集的照片都是通过拍照得到的,切照片的中心一般就是我们的蘑菇了;四是分析图像可知,蘑菇从外观、大小上看区别不打,主要从形状和颜色上进行区分,因此在数据扩增的时候要注意颜色抖动的设置;五是纠结于使用CNN还是现在比较火的vision transformer,考虑到平台要用认证过的模型,因此决定使用CNN;六是数据扩增的选择问题,考虑到类别不均衡问题,我这里用Yolov5中的Mosaic增强方法对同类别的图像进行了增强;七是训练技巧的选择问题,尽量考虑一些提点特别明显的方法,后面会介绍。2 基线模型与调优2.1 基线模型的选择           常用的CNN基线模型有Resnet家族、efficientnet家族等,做了简单的测试,使用resnet50和efficientnetb3的结果不是很理想,因此寻求泛化能力比较强的模型。我选择的模型来自论文Billion-scale semi-supervised learning for image classification,该论文中提出使用海量的无标签图像,通过弱监督的方式,来提升resnet50和resnext101模型在ImageNet数据集上的准确率。通过测试确实效果很炸,因此我们采用半弱监督学习得到的预训练resnet50和resnext101模型。            smsl-resnet50和smsl-resnext101 32x8在ImageNet的Top-1准确率分别为81.2和84.3,效果很不错。在没有太多数据增强和训练技巧加持下,在我们比赛中的成绩,很容易就超过了95%。实验证明确实是非常好的基线模型。    模型确立好以后,那么就需要对模型的输出进行处理,这是常规操作了,将输出的特征维度不变,将输出维度改为9,使用torch很容易实现。    path = remote_helper.get_remote_data('https://www.flyai.com/m/semi_weakly_supervised_resnext101_32x8-b4712904.pth')     net = resnext101_32x8d(pretrained=False)     net.load_state_dict(torch.load(path), strict=True)     feature = net.fc.in_features    net.fc = nn.Linear(in_features=feature, out_features=9, bias=True)2.2 调优           一是数据扩增。将图像尺寸从224调制416, 最后确立在380的时候,可以兼顾训练时间和准确率。批尺寸大小设为8, 因此为了得到较好的收敛速度,使用梯度累积的技术,没8个step做一次梯度更新,这样就相当于批尺寸为64了,其实还可以试下更大的梯度累积,也许效果更好。另外三个比较好的方法,Mosaic,CutMix和TTA对涨点效果较好。对于Mosaic是使用同类别的图像进行马赛克拼接得到一个新图像,来提升该类别图像的数量和多样性,对于CutMix就是拼接两张图片,并按照大小来求损失,这里的CutMix和Mosaic有点不同,就是CutMix是可以用于不同类别图像融合,但是这个比赛中使用的Mosaic只用于同类别的图像融合和扩增。对于TTA,这是测试时增强,在推理的时候,对图像做随机变化,然后融合结果,提升结果的准确性。            二是模型的优化,这里仅仅简单的替换了输出线性层,没有做复杂的特征处理或者Arcface等操作。学习率为0.01,权重衰减为0.001。                三是训练技巧,主要包括标签标签平滑、交叉验证、偏置无L2衰减约束、指数移动平均。其中标签平滑和CutMix类似,主要是针对损失函数处理,用这个技巧将分数从95%提升到96%;交叉验证是常规操作,将分数从96%提升到97%;偏置无L2衰减是指在训练的过程对模型中的偏置不加衰减,可以提升模型的拟合能力;指数移动平均,在最后分数超过95%的时候,开始对每个step的模型做指数移动平均,提升模型的泛化能力。3 总结       一是这个比赛我比第一名少了将近0.1个点,差距还是挺大的。说明图像分类问题虽然实现baseline简单,但是要取得好的成绩还是很困难的,再接再厉吧!二是在比赛中的图像尺寸带来的增益确实不错,如果能够在图像加载时对尺寸做一些优化,比如多尺度图像学习,也许还有不错的收益;三是比赛中只使用了单个模型,效果可能不如多个模型带来增益多;四是弱监督学习可以加入进来;五是在比赛中EMA让人有爱有恨,效果很难感觉出来;在图像识别中,记住label smoothing, 图像扩增、大尺寸图像、强基线模型、CutMix都是可以稳定提点的方法。

1

PyTorch

CNN

ResNeXt

deeper·2021-08-27 17:42 0 阅读 945
没有更多了