pyhaya’s diary

機械学習系の記事をメインで書きます

新卒データサイエンティストが 1 年目にやったこと

私は 2022 年 4 月にウォンテッドリーに新卒として入社しました。私は大学時代は実は物理学を専攻していたため、仕事は最初は慣れないことばかりで苦労しました。そこで、新卒 2 年目に入って少し落ち着いたタイミングで 1 年間の業務内容を振り返り、この記事を執筆することにしました。

この記事の内容は技術書典14で頒布したWANTEDLY TECHBOOK 12に掲載されている記事になります。TECHBOOK 12には他にも良い記事が沢山ありますので気になった方はぜひ!
techbookfest.org

1 年間で何をやってきたか

私はこの 1 年間、メインの仕事である推薦システムの改善はもちろん、推薦システムの改善を行うための知識を学ぶために様々なことに取り組んできました。メインタスクの説明の前にそれらについて軽く触れておくと、社内の機械学習勉強会に毎週参加したり、国内外の学会(JSAI、RecSys、DEIM)に参加してブログを執筆したりしていました(興味のある方は「RecSys2022 Wantedly」と検索してみてください。)。これらの取り組みは進歩の早い機械学習という領域で最新の知見を得てキャッチアップしていくことを1つの目的としていて、大学で情報科学を専攻していなかった私にとっては知識を増やす上で非常に有意義でした。

社内勉強会の話はまた後で触れるとして、私がやってきたメインの仕事に話を戻します。私はこの1年でやってきた仕事は、ざっくりとまとめると以下のようになります。

  • 仮説検証によるユーザ課題の定義
  • 実装
  • テスト(オフラインテスト・オンラインテスト)

これらは独立の作業ではなく、以下のように1つの施策の中でグルグルと繰り返していきます。

ユーザが持っている課題感についてなんとなくわかっている状態からはじめて、まずそのざっくりしたものを仮説検証によって解像度を上げていき、具体的な問題に落とし込みます。このフェーズでユーザがどんな問題になぜ困っているのかを理解できると、課題に対するソリューションをある程度絞ることができます。それを実際にコード上に実装するのが次のフェーズです。この段階では検討したソリューションが本当にユーザ課題を解決できるか確信度が十分には高くないので、ログデータを使って課題の解決が可能かどうか定量的・定性的に評価します(オフラインテスト)。ここで確信度が上がればユーザに実際に出してみて検証を行います(オンラインテスト)。テストがうまく行っても行かなくても、結果からは何かしら新しいユーザに対する知見が得られるのでそこから問題に取り組み直したり新しい課題を定義したりして施策を回していきます。

データサイエンティストが課題の分析・ソリューション検討・実装・テスト・テスト分析の一連のタスクをこなすのは、世間一般のデータサイエンティストとはもしかしたら乖離があるかもしれませんが、ウォンテッドリーでは一貫してデータサイエンティストが担当することによってプロダクトを使ってくれるユーザに対する理解が進み、推薦システムの改善にもとても良い効果があると感じています。

この 1 年間で苦労したこと

1 年間様々な施策に携わってきた中で、当たり前ですが様々な困難に直面しました。その困難は新卒1年目なら誰でも直面しそうなものから、私の場合には大学時代に専攻していた分野の違いによるものまで様々ありました。ここではそれらのいくつかを書いてみたいと思います。

仮説検証でハマったこと

仮説検証はデータサイエンティストのタスクとして最も頻度が高い部類に入るものだと思います。データを使ってユーザ課題を見つけて解決したいというときに、最後まで仮説を持たずに分析を進めることはありません。というより無理です。データを見てそこから仮説を立てて徐々にユーザの持っている課題や解決方法を明らかにしていきます。具体的には私達のチームでは多くの場合以下のような手順でユーザ課題を定義するための仮説検証を進めていきました。

1. ユーザの行動をサンプリングして見たり、軽くデータを見たりしてユーザがなぜどんな問題に困っているかについて仮説を挙げていく
2. 仮説をストーリとして整理する
3. ストーリに沿ってデータを使って検証する

最初のうちは 1 番の仮説だしの部分で苦労しました。ここは会社に入って間もない人はみなそうだと思うのですが、その会社特有の知識が圧倒的に足りない(&私の場合にはドメイン知識もほぼ皆無だった)ので、仮説の質が良くなかったためでした。ここを克服する方法は、今になって振り返ってみても大して近道は無いような気がしていますが、基本的な数字や生データをしっかり見ることが重要だと思っています。ここで言っている「基本的な数字」と言っているのはプロダクトを使っている月間ユーザ数などを指していて、これを把握するだけでぐっとユーザに対する解像度が上がります。

生データを見ることの重要性は、言わずもがなといった感じなのですが最初できていなかったところでした。私は大学時代に物理の実験系の研究室に所属していて普段から生データをチェックしてデータに問題がないか確かめる癖はついていたと思っていたのですが、業務で触れる生データは研究室の実験で得られるデータと比べて桁違いに多いため、ついつい生データではなく統計量をみるという方向に逃げてしまっていました。今になっては理解していますが、分析を行うときには、様々な統計的手法を使ってなめされたデータを見るよりも、生データを見てどのセグメントにどんな特徴がありそうか定性的にでも感覚を掴むほうがその後の分析のスピードが早くなります。

また、良い仮説を出すのに苦労していたときに注目していたもう1つの観点が仮説の網羅性でした。仮説検証はなにか課題があってそれを解決するために用いる手段です。なので出した仮説によって目的が果たせそうかどうかというのはとても重要な要素です。私はこの課題を克服するために仮説を木構造に分解して捉えるということをやってみました。


実装でハマったこと

実装で一番苦労したのが、コードの変更容易性や可読性といったコード品質を気にかけながら実装を進める部分でした。情報系のバックグラウンドを持っている方ならばこのあたりは当たり前なことだと思うのですが、入社するまで私はコードの品質にあまり留意してこなかったので入社してから苦労しました。研究室時代には実験の分析用にコードを書くことがほとんどで、使い捨てだったり、使い回すにしても自分しか使わないか後輩がたまに使うかなぐらいでした。研究室のほかメンバーもバリバリコードを書くような人種ではなかったので、動けば OK、動かなかったらエクセル使えばみたいな感じでした。

ここまでで十分想像がついたと思いますが、こういうコード品質とは無縁のコードを書くのに慣れていると、本番に投入できるような品質のコードを書くとなったときにとても苦労します。私の場合にはここでハマったときに最初、なるべくコードの変更が無いように実装をすればいいのではと安直に考えて取り組んでいたのですがすぐにこの方法は良くないと感じるようになりました。この方法だとコードの品質は上がりませんが複雑さは上がることが多いためです。例として、データに対してなにか処理をするメソッド process_data があったとします。そして今、新しく別の処理をするメソッドを作りたいがその中身は process_data と似ている部分が多かったとします。このとき、コードの変更量を最小限にするというポリシーで動くと以下のようなコードが出来上がるかもしれません。

def process_data(data):
    # ...

    if isinstance(data, str):
        data = json.loads(data)

    elif isinstance(data, dict):  # ← 追加!
        # ...
        if "name" in data:
            process_name(data["name"])
    elif isinstance(data, list):  # ← 追加!
        # ...
    elif isinstance(data, str):  # ← 追加!
        # ...

明らかにコード品質を悪化させていることがわかります。では最善の実装は何かと言ったら時と場合によるというのが正しいでしょう。既存のメソッドと新しく実装するメソッドで共通処理に名前がつけられるなら切り出してもいですし、上の例だと引数の型で分岐させているので新しく実装する部分で入力の型を揃えるという方法もあると思います。process_code というメソッドの名前の抽象度が高すぎるのが原因なのでより具体的な名前をつけ直して、新しいロジック用に新しいメソッドを定義するという方法もあるでしょう。結局いちばん大事なのは思考を機械的にすることなく、コードの品質を保持する・上げるためにどうしたらいいか考え続けることで、それからは本を読んで勉強したりレビューしてもらって考えたりすることを意識的にするようしています。

テストでハマったこと

エンジニアリングの文脈でテストというと指すものが人によって様々だと思いますが、ここではオフラインテスト・オンラインテストについて書きます。オフラインテストというのは、考えた施策が実際にユーザに良い効果をもたらしそうかどうかを過去のログデータなどを使って検証するフェーズです。ここで良さそうということになったら実際にユーザに出して効果があるか検証します(オンラインテスト)。見たい効果を正しく得るにはテスト設計がとても重要ですし、テスト結果が得られたときそれを正しく解釈するにも様々な知識が必要になります。テストをする、となったときに考える必要のあることはぱっと挙げただけでも、

  • テストの対象となるユーザはどんなユーザ?
  • テスト方法はどうする?
  • テスト期間はどのくらいに設定すべき?
  • どの指標がどのような条件を満たしていたら施策の効果があったと判断する?

など多くあります。

特に苦労したのが、「テスト対象ユーザ」の決定部分でした。施策は明確なターゲットユーザのもとに行われるものですが、介入がそのターゲットユーザだけに閉じるとは限りません。例として Wantedly Visit を考えてみます。Wantedly Visit では学生も中途ユーザも同一のプラットフォームで気になる募集を探して話を聞きに行くことができるのですが、ここで学生をターゲットとして体験を良くしたい場合を考えたとします。手段は色々考えられますが、機械学習モデルに新しい特徴量を追加して学生が話しを聞きに行きたいと思える募集をもっと出すようにしようとすると、このモデルの挙動は学生に対して変化するのはもちろん、中途ユーザに対しても変化します。このような状況でテストを学生ユーザだけに限定して進めてしまうと、極端な話、学生の利用は激増して中途ユーザは誰も利用しなくなるという状態になっていても気づけず、学生の結果だけ見て本番環境全体に適用させてしまうという大事故を引き起こしかねません。

そのため、ターゲットユーザはどんなユーザか、介入の影響を受けるユーザはどんなユーザか、の両方を把握しテストの通過条件をそれぞれのユーザセグメントに対して適切に設定する必要があります。このあたりの知見は(少なくとも自分は)大学での研究活動では縁があまりなかったのと、自分の設計があっているのか間違っているのか自己判断が難しいので苦労しました。なのでテスト設計のレビューを積極的にしてもらって、フィードバックから学んでいくというのを中心に、足りない知識を本で補うというような学び方をしていました。一番参考になっていたのは、よくカバ本と呼ばれている、「A/B テスト実践ガイド」でした。A/B テスト実践ガイドでは「テスト時のランダム化単位をしっかり考えて設計することの重要性」や「テストの信頼性を上げるためのガードレールメトリクスの重要性」など、基本的ですが大事なことを多く学べました。

やってみて良かったと感じたこと

ここまでは割と課題にぶつかってそれに対してどうしてきたかという課題ドリブンな話をしてきたのですが、ここでは自分の成長のために色々試してみたことのうち、やってよかったと思っていることをいくつか紹介します。やってみてよかったことで共通しているのは何かしらの「アウトプットをすること」です。普段勉強しているとなにかとインプットが多くなると思いますが、インプットしただけだと理解したつもりでいたけど実は自分の中でちゃんと使える形で整理できていないということがよく起こります。アウトプットして第三者に見せるときには人は必ず情報を整理したり足りない情報を調べたりします。この過程でわかったつもりでいたことに気づけたりして効率的に自身の成長に繋げられたと思っています。

社内勉強会への参加

ウォンテッドリー社内には有志による様々な勉強会が開かれており、私はデータサイエンティストで開催している機械学習輪講会に毎週参加していました。この輪講会では機械学習関連のブログを読んで他のメンバーに紹介したり、論文を読んで議論したりしていました。この勉強会に参加することは、自分で勉強したり情報収集するよりもはるかに効率的に自身の知識を広げられるので参加してみてよかったと思っています。(この機械学習勉強会はオープンで興味のある方は参加もできますのでぜひ https://github.com/wantedly/machine-learning-round-table

毎週勉強会のために論文やブログを読んで発表できるようにまとめたりする作業や、他の人の発表を聞いて新しい知識を得ることはもちろんですが、私の場合にはこの勉強会をきっかけにアクションにつなげることができたりしていて参加してよかったと思っています。ある回で機械学習エンジニアの OSS コミットに関するスライドが紹介されたのですが、それをきっかけに私自身が OSS にコミットしてみたいというモチベーションがわきました。また、別の機会で自分が発表するトピックを探している際に、偶然 SPyQL という OSS を見つけました。これは Python で書かれた SQL のようなもので、CLI 上で CSV ファイルを分析するのに使うことができるツールです。コードを読んでいたら SQL をパースする部分に改善の余地があることを見つけたので勢いでコミットすることにしました。結果、私の提案はメンテナーに受け入れられて無事マージされるところまで行くことができました。更に派生して、この経験は社内 LT での発表やブログの執筆にもつながったので、これ1つで様々な経験・知識が得られたと思っています。

機械学習論文の追試

こちらも元をたどれば上に書いた社内勉強会から派生したものなのですが、最近、機械学習モデルを実際に実装してみることで読むだけではわからない新たな知見を得られるのではないかと考えて、自分で論文で提案されているモデルの実装、追試をしてみました。リポジトリはオープンにしているので興味のある方は見てみてください。

In SIGIR. 165–174.

やってみてわかったことですが、実際に自分で実装してみることで論文の中身の理解を深めることができたと感じています。論文ではよくわからなかった式がコードを読むことで意図を理解できたり、逆に論文で展開されていたロジックはコードにするとこんな感じになるのか、など相互に不足していた知識を補い合うような効果があリました。また、自分で実験環境を持てるので、論文には書いていなかったけど気になる事があったときに実際に回してみて確認することができるのもとても良かったです。特に、なかなか論文の中には書いていないが実務で使うとなったときに必要な情報である、学習時間やリソースの使用量を把握することもできたのは大きな収穫だったと思います。

次にやりたいこと

最後に、私が 1 年間データサイエンティストとしてやってきて、これから何に挑戦したいと思っているかについて少しだけ書きたいと思います。上に書いたように、この1年間でアウトプットの重要性について理解できたのですが、そのアウトプット先は社内での発表やブログに限られており、得られるフィードバックも限定的でした。なのでこれからは外部発表にも参加してどんどん自分の知見を伝えていき、そこで生まれるコミュニケーションを通じてまた新たな知見を得たり、人とのつながりを作ったりということをしていきたいと考えています。

また、実務に直接関連する部分では、自分のできる仮説検証の範囲を広げたいということを考えています。この 1 年間で私が行ってきた仮説検証は、上の例にも出したような「なぜ」に答えるためのものが大半を占めていました。これはつまり、ある程度はっきりした課題があってその解像度を上げることで解決につなげていくという仕事を任されていたことが多かったためです。今後はさらに前段の「ユーザが何に困っているのか」を明らかにするための仮説検証をどんどんやってきたいと思っています。また、これに関連して自分で考えて進めていくという点でもこれからもっとできるようにしていきたいなと思っています。

1 年目に読んで良かった本
  • リーダブルコード
  • コンサル 1 年目に学ぶこと
  • イシューからはじめよ
  • A/B テスト実践ガイド
  • 効果検証入門
  • 因果推論の科学

よかったらTwitterのフォローもお願いします!
twitter.com