Kaggle比赛:用深度学习模型寻找外星人

大赛简介

大赛名称:SETI Breakthrough Listen - E.T. Signal Searc

比赛链接:https://www.kaggle.com/c/seti-breakthrough-listen/overview

赛题背景

“我们一个人在宇宙里吗?”这是最深刻且长期存在的人类问题之一。随着技术的进步,我们正在寻找新的和更强大的方法来寻求答案。加州大学伯克利大学使用世界上最强大的望远镜对数百万颗恒星进行技术扫描。现在希望Kaggle社区能够帮助解释他们收到的信号。

加州大学伯克利分校的Breakthrough Listen 团队使用世界上最强大的望远镜扫描数百万颗恒星以寻找技术迹象。

现在它希望Kaggle 社区帮助解释他们接收到的信号。Listen 团队是外星智慧搜索(SETI) 的一部分,使用地球上最大的可操纵天线,即直径100 米的绿岸望远镜。与任何SETI 搜索一样,交流的动机也是主要挑战。

人类已经建造了大量的无线电设备。很难在现代技术的巨大检测结果中寻找微弱的外星传播针。
当前的方法使用两个过滤器来搜索大海捞针。首先,Listen 团队将目标恒星的扫描与天空其他区域的扫描穿插在一起。两组扫描中出现的任何信号都可能不是来自目标恒星的方向。其次,管道会丢弃不会改变其频率的信号,因为这意味着它们可能在望远镜附近。

运动中的源应该有一个暗示运动的信号,类似于路过的消防车警报器的音调变化。这两个过滤器非常有效,但我们知道它们可以改进。管道无疑会错过有趣的信号,尤其是那些具有复杂时间或频率结构的信号,以及那些在有大量干扰的频谱区域中的信号。在本次比赛中,利用您的数据科学技能帮助识别Breakthrough Listen 目标扫描中的异常信号。

由于没有确认的用于训练机器学习算法的外星信号示例,该团队在来自望远镜的海量数据中加入了一些模拟信号(他们称之为“针”)。他们已经确定了一些隐藏的针,以便您可以训练您的模型以找到更多。数据由二维数组组成,因此可能存在有前景的计算机视觉方法,以及数字信号处理、异常检测等。成功识别最多针的算法将赢得现金奖励,但也有可能帮助回答科学中最大的问题之一。

赛题任务

在这场比赛中,利用算法来识别异常的信号。数据由二维数组组成,因此计算机视觉中可能会有一些有前途的方法,可能涉及的知识包括数字信号处理,异常检测等。
我们比赛的任务就是通过给定的频谱图预测对应的标签:0或者1

数据分析

我们首先看下整体训练集中标签的分布:

从上图可以看出这是一个非常不平衡的数据集
其中有插"针"(TARGET=1)的频谱图如下:

没有插"针"(TARGET=0)的频谱图如下

其实上面两张范例肉眼可以大概分辨得出来TARGET0和TARGET1的差别

EfficientNet模型

我们模型使用当前算是sota的影像分类模型EfficientNet。论文链接:EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks,https://arxiv.org/abs/1905.11946

EfficientNets是google brain的工程师 该模型的基础网络架构是通过使用神经网络架构搜索(neural architecture search简称NAS)设计得到
这里就要讲一下NAS技术

对于NAS 这个task 来说,其实最直觉的方法就是我不断的从search space 当中取出不同的neural architecture ,并且实际的训练之后来获得真正的performance,借着不断的重复这个动作,当我穷尽整个search space 时,我理所当然的就可以得到这个search space 当中最好的那个neural architecture。

而也因为这个简单的概念,所以最早的卷积神经网络(ConvNets)通常是在固定的资源预算下发展起来的,如果有更多的资源可用的话,则会扩大规模以获得更好的精度,比如可以提高网络深度(depth)、网络宽度(width)和输入图像分辨(resolution)大小。但是通过人工去调整depth, width, resolution 的放大或缩小的很困难的,在计算量受限时有放大哪个缩小哪个,这些都是很难去确定的,换句话说,这样的组合空间太大,人力无法穷举。

基于上述背景,该论文提出了一种新的模型缩放方法,它使用一个简单而高效的复合系数来从depth, width, resolution 三个维度放大网络,不会像传统的方法那样任意缩放网络的维度,基于神经结构搜索技术可以获得最优的一组参数(复合系数)。从下图可看出,EfficientNet不仅比别的网络快很多,而且精度也更高。
我使用的是efficientnet_b0,可以在timm这个套件直接import:

https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/efficientnet.py

用tensorboard画出来大概长这样

Baseline代码实践

导入包

# Libraries
import warnings
warnings.filterwarnings('ignore')

import numpy as np
import pandas as pd
import codecs
import os
import glob
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
import cv2
from tqdm import tqdm
from colorama import Fore, Back, Style
r_ = Fore.WHITE
from plotly.offline import iplot
import plotly.express as px
import plotly.graph_objects as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots

from skimage.io import imshow, imread, imsave
from skimage.transform import rotate, AffineTransform, warp,rescale, resize, downscale_local_mean
from skimage import color,data
from skimage.exposure import adjust_gamma
from skimage.util import random_noise

数据加载

我们这里定义一个数据加载的函数,以便后去方便读取数据:

def get_train_filename_by_id(_id: str) -> str:
    return f"../input/train/{_id[0]}/{_id}.npy"

def show_cadence(filename: str, label: int) -> None:
    fig, axes = plt.subplots(6, 1, figsize = (16, 10))
    ax = axes.ravel()
    arr = np.load(filename)
    for i in range(6):
        
        ax[i].imshow(arr[i].astype(float), interpolation='nearest', aspect='auto')
        ax[i].text(5, 100, ["ON", "OFF"][i % 2], bbox={'facecolor': 'white'})
        if i != 5:
            ax[i].set_xticks([])
            
    fig.text(0.5, -0.02, 'Frequency Range', ha='center', fontsize=18)
    fig.text(-0.02, 0.5, 'Seconds', va='center', rotation='vertical', fontsize=18)

    plt.suptitle(f"ID: {os.path.basename(filename)} TARGET: {label}", fontsize=18)
    fig.tight_layout()
    plt.show()

下面是自定义数据集对象:

class ClassificationDataset:
    def __init__(self, image_paths, targets): 
        self.image_paths = image_paths
        self.targets = targets

    def __len__(self):
        return len(self.image_paths)
    
    def __getitem__(self, item):      
        image = np.load(self.image_paths[item]).astype(float)
        targets = self.targets[item]
        image = image / np.array([np.abs(image).max() for i in range(6)]).reshape(6,1 ,1)
        return {
            "image": torch.tensor(image, dtype=torch.float),
            "targets": torch.tensor(targets, dtype=torch.long),
        }

分类模型定义

定义一个基于efficientnet的分类器

class enetv2(nn.Module):
    def __init__(self, backbone, out_dim):
        super(enetv2, self).__init__()
        self.enet = enet.EfficientNet.from_pretrained(backbone)
        # self.enet.load_state_dict(torch.load(pretrained_model[backbone]))
        self.myfc = nn.Linear(self.enet._fc.in_features, out_dim)
        self.enet._fc = nn.Identity()
        self.conv1 = nn.Conv2d(6, 3, kernel_size=3, stride=1, padding=3, bias=False)

    def extract(self, x):
        return self.enet(x)

    def forward(self, x):
        x = self.conv1(x)
        x = self.extract(x)
        x = self.myfc(x)
        return x

模型训练

baseline_name = 'efficientnet-b3'

models = []
device = "cuda"
epochs = 4
Batch_Size = 32
X = df_train.img_path.values
Y = df_train.target.values
skf = StratifiedKFold(n_splits=5, random_state=1024)
fold = 0

for train_index, test_index in skf.split(X, Y):
    
    model = enetv2(baseline_name, out_dim=1)
    model.to(device)
    model = nn.DataParallel(model)
    
    train_index, test_index = train_index[:], test_index[:]
    
    train_images, valid_images = X[train_index], X[test_index]
    train_targets, valid_targets = Y[train_index], Y[test_index]

    train_dataset = ClassificationDataset(image_paths=train_images, targets=train_targets)
    valid_dataset = ClassificationDataset(image_paths=valid_images, targets=valid_targets)
    train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=Batch_Size,shuffle=True, num_workers=12)
    valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=Batch_Size,shuffle=False, num_workers=12)

    optimizer = torch.optim.Adam(model.parameters(), lr=5e-4)
    train(train_loader, valid_loader, model, optimizer, device, fold, 5)
            
    models.append(model)
    fold += 1
    print('')

总结

这篇文章希望能够帮助图像新手入门分类模型,我们主要学习当前sota的影像分类模型efficientnet,和优化器,以及交叉验证等等,其中完整代码包含MixUp,有兴趣同学可以联系小编获取完整代码。

完整代码可通过私信联系小编获取
署名作者:小李飞刀


版权声明:本文为weixin_42645636原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。