HireRoo TechBlog

HireRooの技術ブログ

Githubから技術力を算出する技術の裏側

こんにちは!株式会社ハイヤールー代表葛岡(@kkosukeee)です。久しぶりのテックブログ投稿になります。

本記事では最近リリースされましたHireRooの新機能である「スキルシート機能」の裏側と使用技術・設計に関して執筆いたします。

スキルシート機能は現在Githubの公開情報のみを情報源としますが、Qiita, Twitterなど様々な情報源から有益な情報を取得することができ、様々な分野で応用することが可能です(弊社の場合候補者様の最新の技術トレンドなどを履歴書・職務経歴書がわりに提供しています)。

本記事が少しでもブラックボックス化している公開情報を用いた情報抽出や自然言語処理を用いたスコアリングを明瞭化し、更には同様の機能を開発しようと考えている開発者のご参考になれば幸いです。

なお本機能の開発・リリースはディー・エヌ・エー時代にご一緒しました、MLOpsエンジニア@toyama0919に主導していただきました(ありがとうございました!)。代理執筆になりますが、キャッチアップしながらまとめてみたので是非ご一読下さい。

スキルシート機能とは?

f:id:KKosukeee:20210722160910p:plain
写真1:スキルシートから得られる情報

スキルシート機能とは候補者様がコーディング試験を受ける前に自然言語処理を用いて公開情報などから傾向や技術トレンドなどを自動抽出し、企業様に選考前の情報として提供する機能です。

現段階(2021年7月現在)のスキルシートの情報源はログイン時に連携するGitHubのみで、取れる情報はコミット履歴、得意な言語、経験スキル、興味分野の4つです(内コミット履歴は草情報に等しいです)。

直接選考の判断に使用されることは想定していませんが、これらの情報が選考前にわかることでより質の高い選考が実現できるという仮説をもとに開発された機能です。

システム構成図

HireRooではマイクロサービスアーキテクチャを採用しており、スキルシートを支えるシステムもこのマイクロサービス基盤上で動いています。マイクロサービス基盤に関しては別記事があるのでそちらをご参考にして下さい。

f:id:KKosukeee:20210722161039p:plain
図1:スキルシート機能を実現するサービス群

上図の通りスキルシートの機能を支えるためには大きく2つのサービスがあります。1つはユーザーに紐づくスキルシートを管理しているResumeサービス、もう1つはGitHubのユーザー情報を元にアカウントを自然言語処理を用いて解析するためのAnalyzeサービスがあります。

Resumeサービスは比較的シンプルな構成で設計されており、他のマイクロサービス同様にGoのgRPCサーバーでAnalyzeサービスが解析した結果などをCloudSQLに格納しています。以下のようなインターフェイスをクライアントに公開することでスキルシートのCRUDを可能としております。

service ResumeService {
  rpc CreateResume(CreateResumeRequest) returns (CreateResumeResponse);
  rpc GetResume(GetResumeRequest) returns (GetResumeResponse);
}

message CreateResumeRequest {
  string access_token = 1 [(validate.rules).string.min_len = 1];
  string candidate_id = 2 [(validate.rules).string.min_len = 1];
}

message CreateResumeResponse {
  Resume resume = 1;
}

message GetResumeRequest {
  string candidate_id = 1 [(validate.rules).string.min_len = 1];
}

message GetResumeResponse {
  Resume resume = 1;
}

AnalyzeサービスはPythonのgRPCサーバーとしてCloudRun上で運用されており、ResumeサービスがGitHubのユーザー情報と一緒に解析するためのRPCにリクエストを投げると後述のアルゴリズムと自然言語処理を用いてGitHubの公開情報から候補者のスキルを自動で抽出します。インタフェースの定義も非常にシンプルで以下のようになっています。

service AnalyzeService {
  rpc AnalyzeGithub(AnalyzeGithubRequest) returns (AnalyzeGithubResponse);
}

message AnalyzeGithubRequest {
  string access_token = 1;
}

message AnalyzeGithubResponse {
  repeated Language languages = 1;
  repeated Skill skills = 2;
  repeated Interest interests = 3;
  repeated Contribution contributions = 4;
}

前述の通り現在はGitHubのみを情報源とするため、RPCは1つですが、今後情報源が増えるにつれてRPCも増えていく予定です。実際にどのようにGitHubから情報を抽出しているかは後ほど詳細で触れますが、ざっくりとした構成は以上になります(シンプルで良いですね)。

Github解析ロジック

前述の通りResumeサービスは比較的シンプルな構成になっているため詳細は割愛しますが、候補者の技術力を自然言語処理を用いて解析するAnalyzeサービスは少し複雑な処理をしています。Analyzeサービスは大きく4つの情報を抽出するのですが、その中でも特に複雑な経験スキルと興味分野情報抽出に関しての各ステップをここでは説明します。

アルゴリズム自体はよくあるレコメンドエンジン同様で、事前に公開レポジトリをクローリングし、候補者がコミットしているレポジトリに類似したレポジトリを近傍探索で検索し、類似したレポジトリが持っているトピックを元に経験スキルや興味分野を算出する仕組みです。以下に各ステップの詳細をまとめます。

1: レポジトリのクローリング

GitHubにはトピックという概念が存在しており、トピックを元にレポジトリの検索などができます。例えば以下のリンクは machine-learning というトピックの検索結果であり、Google社製のMLライブラリであるTensorflowやFacebook社製のMLライブラリであるPyTorchなどが結果から確認できます。このように事前にインデックスを構築するための公開レポジトリをそれぞれのトピックを元に検索しクローリングします。

github.com

ここで重要となってくるのは、今回のユースケースではトピックがついてないレポジトリはゴミデータとなるため、必ずトピックが1つ以上ついているレポジトリのみをクローリングするということです。幸いにもトピックからレポジトリを検索した場合に結果として出てくるレポジトリは全てクエリトピックを所有しているため、特段今回はハンドリングが要りません。

また全てクローリングするわけではなく、APIを叩くところはAPIを叩き取得し、GitHubのREADME情報などのcurlを投げれば取れる情報はAPIを使用せず取得するというハイブリッドな方法でレポジトリの情報を取得しています。こうすることでGitHubの厳しいAPI制限に引っかかることなく、必要なレポジトリの情報を抽出することができます。

2: 特徴ベクトルの算出・インデックス構築

クローリングされたレポジトリはそのままでは不要な情報が多すぎます。例えばソースコード自体を自然言語で解析してもどういったレポジトリなのかというのは解析できませんし、誰がコミットしているという情報も少なくともコミッターの属性が抽出できていない環境下では必要のない情報となってしまいます。

レポジトリのカテゴリを識別するために必要な質の高い情報はレポジトリの説明とREADMEのテキストデータとなります。参考にしたのはGitHubのブログ記事であり、この場合はトピックのないレポジトリにトピックをアサインする用途であり今回のユースケースに近い形となっていました。

ステップ1でクローリングしたレポジトリの説明文とREADMEはテキストデータとしてConcatされ、TF-IDFのアルゴリズムをもとに特徴ベクトルに変換されます(TF-IDFの詳細は割愛しますが、こちらの記事が参考になります)。TF-IDFで特徴ベクトルに変換するためにscikit-learn を使用しており、以下のコードが特徴ベクトルを抽出するコードとなります。至ってシンプルで特に複雑なことはしておりません。

from sklearn.feature_extraction.text import TfidfVectorizer

# データセット取得関数
datasets = get_datasets()

# データセットをベクトル化
vectorizer = TfidfVectorizer(max_features=constants.MAX_FEATURES)
transform = vectorizer.fit_transform(datasets)
repository_vectors = transform.toarray().tolist()

開発過程でgensimなどを使用しDoc2VecなどTF-IDFよりは少し高度なモデルを使用しましたが、定量・定性的な観点と計算量やメモリ更には複雑度などの観点から今回はシンプルにTF-IDFのモデルを使用することにしました。それぞれのベクトルをPCAを行い次元圧縮し、可視化した結果は以下のようになります。なんとなくではありますが、Tensorflowの周りにChainerやFastAIなどがいて、それっぽさはあります。

f:id:KKosukeee:20210722200303p:plain
写真2:Tensorflowに類似するレポジトリ群

3: Elasticsearchを使った近傍探索

さて潜在空間にそれぞれのレポジトリを落とし込んだ後は、候補者が実際にコミットしているレポジトリ(以下クエリレポジトリ)に類似しているレポジトリを近傍探索で見つける必要があります。近傍探索を効率的に行えるライブラリなどは多数存在していますが、今回は開発コストを極力下げたいこともあり、Elasticsearchを使用し実現しました。

ElasticsearchにはベクトルをCosine類似度などから効率的に計算できる機能があります。今回はこれを使用してクエリレポジトリを元にステップ2で作成したインデックスに対して検索をかけます。検索部分の実際のコードは以下のようになります。

def get_by_vector(self, query_vector, size=10):
    source = "cosineSimilarity(params.query_vector, 'repository_vector')"
    script_query = {
        "script_score": {
            "query": {"match_all": {}},
            "script": {
                "source": source,
                "params": {"query_vector": query_vector},
            },
        }
    }

    body = {
        "size": size,
        "query": script_query,
        "_source": {"includes": ["full_name", "languages", "topics"]},
    }
    response = self.client.search(index=self.index, body=body)
    return response.get("hits").get("hits")

こうして検索された類似レポジトリにはステップ1で説明した通り1つ以上トピックが付与されています。後はそれらのトピックを集計することで、類似レポジトリの属性情報が高精度で抽出できます。

最後に候補者がスターをつけているレポジトリから算出されたレポジトリは興味分野とみなし、過去に一定数コミットしたことある レポジトリから抽出されたトピックは経験スキルとして見なすことで以下のような情報が算出できます。私は過去にMachine Learningや競プロといったところに興味があったのですが、概ね間違っていなさそうな結果がであることが確認できます。

f:id:KKosukeee:20210722163107p:plain
写真3:kkosukeeeのアカウントから抽出された結果

まとめ

いかがでしたか?スキルシートは候補者の技術力や傾向をコーディング試験を行う前に把握するのに非常に有効な機能の1つであり、今後はキャリアログや発信力などを抽出することによって、コーディング試験で取れる情報と組み合わせてより候補者の技術力を可視化するために進化していきます。

このような類似探索するようなアルゴリズムを応用すると、レコメンドエンジンや画像検索エンジンなどを作成することができ、アイデア次第では非常に面白いアプリケーションを作成することができます(現にメルカリで開発していた画像検索はレポジトリが写真になっただけで、技術に大きな差異はありません)。

HireRooでは今後もこのような面白い技術を一緒に開発できる仲間を募集しております。もし少しでも興味を持たれた方がいれば、ぜひこちらのWantedlyリンクからご応募下さい。

またこの記事を読まれた方で、弊社サービスに少しでもご興味がある方は当社サービスサイトよりお気軽にお問い合わせいただければなと思います。それではまた!

hireroo.io