Don't call me turkry!をcnnでとく。

ネットサーフィン中に面白そうなものを見つけたので、後出しになるが解いてみる。🦃 https://www.kaggle.com/c/dont-call-me-turkey/

学習データ

学習データは1データごとに下のような構造

- audio_embedding
    - 0
      ・
      ・
      ・
    - 9
- is_turkey
- vid_id
- end_time_seconds_youtube_clip
- start_time_seconds_youtube_clip

audio_embeddingとは、VGGishによって、YouTubeの動画データをを1秒ごとに128次元に圧縮したものらしい。これが最大10秒分ある。

vidYouTubeで動画を開いたときにurlの末尾につく、youtube.com/watch?v=2lAe1cqCOXo2lAe1cqCOXoの値のよう。試しにis_turkey=1の動画を見ると、七面鳥の鳴き声を確認できる。

モデル設計

  • データ前処理 10秒に満たないデータがあるため、0で埋める。
    train = pd.read_json('train.json')
    test = pd.read_json('test.json')
    #train data
    train_x_raw=np.array(train.audio_embedding)#    [1195,10,128]たまに足りなくておかしい
    train_x =np.zeros((1195,10,128))
    ##整形
    for i in range(len(train_x_raw)):
        if(np.array(train_x_raw[i]).shape[0] is not 10):#足りない場合埋める
            lack=10 - np.array(train_x_raw[i]).shape[0]
                train_x[i]=np.append(train_x_raw[i],np.zeros((lack,128)    ),axis=0)
        else:#足りてたらnumpyに変換
            train_x[i]=np.array(train_x_raw[i])
    train_y=train.is_turkey
    
    #test data
    test_x_raw=test.audio_embedding#[1196,10,128]
    test_x =np.zeros((1196,10,128))
    for i in range(len(test_x_raw)):
        if(np.array(test_x_raw[i]).shape[0] is not 10):#足りない場合埋める
            lack=10 - np.array(test_x_raw[i]).shape[0]
            test_x[i]=np.append(test_x_raw[i],np.zeros((lack,128)),ax    is=0)
        else:#足りてたらnumpyに変換
            test_x[i]=np.array(test_x_raw[i])
  • モデル定義 10x128を1枚の画像のようにcnnの入力とする。損失関数nn.BCEWithLogitsLoss()はsigmoidを含むため、モデルの出力は(0~1)でない。
    class ML(nn.Module):
        def __init__(self, device='cuda'):
            super().__init__()
            self.device = device
            self.pad = nn.ZeroPad2d(1)
            self.conv1 = nn.Conv2d(1, 64,(2,12))
            self.pool1 = nn.MaxPool2d(2)
            self.conv2 = nn.Conv2d(64, 64, (2,2))
            self.pool2 = nn.MaxPool2d(2)
            self.dropout = nn.Dropout(p=0.3)
            self.f1=nn.ReLU()
            self.f2=nn.ReLU()
            self.l = nn.Linear(5760,1)
        def forward(self,x):
            h=self.conv1(h)
            h=self.f1(h)
            h=self.dropout(h)
            h=self.pool1(h)
            h=self.conv2(h)
            h=self.f2(h)
            h=self.dropout(h)
            h=self.pool2(h)
        
            h=h.reshape(5760)
            h=self.l(h)#出力形式に線形変換
            h=self.dropout(h)
            return h
  • 学習部分
    device = torch.device('cuda')
    model = cnn(device=device).to(device)
    
    
    criterion = nn.BCEWithLogitsLoss()
    optimizer = optimizers.Adam(model.parameters())
    def train_step(x,t):
        model.train()
        y=model(x)
        loss = criterion(y,t)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        return loss
    
    #パラメータ更新開始
    epochs =100
    lists=np.array(range(len(train_x)))
    histories=np.array([])
    for epoch in tqdm(range(epochs)):
        train_loss = 0.
        np.random.shuffle(lists)
        for i in lists:
            x_tmp = np.array(train_x[i]).reshape(1,1,10,128)
            x = torch.from_numpy(x_tmp).type('torch.FloatTensor').to('cuda')#入力形式に変換(入力)
            t = torch.from_numpy(np.array([train_y[i]])).type('torch.Floa    tTensor').to('cuda')#入力形式に変換(出力)
            loss = train_step(x,t)#順伝播,逆伝播,更新
            train_loss += loss.item()
        train_loss /= len(train_x)
        histories=np.append(histories,train_loss)
        print('Epoch: {}, Cost:     {:.3f}'.format(epoch+1,train_loss))
  • 損失関数の推移

  • 結果 実際のcompetitionの様子を見てるとまだまだ上がいるので、改良の余地がありそう。

出典・参考: