Weaviateで日本語を利用した検索という話をしました

Posted by johtani on Tuesday, December 17, 2024

目次

この記事は情報検索・検索技術 - Qiita Advent Calendar 2024 - Qiitaの17日目(ちょっと遅刻?)の記事となります。

【日本初主催】新世代のソフトウェアのためのAIネイティブデータベース Weaviate Japan Summit | Peatix」というイベントでKagomeでテキスト検索できる仕組みを実装したものを取り込んでもらった話をしてきました。

Weaviateとは、ベクトル検索やキーワード検索、RAGなどを手軽に体験できるデータベースになります。 OSSで公開されており、SaaSも提供されています。Goで実装されていることもあり、日本語の検索にKagomeが利用できるかも?ということでちょっと調べて実装してみました。

先週の発表に利用した資料は次のリンク先で公開してあります。

セッションでは、日本語のキーワード検索だけではなく、ベクトル検索やキーワードとベクトルを組み合わせたハイブリッド検索をデモしました。 今回はセッションで話をあまりしていない、デモの仕組みに関してブログにまとめようと思います。

デモデータについて

今回は日本語を用いたデモ(今回取り込んでもらった機能を紹介するデモ)が必須であったため、次のような要件でデモを考えていました。

  • 日本語テキストに対してキーワード検索
  • ベクトル化したデータも検索(Weaviateの得意分野)
  • せっかく日本でやるイベントなので日本語を使ったベクトル検索がいい
  • ハイブリッド検索やフィルタ検索(ベクトル検索した結果をキーワードでフィルタリング)のデモをやりたい

テキストだけで当初はデータを探そうと思っていたのですが、ベクトル検索とキーワードの検索での違いが分かりにくくなりそうかも?という懸念がありました。 それよりも、画像を日本語で検索でき、画像に日本語の文章の説明がついているデータがデモをするときの流れも作りやすいのでは?と思い、まずは、Weaviateのマルチモーダルのデモを探すことに。

デモのシステムのベース

Weaviateの方たちは様々なデモを用意してくれており、マルチモーダル検索のデモも用意されていました。 ベースとしたのは、次のリンク先で公開されているサンプルになります。

このサンプルは3つの要素から構成されています。

  1. Weaviate Server
  2. CLIPモデルを用いたServer(Hugging Faceを利用したサービス
  3. Node.jsによるフロントエンド

最初の2つはDockerで起動するコンテナとなっています。

Weaviateにはモジュールという概念があります。このモジュールの中で、入力されたデータをもとにベクトル化するものをVectorizerと呼んでいます。 今回はmulti2vec-clipというVectorizerを利用して、Weaviateから2のCLIPモデルを用いたサービスを呼び出し、画像やテキストをベクトル化してWeaviateに保存したり利用する形をとっています。

上記のサンプルでは、特定のディレクトリ(images)に入れた画像をWeaviateに登録する処理(Goで書かれたコマンド)が用意されています。

フロントエンドでは、検索窓が用意されておりテキストを入力すると、画像が検索結果として出てくるデモです。

テキストを入力としてベクトル検索をするデモの部分はこれでよいのですが、今回はこれに加えてキーワードで検索できるようなデータが必要となります。

デモデータの準備

キーワード検索をするために、画像+テキスト(しかも日本語がよい)ということで、いくつか物色した結果、採用したでものデータは次のものになりました。

MS COCOと呼ばれる、画像に対していくつかのメタデータを追加してあるデータセットが公開されています。 追加されてデータは次のようなものになります。

  • オブジェクト(画像内の位置と移っているオブジェクトのカテゴリ名)
  • キャプション(画像に関する説明文が5つ)

ただし、キャプションデータはもちろん英語となっています。 このデータに対して、千葉工業大学の研究センターの方が日本語でキャプションデータを生成したものを公開してくれています。

MS COCOのの画像のうち商用利用可能なライセンスの画像を今回のデモ用に利用させていただきました。 それぞれのファイルをダウンロードし、MS COCOの画像ファイル名をキーにして英語と日本語のキャプションデータを配列で持つようなJSONデータを生成し、それをもとにWeaviateにデータ登録する形です。

処理の内容は省きますが、上記のデータセットをもとに次のような1ファイル1JSONを作って、JSONLファイル(1行1JSON)として保存したものを読み込んでWeaviateに登録しています。

{
    "id": "200109",
    "license_id": "4",
    "license_name": "Attribution License",
    ...
    "filename": "COCO_val2014_000000200109.jpg",
    "date_captured": "2013-11-22 02:26:57",
    "caption_en": [
        {
            "id": "645817",
            "content": "A slice of pizza partially obscuring a cell phone."
        },
        ...
    ],
    "caption_jp": [
        {
            "id": "41320",
            "content": "食事をしながらメールチェックをする"
        },
        ...
    ],
    "category": [
        ...
    ]
}

英語のキャプションは登録はしましたが、今回のデモには利用しませんでした(日本語で検索できるところを見せたかったので)

スキーマの定義

上記のデータをもとに考えたWeaviateのスキーマは次のようなものになります。 Weaviateで利用できる日本語のトークナイザーのうち、GSEとKagomeを利用してデモをできるようにそれぞれをTokenizationに指定したフィールドをcaption_ja_gsecaption_jaとして用意しました。

画像からベクトルを生成して保存する定義は、Vectorizerという部分のmulti2vec-clipになります。 実際に、画像からベクトルを生成するのは、multi2vec-clipという名前のコンテナで動いているCLIPのモデルを通して処理をします(コンテナ内部で動いている処理などはこちらで公開されています)。

multiModal := &models.Class{
		Class:       ClassName,
		Description: "Sample class holding all the images",
		ModuleConfig: map[string]interface{}{
			"multi2vec-clip": map[string]interface{}{
				"imageFields": []string{"image"},
			},
		},
		VectorIndexType: "flat",
		Vectorizer:      "multi2vec-clip",
		Properties: []*models.Property{
			{
				DataType:    []string{"string"},
				Description: "The name of the file",
				Name:        "filename",
			},
			{
				DataType:    []string{"string"},
				Description: "The Flickr URL",
				Name:        "flickr_url",
			},
			{
				DataType:    []string{"blob"},
				Description: "Base64 encoded image",
				Name:        "image",
			},
			{
				DataType:     []string{schema.DataTypeTextArray.String()},
				Description:  "Caption in English",
				Name:         "caption_en",
				Tokenization: models.PropertyTokenizationLowercase,
			},
			{
				DataType:     []string{schema.DataTypeTextArray.String()},
				Description:  "Caption in Japanese",
				Name:         "caption_ja_gse",
				Tokenization: models.PropertyTokenizationGse,
			},
			{
				DataType:     []string{schema.DataTypeTextArray.String()},
				Description:  "Caption in Japanese",
				Name:         "caption_ja",
				Tokenization: "kagome_ja", // TODO 1.28リリース時には定数を利用可能
			},
			{
				DataType:     []string{schema.DataTypeTextArray.String()},
				Description:  "Category in English",
				Name:         "category",
				Tokenization: models.PropertyTokenizationLowercase,
			},
		},
	}

あとは、GoのWeaviateのSDKを利用してデータを登録するだけです。 登録処理はサンプルをもとに、上記のフィールドのデータを追加した程度です。

これで、Weaviateに日本語と画像のベクトルのデータが保存できました。あとは検索です。

検索処理

参考にしたサンプルでは、Node.jsで処理が書いてあるのですが、私自身はあまりNode.jsを記述したことがありません。 ということで、さくっと作るためにStreamlitを利用して検索画面を作成しました。

Weaviateの接続部分は簡略化(別の便利関数を作った)してありますが、流れはなんとなくわかっていただけるかな?

デモのできることとしては次のような検索です。

  • GSEのフィールドでキーワード検索
  • Kagomeのフィールドでキーワード検索
  • ベクトル検索(テキストをCLIPでベクトル化して検索)
  • ハイブリッド検索(ベクトル検索+Kagomeフィールドでキーワード検索)
  • ベクトル検索+Kagomeのフィールドでキーワードでフィルタリング

まとめ

駆け足ですが、Weaviateで日本語のマルチモーダル検索をやるときにどんなデモデータでどんなデモをやったかを紹介しました。 完全なものではないので恐縮ですが。。。

Weaviate 1.28(2024/12現在最新版)からKagome JAという日本語のキーワード検索用のトークナイザーが利用できるようになっています。 興味がある方は、ちょっと触ってみてはどうでしょうか?

もう少し時間が取れて整理整頓出来たら、Docker Composeなども含めた形でリポジトリで公開しようと思います。 Weaviateで動かしてみようとしたり、ブログで不明点などがあればXなどで連絡をいただければと。


comments powered by Disqus

See Also by Hugo


Related by prelims-cli