はてなブログのブックマーク数を予測するwebアプリを作った

タイトルのとおり、はてなブログの記事の内容からはてなブックマーク数(はてブ数)を予測するwebアプリを作りました。

※追記: コストが増加してきたので一旦サービスを止めています…

以下では背景や方法、システムの概要や今後の展望について説明します。

背景

近年、インフルエンサーという言葉に代表されるようにSNSなどで発信力を持つことが重要になっています。インフルエンサーが発信する情報がバズることで、情報を多くの人に伝えることが可能になります。

また、企業が技術ブログを運用して自社の技術を発信することも増えています。企業のブランディングという点でも、技術ブログをバズらせて多くの人に見てもらうことは重要です。しかし、バズる記事を作成することは簡単ではありません。

そこでバズる記事を書く支援をするシステム、バズり支援システム(Buzz Automation System; BAS)が求められます。例えば、下書きを作成してシステムに入力すると、文章の構成などを最もバズるように再構成するような利用例が考えられます。

今回はバズり支援システムの第一段階として、企業の技術ブログとして用いられていることが多い、はてなブログのはてブ数を予測するシステムを作成します。作成に至った一番の大きな理由は、私が機械学習を使ったwebアプリを実際に運用・改善したかったからです。

方法

学習データの記事とはてブ数の収集、学習処理について説明します。

記事とブクマ数の収集

ここに一番時間がかかりました。。。最終的に以下の手順で収集しました、

つまり、ある時点のホットエントリのページを起点に、

ページ -> そのページをブックマークしているユーザー -> そのユーザーが他にブックマークしているドメイン

のようにドメインを収集して、そのドメインを起点にクローリングしています。

ものすごく周りくどいですが、ブックマークされていないページを収集することが難しく、上のようなドメインを収集することに着目した手順になりました。

ある時点のホットエントリを利用して、959個のドメインを収集することができました。そこからランダムに100個のドメインを抽出してクローリングを行い、77176個のページを収集することが出来ました。

ドメインを取得できていることを前提とした、クローラーのソースコードは以下です。

github.com

はてブ数の学習

問題を以下の2つに切り分けました。

  • はてブされるかどうかの分類問題
  • はてブされる記事の中ではてブ数の回帰問題

1つでもブックマークされる記事とされない記事では、記事のクオリティにかなり差がある、もしくはブログの存在自体が全く認知されてないことが考えられます。 よって、まずはてブされるかどうかの分類を行い、はてブされると判定された記事に対してははてブ数の回帰を行うようにしました。

はてブ分類問題

正解ラベルとして、はてブされていない記事に0、1つでもはてブされている記事に1のラベルを与えます。

特徴量としては、記事の文章を分かち書きした単語のBoWにSVDで次元削減を行ったもの、単語数、文字数を用いました。今回は早くサービスとして公開したかったため、かなり適当に決めました。

バズるかどうかは、画像の存在もかなり大きいと思いますし、また先程述べたようにブログの存在自体の認知も影響すると思うので、過去の記事やはてブ数も効果的な特徴量な気がします。また、技術的な記事ではプログラムが記載されていることも多く、自然言語とは別な扱いをした方が良い気もします。これらは、今後の課題とします。

また分類器としては、CatBoostを用いました。これもハイパーパラメータチューニングを行いたくなく、デフォルトのパラメータでそこそこの性能が出ると言われているCatBoostを採用しました。

はてブ数回帰問題

はてブ数1以上の記事に対して、回帰モデルを学習します。ただ、はてブ数が少ない記事の方が圧倒的に多くなっており、数千のはてブ数の記事がぽつぽつとある分布になっています。

全てを使って学習すると、少数のはてブ数が多い記事に悪影響を受けそうだったので、はてブ数があるしきい値以上の記事は学習データから除去しました。はてブ数の95パーセンタイル値で74だったので、そこらへんの値をしきい値にしようと思いましたが、一旦雰囲気でしきい値を100に設定しました。

この時点でバズる記事の予測が出来なくなっていますが、これも今後の課題とさせていただきます。

特徴量や回帰モデルは分類問題と同じです。

システムの概要

紆余曲折があり、以下のようなアーキテクチャになりました。基本的にAWSで構築しています。

f:id:pompom168:20200121012006p:plain

後段のEC2では、任意のはてなブログのURLに対してコンテンツをスクレイピングしてはてブ数を予測するAPIが動いています。実装はfastapiで行いました。前段のEC2では、web/APサーバとしてのgunicornを使用してflaskのwebアプリを動かしており、APIにリクエストを送ります。ELBを挟んでいますが、これは冗長化のためではなくSSL化のためです。

この構成になっている理由は、最初は以下の図のようなSPA (Single Page Application) + APIの構成を考えていたからです。APIもGoで実装しようかと思っていたんですが、リリースの早さを優先しPythonで実装しました。fastapiも使ってみたかったこともあるので、とりあえず良しとしました。また、リリースの早さを優先した結果フロント部分をSPAにすることも諦めました。結局PythonでFlaskを使用することにしました。

f:id:pompom168:20200121004356p:plain

ただ、EC2が存在する構成だと割とお金がかかるので、以下のようなサーバレスな構成に移行したいと今は考えています。

f:id:pompom168:20200121004416p:plain

APIとwebアプリそれぞれのリポジトリは以下にあります。

github.com

github.com

まとめと今後の展望

バズり支援システムの第一弾として、はてなブログのブクマ数を予測するwebアプリを作りました。ただ、今後やらなければならない/やりたいことが山ほどあるので改善していこうと思っています。以下のようなことを考えています。

予測に関すること

  • 特徴量に画像を使う
  • はてブ数が100を超えるような記事についても予測を行えるようにする
  • コードの部分をよしなに処理する
  • 予測するページより過去の情報を組み込む
  • モデルの更新を行い最新の情報を使う
  • 外部のトレンド情報(Twitterなどで話題になっていること)を組み込む
  • はてなスターの情報も使用する

システムに関すること

  • モデルの更新を行うための、記事収集・学習ワークフロー
  • フロントのSPA
  • APIをGoで実装
  • サーバレスなアーキテクチャに
  • デプロイ周りを整える
  • CI/CD

ちなみにこの記事の予測ブクマ数は25でした。やはり、バズり支援システム(Buzz Automation System; BAS)の登場が待たれます。