{
  "id": 335800,
  "title": "7th place solution(with inference code)",
  "content": "<h1>UPDATE</h1>\n<p>I published <a href=\"https://www.kaggle.com/code/nadare/7th-place-solution-inference\" target=\"_blank\">our inference code</a><br>\nThe code is dirty, but I hope it helps you</p>\n<h1>UPDATE 2</h1>\n<p>I published <a href=\"https://www.kaggle.com/code/nadare/word-tour-experiment/notebook\" target=\"_blank\">notebook about word tour</a>.<br>\n\"Word Tour\" is one of the important parts of this solution and is likely to be applicable to other tasks as well. Please check this out and upvote.</p>\n<h1>introduction</h1>\n<p>We publish our solution.<br>\nSince it is English generated by google translate, I think that there are some unnatural parts, but please forgive me as I will correct it as much as possible later.</p>\n<h1>overview</h1>\n<p>We preprocessed \"name\" and \"address\", according to each language, and converted them to embedding.<br>\nWe generated candidates from distance, name similarity, word and character precision, and recall based on multiple regression.<br>\nPrediction was made by LightGBM binary classification<br>\nIn the post-processing, the edge whose predicted value exceeds the threshold is divided into subgraphs by Union Find Tree (Disjoint Set), and then the one whose shortest distance is within 2 is selected.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2Fc432cb35d881cf8ca05319de19863afb%2F4sq_overview.png?generation=1657809170858208&amp;alt=media\" alt=\"overview_of_4sq_solution\"></p>\n<p>The jump to 0.933-&gt; 0.951 that everyone cares about happened when I replaced one of the 5-folds from one that was early stopping at 500 iterations to one that was trained at 2000 iterations with all the data.</p>\n<h1>Preprocessing</h1>\n<h2>Natural language preprocessing</h2>\n<p>Since the name and address were multilingual, I prepared three separate processes.</p>\n<ol>\n<li>Almost raw text</li>\n<li>Text for each language with lowercase letters, removal of symbols, and digitization of numerals</li>\n<li>About 2. Text converted to [0-9a-z &amp;] with unidecode</li>\n</ol>\n<p>Also, since there were many nans for the address, we complemented by connecting three neighborhoods of the distance.</p>\n<h3>Processing for each language</h3>\n<p>Basically, all languages ​​have uniform removal of symbols-&gt; lowercase letters / digitization of numbers-&gt; alphabetization.<br>\nSince Japanese, Chinese, and Thai are languages ​​that require word-separation, they are processed individually.</p>\n<p>For Japanese, use the tokenizer <a href=\"https://github.com/WorksApplications/Sudachi\" target=\"_blank\">Sudachi</a> to get the word-separation and reading, and then use <a href=\"https://github.com/miurahr/pykakasi\" target=\"_blank\">Pykakasi</a> to convert it to romaji. did.<br>\nChinese was divided by <a href=\"https://tfhub.dev/google/zh_segmentation/1\" target=\"_blank\">zh_segmehntation</a> and converted to romaji by unidecode.<br>\nThai is divided and romanized using <a href=\"https://pythainlp.github.io/\" target=\"_blank\">PyThaiNLP</a>.</p>\n<h2>city, state pre-processing</h2>\n<p>For city and state, we took out about 2000 in order of the number of appearances and put them in the category of fitting to any of 2000 in the vicinity of the distance.<br>\nWe also converted it to the top 2000 place names using the <a href=\"http://www.geonames.org/\" target=\"_blank\">cities1000</a> dataset and made it into a category. (Hereafter referred to as geo_name)</p>\n<h2>Creating an embedding</h2>\n<p>For the text of 3 in the previous section, the name and address were divided into 32000 subwords using different sentence pieces.<br>\nOn top of this, we learned embedding by the following three unsupervised pre-learning based on word2vec.</p>\n<ul>\n<li>About name Skip-gram task</li>\n<li>Task to predict categories from name, address, city, state, geo_name</li>\n<li>A contrasting learning task that mixes name, address, city, state, geo_name, categories and guesses itself by <a href=\"https://arxiv.org/abs/2104.08821\" target=\"_blank\">SimCSE</a></li>\n</ul>\n<p>When doing these learnings, the method of obtaining negative samples was very important.<br>\nWe were able to perform high-quality learning by generating a negative sample with a random walk for each sample, using the probability based on the negative logarithm of the hadsine distance as the edge for the vicinity of 128 of each sample.</p>\n<h3>Fill in missing values ​​in a category</h3>\n<p>Now that we have a good model for category prediction, we filled in the missing values ​​by making predictions for those with blank categories.</p>\n<h3>Clustering / one-dimensional</h3>\n<p>Each embedding was divided into about 2000 clusters by spherical kmmeans ++.<br>\nWe also implemented kmeans ++ based on the hadsine distance and divided it into about 2000 clusters as well.</p>\n<p>After splitting the cluster, we embedded the information in one dimension by solving the traveling salesman problem for the distance between each vertex.<br>\nThis is a technique called <a href=\"https://arxiv.org/abs/2205.01954\" target=\"_blank\">Word Tour</a>, which allows you to transform embedding into a form that GBDT can easily interpret in just one dimension.<br>\nEach category was sorted by TSP based on the similarity of each kmeans, the similarity of names and addresses with the number of occurrences top2000, and the haversine distances of city, state, and geoname, and used as continuous values.</p>\n<h1>Feature generation</h1>\n<h2>haversine distance</h2>\n<p>The logarithm of the distance between two points was taken and used as a feature. The conversion to logarithm will be useful in later candidate generation.</p>\n<h2>name, address</h2>\n<p>We have created various comparative features for both.</p>\n<h3>Obtained from a public notebook</h3>\n<p>Gestalt Pattern Matching (SequenceMatcher), levenshtein, jaro_winkler (I will omit it because it was introduced in various kernels)</p>\n<h3>Bag of Words, Bag of Char</h3>\n<p>For the word that was divided and normalized, TfIdfVectorizer was set to binary = True, use_idf = False, and 1-gram and 2-gram sparse vectors were created and the similarity was compared.<br>\nAlso, considering the case where characters have meaning like Japanese and Chinese, we created sparse vectors for each character and compared the similarity.</p>\n<h3>ROUGE-N, ROUGE-L</h3>\n<p>ROUGE is an index often used to evaluate sentence summarization tasks. Measure the degree of agreement between sentences and give precision, recall, f1 value.<br>\nROUGE-N was compared using N-GRAM, ROUGE-L was compared using LCS, the former was implemented using tensorflow's Ragged Tensor, and the latter was implemented using tensorflow-text's rouge_l function.</p>\n<h3>Cosine similarity, one-dimensional distance</h3>\n<p>For each embedding, the cosine similarity and the distance after dropping to one dimension are added as features.</p>\n<h2>city / state</h2>\n<p>Similar to embedding, the top 2000 cities and states are listed as features in the order of Count Encoding and traveling salesman problem.</p>\n<h2>categories</h2>\n<p>I also got the category embedding by doing metric learning about the category prediction task during pre-learning.<br>\nLike the other features, the category embedding is based on the count encoding as the features arranged in the traveling salesman problem.</p>\n<h2>phone / url</h2>\n<p>Only whether or not they match after normalization was added as a feature quantity.</p>\n<h1>Candidate generation</h1>\n<p>Candidate generation for learning and prediction combines multiple logics.</p>\n<ul>\n<li>haversine 4 nearest neighbors of distance</li>\n<li>Haversine 12 multiple regressions of distance logarithm and embedding cosine similarity such as name, address, categories, etc.</li>\n<li>About name Similarity by rouge-1 in word unit, precision, recall Total 4</li>\n<li>About name A total of 8 similarities, precision, and recall by rouge-2 in character units</li>\n<li>Top 4 cosine similarity of name</li>\n</ul>\n<h1>predict</h1>\n<p>I used LightGBM. 2000 iterations training was performed on all data without early stopping, and GPU prediction was performed by Forest Inference at the time of prediction.<br>\nThe hyperparameters are as follows, and max_bin has been increased only for some categories.</p>\n<pre><code>max_bin_by_feature = []\nfor col in feature_cols:\n    if (col.endswith (\"kmeans_labels\") or col.endswith (\"pos_tsp_index\")) and col.startswith (\"query\"):\n        print (col)\n        max_bin_by_feature.append (255 * 4)\n    elif (col.endswith (\"kmeans_labels\") or col.endswith (\"pos_tsp_index\")) and col.startswith (\"candidate\"):\n        print (col)\n        max_bin_by_feature.append (255 * 2)\n    elif col.endswith (\"_category_frequent_min_tsp\") or col.endswith (\"_category_frequent_max_tsp\"):\n        print (col)\n        max_bin_by_feature.append (255 * 2)\n    else: else:\n        max_bin_by_feature.append (255)\n\nlgb_params = {\n    \"objective\": \"binary\",\n    \"metric\": \"auc\",\n    \"boosting\":'gbdt',\n    \"max_depth\": -1, -1,\n    \"num_leaves\": 2 ** 12 --1,\n    \"learning_rate\": 0.1,\n    \"bagging_freq\": 1,\n    \"is_unbalance\": True,\n    \"max_bin_by_feature\": max_bin_by_feature,\n     \"bin_construct_sample_cnt\": 200000,\n    \"lambda_l1\": 1.,\n    \"lambda_l2\": 1.,\n    \"bagging_fraction\": 0.9,\n    \"feature_fraction\": 0.6,\n    \"seed\": 0}\n</code></pre>\n<h1>Post-processing</h1>\n<p>The graph was constructed for each group by UnionFind after removing some edges based on the mediation centrality of the graph, with the predicted values ​​of the pair being above a certain level.<br>\nFor each group, the shortest distance to all pairs of unweighted graphs was calculated, and the predicted value was within 256 vertices within 2 distances from each vertex.</p>",
  "comments": [
    {
      "id": 1847449,
      "author_name": "nadare",
      "author_url": "",
      "post_date": "2022-07-08T00:19:42.297000",
      "content": "<h1>Japanese edition</h1>\n<h1>overview</h1>\n<p>私達は\"name\", \"address\", に対して各言語に応じた前処理を行い、embeddingに変換しました。<br>\n私たちは重回帰をベースに距離や名前の類似度・単語や文字単位でのprecision・recallから候補生成を行いました。<br>\n予測はLightGBMの二値分類で行いました<br>\n後処理は予測値が閾値を超えたものを辺として、Union Find Tree(Disjoint Set)でサブグラフに分割したのち、最短距離が2以内のものを選ぶ処理をしました。</p>\n<p>皆さんが気になる0.933 -&gt; 0.951へのジャンプについては、5-fold の一つを500iterationsでearly stoppingさせていたものから全データで2000iterations学習させたものに差し替えた際に起こりました。</p>\n<h1>前処理</h1>\n<h2>自然言語の前処理</h2>\n<p>nameとaddressについては多言語だったので、処理を分けた三つのものを用意しました。</p>\n<ol>\n<li>ほぼそのままのテキスト</li>\n<li>小文字化・記号の除去・数詞の数字化を行った各言語ごとのテキスト</li>\n<li>2.についてunidecodeで全て[0-9a-z&amp;]に変換したテキスト</li>\n</ol>\n<p>またaddressについてはnanが多かったので、私達は距離の近傍3つをつなげて補完しました。</p>\n<h3>各言語ごとの処理</h3>\n<p>基本的にはどの言語も記号の除去-&gt;小文字化・数詞の数字化-&gt;アルファベット化を一律に行いましたが<br>\n日本語・中国語・タイ語については分かち書きが必要な言語であるため個別に処理を行いました。</p>\n<p>日本語は<a href=\"https://github.com/WorksApplications/Sudachi\" target=\"_blank\">Sudachi</a>というtokenizerで分かち書き・読み方を取得し、<a href=\"https://github.com/miurahr/pykakasi\" target=\"_blank\">Pykakasi</a>でローマ字に変換しました。<br>\n中国語は<a href=\"https://tfhub.dev/google/zh_segmentation/1\" target=\"_blank\">zh_segmehntation</a>で分かち書きし、unidecodeでローマ字に変換しました。<br>\nタイ語は<a href=\"https://pythainlp.github.io/\" target=\"_blank\">PyThaiNLP</a>を用いて分かち書きとローマ字化を行いました。</p>\n<h2>city, stateの前処理</h2>\n<p>cityとstateは出現数順に約2000を取り出し、距離の近傍で2000のうちのどれかにあてはめカテゴリにしました。<br>\nまた、<a href=\"http://www.geonames.org/\" target=\"_blank\">cities1000</a>データセットを用いて上位2000の地名に変換しカテゴリにしました。(以下geo_nameと呼びます)</p>\n<h2>embeddingの作成</h2>\n<p>前項の3のテキストについてnameとaddressをそれぞれ別のsentencepieceを用いて32000のサブワードに分割しました。<br>\nこの上でword2vecをベースにした以下の3つの教師なし事前学習によりembeddingを学習しました。</p>\n<ul>\n<li>nameについてSkip-gramタスク</li>\n<li>name, address, city・state・geo_nameからcategoriesを予測するタスク</li>\n<li>name, address, city・state・geo_name, categoriesを混ぜ、<a href=\"https://arxiv.org/abs/2104.08821\" target=\"_blank\">SimCSE</a>により自身を当てる対照学習タスク</li>\n</ul>\n<p>これらの学習を行う際、negative sampleの取得方法は非常に大切でした。<br>\n各サンプルの128近傍についてhaversine距離の対数の負を元にした確率を辺とし、各サンプルごとにランダムウォークでnegative sampleを生成することで質の良い学習を行えました。</p>\n<h3>カテゴリの欠損値埋め</h3>\n<p>カテゴリの予測については良いモデルができたので、カテゴリが空欄になっているものに対して予測を行うことで欠損値を埋めました。</p>\n<h3>クラスタリング・一次元化</h3>\n<p>各embeddingは球面kmeans++により約2000のクラスタへと分割しました。<br>\nまた、haversine距離をもとにしたkmeans++も実装し、同じく約2000のクラスタに分割しました。</p>\n<p>クラスタの分割後は各頂点同士の距離にたいして巡回セールスマン問題を解くことで、情報を一次元に埋め込みました。<br>\nこれは<a href=\"https://arxiv.org/abs/2205.01954\" target=\"_blank\">Word Tour</a>と呼ばれる手法で、これによりembeddingをわずか一次元でGBDTが解釈しやすい形に変換することができます。<br>\n各kmeansの類似度、出現数top2000の名前と住所の類似度、city, state, geonameのhaversine距離から各カテゴリをTSPで並び替え連続値としました。</p>\n<h1>特徴量生成</h1>\n<h2>haversine distance</h2>\n<p>二点間の距離について対数をとって特徴量にしました。対数への変換はのちの候補生成で役に立ちます。</p>\n<h2>name, address</h2>\n<p>両者には様々な比較の特徴量を作成しました。</p>\n<h3>公開notebookから取得したもの</h3>\n<p>Gestalt Pattern Matching(SequenceMatcher), levenshtein, jaro_winkler (様々なkernelで紹介されていたので割愛します)</p>\n<h3>Bag of Words, Bag of Char</h3>\n<p>分かち書きし正規化した単語についてTfIdfVectorizerをbinary=True, use_idf=Falseにし、1-gramと2-gramのスパースベクトルを作り類似度を比較しました。<br>\nまた、日本語や中国語のように文字が意味を持つ場合も考え、文字単位でも同様にスパースベクトルを作成し類似度を比較しました。</p>\n<h3>ROUGE-N, ROUGE-L</h3>\n<p>ROUGEは文章要約タスクの評価に使われることの多い指標です。文章同士について一致度を測りprecision, recall, f1 valueを出します。<br>\nROUGE-NはN-GRAMを用いて比較、ROUGE-LはLCSを用いて比較を行い、前者はtensorflowのRaggedTensorによる比較、後者はtensorflow-textのrouge_l関数を用いて実装しました。</p>\n<h3>コサイン類似度、一次元の距離</h3>\n<p>各embeddingについてコサイン類似度と、一次元に落とした後の距離を特徴量として追加しました。</p>\n<h2>city/state</h2>\n<p>embeddingと同様に、上位2000件のcityとstateをCountEncodingと巡回セールスマン問題で並べた順に特徴量として入れました。</p>\n<h2>categories</h2>\n<p>事前学習の際のカテゴリ予測タスクについてmetric learningを行うことでカテゴリのembeddingも手に入れました。<br>\n他の特徴量と同様にカテゴリのembeddingも巡回セールスマン問題で並べたものとcount encodingを特徴量としました。</p>\n<h2>phone/url</h2>\n<p>正規化を行い一致するかどうかのみ特徴量として加えました。</p>\n<h1>候補生成</h1>\n<p>学習・予測のための候補生成は複数のロジックを組み合わせました。</p>\n<ul>\n<li>haversine距離の最近傍4個</li>\n<li>haversine距離の対数とname, address, categories等のembeddingのコサイン類似度の重回帰12個</li>\n<li>nameについて単語単位でのrouge-1による類似度、precision, recall合計4つ</li>\n<li>nameについて文字単位でのrouge-2による類似度、precision, recallの合計8つ</li>\n<li>nameのコサイン類似度の上位4つ</li>\n</ul>\n<h1>予測</h1>\n<p>LightGBMを用いました。全データについてearly stoppingなしで2000iterations学習を行い、予測時はForestInferenceによるGPUでの予測を行いました。<br>\nハイパーパラメータは以下の通りで、一部のカテゴリだけmax_binを増やしました。</p>\n<pre><code>max_bin_by_feature = []\nfor col in feature_cols:\n    if (col.endswith(\"kmeans_labels\") or col.endswith(\"pos_tsp_index\")) and col.startswith(\"query\"):\n        print(col)\n        max_bin_by_feature.append(255*4)\n    elif (col.endswith(\"kmeans_labels\") or col.endswith(\"pos_tsp_index\")) and col.startswith(\"candidate\"):\n        print(col)\n        max_bin_by_feature.append(255*2) \n    elif col.endswith(\"_category_frequent_min_tsp\") or col.endswith(\"_category_frequent_max_tsp\"):\n        print(col)\n        max_bin_by_feature.append(255*2)\n    else:\n        max_bin_by_feature.append(255)\n\nlgb_params = {\n    \"objective\" : \"binary\",\n    \"metric\" : \"auc\",\n    \"boosting\": 'gbdt',\n    \"max_depth\" : -1,\n    \"num_leaves\" : 2**12 - 1,\n    \"learning_rate\" : 0.1,\n    \"bagging_freq\": 1,\n    \"is_unbalance\": True,\n    \"max_bin_by_feature\": max_bin_by_feature,\n     \"bin_construct_sample_cnt\": 200000,\n    \"lambda_l1\": 1.,\n    \"lambda_l2\": 1.,\n    \"bagging_fraction\" : 0.9,\n    \"feature_fraction\" : 0.6,\n    \"seed\": 0}\n</code></pre>\n<h1>後処理</h1>\n<p>ペアの予測値が一定以上のものを辺とし、グラフの媒介中心性をもとに一部の辺を除いた後UnionFindによりグループごとのグラフを構築しました。<br>\n各グループについて重みなしのグラフの全対最短距離を求め、各頂点から距離が2以内の頂点256個以内を予測値としました。</p>",
      "votes": 5,
      "replies": []
    },
    {
      "id": 1847574,
      "author_name": "nadare",
      "author_url": "",
      "post_date": "2022-07-08T02:06:04.037000",
      "content": "<p>there are word tour's result for categories<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2F116b2ea99df839c15806f8c06cbf10af%2F(42).png?generation=1657245838706707&amp;alt=media\" alt=\"word tour result for categories 1\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2Fd50fab4fb54b6baaf70d8c5ccf56ca42%2Fword_tour_result.png?generation=1657245934032150&amp;alt=media\" alt=\"word tour result for categories 2\"></p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1848711,
      "author_name": "The Devastator",
      "author_url": "",
      "post_date": "2022-07-08T20:51:56.887000",
      "content": "<p>Thank you for your informative post! I'm glad you shared your code as well. <br>\nCongrats, fantastic work! </p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1848349,
      "author_name": "YUNLIN GAO",
      "author_url": "",
      "post_date": "2022-07-08T14:38:48.083000",
      "content": "<p>Congrats for your 7th place in this competition.</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1848280,
      "author_name": "Marília Prata",
      "author_url": "",
      "post_date": "2022-07-08T13:44:53.883000",
      "content": "<p>Congrats for your 7th place in this competition.</p>\n<p>ありがとうございました  for both versions (japanese/english) write-ups.</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1847638,
      "author_name": "clora",
      "author_url": "",
      "post_date": "2022-07-08T03:20:22.460000",
      "content": "<p>Congratulations!🎉<br>\nThank you for the detailed solution. I will use it as a reference.</p>",
      "votes": 0,
      "replies": []
    }
  ],
  "comment_count": 6,
  "messages": [
    {
      "id": 1847448,
      "postDate": "2022-07-08T00:18:16.273Z",
      "content": "<h1>UPDATE</h1>\n<p>I published <a href=\"https://www.kaggle.com/code/nadare/7th-place-solution-inference\" target=\"_blank\">our inference code</a><br>\nThe code is dirty, but I hope it helps you</p>\n<h1>UPDATE 2</h1>\n<p>I published <a href=\"https://www.kaggle.com/code/nadare/word-tour-experiment/notebook\" target=\"_blank\">notebook about word tour</a>.<br>\n\"Word Tour\" is one of the important parts of this solution and is likely to be applicable to other tasks as well. Please check this out and upvote.</p>\n<h1>introduction</h1>\n<p>We publish our solution.<br>\nSince it is English generated by google translate, I think that there are some unnatural parts, but please forgive me as I will correct it as much as possible later.</p>\n<h1>overview</h1>\n<p>We preprocessed \"name\" and \"address\", according to each language, and converted them to embedding.<br>\nWe generated candidates from distance, name similarity, word and character precision, and recall based on multiple regression.<br>\nPrediction was made by LightGBM binary classification<br>\nIn the post-processing, the edge whose predicted value exceeds the threshold is divided into subgraphs by Union Find Tree (Disjoint Set), and then the one whose shortest distance is within 2 is selected.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2Fc432cb35d881cf8ca05319de19863afb%2F4sq_overview.png?generation=1657809170858208&amp;alt=media\" alt=\"overview_of_4sq_solution\"></p>\n<p>The jump to 0.933-&gt; 0.951 that everyone cares about happened when I replaced one of the 5-folds from one that was early stopping at 500 iterations to one that was trained at 2000 iterations with all the data.</p>\n<h1>Preprocessing</h1>\n<h2>Natural language preprocessing</h2>\n<p>Since the name and address were multilingual, I prepared three separate processes.</p>\n<ol>\n<li>Almost raw text</li>\n<li>Text for each language with lowercase letters, removal of symbols, and digitization of numerals</li>\n<li>About 2. Text converted to [0-9a-z &amp;] with unidecode</li>\n</ol>\n<p>Also, since there were many nans for the address, we complemented by connecting three neighborhoods of the distance.</p>\n<h3>Processing for each language</h3>\n<p>Basically, all languages ​​have uniform removal of symbols-&gt; lowercase letters / digitization of numbers-&gt; alphabetization.<br>\nSince Japanese, Chinese, and Thai are languages ​​that require word-separation, they are processed individually.</p>\n<p>For Japanese, use the tokenizer <a href=\"https://github.com/WorksApplications/Sudachi\" target=\"_blank\">Sudachi</a> to get the word-separation and reading, and then use <a href=\"https://github.com/miurahr/pykakasi\" target=\"_blank\">Pykakasi</a> to convert it to romaji. did.<br>\nChinese was divided by <a href=\"https://tfhub.dev/google/zh_segmentation/1\" target=\"_blank\">zh_segmehntation</a> and converted to romaji by unidecode.<br>\nThai is divided and romanized using <a href=\"https://pythainlp.github.io/\" target=\"_blank\">PyThaiNLP</a>.</p>\n<h2>city, state pre-processing</h2>\n<p>For city and state, we took out about 2000 in order of the number of appearances and put them in the category of fitting to any of 2000 in the vicinity of the distance.<br>\nWe also converted it to the top 2000 place names using the <a href=\"http://www.geonames.org/\" target=\"_blank\">cities1000</a> dataset and made it into a category. (Hereafter referred to as geo_name)</p>\n<h2>Creating an embedding</h2>\n<p>For the text of 3 in the previous section, the name and address were divided into 32000 subwords using different sentence pieces.<br>\nOn top of this, we learned embedding by the following three unsupervised pre-learning based on word2vec.</p>\n<ul>\n<li>About name Skip-gram task</li>\n<li>Task to predict categories from name, address, city, state, geo_name</li>\n<li>A contrasting learning task that mixes name, address, city, state, geo_name, categories and guesses itself by <a href=\"https://arxiv.org/abs/2104.08821\" target=\"_blank\">SimCSE</a></li>\n</ul>\n<p>When doing these learnings, the method of obtaining negative samples was very important.<br>\nWe were able to perform high-quality learning by generating a negative sample with a random walk for each sample, using the probability based on the negative logarithm of the hadsine distance as the edge for the vicinity of 128 of each sample.</p>\n<h3>Fill in missing values ​​in a category</h3>\n<p>Now that we have a good model for category prediction, we filled in the missing values ​​by making predictions for those with blank categories.</p>\n<h3>Clustering / one-dimensional</h3>\n<p>Each embedding was divided into about 2000 clusters by spherical kmmeans ++.<br>\nWe also implemented kmeans ++ based on the hadsine distance and divided it into about 2000 clusters as well.</p>\n<p>After splitting the cluster, we embedded the information in one dimension by solving the traveling salesman problem for the distance between each vertex.<br>\nThis is a technique called <a href=\"https://arxiv.org/abs/2205.01954\" target=\"_blank\">Word Tour</a>, which allows you to transform embedding into a form that GBDT can easily interpret in just one dimension.<br>\nEach category was sorted by TSP based on the similarity of each kmeans, the similarity of names and addresses with the number of occurrences top2000, and the haversine distances of city, state, and geoname, and used as continuous values.</p>\n<h1>Feature generation</h1>\n<h2>haversine distance</h2>\n<p>The logarithm of the distance between two points was taken and used as a feature. The conversion to logarithm will be useful in later candidate generation.</p>\n<h2>name, address</h2>\n<p>We have created various comparative features for both.</p>\n<h3>Obtained from a public notebook</h3>\n<p>Gestalt Pattern Matching (SequenceMatcher), levenshtein, jaro_winkler (I will omit it because it was introduced in various kernels)</p>\n<h3>Bag of Words, Bag of Char</h3>\n<p>For the word that was divided and normalized, TfIdfVectorizer was set to binary = True, use_idf = False, and 1-gram and 2-gram sparse vectors were created and the similarity was compared.<br>\nAlso, considering the case where characters have meaning like Japanese and Chinese, we created sparse vectors for each character and compared the similarity.</p>\n<h3>ROUGE-N, ROUGE-L</h3>\n<p>ROUGE is an index often used to evaluate sentence summarization tasks. Measure the degree of agreement between sentences and give precision, recall, f1 value.<br>\nROUGE-N was compared using N-GRAM, ROUGE-L was compared using LCS, the former was implemented using tensorflow's Ragged Tensor, and the latter was implemented using tensorflow-text's rouge_l function.</p>\n<h3>Cosine similarity, one-dimensional distance</h3>\n<p>For each embedding, the cosine similarity and the distance after dropping to one dimension are added as features.</p>\n<h2>city / state</h2>\n<p>Similar to embedding, the top 2000 cities and states are listed as features in the order of Count Encoding and traveling salesman problem.</p>\n<h2>categories</h2>\n<p>I also got the category embedding by doing metric learning about the category prediction task during pre-learning.<br>\nLike the other features, the category embedding is based on the count encoding as the features arranged in the traveling salesman problem.</p>\n<h2>phone / url</h2>\n<p>Only whether or not they match after normalization was added as a feature quantity.</p>\n<h1>Candidate generation</h1>\n<p>Candidate generation for learning and prediction combines multiple logics.</p>\n<ul>\n<li>haversine 4 nearest neighbors of distance</li>\n<li>Haversine 12 multiple regressions of distance logarithm and embedding cosine similarity such as name, address, categories, etc.</li>\n<li>About name Similarity by rouge-1 in word unit, precision, recall Total 4</li>\n<li>About name A total of 8 similarities, precision, and recall by rouge-2 in character units</li>\n<li>Top 4 cosine similarity of name</li>\n</ul>\n<h1>predict</h1>\n<p>I used LightGBM. 2000 iterations training was performed on all data without early stopping, and GPU prediction was performed by Forest Inference at the time of prediction.<br>\nThe hyperparameters are as follows, and max_bin has been increased only for some categories.</p>\n<pre><code>max_bin_by_feature = []\nfor col in feature_cols:\n    if (col.endswith (\"kmeans_labels\") or col.endswith (\"pos_tsp_index\")) and col.startswith (\"query\"):\n        print (col)\n        max_bin_by_feature.append (255 * 4)\n    elif (col.endswith (\"kmeans_labels\") or col.endswith (\"pos_tsp_index\")) and col.startswith (\"candidate\"):\n        print (col)\n        max_bin_by_feature.append (255 * 2)\n    elif col.endswith (\"_category_frequent_min_tsp\") or col.endswith (\"_category_frequent_max_tsp\"):\n        print (col)\n        max_bin_by_feature.append (255 * 2)\n    else: else:\n        max_bin_by_feature.append (255)\n\nlgb_params = {\n    \"objective\": \"binary\",\n    \"metric\": \"auc\",\n    \"boosting\":'gbdt',\n    \"max_depth\": -1, -1,\n    \"num_leaves\": 2 ** 12 --1,\n    \"learning_rate\": 0.1,\n    \"bagging_freq\": 1,\n    \"is_unbalance\": True,\n    \"max_bin_by_feature\": max_bin_by_feature,\n     \"bin_construct_sample_cnt\": 200000,\n    \"lambda_l1\": 1.,\n    \"lambda_l2\": 1.,\n    \"bagging_fraction\": 0.9,\n    \"feature_fraction\": 0.6,\n    \"seed\": 0}\n</code></pre>\n<h1>Post-processing</h1>\n<p>The graph was constructed for each group by UnionFind after removing some edges based on the mediation centrality of the graph, with the predicted values ​​of the pair being above a certain level.<br>\nFor each group, the shortest distance to all pairs of unweighted graphs was calculated, and the predicted value was within 256 vertices within 2 distances from each vertex.</p>",
      "rawMarkdown": "#UPDATE\nI published [our inference code](https://www.kaggle.com/code/nadare/7th-place-solution-inference)\nThe code is dirty, but I hope it helps you\n\n# UPDATE 2\nI published [notebook about word tour](https://www.kaggle.com/code/nadare/word-tour-experiment/notebook).\n\"Word Tour\" is one of the important parts of this solution and is likely to be applicable to other tasks as well. Please check this out and upvote.\n\n# introduction\nWe publish our solution.\nSince it is English generated by google translate, I think that there are some unnatural parts, but please forgive me as I will correct it as much as possible later.\n\n# overview\nWe preprocessed \"name\" and \"address\", according to each language, and converted them to embedding.\nWe generated candidates from distance, name similarity, word and character precision, and recall based on multiple regression.\nPrediction was made by LightGBM binary classification\nIn the post-processing, the edge whose predicted value exceeds the threshold is divided into subgraphs by Union Find Tree (Disjoint Set), and then the one whose shortest distance is within 2 is selected.\n\n![overview_of_4sq_solution](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2Fc432cb35d881cf8ca05319de19863afb%2F4sq_overview.png?generation=1657809170858208&alt=media)\n\nThe jump to 0.933-> 0.951 that everyone cares about happened when I replaced one of the 5-folds from one that was early stopping at 500 iterations to one that was trained at 2000 iterations with all the data.\n\n# Preprocessing\n## Natural language preprocessing\nSince the name and address were multilingual, I prepared three separate processes.\n\n1. Almost raw text\n2. Text for each language with lowercase letters, removal of symbols, and digitization of numerals\n3. About 2. Text converted to [0-9a-z &] with unidecode\n\nAlso, since there were many nans for the address, we complemented by connecting three neighborhoods of the distance.\n\n### Processing for each language\nBasically, all languages ​​have uniform removal of symbols-> lowercase letters / digitization of numbers-> alphabetization.\nSince Japanese, Chinese, and Thai are languages ​​that require word-separation, they are processed individually.\n\nFor Japanese, use the tokenizer [Sudachi] (https://github.com/WorksApplications/Sudachi) to get the word-separation and reading, and then use [Pykakasi] (https://github.com/miurahr/pykakasi) to convert it to romaji. did.\nChinese was divided by [zh_segmehntation] (https://tfhub.dev/google/zh_segmentation/1) and converted to romaji by unidecode.\nThai is divided and romanized using [PyThaiNLP] (https://pythainlp.github.io/).\n\n## city, state pre-processing\nFor city and state, we took out about 2000 in order of the number of appearances and put them in the category of fitting to any of 2000 in the vicinity of the distance.\nWe also converted it to the top 2000 place names using the [cities1000] (http://www.geonames.org/) dataset and made it into a category. (Hereafter referred to as geo_name)\n\n## Creating an embedding\nFor the text of 3 in the previous section, the name and address were divided into 32000 subwords using different sentence pieces.\nOn top of this, we learned embedding by the following three unsupervised pre-learning based on word2vec.\n\n* About name Skip-gram task\n* Task to predict categories from name, address, city, state, geo_name\n* A contrasting learning task that mixes name, address, city, state, geo_name, categories and guesses itself by [SimCSE] (https://arxiv.org/abs/2104.08821)\n\nWhen doing these learnings, the method of obtaining negative samples was very important.\nWe were able to perform high-quality learning by generating a negative sample with a random walk for each sample, using the probability based on the negative logarithm of the hadsine distance as the edge for the vicinity of 128 of each sample.\n\n### Fill in missing values ​​in a category\nNow that we have a good model for category prediction, we filled in the missing values ​​by making predictions for those with blank categories.\n\n### Clustering / one-dimensional\nEach embedding was divided into about 2000 clusters by spherical kmmeans ++.\nWe also implemented kmeans ++ based on the hadsine distance and divided it into about 2000 clusters as well.\n\nAfter splitting the cluster, we embedded the information in one dimension by solving the traveling salesman problem for the distance between each vertex.\nThis is a technique called [Word Tour] (https://arxiv.org/abs/2205.01954), which allows you to transform embedding into a form that GBDT can easily interpret in just one dimension.\nEach category was sorted by TSP based on the similarity of each kmeans, the similarity of names and addresses with the number of occurrences top2000, and the haversine distances of city, state, and geoname, and used as continuous values.\n\n# Feature generation\n\n## haversine distance\nThe logarithm of the distance between two points was taken and used as a feature. The conversion to logarithm will be useful in later candidate generation.\n\n## name, address\nWe have created various comparative features for both.\n\n### Obtained from a public notebook\nGestalt Pattern Matching (SequenceMatcher), levenshtein, jaro_winkler (I will omit it because it was introduced in various kernels)\n\n### Bag of Words, Bag of Char\nFor the word that was divided and normalized, TfIdfVectorizer was set to binary = True, use_idf = False, and 1-gram and 2-gram sparse vectors were created and the similarity was compared.\nAlso, considering the case where characters have meaning like Japanese and Chinese, we created sparse vectors for each character and compared the similarity.\n\n### ROUGE-N, ROUGE-L\nROUGE is an index often used to evaluate sentence summarization tasks. Measure the degree of agreement between sentences and give precision, recall, f1 value.\nROUGE-N was compared using N-GRAM, ROUGE-L was compared using LCS, the former was implemented using tensorflow's Ragged Tensor, and the latter was implemented using tensorflow-text's rouge_l function.\n\n### Cosine similarity, one-dimensional distance\nFor each embedding, the cosine similarity and the distance after dropping to one dimension are added as features.\n\n## city / state\nSimilar to embedding, the top 2000 cities and states are listed as features in the order of Count Encoding and traveling salesman problem.\n\n## categories\nI also got the category embedding by doing metric learning about the category prediction task during pre-learning.\nLike the other features, the category embedding is based on the count encoding as the features arranged in the traveling salesman problem.\n\n## phone / url\nOnly whether or not they match after normalization was added as a feature quantity.\n\n# Candidate generation\nCandidate generation for learning and prediction combines multiple logics.\n\n* haversine 4 nearest neighbors of distance\n* Haversine 12 multiple regressions of distance logarithm and embedding cosine similarity such as name, address, categories, etc.\n* About name Similarity by rouge-1 in word unit, precision, recall Total 4\n* About name A total of 8 similarities, precision, and recall by rouge-2 in character units\n* Top 4 cosine similarity of name\n\n# predict\nI used LightGBM. 2000 iterations training was performed on all data without early stopping, and GPU prediction was performed by Forest Inference at the time of prediction.\nThe hyperparameters are as follows, and max_bin has been increased only for some categories.\n\n```\nmax_bin_by_feature = []\nfor col in feature_cols:\n    if (col.endswith (\"kmeans_labels\") or col.endswith (\"pos_tsp_index\")) and col.startswith (\"query\"):\n        print (col)\n        max_bin_by_feature.append (255 * 4)\n    elif (col.endswith (\"kmeans_labels\") or col.endswith (\"pos_tsp_index\")) and col.startswith (\"candidate\"):\n        print (col)\n        max_bin_by_feature.append (255 * 2)\n    elif col.endswith (\"_category_frequent_min_tsp\") or col.endswith (\"_category_frequent_max_tsp\"):\n        print (col)\n        max_bin_by_feature.append (255 * 2)\n    else: else:\n        max_bin_by_feature.append (255)\n\nlgb_params = {\n    \"objective\": \"binary\",\n    \"metric\": \"auc\",\n    \"boosting\":'gbdt',\n    \"max_depth\": -1, -1,\n    \"num_leaves\": 2 ** 12 --1,\n    \"learning_rate\": 0.1,\n    \"bagging_freq\": 1,\n    \"is_unbalance\": True,\n    \"max_bin_by_feature\": max_bin_by_feature,\n     \"bin_construct_sample_cnt\": 200000,\n    \"lambda_l1\": 1.,\n    \"lambda_l2\": 1.,\n    \"bagging_fraction\": 0.9,\n    \"feature_fraction\": 0.6,\n    \"seed\": 0}\n\n\n```\n\n# Post-processing\nThe graph was constructed for each group by UnionFind after removing some edges based on the mediation centrality of the graph, with the predicted values ​​of the pair being above a certain level.\nFor each group, the shortest distance to all pairs of unweighted graphs was calculated, and the predicted value was within 256 vertices within 2 distances from each vertex.",
      "votes": 65
    },
    {
      "id": 1847449,
      "postDate": "2022-07-08T00:19:42.297Z",
      "content": "<h1>Japanese edition</h1>\n<h1>overview</h1>\n<p>私達は\"name\", \"address\", に対して各言語に応じた前処理を行い、embeddingに変換しました。<br>\n私たちは重回帰をベースに距離や名前の類似度・単語や文字単位でのprecision・recallから候補生成を行いました。<br>\n予測はLightGBMの二値分類で行いました<br>\n後処理は予測値が閾値を超えたものを辺として、Union Find Tree(Disjoint Set)でサブグラフに分割したのち、最短距離が2以内のものを選ぶ処理をしました。</p>\n<p>皆さんが気になる0.933 -&gt; 0.951へのジャンプについては、5-fold の一つを500iterationsでearly stoppingさせていたものから全データで2000iterations学習させたものに差し替えた際に起こりました。</p>\n<h1>前処理</h1>\n<h2>自然言語の前処理</h2>\n<p>nameとaddressについては多言語だったので、処理を分けた三つのものを用意しました。</p>\n<ol>\n<li>ほぼそのままのテキスト</li>\n<li>小文字化・記号の除去・数詞の数字化を行った各言語ごとのテキスト</li>\n<li>2.についてunidecodeで全て[0-9a-z&amp;]に変換したテキスト</li>\n</ol>\n<p>またaddressについてはnanが多かったので、私達は距離の近傍3つをつなげて補完しました。</p>\n<h3>各言語ごとの処理</h3>\n<p>基本的にはどの言語も記号の除去-&gt;小文字化・数詞の数字化-&gt;アルファベット化を一律に行いましたが<br>\n日本語・中国語・タイ語については分かち書きが必要な言語であるため個別に処理を行いました。</p>\n<p>日本語は<a href=\"https://github.com/WorksApplications/Sudachi\" target=\"_blank\">Sudachi</a>というtokenizerで分かち書き・読み方を取得し、<a href=\"https://github.com/miurahr/pykakasi\" target=\"_blank\">Pykakasi</a>でローマ字に変換しました。<br>\n中国語は<a href=\"https://tfhub.dev/google/zh_segmentation/1\" target=\"_blank\">zh_segmehntation</a>で分かち書きし、unidecodeでローマ字に変換しました。<br>\nタイ語は<a href=\"https://pythainlp.github.io/\" target=\"_blank\">PyThaiNLP</a>を用いて分かち書きとローマ字化を行いました。</p>\n<h2>city, stateの前処理</h2>\n<p>cityとstateは出現数順に約2000を取り出し、距離の近傍で2000のうちのどれかにあてはめカテゴリにしました。<br>\nまた、<a href=\"http://www.geonames.org/\" target=\"_blank\">cities1000</a>データセットを用いて上位2000の地名に変換しカテゴリにしました。(以下geo_nameと呼びます)</p>\n<h2>embeddingの作成</h2>\n<p>前項の3のテキストについてnameとaddressをそれぞれ別のsentencepieceを用いて32000のサブワードに分割しました。<br>\nこの上でword2vecをベースにした以下の3つの教師なし事前学習によりembeddingを学習しました。</p>\n<ul>\n<li>nameについてSkip-gramタスク</li>\n<li>name, address, city・state・geo_nameからcategoriesを予測するタスク</li>\n<li>name, address, city・state・geo_name, categoriesを混ぜ、<a href=\"https://arxiv.org/abs/2104.08821\" target=\"_blank\">SimCSE</a>により自身を当てる対照学習タスク</li>\n</ul>\n<p>これらの学習を行う際、negative sampleの取得方法は非常に大切でした。<br>\n各サンプルの128近傍についてhaversine距離の対数の負を元にした確率を辺とし、各サンプルごとにランダムウォークでnegative sampleを生成することで質の良い学習を行えました。</p>\n<h3>カテゴリの欠損値埋め</h3>\n<p>カテゴリの予測については良いモデルができたので、カテゴリが空欄になっているものに対して予測を行うことで欠損値を埋めました。</p>\n<h3>クラスタリング・一次元化</h3>\n<p>各embeddingは球面kmeans++により約2000のクラスタへと分割しました。<br>\nまた、haversine距離をもとにしたkmeans++も実装し、同じく約2000のクラスタに分割しました。</p>\n<p>クラスタの分割後は各頂点同士の距離にたいして巡回セールスマン問題を解くことで、情報を一次元に埋め込みました。<br>\nこれは<a href=\"https://arxiv.org/abs/2205.01954\" target=\"_blank\">Word Tour</a>と呼ばれる手法で、これによりembeddingをわずか一次元でGBDTが解釈しやすい形に変換することができます。<br>\n各kmeansの類似度、出現数top2000の名前と住所の類似度、city, state, geonameのhaversine距離から各カテゴリをTSPで並び替え連続値としました。</p>\n<h1>特徴量生成</h1>\n<h2>haversine distance</h2>\n<p>二点間の距離について対数をとって特徴量にしました。対数への変換はのちの候補生成で役に立ちます。</p>\n<h2>name, address</h2>\n<p>両者には様々な比較の特徴量を作成しました。</p>\n<h3>公開notebookから取得したもの</h3>\n<p>Gestalt Pattern Matching(SequenceMatcher), levenshtein, jaro_winkler (様々なkernelで紹介されていたので割愛します)</p>\n<h3>Bag of Words, Bag of Char</h3>\n<p>分かち書きし正規化した単語についてTfIdfVectorizerをbinary=True, use_idf=Falseにし、1-gramと2-gramのスパースベクトルを作り類似度を比較しました。<br>\nまた、日本語や中国語のように文字が意味を持つ場合も考え、文字単位でも同様にスパースベクトルを作成し類似度を比較しました。</p>\n<h3>ROUGE-N, ROUGE-L</h3>\n<p>ROUGEは文章要約タスクの評価に使われることの多い指標です。文章同士について一致度を測りprecision, recall, f1 valueを出します。<br>\nROUGE-NはN-GRAMを用いて比較、ROUGE-LはLCSを用いて比較を行い、前者はtensorflowのRaggedTensorによる比較、後者はtensorflow-textのrouge_l関数を用いて実装しました。</p>\n<h3>コサイン類似度、一次元の距離</h3>\n<p>各embeddingについてコサイン類似度と、一次元に落とした後の距離を特徴量として追加しました。</p>\n<h2>city/state</h2>\n<p>embeddingと同様に、上位2000件のcityとstateをCountEncodingと巡回セールスマン問題で並べた順に特徴量として入れました。</p>\n<h2>categories</h2>\n<p>事前学習の際のカテゴリ予測タスクについてmetric learningを行うことでカテゴリのembeddingも手に入れました。<br>\n他の特徴量と同様にカテゴリのembeddingも巡回セールスマン問題で並べたものとcount encodingを特徴量としました。</p>\n<h2>phone/url</h2>\n<p>正規化を行い一致するかどうかのみ特徴量として加えました。</p>\n<h1>候補生成</h1>\n<p>学習・予測のための候補生成は複数のロジックを組み合わせました。</p>\n<ul>\n<li>haversine距離の最近傍4個</li>\n<li>haversine距離の対数とname, address, categories等のembeddingのコサイン類似度の重回帰12個</li>\n<li>nameについて単語単位でのrouge-1による類似度、precision, recall合計4つ</li>\n<li>nameについて文字単位でのrouge-2による類似度、precision, recallの合計8つ</li>\n<li>nameのコサイン類似度の上位4つ</li>\n</ul>\n<h1>予測</h1>\n<p>LightGBMを用いました。全データについてearly stoppingなしで2000iterations学習を行い、予測時はForestInferenceによるGPUでの予測を行いました。<br>\nハイパーパラメータは以下の通りで、一部のカテゴリだけmax_binを増やしました。</p>\n<pre><code>max_bin_by_feature = []\nfor col in feature_cols:\n    if (col.endswith(\"kmeans_labels\") or col.endswith(\"pos_tsp_index\")) and col.startswith(\"query\"):\n        print(col)\n        max_bin_by_feature.append(255*4)\n    elif (col.endswith(\"kmeans_labels\") or col.endswith(\"pos_tsp_index\")) and col.startswith(\"candidate\"):\n        print(col)\n        max_bin_by_feature.append(255*2) \n    elif col.endswith(\"_category_frequent_min_tsp\") or col.endswith(\"_category_frequent_max_tsp\"):\n        print(col)\n        max_bin_by_feature.append(255*2)\n    else:\n        max_bin_by_feature.append(255)\n\nlgb_params = {\n    \"objective\" : \"binary\",\n    \"metric\" : \"auc\",\n    \"boosting\": 'gbdt',\n    \"max_depth\" : -1,\n    \"num_leaves\" : 2**12 - 1,\n    \"learning_rate\" : 0.1,\n    \"bagging_freq\": 1,\n    \"is_unbalance\": True,\n    \"max_bin_by_feature\": max_bin_by_feature,\n     \"bin_construct_sample_cnt\": 200000,\n    \"lambda_l1\": 1.,\n    \"lambda_l2\": 1.,\n    \"bagging_fraction\" : 0.9,\n    \"feature_fraction\" : 0.6,\n    \"seed\": 0}\n</code></pre>\n<h1>後処理</h1>\n<p>ペアの予測値が一定以上のものを辺とし、グラフの媒介中心性をもとに一部の辺を除いた後UnionFindによりグループごとのグラフを構築しました。<br>\n各グループについて重みなしのグラフの全対最短距離を求め、各頂点から距離が2以内の頂点256個以内を予測値としました。</p>",
      "rawMarkdown": "Japanese edition\n=============\n\n# overview\n私達は\"name\", \"address\", に対して各言語に応じた前処理を行い、embeddingに変換しました。\n私たちは重回帰をベースに距離や名前の類似度・単語や文字単位でのprecision・recallから候補生成を行いました。\n予測はLightGBMの二値分類で行いました\n後処理は予測値が閾値を超えたものを辺として、Union Find Tree(Disjoint Set)でサブグラフに分割したのち、最短距離が2以内のものを選ぶ処理をしました。\n\n皆さんが気になる0.933 -> 0.951へのジャンプについては、5-fold の一つを500iterationsでearly stoppingさせていたものから全データで2000iterations学習させたものに差し替えた際に起こりました。\n\n# 前処理\n## 自然言語の前処理\nnameとaddressについては多言語だったので、処理を分けた三つのものを用意しました。\n\n1. ほぼそのままのテキスト\n2. 小文字化・記号の除去・数詞の数字化を行った各言語ごとのテキスト\n3. 2.についてunidecodeで全て[0-9a-z&]に変換したテキスト\n\nまたaddressについてはnanが多かったので、私達は距離の近傍3つをつなげて補完しました。\n\n### 各言語ごとの処理\n基本的にはどの言語も記号の除去->小文字化・数詞の数字化->アルファベット化を一律に行いましたが\n日本語・中国語・タイ語については分かち書きが必要な言語であるため個別に処理を行いました。\n\n日本語は[Sudachi](https://github.com/WorksApplications/Sudachi)というtokenizerで分かち書き・読み方を取得し、[Pykakasi](https://github.com/miurahr/pykakasi)でローマ字に変換しました。\n中国語は[zh_segmehntation](https://tfhub.dev/google/zh_segmentation/1)で分かち書きし、unidecodeでローマ字に変換しました。\nタイ語は[PyThaiNLP](https://pythainlp.github.io/)を用いて分かち書きとローマ字化を行いました。\n\n## city, stateの前処理\ncityとstateは出現数順に約2000を取り出し、距離の近傍で2000のうちのどれかにあてはめカテゴリにしました。\nまた、[cities1000](http://www.geonames.org/)データセットを用いて上位2000の地名に変換しカテゴリにしました。(以下geo_nameと呼びます)\n\n## embeddingの作成\n前項の3のテキストについてnameとaddressをそれぞれ別のsentencepieceを用いて32000のサブワードに分割しました。\nこの上でword2vecをベースにした以下の3つの教師なし事前学習によりembeddingを学習しました。\n\n* nameについてSkip-gramタスク\n* name, address, city・state・geo_nameからcategoriesを予測するタスク\n* name, address, city・state・geo_name, categoriesを混ぜ、[SimCSE](https://arxiv.org/abs/2104.08821)により自身を当てる対照学習タスク\n\nこれらの学習を行う際、negative sampleの取得方法は非常に大切でした。\n各サンプルの128近傍についてhaversine距離の対数の負を元にした確率を辺とし、各サンプルごとにランダムウォークでnegative sampleを生成することで質の良い学習を行えました。\n\n### カテゴリの欠損値埋め\nカテゴリの予測については良いモデルができたので、カテゴリが空欄になっているものに対して予測を行うことで欠損値を埋めました。\n\n### クラスタリング・一次元化\n各embeddingは球面kmeans++により約2000のクラスタへと分割しました。\nまた、haversine距離をもとにしたkmeans++も実装し、同じく約2000のクラスタに分割しました。\n\nクラスタの分割後は各頂点同士の距離にたいして巡回セールスマン問題を解くことで、情報を一次元に埋め込みました。\nこれは[Word Tour](https://arxiv.org/abs/2205.01954)と呼ばれる手法で、これによりembeddingをわずか一次元でGBDTが解釈しやすい形に変換することができます。\n各kmeansの類似度、出現数top2000の名前と住所の類似度、city, state, geonameのhaversine距離から各カテゴリをTSPで並び替え連続値としました。\n\n# 特徴量生成\n\n## haversine distance\n二点間の距離について対数をとって特徴量にしました。対数への変換はのちの候補生成で役に立ちます。\n\n## name, address\n両者には様々な比較の特徴量を作成しました。\n\n### 公開notebookから取得したもの\nGestalt Pattern Matching(SequenceMatcher), levenshtein, jaro_winkler (様々なkernelで紹介されていたので割愛します)\n\n### Bag of Words, Bag of Char\n分かち書きし正規化した単語についてTfIdfVectorizerをbinary=True, use_idf=Falseにし、1-gramと2-gramのスパースベクトルを作り類似度を比較しました。\nまた、日本語や中国語のように文字が意味を持つ場合も考え、文字単位でも同様にスパースベクトルを作成し類似度を比較しました。\n\n### ROUGE-N, ROUGE-L\nROUGEは文章要約タスクの評価に使われることの多い指標です。文章同士について一致度を測りprecision, recall, f1 valueを出します。\nROUGE-NはN-GRAMを用いて比較、ROUGE-LはLCSを用いて比較を行い、前者はtensorflowのRaggedTensorによる比較、後者はtensorflow-textのrouge_l関数を用いて実装しました。\n\n### コサイン類似度、一次元の距離\n各embeddingについてコサイン類似度と、一次元に落とした後の距離を特徴量として追加しました。\n\n## city/state\nembeddingと同様に、上位2000件のcityとstateをCountEncodingと巡回セールスマン問題で並べた順に特徴量として入れました。\n\n## categories\n事前学習の際のカテゴリ予測タスクについてmetric learningを行うことでカテゴリのembeddingも手に入れました。\n他の特徴量と同様にカテゴリのembeddingも巡回セールスマン問題で並べたものとcount encodingを特徴量としました。\n\n## phone/url\n正規化を行い一致するかどうかのみ特徴量として加えました。\n\n# 候補生成\n学習・予測のための候補生成は複数のロジックを組み合わせました。\n\n* haversine距離の最近傍4個\n* haversine距離の対数とname, address, categories等のembeddingのコサイン類似度の重回帰12個\n* nameについて単語単位でのrouge-1による類似度、precision, recall合計4つ\n* nameについて文字単位でのrouge-2による類似度、precision, recallの合計8つ\n* nameのコサイン類似度の上位4つ\n\n# 予測\nLightGBMを用いました。全データについてearly stoppingなしで2000iterations学習を行い、予測時はForestInferenceによるGPUでの予測を行いました。\nハイパーパラメータは以下の通りで、一部のカテゴリだけmax_binを増やしました。\n\n```\nmax_bin_by_feature = []\nfor col in feature_cols:\n    if (col.endswith(\"kmeans_labels\") or col.endswith(\"pos_tsp_index\")) and col.startswith(\"query\"):\n        print(col)\n        max_bin_by_feature.append(255*4)\n    elif (col.endswith(\"kmeans_labels\") or col.endswith(\"pos_tsp_index\")) and col.startswith(\"candidate\"):\n        print(col)\n        max_bin_by_feature.append(255*2) \n    elif col.endswith(\"_category_frequent_min_tsp\") or col.endswith(\"_category_frequent_max_tsp\"):\n        print(col)\n        max_bin_by_feature.append(255*2)\n    else:\n        max_bin_by_feature.append(255)\n\nlgb_params = {\n    \"objective\" : \"binary\",\n    \"metric\" : \"auc\",\n    \"boosting\": 'gbdt',\n    \"max_depth\" : -1,\n    \"num_leaves\" : 2**12 - 1,\n    \"learning_rate\" : 0.1,\n    \"bagging_freq\": 1,\n    \"is_unbalance\": True,\n    \"max_bin_by_feature\": max_bin_by_feature,\n     \"bin_construct_sample_cnt\": 200000,\n    \"lambda_l1\": 1.,\n    \"lambda_l2\": 1.,\n    \"bagging_fraction\" : 0.9,\n    \"feature_fraction\" : 0.6,\n    \"seed\": 0}\n\n\n```\n\n# 後処理\nペアの予測値が一定以上のものを辺とし、グラフの媒介中心性をもとに一部の辺を除いた後UnionFindによりグループごとのグラフを構築しました。\n各グループについて重みなしのグラフの全対最短距離を求め、各頂点から距離が2以内の頂点256個以内を予測値としました。",
      "votes": 5
    },
    {
      "id": 1847574,
      "postDate": "2022-07-08T02:06:04.037Z",
      "content": "<p>there are word tour's result for categories<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2F116b2ea99df839c15806f8c06cbf10af%2F(42).png?generation=1657245838706707&amp;alt=media\" alt=\"word tour result for categories 1\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2Fd50fab4fb54b6baaf70d8c5ccf56ca42%2Fword_tour_result.png?generation=1657245934032150&amp;alt=media\" alt=\"word tour result for categories 2\"></p>",
      "rawMarkdown": "there are word tour's result for categories\n![word tour result for categories 1](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2F116b2ea99df839c15806f8c06cbf10af%2F(42).png?generation=1657245838706707&alt=media)\n\n![word tour result for categories 2](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F1958529%2Fd50fab4fb54b6baaf70d8c5ccf56ca42%2Fword_tour_result.png?generation=1657245934032150&alt=media)",
      "votes": 1
    },
    {
      "id": 1848711,
      "postDate": "2022-07-08T20:51:56.887Z",
      "content": "<p>Thank you for your informative post! I'm glad you shared your code as well. <br>\nCongrats, fantastic work! </p>",
      "rawMarkdown": "Thank you for your informative post! I'm glad you shared your code as well. \nCongrats, fantastic work! \n"
    },
    {
      "id": 1848349,
      "postDate": "2022-07-08T14:38:48.083Z",
      "content": "<p>Congrats for your 7th place in this competition.</p>",
      "rawMarkdown": "Congrats for your 7th place in this competition."
    },
    {
      "id": 1848280,
      "postDate": "2022-07-08T13:44:53.883Z",
      "content": "<p>Congrats for your 7th place in this competition.</p>\n<p>ありがとうございました  for both versions (japanese/english) write-ups.</p>",
      "rawMarkdown": "Congrats for your 7th place in this competition.\n\nありがとうございました  for both versions (japanese/english) write-ups."
    },
    {
      "id": 1847638,
      "postDate": "2022-07-08T03:20:22.460Z",
      "content": "<p>Congratulations!🎉<br>\nThank you for the detailed solution. I will use it as a reference.</p>",
      "rawMarkdown": "Congratulations!🎉\nThank you for the detailed solution. I will use it as a reference."
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-24T13:20:36Z",
  "competition_id": 35476,
  "competition_slug": "foursquare-location-matching",
  "team_id": 8606950,
  "solution_url": "/competitions/foursquare-location-matching/writeups/in-tokyo-7th-place-solution-with-inference-code"
}