for Startups Tech blog

for Starupsのテックブログです

「すべては、スタートアップスのために。」for Startups(フォースタートアップス)の技術スタックを紹介します(2021年6月更新)

はじめに

フォースタートアップスCTOの戸村(@KenjiTomura)です。

フォースタートアップスでは展開している各事業の中で、プロダクトや管理ツールなどを開発しています。もちろんインフラ構築から監視、開発に採用する技術選定まで全てチームで議論して採択しています。

自由度があり非常にやりがいのある環境であると感じているのですが、「実際にどんなことをやっているのかよく分からない」というお声をいただくこともあります。本稿においてサービス(プロダクト)と技術視点でフォースタートアップスの取り組みと技術スタックを紹介します。

フォースタに興味のある方や、未来の一緒に働く仲間に読んでいただけると嬉しいです!

フォースタートアップスのご紹介

「for Startups」というビジョンのもと、インターネット/IoTセクターをはじめ、Fintech、リアルビジネス領域も含めた(IT、AI、SaaS、DeepTech、DisruptTech、ドローンテック、MaaS、5G市場など)の転職支援と起業支援を中核とした成長産業支援事業を推進しています。

サービスも提供しており、成⻑産業領域に特化した情報プラットフォーム「STARTUP DB(スタートアップデータベース)」(※スタートアップを中心とした13,000社以上の企業情報を掲載)を展開しています。

ここからフォースタートアップスで利用されている技術スタックについてご紹介させていただきます。

「サービス」と「技術スタック」のご紹介

1. STARTUP DB

STARTUP DBは、国内最大級の成長産業領域に特化した情報プラットフォームです。

企業データベースは、13,000社を越える日本のベンチャー・スタートアップ企業の情報を保有するとともに、起業家・投資家、エコシステムビルダーの方々累計150名以上のインタビューコンテンツをリリースしています。

また、世界最大級のベンチャー企業データベース「Crunchbase」とデータ連携、日本企業の情報を海外のプロフェッショナルに届けることで、国内の成長産業領域市場の発展に貢献しています。

企業情報は専任リサーチャー用の管理画面を用意し、毎日情報をキャッチアップして更新していくのに加え、ニュースなどの公開情報を自動収集する技術的チャレンジも行っております。

 

今後の開発について

2018年にSTARTUP DBが誕生して3年目。2021年3月に大幅リニューアルを行った新生STARTUP DBがリリースされました。

そして2021年6月現在、来たる7月のSTARTUP DB ENTERPRISE正式版のリリースに向けて、様々なエンハンスを行っています。

技術スタックとしてはリニューアル時からそこまで変わっていないものの、リニューアル時に導入していたNuxt.jsやTypeScriptのおかげで、より開発しやすく、より厳密に、より速く、新しい価値をお届けすることができるようになりました。

IEなどのレガシー環境に対する対応も、LambdaTestとJestを組み合わせた自動テストを用いることで、効率的に行っています。

今後は、STARTUP DBに蓄積された大量のデータとフォースタ全体のスタートアップに関する知識を効果的に用いて、利用者に対しもっと高精度な多くの情報を届けていきたいと考えています。

 

技術スタック

■フロントエンド:Nuxt.js / Vue.js / Express / TypeScript
■サーバサイド:Ruby on Rails
■インフラ・開発環境・CI/CD・監視:AWS(ECS・Redis・CloudFront・AWS WAF etc.)/ Firebase Authentication / Terraform・Terraform Cloud / Docker / CircleCI / Rollbar / Sider / Mackerel
■ツール・ドキュメント管理:GitHub / Slack / LambdaTest / Figma / Notion

 

2. スタートアップスデータプラットフォーム(SDP)

スタートアップスデータプラットフォーム(以下、SDP)はユーザーの皆様に安心して素早く大量に精度の高い情報を提供するシステムとなっています。
現在はSTARTUP DBおよびタレントエージェンシー支援システムのバックエンドのAPIサーバーとして稼働しています。
また、精度の高い情報を提供するため、情報を収集する仕組み、および、管理画面もSDB管理システムで開発しています。

 

今後の開発について

まずはSTARTUP DB、タレントエージェンシーシステムを利用しているユーザーに、可用性高くサービスを提供するべく開発を進めています。
これは、障害に対応するということだけでなく障害を発生させづらくする改善なども随時行っています。

また、SDPはAPIサーバー、管理画面、バッチサーバーと多くの役割を担っていることでシステムとして肥大化してきています。直接関連していないシステムを分割することで保守性を高くしていくことにもチャレンジしていきたいです。

 

技術スタック

■サーバサイド:Ruby on Rails / Flask(Python
■インフラ・開発環境・CI/CD・監視:AWS(ECS・Lambda・Elasticsearch・Redis・CloudFront・AWS WAF・Lambda@Edge etc.)/ Firebase Authentication / Terraform・Terraform Cloud / Docker / CircleCI / Rollbar / Sider / Mackerel
■ツール・ドキュメント管理:GitHub / Slack / Zapier / LambdaTest / Figma / Notion

 

3. タレントエージェンシー支援システム(SFA/CRM

タレントエージェンシー支援システム(SFA/CRM)は、日本を代表するスタートアップと、それを加速させることができるタレント(才気あふれる人々)とのより多くの対話の機会を創出するための「マッチングプラットフォーム」です。

また、ヒューマンキャピタリストの生産性向上を通して、「起業は人のブライトキャリア」というマインドのイノベーションを加速させることを狙いとしています。

※今後の展望に関しては、全てをこちらで語ることはできないので是非カジュアル面談でお話しさせていただけると嬉しいです。

 

今後の開発について

昨年度はマイクロサービス化とフロントエンドフレームワークの導入を行い、サービス間や、バックエンドとフロントエンドの責務を分離してきました。

インフラ構成もIaC管理を行うようになり、組織・サービス共にスケールに耐えられる体制が整いました。今年度は事業インパクトを最大化することに、より一層フォーカスを当ててプロダクト開発のレベルアップを図っていきます。

 

技術スタック

■フロントエンド:Nuxt.js / Vue.js / TypeScript
■サーバサイド:Ruby on Rails / Python
■インフラ・開発環境・CI/CD・監視:AWS(ECS・Lambda・Elasticsearch・Redis・CloudFront・AWS WAF etc.) / Terraform・Terraform Cloud / Docker / CircleCI / Rollbar / Sider / Mackerel
■ツール・ドキュメント管理:GitHub / Slack / Zapier / LambdaTest / Adobe XD / Notion

開発手法・環境

アジャイル

弊社では継続的にアジリティやプロダクトの価値を上げ続けるため、スクラムをベースにアジャイルのプラクティスを組み合わせて開発しています。具体的に現在実施している内容としては、定期的なふりかえり、短いイテレーション、モブプログラミング、カンバン、プランニングポーカーなどです。ただしアジャイルコーチ経験があるメンバーと共に自分たちにとって最適な開発スタイルを探求し続けているため、これは現時点でのスナップショットになります。

 

その他

ソフトウェアエンジニアは業務の中で学習をし続けることが大切だと思っています。そのため週に1時間の輪読会、週に30分のLT大会の時間を設けています。これまでの輪読会では「プロダクトマネジメント」「Rubyで作るRuby」「UNIXという考え方」「SQLアンチパターン」などを読んできました。

終わりに

フォースタートアップスのサービスの紹介と技術スタックをまとめてみました。

私たちはビジョンドリブンのチームです。

エンジニアドリブンでもプロダクトドリブンでもありません。

ビジョンを達成するために、どのようなものを作らないといけないか。

そのためにはどのようなエンジニアリングで取り組まないといけないか…という形で落としこんでいくので「技術のみに興味がある」といった志向の方は、少し合わない可能性があります。

ただ、技術的な挑戦がないわけではありません。

ビジョン達成のためには、技術を駆使して解決しなければいけない課題は多く、新しい技術を知らなければいけないので、当然のこととして様々な技術を取り入れています。

ただし、そこに一番の喜びを感じる人だと合わないと思っています。優先順位が高いのはビジョンです。

今後、技術スタックだけでなく、仕事の仕方も公開していければと思います。

【フォースタ テックブログ】デザインスプリントをやってみて開発チームの目線を整えた話

 

はじめに

こんにちは!フォースタートアップス / テックラボ * の藤井(@yutafujii)です。
社内向けのプロダクト「タレントエージェンシー支援システム(SFA/CRM)」*のサーバーサイドエンジニアとして日々活動しています。

*テックラボ…テクノロジーとデザインによってfor Startupsをグロースさせるチーム
*タレントエージェンシー支援システム(SFA/CRM)…日本を代表するスタートアップと、それを加速させることができるタレント(才気あふれる人々)とのより多くの対話の機会を創出するための「マッチングプラットフォーム」

これまで少人数のエンジニアでスピードや機能を優先して開発を進めていたのですが、弊社にUXデザイナーが入社してきたことをきっかけに、プロダクトが巨大化する前ということもあり一度立ち止まってデザインスプリントを行うことにしました。

ここ数年で普及したデザイン思考、デザインシンキングをエンジニアが取り入れることで、

・プロダクトの目指すべきゴールを再度確認すること
・一つ一つの開発がきちんとゴールに向かっていると確信を持ちながら実装できる状態にすること

を達成したいと考えました。

デザインスプリントとは

デザインスプリントは一言でいえば「ビジネス課題に答える5つのプロセス」です。
デザインシンキングを取り入れて目的を達成しようとするアプローチのため、この名前がついています。

GV(Google Ventures)が提唱したプロセス(https://www.gv.com/sprint/)であり、UXデザイン・プロトタイピング・ユーザーテストをアウトプットとして、学びという新たなインプットを得る流れになります。

ソースコードは1行も書きません

デザインスプリントは一般的に、5日間、つまり丸一週間分をかけて行います。その概要は大まかに以下の通りです。

Day1: 意識合わせ・課題マップの作成・取り組む課題の選択

Day2: ソリューション出し(知の探索)・スケッチ・最終日にインタビューするユーザの選定

Day3: ソリューションを絞る(午前)・うまくいった姿までの過程をイラストに並べる(午後)

Day4: プロトタイプ作成(コーディングせずにUIデザインツールを使用する)

Day5: プロトタイプを用いたユーザーインタビュー・学び

プロセスが用意されていれば当然ながら各プロセスごとに道具(ツール・フレームワーク)もあります。
デザインシンキングの文脈で有名な Empathy Map もその一つです。
実際に行うときは、個別の事例に沿ってアレンジを加えると良いでしょう。

 

なぜやるのか

元々デザインスプリントは事業課題の答えを早いサイクルで見つけ出すことに適しています。

実際にコードを書かずに思考・プロトタイプ・ユーザーフィードバックにより仮説を検証するサイクルは魅力的なアプローチでしょう。

ただし私はそれに限られない良さがあると考えています。

それは、このプロセスを通して

・プロジェクトの目標の共有
・メンバー同士の考えや思考法の相互理解

という側面で効果があるためです。プロジェクトのブラッシュアップだけでなく、スタートアップなどの組織づくりにも有用です。

 

いつやるべきか

教科書的には、新たなプロダクトを実際にエンジニアが開発する「前」がベストなタイミングになります。
ただ、すでにプロダクトがあるという場合でも、例えば以下のような時には効果があると考えています。

・プロダクトが目標とする1年後の状態をすぐに言えない
・(技術的に)できることばかりを優先して開発している
・開発工数が小さい事項ばかり実装していることが2ヶ月続いている
・優先順位付けを行った結果のバックログが、プロダクトOKRのKRに貢献するものに見えない
・ユーザーのペルソナを聞くとメンバーによって回答がバラバラ。またはすぐ答えが出てこない
・ユーザーの課題を「それはまるで○○のよう」と身近な行為で例えてもらったときに回答がバラけてしまう。またはすぐ答えが出てこない
・とりあえずユーザーヒアリングを終えた
・チームメンバーが急激に増えている

など

 

ミニ・デザインスプリント

今回チームで実施したのは、このミニ版のようなものです。

1.事前に行ったユーザーヒアリングの内容を集約する
2.UXデザイナーが「一人デザインスプリント」を実施して叩き台となるレポーティング資料・ディスカッション資料を作る
3.チーム全員で資料の中身を議論する。発散して、収束させ、お互いの認識を"同期"させる

フレームワークに従ってレポートまで作ってくれたデザイナーさんに感謝です!

 

ゴール

最初にデザイナーが示したゴールはこちらです。

「メンバー全員が事業の目的・事業課題・プロダクトビジョン・ペルソナを共通認識として持つこと。誰に聞いても同じ答えが返ってくることで、新規メンバーとも課題や目的を共有出来る」

ちなみにこのゴールはとても重要で、起業家が集うBARで、とあるCTOに「まずはとにかくユーザーを知ること。徹底的に足を運び、エンジニアの誰に聞いても全く同じペルソナが返ってくるような状態にしなさい」と言われました。

 

雰囲気作り

見落としがちですが、雰囲気作りはとても重要です。

ゴールを達成するためには参加者に対してどのような姿勢で望んで欲しいのか、どのような行動を望むのかを最初に共有するとすごく良いです。

また、それをサポートするような小物を用意することも大事だったりします。

今回のゴールは「共通認識を持つこと」なので、ちょっとでも意見が違ったり違和感があったら遠慮せず発言することが望まれます。

本心で腹落ちしていないまま終えてしまっては本当の意味で「認識を共有した」とは言えません。

 

そしてそのためには(1)意見を言える気楽さ、(2)柔軟な発想ができる環境を用意した方が良いと考えました。

そのために私たちが行ったことは次のことです。

・ちょっと息抜きをしたくなるおやつ時に実施した(15時からスタート)
・お菓子を事前に持ち寄って(一人で食べきれない結婚式の引出物とかも笑)テーブルに並べた
・食べながらでOK
・もちろんコーヒーも準備
・地べたに座ったり、机に座ったり、立ったりして参加してOK
・発言をとにかく褒める
・ジョークもいれてみんなで笑う、面白いトピックもいれる
・大きな画面を使う

 

使用したフレームワーク・ツール

ざっくりとこんなフレームワークや資料を使用しました。

・User pains
・State it Simply
・Persona
・Empathy Map
・Story Board
・Hills

 

User pains

今回はミニ・デザインスプリントなので、最初にユーザーヒアリング結果をUXデザイナーへ渡しました。
こんな感じで機能要望がざっと40個ほどカテゴリー分けされて並んでいました。

 

State it Simply

・What we do?(それを5歳児にわかるように)
・Who is the users?
・What is their pain?
・What is our business for?

この辺りを言語化します。
この部分はどのくらい先まで見据えるかによっても書く内容(ビジネスの展望や新たなステークホルダーをユーザーにする長期戦略など)が異なってきますが、目先1年くらいにするといいと思います。

もちろん、全社の長期戦略からバックキャストし、プロダクトの長期ロードマップを作って認識共有することも大事なので、それは別途行いましょう。

意外と盛り上がるのが「 ”何をするのか” を5歳児にも説明できるように」だったりします。余分なところが削げ落ちて、動詞や名詞が研ぎ澄まされていくのがわかると思います。

Pain(課題)を考える時も、「それはまるで○○のようだ」と表現することを意識する。

私たちがデザインスプリントを実施したときには

・「それはまるでパズルのピースを埋めるような状態だ」
・「それはまるで料理を30個同時に作っているような状態だ」

などの意見が出て、結局

・「それはまるでパズルを10個同時に作っているような状態だ」

となりました。

 

Persona

ペルソナを設定します。

家庭環境・育ち・趣味・仕事への姿勢や性格などを具体例を含めながらその人のイメージが湧くまで書いていきましょう。

箇条書きで大丈夫です。

その人の名前もつけておくとその後何かと便利です(私たちは山崎翔大さんと飯山美咲さんというペルソナを作り上げました)。

実際には、この部分が最も大事です。

プロダクトによっては複数タイプのユーザーを抱えることがあると思います(プラットフォームがその典型)。その場合はそれぞれについてペルソナを立てて進めましょう。

 

Empathy Map

次に、山崎さんや飯山さん(=ペルソナ)が”言いそうなこと”、”やりそうなこと”、”考えそうなこと”、”感じていそうなこと”を書き表していきましょう。

特にTHOUGHTとFELTの部分は、なぜそれらが表に出てこない(口に出したり、行動にならない)のかを深く考えてみることが大切です。

 

Story Board

今のペルソナユーザーの状態(課題を抱えた状態とします)から、ユーザーが為したいことに至るまで、プロダクトがどんなふうに関与していくかを4コマ漫画的に絵と端的な言葉で書き表していきます。

私たちは6コマを用いて、今のユーザーが “こうして、こうして、こうなって、こうなって、ゴールの姿になる” 、という過程を描きました。

*引用元:https://uxdesign.cc/how-to-storyboard-experiences-fc051e2bc04d

 

Hills

プロダクトによって「誰が(Who)、どんな感じに(Wow)、何を(What)達成してる」のかを改めて考えます。

最も大事なのはWowです。Howではありません。

方法論から考えるのではなく、状態から考える。

描いた理想像では、ユーザーが「どんな気持ちを持って」「どんな感情で」成し遂げたかったコトを達成しているのだろうか?そういった視点が求められます。

どういうことか、少し思考例を示します。

 

例)30分かかるMRIでじっとしていられない子供に、MRIを受けてもらうには?

→ 鎮静剤を打つ(How)のではなく、MRI室を海賊船のようにして楽しみながら受けてもらおう(Wow)

ちなみにWowを考えるというのは、グロースハックでも出てくるアドバイスです。
ユーザーにとってのアハ・モーメント* の発見がPMF達成のサインといってもよいでしょう。

*アハ・モーメント…プロダクトの価値をユーザーが最大限に感じた瞬間

 

イデアリストの順位付け

ミニ・デザインスプリントでは以上を元にして、ペルソナとしたユーザーがStoryBoardに出てくるゴールの姿になるためのアイデアを出しました。

最後にそれらを、縦軸に各アイデアインパクト、横軸に効果の不確実性を描いてそれぞれのアイデアがどのあたりに位置するかマッピングしました。

当然ながら「インパクト大・確実性が高い」というゾーンが優先的な開発事項ということになります。

これらを見つめ直したところでスプリントを終了し、ちょっとした振り返りを行いましょう。

 

やってみてわかったこと

シンプルな本質を見つめることができる

今回4時間をかけて実際にやってみて、最初に感じたのは自分たちが何を作っているのかをちゃんと言語化できていないということです。

何となくでは説明できたけれど、「それって何?」のように1つ突っ込んで質問されるとスラスラと答えられなかったり、参加したエンジニア4人の認識や想いが完全に一致しているわけではなかったり…。

“誰に聞いても同じ答えが返ってくること” という状態を実現するには膝を突き合わせる時間と議論のフレームワーク(=デザインスプリント)がとても有効だと感じました。

 

Vision Driven

また、みんなで話すと自分の考えやチームとしての見解がどんどん研ぎ澄まされるというのも全員で実感しました。

 

この過程で最も重要な役割を果たしたのがMission, Vision, Valueです。

何度も何度も

「でもそれはMissionにある○○に添わないよね」とか

「私たちのMissionは○○だから、5歳児に説明すると○○な感じかな?」とか

「あの時CEOが○○って言ってたのを踏まえて○○の方がフィットする」とか、

会社として実現したい世界や数年先のゴールを意識して目の前に落とし込むことができました。

フォースタートアップスでは1週間に1回は全社MTGでCEOより、熱意や今の想いが聞けますし、毎週Slackでも欠かさず “今、考えていること” を共有してくれます。

そういうカルチャーにもとても助けられました。

 

フォースタートアップスのMVV (出所:会社HP

 

The Team

実際にやってみて、デザインスプリントはチームそのものを強くすると感じました。

私たちは普段からコミュニケーションがとても活発なチームだと思ってはいますが、プロダクトの本質やゴールを何時間も使ってみんなで議論すると、お互いをもっともっと深く理解することができます。

部活の合宿のような感覚で、長時間すぐ傍で過ごしてチームとして強くなるイメージかも知れません。

中には意見に相違があったり考え方が異なる部分もありますが、「同意」できなくてもいいのです。「理解」することがとても重要です。
お互いを知り、理解し、その上で一つの方向を向く。仲間を信頼する。

そのためにもデザインスプリントはいい時間になるはずです。

最後に

長々と書かせていただきましたが

社内向けのタレントエージェンシー支援システム(SFA/CRM)についてデザインスプリントを行った感想でした。

私たちは、日本を代表するスタートアップと、それを加速させることができるタレント(才気あふれる人々)とのより多くの対話の機会を創出するための「マッチングプラットフォーム」を創るという壮大なプロダクトを作っています。

試行錯誤しながら、日本から世界で勝つスタートアップ支援を行っていきたいと思います。

【フォースタ テックブログ】RailsのAutoloadingをClassicモードにしていたらエラーに悩まされたのでZeitwerkモードに移行した話

 

こんにちは。エンジニアの藤井(@yutafujii)です。

今日は、RailsのAutoloadingとReloadingについて解説しつつ、これにまつわる設定ミスでdevelopment環境においてエラーに悩まされたというエピソードをご紹介します。

AutoloadingとReloadingって?

RailsのAutoloadingとReloadingという言葉を、より実務上のありがたみとしてイメージできるように素朴な疑問から考えてみたいと思います。

なぜRailsではrequireを書かなくてもよいのか?
Rubyでは他のファイルを読み込む時には当該ファイルを明示的にrequireしておく必要があります。ところがRailsではモデルでもコントローラーでも、requireを書かずに多くの処理がうまく動きます。

これは、RailsRubyのメソッドをオーバーライトしているためです。具体的には、Moduleクラスのconst_missingというメソッドを上書きしています。このメソッドはメモリ上にロードされてない未知の定数を参照したときに発火するのですが、RubyではNameErrorが出るのに対して、上書きされたRailsのconst_missingではエラーを出す前にその定数が定義されていそうなファイルを推測して自動で探すようになっています。だからAutoloadingと呼ばれています。なお、自動で探す範囲はautoload_pathという変数で管理されています。

そしてもうひとつの疑問。
なぜRailsで開発しているときにファイルに加えた変更がすぐ反映されるのか?

例えばの話ですがproduction環境で稼働しているサーバーに入り、Railsのコントローラーのファイルを書き換えたとしても、その変更はサーバーを再起動しない限り反映されません。しかしdevelopment環境だとファイルを修正すると画面をリロードするだけでその変更が反映されます。Railsがこのような開発体験の良さを実現しているのは、development環境ではファイルの変更履歴をウォッチして、変更を検知したらサーバーが次のリクエストを受理したときに当該ファイルを読み直せるようにしているからです。これをReloadingと呼んでいます。

RailsがAutoloadingとReloadingのためにしていること

概略は説明した通りですが、コードベースでも該当箇所を紹介しておきます。
Autoloadingで説明したconst_missingメソッドのオーバーライトはActiveSupport::Dependenciesというモジュールに記載されています。

また、Reloadingで説明したファイルをウォッチしているというクラスはActiveSupport::FileUpdateCheckerというもので、そのexecuteメソッド(端的に言えばここで変更が生じているファイルをメモリからアンロードする)をコールしているのがRails::Application::Finisherというモジュールです。

Reloadingについて説明を加えると、このFileUpdateCheckerがautoloadされた定数を一旦全てアンロードしますので、次のサーバーリクエストの処理において変更を加えたコントローラーやモデルが参照されると、const_missingが発火してAutoloadingされ、結果として修正後の内容がロードされるという仕組みです。

設定を間違えたらdevelopment環境で見知らぬエラーが

正直に言って、こんなRailsの仕組みを理解したうえで実務の世界に入ったわけはなく、エラーに遭遇して初めてちゃんと調べただけです。

ここからはそのバグについてご紹介します。

私が入社した頃はRailsフルスタックのフレームワークとして利用していたのですが、途中からVueやNuxtをフロントにしてAPIサーバーとしての機能に集約してきました。そうした開発を進めていくなかで、development環境において次のようなエラーが出るようになりました。

A copy of Api::One has been removed from the module tree but is still active!

Api::Oneのコピーはモジュールツリーから削除されたけどまだ利用されています。」とでも訳すのかもしれないですがエラーメッセージの言っていることがイマイチよくわからず、backtraceをみたのですがアプリケーションのコードに到達する前のRailsのコードでエラーになっていたので、これは少し根が深そうだと思ってGoogle検索を頼りました。

同様のエラーに関する記事はいくつか見つかったのですが、実際に効果があったのはdevelopment環境のconfigを変更するという対処でした。

config/environments/development.rbにおいて「クラスをキャッシュしておくか」という設定(config.cache_classes・config.action_controller.perform_caching)をtrueにすることで確かにエラーは出なくなったのですが、これは一度ロードしたクラスをキャッシュし続けるという設定なのでReloadingが効かなくなり、Rails部分のソースコードは(より厳密にはautoload_pathに含まれるファイルは)変更するたびにアプリケーションサーバーを再起動しないと内容が反映されなくなります。

これは開発体験が非常に悪いので、Railsの仕組みを調べながら、根本原因を探していきました。

結論として、エラーの直接的な原因はdevelopment.rbの別の設定にありました。

「クラスのリロードを変更があった場合に限定する」という設定(config.reload_classes_only_on_change)がfalseになっていたために、ソースコードを変更しなくてもリクエストの都度autoloadされた定数を全てアンロードしていました。

この設定そのものが問題ではないのですが、フロントエンドをコンポーネント化してきたことと複合してエラーを生じさせていました。

すなわち、コンポーネント化されたページを開くとページロード直後にJavaScriptが複数のリクエストをほぼ同時にAPIサーバーへ送る状況が生まれたところ、前述のRailsの設定が理由でAPIサーバー側ではリクエストの処理前にautoloadされた定数が全削除され、その結果全く同じモジュールのAutoloadingが2本同時に走るRace condition(競争状態)が発生していました。同一モジュールのRubyオブジェクトが2つできてしまったことで、処理途中のequal?評価(RubyではオブジェクトIDの一致を確認するメソッド)がfalseになり、くだんのエラー

Api::Oneのコピーはモジュールツリーから削除されたけどまだ利用されています。」

が表示された、というわけです。

ちなみに、実際にエラーを起こしたのはルーティングからコントローラーを取得する処理action_dispatch/http/request.rbのcontroller_class_forという部分でした。コントローラー名の文字列から定数を取得するRailsのconstantizeメソッドでコントローラーを示す定数(例えるならApi::Parent::ChildController)をAutoloadingする時にエラーになっていました。

なぜこのような設定になっていたのか

ところで、問題の一因となったconfig.reload_classes_only_on_changeの設定はRailsプロジェクトの初期値がtrueなので、なぜこれがfalseに変更されたのか気になりました。

この変更は3年前に行われており。当時のプルリクにも多くの情報はなかったので推測ではありますが、事の発端はApplicationというモデルを作成したことだったと思われます。

当社のシステムは人材紹介業に関連するものであるために、”応募”の英訳にあたるApplicationという単語をモデル名で利用していました。しかし想像がつくようにApplicationというクラスはRailsプロジェクトそのものにも存在し(config/application.rb)、何らかの機構でApplicationモデルのAutoloadingが上手くできなかったようです。そこでRailsのイニシャライズ直後にapp/models/application.rbをrequireしておくような設定がconfigに書かれていました。

悲しいかなRailsではrequireしたファイルは通常のReloading時にはアンロードされないという性質があるために、今度はApplicationモデルのReloadingができない悩みを抱えていたと思われます、だから強制的に都度定数削除をするconfig.reload_classes_only_on_changeをfalseにしたのではないかと考えています。

今回のバグ修正においてこの部分も見直し、結果的にrequire_dependencyを利用しました。一応ですがRailsガイドではrequire_dependencyはラストリゾートであり最初に検討すべき手段ではないと書かれているのでご注意ください。なお後述するZeitwerkモードの導入でこの対応も不要になりました。

Autoloadingに関するRailsの設計上の疑問と直近の動向

もう少しだけこの定数のAutoloadingについて触れておきましょう。

紹介したエピソードではdevelopment.rbの設定ミスとVueを用いたフロントエンドの分離が競争状態を生んだエラーの理由だと説明しましたが、このエラーはどのRailsプロジェクトでも一般的に再現性があります。

エラーが起きる条件は「2本以上の同時リクエストを受けとりReloading & Autoloadingが2本同時に走ること」ですが、通常の開発のなかでファイルを修正した場合この条件を満たしてしまいます。

実際、フロントのコンポーネントにおけるcreatedフックなどで2本以上のAPIが同時に呼び出されるページでは、Rails API側のファイルを修正すると直後の画面リロード時だけはこのエラーが出ます(出ない時もあります)。ReloadingとAutoloadingが2本走って競争状態が生まれるためです。そのままもう1度画面リロードするとエラーは出なくなりますが、これはReloadingもAutoloadingも走らないからです。

このエラー再現性についてはReproduce用の個人プロジェクトも作って確認しました。

github.com

「これ、フロント分離しているプロジェクトだと悩む人多いんじゃないか?」と思ってRailsのイシューが既にあるか見てみると、確かに1件「LoadError when multiple threads try to load the same namespaced class」というイシューで修正の議論もなされていたようですが、Rubyの改修も必要な内容になっており、最終的には修正は行われていない様子でした。

その代わりだったのかはわかりませんが、Autoloadingの新しいやり方がRails 6.0から導入されています。

Zeitwerk(ツァイトヴェルク)というgemが正式に導入され、そもそものconst_missingに依拠したAutoloadingが見直されました。

なので、Zeitwerkモードを利用していれば、今日紹介したエラーに悩まされる心配はありません。Rails 6.0以前のAutoloadingの方法はClassicモードと呼ばれていますが、これはRailsガイドでdeprecatedとされているので早めに移行しておきましょう。

config/application.rbに1行追加するとZeitwerkモードに移行できます。

config.load_defaults "6.0" # Zeitwerk
config.autoloader = :classic # Classic

私の所属するプロダクトではRailsのバージョンこそ6に上げていたものの、こうした周辺機能のマイグレーションに気づけていない部分もあったので、次回以降気をつけていきたいと思います。

参考リンク

RAILS GUIDES

https://guides.rubyonrails.org/autoloading_and_reloading_constants_classic_mode.html
https://guides.rubyonrails.org/autoloading_and_reloading_constants.html

Rails GitHub

https://github.com/rails/rails/blob/main/activesupport/lib/active_support/dependencies.rb
https://github.com/rails/rails/blob/main/activesupport/lib/active_support/inflector/methods.rb

Rails Issue

https://github.com/rails/rails/issues/33209

Zeitwerk GitHub

https://github.com/fxn/zeitwerk#pronunciation

【フォースタ テックブログ】「ユニコーン企業のひみつ」を読んで自社の開発組織と比べてみた

こんにちは。サーバーサイドエンジニアの速水です。
今回は、「ユニコーン企業のひみつ――Spotifyで学んだソフトウェアづくりと働き方」という書籍のレビューを投稿させていただきます。

www.oreilly.co.jp

フォースタートアップス(以下、フォースタ)でも、スクラムをベースにしたアジャイル開発を行っているわけですが、事業環境や組織は日々変化しており、どう変化に対応していくべきなのか、悩みは尽きません。
ユニコーン企業のひみつ」は、SpotifyAmazonGoogleFacebookといったユニコーン企業はどうやっているのか?というのをヒントに、何万人もの従業員を抱える企業がなぜスタートアップのような組織・環境であり続けられるのかを紐解いていく内容となっており、印象に残ったところ、自らに引き寄せて考えたこと、感想をまとめました。

今回の書評は、弊社の村林(@bayashimura)がこちらのツイートを見つけたことがきっかけになっています。

村林はフォースタの開発組織であるテックラボのアジャイル旗振り役として、振り返りやチームビルディングを浸透させてきました。社内向けのプロダクト「タレントエージェンシー支援システム(SFA/CRM)」を開発しているチームでは、振り返りのファシリテーターを交代で行うなど、スクラムの要素を取り入れながら開発プロセスの改善に努めています。

tech.forstartups.com

tech.forstartups.com

不確実性だらけだからこそ学習が大事

スタートアップで取り組むソフトウェア開発は、不確実性だらけであると語られています。

「答え」が既にわかっているというつもりなら、それは思い込みだ。

わからないから、たくさん実験をして、失敗をすることもあり、ただ毎回インパクトと価値を計測しているから、前に進んでいける。わからないから、「答え」を見つけるために、各自が考えて、手を動かして、学習していかなければならない、と説かれていました。

自らに引き寄せると、やはり普段の仕事の内容は、「答え」がわかっているとは言えません。議論した上で「この方向だよね」という合意はあれど、それが絶対に正解とは言い切れないし、アプローチ方法も様々です。

テックラボでうまくできていると感じるのが、アプローチへの寛容さと、失敗に対する心理的安全性(失敗をしてはいけないという思い込みは払拭する)です。フロント、バックエンド、様々な視点から実現方法を考えるのはもちろん、そもそも手動の作業で同じような物を見せて解決できないか、ヒアリングして課題をもう少し深堀りたい、といったこともフラットに出し合うことができます。失敗はしないに越したことはありませんが、バグが入ったまま機能をリリースしてしまった、レビューでバグを見逃してしまった、という時もあります。そういった時に、チーム全体で今できるリカバリ策を考え、対応する姿勢があるからこそ、苦手な領域の開発であっても挑戦してみよう!という気になります。

一方、インパクトと価値の計測においては、まだまだ課題があるように感じています。社内向けプロダクトは、ビジネス上の価値とプロダクトの価値がぴったり一致はしないため、指標の設定、評価には苦戦しています。

自律、権限、信頼

ユニコーン企業ではアジャイル開発が企業文化に染み込んでおり、わざわざスクラムマスターを置いて形式としてのスクラムをやる必要はないようです。とても自律したチームであり、信頼があるからこそ、権限もしっかり持っています。ですが企業としてバラバラにならないための仕組みとして、"カンパニーベット"を設定するということもやっているようです。
※カンパニーベット(Company Bet):会社が取り組みたい重要事項を、終わらせたい順に並べたリスト。小さな目標ではなく、大きな取り組みに関するもの。

企業においてチームに権限を渡すというのは、そう簡単なことではないと思います。しかし、学習のスピードを出すためには必要なことでもあります。本書の中では経営側の視点で「現場の言い訳を取り除く」と表現されていますが、現場としても権限をもらう以上、本当に自分ごと化できないと辛いことになりそうです。渡す側、もらう側、双方の信頼あっての権限移譲で、それがあっての自律したチームなのだと理解しました。

また、見習いたいポイントとして、データ(数値、ファクト)と解釈(どういう意味を見出したか、仮説)を、フレームワークとして切り分けている点がありました。本書では、DIBBという、やるべきことを系統立てて検証するための意思決定フレームワークが紹介されており、データ(Data)、インサイト(Insight)、確信(Belief)、ベット(Bet)に分解して、整理されていました。
私はこれまで、人への伝わりやすさという観点で「主張+根拠づけとしてのデータ」をまとめてとらえることが多かったのですが、学習の最中ということを考えると、データと解釈をわかりやすく分けて共有した方がチームとしての学習は進むのかもしれません。私も意識してやってみようと思います。

まとめ

著者Jonathan Rasmusson氏のSpotifyでの経験を中心に、ユニコーン企業におけるソフトウェア開発で組織として気をつけていることがわかりやすくまとまっている1冊でした。特に、アジャイル開発におけるスクラムの形式的イメージを強く持っている方にとっては、それが浸透した先を知れる内容となっているので、面白いと思います。

フォースタではエンジニアを募集中です!
開発スタイルや雰囲気はもちろん、事業や技術スタックに関するお話もさせていただきますので、ご興味をお持ち頂けましたら、下記「話を聞きに行きたい」ボタンより気軽にエントリーいただければ幸いです。

【フォースタ テックブログ】CTOが組織をサッカーで例えてみた話

始めに

こんにちは。フォースタートアップスでCTOをしている戸村(@KenjiTomura)です。

最近、サッカー界では欧州スーパーリーグ問題が話題ですね。

クラブ運営の枠に止まらず、ヨーロッパのサッカー文化にも波及する様々な問題が出てきおり実現は難しそうですが、私はサッカーを見ることが好きなので純粋に強いクラブ同士の試合が多く見られるようになるということは楽しみだったので少し残念です。

私がCTOを務めるフォースタートアップスの開発チームにおいても、私の好きなサッカーの哲学を取り入れながらチーム作りをしているので、今回は私が目指している開発組織について記事にさせていただきました。

最近の開発について

開発はプロダクト作りにおいても、技術においてもどんどん進化していっています。さらにはその進化のスピードもどんどん早くなっていっているのは、皆さんも感じているところだと思います。

プロダクトの規模も複雑性も増えていく中で、1人の人間が全ての最新の技術を押さえ、社内の全てのPull Requestを全て確認し、全てのコードを把握し、問題が起きないように開発をしていくことはどんどん難しくなっています。

そのため、CTOや開発責任者が開発の方向性を決めながら、細部はメンバーそれぞれに権限を与え、判断を委ねていくべきだと私は思っています。

この状態はターンのある野球などのスポーツよりは、ターンがなくリアルタイムに進むサッカーが近いのではないかと思います。

そこで私は、サッカーの戦略の中でも1974年W杯のオランダ代表が唯一完成させたと言われているトータルフットボールを目指しています。

トータルフットボールについて

トータルフットボールとはなんなのでしょうか。
以下はWikipediaのトータルフットボールの解説を少し抜粋しました。

1950年代初期に、オーストリア人のヴィリー・メイスルによって考案された「渦巻き」理論がトータルフットボールの原案であるとされている。
それは、個々の選手が思いのままにポジションチェンジを繰り返し、渦を巻くようにチームがダイナミックに機能するというものであった。

ただ、これを可能にするには、選手一人一人が同じくらい高い技術と戦術眼を併せ持ち、なおかつかなりのスタミナが必要だと考えられていた。

さらに、動きの連続性を持ったその渦が、自ら意思を持つように前進と後退を繰り返すためには、渦の中心にいながら、渦の外からその流れを俯瞰できる稀有なビジョンを有し、渦をコントロールするだけの並外れた影響力を具えた選手が必要であった。

何といっても、最大の特徴はポジションが存在しないことである。
当時のオランダ代表にとってポジションとは「キックオフ時の立ち位置」というだけのものであり、攻撃時には選手は積極的にボールを持つ選手を追い抜いて前線に飛び出し、守備時にはFW登録の選手もカバーリングに入る。

サイドバックの選手が前線へ飛び出せばウイングの選手がそのスペースを埋めに下がる。
まさに全員攻撃・全員守備である。

また、“スペース”を最大限活用する考え方から、ウイングを中心としたサイドアタックを積極的に使い、ワイドな攻撃を展開した。

このサッカーを支えたのは選手全員の高い技術、戦術眼、スタミナもさることながら全員が高い守備意識を持っていたことも忘れてはならない点である。

反面、この戦術は理想的だが非効率的とされ、完全分業でポジションを固定した方が効率が良いといった説もあるが、後に様々なチームによって改良され、現代サッカーの戦術に浸透していく事になる。

引用元:https://ja.wikipedia.org/wiki/トータルフットボール

このトータルフットボールは1974年W杯のオランダ代表ヨハン・クライフが唯一完成させたと言われています。 クライフ以後のサッカー界でトータルフットボールを実現したチームは無いとされています。
しかし、後にクライフはバルセロナというクラブの監督となり、その時に司令塔としてプレイしていたグアルディオラへと系譜が繋がれ、形を変えてバルセロナの中にトータルフットボールの新しい形が活きていると私は思っています。

そして、グアルディオラバルセロナの監督になり、メッシを中心とした美しいサッカーをバルセロナで実現したと思っています。

私はこの形の組織を目指しています。

トータルフットボールのような開発組織とは

トータルフットボールのような開発組織とはなんなのでしょうか。
重要なポイントとしては以下の3つです。

  • 激しいポジションチェンジにより、明確なポジションが存在しない
  • 高い位置からのプレス & オフサイドトラップ
  • 選手全員の高い技術、戦術眼、スタミナもさることながら全員が高い守備意識を持つ

一つずつ深掘りしていきましょう。

激しいポジションチェンジにより、明確なポジションが存在しない

クラウドインフラが出てきて以降、エンジニア1人が受け持つ領域はどんどん増えています。
その為、フロントエンドエンジニアがサーバサイドに業務領域をオーバーラップしていくこともあるだろうし、その逆も同じ様にあります。

それはさらにサーバレスなどの技術が進むことによりフロントエンドエンジニアがインフラを理解し、触っていくなどのオーバーラップも拍車がかかっていくと感じています。

弊社でも得意領域などはありますが、あまり領域を明示せずに様々なところにポジションチェンジしながら開発に取り組んでもらうことが多く、エンジニアはエンジニアリングだけではなく、ユーザヒアリングから仕様検討も行うことも多くあり、それが結果的に良いプロダクトを作るための基になっています。

高い位置からのプレス & オフサイドトラップ

サッカー用語で、プレスとはボールを持っている相手にプレッシャーをかけたり、パスコースを塞いだりすることを言います。

オフサイドトラップは説明が難しいので詳細な説明は割愛しますが、相手の攻撃を防ぐディフェンスの戦術の一つです。

このポイントを簡単に説明すると、プレスやオフサイドトラップなどのディフェンス戦術を用いて、守備の時間を減らすことです。

開発において攻撃とはアウトカムを出すこと、ストーリーポイントを消化することなどで、守備とはバグ・障害の対応、手作業(Toil)の運用、リファクタリングなど、やらないといけないが必ずしもプロダクトを前に進めるものではないことだと思っています。
開発においては、ここで定義している守備の時間を減らし、攻撃の時間をできるだけ増やすことが大事です。
守備の時間を減らすためには以下のようなものが挙げられますが、こういったものを適切に取り入れながら守備の時間を減らすという戦術が重要だと思っています。

  • CI / CD
  • Toilの撲滅
  • 明らかに負債となるコードを減らす
  • ポストモーテムによる失敗の共有
  • マネージドサービス、SaaSの活用
  • インフラやコードクオリティなどの適切なモニタリング

そして、より良質な攻撃をするためには、闇雲にコードを書き続けることは正しいとは思っていません。
戦略、プロダクト、ユーザなどの深い理解をした上で書かれたコードは良質なコードになりますし、長くリファクタリングをしなくてもバグを生みづらいコードになっていきます。

選手全員の高い技術、戦術眼、スタミナもさることながら全員が高い守備意識を持つ

特にこのポイントが一番重要だと私は思っています。

要は高いプロフェッショナル意識をもち、1つ1つの仕事に怠慢なプレーをしないということです。
さらには、選手全員というところも大きく重要なポイントです。

サッカーでいえば、一人でも連携が取れていない選手がいるだけで、その選手から連携が崩れボールが取られてしまったり、オフサイドトラップがうまくいかず失点しまったりということが容易に想像できます。
エンジニアも同じで、どんなに使いやすく美しく書かれた優れたコードでも、1人のミスによって、セキュリティの問題に発展し大切なデータを失ったり、サービスの信用を失うこともあります。

なぜサッカーでバルセロナというクラブが強いのかというと、この全メンバーのゴールへ向かうビジョンの一致、高い戦術理解の能力、ハイレベルな一つ一つの技術など、当たり前のレベルが高く設定されていることだと私は思っています。

海外のサッカーの試合を見ていると、同じリーグに属しているクラブチームでも上位のチームはパス精度、パススピード、トラップ精度が圧倒的に高いと思います。そこから相手の守備を崩していき、ゴールにつなげる。

パス精度が高いことによりトラップのしやすいパスになり、トラップ精度が高いことによりパススピードが上がってもトラップのミスが少なくなり、高速にパスを繋げていくことで相手の守備が崩れ、パスがどんどん通りやすくなっていきます。

しかし、この中にトラップ精度が悪い選手などが混じってくるとパススピードを下げないといけなくなり全体の精度が下がっていき、通るはずだったパスが通らなくなってしまいます。

開発組織も同じです。 それぞれの当たり前のレベルが高いからこそクオリティの高いプロダクトが作れます。 デザイナーがどんなに優れていても、フロントエンドエンジニアがそのデザインを再現できなければ意味がありません。 その逆も同じでフロントエンドがどんなに優れていても、デザイナーが優れていなければUXの優れたプロダクトは作れません。

サーバサイドも同じです。

最近マイクロサービスという話が良く出ていますが、分割するポイントを間違えるとリクエストが増え、あっという間に遅いサービスになってしまいます。

さらには一回マイクロサービス化して終わりではなく、サービスは成長していくものなので、新しい機能を追加する度に適切なサービスに機能を追加していかないといけないですし、既存ではなく新規のサービスとして作るなどの判断をしていかないといけません。 そんな状況でプロフェッショナルではない怠慢なプレイをしてしまうと、プロダクト全体のクオリティの低下に繋がってしまいます。

まだプロダクトのクオリティ問題で済んでいれば良いですが、これがセキュリティ問題に発展してしまったとしたら目も当てられません。

最後に

このトータルフットボールという考え方は完成形が難しく、元日本代表監督のイビチャ・オシムも「目指しているのはトータルフットボールだ。ただしそれは永遠に実現されないが。」と言っています。 このトータルフットボールは個人の力に大きく依存するので、得意領域や専門性は必要性ですがオールラウンダーな人を採用していかなければいけません。

それは求人票で厳密に定義された職務を遂行してもらうジョブ型雇用ではなく、優秀なメンバーを採用し、その人にやることを合わせていくようなメンバーシップ型雇用になっていくと思います。 ですが、組織は往々にして大きくなるにつれてジョブ型雇用に組織全体を変えていかないと難しくなると言われています。
フォースタートアップスの開発組織も大きくなるにつれ同じ悩みにぶつかる可能性もあるでしょう。

トータルフットボール的な組織の完成形はオシムの言うようになかなか難しいのかもしれませんが、最良の形を模索しながらより良い組織を作っていきたいと思っています。

【フォースタ テックブログ】タグの自動予測について。STARTUP DBに機械学習を組み込んだ話【後編】

f:id:forStartups:20211025162517p:plain

こんにちは。テックラボの松原です。

以前、投稿しました「タグの自動予測について。STARTUP DBに機械学習を組み込んだ話【前編】」の続きのお話です。

前編では、STARTUP DBに登録する企業のサービスに適したタグはどれなのか予測する仕組みをつくるため、AWSのコグニティブサービスを試して回り、「やりたいことを、それなりの精度でやるためには、自身で実装しないと厳しい」という結論になったというお話でした。

今回は、手前で開発したその仕組みについて、お話をさせてもらおうと思います。

学習モデル作成

前回も簡単に記載しましたが、学習モデルは、以下の図のようなフローで作成しています。

①学習を行うためのデータ取得と解凍・抽出

まず、言語の処理をするためには、自然言語の文章を構造化し大規模に集積したもの「コーパス」が必要になります。品詞などの情報を含んだ百科事典のようなものですかね。
「全集」とも呼ばれるそうです。

当初、STARTUP DBの中にあるデータだけで、コーパス作成を試みたですが、情報量が足りず、計算できなかったり、過学習が起きました。

後でベクトル化の話をしているのですが、ベクトル化できない言葉が多く、ベクトル化できたデータもベクトルの向きがおかしい…という現象が出てきたのです。
ベクトルは「大きさと方向を持つ量」です。[ 1, 3, 2.5 ]のような数のリストとして表現ができます。

もっと大量の、一般的な文章データが必要だということで、利用したのがWikipediaのデータです。

Wikipediaのコンテンツデータは、再配布や再利用のために利用できる一元化されたデータベース・ダンプでの提供が行われています。
日本語のダンプファイル(出力ファイル)は、こちらからダウンロードができるようになっています。

ダウンロードしたファイルは、bz2という圧縮ファイルになっています。
それを解凍すると、下の画像のようなXML形式のデータが確認できます。

このままだと使えないので、このXMLから本文を抽出する必要があります。

そこで抽出に使うのが、オープンソースで提供されているWikiextractorです。
名前の通りですが、Wikipediaのダンプファイルから本文を抽出するためのプログラムです。

ダウンロードした圧縮ファイルを指定して、このようなコマンドでWikiextractorを実行すると

python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2

本文が抽出された、複数のテキストファイルが保存されます。

テキストの中身はこんな感じです。

見て分かる通り、<doc ….>というような、不要な文が残ってはしまいます。
なので、このあたりの不要な文も除去して、学習の大元となるデータの完成です。

分かち書き

日本語は、英語などのスペースで区切られた言語と異なり、どこからどこまでが1つの単語なのか、判別が容易ではありません。

日本語の自然言語処理をするためには、その区切りを見つけるために、形態素解析を用いて分かち書きするというのが一般的です。

最近では、深層学習により適した前処理として、WordPiece、Byte-pair encoding (BPE)、 SentencePiece など、テキストを「サブワード」と呼ばれる単語よりも短い単位に分割する手法が用いられるようになってきていますが、深層学習を用いたいわけでもない、というのと、可能な限りブラックボックスにせず、見えるロジックで解いておきたいという思いがあり、昔ながらのやり方ではありますが、MeCab分かち書きを行うようにしました。

MeCab (和布蕪)とは、京都大学で開発された形態素分析のエンジンです。
2013年からバージョンは0.996のままですが、今でもよく利用されているエンジンです。

他の形態素解析のツールとしては、Sudachiというワークスアプリケーションズが開発したものや、JANOMEというオープンソースもあります。
検索・分析エンジンのElasticsearchでよく使われている、kuromojiというものもあります。

IPAdicやNEologdなどの辞書を使いたいとか、速度を重視した時にMeCabを使うのがお薦めです。(メモリの消費はよろしくないです)

このタグ予測の仕組みの場合、APIのリクエストを受けた時にも形態素解析を動かすようにするのですが、レスポンスに時間を掛けすぎないという目的もあり、MeCabを選択しています。

全部ひっくるめたモデルに文章をインプットして、タグが返ってくるようにするというよりは、途中途中の処理をホワイトにすることで、チューニングがちゃんと行えるようにしたいという意図もあり、APIのコントローラー上で形態素解析を動かす必要が出てきたという経緯です。

辞書は、新語・固有表現に強いNEologdを使っています。

新語に強いと言っても、スタートアップ界隈の言葉は全然カバーしきれず、独自の辞書が必要になりはするのですが…

MeCabを使い、①のテキストを分かち書きすると、以下のような結果を得ることができます。

ちなみに、品詞付での出力をするように設定して実行してます。

見て分かる通り、ノイズがとても多いですね。

「、」「。」などの記号、「の」「は」などの助詞、「きっと」「もっと」などの副詞、これらは、文章全体の意味を捉えるという目的に対しては、不要な情報と言っていいと思います。

なので、あらかじめ除外する品詞や単語を決めておき、分かち書きしながら、取り除くという処理を行います。

# 除外する品詞1リスト
except_main_features = ['記号', '助詞', '助動詞', '感動詞', '接頭詞', '副詞', '連体詞', '接続詞']
# 除外する品詞2リスト
except_sub_features = ['代名詞', '接尾','副詞可能', '自立', '非自立', '形容動詞語幹']

そうすることで、もう少し意味のある言葉のみを抽出することができます。

お見せしている例では助詞と副詞を除いてますが、結果次第では含めても良いかもしれませんね。

参考までに助詞、助動詞などを除いた場合、ベクトルの精度があがるという論文のリンクを貼っておきます。

分かち書き + ノイズの除去を行ったデータをS3にアップロードして、学習データの準備完了です。

③学習

前編でも紹介したSageMakerで提供されているアルゴリズムであるBlazingText、これを用いて、ベクトルデータを生成するモデルを作っていきます。

必要なプログラムは、sagemakerのパッケージに含まれているため、考慮することはハイパーパラメータ(機械学習を行うためのそのアルゴリズムに設定する値)をどうするかぐらいでしょうか?

自身で何度も試して決めることもできますが、自動でハイパーパラメータを調整する仕組みもAWSにはあるので、それを使うのもいいかと思います。

参考: https://t-redactyl.io/blog/2020/11/automatic-word2vec-model-tuning-using-sagemaker.html

まずは、BlazingTextのimageを取得します。

image = sagemaker.amazon.amazon_estimator.get_image_uri(region_name, "blazingtext", "latest")

そのimageを使って、Estimator(トレーニングとかするオブジェクト)を生成します。

estimator = sagemaker.estimator.Estimator(image,
                                          role,
                                          train_instance_count=2,
                                          train_instance_type='ml.c4.2xlarge',
                                          train_volume_size = 30,
                                          train_max_run = 360000,
                                          input_mode= 'File',
                                          output_path=s3_output_location,
                                          sagemaker_session=sess)

Estimatorに対して、ハイパーパラメータを設定後、

estimator.set_hyperparameters(mode="batch_skipgram",
                              epochs=5,
                              min_count=5,
                              sampling_threshold=0.0001,
                              learning_rate=0.05,
                              window_size=5,
                              vector_dim=100,
                              negative_samples=5,
                              batch_size=11, #  = (2*window_size + 1) (Preferred. Used only if mode is batch_skipgram)
                              evaluation=True,# Perform similarity evaluation on WS-353 dataset at the end of training
                              subwords=False) # Subword embedding learning is not supported by batch_skipgram

レーニングデータを指定し、

train_data = sagemaker.session.s3_input(s3_train_data, distribution='FullyReplicated',
                                        content_type='text/plain', s3_data_type='S3Prefix')
data_channels = {'train': train_data}

学習を開始します。

estimator.fit(inputs=data_channels, logs=True)

これでモデルの完成です。

出来たモデルに対して、単語を投げると、その単語のベクトルを得ることができます。

タグの予測

①サービスの説明文の分かち書きとベクトル化

STARTUP DBの管理画面にある、企業のサービス編集画面の「タグ予測」ボタンが押されると、Flaskで構築したアプリケーションサーバーにサービスの説明文が投げられます。
アプリケーションサーバーには、学習用のデータを作成したサーバーと同様に、MeCabがインストールされており、分かち書きが行われます。

アプリケーションサーバーは、その後、分かち書きされたサービスの説明文をBlazingTextで作成したモデルが乗っているエンドポイント(MLホスティングインスタンス)に投げて、各単語のベクトルを取得します。

②タグの一覧の取得とベクトル化

アプリケーションサーバーは、タグの一覧を、企業情報用のサーバーからAPIを叩いて取得します。

その後、アプリケーションサーバーはサービスの説明文と同様に、タグ一覧をモデルが乗っているエンドポイントに投げて、各タグのベクトルを取得します。

③ベクトルのクラスタリング

分かち書きで得た単語の数は、もちろん一つではなく、ベクトルの向きも様々です。
近似のタグを見つけるためには、それらを集約させる必要があります。
そのために、得られたベクトルをクラスタリングさせる必要があります。

複数のベクトルの集まりをテンソルと言いますが、テンソルはn次元の行列という形で表現ができ、行列の次元を削減するためにクラスタリングを利用しています。

次元を削減するというのは、n次元の行列をk次元に減らすという作業です。

次元削減自体にも、主成分分析 (Principal Component Analysis: PCA) や非負値行列因子分解(Non-negative matrix factorization: NMF)など次元削減の手法がありますが、今回の内容においては、クラスタリングでいい、という判断をしています。

SageMakerの提供しているアルゴリズムの中にも、クラスタリングを行えるアルゴリズムがあります。ただし、問題になるのは、そのアルゴリズムの種類です。

SageMakerで用意されているクラスタリングアルゴリズムは、K-meansです。
K-meansは、中心を仮定してクラスタリングする方法で、今回の目的とは実は合致しません。

と、言葉で言っても分かりにくいですが、Python機械学習ライブラリであるscikit-learnのWebページに非常に分かりやすい図があります。

参照: https://scikit-learn.org/stable/auto_examples/cluster/plot_cluster_comparison.html

K-meansは1番左の列の図になりますが、図にもみられるように、境界を越えてクラスタリングが行われてしまうのが見えますでしょうか?

また、密度連結成分を検出するDBSCANは、非常に綺麗に分けてくれるアルゴリズムではありますが、

境界がなさ過ぎるとすべて同じクラスタとして見なされてしまいます。

今回のケースだと、似たようなまとまりの集まりだったとしてもそれをいくつかの方向にクラスタリングして欲しいところです。

なので、タグ予測でやりたいクラスタリングを考慮し、アルゴリズムはSpectral Clusteringを選択しています。

Spectral Clusteringは、固有値によるスペクトル分解を行うもので、内部でK-meansやDBSCANのロジックが動いていたりします。

このSpectral Clusteringを利用するため、SageMakerは利用せず、アプリケーションサーバー内にscikit-learnを用意し、Spectral Clusteringを用いたクラスリングを行わせるという方針をとっている訳です。

クラスタリングしたのち、クラスタリングしたベクトルの平均合成を行い、クラスタの方向を導き出します。

④コサイン類似度を用いた類似のタグの算出

ベクトルの近似値は、「コサイン類似度」によって求めることができます。

ベクトルの行列を内積やらなんやらすることで計算することができます。

参考: https://www.cse.kyoto-su.ac.jp/~g0846020/keywords/cosinSimilarity.html#:~:text=%E3%82%B3%E3%82%B5%E3%82%A4%E3%83%B3%E9%A1%9E%E4%BC%BC%E5%BA%A6%E3%81%A8%E3%81%AF,%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8%E3%81%AB%E3%81%AA%E3%82%8B%E3%80%82

アプリケーションサーバーは、タグのベクトルと、クラスタリングされた説明文のベクトルを比較し、近い順にランク付けを行い、管理画面に返します。

最後、これを管理画面で表示してあげるだけです。

課題

この方法には、まだいくつか課題が残っています。

Wikipediaのデータが一般的過ぎて、サービス固有の名前や説明がうまくベクトル化できない → コーパスにスタートアップ関連の記事を多く取り込む必要がある

②辞書に無い言葉がサービスに多く出てくるため、うまくベクトル化できない単語がある → 独自辞書に随時言葉を登録していく作業が発生する

③あっちこっちで処理をさせるようになっており、仕組みが複雑 → SageMakerで動くモデルを自作し、そこに投げるだけでいい仕組みにしてもいいかもしれない

などが、大きな課題です。

まとめ

ブラックボックスにし過ぎず、理解できるロジックで組み上げたことで、細かい調整がきき、精度も確かに出るようになったと思います。ただし、課題にも挙げているように、予測したい文章と学習させる文章が合うかどうか、未知の言葉に対する対応など、潰しきれない課題はどうしても出てくるものだと実感させられました。

文中でも記載しましたが、深層学習に適した前処理なども確立されてきていますし、日本語で、BERTを用いた自然言語処理などもでてきており、 そういうものを導入することで、未知語への対応も、より高い精度の学習もできる可能性があります。そのような新しい仕組みに入れ替えることも考慮に入れた上で、引き続き、ブラッシュアップをしていこうと思っています。

【フォースタ テックブログ】STARTUP DBリニューアルについて 〜スタートアップ業界にとって新たな一歩をつくる〜

2021年3月に、国内最大級の成長産業に特化した情報プラットフォームである「STARTUP DB」(スタートアップデータベース)のサービスリニューアルを行いました。本記事では、プロダクトオーナーと開発責任者それぞれの目線から今回のリニューアルについて纏めてもらいましたので、リリースに至るまでの道程をご紹介します。

プロダクトオーナーが語るサービスリニューアルについて

こんにちは、弊社で運営している成長産業に特化した情報プラットフォーム「STARTUP DB」のプロダクトオーナー寺田(@yuyaterada)です。この度、2021年3月18日にサービスリニューアルとあわせて、ENTERPRISE β版の機能リリースをしました。リリースまでの経緯とリニューアル内容について紹介をしていきます。

リリースまでの経緯、コンセプト

STARTUP DBは2018年5月31日にサービスを開始してから間もなく3周年を迎えます。

約1年前にプロダクトに関わるメンバーでプロダクト合宿をし、STARTUP DBで目指す世界観やプロダクトポリシーなどについて議論しました。コロナ禍でもあった為、リモートでGoogleのJamboardを使って議論を進めました。その合宿の中で、スタートアップエコシステムとは?誰に対してどんな価値を提供するのか?など深い議論を重ね、チームとして『国内スタートアップエコシステムのキャッシュフローの総量を増やすこと』をプロダクトミッションとして、国内スタートアップとエコシステムビルダー、それぞれの信頼できるパートナーとの出会いを創出することを目指していく方針になりました。

※プロダクト合宿でつかったJamboardの1例

そこで、よりミッション達成に向けて推進するべく、サービスリニューアルPJTを開始しました。

STARTUP DBでは会社のバリューでもある「Startups First(注1)」をプロダクトポリシーとして一番重きをおいています。それを第一前提に、合宿で話をした、直近で実現したいこと、中長期的に達成したいことを踏まえ、リニューアルプロジェクトを進めました。検討を進める中で既存UIでの追加機能開発も検討としてありましたが、アウトプットの点で実現できない部分があり、今回リニューアルという選択をとりました。

2020年5月頃からN1分析(注2)をスタートし、ユーザーシナリオなどを詰めていきました。10月頃から開発をスタートし、約半年弱の開発期間を経て、リニューアルリリースしました。

注1:全ては日本の成長のために。スタートアップスのために。スタートアップス=『進化の中心』にいることを選択する挑戦者達。

注2:1人の顧客を深く知り、そこからアイデアを掴んで実践に落とし込むという手法。西口一希氏著の「たった一人の分析から事業は成長する 実践 顧客起点マーケティング」より

スタートアップとの事業創造をサポートする機能「ENTERPRISE」

今回サービスリニューアルに合わせて、「ENTERPRISE β版」という機能もリリースしています。「ENTERPRISE β版」は、スタートアップとの事業創造をサポートする機能です。主に大手事業会社や投資家を中心としたエコシステムビルダーの皆様と国内スタートアップ、それぞれが信頼のできるパートナーとのアライアンス機会を創造していき、ひいては、スタートアップ、エコシステムビルダー双方のグロース支援に繋げていきたいと考えています。

ENTERPRISE β版」は先着30社審査制で6月末まで無償提供をします。7月以降の正式版リリースに向けて、利用企業と伴走しながら、プロダクトのアップデートをしていく予定です。リリース後多くの反響を頂き、既に先着30社の受付は終了しており、現在正式版の事前案内申込(特典あり)を受け付けしています。

開発責任者が語るリニューアルに伴うシステム変遷

こんにちは。アクセラレーション本部 テックラボグループの竹内です。ここからはSTARTUP DBのリニューアルの技術的な部分について説明します。

リニューアルに伴い変更したインフラ構成

旧STARTUP DBの構成図

新STARTUP DBの構成図

技術構成について

STARTUP DB(NEW)

言語:TypeScript
フレームワーク:Nuxt.js
インフラ:ALB, CloudFront, ECS, Fargate, ElastiCache, S3

SDBユーザー管理システム(NEW)

言語:Ruby
フレームワークRails
インフラ:ALB, ECS, Fargate, ElastiCache, RDS

SDB管理システム

言語:Ruby
フレームワークRails
インフラ:ALB, CloudFront, EC2, RDS, S3, ElastiSearch(NEW)

インフラの変更点について

まず大きな変更点としてRailsで作成していたSTARTUP DBをフロントエンド(STARTUP DB)とバックエンド(SDBユーザー管理システム)に分離しました。さらにEC2からECS+Fargateにしました。Fargateを選択することでEC2インスタンスを一切管理する必要がなくなり日々の負担が大きく軽減できたと思います。SDB管理システムがまだEC2ですが、今後同様にECSにしていく予定です。
SDB管理システムには全文検索を行うためのElasticsearchを追加しています。検索については後述しています。
これらのインフラはTerraformを利用してコード管理できるようになっています。

フロントエンドについて

Nuxt.jsでSSRモードを選択しました。また、CompositionAPIを採用しています。
これはCompositionAPIがVue3.xで正式に採用され今後の主流になるだろうと思ったこと、そしてレイアウト実装とロジックの分離を行いやすくなることを期待して選択しました。
個人としてはロジックの外出しに関してはうまくできたのではないかと思っています。

機能

認証方式の変更(Firebase Authentication)

今回のリニューアルで認証の実装をFirebase Authenticationを利用して認証する方式に変更しました。Firebaseのおかげでアプリケーション側のDBにはFirebaseのuidさえ保存しておけばユーザー管理ができるようになるため、よりセキュアな状態になっています。

Elasticsearchを使っての全文検索

今回、Elasticsearchを導入し、企業名および通称名だけでなく企業紹介の内容やサービス内容でも検索できるようになりました。
これにより、よりスタートアップの情報をお届けできるようなったと思います。ぜひ「ロボット 開発」「ロボット 利用」など色々なキーワードで検索してみてください。
検索結果画面の投資企業は検索キーワードでヒットしたスタートアップに投資している企業が並ぶようになっています。こちらもぜひ利用してみてください。
今後よりブラッシュアップしていきますので楽しみにしていてください。

全ポジション仲間募集中です!

STARTUP DBのプロダクトミッション『国内スタートアップエコシステムのキャッシュフローの総量を増やすこと』を達成していく為にはまだまだ序章です。今後スタートアップ、エコシステムビルダー双方のマッチング機会を増やしていくことで、事業創造の可能性を広げ、スタートアップエコシステムをブーストさせていきたく考えています。やりたいことがまだまだたくさんあり、是非1人目の事業開発、エンジニア、デザイナーなど全方位で仲間を募集しています!