Google Search Console の キーワードを python で クラス分類してカテゴリ分けしてみた結果を記載します。
学習データに依るのかとは思いますが、Google Search Console の キーワードを python でクラスタリングする。 | Monotalk に続き、
カテゴリ分けの足がかりの部分はできた感はあります。1
[1].やはり実務に役立つかは謎です。。
前提
-
入力データ Search Analytics for Sheets - Google スプレッドシート アドオン で、データをGoogle スプレッドシートに Export。
Export 後のスプレッドシートをCSV化したデータを入力データとしています。
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 |
クラス分類の手順
以下の通り、処理を組みました。
クラスタリングには、sklearn
の LinearSVC
を使います。
-
Google スプレッドシートの学習データを
TSV
にして読み込み、キーワードを抽出。 -
サーチコンソールからの取得結果のGoogle スプレッドシートを
CSV
にして読み込み、キーワードを抽出。 -
学習データと、サーチコンソールからの取得結果のキーワードをマージ、キーワード内の単語の出現頻度を数えて、結果を素性ベクトル化する。
2
[2]キーワードをマージは、サーチコンソールからの取得結果のキーワードは時間が経つと変わっていく(増えていく)ため、
素性ベクトル作成時に、次元数を揃える?ために実施しています。(実際いらないのかもしれませんがたぶん必要) -
LinearSVC
で学習 -
CSV
をクラス分類して、スコアを取得、確からしさが低いデータについては、分類結果のラベルは付与せず、unknown
ラベルに置換 -
結果をCSVに書き出し、Google スプレッドシートにインポートし、グラフ表示
各種ライブラリのインストール
必要なライブラリをインストールします。
pip install sklearn numpy scipy pandas
以下のversionが、インストールされました。
pip list | grep -e sklearn -e numpy -e scipy -e pandas
---------------------------
numpy (1.12.1)
pandas (0.20.1)
scipy (0.19.0)
sklearn (0.0)
---------------------------
実装
以下、作ったpython プログラムになります。
search_console_svm_classsifier.py
以下、記事を参考に作成しました。
3.6. scikit-learn: Python での機械学習 — Scipy lecture notes
pythonの機械学習ライブラリscikit-learnの紹介 - 唯物是真 @Scaled_Wurm
-
search_console_svm_classsifier.py
# -*- coding: utf-8 - from __future__ import print_function import sys import numpy as np import pandas as pd from sets import Set from sklearn import svm from sklearn.feature_extraction.text import CountVectorizer 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(" ") after_text = " ".join([keyword for keyword in keywords if check_stop_word(keyword)]) return after_text # report csv を parse する def parse_report_csv(): lines = [] row_count = 1 for line in open('Google_Search_Console.csv', 'r'): if row_count != 1: arr = line.split(",") # キーワードカラムを取り出す lines.append(arr[1]) row_count += 1 return lines # 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 def get_min_abs_dict(array, score): # 最小絶対値として、設定されない値であろう[sys.maxint] を設定 min_abs = sys.maxint index = 0 for i in range(len(array)): item = abs(array[i]) if min_abs > item: min_abs = item index = i return {score[index]: min_abs} # メインメソッド def execute(): # ---------------------------------------------------------------------------- # 3. 学習データと、サーチコンソールからの取得結果のキーワードをマージ、キーワード内の単語の出現頻度を数えて、結果を素性ベクトル化する。 # ------------------------------------------------------ keywords = [] # 学習データからキーワードを取得 for line in read_from_learning_tsv(0): keywords.append(split_keyword(line)) # CSVからキーワードを取得 for line in parse_report_csv(): keywords.append(split_keyword(line)) # テキスト内の単語の出現頻度を数えて、結果を素性ベクトル化する(Bag of words) count_vectorizer = CountVectorizer() # csr_matrix(疎行列)が返る feature_vectors = count_vectorizer.fit_transform(keywords) print("word数:" + str(len(feature_vectors.toarray()[0]))) # 学習したデータのみ切り出し learning_vectors = feature_vectors[:len(read_from_learning_tsv(0))] # データに対応したラベルを取得 learning_labels = np.array(read_from_learning_tsv(1)) # ------------------------------------- # 4. `LinearSVC` で学習 # -------------------------------- # SVCのロード clf = svm.LinearSVC() # 学習 clf.fit(learning_vectors, learning_labels) # -------------------------------------- # 5. `CSV` をクラス分類して、スコアを取得、確からしさが低いデータについては、分類結果のラベルは付与せず、`unknown` ラベルに置換 # -------------------------------------- scores = clf.decision_function(feature_vectors[len(read_from_learning_tsv(0)):]) classes = clf.classes_ labels = [] for score in scores: min_abs_dict = get_min_abs_dict(score, classes) for k, v in min_abs_dict.items(): if 0.90 >= v: # 0.90 以下は、ラベルを設定 labels.append(k) else: # 0.90 より大きい場合は "unknown" labels.append("unknown") # CSVを再度読み込み、ラベル名を追加して、CSV書き出し output_df = pd.read_csv('Google_Search_Console.csv') output_df['label_name'] = labels output_df.to_csv('Google_Search_Console_Output.csv', encoding="utf-8", index=False) if __name__ == '__main__': execute()
-
説明
以下、説明を記載します。
1.
Google スプレッドシートの学習データをTSV
にして読み込み、キーワードを抽出。
TSV
にしているのは、キーワード文字列内にカンマが含まれているためです。
Google_Search_Console.csv
にももちろん含まれますが。。。一旦見ないことにしました。
現在(2017/05/21)だと、Google スプレッドシートのcsvは、値にカンマを含む場合に、
3.
学習データと、サーチコンソールからの取得結果のキーワードをマージ、キーワード内の単語の出現頻度を数えて、結果を素性ベクトル化する。
最初mecab
で形態素解析し、名詞、動詞、形容詞を抜き出していたのですが、あまりいい感じにならず、単純なスペース区切りでキーワードを分割し、
短すぎるワード(2文字以下)を除外して、結果を素性ベクトル化しました。
split_keyword
メソッド内で、実装しています。
素性ベクトル作成後、学習したデータのみ切り出すため、
learning_vectors = feature_vectors[:len(read_from_learning_tsv(0))]
で、TSV
データの行数を頼りにデータを抜き出しています。
4.
LinearSVC
で学習
LinearSVC
を使っているのは、SVC
で分類したところ、あまりスコアがよろしくなかったからです。
パラメータ指定なしのデフォルトで実行しただけなので、この辺りの指定を頑張れば、
もっといい感じで分類できるかもしれません。
5.
CSV
をクラス分類して、スコアを取得、確からしさが低いデータについては、分類結果のラベルは付与せず、unknown
ラベルに置換
decision_function
で、どのラベルが確からしいのか、決定境界からの距離が取得できます。
こいつは、正負の数値が設定されていて、その中で「絶対値がもっとも小さいラベル?」が確からしいので、
get_min_abs_dict
というメソッドを実装して、絶対値が最小のラベル名と、絶対値を辞書にして返すようにしました。
その後、絶対値が0.9
以下だったら、ラベルを使用し、そうでなければ、unknown
となるようにしていますが、
0.9
は、何度か実施してみて、「いい感じ」に間違いがなさそうになった値です。
ちなみに、LinearSVC
で、predict_proba
を実行したところ、そんなメソッドはないらしく、以下のエラーが出力されました。
AttributeError: 'LinearSVC' object has no attribute 'predict_proba'
6.
結果を CSVに書き出し、Google スプレッドシートにインポートし、グラフ表示
一旦CSVで吐き出して、Googleスプレッドシートに転記しました。
以下のような、キーワードのクラス分類結果のラベル名とClick数での円グラフが作成できました。3
[3].unknown
の割合がもっとも大きい..
検索キーワードをLinearSVCでクラス分類してみた感想
以下、実施した結果、定性的わかったことを記載します。
- 1単語のみのキーワードがうまく分類できていない。
3word 以上の分類は、結構うまくいっているように見えましたが、
例えばSonarQube 日本語
がうまく分類できず、unknown
になります。
これは、IntelliJ 日本語
をIntelliJ
とする学習データがあったりするのが、
影響にしているのだろうと思ったりします。
「カテゴリを示すキーワード」の重みづけをする。or 「学習データ」を調整するなどが必要に思いました。
- 形態素解析は不要?
形態素解析後のwordで学習、クラス分類してもいい感じにはならなかったです。
検索エンジンに対して、送りつけるワードは基本スペース区切りなので、
「なんらかの意味をもつWORDがスペース区切りで入力されている」前提で、分析したほうがいい結果がでるのかもしれません。
ここは、KMeans
でクラスタリングしてた時と同じでした。
クラス分類の方法としては、他の方法もあるので、
その他試してみて、丁度いいのを使おうかと思います。4
[4].今のままだと、使いものにならない感が..
以上です。
コメント