ソースコードをWord2Vecとk-meansでクラスタリングしてみた。

通常は自然言語処理に使うWord2Vecですが、まぁソースコードも(プログラミング)言語なわけで、使えるんじゃないかなと。
ってなわけで緩くやっていきます。

今回主に活躍したツール(というかライブラリというか)

  • pygments
    プログラミング言語版の形態素解析ツールみたいな感じです(本来の用途かは知らないけど)。
    自然言語処理で使うmecabみたいな感じで使います。
  • Word2Vec
    簡単にいうと単語をベクトル化してくれるものです。これをすると様々な方法で単語間の距離を測れたり求めたりすることができるようになります。
  • scikit-learn
    Python用の機外学習ライブラリです。今回は簡単なのでこの中のk-meansライブラリを使います。ただしこのライブラリのk-meansは距離関数が変えられないので注意。確かユークリッド距離を使ってるのかな。

使用したデータ

今回は適当に apt-get コマンドで手に入るLinuxのソースコードのうち、Pythonで書かれたコードについて利用しました。
(実際にはちょっと別のPythonファイルも混ざってるけどw)

ソースコードの取得方法についてはこの記事を参考にした記憶があります。

サイト | Ubuntu 16.04: apt sourceでソースコードをダウンロードする

実際やった内容

  1. ソースコードをトークンごとに分かち書きにする。
    これをプログラミング言語ごとにやってあげます。
    今回はLinuxのソースファイルに含まれるPythonのソースコードについて行いました。
    pygments を使用した分かち書きのためのコードは github にアップしてますので0.01mmでも参考になればいいな。

  2. 分かち書きにした内容から Word2Vec でベクトルモデルを作成する。
    Python のライブラリに gensim ってものがあって、そこのWord2Vecを使用しました。
    ソースコードは以下の通りです。(前後関係は5くらいにしてます。)

from gensim.models import word2vec

sentences = word2vec.Text8Corpus('splitPython.txt')

model = word2vec.Word2Vec(sentences, size=100, min_count=2, window=5)
model.save("./splitPython.model")
  1. scikit-learn の k-means でクラスタリングしてあげる。
    コードはこちらのサイトを参考にしています。
    https://hironsan.hatenablog.com/entry/clustering-word-vectors
    これで一通り終わり。

結果

クラスタの数を幾つにするかは諸説あったりして、まぁいろいろ試して良かったものをえると思うのですが、いろいろ試すのめんどくさいので適当に決めてます。(笑)

結果

result = KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
n_clusters=20, n_init=10, n_jobs=-1, precompute_distances='auto',
random_state=42, tol=0.0001, verbose=1)
['import', 'from']
['tweepy', 'auth', 'number', 'version_info', 'insert', 'abspath', 'copyright', 'some', '@tempboxa', 'warning', 'it', 'latex_documents', 'tags', 'search', 'tcm_mod_err', 'fabric_mod_dir_var', 'mkdir', 'fo', 'main', 'input', 'add_option', 'unpack', 'Markus', 'Heiser', 'license']
['CONSUMER_KEY', 'CONSUMER_SECRET', 'ACCESS_TOKEN', 'ACCESS_TOKEN_SECRET', 'screen_name', 'API', 'FAVORITE', 'get_lexer_by_name', 'lexer', 'token_it', 'token_string', 'major', 'minor', 'patch', 'loadConfig', 'extensions', 'master_doc', 'author', 'makefile_version', 'makefile_patchlevel', 'finally', 'release', 'language', 'sphinx_rtd_theme', 'ImportError']
['=', '*', 'and', '>', 'the', '/']
['.', 'format', 'if', '==', 'else', 'line', 'elif', 'write', 'False', 'len', 'True', 'return', 'not', 'continue']
['(', ')', 'print']
[',']
['for', 'i', 'in', '<', 'p']
['range', 'status', 'id', 'sphinx', 'project', 'strip', 'version', 'stderr', 'font', 'be', 'py', 'add', 'argv', 'j', 'r', 'num', 'replace', 'subprocess', 'tcm_dir', 'fabric_mod_dir', 'proto_ident', 'bufi', 'lower', 'help', 'dest']
[':', 'dict', 'name']
['+', 'fabric_mod_name', 'buf']
['text', 'idx', 'key', 'split', 'break', 'pass', 'with', 'color', 'table', 'f', 'string', 'fabric_mod_port', 'msg', 'exit', 'ret', 'global', 'while', 'type', 'str', 'struct', 'read', 'stdout', 'c', 'directive', 'directives']
['sys', 'os', 'path']
['[', ']']
['or', 'append', 'None', 'x', 'a', 'end', 'as', 'of', 'to', 'e', 'is', 'nodes']
['try', 'open', 'val', 'except', 'int', 'close', '!=', 'parser', 'args']
['{', '}', ';']
['%', 'def']
['-', '~']
['re', 'm', 'match', 'group']

クラスタ数(20)とかその辺はとりあえず適当に決めてます。
今回はソースコード中のコメント等は前処理で抜いてますが、カッコや句読点等のトークンはそのままなのでなんかすごいことになってますが、[‘import’, ‘from’], [‘for’, ‘i’, ‘in’, ‘<‘, ‘p’]等、それなりにいい感じにクラスタリングされている気がします。(ちなみに試しにカッコや句読点などのその他のトークンを除いたものではまた違った結果が出てきて面白いです。)

後書き

まぁこれが何に使えるかはわからないけど笑