Talking Hatを例に、研究から実装までのデータサイエンスプロジェクト







1か月前、レンタは、同じハリーポッタートーキングハットが 、4つの学部のいずれかにソーシャルネットワークへのアクセスを提供する参加者を特定するコンテストを開始しました。 競争はうまく行われ、異なる発音の名前は異なる学部によって決定され、同様の英語とロシア語の名前と姓が同様の方法で配布されました。 分布が名と姓のみに依存しているかどうか、そして友人の数や他の要因が何らかの形で考慮されているかどうかはわかりませんが、この競争はこの記事のアイデアを示唆しました:分類器をゼロから訓練して、ユーザーを異なる学部に配布できるようにします。







この記事では、名前と姓に応じて、ハリーポッターの部署に人々を配布する簡単なMLモデルを作成します。CRISPの方法論に従って小さな調査プロセスを実行しました。 つまり、私たち:















このタスクは簡単に思えるかもしれないので、プロセス全体(2時間未満)およびこの記事(読み取り時間が15分未満)に追加の制限を課します。







すでにデータサイエンスの美しい素晴らしい世界に没頭していて、Kaggliteが絶えず見られない場合、または(神が禁じている)同僚とのミーティング中にHadupの長さを測定したい場合、記事は単純で興味をそそられないように見えます。 また、最終モデルの品質はこの記事の主な価値ではありません。 警告しました。 行こう







この記事で使用されているコードを含むgithubリポジトリは、好奇心の強い読者にも利用できます。 エラーが発生した場合は、PRを開いてください。







問題を定式化する



明確な判断基準を持たない問題を無限に解決することは可能です。そのため、入力された行に応答して、「Gryffindor」、「Ravenclaw」、「Hufflepuff」、または「Slytherin」の答えを得ることができるソリューションを得たいとすぐに判断します。







実際、ブラックボックスを取得する必要があります。







" " => [?] => Griffindor
      
      





元の黒い帽子は、性格と個人的な資質に応じて、若いウィザードを部門に配布しました。 タスクの条件に応じたキャラクターと性格に関するデータは利用できないため、参加者の名前と姓を使用します。本のキャラクターを本の出身の学部に対応する学部に分配する必要があることを忘れないでください。 そして、私たちの決定がハリーをハッフルパフまたはレイヴンクローに配布した場合、ポテロマニアは間違いなく動揺します(しかし、本の精神を伝えるためにハリーをグリフィンドールとスリザリンに等しい確率で送るべきです)。







確率について話しているので、より厳密な数学用語で問題を形式化します。 データサイエンスの観点から、分類問題を解決します。つまり、特定のクラスのオブジェクト(名前と姓の形式の文字列)を割り当てます(実際には、ラベルまたはラベルです。これは、yes / no値を持つ数値または4つの変数です) ) 少なくともハリーの場合、グリフィンドールとスリザリンの2つの答えを与えるのが正しいことを理解しています。そのため、帽子が定義する特定の学部を予測しない方がよいでしょうが、この学部に人が割り当てられる確率は、ある種の機能











f<><>=Pgriffindor;Pravenclaw;Phufflpuff;Pslitherin







指標と品質評価



タスクと目標が定式化され、 今、私たちはそれを解決する方法を考えます しかし、それだけではありません。 調査を開始するには、品質指標を入力する必要があります。 つまり、2つの異なるソリューションを相互に比較する方法を決定します。







人生のすべてが良くてシンプルです-スパム検出器は受信メッセージに最小限のスパムを渡す必要があり、必要な文字を最大限に渡す必要があることを直感的に理解しています。







実際には、すべてがより複雑であり、 これの確認は、どのようにどのようなメトリックが使用されるかを説明する多数の 記事です。 練習はこれを最もよく理解するのに役立ちますが、それについては別の投稿を書き、誰もが実際にこれがどのように異なるかを理解して理解できるように、オープンなテーブルを作成することをお約束します。







私たちにとって世帯は「最高のものを選びましょう」がROC AUCです。 これは、この場合のメトリックに必要なものです。誤検知が少なく、実際の予測が正確であればあるほど、ROC AUCが大きくなります。







理想的なROCモデルの場合、AUCは1であり、クラスを絶対的にランダムに定義する理想的なランダムモデル-0.5です。











ROC AUC in[0.5;1]







アルゴリズム



ブラックボックスでは、本のヒーローの分布を考慮し、入力として別の名前と姓を使用し、結果を提供する必要があります。 分類の問題を解決するには、さまざまな機械学習アルゴリズムを使用できます。







ニューラルネットワーク、因数分解マシン、線形回帰、またはSVMなど。







一般的な信念に反して、データサイエンスはニューラルネットワークだけに限定されず、このアイデアを普及させるために、この記事ではニューラルネットワークは好奇心reader盛な読者への演習として残されています。 データ分析で単一のコース(特に主観的に優れたコース-ODS)を受講しなかった 、またはアマチュア漁師の雑誌にも掲載されている機械学習やAIに関するn個のニュースを単に読んだ人は、おそらくアルゴリズムの一般的なグループの名前を満たしました:バギング、ブースティング、サポートベクトル法(SVM)、線形回帰。 問題を解決するために使用するのはそれらです。







そして、より正確に、比較します:









Hogwartsの各学生を学部の1つに配布する問題は、彼に対応する学部を定義することで解決できますが、厳密に言えば、この問題各クラスが個別に属するかどうかを判断する問題を解決することになります。 したがって、この記事のフレームワークでは、各学部に1つずつ、4つのモデルを取得するという目標を設定しました。







データ



トレーニングのための適切なデータセット、さらに重要なことには、適切な目的で使用するための合法的なデータセットを見つけることは、データサイエンスで最も複雑で時間のかかるタスクの1つです。 私たちのタスクでは、ハリー・ポッターの世界に関するウィキアからデータを取得します。 たとえば、このリンクでは、グリフィンドールの学部で学んだすべてのキャラクターを見つけることができます。 この場合、非営利目的でデータを使用することが重要です。したがってこのサイトのライセンスに違反しません。













データサイエンティストはこんなにクールだと思う人のために、データサイエンティストに行って教えます。データのクリーニングと準備などのステップがあることを思い出させてください。 たとえば、「グリフィンドールの七番目の知事」を削除し、「グリフィンドールの未知の少女」を半自動で削除するには、ダウンロードしたデータを手動で管理する必要があります。 実際の作業では、タスクの比例的に大きな部分は常に、データセット内の欠損値の準備、クリーニング、および復元に関連付けられています。







少しctrl + c&ctrl + vを出力すると、英語とロシア語の2つの言語の文字の名前を含む4つのテキストファイルが得られます。







収集したデータを調査します(EDA、探索的データ分析)



この段階では、学部の学生の名前を含む4つのファイルがあります。詳しく見ていきます。







 $ ls ../input griffindor.txt hufflpuff.txt ravenclaw.txt slitherin.txt
      
      





各ファイルには、学生の名前と姓(ある場合)が1行に1つずつ含まれています。







 $ wc -l ../input/*.txt 250 ../input/griffindor.txt 167 ../input/hufflpuff.txt 180 ../input/ravenclaw.txt 254 ../input/slitherin.txt 851 total
      
      





収集されたデータの形式は次のとおりです。







 $ cat ../input/griffindor.txt | head -3 && cat ../input/griffindor.txt | tail -3      Charlie Stainforth Melanie Stanmore Stewart
      
      





私たちの全体の考えは、ブラックボックス(またはブラックハット)が区別することを学ぶことができる名前と姓に類似したものがあるという仮定に基づいています。







アルゴリズムはそのまま行を送ることができますが、基本モデルは「Draco」と「Harry」の違いを自分で理解できないため、結果は良くありません。そのため、名前と姓から記号を抽出する必要があります。







データの準備(機能エンジニアリング)



標識 (または英語の 機能からの機能 -プロパティ)は、オブジェクトの際立ったプロパティです。 過去1年間に転職した回数、左手の指の数、エンジンのエンジン容量、走行距離が100,000 kmを超えるかどうか。 標識のあらゆる種類の分類は非常に多くの発明者によって考案されたものであり、この点に関して単一のシステムは存在せず、単一のシステムになることもできないため、標識の例を次に示します。







  1. 有理数
  2. カテゴリー(最大12、12-18または18+)
  3. バイナリー値(最初のローンを返したかどうか)
  4. 日付、色、株式など


フィーチャ の検索 (または形成)( 英語の フィーチャエンジニアリング )は、データ分析の専門家の研究または作業における独立した段階として際立っていることがよくあります。 実際、常識、経験、仮説テストが実際に行われていると、プロセス自体に役立ちます。 正しい兆候をすぐに推測することは、完全な手と基本的な知識と運を組み合わせることの問題です。 時にはシャーマニズムがありますが、一般的なアプローチは非常に単純です:思いついたことを実行し、新しい記号を追加してソリューションを改善できるかどうかを確認する必要があります。 たとえば、タスクのサインとして、名前にシューという音の数を含めることができます。







モデルの最初のバージョン(実際のデータサイエンス研究-傑作としては決して完成できないため)では、名前と姓に次の機能を使用します。







  1. 1および単語の最後の文字-母音または子音
  2. 二重母音と子音
  3. 母音、子音、聴覚障害者、有声の数
  4. 名前の長さ、姓の長さ
  5. ...


これを行うために、 このリポジトリを基礎として、ラテン文字に使用できるようにクラスを追加します。 これにより、各文字の発音を判断する機会が得られます。







 >> from Phonetic import RussianLetter, EnglishLetter >> RussianLetter('').classify() {'consonant': True, 'deaf': False, 'hard': False, 'mark': False, 'paired': False, 'shock': False, 'soft': False, 'sonorus': True, 'vowel': False} >> EnglishLetter('d').classify() {'consonant': True, 'deaf': False, 'hard': True, 'mark': False, 'paired': False, 'shock': False, 'soft': False, 'sonorus': True, 'vowel': False}
      
      





これで、統計を計算するための簡単な関数を定義できます。例:







 def starts_with_letter(word, letter_type='vowel'): """   ,    . :param word:  :param letter_type: 'vowel'  'consonant'.   . :return: Boolean """ if len(word) == 0: return False return Letter(word[0]).classify()[letter_type] def count_letter_type(word): """       . :param word:  :param debug:    :return: :obj:`dict` of :obj:`str` => :int:count """ count = { 'consonant': 0, 'deaf': 0, 'hard': 0, 'mark': 0, 'paired': 0, 'shock': 0, 'soft': 0, 'sonorus': 0, 'vowel': 0 } for letter in word: classes = Letter(letter).classify() for key in count.keys(): if classes[key]: count[key] += 1 return count
      
      





これらの関数を使用すると、すでに最初の兆候を取得できます。







 from feature_engineering import * >> print("  («»): ", len(""))   («»): 5 >> print(" («»)   : ", starts_with_letter('', 'vowel'))  («»)   : False >> print(" («»)   : ", starts_with_letter('', 'consonant'))  («»)   : True >> count_Harry = count_letter_type("") >> print ("     («»): ", count_Harry['paired'])      («»): 1
      
      





厳密に言えば、これらの関数を使用して、文字列のベクトル表現を取得できます。つまり、マッピングを取得します。











f<Name><Surname>=>lengthnamelengthsurnames...numberof vowelssurnames







これで、機械学習アルゴリズムに入力できるデータセットの形式でデータを提示できます。







 >> from data_loaders import load_processed_data >> hogwarts_df = load_processed_data() >> hogwarts_df.head()
      
      











さらに、その結​​果、各生徒に次のようなサインが表示されます。







 >> hogwarts_df[hogwarts_df.columns].dtypes
      
      





受け取ったサイン
 name object surname object is_english bool name_starts_with_vowel bool name_starts_with_consonant bool name_ends_with_vowel bool name_ends_with_consonant bool name_length int64 name_vowels_count int64 name_double_vowels_count int64 name_consonant_count int64 name_double_consonant_count int64 name_paired_count int64 name_deaf_count int64 name_sonorus_count int64 surname_starts_with_vowel bool surname_starts_with_consonant bool surname_ends_with_vowel bool surname_ends_with_consonant bool surname_length int64 surname_vowels_count int64 surname_double_vowels_count int64 surname_consonant_count int64 surname_double_consonant_count int64 surname_paired_count int64 surname_deaf_count int64 surname_sonorus_count int64 is_griffindor int64 is_hufflpuff int64 is_ravenclaw int64 is_slitherin int64 dtype: object
      
      





最後の4列は対象とされています-学生がどの学部に登録されているかに関する情報が含まれています。







アルゴリズムトレーニング



一言で言えば、アルゴリズムは人々と同じように訓練されます。彼らは間違いを犯し、そこから学びます。 どれだけミスを犯したかを理解するために、アルゴリズムはエラー関数(損失関数、 英語の 損失関数 )を使用します。







原則として、学習プロセスは非常に単純であり、いくつかのステップで構成されています。







  1. 予測を行います。
  2. エラーを評価します。
  3. モデルパラメータを修正します。
  4. 目標に達するか、プロセスが停止するか、データが終了するまで1〜3を繰り返します。
  5. 結果のモデルの品質を評価します。







    もちろん、実際には、すべてが少し複雑です。 たとえば、 過剰適合の現象があります。アルゴリズムは、どの特徴が答えに対応するかを文字通り覚えることができ、したがって、彼が訓練されたものと類似していないオブジェクトの結果を悪化させることができます。 これを回避するために、さまざまな手法とハッキングがあります。









上記のように、4つの問題を解決します:各学部に1つ。 したがって、スリザリンのデータを準備します。







 #  ,     - : >> data_full = hogwarts_df.drop( [ 'name', 'surname', 'is_griffindor', 'is_hufflpuff', 'is_ravenclaw' ], axis=1).copy() #    ,   : >> X_data = data_full.drop('is_slitherin', axis=1) #     ,   1    >> y = data_full.is_slitherin
      
      





学習中、アルゴリズムはその結果を実際のデータと常に比較します。データセットのこの部分は検証に割り当てられているためです。 良いトーンのルールは、アルゴリズムがまったく見なかった個別のデータでアルゴリズムの結果を評価するためにも考慮されます。 したがって、サンプルを70/30の割合で分割し、最初のアルゴリズムをトレーニングします。







 from sklearn.cross_validation import train_test_split from sklearn.ensemble import RandomForestClassifier #      >> seed = 7 #    >> test_size = 0.3 >> X_train, X_test, y_train, y_test = train_test_split(X_data, y, test_size=test_size, random_state=seed) >> rfc = RandomForestClassifier() >> rfc_model = rfc.fit(X_train, y_train)
      
      





できた これで、このモデルの入力にデータを送信すると、結果が生成されます。 これは楽しいので、まずはハリーのモデルがスリザリンを認識しているかどうかを確認します。 これを行うには、まずアルゴリズムの予測を取得するために関数を準備します。







コードを表示
 from data_loaders import parse_line_to_hogwarts_df import pandas as pd def get_single_student_features (name): """      :param name: string     :return: pd.DataFrame     """ featurized_person_df = parse_line_to_hogwarts_df(name) person_df = pd.DataFrame(featurized_person_df, columns=[ 'name', 'surname', 'is_english', 'name_starts_with_vowel', 'name_starts_with_consonant', 'name_ends_with_vowel', 'name_ends_with_consonant', 'name_length', 'name_vowels_count', 'name_double_vowels_count', 'name_consonant_count', 'name_double_consonant_count', 'name_paired_count', 'name_deaf_count', 'name_sonorus_count', 'surname_starts_with_vowel', 'surname_starts_with_consonant', 'surname_ends_with_vowel', 'surname_ends_with_consonant', 'surname_length', 'surname_vowels_count', 'surname_double_vowels_count', 'surname_consonant_count', 'surname_double_consonant_count', 'surname_paired_count', 'surname_deaf_count', 'surname_sonorus_count', ], index=[0] ) featurized_person = person_df.drop( ['name', 'surname'], axis = 1 ) return featurized_person def get_predictions_vector (model, person): """    :param model:   :param person: string   :return: list    """ encoded_person = get_single_student_features(person) return model.predict_proba(encoded_person)[0]
      
      





次に、アルゴリズムの結果を考慮するために小さなテストデータセットを設定します。







 def score_testing_dataset (model): """      . :param model:   """ testing_dataset = [ " ", "Kirill Malev", " ", "Harry Potter", " ", " ","Severus Snape", " ", "Tom Riddle", " ", "Salazar Slytherin"] for name in testing_dataset: print ("{} — {}".format(name, get_predictions_vector(model, name)[1])) score_testing_dataset(rfc_model)
      
      





   — 0.5 Kirill Malev — 0.5   — 0.0 Harry Potter — 0.0   — 0.75   — 0.9 Severus Snape — 0.5   — 0.2 Tom Riddle — 0.5   — 0.2 Salazar Slytherin — 0.3
      
      





結果は疑わしかった。 このモデルによれば、学部の創設者でさえ学部にはいません。 したがって、厳格な品質を評価する必要があります。最初に尋ねたメトリックを見てください。







 from sklearn.metrics import accuracy_score, roc_auc_score, classification_report predictions = rfc_model.predict(X_test) print("Classification report: ") print(classification_report(y_test, predictions)) print("Accuracy for Random Forest Model: %.2f" % (accuracy_score(y_test, predictions) * 100)) print("ROC AUC from first Random Forest Model: %.2f" % (roc_auc_score(y_test, predictions)))
      
      





 Classification report: precision recall f1-score support 0 0.66 0.88 0.75 168 1 0.38 0.15 0.21 89 avg / total 0.56 0.62 0.56 257 Accuracy for Random Forest Model: 62.26 ROC AUC from first Random Forest Model: 0.51
      
      





結果が非常に疑わしかったのは驚くことではありません-ROC AUCが約0.51であるということは、モデルがコイントスよりもわずかに優れていることを示唆しています。







結果をテストします。 品質指標



上記の1つの例を使用して、sklearnインターフェイスをサポートする1​​つのアルゴリズムがどのようにトレーニングされるかを調べました。 残りはまったく同じ方法でトレーニングされるため、すべてのアルゴリズムをトレーニングし、それぞれの場合に最適なアルゴリズムを選択することしかできません。













これは複雑ではありません。アルゴリズムごとに1を標準設定でトレーニングし、セット全体をトレーニングして、アルゴリズムの品質に影響するさまざまなオプションを並べ替えます。 この段階はモデルチューニングまたはハイパーパラメーター最適化と呼ばれ、その本質は非常に簡単です。最良の結果をもたらす設定のセットが選択されます。







 from model_training import train_classifiers from data_loaders import load_processed_data import warnings warnings.filterwarnings('ignore') #   hogwarts_df = load_processed_data() #     data_full = hogwarts_df.drop( [ 'name', 'surname', 'is_griffindor', 'is_hufflpuff', 'is_ravenclaw' ], axis=1).copy() X_data = data_full.drop('is_slitherin', axis=1) y = data_full.is_slitherin #    slitherin_models = train_classifiers(data_full, X_data, y) score_testing_dataset(slitherin_models[5])
      
      





   — 0.09437856871661066 Kirill Malev — 0.20820536334902712   — 0.07550095601699099 Harry Potter — 0.07683794773639624   — 0.9414529336862744   — 0.9293671807790949 Severus Snape — 0.6576783576162999   — 0.18577792617672767 Tom Riddle — 0.8351835484058869   — 0.25930925139546795 Salazar Slytherin — 0.24008788903854789
      
      





このバージョンの数値は過去よりも主観的には良く見えますが、内部の完璧主義者にはまだ十分ではありません。 したがって、より深いレベルに進み、タスクの製品感覚に戻ります。ヒーローが分布帽子によって決定される最も可能性の高い教員を予測する必要があります。 つまり、学部ごとにモデルをトレーニングする必要があります。













 >> from model_training import train_all_models #      >> slitherin_models, griffindor_models, ravenclaw_models, hufflpuff_models = \ train_all_models()
      
      





結果と多項回帰の結果の長い結論
 SVM Default Report Accuracy for SVM Default: 73.93 ROC AUC for SVM Default: 0.53 Tuned SVM Report Accuracy for Tuned SVM: 72.37 ROC AUC for Tuned SVM: 0.50 KNN Default Report Accuracy for KNN Default: 70.04 ROC AUC for KNN Default: 0.58 Tuned KNN Report Accuracy for Tuned KNN: 69.65 ROC AUC for Tuned KNN: 0.58 XGBoost Default Report Accuracy for XGBoost Default: 70.43 ROC AUC for XGBoost Default: 0.54 Tuned XGBoost Report Accuracy for Tuned XGBoost: 68.09 ROC AUC for Tuned XGBoost: 0.56 Random Forest Default Report Accuracy for Random Forest Default: 73.93 ROC AUC for Random Forest Default: 0.62 Tuned Random Forest Report Accuracy for Tuned Random Forest: 74.32 ROC AUC for Tuned Random Forest: 0.54 Extra Trees Default Report Accuracy for Extra Trees Default: 69.26 ROC AUC for Extra Trees Default: 0.57 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 73.54 ROC AUC for Tuned Extra Trees: 0.55 LGBM Default Report Accuracy for LGBM Default: 70.82 ROC AUC for LGBM Default: 0.62 Tuned LGBM Report Accuracy for Tuned LGBM: 74.71 ROC AUC for Tuned LGBM: 0.53 RGF Default Report Accuracy for RGF Default: 70.43 ROC AUC for RGF Default: 0.58 Tuned RGF Report Accuracy for Tuned RGF: 71.60 ROC AUC for Tuned RGF: 0.60 FRGF Default Report Accuracy for FRGF Default: 68.87 ROC AUC for FRGF Default: 0.59 Tuned FRGF Report Accuracy for Tuned FRGF: 69.26 ROC AUC for Tuned FRGF: 0.59 SVM Default Report Accuracy for SVM Default: 70.43 ROC AUC for SVM Default: 0.50 Tuned SVM Report Accuracy for Tuned SVM: 71.60 ROC AUC for Tuned SVM: 0.50 KNN Default Report Accuracy for KNN Default: 63.04 ROC AUC for KNN Default: 0.49 Tuned KNN Report Accuracy for Tuned KNN: 65.76 ROC AUC for Tuned KNN: 0.50 XGBoost Default Report Accuracy for XGBoost Default: 69.65 ROC AUC for XGBoost Default: 0.54 Tuned XGBoost Report Accuracy for Tuned XGBoost: 68.09 ROC AUC for Tuned XGBoost: 0.50 Random Forest Default Report Accuracy for Random Forest Default: 66.15 ROC AUC for Random Forest Default: 0.51 Tuned Random Forest Report Accuracy for Tuned Random Forest: 70.43 ROC AUC for Tuned Random Forest: 0.50 Extra Trees Default Report Accuracy for Extra Trees Default: 64.20 ROC AUC for Extra Trees Default: 0.49 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 70.82 ROC AUC for Tuned Extra Trees: 0.51 LGBM Default Report Accuracy for LGBM Default: 67.70 ROC AUC for LGBM Default: 0.56 Tuned LGBM Report Accuracy for Tuned LGBM: 70.82 ROC AUC for Tuned LGBM: 0.50 RGF Default Report Accuracy for RGF Default: 66.54 ROC AUC for RGF Default: 0.52 Tuned RGF Report Accuracy for Tuned RGF: 65.76 ROC AUC for Tuned RGF: 0.53 FRGF Default Report Accuracy for FRGF Default: 65.76 ROC AUC for FRGF Default: 0.53 Tuned FRGF Report Accuracy for Tuned FRGF: 69.65 ROC AUC for Tuned FRGF: 0.52 SVM Default Report Accuracy for SVM Default: 74.32 ROC AUC for SVM Default: 0.50 Tuned SVM Report Accuracy for Tuned SVM: 74.71 ROC AUC for Tuned SVM: 0.51 KNN Default Report Accuracy for KNN Default: 69.26 ROC AUC for KNN Default: 0.48 Tuned KNN Report Accuracy for Tuned KNN: 73.15 ROC AUC for Tuned KNN: 0.49 XGBoost Default Report Accuracy for XGBoost Default: 72.76 ROC AUC for XGBoost Default: 0.49 Tuned XGBoost Report Accuracy for Tuned XGBoost: 74.32 ROC AUC for Tuned XGBoost: 0.50 Random Forest Default Report Accuracy for Random Forest Default: 73.93 ROC AUC for Random Forest Default: 0.52 Tuned Random Forest Report Accuracy for Tuned Random Forest: 74.32 ROC AUC for Tuned Random Forest: 0.50 Extra Trees Default Report Accuracy for Extra Trees Default: 73.93 ROC AUC for Extra Trees Default: 0.52 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 73.93 ROC AUC for Tuned Extra Trees: 0.50 LGBM Default Report Accuracy for LGBM Default: 73.54 ROC AUC for LGBM Default: 0.52 Tuned LGBM Report Accuracy for Tuned LGBM: 74.32 ROC AUC for Tuned LGBM: 0.50 RGF Default Report Accuracy for RGF Default: 73.54 ROC AUC for RGF Default: 0.51 Tuned RGF Report Accuracy for Tuned RGF: 73.93 ROC AUC for Tuned RGF: 0.50 FRGF Default Report Accuracy for FRGF Default: 73.93 ROC AUC for FRGF Default: 0.53 Tuned FRGF Report Accuracy for Tuned FRGF: 73.93 ROC AUC for Tuned FRGF: 0.50 SVM Default Report Accuracy for SVM Default: 80.54 ROC AUC for SVM Default: 0.50 Tuned SVM Report Accuracy for Tuned SVM: 80.93 ROC AUC for Tuned SVM: 0.52 KNN Default Report Accuracy for KNN Default: 78.60 ROC AUC for KNN Default: 0.50 Tuned KNN Report Accuracy for Tuned KNN: 80.16 ROC AUC for Tuned KNN: 0.51 XGBoost Default Report Accuracy for XGBoost Default: 80.54 ROC AUC for XGBoost Default: 0.50 Tuned XGBoost Report Accuracy for Tuned XGBoost: 77.04 ROC AUC for Tuned XGBoost: 0.52 Random Forest Default Report Accuracy for Random Forest Default: 77.43 ROC AUC for Random Forest Default: 0.49 Tuned Random Forest Report Accuracy for Tuned Random Forest: 80.54 ROC AUC for Tuned Random Forest: 0.50 Extra Trees Default Report Accuracy for Extra Trees Default: 76.26 ROC AUC for Extra Trees Default: 0.48 Tuned Extra Trees Report Accuracy for Tuned Extra Trees: 78.60 ROC AUC for Tuned Extra Trees: 0.50 LGBM Default Report Accuracy for LGBM Default: 75.49 ROC AUC for LGBM Default: 0.51 Tuned LGBM Report Accuracy for Tuned LGBM: 80.54 ROC AUC for Tuned LGBM: 0.50 RGF Default Report Accuracy for RGF Default: 78.99 ROC AUC for RGF Default: 0.52 Tuned RGF Report Accuracy for Tuned RGF: 75.88 ROC AUC for Tuned RGF: 0.55 FRGF Default Report Accuracy for FRGF Default: 76.65 ROC AUC for FRGF Default: 0.50 #   ,       
      
      





 from sklearn.linear_model import LogisticRegression clf = LogisticRegression(random_state=0, solver='lbfgs', multi_class='multinomial') hogwarts_df = load_processed_data_multi() #     data_full = hogwarts_df.drop( [ 'name', 'surname', ], axis=1).copy() X_data = data_full.drop('faculty', axis=1) y = data_full.faculty clf.fit(X_data, y) score_testing_dataset(clf)
      
      





   — [0.3602361 0.16166944 0.16771712 0.31037733] Kirill Malev — [0.47473072 0.16051924 0.13511385 0.22963619]   — [0.38697926 0.19330242 0.17451052 0.2452078 ] Harry Potter — [0.40245098 0.16410043 0.16023278 0.27321581]   — [0.13197025 0.16438855 0.17739254 0.52624866]   — [0.17170203 0.1205678 0.14341742 0.56431275] Severus Snape — [0.15558044 0.21589378 0.17370406 0.45482172]   — [0.39301231 0.07397324 0.1212741 0.41174035] Tom Riddle — [0.26623969 0.14194379 0.1728505 0.41896601]   — [0.24843037 0.21632736 0.21532696 0.3199153 ] Salazar Slytherin — [0.09359144 0.26735897 0.2742305 0.36481909]
      
      





confusion_matrix:







 confusion_matrix(clf.predict(X_data), y)
      
      





 array([[144, 68, 64, 78], [ 8, 9, 8, 6], [ 22, 18, 31, 20], [ 77, 73, 78, 151]])
      
      





 def get_predctions_vector (models, person): predictions = [get_predictions_vector (model, person)[1] for model in models] return { 'slitherin': predictions[0], 'griffindor': predictions[1], 'ravenclaw': predictions[2], 'hufflpuff': predictions[3] } def score_testing_dataset (models): testing_dataset = [ " ", "Kirill Malev", " ", "Harry Potter", " ", " ","Severus Snape", " ", "Tom Riddle", " ", "Salazar Slytherin"] data = [] for name in testing_dataset: predictions = get_predctions_vector(models, name) predictions['name'] = name data.append(predictions) scoring_df = pd.DataFrame(data, columns=['name', 'slitherin', 'griffindor', 'hufflpuff', 'ravenclaw']) return scoring_df # Data Science —    ,       top_models = [ slitherin_models[3], griffindor_models[3], ravenclaw_models[3], hufflpuff_models[3] ] score_testing_dataset(top_models)
      
      





  name slitherin griffindor hufflpuff ravenclaw 0   0.349084 0.266909 0.110311 0.091045 1 Kirill Malev 0.289914 0.376122 0.384986 0.103056 2   0.338258 0.400841 0.016668 0.124825 3 Harry Potter 0.245377 0.357934 0.026287 0.154592 4   0.917423 0.126997 0.176640 0.096570 5   0.969693 0.106384 0.150146 0.082195 6 Severus Snape 0.663732 0.259189 0.290252 0.074148 7   0.268466 0.579401 0.007900 0.083195 8 Tom Riddle 0.639731 0.541184 0.084395 0.156245 9   0.653595 0.147506 0.172940 0.137134 10 Salazar Slytherin 0.647399 0.169964 0.095450 0.26126
      
      











, . ROC AUC , 0.5. , :









, , , , XGBoost CV , .







重要! , 70% . , 4 .







 from model_training import train_production_models from xgboost import XGBClassifier best_models = [] for i in range (0,4): best_models.append(XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1, colsample_bytree=0.7, gamma=0, learning_rate=0.05, max_delta_step=0, max_depth=6, min_child_weight=11, missing=-999, n_estimators=1000, n_jobs=1, nthread=4, objective='binary:logistic', random_state=0, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=1337, silent=1, subsample=0.8)) slitherin_model, griffindor_model, ravenclaw_model, hufflpuff_model = \ train_production_models(best_models) top_models = slitherin_model, griffindor_model, ravenclaw_model, hufflpuff_model score_testing_dataset(top_models)
      
      





 name slitherin griffindor hufflpuff ravenclaw 0   0.273713 0.372337 0.065923 0.279577 1 Kirill Malev 0.401603 0.761467 0.111068 0.023902 2   0.031540 0.616535 0.196342 0.217829 3 Harry Potter 0.183760 0.422733 0.119393 0.173184 4   0.945895 0.021788 0.209820 0.019449 5   0.950932 0.088979 0.084131 0.012575 6 Severus Snape 0.634035 0.088230 0.249871 0.036682 7   0.426440 0.431351 0.028444 0.083636 8 Tom Riddle 0.816804 0.136530 0.069564 0.035500 9   0.409634 0.213925 0.028631 0.252723 10 Salazar Slytherin 0.824590 0.067910 0.111147 0.085710
      
      





, , .







, , . .







 import pickle pickle.dump(slitherin_model, open("../output/slitherin.xgbm", "wb")) pickle.dump(griffindor_model, open("../output/griffindor.xgbm", "wb")) pickle.dump(ravenclaw_model, open("../output/ravenclaw.xgbm", "wb")) pickle.dump(hufflpuff_model, open("../output/hufflpuff.xgbm", "wb"))
      
      







, . , , , .







, , . , . , Data Scientist — -.







:









, docker-, python-. , flask.







 from __future__ import print_function # In python 2.7 import os import subprocess import json import re from flask import Flask, request, jsonify from inspect import getmembers, ismethod import numpy as npb import pandas as pd import math import os import pickle import xgboost as xgb import sys from letter import Letter from talking_hat import * from sklearn.ensemble import RandomForestClassifier import warnings def prod_predict_classes_for_name (full_name): featurized_person = parse_line_to_hogwarts_df(full_name) person_df = pd.DataFrame(featurized_person, columns=[ 'name', 'surname', 'is_english', 'name_starts_with_vowel', 'name_starts_with_consonant', 'name_ends_with_vowel', 'name_ends_with_consonant', 'name_length', 'name_vowels_count', 'name_double_vowels_count', 'name_consonant_count', 'name_double_consonant_count', 'name_paired_count', 'name_deaf_count', 'name_sonorus_count', 'surname_starts_with_vowel', 'surname_starts_with_consonant', 'surname_ends_with_vowel', 'surname_ends_with_consonant', 'surname_length', 'surname_vowels_count', 'surname_double_vowels_count', 'surname_consonant_count', 'surname_double_consonant_count', 'surname_paired_count', 'surname_deaf_count', 'surname_sonorus_count', ], index=[0] ) slitherin_model = pickle.load(open("models/slitherin.xgbm", "rb")) griffindor_model = pickle.load(open("models/griffindor.xgbm", "rb")) ravenclaw_model = pickle.load(open("models/ravenclaw.xgbm", "rb")) hufflpuff_model = pickle.load(open("models/hufflpuff.xgbm", "rb")) predictions = get_predctions_vector([ slitherin_model, griffindor_model, ravenclaw_model, hufflpuff_model ], person_df.drop(['name', 'surname'], axis=1)) return { 'slitherin': float(predictions[0][1]), 'griffindor': float(predictions[1][1]), 'ravenclaw': float(predictions[2][1]), 'hufflpuff': float(predictions[3][1]) } def predict(params): fullname = params['fullname'] print(params) return prod_predict_classes_for_name(fullname) def create_app(): app = Flask(__name__) functions_list = [predict] @app.route('/<func_name>', methods=['POST']) def api_root(func_name): for function in functions_list: if function.__name__ == func_name: try: json_req_data = request.get_json() if json_req_data: res = function(json_req_data) else: return jsonify({"error": "error in receiving the json input"}) except Exception as e: data = { "error": "error while running the function" } if hasattr(e, 'message'): data['message'] = e.message elif len(e.args) >= 1: data['message'] = e.args[0] return jsonify(data) return jsonify({"success": True, "result": res}) output_string = 'function: %s not found' % func_name return jsonify({"error": output_string}) return app if __name__ == '__main__': app = create_app() app.run(host='0.0.0.0')
      
      





Dockerfile:







 FROM datmo/python-base:cpu-py35 #  python3-wheel,        RUN apt-get update; apt-get install -y python3-pip python3-numpy python3-scipy python3-wheel ADD requirements.txt / RUN pip3 install -r /requirements.txt RUN mkdir /code;mkdir /code/models COPY ./python_api.py ./talking_hat.py ./letter.py ./request.py /code/ COPY ./models/* /code/models/ WORKDIR /code CMD python3 /code/python_api.py
      
      





:







 docker build -t talking_hat . && docker rm talking_hat && docker run --name talking_hat -p 5000:5000 talking_hat
      
      







— . , Apache Benchmark . , . — .







 $ ab -p data.json -T application/json -c 50 -n 10000 http://0.0.0.0:5000/predict
      
      





ab
 This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 0.0.0.0 (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Werkzeug/0.14.1 Server Hostname: 0.0.0.0 Server Port: 5000 Document Path: /predict Document Length: 141 bytes Concurrency Level: 50 Time taken for tests: 238.552 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 2880000 bytes Total body sent: 1800000 HTML transferred: 1410000 bytes Requests per second: 41.92 [#/sec] (mean) Time per request: 1192.758 [ms] (mean) Time per request: 23.855 [ms] (mean, across all concurrent requests) Transfer rate: 11.79 [Kbytes/sec] received 7.37 kb/s sent 19.16 kb/s total Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 3 Processing: 199 1191 352.5 1128 3352 Waiting: 198 1190 352.5 1127 3351 Total: 202 1191 352.5 1128 3352 Percentage of the requests served within a certain time (ms) 50% 1128 66% 1277 75% 1378 80% 1451 90% 1668 95% 1860 98% 2096 99% 2260 100% 3352 (longest request)
      
      





, :







 def prod_predict_classes_for_name (full_name): <...> predictions = get_predctions_vector([ app.slitherin_model, app.griffindor_model, app.ravenclaw_model, app.hufflpuff_model ], person_df.drop(['name', 'surname'], axis=1)) return { 'slitherin': float(predictions[0][1]), 'griffindor': float(predictions[1][1]), 'ravenclaw': float(predictions[2][1]), 'hufflpuff': float(predictions[3][1]) } def create_app(): <...> with app.app_context(): app.slitherin_model = pickle.load(open("models/slitherin.xgbm", "rb")) app.griffindor_model = pickle.load(open("models/griffindor.xgbm", "rb")) app.ravenclaw_model = pickle.load(open("models/ravenclaw.xgbm", "rb")) app.hufflpuff_model = pickle.load(open("models/hufflpuff.xgbm", "rb")) return app
      
      





:







 $ docker build -t talking_hat . && docker rm talking_hat && docker run --name talking_hat -p 5000:5000 talking_hat $ ab -p data.json -T application/json -c 50 -n 10000 http://0.0.0.0:5000/predict
      
      





ab
 This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 0.0.0.0 (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Werkzeug/0.14.1 Server Hostname: 0.0.0.0 Server Port: 5000 Document Path: /predict Document Length: 141 bytes Concurrency Level: 50 Time taken for tests: 219.812 seconds Complete requests: 10000 Failed requests: 3 (Connect: 0, Receive: 0, Length: 3, Exceptions: 0) Total transferred: 2879997 bytes Total body sent: 1800000 HTML transferred: 1409997 bytes Requests per second: 45.49 [#/sec] (mean) Time per request: 1099.062 [ms] (mean) Time per request: 21.981 [ms] (mean, across all concurrent requests) Transfer rate: 12.79 [Kbytes/sec] received 8.00 kb/s sent 20.79 kb/s total Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 2 Processing: 235 1098 335.2 1035 3464 Waiting: 235 1097 335.2 1034 3462 Total: 238 1098 335.2 1035 3464 Percentage of the requests served within a certain time (ms) 50% 1035 66% 1176 75% 1278 80% 1349 90% 1541 95% 1736 98% 1967 99% 2141 100% 3464 (longest request)
      
      





できた . , .







おわりに



, . - .







, :









, , . , !







この記事は、データ分析の分野でロシア語を話す多数の専門家を集めたオープンデータサイエンスコミュニティなしでは公開されなかったでしょう。








All Articles