億規模の画像検索を最適化する旅 (2/2)
この記事は、UPYUNによる10億規模の画像検索を最適化する旅の後編です。前編を見逃した方はこちらをクリックしてください。
第二世代の画像検索システム
第二世代の画像検索システムは、技術的にはCNN + Milvusソリューションを採用している。このシステムは特徴ベクトルに基づいており、より優れた技術サポートを提供する。
特徴抽出
コンピュータビジョンの分野では、人工知能の利用が主流となっている。第2世代画像検索システムの特徴抽出も同様に、CNN(畳み込みニューラルネットワーク)を基盤技術としている。
CNNという言葉を理解するのは難しい。ここでは2つの質問に答えることに焦点を当てる:
- CNNは何ができるのか?
- なぜ画像検索にCNNが使えるのか?
写真:memegenerator.net
AI分野には多くの競技があり、画像分類は最も重要な競技の1つです。画像分類の仕事は、写真の内容が猫なのか、犬なのか、リンゴなのか、梨なのか、その他の種類の物体なのかを判断することです。
CNNは何ができるのか?CNNは特徴を抽出し、物体を認識することができる。多次元から特徴を抽出し、画像の特徴が猫や犬の特徴にどれだけ近いかを測定する。最も近いものを識別結果として選択することで、特定の画像の内容が猫なのか犬なのか、あるいはそれ以外のものなのかを示すことができる。
CNNの物体識別機能と画像による検索の関係は?我々が欲しいのは、最終的な識別結果ではなく、多次元から抽出された特徴ベクトルである。似たような内容の2つの画像の特徴ベクトルは近くないといけない。
どのCNNモデルを使うべきか?
答えはVGG16である。なぜVGG16を選ぶのか?第一に、VGG16は優れた汎化能力を持っている、つまり非常に汎用性が高い。第二に、VGG16によって抽出される特徴ベクトルは512次元である。次元数が少ないと精度に影響が出る。次元数が多すぎると、特徴ベクトルの保存と計算にかかるコストが相対的に高くなる。
画像の特徴を抽出するためにCNNを使用することは、主流のソリューションである。モデルにはVGG16を、技術的な実装にはKeras + TensorFlowを使うことができる。Kerasの公式サンプルはこちら:
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
numpy as npをインポート
model = VGG16(weights='imagenet', include_top=False)
img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
特徴量 = model.predict(x)
ここで抽出された特徴は特徴ベクトルである。
1.正規化
この後の操作を容易にするために、特徴量を正規化することがよくある:
その後に使われるのも正規化されたnorm_featです。
2.画像の説明
画像はkeras.preprocessingのimage.load_imgメソッドを用いて読み込まれます:
from keras.preprocessing import image
img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
実際には、これはKerasによって呼び出されるTensorFlowメソッドである。詳細はTensorFlowのドキュメントを参照。最終的な画像オブジェクトは、実際にはPIL Imageインスタンス(TensorFlowが使用するPIL)です。
3.バイト変換
実用的には、画像コンテンツはネットワークを通じて送信されることが多い。そのため、パスから画像を読み込むのではなく、バイトデータを直接画像オブジェクト、つまりPIL画像に変換することを好む:
インポート io
from PIL import Image
# img_bytes: 图片内容バイト
img = Image.open(io.BytesIO(img_bytes))
img = img.convert('RGB')
img = img.resize((224, 224), Image.NEAREST)
上記のimgは、image.load_imgメソッドで得られた結果と同じです。注意すべき点が2つある:
- RGB変換をしなければならない。
- サイズを変更すること(resizeは
load_imgメソッドの2番目のパラメータです)。
4.黒枠処理
スクリーンショットのような画像には、黒枠があることがあります。これらの黒枠は実用的な価値はなく、多くの干渉を引き起こします。このため、黒枠を除去することも一般的に行われています。
黒枠とは本質的に、すべてのピクセルが(0, 0, 0)(RGB画像)であるピクセルの行または列のことです。黒枠を削除するには、これらの行または列を見つけ、それらを削除します。これは実際にはNumPyにおける3次元行列の乗算である。
水平方向の黒枠を削除する例:
# コーディング: utf-8 -*-
numpyをnpとしてインポート
from keras.preprocessing import image
def RemoveBlackEdge(img):
Args:
img:PIL画像インスタンス
戻り値
PIL画像インスタンス
"""
幅 = img.width
img = image.img_to_array(img)
img_without_black = img[~np.all(img == np.zeros((1, width, 3), np.uint8), axis=(1, 2))].
img = image.array_to_img(img_without_black)
戻り値 img
これが、CNNを使って画像の特徴を抽出し、その他の画像処理を実装することについてお話ししたいことの大部分である。次に、ベクトル検索エンジンを見てみよう。
ベクター検索エンジン
画像から特徴ベクトルを抽出する問題は解決した。となると、残る問題は
- 特徴ベクトルをどのように保存するか?
- 特徴ベクトルの類似度をどのように計算するか? オープンソースのベクトル検索エンジンMilvusは、この2つの問題を解決することができる。これまでのところ、私たちの本番環境では問題なく動作している。
Milvusのロゴ](https://assets.zilliz.com/3_milvus_logo_3a7411f2c8.png)
ベクター検索エンジンMilvus
画像から特徴ベクトルを抽出するだけでは十分ではありません。さらに、これらの特徴ベクトルを動的に管理(追加、削除、更新)し、ベクトルの類似度を計算し、最近傍範囲にあるベクトルデータを返す必要があります。オープンソースのベクトル検索エンジンMilvusは、これらのタスクを非常にうまくこなします。
この後は、具体的な実践方法と注意点について説明する。
1.CPUの要件
Milvusを使用するには、CPUがavx2命令セットをサポートしている必要がある。Linuxシステムの場合、以下のコマンドでCPUがサポートしている命令セットを確認してください:
cat /proc/cpuinfo | grep flags</code?
すると、次のような結果が得られます:
flags :fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscplm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppintpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts
フラグの後に続くのは、CPUがサポートしている命令セットである。もちろん、これらは私が必要とするものよりもずっと多い。私は、avx2のような特定の命令セットがサポートされているかどうかを確認したいだけだ。それをフィルタリングするためにgrepを追加するだけだ:
cat /proc/cpuinfo | grep flags | grep avx2
結果が返らない場合は、この特定の命令セットがサポートされていないことを意味する。その場合は、マシンを変更する必要がある。
2.容量計画
キャパシティ・プランニングは、システムを設計する際に最初に検討することである。どれだけのデータを保存する必要があるのか?そのデータが必要とするメモリとディスクの容量は?
簡単な計算をしてみよう。ベクトルの各次元はfloat32です。float32型は4バイトを消費します。とすると、512次元のベクトルには2KBのストレージが必要です。同じ意味で
- 1000個の512次元ベクトルは2MBのストレージを必要とする。
- 100万個の512次元ベクトルには2GBのストレージが必要です。
- 1000万個の512次元ベクトルには20GBのストレージが必要です。
- 1億個の512次元ベクトルは200GBのストレージを必要とする。
- 10億個の512次元ベクトルには2 TBのストレージが必要です。
もしすべてのデータをメモリに格納したいのであれば、システムには少なくとも対応するメモリ容量が必要になる。
公式のサイズ計算ツールを使用することをお勧めします:Milvusサイジングツール。
実際にはメモリはそれほど大きくないかもしれません(メモリが足りなくても問題はありません)。Milvusは自動的にデータをディスクにフラッシュします)。元のベクターデータに加えて、ログなど他のデータの保存も考慮する必要があります。
3.システム構成
システム構成については、Milvusのドキュメントを参照されたい:
- Milvusサーバ設定: https://milvus.io/docs/v0.10.1/milvus_config.md
4.データベース設計
**コレクションとパーティション
- コレクションはテーブルとも呼ばれる。
- パーティションはコレクション内のパーティションを指す。
パーティションがコレクションの下にあることを除けば、パーティションの基本的な実装はコレクションと同じです。しかし、パーティションを使うことで、データの構成がより柔軟になります。また、コレクション内の特定のパーティションをクエリすることで、より良いクエリ結果を得ることができます。
コレクションとパーティションの数は?コレクションとパーティションの基本情報はメタデータにあります。Milvusは内部メタデータ管理にSQLite(Milvus内部統合)またはMySQL(外部接続が必要)を使用します。デフォルトでSQLiteを使用してメタデータを管理する場合、コレクションとパーティションの数が多すぎるとパフォーマンスが著しく低下します。そのため、コレクション数とパーティション数の合計が50,000を超えないようにしてください(Milvus 0.8.0では4,096に制限されています)。それ以上の数を設定する必要がある場合は、外部接続経由でMySQLを使用することをお勧めします。
Milvusのコレクションとパーティションがサポートするデータ構造は非常に単純で、ID + vectorです。つまり、テーブルには2つのカラムしかない:IDとベクトルデータです。
注:*。
- IDは整数でなければならない。
- IDはパーティション内ではなく、コレクション内で一意であることを保証する必要がある。
**条件付きフィルタリング
従来のデータベースを使用する場合、フィールド値をフィルタリング条件として指定することができます。Milvusは全く同じ方法でフィルタリングを行うわけではありませんが、コレクションとパーティションを使って簡単な条件付きフィルタリングを実装することができます。例えば、大量の画像データがあり、そのデータが特定のユーザに属しているとします。その場合、データをユーザーごとにパーティションに分けることができる。したがって、フィルター条件としてユーザーを使うことは、実際にはパーティションを指定することになる。
**構造化データとベクトルマッピング
MilvusはID+ベクトルのデータ構造しかサポートしていません。しかし、ビジネスシナリオにおいて必要なのは、ビジネス上の意味を持つ構造化データである。つまり、ベクトルを通して構造化データを見つける必要があるのです。従って、IDを通して構造化データとベクトルのマッピング関係を維持する必要があります。
構造化データID <--> マッピングテーブル <--> Milvus ID
インデックスの選択
以下の記事を参考にしてください:
- インデックスの種類: https://www.milvus.io/docs/v0.10.1/index.md
- インデックスの選び方: https://medium.com/@milvusio/how-to-choose-an-index-in-milvus-4f3d15259212
5.検索結果の処理
Milvusの検索結果は、ID+距離の集合である:
- ID: コレクション内の ID。
- 距離: 0 ~ 1の距離値は類似度を示し、値が小さいほど2つのベクトルは類似している。
IDが-1のデータをフィルタリングする。
コレクション数が少ない場合、検索結果にIDが-1のデータが含まれることがあります。これを自分でフィルタリングする必要がある。
**ページネーション
ベクトルの検索はかなり異なります。クエリー結果は類似度の降順にソートされ、最も類似した(topK)結果が選択される(topKはクエリー時にユーザーによって指定される)。
Milvusはページ分割をサポートしていない。ページネーション機能が必要な場合は、自前で実装する必要がある。例えば、各ページに10件の結果があり、3ページ目だけを表示したい場合、topK = 30と指定し、最後の10件だけを返す必要があります。
**ビジネスにおける類似度のしきい値
2つの画像のベクトル間の距離は0から1の間です。特定のビジネスシナリオで2つの画像が類似しているかどうかを判断したい場合、この範囲内で閾値を指定する必要があります。距離がしきい値より小さければ2つの画像は似ており、距離がしきい値より大きければ互いにかなり異なっています。閾値は、ご自身のビジネスニーズに合わせて調整する必要があります。
この記事はMilvusのユーザーであり、UPYUNのソフトウェアエンジニアであるrifewangによって書かれました。この記事が気に入ったら、https://github.com/rifewang。
読み続けて

How to Choose the Best Embedding Model for RAG in 2026: 10 Models Benchmarked
We benchmarked 10 embedding models on cross-modal, cross-lingual, long-document, and dimension compression tasks. See which one fits your RAG pipeline.

Migrating from S3 Vectors to Zilliz Cloud: Unlocking the Power of Tiered Storage
Learn how Zilliz Cloud bridges cost and performance with tiered storage and enterprise-grade features, and how to migrate data from AWS S3 Vectors to Zilliz Cloud.

What is the K-Nearest Neighbors (KNN) Algorithm in Machine Learning?
KNN is a supervised machine learning technique and algorithm for classification and regression. This post is the ultimate guide to KNN.
