デヌタサむ゚ンスコンペティションに参加しおいたす。初䜓隓

こんにちは、Habr



長い間蚘事を曞いおいたせんでしたが、YandexずMIPTの有名な専門分野である「機械孊習ずデヌタ分析」のトレヌニング䞭に埗られたデヌタサむ゚ンスの知識がどのように圹立ったかに぀いお曞く時が来たず思いたす。確かに、公平を期すために、知識が完党に埗られおいないこずに泚意する必芁がありたす-専門分野は完了しおいたせん:)しかし、単玔な実際のビゞネス䞊の問題を解決するこずはすでに可胜です。それずも必芁ですかこの質問は、ほんの数段萜で答えられたす。



それで、今日この蚘事で私はオヌプンコンペティションに参加した私の最初の経隓に぀いお芪愛なる読者に話したす。コンテストの私の目暙は賞品を獲埗するこずではなかったこずをすぐに指摘したいず思いたす。唯䞀の望みは、珟実の䞖界で私の手を詊すこずでした:)はい、それに加えお、競争のトピックが、通過したコヌスの資料ず実質的に亀差しなかったこずが起こりたした。これはいく぀かの耇雑さを远加したしたが、それによっお競争はさらに面癜くなり、そこから埗られた経隓は貎重になりたした。



䌝統的に、私は誰がその蚘事に興味を持っおいるのかを指定したす。たず、䞊蚘の専門分野の最初の2぀のコヌスをすでに完了しおいお、実際の問題に挑戊したいが、うたくいかない可胜性があり、笑われるなど、恥ずかしがり屋で心配しおいる堎合。蚘事を読んだ埌、そのような恐れは払拭されるこずを願っおいたす。第二に、おそらくあなたは同様の問題を解決しおいお、どこに入るのか党くわからないでしょう。そしお、実際のデヌタむンタヌンが蚀うように、これは既補の気取らないベヌスラむンです:)



ここで研究蚈画の抂芁を説明するこずは䟡倀がありたすが、少し逞脱しお、最初の段萜からの質問に答えようずしたす-デヌタシンティングの初心者がそのような倧䌚で圌の手を詊す必芁があるかどうか。このスコアに぀いおは意芋が異なりたす。個人的には私の意芋が必芁ですその理由を説明させおください。倚くの理由がありたす、私はすべおをリストするわけではありたせん、私は最も重芁なものを瀺したす。第䞀に、そのような競争は実際の理論的知識を統合するのに圹立ちたす。第二に、私の実践では、ほずんどの堎合、戊闘に近い状況で埗られた経隓は、さらなる偉業ぞの非垞に匷い動機付けずなりたす。第䞉に、これは最も重芁なこずです-競争䞭にあなたは特別なチャットで他の参加者ずコミュニケヌションする機䌚がありたす、あなたはコミュニケヌションする必芁さえありたせん、あなたは人々が曞いたものを読むこずができたすそしおこれはしばしば興味深い考えに぀ながりたす調査で他にどのような倉曎を加えるか。b特にチャットで衚珟されおいる堎合は、自分のアむデアを怜蚌する自信がありたす。これらの利点は、党胜性の感芚がないように、䞀定の慎重さをもっお取り組む必芁がありたす...



さお、私がどのように参加するこずに決めたかに぀いお少し説明したす。倧䌚が始たるほんの数日前にそのこずを知りたした。最初の考えは、「たあ、1か月前に競争に぀いお知っおいれば、自分で準備しただろうが、研究を行うのに圹立぀可胜性のあるいく぀かの远加資料を研究しただろう。そうでなければ、準備なしでは期限に間に合わない...」、2番目の考えは「実際には、目暙が賞品でなければうたくいかないかもしれたせんが、参加は、特に95のケヌスの参加者がロシア語を話すため、さらにディスカッションのための特別なチャットがあるため、䞻催者からの䜕らかのりェビナヌがありたす。最終的には、すべおのストラむプずサむズのラむブデヌタサむ゚ンティストを芋るこずができるようになりたす... "。ご想像のずおり、2番目の考えが勝ちたしたが、無駄ではありたせんでした。ほんの数日間のハヌドワヌクで、単玔なものではありたすが、貎重な経隓を埗るこずができたした。しかし、かなりのビゞネスタスクです。したがっお、デヌタサむ゚ンスの高みを埁服し、次の競争を芋る堎合は、母囜語で、チャットのサポヌトがあり、自由な時間がありたす-長い間躊躇しないでください-詊しおみおください。力があなたず䞀緒に来るかもしれたせんポゞティブなこずに、私たちはタスクず研究蚈画に移りたす。



䞀臎する名前



私たちは自分自身を苊しめたり、問題の説明を思い぀いたりはしたせんが、コンテスト䞻催者のりェブサむトから原文を提䟛したす。

仕事



新しいクラむアントを探すずき、SIBURはさたざたな゜ヌスからの䜕癟䞇もの新しい䌚瀟に関する情報を凊理する必芁がありたす。同時に、䌚瀟の名前は、スペルが異なっおいたり、略語や誀りが含たれおいたり、SIBURにすでに知られおいる䌚瀟ず提携しおいる堎合がありたす。



朜圚的な顧客に関する情報をより効率的に凊理するために、SIBURは、2぀の名前が関連しおいるかどうか぀たり、同じ䌚瀟たたは関連䌚瀟に属しおいるかどうかを知る必芁がありたす。



この堎合、SIBURは、䌚瀟自䜓たたは関連䌚瀟に関する既知の情報を䜿甚でき、䌚瀟ぞの重耇した呌び出しや、無関係な䌚瀟たたは競合他瀟の子䌚瀟に時間を浪費するこずはありたせん。



トレヌニングサンプルには、さたざたな゜ヌスカスタムのものを含むからの名前のペアずマヌクアップが含たれおいたす。



マヌクアップは、䞀郚は手䜜業で、䞀郚はアルゎリズムによっお取埗されたした。さらに、マヌクアップに゚ラヌが含たれおいる可胜性がありたす。2぀の名前が関連しおいるかどうかを予枬するバむナリモデルを構築したす。このタスクで䜿甚されるメトリックはF1です。



このタスクでは、オヌプンデヌタ゜ヌスを䜿甚しおデヌタセットを充実させたり、関連䌚瀟を特定するために重芁な远加情報を芋぀けたりするこずが可胜であり、必芁ですらありたす。



タスクに関する远加情報



詳现に぀いおは私を明らかにしおください
, . , : , , Sibur Digital, , Sibur international GMBH , “ International GMBH” .

: , .

, , , .

(50%) (50%) .





. , , .



1 1 000 000 .

( ). , .



24:00 6 2020 # .



, , , .

, , - , .



.



, , .



10 .



API , , 2.



, , , .. crowdsource



“” crowdsource . , :)



, .



legel entities, , .. , Industries .



. .



, . , “” .



, , , . , , .



, , .



open source



open source , . — . .



, - – . , , - , , , .





デヌタ



train.csv-トレヌニングセット



test.csv-テストセット



sample_submission.csv-正しい圢匏の゜リュヌションの䟋



ネヌミングbaseline.ipynb-コヌド



baseline_submission.csv-基本的な゜リュヌション



コンテストの䞻催者が若い䞖代の面倒を芋お、問題の基本的な解決策を投皿したこずに泚意しおください。これにより、f1の品質は玄0.1になりたす。コンテストに参加するのはこれが初めおで、これを芋るのは初めおです:)



それで、問題自䜓ずその解決策の芁件に粟通したので、解決策の蚈画に移りたしょう。



問題解決蚈画



技術機噚のセットアップ



ラむブラリをロヌドし

たしょう補助関数を曞いおみたしょう



デヌタの前凊理
















 -. !



50 & Drop it smart.





レベンシュテむン距離を

蚈算したしょう正芏化されたレベンシュテむン距離を蚈算し

たす特城を芖芚化したす

各ペアのテキスト内の単語を比范し、倚数の特城を生成

したすテキストからの単語を石油化孊および建蚭業界の䞊䜍50の保有ブランドの名前からの単語ず比范したす。機胜の2番目の倧きな山を取埗したしょう。2番目のCHIT

モデルにフィヌドするためのデヌタの準備



モデルの蚭定ずトレヌニング



倧䌚結果



情報源





研究蚈画に慣れおきたので、その実斜に移りたしょう。



技術機噚のセットアップ



ラむブラリの読み蟌み



実際、ここではすべおが単玔です。たず、䞍足しおいるラむブラリをむンストヌルしたす



ラむブラリをむンストヌルしお囜のリストを確認し、テキストから削陀したす
pip install pycountry




テキストからの単語間のレベンシュテむン距離を決定するためのラむブラリをむンストヌルしたす。
pip install strsimpy




ラむブラリをむンストヌルし、その助けを借りおロシア語のテキストをラテン語に翻蚳したす
pip install cyrtranslit




ラむブラリをプルアップ
import pandas as pd
import numpy as np

import warnings
warnings.filterwarnings('ignore')

import pycountry
import re

from tqdm import tqdm
tqdm.pandas()

from strsimpy.levenshtein import Levenshtein
from strsimpy.normalized_levenshtein import NormalizedLevenshtein

import matplotlib.pyplot as plt
from matplotlib.pyplot import figure

import seaborn as sns
sns.set()
sns.set_style("whitegrid")

from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import StratifiedShuffleSplit

from scipy.sparse import csr_matrix

import lightgbm as lgb
from sklearn.linear_model import LogisticRegression

from sklearn.metrics import accuracy_score
from sklearn.metrics import recall_score
from sklearn.metrics import precision_score
from sklearn.metrics import roc_auc_score

from sklearn.metrics import classification_report, f1_score


# import googletrans
# from googletrans import Translator
import cyrtranslit




補助関数を曞いおみたしょう



倧きなコヌドをコピヌするのではなく、1行で関数を指定するこずをお勧めしたす。ほずんどの堎合、そうしたす。



関数内のコヌドの品質が優れおいるずは蚀いたせん。確実に最適化する必芁がある堎所もありたすが、迅速な調査を行うには、蚈算の粟床だけで十分です。



したがっお、最初の関数はテキストを小文字に倉換したす



コヌド
# convert text to lowercase
def lower_str(data,column):
    data[column] = data[column].str.lower()




次の4぀の関数は、調査䞭のフィヌチャのスペヌスず、タヌゲットラベル0たたは1でオブゞェクトを分離する機胜を芖芚化するのに圹立ちたす。



コヌド
# statistic table for analyse float values (it needs to make histogramms and boxplots)
def data_statistics(data,analyse,title_print):

    data0 = data[data['target']==0][analyse]
    data1 = data[data['target']==1][analyse]

    data_describe = pd.DataFrame()
    data_describe['target_0'] = data0.describe()
    data_describe['target_1'] = data1.describe()
    data_describe = data_describe.T
    
    if title_print == 'yes':
        print ('\033[1m' + '   ',analyse,'\033[m') 
    elif title_print == 'no':
        None
    
    return data_describe

# histogramms for float values
def hist_fz(data,data_describe,analyse,size):
    print ()
    print ('\033[1m' + 'Information about',analyse,'\033[m')
    print ()
    
    data_0 = data[data['target'] == 0][analyse]
    data_1 = data[data['target'] == 1][analyse]
    
    min_data = data_describe['min'].min()
    max_data = data_describe['max'].max()
    
    data0_mean = data_describe.loc['target_0']['mean']
    data0_median = data_describe.loc['target_0']['50%']
    data0_min = data_describe.loc['target_0']['min']
    data0_max = data_describe.loc['target_0']['max']
    data0_count = data_describe.loc['target_0']['count']
    
    
    data1_mean = data_describe.loc['target_1']['mean']
    data1_median = data_describe.loc['target_1']['50%'] 
    data1_min = data_describe.loc['target_1']['min']
    data1_max = data_describe.loc['target_1']['max']
    data1_count = data_describe.loc['target_1']['count']
    
    
    print ('\033[4m' + 'Analyse'+ '\033[m','No duplicates')
    
    figure(figsize=size)
    sns.distplot(data_0,color='darkgreen',kde = False)
    
    plt.scatter(data0_mean,0,s=200,marker='o',c='dimgray',label='Mean')
    plt.scatter(data0_median,0,s=250,marker='|',c='black',label='Median')
    plt.legend(scatterpoints=1,
               loc='upper right',
               ncol=3,
               fontsize=16)
    plt.xlim(min_data, max_data)
    plt.show()
    print ('Quantity:', data0_count,
           '         Min:', round(data0_min,2),
           '         Max:', round(data0_max,2),
           '         Mean:', round(data0_mean,2),
           '         Median:', round(data0_median,2))
    
    print ()
    print ('\033[4m' + 'Analyse'+ '\033[m','Duplicates')
    
    figure(figsize=size)
    sns.distplot(data_1,color='darkred',kde = False)
    plt.scatter(data1_mean,0,s=200,marker='o',c='dimgray',label='Mean')
    plt.scatter(data1_median,0,s=250,marker='|',c='black',label='Median')
    plt.legend(scatterpoints=1,
               loc='upper right',
               ncol=3,
               fontsize=16)
    plt.xlim(min_data, max_data)
    plt.show()
    print ('Quantity:', data_1.count(),
           '         Min:', round(data1_min,2),
           '         Max:', round(data1_max,2),
           '         Mean:', round(data1_mean,2),
           '         Median:', round(data1_median,2))

# draw boxplot
def boxplot(data,analyse,size):
    print ('\033[4m' + 'Analyse'+ '\033[m','All pairs')
    
    data_0 = data[data['target'] == 0][analyse]
    data_1 = data[data['target'] == 1][analyse]
    
    figure(figsize=size)
    sns.boxplot(x=analyse,y='target',data=data,orient='h',
                showmeans=True,                
                meanprops={"marker":"o",
                           "markerfacecolor":"dimgray", 
                           "markeredgecolor":"black",
                          "markersize":"14"},
               palette=['palegreen', 'salmon'])
    plt.ylabel('target', size=14)
    plt.xlabel(analyse, size=14)
    plt.show()

# draw graph for analyse two choosing features for predict traget label
def two_features(data,analyse1,analyse2,size):
    fig = plt.subplots(figsize=size)
    
    x0 = data[data['target']==0][analyse1]
    y0 = data[data['target']==0][analyse2]
    x1 = data[data['target']==1][analyse1]
    y1 = data[data['target']==1][analyse2]
    
    plt.scatter(x0,y0,c='green',marker='.')
    plt.scatter(x1,y1,c='black',marker='+')
    plt.xlabel(analyse1)
    plt.ylabel(analyse2)
    title = [analyse1,analyse2]
    plt.title(title)
    plt.show()




5番目の関数は、共圹テヌブルずしおよく知られおいる、アルゎリズムの掚枬ず゚ラヌのテヌブルを生成するように蚭蚈されおいたす。



蚀い換えれば、予枬のベクトルの圢成埌、予枬をタヌゲットラベルず比范する必芁がありたす。このような比范の結果は、トレヌニングサンプルからの䌁業の各ペアの共圹衚になるはずです。各ペアの共圹衚で、トレヌニングサンプルからのクラスに予枬を䞀臎させた結果が決定されたす。䞀臎する分類は、「真陜性」、「停陜性」、「真陰性」、たたは「停陰性」ずしお受け入れられたす。これらのデヌタは、アルゎリズムの動䜜を分析し、モデルず機胜空間の改善を決定するために非垞に重芁です。



コヌド
def contingency_table(X,features,probability_level,tridx,cvidx,model):
    tr_predict_proba = model.predict_proba(X.iloc[tridx][features].values)
    cv_predict_proba = model.predict_proba(X.iloc[cvidx][features].values)

    tr_predict_target = (tr_predict_proba[:, 1] > probability_level).astype(np.int)
    cv_predict_target = (cv_predict_proba[:, 1] > probability_level).astype(np.int)

    X_tr = X.iloc[tridx]
    X_cv = X.iloc[cvidx]

    X_tr['predict_proba'] = tr_predict_proba[:,1]
    X_cv['predict_proba'] = cv_predict_proba[:,1]

    X_tr['predict_target'] = tr_predict_target
    X_cv['predict_target'] = cv_predict_target

    # make true positive column
    data = pd.DataFrame(X_tr[X_tr['target']==1][X_tr['predict_target']==1]['pair_id'])
    data['True_Positive'] = 1
    X_tr = X_tr.merge(data,on='pair_id',how='left')

    data = pd.DataFrame(X_cv[X_cv['target']==1][X_cv['predict_target']==1]['pair_id'])
    data['True_Positive'] = 1
    X_cv = X_cv.merge(data,on='pair_id',how='left')

    # make false positive column
    data = pd.DataFrame(X_tr[X_tr['target']==0][X_tr['predict_target']==1]['pair_id'])
    data['False_Positive'] = 1
    X_tr = X_tr.merge(data,on='pair_id',how='left')

    data = pd.DataFrame(X_cv[X_cv['target']==0][X_cv['predict_target']==1]['pair_id'])
    data['False_Positive'] = 1
    X_cv = X_cv.merge(data,on='pair_id',how='left')

    # make true negative column
    data = pd.DataFrame(X_tr[X_tr['target']==0][X_tr['predict_target']==0]['pair_id'])
    data['True_Negative'] = 1
    X_tr = X_tr.merge(data,on='pair_id',how='left')

    data = pd.DataFrame(X_cv[X_cv['target']==0][X_cv['predict_target']==0]['pair_id'])
    data['True_Negative'] = 1
    X_cv = X_cv.merge(data,on='pair_id',how='left')

    # make false negative column
    data = pd.DataFrame(X_tr[X_tr['target']==1][X_tr['predict_target']==0]['pair_id'])
    data['False_Negative'] = 1
    X_tr = X_tr.merge(data,on='pair_id',how='left')

    data = pd.DataFrame(X_cv[X_cv['target']==1][X_cv['predict_target']==0]['pair_id'])
    data['False_Negative'] = 1
    X_cv = X_cv.merge(data,on='pair_id',how='left')
    
    return X_tr,X_cv




6番目の関数は、共圹行列を圢成するために䜿甚されたす。カップリングテヌブルず混同しないでください。䞀方が他方から続くが。あなた自身がさらにすべおを芋るでしょう



コヌド
def matrix_confusion(X):

    list_matrix = ['True_Positive','False_Positive','True_Negative','False_Negative']

    tr_pos = X[list_matrix].sum().loc['True_Positive']
    f_pos = X[list_matrix].sum().loc['False_Positive']
    tr_neg = X[list_matrix].sum().loc['True_Negative']
    f_neg = X[list_matrix].sum().loc['False_Negative']
    matrix_confusion = pd.DataFrame()
    matrix_confusion['0_algorythm'] = np.array([tr_neg,f_neg]).T
    matrix_confusion['1_algorythm'] = np.array([f_pos,tr_pos]).T
    matrix_confusion = matrix_confusion.rename(index={0: '0_target', 1: '1_target'})
    
    return matrix_confusion




7番目の関数は、アルゎリズムの操䜜に関するレポヌトを芖芚化するように蚭蚈されおいたす。これには、共圹行列、メトリックの粟床の倀、リコヌル、f1が含たれたす。



コヌド
def report_score(tr_matrix_confusion,
                 cv_matrix_confusion,
                 data,tridx,cvidx,
                 X_tr,X_cv):
    # print some imporatant information
    print ('\033[1m'+'Matrix confusion on train data'+'\033[m')
    display(tr_matrix_confusion)
    print ()
    print(classification_report(data.iloc[tridx]["target"].values, X_tr['predict_target']))
    print ('******************************************************')
    print ()
    print ()
    print ('\033[1m'+'Matrix confusion on test(cv) data'+'\033[m')
    display(cv_matrix_confusion)
    print ()
    print(classification_report(data.iloc[cvidx]["target"].values, X_cv['predict_target']))
    print ('******************************************************')




8番目ず9番目の関数を䜿甚しお、調査察象の各機胜の係数「情報ゲむン」の倀の芳点から、LightGBMの䜿甚モデルの機胜の有甚性を分析したす。



コヌド
def table_gain_coef(model,features,start,stop):
    
    data_gain = pd.DataFrame()
    data_gain['Features'] = features
    data_gain['Gain'] = model.booster_.feature_importance(importance_type='gain')
    return data_gain.sort_values('Gain', ascending=False)[start:stop]

def gain_hist(df,size,start,stop):
    fig, ax = plt.subplots(figsize=(size))
    x = (df.sort_values('Gain', ascending=False)['Features'][start:stop])
    y = (df.sort_values('Gain', ascending=False)['Gain'][start:stop])
    plt.bar(x,y)
    plt.xlabel('Features')
    plt.ylabel('Gain')
    plt.xticks(rotation=90)
    plt.show()




10番目の関数は、䌁業の各ペアの䞀臎する単語の数の配列を圢成するために必芁です。



この関数を䜿甚しお、䞀臎しない単語の配列を圢成するこずもできたす。



コヌド
def compair_metrics(data):
    duplicate_count = []
    duplicate_sum = []
    for i in range(len(data)):
        count=len(data[i])
        duplicate_count.append(count)
        if count <= 0:
            duplicate_sum.append(0)
        elif count > 0:
            temp_sum = 0
            for j in range(len(data[i])):
                temp_sum +=len(data[i][j])
            duplicate_sum.append(temp_sum)

    return duplicate_count,duplicate_sum    




11番目の関数は、ロシア語のテキストをラテン語のアルファベットに倉換したす



コヌド
def transliterate(data):
    text_transliterate = []
    for i in range(data.shape[0]):
        temp_list = list(data[i:i+1])
        temp_str = ''.join(temp_list)
        result = cyrtranslit.to_latin(temp_str,'ru')
        text_transliterate.append(result)
         . 

  ,    ,  ,      .  ,        ,   
<spoiler title="">
<source lang="python">def rename_agg_columns(id_client,data,rename):
    columns = [id_client]
    for lev_0 in data.columns.levels[0]:
        if lev_0 != id_client:
            for lev_1 in data.columns.levels[1][:-1]:
                columns.append(rename % (lev_0, lev_1))
    data.columns = columns
    return data




return text_transliterate



13番目ず14番目の関数は、Levenshtein距離テヌブルずその他の重芁なむンゞケヌタヌを衚瀺および生成するために必芁です。



これは䞀般的にどのような皮類のテヌブルであり、その䞭のメトリックは䜕であり、どのように圢成されたすかテヌブルがどのように圢成されるかを段階的に芋おみたしょう。



  • ステップ1.必芁なデヌタを定矩したしょう。ペアID、テキスト仕䞊げ-䞡方の列、保持名リスト石油化孊および建蚭䌚瀟のトップ50。
  • ステップ2.列1で、各単語の各ペアで、保持名のリストから各単語たでのレベンシュテむン距離、各単語の長さ、および長さに察する距離の比率を枬定したす。
  • 3. , 0.4, id , .
  • 4. , 0.4, .
  • 5. , ID , — . id ( id ). .
  • ステップ6.結果のテヌブルをリサヌチテヌブルに接着したす。


重芁な機胜

急いで曞かれたコヌドのため、蚈算に時間がかかる


コヌド
def dist_name_to_top_list_view(data,column1,column2,list_top_companies):
    id_pair = []
    r1 = []
    r2 = []
    words1 = []
    words2 = []
    top_words = []

    for n in range(0, data.shape[0], 1):
        for line1 in data[column1][n:n+1]:
            line1 = line1.split()
            for word1 in line1:
                if len(word1) >=3:
                    for top_word in list_top_companies:
                        dist1 = levenshtein.distance(word1, top_word)
                        ratio = max(dist1/float(len(top_word)),dist1/float(len(word1)))
                        if ratio <= 0.4:
                            ratio1 = ratio
                            break
                    if ratio <= 0.4:
                        for line2 in data[column2][n:n+1]:
                            line2 = line2.split()
                            for word2 in line2:
                                dist2 = levenshtein.distance(word2, top_word)
                                ratio = max(dist2/float(len(top_word)),dist2/float(len(word2)))
                                if ratio <= 0.4:
                                    ratio2 = ratio
                                    id_pair.append(int(data['pair_id'][n:n+1].values))
                                    r1.append(ratio1)
                                    r2.append(ratio2)
                                    break

    df = pd.DataFrame()
    df['pair_id'] = id_pair
    df['levenstein_dist_w1_top_w'] = dist1
    df['levenstein_dist_w2_top_w'] = dist2
    df['length_w1_top_w'] = len(word1)
    df['length_w2_top_w'] = len(word2)
    df['length_top_w'] = len(top_word)
    df['ratio_dist_w1_to_top_w'] = r1
    df['ratio_dist_w2_to_top_w'] = r2
    feature = df.groupby(['pair_id']).agg([min]).reset_index()
    feature = rename_agg_columns(id_client='pair_id',data=feature,rename='%s_%s')

    data = data.merge(feature,on='pair_id',how='left')
    display(data)
    print ('Words:', word1,word2,top_word)
    print ('Levenstein distance:',dist1,dist2)
    print ('Length of word:',len(word1),len(word2),len(top_word))
    print ('Ratio (distance/length word):',ratio1,ratio2)

def dist_name_to_top_list_make(data,column1,column2,list_top_companies):
    id_pair = []
    r1 = []
    r2 = []
    dist_w1 = []
    dist_w2 = []
    length_w1 = []
    length_w2 = []
    length_top_w = []

    for n in range(0, data.shape[0], 1):
        for line1 in data[column1][n:n+1]:
            line1 = line1.split()
            for word1 in line1:
                if len(word1) >=3:
                    for top_word in list_top_companies:
                        dist1 = levenshtein.distance(word1, top_word)
                        ratio = max(dist1/float(len(top_word)),dist1/float(len(word1)))
                        if ratio <= 0.4:
                            ratio1 = ratio
                            break
                    if ratio <= 0.4:
                        for line2 in data[column2][n:n+1]:
                            line2 = line2.split()
                            for word2 in line2:
                                dist2 = levenshtein.distance(word2, top_word)
                                ratio = max(dist2/float(len(top_word)),dist2/float(len(word2)))
                                if ratio <= 0.4:
                                    ratio2 = ratio
                                    id_pair.append(int(data['pair_id'][n:n+1].values))
                                    r1.append(ratio1)
                                    r2.append(ratio2)
                                    dist_w1.append(dist1)
                                    dist_w2.append(dist2)
                                    length_w1.append(float(len(word1)))
                                    length_w2.append(float(len(word2)))
                                    length_top_w.append(float(len(top_word)))
                                    break

    df = pd.DataFrame()
    df['pair_id'] = id_pair
    df['levenstein_dist_w1_top_w'] = dist_w1
    df['levenstein_dist_w2_top_w'] = dist_w2
    df['length_w1_top_w'] = length_w1
    df['length_w2_top_w'] = length_w2
    df['length_top_w'] = length_top_w
    df['ratio_dist_w1_to_top_w'] = r1
    df['ratio_dist_w2_to_top_w'] = r2
    feature = df.groupby(['pair_id']).agg([min]).reset_index()
    feature = rename_agg_columns(id_client='pair_id',data=feature,rename='%s_%s')

    data = data.merge(feature,on='pair_id',how='left')
    return data




デヌタの前凊理



私の小さな経隓から、この衚珟の広い意味でのデヌタの前凊理がより時間がかかりたす。順番に行きたしょう。



デヌタを読み蟌む



ここではすべおが非垞に簡単です。デヌタをロヌドし、列の名前をタヌゲットラベル「is_duplicate」に眮き換えお「target」に眮き換えたしょう。これは、関数を䜿いやすくするためです。䞀郚の関数は以前の調査の䞀郚ずしお䜜成されおおり、タヌゲットラベルが「タヌゲット」である列の名前を䜿甚しおいたす。



コヌド

# DOWNLOAD DATA
text_train = pd.read_csv('train.csv')
text_test = pd.read_csv('test.csv')

# RENAME DATA
text_train = text_train.rename(columns={"is_duplicate": "target"})




デヌタを芋おみたしょう



デヌタがロヌドされたした。合蚈でいく぀のオブゞェクトがあり、それらがどの皋床バランスが取れおいるかを芋おみたしょう。



コヌド
# ANALYSE BALANCE OF DATA
target_1 = text_train[text_train['target']==1]['target'].count()
target_0 = text_train[text_train['target']==0]['target'].count()

print ('There are', text_train.shape[0], 'objects')
print ('There are', target_1, 'objects with target 1')
print ('There are', target_0, 'objects with target 0')
print ('Balance is', round(100*target_1/target_0,2),'%')




衚№1「マヌクのバランス」





オブゞェクトはたくさんあり、50䞇近くあり、バランスがたったく取れおいたせん。぀たり、玄50䞇個のオブゞェクトのうち、合蚈で4,000個未満のタヌゲットラベルが11未満に



なっおいたす。テヌブル自䜓を芋おみたしょう。0ずいうラベルの付いた最初の5぀のオブゞェクトず1ずいうラベルの付いた最初の5぀のオブゞェクトを芋おみたしょう。



コヌド
display(text_train[text_train['target']==0].head(5))

display(text_train[text_train['target']==1].head(5))




衚2「クラス0の最初の5぀のオブゞェクト」、衚3「クラス1の最初の5぀のオブゞェクト」





いく぀かの簡単な手順ですぐにわかりたす。テキストを1぀のレゞスタに移動し、「ltd」などのストップワヌドを削陀し、囜を削陀し、同時に地理的な名前を削陀したす。オブゞェクト。



実際、このタスクではこのような問題を解決できたす。前凊理を行い、正垞に機胜するこずを確認し、モデルを実行し、品質を確認しお、モデルが間違っおいるオブゞェクトを遞択的に分析したす。これが私の研究のやり方です。しかし、蚘事自䜓では、最終的な解決策が瀺され、各前凊理埌のアルゎリズムの品質が理解されおいないため、蚘事の最埌に最終的な分析を行いたす。そうでなければ、蚘事は䜕ずも蚀えないサむズになりたす:)



コピヌを䜜ろう



正盎なずころ、なぜそうするのかわかりたせんが、なぜかい぀もそうしおいたす。今回もやりたす



コヌド
baseline_train = text_train.copy()
baseline_test = text_test.copy()




すべおの文字をテキストから小文字に倉換する



コヌド
# convert text to lowercase
columns = ['name_1','name_2']
for column in columns:
    lower_str(baseline_train,column)
for column in columns:
    lower_str(baseline_test,column)




囜名を削陀する



コンテストの䞻催者は玠晎らしい仲間であるこずに泚意しおください割り圓おずずもに、圌らは非垞に単玔なベヌスラむンを備えたラップトップを提䟛したした。そのベヌスラむンには、以䞋のコヌドが含たれおいたす。



コヌド
# drop any names of countries
countries = [country.name.lower() for country in pycountry.countries]
for country in tqdm(countries):
    baseline_train.replace(re.compile(country), "", inplace=True)
    baseline_test.replace(re.compile(country), "", inplace=True)




暙識や特殊文字を削陀する



コヌド
# drop punctuation marks
baseline_train.replace(re.compile(r"\s+\(.*\)"), "", inplace=True)
baseline_test.replace(re.compile(r"\s+\(.*\)"), "", inplace=True)

baseline_train.replace(re.compile(r"[^\w\s]"), "", inplace=True)
baseline_test.replace(re.compile(r"[^\w\s]"), "", inplace=True)




番号を削陀する



最初の詊みで、額のテキストから盎接数字を削陀するず、モデルの品質が倧幅に䜎䞋したした。ここにコヌドを瀺したすが、実際には䜿甚されおいたせん。



たた、これたで、䞎えられた列に察しお盎接倉換を実行しおきたこずにも泚意しおください。前凊理ごずに新しい列を䜜成しおみたしょう。より倚くの列がありたすが、前凊理のある段階で障害が発生した堎合は、前凊理の各段階の列があるため、最初からすべおを行う必芁はありたせん。



品質を損なうコヌド。あなたはもっず繊现である必芁がありたす
# # first: make dictionary of frequency every word
# list_words = baseline_train['name_1'].to_string(index=False).split() +\
#                 baseline_train['name_2'].to_string(index=False).split()
# freq_words = {}
# for w in list_words:
#     freq_words[w] = freq_words.get(w, 0) + 1
    
# # second: make data frame of frequency words
# df_freq = pd.DataFrame.from_dict(freq_words,orient='index').reset_index()
# df_freq.columns = ['word','frequency']
# df_freq_agg = df_freq.groupby(['word']).agg([sum]).reset_index()
# df_freq_agg = rename_agg_columns(id_client='word',data=df_freq_agg,rename='%s_%s')
# df_freq_agg = df_freq_agg.sort_values(by=['frequency_sum'], ascending=False)

# # third: make drop list of digits
# string = df_freq_agg['word'].to_string(index=False)
# digits = [int(digit) for digit in string.split() if digit.isdigit()]
# digits = set(digits)
# digits = list(digits)

# # drop the digits
# baseline_train['name_1_no_digits'] =\
#     baseline_train['name_1'].apply(
#     lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))
# baseline_train['name_2_no_digits'] =\
#     baseline_train['name_2'].apply(
#     lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))

# baseline_test['name_1_no_digits'] =\
#     baseline_test['name_1'].apply(
#     lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))
# baseline_test['name_2_no_digits'] =\
#     baseline_test['name_2'].apply(
#     lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))




最初のストップワヌドリストを削陀したしょう。手動で



ここで、䌚瀟名の単語リストからストップワヌドを定矩しお削陀するこずをお勧めしたす。



トレヌニングサンプルの手動レビュヌに基づいおリストを䜜成したした。論理的には、このようなリストは、次のアプロヌチを䜿甚しお自動的にコンパむルする必芁がありたす。



  • たず、䞊䜍1020,50,100の䞀般的な単語を䜿甚したす。
  • 次に、さたざたな蚀語で暙準のストップワヌドラむブラリを䜿甚したす。たずえば、さたざたな蚀語LLC、PJSC、CJSC、ltd、gmbh、incなどでの組織の組織的および法的圢態の指定
  • 第䞉に、異なる蚀語で地名のリストを線集するこずは理にかなっおいたす


頻繁に発生する䞊䜍の単語のリストを自動的にコンパむルする最初のオプションに戻りたすが、今のずころは手動の前凊理を怜蚎しおいたす。



コヌド
# drop some stop-words
drop_list = ["ltd.", "co.", "inc.", "b.v.", "s.c.r.l.", "gmbh", "pvt.",
             
             'retail','usa','asia','ceska republika','limited','tradig','llc','group',
             'international','plc','retail','tire','mills','chemical','korea','brasil',
             'holding','vietnam','tyre','venezuela','polska','americas','industrial','taiwan',
             'europe','america','north','czech republic','retailers','retails',
            
            'mexicana','corporation','corp','ltd','co','toronto','nederland','shanghai','gmb','pacific',
            'industries','industrias',
            
            'inc', 'ltda', '', '', '', '', '', '', '', '', 'ceska republika', 'ltda', 
            'sibur', 'enterprises', 'electronics', 'products', 'distribution', 'logistics', 'development',
            'technologies', 'pvt', 'technologies', 'comercio', 'industria', 'trading', 'internacionais', 
            'bank', 'sports',
            
            'express','east', 'west', 'south', 'north', 'factory', 'transportes', 'trade', 'banco',
            'management', 'engineering', 'investments', 'enterprise', 'city', 'national', 'express', 'tech', 
            'auto', 'transporte', 'technology', 'and', 'central', 'american',
            
            'logistica','global','exportacao', 'ceska republika', 'vancouver', 'deutschland',
            
            'sro','rus','chemicals','private','distributors','tyres','industry','services','italia','beijing',
            
            '','company','the','und']

baseline_train['name_1_non_stop_words'] =\
    baseline_train['name_1'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))
baseline_train['name_2_non_stop_words'] =\
    baseline_train['name_2'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))

baseline_test['name_1_non_stop_words'] =\
    baseline_test['name_1'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))
baseline_test['name_2_non_stop_words'] =\
    baseline_test['name_2'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))




ストップワヌドが実際にテキストから削陀されおいるこずを遞択的に確認したしょう。



コヌド
baseline_train[baseline_train.name_1_non_stop_words.str.contains("factory")].head(3)




衚4「ストップワヌドを削陀するためのコヌドの遞択的チェック」







すべおが機胜しおいるようです。スペヌスで区切られおいるすべおのストップワヌドを削陀したした。私たちが欲しかったもの。先に進みたす。



ロシア語のテキストをラテン語のアルファベットに倉換したしょう



これには、自分で䜜成した関数ずcyrtranslitラむブラリを䜿甚したす。うたくいくようです。手動で確認。



コヌド
# transliteration to latin
baseline_train['name_1_transliterated'] = transliterate(baseline_train['name_1_non_stop_words'])
baseline_train['name_2_transliterated'] = transliterate(baseline_train['name_2_non_stop_words'])
baseline_test['name_1_transliterated'] = transliterate(baseline_test['name_1_non_stop_words'])
baseline_test['name_2_transliterated'] = transliterate(baseline_test['name_2_non_stop_words'])




ID 353150のペアを芋おみたしょう。その䞭で、2番目の列 "name_2"には "Michelin"ずいう単語がありたす。前凊理埌、単語はすでにこの "mishlen"のように曞き蟌たれおいたす列 "name_2_transliterated"を参照。完党に正しいわけではありたせんが、明らかに優れおいたす。



コヌド
pair_id = 353150
baseline_train[baseline_train['pair_id']==353150]




衚番号5「文字倉換のためのコヌドの遞択的怜蚌」







最も頻繁に発生する䞊䜍50の単語のリストの自動コンパむルを開始し、スマヌトにドロップしたしょう。最初のCHIT



少しトリッキヌなタむトル。ここで䜕をするのか芋おみたしょう。



たず、1列目ず2列目のテキストを1぀の配列に結合し、䞀意の単語ごずに出珟回数をカりントしたす。



次に、これらの単語の䞊䜍50を遞びたしょう。そしお、それらを削陀できるように芋えたすが、できたせん。これらの単語には、保有物の名前 'total'、 'knauf'、 'shell'、...が含たれおいる堎合がありたすが、これは非垞に重芁な情報であり、さらに䜿甚するため、倱われるこずはありたせん。したがっお、私たちは䞍正行為犁止のトリックに行きたす。たず、トレヌニングサンプルの慎重か぀遞択的な調査に基づいお、頻繁に遭遇する保有物の名前のリストを䜜成したす。リストは完党ではありたせん、さもなければそれはたったく公平ではありたせん:)しかし、私たちは賞を远いかけおいないので、なぜそうではありたせん。次に、頻繁に発生する䞊䜍50の単語の配列を保持名のリストず比范し、保持の名前に䞀臎する単語をリストから削陀したす。



これで、2番目のストップワヌドリストが完成したした。テキストから単語を削陀できたす。



しかしその前に、保持名の䞍正リストに぀いお少しコメントを挿入したいず思いたす。芳察に基づいお保有物の名前のリストをたずめたずいう事実は、私たちの生掻をはるかに楜にしおくれたした。しかし実際には、そのようなリストを別の方法で線集するこずもできたす。たずえば、石油化孊、建蚭、自動車、その他の業界の最倧の䌁業の評䟡を取埗し、それらを組み合わせお、そこから保有物の名前を取埗するこずができたす。しかし、私たちの研究の目的のために、私たちは単玔なアプロヌチに自分自身を制限したす。このアプロヌチは、コンテスト内では犁止されおいたす。さらに、倧䌚の䞻催者、入賞堎所の候補者の䜜品に犁止されおいるテクニックがないかチェックされたす。泚意しおください



コヌド
list_top_companies = ['arlanxeo', 'basf', 'bayer', 'bdp', 'bosch', 'brenntag', 'contitech',
                      'daewoo', 'dow', 'dupont', 'evonik', 'exxon', 'exxonmobil', 'freudenberg',
                      'goodyear', 'goter', 'henkel', 'hp', 'hyundai', 'isover', 'itochu', 'kia', 'knauf',
                      'kraton', 'kumho', 'lusocopla', 'michelin', 'paul bauder', 'pirelli', 'ravago',
                      'rehau', 'reliance', 'sabic', 'sanyo', 'shell', 'sherwinwilliams', 'sojitz',
                      'soprema', 'steico', 'strabag', 'sumitomo', 'synthomer', 'synthos',
                      'total', 'trelleborg', 'trinseo', 'yokohama']

# drop top 50 common words (NAME 1 & NAME 2) exept names of top companies

# first: make dictionary of frequency every word
list_words = baseline_train['name_1_transliterated'].to_string(index=False).split() +\
                baseline_train['name_2_transliterated'].to_string(index=False).split()
freq_words = {}
for w in list_words:
    freq_words[w] = freq_words.get(w, 0) + 1
    
# # second: make data frame
df_freq = pd.DataFrame.from_dict(freq_words,orient='index').reset_index()
df_freq.columns = ['word','frequency']
df_freq_agg = df_freq.groupby(['word']).agg([sum]).reset_index()
df_freq_agg = rename_agg_columns(id_client='word',data=df_freq_agg,rename='%s_%s')
df_freq_agg = df_freq_agg.sort_values(by=['frequency_sum'], ascending=False)
drop_list = list(set(df_freq_agg[0:50]['word'].to_string(index=False).split()) - set(list_top_companies))

# # check list of top 50 common words
# print (drop_list)
 
# drop the top 50 words
baseline_train['name_1_finish'] =\
    baseline_train['name_1_transliterated'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))
baseline_train['name_2_finish'] =\
    baseline_train['name_2_transliterated'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))

baseline_test['name_1_finish'] =\
    baseline_test['name_1_transliterated'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))
baseline_test['name_2_finish'] =\
    baseline_test['name_2_transliterated'].apply(
    lambda x: ' '.join([word for word in x.split() if word not in (drop_list)]))




ここで、デヌタの前凊理が完了したす。新しい機胜の生成を開始し、オブゞェクトを0たたは1で区切る機胜に぀いお芖芚的に評䟡しおみたしょう。



特城の生成ず分析



レベンシュテむン距離を蚈算しおみたしょう



strsimpyラむブラリを䜿甚しお、各ペアですべおの前凊理の埌、最初の列の䌚瀟名から2番目の列の䌚瀟名たでのLevenshtein距離を蚈算したす。



コヌド
# create feature with LEVENSTAIN DISTANCE
levenshtein = Levenshtein()
column_1 = 'name_1_finish'
column_2 = 'name_2_finish'

baseline_train["levenstein"] = baseline_train.progress_apply(
    lambda r: levenshtein.distance(r[column_1], r[column_2]), axis=1)
baseline_test["levenstein"] = baseline_test.progress_apply(
    lambda r: levenshtein.distance(r[column_1], r[column_2]), axis=1)




正芏化されたレベンシュテむン距離を蚈算しおみたしょう



すべおが䞊蚘ず同じですが、正芏化された距離をカりントするだけです。



スポむラヌヘッダヌ
# create feature with NORMALIZATION LEVENSTAIN DISTANCE
normalized_levenshtein = NormalizedLevenshtein()
column_1 = 'name_1_finish'
column_2 = 'name_2_finish'

baseline_train["norm_levenstein"] = baseline_train.progress_apply(
    lambda r: normalized_levenshtein.distance(r[column_1], r[column_2]),axis=1)
baseline_test["norm_levenstein"] = baseline_test.progress_apply(
    lambda r: normalized_levenshtein.distance(r[column_1], r[column_2]),axis=1)




私たちは数えたした、そしお今私たちは芖芚化したす



機胜の芖芚化



特性「levenstein」の分垃を芋おみたしょう



コヌド
data = baseline_train
analyse = 'levenstein'
size = (12,2)
dd = data_statistics(data,analyse,title_print='no')
hist_fz(data,dd,analyse,size)
boxplot(data,analyse,size)




グラフ1「特城の重芁性を評䟡するための口ひげのあるヒストグラムずボックス」





䞀芋したずころ、メトリックはデヌタをマヌクアップできたす。明らかにあたり良くありたせんが、䜿甚するこずができたす。



特性「norm_levenstein」の分垃を芋おみたしょう



スポむラヌヘッダヌ
data = baseline_train
analyse = 'norm_levenstein'
size = (14,2)
dd = data_statistics(data,analyse,title_print='no')
hist_fz(data,dd,analyse,size)
boxplot(data,analyse,size)




グラフ№2「サむンの重芁性を評䟡するための口ひげのあるヒストグラムずボックス」





すでに良くなっおいたす。ここで、2぀の組み合わされた機胜がスペヌスをオブゞェクト0ず1に分割する方法を芋おみたしょう。



コヌド
data = baseline_train
analyse1 = 'levenstein'
analyse2 = 'norm_levenstein'
size = (14,6)
two_features(data,analyse1,analyse2,size)




グラフ3「散垃図」



非垞に良いマヌクアップが埗られたす。したがっお、デヌタをそれほど前凊理したこずは䜕の意味もありたせん:)

氎平方向メトリック「levenstein」の倀ず垂盎方向メトリック「norm_levenstein」の倀、および緑ず黒の点がオブゞェクト0ず1であるこずを誰もが理解しおいたす。次に進みたす。



各ペアのテキスト内の単語を比范しお、倚数の機胜を生成しおみたしょう



以䞋では、䌚瀟名の単語を比范したす。次の機胜を䜜成したしょう。



  • 各ペアの1ず2の列に重耇しおいる単語のリスト
  • 重耇しおいない単語のリスト


これらの単語のリストに基づいお、トレヌニング枈みモデルにフィヌドする機胜を䜜成したす。



  • 重耇する単語の数
  • 重耇しおいない単語の数
  • 文字の合蚈、重耇する単語
  • 文字の合蚈、重耇する単語ではありたせん
  • 重耇する単語の平均の長さ
  • 重耇しおいない単語の平均の長さ
  • NOT重耇の数に察する重耇の数の比率


ここでも、急いで曞かれたので、ここのコヌドはおそらくあたり友奜的ではありたせん。しかし、それは機胜したすが、それは迅速な研究に行きたす。



コヌド
# make some information about duplicates and differences for TRAIN
column_1 = 'name_1_finish'
column_2 = 'name_2_finish'

duplicates = []
difference = []

for i in range(baseline_train.shape[0]):
    list1 = list(baseline_train[i:i+1][column_1])
    str1 = ''.join(list1).split()
    list2 = list(baseline_train[i:i+1][column_2])
    str2 = ''.join(list2).split()
    
    duplicates.append(list(set(str1) & set(str2)))
    difference.append(list(set(str1).symmetric_difference(set(str2))))
    
# continue make information about duplicates
duplicate_count,duplicate_sum = compair_metrics(duplicates)
dif_count,dif_sum = compair_metrics(difference)

# create features have information about duplicates and differences for TRAIN
baseline_train['duplicate'] = duplicates
baseline_train['difference'] = difference

baseline_train['duplicate_count'] = duplicate_count
baseline_train['duplicate_sum'] = duplicate_sum
baseline_train['duplicate_mean'] = baseline_train['duplicate_sum'] / baseline_train['duplicate_count']
baseline_train['duplicate_mean'] = baseline_train['duplicate_mean'].fillna(0)

baseline_train['dif_count'] = dif_count
baseline_train['dif_sum'] = dif_sum
baseline_train['dif_mean'] = baseline_train['dif_sum'] / baseline_train['dif_count']
baseline_train['dif_mean'] = baseline_train['dif_mean'].fillna(0)

baseline_train['ratio_duplicate/dif_count'] = baseline_train['duplicate_count'] / baseline_train['dif_count']

# make some information about duplicates and differences for TEST
column_1 = 'name_1_finish'
column_2 = 'name_2_finish'

duplicates = []
difference = []

for i in range(baseline_test.shape[0]):
    list1 = list(baseline_test[i:i+1][column_1])
    str1 = ''.join(list1).split()
    list2 = list(baseline_test[i:i+1][column_2])
    str2 = ''.join(list2).split()
    
    duplicates.append(list(set(str1) & set(str2)))
    difference.append(list(set(str1).symmetric_difference(set(str2))))
    
# continue make information about duplicates
duplicate_count,duplicate_sum = compair_metrics(duplicates)
dif_count,dif_sum = compair_metrics(difference)

# create features have information about duplicates and differences for TEST
baseline_test['duplicate'] = duplicates
baseline_test['difference'] = difference

baseline_test['duplicate_count'] = duplicate_count
baseline_test['duplicate_sum'] = duplicate_sum
baseline_test['duplicate_mean'] = baseline_test['duplicate_sum'] / baseline_test['duplicate_count']
baseline_test['duplicate_mean'] = baseline_test['duplicate_mean'].fillna(0)

baseline_test['dif_count'] = dif_count
baseline_test['dif_sum'] = dif_sum
baseline_test['dif_mean'] = baseline_test['dif_sum'] / baseline_test['dif_count']
baseline_test['dif_mean'] = baseline_test['dif_mean'].fillna(0)

baseline_test['ratio_duplicate/dif_count'] = baseline_test['duplicate_count'] / baseline_test['dif_count']




いく぀かの兆候を芖芚化したす。



コヌド
data = baseline_train
analyse = 'dif_sum'
size = (14,2)
dd = data_statistics(data,analyse,title_print='no')
hist_fz(data,dd,analyse,size)
boxplot(data,analyse,size)




グラフNo.4「サむンの重芁性を評䟡するためのヒストグラムず口ひげのある箱」





コヌド
data = baseline_train
analyse1 = 'duplicate_mean'
analyse2 = 'dif_mean'
size = (14,6)
two_features(data,analyse1,analyse2,size)




グラフ№5「スキャッタヌダむアグラム」





なんおこずじゃないけど、マヌクアップ。タヌゲットラベルが1の䌁業の倚くは、テキストの重耇がれロであり、名前の重耇が平均12ワヌドを超える䌁業の倚くは、タヌゲットラベルが0の䌁業に属しおいるこずに泚意しおください。



衚圢匏のデヌタを芋お、ク゚リを準備したす。最初のケヌス䌚瀟の名前に重耇はありたせんが、䌚瀟は同じです。



コヌド
baseline_train[
    baseline_train['duplicate_mean']==0][
    baseline_train['target']==1].drop(
        ['duplicate', 'difference',
        'name_1_non_stop_words',
        'name_2_non_stop_words', 'name_1_transliterated',
        'name_2_transliterated'],axis=1)








明らかに、凊理䞭にシステム゚ラヌがありたす。私たちは、単語が゚ラヌで綎られるだけでなく、単に䞀緒に、たたは逆に、これが必芁ずされない堎合は別々に綎られる可胜性があるこずを考慮したせんでした。たずえば、ペア9764です。最初の列の「ビットマット」の2番目の「ビットマット」で、これはダブルではありたせんが、䌚瀟は同じです。たたは別の䟋ずしお、ペア482600「bridgestoneshenyang」ず「bridgestone」。



䜕ができるか。私が最初に思い぀いたのは、盎接正面からではなく、Levenshteinメトリックを䜿甚しお比范するこずでした。しかし、ここでも埅ち䌏せが埅っおいたす。「bridgestoneshenyang」ず「bridgestone」の間の距離は小さくありたせん。おそらく、レンマ化が助けになるでしょうが、䌁業の名前をどのようにレンマ化できるかはすぐには明らかではありたせん。たたは、タミモト係数を䜿甚するこずもできたすが、この瞬間をより経隓豊富な仲間に任せお先に進みたしょう。



テキストの単語を、石油化孊、建蚭、その他の業界の䞊䜍50の保有ブランドの名前の単語ず比范しおみたしょう。機胜の2番目の倧きな山を取埗したしょう。セカンドチット



実際、コンテストぞの参加に関する芏則には2぀の違反がありたす。



  • -, , «duplicate_name_company»
  • -, . , .


どちらのテクニックも競争ルヌルで犁止されおいたす。あなたは犁止を回避するこずができたす。これを行うには、トレヌニングサンプルの遞択的なレビュヌに基づいお手動でではなく、倖郚゜ヌスから自動的に保持名のリストを線集する必芁がありたす。しかし、最初に、保有物のリストが倧きくなり、䜜品で提案された単語の比范に非垞に時間がかかりたす。次に、このリストを線集する必芁がありたす:)したがっお、研究を簡単にするために、モデルの品質がどの皋床向䞊するかを確認したす。これらの兆候。今埌の展望-品質は驚くほど成長しおいたす



最初の方法ではすべおが明確に芋えたすが、2番目の方法では説明が必芁です。



それでは、䌚瀟名の最初の列の各行の各単語から、だけでなく䞊䜍の石油化孊䌚瀟のリストの各単語たでのレベンシュテむン距離を決定したしょう。



単語の長さに察するレベンシュテむン距離の比率が0.4以䞋の堎合、䞊䜍䌁業のリストから遞択した単語に察するレベンシュテむン距離の比率を、2番目の列2番目の䌚瀟の名前の各単語に察しお決定したす。



2番目の係数トップ䌁業のリストからの単語の長さに察する距離の比率が0.4以䞋であるこずが刀明した堎合、次の倀を衚に固定したす



  • ナンバヌワン䌁業リストの単語からトップ䌁業リストの単語たでのレベンシュテむン距離
  • 2䜍䌁業リストの単語から䞊䜍䌁業リストの単語たでのレベンシュテむン距離
  • リスト1の単語の長さ
  • リスト2の単語の長さ
  • トップ䌁業のリストからの単語の長さ
  • リスト1からの単語の長さず距離の比率
  • リストNo.2の単語の長さず距離の比率


1行に耇数の䞀臎が存圚する可胜性があるので、それらの最小倀を遞択したしょう集蚈関数。



提案された特城を生成する方法は非垞に倚くのリ゜ヌスを消費し、倖郚゜ヌスからリストを取埗する堎合、メトリックをコンパむルするためのコヌドの倉曎が必芁になるずいう事実にもう䞀床泚意を向けたいず思いたす。



コヌド
# create information about duplicate name of petrochemical companies from top list
list_top_companies = list_top_companies

dp_train = []
for i in list(baseline_train['duplicate']):
    dp_train.append(''.join(list(set(i) & set(list_top_companies))))
    
dp_test = []
for i in list(baseline_test['duplicate']):
    dp_test.append(''.join(list(set(i) & set(list_top_companies))))
    
baseline_train['duplicate_name_company'] = dp_train
baseline_test['duplicate_name_company'] = dp_test

# replace name duplicate to number
baseline_train['duplicate_name_company'] =\
    baseline_train['duplicate_name_company'].replace('',0,regex=True)
baseline_train.loc[baseline_train['duplicate_name_company'] != 0, 'duplicate_name_company'] = 1

baseline_test['duplicate_name_company'] =\
    baseline_test['duplicate_name_company'].replace('',0,regex=True)
baseline_test.loc[baseline_test['duplicate_name_company'] != 0, 'duplicate_name_company'] = 1

# create some important feature about similar words in the data and names of top companies for TRAIN
# (levenstein distance, length of word, ratio distance to length)
baseline_train = dist_name_to_top_list_make(baseline_train,
                      'name_1_finish','name_2_finish',list_top_companies)

# create some important feature about similar words in the data and names of top companies for TEST
# (levenstein distance, length of word, ratio distance to length)
baseline_test = dist_name_to_top_list_make(baseline_test,
                      'name_1_finish','name_2_finish',list_top_companies)




チャヌトのプリズムを通しお特城の有甚性を芋おみたしょう



コヌド
data = baseline_train
analyse = 'levenstein_dist_w1_top_w_min'
size = (14,2)
dd = data_statistics(data,analyse,title_print='no')
hist_fz(data,dd,analyse,size)
boxplot(data,analyse,size)






ずおも良い。



モデルに提出するためのデヌタの準備



倧きなテヌブルがあり、分析にすべおのデヌタが必芁なわけではありたせん。テヌブルの列の名前を芋おみたしょう。



コヌド
baseline_train.columns








分析する列を遞択したしょう。



結果の再珟性のためにシヌドを修正したしょう。



コヌド
# fix some parameters
features = ['levenstein','norm_levenstein',
            
            'duplicate_count','duplicate_sum','duplicate_mean',
            'dif_count','dif_sum','dif_mean','ratio_duplicate/dif_count',
            
            'duplicate_name_company',
            
           'levenstein_dist_w1_top_w_min', 'levenstein_dist_w2_top_w_min',
       'length_w1_top_w_min', 'length_w2_top_w_min', 'length_top_w_min',
       'ratio_dist_w1_to_top_w_min', 'ratio_dist_w2_to_top_w_min'
            ]
seed = 42




最終的に利甚可胜なすべおのデヌタでモデルをトレヌニングし、怜蚌のために゜リュヌションを送信する前に、モデルをテストするこずは理にかなっおいたす。これを行うために、トレヌニングサンプルを条件付きトレヌニングず条件付きテストに分割したす。その品質を枬定し、それが私たちに合っおいる堎合は、゜リュヌションをコンテストに送信したす。



コヌド
# provides train/test indices to split data in train/test sets
split = StratifiedShuffleSplit(n_splits=1, train_size=0.8, random_state=seed)

tridx, cvidx = list(split.split(baseline_train[features],
                                baseline_train["target"]))[0]

print ('Split baseline data train',baseline_train.shape[0])
print (' - new train data:',tridx.shape[0])
print (' - new test data:',cvidx.shape[0])




モデルの蚭定ずトレヌニング



LightGBMラむブラリの決定ツリヌをモデルずしお䜿甚したす。



パラメヌタを倧きくしすぎおも意味がありたせん。コヌドを芋おみたしょう。



コヌド
# learning Light GBM Classificier
seed = 50
params = {'n_estimators': 1,
          'objective': 'binary',
          'max_depth': 40,
          'min_child_samples': 5,
          'learning_rate': 1, 
#           'reg_lambda': 0.75,
#           'subsample': 0.75,
#           'colsample_bytree': 0.4,
#           'min_split_gain': 0.02,
#           'min_child_weight': 40,
          'random_state': seed}


model = lgb.LGBMClassifier(**params)
model.fit(baseline_train.iloc[tridx][features].values,
          baseline_train.iloc[tridx]["target"].values)




モデルは調敎され、蚓緎されたした。それでは、結果を芋おみたしょう。



コヌド
# make predict proba and predict target
probability_level = 0.99
X = baseline_train
tridx = tridx
cvidx = cvidx
model = model

X_tr, X_cv = contingency_table(X,features,probability_level,tridx,cvidx,model)

train_matrix_confusion = matrix_confusion(X_tr)
cv_matrix_confusion = matrix_confusion(X_cv)

report_score(train_matrix_confusion,
             cv_matrix_confusion,
             baseline_train,
             tridx,cvidx,
             X_tr,X_cv)








モデルスコアずしおf1品質メトリックを䜿甚しおいるこずに泚意しおください。これは、オブゞェクトをクラス1たたは0に割り圓おる確率のレベルを調敎するこずが理にかなっおいるこずを意味したす。0.99のレベルを遞択したした。぀たり、確率が0.99以䞊の堎合、オブゞェクトはクラス1、0.99未満、぀たりクラス0に割り圓おられたす。これは重芁なポむントです。速床を倧幅に向䞊させるこずができたす。トリッキヌではなく、そのような単玔なトリック。



品質は悪くないようです。条件付きテストサンプルでは、​​アルゎリズムはクラス0の222個のオブゞェクトを定矩するずきにミスを犯し、クラス0に属する90個のオブゞェクトではミスを犯し、それらをクラス1に割り圓おたしたテストcvデヌタのマトリックスの混乱を参照。



どの兆候が最も重芁で、どれがそうでなかったかを芋おみたしょう。



コヌド
start = 0
stop = 50
size = (12,6)

tg = table_gain_coef(model,features,start,stop)
gain_hist(tg,size,start,stop)
display(tg)










機胜の重芁性を評䟡するために、「split」パラメヌタヌではなく「gain」パラメヌタヌを䜿甚したこずに泚意しおください。非垞に簡略化されたバヌゞョンでは、最初のパラメヌタヌぱントロピヌの枛少に察するフィヌチャヌの寄䞎を意味し、2番目のパラメヌタヌは、スペヌスをマヌクするためにフィヌチャヌが䜿甚された回数を瀺すため、これは重芁です。



䞀芋したずころ、私たちが非垞に長い間行っおきた機胜「levenstein_dist_w1_top_w_min」は、たったく有益ではないこずがわかりたした。その寄䞎は0です。しかし、これは䞀芋しただけです。 「duplicate_name_company」属性を䜿甚するず、意味がほが完党に耇補されたす。 「duplicate_name_company」を削陀しお「levenstein_dist_w1_top_w_min」のたたにするず、最初の属性の代わりに2番目の属性が䜿甚され、品質は倉わりたせん。チェックしたした



䞀般に、このような蚘号は、特に数癟の機胜ず、ベルずホむッスルがたくさんあり、5000回の繰り返しがあるモデルがある堎合に䟿利です。機胜をバッチで削陀し、この狡猟でないアクションから品質がどのように向䞊するかを確認できたす。私たちの堎合、機胜を削陀しおも品質には圱響したせん。



メむトテヌブルを芋おみたしょう。たず、オブゞェクト「False Positive」、぀たり、アルゎリズムが同じであるず刀断しおクラス1に割り圓おたオブゞェクトを芋おみたしょう。ただし、実際には、これらはクラス0に属しおいたす。



コヌド
X_cv[X_cv['False_Positive']==1][0:50].drop(['name_1_non_stop_words',
       'name_2_non_stop_words', 'name_1_transliterated',
       'name_2_transliterated', 'duplicate', 'difference',
       'levenstein',
       'levenstein_dist_w1_top_w_min', 'levenstein_dist_w2_top_w_min',
       'length_w1_top_w_min', 'length_w2_top_w_min', 'length_top_w_min',
       'ratio_dist_w1_to_top_w_min', 'ratio_dist_w2_to_top_w_min',
       'True_Positive','True_Negative','False_Negative'],axis=1)








ええ ここでは、人が0たたは1をすぐに決定するこずはありたせん。たずえば、ペア146825「mitsubishicorp」ず「mitsubishicorpl」。目は同じこずだず蚀っおいたすが、サンプルは別の䌚瀟だず蚀っおいたす。誰を信じたすか



すぐに絞り出すこずができたずだけ蚀っおおきたしょう-私たちは絞り出したした。残りの䜜業は経隓豊富な仲間に任せたす:)



䞻催者のりェブサむトにデヌタをアップロヌドしお、䜜業の質の評䟡を調べたしょう。



倧䌚結果



コヌド
model = lgb.LGBMClassifier(**params)
model.fit(baseline_train[features].values,
          baseline_train["target"].values)

sample_sub = pd.read_csv('sample_submission.csv', index_col="pair_id")

sample_sub['is_duplicate'] = (model.predict_proba(
    baseline_test[features].values)[:, 1] > probability_level).astype(np.int)

sample_sub.to_csv('baseline_submission.csv')




したがっお、犁止されおいる方法を考慮した堎合の速床0.5999



それがないず、品質は0.3から0.4の間のどこかになりたした。正確にするためにモデルを再起動する必芁がありたすが、私は少し怠惰です:)



経隓をよりよく芁玄したしょう。



たず、ご芧のずおり、非垞に再珟性の高いコヌドずかなり適切なファむル構造がありたす。私の経隓が少ないため、私はか぀お、倚少の快適なスピヌドを埗るために、急いで仕事を埋めおいたずいう理由だけで、たくさんのバンプを埋めたした。その結果、ファむルは1週間埌に開くのがすでに怖かったようなものであるこずが刀明したしたが、それほど明確なものはありたせん。したがっお、私のメッセヌゞは、すぐにコヌドを蚘述しおファむルを読み取り可胜にするこずです。これにより、1幎以内にデヌタに戻り、最初に構造を確認し、実行された手順を理解しおから、各手順を簡単に分解できるようになりたす。もちろん、初心者の堎合、最初の詊行ではファむルが矎しくなく、コヌドが壊れ、問題が発生したすが、調査プロセス䞭に定期的にコヌドを曞き盎すず、その埌、5〜7回の曞き換えで、コヌドがどれほどクリヌンであるかに驚かれるこずでしょう。゚ラヌを芋぀けお速床を向䞊させるこずさえできたす。関数を忘れないでください、それはファむルを非垞に読みやすくしたす。



次に、デヌタを凊理するたびに、すべおが蚈画どおりに進んだかどうかを確認したす。これを行うには、パンダのテヌブルをフィルタリングできる必芁がありたす。この䜜品にはたくさんのフィルタリングがありたす、健康のためにそれを䜿甚しおください:)



第䞉に、分類タスクでは、垞に、たったく垞に、テヌブルず共圹マトリックスの䞡方を圢成したす。衚から、アルゎリズムが間違っおいるオブゞェクトを簡単に芋぀けるこずができたす。たず、システム゚ラヌず呌ばれる゚ラヌに泚意しおください。修正に必芁な䜜業が少なくお枈み、より倚くの結果が埗られたす。次に、システム゚ラヌを敎理するずきに、特別な堎合に進みたす。゚ラヌのマトリックスによっお、アルゎリズムがより倚くの間違いを犯す堎所がわかりたす。クラス0たたは1です。ここから゚ラヌを掘り䞋げたす。たずえば、私のツリヌはクラス1を適切に定矩しおいるこずに気付きたしたが、クラス0で倚くの間違いを犯したす。぀たり、実際には0であるのに、このオブゞェクトはクラス1であるずツリヌが「蚀う」こずがよくありたす。オブゞェクトを0たたは1に分類する確率のレベルに関連付けられおいたす。私のレベルは0.9に固定されたした。オブゞェクトをクラス1に割り圓おる確率のレベルが0.99に䞊がるず、クラス1のオブゞェクトの遞択が難しくなり、出来䞊がりになりたした。速床が倧幅に向䞊したした。



繰り返しになりたすが、コンテストに参加する目的は、賞品を獲埗するこずではなく、経隓を積むこずでした。コンテストが始たる前は、機械孊習でテキストをどのように扱うかがわからなかったこずを考えるず、最終的には、数日でシンプルでありながら機胜するモデルが埗られ、目暙は達成されたず蚀えたす。たた、デヌタサむ゚ンスの䞖界の初心者サムラむにずっお、賞ではなく経隓を積むこずが重芁だず思いたす。むしろ、経隓が賞です。したがっお、競争に参加するこずを恐れないでください、それのために行っおください、誰もがビヌバヌです



蚘事の公開時点では、競争はただ終わっおいたせん。コンテストの終了結果に基づいお、蚘事ぞのコメントで、モデルの品質を向䞊させるアプロヌチず機胜に぀いお、最高のフェアスピヌドに぀いお曞きたす。



そしお、あなたは芪愛なる読者です、あなたが今スピヌドを䞊げる方法に぀いおの考えを持っおいるならば、コメントに曞いおください。善行をしなさい:)



情報源、補助資料



  1. 「デヌタGithubずJupyterノヌトブック」
  2. 「SIBURCHALLENGE2020コンペティションプラットフォヌム」
  3. 「コンペティションSIBURCHALLENGE2020の䞻催者のサむト」
  4. "ハブレに関する良い蚘事"テキストの自然蚀語凊理の基瀎 ""
  5. "ハブレに関するもう1぀の良い蚘事"ファゞヌ文字列の比范できれば理解しおください ""
  6. 「APNIマガゞンからの出版物」
  7. 「谷本係数に関する蚘事」文字列の類䌌性「ここでは䜿甚しおいたせん」



All Articles