Xcode12時代のCarthageで起こった問題とXCFrameworkへの移行

モバイル基盤部のhiragramです。こんにちは。

私たちは、iOS版クックパッドアプリの開発において、CocoaPodsとCarthageを併用して依存ライブラリを管理しています。しかし、Xcode12の時代がやってきて、Carthageによる依存ライブラリのビルドに問題が生じました。この記事では、どのような問題なのか、そしてどのように対処したのかを紹介します。

3行まとめ

  • Xcode12とCarthageの組み合わせで問題が生じた
  • CarthageからSwiftPMへの移行を模索したが、断念した
  • 2021年2月にリリースされたCarthage 0.37.0でXCFrameworkが正式にサポートされたので、それに移行した

Xcode12と、当時のバージョンのCarthageの組み合わせで生じる問題

Carthageは、中央集権的なCocoaPodsとは対照的に、gitリポジトリを直接指定する分散型のパッケージ管理ツールです。リポジトリに含まれるライブラリのプロジェクト設定を使って、iOSアプリに組み込めるフレームワークをビルドします。Mac上で動くiOSシミュレータと実機の両方で使えるようにするため、それぞれのCPUアーキテクチャのためのバイナリを lipo というツールで1つのバイナリにまとめて、XXX.framework というファイルを作ります。lipoで作られた、複数のアーキテクチャ向けのバイナリを含むものはファットバイナリと呼ばれます。

この仕組みはXcode11以前から用いられてきましたが、Xcode12のツールチェインを使って、従来の方法でライブラリをビルドしようとするとエラーが出て失敗するようになりました。これはXcode12になって、iOSシミュレータ用のバイナリが、従来のIntel製CPUのx86_64用バイナリに加えてApple Siliconのarm64用バイナリを含むようになったことに起因します。iPhone実機用とApple Silicon上のシミュレータ用のバイナリがいずれもarm64用で、lipoの制約によりその両方を1つのファットバイナリに含めることができないためです。

Carthage builds fail at xcrun lipo on Xcode 12 beta (3,4,5...) · Issue #3019 · Carthage/Carthage

こちらのissueでは、シミュレータ向けのビルドをするときに EXCLUDED_ARCHS にarm64を指定することで、 "Apple Silicon上で動くiOSシミュレータ" 向けのコードをバイナリに含めないようにしてarm64(実機用)との衝突を防ぐ、というワークアラウンドが紹介されています。Xcodeのbetaが進むうちに根本的な対処法が見つかるだろうと楽観していましたが、Xcode12が正式にリリースされて以降もこのワークアラウンドが必要であるということがわかりました。そのため、クックパッドアプリもこのワークアラウンドを導入して開発/リリースを続ける必要がありました。

しかし、ワークアラウンドはあくまでその場しのぎですので、抜本的な対応として何をどうするべきか検討することにしました。

Swift Package Manager移行の模索

このセクションで書かれているのは2020年12月時点での状況です。

Carthageに囚われずパッケージ管理について改めて見つめ直す良い機会と捉えて、根本対応の手段を検討しました。その中で、XcodeのGUI上で扱うSwift Package Managerが候補にあがりました。サードパーティの依存管理ツールをハイブリッドで運用するよりも、公式が用意したもののほうが色々と都合がよいに違いありません。

Swift Package Manager(以下SwiftPM)は、Appleが提供する依存パッケージの管理ツールです。Xcode11からSwiftPMの機能が統合され、プロジェクトにSwiftPMのパッケージを含めることができるようになりました。Carthageで導入していた外部ライブラリを、SwiftPM経由で入れるように出来ないかを模索しました。

断念

結論から述べると、SwiftPMへの移行を断念しました。私たちの開発スタイルやプロジェクト構成にマッチしない都合があったからです。その中でもクリティカルだった、ユニットテストに関する問題を紹介します。

RxTestを使ったテストがビルドできない

クックパッドアプリではユニットテストの一部でRxTestを利用しており、リモートブランチを更新するごとにCI環境でテストがビルド/実行されます。しかし、RxSwiftをSwiftPMで導入したときに、RxTestを使っているテストコードがビルドできませんでした。シンボルの重複が起きているというエラーが出て、プロジェクトのビルド設定を変えたりアンブレラフレームワークを挟んだりしても解決できませんでした。RxSwiftのissueでも、多くの人がテストコードのビルドやライブラリのリンクなどについて問題を報告していて、SwiftPM側に問題があるという見解が示されています。

We unfortunately can't support SPM properly due to a critical known bug in SPM affecting many multi-target repos: https://bugs.swift.org/browse/SR-12303

- Migrating from Cocoapods to SPM, UITest target "missing required module 'RxCocoaRuntime'" · Issue #2210 · ReactiveX/RxSwift

上述のコメントからリンクされているバグチケットは報告から半年以上たった現時点でもSwiftPM開発メンバーからのリアクションが無く、私が手元で何かちょっと頑張った程度ではどうにもならないだろうと考え、SwiftPMへの移行を断念しました。

Carthage 0.37.0で新しく追加されたXCFrameworkへの移行

SwiftPMへの移行を模索していたのと同じ時期に、CarthageのリポジトリではXCFrameworkをサポートするための開発が進められていました。 XCFrameworkとは、Xcode11からサポートされた、フレームワークを配布するための新しい構造です。従来の形式では、複数のアーキテクチャのためのバイナリを1つのファットバイナリに統合していましたが、XCFrameworkは単体のプラットフォームのためのフレームワークを複数含む形式です。lipoによって1つのファットバイナリに統合する必要が無くなったため、記事の冒頭で述べたarm64のバイナリが衝突してしまう問題を回避できます。

XCFrameworkの詳細については、WWDC19のトークを御覧ください。

Binary Frameworks in Swift - WWDC19

Carthageでは0.37.0から、 --use-xcframeworks というオプションをつけることでXCFrameworkとしてビルドするようになりました。クックパッドアプリのプロジェクトでは0.37.0を使うことにして、Carthageで入れるライブラリはすべてXCFrameworkにすることにしました。

また、XCFrameworkの場合は従来Build Phaseに足す必要があった copy-frameworks が不要になります。もともと copy-frameworksは、AppStoreへサブミットするときにiOSシミュレータ向けのバイナリを含んでいると機械的にリジェクトされてしまうことに対する回避策でしたが、XCFrameworkは内部でプラットフォームが区別されているため、Xcodeの標準のコピーで事足りるようです。

クックパッドアプリではプロジェクトファイルをgit管理せずXcodeGenで生成していますが、XcodeGenでも従来のフレームワークと同じようにXCFrameworkのリンクを記述できます(クックパッドアプリ開発におけるXcodeGenの活用については、こちらの記事をごらんください)。XcodeGenのymlで、carthage というキーでフレームワークを指定していた所を、 framework というキーで、Carthageのビルドディレクトリにあるxcframeworkファイルを指定するだけです。

Build PhaseのLink Binary With Librariesの欄で、このように従来のフレームワークと同じ様にxcframeworkが指定されています(XcodeGenを使わずにプロジェクトファイルを編集する方は、この様にすれば動くはずです😉)。

f:id:hiragram:20210309145211p:plain

lipo時代のワークアラウンドとして行われていた、arm64用バイナリの除外も必要ないので、M1プロセッサのMac上で動くシミュレータでもアプリを動かすことが出来ます。クックパッドアプリはすでにワークアラウンドを削除しXCFrameworkに移行したバージョンが毎週リリースされており、この移行による問題は今の所報告されていません(クックパッドアプリ開発における毎週の自動サブミットについては、こちらの記事をごらんください)。

ただし、XcodeやCarthageにおけるXCFrameworkのサポートがまだ成熟していないからか、キャッシュの有無の判定が正しくないことがあります。例えばCarthage側でライブラリを更新してバージョンが変わったとき、XcodeやCarthageのキャッシュを一度クリーンしないと新しいXCFrameworkを使ってくれないことが多いです。原因はまだ調べられていませんが、おおよそ以下のことをやると正しくアプリがビルドできるようになります。

  • Xcodeを再起動する
  • Xcodeで Clean Build Folder をする
  • Carthage/Build を削除して再ビルドする

まとめ

iOS版クックパッドアプリでは、Carthageで管理されていたライブラリをXCFrameworkとして扱うようになりました。また、その過程で、SwiftPMなどの別のアプローチについても検証するよい機会となりました。

ちなみに、Xcode12.5 Beta2から、Xcode上のSwiftPMで外部ライブラリをDynamic Frameworkとしてビルドできるようになりました。これによって、先述のRxTestの問題が解決されているかもしれません(まだ未検証です😄)。モバイル基盤部では今後もビルド環境をモダンに保つための取り組みを続けていきます。このような領域に興味がある方は、ぜひ以下のリンクからご応募ください!

info.cookpad.com

Google Apps Script の拡張サービスの TypeScript 用型定義ファイルの自動生成

こんにちは、メディアプロダクト開発部の後藤(id:mtgto)です。

今回は Google Apps Script の28個の拡張サービスについて、 TypeScript 用の型定義ファイル (@types/google-apps-script) を、Web エディタのオートコンプリートマクロ用のデータから自動生成するプログラムを作成した話を紹介します。

Google Apps Script の紹介

読者の皆様はGoogle Apps Scriptはご存知でしょうか。名前は聞いたことがあるけど使ったことはあまりないという方が多いでしょうか。

Google Apps Script を使うことでドキュメント、スプレッドシート、スライド、フォームといった Google サービスのデータの取得・更新などを ECMAScript のプログラムから行うことができます。 例えば、

  • スプレッドシートのセルを読み込んでドキュメントに出力
  • Drive のファイル一覧を読み込んでスプレッドシートにリンク付きで出力
  • 外部 API にアクセスするスクリプトを一定周期ごとに動かす

このようなことを無料で行うことができます1

クックパッドでは Google Workspace をオフィススイートとして広く使用しており、スプレッドシートやドキュメントの自動化・マクロ実装などに Google Apps Script が利用されています。

Google Apps Scriptを普段の作業の改善に使うことも可能です。たとえば定例のテンプレートを google docs で管理してておき、次回の定例前にApps Scriptで雛形を追加する、といったこともECMAScriptを少し書くことで可能です。

私は以前Google Apps Scriptを利用して、期間と会議室の候補を入れると参加者の予定から空き時間&空き会議室を探してくれるウェブアプリを作りました。

f:id:mtgto:20210308214802p:plain
会議時間、参加者、会議室から、全員の空き時間と空き会議室を検索

f:id:mtgto:20210308215018p:plain
検索結果をクリックし、タイトルと説明を埋めて「登録」を押すとGoogle Calendarに予定が作られます

英語版のみですが、Codelabs に概要を知るためのチュートリアルも用意されています。 ざっくり Google Apps Script でできることの概要を知りたい方はちょうどいいかもしれません。

https://developers.google.com/apps-script/quickstart/fundamentals-codelabs?hl=ja#getting_started

Google Apps ScriptをTypeScriptで開発したい

Google Apps Scriptは前述の通り、ECMAScriptで記述する必要があります。 Webエディタも用意されていますが、ユニットテストやトランスパイルを必要とする場合、gitバージョン管理をしたいなどの場合にはGoogle謹製の clasp を使うことでCLIからの開発が実現できます。

clasp + TypeScriptでGoogle Apps Scriptのプログラムを書く場合には、@types/google-apps-script npmパッケージを使うことでGoogle Apps ScriptのAPI定義を利用できます。

標準サービス (Built-in services) と拡張サービス (Advanced services)

Google Apps Script で利用できる Google のサービスには標準サービス (Built-in services) と拡張サービス (Advanced services) という分別があります。

標準サービスのうちでもCalendar, Slides, Driveなど様々なGoogleサービスを利用できますが、さらに拡張サービスを使うことでGoogleの公開APIをECMAScriptから利用することが可能になります。

拡張サービスじゃないとできないことの例としては、

  • 自分以外のメンバーのカレンダーの予定を取得
  • 組織内のメンバーや会議室の情報を取得 (Admin Directory)
  • YouTube、Analytics、BigQuery などの標準サービスの対象外サービスの公開APIの利用

などがあります。

先程紹介したメンバーと会議室の空き時間を調整するアプリでは自分以外のメンバーの予定を調べないとだめなので、拡張サービスの利用がどうしても必要でした。

標準サービス用のTypeScriptの型定義は id:motemen によってAPIリファレンスから自動生成することで用意されていたのですが、拡張サービスの型定義は残念ながら用意されていませんでした。

そこで私が拡張サービスの型定義の自動生成に挑戦することにしました。

https://github.com/mtgto/dts-google-apps-script-advanced

Webエディタの自動補完定義からの自動生成

Google Apps Scriptで利用できる拡張サービスの数は2021年現在 28、利用できる関数の数は 2913 にもなります。

拡張サービスは標準サービスと違って API ドキュメントも公開されていないためスクレイピングによるAPI定義の取得もできません。 そこでWebエディタの自動補完マクロ用に用意されているJSONPデータに注目しました。

Webエディタでは拡張サービスであっても関数一覧が自動補完されます。

f:id:mtgto:20210308215049p:plain
GAS のエディタでの自動補完

自動補完の実現のために拡張サービスごとの関数やクラス構成はJSONPをWebエディタが読み込むことで実現しているようでした。例えば拡張サービスのCalendarの自動補完JSONPですと、

{
  "1": {
    "1": "Calendar_v3",
    "2": [
      {
        "1": "Acl",
        "2": "Calendar_v3.Calendar.V3.Collection.AclCollection",
        "3": 1
      },
      {
        "1": "CalendarList",
        "2": "Calendar_v3.Calendar.V3.Collection.CalendarListCollection",
        "3": 1
      },
      {
        "1": "Calendars",
        "2": "Calendar_v3.Calendar.V3.Collection.CalendarsCollection",
        "3": 1
      },
      ...
      {
        "1": "Events",
        "2": "Calendar_v3.Calendar.V3.Collection.EventsCollection",
        "3": 1
      }
    ],
    "3": [
      {
        "1": "newAclRule",
        "2": "Calendar_v3.Calendar.V3.Schema.AclRule",
        "6": "Create a new instance of AclRule"
      },
      ...

このようなデータが用意されています。2 このJSONをダウンロードし、プログラム内部でTypeScriptのnamespaceとinterfaceの構造に変換し、最後にd.tsファイル形式で出力することができるようにしました。

namespace Calendar {
    namespace Collection {
        interface AclCollection {
            // Returns an access control rule.
            get(calendarId: string, ruleId: string): Schema.AclRule;
            // Returns an access control rule.
            get(calendarId: string, ruleId: string, optionalArgs: object, headers: object): Schema.AclRule;
        }
    }
}

Google Apps Scriptの中がJavaScriptではないなにかで書かれているためか、基本データ型として number の代わりに Integer となっていたりするのでそのような型の変換を行うなどしてTypeScriptの型定義として忠実に再現しています。残念ながらJSONデータの中で名前しか公開されてないデータ構造を関数のレスポンスの型として使っていたりするので、そこは諦めて any で定義しています。

私が最初に型定義ファイルの自動生成プログラムを書いたときには自分が利用したいCalendarとAdmin Directoryにだけ対応していればよかったので、その2つだけDefinitelyTypedにPull Requestを出してマージしてもらいました。 そのあとGoogle Apps Scriptの中の人と話したりしてなんやかんやあり、結局全ての拡張サービスに対応できるようにプログラムの修正をしました。

まとめ

Google Apps Scriptの拡張サービスのTypeScriptの型定義をWebエディタの自動補完用のデータから自動生成するプログラムを書いた話を紹介しました。

Google Workspaceをがっつり利用している会社や組織の場合、Google Apps Scriptの標準サービスだけでも色々なことができることはあると思いますが、さらに拡張サービスを使うことで実はこんなこともできる、みたいな展開があるかもしれません。 また小さいスクリプトをGoogle Apps ScriptのWebエディタから書くのもいいですが、clasp + @types/google-apps-scriptを使うことでCLIでのTypeScriptでの開発もしやすくなっています。

QiitaのGoogle Apps Scriptタグ にもたくさん記事が投稿されていますので興味のある方はご覧ください。

クックパッドでは仲間を募集しています

クックパッドではモダンなフロントエンドの基盤作りレガシーなフロントエンドからの移行を始め、web開発に力を入れています。もしこの記事を見て興味がありましたらお気軽にお問い合わせください。

https://info.cookpad.com/careers/


  1. 注意:無料なのはQuota の範囲内の場合です。

  2. Googleアカウントでログインしてないとダウンロードできないため、28サービス分のJSONPのダウンロードを手動でやる羽目になって地味に面倒でした。

形態素解析を行うだけのバッチをつくる

研究開発部の原島です。今日は表題の渋いバッチをつくった話をします。

あっちでも形態素解析、こっちでも形態素解析

みなさん、形態素解析してますか?してますよね?クックパッドでもさまざまなプロジェクトで形態素解析をしています。

いや、むしろ、しすぎです。プロジェクト A でレシピを解析し、プロジェクト B でもレシピを解析し、プロジェクト C でもレシピを解析し、... といった具合です。ちなみに、形態素解析(の結果)が必要なプロジェクトとしてはレシピの分類やレコメンド、各種分散表現(e.g., word2vec)や BERT の学習などがあります。

もちろん、最終的に得たい解析結果が違うのであれば問題ありません。しかし、私が見たかぎり、ほとんどの場合は同じ(もしくは、同じにできそう)でした。であれば、

  1. 解析器をインストール(→ Dockerfile を試行錯誤)
  2. 解析対象を取得(→ SQL を試行錯誤)
  3. 解析器を実行(→ クックパッドの場合は ECS や IAM の設定を試行錯誤)
  4. 解析結果を保存(→ 同様に S3 や RDS の設定を試行錯誤)

という一連の処理を各開発者が個別に行うのは非効率です。同じ解析器を使い、同じ解析対象(基本的には解析時の全レシピ)を集め、定期的に解析を行い、解析結果を簡単に使い回せるようにしたい。形態素解析が必要なプロジェクトが増えるにつれ、そういう想いが募っていました。

共通化は面倒だけど、難しい話ではない

こうした背景で、重すぎる腰を上げて、共通化を行うことにしました。こういう作業ってどうしても後回しになりがちですよね。各開発者は各プロジェクトを進めたいのであって、他のプロジェクトのことまでケアして共通化を行うのはなかなか面倒です。

一方、この話は技術的には大したものではありません。単に、「形態素解析を行うだけのバッチをつくる」というだけの話です。上の処理 1 から 4 を丁寧に行ない、各プロジェクトが使いやすい形で最終的な解析結果を残せば任務完了です。難しい話ではありません。

形態素解析を行うだけのバッチ

というわけで、そういったバッチをつくってみました。バッチの概観は下図のとおりです。以下では、バッチの各処理について、その詳細をお話しします。

f:id:jharashima:20210307203402j:plain

1. 解析器をインストール

していません。同僚の @himkt がつくった konoha を使いました。konoha はさまざまな形態素解析器(e.g., MeCab、Sudachi、KyTea)のラッパーです。konoha で解析を行なう社内サーバがあったので、解析器のインストールや設定はこのサーバに委ねることにしました(なので、上図にも処理 1 は含まれていません)。なお、クックパッドで使っている解析器は MeCab です。

2. 解析対象を取得

解析対象はレシピ(のタイトルや紹介文、手順など)です。これは Redshift から取得しました。クックパッドではほとんどのデータが Redshift に集約されています。また、Queuery(きゅーり)という社内向けのシステムがあり、UNLOAD を使うことで Redshift に負荷をかけずに SELECT が実行できるようになっています。

今回は、Queuery をさらにラップした corter(かーたー)という社内向けの Python パッケージをつくりました。corter は COllect Recipe-related TExts from Redshift の略で、その名のとおり、レシピに関するテキストを Redshift から収集するためのものです。

以下は、corter でクックパッドの全レシピのタイトルを取得するコードです(レシピ ID はダミーです)。

from corter.agent import RecipeTitleAgent
agent = RecipeTitleAgent()
recipe_ids, titles = agent.collect()
print(recipe_ids[0], titles[0]) # => 12345, 'ナスの肉味噌炒め'

corter の内部には、クックパッドの機械学習エンジニアであれば誰もが試行錯誤したであろう SQL が押し込められています。公開済みのレシピを絞り込む WHERE の条件とか毎回忘れちゃうんですよね。

ちなみに、このような Python パッケージをつくったのにはもう一つ理由があります。生文(形態素解析されていない文)を使うプロジェクトでもこのパッケージを使いたかったからです。このようなプロジェクトとしては、たとえば、SentencePiece の学習などがあります。

3. 解析器を実行

これは簡単です。2 で集めたレシピを 1 で触れた解析サーバに投げているだけです。ただし、現在、クックパッドには全部で約 350 万品のレシピがあります。いくら MeCab が高速でも、全レシピを 1 並列で解析するのは時間がかかります。

そこで、ジョブ管理システム kuroko2 の paralle_fork を使い、10 並列で解析を行うようにしています(正確には、処理 2 の段階で解析対象を 10 分割で取得しています)。これで、350 万品を解析対象としても、タイトルのような短いテキストであれば 5 分程度で、手順のような長いテキストでも 1 時間程度で解析が行えるようになりました。

4. 解析結果の保存

解析結果は Redshift に保存するようにしました。S3 や RDS などの選択肢もありましたが、2 でも触れたように、クックパッドではほとんどのデータが Redshift に集約されています。解析結果も Redshift に保存することで、他のデータと一緒に使いやすいようにしました。

解析結果を Redshift に保存するには、まず、3 の結果を S3 にアップロードします(上図 4-1)。次に、アップロードされた 10 個の解析結果をマージして、1 個の TSV をつくります(4-2)。そして、SQL バッチフレームワーク bricolage を使い、TSV の中身を Redshift に COPY します(4-3)。これで、数億行のロードでも 10 分弱で終わります。

最終的に、下表のようなテーブルに解析結果が保存されます。これはタイトルの解析結果を保存したテーブル(上図では recipe_title_words)です。一行が一単語です。主なカラムは position(単語の出現位置)と surface(表層形)、pos(品詞)、base(原形)です。analyzed_at は、その名のとおり、解析時刻です。

recipe_id position surface pos base analyzed_at
12345 0 ナス 名詞 ナス 2021-03-08 00:00:00
12345 1 助詞 2021-03-08 00:00:00
12345 2 名詞 2021-03-08 00:00:00
12345 3 味噌 名詞 味噌 2021-03-08 00:00:00
12345 4 炒め 動詞 炒める 2021-03-08 00:00:00

2 から 4 を日次で実行

以上の処理 2 から 4(今回、1 はなにもしていません)を、デプロイツール hako を使い、日次で実行しています。具体的には、ECS でコンテナを起動し、そこで処理 2 から 4 を実行しています。これにより、毎日、その日の時点で公開済みのすべてのレシピの解析結果が Redshift に保存されている状態にしています。

余談として、差分更新も考えました。つまり、前日に投稿(正確には公開)されたレシピの解析結果は追加し、修正されたレシピの解析結果は修正し、削除(正確には非公開)されたレシピの解析結果は削除することもできます。しかし、すべてのレシピを解析したところで大して時間がかからず、むしろ差分更新によって複雑性が増すデメリットの方が大きかったので、差分更新はやめました。

こうやって文書にすると、各処理は大変に見えるかもしれません。しかし、実際のところは既存の便利ツール(konoha、Queuery、kuroko2、bricolage、hako)を組み合わせただけで、そんなに大変ではありません。私が頑張ったことと言えば、corter をつくったことくらいでしょうか。これも Queuery のおかげでだいぶ楽をさせてもらいました。

解析結果を各プロジェクトで使う

さて、解析結果はさまざまなプロジェクトで使えなければ意味がありません。そこで、生文だけでなく解析結果も corter で取得できるようにしました。生文を取得するときと同様、裏では Queuery を使っており、UNLOAD でラップした SELECT 文を Redshift で実行しています。

以下は、クックパッドの全レシピのタイトルの解析結果を取得するコードです。解析結果はスペース区切りで返ってきます。オプションを変えることで、品詞で絞り込んだり、ストップワードを弾くこともできます。

from corter.agent import SegmentedRecipeTitleAgent
agent = SegmentedRecipeTitleAgent()
recipe_ids, segmented_titles = agent.collect()
print(recipe_ids[0], segmented_titles[0]) # => 12345, 'ナス の 肉 味噌 炒め'

解析結果が必要なプロジェクトは基本的に Python がベースです。上のコードのあとで schikt-learn なり gensim なり transformers なりを使えば、レシピの分類やレコメンド、各種分散表現や BERT の学習などがすぐに始められます。

こうして、当初の目的どおり、さまざまなプロジェクトで各開発者が個別に形態素解析を行うという事態が避けられるようになりました。

次は?

本エントリの最後に、次に取り組みたいことを三つほど挙げておきます。

一つ目は、今回 Redshift に保存した解析結果をまだ使えていないプロジェクトが残っているので、これらをなくすことです。基本的には、既存のコードを corter に置き換えていくだけです。一方、それだけでは済まないプロジェクトがあります。レシピ検索のインデキシングです。まさに形態素解析が重要なプロジェクトですが、レシピ検索周りはレガシーの巣窟なので、これを置き換えるには相当の時間と覚悟が必要です。

二つ目は、再学習した解析器を使うことです。クックパッドでは、昨年、500 品からなるレシピの解析済みコーパスをつくりました。このコーパスには形態素解析(と固有表現認識、構文解析)の正解データが含まれています。このコーパスで解析器を改善し、解析誤りを減らすことで、形態素解析が必要なすべてのプロジェクトを底上げしたいと考えています。解析済みコーパスと再学習については来週の言語処理学会の @himkt の発表もご覧ください。

三つ目は、Redshift ML を使うことです。Redshift ML、突然現れましたね。上でも述べたように、解析結果は Redshift に保存してあります。これらを特徴量としてモデルを学習し、Redshift 内で推論するというフローをつくれば、レシピの分類などのプロジェクトは大部分を Redshift に任せられるかも?と考えています。Redshift ML についてはまだ勉強不足なので、まずは勉強します。

さて、これらに取り組むだけでも大変ですが、クックパッドには他にも取り組みたいことがたくさんあります。「おもしろそう!」とか「やってみたい!」と思ってくださった方は、ぜひ、採用ページをご覧ください。ご応募をお待ちしております。

info.cookpad.com