SNAハッカソン2019

2019年2月から3月にかけて、 SNA Hackathon 2019ソーシャルネットワークフィードをランク​​付けするコンテストが開催され、そこで私たちのチームが1位になりました。 この記事では、コンテストの組織、私たちが試みた方法、ビッグデータのトレーニングのためのcatboost設定についてお話します。



SNAハッカソン


この名前のハッカソンは3回開催されます。 ok.ruソーシャルネットワークによってそれぞれ編成され、タスクとデータはこのソーシャルネットワークに直接関連しています。
この場合のSNA(ソーシャルネットワーク分析)は、ソーシャルグラフの分析としてではなく、ソーシャルネットワークの分析としてより正確に理解されます。



2014年については言えませんが、2016年と2019年には、データを分析する機能に加えて、ビッグデータを扱うスキルも必要でした。 これらのコンテストに私を惹きつけたのは、機械学習とビッグデータ処理タスクの組み合わせであり、これらの分野での経験が勝つ助けになったと思います。


mlbootcamp


2019年、コンテストはプラットフォームhttps://mlbootcamp.ruで開催されました。


コンテストは2月7日にオンラインで始まり、3つのタスクで構成されました。 誰もがサイトに登録し、 ベースラインをダウンロードして、数時間車をアップロードできました。 3月15日のオンラインステージの終わりに、各ショーのトップ15が、3月30日から4月1日に行われたオフラインステージのMail.ruオフィスに招待されました。


挑戦する


ソースデータは、ユーザー識別子(userId)と投稿識別子(objectId)を提供します。 ユーザーに投稿が表示された場合、データには、userId、objectId、この投稿に対するユーザーの反応(フィードバック)、さまざまな標識のセット、または写真やテキストへのリンクを含む行が含まれます。


userIdobjectIdownerIdフィードバック画像
3555225677[いいね、クリック][hash1]
128425532144[嫌い][hash2、hash3]
13145355677[クリック、再共有][hash2]

テストデータセットには同様の構造が含まれていますが、フィードバックフィールドがありません。 目的は、フィードバック分野での「いいね」反応の存在を予測することです。
送信ファイルの構造は次のとおりです。


userIdSortedList [objectId]
12378.13.54.22
12835.61.55
13135,68,129,11

メトリック-ユーザーによる平均ROC AUC。


データのより詳細な説明は完全なサイトで見つけることができます。 テストや写真などのデータをダウンロードすることもできます。


オンラインステージ


オンライン段階では、タスクは3つの部分に分割されました



オフラインステージ


オフライン段階では、データにはすべての属性が含まれていましたが、テキストと画像はまばらでした。 データセット内の行は、すでに多くありましたが、1.5倍になりました。


問題解決


仕事でcvをやっているので、このコンテストの旅は「画像」タスクから始めました。 提供されたデータは、userId、objectId、ownerId(投稿が公開されているグループ)、投稿の作成および表示のタイムスタンプ、そしてもちろんこの投稿の画像です。
タイムスタンプに基づいていくつかの機能を生成した後、次のアイデアは、imagenetで事前にトレーニングされたニューロンの最後から2番目のレイヤーを取得し、これらの埋め込みを後押しすることでした。



結果は印象的ではありませんでした。 imagenetニューロンからの埋め込みは無関係です。自動エンコーダーをファイルする必要があると思いました。



時間がかかり、結果は改善しませんでした。


機能の生成


画像の操作には多くの時間がかかり、私はもっと簡単なことをすることにしました。
すぐにわかるように、データセットにはいくつかのカテゴリカルサインがあります。あまり気にしないように、私はcatboostを使用しました。 解決策は素晴らしく、設定なしで、すぐにリーダーボードの最初の行に到達しました。


たくさんのデータがあり、それらは寄木細工のフォーマットでレイアウトされているので、二度考えることなく、私はscalaを取り、すべてをスパークで書き始めました。


画像の埋め込みよりも多くの成長をもたらした最も単純な機能:



タイムスタンプから、ユーザーがテープを視聴した時刻(朝/日/夜/夜)を取得できました。 これらのカテゴリを組み合わせることにより、引き続き機能を生成できます。



これにより、メトリックが徐々に改善されました。 ただし、トレーニングデータセットのサイズは約2,000万レコードであるため、機能を追加すると学習が大幅に遅くなります。


データ使用方法を再定義しました。 データは時間に依存しますが、将来的には明示的な情報漏洩は見られませんでしたが、万が一に備えて、次のように破りました。



提供されたトレーニングセット(2月と3月の2週間)は2つのパートに分かれていました。
過去N日間のデータについて、彼はモデルをトレーニングしました。 上記の集計は、テストを含むすべてのデータに基づいて構築されました。 同時に、ターゲット変数のさまざまなエンコーダを構築できるデータが登場しました。 最も簡単なアプローチは、すでに新しい機能を作成しているコードを再利用し、トレーニングされておらずターゲット= 1のデータを送信することです。


したがって、同様の機能が得られました。



つまり、カテゴリ機能のさまざまな組み合わせに応じて、データセットの一部でターゲットのエンコード意味することが判明しました。 原則として、catboostはターゲットエンコーディングも構築します。この観点からは利点はありませんが、たとえば、このグループの投稿が好きなユニークユーザーの数をカウントすることが可能になりました。 同時に、主な目標が達成されました-私のデータセットは数回減少し、特徴の生成を続けることができました。


catboostは、好まれる反応に応じてのみエンコーダーを構築できますが、フィードバックには他の反応があります:再共有、嫌い、嫌い、クリック、無視、これは手動で行うことができます。 データセットを膨張させないように、あらゆる種類の集計を再集計し、重要度の低い機能をふるいにかけました。


その時までに私は大きな差で一位になりました。 唯一の恥ずかしさは、画像の埋め込みがほとんど利益をもたらさないことでした。 このアイデアは、catboostにすべてを与えるようになりました。 クラスタKは画像を意味し、新しいカテゴリフィーチャimageCatを取得します。


KMeansから取得したクラスターを手動でフィルタリングおよびマージした後のクラスを次に示します。



imageCatに基づいて、以下を生成します。



テキスト


画像コンテストの結果は私に合っていたので、テキストを試してみることにしました。 以前、私はテキストをあまり使いませんでした、そして愚かさで、私はtf-idfとsvdで1日を殺しました。 次に、doc2vecを使用したベースラインを確認しました。 doc2vecのパラメーターをわずかに調整して、テキストの埋め込みを取得しました。


そして、彼は単に画像のコードを再利用し、画像の埋め込みをテキストの埋め込みに置き換えました。 その結果、テキストコンテストで2位になりました。


共同システム


まだ「棒を突く」ことのない競技は1つしかありませんでしたが、リーダーボードのAUCから判断すると、この特定の競技の結果はオフラインステージに最も影響を与えそうでした。
ソースデータに含まれるすべての兆候を取り、カテゴリカルな兆候を選択し、画像自体の特徴を除き、画像と同じ集計を計算しました。 キャットブーストに入れるだけで2位になりました。


catboostを最適化する最初のステップ


最初の1位と2番目の2位は私を喜ばせましたが、特別なことは何もしなかったという理解がありました。


コンテストのタスクは、ユーザーのフレームワーク内で投稿をランク付けすることです。この間、分類問題を解決してきました。つまり、間違ったメトリックを最適化しました。


簡単な例を示します。


userIdobjectId予言グラウンドトゥルース
1100.91
1110.81
1120.71
1130.61
1140.50
2150.40
2160.31

小さな置換を行います


userIdobjectId予言グラウンドトゥルース
1100.91
1110.81
1120.71
1130.60
2160.51
2150.40
1140.31

次の結果が得られます。


モデルオークUser1 AUCUser2 AUC平均AUC
オプション10.81,00,00.5
オプション20.70.751,00.875

ご覧のとおり、AUCメトリック全体を改善しても、ユーザー内の平均AUCメトリックが改善されるわけではありません。


Catboostは、すぐにランキング指標を最適化できます。 ランキングメトリック、catboostを使用した場合の成功事例について読み、YetiRankPairwiseを設定して夜間学習を行います。 結果は印象的ではありませんでした。 私はよく勉強していないと判断したので、エラー関数をQueryRMSEに変更しました。これは、catboostのドキュメントから判断すると、より速く収束します。 その結果、分類のトレーニング中と同じ結果が得られましたが、これら2つのモデルのアンサンブルが大幅に増加し、3つすべての大会で1位になりました。


Collaborative Systemsコンペティションのオンラインステージが終了する5分前に、Sergey Shalnovは私を2位に移しました。 私たちが一緒に行ったさらなる方法。


オフライン段階の準備


オンラインステージでの勝利はRTX 2080 TIビデオカードで保証されていましたが、300,000ルーブルの主な賞であり、最終的には1位だったため、この2週間で仕事ができました。


結局のところ、セルゲイはキャットブーストも使用しました。 私たちはアイデアや機能を交換し、アンナベロニカドログシュのレポートについて学びました。そこでは、私の質問の多く、さらにはまだ出ていない質問に対しても答えがありました。


レポートを表示すると、すべてのパラメーターをデフォルト値に戻す必要があり、設定を非常に慎重に調整する必要があるという考えが、一連の標識を修正した後でのみ得られました。 現在、1つのトレーニングには約15時間かかりましたが、1つのモデルでは、ランキングのアンサンブルよりも速度が向上しました。


機能の生成


競争「コラボレーションシステム」では、多数の機能がモデルにとって重要であると評価されています。 たとえば、 auditweights_spark_svdは最も重要な属性であり、その意味についての情報はありません。 重要な兆候に基づいてさまざまなユニットを数える価値があると思いました。 たとえば、ユーザーごと、グループごと、オブジェクトごとの平均auditweights_spark_svd。 同じことは、トレーニングが実行されておらず、ターゲット= 1のデータ、つまりユーザーが気に入ったオブジェクトのユーザーごとの平均auditweights_spark_svdから計算できます。 auditweights_spark_svd以外にも、いくつかの重要な兆候がありました 。 それらのいくつかを次に示します。



たとえば、userIdによるauditweightsCtrGenderの平均値は、userId + ownerIdによるuserOwnerCounterCreateLikesの平均値と同様に重要な機能であることが判明しました。 これにより、フィールドの意味を理解する必要性について考える必要がありました。


他の重要な機能はauditweightsLikesCountauditweightsShowsCountでした 。 一方を他方に分割すると、さらに重要な機能が得られました。


データ漏洩


競争モデルと生産モデルは非常に異なるタスクです。 データを準備する際、すべての詳細を考慮し、テストのターゲット変数に関する重要な情報を転送しないことは非常に困難です。 実稼働ソリューションを作成する場合、モデルのトレーニング時にデータリークの使用を回避しようとします。 しかし、コンテストに勝ちたい場合は、データリークが最良の機能です。


データを調べると、objectIdに従って、 auditweightsLikesCountauditweightsShowsCountの値が変化していることがわかります。つまり、これらの記号の最大値の比率は、配信時の比率よりも変換後をはるかによく反映しています。


最初に見つかったリークはauditweightsLikesCountMax / auditweightsShowsCountMaxでした
しかし、データをより詳しく見るとどうなりますか? 配達日で並べ替えて取得:


objectIduserIdauditweightsShowsCountauditweightsLikesCountターゲット(いいね!)
11123おそらくない
12153おそらくはい
13164

最初のそのような例を見つけたのは驚くべきことであり、私の予測が実現しなかったことが判明しました。 しかし、オブジェクトのフレームワーク内のこれらの記号の最大値が増加するという事実を考えると、あまりにも怠notではなく、 auditweightsShowsCountNextauditweightsLikesCountNext 、つまり次の瞬間の値を見つけることにしました。 機能を追加する
(auditweightsShowsCountNext-auditweightsShowsCount)/(auditweightsLikesCount-auditweightsLikesCountNext) 24時間、急激にジャンプしました。
userId + ownerId内のuserOwnerCounterCreateLikes、たとえば、objectId + userGender内のauditweightsCtrGenderで次の値が見つかった場合、同様のリークが使用される可能性があります。 漏れのあるこのようなフィールドを6つ見つけ、可能な限り情報を引き出しました。


その時までに、私たちは共同の属性から最大限の情報を絞り込んでいたが、画像とテキストのコンテストには戻ってこなかった。 確認すべき素晴らしいアイデアがありました:対応する競技会で、画像やテキストに機能がどれだけ直接与えるか?


画像やテキストに関するコンテストでのリークはありませんでしたが、それまでにcatboostのデフォルトパラメータを返し、コードをコーミングし、いくつかの機能を追加しました。 合計結果:


解決策スピード
画像で最大0.6411
最大画像なし0.6297
2位結果0.6295

解決策スピード
テキストで最大0.666
テキストなしの最大0.660
2位結果0.656

解決策スピード
コラボレーティブで最大0.745
2位結果0.723

多くのテキストや画像が絞り出される可能性は低いことが明らかになり、いくつかの最も興味深いアイデアを試した後、それらとの作業をやめました。


コラボレーションシステムでの機能のさらなる生成は成長をもたらさず、ランキングを開始しました。 オンラインの段階では、分類とランキングのアンサンブルはわずかに増加しましたが、それは分類の訓練が不十分だったためでした。 YetiRanlPairwiseを含むエラー関数はどれも、LogLossの結果に近づきませんでした(0.745対0.725)。 起動できなかったQueryCrossEntropyに希望がありました。


オフラインステージ


オフライン段階では、データ構造は同じままですが、小さな変更があります。



リストされた困難に加えて、大きなプラスが1つありました。RTX2080TIを備えた大規模サーバーがチームに割り当てられました。 私は長い間htopを楽しんでいました。


アイデアは1つでした-すでにそこにあるものを再現するだけです。 サーバーに環境をセットアップするのに数時間を費やした後、結果が再現されていることを徐々に確認し始めました。 私たちが直面している主な問題は、データ量の増加です。 負荷をわずかに減らし、パラメータcatboost ctr_complexity = 1を設定することにしました。 これにより速度は少し低下しますが、私のモデルは動作し始め、結果は良好でした-0.733。 私とは異なり、セルゲイはデータを2つの部分に分割せず、すべてのデータをトレーニングしましたが、これはオンライン段階で最高の結果をもたらしましたが、オフライン段階では多くの困難がありました。 生成したすべての機能を使用して、「額に」キャットブーストを配置しようとすると、オンライン段階では何​​も起こりませんでした。 Sergeyは、たとえばfloat64型をfloat32に変換するなど、型の最適化を行いました。 この記事では、パンダのメモリの最適化に関する情報を見つけることができます。 その結果、SergeyはすべてのデータについてCPUのトレーニングを行い、約0.735になりました。


これらの結果は勝つには十分でしたが、実際のスピードを隠し、他のチームが同じことをしていないことを確信できませんでした。


最後までの戦い


catboostのチューニング


ソリューションは完全に再現され、テキストデータと画像の機能が追加されたため、残りはcatboostパラメーターを調整することだけでした。 Sergeyは少数の反復でCPUを研究し、ctr_complexity = 1で研究しました。 あと1日しかありませんでした。繰り返しを追加するか、ctr_complexityを増やすだけで、午前中はさらに高速になり、1日中歩くことができました。


オフラインの段階では、スコアは非表示にするのが非常に簡単で、サイトで最適なソリューションではないものを選択するだけです。 提出の締め切り前の最後の数分間にリーダーボードの急激な変化が予想され、停止しないことに決めました。


アンナのビデオから、モデルの品質を向上させるには、次のパラメーターを選択するのが最善であることを学びました。



他のパラメーターは最終結果にあまり影響を与えないため、それらを選択しようとしませんでした。 ctr_complexity = 1のGPUデータセットでのトレーニングの1回の反復には20分かかり、縮小されたデータセットで選択されたパラメーターは、完全なデータセットでの最適なパラメーターとわずかに異なりました。 その結果、データの10%で約30回の反復を行い、すべてのデータでさらに約10回の反復を行いました。 およそ次のことが判明しました。



デフォルトのパラメーターでは、モデルは十分にトレーニングされていないと結論付けることができます。


リーダーボードで結果を見たとき、私は非常に驚きました:


モデルモデル1モデル2モデル3アンサンブル
チューニングなし0.74030.74040.74040.7407
チューニングあり0.74060.74050.74060.7408

モデルをすばやく適用する必要がない場合は、パラメーターの選択を、最適化されていないパラメーターの複数のモデルのアンサンブルに置き換えることをお勧めします。


Sergeyは、データセットのサイズを最適化してGPUで実行することに従事していました。 最も簡単なオプションはデータの一部を切り捨てることですが、これを行う方法はいくつかあります。



そして最後に-すべてのオプションのアンサンブルを作成します。


最後のアンサンブル


最終日の夜遅くまでに、モデルのアンサンブルを投稿し、0.742が得られました。 夜、ctr_complexity = 2でモデルを起動し、30分ではなく5時間トレーニングしました。 午前4時にのみ彼女はカウントし、私は最後のアンサンブルを行いました。


問題を解決するためのさまざまなアプローチにより、予測は強く相関せず、アンサンブルが大幅に増加しました。 適切なアンサンブルを取得するには、予測モデルの生の予測を使用して(prediction_type = 'RawFormulaVal')、scale_pos_weight = neg_count / pos_countを設定することをお勧めします。



このサイトでは、プライベートリーダーボードで最終結果を見ることができます。


その他の解決策


多くのチームがレコメンダーシステムアルゴリズムの規範に従いました。 私は、この分野の専門家ではないため、それらを評価することはできませんが、2つの興味深い決定が記憶されました。



おわりに


メモリに最も多く保管されるもの:




受け取った感情、知識、賞品について主催者に感謝します。



Source: https://habr.com/ru/post/J447376/


All Articles