— Algolia
本記事は Algolia Advent calendar 21日目の記事になります。
こんにちは、Algolia サポートエンジニアの半田です。 本日の記事では Algolia で日本語検索をする場合に関連するであろうパラメータ群についてまとめてみたいと思います。なお、本記事の内容は2020年12月時点のものとなり将来的には変更される可能性がありますのでご了承ください。
Algolia は多言語での検索に対応したサービスで、検索機能自体は UTF-16 で表現できる文字に対して検索処理が可能なように作られています。このため、多言語検索への対応として同一のインデックス内でも一つのサイトに対して複数の言語での検索を提供するといった実装も可能です。個人的な経験としては実際のサポートの業務の中でも、英語・日本語の他にヘブライ語やアラビア語、中国語、デンマーク語、ベンガル語などでの検索に関するお問い合わせを経験したことがあり、Algolia が世界中で幅広く使われていることを実感しています。
多言語対応といっても検索機能の基本的な部分は共通であり、一般的な検索エンジンと同じように内部では転置インデックスというデータ構造を使用しています。転置インデックスは(最も基本的には)検索キーワードとそのキーワードを持つドキュメントIDからなるデータ構造で、特定のキーワードを持つ文書を高速に見つけることが可能です。
例えば以下のようなテキストデータがあったとすると、
1[2 {"docID":1, "text":"カツオはサザエの弟"},3 {"docID":2, "text":"サザエはワカメの姉"},4 {"docID":3, "text":"ワカメはカツオの妹"}5]
転置インデックスは形式的には以下のようなデータを保持する形になります。
dictionary | postings(docID) |
---|---|
の | 1,2,3 |
は | 1,2,3 |
カツオ | 1,3 |
サザエ | 1,2 |
ワカメ | 2,3 |
姉 | 2 |
弟 | 1 |
妹 | 3 |
これにより、例えば「ワカメ」というクエリに対しては docID 2,3 の文書を検索結果として素早く返却することができます。
さて、転置インデックスを利用した検索処理において特定の言語でのクエリに対して検索結果を返すためには、転置インデックスに含まれる見出し語を「適切に」インデクシングできる必要があります。このインデクシングの際に適用される処理としてはトークナイズ、正規化などが含まれます。
また、これに加えて検索結果をコントロールするための機能としてはSynonym(同義語)、Stopwords などがあります。
ストレスのない検索体験を実現していくためには、検索対象のデータに合わせてこうしたパラメータを適切にチューニングしていくことが重要です。それでは、Algolia上でこれらの動作に影響を与えるパラメータを実際に見てみたいと思います。
まずはじめにご紹介したいパラメータがindexLanguages
とqueryLanguages
です。これらのパラメータはそれぞれインデックス時・検索時に与えられた文字列を処理する際に考慮する言語知識を指定するもので、デフォルトでは特定の言語向けではなく汎用的な設定となっています。
日本語検索という観点では、トークナイズ時に形態素解析の使用の有無を設定する重要なパラメータとなります。
形態素解析はトークナイズの際に意味を持つ最小単位(形態素)をトークンとして抽出する方式です。 英語などの言語と比べ日本語は表記の際に単語の間に明示的な区切り(スペース等)を入れないため、インデクシング時に意味のあるまとまりを抽出するにはあらかじめ内部的に保持した辞書に基づいて、テキストを分割する必要があります。Algolia では辞書としては UniDic、形態素解析器としてはKuromojiベースの形態素解析を適用しています。 これを有効にしない場合、インデックスは検索語の意味を考慮せずキャラクターベースでのトークナイズを行い、連続した文字列が一致したものを検索結果として返却します。
少し具体的に見てみましょう。例えば「東京都」という文字列を含むドキュメントを持つインデックスに対して「京都」というクエリで検索するケースを考えます。
indexLanguages
と queryLanguages
をデフォルトのままで検索をすると、以下のように「京都」というクエリで「東京都」という文字列がヒットしてしまいます。直感的には「東京都」は「東京」+「都」 という意味的なまとまり(形態素)に分割できますが、この設定ではそうした観点は考慮されず、連続的な文字列が一致したものをヒットとして扱っていることがわかります。
次は、同じインデックスで indexLanguages と queryLanguages を"Japanese"に設定してみます。 すると、形態素解析を使用したトークナイズが有効となるため、「京都」というクエリは 「東京都」をトークナイズした結果( 東京 / 都) と一致しなくなり、「東京都」を含む文書にはヒットしなくなります。
代わりに「東京都」で検索した場合には正しく検索結果を取得することができました。
なお、形態素解析によるトークナイズの結果については API レスポンスの parsedQuery
プロパティにて確認できます。
なお、 indexLanguages
と queryLanguages
を ja
にすると日本語でのtypo-tolerance(タイプミス耐性、タイポ許容性)も有効になります。
これにより前々回の記事でご紹介した「アイルランド」というクエリに対しする「アイスランド」を含むレコードのように、タイポを含むような検索結果も返却されるようになります。
ちなみにデフォルトでは検索ノイズを抑止するためminWordSizefor1Typo
の設定値が4となっており、4文字未満のトークンではTypoを含む検索結果は表示されません。個人的には個々の単語が比較的短い日本語の場合は3か2くらいでも良いかなと思います。
indexLanguages
、 queryLanguages
の値はAlgoliaのダッシュボード上から設定することができます。IndexのConfigurationタブから Language メニューを選択し、+Select one or more languages
ボタンをクリックすると言語名を指定できますので、以下のように 各パラメータに Japanese を指定してください。
API経由で有効にする場合は以下のように言語のISOコードを配列で渡します。
1{2 "queryLanguages":["ja"],3 "indexLanguages":["ja"]4}
なお、indexLanguages
と queryLanguages
については設定は個別に行うことができるものの、日本語の場合にはできるだけ揃えて設定いただくことをおすすめします。パラメータがずれると検索時とインデックス時のトークナイズ結果にズレが生じ、意図せぬ検索結果となるケースがありますのでご注意ください。
続いて正規化処理についてです。日本語トークンの正規化に関する機能としては、現在ベータで提供されている機能とはなりますが transliteration (翻字)が挙げられます。 日本語は漢字・カタカナ・ひらがな・アルファベットと複数の文字種が使用でき、同じ単語を書き表すときも複数の表記方法が存在します。 Transliteration を有効にすると日本語解析の際に使用される辞書をベースにインデックス上にひらがなの読み情報が付与されます。この結果、ひらがなのクエリをなげることで漢字・カタカナのレコードもヒットさせることができるようになります。
また、こちらをクエリサジェスト用のインデックスに適用することによりType-aheadという日本語のクエリ自動補完を実装することもできます。こちらについては昨日の shinodogg さんの記事にてがっつり紹介頂いてますのでよろしければ合わせてご参照ください。
なお、現状の制約として読みをカスタマイズするユーザー辞書機能がないため、UniDicに含まれる語のみの対応となるといった点があります。デフォルトの辞書に含まれない語彙についてはSynonymに追加するといった対応でカバーできます。 前述の通り、こちらはベータ機能とりますので、有効化されたい場合には個別にお声がけください。
ちなみに日本語とは直接関係ないのですが、アルファベットに対する正規化処理としてはこちらに記述があり、大文字を小文字に揃えたり記号の削除を行う処理がデフォルトで適用されています。 この他、一部の言語に特化した処理としては英語等で複数形と単数形の屈折変化を同一視する(ignorePlurals)設定やゲルマン系言語で複合語を分割するdecompoundingなどがあります。
一方で、動詞の活用を正規化したり、語根を抽出するようなステミング処理には対応していません。このため、こうした正規化が必要な場合には必要に応じて個別に後述する Synonym として登録する必要があります。
ストップワード除去(Stop words removal)は検索結果に影響を及ぼさない語を検索処理から除外する機能です。 例えば英語の冠詞("a","the")や日本語の助詞("て"、"に"、"を"、"は")などが含まれます。
Algolia のインデックスではストップワード除去はデフォルトでは無効の状態です。これは、実際の検索ユーザーは自然とストップワードを除いたクエリを入力する傾向があり、あえてクエリに含まれる場合にはストップワードを除去をしないほうが期待される検索結果を得られるケースが多いためです。有効化する場合には removeStopWords パラメータに true もしくは対象の言語名を指定することで有効化できます。
ストップワードの除去対象となる語が含まれている辞書はダッシュボード上での"Dictionaries"メニューから参照や編集が可能です。画面右上で対象となる言語を選択し、辞書に含まれる語彙について個別に無効化したり独自のものを追加することができます。
日本語のストップワード辞書を正しく反映させるためにはqueryLanguages
、indexLanguages
に'ja'
を設定する必要がります。また、カスタムユーザー辞書はアプリケーション共通のものとなり、設定を分けたい場合にはアプリケーションを新たに作る必要がある点にご注意ください。
ストップワードで除去された語彙はAPI レスポンスのparsedQuery
からも除外されるため、query
とparsedQuery
を比較することでストップワードの適用が確認できます。以下のクエリ「天気の子」ではストップワードの「の」が除去されていることが確認できます。
最後にご紹介する設定がSynonymです。Synonymは単語同士を同義語として定義することでグループ化し、直接クエリに含まれない単語であってもクエリにヒットさせる事ができるようになる機能です。 使い方の例としては本来的に同じものを指す語彙の統一(例:「ズボン」と「パンツ」)から略語表記(例:「グラコロ」と「グラタンコロッケバーガー」)、表記ゆれの統一(「iPhone」「アイフォン」「アイホン」)などがありますが、様々な目的で検索漏れを防止するためのカスタマイズが可能になります。
なお、前回ご紹介した Rules でも特定のクエリパターンに一致するケースでクエリを書き換えるといった対応が可能です。RulesとSynonymではそれぞれの特性上結果的にRankingやヒットするアイテムが異なることがあるので、必要に応じて比較してみてください。
今回は Algolia のパラメータの中で特に日本語検索のチューニングに関わるものについて概観してみました。自然言語での検索結果のチューニングというのは多くの場合決定的な解が存在せず、日々の改善の積み重ねが重要になりますが、本記事の内容がそうした改善に少しでも役立てば幸いです。 なお、Algoliaの日本語関連のチューニング機能についてはまだまだ改善の余地が残されておりますので、ご利用いただく中で不明点やご要望などがありましたらぜひAlgoliaサポートまでフィードバックいただければと思います。
それでは皆様、よいクリスマス・年末年始をお迎えください!