Google Search Console の
今回は、sklearn LogisticRegression
ロジスティック回帰 を
前提
入力データ
Search Analytics for Sheets - Google スプレッドシート アドオン で、データを Google スプレッドシートに Export。
Export 後のスプレッドシートを TSV化した データを 入力データと しています。
Search Analytics for Sheets - Google スプレッドシート アドオン の使い方は、 サーチコンソールの 詳細データを Googleスプレッドシートに 自動反映させてTableauに インポートする 方法 :: 「清水 誠」公式サイト が 参考に なりました。 学習データ
入力データの一部の キーワードを 切り出し、 別シートに 貼り付け、 人力で 800キーワードくらいを 分類しました。
それを、tsv 出力した ものを 学習データと しています。
以下、学習データと して 作成した tsv
を一部抜粋し テーブル形式に 変換した ものに なります。
Query | label_name |
---|---|
sonarqube | sonarqube |
no module named | python |
(1_8.w001) the standalone template_* settings were deprecated in django 1.8 | django |
//nosonar | sonarqube |
404 エラー | 404 |
404エラー | 404 |
404エラーページ | 404 |
ロジスティック回帰とは
仕事の
YESと
NOを 明確に 定義できる ものの 予測に 向いている 手法が、 発生確率を 予測する ロジスティック回帰分析です。 商品購入を 促す ダイレクトメールの 送付を 例に とってみましょう。 DMが きっかけで 「購入する」を 1(100%)、 「購入しない」を ゼロ(0%)と 定義すると、 DM送付者1人単位の 購入の 確率を 算出する ことができます。 確率が 高い 順番に DMを 送付すれば、 ランダムに 送付する とき 以上の 効率が 上げられます。 ロジスティック回帰分析に よく 似た 解析手法に 重回帰分析が あります。 重回帰分析は、 「体重(A)から 血圧(B)を 予測する」 場合など、 Aも Bも 連続する 数値であることが 前提です。 これに 対して ロジスティック回帰分析は、 「体重(A)から 高血圧に なる 確率(B)を 予測する」 場合など、 Bを 0と1の 間、 つまり 確率で 表示できることが 前提と なります。
上記を
ロジスティック回帰 - 人工知能に
skeatlearn の
Lec80-81_多クラス分類
上記が
実装
実装は
過去の
この
使用する function を 定義する
以下、
# --------------------------------------------------------------------- # 文字列の処理に必要なfunctionの定義 # ------------------------------ from sets import Set stop_words = Set(['name', 'not', 'the', 'usr', 'you', 'version', 'this']) # stop word のチェック # 2文字以下の文字列、クラスタリングした結果、 # ラベルとして、出力されたあまり意味のわからない単語を除外 def __check_stop_word(word): if word in stop_words: return False if len(word) <= 2: return False return True # キーワードを区切る def split_keyword(text): keywords = text.split(" ") return [keyword for keyword in keywords if __check_stop_word(keyword)] # ストップワードを除外する def exclude_stop_words(text): return " ".join(split_keyword(text))
/Library/Python/2.7/site-packages/ipykernel_launcher.py:4: DeprecationWarning: the sets module is deprecated after removing the cwd from sys.path.
以下、
python sklearn LogisticRegression の
#------------------------------------------------------------------- # TSVのカラム値を抜き出すfunction #------------------------------------------------------ # learning_tsv parseする def read_from_learning_tsv(index): lines = [] row_count = 1 for line in open('LearningData.tsv', 'r'): if row_count != 1: arr = line.split("\t") # キーワードカラムを取り出す lines.append(arr[index]) row_count += 1 return lines # report tsv を parse する def parse_report_tsv(): lines = [] row_count = 1 for line in open('ReportData.tsv', 'r'): if row_count != 1: arr = line.split("\t") # キーワードカラムを取り出す lines.append(arr[1]) row_count += 1 return lines
分類ラベリング処理
処理順序は
- 学習データと、
サーチコンソールからの 取得結果の キーワードを マージ、 キーワード内の 単語の 出現頻度を 数えて、 結果を 素性ベクトル化する。 - LogisticRegression で
学習
順に処理を
1.学習データと、 サーチコンソールからの 取得結果の キーワードを マージ、 キーワード内の 単語の 出現頻度を 数えて、 結果を 素性ベクトル化する。
# ---------------------------------------------------------------------------- # 1. 学習データと、サーチコンソールからの取得結果のキーワードをマージ、キーワード内の単語の出現頻度を数えて、結果を素性ベクトル化する。 # ------------------------------------------------------ # LogisticRegression を import from sklearn.linear_model import LogisticRegression logreg = LogisticRegression() keywords = [] # 学習データからキーワードを取得 for line in read_from_learning_tsv(0): keywords.append(exclude_stop_words(line)) #keywordsの出力 keywords
['404', 'api', 'bootstrap', 'brew', 'centos', 'cron', 'django', 'docker', 'dropwizard', 'easybatch', 'eclipse', 'eclipselink', 'elasticsearch', 'findbugs', 'flyway', 'git', 'google analytics api', 'gradle', 'infer', 'intellij', 'jackson', 'java', 'jetty', 'lambok', 'linux', 'lombok', 'maven', 'memcached', 'mezzanine', 'mongodb', 'nvd3', 'openscap', 'owasp', 'pmd', 'postgres', 'pycharm', 'pylint', 'python', 'querydsl', 'redpen', 'rundeck', 'sonarqube', 'spring-boot', ..., 'brew link', ..., '\xef\xbd\x82\xef\xbd\x92\xef\xbd\x85\xef\xbd\x97', 'could find executable `gauge_home`, `path` `gauge_root`', 'want have current date default, use `django.utils.timezone.now`']
# 実データからキーワードを取得 for line in parse_report_tsv(): keywords.append(exclude_stop_words(line)) keywords
['404', 'api', 'bootstrap', 'brew', 'centos', 'cron', 'django', 'docker', 'dropwizard', 'easybatch', 'eclipse', 'eclipselink', 'elasticsearch', 'findbugs', 'flyway', 'git', 'google analytics api', 'gradle', 'infer', 'intellij', 'jackson', 'java', 'jetty', 'lambok', 'linux', 'lombok', 'maven', 'memcached', 'mezzanine', 'mongodb', 'nvd3', 'openscap', 'owasp', 'pmd', 'postgres', 'pycharm', 'pylint', 'python', ..., 'wsgirequest\n', '\xe3\x82\xaf\xe3\x82\xa8\xe3\x83\xaa\xe3\x82\xb9\xe3\x83\x88\xe3\x83\xaa\xe3\x83\xb3\xe3\x82\xb0\n', ...]
キーワードを
ベクトル化には、CountVectorizer
を
from sklearn.feature_extraction.text import CountVectorizer import numpy as np # テキスト内の単語の出現頻度を数えて、結果を素性ベクトル化する(Bag of words) count_vectorizer = CountVectorizer() # csr_matrix(疎行列)が返る feature_vectors = count_vectorizer.fit_transform(keywords) # 学習したデータのみ切り出し learning_vectors = feature_vectors[:len(read_from_learning_tsv(0))] # データに対応したラベルを取得 learning_labels = np.array(read_from_learning_tsv(1)) learning_vectors
<940x2303 sparse matrix of type '<type 'numpy.int64'>' with 2650 stored elements in Compressed Sparse Row format>
learning_labels
array(['404\n', 'api\n', 'bootstrap\n', 'brew\n', 'centos\n', 'cron\n', 'django\n', 'docker\n', 'dropwizard\n', 'easybatch\n', 'eclipse\n', 'eclipselink\n', 'elasticsearch\n', 'findbugs\n', 'flyway\n', 'git\n', 'google analytics api\n', 'gradle\n', 'infer\n', 'intellij\n', 'jackson\n', 'java\n', 'jetty\n', 'lambok\n', 'linux\n', 'lombok\n', 'maven\n', 'memcached\n', 'mezzanine\n', 'mongodb\n', 'nvd3\n', 'openscap\n', 'owasp\n', 'pmd\n', 'postgres\n', 'pycharm\n', 'pylint\n', 'python\n', 'querydsl\n', 'redpen\n', 'rundeck\n', 'sonarqube\n', 'spring-boot\n', 'VIP\xe7\x9b\xae\xe9\xbb\x92\n', 'webfonts\n', 'wecker\n', 'wicket\n', '\xe6\x8a\x80\xe8\xa1\x93\xe7\x9a\x84\xe8\xb2\xa0\xe5\x82\xb5\n', '\xe6\xad\xaf\xe7\x9f\xb3\n', '\xe7\x84\xa1\xe6\xb0\xb4\xe9\x8d\x8b\n', 'python\n', 'django\n', ..., '\xe6\x8a\x80\xe8\xa1\x93\xe7\x9a\x84\xe8\xb2\xa0\xe5\x82\xb5\n', 'brew\n', 'intellij\n', 'django'], dtype='|S21')
LogisticRegression で 学習
sklearn でLogisticRegression
を
学習自体は、
from sklearn.linear_model import LogisticRegression # 訓練 clf = LogisticRegression() # 第一引数が、行列で、第二引数がラベル clf.fit(learning_vectors, learning_labels)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False)
clf.score(learning_vectors,learning_labels)
0.95851063829787231
スコアは
完全に
scores = clf.predict_proba(feature_vectors[len(read_from_learning_tsv(0)):]) scores
array([[ 0.0072083 , 0.00733393, 0.01281034, ..., 0.00729888, 0.00853825, 0.00572039], [ 0.0108284 , 0.01104879, 0.02168211, ..., 0.01098717, 0.01321534, 0.00829743], [ 0.00546877, 0.00554397, 0.00793214, ..., 0.00552303, 0.00624621, 0.0045466 ], ..., [ 0.00923513, 0.00939803, 0.01667396, ..., 0.0093525 , 0.0109664 , 0.00730852], [ 0.01520893, 0.01553761, 0.03245924, ..., 0.01544563, 0.01881058, 0.01149057], [ 0.01565687, 0.0159937 , 0.0330284 , ..., 0.01589945, 0.01934464, 0.01184186]])
clf.classes_
array(['404\n', 'VIP\xe7\x9b\xae\xe9\xbb\x92\n', 'api\n', 'bootstrap\n', 'brew\n', 'centos\n', 'cron\n', 'django', 'django\n', 'docker\n', 'dropwizard\n', 'easybatch\n', 'eclipse\n', 'eclipselink\n', 'elasticsearch\n', 'findbugs\n', 'flyway\n', 'git\n', 'google analytics api\n', 'gradle\n', 'infer\n', 'intellij\n', 'jackson\n', 'java\n', 'jetty\n', 'lambok\n', 'linux\n', 'lombok\n', 'maven\n', 'memcached\n', 'mezzanine\n', 'mongodb\n', 'nvd3\n', 'openscap\n', 'owasp\n', 'pmd\n', 'postgres\n', 'pycharm\n', 'pylint\n', 'python\n', 'querydsl\n', 'redpen\n', 'rundeck\n', 'sonarqube\n', 'spring-boot\n', 'webfonts\n', 'wecker\n', 'wicket\n', '\xe6\x8a\x80\xe8\xa1\x93\xe7\x9a\x84\xe8\xb2\xa0\xe5\x82\xb5\n', '\xe6\xad\xaf\xe7\x9f\xb3\n', '\xe7\x84\xa1\xe6\xb0\xb4\xe9\x8d\x8b\n'], dtype='|S21')
分類
predict_proba
で
ドキュメントをLogisticRegression
は、decision_function
関数も
predict_proba
で 確率から ラベルを 決定する
def __get_max_label(array, score): index = 0 max_value = 0 for i in range(len(array)): item = array[i] if max_value < item: max_value = item index = i return {score[index]: max_value} scores = clf.predict_proba(feature_vectors[len(read_from_learning_tsv(0)):]) labels = [] classes = clf.classes_ for score in scores: max_dict = __get_max_label(score, classes) for k, v in max_dict.items(): if v > 0.50: # 0.50 より大きい場合、ラベルを設定 labels.append(k) else: # 上記以外の場合は、"unknown" labels.append("unknown") labels
['pycharm\n', 'unknown', 'python\n', 'unknown', 'unknown', 'python\n', ...., 'pycharm\n', ...]
max_scores
[0.5972620299284529, 0.3383567214750145, 0.68749652202323097, 0.2940551988600052, 0.064129770355348184, 0.62262596966624917, 0.68749652202323097, 0.39244242700503712, 0.5972620299284529, ..., 0.5972620299284529, ...]
from collections import Counter counter = Counter(labels) counter
Counter({'api\n': 327, 'bootstrap\n': 210, 'brew\n': 576, 'django': 13, 'django\n': 1969, 'dropwizard\n': 7, 'eclipselink\n': 105, 'elasticsearch\n': 312, 'findbugs\n': 31, 'google analytics api\n': 9, 'gradle\n': 46, 'intellij\n': 2, 'jackson\n': 634, 'java\n': 30, 'linux\n': 93, 'maven\n': 41, 'mezzanine\n': 74, 'mongodb\n': 198, 'pmd\n': 94, 'postgres\n': 40, 'pycharm\n': 460, 'python\n': 889, 'querydsl\n': 77, 'rundeck\n': 11, 'sonarqube\n': 652, 'unknown': 9002, 'wicket\n': 682})
半数は、
decision_function
関数を 実行してみる
clf.decision_function(feature_vectors[len(read_from_learning_tsv(0)):])
array([[-4.60120198, -4.58374887, -4.01834957, ..., -4.58858833, -4.43002503, -4.83446962], [-4.41044913, -4.39005257, -3.70388085, ..., -4.39571457, -4.20856081, -4.67951099], [-4.93822167, -4.9244657 , -4.56311987, ..., -4.92827896, -4.80428225, -5.12410495], ..., [-4.58909269, -4.57142767, -3.99003962, ..., -4.57633428, -4.4153638 , -4.8251841 ], [-4.34944285, -4.3277831 , -3.57658474, ..., -4.33379832, -4.13384321, -4.63295136], [-4.35313569, -4.33157371, -3.59229901, ..., -4.33756156, -4.13859456, -4.63553464]])
値が返ってきます。predict_proba
が
LogisticRegression で 学習 (Cを 変更する )
LogisticRegression に
python sklearn で
from sklearn.linear_model import LogisticRegression # 訓練 clf = LogisticRegression(C=100) # 第一引数が、行列で、第二引数がラベル clf.fit(learning_vectors, learning_labels)
LogisticRegression(C=100, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False)
scores = clf.predict_proba(feature_vectors[len(read_from_learning_tsv(0)):]) scores
array([[ 3.92285260e-04, 4.30773733e-04, 1.03282309e-03, ..., 3.97506780e-04, 4.15491513e-04, 2.20233853e-04], [ 6.92312831e-04, 7.86450652e-04, 2.58540527e-03, ..., 7.04069206e-04, 7.48335417e-04, 3.19176940e-04], [ 2.02331944e-04, 2.17160118e-04, 3.35685910e-04, ..., 2.04163691e-04, 2.11255775e-04, 1.31034168e-04], ..., [ 4.12031300e-04, 4.52898646e-04, 1.05029520e-03, ..., 4.17132918e-04, 4.36528567e-04, 2.26749311e-04], [ 1.32888692e-02, 1.59165270e-02, 1.79309604e-01, ..., 1.36033269e-02, 1.48212636e-02, 4.98320204e-03], [ 1.83712824e-02, 2.19693381e-02, 2.09757293e-01, ..., 1.88044747e-02, 2.04704374e-02, 6.95727218e-03]])
def __get_max_label(array, score): index = 0 max_value = 0 for i in range(len(array)): item = array[i] if max_value < item: max_value = item index = i return {score[index]: max_value} scores = clf.predict_proba(feature_vectors[len(read_from_learning_tsv(0)):]) labels = [] for score in scores: max_dict = __get_max_label(score, classes) for k, v in max_dict.items(): if v > 0.50: # 0.50 より大きい場合、ラベルを設定 labels.append(k) else: # 上記以外の場合は、"unknown" labels.append("unknown") labels
['pycharm\n', 'memcached\n', 'python\n', ..., 'pycharm\n', ...]
from collections import Counter counter = Counter(labels) counter
Counter({'404\n': 78, 'VIP\xe7\x9b\xae\xe9\xbb\x92\n': 51, 'api\n': 593, 'bootstrap\n': 244, 'brew\n': 652, 'centos\n': 21, 'cron\n': 4, 'django': 16, 'django\n': 2249, 'docker\n': 3, 'dropwizard\n': 92, 'easybatch\n': 35, 'eclipse\n': 10, 'eclipselink\n': 150, 'elasticsearch\n': 340, 'findbugs\n': 160, 'flyway\n': 24, 'git\n': 14, 'google analytics api\n': 129, 'gradle\n': 213, 'infer\n': 12, 'intellij\n': 129, 'jackson\n': 964, 'java\n': 698, 'jetty\n': 27, 'lambok\n': 2, 'linux\n': 161, 'lombok\n': 29, 'maven\n': 121, 'memcached\n': 133, 'mezzanine\n': 113, 'mongodb\n': 304, 'nvd3\n': 24, 'openscap\n': 7, 'owasp\n': 8, 'pmd\n': 143, 'postgres\n': 230, 'pycharm\n': 500, 'pylint\n': 5, 'python\n': 1212, 'querydsl\n': 151, 'redpen\n': 6, 'rundeck\n': 81, 'sonarqube\n': 744, 'spring-boot\n': 44, 'unknown': 4583, 'webfonts\n': 28, 'wecker\n': 4, 'wicket\n': 936, '\xe6\x8a\x80\xe8\xa1\x93\xe7\x9a\x84\xe8\xb2\xa0\xe5\x82\xb5\n': 32, '\xe6\xad\xaf\xe7\x9f\xb3\n': 52, '\xe7\x84\xa1\xe6\xb0\xb4\xe9\x8d\x8b\n': 23})
C = 1.0 の
unknown に
まとめ
Google Search Console からsklearn LogisticRegression
ロジスティック回帰 で
以下、
- Cの値を
大きくすると、 正規化が 弱くなり、 ラベリングに ばらつきがでた。 C=1.0 > 正規化が 強い。 C=100 > 正規化が 弱い。 - C以外にも
指定できる パラメータは あるので、 チューニングの 余地は 途方も なくなる。 - ぱっとみの
分類精度は、 ナイーブベイブで 分類した ときの ほうが 良さげ。
以上です。
コメント